slate-react 0.56.1 → 0.57.3

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/dist/index.js CHANGED
@@ -7,65 +7,76 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
7
7
  var React = require('react');
8
8
  var React__default = _interopDefault(React);
9
9
  var slate = require('slate');
10
- var debounce = _interopDefault(require('debounce'));
10
+ var throttle = _interopDefault(require('lodash/throttle'));
11
11
  var scrollIntoView = _interopDefault(require('scroll-into-view-if-needed'));
12
12
  var getDirection = _interopDefault(require('direction'));
13
- var isHotkey = require('is-hotkey');
14
13
  var ReactDOM = _interopDefault(require('react-dom'));
14
+ var isHotkey = require('is-hotkey');
15
+
16
+ function _defineProperty(obj, key, value) {
17
+ if (key in obj) {
18
+ Object.defineProperty(obj, key, {
19
+ value: value,
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true
23
+ });
24
+ } else {
25
+ obj[key] = value;
26
+ }
27
+
28
+ return obj;
29
+ }
30
+
31
+ function _objectWithoutPropertiesLoose(source, excluded) {
32
+ if (source == null) return {};
33
+ var target = {};
34
+ var sourceKeys = Object.keys(source);
35
+ var key, i;
36
+
37
+ for (i = 0; i < sourceKeys.length; i++) {
38
+ key = sourceKeys[i];
39
+ if (excluded.indexOf(key) >= 0) continue;
40
+ target[key] = source[key];
41
+ }
42
+
43
+ return target;
44
+ }
45
+
46
+ function _objectWithoutProperties(source, excluded) {
47
+ if (source == null) return {};
48
+ var target = _objectWithoutPropertiesLoose(source, excluded);
49
+ var key, i;
50
+
51
+ if (Object.getOwnPropertySymbols) {
52
+ var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
53
+
54
+ for (i = 0; i < sourceSymbolKeys.length; i++) {
55
+ key = sourceSymbolKeys[i];
56
+ if (excluded.indexOf(key) >= 0) continue;
57
+ if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
58
+ target[key] = source[key];
59
+ }
60
+ }
61
+
62
+ return target;
63
+ }
15
64
 
16
65
  /**
17
- * Leaf content strings.
18
- */
19
- const String = (props) => {
20
- const { isLast, leaf, parent, text } = props;
21
- const editor = useEditor();
22
- const path = ReactEditor.findPath(editor, text);
23
- const parentPath = slate.Path.parent(path);
24
- // COMPAT: Render text inside void nodes with a zero-width space.
25
- // So the node can contain selection but the text is not visible.
26
- if (editor.isVoid(parent)) {
27
- return React__default.createElement(ZeroWidthString, { length: slate.Node.string(parent).length });
28
- }
29
- // COMPAT: If this is the last text node in an empty block, render a zero-
30
- // width space that will convert into a line break when copying and pasting
31
- // to support expected plain text.
32
- if (leaf.text === '' &&
33
- parent.children[parent.children.length - 1] === text &&
34
- !editor.isInline(parent) &&
35
- slate.Editor.string(editor, parentPath) === '') {
36
- return React__default.createElement(ZeroWidthString, { isLineBreak: true });
37
- }
38
- // COMPAT: If the text is empty, it's because it's on the edge of an inline
39
- // node, so we render a zero-width space so that the selection can be
40
- // inserted next to it still.
41
- if (leaf.text === '') {
42
- return React__default.createElement(ZeroWidthString, null);
43
- }
44
- // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
45
- // so we need to add an extra trailing new lines to prevent that.
46
- if (isLast && leaf.text.slice(-1) === '\n') {
47
- return React__default.createElement(TextString, { isTrailing: true, text: leaf.text });
48
- }
49
- return React__default.createElement(TextString, { text: leaf.text });
50
- };
51
- /**
52
- * Leaf strings with text in them.
53
- */
54
- const TextString = (props) => {
55
- const { text, isTrailing = false } = props;
56
- return (React__default.createElement("span", { "data-slate-string": true },
57
- text,
58
- isTrailing ? '\n' : null));
59
- };
66
+ * An auto-incrementing identifier for keys.
67
+ */
68
+ var n = 0;
60
69
  /**
61
- * Leaf strings without text, render as zero-width strings.
62
- */
63
- const ZeroWidthString = (props) => {
64
- const { length = 0, isLineBreak = false } = props;
65
- return (React__default.createElement("span", { "data-slate-zero-width": isLineBreak ? 'n' : 'z', "data-slate-length": length },
66
- '\uFEFF',
67
- isLineBreak ? React__default.createElement("br", null) : null));
68
- };
70
+ * A class that keeps track of a key string. We use a full class here because we
71
+ * want to be able to use them as keys in `WeakMap` objects.
72
+ */
73
+
74
+ class Key {
75
+ constructor() {
76
+ this.id = "".concat(n++);
77
+ }
78
+
79
+ }
69
80
 
70
81
  /**
71
82
  * Two weak maps that allow us rebuild a path given a node. They are populated
@@ -101,523 +112,51 @@ var EDITOR_TO_ON_CHANGE = new WeakMap();
101
112
  var PLACEHOLDER_SYMBOL = Symbol('placeholder');
102
113
 
103
114
  /**
104
- * Individual leaves in a text node with unique formatting.
105
- */
106
- const Leaf = (props) => {
107
- const { leaf, isLast, text, parent, renderLeaf = (props) => React__default.createElement(DefaultLeaf, Object.assign({}, props)), } = props;
108
- let children = (React__default.createElement(String, { isLast: isLast, leaf: leaf, parent: parent, text: text }));
109
- if (leaf[PLACEHOLDER_SYMBOL]) {
110
- children = (React__default.createElement(React__default.Fragment, null,
111
- React__default.createElement("span", { contentEditable: false, style: {
112
- pointerEvents: 'none',
113
- display: 'inline-block',
114
- verticalAlign: 'text-top',
115
- width: '0',
116
- maxWidth: '100%',
117
- whiteSpace: 'nowrap',
118
- opacity: '0.333',
119
- } }, leaf.placeholder),
120
- children));
121
- }
122
- // COMPAT: Having the `data-` attributes on these leaf elements ensures that
123
- // in certain misbehaving browsers they aren't weirdly cloned/destroyed by
124
- // contenteditable behaviors. (2019/05/08)
125
- const attributes = {
126
- 'data-slate-leaf': true,
127
- };
128
- return renderLeaf({ attributes, children, leaf, text });
129
- };
130
- const MemoizedLeaf = React__default.memo(Leaf, (prev, next) => {
131
- return (next.parent === prev.parent &&
132
- next.isLast === prev.isLast &&
133
- next.renderLeaf === prev.renderLeaf &&
134
- next.text === prev.text &&
135
- slate.Text.matches(next.leaf, prev.leaf));
136
- });
137
- /**
138
- * The default custom leaf renderer.
139
- */
140
- const DefaultLeaf = (props) => {
141
- const { attributes, children } = props;
142
- return React__default.createElement("span", Object.assign({}, attributes), children);
143
- };
144
-
145
- /**
146
- * Prevent warning on SSR by falling back to useEffect when window is not defined
147
- */
148
-
149
- var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
150
-
151
- /**
152
- * Text.
153
- */
154
- const Text = (props) => {
155
- const { decorations, isLast, parent, renderLeaf, text } = props;
156
- const editor = useEditor();
157
- const ref = React.useRef(null);
158
- const leaves = getLeaves(text, decorations);
159
- const key = ReactEditor.findKey(editor, text);
160
- const children = [];
161
- for (let i = 0; i < leaves.length; i++) {
162
- const leaf = leaves[i];
163
- children.push(React__default.createElement(MemoizedLeaf, { isLast: isLast && i === leaves.length - 1, key: `${key.id}-${i}`, leaf: leaf, text: text, parent: parent, renderLeaf: renderLeaf }));
164
- }
165
- // Update element-related weak maps with the DOM element ref.
166
- useIsomorphicLayoutEffect(() => {
167
- if (ref.current) {
168
- KEY_TO_ELEMENT.set(key, ref.current);
169
- NODE_TO_ELEMENT.set(text, ref.current);
170
- ELEMENT_TO_NODE.set(ref.current, text);
171
- }
172
- else {
173
- KEY_TO_ELEMENT.delete(key);
174
- NODE_TO_ELEMENT.delete(text);
175
- }
176
- });
177
- return (React__default.createElement("span", { "data-slate-node": "text", ref: ref }, children));
178
- };
179
- /**
180
- * Get the leaves for a text node given decorations.
181
- */
182
- const getLeaves = (node, decorations) => {
183
- let leaves = [{ ...node }];
184
- for (const dec of decorations) {
185
- const { anchor, focus, ...rest } = dec;
186
- const [start, end] = slate.Range.edges(dec);
187
- const next = [];
188
- let o = 0;
189
- for (const leaf of leaves) {
190
- const { length } = leaf.text;
191
- const offset = o;
192
- o += length;
193
- // If the range encompases the entire leaf, add the range.
194
- if (start.offset <= offset && end.offset >= offset + length) {
195
- Object.assign(leaf, rest);
196
- next.push(leaf);
197
- continue;
198
- }
199
- // If the range starts after the leaf, or ends before it, continue.
200
- if (start.offset > offset + length ||
201
- end.offset < offset ||
202
- (end.offset === offset && offset !== 0)) {
203
- next.push(leaf);
204
- continue;
205
- }
206
- // Otherwise we need to split the leaf, at the start, end, or both,
207
- // and add the range to the middle intersecting section. Do the end
208
- // split first since we don't need to update the offset that way.
209
- let middle = leaf;
210
- let before;
211
- let after;
212
- if (end.offset < offset + length) {
213
- const off = end.offset - offset;
214
- after = { ...middle, text: middle.text.slice(off) };
215
- middle = { ...middle, text: middle.text.slice(0, off) };
216
- }
217
- if (start.offset > offset) {
218
- const off = start.offset - offset;
219
- before = { ...middle, text: middle.text.slice(0, off) };
220
- middle = { ...middle, text: middle.text.slice(off) };
221
- }
222
- Object.assign(middle, rest);
223
- if (before) {
224
- next.push(before);
225
- }
226
- next.push(middle);
227
- if (after) {
228
- next.push(after);
229
- }
230
- }
231
- leaves = next;
232
- }
233
- return leaves;
234
- };
235
- const MemoizedText = React__default.memo(Text, (prev, next) => {
236
- return (next.parent === prev.parent &&
237
- next.isLast === prev.isLast &&
238
- next.renderLeaf === prev.renderLeaf &&
239
- next.text === prev.text);
240
- });
241
-
242
- /**
243
- * A React context for sharing the `selected` state of an element.
244
- */
245
-
246
- var SelectedContext = React.createContext(false);
247
- /**
248
- * Get the current `selected` state of an element.
249
- */
250
-
251
- var useSelected = function useSelected() {
252
- return React.useContext(SelectedContext);
253
- };
254
-
255
- /**
256
- * Element.
257
- */
258
- const Element = (props) => {
259
- const { decorate, decorations, element, renderElement = (p) => React__default.createElement(DefaultElement, Object.assign({}, p)), renderLeaf, selection, } = props;
260
- const ref = React.useRef(null);
261
- const editor = useEditor();
262
- const readOnly = useReadOnly();
263
- const isInline = editor.isInline(element);
264
- const key = ReactEditor.findKey(editor, element);
265
- let children = (React__default.createElement(Children, { decorate: decorate, decorations: decorations, node: element, renderElement: renderElement, renderLeaf: renderLeaf, selection: selection }));
266
- // Attributes that the developer must mix into the element in their
267
- // custom node renderer component.
268
- const attributes = {
269
- 'data-slate-node': 'element',
270
- ref,
271
- };
272
- if (isInline) {
273
- attributes['data-slate-inline'] = true;
274
- }
275
- // If it's a block node with inline children, add the proper `dir` attribute
276
- // for text direction.
277
- if (!isInline && slate.Editor.hasInlines(editor, element)) {
278
- const text = slate.Node.string(element);
279
- const dir = getDirection(text);
280
- if (dir === 'rtl') {
281
- attributes.dir = dir;
282
- }
283
- }
284
- // If it's a void node, wrap the children in extra void-specific elements.
285
- if (slate.Editor.isVoid(editor, element)) {
286
- attributes['data-slate-void'] = true;
287
- if (!readOnly && isInline) {
288
- attributes.contentEditable = false;
289
- }
290
- const Tag = isInline ? 'span' : 'div';
291
- const [[text]] = slate.Node.texts(element);
292
- children = readOnly ? null : (React__default.createElement(Tag, { "data-slate-spacer": true, style: {
293
- height: '0',
294
- color: 'transparent',
295
- outline: 'none',
296
- position: 'absolute',
297
- } },
298
- React__default.createElement(MemoizedText, { decorations: [], isLast: false, parent: element, text: text })));
299
- NODE_TO_INDEX.set(text, 0);
300
- NODE_TO_PARENT.set(text, element);
301
- }
302
- // Update element-related weak maps with the DOM element ref.
303
- useIsomorphicLayoutEffect(() => {
304
- if (ref.current) {
305
- KEY_TO_ELEMENT.set(key, ref.current);
306
- NODE_TO_ELEMENT.set(element, ref.current);
307
- ELEMENT_TO_NODE.set(ref.current, element);
308
- }
309
- else {
310
- KEY_TO_ELEMENT.delete(key);
311
- NODE_TO_ELEMENT.delete(element);
312
- }
313
- });
314
- return (React__default.createElement(SelectedContext.Provider, { value: !!selection }, renderElement({ attributes, children, element })));
315
- };
316
- const MemoizedElement = React__default.memo(Element, (prev, next) => {
317
- return (prev.decorate === next.decorate &&
318
- prev.element === next.element &&
319
- prev.renderElement === next.renderElement &&
320
- prev.renderLeaf === next.renderLeaf &&
321
- isRangeListEqual(prev.decorations, next.decorations) &&
322
- (prev.selection === next.selection ||
323
- (!!prev.selection &&
324
- !!next.selection &&
325
- slate.Range.equals(prev.selection, next.selection))));
326
- });
327
- /**
328
- * The default element renderer.
329
- */
330
- const DefaultElement = (props) => {
331
- const { attributes, children, element } = props;
332
- const editor = useEditor();
333
- const Tag = editor.isInline(element) ? 'span' : 'div';
334
- return (React__default.createElement(Tag, Object.assign({}, attributes, { style: { position: 'relative' } }), children));
335
- };
336
- /**
337
- * Check if a list of ranges is equal to another.
338
- *
339
- * PERF: this requires the two lists to also have the ranges inside them in the
340
- * same order, but this is an okay constraint for us since decorations are
341
- * kept in order, and the odd case where they aren't is okay to re-render for.
342
- */
343
- const isRangeListEqual = (list, another) => {
344
- if (list.length !== another.length) {
345
- return false;
346
- }
347
- for (let i = 0; i < list.length; i++) {
348
- const range = list[i];
349
- const other = another[i];
350
- if (!slate.Range.equals(range, other)) {
351
- return false;
352
- }
353
- }
354
- return true;
355
- };
356
-
357
- /**
358
- * A React context for sharing the `Editor` class.
359
- */
360
- const EditorContext = React.createContext(null);
361
- /**
362
- * Get the current `Editor` class that the component lives under.
363
- */
364
- const useEditor = () => {
365
- const editor = React.useContext(EditorContext);
366
- if (!editor) {
367
- throw new Error(`The \`useEditor\` hook must be used inside the <Slate> component's context.`);
368
- }
369
- return editor;
370
- };
371
-
372
- /**
373
- * Children.
374
- */
375
- const Children = (props) => {
376
- const { decorate, decorations, node, renderElement, renderLeaf, selection, } = props;
377
- const editor = useEditor();
378
- const path = ReactEditor.findPath(editor, node);
379
- const children = [];
380
- const isLeafBlock = slate.Element.isElement(node) &&
381
- !editor.isInline(node) &&
382
- slate.Editor.hasInlines(editor, node);
383
- for (let i = 0; i < node.children.length; i++) {
384
- const p = path.concat(i);
385
- const n = node.children[i];
386
- const key = ReactEditor.findKey(editor, n);
387
- const range = slate.Editor.range(editor, p);
388
- const sel = selection && slate.Range.intersection(range, selection);
389
- const ds = decorate([n, p]);
390
- for (const dec of decorations) {
391
- const d = slate.Range.intersection(dec, range);
392
- if (d) {
393
- ds.push(d);
394
- }
395
- }
396
- if (slate.Element.isElement(n)) {
397
- children.push(React__default.createElement(MemoizedElement, { decorate: decorate, decorations: ds, element: n, key: key.id, renderElement: renderElement, renderLeaf: renderLeaf, selection: sel }));
398
- }
399
- else {
400
- children.push(React__default.createElement(MemoizedText, { decorations: ds, key: key.id, isLast: isLeafBlock && i === node.children.length - 1, parent: node, renderLeaf: renderLeaf, text: n }));
401
- }
402
- NODE_TO_INDEX.set(n, i);
403
- NODE_TO_PARENT.set(n, node);
404
- }
405
- return React__default.createElement(React__default.Fragment, null, children);
406
- };
407
-
408
- var IS_IOS = typeof navigator !== 'undefined' && typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
409
- var IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
410
- var IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
411
- var IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
412
-
413
- /**
414
- * Hotkey mappings for each platform.
415
- */
416
-
417
- var HOTKEYS = {
418
- bold: 'mod+b',
419
- compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
420
- moveBackward: 'left',
421
- moveForward: 'right',
422
- moveWordBackward: 'ctrl+left',
423
- moveWordForward: 'ctrl+right',
424
- deleteBackward: 'shift?+backspace',
425
- deleteForward: 'shift?+delete',
426
- extendBackward: 'shift+left',
427
- extendForward: 'shift+right',
428
- italic: 'mod+i',
429
- splitBlock: 'shift?+enter',
430
- undo: 'mod+z'
431
- };
432
- var APPLE_HOTKEYS = {
433
- moveLineBackward: 'opt+up',
434
- moveLineForward: 'opt+down',
435
- moveWordBackward: 'opt+left',
436
- moveWordForward: 'opt+right',
437
- deleteBackward: ['ctrl+backspace', 'ctrl+h'],
438
- deleteForward: ['ctrl+delete', 'ctrl+d'],
439
- deleteLineBackward: 'cmd+shift?+backspace',
440
- deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
441
- deleteWordBackward: 'opt+shift?+backspace',
442
- deleteWordForward: 'opt+shift?+delete',
443
- extendLineBackward: 'opt+shift+up',
444
- extendLineForward: 'opt+shift+down',
445
- redo: 'cmd+shift+z',
446
- transposeCharacter: 'ctrl+t'
447
- };
448
- var WINDOWS_HOTKEYS = {
449
- deleteWordBackward: 'ctrl+shift?+backspace',
450
- deleteWordForward: 'ctrl+shift?+delete',
451
- redo: ['ctrl+y', 'ctrl+shift+z']
452
- };
453
- /**
454
- * Create a platform-aware hotkey checker.
455
- */
456
-
457
- var create = function create(key) {
458
- var generic = HOTKEYS[key];
459
- var apple = APPLE_HOTKEYS[key];
460
- var windows = WINDOWS_HOTKEYS[key];
461
- var isGeneric = generic && isHotkey.isKeyHotkey(generic);
462
- var isApple = apple && isHotkey.isKeyHotkey(apple);
463
- var isWindows = windows && isHotkey.isKeyHotkey(windows);
464
- return function (event) {
465
- if (isGeneric && isGeneric(event)) return true;
466
- if (IS_APPLE && isApple && isApple(event)) return true;
467
- if (!IS_APPLE && isWindows && isWindows(event)) return true;
468
- return false;
469
- };
470
- };
471
- /**
472
- * Hotkeys.
473
- */
474
-
475
-
476
- var Hotkeys = {
477
- isBold: create('bold'),
478
- isCompose: create('compose'),
479
- isMoveBackward: create('moveBackward'),
480
- isMoveForward: create('moveForward'),
481
- isDeleteBackward: create('deleteBackward'),
482
- isDeleteForward: create('deleteForward'),
483
- isDeleteLineBackward: create('deleteLineBackward'),
484
- isDeleteLineForward: create('deleteLineForward'),
485
- isDeleteWordBackward: create('deleteWordBackward'),
486
- isDeleteWordForward: create('deleteWordForward'),
487
- isExtendBackward: create('extendBackward'),
488
- isExtendForward: create('extendForward'),
489
- isExtendLineBackward: create('extendLineBackward'),
490
- isExtendLineForward: create('extendLineForward'),
491
- isItalic: create('italic'),
492
- isMoveLineBackward: create('moveLineBackward'),
493
- isMoveLineForward: create('moveLineForward'),
494
- isMoveWordBackward: create('moveWordBackward'),
495
- isMoveWordForward: create('moveWordForward'),
496
- isRedo: create('redo'),
497
- isSplitBlock: create('splitBlock'),
498
- isTransposeCharacter: create('transposeCharacter'),
499
- isUndo: create('undo')
500
- };
501
-
502
- /**
503
- * A React context for sharing the `readOnly` state of the editor.
504
- */
505
-
506
- var ReadOnlyContext = React.createContext(false);
507
- /**
508
- * Get the current `readOnly` state of the editor.
115
+ * Types.
509
116
  */
