@contentful/field-editor-rich-text 2.0.0-next.4 → 2.0.0-next.8

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.
@@ -3,16 +3,16 @@ import { toSlatejsDocument, toContentfulDocument } from '@contentful/contentful-
3
3
  import { useEntities, ScheduledIconWithTooltip, MissingEntityCard, AssetThumbnail, getScheduleTooltipContent, EntityProvider } from '@contentful/field-editor-reference';
4
4
  import { entityHelpers, shortenStorageUnit, isValidImage, ModalDialogLauncher, FieldConnector } from '@contentful/field-editor-shared';
5
5
  import { TOP_LEVEL_BLOCKS, BLOCKS, CONTAINERS, VOID_BLOCKS, INLINES, TABLE_BLOCKS, HEADINGS, TEXT_CONTAINERS, LIST_ITEM_BLOCKS, MARKS, EMPTY_DOCUMENT } from '@contentful/rich-text-types';
6
- import { usePlateEditorRef, getNodes, getText, toggleNodeType, onKeyDownToggleElement, setNodes, isAncestorEmpty, getParent, getAbove, findNode, getPlugin, someHtmlElement, isMarkActive, toggleMark, getPluginType, match, KEY_DESERIALIZE_HTML, someNode, insertNodes, getChildren as getChildren$1, isFirstChild, getBlockAbove, getLastChildPath, createDeserializeHtmlPlugin, createDeserializeAstPlugin, Plate } from '@udecode/plate-core';
6
+ import { usePlateEditorRef, getNodes, getText, toggleNodeType, getAbove, onKeyDownToggleElement, setNodes, isAncestorEmpty, getParent, isSelectionAtBlockStart, isSelectionAtBlockEnd, isFirstChild, insertNodes, moveChildren, isBlockAboveEmpty, mockPlugin, getPluginType, ELEMENT_DEFAULT, findNode, someHtmlElement, isMarkActive, toggleMark, match, KEY_DESERIALIZE_HTML, someNode, getChildren as getChildren$1, getBlockAbove, getLastChildPath, createDeserializeHtmlPlugin, createDeserializeAstPlugin, Plate } from '@udecode/plate-core';
7
7
  import { css, cx } from 'emotion';
8
8
  import deepEquals from 'fast-deep-equal';
9
9
  import noop from 'lodash-es/noop';
10
+ import { Text, Element, Editor, Transforms, Range, Path, Node } from 'slate';
10
11
  import constate from 'constate';
11
12
  import { createDeserializeDocxPlugin } from '@udecode/plate-serializer-docx';
12
13
  import { createSoftBreakPlugin as createSoftBreakPlugin$1, createExitBreakPlugin as createExitBreakPlugin$1 } from '@udecode/plate-break';
13
14
  import isHotkey from 'is-hotkey';
14
- import { Text, Element, Editor, Transforms, Range, Path, Node } from 'slate';
15
- import { useSelected, useReadOnly, ReactEditor, useFocused } from 'slate-react';
15
+ import { ReactEditor, useSelected, useReadOnly, useFocused } from 'slate-react';
16
16
  import { AssetCard, Menu, Text as Text$1, Notification, EntryCard, MenuItem, Button, Flex, Icon, InlineEntryCard, Tooltip, ModalContent, Form, FormControl, TextInput, Select, FormLabel, TextLink, ModalControls, IconButton } from '@contentful/f36-components';
17
17
  import mimetype from '@contentful/mimetype';
18
18
  import get from 'lodash-es/get';
