@contentful/field-editor-rich-text 2.0.0-next.21 → 2.0.0-next.22

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.
@@ -2,7 +2,7 @@ import React__default, { createContext, useContext, useMemo, createElement, useE
2
2
  import { useEntities, ScheduledIconWithTooltip, MissingEntityCard, AssetThumbnail, EntityProvider, getScheduleTooltipContent } from '@contentful/field-editor-reference';
3
3
  import { entityHelpers, shortenStorageUnit, isValidImage, ModalDialogLauncher, FieldConnector } from '@contentful/field-editor-shared';
4
4
  import { BLOCKS, INLINES, TABLE_BLOCKS, TEXT_CONTAINERS, HEADINGS, LIST_ITEM_BLOCKS, MARKS, CONTAINERS, TOP_LEVEL_BLOCKS, VOID_BLOCKS, EMPTY_DOCUMENT } from '@contentful/rich-text-types';
5
- import { usePlateEditorState, usePlateEditorRef, getNodes, toggleNodeType, getText, getAbove, setNodes, isAncestorEmpty, getParent, getBlockAbove, isSelectionAtBlockStart, isSelectionAtBlockEnd, isFirstChild, insertNodes, moveChildren, isBlockAboveEmpty, mockPlugin, getPluginType, ELEMENT_DEFAULT, findNode, unwrapNodes, deleteFragment, isCollapsed, isRangeAcrossBlocks, wrapNodes, isMarkActive, toggleMark, someHtmlElement, match, KEY_DESERIALIZE_HTML, hasSingleChild, isLastChild, someNode, getChildren as getChildren$1, getLastChildPath, createDeserializeHtmlPlugin, createDeserializeAstPlugin, createPlateEditor, getPlateActions, Plate } from '@udecode/plate-core';
5
+ import { usePlateEditorRef, usePlateEditorState, getNodes, toggleNodeType, getText, getAbove, setNodes, isAncestorEmpty, match, getLastChildPath, wrapNodes, getPluginType, unwrapNodes, isCollapsed, isRangeAcrossBlocks, ELEMENT_DEFAULT, findNode, getParent, getBlockAbove, isSelectionAtBlockStart, isSelectionAtBlockEnd, isFirstChild, insertNodes, moveChildren, isBlockAboveEmpty, mockPlugin, deleteFragment, isMarkActive, toggleMark, someHtmlElement, KEY_DESERIALIZE_HTML, hasSingleChild, isLastChild, someNode, getChildren as getChildren$1, createDeserializeHtmlPlugin, createDeserializeAstPlugin, createPlateEditor, getPlateSelectors, getPlateActions, Plate } from '@udecode/plate-core';
6
6
  import { css, cx } from 'emotion';
7
7
  import deepEquals from 'fast-deep-equal';
8
8
  import noop from 'lodash-es/noop';
@@ -20,7 +20,8 @@ import { ClockIcon, AssetIcon, EmbeddedEntryBlockIcon, EmbeddedEntryInlineIcon,
20
20
  import tokens from '@contentful/f36-tokens';
21
21
  import find from 'lodash-es/find';
22
22
  import flow from 'lodash-es/flow';
23
- import { getListItemEntry, moveListItemUp, ELEMENT_LI, unwrapList as unwrapList$1, removeFirstListItem, removeListItem, isListNested, deleteForwardList, deleteFragmentList, normalizeList, createListPlugin as createListPlugin$1, ELEMENT_UL, ELEMENT_OL, ELEMENT_LIC } from '@udecode/plate-list';
23
+ import { getListTypes, ELEMENT_LIC, getListItemEntry, isListNested, moveListItemUp, ELEMENT_LI, unwrapList as unwrapList$1, removeFirstListItem, removeListItem, deleteForwardList, deleteFragmentList, normalizeList, createListPlugin as createListPlugin$1, ELEMENT_UL, ELEMENT_OL } from '@udecode/plate-list';
24
+ import castArray from 'lodash-es/castArray';
24
25
  import { createBoldPlugin as createBoldPlugin$1, createCodePlugin as createCodePlugin$1, createItalicPlugin as createItalicPlugin$1, createUnderlinePlugin as createUnderlinePlugin$1 } from '@udecode/plate-basic-marks';
25
26
  import isPlainObject from 'is-plain-obj';
26
27
  import { createParagraphPlugin as createParagraphPlugin$1 } from '@udecode/plate-paragraph';
@@ -944,14 +945,22 @@ function FetchingWrappedEntryCard(props) {
944
945
  var onEntityFetchComplete = props.onEntityFetchComplete;
945
946
  useEffect(function () {
946
947
  if (!entry || entry === 'failed') return;
948
+ var subscribed = true;
947
949
  entityHelpers.getEntryImage({
948
950
  entry: entry,
949
951
  contentType: contentType,
950
952
  localeCode: props.locale,
951
953
  defaultLocaleCode: defaultLocaleCode
952
- }, getOrLoadAsset).then(setFile)["catch"](function () {
953
- return setFile(null);
954
+ }, getOrLoadAsset)["catch"](function () {
955
+ return null;
956
+ }).then(function (file) {
957
+ if (subscribed) {
958
+ setFile(file);
959
+ }
954
960
  });
961
+ return function () {
962
+ subscribed = false;
963
+ };
955
964
  }, [entry, contentType, props.locale, defaultLocaleCode, props.sdk, file, getOrLoadAsset]);
956
965
  useEffect(function () {
957
966
  getOrLoadEntry(props.entryId);
@@ -3716,6 +3725,329 @@ function ListItem(props) {
3716
3725
  }), props.children);
3717
3726
  }
3718
3727
 
3728
+ /**
3729
+ * Credit: Modified version of Plate's list plugin
3730
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3731
+ */
3732
+ var moveListItemDown = function moveListItemDown(editor, _ref) {
3733
+ var list = _ref.list,
3734
+ listItem = _ref.listItem;
3735
+ var listNode = list[0];
3736
+ var listItemPath = listItem[1];
3737
+ var previousListItemPath;
3738
+
3739
+ try {
3740
+ previousListItemPath = Path.previous(listItemPath);
3741
+ } catch (e) {
3742
+ return;
3743
+ } // Previous sibling is the new parent
3744
+
3745
+
3746
+ var previousSiblingItem = Editor.node(editor, previousListItemPath);
3747
+
3748
+ if (previousSiblingItem) {
3749
+ var previousPath = previousSiblingItem[1];
3750
+ var subList = Array.from(Node.children(editor, previousPath)).find(function (_ref2) {
3751
+ var n = _ref2[0];
3752
+ return match(n, {
3753
+ type: getListTypes(editor)
3754
+ });
3755
+ });
3756
+ var newPath = Path.next(getLastChildPath(subList != null ? subList : previousSiblingItem));
3757
+ Editor.withoutNormalizing(editor, function () {
3758
+ if (!subList) {
3759
+ // Create new sub-list
3760
+ wrapNodes(editor, {
3761
+ type: listNode.type,
3762
+ children: [],
3763
+ data: {}
3764
+ }, {
3765
+ at: listItemPath
3766
+ });
3767
+ } // Move the current item to the sub-list
3768
+
3769
+
3770
+ Transforms.moveNodes(editor, {
3771
+ at: listItemPath,
3772
+ to: newPath
3773
+ });
3774
+ });
3775
+ }
3776
+ };
3777
+
3778
+ /**
3779
+ * Credit: Modified version of Plate's list plugin
3780
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3781
+ */
3782
+ var moveListItems = function moveListItems(editor, _temp) {
3783
+ var _editor$selection;
3784
+
3785
+ var _ref = _temp === void 0 ? {} : _temp,
3786
+ _ref$increase = _ref.increase,
3787
+ increase = _ref$increase === void 0 ? true : _ref$increase,
3788
+ _ref$at = _ref.at,
3789
+ at = _ref$at === void 0 ? (_editor$selection = editor.selection) != null ? _editor$selection : undefined : _ref$at;
3790
+
3791
+ var _nodes = getNodes(editor, {
3792
+ at: at,
3793
+ match: {
3794
+ type: getPluginType(editor, ELEMENT_LIC)
3795
+ }
3796
+ }); // Get the selected lic
3797
+
3798
+
3799
+ var lics = Array.from(_nodes);
3800
+ if (!lics.length) return;
3801
+ var highestLicPaths = [];
3802
+ var highestLicPathRefs = []; // Filter out the nested lic, we just need to move the highest ones
3803
+
3804
+ lics.forEach(function (lic) {
3805
+ var licPath = lic[1];
3806
+ var liPath = Path.parent(licPath);
3807
+ var isAncestor = highestLicPaths.some(function (path) {
3808
+ var highestLiPath = Path.parent(path);
3809
+ return Path.isAncestor(highestLiPath, liPath);
3810
+ });
3811
+
3812
+ if (!isAncestor) {
3813
+ highestLicPaths.push(licPath);
3814
+ highestLicPathRefs.push(Editor.pathRef(editor, licPath));
3815
+ }
3816
+ });
3817
+ var licPathRefsToMove = increase ? highestLicPathRefs : highestLicPathRefs.reverse();
3818
+ Editor.withoutNormalizing(editor, function () {
3819
+ licPathRefsToMove.forEach(function (licPathRef) {
3820
+ var licPath = licPathRef.unref();
3821
+ if (!licPath) return;
3822
+ var liEntry = getListItemEntry(editor, {
3823
+ at: licPath
3824
+ });
3825
+
3826
+ if (!liEntry) {
3827
+ return;
3828
+ }
3829
+
3830
+ if (increase) {
3831
+ moveListItemDown(editor, liEntry);
3832
+ } else if (isListNested(editor, liEntry.list[1])) {
3833
+ moveListItemUp(editor, liEntry);
3834
+ }
3835
+ });
3836
+ });
3837
+ };
3838
+
3839
+ /**
3840
+ * Credit: Modified version of Plate's list plugin
3841
+ * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
3842
+ */
3843
+ var listTypes = [BLOCKS.UL_LIST, BLOCKS.OL_LIST];
3844
+ var unwrapList = function unwrapList(editor, _temp) {
3845
+ var _ref = _temp === void 0 ? {} : _temp,
3846
+ at = _ref.at;
3847
+
3848
+ Editor.withoutNormalizing(editor, function () {
3849
+ do {
3850
+ unwrapNodes(editor, {
3851
+ at: at,
3852
+ match: {
3853
+ type: BLOCKS.LIST_ITEM
3854
+ },
3855
+ split: true
3856
+ });
3857
+ unwrapNodes(editor, {
3858
+ at: at,
3859
+ match: {
3860
+ type: listTypes
3861
+ },
3862
+ split: true
3863
+ });
3864
+ } while (getAbove(editor, {
3865
+ match: {
3866
+ type: listTypes,
3867
+ at: at
3868
+ }
3869
+ }));
3870
+ });
3871
+ };
3872
+
3873
+ var listTypes$1 = [BLOCKS.UL_LIST, BLOCKS.OL_LIST];
3874
+ var toggleList = function toggleList(editor, _ref) {
3875
+ var type = _ref.type;
3876
+ return Editor.withoutNormalizing(editor, function () {
3877
+ if (!editor.selection) {
3878
+ return;
3879
+ }
3880
+
3881
+ if (isCollapsed(editor.selection) || !isRangeAcrossBlocks(editor)) {
3882
+ // selection is collapsed
3883
+ var res = getListItemEntry(editor);
3884
+
3885
+ if (res) {
3886
+ var list = res.list;
3887
+
3888
+ if (list[0].type !== type) {
3889
+ setNodes(editor, {
3890
+ type: type
3891
+ }, {
3892
+ at: editor.selection,
3893
+ match: function match(n) {
3894
+ return listTypes$1.includes(n.type);
3895
+ },
3896
+ mode: 'lowest'
3897
+ });
3898
+ } else {
3899
+ unwrapList(editor);
3900
+ }
3901
+ } else {
3902
+ var _list = {
3903
+ type: type,
3904
+ children: [],
3905
+ data: {}
3906
+ };
3907
+ wrapNodes(editor, _list);
3908
+ var nodes = [].concat(getNodes(editor, {
3909
+ match: {
3910
+ type: getPluginType(editor, ELEMENT_DEFAULT)
3911
+ }
3912
+ }));
3913
+ var listItem = {
3914
+ type: BLOCKS.LIST_ITEM,
3915
+ children: [],
3916
+ data: {}
3917
+ };
3918
+
3919
+ for (var _iterator = _createForOfIteratorHelperLoose(nodes), _step; !(_step = _iterator()).done;) {
3920
+ var _step$value = _step.value,
3921
+ path = _step$value[1];
3922
+ wrapNodes(editor, listItem, {
3923
+ at: path
3924
+ });
3925
+ }
3926
+ }
3927
+ } else {
3928
+ // selection is a range
3929
+ var _Range$edges = Range.edges(editor.selection),
3930
+ startPoint = _Range$edges[0],
3931
+ endPoint = _Range$edges[1];
3932
+
3933
+ var commonEntry = Node.common(editor, startPoint.path, endPoint.path);
3934
+
3935
+ if (listTypes$1.includes(commonEntry[0].type) || commonEntry[0].type === BLOCKS.LIST_ITEM) {
3936
+ if (commonEntry[0].type !== type) {
3937
+ var startList = findNode(editor, {
3938
+ at: Range.start(editor.selection),
3939
+ match: {
3940
+ type: listTypes$1
3941
+ },
3942
+ mode: 'lowest'
3943
+ });
3944
+ var endList = findNode(editor, {
3945
+ at: Range.end(editor.selection),
3946
+ match: {
3947
+ type: listTypes$1
3948
+ },
3949
+ mode: 'lowest'
3950
+ });
3951
+
3952
+ if (!startList || !endList) {
3953
+ return;
3954
+ }
3955
+
3956
+ var rangeLength = Math.min(startList[1].length, endList[1].length);
3957
+ setNodes(editor, {
3958
+ type: type
3959
+ }, {
3960
+ at: editor.selection,
3961
+ match: function match(n, path) {
3962
+ return listTypes$1.includes(n.type) && path.length >= rangeLength;
3963
+ },
3964
+ mode: 'all'
3965
+ });
3966
+ } else {
3967
+ unwrapList(editor);
3968
+ }
3969
+ } else {
3970
+ var rootPathLength = commonEntry[1].length;
3971
+
3972
+ var _nodes = Array.from(getNodes(editor, {
3973
+ mode: 'all'
3974
+ })).filter(function (_ref2) {
3975
+ var path = _ref2[1];
3976
+ return path.length === rootPathLength + 1;
3977
+ }).reverse();
3978
+
3979
+ _nodes.forEach(function (n) {
3980
+ if (listTypes$1.includes(n[0].type)) {
3981
+ setNodes(editor, {
3982
+ type: type
3983
+ }, {
3984
+ at: n[1]
3985
+ });
3986
+ } else {
3987
+ setNodes(editor, {
3988
+ type: getPluginType(editor, ELEMENT_LIC)
3989
+ }, {
3990
+ at: n[1]
3991
+ });
3992
+ var _listItem = {
3993
+ type: BLOCKS.LIST_ITEM,
3994
+ children: [],
3995
+ data: {}
3996
+ };
3997
+ wrapNodes(editor, _listItem, {
3998
+ at: n[1]
3999
+ });
4000
+ var _list2 = {
4001
+ type: type,
4002
+ children: [],
4003
+ data: {}
4004
+ };
4005
+ wrapNodes(editor, _list2, {
4006
+ at: n[1]
4007
+ });
4008
+ }
4009
+ });
4010
+ }
4011
+ }
4012
+ });
4013
+ };
4014
+
4015
+ var onKeyDownList = function onKeyDownList(editor, _ref) {
4016
+ var type = _ref.type,
4017
+ hotkey = _ref.options.hotkey;
4018
+ return function (e) {
4019
+ if (e.key === 'Tab' && editor.selection) {
4020
+ var listSelected = getAbove(editor, {
4021
+ at: editor.selection,
4022
+ match: {
4023
+ type: type
4024
+ }
4025
+ });
4026
+
4027
+ if (listSelected) {
4028
+ e.preventDefault();
4029
+ moveListItems(editor, {
4030
+ increase: !e.shiftKey
4031
+ });
4032
+ return;
4033
+ }
4034
+ }
4035
+
4036
+ if (!hotkey) return;
4037
+ var hotkeys = castArray(hotkey);
4038
+
4039
+ for (var _iterator = _createForOfIteratorHelperLoose(hotkeys), _step; !(_step = _iterator()).done;) {
4040
+ var _hotkey = _step.value;
4041
+
4042
+ if (isHotkey(_hotkey)(e)) {
4043
+ toggleList(editor, {
4044
+ type: type
4045
+ });
4046
+ }
4047
+ }
4048
+ };
4049
+ };
4050
+
3719
4051
  var isList = function isList(node) {
3720
4052
  return [BLOCKS.OL_LIST, BLOCKS.UL_LIST].includes(node.type);
3721
4053
  };
@@ -4097,40 +4429,6 @@ var insertListFragment = function insertListFragment(editor) {
4097
4429
  };
4098
4430
  };
4099
4431
 
4100
- /**
4101
- * Credit: Modified version of Plate's list plugin
4102
- * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
4103
- */
4104
- var listTypes = [BLOCKS.UL_LIST, BLOCKS.OL_LIST];
4105
- var unwrapList = function unwrapList(editor, _temp) {
4106
- var _ref = _temp === void 0 ? {} : _temp,
4107
- at = _ref.at;
4108
-
4109
- Editor.withoutNormalizing(editor, function () {
4110
- do {
4111
- unwrapNodes(editor, {
4112
- at: at,
4113
- match: {
4114
- type: BLOCKS.LIST_ITEM
4115
- },
4116
- split: true
4117
- });
4118
- unwrapNodes(editor, {
4119
- at: at,
4120
- match: {
4121
- type: listTypes
4122
- },
4123
- split: true
4124
- });
4125
- } while (getAbove(editor, {
4126
- match: {
4127
- type: listTypes,
4128
- at: at
4129
- }
4130
- }));
4131
- });
4132
- };
4133
-
4134
4432
  /**
4135
4433
  * Credit: Modified version of Plate's list plugin
4136
4434
  * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
@@ -4240,12 +4538,18 @@ var createListPlugin = function createListPlugin() {
4240
4538
  overrideByKey: (_overrideByKey = {}, _overrideByKey[ELEMENT_UL] = {
4241
4539
  type: BLOCKS.UL_LIST,
4242
4540
  component: ListUL,
4541
+ handlers: {
4542
+ onKeyDown: onKeyDownList
4543
+ },
4243
4544
  // The withList is added on ELEMENT_UL plugin in upstream code
4244
4545
  // so we need to override it here
4245
4546
  withOverrides: withList
4246
4547
  }, _overrideByKey[ELEMENT_OL] = {
4247
4548
  type: BLOCKS.OL_LIST,
4248
- component: ListOL
4549
+ component: ListOL,
4550
+ handlers: {
4551
+ onKeyDown: onKeyDownList
4552
+ }
4249
4553
  }, _overrideByKey[ELEMENT_LIC] = {
4250
4554
  type: BLOCKS.PARAGRAPH
4251
4555
  }, _overrideByKey[ELEMENT_LI] = {
@@ -4269,148 +4573,6 @@ var createListPlugin = function createListPlugin() {
4269
4573
  });
4270
4574
  };
4271
4575
 
4272
- var listTypes$1 = [BLOCKS.UL_LIST, BLOCKS.OL_LIST];
4273
- var toggleList = function toggleList(editor, _ref) {
4274
- var type = _ref.type;
4275
- return Editor.withoutNormalizing(editor, function () {
4276
- if (!editor.selection) {
4277
- return;
4278
- }
4279
-
4280
- if (isCollapsed(editor.selection) || !isRangeAcrossBlocks(editor)) {
4281
- // selection is collapsed
4282
- var res = getListItemEntry(editor);
4283
-
4284
- if (res) {
4285
- var list = res.list;
4286
-
4287
- if (list[0].type !== type) {
4288
- setNodes(editor, {
4289
- type: type
4290
- }, {
4291
- at: editor.selection,
4292
- match: function match(n) {
4293
- return listTypes$1.includes(n.type);
4294
- },
4295
- mode: 'lowest'
4296
- });
4297
- } else {
4298
- unwrapList(editor);
4299
- }
4300
- } else {
4301
- var _list = {
4302
- type: type,
4303
- children: [],
4304
- data: {}
4305
- };
4306
- wrapNodes(editor, _list);
4307
- var nodes = [].concat(getNodes(editor, {
4308
- match: {
4309
- type: getPluginType(editor, ELEMENT_DEFAULT)
4310
- }
4311
- }));
4312
- var listItem = {
4313
- type: BLOCKS.LIST_ITEM,
4314
- children: [],
4315
- data: {}
4316
- };
4317
-
4318
- for (var _iterator = _createForOfIteratorHelperLoose(nodes), _step; !(_step = _iterator()).done;) {
4319
- var _step$value = _step.value,
4320
- path = _step$value[1];
4321
- wrapNodes(editor, listItem, {
4322
- at: path
4323
- });
4324
- }
4325
- }
4326
- } else {
4327
- // selection is a range
4328
- var _Range$edges = Range.edges(editor.selection),
4329
- startPoint = _Range$edges[0],
4330
- endPoint = _Range$edges[1];
4331
-
4332
- var commonEntry = Node.common(editor, startPoint.path, endPoint.path);
4333
-
4334
- if (listTypes$1.includes(commonEntry[0].type) || commonEntry[0].type === BLOCKS.LIST_ITEM) {
4335
- if (commonEntry[0].type !== type) {
4336
- var startList = findNode(editor, {
4337
- at: Range.start(editor.selection),
4338
- match: {
4339
- type: listTypes$1
4340
- },
4341
- mode: 'lowest'
4342
- });
4343
- var endList = findNode(editor, {
4344
- at: Range.end(editor.selection),
4345
- match: {
4346
- type: listTypes$1
4347
- },
4348
- mode: 'lowest'
4349
- });
4350
-
4351
- if (!startList || !endList) {
4352
- return;
4353
- }
4354
-
4355
- var rangeLength = Math.min(startList[1].length, endList[1].length);
4356
- setNodes(editor, {
4357
- type: type
4358
- }, {
4359
- at: editor.selection,
4360
- match: function match(n, path) {
4361
- return listTypes$1.includes(n.type) && path.length >= rangeLength;
4362
- },
4363
- mode: 'all'
4364
- });
4365
- } else {
4366
- unwrapList(editor);
4367
- }
4368
- } else {
4369
- var rootPathLength = commonEntry[1].length;
4370
-
4371
- var _nodes = Array.from(getNodes(editor, {
4372
- mode: 'all'
4373
- })).filter(function (_ref2) {
4374
- var path = _ref2[1];
4375
- return path.length === rootPathLength + 1;
4376
- }).reverse();
4377
-
4378
- _nodes.forEach(function (n) {
4379
- if (listTypes$1.includes(n[0].type)) {
4380
- setNodes(editor, {
4381
- type: type
4382
- }, {
4383
- at: n[1]
4384
- });
4385
- } else {
4386
- setNodes(editor, {
4387
- type: getPluginType(editor, ELEMENT_LIC)
4388
- }, {
4389
- at: n[1]
4390
- });
4391
- var _listItem = {
4392
- type: BLOCKS.LIST_ITEM,
4393
- children: [],
4394
- data: {}
4395
- };
4396
- wrapNodes(editor, _listItem, {
4397
- at: n[1]
4398
- });
4399
- var _list2 = {
4400
- type: type,
4401
- children: [],
4402
- data: {}
4403
- };
4404
- wrapNodes(editor, _list2, {
4405
- at: n[1]
4406
- });
4407
- }
4408
- });
4409
- }
4410
- }
4411
- });
4412
- };
4413
-
4414
4576
  function ToolbarListButton(props) {
4415
4577
  var sdk = useSdkContext();
4416
4578
  var editor = useContentfulEditor();
@@ -6212,6 +6374,80 @@ var disableCorePlugins = {
6212
6374
  eventEditor: true
6213
6375
  };
6214
6376
 
6377
+ /**
6378
+ * For legacy reasons, a document may not have any content at all
6379
+ * e.g:
6380
+ *
6381
+ * {nodeType: document, data: {}, content: []}
6382
+ *
6383
+ * Rendering such document will break the Slate editor
6384
+ */
6385
+
6386
+ var hasContent = function hasContent(doc) {
6387
+ if (!doc) {
6388
+ return false;
6389
+ }
6390
+
6391
+ return doc.content.length > 0;
6392
+ };
6393
+ /*
6394
+ Plate api doesn't allow to modify (easily) the editor value programmatically
6395
+ after the editor instance is created
6396
+
6397
+ This function is inspired to https://github.com/udecode/plate/issues/1269#issuecomment-1057643622
6398
+ */
6399
+
6400
+ var setEditorContent = function setEditorContent(editor, nodes) {
6401
+ // Replaces editor content while keeping change history
6402
+ Editor.withoutNormalizing(editor, function () {
6403
+ var children = [].concat(editor.children);
6404
+ children.forEach(function (node) {
6405
+ return editor.apply({
6406
+ type: 'remove_node',
6407
+ path: [0],
6408
+ node: node
6409
+ });
6410
+ });
6411
+
6412
+ if (nodes) {
6413
+ var nodesArray = Node.isNode(nodes) ? [nodes] : nodes;
6414
+ nodesArray.forEach(function (node, i) {
6415
+ return editor.apply({
6416
+ type: 'insert_node',
6417
+ path: [i],
6418
+ node: node
6419
+ });
6420
+ });
6421
+ }
6422
+
6423
+ var point = Editor.end(editor, []);
6424
+
6425
+ if (point) {
6426
+ Transforms.select(editor, point);
6427
+ }
6428
+ });
6429
+ };
6430
+ /**
6431
+ * Converts a contenful rich text document to the corresponding slate editor
6432
+ * value
6433
+ */
6434
+
6435
+ var documentToEditorValue = function documentToEditorValue(doc) {
6436
+ return toSlatejsDocument({
6437
+ document: hasContent(doc) ? doc : EMPTY_DOCUMENT,
6438
+ // TODO: get rid of schema, https://github.com/contentful/field-editors/pull/1065#discussion_r826723248
6439
+ schema: schema
6440
+ });
6441
+ };
6442
+ var normalizeEditorValue = function normalizeEditorValue(value, options) {
6443
+ var editor = createPlateEditor(options);
6444
+ editor.children = value;
6445
+ Editor.normalize(editor, {
6446
+ force: true
6447
+ });
6448
+ return editor.children;
6449
+ };
6450
+
6215
6451
  var STYLE_EDITOR_BORDER = "1px solid " + tokens.gray400;
6216
6452
  var styles$j = {
6217
6453
  root: /*#__PURE__*/css({
@@ -6308,7 +6544,17 @@ var EmbedEntityWidget = function EmbedEntityWidget(_ref) {
6308
6544
  setCanAccessAssets = _useState2[1];
6309
6545
 
6310
6546
  React__default.useEffect(function () {
6311
- sdk.access.can('read', 'Asset').then(setCanAccessAssets);
6547
+ var subscribed = true;
6548
+ sdk.access.can('read', 'Asset').then(function (can) {
6549
+ if (!subscribed) {
6550
+ return;
6551
+ }
6552
+
6553
+ setCanAccessAssets(can);
6554
+ });
6555
+ return function () {
6556
+ subscribed = false;
6557
+ };
6312
6558
  }, [sdk]);
6313
6559
  var inlineEntryEmbedEnabled = isNodeTypeEnabled(sdk.field, INLINES.EMBEDDED_ENTRY);
6314
6560
  var blockEntryEmbedEnabled = isNodeTypeEnabled(sdk.field, BLOCKS.EMBEDDED_ENTRY) && canInsertBlocks;
@@ -6463,50 +6709,6 @@ var StickyToolbarWrapper = function StickyToolbarWrapper(_ref) {
6463
6709
  }, children);
6464
6710
  };
6465
6711
 
6466
- /**
6467
- * For legacy reasons, a document may not have any content at all
6468
- * e.g:
6469
- *
6470
- * {nodeType: document, data: {}, content: []}
6471
- *
6472
- * Rendering such document will break the Slate editor
6473
- */
6474
-
6475
- var hasContent = function hasContent(doc) {
6476
- if (!doc) {
6477
- return false;
6478
- }
6479
-
6480
- return doc.content.length > 0;
6481
- };
6482
-
6483
- var useNormalizedSlateValue = function useNormalizedSlateValue(_ref) {
6484
- var id = _ref.id,
6485
- incomingDoc = _ref.incomingDoc,
6486
- plugins = _ref.plugins;
6487
- return useMemo(function () {
6488
- var editor = createPlateEditor({
6489
- id: id,
6490
- plugins: plugins,
6491
- disableCorePlugins: disableCorePlugins
6492
- });
6493
- var doc = toSlatejsDocument({
6494
- document: hasContent(incomingDoc) ? incomingDoc : EMPTY_DOCUMENT,
6495
- schema: schema
6496
- }); // Sets editor value & kicks normalization
6497
-
6498
- editor.children = doc;
6499
- Editor.normalize(editor, {
6500
- force: true
6501
- }); // TODO: return the editor itself to avoid recompiling & initializing all
6502
- // of the plugins again. It's currently not possible due to a bug in Plate
6503
- // with initialValues
6504
- // See: https://slate-js.slack.com/archives/C013QHXSCG1/p1645112799942819
6505
-
6506
- return editor.children;
6507
- }, [id, plugins, incomingDoc]);
6508
- };
6509
-
6510
6712
  /**
6511
6713
  * Returns whether a given operation is relevant enough to trigger a save.
6512
6714
  */
@@ -6520,8 +6722,10 @@ var isRelevantOperation = function isRelevantOperation(op) {
6520
6722
  };
6521
6723
 
6522
6724
  var useOnValueChanged = function useOnValueChanged(_ref) {
6523
- var editor = _ref.editor,
6524
- handler = _ref.handler;
6725
+ var editorId = _ref.editorId,
6726
+ handler = _ref.handler,
6727
+ skip = _ref.skip,
6728
+ onSkip = _ref.onSkip;
6525
6729
  var onChange = useMemo(function () {
6526
6730
  return debounce(function (document) {
6527
6731
  handler == null ? void 0 : handler(toContentfulDocument({
@@ -6531,45 +6735,84 @@ var useOnValueChanged = function useOnValueChanged(_ref) {
6531
6735
  }, 500);
6532
6736
  }, [handler]);
6533
6737
  return useCallback(function (value) {
6534
- var operations = editor.operations.filter(isRelevantOperation);
6738
+ var editor = getPlateSelectors(editorId).editor();
6739
+
6740
+ if (!editor) {
6741
+ throw new Error('Editor change callback called but editor not defined. Editor id: ' + editorId);
6742
+ }
6743
+
6744
+ var operations = editor == null ? void 0 : editor.operations.filter(isRelevantOperation);
6535
6745
 
6536
6746
  if (operations.length > 0) {
6747
+ if (skip) {
6748
+ onSkip == null ? void 0 : onSkip();
6749
+ return;
6750
+ }
6751
+
6537
6752
  onChange(value);
6538
6753
  }
6539
- }, [editor, onChange]);
6754
+ }, [editorId, onChange, skip, onSkip]);
6540
6755
  };
6541
6756
 
6542
6757
  var _excluded = ["sdk", "isInitiallyDisabled", "onAction"];
6543
6758
  var ConnectedRichTextEditor = function ConnectedRichTextEditor(props) {
6544
- var id = getContentfulEditorId(props.sdk); // TODO: remove in favor of getting the editor from useNormalizedSlateValue after upgrading to Plate v10
6545
-
6546
- var editor = useContentfulEditor(id);
6759
+ var id = getContentfulEditorId(props.sdk);
6547
6760
  var plugins = React__default.useMemo(function () {
6548
6761
  var _props$onAction;
6549
6762
 
6550
6763
  return getPlugins(props.sdk, (_props$onAction = props.onAction) != null ? _props$onAction : noop);
6551
6764
  }, [props.sdk, props.onAction]);
6552
- var initialValue = useNormalizedSlateValue({
6553
- id: id,
6554
- incomingDoc: props.value,
6555
- plugins: plugins
6556
- });
6765
+
6766
+ var _useState = useState(true),
6767
+ isFirstRender = _useState[0],
6768
+ setIsFirstRender = _useState[1];
6769
+
6770
+ var _useState2 = useState(false),
6771
+ pendingExternalUpdate = _useState2[0],
6772
+ setPendingExternalUpdate = _useState2[1];
6773
+
6557
6774
  var onValueChanged = useOnValueChanged({
6558
- editor: editor,
6559
- handler: props.onChange
6775
+ editorId: id,
6776
+ handler: props.onChange,
6777
+ skip: pendingExternalUpdate || isFirstRender,
6778
+ onSkip: function onSkip() {
6779
+ return setPendingExternalUpdate(false);
6780
+ }
6560
6781
  });
6782
+ useEffect(function () {
6783
+ /*
6784
+ This effect is called when the value prop changes. Normally
6785
+ this happens when the value is changed outside of the editor,
6786
+ like in snapshots restoration or remote updates
6787
+ Plate won't update the displayed value on its own, see:
6788
+ - https://github.com/ianstormtaylor/slate/pull/4540
6789
+ - https://github.com/udecode/plate/issues/1169
6790
+ The content is forcely set to the new value and it's ensured
6791
+ the change listener isn't invoked
6792
+ */
6793
+ setIsFirstRender(false);
6794
+ var editor = getPlateSelectors(id).editor();
6795
+
6796
+ if (!editor) {
6797
+ return;
6798
+ }
6799
+
6800
+ setPendingExternalUpdate(true);
6801
+ setEditorContent(editor, documentToEditorValue(props.value));
6802
+ }, [props.value, id]);
6561
6803
  var classNames = cx(styles$j.editor, props.minHeight !== undefined ? css({
6562
6804
  minHeight: props.minHeight
6563
6805
  }) : undefined, props.isDisabled ? styles$j.disabled : styles$j.enabled, props.isToolbarHidden && styles$j.hiddenToolbar);
6564
6806
  useEffect(function () {
6565
- // Ensure the plate state is cleared after the component unmounts
6566
- // This prevent new editors for the same field to display old outdated values
6567
- // Typical scenario: coming back to the entry editor after restoring a previous entry version
6568
- getPlateActions(id).enabled(true);
6569
- return function () {
6570
- return getPlateActions(id).enabled(false);
6571
- };
6572
- }, [id]);
6807
+ if (!isFirstRender) {
6808
+ return;
6809
+ }
6810
+
6811
+ getPlateActions(id).value(normalizeEditorValue(documentToEditorValue(props.value), {
6812
+ plugins: plugins,
6813
+ disableCorePlugins: disableCorePlugins
6814
+ }));
6815
+ }, [isFirstRender, plugins, id, props.value]);
6573
6816
  return /*#__PURE__*/React__default.createElement(SdkProvider, {
6574
6817
  sdk: props.sdk
6575
6818
  }, /*#__PURE__*/React__default.createElement(ContentfulEditorIdProvider, {
@@ -6579,7 +6822,6 @@ var ConnectedRichTextEditor = function ConnectedRichTextEditor(props) {
6579
6822
  "data-test-id": "rich-text-editor"
6580
6823
  }, /*#__PURE__*/React__default.createElement(Plate, {
6581
6824
  id: id,
6582
- initialValue: initialValue,
6583
6825
  plugins: plugins,
6584
6826
  disableCorePlugins: disableCorePlugins,
6585
6827
  editableProps: {
@@ -6603,6 +6845,7 @@ var RichTextEditor = function RichTextEditor(props) {
6603
6845
  var isEmptyValue = useCallback(function (value) {
6604
6846
  return !value || deepEquals(value, EMPTY_DOCUMENT);
6605
6847
  }, []);
6848
+ var id = getContentfulEditorId(props.sdk);
6606
6849
  return /*#__PURE__*/React__default.createElement(EntityProvider, {
6607
6850
  sdk: sdk
6608
6851
  }, /*#__PURE__*/React__default.createElement(FieldConnector, {
@@ -6614,10 +6857,9 @@ var RichTextEditor = function RichTextEditor(props) {
6614
6857
  }, function (_ref) {
6615
6858
  var lastRemoteValue = _ref.lastRemoteValue,
6616
6859
  disabled = _ref.disabled,
6617
- setValue = _ref.setValue,
6618
- externalReset = _ref.externalReset;
6860
+ setValue = _ref.setValue;
6619
6861
  return /*#__PURE__*/React__default.createElement(ConnectedRichTextEditor, Object.assign({}, otherProps, {
6620
- key: "rich-text-editor-" + externalReset,
6862
+ key: "rich-text-editor-" + id,
6621
6863
  value: lastRemoteValue,
6622
6864
  sdk: sdk,
6623
6865
  onAction: onAction,