Skip to content

Commit

Permalink
fix: undo broken in slate
Browse files Browse the repository at this point in the history
fixes #1188
  • Loading branch information
macrozone committed May 16, 2022
1 parent 6dafa39 commit ed7d2b8
Showing 1 changed file with 37 additions and 37 deletions.
74 changes: 37 additions & 37 deletions packages/plugins/content/slate/src/components/SlateProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
import { deepEquals } from '@react-page/editor';
import debounce from 'lodash.debounce';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import type { Node } from 'slate';
import React, { useCallback, useEffect, useMemo } from 'react';
import { createEditor, Transforms } from 'slate';
import { Slate, withReact } from 'slate-react';
import { ReactEditor, Slate, withReact } from 'slate-react';
import withInline from '../slateEnhancer/withInline';
import withPaste from '../slateEnhancer/withPaste';
import type { SlateProps } from '../types/component';
import DialogVisibleProvider from './DialogVisibleProvider';

const SlateProvider: React.FC<SlateProps> = (props) => {
const { data, plugins, children, defaultPluginType } = props;
const [value, setValue] = useState<Node[]>(data?.slate);
const valueRef = useRef(value);

valueRef.current = value;
const editor = useMemo(
() =>
withPaste(
Expand All @@ -29,47 +17,59 @@ const SlateProvider: React.FC<SlateProps> = (props) => {
)(withReact(withInline(plugins)(createEditor()))),
[]
);
const onChangeDebounced = useCallback(
debounce(() => {
props.onChange(
{
slate: valueRef.current,
selection: editor.selection,
},
{
// mark as not undoable when state is same
// that happens if only selection was changed
notUndoable: deepEquals(valueRef.current, data.slate),
}
);
}, 200),
[props.onChange, editor, data.slate]
);

useEffect(() => {
// unfortunatly, slate broke the controlled input pattern. So we have to hack our way around it, see https://github.com/ianstormtaylor/slate/issues/4992
editor.children = data?.slate;

if (data.selection) {
try {
ReactEditor.focus(editor);
} catch (e) {
// ignore, can happen
}
// update seleciton, if changed from outside (e.g. through undo)
Transforms.select(editor, data.selection);
} else {
// deselect, otherwise slate might throw an eerror if cursor is now on a non existing dom node
Transforms.deselect(editor);
}
setValue(data?.slate);
}, [data?.slate, data?.selection]);

const onChange = useCallback(
(v) => {
if (editor.selection) {
setValue(v);
onChangeDebounced();
}
if (
!deepEquals(editor.children, data?.slate) ||
!deepEquals(editor.selection, data?.selection)
)
props.onChange(
{
slate: editor.children,
selection: editor.selection,
},
{
// mark as not undoable when state is same
// that happens if only selection was changed
notUndoable: deepEquals(editor.children, data?.slate),
}
);
},
[onChangeDebounced]
[data?.slate]
);

const initialValue = data?.slate;

return (
<DialogVisibleProvider>
<Slate editor={editor} value={value} onChange={onChange}>
<Slate
editor={editor}
value={
initialValue /*
this is confusingly only for the initial value since slate 0.70something, see https://github.com/ianstormtaylor/slate/issues/4992
*/
}
onChange={onChange}
>
{children}
</Slate>
</DialogVisibleProvider>
Expand Down

0 comments on commit ed7d2b8

Please sign in to comment.