mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-04-20 21:34:53 +02:00
* revert #4455 / #4512; fix triple-click by unhanging range with void * added changeset
This commit is contained in:
parent
8e02c65184
commit
ae65ae5f71
5
.changeset/curly-jobs-reflect.md
Normal file
5
.changeset/curly-jobs-reflect.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'slate-react': patch
|
||||
---
|
||||
|
||||
revert #4455 / #4512; fix triple-click by unhanging range with void
|
@ -565,65 +565,6 @@ export const ReactEditor = {
|
||||
anchorOffset = domRange.anchorOffset
|
||||
focusNode = domRange.focusNode
|
||||
focusOffset = domRange.focusOffset
|
||||
// When triple clicking a block, Chrome will return a selection object whose
|
||||
// focus node is the next element sibling and focusOffset is 0.
|
||||
// This will highlight the corresponding toolbar button for the sibling
|
||||
// block even though users just want to target the previous block.
|
||||
// (2021/08/24)
|
||||
// Signs of a triple click in Chrome
|
||||
// - anchor node will be a text node but focus node won't
|
||||
// - both anchorOffset and focusOffset are 0
|
||||
// - focusNode value will be null since Chrome tries to extend to just the
|
||||
// beginning of the next block
|
||||
if (
|
||||
IS_CHROME &&
|
||||
anchorNode &&
|
||||
focusNode &&
|
||||
anchorNode.nodeType !== focusNode.nodeType &&
|
||||
domRange.anchorOffset === 0 &&
|
||||
domRange.focusOffset === 0 &&
|
||||
focusNode.nodeValue == null
|
||||
) {
|
||||
// If an anchorNode is an element node when triple clicked, then the focusNode
|
||||
// should also be the same as anchorNode when triple clicked.
|
||||
// Otherwise, anchorNode is a text node and we need to
|
||||
// - climb up the DOM tree to get the farthest element node that receives
|
||||
// triple click. It should have atribute 'data-slate-node' = "element"
|
||||
// - get the last child of that element node
|
||||
// - climb down the DOM tree to get the text node of the last child
|
||||
// - this is also the end of the selection aka the focusNode
|
||||
const anchorElement = anchorNode.parentNode as HTMLElement
|
||||
const selectedBlock = anchorElement.closest(
|
||||
'[data-slate-node="element"]'
|
||||
)
|
||||
if (selectedBlock) {
|
||||
// The Slate Text nodes are leaf-level and contains document's text.
|
||||
// However, when represented in the DOM, they are actually Element nodes
|
||||
// and different from the DOM's Text nodes
|
||||
const { childElementCount: slateTextNodeCount } = selectedBlock
|
||||
if (slateTextNodeCount === 1) {
|
||||
focusNode = anchorNode as Text
|
||||
focusOffset = focusNode.length
|
||||
} else if (slateTextNodeCount > 1) {
|
||||
// A element with attribute data-slate-node="element" can have multiple
|
||||
// children with attribute data-slate-node="text". But these children only have
|
||||
// one child at each level.
|
||||
// <span data-slate-node="text">
|
||||
// <span data-slate-leaf="">
|
||||
// <span data-slate-string=""></span>
|
||||
// </span>
|
||||
// </span>
|
||||
const focusElement = selectedBlock.lastElementChild as HTMLElement
|
||||
const nodeIterator = document.createNodeIterator(
|
||||
focusElement,
|
||||
NodeFilter.SHOW_TEXT
|
||||
)
|
||||
focusNode = nodeIterator.nextNode() as Text
|
||||
focusOffset = focusNode.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// COMPAT: There's a bug in chrome that always returns `true` for
|
||||
// `isCollapsed` for a Selection that comes from a ShadowRoot.
|
||||
// (2020/08/08)
|
||||
@ -671,9 +612,21 @@ export const ReactEditor = {
|
||||
return null as T extends true ? Range | null : Range
|
||||
}
|
||||
|
||||
return ({ anchor, focus } as unknown) as T extends true
|
||||
? Range | null
|
||||
: Range
|
||||
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
|
||||
// (meaning that the selection ends before the element)
|
||||
// unhang the range to avoid mistakenly including the void
|
||||
if (
|
||||
Range.isExpanded(range) &&
|
||||
Range.isForward(range) &&
|
||||
isDOMElement(focusNode) &&
|
||||
Editor.void(editor, { at: range.focus, mode: 'highest' })
|
||||
) {
|
||||
range = Editor.unhangRange(editor, range, { voids: true })
|
||||
}
|
||||
|
||||
return (range as unknown) as T extends true ? Range | null : Range
|
||||
},
|
||||
|
||||
hasRange(editor: ReactEditor, range: Range): boolean {
|
||||
|
@ -93,6 +93,7 @@ const Image = ({ attributes, children, element }) => {
|
||||
const focused = useFocused()
|
||||
return (
|
||||
<div {...attributes}>
|
||||
{children}
|
||||
<div contentEditable={false}>
|
||||
<img
|
||||
src={element.url}
|
||||
@ -104,7 +105,6 @@ const Image = ({ attributes, children, element }) => {
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -93,7 +93,11 @@ const toggleMark = (editor, format) => {
|
||||
}
|
||||
|
||||
const isBlockActive = (editor, format) => {
|
||||
const { selection } = editor
|
||||
if (!selection) return false
|
||||
|
||||
const [match] = Editor.nodes(editor, {
|
||||
at: Editor.unhangRange(editor, selection),
|
||||
match: n =>
|
||||
!Editor.isEditor(n) && SlateElement.isElement(n) && n.type === format,
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user