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

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.
Files changed (92) hide show
  1. package/README.md +4 -0
  2. package/dist/ContentfulEditorProvider.d.ts +6 -5
  3. package/dist/RichTextEditor.d.ts +1 -1
  4. package/dist/field-editor-rich-text.cjs.development.js +2693 -1147
  5. package/dist/field-editor-rich-text.cjs.development.js.map +1 -1
  6. package/dist/field-editor-rich-text.cjs.production.min.js +1 -1
  7. package/dist/field-editor-rich-text.cjs.production.min.js.map +1 -1
  8. package/dist/field-editor-rich-text.esm.js +2767 -1221
  9. package/dist/field-editor-rich-text.esm.js.map +1 -1
  10. package/dist/helpers/editor.d.ts +16 -17
  11. package/dist/helpers/environment.d.ts +1 -0
  12. package/dist/helpers/extractNodes.d.ts +2 -3
  13. package/dist/helpers/{sanitizeSlateDoc.d.ts → sanitizeIncomingSlateDoc.d.ts} +2 -1
  14. package/dist/helpers/transformers.d.ts +6 -6
  15. package/dist/plugins/Break/createResetNodePlugin.d.ts +2 -0
  16. package/dist/plugins/Break/index.d.ts +2 -0
  17. package/dist/plugins/EmbeddedEntityBlock/LinkedEntityBlock.d.ts +3 -1
  18. package/dist/plugins/EmbeddedEntityBlock/ToolbarIcon.d.ts +1 -3
  19. package/dist/plugins/EmbeddedEntityBlock/Util.d.ts +2 -1
  20. package/dist/plugins/EmbeddedEntityInline/FetchingWrappedInlineEntryCard.d.ts +1 -0
  21. package/dist/plugins/EmbeddedEntityInline/index.d.ts +2 -2
  22. package/dist/plugins/Hr/index.d.ts +2 -3
  23. package/dist/plugins/Hyperlink/HyperlinkModal.d.ts +5 -4
  24. package/dist/plugins/Hyperlink/components/EntityHyperlink.d.ts +9 -0
  25. package/dist/plugins/Hyperlink/components/ToolbarHyperlinkButton.d.ts +5 -0
  26. package/dist/plugins/Hyperlink/components/UrlHyperlink.d.ts +9 -0
  27. package/dist/plugins/Hyperlink/components/styles.d.ts +4 -0
  28. package/dist/plugins/Hyperlink/createHyperlinkPlugin.d.ts +3 -0
  29. package/dist/plugins/Hyperlink/index.d.ts +2 -8
  30. package/dist/plugins/Hyperlink/useEntityInfo.d.ts +16 -0
  31. package/dist/plugins/Hyperlink/utils.d.ts +4 -2
  32. package/dist/plugins/List/insertListBreak.d.ts +2 -0
  33. package/dist/plugins/List/insertListFragment.d.ts +3 -0
  34. package/dist/plugins/List/onKeyDownList.d.ts +7 -0
  35. package/dist/plugins/List/transforms/deleteBackwardList.d.ts +2 -0
  36. package/dist/plugins/List/transforms/insertListItem.d.ts +5 -0
  37. package/dist/plugins/List/transforms/moveListItemDown.d.ts +11 -0
  38. package/dist/plugins/List/transforms/moveListItems.d.ts +10 -0
  39. package/dist/plugins/List/transforms/toggleList.d.ts +4 -0
  40. package/dist/plugins/List/transforms/unwrapList.d.ts +5 -0
  41. package/dist/plugins/List/utils.d.ts +9 -5
  42. package/dist/plugins/List/withList.d.ts +2 -1
  43. package/dist/plugins/Marks/Bold.d.ts +7 -5
  44. package/dist/plugins/Marks/Code.d.ts +7 -5
  45. package/dist/plugins/Marks/Italic.d.ts +7 -5
  46. package/dist/plugins/Marks/Underline.d.ts +7 -5
  47. package/dist/plugins/Marks/components/MarkToolbarButton.d.ts +13 -0
  48. package/dist/plugins/Marks/helpers.d.ts +4 -0
  49. package/dist/plugins/Normalizer/types.d.ts +4 -3
  50. package/dist/plugins/Normalizer/utils.d.ts +2 -2
  51. package/dist/plugins/Normalizer/withNormalizer.d.ts +2 -2
  52. package/dist/plugins/PasteHTML/createPasteHTMLPlugin.d.ts +6 -0
  53. package/dist/plugins/Quote/shouldResetQuote.d.ts +8 -0
  54. package/dist/plugins/Quote/toggleQuote.d.ts +5 -2
  55. package/dist/plugins/Quote/withQuote.d.ts +3 -0
  56. package/dist/plugins/SlashCommands/SlashCommandsPalette.d.ts +5 -0
  57. package/dist/plugins/SlashCommands/createSlashCommandsPlugin.d.ts +2 -0
  58. package/dist/plugins/SlashCommands/helpers.d.ts +6 -0
  59. package/dist/plugins/SlashCommands/index.d.ts +2 -0
  60. package/dist/plugins/Table/actions/addColumn.d.ts +3 -3
  61. package/dist/plugins/Table/actions/addRow.d.ts +3 -3
  62. package/dist/plugins/Table/actions/setHeader.d.ts +2 -2
  63. package/dist/plugins/Table/createTablePlugin.d.ts +1 -2
  64. package/dist/plugins/Table/helpers.d.ts +8 -7
  65. package/dist/plugins/Table/insertTableFragment.d.ts +3 -0
  66. package/dist/plugins/Table/onKeyDownTable.d.ts +3 -0
  67. package/dist/plugins/Table/tableTracking.d.ts +4 -0
  68. package/dist/plugins/Text/createTextPlugin.d.ts +2 -0
  69. package/dist/plugins/Text/index.d.ts +1 -2
  70. package/dist/plugins/Tracking/createTrackingPlugin.d.ts +11 -0
  71. package/dist/plugins/Tracking/index.d.ts +1 -0
  72. package/dist/plugins/Tracking/utils.d.ts +2 -0
  73. package/dist/plugins/TrailingParagraph/index.d.ts +2 -1
  74. package/dist/plugins/Voids/transformVoid.d.ts +2 -2
  75. package/dist/plugins/index.d.ts +2 -2
  76. package/dist/plugins/links-tracking.d.ts +6 -0
  77. package/dist/plugins/shared/FetchingWrappedAssetCard.d.ts +1 -0
  78. package/dist/plugins/shared/FetchingWrappedEntryCard.d.ts +3 -2
  79. package/dist/prepareDocument.d.ts +20 -0
  80. package/dist/test-utils/assertOutput.d.ts +2 -2
  81. package/dist/test-utils/createEditor.d.ts +5 -6
  82. package/dist/test-utils/jsx.d.ts +1 -1
  83. package/dist/test-utils/mockPlugin.d.ts +1 -1
  84. package/dist/test-utils/setEmptyDataAttribute.d.ts +2 -2
  85. package/dist/types.d.ts +11 -3
  86. package/dist/useOnValueChanged.d.ts +8 -0
  87. package/package.json +16 -15
  88. package/dist/TrackingProvider.d.ts +0 -11
  89. package/dist/plugins/Hyperlink/EntryAssetTooltip.d.ts +0 -9
  90. package/dist/plugins/Hyperlink/truncate.d.ts +0 -1
  91. package/dist/plugins/List/getListInsertFragment.d.ts +0 -6
  92. package/dist/plugins/Table/addTableTrackingEvents.d.ts +0 -3
@@ -6,7 +6,6 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
6
6
 
7
7
  var React = require('react');
8
8
  var React__default = _interopDefault(React);
9
- var contentfulSlatejsAdapter = require('@contentful/contentful-slatejs-adapter');
10
9
  var fieldEditorReference = require('@contentful/field-editor-reference');
11
10
  var fieldEditorShared = require('@contentful/field-editor-shared');
12
11
  var Contentful = require('@contentful/rich-text-types');
@@ -14,12 +13,13 @@ var plateCore = require('@udecode/plate-core');
14
13
  var emotion = require('emotion');
15
14
  var deepEquals = _interopDefault(require('fast-deep-equal'));
16
15
  var noop = _interopDefault(require('lodash/noop'));
17
- var constate = _interopDefault(require('constate'));
18
16
  var plateSerializerDocx = require('@udecode/plate-serializer-docx');
19
17
  var plateBreak = require('@udecode/plate-break');
18
+ var plateResetNode = require('@udecode/plate-reset-node');
20
19
  var isHotkey = _interopDefault(require('is-hotkey'));
21
20
  var slate = require('slate');
22
21
  var Slate = require('slate-react');
22
+ var constate = _interopDefault(require('constate'));
23
23
  var f36Components = require('@contentful/f36-components');
24
24
  var mimetype = _interopDefault(require('@contentful/mimetype'));
25
25
  var get = _interopDefault(require('lodash/get'));
@@ -28,12 +28,16 @@ var tokens = _interopDefault(require('@contentful/f36-tokens'));
28
28
  var find = _interopDefault(require('lodash/find'));
29
29
  var flow = _interopDefault(require('lodash/flow'));
30
30
  var plateList = require('@udecode/plate-list');
31
+ var castArray = _interopDefault(require('lodash/castArray'));
31
32
  var plateBasicMarks = require('@udecode/plate-basic-marks');
32
33
  var isPlainObject = _interopDefault(require('is-plain-obj'));
33
34
  var plateParagraph = require('@udecode/plate-paragraph');
34
35
  var plateSelect = require('@udecode/plate-select');
35
36
  var plateTable = require('@udecode/plate-table');
37
+ var contentfulSlateJSAdapter = require('@contentful/contentful-slatejs-adapter');
38
+ var richTextPlainTextRenderer = require('@contentful/rich-text-plain-text-renderer');
36
39
  var plateTrailingBlock = require('@udecode/plate-trailing-block');
40
+ var debounce = _interopDefault(require('lodash/debounce'));
37
41
  var PropTypes = _interopDefault(require('prop-types'));
38
42
 
39
43
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
@@ -243,159 +247,39 @@ function _createForOfIteratorHelperLoose(o, allowArrayLike) {
243
247
  throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
244
248
  }
245
249
 
246
- var _extends2, _extends4, _inlines;
247
- var inlines = /*#__PURE__*/Object.values(Contentful.INLINES).map(function (type) {
248
- return {
249
- type: type
250
- };
251
- });
252
- var schema = {
253
- document: {
254
- nodes: [{
255
- types: /*#__PURE__*/Contentful.TOP_LEVEL_BLOCKS.map(function (type) {
256
- return {
257
- type: type
258
- };
259
- })
260
- }]
261
- },
262
- blocks: /*#__PURE__*/_extends((_extends2 = {}, _extends2[Contentful.BLOCKS.PARAGRAPH] = {
263
- nodes: [{
264
- match: /*#__PURE__*/[].concat(inlines, [{
265
- object: 'text'
266
- }])
267
- }]
268
- }, _extends2[Contentful.BLOCKS.HEADING_1] = {
269
- nodes: [{
270
- match: /*#__PURE__*/[].concat(inlines, [{
271
- object: 'text'
272
- }])
273
- }]
274
- }, _extends2[Contentful.BLOCKS.HEADING_2] = {
275
- nodes: [{
276
- match: /*#__PURE__*/[].concat(inlines, [{
277
- object: 'text'
278
- }])
279
- }]
280
- }, _extends2[Contentful.BLOCKS.HEADING_3] = {
281
- nodes: [{
282
- match: /*#__PURE__*/[].concat(inlines, [{
283
- object: 'text'
284
- }])
285
- }]
286
- }, _extends2[Contentful.BLOCKS.HEADING_4] = {
287
- nodes: [{
288
- match: /*#__PURE__*/[].concat(inlines, [{
289
- object: 'text'
290
- }])
291
- }]
292
- }, _extends2[Contentful.BLOCKS.HEADING_5] = {
293
- nodes: [{
294
- match: /*#__PURE__*/[].concat(inlines, [{
295
- object: 'text'
296
- }])
297
- }]
298
- }, _extends2[Contentful.BLOCKS.HEADING_6] = {
299
- nodes: [{
300
- match: /*#__PURE__*/[].concat(inlines, [{
301
- object: 'text'
302
- }])
303
- }]
304
- }, _extends2), /*#__PURE__*/Contentful.VOID_BLOCKS.reduce(function (blocks, nodeType) {
305
- var _extends3;
306
-
307
- return _extends({}, blocks, (_extends3 = {}, _extends3[nodeType] = {
308
- isVoid: true
309
- }, _extends3));
310
- }, {}), (_extends4 = {}, _extends4[Contentful.BLOCKS.QUOTE] = {
311
- nodes: [{
312
- match: [/*#__PURE__*/Contentful.CONTAINERS[Contentful.BLOCKS.QUOTE].map(function (type) {
313
- return {
314
- type: type
315
- };
316
- })],
317
- min: 1
318
- }],
319
- normalize: function normalize(editor, error) {
320
- if (error.code === 'child_type_invalid') {
321
- return editor.unwrapBlockByKey(error.node.key, Contentful.BLOCKS.QUOTE);
322
- }
323
- }
324
- }, _extends4)),
325
- inlines: (_inlines = {}, _inlines[Contentful.INLINES.HYPERLINK] = {
326
- nodes: [{
327
- match: [{
328
- object: 'text'
329
- }]
330
- }]
331
- }, _inlines[Contentful.INLINES.ENTRY_HYPERLINK] = {
332
- nodes: [{
333
- match: [{
334
- object: 'text'
335
- }]
336
- }]
337
- }, _inlines[Contentful.INLINES.ASSET_HYPERLINK] = {
338
- nodes: [{
339
- match: [{
340
- object: 'text'
341
- }]
342
- }]
343
- }, _inlines[Contentful.INLINES.EMBEDDED_ENTRY] = {
344
- isVoid: true
345
- }, _inlines)
346
- };
347
-
348
250
  function getContentfulEditorId(sdk) {
349
251
  var entry = sdk.entry,
350
252
  field = sdk.field;
351
- var entryId = entry.getSys().id;
352
- return "rich-text-editor-" + entryId + "-" + field.id + "-" + field.locale;
253
+ var sys = entry.getSys();
254
+ return "rich-text-editor-" + sys.id + "-" + field.id + "-" + field.locale;
353
255
  }
