Skip to content

Commit 617e2ea

Browse files
committed
feat(json-crdt-peritext-ui): 🎸 swap selection edges on Meta + Meta key sequence
1 parent df36299 commit 617e2ea

File tree

4 files changed

+29
-4
lines changed

4 files changed

+29
-4
lines changed

‎src/json-crdt-peritext-ui/events/defaults/PeritextEventDefaults.ts‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,22 @@ export class PeritextEventDefaults implements PeritextEventHandlerMap {
173173
};
174174

175175
public readonly cursor = ({detail}: CustomEvent<events.CursorDetail>) => {
176-
const {at, move, add} = detail;
176+
const {at, move, add, flip} = detail;
177177
if (at === void 0) {
178178
const selection = this.getSelSet(detail);
179179
this.moveSelSet(selection, detail);
180180

181181
// Collapse cursors if there are no visible characters between edges.
182-
if (move && move.length === 1 && move[0][0] === 'focus') {
182+
// (Only for relative focus edge moves.)
183+
if (move && move.length === 1 && move[0][0] === 'focus')
183184
for (const range of selection)
184185
if (range.length() === 0) range.collapseToStart();
185-
}
186+
187+
// Swap anchor and focus edges.
188+
if (flip)
189+
for (const range of selection)
190+
if (range instanceof Cursor) range.anchorSide = range.anchorSide === CursorAnchor.Start
191+
? CursorAnchor.End : CursorAnchor.Start;
186192
} else {
187193
const {txt} = this;
188194
const {editor} = txt;

‎src/json-crdt-peritext-ui/events/types.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,14 @@ export interface CursorDetail extends RangeEventDetail {
247247
* @default false
248248
*/
249249
add?: boolean;
250+
251+
/**
252+
* Swap cursor anchor and focus points. Performed after the move operations
253+
* are applied to the selection set. The flip is applied only to the existing
254+
* cursors in the selection set, not to the new cursor created by the `at`
255+
* field.
256+
*/
257+
flip?: boolean;
250258
}
251259

252260
/**

‎src/json-crdt-peritext-ui/plugins/toolbar/state/ToolbarState.tsx‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ export class ToolbarState implements UiLifeCycles {
111111
el.addEventListener('keydown', onKeyDown);
112112
document.addEventListener('keydown', onKeyDownDocument);
113113
et.addEventListener('cursor', onCursor);
114+
115+
const unsubscribeKeyHistory = dom.keys.history.subscribe(() => {
116+
// Flip selection anchor and focus edges on quick [Meta, Meta] key sequence.
117+
const keys = dom.keys.history.value;
118+
const last = keys[keys.length - 1];
119+
const beforeLast = keys[keys.length - 2];
120+
if (last?.key === 'Meta' && beforeLast?.key === 'Meta')
121+
if (last.ts - beforeLast.ts < 500) et.cursor({flip: true});
122+
});
123+
114124
return () => {
115125
changeUnsubscribe();
116126
unsubscribeMouseDown?.();
@@ -119,6 +129,7 @@ export class ToolbarState implements UiLifeCycles {
119129
el.removeEventListener('keydown', onKeyDown);
120130
document.removeEventListener('keydown', onKeyDownDocument);
121131
et.removeEventListener('cursor', onCursor);
132+
unsubscribeKeyHistory();
122133
};
123134
}
124135

‎src/json-crdt-peritext-ui/web/dom/KeyController.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {UiLifeCycles} from '../types';
44
import type {DomController} from './DomController';
55

66
class KeyPress {
7-
public constructor(public readonly key: string, public readonly time: number) {}
7+
public constructor(public readonly key: string, public readonly ts: number) {}
88
}
99

1010
export interface KeyControllerOpts {}

0 commit comments

Comments
 (0)