Skip to content

Commit

Permalink
[slate-react]: fix selection bugs when multiple editors share value (#…
Browse files Browse the repository at this point in the history
…4475)

* [slate-react]: fix selection bugs when multiple editors share value

The KEY_TO_ELEMENT weakmap must be scoped to the instance to avoid collisions between multiple editors.

This is solved by wrapping it in another WeakMap keyed on the editor object, that returns the KEY_TO_ELEMENT Weakmap for that editor.

* Add changeset

Co-authored-by: Dylan Schiemann <dylan@dojotoolkit.org>
  • Loading branch information
skogsmaskin and dylans authored Aug 25, 2021
1 parent 781b7f7 commit c1433f5
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/calm-books-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'slate-react': patch
---

[slate-react]: fix selection bugs when multiple editors share value
7 changes: 4 additions & 3 deletions packages/slate-react/src/components/element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
ELEMENT_TO_NODE,
NODE_TO_PARENT,
NODE_TO_INDEX,
KEY_TO_ELEMENT,
EDITOR_TO_KEY_TO_ELEMENT,
} from '../utils/weak-maps'
import { isDecoratorRangeListEqual } from '../utils/range-list'
import {
Expand Down Expand Up @@ -120,12 +120,13 @@ const Element = (props: {

// Update element-related weak maps with the DOM element ref.
useIsomorphicLayoutEffect(() => {
const KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor)
if (ref.current) {
KEY_TO_ELEMENT.set(key, ref.current)
KEY_TO_ELEMENT?.set(key, ref.current)
NODE_TO_ELEMENT.set(element, ref.current)
ELEMENT_TO_NODE.set(ref.current, element)
} else {
KEY_TO_ELEMENT.delete(key)
KEY_TO_ELEMENT?.delete(key)
NODE_TO_ELEMENT.delete(element)
}
})
Expand Down
7 changes: 4 additions & 3 deletions packages/slate-react/src/components/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { ReactEditor, useSlateStatic } from '..'
import { RenderLeafProps, RenderPlaceholderProps } from './editable'
import { useIsomorphicLayoutEffect } from '../hooks/use-isomorphic-layout-effect'
import {
KEY_TO_ELEMENT,
NODE_TO_ELEMENT,
ELEMENT_TO_NODE,
EDITOR_TO_KEY_TO_ELEMENT,
} from '../utils/weak-maps'
import { isDecoratorRangeListEqual } from '../utils/range-list'

Expand Down Expand Up @@ -56,12 +56,13 @@ const Text = (props: {

// Update element-related weak maps with the DOM element ref.
useIsomorphicLayoutEffect(() => {
const KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor)
if (ref.current) {
KEY_TO_ELEMENT.set(key, ref.current)
KEY_TO_ELEMENT?.set(key, ref.current)
NODE_TO_ELEMENT.set(text, ref.current)
ELEMENT_TO_NODE.set(ref.current, text)
} else {
KEY_TO_ELEMENT.delete(key)
KEY_TO_ELEMENT?.delete(key)
NODE_TO_ELEMENT.delete(text)
}
})
Expand Down
5 changes: 3 additions & 2 deletions packages/slate-react/src/plugin/react-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import {
ELEMENT_TO_NODE,
IS_FOCUSED,
IS_READ_ONLY,
KEY_TO_ELEMENT,
NODE_TO_INDEX,
NODE_TO_KEY,
NODE_TO_PARENT,
EDITOR_TO_WINDOW,
EDITOR_TO_KEY_TO_ELEMENT,
} from '../utils/weak-maps'
import {
DOMElement,
Expand Down Expand Up @@ -241,9 +241,10 @@ export const ReactEditor = {
*/

toDOMNode(editor: ReactEditor, node: Node): HTMLElement {
const KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor)
const domNode = Editor.isEditor(node)
? EDITOR_TO_ELEMENT.get(editor)
: KEY_TO_ELEMENT.get(ReactEditor.findKey(editor, node))
: KEY_TO_ELEMENT?.get(ReactEditor.findKey(editor, node))

if (!domNode) {
throw new Error(
Expand Down
10 changes: 9 additions & 1 deletion packages/slate-react/src/plugin/with-react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { Editor, Node, Path, Operation, Transforms, Range } from 'slate'

import { ReactEditor } from './react-editor'
import { Key } from '../utils/key'
import { EDITOR_TO_ON_CHANGE, NODE_TO_KEY } from '../utils/weak-maps'
import {
EDITOR_TO_KEY_TO_ELEMENT,
EDITOR_TO_ON_CHANGE,
NODE_TO_KEY,
} from '../utils/weak-maps'
import {
AS_NATIVE,
NATIVE_OPERATIONS,
Expand All @@ -29,6 +33,10 @@ export const withReact = <T extends Editor>(editor: T) => {
const e = editor as T & ReactEditor
const { apply, onChange, deleteBackward } = e

// The WeakMap which maps a key to a specific HTMLElement must be scoped to the editor instance to
// avoid collisions between editors in the DOM that share the same value.
EDITOR_TO_KEY_TO_ELEMENT.set(e, new WeakMap())

e.deleteBackward = unit => {
if (unit !== 'line') {
return deleteBackward(unit)
Expand Down
5 changes: 4 additions & 1 deletion packages/slate-react/src/utils/weak-maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ export const EDITOR_TO_WINDOW: WeakMap<Editor, Window> = new WeakMap()
export const EDITOR_TO_ELEMENT: WeakMap<Editor, HTMLElement> = new WeakMap()
export const EDITOR_TO_PLACEHOLDER: WeakMap<Editor, string> = new WeakMap()
export const ELEMENT_TO_NODE: WeakMap<HTMLElement, Node> = new WeakMap()
export const KEY_TO_ELEMENT: WeakMap<Key, HTMLElement> = new WeakMap()
export const NODE_TO_ELEMENT: WeakMap<Node, HTMLElement> = new WeakMap()
export const NODE_TO_KEY: WeakMap<Node, Key> = new WeakMap()
export const EDITOR_TO_KEY_TO_ELEMENT: WeakMap<
Editor,
WeakMap<Key, HTMLElement>
> = new WeakMap()

/**
* Weak maps for storing editor-related state.
Expand Down

0 comments on commit c1433f5

Please sign in to comment.