@contentful/field-editor-rich-text 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/cjs/Toolbar/components/EmbedEntityWidget.js +8 -3
  2. package/dist/cjs/helpers/__tests__/removeInternalMarks.test.js +37 -21
  3. package/dist/cjs/helpers/getAllowedResourcesForNodeType.js +25 -0
  4. package/dist/cjs/helpers/newResourceEntitySelectorConfigFromRichTextField.js +21 -0
  5. package/dist/cjs/plugins/DragAndDrop/index.js +1 -0
  6. package/dist/cjs/plugins/EmbeddedEntityBlock/LinkedEntityBlock.js +27 -44
  7. package/dist/cjs/plugins/EmbeddedEntityBlock/index.js +3 -35
  8. package/dist/cjs/plugins/EmbeddedEntityInline/index.js +3 -2
  9. package/dist/cjs/plugins/EmbeddedResourceBlock/LinkedResourceBlock.js +54 -0
  10. package/dist/cjs/plugins/EmbeddedResourceBlock/index.js +55 -0
  11. package/dist/cjs/plugins/Hyperlink/components/EntityHyperlink.js +2 -1
  12. package/dist/cjs/plugins/Hyperlink/createHyperlinkPlugin.js +2 -3
  13. package/dist/cjs/plugins/Text/createTextPlugin.js +1 -0
  14. package/dist/cjs/plugins/index.js +2 -0
  15. package/dist/cjs/plugins/links-tracking.js +8 -17
  16. package/dist/cjs/plugins/{EmbeddedEntityBlock/ToolbarIcon.js → shared/EmbeddedBlockToolbarIcon.js} +15 -7
  17. package/dist/cjs/plugins/shared/EmbeddedBlockUtil.js +170 -0
  18. package/dist/cjs/plugins/shared/FetchingWrappedResourceCard.js +110 -0
  19. package/dist/cjs/plugins/shared/LinkedBlockWrapper.js +45 -0
  20. package/dist/cjs/useOnValueChanged.js +1 -1
  21. package/dist/esm/Toolbar/components/EmbedEntityWidget.js +8 -3
  22. package/dist/esm/helpers/__tests__/removeInternalMarks.test.js +37 -21
  23. package/dist/esm/helpers/getAllowedResourcesForNodeType.js +10 -0
  24. package/dist/esm/helpers/newResourceEntitySelectorConfigFromRichTextField.js +6 -0
  25. package/dist/esm/plugins/DragAndDrop/index.js +1 -0
  26. package/dist/esm/plugins/EmbeddedEntityBlock/LinkedEntityBlock.js +27 -44
  27. package/dist/esm/plugins/EmbeddedEntityBlock/index.js +3 -27
  28. package/dist/esm/plugins/EmbeddedEntityInline/index.js +4 -3
  29. package/dist/esm/plugins/EmbeddedResourceBlock/LinkedResourceBlock.js +39 -0
  30. package/dist/esm/plugins/EmbeddedResourceBlock/index.js +45 -0
  31. package/dist/esm/plugins/Hyperlink/components/EntityHyperlink.js +2 -1
  32. package/dist/esm/plugins/Hyperlink/createHyperlinkPlugin.js +2 -3
  33. package/dist/esm/plugins/Text/createTextPlugin.js +1 -0
  34. package/dist/esm/plugins/index.js +2 -0
  35. package/dist/esm/plugins/links-tracking.js +6 -10
  36. package/dist/esm/plugins/{EmbeddedEntityBlock/ToolbarIcon.js → shared/EmbeddedBlockToolbarIcon.js} +14 -6
  37. package/dist/esm/plugins/shared/EmbeddedBlockUtil.js +144 -0
  38. package/dist/esm/plugins/shared/FetchingWrappedResourceCard.js +56 -0
  39. package/dist/esm/plugins/shared/LinkedBlockWrapper.js +30 -0
  40. package/dist/esm/useOnValueChanged.js +1 -1
  41. package/dist/types/dialogs/HypelinkDialog/HyperlinkDialog.d.ts +3 -3
  42. package/dist/types/helpers/getAllowedResourcesForNodeType.d.ts +25 -0
  43. package/dist/types/helpers/newResourceEntitySelectorConfigFromRichTextField.d.ts +16 -0
  44. package/dist/types/plugins/EmbeddedEntityBlock/LinkedEntityBlock.d.ts +0 -1
  45. package/dist/types/plugins/EmbeddedEntityBlock/index.d.ts +1 -2
  46. package/dist/types/plugins/EmbeddedResourceBlock/LinkedResourceBlock.d.ts +18 -0
  47. package/dist/types/plugins/EmbeddedResourceBlock/index.d.ts +3 -0
  48. package/dist/types/plugins/links-tracking.d.ts +3 -3
  49. package/dist/types/plugins/shared/EmbeddedBlockToolbarIcon.d.ts +11 -0
  50. package/dist/types/plugins/shared/EmbeddedBlockUtil.d.ts +8 -0
  51. package/dist/types/plugins/shared/FetchingWrappedResourceCard.d.ts +14 -0
  52. package/dist/types/plugins/shared/LinkedBlockWrapper.d.ts +25 -0
  53. package/dist/types/test-utils/jsx.d.ts +1 -1
  54. package/package.json +7 -7
  55. package/dist/cjs/plugins/EmbeddedEntityBlock/Util.js +0 -108
  56. package/dist/esm/plugins/EmbeddedEntityBlock/Util.js +0 -85
  57. package/dist/types/plugins/EmbeddedEntityBlock/ToolbarIcon.d.ts +0 -11
  58. package/dist/types/plugins/EmbeddedEntityBlock/Util.d.ts +0 -4
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ getWithEmbeddedBlockEvents: function() {
13
+ return getWithEmbeddedBlockEvents;
14
+ },
15
+ selectEntityAndInsert: function() {
16
+ return selectEntityAndInsert;
17
+ },
18
+ selectResourceEntityAndInsert: function() {
19
+ return selectResourceEntityAndInsert;
20
+ }
21
+ });
22
+ const _richtexttypes = require("@contentful/rich-text-types");
23
+ const _ishotkey = _interop_require_default(require("is-hotkey"));
24
+ const _editor = require("../../helpers/editor");
25
+ const _newEntitySelectorConfigFromRichTextField = _interop_require_default(require("../../helpers/newEntitySelectorConfigFromRichTextField"));
26
+ const _newResourceEntitySelectorConfigFromRichTextField = _interop_require_default(require("../../helpers/newResourceEntitySelectorConfigFromRichTextField"));
27
+ const _sdkNavigatorSlideIn = require("../../helpers/sdkNavigatorSlideIn");
28
+ const _internal = require("../../internal");
29
+ function _interop_require_default(obj) {
30
+ return obj && obj.__esModule ? obj : {
31
+ default: obj
32
+ };
33
+ }
34
+ function getWithEmbeddedBlockEvents(nodeType, sdk) {
35
+ return (editor, { options: { hotkey } })=>(event)=>{
36
+ const [, pathToSelectedElement] = (0, _editor.getNodeEntryFromSelection)(editor, nodeType);
37
+ if (pathToSelectedElement) {
38
+ const isDelete = event.key === 'Delete';
39
+ const isBackspace = event.key === 'Backspace';
40
+ if (isDelete || isBackspace) {
41
+ event.preventDefault();
42
+ (0, _internal.removeNodes)(editor, {
43
+ at: pathToSelectedElement
44
+ });
45
+ }
46
+ return;
47
+ }
48
+ if (hotkey && (0, _ishotkey.default)(hotkey, event)) {
49
+ if (nodeType === _richtexttypes.BLOCKS.EMBEDDED_RESOURCE) {
50
+ selectResourceEntityAndInsert(sdk, editor, editor.tracking.onShortcutAction);
51
+ } else {
52
+ selectEntityAndInsert(nodeType, sdk, editor, editor.tracking.onShortcutAction);
53
+ }
54
+ }
55
+ };
56
+ }
57
+ async function selectEntityAndInsert(nodeType, sdk, editor, logAction) {
58
+ logAction('openCreateEmbedDialog', {
59
+ nodeType
60
+ });
61
+ const { field , dialogs } = sdk;
62
+ const baseConfig = (0, _newEntitySelectorConfigFromRichTextField.default)(field, nodeType);
63
+ const selectEntity = baseConfig.entityType === 'Asset' ? dialogs.selectSingleAsset : dialogs.selectSingleEntry;
64
+ const config = {
65
+ ...baseConfig,
66
+ withCreate: true
67
+ };
68
+ const { selection } = editor;
69
+ const rteSlide = (0, _sdkNavigatorSlideIn.watchCurrentSlide)(sdk.navigator);
70
+ const entity = await selectEntity(config);
71
+ if (!entity) {
72
+ logAction('cancelCreateEmbedDialog', {
73
+ nodeType
74
+ });
75
+ } else {
76
+ (0, _internal.select)(editor, selection);
77
+ insertBlock(editor, nodeType, entity);
78
+ ensureFollowingParagraph(editor, [
79
+ _richtexttypes.BLOCKS.EMBEDDED_ASSET,
80
+ _richtexttypes.BLOCKS.EMBEDDED_ENTRY
81
+ ]);
82
+ logAction('insert', {
83
+ nodeType
84
+ });
85
+ }
86
+ rteSlide.onActive(()=>{
87
+ rteSlide.unwatch();
88
+ (0, _editor.focus)(editor);
89
+ });
90
+ }
91
+ async function selectResourceEntityAndInsert(sdk, editor, logAction) {
92
+ logAction('openCreateEmbedDialog', {
93
+ nodeType: _richtexttypes.BLOCKS.EMBEDDED_RESOURCE
94
+ });
95
+ const { field , dialogs } = sdk;
96
+ const config = (0, _newResourceEntitySelectorConfigFromRichTextField.default)(field, _richtexttypes.BLOCKS.EMBEDDED_RESOURCE);
97
+ const { selection } = editor;
98
+ const entity = await dialogs.selectSingleResourceEntry(config);
99
+ if (!entity) {
100
+ logAction('cancelCreateEmbedDialog', {
101
+ nodeType: _richtexttypes.BLOCKS.EMBEDDED_RESOURCE
102
+ });
103
+ } else {
104
+ (0, _internal.select)(editor, selection);
105
+ insertBlock(editor, _richtexttypes.BLOCKS.EMBEDDED_RESOURCE, entity);
106
+ ensureFollowingParagraph(editor, [
107
+ _richtexttypes.BLOCKS.EMBEDDED_RESOURCE
108
+ ]);
109
+ logAction('insert', {
110
+ nodeType: _richtexttypes.BLOCKS.EMBEDDED_RESOURCE
111
+ });
112
+ }
113
+ }
114
+ function ensureFollowingParagraph(editor, nodeTypes) {
115
+ const entityBlock = (0, _internal.getAboveNode)(editor, {
116
+ match: {
117
+ type: nodeTypes
118
+ }
119
+ });
120
+ if (!entityBlock) {
121
+ return;
122
+ }
123
+ const level = entityBlock[1].length - 1;
124
+ const lastNode = (0, _internal.getLastNodeByLevel)(editor, level);
125
+ const isTextContainer = _richtexttypes.TEXT_CONTAINERS.includes(lastNode?.[0].type ?? '');
126
+ if (level !== 0 && !isTextContainer) {
127
+ (0, _editor.insertEmptyParagraph)(editor);
128
+ }
129
+ (0, _editor.moveToTheNextChar)(editor);
130
+ }
131
+ const getLink = (nodeType, entity)=>{
132
+ if (nodeType === _richtexttypes.BLOCKS.EMBEDDED_RESOURCE) {
133
+ return {
134
+ urn: entity.sys.urn,
135
+ type: 'ResourceLink',
136
+ linkType: 'Contentful:Entry'
137
+ };
138
+ }
139
+ return {
140
+ id: entity.sys.id,
141
+ type: 'Link',
142
+ linkType: entity.sys.type
143
+ };
144
+ };
145
+ const createNode = (nodeType, entity)=>{
146
+ return {
147
+ type: nodeType,
148
+ data: {
149
+ target: {
150
+ sys: getLink(nodeType, entity)
151
+ }
152
+ },
153
+ children: [
154
+ {
155
+ text: ''
156
+ }
157
+ ],
158
+ isVoid: true
159
+ };
160
+ };
161
+ function insertBlock(editor, nodeType, entity) {
162
+ if (!editor?.selection) return;
163
+ const linkedEntityBlock = createNode(nodeType, entity);
164
+ const hasText = editor.selection && !!(0, _internal.getText)(editor, editor.selection.focus.path);
165
+ if (hasText) {
166
+ (0, _internal.insertNodes)(editor, linkedEntityBlock);
167
+ } else {
168
+ (0, _internal.setNodes)(editor, linkedEntityBlock);
169
+ }
170
+ }
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "FetchingWrappedResourceCard", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return FetchingWrappedResourceCard;
9
+ }
10
+ });
11
+ const _react = _interop_require_wildcard(require("react"));
12
+ const _f36components = require("@contentful/f36-components");
13
+ const _fieldeditorreference = require("@contentful/field-editor-reference");
14
+ const _fastdeepequal = _interop_require_default(require("fast-deep-equal"));
15
+ function _interop_require_default(obj) {
16
+ return obj && obj.__esModule ? obj : {
17
+ default: obj
18
+ };
19
+ }
20
+ function _getRequireWildcardCache(nodeInterop) {
21
+ if (typeof WeakMap !== "function") return null;
22
+ var cacheBabelInterop = new WeakMap();
23
+ var cacheNodeInterop = new WeakMap();
24
+ return (_getRequireWildcardCache = function(nodeInterop) {
25
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
26
+ })(nodeInterop);
27
+ }
28
+ function _interop_require_wildcard(obj, nodeInterop) {
29
+ if (!nodeInterop && obj && obj.__esModule) {
30
+ return obj;
31
+ }
32
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
33
+ return {
34
+ default: obj
35
+ };
36
+ }
37
+ var cache = _getRequireWildcardCache(nodeInterop);
38
+ if (cache && cache.has(obj)) {
39
+ return cache.get(obj);
40
+ }
41
+ var newObj = {};
42
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
43
+ for(var key in obj){
44
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
45
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
46
+ if (desc && (desc.get || desc.set)) {
47
+ Object.defineProperty(newObj, key, desc);
48
+ } else {
49
+ newObj[key] = obj[key];
50
+ }
51
+ }
52
+ }
53
+ newObj.default = obj;
54
+ if (cache) {
55
+ cache.set(obj, newObj);
56
+ }
57
+ return newObj;
58
+ }
59
+ const InternalEntryCard = _react.memo((props)=>{
60
+ if (props.status === 'loading') {
61
+ return _react.createElement(_f36components.EntryCard, {
62
+ isLoading: true
63
+ });
64
+ }
65
+ if (!props.data || props.status === 'error') {
66
+ return _react.createElement(_fieldeditorreference.MissingEntityCard, {
67
+ entityType: "Entry",
68
+ isDisabled: props.isDisabled,
69
+ onRemove: props.onRemove
70
+ });
71
+ }
72
+ const { contentType , resource: entry , space } = props.data;
73
+ return _react.createElement(_fieldeditorreference.WrappedEntryCard, {
74
+ size: "default",
75
+ getAsset: ()=>Promise.resolve(),
76
+ isSelected: props.isSelected,
77
+ isDisabled: props.isDisabled,
78
+ localeCode: props.data.defaultLocaleCode,
79
+ defaultLocaleCode: props.data.defaultLocaleCode,
80
+ contentType: contentType,
81
+ spaceName: space?.name,
82
+ entry: entry,
83
+ onEdit: props.onEdit,
84
+ onRemove: props.isDisabled ? undefined : props.onRemove,
85
+ isClickable: false,
86
+ getEntityScheduledActions: ()=>Promise.resolve([])
87
+ });
88
+ }, _fastdeepequal.default);
89
+ InternalEntryCard.displayName = 'ReferenceCard';
90
+ const FetchingWrappedResourceCard = (props)=>{
91
+ const { link , onEntityFetchComplete } = props;
92
+ const { data , status } = (0, _fieldeditorreference.useResource)(link.linkType, link.urn);
93
+ _react.useEffect(()=>{
94
+ if (status === 'success') {
95
+ onEntityFetchComplete?.();
96
+ }
97
+ }, [
98
+ onEntityFetchComplete,
99
+ status
100
+ ]);
101
+ return _react.createElement(InternalEntryCard, {
102
+ data: data,
103
+ status: status,
104
+ sdk: props.sdk,
105
+ isDisabled: props.isDisabled,
106
+ isSelected: props.isSelected,
107
+ onEdit: props.onEdit,
108
+ onRemove: props.onRemove
109
+ });
110
+ };
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "LinkedBlockWrapper", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return LinkedBlockWrapper;
9
+ }
10
+ });
11
+ const _react = _interop_require_default(require("react"));
12
+ const _emotion = require("emotion");
13
+ const _environment = require("../../helpers/environment");
14
+ function _interop_require_default(obj) {
15
+ return obj && obj.__esModule ? obj : {
16
+ default: obj
17
+ };
18
+ }
19
+ const styles = {
20
+ root: (0, _emotion.css)({
21
+ marginBottom: '1.25rem !important',
22
+ display: 'block'
23
+ }),
24
+ container: (0, _emotion.css)({
25
+ display: 'inline-block',
26
+ verticalAlign: 'text-top',
27
+ width: '100%'
28
+ })
29
+ };
30
+ const isResourceLink = (link)=>!!link.urn;
31
+ function LinkedBlockWrapper({ attributes , card , children , element }) {
32
+ const link = element.data.target.sys;
33
+ return _react.default.createElement("div", {
34
+ ...attributes,
35
+ className: styles.root,
36
+ "data-entity-type": link.linkType,
37
+ "data-entity-id": isResourceLink(link) ? link.urn : link.id,
38
+ contentEditable: _environment.IS_CHROME ? undefined : false,
39
+ draggable: _environment.IS_CHROME ? true : undefined
40
+ }, _react.default.createElement("div", {
41
+ contentEditable: _environment.IS_CHROME ? false : undefined,
42
+ draggable: _environment.IS_CHROME ? true : undefined,
43
+ className: styles.container
44
+ }, card), children);
45
+ }
@@ -28,7 +28,7 @@ const isRelevantOperation = (op)=>{
28
28
  const useOnValueChanged = ({ editorId , handler , skip , onSkip })=>{
29
29
  const onChange = (0, _react.useMemo)(()=>(0, _debounce.default)((document)=>{
30
30
  const contentfulDocument = (0, _contentfulslatejsadapter.toContentfulDocument)({
31
- document,
31
+ document: document,
32
32
  schema: _Schema.default
33
33
  });
34
34
  const cleanedDocument = (0, _removeInternalMarks.removeInternalMarks)(contentfulDocument);
@@ -3,8 +3,8 @@ import { BLOCKS, INLINES } from '@contentful/rich-text-types';
3
3
  import { useContentfulEditor } from '../../ContentfulEditorProvider';
4
4
  import { isLinkActive } from '../../helpers/editor';
5
5
  import { isNodeTypeEnabled } from '../../helpers/validations';
6
- import { ToolbarIcon as EmbeddedEntityBlockToolbarIcon } from '../../plugins/EmbeddedEntityBlock';
7
6
  import { ToolbarEmbeddedEntityInlineButton } from '../../plugins/EmbeddedEntityInline';
7
+ import { EmbeddedBlockToolbarIcon } from '../../plugins/shared/EmbeddedBlockToolbarIcon';
8
8
  import { useSdkContext } from '../../SdkProvider';
9
9
  import { EmbeddedEntityDropdownButton } from './EmbeddedEntityDropdownButton';
10
10
  export const EmbedEntityWidget = ({ isDisabled , canInsertBlocks })=>{
@@ -15,15 +15,20 @@ export const EmbedEntityWidget = ({ isDisabled , canInsertBlocks })=>{
15
15
  const onToggleEntityDropdown = ()=>setEmbedDropdownOpen(!isEmbedDropdownOpen);
16
16
  const inlineEntryEmbedEnabled = isNodeTypeEnabled(sdk.field, INLINES.EMBEDDED_ENTRY);
17
17
  const blockEntryEmbedEnabled = isNodeTypeEnabled(sdk.field, BLOCKS.EMBEDDED_ENTRY) && canInsertBlocks;
18
+ const blockResourceEmbedEnabled = isNodeTypeEnabled(sdk.field, BLOCKS.EMBEDDED_RESOURCE) && canInsertBlocks;
18
19
  const blockAssetEmbedEnabled = isNodeTypeEnabled(sdk.field, BLOCKS.EMBEDDED_ASSET) && canInsertBlocks;
19
- const actions = React.createElement(React.Fragment, null, blockEntryEmbedEnabled && React.createElement(EmbeddedEntityBlockToolbarIcon, {
20
+ const actions = React.createElement(React.Fragment, null, blockEntryEmbedEnabled && React.createElement(EmbeddedBlockToolbarIcon, {
20
21
  isDisabled: !!isDisabled,
21
22
  nodeType: BLOCKS.EMBEDDED_ENTRY,
22
23
  onClose: onCloseEntityDropdown
24
+ }), blockResourceEmbedEnabled && React.createElement(EmbeddedBlockToolbarIcon, {
25
+ isDisabled: !!isDisabled,
26
+ nodeType: BLOCKS.EMBEDDED_RESOURCE,
27
+ onClose: onCloseEntityDropdown
23
28
  }), inlineEntryEmbedEnabled && React.createElement(ToolbarEmbeddedEntityInlineButton, {
24
29
  isDisabled: !!isDisabled || isLinkActive(editor),
25
30
  onClose: onCloseEntityDropdown
26
- }), blockAssetEmbedEnabled && React.createElement(EmbeddedEntityBlockToolbarIcon, {
31
+ }), blockAssetEmbedEnabled && React.createElement(EmbeddedBlockToolbarIcon, {
27
32
  isDisabled: !!isDisabled,
28
33
  nodeType: BLOCKS.EMBEDDED_ASSET,
29
34
  onClose: onCloseEntityDropdown
@@ -7,36 +7,52 @@ describe('internal mark', ()=>{
7
7
  const data = [
8
8
  {
9
9
  title: 'Paragraph mark is removed',
10
- input: toContentfulDocument(jsx("editor", null, jsx("hp", null, jsx("htext", {
11
- [COMMAND_PROMPT]: true
12
- }))).children),
13
- expected: toContentfulDocument(jsx("editor", null, jsx("hp", null, jsx("htext", null))).children)
10
+ input: toContentfulDocument({
11
+ document: jsx("editor", null, jsx("hp", null, jsx("htext", {
12
+ [COMMAND_PROMPT]: true
13
+ }))).children
14
+ }),
15
+ expected: toContentfulDocument({
16
+ document: jsx("editor", null, jsx("hp", null, jsx("htext", null))).children
17
+ })
14
18
  },
15
19
  {
16
20
  title: 'Heading mark is removed',
17
- input: toContentfulDocument(jsx("editor", null, jsx("hh1", null, jsx("htext", {
18
- [COMMAND_PROMPT]: true
19
- }))).children),
20
- expected: toContentfulDocument(jsx("editor", null, jsx("hh1", null, jsx("htext", null))).children)
21
+ input: toContentfulDocument({
22
+ document: jsx("editor", null, jsx("hh1", null, jsx("htext", {
23
+ [COMMAND_PROMPT]: true
24
+ }))).children
25
+ }),
26
+ expected: toContentfulDocument({
27
+ document: jsx("editor", null, jsx("hh1", null, jsx("htext", null))).children
28
+ })
21
29
  },
22
30
  {
23
31
  title: 'Block quote mark is removed',
24
- input: toContentfulDocument(jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", {
25
- [COMMAND_PROMPT]: true
26
- })))).children),
27
- expected: toContentfulDocument(jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", null)))).children)
32
+ input: toContentfulDocument({
33
+ document: jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", {
34
+ [COMMAND_PROMPT]: true
35
+ })))).children
36
+ }),
37
+ expected: toContentfulDocument({
38
+ document: jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", null)))).children
39
+ })
28
40
  },
29
41
  {
30
42
  title: 'Other marks are not removed',
31
- input: toContentfulDocument(jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", {
32
- bold: true,
33
- underline: true,
34
- [COMMAND_PROMPT]: true
35
- })))).children),
36
- expected: toContentfulDocument(jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", {
37
- bold: true,
38
- underline: true
39
- })))).children)
43
+ input: toContentfulDocument({
44
+ document: jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", {
45
+ bold: true,
46
+ underline: true,
47
+ [COMMAND_PROMPT]: true
48
+ })))).children
49
+ }),
50
+ expected: toContentfulDocument({
51
+ document: jsx("editor", null, jsx("hquote", null, jsx("hp", null, jsx("htext", {
52
+ bold: true,
53
+ underline: true
54
+ })))).children
55
+ })
40
56
  }
