Skip to content

Commit

Permalink
revert #4455 / #4512; fix triple-click by unhanging range with void (#…
Browse files Browse the repository at this point in the history
…4588)

* revert #4455 / #4512; fix triple-click by unhanging range with void

* added changeset
  • Loading branch information
jaked authored Oct 11, 2021
1 parent 8e02c65 commit ae65ae5
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-jobs-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'slate-react': patch
---

revert #4455 / #4512; fix triple-click by unhanging range with void
77 changes: 15 additions & 62 deletions packages/slate-react/src/plugin/react-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion site/examples/images.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const Image = ({ attributes, children, element }) => {
const focused = useFocused()
return (
<div {...attributes}>
{children}
<div contentEditable={false}>
<img
src={element.url}
Expand All @@ -104,7 +105,6 @@ const Image = ({ attributes, children, element }) => {
`}
/>
</div>
{children}
</div>
)
}
Expand Down
4 changes: 4 additions & 0 deletions site/examples/richtext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand Down

0 comments on commit ae65ae5

Please sign in to comment.