@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.
- package/dist/cjs/Toolbar/components/EmbedEntityWidget.js +8 -3
- package/dist/cjs/helpers/__tests__/removeInternalMarks.test.js +37 -21
- package/dist/cjs/helpers/getAllowedResourcesForNodeType.js +25 -0
- package/dist/cjs/helpers/newResourceEntitySelectorConfigFromRichTextField.js +21 -0
- package/dist/cjs/plugins/DragAndDrop/index.js +1 -0
- package/dist/cjs/plugins/EmbeddedEntityBlock/LinkedEntityBlock.js +27 -44
- package/dist/cjs/plugins/EmbeddedEntityBlock/index.js +3 -35
- package/dist/cjs/plugins/EmbeddedEntityInline/index.js +3 -2
- package/dist/cjs/plugins/EmbeddedResourceBlock/LinkedResourceBlock.js +54 -0
- package/dist/cjs/plugins/EmbeddedResourceBlock/index.js +55 -0
- package/dist/cjs/plugins/Hyperlink/components/EntityHyperlink.js +2 -1
- package/dist/cjs/plugins/Hyperlink/createHyperlinkPlugin.js +2 -3
- package/dist/cjs/plugins/Text/createTextPlugin.js +1 -0
- package/dist/cjs/plugins/index.js +2 -0
- package/dist/cjs/plugins/links-tracking.js +8 -17
- package/dist/cjs/plugins/{EmbeddedEntityBlock/ToolbarIcon.js → shared/EmbeddedBlockToolbarIcon.js} +15 -7
- package/dist/cjs/plugins/shared/EmbeddedBlockUtil.js +170 -0
- package/dist/cjs/plugins/shared/FetchingWrappedResourceCard.js +110 -0
- package/dist/cjs/plugins/shared/LinkedBlockWrapper.js +45 -0
- package/dist/cjs/useOnValueChanged.js +1 -1
- package/dist/esm/Toolbar/components/EmbedEntityWidget.js +8 -3
- package/dist/esm/helpers/__tests__/removeInternalMarks.test.js +37 -21
- package/dist/esm/helpers/getAllowedResourcesForNodeType.js +10 -0
- package/dist/esm/helpers/newResourceEntitySelectorConfigFromRichTextField.js +6 -0
- package/dist/esm/plugins/DragAndDrop/index.js +1 -0
- package/dist/esm/plugins/EmbeddedEntityBlock/LinkedEntityBlock.js +27 -44
- package/dist/esm/plugins/EmbeddedEntityBlock/index.js +3 -27
- package/dist/esm/plugins/EmbeddedEntityInline/index.js +4 -3
- package/dist/esm/plugins/EmbeddedResourceBlock/LinkedResourceBlock.js +39 -0
- package/dist/esm/plugins/EmbeddedResourceBlock/index.js +45 -0
- package/dist/esm/plugins/Hyperlink/components/EntityHyperlink.js +2 -1
- package/dist/esm/plugins/Hyperlink/createHyperlinkPlugin.js +2 -3
- package/dist/esm/plugins/Text/createTextPlugin.js +1 -0
- package/dist/esm/plugins/index.js +2 -0
- package/dist/esm/plugins/links-tracking.js +6 -10
- package/dist/esm/plugins/{EmbeddedEntityBlock/ToolbarIcon.js → shared/EmbeddedBlockToolbarIcon.js} +14 -6
- package/dist/esm/plugins/shared/EmbeddedBlockUtil.js +144 -0
- package/dist/esm/plugins/shared/FetchingWrappedResourceCard.js +56 -0
- package/dist/esm/plugins/shared/LinkedBlockWrapper.js +30 -0
- package/dist/esm/useOnValueChanged.js +1 -1
- package/dist/types/dialogs/HypelinkDialog/HyperlinkDialog.d.ts +3 -3
- package/dist/types/helpers/getAllowedResourcesForNodeType.d.ts +25 -0
- package/dist/types/helpers/newResourceEntitySelectorConfigFromRichTextField.d.ts +16 -0
- package/dist/types/plugins/EmbeddedEntityBlock/LinkedEntityBlock.d.ts +0 -1
- package/dist/types/plugins/EmbeddedEntityBlock/index.d.ts +1 -2
- package/dist/types/plugins/EmbeddedResourceBlock/LinkedResourceBlock.d.ts +18 -0
- package/dist/types/plugins/EmbeddedResourceBlock/index.d.ts +3 -0
- package/dist/types/plugins/links-tracking.d.ts +3 -3
- package/dist/types/plugins/shared/EmbeddedBlockToolbarIcon.d.ts +11 -0
- package/dist/types/plugins/shared/EmbeddedBlockUtil.d.ts +8 -0
- package/dist/types/plugins/shared/FetchingWrappedResourceCard.d.ts +14 -0
- package/dist/types/plugins/shared/LinkedBlockWrapper.d.ts +25 -0
- package/dist/types/test-utils/jsx.d.ts +1 -1
- package/package.json +7 -7
- package/dist/cjs/plugins/EmbeddedEntityBlock/Util.js +0 -108
- package/dist/esm/plugins/EmbeddedEntityBlock/Util.js +0 -85
- package/dist/types/plugins/EmbeddedEntityBlock/ToolbarIcon.d.ts +0 -11
- 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(
|
|
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(
|
|
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(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
|
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(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
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:
|
|
13
|
+
component: LinkedEntityBlock,
|
|
38
14
|
options: {
|
|
39
15
|
hotkey
|
|
40
16
|
},
|
|
41
17
|
handlers: {
|
|
42
|
-
onKeyDown:
|
|
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 {
|
|
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:
|
|
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:
|
|
133
|
+
component: EmbeddedEntityInline,
|
|
133
134
|
options: {
|
|
134
135
|
hotkey: 'mod+shift+2'
|
|
135
136
|
},
|