Skip to content

Commit 87f193f

Browse files
committed
slate: set DOM selection to slate selection more robustly on anything that involves focus
1 parent ccb8018 commit 87f193f

File tree

6 files changed

+24
-17
lines changed

6 files changed

+24
-17
lines changed

src/packages/frontend/editors/slate/edit-bar/list-edit.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export const ListEdit: React.FC<Props> = ({ listProperties, editor }) => {
122122
style={{ color: "#666" }}
123123
onClick={() => {
124124
moveListItemUp(editor);
125-
ReactEditor.focus(editor, false, true);
125+
ReactEditor.focus(editor, false);
126126
}}
127127
>
128128
<Icon name={"arrow-up"} />
@@ -137,7 +137,7 @@ export const ListEdit: React.FC<Props> = ({ listProperties, editor }) => {
137137
style={{ color: "#666" }}
138138
onClick={() => {
139139
moveListItemDown(editor);
140-
ReactEditor.focus(editor, false, true);
140+
ReactEditor.focus(editor, false);
141141
}}
142142
>
143143
<Icon name={"arrow-down"} />

src/packages/frontend/editors/slate/editable-markdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ export const EditableMarkdown: React.FC<Props> = React.memo((props: Props) => {
622622
// about selection at all. TODO: we might be able to avoid the
623623
// slateDiff stuff entirely via some tricky stuff, e.g., managing
624624
// the cursor on the plain text side before/after the change, since
625-
// codemirror is much faster att "setValueNoJump".
625+
// codemirror is much faster at "setValueNoJump".
626626
// The main time we use this optimization here is when opening the
627627
// document in the first place, in which case we're converting
628628
// the document from "Loading..." to it's initial value.

src/packages/frontend/editors/slate/elements/code-block/insert-bar.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,21 @@ function InsertButton({ children, onClick }) {
2525
}
2626

2727
export default function InsertBar({ editor, element, info, above }) {
28-
//const { hasLanguageModel } = useFileContext();
29-
3028
const insert = (node: Node, offset = 0) => {
3129
let path = findElement(editor, element);
3230
if (path && !above) {
3331
path = Path.next(path);
3432
}
3533
Transforms.insertNodes(editor, node, { at: path });
36-
ReactEditor.focus(editor, true, true);
34+
ReactEditor.focus(editor, true);
3735
if (path) {
3836
setTimeout(() => {
3937
const sel = {
4038
anchor: { path: path!, offset: 0 },
4139
focus: { path: path!, offset },
4240
};
4341
Transforms.setSelection(editor, sel);
44-
ReactEditor.focus(editor, true, true);
42+
ReactEditor.focus(editor, true);
4543
}, 50);
4644
}
4745
};

src/packages/frontend/editors/slate/elements/codemirror.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export const SlateCodeMirror: React.FC<Props> = React.memo(
129129
...cmOptions?.extraKeys,
130130
"Shift-Enter": () => {
131131
Transforms.move(editor, { distance: 1, unit: "line" });
132-
ReactEditor.focus(editor, false, true);
132+
ReactEditor.focus(editor, false);
133133
onShiftEnter?.();
134134
},
135135
// We make it so doing select all when not everything is
@@ -342,7 +342,7 @@ function cursorHandlers(editor, isInline: boolean | undefined) {
342342
if (cur_line === n && cur_ch === line_length) {
343343
//Transforms.move(editor, { distance: 1, unit: "line" });
344344
moveCursorDown(editor, true);
345-
ReactEditor.focus(editor, false, true);
345+
ReactEditor.focus(editor, false);
346346
return true;
347347
} else {
348348
return false;
@@ -358,7 +358,7 @@ function cursorHandlers(editor, isInline: boolean | undefined) {
358358
if (!isInline) {
359359
moveCursorToBeginningOfBlock(editor);
360360
}
361-
ReactEditor.focus(editor, false, true);
361+
ReactEditor.focus(editor, false);
362362
} else {
363363
commands.goLineUp(cm);
364364
}
@@ -367,7 +367,7 @@ function cursorHandlers(editor, isInline: boolean | undefined) {
367367
const cur = cm.getCursor();
368368
if (cur?.line === cm.firstLine() && cur?.ch == 0) {
369369
Transforms.move(editor, { distance: 1, unit: "line", reverse: true });
370-
ReactEditor.focus(editor, false, true);
370+
ReactEditor.focus(editor, false);
371371
} else {
372372
commands.goCharLeft(cm);
373373
}

src/packages/frontend/editors/slate/format/commands.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,12 @@ export function getCollapsedSelection(editor: SlateEditor): Range {
311311
export function setSelectionAndFocus(editor: ReactEditor, selection): void {
312312
ReactEditor.focus(editor);
313313
Transforms.setSelection(editor, selection);
314+
315+
// see comment in react-editor.ts
316+
const { updateDOMSelection } = editor;
317+
if (updateDOMSelection != null) {
318+
window.requestAnimationFrame(updateDOMSelection);
319+
}
314320
}
315321

316322
export function restoreSelectionAndFocus(editor: SlateEditor): void {

src/packages/frontend/editors/slate/slate-react/plugin/react-editor.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,7 @@ export const ReactEditor = {
137137
* Focus the editor.
138138
*/
139139

140-
focus(
141-
editor: ReactEditor,
142-
force: boolean = false,
143-
preserveSelection: boolean = false,
144-
): void {
140+
focus(editor: ReactEditor, force: boolean = false): void {
145141
const { selection } = editor;
146142
const el = ReactEditor.toDOMNode(editor, editor);
147143
IS_FOCUSED.set(editor, true);
@@ -155,7 +151,7 @@ export const ReactEditor = {
155151
el.blur();
156152
el.focus({ preventScroll: true });
157153
}
158-
if (selection != null && preserveSelection) {
154+
if (selection != null) {
159155
// I've changed the focus method to optionally preserve the selection if there is one.
160156
// However, doing this when not needed may be the cause of
161157
// https://github.com/sagemathinc/cocalc/issues/6803
@@ -166,6 +162,13 @@ export const ReactEditor = {
166162
// it's very important to restore the selection to where it was before
167163
// focusing, since otherwise the selection is reset to the top of the document.
168164
Transforms.setSelection(editor, selection);
165+
// Critical to update the DOM selection ASAP, but not in same render loop (since that's too soon).
166+
// Note that useLayoutEffect is not quick enough and things get broken without this below. An example
167+
// is in virtualized mode when moving a list item down in a list.
168+
const { updateDOMSelection } = editor;
169+
if (updateDOMSelection != null) {
170+
window.requestAnimationFrame(updateDOMSelection);
171+
}
169172
}
170173
},
171174

0 commit comments

Comments
 (0)