1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-10 17:24:02 +02:00

feat: sync selection to extactly matched DOM selection (#4157)

* feat: sync selection to extractly dom selection

* revert mistakenly modified files

* add changeset

* toSlatePoint will throw error again when not extractMatch

* Fix misspellings, rename extractMatch to exactMatch

* rename option to match code style

* Update four-poets-move.md

Co-authored-by: Ian Storm Taylor <ian@ianstormtaylor.com>
This commit is contained in:
Githoniel
2021-04-13 22:58:46 +08:00
committed by GitHub
parent 6a6d9f614d
commit de5cc7e5ed
3 changed files with 59 additions and 20 deletions

View File

@@ -0,0 +1,5 @@
---
'slate-react': patch
---
Fixed a bug when syncing the selection for IME-based editing.

View File

@@ -175,21 +175,23 @@ export const Editable = (props: EditableProps) => {
} }
// If the DOM selection is in the editor and the editor selection is already correct, we're done. // If the DOM selection is in the editor and the editor selection is already correct, we're done.
if ( if (hasDomSelection && hasDomSelectionInEditor && selection) {
hasDomSelection && const slateRange = ReactEditor.toSlateRange(editor, domSelection, {
hasDomSelectionInEditor && exactMatch: true,
selection && })
Range.equals(ReactEditor.toSlateRange(editor, domSelection), selection) if (slateRange && Range.equals(slateRange, selection)) {
) {
return return
} }
}
// when <Editable/> is being controlled through external value // when <Editable/> is being controlled through external value
// then its children might just change - DOM responds to it on its own // then its children might just change - DOM responds to it on its own
// but Slate's value is not being updated through any operation // but Slate's value is not being updated through any operation
// and thus it doesn't transform selection on its own // and thus it doesn't transform selection on its own
if (selection && !ReactEditor.hasRange(editor, selection)) { if (selection && !ReactEditor.hasRange(editor, selection)) {
editor.selection = ReactEditor.toSlateRange(editor, domSelection) editor.selection = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
})
return return
} }
@@ -280,7 +282,9 @@ export const Editable = (props: EditableProps) => {
const [targetRange] = (event as any).getTargetRanges() const [targetRange] = (event as any).getTargetRanges()
if (targetRange) { if (targetRange) {
const range = ReactEditor.toSlateRange(editor, targetRange) const range = ReactEditor.toSlateRange(editor, targetRange, {
exactMatch: false,
})
if (!selection || !Range.equals(selection, range)) { if (!selection || !Range.equals(selection, range)) {
Transforms.select(editor, range) Transforms.select(editor, range)
@@ -444,7 +448,9 @@ export const Editable = (props: EditableProps) => {
isTargetInsideVoid(editor, focusNode) isTargetInsideVoid(editor, focusNode)
if (anchorNodeSelectable && focusNodeSelectable) { if (anchorNodeSelectable && focusNodeSelectable) {
const range = ReactEditor.toSlateRange(editor, domSelection) const range = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
})
Transforms.select(editor, range) Transforms.select(editor, range)
} else { } else {
Transforms.deselect(editor) Transforms.deselect(editor)

View File

@@ -432,7 +432,9 @@ export const ReactEditor = {
} }
// Resolve a Slate range from the DOM range. // Resolve a Slate range from the DOM range.
const range = ReactEditor.toSlateRange(editor, domRange) const range = ReactEditor.toSlateRange(editor, domRange, {
exactMatch: false,
})
return range return range
}, },
@@ -440,8 +442,14 @@ export const ReactEditor = {
* Find a Slate point from a DOM selection's `domNode` and `domOffset`. * Find a Slate point from a DOM selection's `domNode` and `domOffset`.
*/ */
toSlatePoint(editor: ReactEditor, domPoint: DOMPoint): Point { toSlatePoint<T extends boolean>(
const [nearestNode, nearestOffset] = normalizeDOMPoint(domPoint) editor: ReactEditor,
domPoint: DOMPoint,
extractMatch: T
): T extends true ? Point | null : Point {
const [nearestNode, nearestOffset] = extractMatch
? domPoint
: normalizeDOMPoint(domPoint)
const parentNode = nearestNode.parentNode as DOMElement const parentNode = nearestNode.parentNode as DOMElement
let textNode: DOMElement | null = null let textNode: DOMElement | null = null
let offset = 0 let offset = 0
@@ -513,6 +521,9 @@ export const ReactEditor = {
} }
if (!textNode) { if (!textNode) {
if (extractMatch) {
return null as T extends true ? Point | null : Point
}
throw new Error( throw new Error(
`Cannot resolve a Slate point from DOM point: ${domPoint}` `Cannot resolve a Slate point from DOM point: ${domPoint}`
) )
@@ -523,17 +534,21 @@ export const ReactEditor = {
// first, and then afterwards for the correct `element`. (2017/03/03) // first, and then afterwards for the correct `element`. (2017/03/03)
const slateNode = ReactEditor.toSlateNode(editor, textNode!) const slateNode = ReactEditor.toSlateNode(editor, textNode!)
const path = ReactEditor.findPath(editor, slateNode) const path = ReactEditor.findPath(editor, slateNode)
return { path, offset } return { path, offset } as T extends true ? Point | null : Point
}, },
/** /**
* Find a Slate range from a DOM range or selection. * Find a Slate range from a DOM range or selection.
*/ */
toSlateRange( toSlateRange<T extends boolean>(
editor: ReactEditor, editor: ReactEditor,
domRange: DOMRange | DOMStaticRange | DOMSelection domRange: DOMRange | DOMStaticRange | DOMSelection,
): Range { options: {
exactMatch: T
}
): T extends true ? Range | null : Range {
const { exactMatch } = options
const el = isDOMSelection(domRange) const el = isDOMSelection(domRange)
? domRange.anchorNode ? domRange.anchorNode
: domRange.startContainer : domRange.startContainer
@@ -580,12 +595,25 @@ export const ReactEditor = {
) )
} }
const anchor = ReactEditor.toSlatePoint(editor, [anchorNode, anchorOffset]) const anchor = ReactEditor.toSlatePoint(
editor,
[anchorNode, anchorOffset],
exactMatch
)
if (!anchor) {
return null as T extends true ? Range | null : Range
}
const focus = isCollapsed const focus = isCollapsed
? anchor ? anchor
: ReactEditor.toSlatePoint(editor, [focusNode, focusOffset]) : ReactEditor.toSlatePoint(editor, [focusNode, focusOffset], exactMatch)
if (!focus) {
return null as T extends true ? Range | null : Range
}
return { anchor, focus } return ({ anchor, focus } as unknown) as T extends true
? Range | null
: Range
}, },
hasRange(editor: ReactEditor, range: Range): boolean { hasRange(editor: ReactEditor, range: Range): boolean {