slate-react 0.93.0 → 0.94.0

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.es.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import getDirection from 'direction';
2
2
  import debounce from 'lodash/debounce';
3
3
  import throttle from 'lodash/throttle';
4
- import React, { useLayoutEffect, useEffect, useRef, useState, memo, forwardRef, createContext, useContext, useCallback, Component, useReducer, useMemo } from 'react';
4
+ import React, { createContext, useContext, useRef, useEffect, useLayoutEffect, useState, memo, forwardRef, useCallback, Component, useReducer, useMemo } from 'react';
5
5
  import scrollIntoView from 'scroll-into-view-if-needed';
6
- import { Editor, Scrubber, Transforms, Range, Element as Element$1, Path, Node, Text as Text$1, Point } from 'slate';
6
+ import { Transforms, Element as Element$1, Editor, Scrubber, Range, Path, Point, Node, Text as Text$1 } from 'slate';
7
7
  import { ResizeObserver } from '@juggle/resize-observer';
8
8
  import { isKeyHotkey } from 'is-hotkey';
9
9
  import ReactDOM from 'react-dom';
@@ -58,20 +58,51 @@ function _objectWithoutProperties(source, excluded) {
58
58
  }
59
59
 
60
60
  /**
61
- * An auto-incrementing identifier for keys.
61
+ * A React context for sharing the editor object.
62
62
  */
63
- var n = 0;
63
+
64
+ var EditorContext = /*#__PURE__*/createContext(null);
64
65
  /**
65
- * A class that keeps track of a key string. We use a full class here because we
66
- * want to be able to use them as keys in `WeakMap` objects.
66
+ * Get the current editor object from the React context.
67
67
  */
68
68
 
69
- class Key {
70
- constructor() {
71
- this.id = "".concat(n++);
69
+ var useSlateStatic = () => {
70
+ var editor = useContext(EditorContext);
71
+
72
+ if (!editor) {
73
+ throw new Error("The `useSlateStatic` hook must be used inside the <Slate> component's context.");
72
74
  }
73
75
 
74
- }
76
+ return editor;
77
+ };
78
+
79
+ var IS_REACT_VERSION_17_OR_ABOVE = parseInt(React.version.split('.')[0], 10) >= 17;
80
+ var IS_IOS = typeof navigator !== 'undefined' && typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
81
+ var IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
82
+ var IS_ANDROID = typeof navigator !== 'undefined' && /Android/.test(navigator.userAgent);
83
+ var IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
84
+ var IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); // "modern" Edge was released at 79.x
85
+
86
+ var IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])(?:\.)/i.test(navigator.userAgent);
87
+ var IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent); // Native `beforeInput` events don't work well with react on Chrome 75
88
+ // and older, Chrome 76+ can use `beforeInput` though.
89
+
90
+ var IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])(?:\.)/i.test(navigator.userAgent);
91
+ var IS_ANDROID_CHROME_LEGACY = IS_ANDROID && typeof navigator !== 'undefined' && /Chrome?\/(?:[0-5]?\d)(?:\.)/i.test(navigator.userAgent); // Firefox did not support `beforeInput` until `v87`.
92
+
93
+ var IS_FIREFOX_LEGACY = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox\/(?:[0-7][0-9]|[0-8][0-6])(?:\.)).*/i.test(navigator.userAgent); // UC mobile browser
94
+
95
+ var IS_UC_MOBILE = typeof navigator !== 'undefined' && /.*UCBrowser/.test(navigator.userAgent); // Wechat browser
96
+
97
+ var IS_WECHATBROWSER = typeof navigator !== 'undefined' && /.*Wechat/.test(navigator.userAgent); // Check if DOM is available as React does internally.
98
+ // https://github.com/facebook/react/blob/master/packages/shared/ExecutionEnvironment.js
99
+
100
+ var CAN_USE_DOM = !!(typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined'); // COMPAT: Firefox/Edge Legacy don't support the `beforeinput` event
101
+ // Chrome Legacy doesn't support `beforeinput` correctly
102
+
103
+ var HAS_BEFORE_INPUT_SUPPORT = (!IS_CHROME_LEGACY || !IS_ANDROID_CHROME_LEGACY) && !IS_EDGE_LEGACY && // globalThis is undefined in older browsers
104
+ typeof globalThis !== 'undefined' && globalThis.InputEvent && // @ts-ignore The `getTargetRanges` property isn't recognized.
105
+ typeof globalThis.InputEvent.prototype.getTargetRanges === 'function';
75
106
 
76
107
  /**
77
108
  * Two weak maps that allow us rebuild a path given a node. They are populated
@@ -364,59 +395,128 @@ var isTrackedMutation = (editor, mutation, batch) => {
364
395
  return isTrackedMutation(editor, parentMutation, batch);
365
396
  };
366
397
 
367
- var IS_REACT_VERSION_17_OR_ABOVE = parseInt(React.version.split('.')[0], 10) >= 17;
368
- var IS_IOS = typeof navigator !== 'undefined' && typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
369
- var IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
370
- var IS_ANDROID = typeof navigator !== 'undefined' && /Android/.test(navigator.userAgent);
371
- var IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
372
- var IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); // "modern" Edge was released at 79.x
398
+ /**
399
+ * An auto-incrementing identifier for keys.
400
+ */
401
+ var n = 0;
402
+ /**
403
+ * A class that keeps track of a key string. We use a full class here because we
404
+ * want to be able to use them as keys in `WeakMap` objects.
405
+ */
373
406
 
374
- var IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])(?:\.)/i.test(navigator.userAgent);
375
- var IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent); // Native `beforeInput` events don't work well with react on Chrome 75
376
- // and older, Chrome 76+ can use `beforeInput` though.
407
+ class Key {
408
+ constructor() {
409
+ this.id = "".concat(n++);
410
+ }
377
411
 
378
- var IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])(?:\.)/i.test(navigator.userAgent);
379
- var IS_ANDROID_CHROME_LEGACY = IS_ANDROID && typeof navigator !== 'undefined' && /Chrome?\/(?:[0-5]?\d)(?:\.)/i.test(navigator.userAgent); // Firefox did not support `beforeInput` until `v87`.
412
+ }
380
413
 