510
-
511
- var useReadOnly = function useReadOnly() {
512
- return React.useContext(ReadOnlyContext);
513
- };
514
-
515
- /**
516
- * A React context for sharing the `Editor` class, in a way that re-renders the
517
- * context whenever changes occur.
518
- */
519
- const SlateContext = React.createContext(null);
520
- /**
521
- * Get the current `Editor` class that the component lives under.
522
- */
523
- const useSlate = () => {
524
- const context = React.useContext(SlateContext);
525
- if (!context) {
526
- throw new Error(`The \`useSlate\` hook must be used inside the <SlateProvider> component's context.`);
527
- }
528
- const [editor] = context;
529
- return editor;
530
- };
531
-
532
- function _arrayWithHoles(arr) {
533
- if (Array.isArray(arr)) return arr;
534
- }
535
-
536
- var arrayWithHoles = _arrayWithHoles;
537
-
538
- function _iterableToArrayLimit(arr, i) {
539
- if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
540
- return;
541
- }
542
-
543
- var _arr = [];
544
- var _n = true;
545
- var _d = false;
546
- var _e = undefined;
547
-
548
- try {
549
- for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
550
- _arr.push(_s.value);
551
-
552
- if (i && _arr.length === i) break;
553
- }
554
- } catch (err) {
555
- _d = true;
556
- _e = err;
557
- } finally {
558
- try {
559
- if (!_n && _i["return"] != null) _i["return"]();
560
- } finally {
561
- if (_d) throw _e;
562
- }
563
- }
564
-
565
- return _arr;
566
- }
567
-
568
- var iterableToArrayLimit = _iterableToArrayLimit;
569
-
570
- function _nonIterableRest() {
571
- throw new TypeError("Invalid attempt to destructure non-iterable instance");
572
- }
573
-
574
- var nonIterableRest = _nonIterableRest;
575
-
576
- function _slicedToArray(arr, i) {
577
- return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || nonIterableRest();
578
- }
579
-
580
- var slicedToArray = _slicedToArray;
581
-
582
117
  /**
583
118
  * Check if a DOM node is a comment node.
584
119
  */
585
120
 
