diff --git a/.changeset/fifty-ducks-sip.md b/.changeset/fifty-ducks-sip.md new file mode 100644 index 000000000..65acfcec4 --- /dev/null +++ b/.changeset/fifty-ducks-sip.md @@ -0,0 +1,5 @@ +--- +'slate-react': patch +--- + +Fixed selection logic when a controlled editor's nodes change out from under it. diff --git a/packages/slate-react/src/components/editable.tsx b/packages/slate-react/src/components/editable.tsx index d12b2ea87..26d6a52af 100644 --- a/packages/slate-react/src/components/editable.tsx +++ b/packages/slate-react/src/components/editable.tsx @@ -182,6 +182,15 @@ export const Editable = (props: EditableProps) => { return } + // when is being controlled through external value + // then its children might just change - DOM responds to it on its own + // but Slate's value is not being updated through any operation + // and thus it doesn't transform selection on its own + if (selection && !ReactEditor.hasRange(editor, selection)) { + editor.selection = ReactEditor.toSlateRange(editor, domSelection) + return + } + // Otherwise the DOM selection is out of sync, so update it. const el = ReactEditor.toDOMNode(editor, editor) state.isUpdatingSelection = true diff --git a/packages/slate-react/src/plugin/react-editor.ts b/packages/slate-react/src/plugin/react-editor.ts index 8a449a44a..35100a080 100644 --- a/packages/slate-react/src/plugin/react-editor.ts +++ b/packages/slate-react/src/plugin/react-editor.ts @@ -32,6 +32,7 @@ import { IS_CHROME } from '../utils/environment' export interface ReactEditor extends BaseEditor { insertData: (data: DataTransfer) => void setFragmentData: (data: DataTransfer) => void + hasRange: (editor: ReactEditor, range: Range) => boolean } export const ReactEditor = { @@ -573,4 +574,11 @@ export const ReactEditor = { return { anchor, focus } }, + + hasRange(editor: ReactEditor, range: Range): boolean { + const { anchor, focus } = range + return ( + Editor.hasPath(editor, anchor.path) && Editor.hasPath(editor, focus.path) + ) + }, } diff --git a/packages/slate/src/interfaces/editor.ts b/packages/slate/src/interfaces/editor.ts index 5cc811c35..3eac5e0e4 100755 --- a/packages/slate/src/interfaces/editor.ts +++ b/packages/slate/src/interfaces/editor.ts @@ -1060,6 +1060,10 @@ export const Editor: EditorInterface = { return at }, + hasPath(editor: Editor, path: Path): boolean { + return Node.has(editor, path) + }, + /** * Create a mutable ref for a `Path` object, which will stay in sync as new * operations are applied to the editor.