From f3fb40cce044b73b6da13013f90bd7018f2f5d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 31 Mar 2021 22:13:42 +0200 Subject: [PATCH] Fixed an issue with controlled value messing up editor.selection (#3652) * Fixed an issue with controlled value messing up editor.selection * Create fifty-ducks-sip.md Co-authored-by: Ian Storm Taylor --- .changeset/fifty-ducks-sip.md | 5 +++++ packages/slate-react/src/components/editable.tsx | 9 +++++++++ packages/slate-react/src/plugin/react-editor.ts | 8 ++++++++ packages/slate/src/interfaces/editor.ts | 4 ++++ 4 files changed, 26 insertions(+) create mode 100644 .changeset/fifty-ducks-sip.md 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.