1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-14 18:32:01 +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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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,13 +175,13 @@ export const Editable = (props: EditableProps) => {
}
// If the DOM selection is in the editor and the editor selection is already correct, we're done.
if (
hasDomSelection &&
hasDomSelectionInEditor &&
selection &&
Range.equals(ReactEditor.toSlateRange(editor, domSelection), selection)
) {
return
if (hasDomSelection && hasDomSelectionInEditor && selection) {
const slateRange = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: true,
})
if (slateRange && Range.equals(slateRange, selection)) {
return
}
}
// when <Editable/> is being controlled through external value
@ -189,7 +189,9 @@ export const Editable = (props: EditableProps) => {
// 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)
editor.selection = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
})
return
}
@ -280,7 +282,9 @@ export const Editable = (props: EditableProps) => {
const [targetRange] = (event as any).getTargetRanges()
if (targetRange) {
const range = ReactEditor.toSlateRange(editor, targetRange)
const range = ReactEditor.toSlateRange(editor, targetRange, {
exactMatch: false,
})
if (!selection || !Range.equals(selection, range)) {
Transforms.select(editor, range)
@ -444,7 +448,9 @@ export const Editable = (props: EditableProps) => {
isTargetInsideVoid(editor, focusNode)
if (anchorNodeSelectable && focusNodeSelectable) {
const range = ReactEditor.toSlateRange(editor, domSelection)
const range = ReactEditor.toSlateRange(editor, domSelection, {
exactMatch: false,
})
Transforms.select(editor, range)
} else {
Transforms.deselect(editor)

View File

@ -432,7 +432,9 @@ export const ReactEditor = {
}
// Resolve a Slate range from the DOM range.
const range = ReactEditor.toSlateRange(editor, domRange)
const range = ReactEditor.toSlateRange(editor, domRange, {
exactMatch: false,
})
return range
},
@ -440,8 +442,14 @@ export const ReactEditor = {
* Find a Slate point from a DOM selection's `domNode` and `domOffset`.
*/
toSlatePoint(editor: ReactEditor, domPoint: DOMPoint): Point {
const [nearestNode, nearestOffset] = normalizeDOMPoint(domPoint)
toSlatePoint<T extends boolean>(
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
let textNode: DOMElement | null = null
let offset = 0
@ -513,6 +521,9 @@ export const ReactEditor = {
}
if (!textNode) {
if (extractMatch) {
return null as T extends true ? Point | null : Point
}
throw new Error(
`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)
const slateNode = ReactEditor.toSlateNode(editor, textNode!)
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.
*/
toSlateRange(
toSlateRange<T extends boolean>(
editor: ReactEditor,
domRange: DOMRange | DOMStaticRange | DOMSelection
): Range {
domRange: DOMRange | DOMStaticRange | DOMSelection,
options: {
exactMatch: T
}
): T extends true ? Range | null : Range {
const { exactMatch } = options
const el = isDOMSelection(domRange)
? domRange.anchorNode
: 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
? 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 {