586
- var isDOMComment = function isDOMComment(value) {
121
+ var isDOMComment = value => {
587
122
  return isDOMNode(value) && value.nodeType === 8;
588
123
  };
589
124
  /**
590
125
  * Check if a DOM node is an element node.
591
126
  */
592
127
 
593
- var isDOMElement = function isDOMElement(value) {
128
+ var isDOMElement = value => {
594
129
  return isDOMNode(value) && value.nodeType === 1;
595
130
  };
596
131
  /**
597
132
  * Check if a value is a DOM node.
598
133
  */
599
134
 
600
- var isDOMNode = function isDOMNode(value) {
135
+ var isDOMNode = value => {
601
136
  return value instanceof Node;
602
137
  };
603
138
  /**
604
139
  * Check if a DOM node is an element node.
605
140
  */
606
141
 
607
- var isDOMText = function isDOMText(value) {
142
+ var isDOMText = value => {
608
143
  return isDOMNode(value) && value.nodeType === 3;
609
144
  };
145
+ /**
146
+ * Checks whether a paste event is a plaintext-only event.
147
+ */
148
+
149
+ var isPlainTextOnlyPaste = event => {
150
+ return event.clipboardData && event.clipboardData.getData('text/plain') !== '' && event.clipboardData.types.length === 1;
151
+ };
610
152
  /**
611
153
  * Normalize a DOM point so that it always refers to a text node.
612
154
  */
613
155
 
614
- var normalizeDOMPoint = function normalizeDOMPoint(domPoint) {
615
- var _domPoint = slicedToArray(domPoint, 2),
616
- node = _domPoint[0],
617
- offset = _domPoint[1]; // If it's an element node, its offset refers to the index of its children
156
+ var normalizeDOMPoint = domPoint => {
157
+ var [node, offset] = domPoint; // If it's an element node, its offset refers to the index of its children
618
158
  // including comment nodes, so try to find the right text child node.
619
159
 
620
-
621
160
  if (isDOMElement(node) && node.childNodes.length) {
622
161
  var isLast = offset === node.childNodes.length;
623
162
  var direction = isLast ? 'backward' : 'forward';
@@ -642,8 +181,10 @@ var normalizeDOMPoint = function normalizeDOMPoint(domPoint) {
642
181
  * `direction`.
643
182
  */
644
183
 
645
- var getEditableChild = function getEditableChild(parent, index, direction) {
646
- var childNodes = parent.childNodes;
184
+ var getEditableChild = (parent, index, direction) => {
185
+ var {
186
+ childNodes
187
+ } = parent;
647
188
  var child = childNodes[index];
648
189
  var i = index;
649
190
  var triedForward = false;
@@ -675,816 +216,54 @@ var getEditableChild = function getEditableChild(parent, index, direction) {
675
216
 
676
217
  return child;
677
218
  };
678
-
679
- /**
680
- * Editable.
681
- */
682
- const Editable = (props) => {
683
- const { autoFocus, decorate = defaultDecorate, onDOMBeforeInput: propsOnDOMBeforeInput, placeholder, readOnly = false, renderElement, renderLeaf, style = {}, as: Component = 'div', ...attributes } = props;
684
- const editor = useSlate();
685
- const ref = React.useRef(null);
686
- // Update internal state on each render.
687
- IS_READ_ONLY.set(editor, readOnly);
688
- // Keep track of some state for the event handler logic.
689
- const state = React.useMemo(() => ({
690
- isComposing: false,
691
- isUpdatingSelection: false,
692
- latestElement: null,
693
- }), []);
694
- // Update element-related weak maps with the DOM element ref.
695
- useIsomorphicLayoutEffect(() => {
696
- if (ref.current) {
697
- EDITOR_TO_ELEMENT.set(editor, ref.current);
698
- NODE_TO_ELEMENT.set(editor, ref.current);
699
- ELEMENT_TO_NODE.set(ref.current, editor);
700
- }
701
- else {
702
- NODE_TO_ELEMENT.delete(editor);
703
- }
704
- });
705
- // Attach a native DOM event handler for `selectionchange`, because React's
706
- // built-in `onSelect` handler doesn't fire for all selection changes. It's a
707
- // leaky polyfill that only fires on keypresses or clicks. Instead, we want to
708
- // fire for any change to the selection inside the editor. (2019/11/04)
709
- // https://github.com/facebook/react/issues/5785
710
- useIsomorphicLayoutEffect(() => {
711
- window.document.addEventListener('selectionchange', onDOMSelectionChange);
712
- return () => {
713
- window.document.removeEventListener('selectionchange', onDOMSelectionChange);
714
- };
715
- }, []);
716
- // Attach a native DOM event handler for `beforeinput` events, because React's
717
- // built-in `onBeforeInput` is actually a leaky polyfill that doesn't expose
718
- // real `beforeinput` events sadly... (2019/11/04)
719
- // https://github.com/facebook/react/issues/11211
720
- useIsomorphicLayoutEffect(() => {
721
- if (ref.current) {
722
- // @ts-ignore The `beforeinput` event isn't recognized.
723
- ref.current.addEventListener('beforeinput', onDOMBeforeInput);
724
- }
725
- return () => {
726
- if (ref.current) {
727
- // @ts-ignore The `beforeinput` event isn't recognized.
728
- ref.current.removeEventListener('beforeinput', onDOMBeforeInput);
729
- }
730
- };
731
- }, []);
732
- // Whenever the editor updates, make sure the DOM selection state is in sync.
733
- useIsomorphicLayoutEffect(() => {
734
- const { selection } = editor;
735
- const domSelection = window.getSelection();
736
- if (state.isComposing || !domSelection || !ReactEditor.isFocused(editor)) {
737
- return;
738
- }
739
- const hasDomSelection = domSelection.type !== 'None';
740
- // If the DOM selection is properly unset, we're done.
741
- if (!selection && !hasDomSelection) {
742
- return;
743
- }
744
- const newDomRange = selection && ReactEditor.toDOMRange(editor, selection);
745
- // If the DOM selection is already correct, we're done.
746
- if (hasDomSelection &&
747
- newDomRange &&
748
- isRangeEqual(domSelection.getRangeAt(0), newDomRange)) {
749
- return;
750
- }
751
- // Otherwise the DOM selection is out of sync, so update it.
752
- const el = ReactEditor.toDOMNode(editor, editor);
753
- state.isUpdatingSelection = true;
754
- domSelection.removeAllRanges();
755
- if (newDomRange) {
756
- domSelection.addRange(newDomRange);
757
- const leafEl = newDomRange.startContainer.parentElement;
758
- scrollIntoView(leafEl, { scrollMode: 'if-needed' });
759
- }
760
- setTimeout(() => {
761
- // COMPAT: In Firefox, it's not enough to create a range, you also need
762
- // to focus the contenteditable element too. (2016/11/16)
763
- if (newDomRange && IS_FIREFOX) {
764
- el.focus();
765
- }
766
- state.isUpdatingSelection = false;
767
- });
768
- });
769
- // The autoFocus TextareaHTMLAttribute doesn't do anything on a div, so it
770
- // needs to be manually focused.
771
- React.useEffect(() => {
772
- if (ref.current && autoFocus) {
773
- ref.current.focus();
774
- }
775
- }, [autoFocus]);
776
- // Listen on the native `beforeinput` event to get real "Level 2" events. This
777
- // is required because React's `beforeinput` is fake and never really attaches
778
- // to the real event sadly. (2019/11/01)
779
- // https://github.com/facebook/react/issues/11211
780
- const onDOMBeforeInput = React.useCallback((event) => {
781
- if (!readOnly &&
782
- hasEditableTarget(editor, event.target) &&
783
- !isDOMEventHandled(event, propsOnDOMBeforeInput)) {
784
- const { selection } = editor;
785
- const { inputType: type } = event;
786
- const data = event.dataTransfer || event.data || undefined;
787
- // These two types occur while a user is composing text and can't be
788
- // cancelled. Let them through and wait for the composition to end.
789
- if (type === 'insertCompositionText' ||
790
- type === 'deleteCompositionText') {
791
- return;
792
- }
793
- event.preventDefault();
794
- // COMPAT: For the deleting forward/backward input types we don't want
795
- // to change the selection because it is the range that will be deleted,
796
- // and those commands determine that for themselves.
797
- if (!type.startsWith('delete') || type.startsWith('deleteBy')) {
798
- const [targetRange] = event.getTargetRanges();
799
- if (targetRange) {
800
- const range = ReactEditor.toSlateRange(editor, targetRange);
801
- if (!selection || !slate.Range.equals(selection, range)) {
802
- slate.Editor.select(editor, range);
803
- }
804
- }
805
- }
806
- // COMPAT: If the selection is expanded, even if the command seems like
807
- // a delete forward/backward command it should delete the selection.
808
- if (selection &&
809
- slate.Range.isExpanded(selection) &&
810
- type.startsWith('delete')) {
811
- editor.exec({ type: 'delete_fragment' });
812
- return;
813
- }
814
- switch (type) {
815
- case 'deleteByComposition':
816
- case 'deleteByCut':
817
- case 'deleteByDrag': {
818
- editor.exec({ type: 'delete_fragment' });
819
- break;
820
- }
821
- case 'deleteContent':
822
- case 'deleteContentForward': {
823
- editor.exec({ type: 'delete_forward', unit: 'character' });
824
- break;
825
- }
826
- case 'deleteContentBackward': {
827
- editor.exec({ type: 'delete_backward', unit: 'character' });
828
- break;
829
- }
830
- case 'deleteEntireSoftLine': {
831
- editor.exec({ type: 'delete_backward', unit: 'line' });
832
- editor.exec({ type: 'delete_forward', unit: 'line' });
833
- break;
834
- }
835
- case 'deleteHardLineBackward': {
836
- editor.exec({ type: 'delete_backward', unit: 'block' });
837
- break;
838
- }
839
- case 'deleteSoftLineBackward': {
840
- editor.exec({ type: 'delete_backward', unit: 'line' });
841
- break;
842
- }
843
- case 'deleteHardLineForward': {
844
- editor.exec({ type: 'delete_forward', unit: 'block' });
845
- break;
846
- }
847
- case 'deleteSoftLineForward': {
848
- editor.exec({ type: 'delete_forward', unit: 'line' });
849
- break;
850
- }
851
- case 'deleteWordBackward': {
852
- editor.exec({ type: 'delete_backward', unit: 'word' });
853
- break;
854
- }
855
- case 'deleteWordForward': {
856
- editor.exec({ type: 'delete_forward', unit: 'word' });
857
- break;
858
- }
859
- case 'insertLineBreak':
860
- case 'insertParagraph': {
861
- editor.exec({ type: 'insert_break' });
862
- break;
863
- }
864
- case 'insertFromComposition':
865
- case 'insertFromDrop':
866
- case 'insertFromPaste':
867
- case 'insertFromYank':
868
- case 'insertReplacementText':
869
- case 'insertText': {
870
- if (data instanceof DataTransfer) {
871
- editor.exec({ type: 'insert_data', data });
872
- }
873
- else if (typeof data === 'string') {
874
- editor.exec({ type: 'insert_text', text: data });
875
- }
876
- break;
877
- }
878
- }
879
- }
880
- }, []);
881
- // Listen on the native `selectionchange` event to be able to update any time
882
- // the selection changes. This is required because React's `onSelect` is leaky
883
- // and non-standard so it doesn't fire until after a selection has been
884
- // released. This causes issues in situations where another change happens
885
- // while a selection is being dragged.
886
- const onDOMSelectionChange = React.useCallback(debounce(() => {
887
- if (!readOnly && !state.isComposing && !state.isUpdatingSelection) {
888
- const { activeElement } = window.document;
889
- const el = ReactEditor.toDOMNode(editor, editor);
890
- const domSelection = window.getSelection();
891
- const domRange = domSelection &&
892
- domSelection.rangeCount > 0 &&
893
- domSelection.getRangeAt(0);
894
- if (activeElement === el) {
895
- state.latestElement = activeElement;
896
- IS_FOCUSED.set(editor, true);
897
- }
898
- else {
899
- IS_FOCUSED.delete(editor);
900
- }
901
- if (domRange &&
902
- hasEditableTarget(editor, domRange.startContainer) &&
903
- hasEditableTarget(editor, domRange.endContainer)) {
904
- const range = ReactEditor.toSlateRange(editor, domRange);
905
- slate.Editor.select(editor, range);
906
- }
907
- else {
908
- slate.Editor.deselect(editor);
909
- }
910
- }
911
- }, 100), []);
912
- const decorations = decorate([editor, []]);
913
- if (placeholder &&
914
- editor.children.length === 1 &&
915
- Array.from(slate.Node.texts(editor)).length === 1 &&
916
- slate.Node.string(editor) === '') {
917
- const start = slate.Editor.start(editor, []);
918
- decorations.push({
919
- [PLACEHOLDER_SYMBOL]: true,
920
- placeholder,
921
- anchor: start,
922
- focus: start,
923
- });
924
- }
925
- return (React__default.createElement(ReadOnlyContext.Provider, { value: readOnly },
926
- React__default.createElement(Component
927
- // COMPAT: The Grammarly Chrome extension works by changing the DOM
928
- // out from under `contenteditable` elements, which leads to weird
929
- // behaviors so we have to disable it like editor. (2017/04/24)
930
- , Object.assign({ "data-gramm": false, role: readOnly ? undefined : 'textbox' }, attributes, {
931
- // COMPAT: Firefox doesn't support the `beforeinput` event, so we'd
932
- // have to use hacks to make these replacement-based features work.
933
- spellCheck: IS_FIREFOX ? undefined : attributes.spellCheck, autoCorrect: IS_FIREFOX ? undefined : attributes.autoCorrect, autoCapitalize: IS_FIREFOX ? undefined : attributes.autoCapitalize, "data-slate-editor": true, "data-slate-node": "value", contentEditable: readOnly ? undefined : true, suppressContentEditableWarning: true, ref: ref, style: {
934
- // Prevent the default outline styles.
935
- outline: 'none',
936
- // Preserve adjacent whitespace and new lines.
937
- whiteSpace: 'pre-wrap',
938
- // Allow words to break if they are too long.
939
- wordWrap: 'break-word',
940
- // Allow for passed-in styles to override anything.
941
- ...style,
942
- }, onBeforeInput: React.useCallback((event) => {
943
- // COMPAT: Firefox doesn't support the `beforeinput` event, so we
944
- // fall back to React's leaky polyfill instead just for it. It
945
- // only works for the `insertText` input type.
946
- if (IS_FIREFOX && !readOnly) {
947
- event.preventDefault();
948
- const text = event.data;
949
- editor.exec({ type: 'insert_text', text });
950
- }
951
- }, [readOnly]), onBlur: React.useCallback((event) => {
952
- if (readOnly ||
953
- state.isUpdatingSelection ||
954
- !hasEditableTarget(editor, event.target) ||
955
- isEventHandled(event, attributes.onBlur)) {
956
- return;
957
- }
958
- // COMPAT: If the current `activeElement` is still the previous
959
- // one, this is due to the window being blurred when the tab
960
- // itself becomes unfocused, so we want to abort early to allow to
961
- // editor to stay focused when the tab becomes focused again.
962
- if (state.latestElement === window.document.activeElement) {
963
- return;
964
- }
965
- const { relatedTarget } = event;
966
- const el = ReactEditor.toDOMNode(editor, editor);
967
- // COMPAT: The event should be ignored if the focus is returning
968
- // to the editor from an embedded editable element (eg. an <input>
969
- // element inside a void node).
970
- if (relatedTarget === el) {
971
- return;
972
- }
973
- // COMPAT: The event should be ignored if the focus is moving from
974
- // the editor to inside a void node's spacer element.
975
- if (isDOMElement(relatedTarget) &&
976
- relatedTarget.hasAttribute('data-slate-spacer')) {
977
- return;
978
- }
979
- // COMPAT: The event should be ignored if the focus is moving to a
980
- // non- editable section of an element that isn't a void node (eg.
981
- // a list item of the check list example).
982
- if (relatedTarget != null &&
983
- isDOMNode(relatedTarget) &&
984
- ReactEditor.hasDOMNode(editor, relatedTarget)) {
985
- const node = ReactEditor.toSlateNode(editor, relatedTarget);
986
- if (slate.Element.isElement(node) && !editor.isVoid(node)) {
987
- return;
988
- }
989
- }
990
- IS_FOCUSED.delete(editor);
991
- }, [readOnly, attributes.onBlur]), onClick: React.useCallback((event) => {
992
- if (!readOnly &&
993
- hasTarget(editor, event.target) &&
994
- !isEventHandled(event, attributes.onClick) &&
995
- isDOMNode(event.target)) {
996
- const node = ReactEditor.toSlateNode(editor, event.target);
997
- const path = ReactEditor.findPath(editor, node);
998
- const start = slate.Editor.start(editor, path);
999
- if (slate.Editor.void(editor, { at: start })) {
1000
- const range = slate.Editor.range(editor, start);
1001
- slate.Editor.select(editor, range);
1002
- }
1003
- }
1004
- }, [readOnly, attributes.onClick]), onCompositionEnd: React.useCallback((event) => {
1005
- if (hasEditableTarget(editor, event.target) &&
1006
- !isEventHandled(event, attributes.onCompositionEnd)) {
1007
- state.isComposing = false;
1008
- // COMPAT: In Chrome, `beforeinput` events for compositions
1009
- // aren't correct and never fire the "insertFromComposition"
1010
- // type that we need. So instead, insert whenever a composition
1011
- // ends since it will already have been committed to the DOM.
1012
- if (!IS_SAFARI && !IS_FIREFOX && event.data) {
1013
- editor.exec({ type: 'insert_text', text: event.data });
1014
- }
1015
- }
1016
- }, [attributes.onCompositionEnd]), onCompositionStart: React.useCallback((event) => {
1017
- if (hasEditableTarget(editor, event.target) &&
1018
- !isEventHandled(event, attributes.onCompositionStart)) {
1019
- state.isComposing = true;
1020
- }
1021
- }, [attributes.onCompositionStart]), onCopy: React.useCallback((event) => {
1022
- if (hasEditableTarget(editor, event.target) &&
1023
- !isEventHandled(event, attributes.onCopy)) {
1024
- event.preventDefault();
1025
- setFragmentData(event.clipboardData, editor);
1026
- }
1027
- }, [attributes.onCopy]), onCut: React.useCallback((event) => {
1028
- if (!readOnly &&
1029
- hasEditableTarget(editor, event.target) &&
1030
- !isEventHandled(event, attributes.onCut)) {
1031
- event.preventDefault();
1032
- setFragmentData(event.clipboardData, editor);
1033
- const { selection } = editor;
1034
- if (selection && slate.Range.isExpanded(selection)) {
1035
- editor.exec({ type: 'delete_fragment' });
1036
- }
1037
- }
1038
- }, [readOnly, attributes.onCut]), onDragOver: React.useCallback((event) => {
1039
- if (hasTarget(editor, event.target) &&
1040
- !isEventHandled(event, attributes.onDragOver)) {
1041
- // Only when the target is void, call `preventDefault` to signal
1042
- // that drops are allowed. Editable content is droppable by
1043
- // default, and calling `preventDefault` hides the cursor.
1044
- const node = ReactEditor.toSlateNode(editor, event.target);
1045
- if (slate.Editor.isVoid(editor, node)) {
1046
- event.preventDefault();
1047
- }
1048
- }
1049
- }, [attributes.onDragOver]), onDragStart: React.useCallback((event) => {
1050
- if (hasTarget(editor, event.target) &&
1051
- !isEventHandled(event, attributes.onDragStart)) {
1052
- const node = ReactEditor.toSlateNode(editor, event.target);
1053
- const path = ReactEditor.findPath(editor, node);
1054
- const voidMatch = slate.Editor.void(editor, { at: path });
1055
- // If starting a drag on a void node, make sure it is selected
1056
- // so that it shows up in the selection's fragment.
1057
- if (voidMatch) {
1058
- const range = slate.Editor.range(editor, path);
1059
- slate.Editor.select(editor, range);
1060
- }
1061
- setFragmentData(event.dataTransfer, editor);
1062
- }
1063
- }, [attributes.onDragStart]), onDrop: React.useCallback((event) => {
1064
- if (hasTarget(editor, event.target) &&
1065
- !readOnly &&
1066
- !isEventHandled(event, attributes.onDrop)) {
1067
- // COMPAT: Firefox doesn't fire `beforeinput` events at all, and
1068
- // Chromium browsers don't properly fire them for files being
1069
- // dropped into a `contenteditable`. (2019/11/26)
1070
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1028668
1071
- if (IS_FIREFOX ||
1072
- (!IS_SAFARI && event.dataTransfer.files.length > 0)) {
1073
- event.preventDefault();
1074
- const range = ReactEditor.findEventRange(editor, event);
1075
- const data = event.dataTransfer;
1076
- slate.Editor.select(editor, range);
1077
- editor.exec({ type: 'insert_data', data });
1078
- }
1079
- }
1080
- }, [readOnly, attributes.onDrop]), onFocus: React.useCallback((event) => {
1081
- if (!readOnly &&
1082
- !state.isUpdatingSelection &&
1083
- hasEditableTarget(editor, event.target) &&
1084
- !isEventHandled(event, attributes.onFocus)) {
1085
- const el = ReactEditor.toDOMNode(editor, editor);
1086
- state.latestElement = window.document.activeElement;
1087
- // COMPAT: If the editor has nested editable elements, the focus
1088
- // can go to them. In Firefox, this must be prevented because it
1089
- // results in issues with keyboard navigation. (2017/03/30)
1090
- if (IS_FIREFOX && event.target !== el) {
1091
- el.focus();
1092
- return;
1093
- }
1094
- IS_FOCUSED.set(editor, true);
1095
- }
1096
- }, [readOnly, attributes.onFocus]), onKeyDown: React.useCallback((event) => {
1097
- if (!readOnly &&
1098
- hasEditableTarget(editor, event.target) &&
1099
- !isEventHandled(event, attributes.onKeyDown)) {
1100
- const { nativeEvent } = event;
1101
- const { selection } = editor;
1102
- // COMPAT: Since we prevent the default behavior on
1103
- // `beforeinput` events, the browser doesn't think there's ever
1104
- // any history stack to undo or redo, so we have to manage these
1105
- // hotkeys ourselves. (2019/11/06)
1106
- if (Hotkeys.isRedo(nativeEvent)) {
1107
- event.preventDefault();
1108
- editor.exec({ type: 'redo' });
1109
- return;
1110
- }
1111
- if (Hotkeys.isUndo(nativeEvent)) {
1112
- event.preventDefault();
1113
- editor.exec({ type: 'undo' });
1114
- return;
1115
- }
1116
- // COMPAT: Certain browsers don't handle the selection updates
1117
- // properly. In Chrome, the selection isn't properly extended.
1118
- // And in Firefox, the selection isn't properly collapsed.
1119
- // (2017/10/17)
1120
- if (Hotkeys.isMoveLineBackward(nativeEvent)) {
1121
- event.preventDefault();
1122
- slate.Editor.move(editor, { unit: 'line', reverse: true });
1123
- return;
1124
- }
1125
- if (Hotkeys.isMoveLineForward(nativeEvent)) {
1126
- event.preventDefault();
1127
- slate.Editor.move(editor, { unit: 'line' });
1128
- return;
1129
- }
1130
- if (Hotkeys.isExtendLineBackward(nativeEvent)) {
1131
- event.preventDefault();
1132
- slate.Editor.move(editor, {
1133
- unit: 'line',
1134
- edge: 'focus',
1135
- reverse: true,
1136
- });
1137
- return;
1138
- }
1139
- if (Hotkeys.isExtendLineForward(nativeEvent)) {
1140
- event.preventDefault();
1141
- slate.Editor.move(editor, { unit: 'line', edge: 'focus' });
1142
- return;
1143
- }
1144
- // COMPAT: If a void node is selected, or a zero-width text node
1145
- // adjacent to an inline is selected, we need to handle these
1146
- // hotkeys manually because browsers won't be able to skip over
1147
- // the void node with the zero-width space not being an empty
1148
- // string.
1149
- if (Hotkeys.isMoveBackward(nativeEvent)) {
1150
- event.preventDefault();
1151
- if (selection && slate.Range.isCollapsed(selection)) {
1152
- slate.Editor.move(editor, { reverse: true });
1153
- }
1154
- else {
1155
- slate.Editor.collapse(editor, { edge: 'start' });
1156
- }
1157
- return;
1158
- }
1159
- if (Hotkeys.isMoveForward(nativeEvent)) {
1160
- event.preventDefault();
1161
- if (selection && slate.Range.isCollapsed(selection)) {
1162
- slate.Editor.move(editor);
1163
- }
1164
- else {
1165
- slate.Editor.collapse(editor, { edge: 'end' });
1166
- }
1167
- return;
1168
- }
1169
- if (Hotkeys.isMoveWordBackward(nativeEvent)) {
1170
- event.preventDefault();
1171
- slate.Editor.move(editor, { unit: 'word', reverse: true });
1172
- return;
1173
- }
1174
- if (Hotkeys.isMoveWordForward(nativeEvent)) {
1175
- event.preventDefault();
1176
- slate.Editor.move(editor, { unit: 'word' });
1177
- return;
1178
- }
1179
- // COMPAT: Firefox doesn't support the `beforeinput` event, so we
1180
- // fall back to guessing at the input intention for hotkeys.
1181
- // COMPAT: In iOS, some of these hotkeys are handled in the
1182
- if (IS_FIREFOX) {
1183
- // We don't have a core behavior for these, but they change the
1184
- // DOM if we don't prevent them, so we have to.
1185
- if (Hotkeys.isBold(nativeEvent) ||
1186
- Hotkeys.isItalic(nativeEvent) ||
1187
- Hotkeys.isTransposeCharacter(nativeEvent)) {
1188
- event.preventDefault();
1189
- return;
1190
- }
1191
- if (Hotkeys.isSplitBlock(nativeEvent)) {
1192
- event.preventDefault();
1193
- editor.exec({ type: 'insert_break' });
1194
- return;
1195
- }
1196
- if (Hotkeys.isDeleteBackward(nativeEvent)) {
1197
- event.preventDefault();
1198
- if (selection && slate.Range.isExpanded(selection)) {
1199
- editor.exec({ type: 'delete_fragment' });
1200
- }
1201
- else {
1202
- editor.exec({ type: 'delete_backward', unit: 'character' });
1203
- }
1204
- return;
1205
- }
1206
- if (Hotkeys.isDeleteForward(nativeEvent)) {
1207
- event.preventDefault();
1208
- if (selection && slate.Range.isExpanded(selection)) {
1209
- editor.exec({ type: 'delete_fragment' });
1210
- }
1211
- else {
1212
- editor.exec({ type: 'delete_forward', unit: 'character' });
1213
- }
1214
- return;
1215
- }
1216
- if (Hotkeys.isDeleteLineBackward(nativeEvent)) {
1217
- event.preventDefault();
1218
- if (selection && slate.Range.isExpanded(selection)) {
1219
- editor.exec({ type: 'delete_fragment' });
1220
- }
1221
- else {
1222
- editor.exec({ type: 'delete_backward', unit: 'line' });
1223
- }
1224
- return;
1225
- }
1226
- if (Hotkeys.isDeleteLineForward(nativeEvent)) {
1227
- event.preventDefault();
1228
- if (selection && slate.Range.isExpanded(selection)) {
1229
- editor.exec({ type: 'delete_fragment' });
1230
- }
1231
- else {
1232
- editor.exec({ type: 'delete_forward', unit: 'line' });
1233
- }
1234
- return;
1235
- }
1236
- if (Hotkeys.isDeleteWordBackward(nativeEvent)) {
1237
- event.preventDefault();
1238
- if (selection && slate.Range.isExpanded(selection)) {
1239
- editor.exec({ type: 'delete_fragment' });
1240
- }
1241
- else {
1242
- editor.exec({ type: 'delete_backward', unit: 'word' });
1243
- }
1244
- return;
1245
- }
1246
- if (Hotkeys.isDeleteWordForward(nativeEvent)) {
1247
- event.preventDefault();
1248
- if (selection && slate.Range.isExpanded(selection)) {
1249
- editor.exec({ type: 'delete_fragment' });
1250
- }
1251
- else {
1252
- editor.exec({ type: 'delete_forward', unit: 'word' });
1253
- }
1254
- return;
1255
- }
1256
- }
1257
- }
1258
- }, [readOnly, attributes.onKeyDown]), onPaste: React.useCallback((event) => {
1259
- // COMPAT: Firefox doesn't support the `beforeinput` event, so we
1260
- // fall back to React's `onPaste` here instead.
1261
- if (IS_FIREFOX &&
1262
- !readOnly &&
1263
- hasEditableTarget(editor, event.target) &&
1264
- !isEventHandled(event, attributes.onPaste)) {
1265
- event.preventDefault();
1266
- editor.exec({
1267
- type: 'insert_data',
1268
- data: event.clipboardData,
1269
- });
1270
- }
1271
- }, [readOnly, attributes.onPaste]) }),
1272
- React__default.createElement(Children, { decorate: decorate, decorations: decorations, node: editor, renderElement: renderElement, renderLeaf: renderLeaf, selection: editor.selection }))));
1273
- };
1274
- /**
1275
- * A default memoized decorate function.
1276
- */
1277
- const defaultDecorate = () => [];
1278
- /**
1279
- * Check if two DOM range objects are equal.
1280
- */
1281
- const isRangeEqual = (a, b) => {
1282
- return ((a.startContainer === b.startContainer &&
1283
- a.startOffset === b.startOffset &&
1284
- a.endContainer === b.endContainer &&
1285
- a.endOffset === b.endOffset) ||
1286
- (a.startContainer === b.endContainer &&
1287
- a.startOffset === b.endOffset &&
1288
- a.endContainer === b.startContainer &&
1289
- a.endOffset === b.startOffset));
1290
- };
1291
- /**
1292
- * Check if the target is in the editor.
1293
- */
1294
- const hasTarget = (editor, target) => {
1295
- return isDOMNode(target) && ReactEditor.hasDOMNode(editor, target);
1296
- };
1297
- /**
1298
- * Check if the target is editable and in the editor.
1299
- */
1300
- const hasEditableTarget = (editor, target) => {
1301
- return (isDOMNode(target) &&
1302
- ReactEditor.hasDOMNode(editor, target, { editable: true }));
1303
- };
1304
- /**
1305
- * Check if an event is overrided by a handler.
1306
- */
1307
- const isEventHandled = (event, handler) => {
1308
- if (!handler) {
1309
- return false;
1310
- }
1311
- handler(event);
1312
- return event.isDefaultPrevented() || event.isPropagationStopped();
1313
- };
1314
- /**
1315
- * Check if a DOM event is overrided by a handler.
1316
- */
1317
- const isDOMEventHandled = (event, handler) => {
1318
- if (!handler) {
1319
- return false;
1320
- }
1321
- handler(event);
1322
- return event.defaultPrevented;
1323
- };
1324
- /**
1325
- * Set the currently selected fragment to the clipboard.
1326
- */
1327
- const setFragmentData = (dataTransfer, editor) => {
1328
- const { selection } = editor;
1329
- if (!selection) {
1330
- return;
1331
- }
1332
- const [start, end] = slate.Range.edges(selection);
1333
- const startVoid = slate.Editor.void(editor, { at: start.path });
1334
- const endVoid = slate.Editor.void(editor, { at: end.path });
1335
- if (slate.Range.isCollapsed(selection) && !startVoid) {
1336
- return;
1337
- }
1338
- // Create a fake selection so that we can add a Base64-encoded copy of the
1339
- // fragment to the HTML, to decode on future pastes.
1340
- const domRange = ReactEditor.toDOMRange(editor, selection);
1341
- let contents = domRange.cloneContents();
1342
- let attach = contents.childNodes[0];
1343
- // Make sure attach is non-empty, since empty nodes will not get copied.
1344
- contents.childNodes.forEach(node => {
1345
- if (node.textContent && node.textContent.trim() !== '') {
1346
- attach = node;
1347
- }
1348
- });
1349
- // COMPAT: If the end node is a void node, we need to move the end of the
1350
- // range from the void node's spacer span, to the end of the void node's
1351
- // content, since the spacer is before void's content in the DOM.
1352
- if (endVoid) {
1353
- const [voidNode] = endVoid;
1354
- const r = domRange.cloneRange();
1355
- const domNode = ReactEditor.toDOMNode(editor, voidNode);
1356
- r.setEndAfter(domNode);
1357
- contents = r.cloneContents();
1358
- }
1359
- // COMPAT: If the start node is a void node, we need to attach the encoded
1360
- // fragment to the void node's content node instead of the spacer, because
1361
- // attaching it to empty `<div>/<span>` nodes will end up having it erased by
1362
- // most browsers. (2018/04/27)
1363
- if (startVoid) {
1364
- attach = contents.querySelector('[data-slate-spacer]');
1365
- }
1366
- // Remove any zero-width space spans from the cloned DOM so that they don't
1367
- // show up elsewhere when pasted.
1368
- Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(zw => {
1369
- const isNewline = zw.getAttribute('data-slate-zero-width') === 'n';
1370
- zw.textContent = isNewline ? '\n' : '';
1371
- });
1372
- // Set a `data-slate-fragment` attribute on a non-empty node, so it shows up
1373
- // in the HTML, and can be used for intra-Slate pasting. If it's a text
1374
- // node, wrap it in a `<span>` so we have something to set an attribute on.
1375
- if (isDOMText(attach)) {
1376
- const span = document.createElement('span');
1377
- // COMPAT: In Chrome and Safari, if we don't add the `white-space` style
1378
- // then leading and trailing spaces will be ignored. (2017/09/21)
1379
- span.style.whiteSpace = 'pre';
1380
- span.appendChild(attach);
1381
- contents.appendChild(span);
1382
- attach = span;
1383
- }
1384
- const fragment = slate.Node.fragment(editor, selection);
1385
- const string = JSON.stringify(fragment);
1386
- const encoded = window.btoa(encodeURIComponent(string));
1387
- attach.setAttribute('data-slate-fragment', encoded);
1388
- dataTransfer.setData('application/x-slate-fragment', encoded);
1389
- // Add the content to a <div> so that we can get its inner HTML.
1390
- const div = document.createElement('div');
1391
- div.appendChild(contents);
1392
- dataTransfer.setData('text/html', div.innerHTML);
1393
- dataTransfer.setData('text/plain', getPlainText(div));
1394
- };
1395
219
  /**
1396
220
  * Get a plaintext representation of the content of a node, accounting for block
1397
221
  * elements which get a newline appended.
1398
- */
1399
- const getPlainText = (domNode) => {
1400
- let text = '';
1401
- if (isDOMText(domNode) && domNode.nodeValue) {
1402
- return domNode.nodeValue;
1403
- }
1404
- if (isDOMElement(domNode)) {
1405
- for (const childNode of Array.from(domNode.childNodes)) {
1406
- text += getPlainText(childNode);
1407
- }
1408
- const display = getComputedStyle(domNode).getPropertyValue('display');
1409
- if (display === 'block' || display === 'list' || domNode.tagName === 'BR') {
1410
- text += '\n';
1411
- }
1412
- }
1413
- return text;
1414
- };
222
+ *
223
+ * The domNode must be attached to the DOM.
224
+ */
1415
225
 
1416
- function _arrayWithoutHoles(arr) {
1417
- if (Array.isArray(arr)) {
1418
- for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
1419
- arr2[i] = arr[i];
1420
- }
226
+ var getPlainText = domNode => {
227
+ var text = '';
1421
228
 
1422
- return arr2;
229
+ if (isDOMText(domNode) && domNode.nodeValue) {
230
+ return domNode.nodeValue;
1423
231
  }
1424
- }
1425
-
1426
- var arrayWithoutHoles = _arrayWithoutHoles;
1427
232
 
1428
- function _iterableToArray(iter) {
1429
- if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
1430
- }
233
+ if (isDOMElement(domNode)) {
234
+ for (var childNode of Array.from(domNode.childNodes)) {
235
+ text += getPlainText(childNode);
236
+ }
1431
237
 
1432
- var iterableToArray = _iterableToArray;
238
+ var display = getComputedStyle(domNode).getPropertyValue('display');
1433
239
 
1434
- function _nonIterableSpread() {
1435
- throw new TypeError("Invalid attempt to spread non-iterable instance");
1436
- }
240
+ if (display === 'block' || display === 'list' || domNode.tagName === 'BR') {
241
+ text += '\n';
242
+ }
243
+ }
1437
244
 
1438
- var nonIterableSpread = _nonIterableSpread;
245
+ return text;
246
+ };
1439
247
 
1440
- function _toConsumableArray(arr) {
1441
- return arrayWithoutHoles(arr) || iterableToArray(arr) || nonIterableSpread();
1442
- }
248
+ var ReactEditor = {
249
+ /**
250
+ * Find a key for a Slate node.
251
+ */
252
+ findKey(editor, node) {
253
+ var key = NODE_TO_KEY.get(node);
1443
254
 
1444
- var toConsumableArray = _toConsumableArray;
255
+ if (!key) {
256
+ key = new Key();
257
+ NODE_TO_KEY.set(node, key);
258
+ }
1445
259
 
1446
- function _classCallCheck(instance, Constructor) {
1447
- if (!(instance instanceof Constructor)) {
1448
- throw new TypeError("Cannot call a class as a function");
1449
- }
1450
- }
1451
-
1452
- var classCallCheck = _classCallCheck;
1453
-
1454
- /**
1455
- * An auto-incrementing identifier for keys.
1456
- */
1457
- var n = 0;
1458
- /**
1459
- * A class that keeps track of a key string. We use a full class here because we
1460
- * want to be able to use them as keys in `WeakMap` objects.
1461
- */
1462
-
1463
- var Key = function Key() {
1464
- classCallCheck(this, Key);
1465
-
1466
- this.id = "".concat(n++);
1467
- };
1468
-
1469
- var ReactEditor = {
1470
- /**
1471
- * Find a key for a Slate node.
1472
- */
1473
- findKey: function findKey(editor, node) {
1474
- var key = NODE_TO_KEY.get(node);
1475
-
1476
- if (!key) {
1477
- key = new Key();
1478
- NODE_TO_KEY.set(node, key);
1479
- }
1480
-
1481
- return key;
1482
- },
260
+ return key;
261
+ },
1483
262
 
1484
263
  /**
1485
264
  * Find the path of Slate node.
1486
265
  */
1487
- findPath: function findPath(editor, node) {
266
+ findPath(editor, node) {
1488
267
  var path = [];
1489
268
  var child = node;
1490
269
 
@@ -1515,21 +294,21 @@ var ReactEditor = {
1515
294
  /**
1516
295
  * Check if the editor is focused.
1517
296
  */
1518
- isFocused: function isFocused(editor) {
297
+ isFocused(editor) {
1519
298
  return !!IS_FOCUSED.get(editor);
1520
299
  },
1521
300
 
1522
301
  /**
1523
302
  * Check if the editor is in read-only mode.
1524
303
  */
1525
- isReadOnly: function isReadOnly(editor) {
304
+ isReadOnly(editor) {
1526
305
  return !!IS_READ_ONLY.get(editor);
1527
306
  },
1528
307
 
1529
308
  /**
1530
309
  * Blur the editor.
1531
310
  */
1532
- blur: function blur(editor) {
311
+ blur(editor) {
1533
312
  var el = ReactEditor.toDOMNode(editor, editor);
1534
313
  IS_FOCUSED.set(editor, false);
1535
314
 
@@ -1541,7 +320,7 @@ var ReactEditor = {
1541
320
  /**
1542
321
  * Focus the editor.
1543
322
  */
1544
- focus: function focus(editor) {
323
+ focus(editor) {
1545
324
  var el = ReactEditor.toDOMNode(editor, editor);
1546
325
  IS_FOCUSED.set(editor, true);
1547
326
 
@@ -1555,8 +334,10 @@ var ReactEditor = {
1555
334
  /**
1556
335
  * Deselect the editor.
1557
336
  */
1558
- deselect: function deselect(editor) {
1559
- var selection = editor.selection;
337
+ deselect(editor) {
338
+ var {
339
+ selection
340
+ } = editor;
1560
341
  var domSelection = window.getSelection();
1561
342
 
1562
343
  if (domSelection && domSelection.rangeCount > 0) {
@@ -1564,42 +345,57 @@ var ReactEditor = {
1564
345
  }
1565
346
 
1566
347
  if (selection) {
1567
- slate.Editor.deselect(editor);
348
+ slate.Transforms.deselect(editor);
1568
349
  }
1569
350
  },
1570
351
 
1571
352
  /**
1572
353
  * Check if a DOM node is within the editor.
1573
354
  */
1574
- hasDOMNode: function hasDOMNode(editor, target) {
355
+ hasDOMNode(editor, target) {
1575
356
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1576
- var _options$editable = options.editable,
1577
- editable = _options$editable === void 0 ? false : _options$editable;
1578
- var el = ReactEditor.toDOMNode(editor, editor);
1579
- var element; // COMPAT: In Firefox, reading `target.nodeType` will throw an error if
357
+ var {
358
+ editable = false
359
+ } = options;
360
+ var editorEl = ReactEditor.toDOMNode(editor, editor);
361
+ var targetEl; // COMPAT: In Firefox, reading `target.nodeType` will throw an error if
1580
362
  // target is originating from an internal "restricted" element (e.g. a
1581
363
  // stepper arrow on a number input). (2018/05/04)
1582
364
  // https://github.com/ianstormtaylor/slate/issues/1819
1583
365
 
1584
366
  try {
1585
- element = isDOMElement(target) ? target : target.parentElement;
367
+ targetEl = isDOMElement(target) ? target : target.parentElement;
1586
368
  } catch (err) {
1587
369
  if (!err.message.includes('Permission denied to access property "nodeType"')) {
1588
370
  throw err;
1589
371
  }
1590
372
  }
1591
373
 
1592
- if (!element) {
374
+ if (!targetEl) {
1593
375
  return false;
1594
376
  }
1595
377
 
1596
- return element.closest("[data-slate-editor]") === el && (!editable || el.isContentEditable);
378
+ return targetEl.closest("[data-slate-editor]") === editorEl && (!editable || targetEl.isContentEditable || !!targetEl.getAttribute('data-slate-zero-width'));
379
+ },
380
+
381
+ /**
382
+ * Insert data from a `DataTransfer` into the editor.
383
+ */
384
+ insertData(editor, data) {
385
+ editor.insertData(data);
386
+ },
387
+
388
+ /**
389
+ * Sets data from the currently selected fragment on a `DataTransfer`.
390
+ */
391
+ setFragmentData(editor, data) {
392
+ editor.setFragmentData(data);
1597
393
  },
1598
394
 
1599
395
  /**
1600
396
  * Find the native DOM element from a Slate node.
1601
397
  */
1602
- toDOMNode: function toDOMNode(editor, node) {
398
+ toDOMNode(editor, node) {
1603
399
  var domNode = slate.Editor.isEditor(node) ? EDITOR_TO_ELEMENT.get(editor) : KEY_TO_ELEMENT.get(ReactEditor.findKey(editor, node));
1604
400
 
1605
401
  if (!domNode) {
@@ -1612,16 +408,13 @@ var ReactEditor = {
1612
408
  /**
1613
409
  * Find a native DOM selection point from a Slate point.
1614
410
  */
1615
- toDOMPoint: function toDOMPoint(editor, point) {
1616
- var _Editor$node = slate.Editor.node(editor, point.path),
1617
- _Editor$node2 = slicedToArray(_Editor$node, 1),
1618
- node = _Editor$node2[0];
1619
-
411
+ toDOMPoint(editor, point) {
412
+ var [node] = slate.Editor.node(editor, point.path);
1620
413
  var el = ReactEditor.toDOMNode(editor, node);
1621
414
  var domPoint; // If we're inside a void node, force the offset to 0, otherwise the zero
1622
415
  // width spacing character will result in an incorrect offset of 1
1623
416
 
1624
- if (slate.Editor["void"](editor, {
417
+ if (slate.Editor.void(editor, {
1625
418
  at: point
1626
419
  })) {
1627
420
  point = {
@@ -1637,15 +430,16 @@ var ReactEditor = {
1637
430
  var texts = Array.from(el.querySelectorAll(selector));
1638
431
  var start = 0;
1639
432
 
1640
- for (var _i = 0, _texts = texts; _i < _texts.length; _i++) {
1641
- var text = _texts[_i];
433
+ for (var text of texts) {
1642
434
  var domNode = text.childNodes[0];
1643
435
 
1644
436
  if (domNode == null || domNode.textContent == null) {
1645
437
  continue;
1646
438
  }
1647
439
 
1648
- var length = domNode.textContent.length;
440
+ var {
441
+ length
442
+ } = domNode.textContent;
1649
443
  var attr = text.getAttribute('data-slate-length');
1650
444
  var trueLength = attr == null ? length : parseInt(attr, 10);
1651
445
  var end = start + trueLength;
@@ -1669,23 +463,33 @@ var ReactEditor = {
1669
463
  /**
1670
464
  * Find a native DOM range from a Slate `range`.
1671
465
  */
1672
- toDOMRange: function toDOMRange(editor, range) {
1673
- var anchor = range.anchor,
1674
- focus = range.focus;
466
+ toDOMRange(editor, range) {
467
+ var {
468
+ anchor,
469
+ focus
470
+ } = range;
471
+ var isBackward = slate.Range.isBackward(range);
1675
472
  var domAnchor = ReactEditor.toDOMPoint(editor, anchor);
1676
473
  var domFocus = slate.Range.isCollapsed(range) ? domAnchor : ReactEditor.toDOMPoint(editor, focus);
1677
474
  var domRange = window.document.createRange();
1678
- var start = slate.Range.isBackward(range) ? domFocus : domAnchor;
1679
- var end = slate.Range.isBackward(range) ? domAnchor : domFocus;
1680
- domRange.setStart(start[0], start[1]);
1681
- domRange.setEnd(end[0], end[1]);
475
+ var [startNode, startOffset] = isBackward ? domFocus : domAnchor;
476
+ var [endNode, endOffset] = isBackward ? domAnchor : domFocus; // A slate Point at zero-width Leaf always has an offset of 0 but a native DOM selection at
477
+ // zero-width node has an offset of 1 so we have to check if we are in a zero-width node and
478
+ // adjust the offset accordingly.
479
+
480
+ var startEl = isDOMElement(startNode) ? startNode : startNode.parentElement;
481
+ var isStartAtZeroWidth = !!startEl.getAttribute('data-slate-zero-width');
482
+ var endEl = isDOMElement(endNode) ? endNode : endNode.parentElement;
483
+ var isEndAtZeroWidth = !!endEl.getAttribute('data-slate-zero-width');
484
+ domRange.setStart(startNode, isStartAtZeroWidth ? 1 : startOffset);
485
+ domRange.setEnd(endNode, isEndAtZeroWidth ? 1 : endOffset);
1682
486
  return domRange;
1683
487
  },
1684
488
 
1685
489
  /**
1686
490
  * Find a Slate node from a native DOM `element`.
1687
491
  */
1688
- toSlateNode: function toSlateNode(editor, domNode) {
492
+ toSlateNode(editor, domNode) {
1689
493
  var domEl = isDOMElement(domNode) ? domNode : domNode.parentElement;
1690
494
 
1691
495
  if (domEl && !domEl.hasAttribute('data-slate-node')) {
@@ -1704,15 +508,16 @@ var ReactEditor = {
1704
508
  /**
1705
509
  * Get the target range from a DOM `event`.
1706
510
  */
1707
- findEventRange: function findEventRange(editor, event) {
511
+ findEventRange(editor, event) {
1708
512
  if ('nativeEvent' in event) {
1709
513
  event = event.nativeEvent;
1710
514
  }
1711
515
 
1712
- var _event = event,
1713
- x = _event.clientX,
1714
- y = _event.clientY,
1715
- target = _event.target;
516
+ var {
517
+ clientX: x,
518
+ clientY: y,
519
+ target
520
+ } = event;
1716
521
 
1717
522
  if (x == null || y == null) {
1718
523
  throw new Error("Cannot resolve a Slate range from a DOM event: ".concat(event));
@@ -1740,8 +545,9 @@ var ReactEditor = {
1740
545
 
1741
546
 
1742
547
  var domRange;
1743
- var _window = window,
1744
- document = _window.document; // COMPAT: In Firefox, `caretRangeFromPoint` doesn't exist. (2016/07/25)
548
+ var {
549
+ document
550
+ } = window; // COMPAT: In Firefox, `caretRangeFromPoint` doesn't exist. (2016/07/25)
1745
551
 
1746
552
  if (document.caretRangeFromPoint) {
1747
553
  domRange = document.caretRangeFromPoint(x, y);
@@ -1767,12 +573,8 @@ var ReactEditor = {
1767
573
  /**
1768
574
  * Find a Slate point from a DOM selection's `domNode` and `domOffset`.
1769
575
  */
1770
- toSlatePoint: function toSlatePoint(editor, domPoint) {
1771
- var _normalizeDOMPoint = normalizeDOMPoint(domPoint),
1772
- _normalizeDOMPoint2 = slicedToArray(_normalizeDOMPoint, 2),
1773
- nearestNode = _normalizeDOMPoint2[0],
1774
- nearestOffset = _normalizeDOMPoint2[1];
1775
-
576
+ toSlatePoint(editor, domPoint) {
577
+ var [nearestNode, nearestOffset] = normalizeDOMPoint(domPoint);
1776
578
  var parentNode = nearestNode.parentNode;
1777
579
  var textNode = null;
1778
580
  var offset = 0;
@@ -1789,8 +591,8 @@ var ReactEditor = {
1789
591
  range.setStart(textNode, 0);
1790
592
  range.setEnd(nearestNode, nearestOffset);
1791
593
  var contents = range.cloneContents();
1792
- var removals = [].concat(toConsumableArray(contents.querySelectorAll('[data-slate-zero-width]')), toConsumableArray(contents.querySelectorAll('[contenteditable=false]')));
1793
- removals.forEach(function (el) {
594
+ var removals = [...contents.querySelectorAll('[data-slate-zero-width]'), ...contents.querySelectorAll('[contenteditable=false]')];
595
+ removals.forEach(el => {
1794
596
  el.parentNode.removeChild(el);
1795
597
  }); // COMPAT: Edge has a bug where Range.prototype.toString() will
1796
598
  // convert \n into \r\n. The bug causes a loop when slate-react
@@ -1829,15 +631,15 @@ var ReactEditor = {
1829
631
  var slateNode = ReactEditor.toSlateNode(editor, textNode);
1830
632
  var path = ReactEditor.findPath(editor, slateNode);
1831
633
  return {
1832
- path: path,
1833
- offset: offset
634
+ path,
635
+ offset
1834
636
  };
1835
637
  },
1836
638
 
1837
639
  /**
1838
640
  * Find a Slate range from a DOM range or selection.
1839
641
  */
1840
- toSlateRange: function toSlateRange(editor, domRange) {
642
+ toSlateRange(editor, domRange) {
1841
643
  var el = domRange instanceof Selection ? domRange.anchorNode : domRange.startContainer;
1842
644
  var anchorNode;
1843
645
  var anchorOffset;
@@ -1868,10 +670,11 @@ var ReactEditor = {
1868
670
  var anchor = ReactEditor.toSlatePoint(editor, [anchorNode, anchorOffset]);
1869
671
  var focus = isCollapsed ? anchor : ReactEditor.toSlatePoint(editor, [focusNode, focusOffset]);
1870
672
  return {
1871
- anchor: anchor,
1872
- focus: focus
673
+ anchor,
674
+ focus
1873
675
  };
1874
676
  }
677
+
1875
678
  };
1876
679
 
1877
680
  /**
@@ -1883,58 +686,127 @@ var FocusedContext = React.createContext(false);
1883
686
  * Get the current `focused` state of the editor.
1884
687
  */
1885
688
 
1886
- var useFocused = function useFocused() {
689
+ var useFocused = () => {
1887
690
  return React.useContext(FocusedContext);
1888
691
  };
1889
692
 
693
+ /**
694
+ * A React context for sharing the editor object.
695
+ */
696
+
697
+ var EditorContext = React.createContext(null);
698
+ /**
699
+ * Get the current editor object from the React context.
700
+ */
701
+
702
+ var useEditor = () => {
703
+ var editor = React.useContext(EditorContext);
704
+
705
+ if (!editor) {
706
+ throw new Error("The `useEditor` hook must be used inside the <Slate> component's context.");
707
+ }
708
+
709
+ return editor;
710
+ };
711
+
712
+ /**
713
+ * A React context for sharing the editor object, in a way that re-renders the
714
+ * context whenever changes occur.
715
+ */
716
+
717
+ var SlateContext = React.createContext(null);
718
+ /**
719
+ * Get the current editor object from the React context.
720
+ */
721
+
722
+ var useSlate = () => {
723
+ var context = React.useContext(SlateContext);
724
+
725
+ if (!context) {
726
+ throw new Error("The `useSlate` hook must be used inside the <SlateProvider> component's context.");
727
+ }
728
+
729
+ var [editor] = context;
730
+ return editor;
731
+ };
732
+
1890
733
  /**
1891
734
  * A wrapper around the provider to handle `onChange` events, because the editor
1892
735
  * is a mutable singleton so it won't ever register as "changed" otherwise.
1893
- */
1894
- const Slate = (props) => {
1895
- const { editor, children, onChange, value, ...rest } = props;
1896
- const [key, setKey] = React.useState(0);
1897
- const context = React.useMemo(() => {
1898
- editor.children = value;
1899
- Object.assign(editor, rest);
1900
- return [editor];
1901
- }, [key, value, ...Object.values(rest)]);
1902
- const onContextChange = React.useCallback(() => {
1903
- onChange(editor.children);
1904
- setKey(key + 1);
1905
- }, [key, onChange]);
1906
- EDITOR_TO_ON_CHANGE.set(editor, onContextChange);
1907
- return (React__default.createElement(SlateContext.Provider, { value: context },
1908
- React__default.createElement(EditorContext.Provider, { value: editor },
1909
- React__default.createElement(FocusedContext.Provider, { value: ReactEditor.isFocused(editor) }, children))));
736
+ */
737
+
738
+ var Slate = props => {
739
+ var {
740
+ editor,
741
+ children,
742
+ onChange,
743
+ value
744
+ } = props,
745
+ rest = _objectWithoutProperties(props, ["editor", "children", "onChange", "value"]);
746
+
747
+ var [key, setKey] = React.useState(0);
748
+ var context = React.useMemo(() => {
749
+ editor.children = value;
750
+ Object.assign(editor, rest);
751
+ return [editor];
752
+ }, [key, value, ...Object.values(rest)]);
753
+ var onContextChange = React.useCallback(() => {
754
+ onChange(editor.children);
755
+ setKey(key + 1);
756
+ }, [key, onChange]);
757
+ EDITOR_TO_ON_CHANGE.set(editor, onContextChange);
758
+ React.useEffect(() => {
759
+ return () => {
760
+ EDITOR_TO_ON_CHANGE.set(editor, () => {});
761
+ };
762
+ }, []);
763
+ return React__default.createElement(SlateContext.Provider, {
764
+ value: context
765
+ }, React__default.createElement(EditorContext.Provider, {
766
+ value: editor
767
+ }, React__default.createElement(FocusedContext.Provider, {
768
+ value: ReactEditor.isFocused(editor)
769
+ }, children)));
1910
770
  };
1911
771
 
1912
- var ReactCommand = {
1913
- /**
1914
- * Check if a value is a `ReactCommand` object.
1915
- */
1916
- isReactCommand: function isReactCommand(value) {
1917
- if (slate.Command.isCommand(value)) {
1918
- switch (value.type) {
1919
- case 'insert_data':
1920
- return value.data instanceof DataTransfer;
1921
- }
1922
- }
772
+ /**
773
+ * A React context for sharing the `readOnly` state of the editor.
774
+ */
1923
775
 
1924
- return false;
1925
- }
776
+ var ReadOnlyContext = React.createContext(false);
777
+ /**
778
+ * Get the current `readOnly` state of the editor.
779
+ */
780
+
781
+ var useReadOnly = () => {
782
+ return React.useContext(ReadOnlyContext);
783
+ };
784
+
785
+ /**
786
+ * A React context for sharing the `selected` state of an element.
787
+ */
788
+
789
+ var SelectedContext = React.createContext(false);
790
+ /**
791
+ * Get the current `selected` state of an element.
792
+ */
793
+
794
+ var useSelected = () => {
795
+ return React.useContext(SelectedContext);
1926
796
  };
1927
797
 
1928
798
  /**
1929
799
  * `withReact` adds React and DOM specific behaviors to the editor.
1930
800
  */
1931
801
 
1932
- var withReact = function withReact(editor) {
1933
- var apply = editor.apply,
1934
- exec = editor.exec,
1935
- onChange = editor.onChange;
802
+ var withReact = editor => {
803
+ var e = editor;
804
+ var {
805
+ apply,
806
+ onChange
807
+ } = e;
1936
808
 
1937
- editor.apply = function (op) {
809
+ e.apply = op => {
1938
810
  var matches = [];
1939
811
 
1940
812
  switch (op.type) {
@@ -1942,34 +814,11 @@ var withReact = function withReact(editor) {
1942
814
  case 'remove_text':
1943
815
  case 'set_node':
1944
816
  {
1945
- var _iteratorNormalCompletion = true;
1946
- var _didIteratorError = false;
1947
- var _iteratorError = undefined;
1948
-
1949
- try {
1950
- for (var _iterator = slate.Editor.levels(editor, {
1951
- at: op.path
1952
- })[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
1953
- var _step$value = slicedToArray(_step.value, 2),
1954
- node = _step$value[0],
1955
- path = _step$value[1];
1956
-
1957
- var key = ReactEditor.findKey(editor, node);
1958
- matches.push([path, key]);
1959
- }
1960
- } catch (err) {
1961
- _didIteratorError = true;
1962
- _iteratorError = err;
1963
- } finally {
1964
- try {
1965
- if (!_iteratorNormalCompletion && _iterator["return"] != null) {
1966
- _iterator["return"]();
1967
- }
1968
- } finally {
1969
- if (_didIteratorError) {
1970
- throw _iteratorError;
1971
- }
1972
- }
817
+ for (var [node, path] of slate.Editor.levels(e, {
818
+ at: op.path
819
+ })) {
820
+ var key = ReactEditor.findKey(e, node);
821
+ matches.push([path, key]);
1973
822
  }
1974
823
 
1975
824
  break;
@@ -1980,35 +829,12 @@ var withReact = function withReact(editor) {
1980
829
  case 'merge_node':
1981
830
  case 'split_node':
1982
831
  {
1983
- var _iteratorNormalCompletion2 = true;
1984
- var _didIteratorError2 = false;
1985
- var _iteratorError2 = undefined;
832
+ for (var [_node, _path] of slate.Editor.levels(e, {
833
+ at: slate.Path.parent(op.path)
834
+ })) {
835
+ var _key = ReactEditor.findKey(e, _node);
1986
836
 
1987
- try {
1988
- for (var _iterator2 = slate.Editor.levels(editor, {
1989
- at: slate.Path.parent(op.path)
1990
- })[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
1991
- var _step2$value = slicedToArray(_step2.value, 2),
1992
- _node = _step2$value[0],
1993
- _path = _step2$value[1];
1994
-
1995
- var _key = ReactEditor.findKey(editor, _node);
1996
-
1997
- matches.push([_path, _key]);
1998
- }
1999
- } catch (err) {
2000
- _didIteratorError2 = true;
2001
- _iteratorError2 = err;
2002
- } finally {
2003
- try {
2004
- if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) {
2005
- _iterator2["return"]();
2006
- }
2007
- } finally {
2008
- if (_didIteratorError2) {
2009
- throw _iteratorError2;
2010
- }
2011
- }
837
+ matches.push([_path, _key]);
2012
838
  }
2013
839
 
2014
840
  break;
@@ -2017,78 +843,133 @@ var withReact = function withReact(editor) {
2017
843
 
2018
844
  apply(op);
2019
845
 
2020
- for (var _i = 0, _matches = matches; _i < _matches.length; _i++) {
2021
- var _matches$_i = slicedToArray(_matches[_i], 2),
2022
- _path2 = _matches$_i[0],
2023
- _key2 = _matches$_i[1];
2024
-
2025
- var _Editor$node = slate.Editor.node(editor, _path2),
2026
- _Editor$node2 = slicedToArray(_Editor$node, 1),
2027
- _node2 = _Editor$node2[0];
2028
-
846
+ for (var [_path2, _key2] of matches) {
847
+ var [_node2] = slate.Editor.node(e, _path2);
2029
848
  NODE_TO_KEY.set(_node2, _key2);
2030
849
  }
2031
850
  };
2032
851
 
2033
- editor.exec = function (command) {
2034
- if (ReactCommand.isReactCommand(command) && command.type === 'insert_data') {
2035
- var data = command.data;
2036
- var fragment = data.getData('application/x-slate-fragment');
852
+ e.setFragmentData = data => {
853
+ var {
854
+ selection
855
+ } = e;
2037
856
 
2038
- if (fragment) {
2039
- var decoded = decodeURIComponent(window.atob(fragment));
2040
- var parsed = JSON.parse(decoded);
2041
- slate.Editor.insertFragment(editor, parsed);
2042
- return;
857
+ if (!selection) {
858
+ return;
859
+ }
860
+
861
+ var [start, end] = slate.Range.edges(selection);
862
+ var startVoid = slate.Editor.void(e, {
863
+ at: start.path
864
+ });
865
+ var endVoid = slate.Editor.void(e, {
866
+ at: end.path
867
+ });
868
+
869
+ if (slate.Range.isCollapsed(selection) && !startVoid) {
870
+ return;
871
+ } // Create a fake selection so that we can add a Base64-encoded copy of the
872
+ // fragment to the HTML, to decode on future pastes.
873
+
874
+
875
+ var domRange = ReactEditor.toDOMRange(e, selection);
876
+ var contents = domRange.cloneContents();
877
+ var attach = contents.childNodes[0]; // Make sure attach is non-empty, since empty nodes will not get copied.
878
+
879
+ contents.childNodes.forEach(node => {
880
+ if (node.textContent && node.textContent.trim() !== '') {
881
+ attach = node;
2043
882
  }
883
+ }); // COMPAT: If the end node is a void node, we need to move the end of the
884
+ // range from the void node's spacer span, to the end of the void node's
885
+ // content, since the spacer is before void's content in the DOM.
886
+
887
+ if (endVoid) {
888
+ var [voidNode] = endVoid;
889
+ var r = domRange.cloneRange();
890
+ var domNode = ReactEditor.toDOMNode(e, voidNode);
891
+ r.setEndAfter(domNode);
892
+ contents = r.cloneContents();
893
+ } // COMPAT: If the start node is a void node, we need to attach the encoded
894
+ // fragment to the void node's content node instead of the spacer, because
895
+ // attaching it to empty `<div>/<span>` nodes will end up having it erased by
896
+ // most browsers. (2018/04/27)
897
+
898
+
899
+ if (startVoid) {
900
+ attach = contents.querySelector('[data-slate-spacer]');
901
+ } // Remove any zero-width space spans from the cloned DOM so that they don't
902
+ // show up elsewhere when pasted.
903
+
904
+
905
+ Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(zw => {
906
+ var isNewline = zw.getAttribute('data-slate-zero-width') === 'n';
907
+ zw.textContent = isNewline ? '\n' : '';
908
+ }); // Set a `data-slate-fragment` attribute on a non-empty node, so it shows up
909
+ // in the HTML, and can be used for intra-Slate pasting. If it's a text
910
+ // node, wrap it in a `<span>` so we have something to set an attribute on.
911
+
912
+ if (isDOMText(attach)) {
913
+ var span = document.createElement('span'); // COMPAT: In Chrome and Safari, if we don't add the `white-space` style
914
+ // then leading and trailing spaces will be ignored. (2017/09/21)
915
+
916
+ span.style.whiteSpace = 'pre';
917
+ span.appendChild(attach);
918
+ contents.appendChild(span);
919
+ attach = span;
920
+ }
2044
921
 
2045
- var text = data.getData('text/plain');
922
+ var fragment = e.getFragment();
923
+ var string = JSON.stringify(fragment);
924
+ var encoded = window.btoa(encodeURIComponent(string));
925
+ attach.setAttribute('data-slate-fragment', encoded);
926
+ data.setData('application/x-slate-fragment', encoded); // Add the content to a <div> so that we can get its inner HTML.
927
+
928
+ var div = document.createElement('div');
929
+ div.appendChild(contents);
930
+ div.setAttribute('hidden', 'true');
931
+ document.body.appendChild(div);
932
+ data.setData('text/html', div.innerHTML);
933
+ data.setData('text/plain', getPlainText(div));
934
+ document.body.removeChild(div);
935
+ };
2046
936
 
2047
- if (text) {
2048
- var lines = text.split('\n');
2049
- var split = false;
2050
- var _iteratorNormalCompletion3 = true;
2051
- var _didIteratorError3 = false;
2052
- var _iteratorError3 = undefined;
937
+ e.insertData = data => {
938
+ var fragment = data.getData('application/x-slate-fragment');
2053
939
 
2054
- try {
2055
- for (var _iterator3 = lines[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
2056
- var line = _step3.value;
940
+ if (fragment) {
941
+ var decoded = decodeURIComponent(window.atob(fragment));
942
+ var parsed = JSON.parse(decoded);
943
+ slate.Transforms.insertFragment(e, parsed);
944
+ return;
945
+ }
2057
946
 
2058
- if (split) {
2059
- slate.Editor.splitNodes(editor);
2060
- }
947
+ var text = data.getData('text/plain');
2061
948
 
2062
- slate.Editor.insertText(editor, line);
2063
- split = true;
2064
- }
2065
- } catch (err) {
2066
- _didIteratorError3 = true;
2067
- _iteratorError3 = err;
2068
- } finally {
2069
- try {
2070
- if (!_iteratorNormalCompletion3 && _iterator3["return"] != null) {
2071
- _iterator3["return"]();
2072
- }
2073
- } finally {
2074
- if (_didIteratorError3) {
2075
- throw _iteratorError3;
2076
- }
2077
- }
949
+ if (text) {
950
+ var lines = text.split(/\r\n|\r|\n/);
951
+ var split = false;
952
+
953
+ for (var line of lines) {
954
+ if (split) {
955
+ slate.Transforms.splitNodes(e, {
956
+ always: true
957
+ });
2078
958
  }
959
+
960
+ slate.Transforms.insertText(e, line);
961
+ split = true;
2079
962
  }
2080
- } else {
2081
- exec(command);
2082
963
  }
2083
964
  };
2084
965
 
2085
- editor.onChange = function () {
966
+ e.onChange = () => {
2086
967
  // COMPAT: React doesn't batch `setState` hook calls, which means that the
2087
968
  // children and selection can get out of sync for one render pass. So we
2088
969
  // have to use this unstable API to ensure it batches them. (2019/12/03)
2089
970
  // https://github.com/facebook/react/issues/14259#issuecomment-439702367
2090
- ReactDOM.unstable_batchedUpdates(function () {
2091
- var onContextChange = EDITOR_TO_ON_CHANGE.get(editor);
971
+ ReactDOM.unstable_batchedUpdates(() => {
972
+ var onContextChange = EDITOR_TO_ON_CHANGE.get(e);
2092
973
 
2093
974
  if (onContextChange) {
2094
975
  onContextChange();
@@ -2098,13 +979,1308 @@ var withReact = function withReact(editor) {
2098
979
  });
2099
980
  };
2100
981
 
2101
- return editor;
982
+ return e;
983
+ };
984
+
985
+ /**
986
+ * Leaf content strings.
987
+ */
988
+
989
+ var String = props => {
990
+ var {
991
+ isLast,
992
+ leaf,
993
+ parent,
994
+ text
995
+ } = props;
996
+ var editor = useEditor();
997
+ var path = ReactEditor.findPath(editor, text);
998
+ var parentPath = slate.Path.parent(path); // COMPAT: Render text inside void nodes with a zero-width space.
999
+ // So the node can contain selection but the text is not visible.
1000
+
1001
+ if (editor.isVoid(parent)) {
1002
+ return React__default.createElement(ZeroWidthString, {
1003
+ length: slate.Node.string(parent).length
1004
+ });
1005
+ } // COMPAT: If this is the last text node in an empty block, render a zero-
1006
+ // width space that will convert into a line break when copying and pasting
1007
+ // to support expected plain text.
1008
+
1009
+
1010
+ if (leaf.text === '' && parent.children[parent.children.length - 1] === text && !editor.isInline(parent) && slate.Editor.string(editor, parentPath) === '') {
1011
+ return React__default.createElement(ZeroWidthString, {
1012
+ isLineBreak: true
1013
+ });
1014
+ } // COMPAT: If the text is empty, it's because it's on the edge of an inline
1015
+ // node, so we render a zero-width space so that the selection can be
1016
+ // inserted next to it still.
1017
+
1018
+
1019
+ if (leaf.text === '') {
1020
+ return React__default.createElement(ZeroWidthString, null);
1021
+ } // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
1022
+ // so we need to add an extra trailing new lines to prevent that.
1023
+
1024
+
1025
+ if (isLast && leaf.text.slice(-1) === '\n') {
1026
+ return React__default.createElement(TextString, {
1027
+ isTrailing: true,
1028
+ text: leaf.text
1029
+ });
1030
+ }
1031
+
1032
+ return React__default.createElement(TextString, {
1033
+ text: leaf.text
1034
+ });
1035
+ };
1036
+ /**
1037
+ * Leaf strings with text in them.
1038
+ */
1039
+
1040
+
1041
+ var TextString = props => {
1042
+ var {
1043
+ text,
1044
+ isTrailing = false
1045
+ } = props;
1046
+ return React__default.createElement("span", {
1047
+ "data-slate-string": true
1048
+ }, text, isTrailing ? '\n' : null);
1049
+ };
1050
+ /**
1051
+ * Leaf strings without text, render as zero-width strings.
1052
+ */
1053
+
1054
+
1055
+ var ZeroWidthString = props => {
1056
+ var {
1057
+ length = 0,
1058
+ isLineBreak = false
1059
+ } = props;
1060
+ return React__default.createElement("span", {
1061
+ "data-slate-zero-width": isLineBreak ? 'n' : 'z',
1062
+ "data-slate-length": length
1063
+ }, '\uFEFF', isLineBreak ? React__default.createElement("br", null) : null);
1064
+ };
1065
+
1066
+ /**
1067
+ * Individual leaves in a text node with unique formatting.
1068
+ */
1069
+
1070
+ var Leaf = props => {
1071
+ var {
1072
+ leaf,
1073
+ isLast,
1074
+ text,
1075
+ parent,
1076
+ renderLeaf = props => React__default.createElement(DefaultLeaf, Object.assign({}, props))
1077
+ } = props;
1078
+ var children = React__default.createElement(String, {
1079
+ isLast: isLast,
1080
+ leaf: leaf,
1081
+ parent: parent,
1082
+ text: text
1083
+ });
1084
+
1085
+ if (leaf[PLACEHOLDER_SYMBOL]) {
1086
+ children = React__default.createElement(React__default.Fragment, null, React__default.createElement("span", {
1087
+ contentEditable: false,
1088
+ style: {
1089
+ pointerEvents: 'none',
1090
+ display: 'inline-block',
1091
+ verticalAlign: 'text-top',
1092
+ width: '0',
1093
+ maxWidth: '100%',
1094
+ whiteSpace: 'nowrap',
1095
+ opacity: '0.333'
1096
+ }
1097
+ }, leaf.placeholder), children);
1098
+ } // COMPAT: Having the `data-` attributes on these leaf elements ensures that
1099
+ // in certain misbehaving browsers they aren't weirdly cloned/destroyed by
1100
+ // contenteditable behaviors. (2019/05/08)
1101
+
1102
+
1103
+ var attributes = {
1104
+ 'data-slate-leaf': true
1105
+ };
1106
+ return renderLeaf({
1107
+ attributes,
1108
+ children,
1109
+ leaf,
1110
+ text
1111
+ });
1112
+ };
1113
+
1114
+ var MemoizedLeaf = React__default.memo(Leaf, (prev, next) => {
1115
+ return next.parent === prev.parent && next.isLast === prev.isLast && next.renderLeaf === prev.renderLeaf && next.text === prev.text && slate.Text.matches(next.leaf, prev.leaf);
1116
+ });
1117
+ /**
1118
+ * The default custom leaf renderer.
1119
+ */
1120
+
1121
+ var DefaultLeaf = props => {
1122
+ var {
1123
+ attributes,
1124
+ children
1125
+ } = props;
1126
+ return React__default.createElement("span", Object.assign({}, attributes), children);
1127
+ };
1128
+
1129
+ /**
1130
+ * Prevent warning on SSR by falling back to useEffect when window is not defined
1131
+ */
1132
+
1133
+ var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
1134
+
1135
+ /**
1136
+ * Text.
1137
+ */
1138
+
1139
+ var Text = props => {
1140
+ var {
1141
+ decorations,
1142
+ isLast,
1143
+ parent,
1144
+ renderLeaf,
1145
+ text
1146
+ } = props;
1147
+ var editor = useEditor();
1148
+ var ref = React.useRef(null);
1149
+ var leaves = slate.Text.decorations(text, decorations);
1150
+ var key = ReactEditor.findKey(editor, text);
1151
+ var children = [];
1152
+
1153
+ for (var i = 0; i < leaves.length; i++) {
1154
+ var leaf = leaves[i];
1155
+ children.push(React__default.createElement(MemoizedLeaf, {
1156
+ isLast: isLast && i === leaves.length - 1,
1157
+ key: "".concat(key.id, "-").concat(i),
1158
+ leaf: leaf,
1159
+ text: text,
1160
+ parent: parent,
1161
+ renderLeaf: renderLeaf
1162
+ }));
1163
+ } // Update element-related weak maps with the DOM element ref.
1164
+
1165
+
1166
+ useIsomorphicLayoutEffect(() => {
1167
+ if (ref.current) {
1168
+ KEY_TO_ELEMENT.set(key, ref.current);
1169
+ NODE_TO_ELEMENT.set(text, ref.current);
1170
+ ELEMENT_TO_NODE.set(ref.current, text);
1171
+ } else {
1172
+ KEY_TO_ELEMENT.delete(key);
1173
+ NODE_TO_ELEMENT.delete(text);
1174
+ }
1175
+ });
1176
+ return React__default.createElement("span", {
1177
+ "data-slate-node": "text",
1178
+ ref: ref
1179
+ }, children);
1180
+ };
1181
+
1182
+ var MemoizedText = React__default.memo(Text, (prev, next) => {
1183
+ return next.parent === prev.parent && next.isLast === prev.isLast && next.renderLeaf === prev.renderLeaf && next.text === prev.text;
1184
+ });
1185
+
1186
+ /**
1187
+ * Element.
1188
+ */
1189
+
1190
+ var Element = props => {
1191
+ var {
1192
+ decorate,
1193
+ decorations,
1194
+ element,
1195
+ renderElement = p => React__default.createElement(DefaultElement, Object.assign({}, p)),
1196
+ renderLeaf,
1197
+ selection
1198
+ } = props;
1199
+ var ref = React.useRef(null);
1200
+ var editor = useEditor();
1201
+ var readOnly = useReadOnly();
1202
+ var isInline = editor.isInline(element);
1203
+ var key = ReactEditor.findKey(editor, element);
1204
+ var children = React__default.createElement(Children, {
1205
+ decorate: decorate,
1206
+ decorations: decorations,
1207
+ node: element,
1208
+ renderElement: renderElement,
1209
+ renderLeaf: renderLeaf,
1210
+ selection: selection
1211
+ }); // Attributes that the developer must mix into the element in their
1212
+ // custom node renderer component.
1213
+
1214
+ var attributes = {
1215
+ 'data-slate-node': 'element',
1216
+ ref
1217
+ };
1218
+
1219
+ if (isInline) {
1220
+ attributes['data-slate-inline'] = true;
1221
+ } // If it's a block node with inline children, add the proper `dir` attribute
1222
+ // for text direction.
1223
+
1224
+
1225
+ if (!isInline && slate.Editor.hasInlines(editor, element)) {
1226
+ var text = slate.Node.string(element);
1227
+ var dir = getDirection(text);
1228
+
1229
+ if (dir === 'rtl') {
1230
+ attributes.dir = dir;
1231
+ }
1232
+ } // If it's a void node, wrap the children in extra void-specific elements.
1233
+
1234
+
1235
+ if (slate.Editor.isVoid(editor, element)) {
1236
+ attributes['data-slate-void'] = true;
1237
+
1238
+ if (!readOnly && isInline) {
1239
+ attributes.contentEditable = false;
1240
+ }
1241
+
1242
+ var Tag = isInline ? 'span' : 'div';
1243
+ var [[_text]] = slate.Node.texts(element);
1244
+ children = readOnly ? null : React__default.createElement(Tag, {
1245
+ "data-slate-spacer": true,
1246
+ style: {
1247
+ height: '0',
1248
+ color: 'transparent',
1249
+ outline: 'none',
1250
+ position: 'absolute'
1251
+ }
1252
+ }, React__default.createElement(MemoizedText, {
1253
+ decorations: [],
1254
+ isLast: false,
1255
+ parent: element,
1256
+ text: _text
1257
+ }));
1258
+ NODE_TO_INDEX.set(_text, 0);
1259
+ NODE_TO_PARENT.set(_text, element);
1260
+ } // Update element-related weak maps with the DOM element ref.
1261
+
1262
+
1263
+ useIsomorphicLayoutEffect(() => {
1264
+ if (ref.current) {
1265
+ KEY_TO_ELEMENT.set(key, ref.current);
1266
+ NODE_TO_ELEMENT.set(element, ref.current);
1267
+ ELEMENT_TO_NODE.set(ref.current, element);
1268
+ } else {
1269
+ KEY_TO_ELEMENT.delete(key);
1270
+ NODE_TO_ELEMENT.delete(element);
1271
+ }
1272
+ });
1273
+ return React__default.createElement(SelectedContext.Provider, {
1274
+ value: !!selection
1275
+ }, renderElement({
1276
+ attributes,
1277
+ children,
1278
+ element
1279
+ }));
1280
+ };
1281
+
1282
+ var MemoizedElement = React__default.memo(Element, (prev, next) => {
1283
+ return prev.decorate === next.decorate && prev.element === next.element && prev.renderElement === next.renderElement && prev.renderLeaf === next.renderLeaf && isRangeListEqual(prev.decorations, next.decorations) && (prev.selection === next.selection || !!prev.selection && !!next.selection && slate.Range.equals(prev.selection, next.selection));
1284
+ });
1285
+ /**
1286
+ * The default element renderer.
1287
+ */
1288
+
1289
+ var DefaultElement = props => {
1290
+ var {
1291
+ attributes,
1292
+ children,
1293
+ element
1294
+ } = props;
1295
+ var editor = useEditor();
1296
+ var Tag = editor.isInline(element) ? 'span' : 'div';
1297
+ return React__default.createElement(Tag, Object.assign({}, attributes, {
1298
+ style: {
1299
+ position: 'relative'
1300
+ }
1301
+ }), children);
1302
+ };
1303
+ /**
1304
+ * Check if a list of ranges is equal to another.
1305
+ *
1306
+ * PERF: this requires the two lists to also have the ranges inside them in the
1307
+ * same order, but this is an okay constraint for us since decorations are
1308
+ * kept in order, and the odd case where they aren't is okay to re-render for.
1309
+ */
1310
+
1311
+ var isRangeListEqual = (list, another) => {
1312
+ if (list.length !== another.length) {
1313
+ return false;
1314
+ }
1315
+
1316
+ for (var i = 0; i < list.length; i++) {
1317
+ var range = list[i];
1318
+ var other = another[i];
1319
+
1320
+ if (!slate.Range.equals(range, other)) {
1321
+ return false;
1322
+ }
1323
+ }
1324
+
1325
+ return true;
1326
+ };
1327
+
1328
+ /**
1329
+ * Children.
1330
+ */
1331
+
1332
+ var Children = props => {
1333
+ var {
1334
+ decorate,
1335
+ decorations,
1336
+ node,
1337
+ renderElement,
1338
+ renderLeaf,
1339
+ selection
1340
+ } = props;
1341
+ var editor = useEditor();
1342
+ var path = ReactEditor.findPath(editor, node);
1343
+ var children = [];
1344
+ var isLeafBlock = slate.Element.isElement(node) && !editor.isInline(node) && slate.Editor.hasInlines(editor, node);
1345
+
1346
+ for (var i = 0; i < node.children.length; i++) {
1347
+ var p = path.concat(i);
1348
+ var n = node.children[i];
1349
+ var key = ReactEditor.findKey(editor, n);
1350
+ var range = slate.Editor.range(editor, p);
1351
+ var sel = selection && slate.Range.intersection(range, selection);
1352
+ var ds = decorate([n, p]);
1353
+
1354
+ for (var dec of decorations) {
1355
+ var d = slate.Range.intersection(dec, range);
1356
+
1357
+ if (d) {
1358
+ ds.push(d);
1359
+ }
1360
+ }
1361
+
1362
+ if (slate.Element.isElement(n)) {
1363
+ children.push(React__default.createElement(MemoizedElement, {
1364
+ decorate: decorate,
1365
+ decorations: ds,
1366
+ element: n,
1367
+ key: key.id,
1368
+ renderElement: renderElement,
1369
+ renderLeaf: renderLeaf,
1370
+ selection: sel
1371
+ }));
1372
+ } else {
1373
+ children.push(React__default.createElement(MemoizedText, {
1374
+ decorations: ds,
1375
+ key: key.id,
1376
+ isLast: isLeafBlock && i === node.children.length - 1,
1377
+ parent: node,
1378
+ renderLeaf: renderLeaf,
1379
+ text: n
1380
+ }));
1381
+ }
1382
+
1383
+ NODE_TO_INDEX.set(n, i);
1384
+ NODE_TO_PARENT.set(n, node);
1385
+ }
1386
+
1387
+ return React__default.createElement(React__default.Fragment, null, children);
1388
+ };
1389
+
1390
+ var IS_IOS = typeof navigator !== 'undefined' && typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
1391
+ var IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
1392
+ var IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
1393
+ var IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); // "modern" Edge was released at 79.x
1394
+
1395
+ var IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
1396
+
1397
+ /**
1398
+ * Hotkey mappings for each platform.
1399
+ */
1400
+
1401
+ var HOTKEYS = {
1402
+ bold: 'mod+b',
1403
+ compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
1404
+ moveBackward: 'left',
1405
+ moveForward: 'right',
1406
+ moveWordBackward: 'ctrl+left',
1407
+ moveWordForward: 'ctrl+right',
1408
+ deleteBackward: 'shift?+backspace',
1409
+ deleteForward: 'shift?+delete',
1410
+ extendBackward: 'shift+left',
1411
+ extendForward: 'shift+right',
1412
+ italic: 'mod+i',
1413
+ splitBlock: 'shift?+enter',
1414
+ undo: 'mod+z'
1415
+ };
1416
+ var APPLE_HOTKEYS = {
1417
+ moveLineBackward: 'opt+up',
1418
+ moveLineForward: 'opt+down',
1419
+ moveWordBackward: 'opt+left',
1420
+ moveWordForward: 'opt+right',
1421
+ deleteBackward: ['ctrl+backspace', 'ctrl+h'],
1422
+ deleteForward: ['ctrl+delete', 'ctrl+d'],
1423
+ deleteLineBackward: 'cmd+shift?+backspace',
1424
+ deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
1425
+ deleteWordBackward: 'opt+shift?+backspace',
1426
+ deleteWordForward: 'opt+shift?+delete',
1427
+ extendLineBackward: 'opt+shift+up',
1428
+ extendLineForward: 'opt+shift+down',
1429
+ redo: 'cmd+shift+z',
1430
+ transposeCharacter: 'ctrl+t'
1431
+ };
1432
+ var WINDOWS_HOTKEYS = {
1433
+ deleteWordBackward: 'ctrl+shift?+backspace',
1434
+ deleteWordForward: 'ctrl+shift?+delete',
1435
+ redo: ['ctrl+y', 'ctrl+shift+z']
1436
+ };
1437
+ /**
1438
+ * Create a platform-aware hotkey checker.
1439
+ */
1440
+
1441
+ var create = key => {
1442
+ var generic = HOTKEYS[key];
1443
+ var apple = APPLE_HOTKEYS[key];
1444
+ var windows = WINDOWS_HOTKEYS[key];
1445
+ var isGeneric = generic && isHotkey.isKeyHotkey(generic);
1446
+ var isApple = apple && isHotkey.isKeyHotkey(apple);
1447
+ var isWindows = windows && isHotkey.isKeyHotkey(windows);
1448
+ return event => {
1449
+ if (isGeneric && isGeneric(event)) return true;
1450
+ if (IS_APPLE && isApple && isApple(event)) return true;
1451
+ if (!IS_APPLE && isWindows && isWindows(event)) return true;
1452
+ return false;
1453
+ };
1454
+ };
1455
+ /**
1456
+ * Hotkeys.
1457
+ */
1458
+
1459
+
1460
+ var Hotkeys = {
1461
+ isBold: create('bold'),
1462
+ isCompose: create('compose'),
1463
+ isMoveBackward: create('moveBackward'),
1464
+ isMoveForward: create('moveForward'),
1465
+ isDeleteBackward: create('deleteBackward'),
1466
+ isDeleteForward: create('deleteForward'),
1467
+ isDeleteLineBackward: create('deleteLineBackward'),
1468
+ isDeleteLineForward: create('deleteLineForward'),
1469
+ isDeleteWordBackward: create('deleteWordBackward'),
1470
+ isDeleteWordForward: create('deleteWordForward'),
1471
+ isExtendBackward: create('extendBackward'),
1472
+ isExtendForward: create('extendForward'),
1473
+ isExtendLineBackward: create('extendLineBackward'),
1474
+ isExtendLineForward: create('extendLineForward'),
1475
+ isItalic: create('italic'),
1476
+ isMoveLineBackward: create('moveLineBackward'),
1477
+ isMoveLineForward: create('moveLineForward'),
1478
+ isMoveWordBackward: create('moveWordBackward'),
1479
+ isMoveWordForward: create('moveWordForward'),
1480
+ isRedo: create('redo'),
1481
+ isSplitBlock: create('splitBlock'),
1482
+ isTransposeCharacter: create('transposeCharacter'),
1483
+ isUndo: create('undo')
1484
+ };
1485
+
1486
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
1487
+
1488
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
1489
+
1490
+ var HAS_BEFORE_INPUT_SUPPORT = !(IS_FIREFOX || IS_EDGE_LEGACY);
1491
+ /**
1492
+ * Editable.
1493
+ */
1494
+
1495
+ var Editable = props => {
1496
+ var {
1497
+ autoFocus,
1498
+ decorate = defaultDecorate,
1499
+ onDOMBeforeInput: propsOnDOMBeforeInput,
1500
+ placeholder,
1501
+ readOnly = false,
1502
+ renderElement,
1503
+ renderLeaf,
1504
+ style = {},
1505
+ as: Component = 'div'
1506
+ } = props,
1507
+ attributes = _objectWithoutProperties(props, ["autoFocus", "decorate", "onDOMBeforeInput", "placeholder", "readOnly", "renderElement", "renderLeaf", "style", "as"]);
1508
+
1509
+ var editor = useSlate();
1510
+ var ref = React.useRef(null); // Update internal state on each render.
1511
+
1512
+ IS_READ_ONLY.set(editor, readOnly); // Keep track of some state for the event handler logic.
1513
+
1514
+ var state = React.useMemo(() => ({
1515
+ isComposing: false,
1516
+ isUpdatingSelection: false,
1517
+ latestElement: null
1518
+ }), []); // Update element-related weak maps with the DOM element ref.
1519
+
1520
+ useIsomorphicLayoutEffect(() => {
1521
+ if (ref.current) {
1522
+ EDITOR_TO_ELEMENT.set(editor, ref.current);
1523
+ NODE_TO_ELEMENT.set(editor, ref.current);
1524
+ ELEMENT_TO_NODE.set(ref.current, editor);
1525
+ } else {
1526
+ NODE_TO_ELEMENT.delete(editor);
1527
+ }
1528
+ }); // Whenever the editor updates, make sure the DOM selection state is in sync.
1529
+
1530
+ useIsomorphicLayoutEffect(() => {
1531
+ var {
1532
+ selection
1533
+ } = editor;
1534
+ var domSelection = window.getSelection();
1535
+
1536
+ if (state.isComposing || !domSelection || !ReactEditor.isFocused(editor)) {
1537
+ return;
1538
+ }
1539
+
1540
+ var hasDomSelection = domSelection.type !== 'None'; // If the DOM selection is properly unset, we're done.
1541
+
1542
+ if (!selection && !hasDomSelection) {
1543
+ return;
1544
+ } // If the DOM selection is already correct, we're done.
1545
+
1546
+
1547
+ if (hasDomSelection && selection && slate.Range.equals(ReactEditor.toSlateRange(editor, domSelection), selection)) {
1548
+ return;
1549
+ } // Otherwise the DOM selection is out of sync, so update it.
1550
+
1551
+
1552
+ var el = ReactEditor.toDOMNode(editor, editor);
1553
+ state.isUpdatingSelection = true;
1554
+ domSelection.removeAllRanges();
1555
+ var newDomRange = selection && ReactEditor.toDOMRange(editor, selection);
1556
+
1557
+ if (newDomRange) {
1558
+ domSelection.addRange(newDomRange);
1559
+ var leafEl = newDomRange.startContainer.parentElement;
1560
+ scrollIntoView(leafEl, {
1561
+ scrollMode: 'if-needed'
1562
+ });
1563
+ }
1564
+
1565
+ setTimeout(() => {
1566
+ // COMPAT: In Firefox, it's not enough to create a range, you also need
1567
+ // to focus the contenteditable element too. (2016/11/16)
1568
+ if (newDomRange && IS_FIREFOX) {
1569
+ el.focus();
1570
+ }
1571
+
1572
+ state.isUpdatingSelection = false;
1573
+ });
1574
+ }); // The autoFocus TextareaHTMLAttribute doesn't do anything on a div, so it
1575
+ // needs to be manually focused.
1576
+
1577
+ React.useEffect(() => {
1578
+ if (ref.current && autoFocus) {
1579
+ ref.current.focus();
1580
+ }
1581
+ }, [autoFocus]); // Listen on the native `beforeinput` event to get real "Level 2" events. This
1582
+ // is required because React's `beforeinput` is fake and never really attaches
1583
+ // to the real event sadly. (2019/11/01)
1584
+ // https://github.com/facebook/react/issues/11211
1585
+
1586
+ var onDOMBeforeInput = React.useCallback(event => {
1587
+ if (!readOnly && hasEditableTarget(editor, event.target) && !isDOMEventHandled(event, propsOnDOMBeforeInput)) {
1588
+ var {
1589
+ selection
1590
+ } = editor;
1591
+ var {
1592
+ inputType: type
1593
+ } = event;
1594
+ var data = event.dataTransfer || event.data || undefined; // These two types occur while a user is composing text and can't be
1595
+ // cancelled. Let them through and wait for the composition to end.
1596
+
1597
+ if (type === 'insertCompositionText' || type === 'deleteCompositionText') {
1598
+ return;
1599
+ }
1600
+
1601
+ event.preventDefault(); // COMPAT: For the deleting forward/backward input types we don't want
1602
+ // to change the selection because it is the range that will be deleted,
1603
+ // and those commands determine that for themselves.
1604
+
1605
+ if (!type.startsWith('delete') || type.startsWith('deleteBy')) {
1606
+ var [targetRange] = event.getTargetRanges();
1607
+
1608
+ if (targetRange) {
1609
+ var range = ReactEditor.toSlateRange(editor, targetRange);
1610
+
1611
+ if (!selection || !slate.Range.equals(selection, range)) {
1612
+ slate.Transforms.select(editor, range);
1613
+ }
1614
+ }
1615
+ } // COMPAT: If the selection is expanded, even if the command seems like
1616
+ // a delete forward/backward command it should delete the selection.
1617
+
1618
+
1619
+ if (selection && slate.Range.isExpanded(selection) && type.startsWith('delete')) {
1620
+ slate.Editor.deleteFragment(editor);
1621
+ return;
1622
+ }
1623
+
1624
+ switch (type) {
1625
+ case 'deleteByComposition':
1626
+ case 'deleteByCut':
1627
+ case 'deleteByDrag':
1628
+ {
1629
+ slate.Editor.deleteFragment(editor);
1630
+ break;
1631
+ }
1632
+
1633
+ case 'deleteContent':
1634
+ case 'deleteContentForward':
1635
+ {
1636
+ slate.Editor.deleteForward(editor);
1637
+ break;
1638
+ }
1639
+
1640
+ case 'deleteContentBackward':
1641
+ {
1642
+ slate.Editor.deleteBackward(editor);
1643
+ break;
1644
+ }
1645
+
1646
+ case 'deleteEntireSoftLine':
1647
+ {
1648
+ slate.Editor.deleteBackward(editor, {
1649
+ unit: 'line'
1650
+ });
1651
+ slate.Editor.deleteForward(editor, {
1652
+ unit: 'line'
1653
+ });
1654
+ break;
1655
+ }
1656
+
1657
+ case 'deleteHardLineBackward':
1658
+ {
1659
+ slate.Editor.deleteBackward(editor, {
1660
+ unit: 'block'
1661
+ });
1662
+ break;
1663
+ }
1664
+
1665
+ case 'deleteSoftLineBackward':
1666
+ {
1667
+ slate.Editor.deleteBackward(editor, {
1668
+ unit: 'line'
1669
+ });
1670
+ break;
1671
+ }
1672
+
1673
+ case 'deleteHardLineForward':
1674
+ {
1675
+ slate.Editor.deleteForward(editor, {
1676
+ unit: 'block'
1677
+ });
1678
+ break;
1679
+ }
1680
+
1681
+ case 'deleteSoftLineForward':
1682
+ {
1683
+ slate.Editor.deleteForward(editor, {
1684
+ unit: 'line'
1685
+ });
1686
+ break;
1687
+ }
1688
+
1689
+ case 'deleteWordBackward':
1690
+ {
1691
+ slate.Editor.deleteBackward(editor, {
1692
+ unit: 'word'
1693
+ });
1694
+ break;
1695
+ }
1696
+
1697
+ case 'deleteWordForward':
1698
+ {
1699
+ slate.Editor.deleteForward(editor, {
1700
+ unit: 'word'
1701
+ });
1702
+ break;
1703
+ }
1704
+
1705
+ case 'insertLineBreak':
1706
+ case 'insertParagraph':
1707
+ {
1708
+ slate.Editor.insertBreak(editor);
1709
+ break;
1710
+ }
1711
+
1712
+ case 'insertFromComposition':
1713
+ case 'insertFromDrop':
1714
+ case 'insertFromPaste':
1715
+ case 'insertFromYank':
1716
+ case 'insertReplacementText':
1717
+ case 'insertText':
1718
+ {
1719
+ if (data instanceof DataTransfer) {
1720
+ ReactEditor.insertData(editor, data);
1721
+ } else if (typeof data === 'string') {
1722
+ slate.Editor.insertText(editor, data);
1723
+ }
1724
+
1725
+ break;
1726
+ }
1727
+ }
1728
+ }
1729
+ }, [readOnly]); // Attach a native DOM event handler for `beforeinput` events, because React's
1730
+ // built-in `onBeforeInput` is actually a leaky polyfill that doesn't expose
1731
+ // real `beforeinput` events sadly... (2019/11/04)
1732
+ // https://github.com/facebook/react/issues/11211
1733
+
1734
+ useIsomorphicLayoutEffect(() => {
1735
+ if (ref.current) {
1736
+ // @ts-ignore The `beforeinput` event isn't recognized.
1737
+ ref.current.addEventListener('beforeinput', onDOMBeforeInput);
1738
+ }
1739
+
1740
+ return () => {
1741
+ if (ref.current) {
1742
+ // @ts-ignore The `beforeinput` event isn't recognized.
1743
+ ref.current.removeEventListener('beforeinput', onDOMBeforeInput);
1744
+ }
1745
+ };
1746
+ }, [onDOMBeforeInput]); // Listen on the native `selectionchange` event to be able to update any time
1747
+ // the selection changes. This is required because React's `onSelect` is leaky
1748
+ // and non-standard so it doesn't fire until after a selection has been
1749
+ // released. This causes issues in situations where another change happens
1750
+ // while a selection is being dragged.
1751
+
1752
+ var onDOMSelectionChange = React.useCallback(throttle(() => {
1753
+ if (!readOnly && !state.isComposing && !state.isUpdatingSelection) {
1754
+ var {
1755
+ activeElement
1756
+ } = window.document;
1757
+ var el = ReactEditor.toDOMNode(editor, editor);
1758
+ var domSelection = window.getSelection();
1759
+
1760
+ if (activeElement === el) {
1761
+ state.latestElement = activeElement;
1762
+ IS_FOCUSED.set(editor, true);
1763
+ } else {
1764
+ IS_FOCUSED.delete(editor);
1765
+ }
1766
+
1767
+ if (domSelection && hasEditableTarget(editor, domSelection.anchorNode) && hasEditableTarget(editor, domSelection.focusNode)) {
1768
+ var range = ReactEditor.toSlateRange(editor, domSelection);
1769
+ slate.Transforms.select(editor, range);
1770
+ } else {
1771
+ slate.Transforms.deselect(editor);
1772
+ }
1773
+ }
1774
+ }, 100), [readOnly]); // Attach a native DOM event handler for `selectionchange`, because React's
1775
+ // built-in `onSelect` handler doesn't fire for all selection changes. It's a
1776
+ // leaky polyfill that only fires on keypresses or clicks. Instead, we want to
1777
+ // fire for any change to the selection inside the editor. (2019/11/04)
1778
+ // https://github.com/facebook/react/issues/5785
1779
+
1780
+ useIsomorphicLayoutEffect(() => {
1781
+ window.document.addEventListener('selectionchange', onDOMSelectionChange);
1782
+ return () => {
1783
+ window.document.removeEventListener('selectionchange', onDOMSelectionChange);
1784
+ };
1785
+ }, [onDOMSelectionChange]);
1786
+ var decorations = decorate([editor, []]);
1787
+
1788
+ if (placeholder && editor.children.length === 1 && Array.from(slate.Node.texts(editor)).length === 1 && slate.Node.string(editor) === '') {
1789
+ var start = slate.Editor.start(editor, []);
1790
+ decorations.push({
1791
+ [PLACEHOLDER_SYMBOL]: true,
1792
+ placeholder,
1793
+ anchor: start,
1794
+ focus: start
1795
+ });
1796
+ }
1797
+
1798
+ return React__default.createElement(ReadOnlyContext.Provider, {
1799
+ value: readOnly
1800
+ }, React__default.createElement(Component // COMPAT: The Grammarly Chrome extension works by changing the DOM
1801
+ // out from under `contenteditable` elements, which leads to weird
1802
+ // behaviors so we have to disable it like editor. (2017/04/24)
1803
+ , Object.assign({
1804
+ "data-gramm": false,
1805
+ role: readOnly ? undefined : 'textbox'
1806
+ }, attributes, {
1807
+ // COMPAT: Certain browsers don't support the `beforeinput` event, so we'd
1808
+ // have to use hacks to make these replacement-based features work.
1809
+ spellCheck: !HAS_BEFORE_INPUT_SUPPORT ? undefined : attributes.spellCheck,
1810
+ autoCorrect: !HAS_BEFORE_INPUT_SUPPORT ? undefined : attributes.autoCorrect,
1811
+ autoCapitalize: !HAS_BEFORE_INPUT_SUPPORT ? undefined : attributes.autoCapitalize,
1812
+ "data-slate-editor": true,
1813
+ "data-slate-node": "value",
1814
+ contentEditable: readOnly ? undefined : true,
1815
+ suppressContentEditableWarning: true,
1816
+ ref: ref,
1817
+ style: _objectSpread({
1818
+ // Prevent the default outline styles.
1819
+ outline: 'none',
1820
+ // Preserve adjacent whitespace and new lines.
1821
+ whiteSpace: 'pre-wrap',
1822
+ // Allow words to break if they are too long.
1823
+ wordWrap: 'break-word'
1824
+ }, style),
1825
+ onBeforeInput: React.useCallback(event => {
1826
+ // COMPAT: Certain browsers don't support the `beforeinput` event, so we
1827
+ // fall back to React's leaky polyfill instead just for it. It
1828
+ // only works for the `insertText` input type.
1829
+ if (!HAS_BEFORE_INPUT_SUPPORT && !readOnly && !isEventHandled(event, attributes.onBeforeInput) && hasEditableTarget(editor, event.target)) {
1830
+ event.preventDefault();
1831
+ var text = event.data;
1832
+ slate.Editor.insertText(editor, text);
1833
+ }
1834
+ }, [readOnly]),
1835
+ onBlur: React.useCallback(event => {
1836
+ if (readOnly || state.isUpdatingSelection || !hasEditableTarget(editor, event.target) || isEventHandled(event, attributes.onBlur)) {
1837
+ return;
1838
+ } // COMPAT: If the current `activeElement` is still the previous
1839
+ // one, this is due to the window being blurred when the tab
1840
+ // itself becomes unfocused, so we want to abort early to allow to
1841
+ // editor to stay focused when the tab becomes focused again.
1842
+
1843
+
1844
+ if (state.latestElement === window.document.activeElement) {
1845
+ return;
1846
+ }
1847
+
1848
+ var {
1849
+ relatedTarget
1850
+ } = event;
1851
+ var el = ReactEditor.toDOMNode(editor, editor); // COMPAT: The event should be ignored if the focus is returning
1852
+ // to the editor from an embedded editable element (eg. an <input>
1853
+ // element inside a void node).
1854
+
1855
+ if (relatedTarget === el) {
1856
+ return;
1857
+ } // COMPAT: The event should be ignored if the focus is moving from
1858
+ // the editor to inside a void node's spacer element.
1859
+
1860
+
1861
+ if (isDOMElement(relatedTarget) && relatedTarget.hasAttribute('data-slate-spacer')) {
1862
+ return;
1863
+ } // COMPAT: The event should be ignored if the focus is moving to a
1864
+ // non- editable section of an element that isn't a void node (eg.
1865
+ // a list item of the check list example).
1866
+
1867
+
1868
+ if (relatedTarget != null && isDOMNode(relatedTarget) && ReactEditor.hasDOMNode(editor, relatedTarget)) {
1869
+ var node = ReactEditor.toSlateNode(editor, relatedTarget);
1870
+
1871
+ if (slate.Element.isElement(node) && !editor.isVoid(node)) {
1872
+ return;
1873
+ }
1874
+ }
1875
+
1876
+ IS_FOCUSED.delete(editor);
1877
+ }, [readOnly, attributes.onBlur]),
1878
+ onClick: React.useCallback(event => {
1879
+ if (!readOnly && hasTarget(editor, event.target) && !isEventHandled(event, attributes.onClick) && isDOMNode(event.target)) {
1880
+ var node = ReactEditor.toSlateNode(editor, event.target);
1881
+ var path = ReactEditor.findPath(editor, node);
1882
+
1883
+ var _start = slate.Editor.start(editor, path);
1884
+
1885
+ if (slate.Editor.void(editor, {
1886
+ at: _start
1887
+ })) {
1888
+ var range = slate.Editor.range(editor, _start);
1889
+ slate.Transforms.select(editor, range);
1890
+ }
1891
+ }
1892
+ }, [readOnly, attributes.onClick]),
1893
+ onCompositionEnd: React.useCallback(event => {
1894
+ if (hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onCompositionEnd)) {
1895
+ state.isComposing = false; // COMPAT: In Chrome, `beforeinput` events for compositions
1896
+ // aren't correct and never fire the "insertFromComposition"
1897
+ // type that we need. So instead, insert whenever a composition
1898
+ // ends since it will already have been committed to the DOM.
1899
+
1900
+ if (!IS_SAFARI && !IS_FIREFOX && event.data) {
1901
+ slate.Editor.insertText(editor, event.data);
1902
+ }
1903
+ }
1904
+ }, [attributes.onCompositionEnd]),
1905
+ onCompositionStart: React.useCallback(event => {
1906
+ if (hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onCompositionStart)) {
1907
+ state.isComposing = true;
1908
+ }
1909
+ }, [attributes.onCompositionStart]),
1910
+ onCopy: React.useCallback(event => {
1911
+ if (hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onCopy)) {
1912
+ event.preventDefault();
1913
+ ReactEditor.setFragmentData(editor, event.clipboardData);
1914
+ }
1915
+ }, [attributes.onCopy]),
1916
+ onCut: React.useCallback(event => {
1917
+ if (!readOnly && hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onCut)) {
1918
+ event.preventDefault();
1919
+ ReactEditor.setFragmentData(editor, event.clipboardData);
1920
+ var {
1921
+ selection
1922
+ } = editor;
1923
+
1924
+ if (selection && slate.Range.isExpanded(selection)) {
1925
+ slate.Editor.deleteFragment(editor);
1926
+ }
1927
+ }
1928
+ }, [readOnly, attributes.onCut]),
1929
+ onDragOver: React.useCallback(event => {
1930
+ if (hasTarget(editor, event.target) && !isEventHandled(event, attributes.onDragOver)) {
1931
+ // Only when the target is void, call `preventDefault` to signal
1932
+ // that drops are allowed. Editable content is droppable by
1933
+ // default, and calling `preventDefault` hides the cursor.
1934
+ var node = ReactEditor.toSlateNode(editor, event.target);
1935
+
1936
+ if (slate.Editor.isVoid(editor, node)) {
1937
+ event.preventDefault();
1938
+ }
1939
+ }
1940
+ }, [attributes.onDragOver]),
1941
+ onDragStart: React.useCallback(event => {
1942
+ if (hasTarget(editor, event.target) && !isEventHandled(event, attributes.onDragStart)) {
1943
+ var node = ReactEditor.toSlateNode(editor, event.target);
1944
+ var path = ReactEditor.findPath(editor, node);
1945
+ var voidMatch = slate.Editor.void(editor, {
1946
+ at: path
1947
+ }); // If starting a drag on a void node, make sure it is selected
1948
+ // so that it shows up in the selection's fragment.
1949
+
1950
+ if (voidMatch) {
1951
+ var range = slate.Editor.range(editor, path);
1952
+ slate.Transforms.select(editor, range);
1953
+ }
1954
+
1955
+ ReactEditor.setFragmentData(editor, event.dataTransfer);
1956
+ }
1957
+ }, [attributes.onDragStart]),
1958
+ onDrop: React.useCallback(event => {
1959
+ if (hasTarget(editor, event.target) && !readOnly && !isEventHandled(event, attributes.onDrop)) {
1960
+ // COMPAT: Certain browsers don't fire `beforeinput` events at all, and
1961
+ // Chromium browsers don't properly fire them for files being
1962
+ // dropped into a `contenteditable`. (2019/11/26)
1963
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1028668
1964
+ if (!HAS_BEFORE_INPUT_SUPPORT || !IS_SAFARI && event.dataTransfer.files.length > 0) {
1965
+ event.preventDefault();
1966
+ var range = ReactEditor.findEventRange(editor, event);
1967
+ var data = event.dataTransfer;
1968
+ slate.Transforms.select(editor, range);
1969
+ ReactEditor.insertData(editor, data);
1970
+ }
1971
+ }
1972
+ }, [readOnly, attributes.onDrop]),
1973
+ onFocus: React.useCallback(event => {
1974
+ if (!readOnly && !state.isUpdatingSelection && hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onFocus)) {
1975
+ var el = ReactEditor.toDOMNode(editor, editor);
1976
+ state.latestElement = window.document.activeElement; // COMPAT: If the editor has nested editable elements, the focus
1977
+ // can go to them. In Firefox, this must be prevented because it
1978
+ // results in issues with keyboard navigation. (2017/03/30)
1979
+
1980
+ if (IS_FIREFOX && event.target !== el) {
1981
+ el.focus();
1982
+ return;
1983
+ }
1984
+
1985
+ IS_FOCUSED.set(editor, true);
1986
+ }
1987
+ }, [readOnly, attributes.onFocus]),
1988
+ onKeyDown: React.useCallback(event => {
1989
+ if (!readOnly && hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onKeyDown)) {
1990
+ var {
1991
+ nativeEvent
1992
+ } = event;
1993
+ var {
1994
+ selection
1995
+ } = editor; // COMPAT: Since we prevent the default behavior on
1996
+ // `beforeinput` events, the browser doesn't think there's ever
1997
+ // any history stack to undo or redo, so we have to manage these
1998
+ // hotkeys ourselves. (2019/11/06)
1999
+
2000
+ if (Hotkeys.isRedo(nativeEvent)) {
2001
+ event.preventDefault();
2002
+
2003
+ if (editor.redo) {
2004
+ editor.redo();
2005
+ }
2006
+
2007
+ return;
2008
+ }
2009
+
2010
+ if (Hotkeys.isUndo(nativeEvent)) {
2011
+ event.preventDefault();
2012
+
2013
+ if (editor.undo) {
2014
+ editor.undo();
2015
+ }
2016
+
2017
+ return;
2018
+ } // COMPAT: Certain browsers don't handle the selection updates
2019
+ // properly. In Chrome, the selection isn't properly extended.
2020
+ // And in Firefox, the selection isn't properly collapsed.
2021
+ // (2017/10/17)
2022
+
2023
+
2024
+ if (Hotkeys.isMoveLineBackward(nativeEvent)) {
2025
+ event.preventDefault();
2026
+ slate.Transforms.move(editor, {
2027
+ unit: 'line',
2028
+ reverse: true
2029
+ });
2030
+ return;
2031
+ }
2032
+
2033
+ if (Hotkeys.isMoveLineForward(nativeEvent)) {
2034
+ event.preventDefault();
2035
+ slate.Transforms.move(editor, {
2036
+ unit: 'line'
2037
+ });
2038
+ return;
2039
+ }
2040
+
2041
+ if (Hotkeys.isExtendLineBackward(nativeEvent)) {
2042
+ event.preventDefault();
2043
+ slate.Transforms.move(editor, {
2044
+ unit: 'line',
2045
+ edge: 'focus',
2046
+ reverse: true
2047
+ });
2048
+ return;
2049
+ }
2050
+
2051
+ if (Hotkeys.isExtendLineForward(nativeEvent)) {
2052
+ event.preventDefault();
2053
+ slate.Transforms.move(editor, {
2054
+ unit: 'line',
2055
+ edge: 'focus'
2056
+ });
2057
+ return;
2058
+ } // COMPAT: If a void node is selected, or a zero-width text node
2059
+ // adjacent to an inline is selected, we need to handle these
2060
+ // hotkeys manually because browsers won't be able to skip over
2061
+ // the void node with the zero-width space not being an empty
2062
+ // string.
2063
+
2064
+
2065
+ if (Hotkeys.isMoveBackward(nativeEvent)) {
2066
+ event.preventDefault();
2067
+
2068
+ if (selection && slate.Range.isCollapsed(selection)) {
2069
+ slate.Transforms.move(editor, {
2070
+ reverse: true
2071
+ });
2072
+ } else {
2073
+ slate.Transforms.collapse(editor, {
2074
+ edge: 'start'
2075
+ });
2076
+ }
2077
+
2078
+ return;
2079
+ }
2080
+
2081
+ if (Hotkeys.isMoveForward(nativeEvent)) {
2082
+ event.preventDefault();
2083
+
2084
+ if (selection && slate.Range.isCollapsed(selection)) {
2085
+ slate.Transforms.move(editor);
2086
+ } else {
2087
+ slate.Transforms.collapse(editor, {
2088
+ edge: 'end'
2089
+ });
2090
+ }
2091
+
2092
+ return;
2093
+ }
2094
+
2095
+ if (Hotkeys.isMoveWordBackward(nativeEvent)) {
2096
+ event.preventDefault();
2097
+ slate.Transforms.move(editor, {
2098
+ unit: 'word',
2099
+ reverse: true
2100
+ });
2101
+ return;
2102
+ }
2103
+
2104
+ if (Hotkeys.isMoveWordForward(nativeEvent)) {
2105
+ event.preventDefault();
2106
+ slate.Transforms.move(editor, {
2107
+ unit: 'word'
2108
+ });
2109
+ return;
2110
+ } // COMPAT: Certain browsers don't support the `beforeinput` event, so we
2111
+ // fall back to guessing at the input intention for hotkeys.
2112
+ // COMPAT: In iOS, some of these hotkeys are handled in the
2113
+
2114
+
2115
+ if (!HAS_BEFORE_INPUT_SUPPORT) {
2116
+ // We don't have a core behavior for these, but they change the
2117
+ // DOM if we don't prevent them, so we have to.
2118
+ if (Hotkeys.isBold(nativeEvent) || Hotkeys.isItalic(nativeEvent) || Hotkeys.isTransposeCharacter(nativeEvent)) {
2119
+ event.preventDefault();
2120
+ return;
2121
+ }
2122
+
2123
+ if (Hotkeys.isSplitBlock(nativeEvent)) {
2124
+ event.preventDefault();
2125
+ slate.Editor.insertBreak(editor);
2126
+ return;
2127
+ }
2128
+
2129
+ if (Hotkeys.isDeleteBackward(nativeEvent)) {
2130
+ event.preventDefault();
2131
+
2132
+ if (selection && slate.Range.isExpanded(selection)) {
2133
+ slate.Editor.deleteFragment(editor);
2134
+ } else {
2135
+ slate.Editor.deleteBackward(editor);
2136
+ }
2137
+
2138
+ return;
2139
+ }
2140
+
2141
+ if (Hotkeys.isDeleteForward(nativeEvent)) {
2142
+ event.preventDefault();
2143
+
2144
+ if (selection && slate.Range.isExpanded(selection)) {
2145
+ slate.Editor.deleteFragment(editor);
2146
+ } else {
2147
+ slate.Editor.deleteForward(editor);
2148
+ }
2149
+
2150
+ return;
2151
+ }
2152
+
2153
+ if (Hotkeys.isDeleteLineBackward(nativeEvent)) {
2154
+ event.preventDefault();
2155
+
2156
+ if (selection && slate.Range.isExpanded(selection)) {
2157
+ slate.Editor.deleteFragment(editor);
2158
+ } else {
2159
+ slate.Editor.deleteBackward(editor, {
2160
+ unit: 'line'
2161
+ });
2162
+ }
2163
+
2164
+ return;
2165
+ }
2166
+
2167
+ if (Hotkeys.isDeleteLineForward(nativeEvent)) {
2168
+ event.preventDefault();
2169
+
2170
+ if (selection && slate.Range.isExpanded(selection)) {
2171
+ slate.Editor.deleteFragment(editor);
2172
+ } else {
2173
+ slate.Editor.deleteForward(editor, {
2174
+ unit: 'line'
2175
+ });
2176
+ }
2177
+
2178
+ return;
2179
+ }
2180
+
2181
+ if (Hotkeys.isDeleteWordBackward(nativeEvent)) {
2182
+ event.preventDefault();
2183
+
2184
+ if (selection && slate.Range.isExpanded(selection)) {
2185
+ slate.Editor.deleteFragment(editor);
2186
+ } else {
2187
+ slate.Editor.deleteBackward(editor, {
2188
+ unit: 'word'
2189
+ });
2190
+ }
2191
+
2192
+ return;
2193
+ }
2194
+
2195
+ if (Hotkeys.isDeleteWordForward(nativeEvent)) {
2196
+ event.preventDefault();
2197
+
2198
+ if (selection && slate.Range.isExpanded(selection)) {
2199
+ slate.Editor.deleteFragment(editor);
2200
+ } else {
2201
+ slate.Editor.deleteForward(editor, {
2202
+ unit: 'word'
2203
+ });
2204
+ }
2205
+
2206
+ return;
2207
+ }
2208
+ }
2209
+ }
2210
+ }, [readOnly, attributes.onKeyDown]),
2211
+ onPaste: React.useCallback(event => {
2212
+ // COMPAT: Certain browsers don't support the `beforeinput` event, so we
2213
+ // fall back to React's `onPaste` here instead.
2214
+ // COMPAT: Firefox, Chrome and Safari are not emitting `beforeinput` events
2215
+ // when "paste without formatting" option is used.
2216
+ // This unfortunately needs to be handled with paste events instead.
2217
+ if ((!HAS_BEFORE_INPUT_SUPPORT || isPlainTextOnlyPaste(event.nativeEvent)) && !readOnly && hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onPaste)) {
2218
+ event.preventDefault();
2219
+ ReactEditor.insertData(editor, event.clipboardData);
2220
+ }
2221
+ }, [readOnly, attributes.onPaste])
2222
+ }), React__default.createElement(Children, {
2223
+ decorate: decorate,
2224
+ decorations: decorations,
2225
+ node: editor,
2226
+ renderElement: renderElement,
2227
+ renderLeaf: renderLeaf,
2228
+ selection: editor.selection
2229
+ })));
2230
+ };
2231
+ /**
2232
+ * A default memoized decorate function.
2233
+ */
2234
+
2235
+ var defaultDecorate = () => [];
2236
+ /**
2237
+ * Check if the target is in the editor.
2238
+ */
2239
+
2240
+
2241
+ var hasTarget = (editor, target) => {
2242
+ return isDOMNode(target) && ReactEditor.hasDOMNode(editor, target);
2243
+ };
2244
+ /**
2245
+ * Check if the target is editable and in the editor.
2246
+ */
2247
+
2248
+
2249
+ var hasEditableTarget = (editor, target) => {
2250
+ return isDOMNode(target) && ReactEditor.hasDOMNode(editor, target, {
2251
+ editable: true
2252
+ });
2253
+ };
2254
+ /**
2255
+ * Check if an event is overrided by a handler.
2256
+ */
2257
+
2258
+
2259
+ var isEventHandled = (event, handler) => {
2260
+ if (!handler) {
2261
+ return false;
2262
+ }
2263
+
2264
+ handler(event);
2265
+ return event.isDefaultPrevented() || event.isPropagationStopped();
2266
+ };
2267
+ /**
2268
+ * Check if a DOM event is overrided by a handler.
2269
+ */
2270
+
2271
+
2272
+ var isDOMEventHandled = (event, handler) => {
2273
+ if (!handler) {
2274
+ return false;
2275
+ }
2276
+
2277
+ handler(event);
2278
+ return event.defaultPrevented;
2102
2279
  };
2103
2280
 
2104
2281
  exports.DefaultElement = DefaultElement;
2105
2282
  exports.DefaultLeaf = DefaultLeaf;
2106
2283
  exports.Editable = Editable;
2107
- exports.ReactCommand = ReactCommand;
2108
2284
  exports.ReactEditor = ReactEditor;
2109
2285
  exports.Slate = Slate;
2110
2286
  exports.useEditor = useEditor;