Skip to content

Commit

Permalink
defer native events within Editable to avoid bugs with Editor (#4605)
Browse files Browse the repository at this point in the history
* defer native events internally to Editable

* add changeset

* suggestions to make DeferredOperation a closure instead of an object type

Co-authored-by: Nemanja Tosic <[email protected]>

* fix misapplied suggestion

Co-authored-by: Nemanja Tosic <[email protected]>
  • Loading branch information
jaked and nemanja-tosic authored Oct 18, 2021
1 parent f1607da commit 87ab2ef
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 87 deletions.
5 changes: 5 additions & 0 deletions .changeset/many-baboons-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'slate-react': patch
---

defer native events within Editable to avoid bugs with Editor
15 changes: 10 additions & 5 deletions packages/slate-react/src/components/editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ import {
PLACEHOLDER_SYMBOL,
EDITOR_TO_WINDOW,
} from '../utils/weak-maps'
import { asNative, flushNativeEvents } from '../utils/native'

type DeferredOperation = () => void

const Children = (props: Parameters<typeof useChildren>[0]) => (
<React.Fragment>{useChildren(props)}</React.Fragment>
Expand Down Expand Up @@ -124,6 +125,7 @@ export const Editable = (props: EditableProps) => {
// Rerender editor when composition status changed
const [isComposing, setIsComposing] = useState(false)
const ref = useRef<HTMLDivElement>(null)
const deferredOperations = useRef<DeferredOperation[]>([])

// Update internal state on each render.
IS_READ_ONLY.set(editor, readOnly)
Expand Down Expand Up @@ -433,9 +435,9 @@ export const Editable = (props: EditableProps) => {
// Only insertText operations use the native functionality, for now.
// Potentially expand to single character deletes, as well.
if (native) {
asNative(editor, () => Editor.insertText(editor, data), {
onFlushed: () => event.preventDefault(),
})
deferredOperations.current.push(() =>
Editor.insertText(editor, data)
)
} else {
Editor.insertText(editor, data)
}
Expand Down Expand Up @@ -622,7 +624,10 @@ export const Editable = (props: EditableProps) => {
// and we can correctly compare DOM text values in components
// to stop rendering, so that browser functions like autocorrect
// and spellcheck work as expected.
flushNativeEvents(editor)
for (const op of deferredOperations.current) {
op()
}
deferredOperations.current = []
}, [])}
onBlur={useCallback(
(event: React.FocusEvent<HTMLDivElement>) => {
Expand Down
30 changes: 0 additions & 30 deletions packages/slate-react/src/plugin/with-react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ import {
EDITOR_TO_ON_CHANGE,
NODE_TO_KEY,
} from '../utils/weak-maps'
import {
AS_NATIVE,
NATIVE_OPERATIONS,
flushNativeEvents,
} from '../utils/native'
import {
isDOMText,
getPlainText,
Expand Down Expand Up @@ -66,31 +61,6 @@ export const withReact = <T extends Editor>(editor: T) => {
}

e.apply = (op: Operation) => {
// if we're NOT an insert_text and there's a queue
// of native events, bail out and flush the queue.
// otherwise transforms as part of this cycle will
// be incorrect.
//
// This is needed as overriden operations (e.g. `insertText`)
// can call additional transforms, which will need accurate
// content, and will be called _before_ `onInput` is fired.
if (op.type !== 'insert_text') {
AS_NATIVE.set(editor, false)
flushNativeEvents(editor)
}

// If we're in native mode, queue the operation
// and it will be applied later.
if (AS_NATIVE.get(editor)) {
const nativeOps = NATIVE_OPERATIONS.get(editor)
if (nativeOps) {
nativeOps.push(op)
} else {
NATIVE_OPERATIONS.set(editor, [op])
}
return
}

const matches: [Path, Key][] = []

switch (op.type) {
Expand Down
52 changes: 0 additions & 52 deletions packages/slate-react/src/utils/native.ts

This file was deleted.

0 comments on commit 87ab2ef

Please sign in to comment.