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:
parent
6a6d9f614d
commit
de5cc7e5ed
5
.changeset/four-poets-move.md
Normal file
5
.changeset/four-poets-move.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'slate-react': patch
|
||||
---
|
||||
|
||||
Fixed a bug when syncing the selection for IME-based editing.
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user