@@ -20,7 +20,8 @@ import { ClockIcon, AssetIcon, EmbeddedEntryBlockIcon, EmbeddedEntryInlineIcon,
20
20
  import tokens from '@contentful/f36-tokens';
21
21
  import find from 'lodash-es/find';
22
22
  import flow from 'lodash-es/flow';
23
- import { ELEMENT_LI, ELEMENT_UL, ELEMENT_OL, withList as withList$1, createListPlugin as createListPlugin$1, ELEMENT_LIC, toggleList } from '@udecode/plate-list';
23
+ import { getListItemEntry, moveListItemUp, ELEMENT_LI, unwrapList, deleteBackwardList, deleteForwardList, deleteFragmentList, normalizeList, createListPlugin as createListPlugin$1, ELEMENT_UL, ELEMENT_OL, ELEMENT_LIC, toggleList } from '@udecode/plate-list';
24
+ import { onKeyDownResetNode, SIMULATE_BACKSPACE } from '@udecode/plate-reset-node';
24
25
  import { createBoldPlugin as createBoldPlugin$1, createCodePlugin as createCodePlugin$1, createItalicPlugin as createItalicPlugin$1, createUnderlinePlugin as createUnderlinePlugin$1 } from '@udecode/plate-basic-marks';
25
26
  import isPlainObject from 'is-plain-obj';
26
27
  import { createParagraphPlugin as createParagraphPlugin$1 } from '@udecode/plate-paragraph';
@@ -341,8 +342,8 @@ var schema = {
341
342
  function getContentfulEditorId(sdk) {
342
343
  var entry = sdk.entry,
343
344
  field = sdk.field;
344
- var entryId = entry.getSys().id;
345
- return "rich-text-editor-" + entryId + "-" + field.id + "-" + field.locale;
345
+ var sys = entry.getSys();
346
+ return "rich-text-editor-" + sys.id + "-" + field.id + "-" + field.locale;
346
347
  }
347
348
 
348
349
  function useContentfulEditorHook(_ref) {
@@ -483,6 +484,18 @@ function getParents(el) {
483
484
  return parents;
484
485
  }
485
486
 
487
+ // "modern" Edge was released at 79.x
488
+ var IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /*#__PURE__*/ /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent); // Native `beforeInput` events don't work well with react on Chrome 75
489
+ // and older, Chrome 76+ can use `beforeInput` though.
490
+
491
+ var IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /*#__PURE__*/ /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent); // COMPAT: Firefox/Edge Legacy don't support the `beforeinput` event
492
+ // Chrome Legacy doesn't support `beforeinput` correctly
493
+
494
+ var HAS_BEFORE_INPUT_SUPPORT = !IS_CHROME_LEGACY && !IS_EDGE_LEGACY && // globalThis is undefined in older browsers
495
+ typeof globalThis !== 'undefined' && globalThis.InputEvent && typeof globalThis.InputEvent.prototype.getTargetRanges === 'function'; // The `getTargetRanges` property isn't recognized.
496
+
497
+ var IS_SAFARI = typeof navigator !== 'undefined' && /*#__PURE__*/ /Version\/[\d.]+.*Safari/.test(navigator.userAgent);
498
+
486
499
  var LINK_TYPES = [INLINES.HYPERLINK, INLINES.ENTRY_HYPERLINK, INLINES.ASSET_HYPERLINK];
487
500
  function isBlockSelected(editor, type) {
488
501
  var _Array$from = Array.from(Editor.nodes(editor, {
@@ -700,16 +713,27 @@ var isInlineOrText = function isInlineOrText(node) {
700
713
  // either text or inline elements
701
714
  return Text.isText(node) || Element.isElement(node) && INLINE_TYPES.includes(node.type);
702
715
  };
716
+ var focus = function focus(editor) {
717
+ var x = window.scrollX;
718
+ var y = window.scrollY;
719
+ ReactEditor.focus(editor); // Safari has issues with `editor.focus({ preventScroll: true })`, it ignores the option `preventScroll`
720
+
721
+ if (IS_SAFARI) {
722
+ setTimeout(function () {
723
+ window.scrollTo(x, y); // restore position
724
+ }, 0);
725
+ }
726
+ };
703
727
 
704
- // "modern" Edge was released at 79.x
705
- var IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /*#__PURE__*/ /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent); // Native `beforeInput` events don't work well with react on Chrome 75
706
- // and older, Chrome 76+ can use `beforeInput` though.
707
-
708
- var IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /*#__PURE__*/ /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent); // COMPAT: Firefox/Edge Legacy don't support the `beforeinput` event
709
- // Chrome Legacy doesn't support `beforeinput` correctly
710
-
711
- var HAS_BEFORE_INPUT_SUPPORT = !IS_CHROME_LEGACY && !IS_EDGE_LEGACY && // globalThis is undefined in older browsers
712
- typeof globalThis !== 'undefined' && globalThis.InputEvent && typeof globalThis.InputEvent.prototype.getTargetRanges === 'function'; // The `getTargetRanges` property isn't recognized.
728
+ function withLinkTracking(tracking, Component) {
729
+ return function ComponentWithTracking(props) {
730
+ return /*#__PURE__*/React__default.createElement(Component, Object.assign({}, props, {
731
+ onEntityFetchComplete: function onEntityFetchComplete() {
732
+ tracking.onViewportAction('linkRendered');
733
+ }
734
+ }));
735
+ };
736
+ }
713
737
 
714
738
  function useSdk(_ref) {
715
739
  var sdk = _ref.sdk;
@@ -847,10 +871,19 @@ function FetchingWrappedAssetCard(props) {
847
871
  var asset = assets[props.assetId];
848
872
  var defaultLocaleCode = props.sdk.locales["default"];
849
873
  var entityFile = asset != null && (_asset$fields = asset.fields) != null && _asset$fields.file ? asset.fields.file[props.locale] || asset.fields.file[defaultLocaleCode] : undefined;
874
+ var onEntityFetchComplete = props.onEntityFetchComplete;
850
875
  useEffect(function () {
851
876
  getOrLoadAsset(props.assetId);
852
877
  }, [props.assetId]); // eslint-disable-line
853
878
 
879
+ useEffect(function () {
880
+ if (!asset) {
881
+ return;
882
+ }
883
+
884
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
885
+ }, [asset, onEntityFetchComplete]);
886
+
854
887
  function getAssetSrc() {
855
888
  if (!(entityFile != null && entityFile.url)) return '';
856
889
  return entityFile.url + "?h=300";
@@ -975,6 +1008,7 @@ function FetchingWrappedEntryCard(props) {
975
1008
  });
976
1009
  }, [props.sdk, entry]);
977
1010
  var defaultLocaleCode = props.sdk.locales["default"];
1011
+ var onEntityFetchComplete = props.onEntityFetchComplete;
978
1012
  useEffect(function () {
979
1013
  if (!entry || entry === 'failed') return;
980
1014
  entityHelpers.getEntryImage({
@@ -990,6 +1024,14 @@ function FetchingWrappedEntryCard(props) {
990
1024
  getOrLoadEntry(props.entryId);
991
1025
  }, [props.entryId]); // eslint-disable-line
992
1026
 
1027
+ useEffect(function () {
1028
+ if (!entry) {
1029
+ return;
1030
+ }
1031
+
1032
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
1033
+ }, [entry, onEntityFetchComplete]);
1034
+
993
1035
  function renderDropdown() {
994
1036
  if (!props.onEdit || !props.onRemove) return undefined;
995
1037
  return [props.onEdit ? /*#__PURE__*/createElement(MenuItem, {
@@ -1071,13 +1113,14 @@ function FetchingWrappedEntryCard(props) {
1071
1113
 
1072
1114
  var styles$3 = {
1073
1115
  root: /*#__PURE__*/css({
1074
- marginBottom: '1.25rem'
1116
+ marginBottom: '1.25rem !important'
1075
1117
  })
1076
1118
  };
1077
1119
  function LinkedEntityBlock(props) {
1078
1120
  var attributes = props.attributes,
1079
1121
  children = props.children,
1080
- element = props.element;
1122
+ element = props.element,
1123
+ onEntityFetchComplete = props.onEntityFetchComplete;
1081
1124
  var isSelected = useSelected();
1082
1125
  var editor = useContentfulEditor();
1083
1126
  var sdk = useSdkContext();
@@ -1119,7 +1162,8 @@ function LinkedEntityBlock(props) {
1119
1162
  isDisabled: isDisabled,
1120
1163
  isSelected: isSelected,
1121
1164
  onRemove: handleRemoveClick,
1122
- onEdit: handleEditClick
1165
+ onEdit: handleEditClick,
1166
+ onEntityFetchComplete: onEntityFetchComplete
1123
1167
  }), entityType === 'Asset' && /*#__PURE__*/React__default.createElement(FetchingWrappedAssetCard, {
1124
1168
  sdk: sdk,
1125
1169
  assetId: entityId,
@@ -1127,7 +1171,8 @@ function LinkedEntityBlock(props) {
1127
1171
  isDisabled: isDisabled,
1128
1172
  isSelected: isSelected,
1129
1173
  onRemove: handleRemoveClick,
1130
- onEdit: handleEditClick
1174
+ onEdit: handleEditClick,
1175
+ onEntityFetchComplete: onEntityFetchComplete
1131
1176
  })), children);
1132
1177
  }
1133
1178
 
@@ -2031,7 +2076,7 @@ function insertBlock(editor, nodeType, entity) {
2031
2076
  Transforms.setNodes(editor, linkedEntityBlock);
2032
2077
  }
2033
2078
 
2034
- ReactEditor.focus(editor);
2079
+ focus(editor);
2035
2080
  }
2036
2081
 
2037
2082
  var styles$4 = {
@@ -2125,13 +2170,13 @@ function getWithEmbeddedEntityEvents(nodeType, sdk) {
2125
2170
  }
2126
2171
 
2127
2172
  var createEmbeddedEntityPlugin = function createEmbeddedEntityPlugin(nodeType, hotkey) {
2128
- return function (sdk) {
2173
+ return function (sdk, tracking) {
2129
2174
  return {
2130
2175
  key: nodeType,
2131
2176
  type: nodeType,
2132
2177
  isElement: true,
2133
2178
  isVoid: true,
2134
- component: LinkedEntityBlock,
2179
+ component: withLinkTracking(tracking, LinkedEntityBlock),
2135
2180
  options: {
2136
2181
  hotkey: hotkey
2137
2182
  },
@@ -2189,12 +2234,20 @@ function FetchingWrappedInlineEntryCard(props) {
2189
2234
  return entries[props.entryId];
2190
2235
  }, [entries, props.entryId]);
2191
2236
  var allContentTypes = props.sdk.space.getCachedContentTypes();
2237
+ var onEntityFetchComplete = props.onEntityFetchComplete;
2192
2238
  var contentType = React__default.useMemo(function () {
2193
2239
  if (!entry || entry === 'failed' || !allContentTypes) return undefined;
2194
2240
  return allContentTypes.find(function (contentType) {
2195
2241
  return contentType.sys.id === entry.sys.contentType.sys.id;
2196
2242
  });
2197
2243
  }, [allContentTypes, entry]);
2244
+ React__default.useEffect(function () {
2245
+ if (!entry) {
2246
+ return;
2247
+ }
2248
+
2249
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
2250
+ }, [entry, onEntityFetchComplete]);
2198
2251
  var contentTypeName = contentType ? contentType.name : '';
2199
2252
  var title = React__default.useMemo(function () {
2200
2253
  return getEntryTitle({
@@ -2290,12 +2343,10 @@ var styles$6 = {
2290
2343
  marginRight: '10px'
2291
2344
  }),
2292
2345
  root: /*#__PURE__*/css({
2293
- margin: '0 1px',
2346
+ display: 'inline-block',
2347
+ margin: "0 " + tokens.spacing2Xs,
2294
2348
  fontSize: 'inherit',
2295
2349
  span: {
2296
- webkitUserSelect: 'none',
2297
- mozUserSelect: 'none',
2298
- msUserSelect: 'none',
2299
2350
  userSelect: 'none'
2300
2351
  }
2301
2352
  })
@@ -2338,7 +2389,8 @@ function EmbeddedEntityInline(props) {
2338
2389
  isSelected: isSelected,
2339
2390
  isDisabled: isDisabled,
2340
2391
  onRemove: handleRemoveClick,
2341
- onEdit: handleEditClick
2392
+ onEdit: handleEditClick,
2393
+ onEntityFetchComplete: props.onEntityFetchComplete
2342
2394
  })), props.children);
2343
2395
  }
2344
2396
 
@@ -2362,7 +2414,7 @@ function _selectEntityAndInsert$1() {
2362
2414
 
2363
2415
  case 4:
2364
2416
  entry = _context2.sent;
2365
- ReactEditor.focus(editor); // Dialog steals focus from editor, return it.
2417
+ focus(editor); // Dialog steals focus from editor, return it.
2366
2418
 
2367
2419
  if (entry) {
2368
2420
  _context2.next = 8;
@@ -2448,7 +2500,7 @@ function ToolbarEmbeddedEntityInlineButton(props) {
2448
2500
  className: "rich-text__embedded-entry-list-icon " + styles$6.icon
2449
2501
  }), /*#__PURE__*/createElement("span", null, "Inline entry")));
2450
2502
  }
2451
- function createEmbeddedEntityInlinePlugin(sdk) {
2503
+ function createEmbeddedEntityInlinePlugin(sdk, tracking) {
2452
2504
  var htmlAttributeName = 'data-embedded-entity-inline-id';
2453
2505
  return {
2454
2506
  key: INLINES.EMBEDDED_ENTRY,
@@ -2456,7 +2508,7 @@ function createEmbeddedEntityInlinePlugin(sdk) {
2456
2508
  isElement: true,
2457
2509
  isInline: true,
2458
2510
  isVoid: true,
2459
- component: EmbeddedEntityInline,
2511
+ component: withLinkTracking(tracking, EmbeddedEntityInline),
2460
2512
  options: {
2461
2513
  hotkey: 'mod+shift+2'
2462
2514
  },
@@ -2570,14 +2622,25 @@ function ToolbarHeadingButton(props) {
2570
2622
  unwrapFromRoot(editor);
2571
2623
  }
2572
2624
 
2625
+ var prevOnChange = editor.onChange;
2626
+ /*
2627
+ The focus might happen at point in time when
2628
+ `toggleNodeType` changes aren't rendered yet, causing the browser
2629
+ to place the cursor at the start of the text.
2630
+ We wait for the change event before focusing
2631
+ the editor again. This ensures the cursor is back at the previous
2632
+ position.*/
2633
+
2634
+ editor.onChange = function () {
2635
+ focus(editor);
2636
+ editor.onChange = prevOnChange;
2637
+ prevOnChange.apply(void 0, arguments);
2638
+ };
2639
+
2573
2640
  toggleNodeType(editor, {
2574
2641
  activeType: type,
2575
2642
  inactiveType: type
2576
- }); // TODO: Figure out why focus only works with timeout here.
2577
-
2578
- setTimeout(function () {
2579
- ReactEditor.focus(editor);
2580
- }, 0);
2643
+ });
2581
2644
  };
2582
2645
  }
2583
2646
 
@@ -2707,16 +2770,6 @@ var createHeadingPlugin = function createHeadingPlugin() {
2707
2770
  allow: HEADINGS
2708
2771
  }
2709
2772
  }],
2710
- exitBreak: [// Pressing ENTER at the start or end of a heading text inserts a
2711
- // normal paragraph
2712
- {
2713
- hotkey: 'enter',
2714
- query: {
2715
- allow: HEADINGS,
2716
- end: true,
2717
- start: true
2718
- }
2719
- }],
2720
2773
  normalizer: [{
2721
2774
  match: {
2722
2775
  type: HEADINGS
@@ -2727,6 +2780,31 @@ var createHeadingPlugin = function createHeadingPlugin() {
2727
2780
  },
2728
2781
  transform: (_transform = {}, _transform[BLOCKS.PARAGRAPH] = transformUnwrap, _transform["default"] = transformLift, _transform)
2729
2782
  }],
2783
+ then: function then(editor) {
2784
+ return {
2785
+ exitBreak: [// Pressing ENTER at the start or end of a heading text inserts a
2786
+ // normal paragraph.
2787
+ {
2788
+ hotkey: 'enter',
2789
+ query: {
2790
+ allow: HEADINGS,
2791
+ end: true,
2792
+ start: true,
2793
+ // Exclude headings inside lists as it interferes with the list's
2794
+ // insertBreak implementation
2795
+ filter: function filter(_ref2) {
2796
+ var path = _ref2[1];
2797
+ return !getAbove(editor, {
2798
+ at: path,
2799
+ match: {
2800
+ type: BLOCKS.LIST_ITEM
2801
+ }
2802
+ });
2803
+ }
2804
+ }
2805
+ }]
2806
+ };
2807
+ },
2730
2808
  plugins: HEADINGS.map(function (nodeType, idx) {
2731
2809
  var level = idx + 1;
2732
2810
  var tagName = "h" + level;
@@ -2849,7 +2927,7 @@ function ToolbarHrButton(props) {
2849
2927
  hasText ? Transforms.insertNodes(editor, hr) : setNodes(editor, hr); // Move focus to the next paragraph (added by TrailingParagraph plugin)
2850
2928
 
2851
2929
  moveToTheNextLine(editor);
2852
- ReactEditor.focus(editor);
2930
+ focus(editor);
2853
2931
  }
2854
2932
 
2855
2933
  if (!editor) return null;
@@ -3341,7 +3419,7 @@ function _addOrEditLink() {
3341
3419
  path: path
3342
3420
  });
3343
3421
  });
3344
- ReactEditor.focus(editor);
3422
+ focus(editor);
3345
3423
 
3346
3424
  case 15:
3347
3425
  case "end":
@@ -3403,37 +3481,11 @@ function UrlHyperlink(props) {
3403
3481
  var sdk = useSdkContext();
3404
3482
  var uri = props.element.data.uri;
3405
3483
 
3406
- function handleClick(_x) {
3407
- return _handleClick.apply(this, arguments);
3408
- }
3409
-
3410
- function _handleClick() {
3411
- _handleClick = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee(event) {
3412
- return runtime_1.wrap(function _callee$(_context) {
3413
- while (1) {
3414
- switch (_context.prev = _context.next) {
3415
- case 0:
3416
- event.preventDefault();
3417
- event.stopPropagation();
3418
-
3419
- if (editor) {
3420
- _context.next = 4;
3421
- break;
3422
- }
3423
-
3424
- return _context.abrupt("return");
3425
-
3426
- case 4:
3427
- addOrEditLink(editor, sdk);
3428
-
3429
- case 5:
3430
- case "end":
3431
- return _context.stop();
3432
- }
3433
- }
3434
- }, _callee);
3435
- }));
3436
- return _handleClick.apply(this, arguments);
3484
+ function handleClick(event) {
3485
+ event.preventDefault();
3486
+ event.stopPropagation();
3487
+ if (!editor) return;
3488
+ addOrEditLink(editor, sdk);
3437
3489
  }
3438
3490
 
3439
3491
  return /*#__PURE__*/createElement(Tooltip, {
@@ -3456,39 +3508,20 @@ function EntityHyperlink(props) {
3456
3508
  var isReadOnly = useReadOnly();
3457
3509
  var sdk = useSdkContext();
3458
3510
  var target = props.element.data.target;
3511
+ var onEntityFetchComplete = props.onEntityFetchComplete;
3512
+ useEffect(function () {
3513
+ // The real entity loading happens in the tooltip
3514
+ // Since that is deferred the link is considered rendered as soon
3515
+ // the component mounts (link text displayed)
3516
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
3517
+ }, [onEntityFetchComplete]);
3459
3518
  if (!target) return null;
3460
3519
 
3461
- function handleClick(_x2) {
3462
- return _handleClick2.apply(this, arguments);
3463
- }
3464
-
3465
- function _handleClick2() {
3466
- _handleClick2 = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee2(event) {
3467
- return runtime_1.wrap(function _callee2$(_context2) {
3468
- while (1) {
3469
- switch (_context2.prev = _context2.next) {
3470
- case 0:
3471
- event.preventDefault();
3472
- event.stopPropagation();
3473
-
3474
- if (editor) {
3475
- _context2.next = 4;
3476
- break;
3477
- }
3478
-
3479
- return _context2.abrupt("return");
3480
-
3481
- case 4:
3482
- addOrEditLink(editor, sdk);
3483
-
3484
- case 5:
3485
- case "end":
3486
- return _context2.stop();
3487
- }
3488
- }
3489
- }, _callee2);
3490
- }));
3491
- return _handleClick2.apply(this, arguments);
3520
+ function handleClick(event) {
3521
+ event.preventDefault();
3522
+ event.stopPropagation();
3523
+ if (!editor) return;
3524
+ addOrEditLink(editor, sdk);
3492
3525
  }
3493
3526
 
3494
3527
  return /*#__PURE__*/createElement(Tooltip, {
@@ -3501,7 +3534,7 @@ function EntityHyperlink(props) {
3501
3534
  placement: "bottom",
3502
3535
  maxWidth: "auto"
3503
3536
  }, /*#__PURE__*/createElement(TextLink, {
3504
- as: "button",
3537
+ as: "a",
3505
3538
  onClick: handleClick,
3506
3539
  isDisabled: isReadOnly,
3507
3540
  className: styles$c.hyperlink,
@@ -3516,21 +3549,21 @@ function ToolbarHyperlinkButton(props) {
3516
3549
  var sdk = useSdkContext();
3517
3550
 
3518
3551
  function handleClick() {
3519
- return _handleClick3.apply(this, arguments);
3552
+ return _handleClick.apply(this, arguments);
3520
3553
  }
3521
3554
 
3522
- function _handleClick3() {
3523
- _handleClick3 = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee3() {
3524
- return runtime_1.wrap(function _callee3$(_context3) {
3555
+ function _handleClick() {
3556
+ _handleClick = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee() {
3557
+ return runtime_1.wrap(function _callee$(_context) {
3525
3558
  while (1) {
3526
- switch (_context3.prev = _context3.next) {
3559
+ switch (_context.prev = _context.next) {
3527
3560
  case 0:
3528
3561
  if (editor) {
3529
- _context3.next = 2;
3562
+ _context.next = 2;
3530
3563
  break;
3531
3564
  }
3532
3565
 
3533
- return _context3.abrupt("return");
3566
+ return _context.abrupt("return");
3534
3567
 
3535
3568
  case 2:
3536
3569
  if (isActive) {
@@ -3541,12 +3574,12 @@ function ToolbarHyperlinkButton(props) {
3541
3574
 
3542
3575
  case 3:
3543
3576
  case "end":
3544
- return _context3.stop();
3577
+ return _context.stop();
3545
3578
  }
3546
3579
  }
3547
- }, _callee3);
3580
+ }, _callee);
3548
3581
  }));
3549
- return _handleClick3.apply(this, arguments);
3582
+ return _handleClick.apply(this, arguments);
3550
3583
  }
3551
3584
 
3552
3585
  if (!editor) return null;
@@ -3612,7 +3645,7 @@ var getNodeOfType = function getNodeOfType(type) {
3612
3645
  };
3613
3646
  };
3614
3647
 
3615
- var createHyperlinkPlugin = function createHyperlinkPlugin(sdk) {
3648
+ var createHyperlinkPlugin = function createHyperlinkPlugin(sdk, tracking) {
3616
3649
  var common = {
3617
3650
  isElement: true,
3618
3651
  isInline: true
@@ -3643,7 +3676,7 @@ var createHyperlinkPlugin = function createHyperlinkPlugin(sdk) {
3643
3676
  _extends({}, common, {
3644
3677
  key: INLINES.ENTRY_HYPERLINK,
3645
3678
  type: INLINES.ENTRY_HYPERLINK,
3646
- component: EntityHyperlink,
3679
+ component: withLinkTracking(tracking, EntityHyperlink),
3647
3680
  deserializeHtml: {
3648
3681
  rules: [{
3649
3682
  validNodeName: ['A']
@@ -3657,7 +3690,7 @@ var createHyperlinkPlugin = function createHyperlinkPlugin(sdk) {
3657
3690
  _extends({}, common, {
3658
3691
  key: INLINES.ASSET_HYPERLINK,
3659
3692
  type: INLINES.ASSET_HYPERLINK,
3660
- component: EntityHyperlink,
3693
+ component: withLinkTracking(tracking, EntityHyperlink),
3661
3694
  deserializeHtml: {
3662
3695
  rules: [{
3663
3696
  validNodeName: ['A']
@@ -3679,7 +3712,7 @@ var createHyperlinkPlugin = function createHyperlinkPlugin(sdk) {
3679
3712
  };
3680
3713
 
3681
3714
  var _templateObject$4, _templateObject2$3, _templateObject3$3, _styles;
3682
- var baseStyle = /*#__PURE__*/css(_templateObject$4 || (_templateObject$4 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n padding: 0;\n margin: 0 0 1.25rem 1.25rem;\n div:first-child {\n margin: 0;\n line-height: ", ";\n }\n"])), tokens.lineHeightDefault);
3715
+ var baseStyle = /*#__PURE__*/css(_templateObject$4 || (_templateObject$4 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n padding: 0;\n margin: 0 0 1.25rem 1.25rem;\n\n div:first-child {\n margin: 0;\n line-height: ", ";\n }\n"])), tokens.lineHeightDefault);
3683
3716
  var styles$d = (_styles = {}, _styles[BLOCKS.UL_LIST] = /*#__PURE__*/css(_templateObject2$3 || (_templateObject2$3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n list-style-type: disc;\n ul {\n list-style-type: circle;\n ul {\n list-style-type: square;\n }\n }\n "]))), _styles[BLOCKS.OL_LIST] = /*#__PURE__*/css(_templateObject3$3 || (_templateObject3$3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n list-style-type: decimal;\n ol {\n list-style-type: upper-alpha;\n ol {\n list-style-type: lower-roman;\n ol {\n list-style-type: lower-alpha;\n }\n }\n }\n "]))), _styles);
3684
3717
 
3685
3718
  function createList(Tag, block) {
@@ -3694,7 +3727,7 @@ var ListUL = /*#__PURE__*/createList('ul', BLOCKS.UL_LIST);
3694
3727
  var ListOL = /*#__PURE__*/createList('ol', BLOCKS.OL_LIST);
3695
3728
 
3696
3729
  var _templateObject$5;
3697
- var style = /*#__PURE__*/css(_templateObject$5 || (_templateObject$5 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n margin: 0;\n list-style: inherit;\n ol,\n ul {\n margin: 0 0 0 ", ";\n }\n"])), tokens.spacingL);
3730
+ var style = /*#__PURE__*/css(_templateObject$5 || (_templateObject$5 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n margin: 0;\n list-style: inherit;\n margin-top: ", ";\n\n ol,\n ul {\n margin: 0 0 0 ", ";\n }\n"])), tokens.spacingXs, tokens.spacingL);
3698
3731
  function ListItem(props) {
3699
3732
  return /*#__PURE__*/createElement("li", Object.assign({}, props.attributes, {
3700
3733
  className: style
@@ -3762,66 +3795,285 @@ var insertParagraphAsChild = function insertParagraphAsChild(editor, _ref5) {
3762
3795
  };
3763
3796
 
3764
3797
  /**
3765
- * A copy of Plate's list plugin with a few adjustments
3766
- * to support pasting any element
3798
+ * Build a new list item node while preserving marks
3767
3799
  */
3768
- var getListInsertFragment = function getListInsertFragment(editor) {
3769
- var insertFragment = editor.insertFragment;
3770
- var li = getPlugin(editor, ELEMENT_LI);
3771
- var ul = getPlugin(editor, ELEMENT_UL);
3772
- var ol = getPlugin(editor, ELEMENT_OL);
3773
3800
 
3774
- var isListRoot = function isListRoot(node) {
3775
- return [ul.type, ol.type].includes(node.type);
3801
+ var emptyListItemNode = function emptyListItemNode(editor, withChildren) {
3802
+ if (withChildren === void 0) {
3803
+ withChildren = false;
3804
+ }
3805
+
3806
+ var children = [];
3807
+
3808
+ if (withChildren) {
3809
+ var marks = Editor.marks(editor) || {};
3810
+ children = [{
3811
+ type: BLOCKS.PARAGRAPH,
3812
+ data: {},
3813
+ children: [_extends({
3814
+ text: ''
3815
+ }, marks)]
3816
+ }];
3817
+ }
3818
+
3819
+ return {
3820
+ type: BLOCKS.LIST_ITEM,
3821
+ data: {},
3822
+ children: children
3776
3823
  };
3824
+ };
3825
+ /**
3826
+ * Insert list item if selection is in li>p.
3827
+ */
3777
3828
 
3778
- var getFirstAncestorOfType = function getFirstAncestorOfType(root, entry, _ref) {
3779
- var type = _ref.type;
3780
- var ancestor = Path.parent(entry[1]);
3781
3829
 
3782
- while (Node.get(root, ancestor).type !== type) {
3783
- ancestor = Path.parent(ancestor);
3830
+ var insertListItem = function insertListItem(editor) {
3831
+ if (!editor.selection) {
3832
+ return false;
3833
+ } // Naming it paragraph for simplicity but can be a heading as well
3834
+
3835
+
3836
+ var paragraph = getAbove(editor, {
3837
+ match: {
3838
+ type: TEXT_CONTAINERS
3784
3839
  }
3840
+ });
3785
3841
 
3786
- return [Node.get(root, ancestor), ancestor];
3787
- };
3842
+ if (!paragraph) {
3843
+ return false;
3844
+ }
3845
+
3846
+ var paragraphPath = paragraph[1];
3847
+ var listItem = getParent(editor, paragraphPath);
3848
+
3849
+ if (!listItem) {
3850
+ return false;
3851
+ }
3852
+
3853
+ var listItemNode = listItem[0],
3854
+ listItemPath = listItem[1];
3855
+
3856
+ if (listItemNode.type !== BLOCKS.LIST_ITEM) {
3857
+ return false;
3858
+ } // We are in a li>p (or heading)
3859
+
3860
+
3861
+ Editor.withoutNormalizing(editor, function () {
3862
+ if (!editor.selection) {
3863
+ return;
3864
+ } // Check the cursor position in the current paragraph
3865
+
3866
+
3867
+ var isAtStart = isSelectionAtBlockStart(editor);
3868
+ var isAtEnd = isSelectionAtBlockEnd(editor);
3869
+ var isAtStartOfListItem = isAtStart && isFirstChild(paragraphPath);
3870
+ var shouldSplit = !isAtStart && !isAtEnd; // Split the current paragraph content if necessary
3871
+
3872
+ if (shouldSplit) {
3873
+ Transforms.splitNodes(editor);
3874
+ } // Insert the new li
3875
+
3876
+
3877
+ var newListItemPath = isAtStartOfListItem ? listItemPath : Path.next(listItemPath);
3878
+ insertNodes(editor, // Add an empty paragraph to the new li if We will not move some
3879
+ // paragraphs over there.
3880
+ emptyListItemNode(editor, !shouldSplit), {
3881
+ at: newListItemPath
3882
+ }); // Move children *after* selection to the new li
3883
+
3884
+ var fromPath = isAtStart ? paragraphPath : Path.next(paragraphPath);
3885
+ var fromStartIndex = fromPath[fromPath.length - 1] || 0; // On split we don't add paragraph to the new li so we move
3886
+ // content to the very beginning. Otherwise, account for the empty
3887
+ // paragraph at the beginning by moving the content after
3888
+
3889
+ var toPath = newListItemPath.concat([shouldSplit ? 0 : 1]);
3890
+
3891
+ if (!isAtStartOfListItem) {
3892
+ moveChildren(editor, {
3893
+ at: listItemPath,
3894
+ to: toPath,
3895
+ fromStartIndex: fromStartIndex
3896
+ });
3897
+ } // Move cursor to the start of the new li
3898
+
3899
+
3900
+ Transforms.select(editor, newListItemPath);
3901
+ Transforms.collapse(editor, {
3902
+ edge: 'start'
3903
+ });
3904
+ }); // Returning True skips processing other editor.insertBreak handlers
3905
+
3906
+ return true;
3907
+ };
3908
+
3909
+ /**
3910
+ * Credit: Copied & modified version from Plate's list plugin to support
3911
+ * list items with multiple children.
3912
+ *
3913
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3914
+ */
3915
+
3916
+ var listBreak = function listBreak(editor) {
3917
+ if (!editor.selection) return false;
3918
+ var res = getListItemEntry(editor, {});
3919
+ var moved; // If selection is in a li
3920
+
3921
+ if (res) {
3922
+ var list = res.list,
3923
+ listItem = res.listItem;
3924
+ var childNode = listItem[0].children[0]; // If selected li is empty, move it up.
3925
+
3926
+ if (isBlockAboveEmpty(editor) && listItem[0].children.length === 1 && TEXT_CONTAINERS.includes(childNode.type)) {
3927
+ moved = moveListItemUp(editor, {
3928
+ list: list,
3929
+ listItem: listItem
3930
+ });
3931
+ if (moved) return true;
3932
+ }
3933
+ }
3934
+
3935
+ var didReset = onKeyDownResetNode(editor, mockPlugin({
3936
+ options: {
3937
+ rules: [{
3938
+ types: [getPluginType(editor, ELEMENT_LI)],
3939
+ defaultType: getPluginType(editor, ELEMENT_DEFAULT),
3940
+ predicate: function predicate() {
3941
+ return !moved && isBlockAboveEmpty(editor);
3942
+ },
3943
+ onReset: function onReset(_editor) {
3944
+ return unwrapList(_editor);
3945
+ }
3946
+ }]
3947
+ }
3948
+ }))(SIMULATE_BACKSPACE);
3949
+
3950
+ if (didReset) {
3951
+ return true;
3952
+ }
3788
3953
  /**
3789
- * Removes the "empty" leading lis. Empty in this context means lis only with other lis as children.
3790
- *
3791
- * @returns If argument is not a list root, returns it, otherwise returns ul[] or li[].
3954
+ * If selection is in li > p, insert li.
3792
3955
  */
3793
3956
 
3794
3957
 
3795
- var trimList = function trimList(listRoot) {
3796
- if (!isListRoot(listRoot)) {
3797
- return [listRoot];
3798
- }
3958
+ if (!moved) {
3959
+ var inserted = insertListItem(editor);
3960
+ if (inserted) return true;
3961
+ }
3962
+
3963
+ return false;
3964
+ };
3799
3965
 
3800
- var textEntries = Array.from(Node.texts(listRoot));
3801
- var commonAncestorEntry = textEntries.reduce(function (commonAncestor, textEntry) {
3802
- return Path.isAncestor(commonAncestor[1], textEntry[1]) ? commonAncestor : Node.common(listRoot, textEntry[1], commonAncestor[1]);
3803
- }, // any list item would do, we grab the first one
3804
- getFirstAncestorOfType(listRoot, textEntries[0], li));
3805
- return isListRoot(commonAncestorEntry[0]) ? commonAncestorEntry[0].children : [commonAncestorEntry[0]];
3966
+ var insertListBreak = function insertListBreak(editor) {
3967
+ var insertBreak = editor.insertBreak;
3968
+ return function () {
3969
+ if (listBreak(editor)) return;
3970
+ insertBreak();
3806
3971
  };
3972
+ };
3973
+
3974
+ /**
3975
+ * Credit: Modified version of Plate's list plugin
3976
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3977
+ */
3978
+
3979
+ var getFirstAncestorOfType = function getFirstAncestorOfType(root, entry) {
3980
+ var ancestor = Path.parent(entry[1]);
3981
+
3982
+ while (Node.get(root, ancestor).type !== BLOCKS.LIST_ITEM) {
3983
+ ancestor = Path.parent(ancestor);
3984
+ }
3985
+
3986
+ return [Node.get(root, ancestor), ancestor];
3987
+ };
3988
+
3989
+ var isListRoot = function isListRoot(node) {
3990
+ return [BLOCKS.UL_LIST, BLOCKS.OL_LIST].includes(node.type);
3991
+ };
3992
+ /**
3993
+ * Removes the "empty" leading lis. Empty in this context means lis only with other lis as children.
3994
+ *
3995
+ * @returns If argument is not a list root, returns it, otherwise returns ul[] or li[].
3996
+ */
3997
+
3998
+
3999
+ var trimList = function trimList(listRoot) {
4000
+ if (!isListRoot(listRoot)) {
4001
+ return [listRoot];
4002
+ }
4003
+
4004
+ var textEntries = Array.from(Node.texts(listRoot));
4005
+ var commonAncestorEntry = textEntries.reduce(function (commonAncestor, textEntry) {
4006
+ return Path.isAncestor(commonAncestor[1], textEntry[1]) ? commonAncestor : Node.common(listRoot, textEntry[1], commonAncestor[1]);
4007
+ }, // any list item would do, we grab the first one
4008
+ getFirstAncestorOfType(listRoot, textEntries[0]));
4009
+ return isListRoot(commonAncestorEntry[0]) ? commonAncestorEntry[0].children : [commonAncestorEntry[0]];
4010
+ };
4011
+ /**
4012
+ * Removes leading li when pasting a single li with a single child.
4013
+ */
4014
+
4015
+
4016
+ var trimLiWrapper = function trimLiWrapper(nodes) {
4017
+ if (nodes.length !== 1) {
4018
+ return nodes;
4019
+ }
4020
+
4021
+ var node = nodes[0];
4022
+
4023
+ if (node.type !== BLOCKS.LIST_ITEM || node.children.length !== 1) {
4024
+ return nodes;
4025
+ }
3807
4026
 
4027
+ return node.children;
4028
+ };
4029
+
4030
+ var unwrapTextContainerAtStart = function unwrapTextContainerAtStart(nodes) {
4031
+ var node = nodes[0];
4032
+
4033
+ if (TEXT_CONTAINERS.includes(node.type)) {
4034
+ return [].concat(node.children, nodes.slice(1));
4035
+ }
4036
+
4037
+ return nodes;
4038
+ };
4039
+
4040
+ var insertListFragment = function insertListFragment(editor) {
4041
+ var insertFragment = editor.insertFragment;
3808
4042
  return function (fragment) {
4043
+ if (!editor.selection) {
4044
+ return;
4045
+ }
4046
+
3809
4047
  var liEntry = findNode(editor, {
3810
4048
  match: {
3811
- type: li.type
4049
+ type: BLOCKS.LIST_ITEM
3812
4050
  },
3813
4051
  mode: 'lowest'
3814
4052
  });
3815
4053
 
3816
4054
  if (liEntry) {
3817
- var liPath = liEntry[1]; // FIXME: this is a temporarily workaround and needs a follow-up to properly
3818
- // non-text elements
3819
-
3820
- var nodes = fragment.flatMap(function (node) {
4055
+ var nodes = unwrapTextContainerAtStart(trimLiWrapper(fragment.flatMap(function (node) {
3821
4056
  return trimList(node);
4057
+ })));
4058
+ var firstBlockIndex = nodes.findIndex(function (node) {
4059
+ return Editor.isBlock(editor, node);
4060
+ });
4061
+
4062
+ if (firstBlockIndex < 0) {
4063
+ firstBlockIndex = nodes.length;
4064
+ }
4065
+
4066
+ var inlines = nodes.slice(0, firstBlockIndex);
4067
+ var blocks = nodes.slice(firstBlockIndex); // Two calls to insertNodes are required here. Otherwise, all blocks
4068
+ // after a text or inline element occurrence will be unwrapped for
4069
+ // some reason.
4070
+
4071
+ Transforms.insertNodes(editor, inlines, {
4072
+ at: editor.selection,
4073
+ select: true
3822
4074
  });
3823
- return Transforms.insertNodes(editor, nodes, {
3824
- at: editor.selection || Path.next(liPath),
4075
+ return Transforms.insertNodes(editor, blocks, {
4076
+ at: editor.selection,
3825
4077
  select: true
3826
4078
  });
3827
4079
  }
@@ -3833,18 +4085,37 @@ var getListInsertFragment = function getListInsertFragment(editor) {
3833
4085
  };
3834
4086
  };
3835
4087
 
3836
- var options = {
3837
- validLiChildrenTypes: LIST_ITEM_BLOCKS
3838
- };
3839
- var withList = function withList(editor, plugin) {
3840
- var insertFragment = editor.insertFragment;
3841
- withList$1(editor, _extends({}, plugin, {
3842
- options: options
3843
- })); // Reverts any overrides to insertFragment
4088
+ /**
4089
+ * Credit: Modified version of Plate's list plugin
4090
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
4091
+ */
4092
+ var validLiChildrenTypes = LIST_ITEM_BLOCKS;
4093
+ var withList = function withList(editor) {
4094
+ var deleteBackward = editor.deleteBackward,
4095
+ deleteForward = editor.deleteForward,
4096
+ deleteFragment = editor.deleteFragment;
4097
+
4098
+ editor.deleteBackward = function (unit) {
4099
+ if (deleteBackwardList(editor, unit)) return;
4100
+ deleteBackward(unit);
4101
+ };
3844
4102
 
3845
- editor.insertFragment = insertFragment; // Use our custom getListInsertFragment
4103
+ editor.deleteForward = function (unit) {
4104
+ if (deleteForwardList(editor)) return;
4105
+ deleteForward(unit);
4106
+ };
4107
+
4108
+ editor.deleteFragment = function () {
4109
+ if (deleteFragmentList(editor)) return;
4110
+ deleteFragment();
4111
+ };
3846
4112
 
3847
- editor.insertFragment = getListInsertFragment(editor);
4113
+ editor.insertBreak = insertListBreak(editor);
4114
+ editor.insertFragment = insertListFragment(editor); // TODO: replace with Normalizer rules
4115
+
4116
+ editor.normalizeNode = normalizeList(editor, {
4117
+ validLiChildrenTypes: validLiChildrenTypes
4118
+ });
3848
4119
  return editor;
3849
4120
  };
3850
4121
 
@@ -3902,7 +4173,7 @@ function ToolbarListButton(props) {
3902
4173
  toggleList(editor, {
3903
4174
  type: type
3904
4175
  });
3905
- ReactEditor.focus(editor);
4176
+ focus(editor);
3906
4177
  };
3907
4178
  }
3908
4179
 
@@ -3930,7 +4201,7 @@ function ToolbarBoldButton(props) {
3930
4201
  toggleMark(editor, {
3931
4202
  key: MARKS.BOLD
3932
4203
  });
3933
- ReactEditor.focus(editor);
4204
+ focus(editor);
3934
4205
  }
3935
4206
 
3936
4207
  if (!editor) return null;
@@ -3989,7 +4260,7 @@ function ToolbarCodeButton(props) {
3989
4260
  toggleMark(editor, {
3990
4261
  key: MARKS.CODE
3991
4262
  });
3992
- ReactEditor.focus(editor);
4263
+ focus(editor);
3993
4264
  }
3994
4265
 
3995
4266
  if (!editor) return null;
@@ -4039,7 +4310,7 @@ function ToolbarItalicButton(props) {
4039
4310
  toggleMark(editor, {
4040
4311
  key: MARKS.ITALIC
4041
4312
  });
4042
- ReactEditor.focus(editor);
4313
+ focus(editor);
4043
4314
  }
4044
4315
 
4045
4316
  if (!editor) return null;
@@ -4093,7 +4364,7 @@ function ToolbarUnderlineButton(props) {
4093
4364
  toggleMark(editor, {
4094
4365
  key: MARKS.UNDERLINE
4095
4366
  });
4096
- ReactEditor.focus(editor);
4367
+ focus(editor);
4097
4368
  }
4098
4369
 
4099
4370
  if (!editor) return null;
@@ -4514,6 +4785,38 @@ function Quote(props) {
4514
4785
  }), props.children);
4515
4786
  }
4516
4787
 
4788
+ function toggleQuote(editor) {
4789
+ if (!editor.selection) return;
4790
+ var isActive = isBlockSelected(editor, BLOCKS.QUOTE);
4791
+ Editor.withoutNormalizing(editor, function () {
4792
+ Transforms.unwrapNodes(editor, {
4793
+ match: function match(node) {
4794
+ return Element.isElement(node) && node.type === BLOCKS.QUOTE;
4795
+ },
4796
+ split: true
4797
+ });
4798
+
4799
+ if (!isActive) {
4800
+ var quote = {
4801
+ type: BLOCKS.QUOTE,
4802
+ data: {},
4803
+ children: []
4804
+ };
4805
+ Transforms.wrapNodes(editor, quote);
4806
+ }
4807
+ });
4808
+ }
4809
+ var onKeyDownToggleQuote = function onKeyDownToggleQuote(editor, plugin) {
4810
+ return function (event) {
4811
+ var hotkey = plugin.options.hotkey;
4812
+
4813
+ if (hotkey && isHotkey(hotkey, event)) {
4814
+ event.preventDefault();
4815
+ toggleQuote(editor);
4816
+ }
4817
+ };
4818
+ };
4819
+
4517
4820
  function createQuotePlugin() {
4518
4821
  var _transform;
4519
4822
 
@@ -4526,7 +4829,7 @@ function createQuotePlugin() {
4526
4829
  hotkey: 'mod+shift+1'
4527
4830
  },
4528
4831
  handlers: {
4529
- onKeyDown: onKeyDownToggleElement
4832
+ onKeyDown: onKeyDownToggleQuote
4530
4833
  },
4531
4834
  deserializeHtml: {
4532
4835
  rules: [{
@@ -4540,38 +4843,13 @@ function createQuotePlugin() {
4540
4843
  };
4541
4844
  }
4542
4845
 
4543
- function toggleQuote(editor) {
4544
- if (!editor.selection) return;
4545
- var isActive = isBlockSelected(editor, BLOCKS.QUOTE);
4546
- Editor.withoutNormalizing(editor, function () {
4547
- Transforms.unwrapNodes(editor, {
4548
- match: function match(node) {
4549
- return Element.isElement(node) && node.type === BLOCKS.QUOTE;
4550
- },
4551
- split: true
4552
- });
4553
- Transforms.setNodes(editor, {
4554
- type: isActive ? BLOCKS.PARAGRAPH : BLOCKS.QUOTE
4555
- });
4556
-
4557
- if (!isActive) {
4558
- var quote = {
4559
- type: BLOCKS.QUOTE,
4560
- data: {},
4561
- children: []
4562
- };
4563
- Transforms.wrapNodes(editor, quote);
4564
- }
4565
- });
4566
- }
4567
-
4568
4846
  function ToolbarQuoteButton(props) {
4569
4847
  var editor = useContentfulEditor();
4570
4848
 
4571
4849
  function handleOnClick() {
4572
4850
  if (!editor) return;
4573
4851
  toggleQuote(editor);
4574
- ReactEditor.focus(editor);
4852
+ focus(editor);
4575
4853
  }
4576
4854
 
4577
4855
  if (!editor) return null;
@@ -4927,11 +5205,7 @@ var TableActions = function TableActions() {
4927
5205
 
4928
5206
  var close = React__default.useCallback(function () {
4929
5207
  setOpen(false);
4930
- if (!editor) return; // Makes sure we keep the editor in focus when clicking on/out
4931
- // the dropdown menu
4932
-
4933
- ReactEditor.focus(editor);
4934
- }, [editor]);
5208
+ }, []);
4935
5209
  React__default.useEffect(function () {
4936
5210
  setHeaderEnabled(Boolean(editor && isTableHeaderEnabled(editor)));
4937
5211
  }, [editor]);
@@ -5200,7 +5474,7 @@ function ToolbarTableButton(props) {
5200
5474
  case 2:
5201
5475
  onViewportAction('insertTable');
5202
5476
  insertTableAndFocusFirstCell(editor);
5203
- ReactEditor.focus(editor);
5477
+ focus(editor);
5204
5478
 
5205
5479
  case 5:
5206
5480
  case "end":
@@ -5227,22 +5501,52 @@ function createTextPlugin() {
5227
5501
  return {
5228
5502
  key: 'TextPlugin',
5229
5503
  withOverrides: function withOverrides(editor) {
5230
- var deleteForward = editor.deleteForward; // When pressing delete instead of backspace
5504
+ // Reverts the change made upstream that caused the cursor
5505
+ // to be trapped inside inline elements.
5506
+ //
5507
+ // Reverts https://github.com/ianstormtaylor/slate/pull/4578/
5508
+ // Related https://github.com/ianstormtaylor/slate/issues/4704
5509
+ var insertText = editor.insertText;
5510
+
5511
+ editor.insertText = function (text) {
5512
+ var selection = editor.selection; // If the cursor is at the end of an inline, move it outside
5513
+ // before inserting
5514
+
5515
+ if (selection && Range.isCollapsed(selection)) {
5516
+ var _Editor$above;
5517
+
5518
+ var inlinePath = (_Editor$above = Editor.above(editor, {
5519
+ match: function match(n) {
5520
+ return Editor.isInline(editor, n);
5521
+ },
5522
+ mode: 'highest'
5523
+ })) == null ? void 0 : _Editor$above[1];
5524
+
5525
+ if (inlinePath && Editor.isEnd(editor, selection.anchor, inlinePath)) {
5526
+ var point = Editor.after(editor, inlinePath);
5527
+ Transforms.setSelection(editor, {
5528
+ anchor: point,
5529
+ focus: point
5530
+ });
5531
+ }
5532
+ }
5231
5533
 
5232
- editor.deleteForward = function (unit) {
5233
- var _editor$selection;
5534
+ return insertText(text);
5535
+ }; // When pressing delete instead of backspace
5234
5536
 
5235
- var _Editor$nodes = Editor.nodes(editor, {
5236
- at: (_editor$selection = editor.selection) == null ? void 0 : _editor$selection.focus.path,
5237
- match: function match(node) {
5238
- return TEXT_CONTAINERS.includes(node.type);
5537
+
5538
+ var deleteForward = editor.deleteForward;
5539
+
5540
+ editor.deleteForward = function (unit) {
5541
+ var entry = getAbove(editor, {
5542
+ match: {
5543
+ type: TEXT_CONTAINERS
5239
5544
  }
5240
- }),
5241
- nodes = _Editor$nodes[0];
5545
+ });
5242
5546
 
5243
- if (nodes) {
5244
- var paragraphOrHeading = nodes[0],
5245
- path = nodes[1];
5547
+ if (entry) {
5548
+ var paragraphOrHeading = entry[0],
5549
+ path = entry[1];
5246
5550
  var isTextEmpty = isAncestorEmpty(editor, paragraphOrHeading); // We ignore paragraphs/headings that are children of ul, ol, blockquote, tables, etc
5247
5551
 
5248
5552
  var isRootLevel = path.length === 1;
@@ -5299,8 +5603,9 @@ var transformVoid = function transformVoid(editor, _ref) {
5299
5603
  var createVoidsPlugin = function createVoidsPlugin() {
5300
5604
  return {
5301
5605
  key: 'VoidsPlugin',
5302
- exitBreak: [// Can insert before first void block
5303
- {
5606
+ exitBreak: [{
5607
+ // Inserts a new paragraph *before* a void element if it's the very first
5608
+ // node on the editor
5304
5609
  hotkey: 'enter',
5305
5610
  before: true,
5306
5611
  query: {
@@ -5310,14 +5615,17 @@ var createVoidsPlugin = function createVoidsPlugin() {
5310
5615
  return isRootLevel(path) && isFirstChild(path) && !!node.isVoid;
5311
5616
  }
5312
5617
  }
5313
- }, // Can insert after a void block
5314
- {
5618
+ }, {
5619
+ // Inserts a new paragraph on enter when a void element is focused
5315
5620
  hotkey: 'enter',
5621
+ // exploit the internal use of Array.slice(0, level + 1) by the exitBreak plugin
5622
+ // to stay in the parent element
5623
+ level: -2,
5316
5624
  query: {
5317
5625
  filter: function filter(_ref2) {
5318
5626
  var node = _ref2[0],
5319
5627
  path = _ref2[1];
5320
- return !isFirstChild(path) && !!node.isVoid;
5628
+ return !(isRootLevel(path) && isFirstChild(path)) && !!node.isVoid;
5321
5629
  }
5322
5630
  }
5323
5631
  }],
@@ -5346,8 +5654,8 @@ var getPlugins = function getPlugins(sdk, tracking) {
5346
5654
  return [// AST must come after the HTML deserializer
5347
5655
  createDeserializeHtmlPlugin(), createDeserializeAstPlugin(), createDeserializeDocxPlugin(), // Global shortcuts
5348
5656
  createDragAndDropPlugin(), // Block Elements
5349
- createParagraphPlugin(), createListPlugin(), createHrPlugin(), createHeadingPlugin(), createQuotePlugin(), createTablePlugin(tracking), createEmbeddedEntryBlockPlugin(sdk), createEmbeddedAssetBlockPlugin(sdk), // Inline elements
5350
- createHyperlinkPlugin(sdk), createEmbeddedEntityInlinePlugin(sdk), // Marks
5657
+ createParagraphPlugin(), createListPlugin(), createHrPlugin(), createHeadingPlugin(), createQuotePlugin(), createTablePlugin(tracking), createEmbeddedEntryBlockPlugin(sdk, tracking), createEmbeddedAssetBlockPlugin(sdk, tracking), // Inline elements
5658
+ createHyperlinkPlugin(sdk, tracking), createEmbeddedEntityInlinePlugin(sdk, tracking), // Marks
5351
5659
  createMarksPlugin(), // Other
5352
5660
  createTrailingParagraphPlugin(), createTextPlugin(), createVoidsPlugin(), createSelectOnBackspacePlugin(), // Pasting content from other sources
5353
5661
  createPasteHTMLPlugin(), // These plugins drive their configurations from the list of plugins
@@ -5618,13 +5926,9 @@ var StickyToolbarWrapper = function StickyToolbarWrapper(_ref) {
5618
5926
  var _excluded = ["sdk", "isInitiallyDisabled", "onAction"];
5619
5927
  var ConnectedRichTextEditor = function ConnectedRichTextEditor(props) {
5620
5928
  var tracking = useTrackingContext();
5621
- var docFromAdapter = toSlatejsDocument({
5622
- document: props.value || EMPTY_DOCUMENT,
5623
- schema: schema
5624
- });
5625
- var doc = sanitizeIncomingSlateDoc(docFromAdapter);
5929
+ var editor = useContentfulEditor();
5626
5930
 
5627
- var _useState = useState(doc),
5931
+ var _useState = useState([]),
5628
5932
  value = _useState[0],
5629
5933
  setValue = _useState[1];
5630
5934
 
@@ -5634,12 +5938,33 @@ var ConnectedRichTextEditor = function ConnectedRichTextEditor(props) {
5634
5938
  var plugins = React__default.useMemo(function () {
5635
5939
  return getPlugins(props.sdk, tracking);
5636
5940
  }, [props.sdk, tracking]);
5941
+ React__default.useEffect(function () {
5942
+ if (!editor) {
5943
+ return;
5944
+ }
5945
+
5946
+ var docFromAdapter = toSlatejsDocument({
5947
+ document: props.value || EMPTY_DOCUMENT,
5948
+ schema: schema
5949
+ });
5950
+ var doc = sanitizeIncomingSlateDoc(docFromAdapter); // Slate throws an error if the value on the initial render is invalid
5951
+ // so we directly set the value on the editor in order
5952
+ // to be able to trigger normalization on the initial value before rendering
5953
+ // TODO: use https://plate.udecode.io/docs/Plate#normalizeinitialvalue when working
5954
+
5955
+ editor.children = doc;
5956
+ Editor.normalize(editor, {
5957
+ force: true
5958
+ }); // We set the value so that the rendering can take over from here
5959
+
5960
+ setValue(editor.children);
5961
+ }, [props.value, editor]);
5637
5962
  return /*#__PURE__*/React__default.createElement("div", {
5638
5963
  className: styles$j.root,
5639
5964
  "data-test-id": "rich-text-editor"
5640
5965
  }, /*#__PURE__*/React__default.createElement(Plate, {
5641
5966
  id: getContentfulEditorId(props.sdk),
5642
- initialValue: value,
5967
+ value: value,
5643
5968
  plugins: plugins,
5644
5969
  disableCorePlugins: disableCorePlugins,
5645
5970
  editableProps: {