slate-react 0.72.1 → 0.72.2-20211116213954

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
@@ -172,6 +172,13 @@ var EDITOR_TO_KEY_TO_ELEMENT = new WeakMap();
172
172
 
173
173
  var IS_READ_ONLY = new WeakMap();
174
174
  var IS_FOCUSED = new WeakMap();
175
+ var IS_COMPOSING = new WeakMap();
176
+ var IS_ON_COMPOSITION_END = new WeakMap();
177
+ /**
178
+ * Weak maps for saving text on composition stage.
179
+ */
180
+
181
+ var EDITOR_ON_COMPOSITION_TEXT = new WeakMap();
175
182
  /**
176
183
  * Weak map for associating the context `onChange` context with the plugin.
177
184
  */
@@ -2146,6 +2153,233 @@ var isDOMEventHandled = (event, handler) => {
2146
2153
  return event.defaultPrevented;
2147
2154
  };
2148
2155
 
2156
+ /**
2157
+ * Returns the number of characters that are the same at the beginning of the
2158
+ * String.
2159
+ *
2160
+ * @param prev the previous text
2161
+ * @param next the next text
2162
+ * @returns the offset of the start of the difference; null if there is no difference
2163
+ */
2164
+
2165
+ function getDiffStart(prev, next) {
2166
+ var length = Math.min(prev.length, next.length);
2167
+
2168
+ for (var i = 0; i < length; i++) {
2169
+ if (prev.charAt(i) !== next.charAt(i)) return i;
2170
+ }
2171
+
2172
+ if (prev.length !== next.length) return length;
2173
+ return null;
2174
+ }
2175
+ /**
2176
+ * Returns the number of characters that are the same at the end of the String
2177
+ * up to `max`. Max prevents double-counting characters when there are
2178
+ * multiple duplicate characters around the diff area.
2179
+ *
2180
+ * @param prev the previous text
2181
+ * @param next the next text
2182
+ * @param max the max length to test.
2183
+ * @returns number of characters that are the same at the end of the string
2184
+ */
2185
+
2186
+
2187
+ function getDiffEnd(prev, next, max) {
2188
+ var prevLength = prev.length;
2189
+ var nextLength = next.length;
2190
+ var length = Math.min(prevLength, nextLength, max);
2191
+
2192
+ for (var i = 0; i < length; i++) {
2193
+ var prevChar = prev.charAt(prevLength - i - 1);
2194
+ var nextChar = next.charAt(nextLength - i - 1);
2195
+ if (prevChar !== nextChar) return i;
2196
+ }
2197
+
2198
+ if (prev.length !== next.length) return length;
2199
+ return null;
2200
+ }
2201
+ /**
2202
+ * Takes two strings and returns an object representing two offsets. The
2203
+ * first, `start` represents the number of characters that are the same at
2204
+ * the front of the String. The `end` represents the number of characters
2205
+ * that are the same at the end of the String.
2206
+ *
2207
+ * Returns null if they are identical.
2208
+ *
2209
+ * @param prev the previous text
2210
+ * @param next the next text
2211
+ * @returns the difference text range; null if there are no differences.
2212
+ */
2213
+
2214
+
2215
+ function getDiffOffsets(prev, next) {
2216
+ if (prev === next) return null;
2217
+ var start = getDiffStart(prev, next);
2218
+ if (start === null) return null;
2219
+ var maxEnd = Math.min(prev.length - start, next.length - start);
2220
+ var end = getDiffEnd(prev, next, maxEnd);
2221
+ if (end === null) return null;
2222
+ return {
2223
+ start,
2224
+ end
2225
+ };
2226
+ }
2227
+ /**
2228
+ * Takes a text string and returns a slice from the string at the given text range
2229
+ *
2230
+ * @param text the text
2231
+ * @param offsets the text range
2232
+ * @returns the text slice at text range
2233
+ */
2234
+
2235
+
2236
+ function sliceText(text, offsets) {
2237
+ return text.slice(offsets.start, text.length - offsets.end);
2238
+ }
2239
+ /**
2240
+ * Takes two strings and returns a smart diff that can be used to describe the
2241
+ * change in a way that can be used as operations like inserting, removing or
2242
+ * replacing text.
2243
+ *
2244
+ * @param prev the previous text
2245
+ * @param next the next text
2246
+ * @returns the text difference
2247
+ */
2248
+
2249
+
2250
+ function diffText(prev, next) {
2251
+ if (prev === undefined || next === undefined) return null;
2252
+ var offsets = getDiffOffsets(prev, next);
2253
+ if (offsets == null) return null;
2254
+ var insertText = sliceText(next, offsets);
2255
+ var removeText = sliceText(prev, offsets);
2256
+ return {
2257
+ start: offsets.start,
2258
+ end: prev.length - offsets.end,
2259
+ insertText,
2260
+ removeText
2261
+ };
2262
+ }
2263
+ function combineInsertedText(insertedText) {
2264
+ return insertedText.reduce((acc, _ref) => {
2265
+ var {
2266
+ text
2267
+ } = _ref;
2268
+ return "".concat(acc).concat(text.insertText);
2269
+ }, '');
2270
+ }
2271
+ function getTextInsertion(editor, domNode) {
2272
+ var node = ReactEditor.toSlateNode(editor, domNode);
2273
+
2274
+ if (!Text$1.isText(node)) {
2275
+ return undefined;
2276
+ }
2277
+
2278
+ var prevText = node.text;
2279
+ var nextText = domNode.textContent; // textContent will pad an extra \n when the textContent ends with an \n
2280
+
2281
+ if (nextText.endsWith('\n')) {
2282
+ nextText = nextText.slice(0, nextText.length - 1);
2283
+ } // If the text is no different, there is no diff.
2284
+
2285
+
2286
+ if (nextText !== prevText) {
2287
+ var textDiff = diffText(prevText, nextText);
2288
+
2289
+ if (textDiff !== null) {
2290
+ var textPath = ReactEditor.findPath(editor, node);
2291
+ return {
2292
+ text: textDiff,
2293
+ path: textPath
2294
+ };
2295
+ }
2296
+ }
2297
+
2298
+ return undefined;
2299
+ }
2300
+ function normalizeTextInsertionRange(editor, range, _ref2) {
2301
+ var {
2302
+ path,
2303
+ text
2304
+ } = _ref2;
2305
+ var insertionRange = {
2306
+ anchor: {
2307
+ path,
2308
+ offset: text.start
2309
+ },
2310
+ focus: {
2311
+ path,
2312
+ offset: text.end
2313
+ }
2314
+ };
2315
+
2316
+ if (!range || !Range.isCollapsed(range)) {
2317
+ return insertionRange;
2318
+ }
2319
+
2320
+ var {
2321
+ insertText,
2322
+ removeText
2323
+ } = text;
2324
+ var isSingleCharacterInsertion = insertText.length === 1 || removeText.length === 1;
2325
+ /**
2326
+ * This code handles edge cases that arise from text diffing when the
2327
+ * inserted or removed character is a single character, and the character
2328
+ * right before or after the anchor is the same as the one being inserted or
2329
+ * removed.
2330
+ *
2331
+ * Take this example: hello|o
2332
+ *
2333
+ * If another `o` is inserted at the selection's anchor in the example above,
2334
+ * it should be inserted at the anchor, but using text diffing, we actually
2335
+ * detect that the character was inserted after the second `o`:
2336
+ *
2337
+ * helloo[o]|
2338
+ *
2339
+ * Instead, in these very specific edge cases, we assume that the character
2340
+ * needs to be inserted after the anchor rather than where the diff was found:
2341
+ *
2342
+ * hello[o]|o
2343
+ */
2344
+
2345
+ if (isSingleCharacterInsertion && Path.equals(range.anchor.path, path)) {
2346
+ var [_text] = Array.from(Editor.nodes(editor, {
2347
+ at: range,
2348
+ match: Text$1.isText
2349
+ }));
2350
+
2351
+ if (_text) {
2352
+ var [node] = _text;
2353
+ var {
2354
+ anchor
2355
+ } = range;
2356
+ var characterBeforeAnchor = node.text[anchor.offset - 1];
2357
+ var characterAfterAnchor = node.text[anchor.offset];
2358
+
2359
+ if (insertText.length === 1 && insertText === characterAfterAnchor) {
2360
+ // Assume text should be inserted at the anchor
2361
+ return range;
2362
+ }
2363
+
2364
+ if (removeText.length === 1 && removeText === characterBeforeAnchor) {
2365
+ // Assume text should be removed right before the anchor
2366
+ return {
2367
+ anchor: {
2368
+ path,
2369
+ offset: anchor.offset - 1
2370
+ },
2371
+ focus: {
2372
+ path,
2373
+ offset: anchor.offset
2374
+ }
2375
+ };
2376
+ }
2377
+ }
2378
+ }
2379
+
2380
+ return insertionRange;
2381
+ }
2382
+
2149
2383
  /**
2150
2384
  * An auto-incrementing identifier for keys.
2151
2385
  */
