1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-23 15:32:59 +02:00

Fix firefox disconnected selection api usage (#5486)

* Fix firefox disconnected selection api usage

* Add changeset

* Fix typo + add link to explanation

* Fix integration tests
This commit is contained in:
WcaleNieWolny
2023-07-26 16:09:26 +02:00
committed by GitHub
parent ca77e934bc
commit 8b548fb53a
3 changed files with 69 additions and 61 deletions

View File

@@ -0,0 +1,5 @@
---
'slate-react': minor
---
Fix invalid usage of the selection API in firefox

View File

@@ -305,12 +305,33 @@ export const Editable = (props: EditableProps) => {
return
}
// Get anchorNode and focusNode
const focusNode = domSelection.focusNode
let anchorNode
// COMPAT: In firefox the normal seletion way does not work
// (https://github.com/ianstormtaylor/slate/pull/5486#issue-1820720223)
if (IS_FIREFOX && domSelection.rangeCount > 1) {
const firstRange = domSelection.getRangeAt(0)
const lastRange = domSelection.getRangeAt(domSelection.rangeCount - 1)
// Right to left
if (firstRange.startContainer === focusNode) {
anchorNode = lastRange.endContainer
} else {
// Left to right
anchorNode = firstRange.startContainer
}
} else {
anchorNode = domSelection.anchorNode
}
// verify that the dom selection is in the editor
const editorElement = EDITOR_TO_ELEMENT.get(editor)!
let hasDomSelectionInEditor = false
if (
editorElement.contains(domSelection.anchorNode) &&
editorElement.contains(domSelection.focusNode)
editorElement.contains(anchorNode) &&
editorElement.contains(focusNode)
) {
hasDomSelectionInEditor = true
}
@@ -336,7 +357,6 @@ export const Editable = (props: EditableProps) => {
}
// Ensure selection is inside the mark placeholder
const { anchorNode } = domSelection
if (
anchorNode?.parentElement?.hasAttribute(
'data-slate-mark-placeholder'
@@ -391,19 +411,16 @@ export const Editable = (props: EditableProps) => {
return newDomRange
}
const newDomRange = setDomSelection()
// In firefox if there is more then 1 range and we call setDomSelection we remove the ability to select more cells in a table
if (domSelection.rangeCount <= 1) {
setDomSelection()
}
const ensureSelection =
androidInputManagerRef.current?.isFlushing() === 'action'
if (!IS_ANDROID || !ensureSelection) {
setTimeout(() => {
// COMPAT: In Firefox, it's not enough to create a range, you also need
// to focus the contenteditable element too. (2016/11/16)
if (newDomRange && IS_FIREFOX) {
const el = ReactEditor.toDOMNode(editor, editor)
el.focus()
}
state.isUpdatingSelection = false
})
return

View File

@@ -829,15 +829,37 @@ export const ReactEditor: ReactEditorInterface = {
if (el) {
if (isDOMSelection(domRange)) {
// COMPAT: In firefox the normal seletion way does not work
// (https://github.com/ianstormtaylor/slate/pull/5486#issue-1820720223)
if (IS_FIREFOX && domRange.rangeCount > 1) {
focusNode = domRange.focusNode // Focus node works fine
const firstRange = domRange.getRangeAt(0)
const lastRange = domRange.getRangeAt(domRange.rangeCount - 1)
// Right to left
if (firstRange.startContainer === focusNode) {
anchorNode = lastRange.endContainer
anchorOffset = lastRange.endOffset
focusOffset = firstRange.startOffset
} else {
// Left to right
anchorNode = firstRange.startContainer
anchorOffset = firstRange.endOffset
focusOffset = lastRange.startOffset
}
} else {
anchorNode = domRange.anchorNode
anchorOffset = domRange.anchorOffset
focusNode = domRange.focusNode
focusOffset = domRange.focusOffset
}
// COMPAT: There's a bug in chrome that always returns `true` for
// `isCollapsed` for a Selection that comes from a ShadowRoot.
// (2020/08/08)
// https://bugs.chromium.org/p/chromium/issues/detail?id=447523
if (IS_CHROME && hasShadowRoot(anchorNode)) {
// IsCollapsed might not work in firefox, but this will
if ((IS_CHROME && hasShadowRoot(anchorNode)) || IS_FIREFOX) {
isCollapsed =
domRange.anchorNode === domRange.focusNode &&
domRange.anchorOffset === domRange.focusOffset
@@ -876,15 +898,19 @@ export const ReactEditor: ReactEditorInterface = {
focusOffset = anchorNode.textContent?.length || 0
}
let anchor = ReactEditor.toSlatePoint(editor, [anchorNode, anchorOffset], {
const anchor = ReactEditor.toSlatePoint(
editor,
[anchorNode, anchorOffset],
{
exactMatch,
suppressThrow,
})
}
)
if (!anchor) {
return null as T extends true ? Range | null : Range
}
let focus = isCollapsed
const focus = isCollapsed
? anchor
: ReactEditor.toSlatePoint(editor, [focusNode, focusOffset], {
exactMatch,
@@ -894,46 +920,6 @@ export const ReactEditor: ReactEditorInterface = {
return null as T extends true ? Range | null : Range
}
/**
* suppose we have this document:
*
* { type: 'paragraph',
* children: [
* { text: 'foo ' },
* { text: 'bar' },
* { text: ' baz' }
* ]
* }
*
* a double click on "bar" on chrome will create this range:
*
* anchor -> [0,1] offset 0
* focus -> [0,1] offset 3
*
* while on firefox will create this range:
*
* anchor -> [0,0] offset 4
* focus -> [0,2] offset 0
*
* let's try to fix it...
*/
if (IS_FIREFOX && !isCollapsed && anchorNode !== focusNode) {
const isEnd = Editor.isEnd(editor, anchor!, anchor.path)
const isStart = Editor.isStart(editor, focus!, focus.path)
if (isEnd) {
const after = Editor.after(editor, anchor as Point)
// Editor.after() might return undefined
anchor = (after || anchor!) as T extends true ? Point | null : Point
}
if (isStart) {
const before = Editor.before(editor, focus as Point)
focus = (before || focus!) as T extends true ? Point | null : Point
}
}
let range: Range = { anchor: anchor as Point, focus: focus as Point }
// if the selection is a hanging range that ends in a void
// and the DOM focus is an Element