41
57
  ];
42
58
  for (const { input , expected , title } of data){
@@ -0,0 +1,10 @@
1
+ import find from 'lodash/find';
2
+ import flow from 'lodash/flow';
3
+ import get from 'lodash/get';
4
+ export default function getAllowedResourcesForNodeType(field, nodeType) {
5
+ return flow((validations)=>find(validations, 'nodes'), (validations)=>get(validations, [
6
+ 'nodes',
7
+ nodeType,
8
+ 'allowedResources'
9
+ ], []))(field.validations);
10
+ }
@@ -0,0 +1,6 @@
1
+ import getAllowedResourcesForNodeType from './getAllowedResourcesForNodeType';
2
+ export default function newResourceEntitySelectorConfigFromRichTextField(field, nodeType) {
3
+ return {
4
+ allowedResources: getAllowedResourcesForNodeType(field, nodeType)
5
+ };
6
+ }
@@ -4,6 +4,7 @@ export function createDragAndDropPlugin() {
4
4
  const DRAGGABLE_TYPES = [
5
5
  BLOCKS.EMBEDDED_ENTRY,
6
6
  BLOCKS.EMBEDDED_ASSET,
7
+ BLOCKS.EMBEDDED_RESOURCE,
7
8
  BLOCKS.HR,
8
9
  INLINES.EMBEDDED_ENTRY
9
10
  ];
@@ -1,26 +1,16 @@
1
1
  import * as React from 'react';
2
- import { css } from 'emotion';
3
2
  import { useReadOnly, useSelected } from 'slate-react';
4
3
  import { useContentfulEditor } from '../../ContentfulEditorProvider';
5
- import { IS_CHROME } from '../../helpers/environment';
6
4
  import { findNodePath } from '../../internal/queries';
7
5
  import { removeNodes } from '../../internal/transforms';
8
6
  import { useSdkContext } from '../../SdkProvider';
7
+ import { useLinkTracking } from '../links-tracking';
9
8
  import { FetchingWrappedAssetCard } from '../shared/FetchingWrappedAssetCard';
10
9
  import { FetchingWrappedEntryCard } from '../shared/FetchingWrappedEntryCard';
11
- const styles = {
12
- root: css({
13
- marginBottom: '1.25rem !important',
14
- display: 'block'
15
- }),
16
- container: css({
17
- display: 'inline-block',
18
- verticalAlign: 'text-top',
19
- width: '100%'
20
- })
21
- };
10
+ import { LinkedBlockWrapper } from '../shared/LinkedBlockWrapper';
22
11
  export function LinkedEntityBlock(props) {
23
- const { attributes , children , element , onEntityFetchComplete } = props;
12
+ const { attributes , children , element } = props;
13
+ const { onEntityFetchComplete } = useLinkTracking();
24
14
  const isSelected = useSelected();
25
15
  const editor = useContentfulEditor();
26
16
  const sdk = useSdkContext();
@@ -46,34 +36,27 @@ export function LinkedEntityBlock(props) {
46
36
  editor,
47
37
  element
48
38
  ]);
49
- return React.createElement("div", {
50
- ...attributes,
51
- className: styles.root,
52
- "data-entity-type": entityType,
53
- "data-entity-id": entityId,
54
- contentEditable: IS_CHROME ? undefined : false,
55
- draggable: IS_CHROME ? true : undefined
56
- }, React.createElement("div", {
57
- contentEditable: IS_CHROME ? false : undefined,
58
- draggable: IS_CHROME ? true : undefined,
59
- className: styles.container
60
- }, entityType === 'Entry' && React.createElement(FetchingWrappedEntryCard, {
61
- sdk: sdk,
62
- entryId: entityId,
63
- locale: sdk.field.locale,
64
- isDisabled: isDisabled,
65
- isSelected: isSelected,
66
- onRemove: handleRemoveClick,
67
- onEdit: handleEditClick,
68
- onEntityFetchComplete: onEntityFetchComplete
69
- }), entityType === 'Asset' && React.createElement(FetchingWrappedAssetCard, {
70
- sdk: sdk,
71
- assetId: entityId,
72
- locale: sdk.field.locale,
73
- isDisabled: isDisabled,
74
- isSelected: isSelected,
75
- onRemove: handleRemoveClick,
76
- onEdit: handleEditClick,
77
- onEntityFetchComplete: onEntityFetchComplete
78
- })), children);
39
+ return React.createElement(LinkedBlockWrapper, {
40
+ attributes: attributes,
41
+ card: React.createElement(React.Fragment, null, entityType === 'Entry' && React.createElement(FetchingWrappedEntryCard, {
42
+ sdk: sdk,
43
+ entryId: entityId,
44
+ locale: sdk.field.locale,
45
+ isDisabled: isDisabled,
46
+ isSelected: isSelected,
47
+ onRemove: handleRemoveClick,
48
+ onEdit: handleEditClick,
49
+ onEntityFetchComplete: onEntityFetchComplete
50
+ }), entityType === 'Asset' && React.createElement(FetchingWrappedAssetCard, {
51
+ sdk: sdk,
52
+ assetId: entityId,
53
+ locale: sdk.field.locale,
54
+ isDisabled: isDisabled,
55
+ isSelected: isSelected,
56
+ onRemove: handleRemoveClick,
57
+ onEdit: handleEditClick,
58
+ onEntityFetchComplete: onEntityFetchComplete
59
+ })),
60
+ element: element
61
+ }, children);
79
62
  }
@@ -1,45 +1,21 @@
1
1
  import { BLOCKS } from '@contentful/rich-text-types';
2
- import isHotkey from 'is-hotkey';
3
- import { getNodeEntryFromSelection } from '../../helpers/editor';
4
- import { removeNodes } from '../../internal/transforms';
5
- import { withLinkTracking } from '../links-tracking';
2
+ import { getWithEmbeddedBlockEvents } from '../shared/EmbeddedBlockUtil';
6
3
  import { LinkedEntityBlock } from './LinkedEntityBlock';
7
- import { selectEntityAndInsert } from './Util';
8
- export { EmbeddedEntityBlockToolbarIcon as ToolbarIcon } from './ToolbarIcon';
9
4
  const entityTypes = {
10
5
  [BLOCKS.EMBEDDED_ENTRY]: 'Entry',
11
6
  [BLOCKS.EMBEDDED_ASSET]: 'Asset'
12
7
  };
13
- function getWithEmbeddedEntityEvents(nodeType, sdk) {
14
- return (editor, { options: { hotkey } })=>(event)=>{
15
- const [, pathToSelectedElement] = getNodeEntryFromSelection(editor, nodeType);
16
- if (pathToSelectedElement) {
17
- const isDelete = event.key === 'Delete';
18
- const isBackspace = event.key === 'Backspace';
19
- if (isDelete || isBackspace) {
20
- event.preventDefault();
21
- removeNodes(editor, {
22
- at: pathToSelectedElement
23
- });
24
- }
25
- return;
26
- }
27
- if (hotkey && isHotkey(hotkey, event)) {
28
- selectEntityAndInsert(nodeType, sdk, editor, editor.tracking.onShortcutAction);
29
- }
30
- };
31
- }
32
8
  const createEmbeddedEntityPlugin = (nodeType, hotkey)=>(sdk)=>({
33
9
  key: nodeType,
34
10
  type: nodeType,
35
11
  isElement: true,
36
12
  isVoid: true,
37
- component: withLinkTracking(LinkedEntityBlock),
13
+ component: LinkedEntityBlock,
38
14
  options: {
39
15
  hotkey
40
16
  },
41
17
  handlers: {
42
- onKeyDown: getWithEmbeddedEntityEvents(nodeType, sdk)
18
+ onKeyDown: getWithEmbeddedBlockEvents(nodeType, sdk)
43
19
  },
44
20
  deserializeHtml: {
45
21
  rules: [
@@ -14,7 +14,7 @@ import { watchCurrentSlide } from '../../helpers/sdkNavigatorSlideIn';
14
14
  import { findNodePath } from '../../internal/queries';
15
15
  import { insertNodes, removeNodes, select } from '../../internal/transforms';
16
16
  import { useSdkContext } from '../../SdkProvider';
17
- import { withLinkTracking } from '../links-tracking';
17
+ import { useLinkTracking } from '../links-tracking';
18
18
  import { FetchingWrappedInlineEntryCard } from './FetchingWrappedInlineEntryCard';
19
19
  import { createInlineEntryNode } from './Util';
20
20
  const styles = {
@@ -36,6 +36,7 @@ function EmbeddedEntityInline(props) {
36
36
  const isSelected = useSelected();
37
37
  const { id: entryId } = props.element.data.target.sys;
38
38
  const isDisabled = useReadOnly();
39
+ const { onEntityFetchComplete } = useLinkTracking();
39
40
  function handleEditClick() {
40
41
  return sdk.navigator.openEntry(entryId, {
41
42
  slideIn: {
@@ -68,7 +69,7 @@ function EmbeddedEntityInline(props) {
68
69
  isDisabled: isDisabled,
69
70
  onRemove: handleRemoveClick,
70
71
  onEdit: handleEditClick,
71
- onEntityFetchComplete: props.onEntityFetchComplete
72
+ onEntityFetchComplete: onEntityFetchComplete
72
73
  })), props.children);
73
74
  }
74
75
  async function selectEntityAndInsert(editor, sdk, logAction) {
@@ -129,7 +130,7 @@ export function createEmbeddedEntityInlinePlugin(sdk) {
129
130
  isElement: true,
130
131
  isInline: true,
131
132
  isVoid: true,
132
- component: withLinkTracking(EmbeddedEntityInline),
133
+ component: EmbeddedEntityInline,
133
134
  options: {
134
135
  hotkey: 'mod+shift+2'
135
136
  },