@@ -2715,247 +2949,20 @@ var ReactEditor = {
2715
2949
 
2716
2950
  };
2717
2951
 
2718
- /**
2719
- * Returns the number of characters that are the same at the beginning of the
2720
- * String.
2721
- *
2722
- * @param prev the previous text
2723
- * @param next the next text
2724
- * @returns the offset of the start of the difference; null if there is no difference
2725
- */
2726
-
2727
- function getDiffStart(prev, next) {
2728
- var length = Math.min(prev.length, next.length);
2729
-
2730
- for (var i = 0; i < length; i++) {
2731
- if (prev.charAt(i) !== next.charAt(i)) return i;
2732
- }
2733
-
2734
- if (prev.length !== next.length) return length;
2735
- return null;
2736
- }
2737
- /**
2738
- * Returns the number of characters that are the same at the end of the String
2739
- * up to `max`. Max prevents double-counting characters when there are
2740
- * multiple duplicate characters around the diff area.
2741
- *
2742
- * @param prev the previous text
2743
- * @param next the next text
2744
- * @param max the max length to test.
2745
- * @returns number of characters that are the same at the end of the string
2746
- */
2747
-
2748
-
2749
- function getDiffEnd(prev, next, max) {
2750
- var prevLength = prev.length;
2751
- var nextLength = next.length;
2752
- var length = Math.min(prevLength, nextLength, max);
2753
-
2754
- for (var i = 0; i < length; i++) {
2755
- var prevChar = prev.charAt(prevLength - i - 1);
2756
- var nextChar = next.charAt(nextLength - i - 1);
2757
- if (prevChar !== nextChar) return i;
2758
- }
2759
-
2760
- if (prev.length !== next.length) return length;
2761
- return null;
2762
- }
2763
- /**
2764
- * Takes two strings and returns an object representing two offsets. The
2765
- * first, `start` represents the number of characters that are the same at
2766
- * the front of the String. The `end` represents the number of characters
2767
- * that are the same at the end of the String.
2768
- *
2769
- * Returns null if they are identical.
2770
- *
2771
- * @param prev the previous text
2772
- * @param next the next text
2773
- * @returns the difference text range; null if there are no differences.
2774
- */
2775
-
2776
-
2777
- function getDiffOffsets(prev, next) {
2778
- if (prev === next) return null;
2779
- var start = getDiffStart(prev, next);
2780
- if (start === null) return null;
2781
- var maxEnd = Math.min(prev.length - start, next.length - start);
2782
- var end = getDiffEnd(prev, next, maxEnd);
2783
- if (end === null) return null;
2784
- return {
2785
- start,
2786
- end
2787
- };
2788
- }
2789
- /**
2790
- * Takes a text string and returns a slice from the string at the given text range
2791
- *
2792
- * @param text the text
2793
- * @param offsets the text range
2794
- * @returns the text slice at text range
2795
- */
2796
-
2797
-
2798
- function sliceText(text, offsets) {
2799
- return text.slice(offsets.start, text.length - offsets.end);
2800
- }
2801
- /**
2802
- * Takes two strings and returns a smart diff that can be used to describe the
2803
- * change in a way that can be used as operations like inserting, removing or
2804
- * replacing text.
2805
- *
2806
- * @param prev the previous text
2807
- * @param next the next text
2808
- * @returns the text difference
2809
- */
2810
-
2811
-
2812
- function diffText(prev, next) {
2813
- if (prev === undefined || next === undefined) return null;
2814
- var offsets = getDiffOffsets(prev, next);
2815
- if (offsets == null) return null;
2816
- var insertText = sliceText(next, offsets);
2817
- var removeText = sliceText(prev, offsets);
2818
- return {
2819
- start: offsets.start,
2820
- end: prev.length - offsets.end,
2821
- insertText,
2822
- removeText
2823
- };
2824
- }
2825
- function combineInsertedText(insertedText) {
2826
- return insertedText.reduce((acc, _ref) => {
2827
- var {
2828
- text
2829
- } = _ref;
2830
- return "".concat(acc).concat(text.insertText);
2831
- }, '');
2832
- }
2833
- function getTextInsertion(editor, domNode) {
2834
- var node = ReactEditor.toSlateNode(editor, domNode);
2835
-
2836
- if (!Text$1.isText(node)) {
2837
- return undefined;
2838
- }
2839
-
2840
- var prevText = node.text;
2841
- var nextText = domNode.textContent; // textContent will pad an extra \n when the textContent ends with an \n
2842
-
2843
- if (nextText.endsWith('\n')) {
2844
- nextText = nextText.slice(0, nextText.length - 1);
2845
- } // If the text is no different, there is no diff.
2846
-
2847
-
2848
- if (nextText !== prevText) {
2849
- var textDiff = diffText(prevText, nextText);
2850
-
2851
- if (textDiff !== null) {
2852
- var textPath = ReactEditor.findPath(editor, node);
2853
- return {
2854
- text: textDiff,
2855
- path: textPath
2856
- };
2857
- }
2858
- }
2859
-
2860
- return undefined;
2861
- }
2862
- function normalizeTextInsertionRange(editor, range, _ref2) {
2863
- var {
2864
- path,
2865
- text
2866
- } = _ref2;
2867
- var insertionRange = {
2868
- anchor: {
2869
- path,
2870
- offset: text.start
2871
- },
2872
- focus: {
2873
- path,
2874
- offset: text.end
2875
- }
2876
- };
2877
-
2878
- if (!range || !Range.isCollapsed(range)) {
2879
- return insertionRange;
2880
- }
2881
-
2882
- var {
2883
- insertText,
2884
- removeText
2885
- } = text;
2886
- var isSingleCharacterInsertion = insertText.length === 1 || removeText.length === 1;
2887
- /**
2888
- * This code handles edge cases that arise from text diffing when the
2889
- * inserted or removed character is a single character, and the character
2890
- * right before or after the anchor is the same as the one being inserted or
2891
- * removed.
2892
- *
2893
- * Take this example: hello|o
2894
- *
2895
- * If another `o` is inserted at the selection's anchor in the example above,
2896
- * it should be inserted at the anchor, but using text diffing, we actually
2897
- * detect that the character was inserted after the second `o`:
2898
- *
2899
- * helloo[o]|
2900
- *
2901
- * Instead, in these very specific edge cases, we assume that the character
2902
- * needs to be inserted after the anchor rather than where the diff was found:
2903
- *
2904
- * hello[o]|o
2905
- */
2906
-
2907
- if (isSingleCharacterInsertion && Path.equals(range.anchor.path, path)) {
2908
- var [_text] = Array.from(Editor.nodes(editor, {
2909
- at: range,
2910
- match: Text$1.isText
2911
- }));
2912
-
2913
- if (_text) {
2914
- var [node] = _text;
2915
- var {
2916
- anchor
2917
- } = range;
2918
- var characterBeforeAnchor = node.text[anchor.offset - 1];
2919
- var characterAfterAnchor = node.text[anchor.offset];
2920
-
2921
- if (insertText.length === 1 && insertText === characterAfterAnchor) {
2922
- // Assume text should be inserted at the anchor
2923
- return range;
2924
- }
2925
-
2926
- if (removeText.length === 1 && removeText === characterBeforeAnchor) {
2927
- // Assume text should be removed right before the anchor
2928
- return {
2929
- anchor: {
2930
- path,
2931
- offset: anchor.offset - 1
2932
- },
2933
- focus: {
2934
- path,
2935
- offset: anchor.offset
2936
- }
2937
- };
2938
- }
2939
- }
2940
- }
2941
-
2942
- return insertionRange;
2943
- }
2944
-
2945
- function gatherMutationData(editor, mutations) {
2946
- var addedNodes = [];
2947
- var removedNodes = [];
2948
- var insertedText = [];
2949
- var characterDataMutations = [];
2950
- mutations.forEach(mutation => {
2951
- switch (mutation.type) {
2952
- case 'childList':
2953
- {
2954
- if (mutation.addedNodes.length) {
2955
- mutation.addedNodes.forEach(addedNode => {
2956
- addedNodes.push(addedNode);
2957
- });
2958
- }
2952
+ function gatherMutationData(editor, mutations) {
2953
+ var addedNodes = [];
2954
+ var removedNodes = [];
2955
+ var insertedText = [];
2956
+ var characterDataMutations = [];
2957
+ mutations.forEach(mutation => {
2958
+ switch (mutation.type) {
2959
+ case 'childList':
2960
+ {
2961
+ if (mutation.addedNodes.length) {
2962
+ mutation.addedNodes.forEach(addedNode => {
2963
+ addedNodes.push(addedNode);
2964
+ });
2965
+ }
2959
2966
 
2960
2967
  mutation.removedNodes.forEach(removedNode => {
2961
2968
  removedNodes.push(removedNode);
@@ -3163,7 +3170,15 @@ class AndroidInputManager {
3163
3170
  var {
3164
3171
  selection,
3165
3172
  marks
3166
- } = this.editor; // Insert the batched text diffs
3173
+ } = this.editor; // If it is in composing or after `onCompositionend`, set `EDITOR_ON_COMPOSITION_TEXT` and return.
3174
+ // Text will be inserted on compositionend event.
3175
+
3176
+ if (IS_COMPOSING.get(this.editor) || IS_ON_COMPOSITION_END.get(this.editor)) {
3177
+ EDITOR_ON_COMPOSITION_TEXT.set(this.editor, insertedText);
3178
+ IS_ON_COMPOSITION_END.set(this.editor, false);
3179
+ return;
3180
+ } // Insert the batched text diffs
3181
+
3167
3182
 
3168
3183
  insertedText.forEach(insertion => {
3169
3184
  var text = insertion.text.insertText;
@@ -3420,7 +3435,10 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
3420
3435
  /**
3421
3436
  * Editable.
3422
3437
  */
3438
+ // https://github.com/facebook/draft-js/blob/main/src/component/handlers/composition/DraftEditorCompositionHandler.js#L41
3439
+ // When using keyboard English association function, conpositionEnd triggered too fast, resulting in after `insertText` still maintain association state.
3423
3440
 
3441
+ var RESOLVE_DELAY = 20;
3424
3442
  var AndroidEditable = props => {
3425
3443
  var {
3426
3444
  autoFocus,
@@ -3436,13 +3454,16 @@ var AndroidEditable = props => {
3436
3454
  } = props,
3437
3455
  attributes = _objectWithoutProperties(props, _excluded$1);
3438
3456
 
3439
- var editor = useSlate();
3457
+ var editor = useSlate(); // Rerender editor when composition status changed
3458
+
3459
+ var [isComposing, setIsComposing] = useState(false);
3440
3460
  var ref = useRef(null);
3441
3461
  var inputManager = useAndroidInputManager(ref); // Update internal state on each render.
3442
3462
 
3443
3463
  IS_READ_ONLY.set(editor, readOnly); // Keep track of some state for the event handler logic.
3444
3464
 
3445
3465
  var state = useMemo(() => ({
3466
+ isComposing: false,
3446
3467
  isUpdatingSelection: false,
3447
3468
  latestElement: null
3448
3469
  }), []);
@@ -3469,7 +3490,7 @@ var AndroidEditable = props => {
3469
3490
  var root = ReactEditor.findDocumentOrShadowRoot(editor);
3470
3491
  var domSelection = root.getSelection();
3471
3492
 
3472
- if (!domSelection || !ReactEditor.isFocused(editor)) {
3493
+ if (state.isComposing || !domSelection || !ReactEditor.isFocused(editor)) {
3473
3494
  return;
3474
3495
  }
3475
3496
 
@@ -3549,26 +3570,7 @@ var AndroidEditable = props => {
3549
3570
  if (ref.current && autoFocus) {
3550
3571
  ref.current.focus();
3551
3572
  }
3552
- }, [autoFocus]); // Listen on the native `beforeinput` event to get real "Level 2" events. This
3553
- // is required because React's `beforeinput` is fake and never really attaches
3554
- // to the real event sadly. (2019/11/01)
3555
- // https://github.com/facebook/react/issues/11211
3556
-
3557
- var onDOMBeforeInput = useCallback(event => {
3558
- if (!readOnly && hasEditableTarget(editor, event.target) && !isDOMEventHandled(event, propsOnDOMBeforeInput)) {
3559
- inputManager.onUserInput();
3560
- }
3561
- }, [readOnly, propsOnDOMBeforeInput]); // Attach a native DOM event handler for `beforeinput` events, because React's
3562
- // built-in `onBeforeInput` is actually a leaky polyfill that doesn't expose
3563
- // real `beforeinput` events sadly... (2019/11/04)
3564
-
3565
- useIsomorphicLayoutEffect(() => {
3566
- var node = ref.current; // @ts-ignore The `beforeinput` event isn't recognized.
3567
-
3568
- node === null || node === void 0 ? void 0 : node.addEventListener('beforeinput', onDOMBeforeInput); // @ts-ignore The `beforeinput` event isn't recognized.
3569
-
3570
- return () => node === null || node === void 0 ? void 0 : node.removeEventListener('beforeinput', onDOMBeforeInput);
3571
- }, [contentKey, propsOnDOMBeforeInput]); // Listen on the native `selectionchange` event to be able to update any time
3573
+ }, [autoFocus]); // Listen on the native `selectionchange` event to be able to update any time
3572
3574
  // the selection changes. This is required because React's `onSelect` is leaky
3573
3575
  // and non-standard so it doesn't fire until after a selection has been
3574
3576
  // released. This causes issues in situations where another change happens
@@ -3576,7 +3578,7 @@ var AndroidEditable = props => {
3576
3578
 
3577
3579
  var onDOMSelectionChange = useCallback(throttle(() => {
3578
3580
  try {
3579
- if (!state.isUpdatingSelection && !inputManager.isReconciling.current) {
3581
+ if (!state.isComposing && !state.isUpdatingSelection && !inputManager.isReconciling.current) {
3580
3582
  var root = ReactEditor.findDocumentOrShadowRoot(editor);
3581
3583
  var {
3582
3584
  activeElement
@@ -3614,7 +3616,31 @@ var AndroidEditable = props => {
3614
3616
  }
3615
3617
  } catch (_unused2) {// Failed to update selection, likely due to reconciliation error
3616
3618
  }
3617
- }, 100), [readOnly]); // Attach a native DOM event handler for `selectionchange`, because React's
3619
+ }, 100), [readOnly]);
3620
+ var scheduleOnDOMSelectionChange = useMemo(() => debounce(onDOMSelectionChange, 0), [onDOMSelectionChange]); // Listen on the native `beforeinput` event to get real "Level 2" events. This
3621
+ // is required because React's `beforeinput` is fake and never really attaches
3622
+ // to the real event sadly. (2019/11/01)
3623
+ // https://github.com/facebook/react/issues/11211
3624
+
3625
+ var onDOMBeforeInput = useCallback(event => {
3626
+ if (!readOnly && hasEditableTarget(editor, event.target) && !isDOMEventHandled(event, propsOnDOMBeforeInput)) {
3627
+ // Some IMEs/Chrome extensions like e.g. Grammarly set the selection immediately before
3628
+ // triggering a `beforeinput` expecting the change to be applied to the immediately before
3629
+ // set selection.
3630
+ scheduleOnDOMSelectionChange.flush();
3631
+ inputManager.onUserInput();
3632
+ }
3633
+ }, [readOnly, propsOnDOMBeforeInput]); // Attach a native DOM event handler for `beforeinput` events, because React's
3634
+ // built-in `onBeforeInput` is actually a leaky polyfill that doesn't expose
3635
+ // real `beforeinput` events sadly... (2019/11/04)
3636
+
3637
+ useIsomorphicLayoutEffect(() => {
3638
+ var node = ref.current; // @ts-ignore The `beforeinput` event isn't recognized.
3639
+
3640
+ node === null || node === void 0 ? void 0 : node.addEventListener('beforeinput', onDOMBeforeInput); // @ts-ignore The `beforeinput` event isn't recognized.
3641
+
3642
+ return () => node === null || node === void 0 ? void 0 : node.removeEventListener('beforeinput', onDOMBeforeInput);
3643
+ }, [contentKey, propsOnDOMBeforeInput]); // Attach a native DOM event handler for `selectionchange`, because React's
3618
3644
  // built-in `onSelect` handler doesn't fire for all selection changes. It's a
3619
3645
  // leaky polyfill that only fires on keypresses or clicks. Instead, we want to
3620
3646
  // fire for any change to the selection inside the editor. (2019/11/04)
@@ -3622,14 +3648,14 @@ var AndroidEditable = props => {
3622
3648
 
3623
3649
  useIsomorphicLayoutEffect(() => {
3624
3650
  var window = ReactEditor.getWindow(editor);
3625
- window.document.addEventListener('selectionchange', onDOMSelectionChange);
3651
+ window.document.addEventListener('selectionchange', scheduleOnDOMSelectionChange);
3626
3652
  return () => {
3627
- window.document.removeEventListener('selectionchange', onDOMSelectionChange);
3653
+ window.document.removeEventListener('selectionchange', scheduleOnDOMSelectionChange);
3628
3654
  };
3629
- });
3655
+ }, [scheduleOnDOMSelectionChange]);
3630
3656
  var decorations = decorate([editor, []]);
3631
3657
 
3632
- if (placeholder && editor.children.length === 1 && Array.from(Node.texts(editor)).length === 1 && Node.string(editor) === '') {
3658
+ if (placeholder && editor.children.length === 1 && Array.from(Node.texts(editor)).length === 1 && Node.string(editor) === '' && !isComposing) {
3633
3659
  var start = Editor.start(editor, []);
3634
3660
  decorations.push({
3635
3661
  [PLACEHOLDER_SYMBOL]: true,
@@ -3744,6 +3770,94 @@ var AndroidEditable = props => {
3744
3770
 
3745
3771
  IS_FOCUSED.delete(editor);
3746
3772
  }, [readOnly, attributes.onBlur]),
3773
+ onClick: useCallback(event => {
3774
+ if (!readOnly && hasTarget(editor, event.target) && !isEventHandled(event, attributes.onClick) && isDOMNode(event.target)) {
3775
+ var node = ReactEditor.toSlateNode(editor, event.target);
3776
+ var path = ReactEditor.findPath(editor, node); // At this time, the Slate document may be arbitrarily different,
3777
+ // because onClick handlers can change the document before we get here.
3778
+ // Therefore we must check that this path actually exists,
3779
+ // and that it still refers to the same node.
3780
+
3781
+ if (Editor.hasPath(editor, path)) {
3782
+ var lookupNode = Node.get(editor, path);
3783
+
3784
+ if (lookupNode === node) {
3785
+ var _start = Editor.start(editor, path);
3786
+
3787
+ var end = Editor.end(editor, path);
3788
+ var startVoid = Editor.void(editor, {
3789
+ at: _start
3790
+ });
3791
+ var endVoid = Editor.void(editor, {
3792
+ at: end
3793
+ });
3794
+
3795
+ if (startVoid && endVoid && Path.equals(startVoid[1], endVoid[1])) {
3796
+ var range = Editor.range(editor, _start);
3797
+ Transforms.select(editor, range);
3798
+ }
3799
+ }
3800
+ }
3801
+ }
3802
+ }, [readOnly, attributes.onClick]),
3803
+ onCompositionEnd: useCallback(event => {
3804
+ if (hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onCompositionEnd)) {
3805
+ scheduleOnDOMSelectionChange.flush();
3806
+ setTimeout(() => {
3807
+ state.isComposing && setIsComposing(false);
3808
+ state.isComposing = false;
3809
+ IS_COMPOSING.set(editor, false);
3810
+ IS_ON_COMPOSITION_END.set(editor, true);
3811
+ var insertedText = EDITOR_ON_COMPOSITION_TEXT.get(editor) || []; // `insertedText` is set in `MutationObserver` constructor.
3812
+ // If open phone keyboard association function, `CompositionEvent` will be triggered.
3813
+
3814
+ if (!insertedText.length) {
3815
+ return;
3816
+ }
3817
+
3818
+ EDITOR_ON_COMPOSITION_TEXT.set(editor, []);
3819
+ var {
3820
+ selection,
3821
+ marks
3822
+ } = editor;
3823
+ insertedText.forEach(insertion => {
3824
+ var text = insertion.text.insertText;
3825
+ var at = normalizeTextInsertionRange(editor, selection, insertion);
3826
+
3827
+ if (marks) {
3828
+ var node = _objectSpread({
3829
+ text
3830
+ }, marks);
3831
+
3832
+ Transforms.insertNodes(editor, node, {
3833
+ match: Text$1.isText,
3834
+ at,
3835
+ select: true
3836
+ });
3837
+ editor.marks = null;
3838
+ } else {
3839
+ Transforms.insertText(editor, text, {
3840
+ at
3841
+ });
3842
+ }
3843
+ });
3844
+ }, RESOLVE_DELAY);
3845
+ }
3846
+ }, [attributes.onCompositionEnd]),
3847
+ onCompositionUpdate: useCallback(event => {
3848
+ if (hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onCompositionUpdate)) {
3849
+ !state.isComposing && setIsComposing(true);
3850
+ state.isComposing = true;
3851
+ IS_COMPOSING.set(editor, true);
3852
+ }
3853
+ }, [attributes.onCompositionUpdate]),
3854
+ onCompositionStart: useCallback(event => {
3855
+ if (hasEditableTarget(editor, event.target) && !isEventHandled(event, attributes.onCompositionStart)) {
3856
+ !state.isComposing && setIsComposing(true);
3857
+ state.isComposing = true;
3858
+ IS_COMPOSING.set(editor, true);
3859
+ }
3860
+ }, [attributes.onCompositionStart]),
3747
3861
  onPaste: useCallback(event => {
3748
3862
  // this will make application/x-slate-fragment exist when onPaste attributes is passed
3749
3863
  event.clipboardData = getClipboardData(event.clipboardData); // This unfortunately needs to be handled with paste events instead.