@plone/volto-slate 18.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +6 -0
- package/.release-it.json +25 -0
- package/CHANGELOG.md +19 -0
- package/LICENSE.md +21 -0
- package/README.md +10 -0
- package/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
- package/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
- package/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
- package/build/messages/src/blocks/Text/SlashMenu.json +6 -0
- package/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
- package/build/messages/src/editor/plugins/Link/index.json +10 -0
- package/build/messages/src/editor/plugins/Table/index.json +30 -0
- package/build/messages/src/elementEditor/messages.json +10 -0
- package/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
- package/build/messages/src/widgets/RichTextWidgetView.json +6 -0
- package/locales/de/LC_MESSAGES/volto.po +148 -0
- package/locales/en/LC_MESSAGES/volto.po +148 -0
- package/locales/volto.pot +182 -0
- package/package.json +42 -0
- package/src/actions/content.js +30 -0
- package/src/actions/index.js +3 -0
- package/src/actions/plugins.js +9 -0
- package/src/actions/selection.js +22 -0
- package/src/blocks/Table/Cell.jsx +87 -0
- package/src/blocks/Table/Cell.test.js +54 -0
- package/src/blocks/Table/TableBlockEdit.jsx +694 -0
- package/src/blocks/Table/TableBlockEdit.test.js +40 -0
- package/src/blocks/Table/TableBlockView.jsx +150 -0
- package/src/blocks/Table/TableBlockView.test.js +49 -0
- package/src/blocks/Table/__snapshots__/Cell.test.js.snap +3 -0
- package/src/blocks/Table/__snapshots__/TableBlockEdit.test.js.snap +22 -0
- package/src/blocks/Table/__snapshots__/TableBlockView.test.js.snap +27 -0
- package/src/blocks/Table/deconstruct.js +113 -0
- package/src/blocks/Table/extensions/normalizeTable.js +5 -0
- package/src/blocks/Table/index.js +60 -0
- package/src/blocks/Table/schema.js +122 -0
- package/src/blocks/Text/DefaultTextBlockEditor.jsx +304 -0
- package/src/blocks/Text/DetachedTextBlockEditor.jsx +77 -0
- package/src/blocks/Text/MarkdownIntroduction.jsx +59 -0
- package/src/blocks/Text/PluginSidebar.jsx +18 -0
- package/src/blocks/Text/ShortcutListing.jsx +28 -0
- package/src/blocks/Text/SlashMenu.jsx +203 -0
- package/src/blocks/Text/TextBlockEdit.jsx +38 -0
- package/src/blocks/Text/TextBlockEdit.test.js +107 -0
- package/src/blocks/Text/TextBlockSchema.js +54 -0
- package/src/blocks/Text/TextBlockView.jsx +31 -0
- package/src/blocks/Text/__snapshots__/TextBlockEdit.test.js.snap +62 -0
- package/src/blocks/Text/css/editor.css +18 -0
- package/src/blocks/Text/extensions/Readme.md +49 -0
- package/src/blocks/Text/extensions/breakList.js +100 -0
- package/src/blocks/Text/extensions/index.js +6 -0
- package/src/blocks/Text/extensions/insertBreak.js +57 -0
- package/src/blocks/Text/extensions/isSelected.js +7 -0
- package/src/blocks/Text/extensions/normalizeExternalData.js +7 -0
- package/src/blocks/Text/extensions/withDeserializers.js +87 -0
- package/src/blocks/Text/extensions/withLists.js +5 -0
- package/src/blocks/Text/index.js +171 -0
- package/src/blocks/Text/keyboard/backspaceInList.js +58 -0
- package/src/blocks/Text/keyboard/breakBlocks.js +3 -0
- package/src/blocks/Text/keyboard/cancelEsc.js +7 -0
- package/src/blocks/Text/keyboard/indentListItems.js +240 -0
- package/src/blocks/Text/keyboard/index.js +52 -0
- package/src/blocks/Text/keyboard/joinBlocks.js +180 -0
- package/src/blocks/Text/keyboard/moveListItems.js +124 -0
- package/src/blocks/Text/keyboard/slashMenu.js +19 -0
- package/src/blocks/Text/keyboard/softBreak.js +7 -0
- package/src/blocks/Text/keyboard/traverseBlocks.js +81 -0
- package/src/blocks/Text/keyboard/unwrapEmptyString.js +26 -0
- package/src/blocks/Text/schema.js +39 -0
- package/src/constants.js +123 -0
- package/src/editor/EditorContext.jsx +5 -0
- package/src/editor/EditorReference.jsx +22 -0
- package/src/editor/SlateEditor.jsx +375 -0
- package/src/editor/config.jsx +344 -0
- package/src/editor/decorate.js +68 -0
- package/src/editor/deserialize.js +185 -0
- package/src/editor/extensions/index.js +6 -0
- package/src/editor/extensions/insertBreak.js +15 -0
- package/src/editor/extensions/insertData.js +161 -0
- package/src/editor/extensions/isInline.js +14 -0
- package/src/editor/extensions/normalizeExternalData.js +8 -0
- package/src/editor/extensions/normalizeNode.js +48 -0
- package/src/editor/extensions/withDeserializers.js +15 -0
- package/src/editor/extensions/withTestingFeatures.jsx +84 -0
- package/src/editor/index.js +14 -0
- package/src/editor/less/editor.less +173 -0
- package/src/editor/less/globals.less +18 -0
- package/src/editor/less/slate.less +28 -0
- package/src/editor/plugins/AdvancedLink/deserialize.js +90 -0
- package/src/editor/plugins/AdvancedLink/extensions.js +32 -0
- package/src/editor/plugins/AdvancedLink/index.js +50 -0
- package/src/editor/plugins/AdvancedLink/render.jsx +37 -0
- package/src/editor/plugins/AdvancedLink/schema.js +114 -0
- package/src/editor/plugins/AdvancedLink/styles.less +8 -0
- package/src/editor/plugins/Blockquote/index.js +30 -0
- package/src/editor/plugins/Callout/index.js +34 -0
- package/src/editor/plugins/Image/deconstruct.js +30 -0
- package/src/editor/plugins/Image/extensions.js +51 -0
- package/src/editor/plugins/Image/index.js +11 -0
- package/src/editor/plugins/Image/render.jsx +22 -0
- package/src/editor/plugins/Link/extensions.js +58 -0
- package/src/editor/plugins/Link/index.js +159 -0
- package/src/editor/plugins/Link/render.jsx +54 -0
- package/src/editor/plugins/Markdown/constants.js +81 -0
- package/src/editor/plugins/Markdown/extensions.js +336 -0
- package/src/editor/plugins/Markdown/index.js +28 -0
- package/src/editor/plugins/Markdown/utils.js +198 -0
- package/src/editor/plugins/StyleMenu/StyleMenu.jsx +153 -0
- package/src/editor/plugins/StyleMenu/index.js +19 -0
- package/src/editor/plugins/StyleMenu/style.less +29 -0
- package/src/editor/plugins/StyleMenu/utils.js +168 -0
- package/src/editor/plugins/Table/TableButton.jsx +142 -0
- package/src/editor/plugins/Table/TableCell.jsx +44 -0
- package/src/editor/plugins/Table/TableContainer.jsx +37 -0
- package/src/editor/plugins/Table/TableSizePicker.jsx +83 -0
- package/src/editor/plugins/Table/extensions.js +87 -0
- package/src/editor/plugins/Table/index.js +390 -0
- package/src/editor/plugins/Table/less/public.less +29 -0
- package/src/editor/plugins/Table/less/table.less +28 -0
- package/src/editor/plugins/Table/render.jsx +30 -0
- package/src/editor/plugins/index.js +19 -0
- package/src/editor/render.jsx +224 -0
- package/src/editor/ui/BasicToolbar.jsx +11 -0
- package/src/editor/ui/BlockButton.jsx +31 -0
- package/src/editor/ui/ClearFormattingButton.jsx +21 -0
- package/src/editor/ui/ExpandedToolbar.jsx +18 -0
- package/src/editor/ui/Expando.jsx +5 -0
- package/src/editor/ui/InlineToolbar.jsx +69 -0
- package/src/editor/ui/MarkButton.jsx +23 -0
- package/src/editor/ui/MarkElementButton.jsx +30 -0
- package/src/editor/ui/Menu.jsx +13 -0
- package/src/editor/ui/PositionedToolbar.jsx +32 -0
- package/src/editor/ui/Separator.jsx +7 -0
- package/src/editor/ui/SlateContextToolbar.jsx +13 -0
- package/src/editor/ui/SlateToolbar.jsx +96 -0
- package/src/editor/ui/Toolbar.jsx +103 -0
- package/src/editor/ui/ToolbarButton.jsx +33 -0
- package/src/editor/ui/ToolbarButton.test.js +25 -0
- package/src/editor/ui/__snapshots__/ToolbarButton.test.js.snap +16 -0
- package/src/editor/ui/index.js +15 -0
- package/src/editor/utils.js +248 -0
- package/src/elementEditor/ContextButtons.jsx +57 -0
- package/src/elementEditor/PluginEditor.jsx +124 -0
- package/src/elementEditor/Readme.md +6 -0
- package/src/elementEditor/SchemaProvider.jsx +4 -0
- package/src/elementEditor/SidebarEditor.jsx +46 -0
- package/src/elementEditor/ToolbarButton.jsx +44 -0
- package/src/elementEditor/index.js +5 -0
- package/src/elementEditor/makeInlineElementPlugin.js +100 -0
- package/src/elementEditor/messages.js +14 -0
- package/src/elementEditor/utils.js +227 -0
- package/src/hooks/index.js +3 -0
- package/src/hooks/useEditorContext.js +6 -0
- package/src/hooks/useIsomorphicLayoutEffect.js +7 -0
- package/src/hooks/useSelectionPosition.js +25 -0
- package/src/i18n.js +180 -0
- package/src/icons/hashlink.svg +57 -0
- package/src/index.js +61 -0
- package/src/reducers/content.js +74 -0
- package/src/reducers/index.js +3 -0
- package/src/reducers/plugins.js +17 -0
- package/src/reducers/selection.js +16 -0
- package/src/utils/blocks.js +379 -0
- package/src/utils/blocks.test.js +138 -0
- package/src/utils/editor.js +31 -0
- package/src/utils/image.js +25 -0
- package/src/utils/index.js +11 -0
- package/src/utils/internals.js +46 -0
- package/src/utils/lists.js +92 -0
- package/src/utils/marks.js +104 -0
- package/src/utils/mime-types.js +24 -0
- package/src/utils/nodes.js +4 -0
- package/src/utils/ops.js +20 -0
- package/src/utils/random.js +17 -0
- package/src/utils/selection.js +236 -0
- package/src/utils/slate-string-utils.js +409 -0
- package/src/utils/volto-blocks.js +314 -0
- package/src/widgets/ErrorBoundary.jsx +27 -0
- package/src/widgets/HtmlSlateWidget.jsx +138 -0
- package/src/widgets/ObjectByTypeWidget.jsx +49 -0
- package/src/widgets/RichTextWidget.jsx +72 -0
- package/src/widgets/RichTextWidgetView.jsx +36 -0
- package/src/widgets/style.css +21 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import ReactDOM from 'react-dom';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import { isEqual } from 'lodash';
|
|
4
|
+
import { Transforms, Editor } from 'slate'; // , Transforms
|
|
5
|
+
import { Slate, Editable, ReactEditor } from 'slate-react';
|
|
6
|
+
import React, { Component } from 'react'; // , useState
|
|
7
|
+
import { v4 as uuid } from 'uuid';
|
|
8
|
+
|
|
9
|
+
import config from '@plone/volto/registry';
|
|
10
|
+
|
|
11
|
+
import { Element, Leaf } from './render';
|
|
12
|
+
|
|
13
|
+
import withTestingFeatures from './extensions/withTestingFeatures';
|
|
14
|
+
import {
|
|
15
|
+
makeEditor,
|
|
16
|
+
toggleInlineFormat,
|
|
17
|
+
toggleMark,
|
|
18
|
+
parseDefaultSelection,
|
|
19
|
+
} from '@plone/volto-slate/utils';
|
|
20
|
+
import { InlineToolbar } from './ui';
|
|
21
|
+
import EditorContext from './EditorContext';
|
|
22
|
+
|
|
23
|
+
import isHotkey from 'is-hotkey';
|
|
24
|
+
|
|
25
|
+
import './less/editor.less';
|
|
26
|
+
|
|
27
|
+
import Toolbar from './ui/Toolbar';
|
|
28
|
+
|
|
29
|
+
const handleHotKeys = (editor, event, config) => {
|
|
30
|
+
let wasHotkey = false;
|
|
31
|
+
|
|
32
|
+
for (const hk of Object.entries(config.hotkeys)) {
|
|
33
|
+
const [shortcut, { format, type }] = hk;
|
|
34
|
+
if (isHotkey(shortcut, event)) {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
|
|
37
|
+
if (type === 'inline') {
|
|
38
|
+
toggleInlineFormat(editor, format);
|
|
39
|
+
} else {
|
|
40
|
+
// type === 'mark'
|
|
41
|
+
toggleMark(editor, format);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
wasHotkey = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return wasHotkey;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// TODO: implement onFocus
|
|
52
|
+
class SlateEditor extends Component {
|
|
53
|
+
constructor(props) {
|
|
54
|
+
super(props);
|
|
55
|
+
|
|
56
|
+
this.createEditor = this.createEditor.bind(this);
|
|
57
|
+
this.multiDecorator = this.multiDecorator.bind(this);
|
|
58
|
+
this.handleChange = this.handleChange.bind(this);
|
|
59
|
+
this.getSavedSelection = this.getSavedSelection.bind(this);
|
|
60
|
+
this.setSavedSelection = this.setSavedSelection.bind(this);
|
|
61
|
+
|
|
62
|
+
this.savedSelection = null;
|
|
63
|
+
|
|
64
|
+
const uid = uuid(); // used to namespace the editor's plugins
|
|
65
|
+
|
|
66
|
+
this.slateSettings = props.slateSettings || config.settings.slate;
|
|
67
|
+
|
|
68
|
+
this.state = {
|
|
69
|
+
editor: this.createEditor(uid),
|
|
70
|
+
showExpandedToolbar: config.settings.slate.showExpandedToolbar,
|
|
71
|
+
internalValue: this.props.value || this.slateSettings.defaultValue(),
|
|
72
|
+
uid,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
this.editor = null;
|
|
76
|
+
this.selectionTimeout = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getSavedSelection() {
|
|
80
|
+
return this.savedSelection;
|
|
81
|
+
}
|
|
82
|
+
setSavedSelection(selection) {
|
|
83
|
+
this.savedSelection = selection;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
createEditor(uid) {
|
|
87
|
+
// extensions are "editor plugins" or "editor wrappers". It's a similar
|
|
88
|
+
// similar to OOP inheritance, where a callable creates a new copy of the
|
|
89
|
+
// editor, while replacing or adding new capabilities to that editor.
|
|
90
|
+
// Extensions are purely JS, no React components.
|
|
91
|
+
const editor = makeEditor({ extensions: this.props.extensions });
|
|
92
|
+
|
|
93
|
+
// When the editor loses focus it no longer has a valid selections. This
|
|
94
|
+
// makes it impossible to have complex types of interactions (like filling
|
|
95
|
+
// in another text box, operating a select menu, etc). For this reason we
|
|
96
|
+
// save the active selection
|
|
97
|
+
|
|
98
|
+
editor.getSavedSelection = this.getSavedSelection;
|
|
99
|
+
editor.setSavedSelection = this.setSavedSelection;
|
|
100
|
+
editor.uid = uid || this.state.uid;
|
|
101
|
+
|
|
102
|
+
return editor;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
handleChange(value) {
|
|
106
|
+
ReactDOM.unstable_batchedUpdates(() => {
|
|
107
|
+
this.setState({ internalValue: value });
|
|
108
|
+
if (this.props.onChange && !isEqual(value, this.props.value)) {
|
|
109
|
+
this.props.onChange(value, this.editor);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
multiDecorator([node, path]) {
|
|
115
|
+
// Decorations (such as higlighting node types, selection, etc).
|
|
116
|
+
const { runtimeDecorators = [] } = this.slateSettings;
|
|
117
|
+
return runtimeDecorators.reduce(
|
|
118
|
+
(acc, deco) => deco(this.state.editor, [node, path], acc),
|
|
119
|
+
[],
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
componentDidMount() {
|
|
124
|
+
// watch the dom change
|
|
125
|
+
|
|
126
|
+
if (this.props.selected) {
|
|
127
|
+
let focused = true;
|
|
128
|
+
try {
|
|
129
|
+
focused = ReactEditor.isFocused(this.state.editor);
|
|
130
|
+
} catch {}
|
|
131
|
+
if (!focused) {
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
try {
|
|
134
|
+
ReactEditor.focus(this.state.editor);
|
|
135
|
+
} catch {}
|
|
136
|
+
}, 100); // flush
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.state.editor.normalize({ force: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
componentWillUnmount() {
|
|
144
|
+
this.isUnmounted = true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
componentDidUpdate(prevProps) {
|
|
148
|
+
if (!isEqual(prevProps.extensions, this.props.extensions)) {
|
|
149
|
+
this.setState({ editor: this.createEditor() });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
this.props.value &&
|
|
155
|
+
!isEqual(this.props.value, this.state.internalValue)
|
|
156
|
+
) {
|
|
157
|
+
const { editor } = this.state;
|
|
158
|
+
editor.children = this.props.value;
|
|
159
|
+
|
|
160
|
+
if (this.props.defaultSelection) {
|
|
161
|
+
const selection = parseDefaultSelection(
|
|
162
|
+
editor,
|
|
163
|
+
this.props.defaultSelection,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
ReactEditor.focus(editor);
|
|
167
|
+
Transforms.select(editor, selection);
|
|
168
|
+
} else {
|
|
169
|
+
Transforms.select(editor, Editor.end(editor, []));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.setState({
|
|
173
|
+
internalValue: this.props.value,
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const { editor } = this.state;
|
|
179
|
+
|
|
180
|
+
if (!prevProps.selected && this.props.selected) {
|
|
181
|
+
// if the SlateEditor becomes selected from unselected
|
|
182
|
+
|
|
183
|
+
if (window.getSelection().type === 'None') {
|
|
184
|
+
// TODO: why is this condition checked?
|
|
185
|
+
Transforms.select(
|
|
186
|
+
this.state.editor,
|
|
187
|
+
Editor.range(this.state.editor, Editor.start(this.state.editor, [])),
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
ReactEditor.focus(this.state.editor);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (this.props.selected && this.props.onUpdate) {
|
|
195
|
+
this.props.onUpdate(editor);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
shouldComponentUpdate(nextProps, nextState) {
|
|
200
|
+
const { selected = true, value, readOnly } = nextProps;
|
|
201
|
+
const res =
|
|
202
|
+
selected ||
|
|
203
|
+
this.props.selected !== selected ||
|
|
204
|
+
this.props.readOnly !== readOnly ||
|
|
205
|
+
!isEqual(value, this.props.value);
|
|
206
|
+
return res;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
render() {
|
|
210
|
+
const {
|
|
211
|
+
selected,
|
|
212
|
+
placeholder,
|
|
213
|
+
onKeyDown,
|
|
214
|
+
testingEditorRef,
|
|
215
|
+
readOnly,
|
|
216
|
+
className,
|
|
217
|
+
renderExtensions = [],
|
|
218
|
+
editableProps = {},
|
|
219
|
+
} = this.props;
|
|
220
|
+
const slateSettings = this.slateSettings;
|
|
221
|
+
|
|
222
|
+
// renderExtensions is needed because the editor is memoized, so if these
|
|
223
|
+
// extensions need an updated state (for example to insert updated
|
|
224
|
+
// blockProps) then we need to always wrap the editor with them
|
|
225
|
+
const editor = renderExtensions.reduce(
|
|
226
|
+
(acc, apply) => apply(acc),
|
|
227
|
+
this.state.editor,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Reset selection if field is reset
|
|
231
|
+
if (
|
|
232
|
+
editor.selection &&
|
|
233
|
+
this.props.value?.length === 1 &&
|
|
234
|
+
this.props.value[0].children.length === 1 &&
|
|
235
|
+
this.props.value[0].children[0].text === ''
|
|
236
|
+
) {
|
|
237
|
+
Transforms.select(editor, {
|
|
238
|
+
anchor: { path: [0, 0], offset: 0 },
|
|
239
|
+
focus: { path: [0, 0], offset: 0 },
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
this.editor = editor;
|
|
243
|
+
|
|
244
|
+
if (testingEditorRef) {
|
|
245
|
+
testingEditorRef.current = editor;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// debug-values are `data-` HTML attributes in withTestingFeatures HOC
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<div
|
|
252
|
+
{...this.props['debug-values']}
|
|
253
|
+
className={cx('slate-editor', {
|
|
254
|
+
'show-toolbar': this.state.showExpandedToolbar,
|
|
255
|
+
selected,
|
|
256
|
+
})}
|
|
257
|
+
tabIndex={-1}
|
|
258
|
+
>
|
|
259
|
+
<EditorContext.Provider value={editor}>
|
|
260
|
+
<Slate
|
|
261
|
+
editor={editor}
|
|
262
|
+
initialValue={this.props.value || slateSettings.defaultValue()}
|
|
263
|
+
onChange={this.handleChange}
|
|
264
|
+
>
|
|
265
|
+
{selected ? (
|
|
266
|
+
<>
|
|
267
|
+
<InlineToolbar
|
|
268
|
+
editor={editor}
|
|
269
|
+
className={className}
|
|
270
|
+
slateSettings={this.props.slateSettings}
|
|
271
|
+
/>
|
|
272
|
+
{Object.keys(slateSettings.elementToolbarButtons).map(
|
|
273
|
+
(t, i) => {
|
|
274
|
+
return (
|
|
275
|
+
<Toolbar elementType={t} key={i}>
|
|
276
|
+
{slateSettings.elementToolbarButtons[t].map(
|
|
277
|
+
(Btn, b) => {
|
|
278
|
+
return <Btn editor={editor} key={b} />;
|
|
279
|
+
},
|
|
280
|
+
)}
|
|
281
|
+
</Toolbar>
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
)}
|
|
285
|
+
</>
|
|
286
|
+
) : (
|
|
287
|
+
''
|
|
288
|
+
)}
|
|
289
|
+
<Editable
|
|
290
|
+
tabIndex={this.props.tabIndex || 0}
|
|
291
|
+
readOnly={readOnly}
|
|
292
|
+
placeholder={placeholder}
|
|
293
|
+
renderElement={(props) => <Element {...props} />}
|
|
294
|
+
renderLeaf={(props) => <Leaf {...props} />}
|
|
295
|
+
decorate={this.multiDecorator}
|
|
296
|
+
spellCheck={false}
|
|
297
|
+
scrollSelectionIntoView={
|
|
298
|
+
slateSettings.scrollIntoView ? undefined : () => null
|
|
299
|
+
}
|
|
300
|
+
onBlur={() => {
|
|
301
|
+
this.props.onBlur && this.props.onBlur();
|
|
302
|
+
return null;
|
|
303
|
+
}}
|
|
304
|
+
onClick={this.props.onClick}
|
|
305
|
+
onSelect={(e) => {
|
|
306
|
+
if (!selected && this.props.onFocus) {
|
|
307
|
+
// we can't overwrite the onFocus of Editable, as the onFocus
|
|
308
|
+
// in Slate has too much builtin behaviour that's not
|
|
309
|
+
// accessible otherwise. Instead we try to detect such an
|
|
310
|
+
// event based on observing selected state
|
|
311
|
+
if (!editor.selection) {
|
|
312
|
+
setTimeout(() => {
|
|
313
|
+
this.props.onFocus();
|
|
314
|
+
}, 100); // TODO: why 100 is chosen here?
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (this.selectionTimeout) clearTimeout(this.selectionTimeout);
|
|
319
|
+
this.selectionTimeout = setTimeout(() => {
|
|
320
|
+
if (
|
|
321
|
+
editor.selection &&
|
|
322
|
+
!isEqual(editor.selection, this.savedSelection) &&
|
|
323
|
+
!this.isUnmounted
|
|
324
|
+
) {
|
|
325
|
+
this.setState((state) => ({ update: !this.state.update }));
|
|
326
|
+
this.setSavedSelection(
|
|
327
|
+
JSON.parse(JSON.stringify(editor.selection)),
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}, 200);
|
|
331
|
+
}}
|
|
332
|
+
onKeyDown={(event) => {
|
|
333
|
+
const handled = handleHotKeys(editor, event, slateSettings);
|
|
334
|
+
if (handled) return;
|
|
335
|
+
onKeyDown && onKeyDown({ editor, event });
|
|
336
|
+
}}
|
|
337
|
+
{...editableProps}
|
|
338
|
+
/>
|
|
339
|
+
{selected &&
|
|
340
|
+
slateSettings.persistentHelpers.map((Helper, i) => {
|
|
341
|
+
return <Helper key={i} editor={editor} />;
|
|
342
|
+
})}
|
|
343
|
+
{this.props.debug ? (
|
|
344
|
+
<ul>
|
|
345
|
+
<li>{selected ? 'selected' : 'no-selected'}</li>
|
|
346
|
+
<li>
|
|
347
|
+
savedSelection: {JSON.stringify(editor.getSavedSelection())}
|
|
348
|
+
</li>
|
|
349
|
+
<li>live selection: {JSON.stringify(editor.selection)}</li>
|
|
350
|
+
<li>children: {JSON.stringify(editor.children)}</li>
|
|
351
|
+
<li> {selected ? 'selected' : 'notselected'}</li>
|
|
352
|
+
<li>
|
|
353
|
+
{ReactEditor.isFocused(editor) ? 'focused' : 'unfocused'}
|
|
354
|
+
</li>
|
|
355
|
+
</ul>
|
|
356
|
+
) : (
|
|
357
|
+
''
|
|
358
|
+
)}
|
|
359
|
+
{this.props.children}
|
|
360
|
+
</Slate>
|
|
361
|
+
</EditorContext.Provider>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
SlateEditor.defaultProps = {
|
|
368
|
+
extensions: [],
|
|
369
|
+
className: '',
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// May be needed to wrap in React.memo(), it used to be wrapped in connect()
|
|
373
|
+
export default __CLIENT__ && window?.Cypress
|
|
374
|
+
? withTestingFeatures(SlateEditor)
|
|
375
|
+
: SlateEditor;
|