diff --git a/packages/slate-react/src/components/editable.tsx b/packages/slate-react/src/components/editable.tsx index 7a3642613..5539d1089 100644 --- a/packages/slate-react/src/components/editable.tsx +++ b/packages/slate-react/src/components/editable.tsx @@ -145,13 +145,11 @@ export const Editable = (props: EditableProps) => { return } - const newDomRange = selection && ReactEditor.toDOMRange(editor, selection) - // If the DOM selection is already correct, we're done. if ( hasDomSelection && - newDomRange && - isRangeEqual(domSelection.getRangeAt(0), newDomRange) + selection && + Range.equals(ReactEditor.toSlateRange(editor, domSelection), selection) ) { return } @@ -161,6 +159,8 @@ export const Editable = (props: EditableProps) => { state.isUpdatingSelection = true domSelection.removeAllRanges() + const newDomRange = selection && ReactEditor.toDOMRange(editor, selection) + if (newDomRange) { domSelection.addRange(newDomRange!) const leafEl = newDomRange.startContainer.parentElement! @@ -356,10 +356,6 @@ export const Editable = (props: EditableProps) => { const { activeElement } = window.document const el = ReactEditor.toDOMNode(editor, editor) const domSelection = window.getSelection() - const domRange = - domSelection && - domSelection.rangeCount > 0 && - domSelection.getRangeAt(0) if (activeElement === el) { state.latestElement = activeElement @@ -369,11 +365,11 @@ export const Editable = (props: EditableProps) => { } if ( - domRange && - hasEditableTarget(editor, domRange.startContainer) && - hasEditableTarget(editor, domRange.endContainer) + domSelection && + hasEditableTarget(editor, domSelection.anchorNode) && + hasEditableTarget(editor, domSelection.focusNode) ) { - const range = ReactEditor.toSlateRange(editor, domRange) + const range = ReactEditor.toSlateRange(editor, domSelection) Transforms.select(editor, range) } else { Transforms.deselect(editor) diff --git a/packages/slate-react/src/plugin/react-editor.ts b/packages/slate-react/src/plugin/react-editor.ts index dff39614e..f0e47cb7b 100644 --- a/packages/slate-react/src/plugin/react-editor.ts +++ b/packages/slate-react/src/plugin/react-editor.ts @@ -264,16 +264,30 @@ export const ReactEditor = { toDOMRange(editor: ReactEditor, range: Range): DOMRange { const { anchor, focus } = range + const isBackward = Range.isBackward(range) const domAnchor = ReactEditor.toDOMPoint(editor, anchor) const domFocus = Range.isCollapsed(range) ? domAnchor : ReactEditor.toDOMPoint(editor, focus) const domRange = window.document.createRange() - const start = Range.isBackward(range) ? domFocus : domAnchor - const end = Range.isBackward(range) ? domAnchor : domFocus - domRange.setStart(start[0], start[1]) - domRange.setEnd(end[0], end[1]) + const [startNode, startOffset] = isBackward ? domFocus : domAnchor + const [endNode, endOffset] = isBackward ? domAnchor : domFocus + + // A slate Point at zero-width Leaf always has an offset of 0 but a native DOM selection at + // zero-width node has an offset of 1 so we have to check if we are in a zero-width node and + // adjust the offset accordingly. + const startEl = (isDOMElement(startNode) + ? startNode + : startNode.parentElement) as HTMLElement + const isStartAtZeroWidth = !!startEl.getAttribute('data-slate-zero-width') + const endEl = (isDOMElement(endNode) + ? endNode + : endNode.parentElement) as HTMLElement + const isEndAtZeroWidth = !!endEl.getAttribute('data-slate-zero-width') + + domRange.setStart(startNode, isStartAtZeroWidth ? 1 : startOffset) + domRange.setEnd(endNode, isEndAtZeroWidth ? 1 : endOffset) return domRange },