381
- var IS_FIREFOX_LEGACY = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox\/(?:[0-7][0-9]|[0-8][0-6])(?:\.)).*/i.test(navigator.userAgent); // UC mobile browser
414
+ var ReactEditor = {
415
+ androidPendingDiffs: editor => EDITOR_TO_PENDING_DIFFS.get(editor),
416
+ androidScheduleFlush: editor => {
417
+ var _EDITOR_TO_SCHEDULE_F;
382
418
 
383
- var IS_UC_MOBILE = typeof navigator !== 'undefined' && /.*UCBrowser/.test(navigator.userAgent); // Wechat browser
419
+ (_EDITOR_TO_SCHEDULE_F = EDITOR_TO_SCHEDULE_FLUSH.get(editor)) === null || _EDITOR_TO_SCHEDULE_F === void 0 ? void 0 : _EDITOR_TO_SCHEDULE_F();
420
+ },
421
+ blur: editor => {
422
+ var el = ReactEditor.toDOMNode(editor, editor);
423
+ var root = ReactEditor.findDocumentOrShadowRoot(editor);
424
+ IS_FOCUSED.set(editor, false);
384
425
 
385
- var IS_WECHATBROWSER = typeof navigator !== 'undefined' && /.*Wechat/.test(navigator.userAgent); // Check if DOM is available as React does internally.
386
- // https://github.com/facebook/react/blob/master/packages/shared/ExecutionEnvironment.js
426
+ if (root.activeElement === el) {
427
+ el.blur();
428
+ }
429
+ },
430
+ deselect: editor => {
431
+ var {
432
+ selection
433
+ } = editor;
434
+ var root = ReactEditor.findDocumentOrShadowRoot(editor);
435
+ var domSelection = root.getSelection();
387
436
 
388
- var CAN_USE_DOM = !!(typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined'); // COMPAT: Firefox/Edge Legacy don't support the `beforeinput` event
389
- // Chrome Legacy doesn't support `beforeinput` correctly
437
+ if (domSelection && domSelection.rangeCount > 0) {
438
+ domSelection.removeAllRanges();
439
+ }
390
440
 
391
- var HAS_BEFORE_INPUT_SUPPORT = (!IS_CHROME_LEGACY || !IS_ANDROID_CHROME_LEGACY) && !IS_EDGE_LEGACY && // globalThis is undefined in older browsers
392
- typeof globalThis !== 'undefined' && globalThis.InputEvent && // @ts-ignore The `getTargetRanges` property isn't recognized.
393
- typeof globalThis.InputEvent.prototype.getTargetRanges === 'function';
441
+ if (selection) {
442
+ Transforms.deselect(editor);
443
+ }
444
+ },
445
+ findDocumentOrShadowRoot: editor => {
446
+ var el = ReactEditor.toDOMNode(editor, editor);
447
+ var root = el.getRootNode();
394
448
 
395
- var ReactEditor = {
396
- /**
397
- * Check if the user is currently composing inside the editor.
398
- */
399
- isComposing(editor) {
400
- return !!IS_COMPOSING.get(editor);
449
+ if ((root instanceof Document || root instanceof ShadowRoot) && root.getSelection != null) {
450
+ return root;
451
+ }
452
+
453
+ return el.ownerDocument;
401
454
  },
455
+ findEventRange: (editor, event) => {
456
+ if ('nativeEvent' in event) {
457
+ event = event.nativeEvent;
458
+ }
402
459
 
403
- /**
404
- * Return the host window of the current editor.
405
- */
406
- getWindow(editor) {
407
- var window = EDITOR_TO_WINDOW.get(editor);
460
+ var {
461
+ clientX: x,
462
+ clientY: y,
463
+ target
464
+ } = event;
408
465
 
409
- if (!window) {
410
- throw new Error('Unable to find a host window element for this editor');
466
+ if (x == null || y == null) {
467
+ throw new Error("Cannot resolve a Slate range from a DOM event: ".concat(event));
411
468
  }
412
469
 
413
- return window;
414
- },
470
+ var node = ReactEditor.toSlateNode(editor, event.target);
471
+ var path = ReactEditor.findPath(editor, node); // If the drop target is inside a void node, move it into either the
472
+ // next or previous node, depending on which side the `x` and `y`
473
+ // coordinates are closest to.
415
474
 
416
- /**
417
- * Find a key for a Slate node.
418
- */
419
- findKey(editor, node) {
475
+ if (Element$1.isElement(node) && Editor.isVoid(editor, node)) {
476
+ var rect = target.getBoundingClientRect();
477
+ var isPrev = editor.isInline(node) ? x - rect.left < rect.left + rect.width - x : y - rect.top < rect.top + rect.height - y;
478
+ var edge = Editor.point(editor, path, {
479
+ edge: isPrev ? 'start' : 'end'
480
+ });
481
+ var point = isPrev ? Editor.before(editor, edge) : Editor.after(editor, edge);
482
+
483
+ if (point) {
484
+ var _range = Editor.range(editor, point);
485
+
486
+ return _range;
487
+ }
488
+ } // Else resolve a range from the caret position where the drop occured.
489
+
490
+
491
+ var domRange;
492
+ var {
493
+ document
494
+ } = ReactEditor.getWindow(editor); // COMPAT: In Firefox, `caretRangeFromPoint` doesn't exist. (2016/07/25)
495
+
496
+ if (document.caretRangeFromPoint) {
497
+ domRange = document.caretRangeFromPoint(x, y);
498
+ } else {
499
+ var position = document.caretPositionFromPoint(x, y);
500
+
501
+ if (position) {
502
+ domRange = document.createRange();
503
+ domRange.setStart(position.offsetNode, position.offset);
504
+ domRange.setEnd(position.offsetNode, position.offset);
505
+ }
506
+ }
507
+
508
+ if (!domRange) {
509
+ throw new Error("Cannot resolve a Slate range from a DOM event: ".concat(event));
510
+ } // Resolve a Slate range from the DOM range.
511
+
512
+
513
+ var range = ReactEditor.toSlateRange(editor, domRange, {
514
+ exactMatch: false,
515
+ suppressThrow: false
516
+ });
517
+ return range;
518
+ },
519
+ findKey: (editor, node) => {
420
520
  var key = NODE_TO_KEY.get(node);
421
521
 
422
522
  if (!key) {
@@ -426,11 +526,7 @@ var ReactEditor = {
426
526
 
427
527
  return key;
428
528
  },
429
-
430
- /**
431
- * Find the path of Slate node.
432
- */
433
- findPath(editor, node) {
529
+ findPath: (editor, node) => {
434
530
  var path = [];
435
531
  var child = node;
436
532
 
@@ -457,52 +553,7 @@ var ReactEditor = {
457
553
 
458
554
  throw new Error("Unable to find the path for Slate node: ".concat(Scrubber.stringify(node)));
459
555
  },
460
-
461
- /**
462
- * Find the DOM node that implements DocumentOrShadowRoot for the editor.
463
- */
464
- findDocumentOrShadowRoot(editor) {
465
- var el = ReactEditor.toDOMNode(editor, editor);
466
- var root = el.getRootNode();
467
-
468
- if ((root instanceof Document || root instanceof ShadowRoot) && root.getSelection != null) {
469
- return root;
470
- }
471
-
472
- return el.ownerDocument;
473
- },
474
-
475
- /**
476
- * Check if the editor is focused.
477
- */
478
- isFocused(editor) {
479
- return !!IS_FOCUSED.get(editor);
480
- },
481
-
482
- /**
483
- * Check if the editor is in read-only mode.
484
- */
485
- isReadOnly(editor) {
486
- return !!IS_READ_ONLY.get(editor);
487
- },
488
-
489
- /**
490
- * Blur the editor.
491
- */
492
- blur(editor) {
493
- var el = ReactEditor.toDOMNode(editor, editor);
494
- var root = ReactEditor.findDocumentOrShadowRoot(editor);
495
- IS_FOCUSED.set(editor, false);
496
-
497
- if (root.activeElement === el) {
498
- el.blur();
499
- }
500
- },
501
-
502
- /**
503
- * Focus the editor.
504
- */
505
- focus(editor) {
556
+ focus: editor => {
506
557
  var el = ReactEditor.toDOMNode(editor, editor);
507
558
  var root = ReactEditor.findDocumentOrShadowRoot(editor);
508
559
  IS_FOCUSED.set(editor, true);
@@ -513,30 +564,16 @@ var ReactEditor = {
513
564
  });
514
565
  }
515
566
  },
567
+ getWindow: editor => {
568
+ var window = EDITOR_TO_WINDOW.get(editor);
516
569
 
517
- /**
518
- * Deselect the editor.
519
- */
520
- deselect(editor) {
521
- var {
522
- selection
523
- } = editor;
524
- var root = ReactEditor.findDocumentOrShadowRoot(editor);
525
- var domSelection = root.getSelection();
526
-
527
- if (domSelection && domSelection.rangeCount > 0) {
528
- domSelection.removeAllRanges();
570
+ if (!window) {
571
+ throw new Error('Unable to find a host window element for this editor');
529
572
  }
530
573
 
531
- if (selection) {
532
- Transforms.deselect(editor);
533
- }
574
+ return window;
534
575
  },
535
-
536
- /**
537
- * Check if a DOM node is within the editor.
538
- */
539
- hasDOMNode(editor, target) {
576
+ hasDOMNode: function hasDOMNode(editor, target) {
540
577
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
541
578
  var {
542
579
  editable = false
@@ -563,39 +600,35 @@ var ReactEditor = {
563
600
  // this is the core logic that lets you know you got the right editor.selection instead of null when editor is contenteditable="false"(readOnly)
564
601
  targetEl.closest('[contenteditable="false"]') === editorEl || !!targetEl.getAttribute('data-slate-zero-width'));
565
602
  },
566
-
567
- /**
568
- * Insert data from a `DataTransfer` into the editor.
569
- */
570
- insertData(editor, data) {
571
- editor.insertData(data);
603
+ hasEditableTarget: (editor, target) => isDOMNode(target) && ReactEditor.hasDOMNode(editor, target, {
604
+ editable: true
605
+ }),
606
+ hasRange: (editor, range) => {
607
+ var {
608
+ anchor,
609
+ focus
610
+ } = range;
611
+ return Editor.hasPath(editor, anchor.path) && Editor.hasPath(editor, focus.path);
572
612
  },
573
-
574
- /**
575
- * Insert fragment data from a `DataTransfer` into the editor.
576
- */
577
- insertFragmentData(editor, data) {
578
- return editor.insertFragmentData(data);
613
+ hasSelectableTarget: (editor, target) => ReactEditor.hasEditableTarget(editor, target) || ReactEditor.isTargetInsideNonReadonlyVoid(editor, target),
614
+ hasTarget: (editor, target) => isDOMNode(target) && ReactEditor.hasDOMNode(editor, target),
615
+ insertData: (editor, data) => {
616
+ editor.insertData(data);
579
617
  },
580
-
581
- /**
582
- * Insert text data from a `DataTransfer` into the editor.
583
- */
584
- insertTextData(editor, data) {
585
- return editor.insertTextData(data);
618
+ insertFragmentData: (editor, data) => editor.insertFragmentData(data),
619
+ insertTextData: (editor, data) => editor.insertTextData(data),
620
+ isComposing: editor => {
621
+ return !!IS_COMPOSING.get(editor);
586
622
  },
587
-
588
- /**
589
- * Sets data from the currently selected fragment on a `DataTransfer`.
590
- */
591
- setFragmentData(editor, data, originEvent) {
592
- editor.setFragmentData(data, originEvent);
623
+ isFocused: editor => !!IS_FOCUSED.get(editor),
624
+ isReadOnly: editor => !!IS_READ_ONLY.get(editor),
625
+ isTargetInsideNonReadonlyVoid: (editor, target) => {
626
+ if (IS_READ_ONLY.get(editor)) return false;
627
+ var slateNode = ReactEditor.hasTarget(editor, target) && ReactEditor.toSlateNode(editor, target);
628
+ return Element$1.isElement(slateNode) && Editor.isVoid(editor, slateNode);
593
629
  },
594
-
595
- /**
596
- * Find the native DOM element from a Slate node.
597
- */
598
- toDOMNode(editor, node) {
630
+ setFragmentData: (editor, data, originEvent) => editor.setFragmentData(data, originEvent),
631
+ toDOMNode: (editor, node) => {
599
632
  var KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor);
600
633
  var domNode = Editor.isEditor(node) ? EDITOR_TO_ELEMENT.get(editor) : KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.get(ReactEditor.findKey(editor, node));
601
634
 
@@ -605,11 +638,7 @@ var ReactEditor = {
605
638
 
606
639
  return domNode;
607
640
  },
608
-
609
- /**
610
- * Find a native DOM selection point from a Slate point.
611
- */
612
- toDOMPoint(editor, point) {
641
+ toDOMPoint: (editor, point) => {
613
642
  var [node] = Editor.node(editor, point.path);
614
643
  var el = ReactEditor.toDOMNode(editor, node);
615
644
  var domPoint; // If we're inside a void node, force the offset to 0, otherwise the zero
@@ -677,16 +706,7 @@ var ReactEditor = {
677
706
 
678
707
  return domPoint;
679
708
  },
680
-
681
- /**
682
- * Find a native DOM range from a Slate `range`.
683
- *
684
- * Notice: the returned range will always be ordinal regardless of the direction of Slate `range` due to DOM API limit.
685
- *
686
- * there is no way to create a reverse DOM Range using Range.setStart/setEnd
687
- * according to https://dom.spec.whatwg.org/#concept-range-bp-set.
688
- */
689
- toDOMRange(editor, range) {
709
+ toDOMRange: (editor, range) => {
690
710
  var {
691
711
  anchor,
692
712
  focus
@@ -709,11 +729,7 @@ var ReactEditor = {
709
729
  domRange.setEnd(endNode, isEndAtZeroWidth ? 1 : endOffset);
710
730
  return domRange;
711
731
  },
712
-
713
- /**
714
- * Find a Slate node from a native DOM `element`.
715
- */
716
- toSlateNode(editor, domNode) {
732
+ toSlateNode: (editor, domNode) => {
717
733
  var domEl = isDOMElement(domNode) ? domNode : domNode.parentElement;
718
734
 
719
735
  if (domEl && !domEl.hasAttribute('data-slate-node')) {
@@ -728,79 +744,7 @@ var ReactEditor = {
728
744
 
729
745
  return node;
730
746
  },
731
-
732
- /**
733
- * Get the target range from a DOM `event`.
734
- */
735
- findEventRange(editor, event) {
736
- if ('nativeEvent' in event) {
737
- event = event.nativeEvent;
738
- }
739
-
740
- var {
741
- clientX: x,
742
- clientY: y,
743
- target
744
- } = event;
745
-
746
- if (x == null || y == null) {
747
- throw new Error("Cannot resolve a Slate range from a DOM event: ".concat(event));
748
- }
749
-
750
- var node = ReactEditor.toSlateNode(editor, event.target);
751
- var path = ReactEditor.findPath(editor, node); // If the drop target is inside a void node, move it into either the
752
- // next or previous node, depending on which side the `x` and `y`
753
- // coordinates are closest to.
754
-
755
- if (Element$1.isElement(node) && Editor.isVoid(editor, node)) {
756
- var rect = target.getBoundingClientRect();
757
- var isPrev = editor.isInline(node) ? x - rect.left < rect.left + rect.width - x : y - rect.top < rect.top + rect.height - y;
758
- var edge = Editor.point(editor, path, {
759
- edge: isPrev ? 'start' : 'end'
760
- });
761
- var point = isPrev ? Editor.before(editor, edge) : Editor.after(editor, edge);
762
-
763
- if (point) {
764
- var _range = Editor.range(editor, point);
765
-
766
- return _range;
767
- }
768
- } // Else resolve a range from the caret position where the drop occured.
769
-
770
-
771
- var domRange;
772
- var {
773
- document
774
- } = ReactEditor.getWindow(editor); // COMPAT: In Firefox, `caretRangeFromPoint` doesn't exist. (2016/07/25)
775
-
776
- if (document.caretRangeFromPoint) {
777
- domRange = document.caretRangeFromPoint(x, y);
778
- } else {
779
- var position = document.caretPositionFromPoint(x, y);
780
-
781
- if (position) {
782
- domRange = document.createRange();
783
- domRange.setStart(position.offsetNode, position.offset);
784
- domRange.setEnd(position.offsetNode, position.offset);
785
- }
786
- }
787
-
788
- if (!domRange) {
789
- throw new Error("Cannot resolve a Slate range from a DOM event: ".concat(event));
790
- } // Resolve a Slate range from the DOM range.
791
-
792
-
793
- var range = ReactEditor.toSlateRange(editor, domRange, {
794
- exactMatch: false,
795
- suppressThrow: false
796
- });
797
- return range;
798
- },
799
-
800
- /**
801
- * Find a Slate point from a DOM selection's `domNode` and `domOffset`.
802
- */
803
- toSlatePoint(editor, domPoint, options) {
747
+ toSlatePoint: (editor, domPoint, options) => {
804
748
  var {
805
749
  exactMatch,
806
750
  suppressThrow
@@ -932,11 +876,7 @@ var ReactEditor = {
932
876
  offset
933
877
  };
934
878
  },
935
-
936
- /**
937
- * Find a Slate range from a DOM range or selection.
938
- */
939
- toSlateRange(editor, domRange, options) {
879
+ toSlateRange: (editor, domRange, options) => {
940
880
  var {
941
881
  exactMatch,
942
882
  suppressThrow
@@ -1062,2199 +1002,2123 @@ var ReactEditor = {
1062
1002
  }
1063
1003
 
1064
1004
  return range;
1065
- },
1066
-
1067
- hasRange(editor, range) {
1068
- var {
1069
- anchor,
1070
- focus
1071
- } = range;
1072
- return Editor.hasPath(editor, anchor.path) && Editor.hasPath(editor, focus.path);
1073
- },
1074
-
1075
- /**
1076
- * Check if the target is in the editor.
1077
- */
1078
- hasTarget(editor, target) {
1079
- return isDOMNode(target) && ReactEditor.hasDOMNode(editor, target);
1080
- },
1081
-
1082
- /**
1083
- * Check if the target is editable and in the editor.
1084
- */
1085
- hasEditableTarget(editor, target) {
1086
- return isDOMNode(target) && ReactEditor.hasDOMNode(editor, target, {
1087
- editable: true
1088
- });
1089
- },
1005
+ }
1006
+ };
1090
1007
 
1091
- /**
1092
- * Check if the target can be selectable
1093
- */
1094
- hasSelectableTarget(editor, target) {
1095
- return ReactEditor.hasEditableTarget(editor, target) || ReactEditor.isTargetInsideNonReadonlyVoid(editor, target);
1096
- },
1008
+ /**
1009
+ * Check whether a text diff was applied in a way we can perform the pending action on /
1010
+ * recover the pending selection.
1011
+ */
1097
1012
 
1098
- /**
1099
- * Check if the target is inside void and in an non-readonly editor.
1100
- */
1101
- isTargetInsideNonReadonlyVoid(editor, target) {
1102
- if (IS_READ_ONLY.get(editor)) return false;
1103
- var slateNode = ReactEditor.hasTarget(editor, target) && ReactEditor.toSlateNode(editor, target);
1104
- return Element$1.isElement(slateNode) && Editor.isVoid(editor, slateNode);
1105
- },
1013
+ function verifyDiffState(editor, textDiff) {
1014
+ var {
1015
+ path,
1016
+ diff
1017
+ } = textDiff;
1106
1018
 
1107
- /**
1108
- * Experimental and android specific: Flush all pending diffs and cancel composition at the next possible time.
1109
- */
1110
- androidScheduleFlush(editor) {
1111
- var _EDITOR_TO_SCHEDULE_F;
1019
+ if (!Editor.hasPath(editor, path)) {
1020
+ return false;
1021
+ }
1112
1022
 
1113
- (_EDITOR_TO_SCHEDULE_F = EDITOR_TO_SCHEDULE_FLUSH.get(editor)) === null || _EDITOR_TO_SCHEDULE_F === void 0 ? void 0 : _EDITOR_TO_SCHEDULE_F();
1114
- },
1023
+ var node = Node.get(editor, path);
1115
1024
 
1116
- /**
1117
- * Experimental and android specific: Get pending diffs
1118
- */
1119
- androidPendingDiffs(editor) {
1120
- return EDITOR_TO_PENDING_DIFFS.get(editor);
1025
+ if (!Text$1.isText(node)) {
1026
+ return false;
1121
1027
  }
1122
1028
 
1123
- };
1029
+ if (diff.start !== node.text.length || diff.text.length === 0) {
1030
+ return node.text.slice(diff.start, diff.start + diff.text.length) === diff.text;
1031
+ }
1124
1032
 
1125
- var _excluded$3 = ["anchor", "focus"],
1126
- _excluded2$1 = ["anchor", "focus"];
1127
- var shallowCompare = (obj1, obj2) => Object.keys(obj1).length === Object.keys(obj2).length && Object.keys(obj1).every(key => obj2.hasOwnProperty(key) && obj1[key] === obj2[key]);
1033
+ var nextPath = Path.next(path);
1128
1034
 
1129
- var isDecorationFlagsEqual = (range, other) => {
1130
- var rangeOwnProps = _objectWithoutProperties(range, _excluded$3);
1035
+ if (!Editor.hasPath(editor, nextPath)) {
1036
+ return false;
1037
+ }
1131
1038
 
1132
- var otherOwnProps = _objectWithoutProperties(other, _excluded2$1);
1039
+ var nextNode = Node.get(editor, nextPath);
1040
+ return Text$1.isText(nextNode) && nextNode.text.startsWith(diff.text);
1041
+ }
1042
+ function applyStringDiff(text) {
1043
+ for (var _len = arguments.length, diffs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
1044
+ diffs[_key - 1] = arguments[_key];
1045
+ }
1133
1046
 
1134
- return range[PLACEHOLDER_SYMBOL] === other[PLACEHOLDER_SYMBOL] && shallowCompare(rangeOwnProps, otherOwnProps);
1135
- };
1136
- /**
1137
- * Check if a list of decorator ranges are equal to another.
1138
- *
1139
- * PERF: this requires the two lists to also have the ranges inside them in the
1140
- * same order, but this is an okay constraint for us since decorations are
1141
- * kept in order, and the odd case where they aren't is okay to re-render for.
1142
- */
1047
+ return diffs.reduce((text, diff) => text.slice(0, diff.start) + diff.text + text.slice(diff.end), text);
1048
+ }
1143
1049
 
1050
+ function longestCommonPrefixLength(str, another) {
1051
+ var length = Math.min(str.length, another.length);
1144
1052
 
1145
- var isElementDecorationsEqual = (list, another) => {
1146
- if (list.length !== another.length) {
1147
- return false;
1053
+ for (var i = 0; i < length; i++) {
1054
+ if (str.charAt(i) !== another.charAt(i)) {
1055
+ return i;
1056
+ }
1148
1057
  }
1149
1058
 
1150
- for (var i = 0; i < list.length; i++) {
1151
- var range = list[i];
1152
- var other = another[i];
1059
+ return length;
1060
+ }
1153
1061
 
1154
- if (!Range.equals(range, other) || !isDecorationFlagsEqual(range, other)) {
1155
- return false;
1062
+ function longestCommonSuffixLength(str, another, max) {
1063
+ var length = Math.min(str.length, another.length, max);
1064
+
1065
+ for (var i = 0; i < length; i++) {
1066
+ if (str.charAt(str.length - i - 1) !== another.charAt(another.length - i - 1)) {
1067
+ return i;
1156
1068
  }
1157
1069
  }
1158
1070
 
1159
- return true;
1160
- };
1071
+ return length;
1072
+ }
1161
1073
  /**
1162
- * Check if a list of decorator ranges are equal to another.
1163
- *
1164
- * PERF: this requires the two lists to also have the ranges inside them in the
1165
- * same order, but this is an okay constraint for us since decorations are
1166
- * kept in order, and the odd case where they aren't is okay to re-render for.
1074
+ * Remove redundant changes from the diff so that it spans the minimal possible range
1167
1075
  */
1168
1076
 
1169
- var isTextDecorationsEqual = (list, another) => {
1170
- if (list.length !== another.length) {
1171
- return false;
1172
- }
1173
1077
 
1174
- for (var i = 0; i < list.length; i++) {
1175
- var range = list[i];
1176
- var other = another[i]; // compare only offsets because paths doesn't matter for text
1078
+ function normalizeStringDiff(targetText, diff) {
1079
+ var {
1080
+ start,
1081
+ end,
1082
+ text
1083
+ } = diff;
1084
+ var removedText = targetText.slice(start, end);
1085
+ var prefixLength = longestCommonPrefixLength(removedText, text);
1086
+ var max = Math.min(removedText.length - prefixLength, text.length - prefixLength);
1087
+ var suffixLength = longestCommonSuffixLength(removedText, text, max);
1088
+ var normalized = {
1089
+ start: start + prefixLength,
1090
+ end: end - suffixLength,
1091
+ text: text.slice(prefixLength, text.length - suffixLength)
1092
+ };
1177
1093
 
1178
- if (range.anchor.offset !== other.anchor.offset || range.focus.offset !== other.focus.offset || !isDecorationFlagsEqual(range, other)) {
1179
- return false;
1180
- }
1094
+ if (normalized.start === normalized.end && normalized.text.length === 0) {
1095
+ return null;
1181
1096
  }
1182
1097
 
1183
- return true;
1184
- };
1185
-
1098
+ return normalized;
1099
+ }
1186
1100
  /**
1187
- * Prevent warning on SSR by falling back to useEffect when DOM isn't available
1101
+ * Return a string diff that is equivalent to applying b after a spanning the range of
1102
+ * both changes
1188
1103
  */
1189
1104
 
1190
- var useIsomorphicLayoutEffect = CAN_USE_DOM ? useLayoutEffect : useEffect;
1191
-
1105
+ function mergeStringDiffs(targetText, a, b) {
1106
+ var start = Math.min(a.start, b.start);
1107
+ var overlap = Math.max(0, Math.min(a.start + a.text.length, b.end) - b.start);
1108
+ var applied = applyStringDiff(targetText, a, b);
1109
+ var sliceEnd = Math.max(b.start + b.text.length, a.start + a.text.length + (a.start + a.text.length > b.start ? b.text.length : 0) - overlap);
1110
+ var text = applied.slice(start, sliceEnd);
1111
+ var end = Math.max(a.end, b.end - a.text.length + (a.end - a.start));
1112
+ return normalizeStringDiff(targetText, {
1113
+ start,
1114
+ end,
1115
+ text
1116
+ });
1117
+ }
1192
1118
  /**
1193
- * Leaf content strings.
1119
+ * Get the slate range the text diff spans.
1194
1120
  */
1195
1121
 
1196
- var String = props => {
1122
+ function targetRange(textDiff) {
1197
1123
  var {
1198
- isLast,
1199
- leaf,
1200
- parent,
1201
- text
1202
- } = props;
1203
- var editor = useSlateStatic();
1204
- var path = ReactEditor.findPath(editor, text);
1205
- var parentPath = Path.parent(path);
1206
- var isMarkPlaceholder = leaf[MARK_PLACEHOLDER_SYMBOL] === true; // COMPAT: Render text inside void nodes with a zero-width space.
1207
- // So the node can contain selection but the text is not visible.
1208
-
1209
- if (editor.isVoid(parent)) {
1210
- return /*#__PURE__*/React.createElement(ZeroWidthString, {
1211
- length: Node.string(parent).length
1212
- });
1213
- } // COMPAT: If this is the last text node in an empty block, render a zero-
1214
- // width space that will convert into a line break when copying and pasting
1215
- // to support expected plain text.
1124
+ path,
1125
+ diff
1126
+ } = textDiff;
1127
+ return {
1128
+ anchor: {
1129
+ path,
1130
+ offset: diff.start
1131
+ },
1132
+ focus: {
1133
+ path,
1134
+ offset: diff.end
1135
+ }
1136
+ };
1137
+ }
1138
+ /**
1139
+ * Normalize a 'pending point' a.k.a a point based on the dom state before applying
1140
+ * the pending diffs. Since the pending diffs might have been inserted with different
1141
+ * marks we have to 'walk' the offset from the starting position to ensure we still
1142
+ * have a valid point inside the document
1143
+ */
1216
1144
 
1145
+ function normalizePoint(editor, point) {
1146
+ var {
1147
+ path,
1148
+ offset
1149
+ } = point;
1217
1150
 
1218
- if (leaf.text === '' && parent.children[parent.children.length - 1] === text && !editor.isInline(parent) && Editor.string(editor, parentPath) === '') {
1219
- return /*#__PURE__*/React.createElement(ZeroWidthString, {
1220
- isLineBreak: true,
1221
- isMarkPlaceholder: isMarkPlaceholder
1222
- });
1223
- } // COMPAT: If the text is empty, it's because it's on the edge of an inline
1224
- // node, so we render a zero-width space so that the selection can be
1225
- // inserted next to it still.
1151
+ if (!Editor.hasPath(editor, path)) {
1152
+ return null;
1153
+ }
1226
1154
 
1155
+ var leaf = Node.get(editor, path);
1227
1156
 
1228
- if (leaf.text === '') {
1229
- return /*#__PURE__*/React.createElement(ZeroWidthString, {
1230
- isMarkPlaceholder: isMarkPlaceholder
1231
- });
1232
- } // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
1233
- // so we need to add an extra trailing new lines to prevent that.
1157
+ if (!Text$1.isText(leaf)) {
1158
+ return null;
1159
+ }
1234
1160
 
1161
+ var parentBlock = Editor.above(editor, {
1162
+ match: n => Element$1.isElement(n) && Editor.isBlock(editor, n),
1163
+ at: path
1164
+ });
1235
1165
 
1236
- if (isLast && leaf.text.slice(-1) === '\n') {
1237
- return /*#__PURE__*/React.createElement(TextString, {
1238
- isTrailing: true,
1239
- text: leaf.text
1240
- });
1166
+ if (!parentBlock) {
1167
+ return null;
1241
1168
  }
1242
1169
 
1243
- return /*#__PURE__*/React.createElement(TextString, {
1244
- text: leaf.text
1245
- });
1246
- };
1247
- /**
1248
- * Leaf strings with text in them.
1249
- */
1170
+ while (offset > leaf.text.length) {
1171
+ var entry = Editor.next(editor, {
1172
+ at: path,
1173
+ match: Text$1.isText
1174
+ });
1250
1175
 
1176
+ if (!entry || !Path.isDescendant(entry[1], parentBlock[1])) {
1177
+ return null;
1178
+ }
1251
1179
 
1252
- var TextString = props => {
1253
- var {
1254
- text,
1255
- isTrailing = false
1256
- } = props;
1257
- var ref = useRef(null);
1180
+ offset -= leaf.text.length;
1181
+ leaf = entry[0];
1182
+ path = entry[1];
1183
+ }
1258
1184
 
1259
- var getTextContent = () => {
1260
- return "".concat(text !== null && text !== void 0 ? text : '').concat(isTrailing ? '\n' : '');
1185
+ return {
1186
+ path,
1187
+ offset
1261
1188
  };
1189
+ }
1190
+ /**
1191
+ * Normalize a 'pending selection' to ensure it's valid in the current document state.
1192
+ */
1262
1193
 
1263
- var [initialText] = useState(getTextContent); // This is the actual text rendering boundary where we interface with the DOM
1264
- // The text is not rendered as part of the virtual DOM, as since we handle basic character insertions natively,
1265
- // updating the DOM is not a one way dataflow anymore. What we need here is not reconciliation and diffing
1266
- // with previous version of the virtual DOM, but rather diffing with the actual DOM element, and replace the DOM <span> content
1267
- // exactly if and only if its current content does not match our current virtual DOM.
1268
- // Otherwise the DOM TextNode would always be replaced by React as the user types, which interferes with native text features,
1269
- // eg makes native spellcheck opt out from checking the text node.
1270
- // useLayoutEffect: updating our span before browser paint
1271
-
1272
- useIsomorphicLayoutEffect(() => {
1273
- // null coalescing text to make sure we're not outputing "null" as a string in the extreme case it is nullish at runtime
1274
- var textWithTrailing = getTextContent();
1194
+ function normalizeRange(editor, range) {
1195
+ var anchor = normalizePoint(editor, range.anchor);
1275
1196
 
1276
- if (ref.current && ref.current.textContent !== textWithTrailing) {
1277
- ref.current.textContent = textWithTrailing;
1278
- } // intentionally not specifying dependencies, so that this effect runs on every render
1279
- // as this effectively replaces "specifying the text in the virtual DOM under the <span> below" on each render
1197
+ if (!anchor) {
1198
+ return null;
1199
+ }
1280
1200
 
1281
- }); // We intentionally render a memoized <span> that only receives the initial text content when the component is mounted.
1282
- // We defer to the layout effect above to update the `textContent` of the span element when needed.
1201
+ if (Range.isCollapsed(range)) {
1202
+ return {
1203
+ anchor,
1204
+ focus: anchor
1205
+ };
1206
+ }
1283
1207
 
1284
- return /*#__PURE__*/React.createElement(MemoizedText$1, {
1285
- ref: ref
1286
- }, initialText);
1287
- };
1208
+ var focus = normalizePoint(editor, range.focus);
1288
1209
 
1289
- var MemoizedText$1 = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
1290
- return /*#__PURE__*/React.createElement("span", {
1291
- "data-slate-string": true,
1292
- ref: ref
1293
- }, props.children);
1294
- }));
1295
- /**
1296
- * Leaf strings without text, render as zero-width strings.
1297
- */
1210
+ if (!focus) {
1211
+ return null;
1212
+ }
1298
1213
 
1299
- var ZeroWidthString = props => {
1300
- var {
1301
- length = 0,
1302
- isLineBreak = false,
1303
- isMarkPlaceholder = false
1304
- } = props;
1305
- var attributes = {
1306
- 'data-slate-zero-width': isLineBreak ? 'n' : 'z',
1307
- 'data-slate-length': length
1214
+ return {
1215
+ anchor,
1216
+ focus
1308
1217
  };
1218
+ }
1219
+ function transformPendingPoint(editor, point, op) {
1220
+ var pendingDiffs = EDITOR_TO_PENDING_DIFFS.get(editor);
1221
+ var textDiff = pendingDiffs === null || pendingDiffs === void 0 ? void 0 : pendingDiffs.find(_ref => {
1222
+ var {
1223
+ path
1224
+ } = _ref;
1225
+ return Path.equals(path, point.path);
1226
+ });
1309
1227
 
1310
- if (isMarkPlaceholder) {
1311
- attributes['data-slate-mark-placeholder'] = true;
1228
+ if (!textDiff || point.offset <= textDiff.diff.start) {
1229
+ return Point.transform(point, op, {
1230
+ affinity: 'backward'
1231
+ });
1312
1232
  }
1313
1233
 
1314
- return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes), !IS_ANDROID || !isLineBreak ? '\uFEFF' : null, isLineBreak ? /*#__PURE__*/React.createElement("br", null) : null);
1315
- };
1234
+ var {
1235
+ diff
1236
+ } = textDiff; // Point references location inside the diff => transform the point based on the location
1237
+ // the diff will be applied to and add the offset inside the diff.
1316
1238
 
1317
- /**
1318
- * A React context for sharing the editor object.
1319
- */
1239
+ if (point.offset <= diff.start + diff.text.length) {
1240
+ var _anchor = {
1241
+ path: point.path,
1242
+ offset: diff.start
1243
+ };
1320
1244
 
1321
- var EditorContext = /*#__PURE__*/createContext(null);
1322
- /**
1323
- * Get the current editor object from the React context.
1324
- */
1245
+ var _transformed = Point.transform(_anchor, op, {
1246
+ affinity: 'backward'
1247
+ });
1325
1248
 
1326
- var useSlateStatic = () => {
1327
- var editor = useContext(EditorContext);
1249
+ if (!_transformed) {
1250
+ return null;
1251
+ }
1328
1252
 
1329
- if (!editor) {
1330
- throw new Error("The `useSlateStatic` hook must be used inside the <Slate> component's context.");
1331
- }
1253
+ return {
1254
+ path: _transformed.path,
1255
+ offset: _transformed.offset + point.offset - diff.start
1256
+ };
1257
+ } // Point references location after the diff
1332
1258
 
1333
- return editor;
1334
- };
1335
1259
 
1336
- function disconnectPlaceholderResizeObserver(placeholderResizeObserver, releaseObserver) {
1337
- if (placeholderResizeObserver.current) {
1338
- placeholderResizeObserver.current.disconnect();
1260
+ var anchor = {
1261
+ path: point.path,
1262
+ offset: point.offset - diff.text.length + diff.end - diff.start
1263
+ };
1264
+ var transformed = Point.transform(anchor, op, {
1265
+ affinity: 'backward'
1266
+ });
1339
1267
 
1340
- if (releaseObserver) {
1341
- placeholderResizeObserver.current = null;
1342
- }
1268
+ if (!transformed) {
1269
+ return null;
1343
1270
  }
1344
- }
1345
1271
 
1346
- function clearTimeoutRef(timeoutRef) {
1347
- if (timeoutRef.current) {
1348
- clearTimeout(timeoutRef.current);
1349
- timeoutRef.current = null;
1272
+ if (op.type === 'split_node' && Path.equals(op.path, point.path) && anchor.offset < op.position && diff.start < op.position) {
1273
+ return transformed;
1350
1274
  }
1275
+
1276
+ return {
1277
+ path: transformed.path,
1278
+ offset: transformed.offset + diff.text.length - diff.end + diff.start
1279
+ };
1351
1280
  }
1352
- /**
1353
- * Individual leaves in a text node with unique formatting.
1354
- */
1281
+ function transformPendingRange(editor, range, op) {
1282
+ var anchor = transformPendingPoint(editor, range.anchor, op);
1355
1283
 
1284
+ if (!anchor) {
1285
+ return null;
1286
+ }
1356
1287
 
1357
- var Leaf = props => {
1358
- var {
1359
- leaf,
1360
- isLast,
1361
- text,
1362
- parent,
1363
- renderPlaceholder,
1364
- renderLeaf = props => /*#__PURE__*/React.createElement(DefaultLeaf, Object.assign({}, props))
1365
- } = props;
1366
- var editor = useSlateStatic();
1367
- var placeholderResizeObserver = useRef(null);
1368
- var placeholderRef = useRef(null);
1369
- var [showPlaceholder, setShowPlaceholder] = useState(false);
1370
- var showPlaceholderTimeoutRef = useRef(null);
1371
- var callbackPlaceholderRef = useCallback(placeholderEl => {
1372
- disconnectPlaceholderResizeObserver(placeholderResizeObserver, placeholderEl == null);
1373
-
1374
- if (placeholderEl == null) {
1375
- var _leaf$onPlaceholderRe;
1376
-
1377
- EDITOR_TO_PLACEHOLDER_ELEMENT.delete(editor);
1378
- (_leaf$onPlaceholderRe = leaf.onPlaceholderResize) === null || _leaf$onPlaceholderRe === void 0 ? void 0 : _leaf$onPlaceholderRe.call(leaf, null);
1379
- } else {
1380
- EDITOR_TO_PLACEHOLDER_ELEMENT.set(editor, placeholderEl);
1381
-
1382
- if (!placeholderResizeObserver.current) {
1383
- // Create a new observer and observe the placeholder element.
1384
- var ResizeObserver$1 = window.ResizeObserver || ResizeObserver;
1385
- placeholderResizeObserver.current = new ResizeObserver$1(() => {
1386
- var _leaf$onPlaceholderRe2;
1387
-
1388
- (_leaf$onPlaceholderRe2 = leaf.onPlaceholderResize) === null || _leaf$onPlaceholderRe2 === void 0 ? void 0 : _leaf$onPlaceholderRe2.call(leaf, placeholderEl);
1389
- });
1390
- }
1391
-
1392
- placeholderResizeObserver.current.observe(placeholderEl);
1393
- placeholderRef.current = placeholderEl;
1394
- }
1395
- }, [placeholderRef, leaf, editor]);
1396
- var children = /*#__PURE__*/React.createElement(String, {
1397
- isLast: isLast,
1398
- leaf: leaf,
1399
- parent: parent,
1400
- text: text
1401
- });
1402
- var leafIsPlaceholder = leaf[PLACEHOLDER_SYMBOL];
1403
- useEffect(() => {
1404
- if (leafIsPlaceholder) {
1405
- if (!showPlaceholderTimeoutRef.current) {
1406
- // Delay the placeholder so it will not render in a selection
1407
- showPlaceholderTimeoutRef.current = setTimeout(() => {
1408
- setShowPlaceholder(true);
1409
- showPlaceholderTimeoutRef.current = null;
1410
- }, 300);
1411
- }
1412
- } else {
1413
- clearTimeoutRef(showPlaceholderTimeoutRef);
1414
- setShowPlaceholder(false);
1415
- }
1416
-
1417
- return () => clearTimeoutRef(showPlaceholderTimeoutRef);
1418
- }, [leafIsPlaceholder, setShowPlaceholder]);
1419
-
1420
- if (leafIsPlaceholder && showPlaceholder) {
1421
- var placeholderProps = {
1422
- children: leaf.placeholder,
1423
- attributes: {
1424
- 'data-slate-placeholder': true,
1425
- style: {
1426
- position: 'absolute',
1427
- pointerEvents: 'none',
1428
- width: '100%',
1429
- maxWidth: '100%',
1430
- display: 'block',
1431
- opacity: '0.333',
1432
- userSelect: 'none',
1433
- textDecoration: 'none',
1434
- // Fixes https://github.com/udecode/plate/issues/2315
1435
- WebkitUserModify: IS_SAFARI ? 'inherit' : undefined
1436
- },
1437
- contentEditable: false,
1438
- ref: callbackPlaceholderRef
1439
- }
1288
+ if (Range.isCollapsed(range)) {
1289
+ return {
1290
+ anchor,
1291
+ focus: anchor
1440
1292
  };
1441
- children = /*#__PURE__*/React.createElement(React.Fragment, null, renderPlaceholder(placeholderProps), children);
1442
- } // COMPAT: Having the `data-` attributes on these leaf elements ensures that
1443
- // in certain misbehaving browsers they aren't weirdly cloned/destroyed by
1444
- // contenteditable behaviors. (2019/05/08)
1445
-
1446
-
1447
- var attributes = {
1448
- 'data-slate-leaf': true
1449
- };
1450
- return renderLeaf({
1451
- attributes,
1452
- children,
1453
- leaf,
1454
- text
1455
- });
1456
- };
1457
-
1458
- var MemoizedLeaf = /*#__PURE__*/React.memo(Leaf, (prev, next) => {
1459
- return next.parent === prev.parent && next.isLast === prev.isLast && next.renderLeaf === prev.renderLeaf && next.renderPlaceholder === prev.renderPlaceholder && next.text === prev.text && Text$1.equals(next.leaf, prev.leaf) && next.leaf[PLACEHOLDER_SYMBOL] === prev.leaf[PLACEHOLDER_SYMBOL];
1460
- });
1461
- var DefaultLeaf = props => {
1462
- var {
1463
- attributes,
1464
- children
1465
- } = props;
1466
- return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes), children);
1467
- };
1468
-
1469
- /**
1470
- * Text.
1471
- */
1472
-
1473
- var Text = props => {
1474
- var {
1475
- decorations,
1476
- isLast,
1477
- parent,
1478
- renderPlaceholder,
1479
- renderLeaf,
1480
- text
1481
- } = props;
1482
- var editor = useSlateStatic();
1483
- var ref = useRef(null);
1484
- var leaves = Text$1.decorations(text, decorations);
1485
- var key = ReactEditor.findKey(editor, text);
1486
- var children = [];
1487
-
1488
- for (var i = 0; i < leaves.length; i++) {
1489
- var leaf = leaves[i];
1490
- children.push( /*#__PURE__*/React.createElement(MemoizedLeaf, {
1491
- isLast: isLast && i === leaves.length - 1,
1492
- key: "".concat(key.id, "-").concat(i),
1493
- renderPlaceholder: renderPlaceholder,
1494
- leaf: leaf,
1495
- text: text,
1496
- parent: parent,
1497
- renderLeaf: renderLeaf
1498
- }));
1499
- } // Update element-related weak maps with the DOM element ref.
1500
-
1501
-
1502
- var callbackRef = useCallback(span => {
1503
- var KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor);
1504
-
1505
- if (span) {
1506
- KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.set(key, span);
1507
- NODE_TO_ELEMENT.set(text, span);
1508
- ELEMENT_TO_NODE.set(span, text);
1509
- } else {
1510
- KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.delete(key);
1511
- NODE_TO_ELEMENT.delete(text);
1512
-
1513
- if (ref.current) {
1514
- ELEMENT_TO_NODE.delete(ref.current);
1515
- }
1516
- }
1517
-
1518
- ref.current = span;
1519
- }, [ref, editor, key, text]);
1520
- return /*#__PURE__*/React.createElement("span", {
1521
- "data-slate-node": "text",
1522
- ref: callbackRef
1523
- }, children);
1524
- };
1525
-
1526
- var MemoizedText = /*#__PURE__*/React.memo(Text, (prev, next) => {
1527
- return next.parent === prev.parent && next.isLast === prev.isLast && next.renderLeaf === prev.renderLeaf && next.renderPlaceholder === prev.renderPlaceholder && next.text === prev.text && isTextDecorationsEqual(next.decorations, prev.decorations);
1528
- });
1529
-
1530
- /**
1531
- * Element.
1532
- */
1533
-
1534
- var Element = props => {
1535
- var {
1536
- decorations,
1537
- element,
1538
- renderElement = p => /*#__PURE__*/React.createElement(DefaultElement, Object.assign({}, p)),
1539
- renderPlaceholder,
1540
- renderLeaf,
1541
- selection
1542
- } = props;
1543
- var editor = useSlateStatic();
1544
- var readOnly = useReadOnly();
1545
- var isInline = editor.isInline(element);
1546
- var key = ReactEditor.findKey(editor, element);
1547
- var ref = useCallback(ref => {
1548
- // Update element-related weak maps with the DOM element ref.
1549
- var KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor);
1550
-
1551
- if (ref) {
1552
- KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.set(key, ref);
1553
- NODE_TO_ELEMENT.set(element, ref);
1554
- ELEMENT_TO_NODE.set(ref, element);
1555
- } else {
1556
- KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.delete(key);
1557
- NODE_TO_ELEMENT.delete(element);
1558
- }
1559
- }, [editor, key, element]);
1560
- var children = useChildren({
1561
- decorations,
1562
- node: element,
1563
- renderElement,
1564
- renderPlaceholder,
1565
- renderLeaf,
1566
- selection
1567
- }); // Attributes that the developer must mix into the element in their
1568
- // custom node renderer component.
1569
-
1570
- var attributes = {
1571
- 'data-slate-node': 'element',
1572
- ref
1573
- };
1574
-
1575
- if (isInline) {
1576
- attributes['data-slate-inline'] = true;
1577
- } // If it's a block node with inline children, add the proper `dir` attribute
1578
- // for text direction.
1579
-
1580
-
1581
- if (!isInline && Editor.hasInlines(editor, element)) {
1582
- var text = Node.string(element);
1583
- var dir = getDirection(text);
1584
-
1585
- if (dir === 'rtl') {
1586
- attributes.dir = dir;
1587
- }
1588
- } // If it's a void node, wrap the children in extra void-specific elements.
1589
-
1590
-
1591
- if (Editor.isVoid(editor, element)) {
1592
- attributes['data-slate-void'] = true;
1593
-
1594
- if (!readOnly && isInline) {
1595
- attributes.contentEditable = false;
1596
- }
1597
-
1598
- var Tag = isInline ? 'span' : 'div';
1599
- var [[_text]] = Node.texts(element);
1600
- children = /*#__PURE__*/React.createElement(Tag, {
1601
- "data-slate-spacer": true,
1602
- style: {
1603
- height: '0',
1604
- color: 'transparent',
1605
- outline: 'none',
1606
- position: 'absolute'
1607
- }
1608
- }, /*#__PURE__*/React.createElement(MemoizedText, {
1609
- renderPlaceholder: renderPlaceholder,
1610
- decorations: [],
1611
- isLast: false,
1612
- parent: element,
1613
- text: _text
1614
- }));
1615
- NODE_TO_INDEX.set(_text, 0);
1616
- NODE_TO_PARENT.set(_text, element);
1617
1293
  }
1618
1294
 
1619
- return renderElement({
1620
- attributes,
1621
- children,
1622
- element
1623
- });
1624
- };
1625
-
1626
- var MemoizedElement = /*#__PURE__*/React.memo(Element, (prev, next) => {
1627
- return prev.element === next.element && prev.renderElement === next.renderElement && prev.renderLeaf === next.renderLeaf && prev.renderPlaceholder === next.renderPlaceholder && isElementDecorationsEqual(prev.decorations, next.decorations) && (prev.selection === next.selection || !!prev.selection && !!next.selection && Range.equals(prev.selection, next.selection));
1628
- });
1629
- /**
1630
- * The default element renderer.
1631
- */
1632
-
1633
- var DefaultElement = props => {
1634
- var {
1635
- attributes,
1636
- children,
1637
- element
1638
- } = props;
1639
- var editor = useSlateStatic();
1640
- var Tag = editor.isInline(element) ? 'span' : 'div';
1641
- return /*#__PURE__*/React.createElement(Tag, Object.assign({}, attributes, {
1642
- style: {
1643
- position: 'relative'
1644
- }
1645
- }), children);
1646
- };
1647
-
1648
- /**
1649
- * A React context for sharing the `decorate` prop of the editable.
1650
- */
1651
-
1652
- var DecorateContext = /*#__PURE__*/createContext(() => []);
1653
- /**
1654
- * Get the current `decorate` prop of the editable.
1655
- */
1656
-
1657
- var useDecorate = () => {
1658
- return useContext(DecorateContext);
1659
- };
1660
-
1661
- /**
1662
- * A React context for sharing the `selected` state of an element.
1663
- */
1664
-
1665
- var SelectedContext = /*#__PURE__*/createContext(false);
1666
- /**
1667
- * Get the current `selected` state of an element.
1668
- */
1669
-
1670
- var useSelected = () => {
1671
- return useContext(SelectedContext);
1672
- };
1295
+ var focus = transformPendingPoint(editor, range.focus, op);
1673
1296
 
1674
- /**
1675
- * Children.
1676
- */
1297
+ if (!focus) {
1298
+ return null;
1299
+ }
1677
1300
 
1678
- var useChildren = props => {
1301
+ return {
1302
+ anchor,
1303
+ focus
1304
+ };
1305
+ }
1306
+ function transformTextDiff(textDiff, op) {
1679
1307
  var {
1680
- decorations,
1681
- node,
1682
- renderElement,
1683
- renderPlaceholder,
1684
- renderLeaf,
1685
- selection
1686
- } = props;
1687
- var decorate = useDecorate();
1688
- var editor = useSlateStatic();
1689
- var path = ReactEditor.findPath(editor, node);
1690
- var children = [];
1691
- var isLeafBlock = Element$1.isElement(node) && !editor.isInline(node) && Editor.hasInlines(editor, node);
1308
+ path,
1309
+ diff,
1310
+ id
1311
+ } = textDiff;
1692
1312
 
1693
- for (var i = 0; i < node.children.length; i++) {
1694
- var p = path.concat(i);
1695
- var n = node.children[i];
1696
- var key = ReactEditor.findKey(editor, n);
1697
- var range = Editor.range(editor, p);
1698
- var sel = selection && Range.intersection(range, selection);
1699
- var ds = decorate([n, p]);
1313
+ switch (op.type) {
1314
+ case 'insert_text':
1315
+ {
1316
+ if (!Path.equals(op.path, path) || op.offset >= diff.end) {
1317
+ return textDiff;
1318
+ }
1700
1319
 
1701
- for (var dec of decorations) {
1702
- var d = Range.intersection(dec, range);
1320
+ if (op.offset <= diff.start) {
1321
+ return {
1322
+ diff: {
1323
+ start: op.text.length + diff.start,
1324
+ end: op.text.length + diff.end,
1325
+ text: diff.text
1326
+ },
1327
+ id,
1328
+ path
1329
+ };
1330
+ }
1703
1331
 
1704
- if (d) {
1705
- ds.push(d);
1332
+ return {
1333
+ diff: {
1334
+ start: diff.start,
1335
+ end: diff.end + op.text.length,
1336
+ text: diff.text
1337
+ },
1338
+ id,
1339
+ path
1340
+ };
1706
1341
  }
1707
- }
1708
-
1709
- if (Element$1.isElement(n)) {
1710
- children.push( /*#__PURE__*/React.createElement(SelectedContext.Provider, {
1711
- key: "provider-".concat(key.id),
1712
- value: !!sel
1713
- }, /*#__PURE__*/React.createElement(MemoizedElement, {
1714
- decorations: ds,
1715
- element: n,
1716
- key: key.id,
1717
- renderElement: renderElement,
1718
- renderPlaceholder: renderPlaceholder,
1719
- renderLeaf: renderLeaf,
1720
- selection: sel
1721
- })));
1722
- } else {
1723
- children.push( /*#__PURE__*/React.createElement(MemoizedText, {
1724
- decorations: ds,
1725
- key: key.id,
1726
- isLast: isLeafBlock && i === node.children.length - 1,
1727
- parent: node,
1728
- renderPlaceholder: renderPlaceholder,
1729
- renderLeaf: renderLeaf,
1730
- text: n
1731
- }));
1732
- }
1733
-
1734
- NODE_TO_INDEX.set(n, i);
1735
- NODE_TO_PARENT.set(n, node);
1736
- }
1737
-
1738
- return children;
1739
- };
1740
1342
 
1741
- /**
1742
- * A React context for sharing the `readOnly` state of the editor.
1743
- */
1343
+ case 'remove_text':
1344
+ {
1345
+ if (!Path.equals(op.path, path) || op.offset >= diff.end) {
1346
+ return textDiff;
1347
+ }
1744
1348
 
1745
- var ReadOnlyContext = /*#__PURE__*/createContext(false);
1746
- /**
1747
- * Get the current `readOnly` state of the editor.
1748
- */
1349
+ if (op.offset + op.text.length <= diff.start) {
1350
+ return {
1351
+ diff: {
1352
+ start: diff.start - op.text.length,
1353
+ end: diff.end - op.text.length,
1354
+ text: diff.text
1355
+ },
1356
+ id,
1357
+ path
1358
+ };
1359
+ }
1749
1360
 
1750
- var useReadOnly = () => {
1751
- return useContext(ReadOnlyContext);
1752
- };
1361
+ return {
1362
+ diff: {
1363
+ start: diff.start,
1364
+ end: diff.end - op.text.length,
1365
+ text: diff.text
1366
+ },
1367
+ id,
1368
+ path
1369
+ };
1370
+ }
1753
1371
 
1754
- var SlateContext = /*#__PURE__*/createContext(null);
1755
- /**
1756
- * Get the current editor object from the React context.
1757
- */
1372
+ case 'split_node':
1373
+ {
1374
+ if (!Path.equals(op.path, path) || op.position >= diff.end) {
1375
+ return {
1376
+ diff,
1377
+ id,
1378
+ path: Path.transform(path, op, {
1379
+ affinity: 'backward'
1380
+ })
1381
+ };
1382
+ }
1758
1383
 
1759
- var useSlate = () => {
1760
- var context = useContext(SlateContext);
1384
+ if (op.position > diff.start) {
1385
+ return {
1386
+ diff: {
1387
+ start: diff.start,
1388
+ end: Math.min(op.position, diff.end),
1389
+ text: diff.text
1390
+ },
1391
+ id,
1392
+ path
1393
+ };
1394
+ }
1761
1395
 
1762
- if (!context) {
1763
- throw new Error("The `useSlate` hook must be used inside the <Slate> component's context.");
1764
- }
1396
+ return {
1397
+ diff: {
1398
+ start: diff.start - op.position,
1399
+ end: diff.end - op.position,
1400
+ text: diff.text
1401
+ },
1402
+ id,
1403
+ path: Path.transform(path, op, {
1404
+ affinity: 'forward'
1405
+ })
1406
+ };
1407
+ }
1765
1408
 
1766
- var {
1767
- editor
1768
- } = context;
1769
- return editor;
1770
- };
1771
- var useSlateWithV = () => {
1772
- var context = useContext(SlateContext);
1409
+ case 'merge_node':
1410
+ {
1411
+ if (!Path.equals(op.path, path)) {
1412
+ return {
1413
+ diff,
1414
+ id,
1415
+ path: Path.transform(path, op)
1416
+ };
1417
+ }
1773
1418
 
1774
- if (!context) {
1775
- throw new Error("The `useSlate` hook must be used inside the <Slate> component's context.");
1419
+ return {
1420
+ diff: {
1421
+ start: diff.start + op.position,
1422
+ end: diff.end + op.position,
1423
+ text: diff.text
1424
+ },
1425
+ id,
1426
+ path: Path.transform(path, op)
1427
+ };
1428
+ }
1776
1429
  }
1777
1430
 
1778
- return context;
1779
- };
1780
-
1781
- var TRIPLE_CLICK = 3;
1782
-
1783
- /**
1784
- * Hotkey mappings for each platform.
1785
- */
1786
-
1787
- var HOTKEYS = {
1788
- bold: 'mod+b',
1789
- compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
1790
- moveBackward: 'left',
1791
- moveForward: 'right',
1792
- moveWordBackward: 'ctrl+left',
1793
- moveWordForward: 'ctrl+right',
1794
- deleteBackward: 'shift?+backspace',
1795
- deleteForward: 'shift?+delete',
1796
- extendBackward: 'shift+left',
1797
- extendForward: 'shift+right',
1798
- italic: 'mod+i',
1799
- insertSoftBreak: 'shift+enter',
1800
- splitBlock: 'enter',
1801
- undo: 'mod+z'
1802
- };
1803
- var APPLE_HOTKEYS = {
1804
- moveLineBackward: 'opt+up',
1805
- moveLineForward: 'opt+down',
1806
- moveWordBackward: 'opt+left',
1807
- moveWordForward: 'opt+right',
1808
- deleteBackward: ['ctrl+backspace', 'ctrl+h'],
1809
- deleteForward: ['ctrl+delete', 'ctrl+d'],
1810
- deleteLineBackward: 'cmd+shift?+backspace',
1811
- deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
1812
- deleteWordBackward: 'opt+shift?+backspace',
1813
- deleteWordForward: 'opt+shift?+delete',
1814
- extendLineBackward: 'opt+shift+up',
1815
- extendLineForward: 'opt+shift+down',
1816
- redo: 'cmd+shift+z',
1817
- transposeCharacter: 'ctrl+t'
1818
- };
1819
- var WINDOWS_HOTKEYS = {
1820
- deleteWordBackward: 'ctrl+shift?+backspace',
1821
- deleteWordForward: 'ctrl+shift?+delete',
1822
- redo: ['ctrl+y', 'ctrl+shift+z']
1823
- };
1824
- /**
1825
- * Create a platform-aware hotkey checker.
1826
- */
1431
+ var newPath = Path.transform(path, op);
1827
1432
 
1828
- var create = key => {
1829
- var generic = HOTKEYS[key];
1830
- var apple = APPLE_HOTKEYS[key];
1831
- var windows = WINDOWS_HOTKEYS[key];
1832
- var isGeneric = generic && isKeyHotkey(generic);
1833
- var isApple = apple && isKeyHotkey(apple);
1834
- var isWindows = windows && isKeyHotkey(windows);
1835
- return event => {
1836
- if (isGeneric && isGeneric(event)) return true;
1837
- if (IS_APPLE && isApple && isApple(event)) return true;
1838
- if (!IS_APPLE && isWindows && isWindows(event)) return true;
1839
- return false;
1433
+ if (!newPath) {
1434
+ return null;
1435
+ }
1436
+
1437
+ return {
1438
+ diff,
1439
+ path: newPath,
1440
+ id
1840
1441
  };
1841
- };
1842
- /**
1843
- * Hotkeys.
1844
- */
1442
+ }
1845
1443
 
1444
+ function ownKeys$3(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; }
1846
1445
 
1847
- var Hotkeys = {
1848
- isBold: create('bold'),
1849
- isCompose: create('compose'),
1850
- isMoveBackward: create('moveBackward'),
1851
- isMoveForward: create('moveForward'),
1852
- isDeleteBackward: create('deleteBackward'),
1853
- isDeleteForward: create('deleteForward'),
1854
- isDeleteLineBackward: create('deleteLineBackward'),
1855
- isDeleteLineForward: create('deleteLineForward'),
1856
- isDeleteWordBackward: create('deleteWordBackward'),
1857
- isDeleteWordForward: create('deleteWordForward'),
1858
- isExtendBackward: create('extendBackward'),
1859
- isExtendForward: create('extendForward'),
1860
- isExtendLineBackward: create('extendLineBackward'),
1861
- isExtendLineForward: create('extendLineForward'),
1862
- isItalic: create('italic'),
1863
- isMoveLineBackward: create('moveLineBackward'),
1864
- isMoveLineForward: create('moveLineForward'),
1865
- isMoveWordBackward: create('moveWordBackward'),
1866
- isMoveWordForward: create('moveWordForward'),
1867
- isRedo: create('redo'),
1868
- isSoftBreak: create('insertSoftBreak'),
1869
- isSplitBlock: create('splitBlock'),
1870
- isTransposeCharacter: create('transposeCharacter'),
1871
- isUndo: create('undo')
1872
- };
1446
+ function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$3(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
1447
+ // When using keyboard English association function, conpositionEnd triggered too fast, resulting in after `insertText` still maintain association state.
1873
1448
 
1874
- var createRestoreDomManager = (editor, receivedUserInput) => {
1875
- var bufferedMutations = [];
1449
+ var RESOLVE_DELAY = 25; // Time with no user interaction before the current user action is considered as done.
1876
1450
 
1877
- var clear = () => {
1878
- bufferedMutations = [];
1451
+ var FLUSH_DELAY = 200; // Replace with `const debug = console.log` to debug
1452
+
1453
+ var debug = function debug() {}; // Type guard to check if a value is a DataTransfer
1454
+
1455
+
1456
+ var isDataTransfer = value => (value === null || value === void 0 ? void 0 : value.constructor.name) === 'DataTransfer';
1457
+
1458
+ function createAndroidInputManager(_ref) {
1459
+ var {
1460
+ editor,
1461
+ scheduleOnDOMSelectionChange,
1462
+ onDOMSelectionChange
1463
+ } = _ref;
1464
+ var flushing = false;
1465
+ var compositionEndTimeoutId = null;
1466
+ var flushTimeoutId = null;
1467
+ var actionTimeoutId = null;
1468
+ var idCounter = 0;
1469
+ var insertPositionHint = false;
1470
+
1471
+ var applyPendingSelection = () => {
1472
+ var pendingSelection = EDITOR_TO_PENDING_SELECTION.get(editor);
1473
+ EDITOR_TO_PENDING_SELECTION.delete(editor);
1474
+
1475
+ if (pendingSelection) {
1476
+ var {
1477
+ selection
1478
+ } = editor;
1479
+ var normalized = normalizeRange(editor, pendingSelection);
1480
+
1481
+ if (normalized && (!selection || !Range.equals(normalized, selection))) {
1482
+ Transforms.select(editor, normalized);
1483
+ }
1484
+ }
1879
1485
  };
1880
1486
 
1881
- var registerMutations = mutations => {
1882
- if (!receivedUserInput.current) {
1487
+ var performAction = () => {
1488
+ var action = EDITOR_TO_PENDING_ACTION.get(editor);
1489
+ EDITOR_TO_PENDING_ACTION.delete(editor);
1490
+
1491
+ if (!action) {
1883
1492
  return;
1884
1493
  }
1885
1494
 
1886
- var trackedMutations = mutations.filter(mutation => isTrackedMutation(editor, mutation, mutations));
1887
- bufferedMutations.push(...trackedMutations);
1888
- };
1495
+ if (action.at) {
1496
+ var target = Point.isPoint(action.at) ? normalizePoint(editor, action.at) : normalizeRange(editor, action.at);
1889
1497
 
1890
- function restoreDOM() {
1891
- if (bufferedMutations.length > 0) {
1892
- bufferedMutations.reverse().forEach(mutation => {
1893
- if (mutation.type === 'characterData') {
1894
- // We don't want to restore the DOM for characterData mutations
1895
- // because this interrupts the composition.
1896
- return;
1897
- }
1498
+ if (!target) {
1499
+ return;
1500
+ }
1898
1501
 
1899
- mutation.removedNodes.forEach(node => {
1900
- mutation.target.insertBefore(node, mutation.nextSibling);
1901
- });
1902
- mutation.addedNodes.forEach(node => {
1903
- mutation.target.removeChild(node);
1904
- });
1905
- }); // Clear buffered mutations to ensure we don't undo them twice
1502
+ var _targetRange = Editor.range(editor, target);
1906
1503
 
1907
- clear();
1504
+ if (!editor.selection || !Range.equals(editor.selection, _targetRange)) {
1505
+ Transforms.select(editor, target);
1506
+ }
1908
1507
  }
1909
- }
1910
1508
 
1911
- return {
1912
- registerMutations,
1913
- restoreDOM,
1914
- clear
1509
+ action.run();
1915
1510
  };
1916
- };
1917
1511
 
1918
- var MUTATION_OBSERVER_CONFIG$1 = {
1919
- subtree: true,
1920
- childList: true,
1921
- characterData: true,
1922
- characterDataOldValue: true
1923
- }; // We have to use a class component here since we rely on `getSnapshotBeforeUpdate` which has no FC equivalent
1924
- // to run code synchronously immediately before react commits the component update to the DOM.
1512
+ var flush = () => {
1513
+ if (flushTimeoutId) {
1514
+ clearTimeout(flushTimeoutId);
1515
+ flushTimeoutId = null;
1516
+ }
1925
1517
 
1926
- class RestoreDOMComponent extends Component {
1927
- constructor() {
1928
- super(...arguments);
1929
- this.context = null;
1930
- this.manager = null;
1931
- this.mutationObserver = null;
1932
- }
1518
+ if (actionTimeoutId) {
1519
+ clearTimeout(actionTimeoutId);
1520
+ actionTimeoutId = null;
1521
+ }
1933
1522
 
1934
- observe() {
1935
- var _this$mutationObserve;
1523
+ if (!hasPendingDiffs() && !hasPendingAction()) {
1524
+ applyPendingSelection();
1525
+ return;
1526
+ }
1936
1527
 
1937
- var {
1938
- node
1939
- } = this.props;
1528
+ if (!flushing) {
1529
+ flushing = true;
1530
+ setTimeout(() => flushing = false);
1531
+ }
1940
1532
 
1941
- if (!node.current) {
1942
- throw new Error('Failed to attach MutationObserver, `node` is undefined');
1533
+ if (hasPendingAction()) {
1534
+ flushing = 'action';
1943
1535
  }
1944
1536
 
1945
- (_this$mutationObserve = this.mutationObserver) === null || _this$mutationObserve === void 0 ? void 0 : _this$mutationObserve.observe(node.current, MUTATION_OBSERVER_CONFIG$1);
1946
- }
1537
+ var selectionRef = editor.selection && Editor.rangeRef(editor, editor.selection, {
1538
+ affinity: 'forward'
1539
+ });
1540
+ EDITOR_TO_USER_MARKS.set(editor, editor.marks);
1541
+ debug('flush', EDITOR_TO_PENDING_ACTION.get(editor), EDITOR_TO_PENDING_DIFFS.get(editor));
1542
+ var scheduleSelectionChange = hasPendingDiffs();
1543
+ var diff;
1947
1544
 
1948
- componentDidMount() {
1949
- var {
1950
- receivedUserInput
1951
- } = this.props;
1952
- var editor = this.context;
1953
- this.manager = createRestoreDomManager(editor, receivedUserInput);
1954
- this.mutationObserver = new MutationObserver(this.manager.registerMutations);
1955
- this.observe();
1956
- }
1545
+ while (diff = (_EDITOR_TO_PENDING_DI = EDITOR_TO_PENDING_DIFFS.get(editor)) === null || _EDITOR_TO_PENDING_DI === void 0 ? void 0 : _EDITOR_TO_PENDING_DI[0]) {
1546
+ var _EDITOR_TO_PENDING_DI, _EDITOR_TO_PENDING_DI2;
1957
1547
 
1958
- getSnapshotBeforeUpdate() {
1959
- var _this$mutationObserve2, _this$mutationObserve3, _this$manager2;
1548
+ var pendingMarks = EDITOR_TO_PENDING_INSERTION_MARKS.get(editor);
1960
1549
 
1961
- var pendingMutations = (_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 ? void 0 : _this$mutationObserve2.takeRecords();
1550
+ if (pendingMarks !== undefined) {
1551
+ EDITOR_TO_PENDING_INSERTION_MARKS.delete(editor);
1552
+ editor.marks = pendingMarks;
1553
+ }
1962
1554
 
1963
- if (pendingMutations !== null && pendingMutations !== void 0 && pendingMutations.length) {
1964
- var _this$manager;
1555
+ if (pendingMarks && insertPositionHint === false) {
1556
+ insertPositionHint = null;
1557
+ }
1558
+
1559
+ var range = targetRange(diff);
1560
+
1561
+ if (!editor.selection || !Range.equals(editor.selection, range)) {
1562
+ Transforms.select(editor, range);
1563
+ }
1564
+
1565
+ if (diff.diff.text) {
1566
+ Editor.insertText(editor, diff.diff.text);
1567
+ } else {
1568
+ Editor.deleteFragment(editor);
1569
+ } // Remove diff only after we have applied it to account for it when transforming
1570
+ // pending ranges.
1571
+
1572
+
1573
+ EDITOR_TO_PENDING_DIFFS.set(editor, (_EDITOR_TO_PENDING_DI2 = EDITOR_TO_PENDING_DIFFS.get(editor)) === null || _EDITOR_TO_PENDING_DI2 === void 0 ? void 0 : _EDITOR_TO_PENDING_DI2.filter(_ref2 => {
1574
+ var {
1575
+ id
1576
+ } = _ref2;
1577
+ return id !== diff.id;
1578
+ }));
1579
+
1580
+ if (!verifyDiffState(editor, diff)) {
1581
+ scheduleSelectionChange = false;
1582
+ EDITOR_TO_PENDING_ACTION.delete(editor);
1583
+ EDITOR_TO_USER_MARKS.delete(editor);
1584
+ flushing = 'action'; // Ensure we don't restore the pending user (dom) selection
1585
+ // since the document and dom state do not match.
1586
+
1587
+ EDITOR_TO_PENDING_SELECTION.delete(editor);
1588
+ scheduleOnDOMSelectionChange.cancel();
1589
+ onDOMSelectionChange.cancel();
1590
+ selectionRef === null || selectionRef === void 0 ? void 0 : selectionRef.unref();
1591
+ }
1592
+ }
1593
+
1594
+ var selection = selectionRef === null || selectionRef === void 0 ? void 0 : selectionRef.unref();
1595
+
1596
+ if (selection && !EDITOR_TO_PENDING_SELECTION.get(editor) && (!editor.selection || !Range.equals(selection, editor.selection))) {
1597
+ Transforms.select(editor, selection);
1598
+ }
1599
+
1600
+ if (hasPendingAction()) {
1601
+ performAction();
1602
+ return;
1603
+ } // COMPAT: The selectionChange event is fired after the action is performed,
1604
+ // so we have to manually schedule it to ensure we don't 'throw away' the selection
1605
+ // while rendering if we have pending changes.
1965
1606
 
1966
- (_this$manager = this.manager) === null || _this$manager === void 0 ? void 0 : _this$manager.registerMutations(pendingMutations);
1967
- }
1968
1607
 
1969
- (_this$mutationObserve3 = this.mutationObserver) === null || _this$mutationObserve3 === void 0 ? void 0 : _this$mutationObserve3.disconnect();
1970
- (_this$manager2 = this.manager) === null || _this$manager2 === void 0 ? void 0 : _this$manager2.restoreDOM();
1971
- return null;
1972
- }
1608
+ if (scheduleSelectionChange) {
1609
+ scheduleOnDOMSelectionChange();
1610
+ }
1973
1611
 
1974
- componentDidUpdate() {
1975
- var _this$manager3;
1612
+ scheduleOnDOMSelectionChange.flush();
1613
+ onDOMSelectionChange.flush();
1614
+ applyPendingSelection();
1615
+ var userMarks = EDITOR_TO_USER_MARKS.get(editor);
1616
+ EDITOR_TO_USER_MARKS.delete(editor);
1976
1617
 
1977
- (_this$manager3 = this.manager) === null || _this$manager3 === void 0 ? void 0 : _this$manager3.clear();
1978
- this.observe();
1979
- }
1618
+ if (userMarks !== undefined) {
1619
+ editor.marks = userMarks;
1620
+ editor.onChange();
1621
+ }
1622
+ };
1980
1623
 
1981
- componentWillUnmount() {
1982
- var _this$mutationObserve4;
1624
+ var handleCompositionEnd = _event => {
1625
+ if (compositionEndTimeoutId) {
1626
+ clearTimeout(compositionEndTimeoutId);
1627
+ }
1983
1628
 
1984
- (_this$mutationObserve4 = this.mutationObserver) === null || _this$mutationObserve4 === void 0 ? void 0 : _this$mutationObserve4.disconnect();
1985
- }
1629
+ compositionEndTimeoutId = setTimeout(() => {
1630
+ IS_COMPOSING.set(editor, false);
1631
+ flush();
1632
+ }, RESOLVE_DELAY);
1633
+ };
1986
1634
 
1987
- render() {
1988
- return this.props.children;
1989
- }
1635
+ var handleCompositionStart = _event => {
1636
+ IS_COMPOSING.set(editor, true);
1990
1637
 
1991
- }
1638
+ if (compositionEndTimeoutId) {
1639
+ clearTimeout(compositionEndTimeoutId);
1640
+ compositionEndTimeoutId = null;
1641
+ }
1642
+ };
1992
1643
 
1993
- RestoreDOMComponent.contextType = EditorContext;
1994
- var RestoreDOM = IS_ANDROID ? RestoreDOMComponent : _ref => {
1995
- var {
1996
- children
1997
- } = _ref;
1998
- return /*#__PURE__*/React.createElement(React.Fragment, null, children);
1999
- };
1644
+ var updatePlaceholderVisibility = function updatePlaceholderVisibility() {
1645
+ var forceHide = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
1646
+ var placeholderElement = EDITOR_TO_PLACEHOLDER_ELEMENT.get(editor);
2000
1647
 
2001
- /**
2002
- * Check whether a text diff was applied in a way we can perform the pending action on /
2003
- * recover the pending selection.
2004
- */
1648
+ if (!placeholderElement) {
1649
+ return;
1650
+ }
2005
1651
 
2006
- function verifyDiffState(editor, textDiff) {
2007
- var {
2008
- path,
2009
- diff
2010
- } = textDiff;
1652
+ if (hasPendingDiffs() || forceHide) {
1653
+ placeholderElement.style.display = 'none';
1654
+ return;
1655
+ }
2011
1656
 
2012
- if (!Editor.hasPath(editor, path)) {
2013
- return false;
2014
- }
1657
+ placeholderElement.style.removeProperty('display');
1658
+ };
2015
1659
 
2016
- var node = Node.get(editor, path);
1660
+ var storeDiff = (path, diff) => {
1661
+ var _EDITOR_TO_PENDING_DI3;
1662
+ var pendingDiffs = (_EDITOR_TO_PENDING_DI3 = EDITOR_TO_PENDING_DIFFS.get(editor)) !== null && _EDITOR_TO_PENDING_DI3 !== void 0 ? _EDITOR_TO_PENDING_DI3 : [];
1663
+ EDITOR_TO_PENDING_DIFFS.set(editor, pendingDiffs);
1664
+ var target = Node.leaf(editor, path);
1665
+ var idx = pendingDiffs.findIndex(change => Path.equals(change.path, path));
2017
1666
 
2018
- if (!Text$1.isText(node)) {
2019
- return false;
2020
- }
1667
+ if (idx < 0) {
1668
+ var normalized = normalizeStringDiff(target.text, diff);
2021
1669
 
2022
- if (diff.start !== node.text.length || diff.text.length === 0) {
2023
- return node.text.slice(diff.start, diff.start + diff.text.length) === diff.text;
2024
- }
1670
+ if (normalized) {
1671
+ pendingDiffs.push({
1672
+ path,
1673
+ diff,
1674
+ id: idCounter++
1675
+ });
1676
+ }
2025
1677
 
2026
- var nextPath = Path.next(path);
1678
+ updatePlaceholderVisibility();
1679
+ return;
1680
+ }
2027
1681
 
2028
- if (!Editor.hasPath(editor, nextPath)) {
2029
- return false;
2030
- }
1682
+ var merged = mergeStringDiffs(target.text, pendingDiffs[idx].diff, diff);
2031
1683
 
2032
- var nextNode = Node.get(editor, nextPath);
2033
- return Text$1.isText(nextNode) && nextNode.text.startsWith(diff.text);
2034
- }
2035
- function applyStringDiff(text) {
2036
- for (var _len = arguments.length, diffs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
2037
- diffs[_key - 1] = arguments[_key];
2038
- }
1684
+ if (!merged) {
1685
+ pendingDiffs.splice(idx, 1);
1686
+ updatePlaceholderVisibility();
1687
+ return;
1688
+ }
2039
1689
 
2040
- return diffs.reduce((text, diff) => text.slice(0, diff.start) + diff.text + text.slice(diff.end), text);
2041
- }
1690
+ pendingDiffs[idx] = _objectSpread$3(_objectSpread$3({}, pendingDiffs[idx]), {}, {
1691
+ diff: merged
1692
+ });
1693
+ };
2042
1694
 
2043
- function longestCommonPrefixLength(str, another) {
2044
- var length = Math.min(str.length, another.length);
1695
+ var scheduleAction = function scheduleAction(run) {
1696
+ var {
1697
+ at
1698
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1699
+ insertPositionHint = false;
1700
+ EDITOR_TO_PENDING_SELECTION.delete(editor);
1701
+ scheduleOnDOMSelectionChange.cancel();
1702
+ onDOMSelectionChange.cancel();
2045
1703
 
2046
- for (var i = 0; i < length; i++) {
2047
- if (str.charAt(i) !== another.charAt(i)) {
2048
- return i;
1704
+ if (hasPendingAction()) {
1705
+ flush();
2049
1706
  }
2050
- }
2051
1707
 
2052
- return length;
2053
- }
1708
+ EDITOR_TO_PENDING_ACTION.set(editor, {
1709
+ at,
1710
+ run
1711
+ }); // COMPAT: When deleting before a non-contenteditable element chrome only fires a beforeinput,
1712
+ // (no input) and doesn't perform any dom mutations. Without a flush timeout we would never flush
1713
+ // in this case and thus never actually perform the action.
2054
1714
 
2055
- function longestCommonSuffixLength(str, another, max) {
2056
- var length = Math.min(str.length, another.length, max);
1715
+ actionTimeoutId = setTimeout(flush);
1716
+ };
2057
1717
 
2058
- for (var i = 0; i < length; i++) {
2059
- if (str.charAt(str.length - i - 1) !== another.charAt(another.length - i - 1)) {
2060
- return i;
1718
+ var handleDOMBeforeInput = event => {
1719
+ var _targetRange2;
1720
+
1721
+ if (flushTimeoutId) {
1722
+ clearTimeout(flushTimeoutId);
1723
+ flushTimeoutId = null;
2061
1724
  }
2062
- }
2063
1725
 
2064
- return length;
2065
- }
2066
- /**
2067
- * Remove redundant changes from the diff so that it spans the minimal possible range
2068
- */
1726
+ var {
1727
+ inputType: type
1728
+ } = event;
1729
+ var targetRange = null;
1730
+ var data = event.dataTransfer || event.data || undefined;
2069
1731
 
1732
+ if (insertPositionHint !== false && type !== 'insertText' && type !== 'insertCompositionText') {
1733
+ insertPositionHint = false;
1734
+ }
2070
1735
 
2071
- function normalizeStringDiff(targetText, diff) {
2072
- var {
2073
- start,
2074
- end,
2075
- text
2076
- } = diff;
2077
- var removedText = targetText.slice(start, end);
2078
- var prefixLength = longestCommonPrefixLength(removedText, text);
2079
- var max = Math.min(removedText.length - prefixLength, text.length - prefixLength);
2080
- var suffixLength = longestCommonSuffixLength(removedText, text, max);
2081
- var normalized = {
2082
- start: start + prefixLength,
2083
- end: end - suffixLength,
2084
- text: text.slice(prefixLength, text.length - suffixLength)
2085
- };
1736
+ var [nativeTargetRange] = event.getTargetRanges();
2086
1737
 
2087
- if (normalized.start === normalized.end && normalized.text.length === 0) {
2088
- return null;
2089
- }
1738
+ if (nativeTargetRange) {
1739
+ targetRange = ReactEditor.toSlateRange(editor, nativeTargetRange, {
1740
+ exactMatch: false,
1741
+ suppressThrow: true
1742
+ });
1743
+ } // COMPAT: SelectionChange event is fired after the action is performed, so we
1744
+ // have to manually get the selection here to ensure it's up-to-date.
2090
1745
 
2091
- return normalized;
2092
- }
2093
- /**
2094
- * Return a string diff that is equivalent to applying b after a spanning the range of
2095
- * both changes
2096
- */
2097
1746
 
2098
- function mergeStringDiffs(targetText, a, b) {
2099
- var start = Math.min(a.start, b.start);
2100
- var overlap = Math.max(0, Math.min(a.start + a.text.length, b.end) - b.start);
2101
- var applied = applyStringDiff(targetText, a, b);
2102
- var sliceEnd = Math.max(b.start + b.text.length, a.start + a.text.length + (a.start + a.text.length > b.start ? b.text.length : 0) - overlap);
2103
- var text = applied.slice(start, sliceEnd);
2104
- var end = Math.max(a.end, b.end - a.text.length + (a.end - a.start));
2105
- return normalizeStringDiff(targetText, {
2106
- start,
2107
- end,
2108
- text
2109
- });
2110
- }
2111
- /**
2112
- * Get the slate range the text diff spans.
2113
- */
1747
+ var window = ReactEditor.getWindow(editor);
1748
+ var domSelection = window.getSelection();
2114
1749
 
2115
- function targetRange(textDiff) {
2116
- var {
2117
- path,
2118
- diff
2119
- } = textDiff;
2120
- return {
2121
- anchor: {
2122
- path,
2123
- offset: diff.start
2124
- },
2125
- focus: {
2126
- path,
2127
- offset: diff.end
1750
+ if (!targetRange && domSelection) {
1751
+ nativeTargetRange = domSelection;
1752
+ targetRange = ReactEditor.toSlateRange(editor, domSelection, {
1753
+ exactMatch: false,
1754
+ suppressThrow: true
1755
+ });
2128
1756
  }
2129
- };
2130
- }
2131
- /**
2132
- * Normalize a 'pending point' a.k.a a point based on the dom state before applying
2133
- * the pending diffs. Since the pending diffs might have been inserted with different
2134
- * marks we have to 'walk' the offset from the starting position to ensure we still
2135
- * have a valid point inside the document
2136
- */
2137
1757
 
2138
- function normalizePoint(editor, point) {
2139
- var {
2140
- path,
2141
- offset
2142
- } = point;
1758
+ targetRange = (_targetRange2 = targetRange) !== null && _targetRange2 !== void 0 ? _targetRange2 : editor.selection;
1759
+
1760
+ if (!targetRange) {
1761
+ return;
1762
+ } // By default, the input manager tries to store text diffs so that we can
1763
+ // defer flushing them at a later point in time. We don't want to flush
1764
+ // for every input event as this can be expensive. However, there are some
1765
+ // scenarios where we cannot safely store the text diff and must instead
1766
+ // schedule an action to let Slate normalize the editor state.
2143
1767
 
2144
- if (!Editor.hasPath(editor, path)) {
2145
- return null;
2146
- }
2147
1768
 
2148
- var leaf = Node.get(editor, path);
1769
+ var canStoreDiff = true;
2149
1770
 
2150
- if (!Text$1.isText(leaf)) {
2151
- return null;
2152
- }
1771
+ if (type.startsWith('delete')) {
1772
+ if (Range.isExpanded(targetRange)) {
1773
+ var [_start, _end] = Range.edges(targetRange);
2153
1774
 
2154
- var parentBlock = Editor.above(editor, {
2155
- match: n => Element$1.isElement(n) && Editor.isBlock(editor, n),
2156
- at: path
2157
- });
1775
+ var _leaf = Node.leaf(editor, _start.path);
2158
1776
 
2159
- if (!parentBlock) {
2160
- return null;
2161
- }
1777
+ if (_leaf.text.length === _start.offset && _end.offset === 0) {
1778
+ var next = Editor.next(editor, {
1779
+ at: _start.path,
1780
+ match: Text$1.isText
1781
+ });
2162
1782
 
2163
- while (offset > leaf.text.length) {
2164
- var entry = Editor.next(editor, {
2165
- at: path,
2166
- match: Text$1.isText
2167
- });
1783
+ if (next && Path.equals(next[1], _end.path)) {
1784
+ targetRange = {
1785
+ anchor: _end,
1786
+ focus: _end
1787
+ };
1788
+ }
1789
+ }
1790
+ }
2168
1791
 
2169
- if (!entry || !Path.isDescendant(entry[1], parentBlock[1])) {
2170
- return null;
2171
- }
1792
+ var direction = type.endsWith('Backward') ? 'backward' : 'forward';
1793
+ var [start, end] = Range.edges(targetRange);
1794
+ var [leaf, path] = Editor.leaf(editor, start.path);
1795
+ var diff = {
1796
+ text: '',
1797
+ start: start.offset,
1798
+ end: end.offset
1799
+ };
1800
+ var pendingDiffs = EDITOR_TO_PENDING_DIFFS.get(editor);
1801
+ var relevantPendingDiffs = pendingDiffs === null || pendingDiffs === void 0 ? void 0 : pendingDiffs.find(change => Path.equals(change.path, path));
1802
+ var diffs = relevantPendingDiffs ? [relevantPendingDiffs.diff, diff] : [diff];
1803
+ var text = applyStringDiff(leaf.text, ...diffs);
2172
1804
 
2173
- offset -= leaf.text.length;
2174
- leaf = entry[0];
2175
- path = entry[1];
2176
- }
1805
+ if (text.length === 0) {
1806
+ // Text leaf will be removed, so we need to schedule an
1807
+ // action to remove it so that Slate can normalize instead
1808
+ // of storing as a diff
1809
+ canStoreDiff = false;
1810
+ }
2177
1811
 
2178
- return {
2179
- path,
2180
- offset
2181
- };
2182
- }
2183
- /**
2184
- * Normalize a 'pending selection' to ensure it's valid in the current document state.
2185
- */
1812
+ if (Range.isExpanded(targetRange)) {
1813
+ if (canStoreDiff && Path.equals(targetRange.anchor.path, targetRange.focus.path)) {
1814
+ var point = {
1815
+ path: targetRange.anchor.path,
1816
+ offset: start.offset
1817
+ };
1818
+ var range = Editor.range(editor, point, point);
1819
+ handleUserSelect(range);
1820
+ return storeDiff(targetRange.anchor.path, {
1821
+ text: '',
1822
+ end: end.offset,
1823
+ start: start.offset
1824
+ });
1825
+ }
2186
1826
 
2187
- function normalizeRange(editor, range) {
2188
- var anchor = normalizePoint(editor, range.anchor);
1827
+ return scheduleAction(() => Editor.deleteFragment(editor, {
1828
+ direction
1829
+ }), {
1830
+ at: targetRange
1831
+ });
1832
+ }
1833
+ }
2189
1834
 
2190
- if (!anchor) {
2191
- return null;
2192
- }
1835
+ switch (type) {
1836
+ case 'deleteByComposition':
1837
+ case 'deleteByCut':
1838
+ case 'deleteByDrag':
1839
+ {
1840
+ return scheduleAction(() => Editor.deleteFragment(editor), {
1841
+ at: targetRange
1842
+ });
1843
+ }
2193
1844
 
2194
- if (Range.isCollapsed(range)) {
2195
- return {
2196
- anchor,
2197
- focus: anchor
2198
- };
2199
- }
1845
+ case 'deleteContent':
1846
+ case 'deleteContentForward':
1847
+ {
1848
+ var {
1849
+ anchor
1850
+ } = targetRange;
2200
1851
 
2201
- var focus = normalizePoint(editor, range.focus);
1852
+ if (canStoreDiff && Range.isCollapsed(targetRange)) {
1853
+ var targetNode = Node.leaf(editor, anchor.path);
2202
1854
 
2203
- if (!focus) {
2204
- return null;
2205
- }
1855
+ if (anchor.offset < targetNode.text.length) {
1856
+ return storeDiff(anchor.path, {
1857
+ text: '',
1858
+ start: anchor.offset,
1859
+ end: anchor.offset + 1
1860
+ });
1861
+ }
1862
+ }
2206
1863
 
2207
- return {
2208
- anchor,
2209
- focus
2210
- };
2211
- }
2212
- function transformPendingPoint(editor, point, op) {
2213
- var pendingDiffs = EDITOR_TO_PENDING_DIFFS.get(editor);
2214
- var textDiff = pendingDiffs === null || pendingDiffs === void 0 ? void 0 : pendingDiffs.find(_ref => {
2215
- var {
2216
- path
2217
- } = _ref;
2218
- return Path.equals(path, point.path);
2219
- });
1864
+ return scheduleAction(() => Editor.deleteForward(editor), {
1865
+ at: targetRange
1866
+ });
1867
+ }
2220
1868
 
2221
- if (!textDiff || point.offset <= textDiff.diff.start) {
2222
- return Point.transform(point, op, {
2223
- affinity: 'backward'
2224
- });
2225
- }
1869
+ case 'deleteContentBackward':
1870
+ {
1871
+ var _nativeTargetRange;
2226
1872
 
2227
- var {
2228
- diff
2229
- } = textDiff; // Point references location inside the diff => transform the point based on the location
2230
- // the diff will be applied to and add the offset inside the diff.
1873
+ var {
1874
+ anchor: _anchor
1875
+ } = targetRange; // If we have a mismatch between the native and slate selection being collapsed
1876
+ // we are most likely deleting a zero-width placeholder and thus should perform it
1877
+ // as an action to ensure correct behavior (mostly happens with mark placeholders)
2231
1878
 
2232
- if (point.offset <= diff.start + diff.text.length) {
2233
- var _anchor = {
2234
- path: point.path,
2235
- offset: diff.start
2236
- };
1879
+ var nativeCollapsed = isDOMSelection(nativeTargetRange) ? nativeTargetRange.isCollapsed : !!((_nativeTargetRange = nativeTargetRange) !== null && _nativeTargetRange !== void 0 && _nativeTargetRange.collapsed);
2237
1880
 
2238
- var _transformed = Point.transform(_anchor, op, {
2239
- affinity: 'backward'
2240
- });
1881
+ if (canStoreDiff && nativeCollapsed && Range.isCollapsed(targetRange) && _anchor.offset > 0) {
1882
+ return storeDiff(_anchor.path, {
1883
+ text: '',
1884
+ start: _anchor.offset - 1,
1885
+ end: _anchor.offset
1886
+ });
1887
+ }
2241
1888
 
2242
- if (!_transformed) {
2243
- return null;
2244
- }
1889
+ return scheduleAction(() => Editor.deleteBackward(editor), {
1890
+ at: targetRange
1891
+ });
1892
+ }
2245
1893
 
2246
- return {
2247
- path: _transformed.path,
2248
- offset: _transformed.offset + point.offset - diff.start
2249
- };
2250
- } // Point references location after the diff
1894
+ case 'deleteEntireSoftLine':
1895
+ {
1896
+ return scheduleAction(() => {
1897
+ Editor.deleteBackward(editor, {
1898
+ unit: 'line'
1899
+ });
1900
+ Editor.deleteForward(editor, {
1901
+ unit: 'line'
1902
+ });
1903
+ }, {
1904
+ at: targetRange
1905
+ });
1906
+ }
1907
+
1908
+ case 'deleteHardLineBackward':
1909
+ {
1910
+ return scheduleAction(() => Editor.deleteBackward(editor, {
1911
+ unit: 'block'
1912
+ }), {
1913
+ at: targetRange
1914
+ });
1915
+ }
2251
1916
 
1917
+ case 'deleteSoftLineBackward':
1918
+ {
1919
+ return scheduleAction(() => Editor.deleteBackward(editor, {
1920
+ unit: 'line'
1921
+ }), {
1922
+ at: targetRange
1923
+ });
1924
+ }
2252
1925
 
2253
- var anchor = {
2254
- path: point.path,
2255
- offset: point.offset - diff.text.length + diff.end - diff.start
2256
- };
2257
- var transformed = Point.transform(anchor, op, {
2258
- affinity: 'backward'
2259
- });
1926
+ case 'deleteHardLineForward':
1927
+ {
1928
+ return scheduleAction(() => Editor.deleteForward(editor, {
1929
+ unit: 'block'
1930
+ }), {
1931
+ at: targetRange
1932
+ });
1933
+ }
2260
1934
 
2261
- if (!transformed) {
2262
- return null;
2263
- }
1935
+ case 'deleteSoftLineForward':
1936
+ {
1937
+ return scheduleAction(() => Editor.deleteForward(editor, {
1938
+ unit: 'line'
1939
+ }), {
1940
+ at: targetRange
1941
+ });
1942
+ }
2264
1943
 
2265
- if (op.type === 'split_node' && Path.equals(op.path, point.path) && anchor.offset < op.position && diff.start < op.position) {
2266
- return transformed;
2267
- }
1944
+ case 'deleteWordBackward':
1945
+ {
1946
+ return scheduleAction(() => Editor.deleteBackward(editor, {
1947
+ unit: 'word'
1948
+ }), {
1949
+ at: targetRange
1950
+ });
1951
+ }
2268
1952
 
2269
- return {
2270
- path: transformed.path,
2271
- offset: transformed.offset + diff.text.length - diff.end + diff.start
2272
- };
2273
- }
2274
- function transformPendingRange(editor, range, op) {
2275
- var anchor = transformPendingPoint(editor, range.anchor, op);
1953
+ case 'deleteWordForward':
1954
+ {
1955
+ return scheduleAction(() => Editor.deleteForward(editor, {
1956
+ unit: 'word'
1957
+ }), {
1958
+ at: targetRange
1959
+ });
1960
+ }
2276
1961
 
2277
- if (!anchor) {
2278
- return null;
2279
- }
1962
+ case 'insertLineBreak':
1963
+ {
1964
+ return scheduleAction(() => Editor.insertSoftBreak(editor), {
1965
+ at: targetRange
1966
+ });
1967
+ }
2280
1968
 
2281
- if (Range.isCollapsed(range)) {
2282
- return {
2283
- anchor,
2284
- focus: anchor
2285
- };
2286
- }
1969
+ case 'insertParagraph':
1970
+ {
1971
+ return scheduleAction(() => Editor.insertBreak(editor), {
1972
+ at: targetRange
1973
+ });
1974
+ }
2287
1975
 
2288
- var focus = transformPendingPoint(editor, range.focus, op);
1976
+ case 'insertCompositionText':
1977
+ case 'deleteCompositionText':
1978
+ case 'insertFromComposition':
1979
+ case 'insertFromDrop':
1980
+ case 'insertFromPaste':
1981
+ case 'insertFromYank':
1982
+ case 'insertReplacementText':
1983
+ case 'insertText':
1984
+ {
1985
+ if (isDataTransfer(data)) {
1986
+ return scheduleAction(() => ReactEditor.insertData(editor, data), {
1987
+ at: targetRange
1988
+ });
1989
+ }
2289
1990
 
2290
- if (!focus) {
2291
- return null;
2292
- }
1991
+ var _text = data !== null && data !== void 0 ? data : ''; // COMPAT: If we are writing inside a placeholder, the ime inserts the text inside
1992
+ // the placeholder itself and thus includes the zero-width space inside edit events.
2293
1993
 
2294
- return {
2295
- anchor,
2296
- focus
2297
- };
2298
- }
2299
- function transformTextDiff(textDiff, op) {
2300
- var {
2301
- path,
2302
- diff,
2303
- id
2304
- } = textDiff;
2305
1994
 
2306
- switch (op.type) {
2307
- case 'insert_text':
2308
- {
2309
- if (!Path.equals(op.path, path) || op.offset >= diff.end) {
2310
- return textDiff;
2311
- }
1995
+ if (EDITOR_TO_PENDING_INSERTION_MARKS.get(editor)) {
1996
+ _text = _text.replace('\uFEFF', '');
1997
+ } // Pastes from the Android clipboard will generate `insertText` events.
1998
+ // If the copied text contains any newlines, Android will append an
1999
+ // extra newline to the end of the copied text.
2312
2000
 
2313
- if (op.offset <= diff.start) {
2314
- return {
2315
- diff: {
2316
- start: op.text.length + diff.start,
2317
- end: op.text.length + diff.end,
2318
- text: diff.text
2319
- },
2320
- id,
2321
- path
2322
- };
2323
- }
2324
2001
 
2325
- return {
2326
- diff: {
2327
- start: diff.start,
2328
- end: diff.end + op.text.length,
2329
- text: diff.text
2330
- },
2331
- id,
2332
- path
2333
- };
2334
- }
2002
+ if (type === 'insertText' && /.*\n.*\n$/.test(_text)) {
2003
+ _text = _text.slice(0, -1);
2004
+ } // If the text includes a newline, split it at newlines and paste each component
2005
+ // string, with soft breaks in between each.
2335
2006
 
2336
- case 'remove_text':
2337
- {
2338
- if (!Path.equals(op.path, path) || op.offset >= diff.end) {
2339
- return textDiff;
2340
- }
2341
2007
 
2342
- if (op.offset + op.text.length <= diff.start) {
2343
- return {
2344
- diff: {
2345
- start: diff.start - op.text.length,
2346
- end: diff.end - op.text.length,
2347
- text: diff.text
2348
- },
2349
- id,
2350
- path
2351
- };
2352
- }
2008
+ if (_text.includes('\n')) {
2009
+ return scheduleAction(() => {
2010
+ var parts = _text.split('\n');
2353
2011
 
2354
- return {
2355
- diff: {
2356
- start: diff.start,
2357
- end: diff.end - op.text.length,
2358
- text: diff.text
2359
- },
2360
- id,
2361
- path
2362
- };
2363
- }
2012
+ parts.forEach((line, i) => {
2013
+ if (line) {
2014
+ Editor.insertText(editor, line);
2015
+ }
2364
2016
 
2365
- case 'split_node':
2366
- {
2367
- if (!Path.equals(op.path, path) || op.position >= diff.end) {
2368
- return {
2369
- diff,
2370
- id,
2371
- path: Path.transform(path, op, {
2372
- affinity: 'backward'
2373
- })
2374
- };
2375
- }
2017
+ if (i !== parts.length - 1) {
2018
+ Editor.insertSoftBreak(editor);
2019
+ }
2020
+ });
2021
+ }, {
2022
+ at: targetRange
2023
+ });
2024
+ }
2376
2025
 
2377
- if (op.position > diff.start) {
2378
- return {
2379
- diff: {
2380
- start: diff.start,
2381
- end: Math.min(op.position, diff.end),
2382
- text: diff.text
2383
- },
2384
- id,
2385
- path
2386
- };
2387
- }
2026
+ if (Path.equals(targetRange.anchor.path, targetRange.focus.path)) {
2027
+ var [_start2, _end2] = Range.edges(targetRange);
2028
+ var _diff = {
2029
+ start: _start2.offset,
2030
+ end: _end2.offset,
2031
+ text: _text
2032
+ }; // COMPAT: Swiftkey has a weird bug where the target range of the 2nd word
2033
+ // inserted after a mark placeholder is inserted with an anchor offset off by 1.
2034
+ // So writing 'some text' will result in 'some ttext'. Luckily all 'normal' insert
2035
+ // text events are fired with the correct target ranges, only the final 'insertComposition'
2036
+ // isn't, so we can adjust the target range start offset if we are confident this is the
2037
+ // swiftkey insert causing the issue.
2388
2038
 
2389
- return {
2390
- diff: {
2391
- start: diff.start - op.position,
2392
- end: diff.end - op.position,
2393
- text: diff.text
2394
- },
2395
- id,
2396
- path: Path.transform(path, op, {
2397
- affinity: 'forward'
2398
- })
2399
- };
2400
- }
2039
+ if (_text && insertPositionHint && type === 'insertCompositionText') {
2040
+ var hintPosition = insertPositionHint.start + insertPositionHint.text.search(/\S|$/);
2401
2041
 
2402
- case 'merge_node':
2403
- {
2404
- if (!Path.equals(op.path, path)) {
2405
- return {
2406
- diff,
2407
- id,
2408
- path: Path.transform(path, op)
2409
- };
2410
- }
2042
+ var diffPosition = _diff.start + _diff.text.search(/\S|$/);
2411
2043
 
2412
- return {
2413
- diff: {
2414
- start: diff.start + op.position,
2415
- end: diff.end + op.position,
2416
- text: diff.text
2417
- },
2418
- id,
2419
- path: Path.transform(path, op)
2420
- };
2421
- }
2422
- }
2044
+ if (diffPosition === hintPosition + 1 && _diff.end === insertPositionHint.start + insertPositionHint.text.length) {
2045
+ _diff.start -= 1;
2046
+ insertPositionHint = null;
2047
+ scheduleFlush();
2048
+ } else {
2049
+ insertPositionHint = false;
2050
+ }
2051
+ } else if (type === 'insertText') {
2052
+ if (insertPositionHint === null) {
2053
+ insertPositionHint = _diff;
2054
+ } else if (insertPositionHint && Range.isCollapsed(targetRange) && insertPositionHint.end + insertPositionHint.text.length === _start2.offset) {
2055
+ insertPositionHint = _objectSpread$3(_objectSpread$3({}, insertPositionHint), {}, {
2056
+ text: insertPositionHint.text + _text
2057
+ });
2058
+ } else {
2059
+ insertPositionHint = false;
2060
+ }
2061
+ } else {
2062
+ insertPositionHint = false;
2063
+ }
2423
2064
 
2424
- var newPath = Path.transform(path, op);
2065
+ if (canStoreDiff) {
2066
+ storeDiff(_start2.path, _diff);
2067
+ return;
2068
+ }
2069
+ }
2425
2070
 
2426
- if (!newPath) {
2427
- return null;
2428
- }
2071
+ return scheduleAction(() => Editor.insertText(editor, _text), {
2072
+ at: targetRange
2073
+ });
2074
+ }
2075
+ }
2076
+ };
2429
2077
 
2430
- return {
2431
- diff,
2432
- path: newPath,
2433
- id
2078
+ var hasPendingAction = () => {
2079
+ return !!EDITOR_TO_PENDING_ACTION.get(editor);
2434
2080
  };
2435
- }
2436
2081
 
2437
- function ownKeys$3(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; }
2082
+ var hasPendingDiffs = () => {
2083
+ var _EDITOR_TO_PENDING_DI4;
2438
2084
 
2439
- function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$3(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
2440
- // When using keyboard English association function, conpositionEnd triggered too fast, resulting in after `insertText` still maintain association state.
2085
+ return !!((_EDITOR_TO_PENDING_DI4 = EDITOR_TO_PENDING_DIFFS.get(editor)) !== null && _EDITOR_TO_PENDING_DI4 !== void 0 && _EDITOR_TO_PENDING_DI4.length);
2086
+ };
2441
2087
 
2442
- var RESOLVE_DELAY = 25; // Time with no user interaction before the current user action is considered as done.
2088
+ var hasPendingChanges = () => {
2089
+ return hasPendingAction() || hasPendingDiffs();
2090
+ };
2443
2091
 
2444
- var FLUSH_DELAY = 200; // Replace with `const debug = console.log` to debug
2092
+ var isFlushing = () => {
2093
+ return flushing;
2094
+ };
2445
2095
 
2446
- var debug = function debug() {}; // Type guard to check if a value is a DataTransfer
2096
+ var handleUserSelect = range => {
2097
+ EDITOR_TO_PENDING_SELECTION.set(editor, range);
2447
2098
 
2099
+ if (flushTimeoutId) {
2100
+ clearTimeout(flushTimeoutId);
2101
+ flushTimeoutId = null;
2102
+ }
2448
2103
 
2449
- var isDataTransfer = value => (value === null || value === void 0 ? void 0 : value.constructor.name) === 'DataTransfer';
2104
+ var {
2105
+ selection
2106
+ } = editor;
2450
2107
 
2451
- function createAndroidInputManager(_ref) {
2452
- var {
2453
- editor,
2454
- scheduleOnDOMSelectionChange,
2455
- onDOMSelectionChange
2456
- } = _ref;
2457
- var flushing = false;
2458
- var compositionEndTimeoutId = null;
2459
- var flushTimeoutId = null;
2460
- var actionTimeoutId = null;
2461
- var idCounter = 0;
2462
- var insertPositionHint = false;
2108
+ if (!range) {
2109
+ return;
2110
+ }
2463
2111
 
2464
- var applyPendingSelection = () => {
2465
- var pendingSelection = EDITOR_TO_PENDING_SELECTION.get(editor);
2466
- EDITOR_TO_PENDING_SELECTION.delete(editor);
2112
+ var pathChanged = !selection || !Path.equals(selection.anchor.path, range.anchor.path);
2113
+ var parentPathChanged = !selection || !Path.equals(selection.anchor.path.slice(0, -1), range.anchor.path.slice(0, -1));
2467
2114
 
2468
- if (pendingSelection) {
2469
- var {
2470
- selection
2471
- } = editor;
2472
- var normalized = normalizeRange(editor, pendingSelection);
2115
+ if (pathChanged && insertPositionHint || parentPathChanged) {
2116
+ insertPositionHint = false;
2117
+ }
2473
2118
 
2474
- if (normalized && (!selection || !Range.equals(normalized, selection))) {
2475
- Transforms.select(editor, normalized);
2476
- }
2119
+ if (pathChanged || hasPendingDiffs()) {
2120
+ flushTimeoutId = setTimeout(flush, FLUSH_DELAY);
2477
2121
  }
2478
2122
  };
2479
2123
 
2480
- var performAction = () => {
2481
- var action = EDITOR_TO_PENDING_ACTION.get(editor);
2482
- EDITOR_TO_PENDING_ACTION.delete(editor);
2124
+ var handleInput = () => {
2125
+ if (hasPendingAction() || !hasPendingDiffs()) {
2126
+ flush();
2127
+ }
2128
+ };
2483
2129
 
2484
- if (!action) {
2485
- return;
2130
+ var handleKeyDown = _ => {
2131
+ // COMPAT: Swiftkey closes the keyboard when typing inside a empty node
2132
+ // directly next to a non-contenteditable element (= the placeholder).
2133
+ // The only event fired soon enough for us to allow hiding the placeholder
2134
+ // without swiftkey picking it up is the keydown event, so we have to hide it
2135
+ // here. See https://github.com/ianstormtaylor/slate/pull/4988#issuecomment-1201050535
2136
+ if (!hasPendingDiffs()) {
2137
+ updatePlaceholderVisibility(true);
2138
+ setTimeout(updatePlaceholderVisibility);
2486
2139
  }
2140
+ };
2487
2141
 
2488
- if (action.at) {
2489
- var target = Point.isPoint(action.at) ? normalizePoint(editor, action.at) : normalizeRange(editor, action.at);
2142
+ var scheduleFlush = () => {
2143
+ if (!hasPendingAction()) {
2144
+ actionTimeoutId = setTimeout(flush);
2145
+ }
2146
+ };
2490
2147
 
2491
- if (!target) {
2492
- return;
2493
- }
2148
+ var handleDomMutations = mutations => {
2149
+ if (hasPendingDiffs() || hasPendingAction()) {
2150
+ return;
2151
+ }
2494
2152
 
2495
- var _targetRange = Editor.range(editor, target);
2153
+ if (mutations.some(mutation => isTrackedMutation(editor, mutation, mutations))) {
2154
+ var _EDITOR_TO_FORCE_REND;
2496
2155
 
2497
- if (!editor.selection || !Range.equals(editor.selection, _targetRange)) {
2498
- Transforms.select(editor, target);
2499
- }
2156
+ // Cause a re-render to restore the dom state if we encounter tracked mutations without
2157
+ // a corresponding pending action.
2158
+ (_EDITOR_TO_FORCE_REND = EDITOR_TO_FORCE_RENDER.get(editor)) === null || _EDITOR_TO_FORCE_REND === void 0 ? void 0 : _EDITOR_TO_FORCE_REND();
2500
2159
  }
2160
+ };
2501
2161
 
2502
- action.run();
2162
+ return {
2163
+ flush,
2164
+ scheduleFlush,
2165
+ hasPendingDiffs,
2166
+ hasPendingAction,
2167
+ hasPendingChanges,
2168
+ isFlushing,
2169
+ handleUserSelect,
2170
+ handleCompositionEnd,
2171
+ handleCompositionStart,
2172
+ handleDOMBeforeInput,
2173
+ handleKeyDown,
2174
+ handleDomMutations,
2175
+ handleInput
2503
2176
  };
2177
+ }
2504
2178
 
2505
- var flush = () => {
2506
- if (flushTimeoutId) {
2507
- clearTimeout(flushTimeoutId);
2508
- flushTimeoutId = null;
2509
- }
2179
+ function useIsMounted() {
2180
+ var isMountedRef = useRef(false);
2181
+ useEffect(() => {
2182
+ isMountedRef.current = true;
2183
+ return () => {
2184
+ isMountedRef.current = false;
2185
+ };
2186
+ }, []);
2187
+ return isMountedRef.current;
2188
+ }
2510
2189
 
2511
- if (actionTimeoutId) {
2512
- clearTimeout(actionTimeoutId);
2513
- actionTimeoutId = null;
2514
- }
2190
+ /**
2191
+ * Prevent warning on SSR by falling back to useEffect when DOM isn't available
2192
+ */
2515
2193
 
2516
- if (!hasPendingDiffs() && !hasPendingAction()) {
2517
- applyPendingSelection();
2518
- return;
2519
- }
2194
+ var useIsomorphicLayoutEffect = CAN_USE_DOM ? useLayoutEffect : useEffect;
2520
2195
 
2521
- if (!flushing) {
2522
- flushing = true;
2523
- setTimeout(() => flushing = false);
2196
+ function useMutationObserver(node, callback, options) {
2197
+ var [mutationObserver] = useState(() => new MutationObserver(callback));
2198
+ useIsomorphicLayoutEffect(() => {
2199
+ // Discard mutations caused during render phase. This works due to react calling
2200
+ // useLayoutEffect synchronously after the render phase before the next tick.
2201
+ mutationObserver.takeRecords();
2202
+ });
2203
+ useEffect(() => {
2204
+ if (!node.current) {
2205
+ throw new Error('Failed to attach MutationObserver, `node` is undefined');
2524
2206
  }
2525
2207
 
2526
- if (hasPendingAction()) {
2527
- flushing = 'action';
2528
- }
2208
+ mutationObserver.observe(node.current, options);
2209
+ return () => mutationObserver.disconnect();
2210
+ }, [mutationObserver, node, options]);
2211
+ }
2529
2212
 
2530
- var selectionRef = editor.selection && Editor.rangeRef(editor, editor.selection, {
2531
- affinity: 'forward'
2532
- });
2533
- EDITOR_TO_USER_MARKS.set(editor, editor.marks);
2534
- debug('flush', EDITOR_TO_PENDING_ACTION.get(editor), EDITOR_TO_PENDING_DIFFS.get(editor));
2535
- var scheduleSelectionChange = hasPendingDiffs();
2536
- var diff;
2213
+ var _excluded$3 = ["node"];
2537
2214
 
2538
- while (diff = (_EDITOR_TO_PENDING_DI = EDITOR_TO_PENDING_DIFFS.get(editor)) === null || _EDITOR_TO_PENDING_DI === void 0 ? void 0 : _EDITOR_TO_PENDING_DI[0]) {
2539
- var _EDITOR_TO_PENDING_DI, _EDITOR_TO_PENDING_DI2;
2215
+ function ownKeys$2(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; }
2216
+
2217
+ function _objectSpread$2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$2(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$2(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
2218
+ var MUTATION_OBSERVER_CONFIG$1 = {
2219
+ subtree: true,
2220
+ childList: true,
2221
+ characterData: true
2222
+ };
2223
+ var useAndroidInputManager = !IS_ANDROID ? () => null : _ref => {
2224
+ var {
2225
+ node
2226
+ } = _ref,
2227
+ options = _objectWithoutProperties(_ref, _excluded$3);
2228
+
2229
+ if (!IS_ANDROID) {
2230
+ return null;
2231
+ }
2232
+
2233
+ var editor = useSlateStatic();
2234
+ var isMounted = useIsMounted();
2235
+ var [inputManager] = useState(() => createAndroidInputManager(_objectSpread$2({
2236
+ editor
2237
+ }, options)));
2238
+ useMutationObserver(node, inputManager.handleDomMutations, MUTATION_OBSERVER_CONFIG$1);
2239
+ EDITOR_TO_SCHEDULE_FLUSH.set(editor, inputManager.scheduleFlush);
2240
+
2241
+ if (isMounted) {
2242
+ inputManager.flush();
2243
+ }
2244
+
2245
+ return inputManager;
2246
+ };
2247
+
2248
+ var _excluded$2 = ["anchor", "focus"],
2249
+ _excluded2$1 = ["anchor", "focus"];
2250
+ var shallowCompare = (obj1, obj2) => Object.keys(obj1).length === Object.keys(obj2).length && Object.keys(obj1).every(key => obj2.hasOwnProperty(key) && obj1[key] === obj2[key]);
2251
+
2252
+ var isDecorationFlagsEqual = (range, other) => {
2253
+ var rangeOwnProps = _objectWithoutProperties(range, _excluded$2);
2254
+
2255
+ var otherOwnProps = _objectWithoutProperties(other, _excluded2$1);
2256
+
2257
+ return range[PLACEHOLDER_SYMBOL] === other[PLACEHOLDER_SYMBOL] && shallowCompare(rangeOwnProps, otherOwnProps);
2258
+ };
2259
+ /**
2260
+ * Check if a list of decorator ranges are equal to another.
2261
+ *
2262
+ * PERF: this requires the two lists to also have the ranges inside them in the
2263
+ * same order, but this is an okay constraint for us since decorations are
2264
+ * kept in order, and the odd case where they aren't is okay to re-render for.
2265
+ */
2266
+
2267
+
2268
+ var isElementDecorationsEqual = (list, another) => {
2269
+ if (list.length !== another.length) {
2270
+ return false;
2271
+ }
2272
+
2273
+ for (var i = 0; i < list.length; i++) {
2274
+ var range = list[i];
2275
+ var other = another[i];
2276
+
2277
+ if (!Range.equals(range, other) || !isDecorationFlagsEqual(range, other)) {
2278
+ return false;
2279
+ }
2280
+ }
2281
+
2282
+ return true;
2283
+ };
2284
+ /**
2285
+ * Check if a list of decorator ranges are equal to another.
2286
+ *
2287
+ * PERF: this requires the two lists to also have the ranges inside them in the
2288
+ * same order, but this is an okay constraint for us since decorations are
2289
+ * kept in order, and the odd case where they aren't is okay to re-render for.
2290
+ */
2540
2291
 
2541
- var pendingMarks = EDITOR_TO_PENDING_INSERTION_MARKS.get(editor);
2292
+ var isTextDecorationsEqual = (list, another) => {
2293
+ if (list.length !== another.length) {
2294
+ return false;
2295
+ }
2542
2296
 
2543
- if (pendingMarks !== undefined) {
2544
- EDITOR_TO_PENDING_INSERTION_MARKS.delete(editor);
2545
- editor.marks = pendingMarks;
2546
- }
2297
+ for (var i = 0; i < list.length; i++) {
2298
+ var range = list[i];
2299
+ var other = another[i]; // compare only offsets because paths doesn't matter for text
2547
2300
 
2548
- if (pendingMarks && insertPositionHint === false) {
2549
- insertPositionHint = null;
2550
- }
2301
+ if (range.anchor.offset !== other.anchor.offset || range.focus.offset !== other.focus.offset || !isDecorationFlagsEqual(range, other)) {
2302
+ return false;
2303
+ }
2304
+ }
2551
2305
 
2552
- var range = targetRange(diff);
2306
+ return true;
2307
+ };
2553
2308
 
2554
- if (!editor.selection || !Range.equals(editor.selection, range)) {
2555
- Transforms.select(editor, range);
2556
- }
2309
+ /**
2310
+ * Leaf content strings.
2311
+ */
2557
2312
 
2558
- if (diff.diff.text) {
2559
- Editor.insertText(editor, diff.diff.text);
2560
- } else {
2561
- Editor.deleteFragment(editor);
2562
- } // Remove diff only after we have applied it to account for it when transforming
2563
- // pending ranges.
2313
+ var String = props => {
2314
+ var {
2315
+ isLast,
2316
+ leaf,
2317
+ parent,
2318
+ text
2319
+ } = props;
2320
+ var editor = useSlateStatic();
2321
+ var path = ReactEditor.findPath(editor, text);
2322
+ var parentPath = Path.parent(path);
2323
+ var isMarkPlaceholder = leaf[MARK_PLACEHOLDER_SYMBOL] === true; // COMPAT: Render text inside void nodes with a zero-width space.
2324
+ // So the node can contain selection but the text is not visible.
2564
2325
 
2326
+ if (editor.isVoid(parent)) {
2327
+ return /*#__PURE__*/React.createElement(ZeroWidthString, {
2328
+ length: Node.string(parent).length
2329
+ });
2330
+ } // COMPAT: If this is the last text node in an empty block, render a zero-
2331
+ // width space that will convert into a line break when copying and pasting
2332
+ // to support expected plain text.
2565
2333
 
2566
- EDITOR_TO_PENDING_DIFFS.set(editor, (_EDITOR_TO_PENDING_DI2 = EDITOR_TO_PENDING_DIFFS.get(editor)) === null || _EDITOR_TO_PENDING_DI2 === void 0 ? void 0 : _EDITOR_TO_PENDING_DI2.filter(_ref2 => {
2567
- var {
2568
- id
2569
- } = _ref2;
2570
- return id !== diff.id;
2571
- }));
2572
2334
 
2573
- if (!verifyDiffState(editor, diff)) {
2574
- scheduleSelectionChange = false;
2575
- EDITOR_TO_PENDING_ACTION.delete(editor);
2576
- EDITOR_TO_USER_MARKS.delete(editor);
2577
- flushing = 'action'; // Ensure we don't restore the pending user (dom) selection
2578
- // since the document and dom state do not match.
2335
+ if (leaf.text === '' && parent.children[parent.children.length - 1] === text && !editor.isInline(parent) && Editor.string(editor, parentPath) === '') {
2336
+ return /*#__PURE__*/React.createElement(ZeroWidthString, {
2337
+ isLineBreak: true,
2338
+ isMarkPlaceholder: isMarkPlaceholder
2339
+ });
2340
+ } // COMPAT: If the text is empty, it's because it's on the edge of an inline
2341
+ // node, so we render a zero-width space so that the selection can be
2342
+ // inserted next to it still.
2579
2343
 
2580
- EDITOR_TO_PENDING_SELECTION.delete(editor);
2581
- scheduleOnDOMSelectionChange.cancel();
2582
- onDOMSelectionChange.cancel();
2583
- selectionRef === null || selectionRef === void 0 ? void 0 : selectionRef.unref();
2584
- }
2585
- }
2586
2344
 
2587
- var selection = selectionRef === null || selectionRef === void 0 ? void 0 : selectionRef.unref();
2345
+ if (leaf.text === '') {
2346
+ return /*#__PURE__*/React.createElement(ZeroWidthString, {
2347
+ isMarkPlaceholder: isMarkPlaceholder
2348
+ });
2349
+ } // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
2350
+ // so we need to add an extra trailing new lines to prevent that.
2588
2351
 
2589
- if (selection && !EDITOR_TO_PENDING_SELECTION.get(editor) && (!editor.selection || !Range.equals(selection, editor.selection))) {
2590
- Transforms.select(editor, selection);
2591
- }
2592
2352
 
2593
- if (hasPendingAction()) {
2594
- performAction();
2595
- return;
2596
- } // COMPAT: The selectionChange event is fired after the action is performed,
2597
- // so we have to manually schedule it to ensure we don't 'throw away' the selection
2598
- // while rendering if we have pending changes.
2353
+ if (isLast && leaf.text.slice(-1) === '\n') {
2354
+ return /*#__PURE__*/React.createElement(TextString, {
2355
+ isTrailing: true,
2356
+ text: leaf.text
2357
+ });
2358
+ }
2599
2359
 
2360
+ return /*#__PURE__*/React.createElement(TextString, {
2361
+ text: leaf.text
2362
+ });
2363
+ };
2364
+ /**
2365
+ * Leaf strings with text in them.
2366
+ */
2600
2367
 
2601
- if (scheduleSelectionChange) {
2602
- scheduleOnDOMSelectionChange();
2603
- }
2604
2368
 
2605
- scheduleOnDOMSelectionChange.flush();
2606
- onDOMSelectionChange.flush();
2607
- applyPendingSelection();
2608
- var userMarks = EDITOR_TO_USER_MARKS.get(editor);
2609
- EDITOR_TO_USER_MARKS.delete(editor);
2369
+ var TextString = props => {
2370
+ var {
2371
+ text,
2372
+ isTrailing = false
2373
+ } = props;
2374
+ var ref = useRef(null);
2610
2375
 
2611
- if (userMarks !== undefined) {
2612
- editor.marks = userMarks;
2613
- editor.onChange();
2614
- }
2376
+ var getTextContent = () => {
2377
+ return "".concat(text !== null && text !== void 0 ? text : '').concat(isTrailing ? '\n' : '');
2615
2378
  };
2616
2379
 
2617
- var handleCompositionEnd = _event => {
2618
- if (compositionEndTimeoutId) {
2619
- clearTimeout(compositionEndTimeoutId);
2620
- }
2621
-
2622
- compositionEndTimeoutId = setTimeout(() => {
2623
- IS_COMPOSING.set(editor, false);
2624
- flush();
2625
- }, RESOLVE_DELAY);
2626
- };
2380
+ var [initialText] = useState(getTextContent); // This is the actual text rendering boundary where we interface with the DOM
2381
+ // The text is not rendered as part of the virtual DOM, as since we handle basic character insertions natively,
2382
+ // updating the DOM is not a one way dataflow anymore. What we need here is not reconciliation and diffing
2383
+ // with previous version of the virtual DOM, but rather diffing with the actual DOM element, and replace the DOM <span> content
2384
+ // exactly if and only if its current content does not match our current virtual DOM.
2385
+ // Otherwise the DOM TextNode would always be replaced by React as the user types, which interferes with native text features,
2386
+ // eg makes native spellcheck opt out from checking the text node.
2387
+ // useLayoutEffect: updating our span before browser paint
2627
2388
 
2628
- var handleCompositionStart = _event => {
2629
- IS_COMPOSING.set(editor, true);
2389
+ useIsomorphicLayoutEffect(() => {
2390
+ // null coalescing text to make sure we're not outputing "null" as a string in the extreme case it is nullish at runtime
2391
+ var textWithTrailing = getTextContent();
2630
2392
 
2631
- if (compositionEndTimeoutId) {
2632
- clearTimeout(compositionEndTimeoutId);
2633
- compositionEndTimeoutId = null;
2634
- }
2635
- };
2393
+ if (ref.current && ref.current.textContent !== textWithTrailing) {
2394
+ ref.current.textContent = textWithTrailing;
2395
+ } // intentionally not specifying dependencies, so that this effect runs on every render
2396
+ // as this effectively replaces "specifying the text in the virtual DOM under the <span> below" on each render
2636
2397
 
2637
- var updatePlaceholderVisibility = function updatePlaceholderVisibility() {
2638
- var forceHide = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
2639
- var placeholderElement = EDITOR_TO_PLACEHOLDER_ELEMENT.get(editor);
2398
+ }); // We intentionally render a memoized <span> that only receives the initial text content when the component is mounted.
2399
+ // We defer to the layout effect above to update the `textContent` of the span element when needed.
2640
2400
 
2641
- if (!placeholderElement) {
2642
- return;
2643
- }
2401
+ return /*#__PURE__*/React.createElement(MemoizedText$1, {
2402
+ ref: ref
2403
+ }, initialText);
2404
+ };
2644
2405
 
2645
- if (hasPendingDiffs() || forceHide) {
2646
- placeholderElement.style.display = 'none';
2647
- return;
2648
- }
2406
+ var MemoizedText$1 = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((props, ref) => {
2407
+ return /*#__PURE__*/React.createElement("span", {
2408
+ "data-slate-string": true,
2409
+ ref: ref
2410
+ }, props.children);
2411
+ }));
2412
+ /**
2413
+ * Leaf strings without text, render as zero-width strings.
2414
+ */
2649
2415
 
2650
- placeholderElement.style.removeProperty('display');
2416
+ var ZeroWidthString = props => {
2417
+ var {
2418
+ length = 0,
2419
+ isLineBreak = false,
2420
+ isMarkPlaceholder = false
2421
+ } = props;
2422
+ var attributes = {
2423
+ 'data-slate-zero-width': isLineBreak ? 'n' : 'z',
2424
+ 'data-slate-length': length
2651
2425
  };
2652
2426
 
2653
- var storeDiff = (path, diff) => {
2654
- var _EDITOR_TO_PENDING_DI3;
2655
- var pendingDiffs = (_EDITOR_TO_PENDING_DI3 = EDITOR_TO_PENDING_DIFFS.get(editor)) !== null && _EDITOR_TO_PENDING_DI3 !== void 0 ? _EDITOR_TO_PENDING_DI3 : [];
2656
- EDITOR_TO_PENDING_DIFFS.set(editor, pendingDiffs);
2657
- var target = Node.leaf(editor, path);
2658
- var idx = pendingDiffs.findIndex(change => Path.equals(change.path, path));
2659
-
2660
- if (idx < 0) {
2661
- var normalized = normalizeStringDiff(target.text, diff);
2662
-
2663
- if (normalized) {
2664
- pendingDiffs.push({
2665
- path,
2666
- diff,
2667
- id: idCounter++
2668
- });
2669
- }
2427
+ if (isMarkPlaceholder) {
2428
+ attributes['data-slate-mark-placeholder'] = true;
2429
+ }
2670
2430
 
2671
- updatePlaceholderVisibility();
2672
- return;
2673
- }
2431
+ return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes), !IS_ANDROID || !isLineBreak ? '\uFEFF' : null, isLineBreak ? /*#__PURE__*/React.createElement("br", null) : null);
2432
+ };
2674
2433
 
2675
- var merged = mergeStringDiffs(target.text, pendingDiffs[idx].diff, diff);
2434
+ function disconnectPlaceholderResizeObserver(placeholderResizeObserver, releaseObserver) {
2435
+ if (placeholderResizeObserver.current) {
2436
+ placeholderResizeObserver.current.disconnect();
2676
2437
 
2677
- if (!merged) {
2678
- pendingDiffs.splice(idx, 1);
2679
- updatePlaceholderVisibility();
2680
- return;
2438
+ if (releaseObserver) {
2439
+ placeholderResizeObserver.current = null;
2681
2440
  }
2441
+ }
2442
+ }
2682
2443
 
2683
- pendingDiffs[idx] = _objectSpread$3(_objectSpread$3({}, pendingDiffs[idx]), {}, {
2684
- diff: merged
2685
- });
2686
- };
2687
-
2688
- var scheduleAction = function scheduleAction(run) {
2689
- var {
2690
- at
2691
- } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2692
- insertPositionHint = false;
2693
- EDITOR_TO_PENDING_SELECTION.delete(editor);
2694
- scheduleOnDOMSelectionChange.cancel();
2695
- onDOMSelectionChange.cancel();
2444
+ function clearTimeoutRef(timeoutRef) {
2445
+ if (timeoutRef.current) {
2446
+ clearTimeout(timeoutRef.current);
2447
+ timeoutRef.current = null;
2448
+ }
2449
+ }
2450
+ /**
2451
+ * Individual leaves in a text node with unique formatting.
2452
+ */
2696
2453
 
2697
- if (hasPendingAction()) {
2698
- flush();
2699
- }
2700
2454
 
2701
- EDITOR_TO_PENDING_ACTION.set(editor, {
2702
- at,
2703
- run
2704
- }); // COMPAT: When deleting before a non-contenteditable element chrome only fires a beforeinput,
2705
- // (no input) and doesn't perform any dom mutations. Without a flush timeout we would never flush
2706
- // in this case and thus never actually perform the action.
2455
+ var Leaf = props => {
2456
+ var {
2457
+ leaf,
2458
+ isLast,
2459
+ text,
2460
+ parent,
2461
+ renderPlaceholder,
2462
+ renderLeaf = props => /*#__PURE__*/React.createElement(DefaultLeaf, Object.assign({}, props))
2463
+ } = props;
2464
+ var editor = useSlateStatic();
2465
+ var placeholderResizeObserver = useRef(null);
2466
+ var placeholderRef = useRef(null);
2467
+ var [showPlaceholder, setShowPlaceholder] = useState(false);
2468
+ var showPlaceholderTimeoutRef = useRef(null);
2469
+ var callbackPlaceholderRef = useCallback(placeholderEl => {
2470
+ disconnectPlaceholderResizeObserver(placeholderResizeObserver, placeholderEl == null);
2707
2471
 
2708
- actionTimeoutId = setTimeout(flush);
2709
- };
2472
+ if (placeholderEl == null) {
2473
+ var _leaf$onPlaceholderRe;
2710
2474
 
2711
- var handleDOMBeforeInput = event => {
2712
- var _targetRange2;
2475
+ EDITOR_TO_PLACEHOLDER_ELEMENT.delete(editor);
2476
+ (_leaf$onPlaceholderRe = leaf.onPlaceholderResize) === null || _leaf$onPlaceholderRe === void 0 ? void 0 : _leaf$onPlaceholderRe.call(leaf, null);
2477
+ } else {
2478
+ EDITOR_TO_PLACEHOLDER_ELEMENT.set(editor, placeholderEl);
2713
2479
 
2714
- if (flushTimeoutId) {
2715
- clearTimeout(flushTimeoutId);
2716
- flushTimeoutId = null;
2717
- }
2480
+ if (!placeholderResizeObserver.current) {
2481
+ // Create a new observer and observe the placeholder element.
2482
+ var ResizeObserver$1 = window.ResizeObserver || ResizeObserver;
2483
+ placeholderResizeObserver.current = new ResizeObserver$1(() => {
2484
+ var _leaf$onPlaceholderRe2;
2718
2485
 
2719
- var {
2720
- inputType: type
2721
- } = event;
2722
- var targetRange = null;
2723
- var data = event.dataTransfer || event.data || undefined;
2486
+ (_leaf$onPlaceholderRe2 = leaf.onPlaceholderResize) === null || _leaf$onPlaceholderRe2 === void 0 ? void 0 : _leaf$onPlaceholderRe2.call(leaf, placeholderEl);
2487
+ });
2488
+ }
2724
2489
 
2725
- if (insertPositionHint !== false && type !== 'insertText' && type !== 'insertCompositionText') {
2726
- insertPositionHint = false;
2490
+ placeholderResizeObserver.current.observe(placeholderEl);
2491
+ placeholderRef.current = placeholderEl;
2492
+ }
2493
+ }, [placeholderRef, leaf, editor]);
2494
+ var children = /*#__PURE__*/React.createElement(String, {
2495
+ isLast: isLast,
2496
+ leaf: leaf,
2497
+ parent: parent,
2498
+ text: text
2499
+ });
2500
+ var leafIsPlaceholder = leaf[PLACEHOLDER_SYMBOL];
2501
+ useEffect(() => {
2502
+ if (leafIsPlaceholder) {
2503
+ if (!showPlaceholderTimeoutRef.current) {
2504
+ // Delay the placeholder so it will not render in a selection
2505
+ showPlaceholderTimeoutRef.current = setTimeout(() => {
2506
+ setShowPlaceholder(true);
2507
+ showPlaceholderTimeoutRef.current = null;
2508
+ }, 300);
2509
+ }
2510
+ } else {
2511
+ clearTimeoutRef(showPlaceholderTimeoutRef);
2512
+ setShowPlaceholder(false);
2727
2513
  }
2728
2514
 
2729
- var [nativeTargetRange] = event.getTargetRanges();
2730
-
2731
- if (nativeTargetRange) {
2732
- targetRange = ReactEditor.toSlateRange(editor, nativeTargetRange, {
2733
- exactMatch: false,
2734
- suppressThrow: true
2735
- });
2736
- } // COMPAT: SelectionChange event is fired after the action is performed, so we
2737
- // have to manually get the selection here to ensure it's up-to-date.
2515
+ return () => clearTimeoutRef(showPlaceholderTimeoutRef);
2516
+ }, [leafIsPlaceholder, setShowPlaceholder]);
2738
2517
 
2518
+ if (leafIsPlaceholder && showPlaceholder) {
2519
+ var placeholderProps = {
2520
+ children: leaf.placeholder,
2521
+ attributes: {
2522
+ 'data-slate-placeholder': true,
2523
+ style: {
2524
+ position: 'absolute',
2525
+ pointerEvents: 'none',
2526
+ width: '100%',
2527
+ maxWidth: '100%',
2528
+ display: 'block',
2529
+ opacity: '0.333',
2530
+ userSelect: 'none',
2531
+ textDecoration: 'none',
2532
+ // Fixes https://github.com/udecode/plate/issues/2315
2533
+ WebkitUserModify: IS_SAFARI ? 'inherit' : undefined
2534
+ },
2535
+ contentEditable: false,
2536
+ ref: callbackPlaceholderRef
2537
+ }
2538
+ };
2539
+ children = /*#__PURE__*/React.createElement(React.Fragment, null, renderPlaceholder(placeholderProps), children);
2540
+ } // COMPAT: Having the `data-` attributes on these leaf elements ensures that
2541
+ // in certain misbehaving browsers they aren't weirdly cloned/destroyed by
2542
+ // contenteditable behaviors. (2019/05/08)
2739
2543
 
2740
- var window = ReactEditor.getWindow(editor);
2741
- var domSelection = window.getSelection();
2742
2544
 
2743
- if (!targetRange && domSelection) {
2744
- nativeTargetRange = domSelection;
2745
- targetRange = ReactEditor.toSlateRange(editor, domSelection, {
2746
- exactMatch: false,
2747
- suppressThrow: true
2748
- });
2749
- }
2545
+ var attributes = {
2546
+ 'data-slate-leaf': true
2547
+ };
2548
+ return renderLeaf({
2549
+ attributes,
2550
+ children,
2551
+ leaf,
2552
+ text
2553
+ });
2554
+ };
2750
2555
 
2751
- targetRange = (_targetRange2 = targetRange) !== null && _targetRange2 !== void 0 ? _targetRange2 : editor.selection;
2556
+ var MemoizedLeaf = /*#__PURE__*/React.memo(Leaf, (prev, next) => {
2557
+ return next.parent === prev.parent && next.isLast === prev.isLast && next.renderLeaf === prev.renderLeaf && next.renderPlaceholder === prev.renderPlaceholder && next.text === prev.text && Text$1.equals(next.leaf, prev.leaf) && next.leaf[PLACEHOLDER_SYMBOL] === prev.leaf[PLACEHOLDER_SYMBOL];
2558
+ });
2559
+ var DefaultLeaf = props => {
2560
+ var {
2561
+ attributes,
2562
+ children
2563
+ } = props;
2564
+ return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes), children);
2565
+ };
2752
2566
 
2753
- if (!targetRange) {
2754
- return;
2755
- } // By default, the input manager tries to store text diffs so that we can
2756
- // defer flushing them at a later point in time. We don't want to flush
2757
- // for every input event as this can be expensive. However, there are some
2758
- // scenarios where we cannot safely store the text diff and must instead
2759
- // schedule an action to let Slate normalize the editor state.
2567
+ /**
2568
+ * Text.
2569
+ */
2760
2570
 
2571
+ var Text = props => {
2572
+ var {
2573
+ decorations,
2574
+ isLast,
2575
+ parent,
2576
+ renderPlaceholder,
2577
+ renderLeaf,
2578
+ text
2579
+ } = props;
2580
+ var editor = useSlateStatic();
2581
+ var ref = useRef(null);
2582
+ var leaves = Text$1.decorations(text, decorations);
2583
+ var key = ReactEditor.findKey(editor, text);
2584
+ var children = [];
2761
2585
 
2762
- var canStoreDiff = true;
2586
+ for (var i = 0; i < leaves.length; i++) {
2587
+ var leaf = leaves[i];
2588
+ children.push( /*#__PURE__*/React.createElement(MemoizedLeaf, {
2589
+ isLast: isLast && i === leaves.length - 1,
2590
+ key: "".concat(key.id, "-").concat(i),
2591
+ renderPlaceholder: renderPlaceholder,
2592
+ leaf: leaf,
2593
+ text: text,
2594
+ parent: parent,
2595
+ renderLeaf: renderLeaf
2596
+ }));
2597
+ } // Update element-related weak maps with the DOM element ref.
2763
2598
 
2764
- if (type.startsWith('delete')) {
2765
- if (Range.isExpanded(targetRange)) {
2766
- var [_start, _end] = Range.edges(targetRange);
2767
2599
 
2768
- var _leaf = Node.leaf(editor, _start.path);
2600
+ var callbackRef = useCallback(span => {
2601
+ var KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor);
2769
2602
 
2770
- if (_leaf.text.length === _start.offset && _end.offset === 0) {
2771
- var next = Editor.next(editor, {
2772
- at: _start.path,
2773
- match: Text$1.isText
2774
- });
2603
+ if (span) {
2604
+ KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.set(key, span);
2605
+ NODE_TO_ELEMENT.set(text, span);
2606
+ ELEMENT_TO_NODE.set(span, text);
2607
+ } else {
2608
+ KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.delete(key);
2609
+ NODE_TO_ELEMENT.delete(text);
2775
2610
 
2776
- if (next && Path.equals(next[1], _end.path)) {
2777
- targetRange = {
2778
- anchor: _end,
2779
- focus: _end
2780
- };
2781
- }
2782
- }
2611
+ if (ref.current) {
2612
+ ELEMENT_TO_NODE.delete(ref.current);
2783
2613
  }
2614
+ }
2784
2615
 
2785
- var direction = type.endsWith('Backward') ? 'backward' : 'forward';
2786
- var [start, end] = Range.edges(targetRange);
2787
- var [leaf, path] = Editor.leaf(editor, start.path);
2788
- var diff = {
2789
- text: '',
2790
- start: start.offset,
2791
- end: end.offset
2792
- };
2793
- var pendingDiffs = EDITOR_TO_PENDING_DIFFS.get(editor);
2794
- var relevantPendingDiffs = pendingDiffs === null || pendingDiffs === void 0 ? void 0 : pendingDiffs.find(change => Path.equals(change.path, path));
2795
- var diffs = relevantPendingDiffs ? [relevantPendingDiffs.diff, diff] : [diff];
2796
- var text = applyStringDiff(leaf.text, ...diffs);
2797
-
2798
- if (text.length === 0) {
2799
- // Text leaf will be removed, so we need to schedule an
2800
- // action to remove it so that Slate can normalize instead
2801
- // of storing as a diff
2802
- canStoreDiff = false;
2803
- }
2616
+ ref.current = span;
2617
+ }, [ref, editor, key, text]);
2618
+ return /*#__PURE__*/React.createElement("span", {
2619
+ "data-slate-node": "text",
2620
+ ref: callbackRef
2621
+ }, children);
2622
+ };
2804
2623
 
2805
- if (Range.isExpanded(targetRange)) {
2806
- if (canStoreDiff && Path.equals(targetRange.anchor.path, targetRange.focus.path)) {
2807
- var point = {
2808
- path: targetRange.anchor.path,
2809
- offset: start.offset
2810
- };
2811
- var range = Editor.range(editor, point, point);
2812
- handleUserSelect(range);
2813
- return storeDiff(targetRange.anchor.path, {
2814
- text: '',
2815
- end: end.offset,
2816
- start: start.offset
2817
- });
2818
- }
2624
+ var MemoizedText = /*#__PURE__*/React.memo(Text, (prev, next) => {
2625
+ return next.parent === prev.parent && next.isLast === prev.isLast && next.renderLeaf === prev.renderLeaf && next.renderPlaceholder === prev.renderPlaceholder && next.text === prev.text && isTextDecorationsEqual(next.decorations, prev.decorations);
2626
+ });
2819
2627
 
2820
- return scheduleAction(() => Editor.deleteFragment(editor, {
2821
- direction
2822
- }), {
2823
- at: targetRange
2824
- });
2825
- }
2628
+ /**
2629
+ * Element.
2630
+ */
2631
+
2632
+ var Element = props => {
2633
+ var {
2634
+ decorations,
2635
+ element,
2636
+ renderElement = p => /*#__PURE__*/React.createElement(DefaultElement, Object.assign({}, p)),
2637
+ renderPlaceholder,
2638
+ renderLeaf,
2639
+ selection
2640
+ } = props;
2641
+ var editor = useSlateStatic();
2642
+ var readOnly = useReadOnly();
2643
+ var isInline = editor.isInline(element);
2644
+ var key = ReactEditor.findKey(editor, element);
2645
+ var ref = useCallback(ref => {
2646
+ // Update element-related weak maps with the DOM element ref.
2647
+ var KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor);
2648
+
2649
+ if (ref) {
2650
+ KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.set(key, ref);
2651
+ NODE_TO_ELEMENT.set(element, ref);
2652
+ ELEMENT_TO_NODE.set(ref, element);
2653
+ } else {
2654
+ KEY_TO_ELEMENT === null || KEY_TO_ELEMENT === void 0 ? void 0 : KEY_TO_ELEMENT.delete(key);
2655
+ NODE_TO_ELEMENT.delete(element);
2826
2656
  }
2657
+ }, [editor, key, element]);
2658
+ var children = useChildren({
2659
+ decorations,
2660
+ node: element,
2661
+ renderElement,
2662
+ renderPlaceholder,
2663
+ renderLeaf,
2664
+ selection
2665
+ }); // Attributes that the developer must mix into the element in their
2666
+ // custom node renderer component.
2827
2667
 
2828
- switch (type) {
2829
- case 'deleteByComposition':
2830
- case 'deleteByCut':
2831
- case 'deleteByDrag':
2832
- {
2833
- return scheduleAction(() => Editor.deleteFragment(editor), {
2834
- at: targetRange
2835
- });
2836
- }
2668
+ var attributes = {
2669
+ 'data-slate-node': 'element',
2670
+ ref
2671
+ };
2837
2672
 
2838
- case 'deleteContent':
2839
- case 'deleteContentForward':
2840
- {
2841
- var {
2842
- anchor
2843
- } = targetRange;
2673
+ if (isInline) {
2674
+ attributes['data-slate-inline'] = true;
2675
+ } // If it's a block node with inline children, add the proper `dir` attribute
2676
+ // for text direction.
2844
2677
 
2845
- if (canStoreDiff && Range.isCollapsed(targetRange)) {
2846
- var targetNode = Node.leaf(editor, anchor.path);
2847
2678
 
2848
- if (anchor.offset < targetNode.text.length) {
2849
- return storeDiff(anchor.path, {
2850
- text: '',
2851
- start: anchor.offset,
2852
- end: anchor.offset + 1
2853
- });
2854
- }
2855
- }
2679
+ if (!isInline && Editor.hasInlines(editor, element)) {
2680
+ var text = Node.string(element);
2681
+ var dir = getDirection(text);
2856
2682
 
2857
- return scheduleAction(() => Editor.deleteForward(editor), {
2858
- at: targetRange
2859
- });
2860
- }
2683
+ if (dir === 'rtl') {
2684
+ attributes.dir = dir;
2685
+ }
2686
+ } // If it's a void node, wrap the children in extra void-specific elements.
2861
2687
 
2862
- case 'deleteContentBackward':
2863
- {
2864
- var _nativeTargetRange;
2865
2688
 
2866
- var {
2867
- anchor: _anchor
2868
- } = targetRange; // If we have a mismatch between the native and slate selection being collapsed
2869
- // we are most likely deleting a zero-width placeholder and thus should perform it
2870
- // as an action to ensure correct behavior (mostly happens with mark placeholders)
2689
+ if (Editor.isVoid(editor, element)) {
2690
+ attributes['data-slate-void'] = true;
2871
2691
 
2872
- var nativeCollapsed = isDOMSelection(nativeTargetRange) ? nativeTargetRange.isCollapsed : !!((_nativeTargetRange = nativeTargetRange) !== null && _nativeTargetRange !== void 0 && _nativeTargetRange.collapsed);
2692
+ if (!readOnly && isInline) {
2693
+ attributes.contentEditable = false;
2694
+ }
2873
2695
 
2874
- if (canStoreDiff && nativeCollapsed && Range.isCollapsed(targetRange) && _anchor.offset > 0) {
2875
- return storeDiff(_anchor.path, {
2876
- text: '',
2877
- start: _anchor.offset - 1,
2878
- end: _anchor.offset
2879
- });
2880
- }
2696
+ var Tag = isInline ? 'span' : 'div';
2697
+ var [[_text]] = Node.texts(element);
2698
+ children = /*#__PURE__*/React.createElement(Tag, {
2699
+ "data-slate-spacer": true,
2700
+ style: {
2701
+ height: '0',
2702
+ color: 'transparent',
2703
+ outline: 'none',
2704
+ position: 'absolute'
2705
+ }
2706
+ }, /*#__PURE__*/React.createElement(MemoizedText, {
2707
+ renderPlaceholder: renderPlaceholder,
2708
+ decorations: [],
2709
+ isLast: false,
2710
+ parent: element,
2711
+ text: _text
2712
+ }));
2713
+ NODE_TO_INDEX.set(_text, 0);
2714
+ NODE_TO_PARENT.set(_text, element);
2715
+ }
2881
2716
 
2882
- return scheduleAction(() => Editor.deleteBackward(editor), {
2883
- at: targetRange
2884
- });
2885
- }
2717
+ return renderElement({
2718
+ attributes,
2719
+ children,
2720
+ element
2721
+ });
2722
+ };
2886
2723
 
2887
- case 'deleteEntireSoftLine':
2888
- {
2889
- return scheduleAction(() => {
2890
- Editor.deleteBackward(editor, {
2891
- unit: 'line'
2892
- });
2893
- Editor.deleteForward(editor, {
2894
- unit: 'line'
2895
- });
2896
- }, {
2897
- at: targetRange
2898
- });
2899
- }
2724
+ var MemoizedElement = /*#__PURE__*/React.memo(Element, (prev, next) => {
2725
+ return prev.element === next.element && prev.renderElement === next.renderElement && prev.renderLeaf === next.renderLeaf && prev.renderPlaceholder === next.renderPlaceholder && isElementDecorationsEqual(prev.decorations, next.decorations) && (prev.selection === next.selection || !!prev.selection && !!next.selection && Range.equals(prev.selection, next.selection));
2726
+ });
2727
+ /**
2728
+ * The default element renderer.
2729
+ */
2900
2730
 
2901
- case 'deleteHardLineBackward':
2902
- {
2903
- return scheduleAction(() => Editor.deleteBackward(editor, {
2904
- unit: 'block'
2905
- }), {
2906
- at: targetRange
2907
- });
2908
- }
2731
+ var DefaultElement = props => {
2732
+ var {
2733
+ attributes,
2734
+ children,
2735
+ element
2736
+ } = props;
2737
+ var editor = useSlateStatic();
2738
+ var Tag = editor.isInline(element) ? 'span' : 'div';
2739
+ return /*#__PURE__*/React.createElement(Tag, Object.assign({}, attributes, {
2740
+ style: {
2741
+ position: 'relative'
2742
+ }
2743
+ }), children);
2744
+ };
2909
2745
 
2910
- case 'deleteSoftLineBackward':
2911
- {
2912
- return scheduleAction(() => Editor.deleteBackward(editor, {
2913
- unit: 'line'
2914
- }), {
2915
- at: targetRange
2916
- });
2917
- }
2746
+ /**
2747
+ * A React context for sharing the `decorate` prop of the editable.
2748
+ */
2918
2749
 
2919
- case 'deleteHardLineForward':
2920
- {
2921
- return scheduleAction(() => Editor.deleteForward(editor, {
2922
- unit: 'block'
2923
- }), {
2924
- at: targetRange
2925
- });
2926
- }
2750
+ var DecorateContext = /*#__PURE__*/createContext(() => []);
2751
+ /**
2752
+ * Get the current `decorate` prop of the editable.
2753
+ */
2927
2754
 
2928
- case 'deleteSoftLineForward':
2929
- {
2930
- return scheduleAction(() => Editor.deleteForward(editor, {
2931
- unit: 'line'
2932
- }), {
2933
- at: targetRange
2934
- });
2935
- }
2755
+ var useDecorate = () => {
2756
+ return useContext(DecorateContext);
2757
+ };
2936
2758
 
2937
- case 'deleteWordBackward':
2938
- {
2939
- return scheduleAction(() => Editor.deleteBackward(editor, {
2940
- unit: 'word'
2941
- }), {
2942
- at: targetRange
2943
- });
2944
- }
2759
+ /**
2760
+ * A React context for sharing the `selected` state of an element.
2761
+ */
2945
2762
 
2946
- case 'deleteWordForward':
2947
- {
2948
- return scheduleAction(() => Editor.deleteForward(editor, {
2949
- unit: 'word'
2950
- }), {
2951
- at: targetRange
2952
- });
2953
- }
2763
+ var SelectedContext = /*#__PURE__*/createContext(false);
2764
+ /**
2765
+ * Get the current `selected` state of an element.
2766
+ */
2954
2767
 
2955
- case 'insertLineBreak':
2956
- {
2957
- return scheduleAction(() => Editor.insertSoftBreak(editor), {
2958
- at: targetRange
2959
- });
2960
- }
2768
+ var useSelected = () => {
2769
+ return useContext(SelectedContext);
2770
+ };
2961
2771
 
2962
- case 'insertParagraph':
2963
- {
2964
- return scheduleAction(() => Editor.insertBreak(editor), {
2965
- at: targetRange
2966
- });
2967
- }
2772
+ /**
2773
+ * Children.
2774
+ */
2775
+
2776
+ var useChildren = props => {
2777
+ var {
2778
+ decorations,
2779
+ node,
2780
+ renderElement,
2781
+ renderPlaceholder,
2782
+ renderLeaf,
2783
+ selection
2784
+ } = props;
2785
+ var decorate = useDecorate();
2786
+ var editor = useSlateStatic();
2787
+ var path = ReactEditor.findPath(editor, node);
2788
+ var children = [];
2789
+ var isLeafBlock = Element$1.isElement(node) && !editor.isInline(node) && Editor.hasInlines(editor, node);
2968
2790
 
2969
- case 'insertCompositionText':
2970
- case 'deleteCompositionText':
2971
- case 'insertFromComposition':
2972
- case 'insertFromDrop':
2973
- case 'insertFromPaste':
2974
- case 'insertFromYank':
2975
- case 'insertReplacementText':
2976
- case 'insertText':
2977
- {
2978
- if (isDataTransfer(data)) {
2979
- return scheduleAction(() => ReactEditor.insertData(editor, data), {
2980
- at: targetRange
2981
- });
2982
- }
2791
+ for (var i = 0; i < node.children.length; i++) {
2792
+ var p = path.concat(i);
2793
+ var n = node.children[i];
2794
+ var key = ReactEditor.findKey(editor, n);
2795
+ var range = Editor.range(editor, p);
2796
+ var sel = selection && Range.intersection(range, selection);
2797
+ var ds = decorate([n, p]);
2983
2798
 
2984
- var _text = data !== null && data !== void 0 ? data : ''; // COMPAT: If we are writing inside a placeholder, the ime inserts the text inside
2985
- // the placeholder itself and thus includes the zero-width space inside edit events.
2799
+ for (var dec of decorations) {
2800
+ var d = Range.intersection(dec, range);
2986
2801
 
2802
+ if (d) {
2803
+ ds.push(d);
2804
+ }
2805
+ }
2987
2806
 
2988
- if (EDITOR_TO_PENDING_INSERTION_MARKS.get(editor)) {
2989
- _text = _text.replace('\uFEFF', '');
2990
- } // Pastes from the Android clipboard will generate `insertText` events.
2991
- // If the copied text contains any newlines, Android will append an
2992
- // extra newline to the end of the copied text.
2807
+ if (Element$1.isElement(n)) {
2808
+ children.push( /*#__PURE__*/React.createElement(SelectedContext.Provider, {
2809
+ key: "provider-".concat(key.id),
2810
+ value: !!sel
2811
+ }, /*#__PURE__*/React.createElement(MemoizedElement, {
2812
+ decorations: ds,
2813
+ element: n,
2814
+ key: key.id,
2815
+ renderElement: renderElement,
2816
+ renderPlaceholder: renderPlaceholder,
2817
+ renderLeaf: renderLeaf,
2818
+ selection: sel
2819
+ })));
2820
+ } else {
2821
+ children.push( /*#__PURE__*/React.createElement(MemoizedText, {
2822
+ decorations: ds,
2823
+ key: key.id,
2824
+ isLast: isLeafBlock && i === node.children.length - 1,
2825
+ parent: node,
2826
+ renderPlaceholder: renderPlaceholder,
2827
+ renderLeaf: renderLeaf,
2828
+ text: n
2829
+ }));
2830
+ }
2993
2831
 
2832
+ NODE_TO_INDEX.set(n, i);
2833
+ NODE_TO_PARENT.set(n, node);
2834
+ }
2994
2835
 
2995
- if (type === 'insertText' && /.*\n.*\n$/.test(_text)) {
2996
- _text = _text.slice(0, -1);
2997
- } // If the text includes a newline, split it at newlines and paste each component
2998
- // string, with soft breaks in between each.
2836
+ return children;
2837
+ };
2999
2838
 
2839
+ /**
2840
+ * A React context for sharing the `readOnly` state of the editor.
2841
+ */
3000
2842
 
3001
- if (_text.includes('\n')) {
3002
- return scheduleAction(() => {
3003
- var parts = _text.split('\n');
2843
+ var ReadOnlyContext = /*#__PURE__*/createContext(false);
2844
+ /**
2845
+ * Get the current `readOnly` state of the editor.
2846
+ */
3004
2847
 
3005
- parts.forEach((line, i) => {
3006
- if (line) {
3007
- Editor.insertText(editor, line);
3008
- }
2848
+ var useReadOnly = () => {
2849
+ return useContext(ReadOnlyContext);
2850
+ };
3009
2851
 
3010
- if (i !== parts.length - 1) {
3011
- Editor.insertSoftBreak(editor);
3012
- }
3013
- });
3014
- }, {
3015
- at: targetRange
3016
- });
3017
- }
2852
+ var SlateContext = /*#__PURE__*/createContext(null);
2853
+ /**
2854
+ * Get the current editor object from the React context.
2855
+ */
3018
2856
 
3019
- if (Path.equals(targetRange.anchor.path, targetRange.focus.path)) {
3020
- var [_start2, _end2] = Range.edges(targetRange);
3021
- var _diff = {
3022
- start: _start2.offset,
3023
- end: _end2.offset,
3024
- text: _text
3025
- }; // COMPAT: Swiftkey has a weird bug where the target range of the 2nd word
3026
- // inserted after a mark placeholder is inserted with an anchor offset off by 1.
3027
- // So writing 'some text' will result in 'some ttext'. Luckily all 'normal' insert
3028
- // text events are fired with the correct target ranges, only the final 'insertComposition'
3029
- // isn't, so we can adjust the target range start offset if we are confident this is the
3030
- // swiftkey insert causing the issue.
2857
+ var useSlate = () => {
2858
+ var context = useContext(SlateContext);
3031
2859
 
3032
- if (_text && insertPositionHint && type === 'insertCompositionText') {
3033
- var hintPosition = insertPositionHint.start + insertPositionHint.text.search(/\S|$/);
2860
+ if (!context) {
2861
+ throw new Error("The `useSlate` hook must be used inside the <Slate> component's context.");
2862
+ }
3034
2863
 
3035
- var diffPosition = _diff.start + _diff.text.search(/\S|$/);
2864
+ var {
2865
+ editor
2866
+ } = context;
2867
+ return editor;
2868
+ };
2869
+ var useSlateWithV = () => {
2870
+ var context = useContext(SlateContext);
3036
2871
 
3037
- if (diffPosition === hintPosition + 1 && _diff.end === insertPositionHint.start + insertPositionHint.text.length) {
3038
- _diff.start -= 1;
3039
- insertPositionHint = null;
3040
- scheduleFlush();
3041
- } else {
3042
- insertPositionHint = false;
3043
- }
3044
- } else if (type === 'insertText') {
3045
- if (insertPositionHint === null) {
3046
- insertPositionHint = _diff;
3047
- } else if (insertPositionHint && Range.isCollapsed(targetRange) && insertPositionHint.end + insertPositionHint.text.length === _start2.offset) {
3048
- insertPositionHint = _objectSpread$3(_objectSpread$3({}, insertPositionHint), {}, {
3049
- text: insertPositionHint.text + _text
3050
- });
3051
- } else {
3052
- insertPositionHint = false;
3053
- }
3054
- } else {
3055
- insertPositionHint = false;
3056
- }
2872
+ if (!context) {
2873
+ throw new Error("The `useSlate` hook must be used inside the <Slate> component's context.");
2874
+ }
3057
2875
 
3058
- if (canStoreDiff) {
3059
- storeDiff(_start2.path, _diff);
3060
- return;
3061
- }
3062
- }
2876
+ return context;
2877
+ };
3063
2878
 
3064
- return scheduleAction(() => Editor.insertText(editor, _text), {
3065
- at: targetRange
3066
- });
3067
- }
2879
+ function useTrackUserInput() {
2880
+ var editor = useSlateStatic();
2881
+ var receivedUserInput = useRef(false);
2882
+ var animationFrameIdRef = useRef(0);
2883
+ var onUserInput = useCallback(() => {
2884
+ if (receivedUserInput.current) {
2885
+ return;
3068
2886
  }
3069
- };
3070
2887
 
3071
- var hasPendingAction = () => {
3072
- return !!EDITOR_TO_PENDING_ACTION.get(editor);
2888
+ receivedUserInput.current = true;
2889
+ var window = ReactEditor.getWindow(editor);
2890
+ window.cancelAnimationFrame(animationFrameIdRef.current);
2891
+ animationFrameIdRef.current = window.requestAnimationFrame(() => {
2892
+ receivedUserInput.current = false;
2893
+ });
2894
+ }, [editor]);
2895
+ useEffect(() => () => cancelAnimationFrame(animationFrameIdRef.current), []);
2896
+ return {
2897
+ receivedUserInput,
2898
+ onUserInput
3073
2899
  };
2900
+ }
3074
2901
 
3075
- var hasPendingDiffs = () => {
3076
- var _EDITOR_TO_PENDING_DI4;
2902
+ var TRIPLE_CLICK = 3;
3077
2903
 
3078
- return !!((_EDITOR_TO_PENDING_DI4 = EDITOR_TO_PENDING_DIFFS.get(editor)) !== null && _EDITOR_TO_PENDING_DI4 !== void 0 && _EDITOR_TO_PENDING_DI4.length);
3079
- };
2904
+ /**
2905
+ * Hotkey mappings for each platform.
2906
+ */
3080
2907
 
3081
- var hasPendingChanges = () => {
3082
- return hasPendingAction() || hasPendingDiffs();
2908
+ var HOTKEYS = {
2909
+ bold: 'mod+b',
2910
+ compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
2911
+ moveBackward: 'left',
2912
+ moveForward: 'right',
2913
+ moveWordBackward: 'ctrl+left',
2914
+ moveWordForward: 'ctrl+right',
2915
+ deleteBackward: 'shift?+backspace',
2916
+ deleteForward: 'shift?+delete',
2917
+ extendBackward: 'shift+left',
2918
+ extendForward: 'shift+right',
2919
+ italic: 'mod+i',
2920
+ insertSoftBreak: 'shift+enter',
2921
+ splitBlock: 'enter',
2922
+ undo: 'mod+z'
2923
+ };
2924
+ var APPLE_HOTKEYS = {
2925
+ moveLineBackward: 'opt+up',
2926
+ moveLineForward: 'opt+down',
2927
+ moveWordBackward: 'opt+left',
2928
+ moveWordForward: 'opt+right',
2929
+ deleteBackward: ['ctrl+backspace', 'ctrl+h'],
2930
+ deleteForward: ['ctrl+delete', 'ctrl+d'],
2931
+ deleteLineBackward: 'cmd+shift?+backspace',
2932
+ deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
2933
+ deleteWordBackward: 'opt+shift?+backspace',
2934
+ deleteWordForward: 'opt+shift?+delete',
2935
+ extendLineBackward: 'opt+shift+up',
2936
+ extendLineForward: 'opt+shift+down',
2937
+ redo: 'cmd+shift+z',
2938
+ transposeCharacter: 'ctrl+t'
2939
+ };
2940
+ var WINDOWS_HOTKEYS = {
2941
+ deleteWordBackward: 'ctrl+shift?+backspace',
2942
+ deleteWordForward: 'ctrl+shift?+delete',
2943
+ redo: ['ctrl+y', 'ctrl+shift+z']
2944
+ };
2945
+ /**
2946
+ * Create a platform-aware hotkey checker.
2947
+ */
2948
+
2949
+ var create = key => {
2950
+ var generic = HOTKEYS[key];
2951
+ var apple = APPLE_HOTKEYS[key];
2952
+ var windows = WINDOWS_HOTKEYS[key];
2953
+ var isGeneric = generic && isKeyHotkey(generic);
2954
+ var isApple = apple && isKeyHotkey(apple);
2955
+ var isWindows = windows && isKeyHotkey(windows);
2956
+ return event => {
2957
+ if (isGeneric && isGeneric(event)) return true;
2958
+ if (IS_APPLE && isApple && isApple(event)) return true;
2959
+ if (!IS_APPLE && isWindows && isWindows(event)) return true;
2960
+ return false;
3083
2961
  };
2962
+ };
2963
+ /**
2964
+ * Hotkeys.
2965
+ */
3084
2966
 
3085
- var isFlushing = () => {
3086
- return flushing;
3087
- };
3088
2967
 
3089
- var handleUserSelect = range => {
3090
- EDITOR_TO_PENDING_SELECTION.set(editor, range);
2968
+ var Hotkeys = {
2969
+ isBold: create('bold'),
2970
+ isCompose: create('compose'),
2971
+ isMoveBackward: create('moveBackward'),
2972
+ isMoveForward: create('moveForward'),
2973
+ isDeleteBackward: create('deleteBackward'),
2974
+ isDeleteForward: create('deleteForward'),
2975
+ isDeleteLineBackward: create('deleteLineBackward'),
2976
+ isDeleteLineForward: create('deleteLineForward'),
2977
+ isDeleteWordBackward: create('deleteWordBackward'),
2978
+ isDeleteWordForward: create('deleteWordForward'),
2979
+ isExtendBackward: create('extendBackward'),
2980
+ isExtendForward: create('extendForward'),
2981
+ isExtendLineBackward: create('extendLineBackward'),
2982
+ isExtendLineForward: create('extendLineForward'),
2983
+ isItalic: create('italic'),
2984
+ isMoveLineBackward: create('moveLineBackward'),
2985
+ isMoveLineForward: create('moveLineForward'),
2986
+ isMoveWordBackward: create('moveWordBackward'),
2987
+ isMoveWordForward: create('moveWordForward'),
2988
+ isRedo: create('redo'),
2989
+ isSoftBreak: create('insertSoftBreak'),
2990
+ isSplitBlock: create('splitBlock'),
2991
+ isTransposeCharacter: create('transposeCharacter'),
2992
+ isUndo: create('undo')
2993
+ };
3091
2994
 
3092
- if (flushTimeoutId) {
3093
- clearTimeout(flushTimeoutId);
3094
- flushTimeoutId = null;
3095
- }
2995
+ var createRestoreDomManager = (editor, receivedUserInput) => {
2996
+ var bufferedMutations = [];
3096
2997
 
3097
- var {
3098
- selection
3099
- } = editor;
2998
+ var clear = () => {
2999
+ bufferedMutations = [];
3000
+ };
3100
3001
 
3101
- if (!range) {
3002
+ var registerMutations = mutations => {
3003
+ if (!receivedUserInput.current) {
3102
3004
  return;
3103
3005
  }
3104
3006
 
3105
- var pathChanged = !selection || !Path.equals(selection.anchor.path, range.anchor.path);
3106
- var parentPathChanged = !selection || !Path.equals(selection.anchor.path.slice(0, -1), range.anchor.path.slice(0, -1));
3107
-
3108
- if (pathChanged && insertPositionHint || parentPathChanged) {
3109
- insertPositionHint = false;
3110
- }
3111
-
3112
- if (pathChanged || hasPendingDiffs()) {
3113
- flushTimeoutId = setTimeout(flush, FLUSH_DELAY);
3114
- }
3007
+ var trackedMutations = mutations.filter(mutation => isTrackedMutation(editor, mutation, mutations));
3008
+ bufferedMutations.push(...trackedMutations);
3115
3009
  };
3116
3010
 
3117
- var handleInput = () => {
3118
- if (hasPendingAction() || !hasPendingDiffs()) {
3119
- flush();
3120
- }
3121
- };
3011
+ function restoreDOM() {
3012
+ if (bufferedMutations.length > 0) {
3013
+ bufferedMutations.reverse().forEach(mutation => {
3014
+ if (mutation.type === 'characterData') {
3015
+ // We don't want to restore the DOM for characterData mutations
3016
+ // because this interrupts the composition.
3017
+ return;
3018
+ }
3122
3019
 
3123
- var handleKeyDown = _ => {
3124
- // COMPAT: Swiftkey closes the keyboard when typing inside a empty node
3125
- // directly next to a non-contenteditable element (= the placeholder).
3126
- // The only event fired soon enough for us to allow hiding the placeholder
3127
- // without swiftkey picking it up is the keydown event, so we have to hide it
3128
- // here. See https://github.com/ianstormtaylor/slate/pull/4988#issuecomment-1201050535
3129
- if (!hasPendingDiffs()) {
3130
- updatePlaceholderVisibility(true);
3131
- setTimeout(updatePlaceholderVisibility);
3132
- }
3133
- };
3020
+ mutation.removedNodes.forEach(node => {
3021
+ mutation.target.insertBefore(node, mutation.nextSibling);
3022
+ });
3023
+ mutation.addedNodes.forEach(node => {
3024
+ mutation.target.removeChild(node);
3025
+ });
3026
+ }); // Clear buffered mutations to ensure we don't undo them twice
3134
3027
 
3135
- var scheduleFlush = () => {
3136
- if (!hasPendingAction()) {
3137
- actionTimeoutId = setTimeout(flush);
3028
+ clear();
3138
3029
  }
3139
- };
3030
+ }
3140
3031
 
3141
- var handleDomMutations = mutations => {
3142
- if (hasPendingDiffs() || hasPendingAction()) {
3143
- return;
3144
- }
3032
+ return {
3033
+ registerMutations,
3034
+ restoreDOM,
3035
+ clear
3036
+ };
3037
+ };
3145
3038
 
3146
- if (mutations.some(mutation => isTrackedMutation(editor, mutation, mutations))) {
3147
- var _EDITOR_TO_FORCE_REND;
3039
+ var MUTATION_OBSERVER_CONFIG = {
3040
+ subtree: true,
3041
+ childList: true,
3042
+ characterData: true,
3043
+ characterDataOldValue: true
3044
+ }; // We have to use a class component here since we rely on `getSnapshotBeforeUpdate` which has no FC equivalent
3045
+ // to run code synchronously immediately before react commits the component update to the DOM.
3148
3046
 
3149
- // Cause a re-render to restore the dom state if we encounter tracked mutations without
3150
- // a corresponding pending action.
3151
- (_EDITOR_TO_FORCE_REND = EDITOR_TO_FORCE_RENDER.get(editor)) === null || _EDITOR_TO_FORCE_REND === void 0 ? void 0 : _EDITOR_TO_FORCE_REND();
3152
- }
3153
- };
3047
+ class RestoreDOMComponent extends Component {
3048
+ constructor() {
3049
+ super(...arguments);
3050
+ this.context = null;
3051
+ this.manager = null;
3052
+ this.mutationObserver = null;
3053
+ }
3154
3054
 
3155
- return {
3156
- flush,
3157
- scheduleFlush,
3158
- hasPendingDiffs,
3159
- hasPendingAction,
3160
- hasPendingChanges,
3161
- isFlushing,
3162
- handleUserSelect,
3163
- handleCompositionEnd,
3164
- handleCompositionStart,
3165
- handleDOMBeforeInput,
3166
- handleKeyDown,
3167
- handleDomMutations,
3168
- handleInput
3169
- };
3170
- }
3055
+ observe() {
3056
+ var _this$mutationObserve;
3171
3057
 
3172
- function useIsMounted() {
3173
- var isMountedRef = useRef(false);
3174
- useEffect(() => {
3175
- isMountedRef.current = true;
3176
- return () => {
3177
- isMountedRef.current = false;
3178
- };
3179
- }, []);
3180
- return isMountedRef.current;
3181
- }
3058
+ var {
3059
+ node
3060
+ } = this.props;
3182
3061
 
3183
- function useMutationObserver(node, callback, options) {
3184
- var [mutationObserver] = useState(() => new MutationObserver(callback));
3185
- useIsomorphicLayoutEffect(() => {
3186
- // Discard mutations caused during render phase. This works due to react calling
3187
- // useLayoutEffect synchronously after the render phase before the next tick.
3188
- mutationObserver.takeRecords();
3189
- });
3190
- useEffect(() => {
3191
3062
  if (!node.current) {
3192
3063
  throw new Error('Failed to attach MutationObserver, `node` is undefined');
3193
3064
  }
3194
3065
 
3195
- mutationObserver.observe(node.current, options);
3196
- return () => mutationObserver.disconnect();
3197
- }, [mutationObserver, node, options]);
3198
- }
3066
+ (_this$mutationObserve = this.mutationObserver) === null || _this$mutationObserve === void 0 ? void 0 : _this$mutationObserve.observe(node.current, MUTATION_OBSERVER_CONFIG);
3067
+ }
3199
3068
 
3200
- var _excluded$2 = ["node"];
3069
+ componentDidMount() {
3070
+ var {
3071
+ receivedUserInput
3072
+ } = this.props;
3073
+ var editor = this.context;
3074
+ this.manager = createRestoreDomManager(editor, receivedUserInput);
3075
+ this.mutationObserver = new MutationObserver(this.manager.registerMutations);
3076
+ this.observe();
3077
+ }
3201
3078
 
3202
- function ownKeys$2(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; }
3079
+ getSnapshotBeforeUpdate() {
3080
+ var _this$mutationObserve2, _this$mutationObserve3, _this$manager2;
3203
3081
 
3204
- function _objectSpread$2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$2(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$2(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
3205
- var MUTATION_OBSERVER_CONFIG = {
3206
- subtree: true,
3207
- childList: true,
3208
- characterData: true
3209
- };
3210
- var useAndroidInputManager = !IS_ANDROID ? () => null : _ref => {
3211
- var {
3212
- node
3213
- } = _ref,
3214
- options = _objectWithoutProperties(_ref, _excluded$2);
3082
+ var pendingMutations = (_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 ? void 0 : _this$mutationObserve2.takeRecords();
3215
3083
 
3216
- if (!IS_ANDROID) {
3084
+ if (pendingMutations !== null && pendingMutations !== void 0 && pendingMutations.length) {
3085
+ var _this$manager;
3086
+
3087
+ (_this$manager = this.manager) === null || _this$manager === void 0 ? void 0 : _this$manager.registerMutations(pendingMutations);
3088
+ }
3089
+
3090
+ (_this$mutationObserve3 = this.mutationObserver) === null || _this$mutationObserve3 === void 0 ? void 0 : _this$mutationObserve3.disconnect();
3091
+ (_this$manager2 = this.manager) === null || _this$manager2 === void 0 ? void 0 : _this$manager2.restoreDOM();
3217
3092
  return null;
3218
3093
  }
3219
3094
 
3220
- var editor = useSlateStatic();
3221
- var isMounted = useIsMounted();
3222
- var [inputManager] = useState(() => createAndroidInputManager(_objectSpread$2({
3223
- editor
3224
- }, options)));
3225
- useMutationObserver(node, inputManager.handleDomMutations, MUTATION_OBSERVER_CONFIG);
3226
- EDITOR_TO_SCHEDULE_FLUSH.set(editor, inputManager.scheduleFlush);
3095
+ componentDidUpdate() {
3096
+ var _this$manager3;
3227
3097
 
3228
- if (isMounted) {
3229
- inputManager.flush();
3098
+ (_this$manager3 = this.manager) === null || _this$manager3 === void 0 ? void 0 : _this$manager3.clear();
3099
+ this.observe();
3230
3100
  }
3231
3101
 
3232
- return inputManager;
3233
- };
3102
+ componentWillUnmount() {
3103
+ var _this$mutationObserve4;
3234
3104
 
3235
- function useTrackUserInput() {
3236
- var editor = useSlateStatic();
3237
- var receivedUserInput = useRef(false);
3238
- var animationFrameIdRef = useRef(0);
3239
- var onUserInput = useCallback(() => {
3240
- if (receivedUserInput.current) {
3241
- return;
3242
- }
3105
+ (_this$mutationObserve4 = this.mutationObserver) === null || _this$mutationObserve4 === void 0 ? void 0 : _this$mutationObserve4.disconnect();
3106
+ }
3107
+
3108
+ render() {
3109
+ return this.props.children;
3110
+ }
3243
3111
 
3244
- receivedUserInput.current = true;
3245
- var window = ReactEditor.getWindow(editor);
3246
- window.cancelAnimationFrame(animationFrameIdRef.current);
3247
- animationFrameIdRef.current = window.requestAnimationFrame(() => {
3248
- receivedUserInput.current = false;
3249
- });
3250
- }, [editor]);
3251
- useEffect(() => () => cancelAnimationFrame(animationFrameIdRef.current), []);
3252
- return {
3253
- receivedUserInput,
3254
- onUserInput
3255
- };
3256
3112
  }
3257
3113
 
3114
+ RestoreDOMComponent.contextType = EditorContext;
3115
+ var RestoreDOM = IS_ANDROID ? RestoreDOMComponent : _ref => {
3116
+ var {
3117
+ children
3118
+ } = _ref;
3119
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
3120
+ };
3121
+
3258
3122
  var _excluded$1 = ["autoFocus", "decorate", "onDOMBeforeInput", "placeholder", "readOnly", "renderElement", "renderLeaf", "renderPlaceholder", "scrollSelectionIntoView", "style", "as", "disableDefaultStyles"],
3259
3123
  _excluded2 = ["text"];
3260
3124