256
+ var editorContext = /*#__PURE__*/React.createContext('');
257
+ var ContentfulEditorIdProvider = editorContext.Provider;
258
+ function useContentfulEditorId(id) {
259
+ var contextId = React.useContext(editorContext);
354
260
 
355
- function useContentfulEditorHook(_ref) {
356
- var sdk = _ref.sdk;
357
- var editorId = getContentfulEditorId(sdk);
358
- var editor = plateCore.usePlateEditorRef(editorId);
359
- return editor;
360
- }
361
-
362
- var _constate = /*#__PURE__*/constate(useContentfulEditorHook),
363
- ContentfulEditorProvider = _constate[0],
364
- useContentfulEditor = _constate[1];
365
-
366
- var isTextElement = function isTextElement(node) {
367
- return 'text' in node;
368
- };
369
- /**
370
- * Ensures incoming void nodes have a child leaf text element.
371
- */
372
-
373
-
374
- function sanitizeIncomingSlateDoc(nodes) {
375
- if (nodes === void 0) {
376
- nodes = [];
261
+ if (id) {
262
+ return id;
377
263
  }
378
264
 
379
- return nodes.map(function (node) {
380
- var _node$children;
265
+ if (!contextId) {
266
+ throw new Error('could not find editor id. Please ensure the component is wrapped in <ContentfulEditorIdProvider> ');
267
+ }
381
268
 
382
- if (isTextElement(node)) {
383
- return node;
384
- }
269
+ return contextId;
270
+ } // This hook re-renders when the value changes
271
+ // Use case: Toolbar icons, for example
385
272
 
386
- if (node.isVoid && ((_node$children = node.children) == null ? void 0 : _node$children.length) === 0) {
387
- return _extends({}, node, {
388
- children: [{
389
- text: '',
390
- data: {}
391
- }]
392
- });
393
- }
273
+ function useContentfulEditor(id) {
274
+ var editorId = useContentfulEditorId(id);
275
+ var editor = plateCore.usePlateEditorState(editorId);
276
+ return editor;
277
+ } // This doesn't re-render when the value changes
394
278
 
395
- return _extends({}, node, {
396
- children: sanitizeIncomingSlateDoc(node.children)
397
- });
398
- });
279
+ function useContentfulEditorRef(id) {
280
+ var editorId = useContentfulEditorId(id);
281
+ var editor = plateCore.usePlateEditorRef(editorId);
282
+ return editor;
399
283
  }
400
284
 
401
285
  var createSoftBreakPlugin = function createSoftBreakPlugin() {
@@ -431,6 +315,33 @@ var createExitBreakPlugin = function createExitBreakPlugin() {
431
315
  });
432
316
  };
433
317
 
318
+ var createResetNodePlugin = function createResetNodePlugin() {
319
+ return plateResetNode.createResetNodePlugin({
320
+ options: {
321
+ rules: []
322
+ },
323
+ then: function then(editor) {
324
+ var rules = editor.plugins.flatMap(function (p) {
325
+ return p.resetNode || [];
326
+ }); // set defaultType to Paragraph if not set
327
+
328
+ for (var _iterator = _createForOfIteratorHelperLoose(rules), _step; !(_step = _iterator()).done;) {
329
+ var rule = _step.value;
330
+
331
+ if (!rule.defaultType) {
332
+ rule.defaultType = Contentful.BLOCKS.PARAGRAPH;
333
+ }
334
+ }
335
+
336
+ return {
337
+ options: {
338
+ rules: rules
339
+ }
340
+ };
341
+ }
342
+ });
343
+ };
344
+
434
345
  function createDragAndDropPlugin() {
435
346
  var DRAGGABLE_TYPES = [Contentful.BLOCKS.EMBEDDED_ENTRY, Contentful.BLOCKS.EMBEDDED_ASSET, Contentful.BLOCKS.HR, Contentful.INLINES.EMBEDDED_ENTRY];
436
347
  /**
@@ -490,6 +401,18 @@ function getParents(el) {
490
401
  return parents;
491
402
  }
492
403
 
404
+ // "modern" Edge was released at 79.x
405
+ 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
406
+ // and older, Chrome 76+ can use `beforeInput` though.
407
+
408
+ 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
409
+ // Chrome Legacy doesn't support `beforeinput` correctly
410
+
411
+ var HAS_BEFORE_INPUT_SUPPORT = !IS_CHROME_LEGACY && !IS_EDGE_LEGACY && // globalThis is undefined in older browsers
412
+ typeof globalThis !== 'undefined' && globalThis.InputEvent && typeof globalThis.InputEvent.prototype.getTargetRanges === 'function'; // The `getTargetRanges` property isn't recognized.
413
+
414
+ var IS_SAFARI = typeof navigator !== 'undefined' && /*#__PURE__*/ /Version\/[\d.]+.*Safari/.test(navigator.userAgent);
415
+
493
416
  var LINK_TYPES = [Contentful.INLINES.HYPERLINK, Contentful.INLINES.ENTRY_HYPERLINK, Contentful.INLINES.ASSET_HYPERLINK];
494
417
  function isBlockSelected(editor, type) {
495
418
  var _Array$from = Array.from(slate.Editor.nodes(editor, {
@@ -504,10 +427,12 @@ function isBlockSelected(editor, type) {
504
427
  function isRootLevel(path) {
505
428
  return path.length === 1;
506
429
  }
507
- function getNodeEntryFromSelection(editor, nodeTypeOrTypes) {
508
- if (!editor.selection) return [];
430
+ function getNodeEntryFromSelection(editor, nodeTypeOrTypes, path) {
431
+ var _path, _editor$selection;
432
+
433
+ path = (_path = path) != null ? _path : (_editor$selection = editor.selection) == null ? void 0 : _editor$selection.focus.path;
434
+ if (!path) return [];
509
435
  var nodeTypes = Array.isArray(nodeTypeOrTypes) ? nodeTypeOrTypes : [nodeTypeOrTypes];
510
- var path = editor.selection.focus.path;
511
436
 
512
437
  for (var i = 0; i < path.length; i++) {
513
438
  var nodeEntry = slate.Editor.node(editor, path.slice(0, i + 1));
@@ -530,10 +455,32 @@ function moveToTheNextLine(editor) {
530
455
  unit: 'line'
531
456
  });
532
457
  }
458
+ function moveToTheNextChar(editor) {
459
+ slate.Transforms.move(editor, {
460
+ distance: 1,
461
+ unit: 'offset'
462
+ });
463
+ }
464
+ function insertEmptyParagraph(editor, options) {
465
+ var emptyParagraph = {
466
+ type: Contentful.BLOCKS.PARAGRAPH,
467
+ children: [{
468
+ text: ''
469
+ }],
470
+ data: {},
471
+ isVoid: false
472
+ };
473
+ slate.Transforms.insertNodes(editor, emptyParagraph, options);
474
+ }
533
475
  function getElementFromCurrentSelection(editor) {
534
476
  if (!editor.selection) return [];
535
477
  return Array.from(slate.Editor.nodes(editor, {
536
- at: editor.selection.focus,
478
+ /**
479
+ * editor.select is a Range, which includes anchor and focus, the beginning and the end of a selection
480
+ * when using only editor.selection.focus, we might get only the end of the selection, or where the text cursor is
481
+ * and in some cases getting the next element instead of the one we want
482
+ **/
483
+ at: editor.selection,
537
484
  match: function match(node) {
538
485
  return slate.Element.isElement(node);
539
486
  }
@@ -560,6 +507,10 @@ function insertLink(editor, options) {
560
507
  } // TODO: move to hyperlink plugin
561
508
 
562
509
  function isLinkActive(editor) {
510
+ if (!editor) {
511
+ return false;
512
+ }
513
+
563
514
  var _Array$from2 = Array.from(slate.Editor.nodes(editor, {
564
515
  match: function match(node) {
565
516
  return !slate.Editor.isEditor(node) && slate.Element.isElement(node) && LINK_TYPES.includes(node.type);
@@ -639,84 +590,41 @@ function getAncestorPathFromSelection(editor) {
639
590
  return level.length === 1;
640
591
  });
641
592
  }
642
- function shouldUnwrapBlockquote(editor, type) {
643
- var isQuoteSelected = isBlockSelected(editor, Contentful.BLOCKS.QUOTE);
644
- var isValidType = [].concat(Contentful.HEADINGS, [Contentful.BLOCKS.OL_LIST, Contentful.BLOCKS.UL_LIST, Contentful.BLOCKS.HR]).includes(type);
645
- return isQuoteSelected && isValidType;
646
- }
647
- function unwrapFromRoot(editor) {
648
- var ancestorPath = getAncestorPathFromSelection(editor);
649
- slate.Transforms.unwrapNodes(editor, {
650
- at: ancestorPath
651
- });
652
- }
653
- var isAtEndOfTextSelection = function isAtEndOfTextSelection(editor) {
654
- var _editor$selection, _editor$selection2;
655
-
656
- return ((_editor$selection = editor.selection) == null ? void 0 : _editor$selection.focus.offset) === plateCore.getText(editor, (_editor$selection2 = editor.selection) == null ? void 0 : _editor$selection2.focus.path).length;
657
- };
658
- function currentSelectionStartsTableCell(editor) {
659
- var _editor$selection3;
660
-
661
- var _getNodeEntryFromSele2 = getNodeEntryFromSelection(editor, [Contentful.BLOCKS.TABLE_CELL, Contentful.BLOCKS.TABLE_HEADER_CELL]),
662
- tableCellNode = _getNodeEntryFromSele2[0],
663
- path = _getNodeEntryFromSele2[1];
664
-
665
- return !!tableCellNode && (!plateCore.getText(editor, path) || ((_editor$selection3 = editor.selection) == null ? void 0 : _editor$selection3.focus.offset) === 0);
666
- }
667
- /**
668
- * This traversal strategy is unfortunately necessary because Slate doesn't
669
- * expose something like Node.next(editor).
670
- */
671
-
672
- function getNextNode(editor) {
673
- if (!editor.selection) {
674
- return null;
675
- }
676
-
677
- var descendants = slate.Node.descendants(editor, {
678
- from: editor.selection.focus.path
679
- }); // eslint-disable-next-line no-constant-condition
680
-
681
- while (true) {
682
- var _descendants$next = descendants.next(),
683
- done = _descendants$next.done,
684
- value = _descendants$next.value;
685
-
686
- if (done) {
687
- return null;
688
- }
689
-
690
- var node = value[0],
691
- path = value[1];
692
-
693
- if (slate.Path.isCommon(path, editor.selection.focus.path)) {
694
- continue;
695
- }
696
-
697
- return node;
698
- }
699
- } // TODO: move to table plugin
700
-
701
- function currentSelectionPrecedesTableCell(editor) {
702
- var nextNode = getNextNode(editor);
703
- return !!nextNode && Contentful.TABLE_BLOCKS.includes(nextNode.type) && isAtEndOfTextSelection(editor);
704
- }
705
593
  var INLINE_TYPES = /*#__PURE__*/Object.values(Contentful.INLINES);
706
594
  var isInlineOrText = function isInlineOrText(node) {
707
595
  // either text or inline elements
708
596
  return slate.Text.isText(node) || slate.Element.isElement(node) && INLINE_TYPES.includes(node.type);
709
597
  };
598
+ var focus = function focus(editor) {
599
+ var x = window.scrollX;
600
+ var y = window.scrollY;
601
+ Slate.ReactEditor.focus(editor); // Safari has issues with `editor.focus({ preventScroll: true })`, it ignores the option `preventScroll`
602
+
603
+ if (IS_SAFARI) {
604
+ setTimeout(function () {
605
+ window.scrollTo(x, y); // restore position
606
+ }, 0);
607
+ }
608
+ };
609
+ function toggleElement(editor, options, editorOptions) {
610
+ plateCore.toggleNodeType(editor, options, editorOptions); // We must reset `data` from one element to another
710
611
 
711
- // "modern" Edge was released at 79.x
712
- 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
713
- // and older, Chrome 76+ can use `beforeInput` though.
714
-
715
- 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
716
- // Chrome Legacy doesn't support `beforeinput` correctly
612
+ slate.Transforms.setNodes(editor, {
613
+ data: {}
614
+ });
615
+ }
717
616
 
718
- var HAS_BEFORE_INPUT_SUPPORT = !IS_CHROME_LEGACY && !IS_EDGE_LEGACY && // globalThis is undefined in older browsers
719
- typeof globalThis !== 'undefined' && globalThis.InputEvent && typeof globalThis.InputEvent.prototype.getTargetRanges === 'function'; // The `getTargetRanges` property isn't recognized.
617
+ function withLinkTracking(Component) {
618
+ return function ComponentWithTracking(props) {
619
+ var editor = useContentfulEditorRef();
620
+ var onEntityFetchComplete = React__default.useCallback(function () {
621
+ return editor.tracking.onViewportAction('linkRendered');
622
+ }, [editor]);
623
+ return /*#__PURE__*/React__default.createElement(Component, Object.assign({}, props, {
624
+ onEntityFetchComplete: onEntityFetchComplete
625
+ }));
626
+ };
627
+ }
720
628
 
721
629
  function useSdk(_ref) {
722
630
  var sdk = _ref.sdk;
@@ -727,9 +635,9 @@ function useSdk(_ref) {
727
635
  return sdkMemo;
728
636
  }
729
637
 
730
- var _constate$1 = /*#__PURE__*/constate(useSdk),
731
- SdkProvider = _constate$1[0],
732
- useSdkContext = _constate$1[1];
638
+ var _constate = /*#__PURE__*/constate(useSdk),
639
+ SdkProvider = _constate[0],
640
+ useSdkContext = _constate[1];
733
641
 
734
642
  var styles = {
735
643
  scheduleIcon: /*#__PURE__*/emotion.css({
@@ -824,7 +732,6 @@ function renderActions(props) {
824
732
  key: "section-title"
825
733
  }, "Actions"), onEdit ? /*#__PURE__*/React.createElement(f36Components.Menu.Item, {
826
734
  key: "edit",
827
- disabled: isDisabled,
828
735
  onClick: onEdit,
829
736
  testId: "card-action-edit"
830
737
  }, "Edit") : null, entityFile ? /*#__PURE__*/React.createElement(f36Components.Menu.Item, {
@@ -854,10 +761,19 @@ function FetchingWrappedAssetCard(props) {
854
761
  var asset = assets[props.assetId];
855
762
  var defaultLocaleCode = props.sdk.locales["default"];
856
763
  var entityFile = asset != null && (_asset$fields = asset.fields) != null && _asset$fields.file ? asset.fields.file[props.locale] || asset.fields.file[defaultLocaleCode] : undefined;
764
+ var onEntityFetchComplete = props.onEntityFetchComplete;
857
765
  React.useEffect(function () {
858
766
  getOrLoadAsset(props.assetId);
859
767
  }, [props.assetId]); // eslint-disable-line
860
768
 
769
+ React.useEffect(function () {
770
+ if (!asset) {
771
+ return;
772
+ }
773
+
774
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
775
+ }, [asset, onEntityFetchComplete]);
776
+
861
777
  function getAssetSrc() {
862
778
  if (!(entityFile != null && entityFile.url)) return '';
863
779
  return entityFile.url + "?h=300";
@@ -982,26 +898,42 @@ function FetchingWrappedEntryCard(props) {
982
898
  });
983
899
  }, [props.sdk, entry]);
984
900
  var defaultLocaleCode = props.sdk.locales["default"];
901
+ var onEntityFetchComplete = props.onEntityFetchComplete;
985
902
  React.useEffect(function () {
986
903
  if (!entry || entry === 'failed') return;
904
+ var subscribed = true;
987
905
  fieldEditorShared.entityHelpers.getEntryImage({
988
906
  entry: entry,
989
907
  contentType: contentType,
990
908
  localeCode: props.locale,
991
909
  defaultLocaleCode: defaultLocaleCode
992
- }, getOrLoadAsset).then(setFile)["catch"](function () {
993
- return setFile(null);
910
+ }, getOrLoadAsset)["catch"](function () {
911
+ return null;
912
+ }).then(function (file) {
913
+ if (subscribed) {
914
+ setFile(file);
915
+ }
994
916
  });
917
+ return function () {
918
+ subscribed = false;
919
+ };
995
920
  }, [entry, contentType, props.locale, defaultLocaleCode, props.sdk, file, getOrLoadAsset]);
996
921
  React.useEffect(function () {
997
922
  getOrLoadEntry(props.entryId);
998
923
  }, [props.entryId]); // eslint-disable-line
999
924
 
925
+ React.useEffect(function () {
926
+ if (!entry) {
927
+ return;
928
+ }
929
+
930
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
931
+ }, [entry, onEntityFetchComplete]);
932
+
1000
933
  function renderDropdown() {
1001
934
  if (!props.onEdit || !props.onRemove) return undefined;
1002
935
  return [props.onEdit ? /*#__PURE__*/React.createElement(f36Components.MenuItem, {
1003
936
  key: "edit",
1004
- disabled: props.isDisabled,
1005
937
  testId: "card-action-edit",
1006
938
  onClick: function onClick() {
1007
939
  props.onEdit && props.onEdit();
@@ -1078,13 +1010,22 @@ function FetchingWrappedEntryCard(props) {
1078
1010
 
1079
1011
  var styles$3 = {
1080
1012
  root: /*#__PURE__*/emotion.css({
1081
- marginBottom: '1.25rem'
1013
+ marginBottom: '1.25rem !important',
1014
+ display: 'block'
1015
+ }),
1016
+ container: /*#__PURE__*/emotion.css({
1017
+ // The next 2 properties ensure Entity card won't be aligned above
1018
+ // a list item marker (i.e. bullet)
1019
+ display: 'inline-block',
1020
+ verticalAlign: 'text-top',
1021
+ width: '100%'
1082
1022
  })
1083
1023
  };
1084
1024
  function LinkedEntityBlock(props) {
1085
1025
  var attributes = props.attributes,
1086
1026
  children = props.children,
1087
- element = props.element;
1027
+ element = props.element,
1028
+ onEntityFetchComplete = props.onEntityFetchComplete;
1088
1029
  var isSelected = Slate.useSelected();
1089
1030
  var editor = useContentfulEditor();
1090
1031
  var sdk = useSdkContext();
@@ -1118,7 +1059,8 @@ function LinkedEntityBlock(props) {
1118
1059
  }), /*#__PURE__*/React__default.createElement("div", {
1119
1060
  // COMPAT: This makes copy & paste work for Chromium/Blink browsers and Safari
1120
1061
  contentEditable: HAS_BEFORE_INPUT_SUPPORT ? false : undefined,
1121
- draggable: HAS_BEFORE_INPUT_SUPPORT ? true : undefined
1062
+ draggable: HAS_BEFORE_INPUT_SUPPORT ? true : undefined,
1063
+ className: styles$3.container
1122
1064
  }, entityType === 'Entry' && /*#__PURE__*/React__default.createElement(FetchingWrappedEntryCard, {
1123
1065
  sdk: sdk,
1124
1066
  entryId: entityId,
@@ -1126,7 +1068,8 @@ function LinkedEntityBlock(props) {
1126
1068
  isDisabled: isDisabled,
1127
1069
  isSelected: isSelected,
1128
1070
  onRemove: handleRemoveClick,
1129
- onEdit: handleEditClick
1071
+ onEdit: handleEditClick,
1072
+ onEntityFetchComplete: onEntityFetchComplete
1130
1073
  }), entityType === 'Asset' && /*#__PURE__*/React__default.createElement(FetchingWrappedAssetCard, {
1131
1074
  sdk: sdk,
1132
1075
  assetId: entityId,
@@ -1134,7 +1077,8 @@ function LinkedEntityBlock(props) {
1134
1077
  isDisabled: isDisabled,
1135
1078
  isSelected: isSelected,
1136
1079
  onRemove: handleRemoveClick,
1137
- onEdit: handleEditClick
1080
+ onEdit: handleEditClick,
1081
+ onEntityFetchComplete: onEntityFetchComplete
1138
1082
  })), children);
1139
1083
  }
1140
1084
 
@@ -1235,11 +1179,9 @@ var runtime_1 = /*#__PURE__*/createCommonjsModule(function (module) {
1235
1179
 
1236
1180
 
1237
1181
  var IteratorPrototype = {};
1238
-
1239
- IteratorPrototype[iteratorSymbol] = function () {
1182
+ define(IteratorPrototype, iteratorSymbol, function () {
1240
1183
  return this;
1241
- };
1242
-
1184
+ });
1243
1185
  var getProto = Object.getPrototypeOf;
1244
1186
  var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
1245
1187
 
@@ -1250,8 +1192,9 @@ var runtime_1 = /*#__PURE__*/createCommonjsModule(function (module) {
1250
1192
  }
1251
1193
 
1252
1194
  var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
1253
- GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
1254
- GeneratorFunctionPrototype.constructor = GeneratorFunction;
1195
+ GeneratorFunction.prototype = GeneratorFunctionPrototype;
1196
+ define(Gp, "constructor", GeneratorFunctionPrototype);
1197
+ define(GeneratorFunctionPrototype, "constructor", GeneratorFunction);
1255
1198
  GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the
1256
1199
  // Iterator interface in terms of a single ._invoke method.
1257
1200
 
@@ -1356,11 +1299,9 @@ var runtime_1 = /*#__PURE__*/createCommonjsModule(function (module) {
1356
1299
  }
1357
1300
 
1358
1301
  defineIteratorMethods(AsyncIterator.prototype);
1359
-
1360
- AsyncIterator.prototype[asyncIteratorSymbol] = function () {
1302
+ define(AsyncIterator.prototype, asyncIteratorSymbol, function () {
1361
1303
  return this;
1362
- };
1363
-
1304
+ });
1364
1305
  exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of
1365
1306
  // AsyncIterator objects; they just return a Promise for the value of
1366
1307
  // the final result produced by the iterator.
@@ -1537,13 +1478,12 @@ var runtime_1 = /*#__PURE__*/createCommonjsModule(function (module) {
1537
1478
  // object to not be returned from this call. This ensures that doesn't happen.
1538
1479
  // See https://github.com/facebook/regenerator/issues/274 for more details.
1539
1480
 
1540
- Gp[iteratorSymbol] = function () {
1481
+ define(Gp, iteratorSymbol, function () {
1541
1482
  return this;
1542
- };
1543
-
1544
- Gp.toString = function () {
1483
+ });
1484
+ define(Gp, "toString", function () {
1545
1485
  return "[object Generator]";
1546
- };
1486
+ });
1547
1487
 
1548
1488
  function pushTryEntry(locs) {
1549
1489
  var entry = {
@@ -1855,14 +1795,19 @@ var runtime_1 = /*#__PURE__*/createCommonjsModule(function (module) {
1855
1795
  } catch (accidentalStrictMode) {
1856
1796
  // This module should not be running in strict mode, so the above
1857
1797
  // assignment should always work unless something is misconfigured. Just
1858
- // in case runtime.js accidentally runs in strict mode, we can escape
1798
+ // in case runtime.js accidentally runs in strict mode, in modern engines
1799
+ // we can explicitly access globalThis. In older engines we can escape
1859
1800
  // strict mode using a global Function call. This could conceivably fail
1860
1801
  // if a Content Security Policy forbids using Function, but in that case
1861
1802
  // the proper solution is to fix the accidental strict mode problem. If
1862
1803
  // you've misconfigured your bundler to force strict mode and applied a
1863
1804
  // CSP to forbid Function, and you're not willing to fix either of those
1864
1805
  // problems, please detail your unique predicament in a GitHub issue.
1865
- Function("r", "regeneratorRuntime = r")(runtime);
1806
+ if (typeof globalThis === "object") {
1807
+ globalThis.regeneratorRuntime = runtime;
1808
+ } else {
1809
+ Function("r", "regeneratorRuntime = r")(runtime);
1810
+ }
1866
1811
  }
1867
1812
  });
1868
1813
 
@@ -1957,12 +1902,11 @@ function _selectEntityAndInsert() {
1957
1902
  config = _extends({}, baseConfig, {
1958
1903
  withCreate: true
1959
1904
  });
1960
- _context.prev = 5;
1961
1905
  selection = editor.selection;
1962
- _context.next = 9;
1906
+ _context.next = 8;
1963
1907
  return selectEntity(config);
1964
1908
 
1965
- case 9:
1909
+ case 8:
1966
1910
  entity = _context.sent;
1967
1911
 
1968
1912
  if (entity) {
@@ -1970,43 +1914,66 @@ function _selectEntityAndInsert() {
1970
1914
  break;
1971
1915
  }
1972
1916
 
1917
+ logAction('cancelCreateEmbedDialog', {
1918
+ nodeType: nodeType
1919
+ });
1973
1920
  return _context.abrupt("return");
1974
1921
 
1975
1922
  case 12:
1976
1923
  slate.Transforms.select(editor, selection);
1977
1924
  insertBlock(editor, nodeType, entity);
1925
+ ensureFollowingParagraph(editor);
1978
1926
  logAction('insert', {
1979
1927
  nodeType: nodeType
1980
1928
  });
1981
- _context.next = 24;
1982
- break;
1983
-
1984
- case 17:
1985
- _context.prev = 17;
1986
- _context.t0 = _context["catch"](5);
1987
-
1988
- if (!_context.t0) {
1989
- _context.next = 23;
1990
- break;
1991
- }
1992
-
1993
- throw _context.t0;
1994
-
1995
- case 23:
1996
- logAction('cancelCreateEmbedDialog', {
1997
- nodeType: nodeType
1998
- });
1999
1929
 
2000
- case 24:
1930
+ case 16:
2001
1931
  case "end":
2002
1932
  return _context.stop();
2003
1933
  }
2004
1934
  }
2005
- }, _callee, null, [[5, 17]]);
1935
+ }, _callee);
2006
1936
  }));
2007
1937
  return _selectEntityAndInsert.apply(this, arguments);
2008
1938
  }
2009
1939
 
1940
+ function ensureFollowingParagraph(editor) {
1941
+ /*
1942
+ If the new block isn't followed by a sibling paragraph we insert a new empty one
1943
+ */
1944
+ var next = slate.Editor.next(editor);
1945
+
1946
+ if (!next) {
1947
+ return insertEmptyParagraph(editor);
1948
+ }
1949
+
1950
+ var parent = slate.Editor.above(editor, {
1951
+ voids: false,
1952
+ match: function match(e) {
1953
+ return !slate.Element.isElement(e) || ![Contentful.BLOCKS.EMBEDDED_ASSET, Contentful.BLOCKS.EMBEDDED_ENTRY].includes(e.type);
1954
+ }
1955
+ });
1956
+
1957
+ if (slate.Editor.isEditor(parent)) {
1958
+ // at level 0, a following paragraph is handled by the tralingParagraph plugin
1959
+ moveToTheNextChar(editor);
1960
+ return;
1961
+ }
1962
+
1963
+ var paragraph = slate.Editor.above(editor, {
1964
+ at: next[1],
1965
+ match: function match(e) {
1966
+ return slate.Element.isElement(e) && Contentful.TEXT_CONTAINERS.includes(e.type);
1967
+ }
1968
+ });
1969
+
1970
+ if (!paragraph || !parent || !slate.Path.isChild(paragraph[1], parent[1])) {
1971
+ return insertEmptyParagraph(editor);
1972
+ }
1973
+
1974
+ moveToTheNextChar(editor);
1975
+ }
1976
+
2010
1977
  var createNode = function createNode(nodeType, entity) {
2011
1978
  return {
2012
1979
  type: nodeType,
@@ -2038,7 +2005,7 @@ function insertBlock(editor, nodeType, entity) {
2038
2005
  slate.Transforms.setNodes(editor, linkedEntityBlock);
2039
2006
  }
2040
2007
 
2041
- Slate.ReactEditor.focus(editor);
2008
+ focus(editor);
2042
2009
  }
2043
2010
 
2044
2011
  var styles$4 = {
@@ -2047,31 +2014,39 @@ var styles$4 = {
2047
2014
  })
2048
2015
  };
2049
2016
  function EmbeddedEntityBlockToolbarIcon(_ref) {
2050
- var isButton = _ref.isButton,
2051
- isDisabled = _ref.isDisabled,
2052
- logAction = _ref.logAction,
2017
+ var isDisabled = _ref.isDisabled,
2053
2018
  nodeType = _ref.nodeType,
2054
2019
  onClose = _ref.onClose;
2055
2020
  var editor = useContentfulEditor();
2056
2021
  var sdk = useSdkContext();
2057
2022
 
2058
- var handleClick = function handleClick(event) {
2059
- event.preventDefault();
2060
- onClose();
2061
- selectEntityAndInsert(nodeType, sdk, editor, logAction || noop);
2062
- };
2023
+ var handleClick = /*#__PURE__*/function () {
2024
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee(event) {
2025
+ return runtime_1.wrap(function _callee$(_context) {
2026
+ while (1) {
2027
+ switch (_context.prev = _context.next) {
2028
+ case 0:
2029
+ event.preventDefault();
2030
+ onClose();
2031
+ _context.next = 4;
2032
+ return selectEntityAndInsert(nodeType, sdk, editor, editor.tracking.onToolbarAction);
2033
+
2034
+ case 4:
2035
+ case "end":
2036
+ return _context.stop();
2037
+ }
2038
+ }
2039
+ }, _callee);
2040
+ }));
2041
+
2042
+ return function handleClick(_x) {
2043
+ return _ref2.apply(this, arguments);
2044
+ };
2045
+ }();
2063
2046
 
2064
2047
  var type = getEntityTypeFromNodeType(nodeType);
2065
2048
  var baseClass = "rich-text__" + nodeType;
2066
- return isButton ? /*#__PURE__*/React__default.createElement(f36Components.Button, {
2067
- isDisabled: isDisabled,
2068
- className: baseClass + "-button",
2069
- size: "small",
2070
- onClick: handleClick,
2071
- startIcon: type === 'Asset' ? /*#__PURE__*/React__default.createElement(f36Icons.AssetIcon, null) : /*#__PURE__*/React__default.createElement(f36Icons.EmbeddedEntryBlockIcon, null),
2072
- variant: "secondary",
2073
- testId: "toolbar-toggle-" + nodeType
2074
- }, "Embed " + type.toLowerCase()) : /*#__PURE__*/React__default.createElement(f36Components.Menu.Item, {
2049
+ return /*#__PURE__*/React__default.createElement(f36Components.Menu.Item, {
2075
2050
  disabled: isDisabled,
2076
2051
  className: baseClass + "-list-item",
2077
2052
  onClick: handleClick,
@@ -2125,7 +2100,7 @@ function getWithEmbeddedEntityEvents(nodeType, sdk) {
2125
2100
  }
2126
2101
 
2127
2102
  if (hotkey && isHotkey(hotkey, event)) {
2128
- selectEntityAndInsert(nodeType, sdk, editor, noop);
2103
+ selectEntityAndInsert(nodeType, sdk, editor, editor.tracking.onShortcutAction);
2129
2104
  }
2130
2105
  };
2131
2106
  };
@@ -2138,7 +2113,7 @@ var createEmbeddedEntityPlugin = function createEmbeddedEntityPlugin(nodeType, h
2138
2113
  type: nodeType,
2139
2114
  isElement: true,
2140
2115
  isVoid: true,
2141
- component: LinkedEntityBlock,
2116
+ component: withLinkTracking(LinkedEntityBlock),
2142
2117
  options: {
2143
2118
  hotkey: hotkey
2144
2119
  },
@@ -2196,12 +2171,20 @@ function FetchingWrappedInlineEntryCard(props) {
2196
2171
  return entries[props.entryId];
2197
2172
  }, [entries, props.entryId]);
2198
2173
  var allContentTypes = props.sdk.space.getCachedContentTypes();
2174
+ var onEntityFetchComplete = props.onEntityFetchComplete;
2199
2175
  var contentType = React__default.useMemo(function () {
2200
2176
  if (!entry || entry === 'failed' || !allContentTypes) return undefined;
2201
2177
  return allContentTypes.find(function (contentType) {
2202
2178
  return contentType.sys.id === entry.sys.contentType.sys.id;
2203
2179
  });
2204
2180
  }, [allContentTypes, entry]);
2181
+ React__default.useEffect(function () {
2182
+ if (!entry) {
2183
+ return;
2184
+ }
2185
+
2186
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
2187
+ }, [entry, onEntityFetchComplete]);
2205
2188
  var contentTypeName = contentType ? contentType.name : '';
2206
2189
  var title = React__default.useMemo(function () {
2207
2190
  return getEntryTitle({
@@ -2251,27 +2234,26 @@ function FetchingWrappedInlineEntryCard(props) {
2251
2234
  return /*#__PURE__*/React__default.createElement(f36Components.InlineEntryCard, {
2252
2235
  testId: Contentful.INLINES.EMBEDDED_ENTRY,
2253
2236
  isSelected: props.isSelected,
2254
- title: /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", null, contentTypeName, " ", title), /*#__PURE__*/React__default.createElement(fieldEditorReference.ScheduledIconWithTooltip, {
2255
- getEntityScheduledActions: loadEntityScheduledActions,
2256
- entityType: "Entry",
2257
- entityId: entry.sys.id
2258
- }, /*#__PURE__*/React__default.createElement(f36Icons.ClockIcon, {
2259
- className: styles$5.scheduledIcon,
2260
- variant: "muted",
2261
- testId: "scheduled-icon"
2262
- }))),
2237
+ title: contentTypeName + ": " + title,
2263
2238
  status: status,
2264
2239
  actions: [/*#__PURE__*/React__default.createElement(f36Components.MenuItem, {
2265
2240
  key: "edit",
2266
- onClick: props.onEdit,
2267
- disabled: props.isDisabled
2241
+ onClick: props.onEdit
2268
2242
  }, "Edit"), /*#__PURE__*/React__default.createElement(f36Components.MenuItem, {
2269
2243
  key: "remove",
2270
2244
  onClick: props.onRemove,
2271
2245
  disabled: props.isDisabled,
2272
2246
  testId: "card-action-remove"
2273
2247
  }, "Remove")]
2274
- });
2248
+ }, /*#__PURE__*/React__default.createElement(fieldEditorReference.ScheduledIconWithTooltip, {
2249
+ getEntityScheduledActions: loadEntityScheduledActions,
2250
+ entityType: "Entry",
2251
+ entityId: entry.sys.id
2252
+ }, /*#__PURE__*/React__default.createElement(f36Icons.ClockIcon, {
2253
+ className: styles$5.scheduledIcon,
2254
+ variant: "muted",
2255
+ testId: "scheduled-icon"
2256
+ })), /*#__PURE__*/React__default.createElement(f36Components.Text, null, title));
2275
2257
  }
2276
2258
 
2277
2259
  function createInlineEntryNode(id) {
@@ -2297,12 +2279,10 @@ var styles$6 = {
2297
2279
  marginRight: '10px'
2298
2280
  }),
2299
2281
  root: /*#__PURE__*/emotion.css({
2300
- margin: '0 1px',
2282
+ display: 'inline-block',
2283
+ margin: "0 " + tokens.spacing2Xs,
2301
2284
  fontSize: 'inherit',
2302
2285
  span: {
2303
- webkitUserSelect: 'none',
2304
- mozUserSelect: 'none',
2305
- msUserSelect: 'none',
2306
2286
  userSelect: 'none'
2307
2287
  }
2308
2288
  })
@@ -2345,48 +2325,61 @@ function EmbeddedEntityInline(props) {
2345
2325
  isSelected: isSelected,
2346
2326
  isDisabled: isDisabled,
2347
2327
  onRemove: handleRemoveClick,
2348
- onEdit: handleEditClick
2328
+ onEdit: handleEditClick,
2329
+ onEntityFetchComplete: props.onEntityFetchComplete
2349
2330
  })), props.children);
2350
2331
  }
2351
2332
 
2352
- function selectEntityAndInsert$1(_x, _x2) {
2333
+ function selectEntityAndInsert$1(_x, _x2, _x3) {
2353
2334
  return _selectEntityAndInsert$1.apply(this, arguments);
2354
2335
  }
2355
2336
 
2356
2337
  function _selectEntityAndInsert$1() {
2357
- _selectEntityAndInsert$1 = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee2(editor, sdk) {
2338
+ _selectEntityAndInsert$1 = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee2(editor, sdk, logAction) {
2358
2339
  var config, selection, entry, inlineEntryNode;
2359
2340
  return runtime_1.wrap(function _callee2$(_context2) {
2360
2341
  while (1) {
2361
2342
  switch (_context2.prev = _context2.next) {
2362
2343
  case 0:
2344
+ logAction('openCreateEmbedDialog', {
2345
+ nodeType: Contentful.INLINES.EMBEDDED_ENTRY
2346
+ });
2363
2347
  config = _extends({}, newEntitySelectorConfigFromRichTextField(sdk.field, Contentful.INLINES.EMBEDDED_ENTRY), {
2364
2348
  withCreate: true
2365
2349
  });
2366
2350
  selection = editor.selection;
2367
- _context2.next = 4;
2351
+ _context2.next = 5;
2368
2352
  return sdk.dialogs.selectSingleEntry(config);
2369
2353
 
2370
- case 4:
2354
+ case 5:
2371
2355
  entry = _context2.sent;
2372
- Slate.ReactEditor.focus(editor); // Dialog steals focus from editor, return it.
2356
+ focus(editor); // Dialog steals focus from editor, return it.
2373
2357
 
2374
2358
  if (entry) {
2375
- _context2.next = 8;
2359
+ _context2.next = 10;
2376
2360
  break;
2377
2361
  }
2378
2362
 
2363
+ logAction('cancelCreateEmbedDialog', {
2364
+ nodeType: Contentful.INLINES.EMBEDDED_ENTRY
2365
+ });
2379
2366
  return _context2.abrupt("return");
2380
2367
 
2381
- case 8:
2382
- inlineEntryNode = createInlineEntryNode(entry.sys.id); // Got to wait until focus is really back on the editor or setSelection() won't work.
2383
-
2384
- setTimeout(function () {
2385
- slate.Transforms.setSelection(editor, selection);
2386
- slate.Transforms.insertNodes(editor, inlineEntryNode);
2387
- }, 0);
2388
-
2389
2368
  case 10:
2369
+ inlineEntryNode = createInlineEntryNode(entry.sys.id);
2370
+ logAction('insert', {
2371
+ nodeType: Contentful.INLINES.EMBEDDED_ENTRY
2372
+ }); // Got to wait until focus is really back on the editor or setSelection() won't work.
2373
+
2374
+ return _context2.abrupt("return", new Promise(function (resolve) {
2375
+ setTimeout(function () {
2376
+ slate.Transforms.setSelection(editor, selection);
2377
+ slate.Transforms.insertNodes(editor, inlineEntryNode);
2378
+ resolve();
2379
+ }, 0);
2380
+ }));
2381
+
2382
+ case 13:
2390
2383
  case "end":
2391
2384
  return _context2.stop();
2392
2385
  }
@@ -2400,7 +2393,7 @@ function ToolbarEmbeddedEntityInlineButton(props) {
2400
2393
  var editor = useContentfulEditor();
2401
2394
  var sdk = useSdkContext();
2402
2395
 
2403
- function handleClick(_x3) {
2396
+ function handleClick(_x4) {
2404
2397
  return _handleClick.apply(this, arguments);
2405
2398
  }
2406
2399
 
@@ -2422,9 +2415,12 @@ function ToolbarEmbeddedEntityInlineButton(props) {
2422
2415
  case 3:
2423
2416
  props.onClose();
2424
2417
  _context.next = 6;
2425
- return selectEntityAndInsert$1(editor, sdk);
2418
+ return selectEntityAndInsert$1(editor, sdk, editor.tracking.onToolbarAction);
2426
2419
 
2427
2420
  case 6:
2421
+ moveToTheNextChar(editor);
2422
+
2423
+ case 7:
2428
2424
  case "end":
2429
2425
  return _context.stop();
2430
2426
  }
@@ -2434,15 +2430,7 @@ function ToolbarEmbeddedEntityInlineButton(props) {
2434
2430
  return _handleClick.apply(this, arguments);
2435
2431
  }
2436
2432
 
2437
- return props.isButton ? /*#__PURE__*/React.createElement(f36Components.Button, {
2438
- isDisabled: props.isDisabled,
2439
- className: Contentful.INLINES.EMBEDDED_ENTRY + "-button",
2440
- size: "small",
2441
- onClick: handleClick,
2442
- startIcon: /*#__PURE__*/React.createElement(f36Icons.EmbeddedEntryInlineIcon, null),
2443
- variant: "secondary",
2444
- testId: "toolbar-toggle-" + Contentful.INLINES.EMBEDDED_ENTRY
2445
- }, "Embed inline entry") : /*#__PURE__*/React.createElement(f36Components.Menu.Item, {
2433
+ return /*#__PURE__*/React.createElement(f36Components.Menu.Item, {
2446
2434
  disabled: props.isDisabled,
2447
2435
  className: "rich-text__entry-link-block-button",
2448
2436
  testId: "toolbar-toggle-" + Contentful.INLINES.EMBEDDED_ENTRY,
@@ -2463,7 +2451,7 @@ function createEmbeddedEntityInlinePlugin(sdk) {
2463
2451
  isElement: true,
2464
2452
  isInline: true,
2465
2453
  isVoid: true,
2466
- component: EmbeddedEntityInline,
2454
+ component: withLinkTracking(EmbeddedEntityInline),
2467
2455
  options: {
2468
2456
  hotkey: 'mod+shift+2'
2469
2457
  },
@@ -2489,7 +2477,7 @@ function getWithEmbeddedEntryInlineEvents(sdk) {
2489
2477
  if (!editor) return;
2490
2478
 
2491
2479
  if (hotkey && isHotkey(hotkey, event)) {
2492
- selectEntityAndInsert$1(editor, sdk);
2480
+ selectEntityAndInsert$1(editor, sdk, editor.tracking.onShortcutAction);
2493
2481
  }
2494
2482
  };
2495
2483
  };
@@ -2572,19 +2560,29 @@ function ToolbarHeadingButton(props) {
2572
2560
  if (!(editor != null && editor.selection)) return;
2573
2561
  setSelected(type);
2574
2562
  setOpen(false);
2563
+ var prevOnChange = editor.onChange;
2564
+ /*
2565
+ The focus might happen at point in time when
2566
+ `toggleElement` (helper for toggleNodeType) changes aren't rendered yet, causing the browser
2567
+ to place the cursor at the start of the text.
2568
+ We wait for the change event before focusing
2569
+ the editor again. This ensures the cursor is back at the previous
2570
+ position.*/
2571
+
2572
+ editor.onChange = function () {
2573
+ focus(editor);
2574
+ editor.onChange = prevOnChange;
2575
+ prevOnChange.apply(void 0, arguments);
2576
+ };
2575
2577
 
2576
- if (shouldUnwrapBlockquote(editor, type)) {
2577
- unwrapFromRoot(editor);
2578
- }
2579
-
2580
- plateCore.toggleNodeType(editor, {
2578
+ var isActive = isBlockSelected(editor, type);
2579
+ editor.tracking.onToolbarAction(isActive ? 'remove' : 'insert', {
2580
+ nodeType: type
2581
+ });
2582
+ toggleElement(editor, {
2581
2583
  activeType: type,
2582
2584
  inactiveType: type
2583
- }); // TODO: Figure out why focus only works with timeout here.
2584
-
2585
- setTimeout(function () {
2586
- Slate.ReactEditor.focus(editor);
2587
- }, 0);
2585
+ });
2588
2586
  };
2589
2587
  }
2590
2588
 
@@ -2622,7 +2620,7 @@ function extractNodes(editor, path, match) {
2622
2620
  return Array.from(plateCore.getNodes(editor, {
2623
2621
  match: match,
2624
2622
  at: path,
2625
- mode: 'all'
2623
+ mode: 'lowest'
2626
2624
  })).map(function (_ref) {
2627
2625
  var node = _ref[0];
2628
2626
  return node;
@@ -2702,6 +2700,24 @@ function createHeading(Tag, block) {
2702
2700
 
2703
2701
  var HeadingComponents = (_HeadingComponents = {}, _HeadingComponents[Contentful.BLOCKS.HEADING_1] = /*#__PURE__*/React.memo( /*#__PURE__*/createHeading('h1', Contentful.BLOCKS.HEADING_1)), _HeadingComponents[Contentful.BLOCKS.HEADING_2] = /*#__PURE__*/React.memo( /*#__PURE__*/createHeading('h2', Contentful.BLOCKS.HEADING_2)), _HeadingComponents[Contentful.BLOCKS.HEADING_3] = /*#__PURE__*/React.memo( /*#__PURE__*/createHeading('h3', Contentful.BLOCKS.HEADING_3)), _HeadingComponents[Contentful.BLOCKS.HEADING_4] = /*#__PURE__*/React.memo( /*#__PURE__*/createHeading('h4', Contentful.BLOCKS.HEADING_4)), _HeadingComponents[Contentful.BLOCKS.HEADING_5] = /*#__PURE__*/React.memo( /*#__PURE__*/createHeading('h5', Contentful.BLOCKS.HEADING_5)), _HeadingComponents[Contentful.BLOCKS.HEADING_6] = /*#__PURE__*/React.memo( /*#__PURE__*/createHeading('h6', Contentful.BLOCKS.HEADING_6)), _HeadingComponents);
2704
2702
 
2703
+ var buildHeadingEventHandler = function buildHeadingEventHandler(type) {
2704
+ return function (editor, _ref) {
2705
+ var hotkey = _ref.options.hotkey;
2706
+ return function (event) {
2707
+ if (editor.selection && hotkey && isHotkey(hotkey, event)) {
2708
+ var isActive = isBlockSelected(editor, type);
2709
+ editor.tracking.onShortcutAction(isActive ? 'remove' : 'insert', {
2710
+ nodeType: type
2711
+ });
2712
+ toggleElement(editor, {
2713
+ activeType: type,
2714
+ inactiveType: Contentful.BLOCKS.PARAGRAPH
2715
+ });
2716
+ }
2717
+ };
2718
+ };
2719
+ };
2720
+
2705
2721
  var createHeadingPlugin = function createHeadingPlugin() {
2706
2722
  var _transform;
2707
2723
 
@@ -2714,26 +2730,41 @@ var createHeadingPlugin = function createHeadingPlugin() {
2714
2730
  allow: Contentful.HEADINGS
2715
2731
  }
2716
2732
  }],
2717
- exitBreak: [// Pressing ENTER at the start or end of a heading text inserts a
2718
- // normal paragraph
2719
- {
2720
- hotkey: 'enter',
2721
- query: {
2722
- allow: Contentful.HEADINGS,
2723
- end: true,
2724
- start: true
2725
- }
2726
- }],
2727
2733
  normalizer: [{
2728
2734
  match: {
2729
2735
  type: Contentful.HEADINGS
2730
2736
  },
2731
- validChildren: function validChildren(_, _ref) {
2732
- var node = _ref[0];
2737
+ validChildren: function validChildren(_, _ref2) {
2738
+ var node = _ref2[0];
2733
2739
  return isInlineOrText(node);
2734
2740
  },
2735
2741
  transform: (_transform = {}, _transform[Contentful.BLOCKS.PARAGRAPH] = transformUnwrap, _transform["default"] = transformLift, _transform)
2736
2742
  }],
2743
+ then: function then(editor) {
2744
+ return {
2745
+ exitBreak: [// Pressing ENTER at the start or end of a heading text inserts a
2746
+ // normal paragraph.
2747
+ {
2748
+ hotkey: 'enter',
2749
+ query: {
2750
+ allow: Contentful.HEADINGS,
2751
+ end: true,
2752
+ start: true,
2753
+ // Exclude headings inside lists as it interferes with the list's
2754
+ // insertBreak implementation
2755
+ filter: function filter(_ref3) {
2756
+ var path = _ref3[1];
2757
+ return !plateCore.getAbove(editor, {
2758
+ at: path,
2759
+ match: {
2760
+ type: Contentful.BLOCKS.LIST_ITEM
2761
+ }
2762
+ });
2763
+ }
2764
+ }
2765
+ }]
2766
+ };
2767
+ },
2737
2768
  plugins: Contentful.HEADINGS.map(function (nodeType, idx) {
2738
2769
  var level = idx + 1;
2739
2770
  var tagName = "h" + level;
@@ -2746,7 +2777,7 @@ var createHeadingPlugin = function createHeadingPlugin() {
2746
2777
  hotkey: ["mod+alt+" + level]
2747
2778
  },
2748
2779
  handlers: {
2749
- onKeyDown: plateCore.onKeyDownToggleElement
2780
+ onKeyDown: buildHeadingEventHandler(nodeType)
2750
2781
  },
2751
2782
  deserializeHtml: {
2752
2783
  rules: [{
@@ -2818,10 +2849,6 @@ function withHrEvents(editor) {
2818
2849
  pathToSelectedHr = _getNodeEntryFromSele[1];
2819
2850
 
2820
2851
  if (pathToSelectedHr) {
2821
- if (shouldUnwrapBlockquote(editor, Contentful.BLOCKS.HR)) {
2822
- unwrapFromRoot(editor);
2823
- }
2824
-
2825
2852
  var isBackspace = event.key === 'Backspace';
2826
2853
  var isDelete = event.key === 'Delete';
2827
2854
 
@@ -2839,11 +2866,6 @@ function ToolbarHrButton(props) {
2839
2866
 
2840
2867
  function handleOnClick() {
2841
2868
  if (!(editor != null && editor.selection)) return;
2842
-
2843
- if (shouldUnwrapBlockquote(editor, Contentful.BLOCKS.HR)) {
2844
- unwrapFromRoot(editor);
2845
- }
2846
-
2847
2869
  var hr = {
2848
2870
  type: Contentful.BLOCKS.HR,
2849
2871
  data: {},
@@ -2856,7 +2878,7 @@ function ToolbarHrButton(props) {
2856
2878
  hasText ? slate.Transforms.insertNodes(editor, hr) : plateCore.setNodes(editor, hr); // Move focus to the next paragraph (added by TrailingParagraph plugin)
2857
2879
 
2858
2880
  moveToTheNextLine(editor);
2859
- Slate.ReactEditor.focus(editor);
2881
+ focus(editor);
2860
2882
  }
2861
2883
 
2862
2884
  if (!editor) return null;
@@ -2905,159 +2927,6 @@ var createHrPlugin = function createHrPlugin() {
2905
2927
  };
2906
2928
  };
2907
2929
 
2908
- function truncate(str, length) {
2909
- if (typeof str === 'string' && str.length > length) {
2910
- return str && str.substr(0, length + 1) // +1 to look ahead and be replaced below.
2911
- // Get rid of orphan letters but not one letter words (I, a, 2).
2912
- // Try to not have “.” as last character to avoid awkward “....”.
2913
- .replace(/(\s+\S(?=\S)|\s*)\.?.$/, '…');
2914
- }
2915
-
2916
- return str;
2917
- }
2918
-
2919
- function getEntityInfo(_ref) {
2920
- var entityTitle = _ref.entityTitle,
2921
- entityStatus = _ref.entityStatus,
2922
- contentTypeName = _ref.contentTypeName;
2923
- var title = truncate(entityTitle, 60) || 'Untitled';
2924
- return (contentTypeName || 'Asset') + " \"" + title + "\", " + entityStatus;
2925
- }
2926
-
2927
- function EntryAssetTooltip(_ref2) {
2928
- var id = _ref2.id,
2929
- type = _ref2.type,
2930
- sdk = _ref2.sdk;
2931
-
2932
- var _React$useState = React.useState(''),
2933
- entityTitle = _React$useState[0],
2934
- setEntityTitle = _React$useState[1];
2935
-
2936
- var _React$useState2 = React.useState(''),
2937
- entityStatus = _React$useState2[0],
2938
- setEntityStatus = _React$useState2[1];
2939
-
2940
- var _React$useState3 = React.useState([]),
2941
- jobs = _React$useState3[0],
2942
- setJobs = _React$useState3[1];
2943
-
2944
- var _React$useState4 = React.useState(true),
2945
- isLoading = _React$useState4[0],
2946
- setIsLoading = _React$useState4[1];
2947
-
2948
- var _React$useState5 = React.useState(false),
2949
- hasError = _React$useState5[0],
2950
- setHasError = _React$useState5[1];
2951
-
2952
- React.useEffect(function () {
2953
- function loadContent() {
2954
- return _loadContent.apply(this, arguments);
2955
- }
2956
-
2957
- function _loadContent() {
2958
- _loadContent = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee() {
2959
- var actions, contentType, entity, contentTypeId, contentTypes, _entityTitle, _jobs, _entityStatus;
2960
-
2961
- return runtime_1.wrap(function _callee$(_context) {
2962
- while (1) {
2963
- switch (_context.prev = _context.next) {
2964
- case 0:
2965
- _context.prev = 0;
2966
- setIsLoading(true);
2967
- setHasError(false);
2968
- actions = {
2969
- Asset: {
2970
- getEntity: sdk.space.getAsset,
2971
- getEntityTitle: function getEntityTitle(args) {
2972
- return fieldEditorShared.entityHelpers.getAssetTitle(_extends({}, args, {
2973
- asset: args.entity
2974
- }));
2975
- }
2976
- },
2977
- Entry: {
2978
- getEntity: sdk.space.getEntry,
2979
- getEntityTitle: function getEntityTitle(args) {
2980
- return fieldEditorShared.entityHelpers.getEntryTitle(_extends({}, args, {
2981
- entry: args.entity
2982
- }));
2983
- }
2984
- }
2985
- };
2986
- _context.next = 6;
2987
- return actions[type].getEntity(id);
2988
-
2989
- case 6:
2990
- entity = _context.sent;
2991
-
2992
- if (entity.sys.contentType) {
2993
- contentTypeId = entity.sys.contentType.sys.id;
2994
- contentTypes = sdk.space.getCachedContentTypes();
2995
- contentType = contentTypes.find(function (ct) {
2996
- return ct.sys.id === contentTypeId;
2997
- });
2998
- }
2999
-
3000
- _entityTitle = actions[type].getEntityTitle({
3001
- entity: entity,
3002
- contentType: contentType,
3003
- localeCode: sdk.field.locale,
3004
- defaultLocaleCode: sdk.locales["default"],
3005
- entityType: type
3006
- });
3007
- _context.next = 11;
3008
- return sdk.space.getEntityScheduledActions(type, id);
3009
-
3010
- case 11:
3011
- _jobs = _context.sent;
3012
- _entityStatus = fieldEditorShared.entityHelpers.getEntryStatus(entity.sys);
3013
- setEntityTitle(_entityTitle);
3014
- setEntityStatus(_entityStatus);
3015
- setJobs(_jobs);
3016
- _context.next = 22;
3017
- break;
3018
-
3019
- case 18:
3020
- _context.prev = 18;
3021
- _context.t0 = _context["catch"](0);
3022
- console.log(_context.t0);
3023
- setHasError(true);
3024
-
3025
- case 22:
3026
- _context.prev = 22;
3027
- setIsLoading(false);
3028
- return _context.finish(22);
3029
-
3030
- case 25:
3031
- case "end":
3032
- return _context.stop();
3033
- }
3034
- }
3035
- }, _callee, null, [[0, 18, 22, 25]]);
3036
- }));
3037
- return _loadContent.apply(this, arguments);
3038
- }
3039
-
3040
- loadContent();
3041
- }, [id, type, sdk]);
3042
-
3043
- if (isLoading) {
3044
- return /*#__PURE__*/React.createElement(React.Fragment, null, "Loading " + type.toLowerCase() + "...");
3045
- }
3046
-
3047
- if (hasError) {
3048
- return /*#__PURE__*/React.createElement(React.Fragment, null, type + " missing or inaccessible");
3049
- }
3050
-
3051
- return /*#__PURE__*/React.createElement(React.Fragment, null, getEntityInfo({
3052
- entityTitle: entityTitle,
3053
- contentTypeName: type,
3054
- entityStatus: entityStatus
3055
- }), ' ', jobs.length > 0 ? fieldEditorReference.getScheduleTooltipContent({
3056
- job: jobs[0],
3057
- jobsCount: jobs.length
3058
- }) : '');
3059
- }
3060
-
3061
2930
  var _templateObject$3, _SYS_LINK_TYPES, _LINK_TYPE_SELECTION_;
3062
2931
  var styles$b = {
3063
2932
  removeSelectionLabel: /*#__PURE__*/emotion.css(_templateObject$3 || (_templateObject$3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n margin-left: ", ";\n "])), tokens.spacingS)
@@ -3217,7 +3086,8 @@ function HyperlinkModal(props) {
3217
3086
  onChange: function onChange(event) {
3218
3087
  return setLinkType(event.target.value);
3219
3088
  },
3220
- testId: "link-type-input"
3089
+ testId: "link-type-input",
3090
+ isDisabled: props.readonly
3221
3091
  }, enabledLinkTypes.map(function (nodeType) {
3222
3092
  return /*#__PURE__*/React.createElement(f36Components.Select.Option, {
3223
3093
  key: nodeType,
@@ -3233,11 +3103,12 @@ function HyperlinkModal(props) {
3233
3103
  setLinkEntity(null);
3234
3104
  setLinkTarget(event.target.value);
3235
3105
  },
3236
- testId: "link-target-input"
3106
+ testId: "link-target-input",
3107
+ isDisabled: props.readonly
3237
3108
  }), /*#__PURE__*/React.createElement(f36Components.FormControl.HelpText, null, "A protocol may be required, e.g. https://")), linkType !== Contentful.INLINES.HYPERLINK && /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(f36Components.FormLabel, {
3238
3109
  isRequired: true,
3239
3110
  htmlFor: ""
3240
- }, "Link target", ' '), linkEntity && linkEntity.sys.linkType === SYS_LINK_TYPES[linkType] ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(f36Components.TextLink, {
3111
+ }, "Link target", ' '), linkEntity && linkEntity.sys.linkType === SYS_LINK_TYPES[linkType] ? /*#__PURE__*/React.createElement(React.Fragment, null, !props.readonly && /*#__PURE__*/React.createElement(f36Components.TextLink, {
3241
3112
  testId: "entity-selection-link",
3242
3113
  onClick: resetLinkEntity,
3243
3114
  className: styles$b.removeSelectionLabel
@@ -3271,32 +3142,37 @@ function HyperlinkModal(props) {
3271
3142
  type: "submit",
3272
3143
  variant: "positive",
3273
3144
  size: "small",
3274
- isDisabled: !isLinkComplete(),
3145
+ isDisabled: props.readonly || !isLinkComplete(),
3275
3146
  onClick: handleOnSubmit,
3276
3147
  testId: "confirm-cta"
3277
3148
  }, props.linkType ? 'Update' : 'Insert'))));
3278
3149
  }
3279
- function addOrEditLink(_x, _x2) {
3150
+ function addOrEditLink(_x, _x2, _x3, _x4) {
3280
3151
  return _addOrEditLink.apply(this, arguments);
3281
3152
  }
3282
3153
 
3283
3154
  function _addOrEditLink() {
3284
- _addOrEditLink = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee3(editor, sdk) {
3285
- var linkType, linkText, linkTarget, linkEntity, _getNodeEntryFromSele, node, path, selectionBeforeBlur, currentLinkText, data, text, url, type, target;
3155
+ _addOrEditLink = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee3(editor, sdk, logAction, targetPath) {
3156
+ var _target$sys$linkType;
3157
+
3158
+ var isReadOnly, selectionBeforeBlur, linkType, linkText, linkTarget, linkEntity, _getNodeEntryFromSele, node, path, selectionAfterFocus, currentLinkText, isEditing, data, text, url, type, target;
3286
3159
 
3287
3160
  return runtime_1.wrap(function _callee3$(_context3) {
3288
3161
  while (1) {
3289
3162
  switch (_context3.prev = _context3.next) {
3290
3163
  case 0:
3291
- if (editor.selection) {
3292
- _context3.next = 2;
3164
+ isReadOnly = Slate.ReactEditor.isReadOnly(editor);
3165
+ selectionBeforeBlur = editor.selection ? _extends({}, editor.selection) : undefined;
3166
+
3167
+ if (!(!targetPath && !selectionBeforeBlur)) {
3168
+ _context3.next = 4;
3293
3169
  break;
3294
3170
  }
3295
3171
 
3296
3172
  return _context3.abrupt("return");
3297
3173
 
3298
- case 2:
3299
- _getNodeEntryFromSele = getNodeEntryFromSelection(editor, LINK_TYPES), node = _getNodeEntryFromSele[0], path = _getNodeEntryFromSele[1];
3174
+ case 4:
3175
+ _getNodeEntryFromSele = getNodeEntryFromSelection(editor, LINK_TYPES, targetPath), node = _getNodeEntryFromSele[0], path = _getNodeEntryFromSele[1];
3300
3176
 
3301
3177
  if (node && path) {
3302
3178
  linkType = node.type;
@@ -3305,11 +3181,13 @@ function _addOrEditLink() {
3305
3181
  linkEntity = node.data.target;
3306
3182
  }
3307
3183
 
3308
- selectionBeforeBlur = _extends({}, editor.selection);
3309
- currentLinkText = linkText || slate.Editor.string(editor, editor.selection);
3310
- _context3.next = 8;
3184
+ selectionAfterFocus = targetPath != null ? targetPath : selectionBeforeBlur;
3185
+ currentLinkText = linkText || (editor.selection ? slate.Editor.string(editor, editor.selection) : '');
3186
+ isEditing = Boolean(node && path);
3187
+ logAction(isEditing ? 'openEditHyperlinkDialog' : 'openCreateHyperlinkDialog');
3188
+ _context3.next = 12;
3311
3189
  return fieldEditorShared.ModalDialogLauncher.openDialog({
3312
- title: linkType ? 'Edit hyperlink' : 'Insert hyperlink',
3190
+ title: isEditing ? 'Edit hyperlink' : 'Insert hyperlink',
3313
3191
  width: 'large',
3314
3192
  shouldCloseOnEscapePress: true,
3315
3193
  shouldCloseOnOverlayClick: true,
@@ -3322,23 +3200,26 @@ function _addOrEditLink() {
3322
3200
  linkType: linkType,
3323
3201
  linkEntity: linkEntity,
3324
3202
  onClose: onClose,
3325
- sdk: sdk
3203
+ sdk: sdk,
3204
+ readonly: isReadOnly
3326
3205
  });
3327
3206
  });
3328
3207
 
3329
- case 8:
3208
+ case 12:
3330
3209
  data = _context3.sent;
3210
+ slate.Transforms.select(editor, selectionAfterFocus);
3331
3211
 
3332
3212
  if (data) {
3333
- _context3.next = 11;
3213
+ _context3.next = 18;
3334
3214
  break;
3335
3215
  }
3336
3216
 
3217
+ focus(editor);
3218
+ logAction(isEditing ? 'cancelEditHyperlinkDialog' : 'cancelCreateHyperlinkDialog');
3337
3219
  return _context3.abrupt("return");
3338
3220
 
3339
- case 11:
3221
+ case 18:
3340
3222
  text = data.linkText, url = data.linkTarget, type = data.linkType, target = data.linkEntity;
3341
- slate.Transforms.select(editor, selectionBeforeBlur);
3342
3223
  slate.Editor.withoutNormalizing(editor, function () {
3343
3224
  insertLink(editor, {
3344
3225
  text: text,
@@ -3348,9 +3229,13 @@ function _addOrEditLink() {
3348
3229
  path: path
3349
3230
  });
3350
3231
  });
3351
- Slate.ReactEditor.focus(editor);
3232
+ logAction(isEditing ? 'edit' : 'insert', {
3233
+ nodeType: type,
3234
+ linkType: (_target$sys$linkType = target == null ? void 0 : target.sys.linkType) != null ? _target$sys$linkType : 'uri'
3235
+ });
3236
+ focus(editor);
3352
3237
 
3353
- case 15:
3238
+ case 22:
3354
3239
  case "end":
3355
3240
  return _context3.stop();
3356
3241
  }
@@ -3360,80 +3245,37 @@ function _addOrEditLink() {
3360
3245
  return _addOrEditLink.apply(this, arguments);
3361
3246
  }
3362
3247
 
3363
- var hasText = function hasText(editor, entry) {
3364
- var node = entry[0];
3365
- return !plateCore.isAncestorEmpty(editor, node) && slate.Node.string(node).trim() !== '';
3366
- };
3367
-
3368
- var styles$c = {
3369
- hyperlinkWrapper: /*#__PURE__*/emotion.css({
3370
- display: 'inline',
3371
- position: 'static',
3372
- a: {
3373
- fontSize: 'inherit !important'
3374
- }
3375
- }),
3376
- hyperlink: /*#__PURE__*/emotion.css({
3377
- fontSize: 'inherit !important',
3378
- display: 'inline !important',
3379
- '&:hover': {
3380
- fill: tokens.gray900
3381
- },
3382
- '&:focus': {
3383
- fill: tokens.gray900
3384
- }
3385
- }),
3386
- hyperlinkIEFallback: /*#__PURE__*/emotion.css({
3387
- color: '#1683d0',
3388
- textDecoration: 'underline'
3389
- }),
3390
- // TODO: use these styles once we can use the icon
3391
- hyperlinkIcon: /*#__PURE__*/emotion.css({
3392
- position: 'relative',
3393
- top: '4px',
3394
- height: '14px',
3395
- margin: '0 -2px 0 -1px',
3396
- webkitTransition: 'fill 100ms ease-in-out',
3397
- transition: 'fill 100ms ease-in-out',
3398
- '&:hover': {
3399
- fill: tokens.gray900
3400
- },
3401
- '&:focus': {
3402
- fill: tokens.gray900
3403
- }
3404
- })
3405
- };
3406
-
3407
- function UrlHyperlink(props) {
3248
+ function ToolbarHyperlinkButton(props) {
3408
3249
  var editor = useContentfulEditor();
3409
- var isReadOnly = Slate.useReadOnly();
3250
+ var isActive = !!(editor && isLinkActive(editor));
3410
3251
  var sdk = useSdkContext();
3411
- var uri = props.element.data.uri;
3412
3252
 
3413
- function handleClick(_x) {
3253
+ function handleClick() {
3414
3254
  return _handleClick.apply(this, arguments);
3415
3255
  }
3416
3256
 
3417
3257
  function _handleClick() {
3418
- _handleClick = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee(event) {
3258
+ _handleClick = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee() {
3419
3259
  return runtime_1.wrap(function _callee$(_context) {
3420
3260
  while (1) {
3421
3261
  switch (_context.prev = _context.next) {
3422
3262
  case 0:
3423
- event.preventDefault();
3424
- event.stopPropagation();
3425
-
3426
3263
  if (editor) {
3427
- _context.next = 4;
3264
+ _context.next = 2;
3428
3265
  break;
3429
3266
  }
3430
3267
 
3431
3268
  return _context.abrupt("return");
3432
3269
 
3433
- case 4:
3434
- addOrEditLink(editor, sdk);
3270
+ case 2:
3271
+ if (isActive) {
3272
+ unwrapLink(editor);
3273
+ editor.tracking.onToolbarAction('unlinkHyperlinks');
3274
+ } else {
3275
+ addOrEditLink(editor, sdk, editor.tracking.onToolbarAction);
3276
+ }
3435
3277
 
3436
- case 5:
3278
+ case 3:
3437
3279
  case "end":
3438
3280
  return _context.stop();
3439
3281
  }
@@ -3443,127 +3285,258 @@ function UrlHyperlink(props) {
3443
3285
  return _handleClick.apply(this, arguments);
3444
3286
  }
3445
3287
 
3446
- return /*#__PURE__*/React.createElement(f36Components.Tooltip, {
3447
- content: uri,
3448
- targetWrapperClassName: styles$c.hyperlinkWrapper,
3449
- placement: "bottom",
3450
- maxWidth: "auto"
3451
- }, /*#__PURE__*/React.createElement(f36Components.TextLink, {
3452
- as: "a",
3453
- href: uri,
3454
- rel: "noopener noreferrer",
3288
+ if (!editor) return null;
3289
+ return /*#__PURE__*/React.createElement(ToolbarButton, {
3290
+ title: "Hyperlink",
3291
+ testId: "hyperlink-toolbar-button",
3455
3292
  onClick: handleClick,
3456
- isDisabled: isReadOnly,
3457
- className: styles$c.hyperlink
3458
- }, props.children));
3293
+ isActive: isActive,
3294
+ isDisabled: props.isDisabled
3295
+ }, /*#__PURE__*/React.createElement(f36Icons.LinkIcon, null));
3459
3296
  }
3460
3297
 
3461
- function EntityHyperlink(props) {
3462
- var editor = useContentfulEditor();
3463
- var isReadOnly = Slate.useReadOnly();
3464
- var sdk = useSdkContext();
3465
- var target = props.element.data.target;
3466
- if (!target) return null;
3467
-
3468
- function handleClick(_x2) {
3469
- return _handleClick2.apply(this, arguments);
3470
- }
3298
+ var hasText = function hasText(editor, entry) {
3299
+ var node = entry[0];
3300
+ return !plateCore.isAncestorEmpty(editor, node) && slate.Node.string(node).trim() !== '';
3301
+ };
3471
3302
 
3472
- function _handleClick2() {
3473
- _handleClick2 = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee2(event) {
3474
- return runtime_1.wrap(function _callee2$(_context2) {
3475
- while (1) {
3476
- switch (_context2.prev = _context2.next) {
3477
- case 0:
3478
- event.preventDefault();
3479
- event.stopPropagation();
3303
+ function truncate(str, length) {
3304
+ if (typeof str === 'string' && str.length > length) {
3305
+ return str && str.substr(0, length + 1) // +1 to look ahead and be replaced below.
3306
+ // Get rid of orphan letters but not one letter words (I, a, 2).
3307
+ // Try to not have “.” as last character to avoid awkward “....”.
3308
+ .replace(/(\s+\S(?=\S)|\s*)\.?.$/, '…');
3309
+ }
3480
3310
 
3481
- if (editor) {
3482
- _context2.next = 4;
3483
- break;
3484
- }
3311
+ return str;
3312
+ }
3485
3313
 
3486
- return _context2.abrupt("return");
3314
+ function getEntityInfo(data) {
3315
+ if (!data) {
3316
+ return '';
3317
+ }
3487
3318
 
3488
- case 4:
3489
- addOrEditLink(editor, sdk);
3319
+ var entityTitle = data.entityTitle,
3320
+ contentTypeName = data.contentTypeName,
3321
+ entityStatus = data.entityStatus,
3322
+ jobs = data.jobs;
3323
+ var title = truncate(entityTitle, 60) || 'Untitled';
3324
+ var scheduledActions = jobs.length > 0 ? fieldEditorReference.getScheduleTooltipContent({
3325
+ job: jobs[0],
3326
+ jobsCount: jobs.length
3327
+ }) : '';
3328
+ return (contentTypeName || 'Asset') + " \"" + title + "\", " + entityStatus + " " + scheduledActions;
3329
+ }
3490
3330
 
3491
- case 5:
3492
- case "end":
3493
- return _context2.stop();
3494
- }
3331
+ function fetchAllData(_x) {
3332
+ return _fetchAllData.apply(this, arguments);
3333
+ }
3334
+
3335
+ function _fetchAllData() {
3336
+ _fetchAllData = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee(_ref) {
3337
+ var sdk, entityId, entityType, localeCode, defaultLocaleCode, contentType, getEntity, entity, contentTypeId, entityTitle, entityDescription, jobs, entityStatus;
3338
+ return runtime_1.wrap(function _callee$(_context) {
3339
+ while (1) {
3340
+ switch (_context.prev = _context.next) {
3341
+ case 0:
3342
+ sdk = _ref.sdk, entityId = _ref.entityId, entityType = _ref.entityType, localeCode = _ref.localeCode, defaultLocaleCode = _ref.defaultLocaleCode;
3343
+ getEntity = entityType === 'Entry' ? sdk.space.getEntry : sdk.space.getAsset;
3344
+ _context.next = 4;
3345
+ return getEntity(entityId);
3346
+
3347
+ case 4:
3348
+ entity = _context.sent;
3349
+
3350
+ if (entity.sys.contentType) {
3351
+ contentTypeId = entity.sys.contentType.sys.id;
3352
+ contentType = sdk.space.getCachedContentTypes().find(function (ct) {
3353
+ return ct.sys.id === contentTypeId;
3354
+ });
3355
+ }
3356
+
3357
+ entityTitle = entityType === 'Entry' ? fieldEditorShared.entityHelpers.getEntryTitle({
3358
+ entry: entity,
3359
+ contentType: contentType,
3360
+ localeCode: localeCode,
3361
+ defaultLocaleCode: defaultLocaleCode,
3362
+ defaultTitle: 'Untitled'
3363
+ }) : fieldEditorShared.entityHelpers.getAssetTitle({
3364
+ asset: entity,
3365
+ localeCode: localeCode,
3366
+ defaultLocaleCode: defaultLocaleCode,
3367
+ defaultTitle: 'Untitled'
3368
+ });
3369
+ entityDescription = fieldEditorShared.entityHelpers.getEntityDescription({
3370
+ entity: entity,
3371
+ contentType: contentType,
3372
+ localeCode: localeCode,
3373
+ defaultLocaleCode: defaultLocaleCode
3374
+ });
3375
+ _context.next = 10;
3376
+ return sdk.space.getEntityScheduledActions(entityType, entityId);
3377
+
3378
+ case 10:
3379
+ jobs = _context.sent;
3380
+ entityStatus = fieldEditorShared.entityHelpers.getEntryStatus(entity.sys);
3381
+ return _context.abrupt("return", {
3382
+ jobs: jobs,
3383
+ entity: entity,
3384
+ entityTitle: entityTitle,
3385
+ entityDescription: entityDescription,
3386
+ entityStatus: entityStatus,
3387
+ contentTypeName: contentType ? contentType.name : ''
3388
+ });
3389
+
3390
+ case 13:
3391
+ case "end":
3392
+ return _context.stop();
3495
3393
  }
3496
- }, _callee2);
3497
- }));
3498
- return _handleClick2.apply(this, arguments);
3394
+ }
3395
+ }, _callee);
3396
+ }));
3397
+ return _fetchAllData.apply(this, arguments);
3398
+ }
3399
+
3400
+ function useRequestStatus(_ref2) {
3401
+ var sdk = _ref2.sdk,
3402
+ target = _ref2.target,
3403
+ onEntityFetchComplete = _ref2.onEntityFetchComplete;
3404
+
3405
+ var _useState = React.useState({
3406
+ type: 'loading'
3407
+ }),
3408
+ requestStatus = _useState[0],
3409
+ setRequestStatus = _useState[1];
3410
+
3411
+ React.useEffect(function () {
3412
+ if (target) {
3413
+ var _target$sys, _target$sys2;
3414
+
3415
+ fetchAllData({
3416
+ sdk: sdk,
3417
+ entityId: target == null ? void 0 : (_target$sys = target.sys) == null ? void 0 : _target$sys.id,
3418
+ entityType: target == null ? void 0 : (_target$sys2 = target.sys) == null ? void 0 : _target$sys2.linkType,
3419
+ localeCode: sdk.field.locale,
3420
+ defaultLocaleCode: sdk.locales["default"]
3421
+ }).then(function (entityInfo) {
3422
+ setRequestStatus({
3423
+ type: 'success',
3424
+ data: entityInfo
3425
+ });
3426
+ })["catch"](function (e) {
3427
+ console.log(e);
3428
+ setRequestStatus({
3429
+ type: 'error',
3430
+ error: e
3431
+ });
3432
+ })["finally"](function () {
3433
+ onEntityFetchComplete == null ? void 0 : onEntityFetchComplete();
3434
+ });
3435
+ }
3436
+ }, [sdk, target, onEntityFetchComplete]);
3437
+ return requestStatus;
3438
+ }
3439
+
3440
+ function useEntityInfo(props) {
3441
+ var status = useRequestStatus(props);
3442
+ var linkType = props.target.sys.linkType;
3443
+
3444
+ if (status.type === 'loading') {
3445
+ return "Loading " + linkType.toLowerCase() + "...";
3446
+ }
3447
+
3448
+ if (status.type === 'error') {
3449
+ return linkType + " missing or inaccessible";
3450
+ }
3451
+
3452
+ return getEntityInfo(status.data);
3453
+ }
3454
+
3455
+ var styles$c = {
3456
+ hyperlinkWrapper: /*#__PURE__*/emotion.css({
3457
+ display: 'inline',
3458
+ position: 'static',
3459
+ a: {
3460
+ fontSize: 'inherit !important'
3461
+ }
3462
+ }),
3463
+ hyperlink: /*#__PURE__*/emotion.css({
3464
+ fontSize: 'inherit !important',
3465
+ display: 'inline !important',
3466
+ '&:hover': {
3467
+ fill: tokens.gray900
3468
+ },
3469
+ '&:focus': {
3470
+ fill: tokens.gray900
3471
+ }
3472
+ })
3473
+ };
3474
+
3475
+ function EntityHyperlink(props) {
3476
+ var editor = useContentfulEditor();
3477
+ var sdk = useSdkContext();
3478
+ var target = props.element.data.target;
3479
+ var onEntityFetchComplete = props.onEntityFetchComplete;
3480
+ var tooltipContent = useEntityInfo({
3481
+ target: target,
3482
+ sdk: sdk,
3483
+ onEntityFetchComplete: onEntityFetchComplete
3484
+ });
3485
+ if (!target) return null;
3486
+
3487
+ function handleClick(event) {
3488
+ event.preventDefault();
3489
+ event.stopPropagation();
3490
+ if (!editor) return;
3491
+ var p = Slate.ReactEditor.toSlatePoint(editor, [event.target, 0], {
3492
+ exactMatch: false,
3493
+ suppressThrow: false
3494
+ });
3495
+ addOrEditLink(editor, sdk, editor.tracking.onViewportAction, p.path);
3499
3496
  }
3500
3497
 
3501
3498
  return /*#__PURE__*/React.createElement(f36Components.Tooltip, {
3502
- content: /*#__PURE__*/React.createElement(EntryAssetTooltip, {
3503
- id: target.sys.id,
3504
- type: target.sys.linkType,
3505
- sdk: sdk
3506
- }),
3499
+ content: tooltipContent,
3507
3500
  targetWrapperClassName: styles$c.hyperlinkWrapper,
3508
3501
  placement: "bottom",
3509
3502
  maxWidth: "auto"
3510
3503
  }, /*#__PURE__*/React.createElement(f36Components.TextLink, {
3511
- as: "button",
3504
+ as: "a",
3512
3505
  onClick: handleClick,
3513
- isDisabled: isReadOnly,
3514
3506
  className: styles$c.hyperlink,
3515
3507
  "data-link-type": target.sys.linkType,
3516
3508
  "data-link-id": target.sys.id
3517
3509
  }, props.children));
3518
3510
  }
3519
3511
 
3520
- function ToolbarHyperlinkButton(props) {
3512
+ function UrlHyperlink(props) {
3521
3513
  var editor = useContentfulEditor();
3522
- var isActive = !!(editor && isLinkActive(editor));
3523
3514
  var sdk = useSdkContext();
3515
+ var uri = props.element.data.uri;
3524
3516
 
3525
- function handleClick() {
3526
- return _handleClick3.apply(this, arguments);
3527
- }
3528
-
3529
- function _handleClick3() {
3530
- _handleClick3 = _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee3() {
3531
- return runtime_1.wrap(function _callee3$(_context3) {
3532
- while (1) {
3533
- switch (_context3.prev = _context3.next) {
3534
- case 0:
3535
- if (editor) {
3536
- _context3.next = 2;
3537
- break;
3538
- }
3539
-
3540
- return _context3.abrupt("return");
3541
-
3542
- case 2:
3543
- if (isActive) {
3544
- unwrapLink(editor);
3545
- } else {
3546
- addOrEditLink(editor, sdk);
3547
- }
3548
-
3549
- case 3:
3550
- case "end":
3551
- return _context3.stop();
3552
- }
3553
- }
3554
- }, _callee3);
3555
- }));
3556
- return _handleClick3.apply(this, arguments);
3517
+ function handleClick(event) {
3518
+ event.preventDefault();
3519
+ event.stopPropagation();
3520
+ if (!editor) return;
3521
+ var p = Slate.ReactEditor.toSlatePoint(editor, [event.target, 0], {
3522
+ exactMatch: false,
3523
+ suppressThrow: false
3524
+ });
3525
+ addOrEditLink(editor, sdk, editor.tracking.onViewportAction, p.path);
3557
3526
  }
3558
3527
 
3559
- if (!editor) return null;
3560
- return /*#__PURE__*/React.createElement(ToolbarButton, {
3561
- title: "Hyperlink",
3562
- testId: "hyperlink-toolbar-button",
3528
+ return /*#__PURE__*/React.createElement(f36Components.Tooltip, {
3529
+ content: uri,
3530
+ targetWrapperClassName: styles$c.hyperlinkWrapper,
3531
+ placement: "bottom",
3532
+ maxWidth: "auto"
3533
+ }, /*#__PURE__*/React.createElement(f36Components.TextLink, {
3534
+ as: "a",
3535
+ href: uri,
3536
+ rel: "noopener noreferrer",
3563
3537
  onClick: handleClick,
3564
- isActive: isActive,
3565
- isDisabled: props.isDisabled
3566
- }, /*#__PURE__*/React.createElement(f36Icons.LinkIcon, null));
3538
+ className: styles$c.hyperlink
3539
+ }, props.children));
3567
3540
  }
3568
3541
 
3569
3542
  var isAnchor = function isAnchor(element) {
@@ -3571,11 +3544,11 @@ var isAnchor = function isAnchor(element) {
3571
3544
  };
3572
3545
 
3573
3546
  var isEntryAnchor = function isEntryAnchor(element) {
3574
- return isAnchor(element) && element.getAttribute('data-link-type') === 'Entry';
3547
+ return element.nodeName === 'A' && element.getAttribute('data-link-type') === 'Entry';
3575
3548
  };
3576
3549
 
3577
3550
  var isAssetAnchor = function isAssetAnchor(element) {
3578
- return isAnchor(element) && element.getAttribute('data-link-type') === 'Asset';
3551
+ return element.nodeName === 'A' && element.getAttribute('data-link-type') === 'Asset';
3579
3552
  };
3580
3553
 
3581
3554
  var buildHyperlinkEventHandler = function buildHyperlinkEventHandler(sdk) {
@@ -3592,8 +3565,9 @@ var buildHyperlinkEventHandler = function buildHyperlinkEventHandler(sdk) {
3592
3565
 
3593
3566
  if (isLinkActive(editor)) {
3594
3567
  unwrapLink(editor);
3568
+ editor.tracking.onShortcutAction('unlinkHyperlinks');
3595
3569
  } else {
3596
- addOrEditLink(editor, sdk);
3570
+ addOrEditLink(editor, sdk, editor.tracking.onShortcutAction);
3597
3571
  }
3598
3572
  };
3599
3573
  };
@@ -3650,7 +3624,7 @@ var createHyperlinkPlugin = function createHyperlinkPlugin(sdk) {
3650
3624
  _extends({}, common, {
3651
3625
  key: Contentful.INLINES.ENTRY_HYPERLINK,
3652
3626
  type: Contentful.INLINES.ENTRY_HYPERLINK,
3653
- component: EntityHyperlink,
3627
+ component: withLinkTracking(EntityHyperlink),
3654
3628
  deserializeHtml: {
3655
3629
  rules: [{
3656
3630
  validNodeName: ['A']
@@ -3664,7 +3638,7 @@ var createHyperlinkPlugin = function createHyperlinkPlugin(sdk) {
3664
3638
  _extends({}, common, {
3665
3639
  key: Contentful.INLINES.ASSET_HYPERLINK,
3666
3640
  type: Contentful.INLINES.ASSET_HYPERLINK,
3667
- component: EntityHyperlink,
3641
+ component: withLinkTracking(EntityHyperlink),
3668
3642
  deserializeHtml: {
3669
3643
  rules: [{
3670
3644
  validNodeName: ['A']
@@ -3686,7 +3660,7 @@ var createHyperlinkPlugin = function createHyperlinkPlugin(sdk) {
3686
3660
  };
3687
3661
 
3688
3662
  var _templateObject$4, _templateObject2$3, _templateObject3$3, _styles;
3689
- var baseStyle = /*#__PURE__*/emotion.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);
3663
+ var baseStyle = /*#__PURE__*/emotion.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);
3690
3664
  var styles$d = (_styles = {}, _styles[Contentful.BLOCKS.UL_LIST] = /*#__PURE__*/emotion.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[Contentful.BLOCKS.OL_LIST] = /*#__PURE__*/emotion.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);
3691
3665
 
3692
3666
  function createList(Tag, block) {
@@ -3701,13 +3675,349 @@ var ListUL = /*#__PURE__*/createList('ul', Contentful.BLOCKS.UL_LIST);
3701
3675
  var ListOL = /*#__PURE__*/createList('ol', Contentful.BLOCKS.OL_LIST);
3702
3676
 
3703
3677
  var _templateObject$5;
3704
- var style = /*#__PURE__*/emotion.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);
3678
+ var style = /*#__PURE__*/emotion.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);
3705
3679
  function ListItem(props) {
3706
3680
  return /*#__PURE__*/React.createElement("li", Object.assign({}, props.attributes, {
3707
3681
  className: style
3708
3682
  }), props.children);
3709
3683
  }
3710
3684
 
3685
+ /**
3686
+ * Credit: Modified version of Plate's list plugin
3687
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3688
+ */
3689
+ var moveListItemDown = function moveListItemDown(editor, _ref) {
3690
+ var list = _ref.list,
3691
+ listItem = _ref.listItem;
3692
+ var listNode = list[0];
3693
+ var listItemPath = listItem[1];
3694
+ var previousListItemPath;
3695
+
3696
+ try {
3697
+ previousListItemPath = slate.Path.previous(listItemPath);
3698
+ } catch (e) {
3699
+ return;
3700
+ } // Previous sibling is the new parent
3701
+
3702
+
3703
+ var previousSiblingItem = slate.Editor.node(editor, previousListItemPath);
3704
+
3705
+ if (previousSiblingItem) {
3706
+ var previousPath = previousSiblingItem[1];
3707
+ var subList = Array.from(slate.Node.children(editor, previousPath)).find(function (_ref2) {
3708
+ var n = _ref2[0];
3709
+ return plateCore.match(n, {
3710
+ type: plateList.getListTypes(editor)
3711
+ });
3712
+ });
3713
+ var newPath = slate.Path.next(plateCore.getLastChildPath(subList != null ? subList : previousSiblingItem));
3714
+ slate.Editor.withoutNormalizing(editor, function () {
3715
+ if (!subList) {
3716
+ // Create new sub-list
3717
+ plateCore.wrapNodes(editor, {
3718
+ type: listNode.type,
3719
+ children: [],
3720
+ data: {}
3721
+ }, {
3722
+ at: listItemPath
3723
+ });
3724
+ } // Move the current item to the sub-list
3725
+
3726
+
3727
+ slate.Transforms.moveNodes(editor, {
3728
+ at: listItemPath,
3729
+ to: newPath
3730
+ });
3731
+ });
3732
+ }
3733
+ };
3734
+
3735
+ /**
3736
+ * Credit: Modified version of Plate's list plugin
3737
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3738
+ */
3739
+ var moveListItems = function moveListItems(editor, _temp) {
3740
+ var _editor$selection;
3741
+
3742
+ var _ref = _temp === void 0 ? {} : _temp,
3743
+ _ref$increase = _ref.increase,
3744
+ increase = _ref$increase === void 0 ? true : _ref$increase,
3745
+ _ref$at = _ref.at,
3746
+ at = _ref$at === void 0 ? (_editor$selection = editor.selection) != null ? _editor$selection : undefined : _ref$at;
3747
+
3748
+ var _nodes = plateCore.getNodes(editor, {
3749
+ at: at,
3750
+ match: {
3751
+ type: plateCore.getPluginType(editor, plateList.ELEMENT_LIC)
3752
+ }
3753
+ }); // Get the selected lic
3754
+
3755
+
3756
+ var lics = Array.from(_nodes);
3757
+ if (!lics.length) return;
3758
+ var highestLicPaths = [];
3759
+ var highestLicPathRefs = []; // Filter out the nested lic, we just need to move the highest ones
3760
+
3761
+ lics.forEach(function (lic) {
3762
+ var licPath = lic[1];
3763
+ var liPath = slate.Path.parent(licPath);
3764
+ var isAncestor = highestLicPaths.some(function (path) {
3765
+ var highestLiPath = slate.Path.parent(path);
3766
+ return slate.Path.isAncestor(highestLiPath, liPath);
3767
+ });
3768
+
3769
+ if (!isAncestor) {
3770
+ highestLicPaths.push(licPath);
3771
+ highestLicPathRefs.push(slate.Editor.pathRef(editor, licPath));
3772
+ }
3773
+ });
3774
+ var licPathRefsToMove = increase ? highestLicPathRefs : highestLicPathRefs.reverse();
3775
+ slate.Editor.withoutNormalizing(editor, function () {
3776
+ licPathRefsToMove.forEach(function (licPathRef) {
3777
+ var licPath = licPathRef.unref();
3778
+ if (!licPath) return;
3779
+ var liEntry = plateList.getListItemEntry(editor, {
3780
+ at: licPath
3781
+ });
3782
+
3783
+ if (!liEntry) {
3784
+ return;
3785
+ }
3786
+
3787
+ if (increase) {
3788
+ moveListItemDown(editor, liEntry);
3789
+ } else if (plateList.isListNested(editor, liEntry.list[1])) {
3790
+ plateList.moveListItemUp(editor, liEntry);
3791
+ }
3792
+ });
3793
+ });
3794
+ };
3795
+
3796
+ /**
3797
+ * Credit: Modified version of Plate's list plugin
3798
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3799
+ */
3800
+
3801
+ function hasUnliftedListItems(editor, at) {
3802
+ return slate.Editor.nodes(editor, {
3803
+ at: at,
3804
+ match: function match(node, path) {
3805
+ return slate.Element.isElement(node) && node.type === Contentful.BLOCKS.LIST_ITEM && path.length >= 2;
3806
+ }
3807
+ }).next().done;
3808
+ }
3809
+
3810
+ var unwrapList = function unwrapList(editor, _temp) {
3811
+ var _ref = _temp === void 0 ? {} : _temp,
3812
+ at = _ref.at;
3813
+
3814
+ slate.Editor.withoutNormalizing(editor, function () {
3815
+ do {
3816
+ // lift list items to the root level
3817
+ slate.Transforms.liftNodes(editor, {
3818
+ at: at,
3819
+ match: function match(node) {
3820
+ return slate.Element.isElement(node) && node.type === Contentful.BLOCKS.LIST_ITEM;
3821
+ },
3822
+ mode: 'lowest'
3823
+ });
3824
+ } while (!hasUnliftedListItems(editor, at)); // finally unwrap all lifted items
3825
+
3826
+
3827
+ plateCore.unwrapNodes(editor, {
3828
+ at: at,
3829
+ match: {
3830
+ type: Contentful.BLOCKS.LIST_ITEM
3831
+ },
3832
+ split: false
3833
+ });
3834
+ });
3835
+ };
3836
+
3837
+ var listTypes = [Contentful.BLOCKS.UL_LIST, Contentful.BLOCKS.OL_LIST];
3838
+ var toggleList = function toggleList(editor, _ref) {
3839
+ var type = _ref.type;
3840
+ return slate.Editor.withoutNormalizing(editor, function () {
3841
+ if (!editor.selection) {
3842
+ return;
3843
+ }
3844
+
3845
+ if (plateCore.isCollapsed(editor.selection) || !plateCore.isRangeAcrossBlocks(editor)) {
3846
+ // selection is collapsed
3847
+ var res = plateList.getListItemEntry(editor);
3848
+
3849
+ if (res) {
3850
+ var list = res.list;
3851
+
3852
+ if (list[0].type !== type) {
3853
+ plateCore.setNodes(editor, {
3854
+ type: type
3855
+ }, {
3856
+ at: editor.selection,
3857
+ match: function match(n) {
3858
+ return listTypes.includes(n.type);
3859
+ },
3860
+ mode: 'lowest'
3861
+ });
3862
+ } else {
3863
+ unwrapList(editor);
3864
+ }
3865
+ } else {
3866
+ var _list = {
3867
+ type: type,
3868
+ children: [],
3869
+ data: {}
3870
+ };
3871
+ plateCore.wrapNodes(editor, _list);
3872
+ var nodes = [].concat(plateCore.getNodes(editor, {
3873
+ match: {
3874
+ type: plateCore.getPluginType(editor, plateCore.ELEMENT_DEFAULT)
3875
+ }
3876
+ }));
3877
+ var listItem = {
3878
+ type: Contentful.BLOCKS.LIST_ITEM,
3879
+ children: [],
3880
+ data: {}
3881
+ };
3882
+
3883
+ for (var _iterator = _createForOfIteratorHelperLoose(nodes), _step; !(_step = _iterator()).done;) {
3884
+ var _step$value = _step.value,
3885
+ path = _step$value[1];
3886
+ plateCore.wrapNodes(editor, listItem, {
3887
+ at: path
3888
+ });
3889
+ }
3890
+ }
3891
+ } else {
3892
+ // selection is a range
3893
+ var _Range$edges = slate.Range.edges(editor.selection),
3894
+ startPoint = _Range$edges[0],
3895
+ endPoint = _Range$edges[1];
3896
+
3897
+ var commonEntry = slate.Node.common(editor, startPoint.path, endPoint.path);
3898
+
3899
+ if (listTypes.includes(commonEntry[0].type) || commonEntry[0].type === Contentful.BLOCKS.LIST_ITEM) {
3900
+ var listType = commonEntry[0].type;
3901
+
3902
+ if (commonEntry[0].type === Contentful.BLOCKS.LIST_ITEM) {
3903
+ listType = slate.Editor.parent(editor, commonEntry[1])[0].type;
3904
+ }
3905
+
3906
+ if (listType !== type) {
3907
+ var startList = plateCore.findNode(editor, {
3908
+ at: slate.Range.start(editor.selection),
3909
+ match: {
3910
+ type: listTypes
3911
+ },
3912
+ mode: 'lowest'
3913
+ });
3914
+ var endList = plateCore.findNode(editor, {
3915
+ at: slate.Range.end(editor.selection),
3916
+ match: {
3917
+ type: listTypes
3918
+ },
3919
+ mode: 'lowest'
3920
+ });
3921
+
3922
+ if (!startList || !endList) {
3923
+ return;
3924
+ }
3925
+
3926
+ var rangeLength = Math.min(startList[1].length, endList[1].length);
3927
+ plateCore.setNodes(editor, {
3928
+ type: type
3929
+ }, {
3930
+ at: editor.selection,
3931
+ match: function match(n, path) {
3932
+ return listTypes.includes(n.type) && path.length >= rangeLength;
3933
+ },
3934
+ mode: 'all'
3935
+ });
3936
+ } else {
3937
+ unwrapList(editor);
3938
+ }
3939
+ } else {
3940
+ var rootPathLength = commonEntry[1].length;
3941
+
3942
+ var _nodes = Array.from(plateCore.getNodes(editor, {
3943
+ mode: 'all'
3944
+ })).filter(function (_ref2) {
3945
+ var path = _ref2[1];
3946
+ return path.length === rootPathLength + 1;
3947
+ }).reverse();
3948
+
3949
+ _nodes.forEach(function (n) {
3950
+ if (listTypes.includes(n[0].type)) {
3951
+ plateCore.setNodes(editor, {
3952
+ type: type
3953
+ }, {
3954
+ at: n[1]
3955
+ });
3956
+ } else {
3957
+ plateCore.setNodes(editor, {
3958
+ type: plateCore.getPluginType(editor, plateList.ELEMENT_LIC)
3959
+ }, {
3960
+ at: n[1]
3961
+ });
3962
+ var _listItem = {
3963
+ type: Contentful.BLOCKS.LIST_ITEM,
3964
+ children: [],
3965
+ data: {}
3966
+ };
3967
+ plateCore.wrapNodes(editor, _listItem, {
3968
+ at: n[1]
3969
+ });
3970
+ var _list2 = {
3971
+ type: type,
3972
+ children: [],
3973
+ data: {}
3974
+ };
3975
+ plateCore.wrapNodes(editor, _list2, {
3976
+ at: n[1]
3977
+ });
3978
+ }
3979
+ });
3980
+ }
3981
+ }
3982
+ });
3983
+ };
3984
+
3985
+ var onKeyDownList = function onKeyDownList(editor, _ref) {
3986
+ var type = _ref.type,
3987
+ hotkey = _ref.options.hotkey;
3988
+ return function (e) {
3989
+ if (e.key === 'Tab' && editor.selection) {
3990
+ var listSelected = plateCore.getAbove(editor, {
3991
+ at: editor.selection,
3992
+ match: {
3993
+ type: type
3994
+ }
3995
+ });
3996
+
3997
+ if (listSelected) {
3998
+ e.preventDefault();
3999
+ moveListItems(editor, {
4000
+ increase: !e.shiftKey
4001
+ });
4002
+ return;
4003
+ }
4004
+ }
4005
+
4006
+ if (!hotkey) return;
4007
+ var hotkeys = castArray(hotkey);
4008
+
4009
+ for (var _iterator = _createForOfIteratorHelperLoose(hotkeys), _step; !(_step = _iterator()).done;) {
4010
+ var _hotkey = _step.value;
4011
+
4012
+ if (isHotkey(_hotkey)(e)) {
4013
+ toggleList(editor, {
4014
+ type: type
4015
+ });
4016
+ }
4017
+ }
4018
+ };
4019
+ };
4020
+
3711
4021
  var isList = function isList(node) {
3712
4022
  return [Contentful.BLOCKS.OL_LIST, Contentful.BLOCKS.UL_LIST].includes(node.type);
3713
4023
  };
@@ -3755,8 +4065,18 @@ var isNonEmptyListItem = function isNonEmptyListItem(editor, _ref4) {
3755
4065
  var listItemChildren = Array.from(slate.Node.children(editor, path));
3756
4066
  return listItemChildren.length !== 0;
3757
4067
  };
3758
- var insertParagraphAsChild = function insertParagraphAsChild(editor, _ref5) {
3759
- var path = _ref5[1];
4068
+ var firstNodeIsNotList = function firstNodeIsNotList(_editor, _ref5) {
4069
+ var node = _ref5[0];
4070
+
4071
+ if (node.children.length === 1) {
4072
+ var firstNode = node.children[0];
4073
+ return !slate.Text.isText(firstNode) && !isList(firstNode);
4074
+ }
4075
+
4076
+ return true;
4077
+ };
4078
+ var insertParagraphAsChild = function insertParagraphAsChild(editor, _ref6) {
4079
+ var path = _ref6[1];
3760
4080
  slate.Transforms.insertNodes(editor, [{
3761
4081
  type: Contentful.BLOCKS.PARAGRAPH,
3762
4082
  data: {},
@@ -3767,68 +4087,325 @@ var insertParagraphAsChild = function insertParagraphAsChild(editor, _ref5) {
3767
4087
  at: path.concat([0])
3768
4088
  });
3769
4089
  };
4090
+ var replaceNodeWithListItems = function replaceNodeWithListItems(editor, entry) {
4091
+ var node = entry[0],
4092
+ path = entry[1];
4093
+ slate.Transforms.removeNodes(editor, {
4094
+ at: path
4095
+ });
4096
+ slate.Transforms.insertNodes(editor, node.children[0].children, {
4097
+ at: path
4098
+ });
4099
+ };
4100
+ var isListTypeActive = function isListTypeActive(editor, type) {
4101
+ var selection = editor.selection;
4102
+
4103
+ if (!selection) {
4104
+ return false;
4105
+ }
4106
+
4107
+ if (slate.Range.isExpanded(selection)) {
4108
+ var _Range$edges = slate.Range.edges(selection),
4109
+ start = _Range$edges[0],
4110
+ end = _Range$edges[1];
4111
+
4112
+ var node = slate.Node.common(editor, start.path, end.path);
4113
+
4114
+ if (node[0].type === type) {
4115
+ return true;
4116
+ }
4117
+ } // Lists can be nested. Here, we take the list type at the lowest level
4118
+
4119
+
4120
+ var listNode = plateCore.getBlockAbove(editor, {
4121
+ match: {
4122
+ type: [Contentful.BLOCKS.OL_LIST, Contentful.BLOCKS.UL_LIST]
4123
+ },
4124
+ mode: 'lowest'
4125
+ });
4126
+ return (listNode == null ? void 0 : listNode[0].type) === type;
4127
+ };
3770
4128
 
3771
4129
  /**
3772
- * A copy of Plate's list plugin with a few adjustments
3773
- * to support pasting any element
4130
+ * Build a new list item node while preserving marks
3774
4131
  */
3775
- var getListInsertFragment = function getListInsertFragment(editor) {
3776
- var insertFragment = editor.insertFragment;
3777
- var li = plateCore.getPlugin(editor, plateList.ELEMENT_LI);
3778
- var ul = plateCore.getPlugin(editor, plateList.ELEMENT_UL);
3779
- var ol = plateCore.getPlugin(editor, plateList.ELEMENT_OL);
3780
4132
 
3781
- var isListRoot = function isListRoot(node) {
3782
- return [ul.type, ol.type].includes(node.type);
4133
+ var emptyListItemNode = function emptyListItemNode(editor, withChildren) {
4134
+ if (withChildren === void 0) {
4135
+ withChildren = false;
4136
+ }
4137
+
4138
+ var children = [];
4139
+
4140
+ if (withChildren) {
4141
+ var marks = slate.Editor.marks(editor) || {};
4142
+ children = [{
4143
+ type: Contentful.BLOCKS.PARAGRAPH,
4144
+ data: {},
4145
+ children: [_extends({
4146
+ text: ''
4147
+ }, marks)]
4148
+ }];
4149
+ }
4150
+
4151
+ return {
4152
+ type: Contentful.BLOCKS.LIST_ITEM,
4153
+ data: {},
4154
+ children: children
3783
4155
  };
4156
+ };
4157
+ /**
4158
+ * Insert list item if selection is in li>p.
4159
+ */
3784
4160
 
3785
- var getFirstAncestorOfType = function getFirstAncestorOfType(root, entry, _ref) {
3786
- var type = _ref.type;
3787
- var ancestor = slate.Path.parent(entry[1]);
3788
4161
 
3789
- while (slate.Node.get(root, ancestor).type !== type) {
3790
- ancestor = slate.Path.parent(ancestor);
4162
+ var insertListItem = function insertListItem(editor) {
4163
+ if (!editor.selection) {
4164
+ return false;
4165
+ } // Naming it paragraph for simplicity but can be a heading as well
4166
+
4167
+
4168
+ var paragraph = plateCore.getAbove(editor, {
4169
+ match: {
4170
+ type: Contentful.TEXT_CONTAINERS
3791
4171
  }
4172
+ });
3792
4173
 
3793
- return [slate.Node.get(root, ancestor), ancestor];
3794
- };
4174
+ if (!paragraph) {
4175
+ return false;
4176
+ }
4177
+
4178
+ var paragraphPath = paragraph[1];
4179
+ var listItem = plateCore.getParent(editor, paragraphPath);
4180
+
4181
+ if (!listItem) {
4182
+ return false;
4183
+ }
4184
+
4185
+ var listItemNode = listItem[0],
4186
+ listItemPath = listItem[1];
4187
+
4188
+ if (listItemNode.type !== Contentful.BLOCKS.LIST_ITEM) {
4189
+ return false;
4190
+ } // We are in a li>p (or heading)
4191
+
4192
+
4193
+ slate.Editor.withoutNormalizing(editor, function () {
4194
+ if (!editor.selection) {
4195
+ return;
4196
+ } // Check the cursor position in the current paragraph
4197
+
4198
+
4199
+ var isAtStart = plateCore.isSelectionAtBlockStart(editor);
4200
+ var isAtEnd = plateCore.isSelectionAtBlockEnd(editor);
4201
+ var isAtStartOfListItem = isAtStart && plateCore.isFirstChild(paragraphPath);
4202
+ var shouldSplit = !isAtStart && !isAtEnd; // Split the current paragraph content if necessary
4203
+
4204
+ if (shouldSplit) {
4205
+ slate.Transforms.splitNodes(editor);
4206
+ } // Insert the new li
4207
+
4208
+
4209
+ var newListItemPath = isAtStartOfListItem ? listItemPath : slate.Path.next(listItemPath);
4210
+ plateCore.insertNodes(editor, // Add an empty paragraph to the new li if We will not move some
4211
+ // paragraphs over there.
4212
+ emptyListItemNode(editor, !shouldSplit), {
4213
+ at: newListItemPath
4214
+ }); // Move children *after* selection to the new li
4215
+
4216
+ var fromPath = isAtStart ? paragraphPath : slate.Path.next(paragraphPath);
4217
+ var fromStartIndex = fromPath[fromPath.length - 1] || 0; // On split we don't add paragraph to the new li so we move
4218
+ // content to the very beginning. Otherwise, account for the empty
4219
+ // paragraph at the beginning by moving the content after
4220
+
4221
+ var toPath = newListItemPath.concat([shouldSplit ? 0 : 1]);
4222
+
4223
+ if (!isAtStartOfListItem) {
4224
+ plateCore.moveChildren(editor, {
4225
+ at: listItemPath,
4226
+ to: toPath,
4227
+ fromStartIndex: fromStartIndex
4228
+ });
4229
+ } // Move cursor to the start of the new li
4230
+
4231
+
4232
+ slate.Transforms.select(editor, newListItemPath);
4233
+ slate.Transforms.collapse(editor, {
4234
+ edge: 'start'
4235
+ });
4236
+ }); // Returning True skips processing other editor.insertBreak handlers
4237
+
4238
+ return true;
4239
+ };
4240
+
4241
+ /**
4242
+ * Credit: Copied & modified version from Plate's list plugin to support
4243
+ * list items with multiple children.
4244
+ *
4245
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
4246
+ */
4247
+
4248
+ var listBreak = function listBreak(editor) {
4249
+ if (!editor.selection) return false;
4250
+ var res = plateList.getListItemEntry(editor, {});
4251
+ var moved; // If selection is in a li
4252
+
4253
+ if (res) {
4254
+ var list = res.list,
4255
+ listItem = res.listItem;
4256
+ var childNode = listItem[0].children[0]; // If selected li is empty, move it up.
4257
+
4258
+ if (plateCore.isBlockAboveEmpty(editor) && listItem[0].children.length === 1 && Contentful.TEXT_CONTAINERS.includes(childNode.type)) {
4259
+ moved = plateList.moveListItemUp(editor, {
4260
+ list: list,
4261
+ listItem: listItem
4262
+ });
4263
+ if (moved) return true;
4264
+ }
4265
+ }
4266
+
4267
+ var didReset = plateResetNode.onKeyDownResetNode(editor, plateCore.mockPlugin({
4268
+ options: {
4269
+ rules: [{
4270
+ types: [plateCore.getPluginType(editor, plateList.ELEMENT_LI)],
4271
+ defaultType: plateCore.getPluginType(editor, plateCore.ELEMENT_DEFAULT),
4272
+ predicate: function predicate() {
4273
+ return !moved && plateCore.isBlockAboveEmpty(editor);
4274
+ },
4275
+ onReset: function onReset(_editor) {
4276
+ return plateList.unwrapList(_editor);
4277
+ }
4278
+ }]
4279
+ }
4280
+ }))(plateResetNode.SIMULATE_BACKSPACE);
4281
+
4282
+ if (didReset) {
4283
+ return true;
4284
+ }
3795
4285
  /**
3796
- * Removes the "empty" leading lis. Empty in this context means lis only with other lis as children.
3797
- *
3798
- * @returns If argument is not a list root, returns it, otherwise returns ul[] or li[].
4286
+ * If selection is in li > p, insert li.
3799
4287
  */
3800
4288
 
3801
4289
 
3802
- var trimList = function trimList(listRoot) {
3803
- if (!isListRoot(listRoot)) {
3804
- return [listRoot];
3805
- }
4290
+ if (!moved) {
4291
+ var inserted = insertListItem(editor);
4292
+ if (inserted) return true;
4293
+ }
3806
4294
 
3807
- var textEntries = Array.from(slate.Node.texts(listRoot));
3808
- var commonAncestorEntry = textEntries.reduce(function (commonAncestor, textEntry) {
3809
- return slate.Path.isAncestor(commonAncestor[1], textEntry[1]) ? commonAncestor : slate.Node.common(listRoot, textEntry[1], commonAncestor[1]);
3810
- }, // any list item would do, we grab the first one
3811
- getFirstAncestorOfType(listRoot, textEntries[0], li));
3812
- return isListRoot(commonAncestorEntry[0]) ? commonAncestorEntry[0].children : [commonAncestorEntry[0]];
4295
+ return false;
4296
+ };
4297
+
4298
+ var insertListBreak = function insertListBreak(editor) {
4299
+ var insertBreak = editor.insertBreak;
4300
+ return function () {
4301
+ if (listBreak(editor)) return;
4302
+ insertBreak();
3813
4303
  };
4304
+ };
4305
+
4306
+ /**
4307
+ * Credit: Modified version of Plate's list plugin
4308
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
4309
+ */
4310
+
4311
+ var getFirstAncestorOfType = function getFirstAncestorOfType(root, entry) {
4312
+ var ancestor = slate.Path.parent(entry[1]);
4313
+
4314
+ while (slate.Node.get(root, ancestor).type !== Contentful.BLOCKS.LIST_ITEM) {
4315
+ ancestor = slate.Path.parent(ancestor);
4316
+ }
4317
+
4318
+ return [slate.Node.get(root, ancestor), ancestor];
4319
+ };
4320
+
4321
+ var isListRoot = function isListRoot(node) {
4322
+ return [Contentful.BLOCKS.UL_LIST, Contentful.BLOCKS.OL_LIST].includes(node.type);
4323
+ };
4324
+ /**
4325
+ * Removes the "empty" leading lis. Empty in this context means lis only with other lis as children.
4326
+ *
4327
+ * @returns If argument is not a list root, returns it, otherwise returns ul[] or li[].
4328
+ */
4329
+
4330
+
4331
+ var trimList = function trimList(listRoot) {
4332
+ if (!isListRoot(listRoot)) {
4333
+ return [listRoot];
4334
+ }
4335
+
4336
+ var textEntries = Array.from(slate.Node.texts(listRoot));
4337
+ var commonAncestorEntry = textEntries.reduce(function (commonAncestor, textEntry) {
4338
+ return slate.Path.isAncestor(commonAncestor[1], textEntry[1]) ? commonAncestor : slate.Node.common(listRoot, textEntry[1], commonAncestor[1]);
4339
+ }, // any list item would do, we grab the first one
4340
+ getFirstAncestorOfType(listRoot, textEntries[0]));
4341
+ return isListRoot(commonAncestorEntry[0]) ? commonAncestorEntry[0].children : [commonAncestorEntry[0]];
4342
+ };
4343
+ /**
4344
+ * Removes leading li when pasting a single li with a single child.
4345
+ */
4346
+
4347
+
4348
+ var trimLiWrapper = function trimLiWrapper(nodes) {
4349
+ if (nodes.length !== 1) {
4350
+ return nodes;
4351
+ }
4352
+
4353
+ var node = nodes[0];
4354
+
4355
+ if (node.type !== Contentful.BLOCKS.LIST_ITEM || node.children.length !== 1) {
4356
+ return nodes;
4357
+ }
4358
+
4359
+ return node.children;
4360
+ };
4361
+
4362
+ var unwrapTextContainerAtStart = function unwrapTextContainerAtStart(nodes) {
4363
+ var node = nodes[0];
4364
+
4365
+ if (Contentful.TEXT_CONTAINERS.includes(node.type)) {
4366
+ return [].concat(node.children, nodes.slice(1));
4367
+ }
3814
4368
 
4369
+ return nodes;
4370
+ };
4371
+
4372
+ var insertListFragment = function insertListFragment(editor) {
4373
+ var insertFragment = editor.insertFragment;
3815
4374
  return function (fragment) {
4375
+ if (!editor.selection) {
4376
+ return;
4377
+ }
4378
+
3816
4379
  var liEntry = plateCore.findNode(editor, {
3817
4380
  match: {
3818
- type: li.type
4381
+ type: Contentful.BLOCKS.LIST_ITEM
3819
4382
  },
3820
4383
  mode: 'lowest'
3821
4384
  });
3822
4385
 
3823
4386
  if (liEntry) {
3824
- var liPath = liEntry[1]; // FIXME: this is a temporarily workaround and needs a follow-up to properly
3825
- // non-text elements
3826
-
3827
- var nodes = fragment.flatMap(function (node) {
4387
+ var nodes = unwrapTextContainerAtStart(trimLiWrapper(fragment.flatMap(function (node) {
3828
4388
  return trimList(node);
4389
+ })));
4390
+ var firstBlockIndex = nodes.findIndex(function (node) {
4391
+ return slate.Editor.isBlock(editor, node);
4392
+ });
4393
+
4394
+ if (firstBlockIndex < 0) {
4395
+ firstBlockIndex = nodes.length;
4396
+ }
4397
+
4398
+ var inlines = nodes.slice(0, firstBlockIndex);
4399
+ var blocks = nodes.slice(firstBlockIndex); // Two calls to insertNodes are required here. Otherwise, all blocks
4400
+ // after a text or inline element occurrence will be unwrapped for
4401
+ // some reason.
4402
+
4403
+ slate.Transforms.insertNodes(editor, inlines, {
4404
+ at: editor.selection,
4405
+ select: true
3829
4406
  });
3830
- return slate.Transforms.insertNodes(editor, nodes, {
3831
- at: editor.selection || slate.Path.next(liPath),
4407
+ return slate.Transforms.insertNodes(editor, blocks, {
4408
+ at: editor.selection,
3832
4409
  select: true
3833
4410
  });
3834
4411
  }
@@ -3840,18 +4417,98 @@ var getListInsertFragment = function getListInsertFragment(editor) {
3840
4417
  };
3841
4418
  };
3842
4419
 
3843
- var options = {
3844
- validLiChildrenTypes: Contentful.LIST_ITEM_BLOCKS
4420
+ /**
4421
+ * Credit: Modified version of Plate's list plugin
4422
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
4423
+ */
4424
+ var deleteBackwardList = function deleteBackwardList(editor, unit) {
4425
+ var res = plateList.getListItemEntry(editor, {});
4426
+ var moved = false;
4427
+
4428
+ if (res) {
4429
+ var list = res.list,
4430
+ listItem = res.listItem;
4431
+
4432
+ if (plateCore.isSelectionAtBlockStart(editor, {
4433
+ match: function match(node) {
4434
+ return node.type === Contentful.BLOCKS.LIST_ITEM;
4435
+ }
4436
+ })) {
4437
+ slate.Editor.withoutNormalizing(editor, function () {
4438
+ moved = plateList.removeFirstListItem(editor, {
4439
+ list: list,
4440
+ listItem: listItem
4441
+ });
4442
+ if (moved) return;
4443
+ moved = plateList.removeListItem(editor, {
4444
+ list: list,
4445
+ listItem: listItem
4446
+ });
4447
+ if (moved) return;
4448
+
4449
+ if (plateCore.isFirstChild(listItem[1]) && !plateList.isListNested(editor, list[1])) {
4450
+ plateResetNode.onKeyDownResetNode(editor, plateCore.mockPlugin({
4451
+ options: {
4452
+ rules: [{
4453
+ types: [Contentful.BLOCKS.LIST_ITEM],
4454
+ defaultType: Contentful.BLOCKS.PARAGRAPH,
4455
+ hotkey: 'backspace',
4456
+ predicate: function predicate() {
4457
+ return plateCore.isSelectionAtBlockStart(editor);
4458
+ },
4459
+ onReset: function onReset(e) {
4460
+ return unwrapList(e);
4461
+ }
4462
+ }]
4463
+ }
4464
+ }))(plateResetNode.SIMULATE_BACKSPACE);
4465
+ moved = true;
4466
+ return;
4467
+ }
4468
+
4469
+ plateCore.deleteFragment(editor, {
4470
+ unit: unit,
4471
+ reverse: true
4472
+ });
4473
+ moved = true;
4474
+ });
4475
+ }
4476
+ }
4477
+
4478
+ return moved;
3845
4479
  };
3846
- var withList = function withList(editor, plugin) {
3847
- var insertFragment = editor.insertFragment;
3848
- plateList.withList(editor, _extends({}, plugin, {
3849
- options: options
3850
- })); // Reverts any overrides to insertFragment
3851
4480
 
3852
- editor.insertFragment = insertFragment; // Use our custom getListInsertFragment
4481
+ /**
4482
+ * Credit: Modified version of Plate's list plugin
4483
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
4484
+ */
4485
+ var validLiChildrenTypes = Contentful.LIST_ITEM_BLOCKS;
4486
+ var withList = function withList(editor) {
4487
+ var deleteBackward = editor.deleteBackward,
4488
+ deleteForward = editor.deleteForward,
4489
+ deleteFragment = editor.deleteFragment;
4490
+
4491
+ editor.deleteBackward = function (unit) {
4492
+ if (deleteBackwardList(editor, unit)) return;
4493
+ deleteBackward(unit);
4494
+ };
4495
+
4496
+ editor.deleteForward = function (unit) {
4497
+ if (plateList.deleteForwardList(editor)) return;
4498
+ deleteForward(unit);
4499
+ };
4500
+
4501
+ editor.deleteFragment = function () {
4502
+ if (plateList.deleteFragmentList(editor)) return;
4503
+ deleteFragment();
4504
+ };
4505
+
4506
+ editor.insertBreak = insertListBreak(editor);
4507
+ editor.insertFragment = insertListFragment(editor); // TODO: replace with Normalizer rules
3853
4508
 
3854
- editor.insertFragment = getListInsertFragment(editor);
4509
+ editor.normalizeNode = plateList.normalizeList(editor, {
4510
+ validLiChildrenTypes: validLiChildrenTypes
4511
+ });
3855
4512
  return editor;
3856
4513
  };
3857
4514
 
@@ -3869,17 +4526,24 @@ var createListPlugin = function createListPlugin() {
3869
4526
  overrideByKey: (_overrideByKey = {}, _overrideByKey[plateList.ELEMENT_UL] = {
3870
4527
  type: Contentful.BLOCKS.UL_LIST,
3871
4528
  component: ListUL,
4529
+ handlers: {
4530
+ onKeyDown: onKeyDownList
4531
+ },
3872
4532
  // The withList is added on ELEMENT_UL plugin in upstream code
3873
4533
  // so we need to override it here
3874
4534
  withOverrides: withList
3875
4535
  }, _overrideByKey[plateList.ELEMENT_OL] = {
3876
4536
  type: Contentful.BLOCKS.OL_LIST,
3877
- component: ListOL
4537
+ component: ListOL,
4538
+ handlers: {
4539
+ onKeyDown: onKeyDownList
4540
+ }
3878
4541
  }, _overrideByKey[plateList.ELEMENT_LIC] = {
3879
4542
  type: Contentful.BLOCKS.PARAGRAPH
3880
4543
  }, _overrideByKey[plateList.ELEMENT_LI] = {
3881
4544
  type: Contentful.BLOCKS.LIST_ITEM,
3882
4545
  component: ListItem,
4546
+ // @ts-expect-error
3883
4547
  normalizer: [{
3884
4548
  validNode: hasListAsDirectParent,
3885
4549
  transform: normalizeOrphanedListItem
@@ -3889,6 +4553,9 @@ var createListPlugin = function createListPlugin() {
3889
4553
  }, {
3890
4554
  validChildren: Contentful.LIST_ITEM_BLOCKS,
3891
4555
  transform: transformParagraphs
4556
+ }, {
4557
+ validNode: firstNodeIsNotList,
4558
+ transform: replaceNodeWithListItems
3892
4559
  }]
3893
4560
  }, _overrideByKey)
3894
4561
  });
@@ -3901,15 +4568,10 @@ function ToolbarListButton(props) {
3901
4568
  function handleClick(type) {
3902
4569
  return function () {
3903
4570
  if (!(editor != null && editor.selection)) return;
3904
-
3905
- if (shouldUnwrapBlockquote(editor, type)) {
3906
- unwrapFromRoot(editor);
3907
- }
3908
-
3909
- plateList.toggleList(editor, {
4571
+ toggleList(editor, {
3910
4572
  type: type
3911
4573
  });
3912
- Slate.ReactEditor.focus(editor);
4574
+ focus(editor);
3913
4575
  };
3914
4576
  }
3915
4577
 
@@ -3918,37 +4580,73 @@ function ToolbarListButton(props) {
3918
4580
  title: "UL",
3919
4581
  testId: "ul-toolbar-button",
3920
4582
  onClick: handleClick(Contentful.BLOCKS.UL_LIST),
3921
- isActive: isBlockSelected(editor, Contentful.BLOCKS.UL_LIST),
4583
+ isActive: isListTypeActive(editor, Contentful.BLOCKS.UL_LIST),
3922
4584
  isDisabled: props.isDisabled
3923
4585
  }, /*#__PURE__*/React.createElement(f36Icons.ListBulletedIcon, null)), isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.OL_LIST) && /*#__PURE__*/React.createElement(ToolbarButton, {
3924
4586
  title: "OL",
3925
4587
  testId: "ol-toolbar-button",
3926
4588
  onClick: handleClick(Contentful.BLOCKS.OL_LIST),
3927
- isActive: isBlockSelected(editor, Contentful.BLOCKS.OL_LIST),
4589
+ isActive: isListTypeActive(editor, Contentful.BLOCKS.OL_LIST),
3928
4590
  isDisabled: props.isDisabled
3929
4591
  }, /*#__PURE__*/React.createElement(f36Icons.ListNumberedIcon, null)));
3930
4592
  }
3931
4593
 
3932
- function ToolbarBoldButton(props) {
3933
- var editor = useContentfulEditor();
4594
+ var createMarkToolbarButton = function createMarkToolbarButton(_ref) {
4595
+ var mark = _ref.mark,
4596
+ title = _ref.title,
4597
+ icon = _ref.icon;
3934
4598
 
3935
- function handleClick() {
3936
- if (!(editor != null && editor.selection)) return;
3937
- plateCore.toggleMark(editor, {
3938
- key: Contentful.MARKS.BOLD
3939
- });
3940
- Slate.ReactEditor.focus(editor);
3941
- }
4599
+ var Mark = function Mark(_ref2) {
4600
+ var isDisabled = _ref2.isDisabled;
4601
+ var editor = useContentfulEditor();
4602
+ var handleClick = React.useCallback(function () {
4603
+ if (!(editor != null && editor.selection)) return;
4604
+ var isActive = plateCore.isMarkActive(editor, mark);
4605
+ editor.tracking.onToolbarAction(isActive ? 'unmark' : 'mark', {
4606
+ markType: mark
4607
+ });
4608
+ plateCore.toggleMark(editor, {
4609
+ key: mark
4610
+ });
4611
+ focus(editor);
4612
+ }, [editor]);
4613
+ if (!editor) return null;
4614
+ return /*#__PURE__*/React.createElement(ToolbarButton, {
4615
+ title: title,
4616
+ testId: mark + "-toolbar-button",
4617
+ onClick: handleClick,
4618
+ isActive: plateCore.isMarkActive(editor, mark),
4619
+ isDisabled: isDisabled
4620
+ }, icon);
4621
+ };
3942
4622
 
3943
- if (!editor) return null;
3944
- return /*#__PURE__*/React.createElement(ToolbarButton, {
3945
- title: "Bold",
3946
- testId: "bold-toolbar-button",
3947
- onClick: handleClick,
3948
- isActive: plateCore.isMarkActive(editor, Contentful.MARKS.BOLD),
3949
- isDisabled: props.isDisabled
3950
- }, /*#__PURE__*/React.createElement(f36Icons.FormatBoldIcon, null));
3951
- }
4623
+ Mark.displayName = mark;
4624
+ return Mark;
4625
+ };
4626
+
4627
+ var buildMarkEventHandler = function buildMarkEventHandler(type) {
4628
+ return function (editor, _ref) {
4629
+ var hotkey = _ref.options.hotkey;
4630
+ return function (event) {
4631
+ if (editor.selection && hotkey && isHotkey(hotkey, event)) {
4632
+ event.preventDefault();
4633
+ var isActive = plateCore.isMarkActive(editor, type);
4634
+ editor.tracking.onShortcutAction(isActive ? 'unmark' : 'mark', {
4635
+ markType: type
4636
+ });
4637
+ plateCore.toggleMark(editor, {
4638
+ key: type
4639
+ });
4640
+ }
4641
+ };
4642
+ };
4643
+ };
4644
+
4645
+ var ToolbarBoldButton = /*#__PURE__*/createMarkToolbarButton({
4646
+ title: 'Bold',
4647
+ mark: Contentful.MARKS.BOLD,
4648
+ icon: /*#__PURE__*/React.createElement(f36Icons.FormatBoldIcon, null)
4649
+ });
3952
4650
  var styles$e = {
3953
4651
  bold: /*#__PURE__*/emotion.css({
3954
4652
  fontWeight: 600
@@ -3971,6 +4669,9 @@ var createBoldPlugin = function createBoldPlugin() {
3971
4669
  options: {
3972
4670
  hotkey: ['mod+b']
3973
4671
  },
4672
+ handlers: {
4673
+ onKeyDown: buildMarkEventHandler(Contentful.MARKS.BOLD)
4674
+ },
3974
4675
  deserializeHtml: {
3975
4676
  rules: [{
3976
4677
  validNodeName: ['STRONG', 'B']
@@ -3985,29 +4686,14 @@ var createBoldPlugin = function createBoldPlugin() {
3985
4686
  });
3986
4687
  }
3987
4688
  }
3988
- });
3989
- };
3990
-
3991
- function ToolbarCodeButton(props) {
3992
- var editor = useContentfulEditor();
3993
-
3994
- function handleClick() {
3995
- if (!(editor != null && editor.selection)) return;
3996
- plateCore.toggleMark(editor, {
3997
- key: Contentful.MARKS.CODE
3998
- });
3999
- Slate.ReactEditor.focus(editor);
4000
- }
4001
-
4002
- if (!editor) return null;
4003
- return /*#__PURE__*/React.createElement(ToolbarButton, {
4004
- title: "Code",
4005
- testId: "code-toolbar-button",
4006
- onClick: handleClick,
4007
- isActive: plateCore.isMarkActive(editor, Contentful.MARKS.CODE),
4008
- isDisabled: props.isDisabled
4009
- }, /*#__PURE__*/React.createElement(f36Icons.CodeIcon, null));
4010
- }
4689
+ });
4690
+ };
4691
+
4692
+ var ToolbarCodeButton = /*#__PURE__*/createMarkToolbarButton({
4693
+ title: 'Code',
4694
+ mark: Contentful.MARKS.CODE,
4695
+ icon: /*#__PURE__*/React.createElement(f36Icons.CodeIcon, null)
4696
+ });
4011
4697
  var styles$f = {
4012
4698
  code: /*#__PURE__*/emotion.css({
4013
4699
  fontFamily: 'monospace',
@@ -4026,6 +4712,9 @@ var createCodePlugin = function createCodePlugin() {
4026
4712
  options: {
4027
4713
  hotkey: ['mod+/']
4028
4714
  },
4715
+ handlers: {
4716
+ onKeyDown: buildMarkEventHandler(Contentful.MARKS.CODE)
4717
+ },
4029
4718
  deserializeHtml: {
4030
4719
  rules: [{
4031
4720
  validNodeName: ['CODE', 'PRE']
@@ -4038,26 +4727,11 @@ var createCodePlugin = function createCodePlugin() {
4038
4727
  });
4039
4728
  };
4040
4729
 
4041
- function ToolbarItalicButton(props) {
4042
- var editor = useContentfulEditor();
4043
-
4044
- function handleClick() {
4045
- if (!(editor != null && editor.selection)) return;
4046
- plateCore.toggleMark(editor, {
4047
- key: Contentful.MARKS.ITALIC
4048
- });
4049
- Slate.ReactEditor.focus(editor);
4050
- }
4051
-
4052
- if (!editor) return null;
4053
- return /*#__PURE__*/React.createElement(ToolbarButton, {
4054
- title: "Italic",
4055
- testId: "italic-toolbar-button",
4056
- onClick: handleClick,
4057
- isActive: plateCore.isMarkActive(editor, Contentful.MARKS.ITALIC),
4058
- isDisabled: props.isDisabled
4059
- }, /*#__PURE__*/React.createElement(f36Icons.FormatItalicIcon, null));
4060
- }
4730
+ var ToolbarItalicButton = /*#__PURE__*/createMarkToolbarButton({
4731
+ title: 'Italic',
4732
+ mark: Contentful.MARKS.ITALIC,
4733
+ icon: /*#__PURE__*/React.createElement(f36Icons.FormatItalicIcon, null)
4734
+ });
4061
4735
  var styles$g = {
4062
4736
  italic: /*#__PURE__*/emotion.css({
4063
4737
  fontStyle: 'italic'
@@ -4075,6 +4749,9 @@ var createItalicPlugin = function createItalicPlugin() {
4075
4749
  options: {
4076
4750
  hotkey: ['mod+i']
4077
4751
  },
4752
+ handlers: {
4753
+ onKeyDown: buildMarkEventHandler(Contentful.MARKS.ITALIC)
4754
+ },
4078
4755
  deserializeHtml: {
4079
4756
  rules: [{
4080
4757
  validNodeName: ['I', 'EM']
@@ -4092,26 +4769,11 @@ var createItalicPlugin = function createItalicPlugin() {
4092
4769
  });
4093
4770
  };
4094
4771
 
4095
- function ToolbarUnderlineButton(props) {
4096
- var editor = useContentfulEditor();
4097
-
4098
- function handleClick() {
4099
- if (!(editor != null && editor.selection)) return;
4100
- plateCore.toggleMark(editor, {
4101
- key: Contentful.MARKS.UNDERLINE
4102
- });
4103
- Slate.ReactEditor.focus(editor);
4104
- }
4105
-
4106
- if (!editor) return null;
4107
- return /*#__PURE__*/React.createElement(ToolbarButton, {
4108
- title: "Underline",
4109
- testId: "underline-toolbar-button",
4110
- onClick: handleClick,
4111
- isActive: plateCore.isMarkActive(editor, Contentful.MARKS.UNDERLINE),
4112
- isDisabled: props.isDisabled
4113
- }, /*#__PURE__*/React.createElement(f36Icons.FormatUnderlinedIcon, null));
4114
- }
4772
+ var ToolbarUnderlineButton = /*#__PURE__*/createMarkToolbarButton({
4773
+ title: 'Underline',
4774
+ mark: Contentful.MARKS.UNDERLINE,
4775
+ icon: /*#__PURE__*/React.createElement(f36Icons.FormatUnderlinedIcon, null)
4776
+ });
4115
4777
  function Underline(props) {
4116
4778
  return /*#__PURE__*/React.createElement("u", Object.assign({}, props.attributes), props.children);
4117
4779
  }
@@ -4122,6 +4784,9 @@ var createUnderlinePlugin = function createUnderlinePlugin() {
4122
4784
  options: {
4123
4785
  hotkey: ['mod+u']
4124
4786
  },
4787
+ handlers: {
4788
+ onKeyDown: buildMarkEventHandler(Contentful.MARKS.UNDERLINE)
4789
+ },
4125
4790
  deserializeHtml: {
4126
4791
  rules: [{
4127
4792
  validNodeName: ['U']
@@ -4344,6 +5009,7 @@ var withNormalizer = function withNormalizer(editor) {
4344
5009
  var createNormalizerPlugin = function createNormalizerPlugin() {
4345
5010
  return {
4346
5011
  key: 'NormalizerPlugin',
5012
+ // @ts-expect-error
4347
5013
  withOverrides: withNormalizer
4348
5014
  };
4349
5015
  };
@@ -4363,6 +5029,18 @@ function isEmptyElement(element) {
4363
5029
  return element.textContent === '';
4364
5030
  }
4365
5031
 
5032
+ var buildParagraphKeyDownHandler = function buildParagraphKeyDownHandler(editor, _ref) {
5033
+ var hotkey = _ref.options.hotkey;
5034
+ return function (event) {
5035
+ if (editor.selection && hotkey && isHotkey(hotkey, event)) {
5036
+ toggleElement(editor, {
5037
+ activeType: Contentful.BLOCKS.PARAGRAPH,
5038
+ inactiveType: Contentful.BLOCKS.PARAGRAPH
5039
+ });
5040
+ }
5041
+ };
5042
+ };
5043
+
4366
5044
  var createParagraphPlugin = function createParagraphPlugin() {
4367
5045
  var _transform;
4368
5046
 
@@ -4372,6 +5050,9 @@ var createParagraphPlugin = function createParagraphPlugin() {
4372
5050
  options: {
4373
5051
  hotkey: ['mod+opt+0']
4374
5052
  },
5053
+ handlers: {
5054
+ onKeyDown: buildParagraphKeyDownHandler
5055
+ },
4375
5056
  softBreak: [// create a new line with SHIFT+Enter inside a paragraph
4376
5057
  {
4377
5058
  hotkey: 'shift+enter',
@@ -4388,8 +5069,8 @@ var createParagraphPlugin = function createParagraphPlugin() {
4388
5069
  }
4389
5070
  },
4390
5071
  normalizer: [{
4391
- validChildren: function validChildren(_, _ref) {
4392
- var node = _ref[0];
5072
+ validChildren: function validChildren(_, _ref2) {
5073
+ var node = _ref2[0];
4393
5074
  return isInlineOrText(node);
4394
5075
  },
4395
5076
  transform: (_transform = {}, _transform[Contentful.BLOCKS.PARAGRAPH] = transformUnwrap, _transform["default"] = transformLift, _transform)
@@ -4482,16 +5163,59 @@ var sanitizeHTML = function sanitizeHTML(html) {
4482
5163
  // Parse the HTML string and pipe it through our transformers
4483
5164
  var doc = transformers.reduce(function (value, cb) {
4484
5165
  return cb(value);
4485
- }, new DOMParser().parseFromString(html, 'text/html')); // Remove whitespace between tags
5166
+ }, new DOMParser().parseFromString(html, 'text/html'));
5167
+ return doc.body.innerHTML.replace(/>\s+</g, '><') // Remove whitespace between tags
5168
+ .replace(/(.*)<div.*>(<table.*<\/table>)<\/div>(.*)/g, '$1$2$3'); // remove div containers from tables
5169
+ };
5170
+
5171
+ /**
5172
+ * Get x-slate-fragment attribute from data-slate-fragment
5173
+ */
4486
5174
 
4487
- return doc.body.innerHTML.replace(/>\s+</g, '><');
5175
+ var catchSlateFragment = /data-slate-fragment="(.+?)"/m;
5176
+ var getSlateFragmentAttribute = function getSlateFragmentAttribute(dataTransfer) {
5177
+ var htmlData = dataTransfer.getData('text/html');
5178
+
5179
+ var _ref = htmlData.match(catchSlateFragment) || [],
5180
+ fragment = _ref[1];
5181
+
5182
+ return fragment;
4488
5183
  };
5184
+ /**
5185
+ * Get the x-slate-fragment attribute that exist in text/html data
5186
+ * and append it to the DataTransfer object
5187
+ */
5188
+
5189
+ var ensureXSlateFragment = function ensureXSlateFragment(dataTransfer) {
5190
+ if (!dataTransfer.getData('application/x-slate-fragment')) {
5191
+ var fragment = getSlateFragmentAttribute(dataTransfer);
5192
+
5193
+ if (fragment) {
5194
+ var clipboardData = new DataTransfer();
5195
+ dataTransfer.types.forEach(function (type) {
5196
+ clipboardData.setData(type, dataTransfer.getData(type));
5197
+ });
5198
+ clipboardData.setData('application/x-slate-fragment', fragment);
5199
+ return clipboardData;
5200
+ }
5201
+ }
4489
5202
 
5203
+ return dataTransfer;
5204
+ };
4490
5205
  var createPasteHTMLPlugin = function createPasteHTMLPlugin() {
4491
5206
  var _pluginsByKey;
4492
5207
 
4493
5208
  return {
4494
5209
  key: 'PasteHTMLPlugin',
5210
+ withOverrides: function withOverrides(editor) {
5211
+ var insertData = editor.insertData;
5212
+
5213
+ editor.insertData = function (data) {
5214
+ return insertData(ensureXSlateFragment(data));
5215
+ };
5216
+
5217
+ return editor;
5218
+ },
4495
5219
  inject: {
4496
5220
  pluginsByKey: (_pluginsByKey = {}, _pluginsByKey[plateCore.KEY_DESERIALIZE_HTML] = {
4497
5221
  editor: {
@@ -4510,10 +5234,7 @@ var style$1 = /*#__PURE__*/emotion.css({
4510
5234
  margin: '0 0 1.3125rem',
4511
5235
  borderLeft: "6px solid " + tokens.gray200,
4512
5236
  paddingLeft: '0.875rem',
4513
- fontStyle: 'normal',
4514
- '& a': {
4515
- color: 'inherit'
4516
- }
5237
+ fontStyle: 'normal'
4517
5238
  });
4518
5239
  function Quote(props) {
4519
5240
  return /*#__PURE__*/React.createElement("blockquote", Object.assign({}, props.attributes, {
@@ -4521,45 +5242,61 @@ function Quote(props) {
4521
5242
  }), props.children);
4522
5243
  }
4523
5244
 
4524
- function createQuotePlugin() {
4525
- var _transform;
5245
+ /**
5246
+ * Returns true if we are:
5247
+ * 1) Inside a blockquote
5248
+ * 2) With no only one child paragraph/heading and
5249
+ * 3) that child is empty
5250
+ */
4526
5251
 
4527
- return {
4528
- key: Contentful.BLOCKS.QUOTE,
4529
- type: Contentful.BLOCKS.QUOTE,
4530
- isElement: true,
4531
- component: Quote,
4532
- options: {
4533
- hotkey: 'mod+shift+1'
4534
- },
4535
- handlers: {
4536
- onKeyDown: plateCore.onKeyDownToggleElement
5252
+ var shouldResetQuoteOnBackspace = function shouldResetQuoteOnBackspace(editor) {
5253
+ var container = plateCore.getAbove(editor, {
5254
+ match: {
5255
+ type: Contentful.TEXT_CONTAINERS
4537
5256
  },
4538
- deserializeHtml: {
4539
- rules: [{
4540
- validNodeName: 'BLOCKQUOTE'
4541
- }]
5257
+ mode: 'lowest'
5258
+ });
5259
+
5260
+ if (!container) {
5261
+ return false;
5262
+ }
5263
+
5264
+ if (!plateCore.isAncestorEmpty(editor, container[0])) {
5265
+ return false;
5266
+ }
5267
+
5268
+ var quote = plateCore.getBlockAbove(editor, {
5269
+ match: {
5270
+ type: Contentful.BLOCKS.QUOTE
4542
5271
  },
4543
- normalizer: [{
4544
- validChildren: Contentful.CONTAINERS[Contentful.BLOCKS.QUOTE],
4545
- transform: (_transform = {}, _transform[Contentful.BLOCKS.QUOTE] = transformUnwrap, _transform[Contentful.BLOCKS.HEADING_1] = transformUnwrap, _transform[Contentful.BLOCKS.HEADING_2] = transformUnwrap, _transform[Contentful.BLOCKS.HEADING_3] = transformUnwrap, _transform[Contentful.BLOCKS.HEADING_4] = transformUnwrap, _transform[Contentful.BLOCKS.HEADING_5] = transformUnwrap, _transform[Contentful.BLOCKS.HEADING_6] = transformUnwrap, _transform["default"] = transformLift, _transform)
4546
- }]
4547
- };
4548
- }
5272
+ mode: 'lowest'
5273
+ });
5274
+
5275
+ if (!quote) {
5276
+ return false;
5277
+ }
5278
+
5279
+ if (plateCore.hasSingleChild(quote[0]) && plateCore.isLastChild(quote, container[1])) {
5280
+ return true;
5281
+ }
4549
5282
 
4550
- function toggleQuote(editor) {
5283
+ return false;
5284
+ };
5285
+
5286
+ function toggleQuote(editor, logAction) {
4551
5287
  if (!editor.selection) return;
4552
5288
  var isActive = isBlockSelected(editor, Contentful.BLOCKS.QUOTE);
5289
+ logAction == null ? void 0 : logAction(isActive ? 'remove' : 'insert', {
5290
+ nodeType: Contentful.BLOCKS.QUOTE
5291
+ });
4553
5292
  slate.Editor.withoutNormalizing(editor, function () {
5293
+ if (!editor.selection) return;
4554
5294
  slate.Transforms.unwrapNodes(editor, {
4555
5295
  match: function match(node) {
4556
5296
  return slate.Element.isElement(node) && node.type === Contentful.BLOCKS.QUOTE;
4557
5297
  },
4558
5298
  split: true
4559
5299
  });
4560
- slate.Transforms.setNodes(editor, {
4561
- type: isActive ? Contentful.BLOCKS.PARAGRAPH : Contentful.BLOCKS.QUOTE
4562
- });
4563
5300
 
4564
5301
  if (!isActive) {
4565
5302
  var quote = {
@@ -4571,14 +5308,106 @@ function toggleQuote(editor) {
4571
5308
  }
4572
5309
  });
4573
5310
  }
5311
+ var onKeyDownToggleQuote = function onKeyDownToggleQuote(editor, plugin) {
5312
+ return function (event) {
5313
+ var hotkey = plugin.options.hotkey;
5314
+
5315
+ if (hotkey && isHotkey(hotkey, event)) {
5316
+ event.preventDefault();
5317
+ toggleQuote(editor, editor.tracking.onShortcutAction);
5318
+ }
5319
+ };
5320
+ };
5321
+
5322
+ var withQuote = function withQuote(editor) {
5323
+ var insertFragment = editor.insertFragment;
5324
+
5325
+ editor.insertFragment = function (fragment) {
5326
+ var startingNode = fragment.length && fragment[0];
5327
+ var startsWithBlockquote = slate.Element.isElement(startingNode) && startingNode.type === Contentful.BLOCKS.QUOTE;
5328
+ var containerEntry = plateCore.getAbove(editor, {
5329
+ match: {
5330
+ type: Contentful.TEXT_CONTAINERS
5331
+ }
5332
+ });
5333
+ var containerIsNotEmpty = containerEntry && slate.Node.string(containerEntry[0]) !== '';
5334
+
5335
+ if (startsWithBlockquote && containerIsNotEmpty) {
5336
+ var selection = editor.selection;
5337
+
5338
+ var isContentSelected = function isContentSelected(selection) {
5339
+ return !!selection && slate.Point.compare(selection.anchor, selection.focus) !== 0;
5340
+ }; // if something is selected (highlighted) we replace the selection
5341
+
5342
+
5343
+ if (isContentSelected(selection)) {
5344
+ slate.Transforms["delete"](editor, {
5345
+ at: selection
5346
+ });
5347
+ } // get the cursor entry again, it may be different after deletion
5348
+
5349
+
5350
+ var _containerEntry = plateCore.getAbove(editor, {
5351
+ match: {
5352
+ type: Contentful.TEXT_CONTAINERS
5353
+ }
5354
+ });
5355
+
5356
+ var _containerIsNotEmpty = _containerEntry && slate.Node.string(_containerEntry[0]) !== '';
5357
+
5358
+ if (_containerIsNotEmpty) {
5359
+ slate.Transforms.insertNodes(editor, fragment);
5360
+ return;
5361
+ }
5362
+ }
5363
+
5364
+ insertFragment(fragment);
5365
+ };
5366
+
5367
+ return editor;
5368
+ };
5369
+
5370
+ function createQuotePlugin() {
5371
+ var _transform;
5372
+
5373
+ return {
5374
+ key: Contentful.BLOCKS.QUOTE,
5375
+ type: Contentful.BLOCKS.QUOTE,
5376
+ isElement: true,
5377
+ component: Quote,
5378
+ options: {
5379
+ hotkey: 'mod+shift+1'
5380
+ },
5381
+ handlers: {
5382
+ onKeyDown: onKeyDownToggleQuote
5383
+ },
5384
+ deserializeHtml: {
5385
+ rules: [{
5386
+ validNodeName: 'BLOCKQUOTE'
5387
+ }]
5388
+ },
5389
+ resetNode: [{
5390
+ // toggle off blockquote on backspace when it's empty
5391
+ hotkey: 'backspace',
5392
+ types: [Contentful.BLOCKS.QUOTE],
5393
+ predicate: shouldResetQuoteOnBackspace,
5394
+ onReset: toggleQuote
5395
+ }],
5396
+ normalizer: [{
5397
+ validChildren: Contentful.CONTAINERS[Contentful.BLOCKS.QUOTE],
5398
+ transform: (_transform = {}, _transform[Contentful.BLOCKS.QUOTE] = transformUnwrap, _transform["default"] = transformLift, _transform)
5399
+ }],
5400
+ withOverrides: withQuote
5401
+ };
5402
+ }
4574
5403
 
4575
5404
  function ToolbarQuoteButton(props) {
4576
5405
  var editor = useContentfulEditor();
4577
5406
 
4578
5407
  function handleOnClick() {
4579
5408
  if (!editor) return;
4580
- toggleQuote(editor);
4581
- Slate.ReactEditor.focus(editor);
5409
+ toggleQuote(editor, editor.tracking.onToolbarAction);
5410
+ focus(editor);
4582
5411
  }
4583
5412
 
4584
5413
  if (!editor) return null;
@@ -4595,87 +5424,192 @@ var createSelectOnBackspacePlugin = function createSelectOnBackspacePlugin() {
4595
5424
  return plateSelect.createSelectOnBackspacePlugin({
4596
5425
  options: {
4597
5426
  query: {
4598
- allow: [Contentful.BLOCKS.EMBEDDED_ASSET, Contentful.BLOCKS.EMBEDDED_ENTRY, Contentful.BLOCKS.HR, Contentful.INLINES.EMBEDDED_ENTRY]
5427
+ // `createTextPlugin` is taking care of block elements
5428
+ allow: [Contentful.INLINES.EMBEDDED_ENTRY]
4599
5429
  }
4600
5430
  }
4601
5431
  });
4602
5432
  };
4603
5433
 
4604
- function hasTables(nodes) {
4605
- return nodes.some(function (_ref) {
4606
- var type = _ref.type;
4607
- return type === Contentful.BLOCKS.TABLE;
4608
- });
4609
- }
5434
+ function getCaretTopPoint() {
5435
+ var sel = document.getSelection();
5436
+ if (!sel) return;
5437
+ var r = sel.getRangeAt(0);
5438
+ var rect;
5439
+ var r2; // supposed to be textNode in most cases
5440
+ // but div[contenteditable] when empty
5441
+
5442
+ var node = r.startContainer;
5443
+ var offset = r.startOffset;
5444
+
5445
+ if (offset > 0) {
5446
+ // new range, don't influence DOM state
5447
+ r2 = document.createRange();
5448
+ r2.setStart(node, offset - 1);
5449
+ r2.setEnd(node, offset); // https://developer.mozilla.org/en-US/docs/Web/API/range.getBoundingClientRect
5450
+ // IE9, Safari?(but look good in Safari 8)
5451
+
5452
+ rect = r2.getBoundingClientRect();
5453
+ return {
5454
+ left: rect.right,
5455
+ top: rect.top
5456
+ }; // @ts-expect-error
5457
+ } else if (offset < node.length) {
5458
+ r2 = document.createRange(); // similar but select next on letter
5459
+
5460
+ r2.setStart(node, offset);
5461
+ r2.setEnd(node, offset + 1);
5462
+ rect = r2.getBoundingClientRect();
5463
+ return {
5464
+ left: rect.left,
5465
+ top: rect.top
5466
+ };
5467
+ } else {
5468
+ // textNode has length
5469
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element.getBoundingClientRect
5470
+ // @ts-expect-error
5471
+ rect = node.getBoundingClientRect(); // @ts-expect-error
4610
5472
 
4611
- var isTableHeaderCell = function isTableHeaderCell(_ref2) {
4612
- var type = _ref2.type;
4613
- return type === Contentful.BLOCKS.TABLE_HEADER_CELL;
4614
- };
5473
+ var styles = getComputedStyle(node);
5474
+ var lineHeight = parseInt(styles.lineHeight);
5475
+ var fontSize = parseInt(styles.fontSize); // roughly half the whitespace... but not exactly
4615
5476
 
4616
- function hasHeadersOutsideFirstRow(nodes) {
4617
- return nodes.filter(function (_ref3) {
4618
- var type = _ref3.type;
4619
- return type === Contentful.BLOCKS.TABLE;
4620
- }).flatMap(function (_ref4) {
4621
- var children = _ref4.children;
4622
- return children.slice(1);
4623
- }).some(function (_ref5) {
4624
- var children = _ref5.children;
4625
- return children.some(isTableHeaderCell);
4626
- });
5477
+ var delta = (lineHeight - fontSize) / 2;
5478
+ return {
5479
+ left: rect.left,
5480
+ top: rect.top + delta
5481
+ };
5482
+ }
5483
+ }
5484
+ function closePanel(editorId) {
5485
+ document.dispatchEvent(new CustomEvent('close-rte-palette-commands', {
5486
+ detail: {
5487
+ editorId: editorId
5488
+ }
5489
+ }));
5490
+ }
5491
+ function openPanel(editorId) {
5492
+ document.dispatchEvent(new CustomEvent('open-rte-palette-commands', {
5493
+ detail: {
5494
+ editorId: editorId
5495
+ }
5496
+ }));
4627
5497
  }
4628
5498
 
4629
- function addTableTrackingEvents(editor, _ref6) {
4630
- var onViewportAction = _ref6.onViewportAction;
4631
- var insertData = editor.insertData;
4632
-
4633
- editor.insertData = function (data) {
4634
- var html = data.getData('text/html');
5499
+ // import debounce from 'lodash/debounce';
5500
+ var SLASH_COMMANDS_PLUGIN_KEY = 'SlashCommands'; // TODO: Explore a solution using marks and ReactDOM.createPortal to activate the commands panel
4635
5501
 
4636
- if (html) {
4637
- var markupBefore = editor.children;
4638
- insertData(data);
4639
- var markupAfter = editor.children;
4640
- setTimeout(function () {
4641
- if (hasTables(markupBefore)) return;
5502
+ function createSlashCommandsPlugin() {
5503
+ return {
5504
+ key: SLASH_COMMANDS_PLUGIN_KEY,
5505
+ type: SLASH_COMMANDS_PLUGIN_KEY,
5506
+ handlers: {
5507
+ onClick: function onClick(editor) {
5508
+ return function () {
5509
+ closePanel(editor.id);
5510
+ };
5511
+ },
5512
+ onKeyDown: function onKeyDown(editor) {
5513
+ return function (event) {
5514
+ closePanel(editor.id);
4642
5515
 
4643
- if (hasTables(markupAfter)) {
4644
- onViewportAction('paste', {
4645
- tablePasted: true,
4646
- hasHeadersOutsideFirstRow: hasHeadersOutsideFirstRow(markupAfter)
4647
- });
4648
- }
4649
- }, 1);
4650
- } else {
4651
- insertData(data);
5516
+ if (event.key === '/') {
5517
+ openPanel(editor.id);
5518
+ }
5519
+ };
5520
+ }
4652
5521
  }
4653
5522
  };
4654
5523
  }
4655
5524
 
4656
- function useTracking(_ref) {
4657
- var onAction = _ref.onAction;
4658
- var trackingMemo = React.useMemo(function () {
4659
- return {
4660
- onViewportAction: function onViewportAction(actionName, data) {
4661
- if (data === void 0) {
4662
- data = {};
4663
- }
5525
+ var style$2 = {
5526
+ container: function container(_ref) {
5527
+ var top = _ref.top,
5528
+ left = _ref.left;
5529
+ return emotion.css({
5530
+ position: 'fixed',
5531
+ top: top - 14,
5532
+ left: left + 10,
5533
+ zIndex: 1,
5534
+ boxShadow: '0 5px 15px rgba(0, 0, 0, 0.15)',
5535
+ borderRadius: '8px',
5536
+ userSelect: 'none'
5537
+ });
5538
+ }
5539
+ };
5540
+ function SlashCommandsPalette(_ref2) {
5541
+ var editorId = _ref2.editorId;
5542
+
5543
+ var _React$useState = React.useState(null),
5544
+ position = _React$useState[0],
5545
+ setPosition = _React$useState[1];
5546
+
5547
+ var _React$useState2 = React.useState(false),
5548
+ isOpen = _React$useState2[0],
5549
+ setIsOpen = _React$useState2[1]; // The user is not annoyed every time they type `/`
5550
+
5551
+
5552
+ var MAX_TRIES = 3;
5553
+
5554
+ var _React$useState3 = React.useState(0),
5555
+ currentTries = _React$useState3[0],
5556
+ setCurrentTries = _React$useState3[1];
5557
+
5558
+ React.useEffect(function () {
5559
+ function handler(event) {
5560
+ if (editorId !== event.detail.editorId) return;
5561
+ var topLeft = getCaretTopPoint();
5562
+ if (!topLeft) return;
5563
+ setPosition(topLeft);
5564
+ setIsOpen(true);
5565
+ setCurrentTries(function (curr) {
5566
+ return curr + 1;
5567
+ });
5568
+ }
5569
+
5570
+ document.addEventListener('open-rte-palette-commands', handler);
5571
+ return function () {
5572
+ document.removeEventListener('open-rte-palette-commands', handler);
5573
+ };
5574
+ }, [editorId]);
5575
+ React.useEffect(function () {
5576
+ function handler(event) {
5577
+ if (editorId !== event.detail.editorId) return;
5578
+ closePanel();
5579
+ }
5580
+
5581
+ document.addEventListener('close-rte-palette-commands', handler);
5582
+ return function () {
5583
+ return document.removeEventListener('close-rte-palette-commands', handler);
5584
+ };
5585
+ }, [editorId]);
5586
+ React.useEffect(function () {
5587
+ if (!isOpen) return;
5588
+
5589
+ function handler() {
5590
+ closePanel();
5591
+ }
5592
+
5593
+ window.addEventListener('resize', handler);
5594
+ window.addEventListener('scroll', handler, true);
5595
+ return function () {
5596
+ window.removeEventListener('resize', handler);
5597
+ window.removeEventListener('scroll', handler, true);
5598
+ };
5599
+ }, [isOpen]);
5600
+
5601
+ function closePanel() {
5602
+ setIsOpen(false);
5603
+ setPosition(null);
5604
+ }
4664
5605
 
4665
- return onAction(actionName, _extends({
4666
- origin: 'viewport-interaction'
4667
- }, data));
4668
- }
4669
- };
4670
- }, [] // eslint-disable-line
4671
- );
4672
- return trackingMemo;
5606
+ if (!isOpen || !position || currentTries > MAX_TRIES) return null;
5607
+ return /*#__PURE__*/React.createElement("div", {
5608
+ className: style$2.container(position),
5609
+ "data-test-id": "rte-slash-commands"
5610
+ }, /*#__PURE__*/React.createElement(f36Components.Card, null, /*#__PURE__*/React.createElement(f36Components.Text, null, "Slash commands are temporarily unavailable.")));
4673
5611
  }
4674
5612
 
4675
- var _constate$2 = /*#__PURE__*/constate(useTracking),
4676
- TrackingProvider = _constate$2[0],
4677
- useTrackingContext = _constate$2[1];
4678
-
4679
5613
  var addRow = function addRow(editor, getNextRowPath) {
4680
5614
  if (plateCore.someNode(editor, {
4681
5615
  match: {
@@ -4901,6 +5835,9 @@ var isNotEmpty = function isNotEmpty(editor, _ref4) {
4901
5835
  var path = _ref4[1];
4902
5836
  return Array.from(slate.Node.children(editor, path)).length !== 0;
4903
5837
  };
5838
+ var isTable = function isTable(node) {
5839
+ return slate.Element.isElement(node) && node.type === Contentful.BLOCKS.TABLE;
5840
+ };
4904
5841
 
4905
5842
  var styles$i = {
4906
5843
  topRight: /*#__PURE__*/emotion.css({
@@ -4921,9 +5858,6 @@ var TableActions = function TableActions() {
4921
5858
  var editor = useContentfulEditor();
4922
5859
  var isDisabled = Slate.useReadOnly();
4923
5860
 
4924
- var _useTrackingContext = useTrackingContext(),
4925
- onViewportAction = _useTrackingContext.onViewportAction;
4926
-
4927
5861
  var _React$useState = React__default.useState(false),
4928
5862
  isOpen = _React$useState[0],
4929
5863
  setOpen = _React$useState[1];
@@ -4934,11 +5868,7 @@ var TableActions = function TableActions() {
4934
5868
 
4935
5869
  var close = React__default.useCallback(function () {
4936
5870
  setOpen(false);
4937
- if (!editor) return; // Makes sure we keep the editor in focus when clicking on/out
4938
- // the dropdown menu
4939
-
4940
- Slate.ReactEditor.focus(editor);
4941
- }, [editor]);
5871
+ }, []);
4942
5872
  React__default.useEffect(function () {
4943
5873
  setHeaderEnabled(Boolean(editor && isTableHeaderEnabled(editor)));
4944
5874
  }, [editor]);
@@ -4977,11 +5907,11 @@ var TableActions = function TableActions() {
4977
5907
  }); // Tracking
4978
5908
 
4979
5909
  var actionName = type + "Table" + (element === 'Table' ? '' : element);
4980
- onViewportAction(actionName, {
5910
+ editor.tracking.onViewportAction(actionName, {
4981
5911
  tableSize: tableSize
4982
5912
  });
4983
5913
  };
4984
- }, [editor, isHeaderEnabled, close, onViewportAction]);
5914
+ }, [editor, isHeaderEnabled, close]);
4985
5915
 
4986
5916
  if (isDisabled) {
4987
5917
  return null;
@@ -5023,89 +5953,465 @@ var TableActions = function TableActions() {
5023
5953
  };
5024
5954
 
5025
5955
  var _templateObject$7;
5026
- var style$2 = /*#__PURE__*/emotion.css(_templateObject$7 || (_templateObject$7 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n border: 1px solid ", ";\n border-collapse: collapse;\n padding: 10px 12px;\n min-width: 48px;\n position: relative;\n vertical-align: top;\n\n div:last-child {\n margin-bottom: 0;\n }\n"])), tokens.gray400);
5956
+ var style$3 = /*#__PURE__*/emotion.css(_templateObject$7 || (_templateObject$7 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n border: 1px solid ", ";\n border-collapse: collapse;\n padding: 10px 12px;\n min-width: 48px;\n position: relative;\n vertical-align: top;\n\n div:last-child {\n margin-bottom: 0;\n }\n"])), tokens.gray400);
5027
5957
  var Cell = function Cell(props) {
5028
5958
  var isSelected = Slate.useSelected();
5029
5959
  return /*#__PURE__*/React.createElement("td", Object.assign({}, props.attributes, props.element.data, {
5030
- className: style$2
5960
+ className: style$3
5031
5961
  }), isSelected && /*#__PURE__*/React.createElement(TableActions, null), props.children);
5032
5962
  };
5033
5963
 
5034
5964
  var _templateObject$8;
5035
- var style$3 = /*#__PURE__*/emotion.css(_templateObject$8 || (_templateObject$8 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n background-color: ", ";\n border: 1px solid ", ";\n border-collapse: collapse;\n padding: 10px 12px;\n font-weight: ", ";\n text-align: left;\n min-width: 48px;\n position: relative;\n\n div:last-child {\n margin-bottom: 0;\n }\n"])), tokens.gray200, tokens.gray400, tokens.fontWeightMedium);
5965
+ var style$4 = /*#__PURE__*/emotion.css(_templateObject$8 || (_templateObject$8 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n background-clip: padding-box;\n background-color: ", ";\n border: 1px solid ", ";\n border-collapse: collapse;\n padding: 10px 12px;\n font-weight: ", ";\n text-align: left;\n min-width: 48px;\n position: relative;\n\n div:last-child {\n margin-bottom: 0;\n }\n"])), tokens.gray200, tokens.gray400, tokens.fontWeightNormal);
5036
5966
  var HeaderCell = function HeaderCell(props) {
5037
5967
  var isSelected = Slate.useSelected();
5038
5968
  return /*#__PURE__*/React.createElement("th", Object.assign({}, props.attributes, props.element.data, {
5039
- className: style$3
5969
+ className: style$4
5040
5970
  }), isSelected && /*#__PURE__*/React.createElement(TableActions, null), props.children);
5041
5971
  };
5042
5972
 
5043
5973
  var _templateObject$9;
5044
- var style$4 = /*#__PURE__*/emotion.css(_templateObject$9 || (_templateObject$9 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n border: 1px solid ", ";\n border-collapse: collapse;\n\n &:hover td {\n background-color: transparent !important;\n }\n"])), tokens.gray400);
5974
+ var style$5 = /*#__PURE__*/emotion.css(_templateObject$9 || (_templateObject$9 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n border: 1px solid ", ";\n border-collapse: collapse;\n\n &:hover td {\n background-color: transparent !important;\n }\n"])), tokens.gray400);
5045
5975
  var Row = function Row(props) {
5046
5976
  return /*#__PURE__*/React.createElement("tr", Object.assign({}, props.attributes, {
5047
- className: style$4
5977
+ className: style$5
5048
5978
  }), props.children);
5049
5979
  };
5050
5980
 
5051
- var _templateObject$a;
5052
- var style$5 = /*#__PURE__*/emotion.css(_templateObject$a || (_templateObject$a = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n margin-bottom: 1.5em;\n border-collapse: collapse;\n border-radius: 5px;\n border-style: hidden;\n box-shadow: 0 0 0 1px ", ";\n width: 100%;\n table-layout: fixed;\n overflow: hidden;\n"])), tokens.gray400);
5053
- var Table = function Table(props) {
5054
- return /*#__PURE__*/React.createElement("table", Object.assign({}, props.attributes, {
5055
- className: style$5
5056
- }), /*#__PURE__*/React.createElement("tbody", null, props.children));
5057
- };
5981
+ var _templateObject$a;
5982
+ var style$6 = /*#__PURE__*/emotion.css(_templateObject$a || (_templateObject$a = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n margin-bottom: 1.5em;\n border-collapse: collapse;\n border-radius: 5px;\n border-style: hidden;\n box-shadow: 0 0 0 1px ", ";\n width: 100%;\n table-layout: fixed;\n overflow: hidden;\n"])), tokens.gray400);
5983
+ var Table = function Table(props) {
5984
+ return /*#__PURE__*/React.createElement("div", {
5985
+ "data-block-type": Contentful.BLOCKS.TABLE
5986
+ }, /*#__PURE__*/React.createElement("table", Object.assign({
5987
+ className: style$6
5988
+ }, props.attributes), /*#__PURE__*/React.createElement("tbody", null, props.children)));
5989
+ };
5990
+
5991
+ /**
5992
+ * Removes table wrappers when pasting a single table cell
5993
+ *
5994
+ * In Plate/Slate, copying the content of a table cell wraps
5995
+ * it in a <table><tr><td>{content}</td></tr></table> even
5996
+ * when copying partial cell content.
5997
+ *
5998
+ * That's really annoying as there is no way to remove the table
5999
+ * wrappers in that case.
6000
+ */
6001
+
6002
+ var trimUnnecessaryTableWrapper = function trimUnnecessaryTableWrapper(node) {
6003
+ var _node$children, _row$children;
6004
+
6005
+ if (!slate.Element.isElement(node)) {
6006
+ return [node];
6007
+ } // must be a table with a single row
6008
+
6009
+
6010
+ if (node.type !== Contentful.BLOCKS.TABLE || ((_node$children = node.children) == null ? void 0 : _node$children.length) !== 1) {
6011
+ return [node];
6012
+ }
6013
+
6014
+ var row = node.children[0]; // the row must contain a single cell
6015
+
6016
+ if ((row == null ? void 0 : (_row$children = row.children) == null ? void 0 : _row$children.length) !== 1) {
6017
+ return [node];
6018
+ }
6019
+
6020
+ var cell = row.children[0];
6021
+ return cell.children;
6022
+ };
6023
+
6024
+ var insertTableFragment = function insertTableFragment(editor) {
6025
+ var insertFragment = editor.insertFragment;
6026
+ return function (fragments) {
6027
+ var _editor$selection;
6028
+
6029
+ if (!editor.selection) {
6030
+ return;
6031
+ }
6032
+
6033
+ fragments = fragments.flatMap(trimUnnecessaryTableWrapper); // We need to make sure we have a new, empty and clean paragraph in order to paste tables as-is due to how Slate behaves
6034
+ // More info: https://github.com/ianstormtaylor/slate/pull/4489 and https://github.com/ianstormtaylor/slate/issues/4542
6035
+
6036
+ var isInsertingTable = fragments.some(function (fragment) {
6037
+ return isTable(fragment);
6038
+ });
6039
+ var isTableFirstFragment = fragments.findIndex(function (fragment) {
6040
+ return isTable(fragment);
6041
+ }) === 0;
6042
+ var currentLineHasText = plateCore.getText(editor, (_editor$selection = editor.selection) == null ? void 0 : _editor$selection.focus.path) !== '';
6043
+
6044
+ if (isInsertingTable && isTableFirstFragment && currentLineHasText) {
6045
+ insertEmptyParagraph(editor);
6046
+ }
6047
+
6048
+ return insertFragment(fragments);
6049
+ };
6050
+ };
6051
+
6052
+ var onKeyDownTable = function onKeyDownTable(editor, plugin) {
6053
+ var defaultHandler = plateTable.onKeyDownTable(editor, plugin);
6054
+ return function (event) {
6055
+ // This fixes `Cannot resolve a Slate point from DOM point: [object HTMLDivElement]` when typing while the cursor is before table
6056
+ var windowSelection = window.getSelection();
6057
+
6058
+ if (windowSelection) {
6059
+ var _windowSelection$anch, _windowSelection$anch2;
6060
+
6061
+ // @ts-expect-error
6062
+ var blockType = (_windowSelection$anch = windowSelection.anchorNode.attributes) == null ? void 0 : (_windowSelection$anch2 = _windowSelection$anch['data-block-type']) == null ? void 0 : _windowSelection$anch2.value; // this attribute comes from `plugins/Table/components/Table.tsx`
6063
+
6064
+ var isBeforeTable = blockType === Contentful.BLOCKS.TABLE;
6065
+
6066
+ if (isBeforeTable) {
6067
+ if (event.key === 'Enter') {
6068
+ var above = plateCore.getAbove(editor, {
6069
+ match: {
6070
+ type: Contentful.BLOCKS.TABLE
6071
+ }
6072
+ });
6073
+ if (!above) return;
6074
+ var tablePath = above[1];
6075
+ insertEmptyParagraph(editor, {
6076
+ at: tablePath,
6077
+ select: true
6078
+ });
6079
+ }
6080
+
6081
+ event.preventDefault();
6082
+ event.stopPropagation();
6083
+ return;
6084
+ }
6085
+ } // Pressing Tab on the last cell creates a new row
6086
+ // Otherwise, jumping between cells is handled in the defaultKeyDownTable
6087
+
6088
+
6089
+ if (event.key === 'Tab' && !event.shiftKey) {
6090
+ event.preventDefault();
6091
+ var res = plateTable.getTableCellEntry(editor, {});
6092
+
6093
+ if (res) {
6094
+ var tableElement = res.tableElement,
6095
+ tableRow = res.tableRow,
6096
+ tableCell = res.tableCell;
6097
+ var isLastCell = plateCore.isLastChild(tableRow, tableCell[1]);
6098
+ var isLastRow = plateCore.isLastChild(tableElement, tableRow[1]);
6099
+
6100
+ if (isLastRow && isLastCell) {
6101
+ addRowBelow(editor); // skip default handler
6102
+
6103
+ return;
6104
+ }
6105
+ }
6106
+ }
6107
+
6108
+ defaultHandler(event);
6109
+ };
6110
+ };
6111
+
6112
+ var _extends2, _extends4, _inlines;
6113
+ var inlines = /*#__PURE__*/Object.values(Contentful.INLINES).map(function (type) {
6114
+ return {
6115
+ type: type
6116
+ };
6117
+ });
6118
+ var schema = {
6119
+ document: {
6120
+ nodes: [{
6121
+ types: /*#__PURE__*/Contentful.TOP_LEVEL_BLOCKS.map(function (type) {
6122
+ return {
6123
+ type: type
6124
+ };
6125
+ })
6126
+ }]
6127
+ },
6128
+ blocks: /*#__PURE__*/_extends((_extends2 = {}, _extends2[Contentful.BLOCKS.PARAGRAPH] = {
6129
+ nodes: [{
6130
+ match: /*#__PURE__*/[].concat(inlines, [{
6131
+ object: 'text'
6132
+ }])
6133
+ }]
6134
+ }, _extends2[Contentful.BLOCKS.HEADING_1] = {
6135
+ nodes: [{
6136
+ match: /*#__PURE__*/[].concat(inlines, [{
6137
+ object: 'text'
6138
+ }])
6139
+ }]
6140
+ }, _extends2[Contentful.BLOCKS.HEADING_2] = {
6141
+ nodes: [{
6142
+ match: /*#__PURE__*/[].concat(inlines, [{
6143
+ object: 'text'
6144
+ }])
6145
+ }]
6146
+ }, _extends2[Contentful.BLOCKS.HEADING_3] = {
6147
+ nodes: [{
6148
+ match: /*#__PURE__*/[].concat(inlines, [{
6149
+ object: 'text'
6150
+ }])
6151
+ }]
6152
+ }, _extends2[Contentful.BLOCKS.HEADING_4] = {
6153
+ nodes: [{
6154
+ match: /*#__PURE__*/[].concat(inlines, [{
6155
+ object: 'text'
6156
+ }])
6157
+ }]
6158
+ }, _extends2[Contentful.BLOCKS.HEADING_5] = {
6159
+ nodes: [{
6160
+ match: /*#__PURE__*/[].concat(inlines, [{
6161
+ object: 'text'
6162
+ }])
6163
+ }]
6164
+ }, _extends2[Contentful.BLOCKS.HEADING_6] = {
6165
+ nodes: [{
6166
+ match: /*#__PURE__*/[].concat(inlines, [{
6167
+ object: 'text'
6168
+ }])
6169
+ }]
6170
+ }, _extends2), /*#__PURE__*/Contentful.VOID_BLOCKS.reduce(function (blocks, nodeType) {
6171
+ var _extends3;
6172
+
6173
+ return _extends({}, blocks, (_extends3 = {}, _extends3[nodeType] = {
6174
+ isVoid: true
6175
+ }, _extends3));
6176
+ }, {}), (_extends4 = {}, _extends4[Contentful.BLOCKS.QUOTE] = {
6177
+ nodes: [{
6178
+ match: [/*#__PURE__*/Contentful.CONTAINERS[Contentful.BLOCKS.QUOTE].map(function (type) {
6179
+ return {
6180
+ type: type
6181
+ };
6182
+ })],
6183
+ min: 1
6184
+ }],
6185
+ normalize: function normalize(editor, error) {
6186
+ if (error.code === 'child_type_invalid') {
6187
+ return editor.unwrapBlockByKey(error.node.key, Contentful.BLOCKS.QUOTE);
6188
+ }
6189
+ }
6190
+ }, _extends4)),
6191
+ inlines: (_inlines = {}, _inlines[Contentful.INLINES.HYPERLINK] = {
6192
+ nodes: [{
6193
+ match: [{
6194
+ object: 'text'
6195
+ }]
6196
+ }]
6197
+ }, _inlines[Contentful.INLINES.ENTRY_HYPERLINK] = {
6198
+ nodes: [{
6199
+ match: [{
6200
+ object: 'text'
6201
+ }]
6202
+ }]
6203
+ }, _inlines[Contentful.INLINES.ASSET_HYPERLINK] = {
6204
+ nodes: [{
6205
+ match: [{
6206
+ object: 'text'
6207
+ }]
6208
+ }]
6209
+ }, _inlines[Contentful.INLINES.EMBEDDED_ENTRY] = {
6210
+ isVoid: true
6211
+ }, _inlines)
6212
+ };
6213
+
6214
+ function getCharacterCount(editor) {
6215
+ var document = contentfulSlateJSAdapter.toContentfulDocument({
6216
+ document: editor.children,
6217
+ schema: schema
6218
+ });
6219
+ return richTextPlainTextRenderer.documentToPlainTextString(document).length;
6220
+ }
6221
+
6222
+ var actionOrigin = {
6223
+ TOOLBAR: 'toolbar-icon',
6224
+ SHORTCUT: 'shortcut',
6225
+ VIEWPORT: 'viewport-interaction',
6226
+ COMMAND_PALETTE: 'command-palette'
6227
+ };
6228
+ function getPastingSource(data) {
6229
+ var textHtml = data.getData('text/html');
6230
+ var doc = new DOMParser().parseFromString(textHtml, 'text/html');
6231
+
6232
+ if (doc.querySelector('[id*="docs-internal-guid"]')) {
6233
+ return 'Google Docs';
6234
+ }
6235
+
6236
+ if (doc.querySelector('google-sheets-html-origin') || doc.querySelector('[data-sheets-value]')) {
6237
+ return 'Google Spreadsheets';
6238
+ }
6239
+
6240
+ if (doc.querySelector('meta[content*="Microsoft Excel"]')) {
6241
+ return 'Microsoft Excel';
6242
+ }
6243
+
6244
+ if (doc.querySelector('meta[content*="Microsoft Word"]')) {
6245
+ return 'Microsoft Word';
6246
+ } // TODO: MS Word Online doesn't give us specific tags, we might need to have a closer look at its tracking result since we are using generic values to identify it
6247
+
6248
+
6249
+ if (doc.querySelector('[style*="Arial_MSFontService"]') && (doc.querySelector('.TextRun') || doc.querySelector('.OutlineElement'))) {
6250
+ return 'Microsoft Word Online';
6251
+ }
6252
+
6253
+ if (doc.querySelector('meta[content="Cocoa HTML Writer"]')) {
6254
+ return 'Apple Notes';
6255
+ }
6256
+
6257
+ if (doc.querySelector('[style*="Slack-Lato, Slack-Fractions"]')) {
6258
+ return 'Slack';
6259
+ }
6260
+
6261
+ return 'Unknown';
6262
+ }
6263
+ var createTrackingPlugin = function createTrackingPlugin(onAction) {
6264
+ var trackingActions = {
6265
+ onViewportAction: function onViewportAction(actionName, data) {
6266
+ if (data === void 0) {
6267
+ data = {};
6268
+ }
6269
+
6270
+ return onAction(actionName, _extends({
6271
+ origin: actionOrigin.VIEWPORT
6272
+ }, data));
6273
+ },
6274
+ onShortcutAction: function onShortcutAction(actionName, data) {
6275
+ if (data === void 0) {
6276
+ data = {};
6277
+ }
6278
+
6279
+ return onAction(actionName, _extends({
6280
+ origin: actionOrigin.SHORTCUT
6281
+ }, data));
6282
+ },
6283
+ onToolbarAction: function onToolbarAction(actionName, data) {
6284
+ if (data === void 0) {
6285
+ data = {};
6286
+ }
6287
+
6288
+ return onAction(actionName, _extends({
6289
+ origin: actionOrigin.TOOLBAR
6290
+ }, data));
6291
+ },
6292
+ onCommandPaletteAction: function onCommandPaletteAction(actionName, data) {
6293
+ if (data === void 0) {
6294
+ data = {};
6295
+ }
6296
+
6297
+ return onAction(actionName, _extends({
6298
+ origin: actionOrigin.COMMAND_PALETTE
6299
+ }, data));
6300
+ }
6301
+ };
6302
+ return {
6303
+ key: 'TrackingPlugin',
6304
+ withOverrides: function withOverrides(editor) {
6305
+ var insertData = editor.insertData;
6306
+ editor.tracking = trackingActions;
6307
+
6308
+ editor.insertData = function (data) {
6309
+ var isCopyAndPaste = data.types.length !== 0;
6310
+
6311
+ if (isCopyAndPaste) {
6312
+ var _window$getSelection;
6313
+
6314
+ var characterCountSelection = (_window$getSelection = window.getSelection()) == null ? void 0 : _window$getSelection.toString().length;
6315
+ var characterCountBefore = getCharacterCount(editor);
6316
+ setTimeout(function () {
6317
+ var characterCountAfter = getCharacterCount(editor);
6318
+ trackingActions.onShortcutAction('paste', {
6319
+ characterCountAfter: characterCountAfter,
6320
+ characterCountBefore: characterCountBefore,
6321
+ characterCountSelection: characterCountSelection,
6322
+ source: getPastingSource(data)
6323
+ });
6324
+ });
6325
+ }
6326
+
6327
+ insertData(data);
6328
+ };
6329
+
6330
+ return editor;
6331
+ }
6332
+ };
6333
+ };
6334
+
6335
+ function hasTables(nodes) {
6336
+ return nodes.some(function (_ref) {
6337
+ var type = _ref.type;
6338
+ return type === Contentful.BLOCKS.TABLE;
6339
+ });
6340
+ }
6341
+
6342
+ var isTableHeaderCell = function isTableHeaderCell(_ref2) {
6343
+ var type = _ref2.type;
6344
+ return type === Contentful.BLOCKS.TABLE_HEADER_CELL;
6345
+ };
6346
+
6347
+ function hasHeadersOutsideFirstRow(nodes) {
6348
+ return nodes.filter(function (_ref3) {
6349
+ var type = _ref3.type;
6350
+ return type === Contentful.BLOCKS.TABLE;
6351
+ }).flatMap(function (_ref4) {
6352
+ var children = _ref4.children;
6353
+ return children.slice(1);
6354
+ }).some(function (_ref5) {
6355
+ var children = _ref5.children;
6356
+ return children.some(isTableHeaderCell);
6357
+ });
6358
+ }
6359
+
6360
+ function addTableTrackingEvents(editor) {
6361
+ var insertData = editor.insertData;
6362
+
6363
+ editor.insertData = function (data) {
6364
+ var html = data.getData('text/html');
6365
+
6366
+ if (html) {
6367
+ var markupBefore = editor.children;
6368
+ insertData(data);
6369
+ var markupAfter = editor.children;
6370
+ setTimeout(function () {
6371
+ if (hasTables(markupBefore)) return;
6372
+
6373
+ if (hasTables(markupAfter)) {
6374
+ editor.tracking.onViewportAction('paste', {
6375
+ tablePasted: true,
6376
+ source: getPastingSource(data),
6377
+ hasHeadersOutsideFirstRow: hasHeadersOutsideFirstRow(markupAfter)
6378
+ });
6379
+ }
6380
+ }, 1);
6381
+ } else {
6382
+ insertData(data);
6383
+ }
6384
+ };
6385
+ }
6386
+ var withInvalidCellChildrenTracking = function withInvalidCellChildrenTracking(transformer) {
6387
+ return function (editor, childEntry) {
6388
+ var node = childEntry[0];
5058
6389
 
5059
- var createTableOnKeyDown = function createTableOnKeyDown(editor, plugin) {
5060
- var defaultHandler = plateTable.onKeyDownTable(editor, plugin);
5061
- return function (event) {
5062
- if (event.key === 'Backspace' && currentSelectionStartsTableCell(editor) || event.key === 'Delete' && currentSelectionPrecedesTableCell(editor)) {
5063
- // The default behavior here would be to delete the preceding or forthcoming
5064
- // leaf node, in this case a cell or header cell. But we don't want to do that,
5065
- // because it would leave us with a non-standard number of table cells.
5066
- event.preventDefault();
5067
- event.stopPropagation();
5068
- return;
6390
+ if (slate.Element.isElement(node)) {
6391
+ var _editor$tracking;
6392
+
6393
+ (_editor$tracking = editor.tracking) == null ? void 0 : _editor$tracking.onViewportAction('invalidTablePaste', {
6394
+ nodeType: node.type
6395
+ });
5069
6396
  }
5070
6397
 
5071
- defaultHandler(event);
6398
+ return transformer(editor, childEntry);
5072
6399
  };
5073
6400
  };
5074
6401
 
5075
- var createTablePlugin = function createTablePlugin(tracking) {
6402
+ var createTablePlugin = function createTablePlugin() {
5076
6403
  var _overrideByKey;
5077
6404
 
5078
6405
  return plateTable.createTablePlugin({
5079
6406
  type: Contentful.BLOCKS.TABLE,
5080
6407
  handlers: {
5081
- onKeyDown: createTableOnKeyDown
6408
+ onKeyDown: onKeyDownTable
5082
6409
  },
5083
- withOverrides: function withOverrides(editor) {
5084
- addTableTrackingEvents(editor, tracking);
5085
- var insertFragment = editor.insertFragment;
5086
-
5087
- editor.insertFragment = function (fragments) {
5088
- // We need to make sure we have a new, empty and clean paragraph in order to paste tables as-is due to how Slate behaves
5089
- // More info: https://github.com/ianstormtaylor/slate/pull/4489 and https://github.com/ianstormtaylor/slate/issues/4542
5090
- var fragmentHasTable = fragments.some(function (fragment) {
5091
- return fragment.type === Contentful.BLOCKS.TABLE;
5092
- });
5093
-
5094
- if (fragmentHasTable) {
5095
- var emptyParagraph = {
5096
- type: Contentful.BLOCKS.PARAGRAPH,
5097
- children: [{
5098
- text: ''
5099
- }],
5100
- data: {},
5101
- isVoid: false
5102
- };
5103
- slate.Transforms.insertNodes(editor, emptyParagraph);
5104
- }
5105
-
5106
- insertFragment(fragments);
5107
- };
5108
-
6410
+ withOverrides: function withOverrides(editor, plugin) {
6411
+ // injects important fixes from plate's original table plugin
6412
+ plateTable.withTable(editor, plugin);
6413
+ addTableTrackingEvents(editor);
6414
+ editor.insertFragment = insertTableFragment(editor);
5109
6415
  return editor;
5110
6416
  },
5111
6417
  overrideByKey: (_overrideByKey = {}, _overrideByKey[plateTable.ELEMENT_TABLE] = {
@@ -5166,14 +6472,14 @@ var createTablePlugin = function createTablePlugin(tracking) {
5166
6472
  component: HeaderCell,
5167
6473
  normalizer: [{
5168
6474
  validChildren: Contentful.CONTAINERS[Contentful.BLOCKS.TABLE_HEADER_CELL],
5169
- transform: transformParagraphs
6475
+ transform: withInvalidCellChildrenTracking(transformParagraphs)
5170
6476
  }]
5171
6477
  }, _overrideByKey[plateTable.ELEMENT_TD] = {
5172
6478
  type: Contentful.BLOCKS.TABLE_CELL,
5173
6479
  component: Cell,
5174
6480
  normalizer: [{
5175
6481
  validChildren: Contentful.CONTAINERS[Contentful.BLOCKS.TABLE_CELL],
5176
- transform: transformParagraphs
6482
+ transform: withInvalidCellChildrenTracking(transformParagraphs)
5177
6483
  }]
5178
6484
  }, _overrideByKey)
5179
6485
  });
@@ -5181,10 +6487,6 @@ var createTablePlugin = function createTablePlugin(tracking) {
5181
6487
 
5182
6488
  function ToolbarTableButton(props) {
5183
6489
  var editor = useContentfulEditor();
5184
-
5185
- var _useTrackingContext = useTrackingContext(),
5186
- onViewportAction = _useTrackingContext.onViewportAction;
5187
-
5188
6490
  var isActive = editor && isTableActive(editor);
5189
6491
 
5190
6492
  function handleClick() {
@@ -5205,9 +6507,9 @@ function ToolbarTableButton(props) {
5205
6507
  return _context.abrupt("return");
5206
6508
 
5207
6509
  case 2:
5208
- onViewportAction('insertTable');
6510
+ editor.tracking.onToolbarAction('insertTable');
5209
6511
  insertTableAndFocusFirstCell(editor);
5210
- Slate.ReactEditor.focus(editor);
6512
+ focus(editor);
5211
6513
 
5212
6514
  case 5:
5213
6515
  case "end":
@@ -5233,81 +6535,172 @@ function ToolbarTableButton(props) {
5233
6535
  function createTextPlugin() {
5234
6536
  return {
5235
6537
  key: 'TextPlugin',
6538
+ handlers: {
6539
+ // Triple selection in a non-Firefox browser undesirably selects
6540
+ // the start of the next block. Editor.unhangRange helps removing
6541
+ // the extra block at the end.
6542
+ onMouseUp: function onMouseUp(editor) {
6543
+ return function () {
6544
+ if (!editor.selection) {
6545
+ return;
6546
+ }
6547
+
6548
+ slate.Transforms.setSelection(editor, slate.Editor.unhangRange(editor, editor.selection));
6549
+ };
6550
+ }
6551
+ },
5236
6552
  withOverrides: function withOverrides(editor) {
5237
- var deleteForward = editor.deleteForward; // When pressing delete instead of backspace
6553
+ // Reverts the change made upstream that caused the cursor
6554
+ // to be trapped inside inline elements.
6555
+ //
6556
+ // Reverts https://github.com/ianstormtaylor/slate/pull/4578/
6557
+ // Related https://github.com/ianstormtaylor/slate/issues/4704
6558
+ var insertText = editor.insertText;
6559
+
6560
+ editor.insertText = function (text) {
6561
+ var selection = editor.selection; // If the cursor is at the end of an inline, move it outside
6562
+ // before inserting
6563
+
6564
+ if (selection && slate.Range.isCollapsed(selection)) {
6565
+ var _Editor$above;
6566
+
6567
+ var inlinePath = (_Editor$above = slate.Editor.above(editor, {
6568
+ match: function match(n) {
6569
+ return slate.Editor.isInline(editor, n);
6570
+ },
6571
+ mode: 'highest'
6572
+ })) == null ? void 0 : _Editor$above[1];
6573
+
6574
+ if (inlinePath && slate.Editor.isEnd(editor, selection.anchor, inlinePath)) {
6575
+ var point = slate.Editor.after(editor, inlinePath);
6576
+ slate.Transforms.setSelection(editor, {
6577
+ anchor: point,
6578
+ focus: point
6579
+ });
6580
+ }
6581
+ }
5238
6582
 
5239
- editor.deleteForward = function (unit) {
5240
- var _editor$selection;
6583
+ return insertText(text);
6584
+ }; // When pressing delete instead of backspace
5241
6585
 
5242
- var _Editor$nodes = slate.Editor.nodes(editor, {
5243
- at: (_editor$selection = editor.selection) == null ? void 0 : _editor$selection.focus.path,
5244
- match: function match(node) {
5245
- return Contentful.TEXT_CONTAINERS.includes(node.type);
5246
- }
5247
- }),
5248
- nodes = _Editor$nodes[0];
5249
6586
 
5250
- if (nodes) {
5251
- var paragraphOrHeading = nodes[0],
5252
- path = nodes[1];
5253
- var isTextEmpty = plateCore.isAncestorEmpty(editor, paragraphOrHeading); // We ignore paragraphs/headings that are children of ul, ol, blockquote, tables, etc
6587
+ var deleteForward = editor.deleteForward,
6588
+ deleteBackward = editor.deleteBackward;
5254
6589
 
5255
- var isRootLevel = path.length === 1;
6590
+ editor.deleteBackward = function (unit) {
6591
+ deleteEmptyParagraph(unit, editor, deleteBackward);
6592
+ };
5256
6593
 
5257
- if (isTextEmpty && isRootLevel) {
5258
- slate.Transforms.removeNodes(editor, {
5259
- at: path
5260
- });
5261
- } else {
5262
- deleteForward(unit);
5263
- }
5264
- } else {
5265
- deleteForward(unit);
5266
- }
6594
+ editor.deleteForward = function (unit) {
6595
+ deleteEmptyParagraph(unit, editor, deleteForward);
5267
6596
  };
5268
6597
 
6598
+ fixPasteAsPlainText(editor);
5269
6599
  return editor;
5270
6600
  }
5271
6601
  };
5272
6602
  }
5273
6603
 
5274
- var createTrailingParagraphPlugin = function createTrailingParagraphPlugin() {
5275
- return plateTrailingBlock.createTrailingBlockPlugin({
5276
- options: {
5277
- type: Contentful.BLOCKS.PARAGRAPH,
5278
- level: 0
6604
+ function deleteEmptyParagraph(unit, editor, deleteFunction) {
6605
+ var entry = plateCore.getAbove(editor, {
6606
+ match: {
6607
+ type: Contentful.TEXT_CONTAINERS
5279
6608
  }
5280
6609
  });
5281
- };
5282
6610
 
6611
+ if (entry) {
6612
+ var paragraphOrHeading = entry[0],
6613
+ path = entry[1];
6614
+ var isTextEmpty = plateCore.isAncestorEmpty(editor, paragraphOrHeading); // We ignore paragraphs/headings that are children of ul, ol, blockquote, tables, etc
6615
+
6616
+ var isRootLevel = path.length === 1;
6617
+ var hasSiblings = editor.children.length > 1; // prevent editor from losing focus
6618
+
6619
+ if (isTextEmpty && isRootLevel && hasSiblings) {
6620
+ slate.Transforms.removeNodes(editor, {
6621
+ at: path
6622
+ });
6623
+ var prevNode = slate.Editor.before(editor, editor.selection, {
6624
+ unit: unit
6625
+ });
6626
+
6627
+ if (prevNode) {
6628
+ var _Editor$nodes = slate.Editor.nodes(editor, {
6629
+ match: function match(node) {
6630
+ return plateCore.queryNode([node, prevNode.path], {
6631
+ allow: [Contentful.BLOCKS.EMBEDDED_ASSET, Contentful.BLOCKS.EMBEDDED_ENTRY, Contentful.BLOCKS.HR]
6632
+ });
6633
+ },
6634
+ at: prevNode
6635
+ }),
6636
+ prevCell = _Editor$nodes[0];
6637
+
6638
+ if (prevCell) {
6639
+ slate.Transforms.select(editor, prevNode);
6640
+ }
6641
+ }
6642
+ } else {
6643
+ deleteFunction(unit);
6644
+ }
6645
+ } else {
6646
+ deleteFunction(unit);
6647
+ }
6648
+ }
5283
6649
  /**
5284
- * Re-creates a void node with valid children.
6650
+ * To be compatible with the old behavior we need to treat each 2 consecutive
6651
+ * line breaks as a new paragraph when pasting as plain text (also known as
6652
+ * paste and match style in macOS)
5285
6653
  */
5286
6654
 
5287
- var transformVoid = function transformVoid(editor, _ref) {
5288
- var node = _ref[0],
5289
- path = _ref[1];
5290
6655
 
5291
- var validVoid = _extends({}, node, {
5292
- children: [{
5293
- text: ''
5294
- }]
5295
- }); // A workaround because Slate doesn't allow adjusting void nodes children
6656
+ function fixPasteAsPlainText(editor) {
6657
+ editor.insertTextData = function (data) {
6658
+ var text = data.getData('text/plain');
5296
6659
 
6660
+ if (!text) {
6661
+ return false;
6662
+ }
5297
6663
 
5298
- slate.Transforms.removeNodes(editor, {
5299
- at: path
5300
- });
5301
- slate.Transforms.insertNodes(editor, [validVoid], {
5302
- at: path
6664
+ var lines = text.split(/\n{2}/);
6665
+ var split = false;
6666
+
6667
+ for (var _iterator = _createForOfIteratorHelperLoose(lines), _step; !(_step = _iterator()).done;) {
6668
+ var line = _step.value;
6669
+
6670
+ // empty lines
6671
+ if (/^(\r\n?|\n)$/.test(line)) {
6672
+ continue;
6673
+ }
6674
+
6675
+ if (split) {
6676
+ slate.Transforms.splitNodes(editor, {
6677
+ always: true
6678
+ });
6679
+ }
6680
+
6681
+ editor.insertText(line);
6682
+ split = true;
6683
+ }
6684
+
6685
+ return true;
6686
+ };
6687
+ }
6688
+
6689
+ var createTrailingParagraphPlugin = function createTrailingParagraphPlugin() {
6690
+ return plateTrailingBlock.createTrailingBlockPlugin({
6691
+ options: {
6692
+ type: Contentful.BLOCKS.PARAGRAPH,
6693
+ level: 0
6694
+ }
5303
6695
  });
5304
6696
  };
5305
6697
 
5306
6698
  var createVoidsPlugin = function createVoidsPlugin() {
5307
6699
  return {
5308
6700
  key: 'VoidsPlugin',
5309
- exitBreak: [// Can insert before first void block
5310
- {
6701
+ exitBreak: [{
6702
+ // Inserts a new paragraph *before* a void element if it's the very first
6703
+ // node on the editor
5311
6704
  hotkey: 'enter',
5312
6705
  before: true,
5313
6706
  query: {
@@ -5317,49 +6710,35 @@ var createVoidsPlugin = function createVoidsPlugin() {
5317
6710
  return isRootLevel(path) && plateCore.isFirstChild(path) && !!node.isVoid;
5318
6711
  }
5319
6712
  }
5320
- }, // Can insert after a void block
5321
- {
6713
+ }, {
6714
+ // Inserts a new paragraph on enter when a void element is focused
5322
6715
  hotkey: 'enter',
6716
+ // exploit the internal use of Array.slice(0, level + 1) by the exitBreak plugin
6717
+ // to stay in the parent element
6718
+ level: -2,
5323
6719
  query: {
5324
6720
  filter: function filter(_ref2) {
5325
6721
  var node = _ref2[0],
5326
6722
  path = _ref2[1];
5327
- return !plateCore.isFirstChild(path) && !!node.isVoid;
6723
+ return !(isRootLevel(path) && plateCore.isFirstChild(path)) && !!node.isVoid;
5328
6724
  }
5329
6725
  }
5330
- }],
5331
- normalizer: [{
5332
- match: {
5333
- isVoid: true
5334
- },
5335
- validNode: function validNode(editor, _ref3) {
5336
- var path = _ref3[1];
5337
- var children = Array.from(slate.Node.children(editor, path));
5338
-
5339
- if (children.length !== 1) {
5340
- return false;
5341
- }
5342
-
5343
- var _children$ = children[0],
5344
- textNode = _children$[0];
5345
- return slate.Text.isText(textNode) && textNode.text === '';
5346
- },
5347
- transform: transformVoid
5348
6726
  }]
5349
6727
  };
5350
6728
  };
5351
6729
 
5352
- var getPlugins = function getPlugins(sdk, tracking) {
6730
+ var getPlugins = function getPlugins(sdk, onAction) {
5353
6731
  return [// AST must come after the HTML deserializer
5354
- plateCore.createDeserializeHtmlPlugin(), plateCore.createDeserializeAstPlugin(), plateSerializerDocx.createDeserializeDocxPlugin(), // Global shortcuts
5355
- createDragAndDropPlugin(), // Block Elements
5356
- createParagraphPlugin(), createListPlugin(), createHrPlugin(), createHeadingPlugin(), createQuotePlugin(), createTablePlugin(tracking), createEmbeddedEntryBlockPlugin(sdk), createEmbeddedAssetBlockPlugin(sdk), // Inline elements
6732
+ plateCore.createDeserializeHtmlPlugin(), plateCore.createDeserializeAstPlugin(), plateSerializerDocx.createDeserializeDocxPlugin(), // Tracking - This should come first so all plugins below will have access to `editor.tracking`
6733
+ createTrackingPlugin(onAction), // Global / Global shortcuts
6734
+ createDragAndDropPlugin(), createSlashCommandsPlugin(), // Block Elements
6735
+ createParagraphPlugin(), createListPlugin(), createHrPlugin(), createHeadingPlugin(), createQuotePlugin(), createTablePlugin(), createEmbeddedEntryBlockPlugin(sdk), createEmbeddedAssetBlockPlugin(sdk), // Inline elements
5357
6736
  createHyperlinkPlugin(sdk), createEmbeddedEntityInlinePlugin(sdk), // Marks
5358
6737
  createMarksPlugin(), // Other
5359
6738
  createTrailingParagraphPlugin(), createTextPlugin(), createVoidsPlugin(), createSelectOnBackspacePlugin(), // Pasting content from other sources
5360
6739
  createPasteHTMLPlugin(), // These plugins drive their configurations from the list of plugins
5361
6740
  // above. They MUST come last.
5362
- createSoftBreakPlugin(), createExitBreakPlugin(), createNormalizerPlugin()];
6741
+ createSoftBreakPlugin(), createExitBreakPlugin(), createResetNodePlugin(), createNormalizerPlugin()];
5363
6742
  };
5364
6743
  var disableCorePlugins = {
5365
6744
  // Temporarily until the upstream issue is fixed.
@@ -5371,6 +6750,117 @@ var disableCorePlugins = {
5371
6750
  eventEditor: true
5372
6751
  };
5373
6752
 
6753
+ var isTextElement = function isTextElement(node) {
6754
+ return 'text' in node;
6755
+ };
6756
+ /**
6757
+ * Ensures all nodes have a child leaf text element. This should be handled by
6758
+ * Slate but its behavior has proven to be buggy and unpredictable.
6759
+ */
6760
+
6761
+
6762
+ function sanitizeIncomingSlateDoc(nodes) {
6763
+ if (nodes === void 0) {
6764
+ nodes = [];
6765
+ }
6766
+
6767
+ return nodes.map(function (node) {
6768
+ var _node$children;
6769
+
6770
+ if (isTextElement(node)) {
6771
+ return node;
6772
+ }
6773
+
6774
+ if (((_node$children = node.children) == null ? void 0 : _node$children.length) === 0) {
6775
+ return _extends({}, node, {
6776
+ children: [{
6777
+ text: '',
6778
+ data: {}
6779
+ }]
6780
+ });
6781
+ }
6782
+
6783
+ return _extends({}, node, {
6784
+ children: sanitizeIncomingSlateDoc(node.children)
6785
+ });
6786
+ });
6787
+ }
6788
+
6789
+ /**
6790
+ * For legacy reasons, a document may not have any content at all
6791
+ * e.g:
6792
+ *
6793
+ * {nodeType: document, data: {}, content: []}
6794
+ *
6795
+ * Rendering such document will break the Slate editor
6796
+ */
6797
+
6798
+ var hasContent = function hasContent(doc) {
6799
+ if (!doc) {
6800
+ return false;
6801
+ }
6802
+
6803
+ return doc.content.length > 0;
6804
+ };
6805
+ /*
6806
+ Plate api doesn't allow to modify (easily) the editor value programmatically
6807
+ after the editor instance is created
6808
+
6809
+ This function is inspired to https://github.com/udecode/plate/issues/1269#issuecomment-1057643622
6810
+ */
6811
+
6812
+ var setEditorContent = function setEditorContent(editor, nodes) {
6813
+ // Replaces editor content while keeping change history
6814
+ slate.Editor.withoutNormalizing(editor, function () {
6815
+ var children = [].concat(editor.children);
6816
+ children.forEach(function (node) {
6817
+ return editor.apply({
6818
+ type: 'remove_node',
6819
+ path: [0],
6820
+ node: node
6821
+ });
6822
+ });
6823
+
6824
+ if (nodes) {
6825
+ var nodesArray = slate.Node.isNode(nodes) ? [nodes] : nodes;
6826
+ nodesArray.forEach(function (node, i) {
6827
+ return editor.apply({
6828
+ type: 'insert_node',
6829
+ path: [i],
6830
+ node: node
6831
+ });
6832
+ });
6833
+ }
6834
+
6835
+ var point = slate.Editor.end(editor, []);
6836
+
6837
+ if (point) {
6838
+ slate.Transforms.select(editor, point);
6839
+ }
6840
+ });
6841
+ };
6842
+ /**
6843
+ * Converts a Contentful rich text document to the corresponding slate editor
6844
+ * value
6845
+ */
6846
+
6847
+ var documentToEditorValue = function documentToEditorValue(doc) {
6848
+ var slateDoc = contentfulSlateJSAdapter.toSlatejsDocument({
6849
+ document: hasContent(doc) ? doc : Contentful.EMPTY_DOCUMENT,
6850
+ // TODO: get rid of schema, https://github.com/contentful/field-editors/pull/1065#discussion_r826723248
6851
+ schema: schema
6852
+ });
6853
+ return sanitizeIncomingSlateDoc(slateDoc);
6854
+ };
6855
+ var normalizeEditorValue = function normalizeEditorValue(value, options) {
6856
+ var editor = plateCore.createPlateEditor(options);
6857
+ editor.children = value;
6858
+ slate.Editor.normalize(editor, {
6859
+ force: true
6860
+ });
6861
+ return editor.children;
6862
+ };
6863
+
5374
6864
  var STYLE_EDITOR_BORDER = "1px solid " + tokens.gray400;
5375
6865
  var styles$j = {
5376
6866
  root: /*#__PURE__*/emotion.css({
@@ -5417,7 +6907,8 @@ var styles$j = {
5417
6907
  }
5418
6908
  }),
5419
6909
  disabled: /*#__PURE__*/emotion.css({
5420
- background: tokens.gray100
6910
+ background: tokens.gray100,
6911
+ cursor: 'not-allowed'
5421
6912
  })
5422
6913
  };
5423
6914
 
@@ -5462,45 +6953,23 @@ var EmbedEntityWidget = function EmbedEntityWidget(_ref) {
5462
6953
  return setEmbedDropdownOpen(!isEmbedDropdownOpen);
5463
6954
  };
5464
6955
 
5465
- var _useState2 = React.useState(false),
5466
- canAccessAssets = _useState2[0],
5467
- setCanAccessAssets = _useState2[1];
5468
-
5469
- React__default.useEffect(function () {
5470
- sdk.access.can('read', 'Asset').then(setCanAccessAssets);
5471
- }, [sdk]);
5472
6956
  var inlineEntryEmbedEnabled = isNodeTypeEnabled(sdk.field, Contentful.INLINES.EMBEDDED_ENTRY);
5473
- var blockEntryEmbedEnabled = isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.EMBEDDED_ENTRY) && canInsertBlocks;
5474
- var blockAssetEmbedEnabled = canAccessAssets && isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.EMBEDDED_ASSET) && canInsertBlocks;
5475
- var numEnabledEmbeds = [inlineEntryEmbedEnabled, blockEntryEmbedEnabled, blockAssetEmbedEnabled].filter(Boolean).length;
5476
- var shouldDisplayDropdown = numEnabledEmbeds > 1 || isDisabled; // Avoids UI glitching when switching back and forth between
5477
- // different layouts
6957
+ var blockEntryEmbedEnabled = isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.EMBEDDED_ENTRY) && canInsertBlocks; // Removed access check following https://contentful.atlassian.net/browse/DANTE-486
6958
+ // TODO: refine permissions check in order to account for tags in rules and then readd access.can('read', 'Asset')
5478
6959
 
5479
- React__default.useEffect(function () {
5480
- if (!shouldDisplayDropdown) {
5481
- setEmbedDropdownOpen(false);
5482
- }
5483
- }, [shouldDisplayDropdown]);
6960
+ var blockAssetEmbedEnabled = isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.EMBEDDED_ASSET) && canInsertBlocks;
5484
6961
  var actions = /*#__PURE__*/React__default.createElement(React__default.Fragment, null, blockEntryEmbedEnabled && /*#__PURE__*/React__default.createElement(EmbeddedEntityBlockToolbarIcon, {
5485
6962
  isDisabled: !!isDisabled,
5486
6963
  nodeType: Contentful.BLOCKS.EMBEDDED_ENTRY,
5487
- onClose: onCloseEntityDropdown,
5488
- isButton: !shouldDisplayDropdown
6964
+ onClose: onCloseEntityDropdown
5489
6965
  }), inlineEntryEmbedEnabled && /*#__PURE__*/React__default.createElement(ToolbarEmbeddedEntityInlineButton, {
5490
6966
  isDisabled: !!isDisabled || isLinkActive(editor),
5491
- onClose: onCloseEntityDropdown,
5492
- isButton: !shouldDisplayDropdown
6967
+ onClose: onCloseEntityDropdown
5493
6968
  }), blockAssetEmbedEnabled && /*#__PURE__*/React__default.createElement(EmbeddedEntityBlockToolbarIcon, {
5494
6969
  isDisabled: !!isDisabled,
5495
6970
  nodeType: Contentful.BLOCKS.EMBEDDED_ASSET,
5496
- onClose: onCloseEntityDropdown,
5497
- isButton: !shouldDisplayDropdown
6971
+ onClose: onCloseEntityDropdown
5498
6972
  }));
5499
-
5500
- if (!shouldDisplayDropdown) {
5501
- return actions;
5502
- }
5503
-
5504
6973
  return /*#__PURE__*/React__default.createElement(EmbeddedEntityDropdownButton, {
5505
6974
  isDisabled: isDisabled,
5506
6975
  onClose: onCloseEntityDropdown,
@@ -5576,10 +7045,10 @@ var Toolbar = function Toolbar(_ref) {
5576
7045
  isDisabled: isDisabled
5577
7046
  })), validationInfo.isAnyBlockFormattingEnabled && /*#__PURE__*/React__default.createElement("span", {
5578
7047
  className: styles$k.divider
5579
- }), isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.QUOTE) && /*#__PURE__*/React__default.createElement(ToolbarQuoteButton, {
5580
- isDisabled: isDisabled || !canInsertBlocks
5581
7048
  }), /*#__PURE__*/React__default.createElement(ToolbarListButton, {
5582
7049
  isDisabled: isDisabled || !canInsertBlocks
7050
+ }), isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.QUOTE) && /*#__PURE__*/React__default.createElement(ToolbarQuoteButton, {
7051
+ isDisabled: isDisabled || !canInsertBlocks
5583
7052
  }), isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.HR) && /*#__PURE__*/React__default.createElement(ToolbarHrButton, {
5584
7053
  isDisabled: isDisabled || !canInsertBlocks
5585
7054
  }), isNodeTypeEnabled(sdk.field, Contentful.BLOCKS.TABLE) && /*#__PURE__*/React__default.createElement(ToolbarTableButton, {
@@ -5622,50 +7091,133 @@ var StickyToolbarWrapper = function StickyToolbarWrapper(_ref) {
5622
7091
  }, children);
5623
7092
  };
5624
7093
 
7094
+ /**
7095
+ * Returns whether a given operation is relevant enough to trigger a save.
7096
+ */
7097
+
7098
+ var isRelevantOperation = function isRelevantOperation(op) {
7099
+ if (op.type === 'set_selection') {
7100
+ return false;
7101
+ }
7102
+
7103
+ return true;
7104
+ };
7105
+
7106
+ var useOnValueChanged = function useOnValueChanged(_ref) {
7107
+ var editorId = _ref.editorId,
7108
+ handler = _ref.handler,
7109
+ skip = _ref.skip,
7110
+ onSkip = _ref.onSkip;
7111
+ var onChange = React.useMemo(function () {
7112
+ return debounce(function (document) {
7113
+ handler == null ? void 0 : handler(contentfulSlateJSAdapter.toContentfulDocument({
7114
+ document: document,
7115
+ schema: schema
7116
+ }));
7117
+ }, 500);
7118
+ }, [handler]);
7119
+ return React.useCallback(function (value) {
7120
+ var editor = plateCore.getPlateSelectors(editorId).editor();
7121
+
7122
+ if (!editor) {
7123
+ throw new Error('Editor change callback called but editor not defined. Editor id: ' + editorId);
7124
+ }
7125
+
7126
+ var operations = editor == null ? void 0 : editor.operations.filter(isRelevantOperation);
7127
+
7128
+ if (operations.length > 0) {
7129
+ if (skip) {
7130
+ onSkip == null ? void 0 : onSkip();
7131
+ return;
7132
+ }
7133
+
7134
+ onChange(value);
7135
+ }
7136
+ }, [editorId, onChange, skip, onSkip]);
7137
+ };
7138
+
5625
7139
  var _excluded = ["sdk", "isInitiallyDisabled", "onAction"];
5626
7140
  var ConnectedRichTextEditor = function ConnectedRichTextEditor(props) {
5627
- var tracking = useTrackingContext();
5628
- var docFromAdapter = contentfulSlatejsAdapter.toSlatejsDocument({
5629
- document: props.value || Contentful.EMPTY_DOCUMENT,
5630
- schema: schema
7141
+ var id = getContentfulEditorId(props.sdk);
7142
+ var plugins = React__default.useMemo(function () {
7143
+ var _props$onAction;
7144
+
7145
+ return getPlugins(props.sdk, (_props$onAction = props.onAction) != null ? _props$onAction : noop);
7146
+ }, [props.sdk, props.onAction]);
7147
+
7148
+ var _useState = React.useState(true),
7149
+ isFirstRender = _useState[0],
7150
+ setIsFirstRender = _useState[1];
7151
+
7152
+ var _useState2 = React.useState(false),
7153
+ pendingExternalUpdate = _useState2[0],
7154
+ setPendingExternalUpdate = _useState2[1];
7155
+
7156
+ var onValueChanged = useOnValueChanged({
7157
+ editorId: id,
7158
+ handler: props.onChange,
7159
+ skip: pendingExternalUpdate || isFirstRender,
7160
+ onSkip: function onSkip() {
7161
+ return setPendingExternalUpdate(false);
7162
+ }
5631
7163
  });
5632
- var doc = sanitizeIncomingSlateDoc(docFromAdapter);
7164
+ React.useEffect(function () {
7165
+ /*
7166
+ This effect is called when the value prop changes. Normally
7167
+ this happens when the value is changed outside of the editor,
7168
+ like in snapshots restoration or remote updates
7169
+ Plate won't update the displayed value on its own, see:
7170
+ - https://github.com/ianstormtaylor/slate/pull/4540
7171
+ - https://github.com/udecode/plate/issues/1169
7172
+ The content is forcely set to the new value and it's ensured
7173
+ the change listener isn't invoked
7174
+ */
7175
+ setIsFirstRender(false);
7176
+ var editor = plateCore.getPlateSelectors(id).editor();
5633
7177
 
5634
- var _useState = React.useState(doc),
5635
- value = _useState[0],
5636
- setValue = _useState[1];
7178
+ if (!editor) {
7179
+ return;
7180
+ }
5637
7181
 
7182
+ setPendingExternalUpdate(true);
7183
+ setEditorContent(editor, documentToEditorValue(props.value));
7184
+ }, [props.value, id]);
5638
7185
  var classNames = emotion.cx(styles$j.editor, props.minHeight !== undefined ? emotion.css({
5639
7186
  minHeight: props.minHeight
5640
7187
  }) : undefined, props.isDisabled ? styles$j.disabled : styles$j.enabled, props.isToolbarHidden && styles$j.hiddenToolbar);
5641
- var plugins = React__default.useMemo(function () {
5642
- return getPlugins(props.sdk, tracking);
5643
- }, [props.sdk, tracking]);
5644
- return /*#__PURE__*/React__default.createElement("div", {
7188
+ React.useEffect(function () {
7189
+ if (!isFirstRender) {
7190
+ return;
7191
+ }
7192
+
7193
+ plateCore.getPlateActions(id).value(normalizeEditorValue(documentToEditorValue(props.value), {
7194
+ plugins: plugins,
7195
+ disableCorePlugins: disableCorePlugins
7196
+ }));
7197
+ }, [isFirstRender, plugins, id, props.value]);
7198
+ return /*#__PURE__*/React__default.createElement(SdkProvider, {
7199
+ sdk: props.sdk
7200
+ }, /*#__PURE__*/React__default.createElement(ContentfulEditorIdProvider, {
7201
+ value: id
7202
+ }, /*#__PURE__*/React__default.createElement("div", {
5645
7203
  className: styles$j.root,
5646
7204
  "data-test-id": "rich-text-editor"
5647
7205
  }, /*#__PURE__*/React__default.createElement(plateCore.Plate, {
5648
- id: getContentfulEditorId(props.sdk),
5649
- initialValue: value,
7206
+ id: id,
5650
7207
  plugins: plugins,
5651
7208
  disableCorePlugins: disableCorePlugins,
5652
7209
  editableProps: {
5653
7210
  className: classNames,
5654
7211
  readOnly: props.isDisabled
5655
7212
  },
5656
- onChange: function onChange(slateDoc) {
5657
- setValue(slateDoc);
5658
- var contentfulDoc = contentfulSlatejsAdapter.toContentfulDocument({
5659
- document: slateDoc,
5660
- schema: schema
5661
- });
5662
- props.onChange == null ? void 0 : props.onChange(contentfulDoc);
5663
- }
7213
+ onChange: onValueChanged
5664
7214
  }, !props.isToolbarHidden && /*#__PURE__*/React__default.createElement(StickyToolbarWrapper, {
5665
7215
  isDisabled: props.isDisabled
5666
7216
  }, /*#__PURE__*/React__default.createElement(Toolbar, {
5667
7217
  isDisabled: props.isDisabled
5668
- }))));
7218
+ })), /*#__PURE__*/React__default.createElement(SlashCommandsPalette, {
7219
+ editorId: id
7220
+ })))));
5669
7221
  };
5670
7222
 
5671
7223
  var RichTextEditor = function RichTextEditor(props) {
@@ -5677,12 +7229,9 @@ var RichTextEditor = function RichTextEditor(props) {
5677
7229
  var isEmptyValue = React.useCallback(function (value) {
5678
7230
  return !value || deepEquals(value, Contentful.EMPTY_DOCUMENT);
5679
7231
  }, []);
7232
+ var id = getContentfulEditorId(props.sdk);
5680
7233
  return /*#__PURE__*/React__default.createElement(fieldEditorReference.EntityProvider, {
5681
7234
  sdk: sdk
5682
- }, /*#__PURE__*/React__default.createElement(SdkProvider, {
5683
- sdk: sdk
5684
- }, /*#__PURE__*/React__default.createElement(TrackingProvider, {
5685
- onAction: onAction || noop
5686
7235
  }, /*#__PURE__*/React__default.createElement(fieldEditorShared.FieldConnector, {
5687
7236
  throttle: 0,
5688
7237
  field: sdk.field,
@@ -5692,19 +7241,16 @@ var RichTextEditor = function RichTextEditor(props) {
5692
7241
  }, function (_ref) {
5693
7242
  var lastRemoteValue = _ref.lastRemoteValue,
5694
7243
  disabled = _ref.disabled,
5695
- setValue = _ref.setValue,
5696
- externalReset = _ref.externalReset;
5697
- return /*#__PURE__*/React__default.createElement(ContentfulEditorProvider, {
5698
- sdk: sdk
5699
- }, /*#__PURE__*/React__default.createElement(ConnectedRichTextEditor, Object.assign({}, otherProps, {
5700
- key: "rich-text-editor-" + externalReset,
7244
+ setValue = _ref.setValue;
7245
+ return /*#__PURE__*/React__default.createElement(ConnectedRichTextEditor, Object.assign({}, otherProps, {
7246
+ key: "rich-text-editor-" + id,
5701
7247
  value: lastRemoteValue,
5702
7248
  sdk: sdk,
5703
- onAction: onAction || noop,
7249
+ onAction: onAction,
5704
7250
  isDisabled: disabled,
5705
7251
  onChange: setValue
5706
- })));
5707
- }))));
7252
+ }));
7253
+ }));
5708
7254
  };
5709
7255
 
5710
7256
  var LINK_TYPES$1 = {