@dotcms/react 0.0.1-beta.4 → 0.0.1-beta.41
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/README.md +539 -80
- package/index.esm.js +49 -2169
- package/next.esm.js +1037 -236
- package/package.json +10 -5
- package/src/lib/deprecated/components/BlockEditorRenderer/blocks/Contentlet.d.ts +1 -1
- package/src/lib/deprecated/components/DotEditableText/DotEditableText.d.ts +1 -0
- package/src/lib/deprecated/components/DotcmsLayout/DotcmsLayout.d.ts +1 -0
- package/src/lib/deprecated/mocks/mockPageContext.d.ts +1 -0
- package/src/lib/next/__test__/mock.d.ts +2 -1
- package/src/lib/next/components/Column/Column.d.ts +1 -1
- package/src/lib/next/components/Container/Container.d.ts +1 -1
- package/src/lib/next/components/Container/{ContainerFallbakcs.d.ts → ContainerFallbacks.d.ts} +2 -2
- package/src/lib/next/components/Contentlet/Contentlet.d.ts +2 -2
- package/src/lib/next/components/DotCMSBlockEditorRenderer/DotCMSBlockEditorRenderer.d.ts +27 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/BlockEditorBlock.d.ts +15 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Code.d.ts +24 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/DotContent.d.ts +14 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Image.d.ts +10 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Lists.d.ts +26 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/NoComponentProvided.d.ts +3 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Table.d.ts +16 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Texts.d.ts +81 -0
- package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Video.d.ts +10 -0
- package/src/lib/next/components/DotCMSEditableText/DotCMSEditableText.d.ts +32 -0
- package/src/lib/next/components/DotCMSEditableText/utils.d.ts +36 -0
- package/src/lib/next/components/DotCMSLayoutBody/DotCMSLayoutBody.d.ts +5 -4
- package/src/lib/next/components/DotCMSLayoutBody/components/ErrorMessage.d.ts +1 -4
- package/src/lib/next/components/DotCMSShow/DotCMSShow.d.ts +49 -0
- package/src/lib/next/components/FallbackComponent/FallbackComponent.d.ts +6 -6
- package/src/lib/next/components/Row/Row.d.ts +1 -1
- package/src/lib/next/contexts/DotCMSPageContext.d.ts +2 -3
- package/src/lib/next/hooks/useDotCMSShowWhen.d.ts +31 -0
- package/src/lib/next/hooks/useEditableDotCMSPage.d.ts +90 -0
- package/src/lib/next/hooks/useIsDevMode.d.ts +2 -5
- package/src/next.d.ts +5 -0
- package/web.url-search-params.size.esm.js +4216 -0
- package/es.regexp.to-string.esm.js +0 -1878
- package/src/lib/next/types.d.ts +0 -421
- package/src/lib/next/utils/index.d.ts +0 -136
package/next.esm.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { s as styleInject } from './web.url-search-params.size.esm.js';
|
|
1
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
3
|
import { createContext, useContext, useState, useEffect, useLayoutEffect, useRef, useMemo } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { getUVEState } from '@dotcms/uve';
|
|
5
|
-
import {
|
|
4
|
+
import { UVE_MODE, UVEEventType, DotCMSUVEAction } from '@dotcms/types';
|
|
5
|
+
import { getUVEState, initUVE, updateNavigation, createUVESubscription, sendMessageToUVE } from '@dotcms/uve';
|
|
6
|
+
import { DEVELOPMENT_MODE, EMPTY_CONTAINER_STYLE_REACT, getDotContentletAttributes, CUSTOM_NO_COMPONENT, getContainersData, getContentletsInContainer, getDotContainerAttributes, getColumnPositionClasses, combineClasses, __DEFAULT_TINYMCE_CONFIG__, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__, __TINYMCE_PATH_ON_DOTCMS__, isValidBlocks } from '@dotcms/uve/internal';
|
|
7
|
+
import { Editor } from '@tinymce/tinymce-react';
|
|
8
|
+
import { __DOTCMS_UVE_EVENT__, BlockEditorDefaultBlocks } from '@dotcms/types/internal';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* The `PageContext` is a React context that provides access to the DotCMS page context.
|
|
@@ -20,18 +23,15 @@ const DotCMSPageContext = /*#__PURE__*/createContext({
|
|
|
20
23
|
* A React hook that determines if the current environment is in development mode.
|
|
21
24
|
*
|
|
22
25
|
* The hook returns `true` if either:
|
|
23
|
-
* - The
|
|
24
|
-
* - The application is running inside the DotCMS editor (as determined by `isInsideEditor()`).
|
|
26
|
+
* - The application is running inside the DotCMS editor (as determined by `getUVEState()`).
|
|
25
27
|
*
|
|
26
|
-
* @param {DotCMSPageRendererMode} [renderMode] - Optional override for the render mode.
|
|
27
28
|
* @returns {boolean} - `true` if in development mode or inside the editor; otherwise, `false`.
|
|
28
29
|
*/
|
|
29
|
-
const useIsDevMode =
|
|
30
|
+
const useIsDevMode = () => {
|
|
30
31
|
const {
|
|
31
32
|
mode
|
|
32
33
|
} = useContext(DotCMSPageContext);
|
|
33
|
-
const
|
|
34
|
-
const [isDevMode, setIsDevMode] = useState(effectiveMode === 'development');
|
|
34
|
+
const [isDevMode, setIsDevMode] = useState(mode === 'development');
|
|
35
35
|
useEffect(() => {
|
|
36
36
|
var _getUVEState;
|
|
37
37
|
// Inside UVE we rely on the UVE state to determine if we are in development mode
|
|
@@ -41,9 +41,8 @@ const useIsDevMode = renderMode => {
|
|
|
41
41
|
setIsDevMode(isUVEInEditor);
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}, [renderMode, mode]);
|
|
44
|
+
setIsDevMode(mode === DEVELOPMENT_MODE);
|
|
45
|
+
}, [mode]);
|
|
47
46
|
return isDevMode;
|
|
48
47
|
};
|
|
49
48
|
|
|
@@ -52,17 +51,12 @@ const useIsDevMode = renderMode => {
|
|
|
52
51
|
*
|
|
53
52
|
* @return {JSX.Element} Error message component
|
|
54
53
|
*/
|
|
55
|
-
const ErrorMessage = ({
|
|
56
|
-
|
|
57
|
-
}) => {
|
|
54
|
+
const ErrorMessage = () => {
|
|
55
|
+
const isDevMode = useIsDevMode();
|
|
58
56
|
useEffect(() => {
|
|
59
57
|
console.warn('Missing required layout.body property in page');
|
|
60
58
|
}, []);
|
|
61
|
-
|
|
62
|
-
if (!isDevMode) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
return jsxs("div", {
|
|
59
|
+
return isDevMode && jsxs("div", {
|
|
66
60
|
"data-testid": "error-message",
|
|
67
61
|
style: {
|
|
68
62
|
padding: '1rem',
|
|
@@ -97,207 +91,6 @@ var css_248z = ".Column-module_col-start-1__xylw6 {\n grid-column-start: 1;\n
|
|
|
97
91
|
var styles = {"col-start-1":"Column-module_col-start-1__xylw6","col-start-2":"Column-module_col-start-2__Mod81","col-start-3":"Column-module_col-start-3__HHbXB","col-start-4":"Column-module_col-start-4__Uk-Qj","col-start-5":"Column-module_col-start-5__jlV8e","col-start-6":"Column-module_col-start-6__oi8k0","col-start-7":"Column-module_col-start-7__EmPky","col-start-8":"Column-module_col-start-8__hLI1h","col-start-9":"Column-module_col-start-9__Kcv9X","col-start-10":"Column-module_col-start-10__-MOrt","col-start-11":"Column-module_col-start-11__gDEQM","col-start-12":"Column-module_col-start-12__omVX6","col-end-1":"Column-module_col-end-1__Ho2y9","col-end-2":"Column-module_col-end-2__KwFu9","col-end-3":"Column-module_col-end-3__vfbJk","col-end-4":"Column-module_col-end-4__d4pyL","col-end-5":"Column-module_col-end-5__6yPd4","col-end-6":"Column-module_col-end-6__xQpAX","col-end-7":"Column-module_col-end-7__CCF7e","col-end-8":"Column-module_col-end-8__fVWEi","col-end-9":"Column-module_col-end-9__tpIGv","col-end-10":"Column-module_col-end-10__SX75K","col-end-11":"Column-module_col-end-11__9K1zv","col-end-12":"Column-module_col-end-12__oqTiE","col-end-13":"Column-module_col-end-13__L-nK9"};
|
|
98
92
|
styleInject(css_248z);
|
|
99
93
|
|
|
100
|
-
const endClassMap = {
|
|
101
|
-
1: 'col-end-1',
|
|
102
|
-
2: 'col-end-2',
|
|
103
|
-
3: 'col-end-3',
|
|
104
|
-
4: 'col-end-4',
|
|
105
|
-
5: 'col-end-5',
|
|
106
|
-
6: 'col-end-6',
|
|
107
|
-
7: 'col-end-7',
|
|
108
|
-
8: 'col-end-8',
|
|
109
|
-
9: 'col-end-9',
|
|
110
|
-
10: 'col-end-10',
|
|
111
|
-
11: 'col-end-11',
|
|
112
|
-
12: 'col-end-12',
|
|
113
|
-
13: 'col-end-13'
|
|
114
|
-
};
|
|
115
|
-
const startClassMap = {
|
|
116
|
-
1: 'col-start-1',
|
|
117
|
-
2: 'col-start-2',
|
|
118
|
-
3: 'col-start-3',
|
|
119
|
-
4: 'col-start-4',
|
|
120
|
-
5: 'col-start-5',
|
|
121
|
-
6: 'col-start-6',
|
|
122
|
-
7: 'col-start-7',
|
|
123
|
-
8: 'col-start-8',
|
|
124
|
-
9: 'col-start-9',
|
|
125
|
-
10: 'col-start-10',
|
|
126
|
-
11: 'col-start-11',
|
|
127
|
-
12: 'col-start-12'
|
|
128
|
-
};
|
|
129
|
-
/**
|
|
130
|
-
* @internal
|
|
131
|
-
*
|
|
132
|
-
* Combine classes into a single string.
|
|
133
|
-
*
|
|
134
|
-
* @param {string[]} classes
|
|
135
|
-
* @returns {string} Combined classes
|
|
136
|
-
*/
|
|
137
|
-
const combineClasses = classes => classes.filter(Boolean).join(' ');
|
|
138
|
-
/**
|
|
139
|
-
* @internal
|
|
140
|
-
*
|
|
141
|
-
* Calculates and returns the CSS Grid positioning classes for a column based on its configuration.
|
|
142
|
-
* Uses a 12-column grid system where columns are positioned using grid-column-start and grid-column-end.
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* ```typescript
|
|
146
|
-
* const classes = getColumnPositionClasses({
|
|
147
|
-
* leftOffset: 1, // Starts at the first column
|
|
148
|
-
* width: 6 // Spans 6 columns
|
|
149
|
-
* });
|
|
150
|
-
* // Returns: { startClass: 'col-start-1', endClass: 'col-end-7' }
|
|
151
|
-
* ```
|
|
152
|
-
*
|
|
153
|
-
* @param {DotPageAssetLayoutColumn} column - Column configuration object
|
|
154
|
-
* @param {number} column.leftOffset - Starting position (0-based) in the grid
|
|
155
|
-
* @param {number} column.width - Number of columns to span
|
|
156
|
-
* @returns {{ startClass: string, endClass: string }} Object containing CSS class names for grid positioning
|
|
157
|
-
*/
|
|
158
|
-
const getColumnPositionClasses = column => {
|
|
159
|
-
const {
|
|
160
|
-
leftOffset,
|
|
161
|
-
width
|
|
162
|
-
} = column;
|
|
163
|
-
const startClass = startClassMap[leftOffset];
|
|
164
|
-
const endClass = endClassMap[leftOffset + width];
|
|
165
|
-
return {
|
|
166
|
-
startClass,
|
|
167
|
-
endClass
|
|
168
|
-
};
|
|
169
|
-
};
|
|
170
|
-
/**
|
|
171
|
-
* @internal
|
|
172
|
-
*
|
|
173
|
-
* Helper function that returns an object containing the dotCMS data attributes.
|
|
174
|
-
* @param {DotCMSContentlet} contentlet - The contentlet to get the attributes for
|
|
175
|
-
* @param {string} container - The container to get the attributes for
|
|
176
|
-
* @returns {DotContentletAttributes} The dotCMS data attributes
|
|
177
|
-
*/
|
|
178
|
-
function getDotContentletAttributes(contentlet, container) {
|
|
179
|
-
return {
|
|
180
|
-
'data-dot-identifier': contentlet == null ? void 0 : contentlet.identifier,
|
|
181
|
-
'data-dot-basetype': contentlet == null ? void 0 : contentlet.baseType,
|
|
182
|
-
'data-dot-title': (contentlet == null ? void 0 : contentlet.widgetTitle) || (contentlet == null ? void 0 : contentlet.title),
|
|
183
|
-
'data-dot-inode': contentlet == null ? void 0 : contentlet.inode,
|
|
184
|
-
'data-dot-type': contentlet == null ? void 0 : contentlet.contentType,
|
|
185
|
-
'data-dot-container': container,
|
|
186
|
-
'data-dot-on-number-of-pages': contentlet == null ? void 0 : contentlet.onNumberOfPages
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* @internal
|
|
191
|
-
*
|
|
192
|
-
* Retrieves container data from a DotCMS page asset using the container reference.
|
|
193
|
-
* This function processes the container information and returns a standardized format
|
|
194
|
-
* for container editing.
|
|
195
|
-
*
|
|
196
|
-
* @param {DotCMSPageAsset} dotCMSPageAsset - The page asset containing all containers data
|
|
197
|
-
* @param {DotCMSColumnContainer} columContainer - The container reference from the layout
|
|
198
|
-
* @throws {Error} When page asset is invalid or container is not found
|
|
199
|
-
* @returns {EditableContainerData} Formatted container data for editing
|
|
200
|
-
*
|
|
201
|
-
* @example
|
|
202
|
-
* const containerData = getContainersData(pageAsset, containerRef);
|
|
203
|
-
* // Returns: { uuid: '123', identifier: 'cont1', acceptTypes: 'type1,type2', maxContentlets: 5 }
|
|
204
|
-
*/
|
|
205
|
-
const getContainersData = (dotCMSPageAsset, columContainer) => {
|
|
206
|
-
var _containerStructures$, _container$parentPerm, _container$maxContent;
|
|
207
|
-
const {
|
|
208
|
-
identifier,
|
|
209
|
-
uuid
|
|
210
|
-
} = columContainer;
|
|
211
|
-
const dotContainer = dotCMSPageAsset.containers[identifier];
|
|
212
|
-
if (!dotContainer) {
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
const {
|
|
216
|
-
containerStructures,
|
|
217
|
-
container
|
|
218
|
-
} = dotContainer;
|
|
219
|
-
const acceptTypes = (_containerStructures$ = containerStructures == null ? void 0 : containerStructures.map(structure => structure.contentTypeVar).join(',')) != null ? _containerStructures$ : '';
|
|
220
|
-
const variantId = container == null || (_container$parentPerm = container.parentPermissionable) == null ? void 0 : _container$parentPerm.variantId;
|
|
221
|
-
const maxContentlets = (_container$maxContent = container == null ? void 0 : container.maxContentlets) != null ? _container$maxContent : 0;
|
|
222
|
-
const path = container == null ? void 0 : container.path;
|
|
223
|
-
return {
|
|
224
|
-
uuid,
|
|
225
|
-
variantId,
|
|
226
|
-
acceptTypes,
|
|
227
|
-
maxContentlets,
|
|
228
|
-
identifier: path != null ? path : identifier
|
|
229
|
-
};
|
|
230
|
-
};
|
|
231
|
-
/**
|
|
232
|
-
* @internal
|
|
233
|
-
*
|
|
234
|
-
* Retrieves the contentlets (content items) associated with a specific container.
|
|
235
|
-
* Handles different UUID formats and provides warning for missing contentlets.
|
|
236
|
-
*
|
|
237
|
-
* @param {DotCMSPageAsset} dotCMSPageAsset - The page asset containing all containers data
|
|
238
|
-
* @param {DotCMSColumnContainer} columContainer - The container reference from the layout
|
|
239
|
-
* @returns {DotCMSContentlet[]} Array of contentlets in the container
|
|
240
|
-
*
|
|
241
|
-
* @example
|
|
242
|
-
* const contentlets = getContentletsInContainer(pageAsset, containerRef);
|
|
243
|
-
* // Returns: [{ identifier: 'cont1', ... }, { identifier: 'cont2', ... }]
|
|
244
|
-
*/
|
|
245
|
-
const getContentletsInContainer = (dotCMSPageAsset, columContainer) => {
|
|
246
|
-
const {
|
|
247
|
-
identifier,
|
|
248
|
-
uuid
|
|
249
|
-
} = columContainer;
|
|
250
|
-
const {
|
|
251
|
-
contentlets
|
|
252
|
-
} = dotCMSPageAsset.containers[identifier];
|
|
253
|
-
const contentletsInContainer = contentlets[`uuid-${uuid}`] || contentlets[`uuid-dotParser_${uuid}`] || [];
|
|
254
|
-
if (!contentletsInContainer) {
|
|
255
|
-
console.warn(`We couldn't find the contentlets for the container with the identifier ${identifier} and the uuid ${uuid} becareful by adding content to this container.\nWe recommend to change the container in the layout and add the content again.`);
|
|
256
|
-
}
|
|
257
|
-
return contentletsInContainer;
|
|
258
|
-
};
|
|
259
|
-
/**
|
|
260
|
-
* @internal
|
|
261
|
-
*
|
|
262
|
-
* Generates the required DotCMS data attributes for a container element.
|
|
263
|
-
* These attributes are used by DotCMS for container identification and functionality.
|
|
264
|
-
*
|
|
265
|
-
* @param {EditableContainerData} params - Container data including uuid, identifier, acceptTypes, and maxContentlets
|
|
266
|
-
* @returns {DotContainerAttributes} Object containing all necessary data attributes
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* const attributes = getDotContainerAttributes({
|
|
270
|
-
* uuid: '123',
|
|
271
|
-
* identifier: 'cont1',
|
|
272
|
-
* acceptTypes: 'type1,type2',
|
|
273
|
-
* maxContentlets: 5
|
|
274
|
-
* });
|
|
275
|
-
* // Returns: { 'data-dot-object': 'container', 'data-dot-identifier': 'cont1', ... }
|
|
276
|
-
*/
|
|
277
|
-
function getDotContainerAttributes({
|
|
278
|
-
uuid,
|
|
279
|
-
identifier,
|
|
280
|
-
acceptTypes,
|
|
281
|
-
maxContentlets
|
|
282
|
-
}) {
|
|
283
|
-
return {
|
|
284
|
-
'data-dot-object': 'container',
|
|
285
|
-
'data-dot-accept-types': acceptTypes,
|
|
286
|
-
'data-dot-identifier': identifier,
|
|
287
|
-
'data-max-contentlets': maxContentlets.toString(),
|
|
288
|
-
'data-dot-uuid': uuid
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const EMPTY_CONTAINER_STYLE = {
|
|
293
|
-
width: '100%',
|
|
294
|
-
backgroundColor: '#ECF0FD',
|
|
295
|
-
display: 'flex',
|
|
296
|
-
justifyContent: 'center',
|
|
297
|
-
alignItems: 'center',
|
|
298
|
-
color: '#030E32',
|
|
299
|
-
height: '10rem'
|
|
300
|
-
};
|
|
301
94
|
/**
|
|
302
95
|
* @internal
|
|
303
96
|
*
|
|
@@ -309,7 +102,7 @@ const EMPTY_CONTAINER_STYLE = {
|
|
|
309
102
|
* @param {string} props.identifier - Container identifier
|
|
310
103
|
* @returns {JSX.Element | null} Message about missing container or null in production
|
|
311
104
|
*/
|
|
312
|
-
const
|
|
105
|
+
const ContainerNotFound = ({
|
|
313
106
|
identifier
|
|
314
107
|
}) => {
|
|
315
108
|
const isDevMode = useIsDevMode();
|
|
@@ -324,7 +117,7 @@ const ContainerNoFound = ({
|
|
|
324
117
|
}
|
|
325
118
|
return jsxs("div", {
|
|
326
119
|
"data-testid": "container-not-found",
|
|
327
|
-
style:
|
|
120
|
+
style: EMPTY_CONTAINER_STYLE_REACT,
|
|
328
121
|
children: ["This container with identifier ", identifier, " was not found."]
|
|
329
122
|
});
|
|
330
123
|
};
|
|
@@ -342,9 +135,10 @@ const EmptyContainer = dotAttributes => {
|
|
|
342
135
|
return null;
|
|
343
136
|
}
|
|
344
137
|
return jsx("div", Object.assign({}, dotAttributes, {
|
|
345
|
-
style:
|
|
138
|
+
style: EMPTY_CONTAINER_STYLE_REACT,
|
|
346
139
|
children: jsx("span", {
|
|
347
140
|
"data-testid": "empty-container-message",
|
|
141
|
+
"data-dot-object": "empty-content",
|
|
348
142
|
children: "This container is empty."
|
|
349
143
|
})
|
|
350
144
|
}));
|
|
@@ -422,7 +216,7 @@ function FallbackComponent({
|
|
|
422
216
|
*
|
|
423
217
|
* Component to render when there is no component for the content type.
|
|
424
218
|
*
|
|
425
|
-
* @param {
|
|
219
|
+
* @param {DotCMSBasicContentlet} contentType - The content type that couldn't be rendered
|
|
426
220
|
* @return {*}
|
|
427
221
|
*/
|
|
428
222
|
function NoComponent({
|
|
@@ -493,7 +287,7 @@ function CustomComponent({
|
|
|
493
287
|
if (UserComponent) {
|
|
494
288
|
return jsx(UserComponent, Object.assign({}, contentlet));
|
|
495
289
|
}
|
|
496
|
-
const UserNoComponent = userComponents[
|
|
290
|
+
const UserNoComponent = userComponents[CUSTOM_NO_COMPONENT];
|
|
497
291
|
return jsx(FallbackComponent, {
|
|
498
292
|
UserNoComponent: UserNoComponent,
|
|
499
293
|
contentlet: contentlet
|
|
@@ -528,7 +322,7 @@ function Container({
|
|
|
528
322
|
const containerData = useMemo(() => getContainersData(pageAsset, container), [pageAsset, container]);
|
|
529
323
|
const contentlets = useMemo(() => getContentletsInContainer(pageAsset, container), [pageAsset, container]);
|
|
530
324
|
if (!containerData) {
|
|
531
|
-
return jsx(
|
|
325
|
+
return jsx(ContainerNotFound, {
|
|
532
326
|
identifier: container.identifier
|
|
533
327
|
});
|
|
534
328
|
}
|
|
@@ -598,7 +392,7 @@ function Column({
|
|
|
598
392
|
const Row = ({
|
|
599
393
|
row
|
|
600
394
|
}) => {
|
|
601
|
-
const customRowClass =
|
|
395
|
+
const customRowClass = combineClasses([row.styleClass || '', styles$1.row]);
|
|
602
396
|
return jsx("div", {
|
|
603
397
|
className: "dot-row-container",
|
|
604
398
|
children: jsx("div", {
|
|
@@ -634,11 +428,6 @@ const DotCMSLayoutBody = ({
|
|
|
634
428
|
}) => {
|
|
635
429
|
var _page$layout;
|
|
636
430
|
const dotCMSPageBody = page == null || (_page$layout = page.layout) == null ? void 0 : _page$layout.body;
|
|
637
|
-
if (!dotCMSPageBody) {
|
|
638
|
-
return jsx(ErrorMessage, {
|
|
639
|
-
mode: _mode
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
431
|
const contextValue = {
|
|
643
432
|
pageAsset: page,
|
|
644
433
|
userComponents: _components,
|
|
@@ -646,10 +435,1022 @@ const DotCMSLayoutBody = ({
|
|
|
646
435
|
};
|
|
647
436
|
return jsx(DotCMSPageContext.Provider, {
|
|
648
437
|
value: contextValue,
|
|
649
|
-
children: dotCMSPageBody.rows.map((row, index) => jsx(Row, {
|
|
438
|
+
children: dotCMSPageBody ? dotCMSPageBody.rows.map((row, index) => jsx(Row, {
|
|
650
439
|
row: row
|
|
651
|
-
}, index))
|
|
440
|
+
}, index)) : jsx(ErrorMessage, {})
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Custom hook to determine if the current UVE (Universal Visual Editor) mode
|
|
446
|
+
* matches the specified mode. This hook is useful for conditionally rendering
|
|
447
|
+
* components based on the UVE mode.
|
|
448
|
+
*
|
|
449
|
+
* @param {UVE_MODE} when - The UVE mode to check against.
|
|
450
|
+
* @returns {boolean} True if the current UVE mode matches the specified mode, otherwise false.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* // Basic usage: Check if the UVE is in edit mode
|
|
454
|
+
* const showInEditMode = useDotCMSShowWhen(UVE_MODE.EDIT);
|
|
455
|
+
* if (showInEditMode) {
|
|
456
|
+
* // Render edit-specific components
|
|
457
|
+
* }
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* // Check if the UVE is in preview mode
|
|
461
|
+
* const showInPreviewMode = useDotCMSShowWhen(UVE_MODE.PREVIEW);
|
|
462
|
+
* if (showInPreviewMode) {
|
|
463
|
+
* // Render preview-specific components
|
|
464
|
+
* }
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* // Check if the UVE is in live mode
|
|
468
|
+
* const showInLiveMode = useDotCMSShowWhen(UVE_MODE.LIVE);
|
|
469
|
+
* if (showInLiveMode) {
|
|
470
|
+
* // Render live-specific components
|
|
471
|
+
* }
|
|
472
|
+
*/
|
|
473
|
+
const useDotCMSShowWhen = when => {
|
|
474
|
+
const [show, setShow] = useState(false);
|
|
475
|
+
useEffect(() => {
|
|
476
|
+
var _getUVEState;
|
|
477
|
+
setShow(((_getUVEState = getUVEState()) == null ? void 0 : _getUVEState.mode) === when);
|
|
478
|
+
}, [when]);
|
|
479
|
+
return show;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* DotCMSShow component is used to conditionally render its children
|
|
484
|
+
* based on the Universal Visual Editor (UVE) mode. It checks if the UVE
|
|
485
|
+
* is in a specified mode and only renders its children in that case.
|
|
486
|
+
*
|
|
487
|
+
* @param {Object} props - The component props.
|
|
488
|
+
* @param {React.ReactNode} props.children - The children to be rendered when the condition is met.
|
|
489
|
+
* @param {UVE_MODE} [props.when=UVE_MODE.EDIT] - The UVE mode in which the children should be rendered.
|
|
490
|
+
* @returns {React.ReactNode | null} The children if the current UVE mode matches the `when` prop, otherwise null.
|
|
491
|
+
*
|
|
492
|
+
* @example
|
|
493
|
+
* // Basic usage: Render content only in edit mode
|
|
494
|
+
* <DotCMSShow when={UVE_MODE.EDIT}>
|
|
495
|
+
* <div>Edit Mode Content</div>
|
|
496
|
+
* </DotCMSShow>
|
|
497
|
+
*
|
|
498
|
+
* // This will render <div>Edit Mode Content</div> only if the UVE is in edit mode.
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* // Render content in preview mode
|
|
502
|
+
* <DotCMSShow when={UVE_MODE.PREVIEW}>
|
|
503
|
+
* <MyCustomPreviewComponent />
|
|
504
|
+
* </DotCMSShow>
|
|
505
|
+
*
|
|
506
|
+
* // MyCustomPreviewComponent will only be rendered if the UVE is in preview mode.
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* // Render content in live mode
|
|
510
|
+
* <DotCMSShow when={UVE_MODE.LIVE}>
|
|
511
|
+
* <LiveContentComponent />
|
|
512
|
+
* </DotCMSShow>
|
|
513
|
+
*
|
|
514
|
+
* // LiveContentComponent will only be rendered if the UVE is in live mode.
|
|
515
|
+
*/
|
|
516
|
+
const DotCMSShow = ({
|
|
517
|
+
children,
|
|
518
|
+
when: _when = UVE_MODE.EDIT
|
|
519
|
+
}) => {
|
|
520
|
+
const show = useDotCMSShowWhen(_when);
|
|
521
|
+
if (!show) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
return children;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Custom hook to manage the editable state of a DotCMS page.
|
|
529
|
+
*
|
|
530
|
+
* This hook initializes the Universal Visual Editor (UVE) and subscribes to content changes.
|
|
531
|
+
* It updates the editable page state when content changes are detected in the UVE,
|
|
532
|
+
* ensuring your React components always display the latest content when editing in DotCMS.
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* // Import the hook and the client
|
|
537
|
+
* import { useEditableDotCMSPage } from '@dotcms/react';
|
|
538
|
+
* import { createDotCMSClient } from '@dotcms/client';
|
|
539
|
+
*
|
|
540
|
+
* // Create the client
|
|
541
|
+
* const client = createDotCMSClient({
|
|
542
|
+
* dotcmsURL: 'https://your-dotcms-instance.com',
|
|
543
|
+
* authToken: 'your-auth-token'
|
|
544
|
+
* });
|
|
545
|
+
*
|
|
546
|
+
* // Get the page
|
|
547
|
+
* const page = await client.page.get('/', {
|
|
548
|
+
* languageId: '1',
|
|
549
|
+
* });
|
|
550
|
+
*
|
|
551
|
+
* // Use the hook to get an editable version of the page
|
|
552
|
+
* const editablePage = useEditableDotCMSPage(page);
|
|
553
|
+
*
|
|
554
|
+
* // Then use the page data in your component
|
|
555
|
+
* return (
|
|
556
|
+
* <div>
|
|
557
|
+
* <h1>{editablePage.page.title}</h1>
|
|
558
|
+
* <div dangerouslySetInnerHTML={{ __html: editablePage.page.body }} />
|
|
559
|
+
* </div>
|
|
560
|
+
* );
|
|
561
|
+
* ```
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* ```ts
|
|
565
|
+
* // Import the hook and the client
|
|
566
|
+
* import { useEditableDotCMSPage } from '@dotcms/react';
|
|
567
|
+
* import { createDotCMSClient } from '@dotcms/client';
|
|
568
|
+
*
|
|
569
|
+
* // Create the client
|
|
570
|
+
* const client = createDotCMSClient({
|
|
571
|
+
* dotcmsURL: 'https://your-dotcms-instance.com',
|
|
572
|
+
* authToken: 'your-auth-token'
|
|
573
|
+
* });
|
|
574
|
+
*
|
|
575
|
+
* // Get the page with GraphQL content
|
|
576
|
+
* const page = await client.page.get('/', {
|
|
577
|
+
* languageId: '1',
|
|
578
|
+
* graphql: {
|
|
579
|
+
* content: {
|
|
580
|
+
* products: `ProductCollection(query: "+title:snow", limit: 10, offset: 0, sortBy: "score") {
|
|
581
|
+
* title
|
|
582
|
+
* urlMap
|
|
583
|
+
* category {
|
|
584
|
+
* name
|
|
585
|
+
* inode
|
|
586
|
+
* }
|
|
587
|
+
* retailPrice
|
|
588
|
+
* image {
|
|
589
|
+
* versionPath
|
|
590
|
+
* }
|
|
591
|
+
* }`
|
|
592
|
+
* }
|
|
593
|
+
* }
|
|
594
|
+
* });
|
|
595
|
+
*
|
|
596
|
+
* // Use the hook to get an editable version of the page and its content
|
|
597
|
+
* const editablePage = useEditableDotCMSPage(page);
|
|
598
|
+
*
|
|
599
|
+
* // Access both page data and GraphQL content
|
|
600
|
+
* const { page: pageData, content } = editablePage;
|
|
601
|
+
*
|
|
602
|
+
* // Use the products from GraphQL content
|
|
603
|
+
* return (
|
|
604
|
+
* <div>
|
|
605
|
+
* <h1>{pageData.title}</h1>
|
|
606
|
+
* <ProductList products={content.products} />
|
|
607
|
+
* </div>
|
|
608
|
+
* );
|
|
609
|
+
* ```
|
|
610
|
+
* @param {DotCMSPageResponse} pageResponse - The initial editable page data from client.page.get().
|
|
611
|
+
*
|
|
612
|
+
* @returns {DotCMSPageResponse} The updated editable page state that reflects any changes made in the UVE.
|
|
613
|
+
* The structure includes page data and any GraphQL content that was requested.
|
|
614
|
+
*/
|
|
615
|
+
const useEditableDotCMSPage = pageResponse => {
|
|
616
|
+
const [updatedPageResponse, setUpdatedPageResponse] = useState(pageResponse);
|
|
617
|
+
useEffect(() => {
|
|
618
|
+
var _pageResponse$pageAss;
|
|
619
|
+
if (!getUVEState()) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (!pageResponse) {
|
|
623
|
+
console.warn('[useEditableDotCMSPage]: No DotCMSPageResponse provided');
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const pageURI = pageResponse == null || (_pageResponse$pageAss = pageResponse.pageAsset) == null || (_pageResponse$pageAss = _pageResponse$pageAss.page) == null ? void 0 : _pageResponse$pageAss.pageURI;
|
|
627
|
+
const {
|
|
628
|
+
destroyUVESubscriptions
|
|
629
|
+
} = initUVE(pageResponse);
|
|
630
|
+
// Update the navigation to the pageURI, when we have a pageURI
|
|
631
|
+
// Sometimes the page is null due to permissions, so we don't want to update the navigation
|
|
632
|
+
// And wait for the UVE to resolve the page
|
|
633
|
+
if (pageURI) {
|
|
634
|
+
updateNavigation(pageURI);
|
|
635
|
+
}
|
|
636
|
+
return () => {
|
|
637
|
+
destroyUVESubscriptions();
|
|
638
|
+
};
|
|
639
|
+
}, [pageResponse]);
|
|
640
|
+
useEffect(() => {
|
|
641
|
+
const {
|
|
642
|
+
unsubscribe
|
|
643
|
+
} = createUVESubscription(UVEEventType.CONTENT_CHANGES, payload => {
|
|
644
|
+
setUpdatedPageResponse(payload);
|
|
645
|
+
});
|
|
646
|
+
return () => {
|
|
647
|
+
unsubscribe();
|
|
648
|
+
};
|
|
649
|
+
}, []);
|
|
650
|
+
return updatedPageResponse;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
const DEFAULT_TINYMCE_CONFIG = Object.assign({}, __DEFAULT_TINYMCE_CONFIG__, {
|
|
654
|
+
licenseKey: 'gpl' // Using self-hosted license key
|
|
655
|
+
});
|
|
656
|
+
const TINYMCE_CONFIG = {
|
|
657
|
+
full: Object.assign({}, DEFAULT_TINYMCE_CONFIG, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.full),
|
|
658
|
+
plain: Object.assign({}, DEFAULT_TINYMCE_CONFIG, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.plain),
|
|
659
|
+
minimal: Object.assign({}, DEFAULT_TINYMCE_CONFIG, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.minimal)
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Allows inline edit content pulled from dotCMS API using TinyMCE editor
|
|
664
|
+
*
|
|
665
|
+
* @export
|
|
666
|
+
* @component
|
|
667
|
+
* @param {Readonly<DotCMSEditableTextProps>} props {
|
|
668
|
+
* mode = 'plain',
|
|
669
|
+
* format = 'text',
|
|
670
|
+
* contentlet,
|
|
671
|
+
* fieldName = ''
|
|
672
|
+
* }
|
|
673
|
+
* @example
|
|
674
|
+
* ```javascript
|
|
675
|
+
* import { DotCMSEditableText } from '@dotcms/react';
|
|
676
|
+
*
|
|
677
|
+
* const MyContentletWithTitle = ({ contentlet }) => (
|
|
678
|
+
* <h2>
|
|
679
|
+
* <DotCMSEditableText
|
|
680
|
+
* contentlet={contentlet}
|
|
681
|
+
* fieldName="title"
|
|
682
|
+
* mode='full'
|
|
683
|
+
* format='text'/>
|
|
684
|
+
* </h2>
|
|
685
|
+
* );
|
|
686
|
+
* ```
|
|
687
|
+
* @returns {JSX.Element} A component to edit content inline
|
|
688
|
+
*/
|
|
689
|
+
function DotCMSEditableText({
|
|
690
|
+
mode = 'plain',
|
|
691
|
+
format = 'text',
|
|
692
|
+
contentlet,
|
|
693
|
+
fieldName
|
|
694
|
+
}) {
|
|
695
|
+
const editorRef = useRef(null);
|
|
696
|
+
const [scriptSrc, setScriptSrc] = useState('');
|
|
697
|
+
const [initEditor, setInitEditor] = useState(false);
|
|
698
|
+
const [content, setContent] = useState((contentlet == null ? void 0 : contentlet[fieldName]) || '');
|
|
699
|
+
useEffect(() => {
|
|
700
|
+
setContent((contentlet == null ? void 0 : contentlet[fieldName]) || '');
|
|
701
|
+
}, [fieldName, contentlet]);
|
|
702
|
+
useEffect(() => {
|
|
703
|
+
var _state$dotCMSHost, _editorRef$current;
|
|
704
|
+
const state = getUVEState();
|
|
705
|
+
setInitEditor((state == null ? void 0 : state.mode) === UVE_MODE.EDIT && !!(state != null && (_state$dotCMSHost = state.dotCMSHost) != null && _state$dotCMSHost.length));
|
|
706
|
+
if (!contentlet || !fieldName) {
|
|
707
|
+
console.error('[DotCMSEditableText]: contentlet or fieldName is missing', 'Ensure that all needed props are passed to view and edit the content');
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (state && state.mode !== UVE_MODE.EDIT) {
|
|
711
|
+
console.warn('[DotCMSEditableText]: TinyMCE is not available in the current mode');
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (!(state != null && state.dotCMSHost)) {
|
|
715
|
+
console.warn('[DotCMSEditableText]: The `dotCMSHost` parameter is not defined. Check that the UVE is sending the correct parameters.');
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const createURL = new URL(__TINYMCE_PATH_ON_DOTCMS__, state.dotCMSHost);
|
|
719
|
+
setScriptSrc(createURL.toString());
|
|
720
|
+
const content = (contentlet == null ? void 0 : contentlet[fieldName]) || '';
|
|
721
|
+
(_editorRef$current = editorRef.current) == null || _editorRef$current.setContent(content, {
|
|
722
|
+
format
|
|
723
|
+
});
|
|
724
|
+
}, [format, fieldName, contentlet, content]);
|
|
725
|
+
useEffect(() => {
|
|
726
|
+
var _getUVEState;
|
|
727
|
+
if (((_getUVEState = getUVEState()) == null ? void 0 : _getUVEState.mode) !== UVE_MODE.EDIT) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const onMessage = ({
|
|
731
|
+
data
|
|
732
|
+
}) => {
|
|
733
|
+
const {
|
|
734
|
+
name,
|
|
735
|
+
payload
|
|
736
|
+
} = data;
|
|
737
|
+
if (name !== __DOTCMS_UVE_EVENT__.UVE_COPY_CONTENTLET_INLINE_EDITING_SUCCESS) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const {
|
|
741
|
+
oldInode,
|
|
742
|
+
inode
|
|
743
|
+
} = payload;
|
|
744
|
+
const currentInode = contentlet.inode;
|
|
745
|
+
const shouldFocus = currentInode === oldInode || currentInode === inode;
|
|
746
|
+
if (shouldFocus) {
|
|
747
|
+
var _editorRef$current2;
|
|
748
|
+
(_editorRef$current2 = editorRef.current) == null || _editorRef$current2.focus();
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
window.addEventListener('message', onMessage);
|
|
752
|
+
return () => {
|
|
753
|
+
window.removeEventListener('message', onMessage);
|
|
754
|
+
};
|
|
755
|
+
}, [contentlet == null ? void 0 : contentlet.inode]);
|
|
756
|
+
const onMouseDown = event => {
|
|
757
|
+
var _editorRef$current3;
|
|
758
|
+
const {
|
|
759
|
+
onNumberOfPages = 1
|
|
760
|
+
} = contentlet;
|
|
761
|
+
const {
|
|
762
|
+
inode,
|
|
763
|
+
languageId: language
|
|
764
|
+
} = contentlet;
|
|
765
|
+
if (Number(onNumberOfPages) <= 1 || (_editorRef$current3 = editorRef.current) != null && _editorRef$current3.hasFocus()) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
event.stopPropagation();
|
|
769
|
+
event.preventDefault();
|
|
770
|
+
sendMessageToUVE({
|
|
771
|
+
action: DotCMSUVEAction.COPY_CONTENTLET_INLINE_EDITING,
|
|
772
|
+
payload: {
|
|
773
|
+
dataset: {
|
|
774
|
+
inode,
|
|
775
|
+
language,
|
|
776
|
+
fieldName
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
};
|
|
781
|
+
const onFocusOut = () => {
|
|
782
|
+
var _editorRef$current4, _editorRef$current5;
|
|
783
|
+
const editedContent = ((_editorRef$current4 = editorRef.current) == null ? void 0 : _editorRef$current4.getContent({
|
|
784
|
+
format: format
|
|
785
|
+
})) || '';
|
|
786
|
+
const {
|
|
787
|
+
inode,
|
|
788
|
+
languageId: langId
|
|
789
|
+
} = contentlet;
|
|
790
|
+
if (!((_editorRef$current5 = editorRef.current) != null && _editorRef$current5.isDirty()) || !didContentChange(editedContent)) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
sendMessageToUVE({
|
|
794
|
+
action: DotCMSUVEAction.UPDATE_CONTENTLET_INLINE_EDITING,
|
|
795
|
+
payload: {
|
|
796
|
+
content: editedContent,
|
|
797
|
+
dataset: {
|
|
798
|
+
inode,
|
|
799
|
+
langId,
|
|
800
|
+
fieldName
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
};
|
|
805
|
+
const didContentChange = editedContent => {
|
|
806
|
+
return content !== editedContent;
|
|
807
|
+
};
|
|
808
|
+
if (!initEditor) {
|
|
809
|
+
// We can let the user pass the Child Component and create a root to get the HTML for the editor
|
|
810
|
+
return jsx("span", {
|
|
811
|
+
dangerouslySetInnerHTML: {
|
|
812
|
+
__html: content
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
return jsx("div", {
|
|
817
|
+
style: {
|
|
818
|
+
outline: '2px solid #006ce7',
|
|
819
|
+
borderRadius: '4px'
|
|
820
|
+
},
|
|
821
|
+
children: jsx(Editor, {
|
|
822
|
+
tinymceScriptSrc: scriptSrc,
|
|
823
|
+
inline: true,
|
|
824
|
+
onInit: (_, editor) => editorRef.current = editor,
|
|
825
|
+
init: TINYMCE_CONFIG[mode],
|
|
826
|
+
initialValue: content,
|
|
827
|
+
onMouseDown: onMouseDown,
|
|
828
|
+
onFocusOut: onFocusOut
|
|
829
|
+
})
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Renders a code block component.
|
|
835
|
+
*
|
|
836
|
+
* @param attrs - The attributes of the code block.
|
|
837
|
+
* @param children - The content of the code block.
|
|
838
|
+
* @returns The rendered code block component.
|
|
839
|
+
*/
|
|
840
|
+
const CodeBlock = ({
|
|
841
|
+
node,
|
|
842
|
+
children
|
|
843
|
+
}) => {
|
|
844
|
+
var _node$attrs;
|
|
845
|
+
const language = (node == null || (_node$attrs = node.attrs) == null ? void 0 : _node$attrs.language) || '';
|
|
846
|
+
return jsx("pre", {
|
|
847
|
+
"data-language": language,
|
|
848
|
+
children: jsx("code", {
|
|
849
|
+
children: children
|
|
850
|
+
})
|
|
851
|
+
});
|
|
852
|
+
};
|
|
853
|
+
/**
|
|
854
|
+
* Renders a blockquote component.
|
|
855
|
+
*
|
|
856
|
+
* @param children - The content to be rendered inside the blockquote.
|
|
857
|
+
* @returns The rendered blockquote component.
|
|
858
|
+
*/
|
|
859
|
+
const BlockQuote = ({
|
|
860
|
+
children
|
|
861
|
+
}) => {
|
|
862
|
+
return jsx("blockquote", {
|
|
863
|
+
children: children
|
|
864
|
+
});
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const NoComponentProvided = ({
|
|
868
|
+
contentType
|
|
869
|
+
}) => {
|
|
870
|
+
const style = {
|
|
871
|
+
backgroundColor: '#fffaf0',
|
|
872
|
+
color: '#333',
|
|
873
|
+
padding: '1rem',
|
|
874
|
+
borderRadius: '0.5rem',
|
|
875
|
+
marginBottom: '1rem',
|
|
876
|
+
marginTop: '1rem',
|
|
877
|
+
border: '1px solid #ed8936'
|
|
878
|
+
};
|
|
879
|
+
return jsxs("div", {
|
|
880
|
+
"data-testid": "no-component-provided",
|
|
881
|
+
style: style,
|
|
882
|
+
children: [jsx("strong", {
|
|
883
|
+
style: {
|
|
884
|
+
color: '#c05621'
|
|
885
|
+
},
|
|
886
|
+
children: "Dev Warning"
|
|
887
|
+
}), ": No component or custom renderer provided for content type", jsx("strong", {
|
|
888
|
+
style: {
|
|
889
|
+
color: '#c05621'
|
|
890
|
+
},
|
|
891
|
+
children: contentType || 'Unknown'
|
|
892
|
+
}), ".", jsx("br", {}), "Please refer to the", jsx("a", {
|
|
893
|
+
href: "https://dev.dotcms.com/docs/block-editor",
|
|
894
|
+
target: "_blank",
|
|
895
|
+
rel: "noopener noreferrer",
|
|
896
|
+
style: {
|
|
897
|
+
color: '#c05621'
|
|
898
|
+
},
|
|
899
|
+
children: "Block Editor Custom Renderers Documentation"
|
|
900
|
+
}), ' ', "for guidance."]
|
|
901
|
+
});
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
const DOT_CONTENT_NO_DATA_MESSAGE = '[DotCMSBlockEditorRenderer]: No data provided for Contentlet Block. Try to add a contentlet to the block editor. If the error persists, please contact the DotCMS support team.';
|
|
905
|
+
const DOT_CONTENT_NO_MATCHING_COMPONENT_MESSAGE = contentType => `[DotCMSBlockEditorRenderer]: No matching component found for content type: ${contentType}. Provide a custom renderer for this content type to fix this error.`;
|
|
906
|
+
/**
|
|
907
|
+
* Renders a DotContent component.
|
|
908
|
+
*
|
|
909
|
+
* @param {DotContentProps} props - The props for the DotContent component.
|
|
910
|
+
* @returns {JSX.Element} The rendered DotContent component.
|
|
911
|
+
*/
|
|
912
|
+
const DotContent = ({
|
|
913
|
+
customRenderers,
|
|
914
|
+
node
|
|
915
|
+
}) => {
|
|
916
|
+
const isDevMode = useIsDevMode();
|
|
917
|
+
const {
|
|
918
|
+
attrs = {}
|
|
919
|
+
} = node;
|
|
920
|
+
const {
|
|
921
|
+
data
|
|
922
|
+
} = attrs;
|
|
923
|
+
if (!data) {
|
|
924
|
+
console.error(DOT_CONTENT_NO_DATA_MESSAGE);
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
const {
|
|
928
|
+
contentType = 'Unknown Content Type'
|
|
929
|
+
} = data;
|
|
930
|
+
const Component = customRenderers[contentType];
|
|
931
|
+
/* In dev mode, show a helpful message for unknown content types */
|
|
932
|
+
if (isDevMode && !Component) {
|
|
933
|
+
return jsx(NoComponentProvided, {
|
|
934
|
+
contentType: contentType
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
/* In production, use default component if no matching component found */
|
|
938
|
+
if (!Component) {
|
|
939
|
+
console.warn(DOT_CONTENT_NO_MATCHING_COMPONENT_MESSAGE(contentType));
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
return jsx(Component, Object.assign({}, node));
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Renders an image component for dotCMS.
|
|
947
|
+
*
|
|
948
|
+
* @param node - The node for the DotCMSImage component.
|
|
949
|
+
* @returns The rendered image component.
|
|
950
|
+
*/
|
|
951
|
+
const DotCMSImage = ({
|
|
952
|
+
node
|
|
953
|
+
}) => {
|
|
954
|
+
const {
|
|
955
|
+
src,
|
|
956
|
+
alt
|
|
957
|
+
} = node.attrs;
|
|
958
|
+
return jsx("img", {
|
|
959
|
+
alt: alt,
|
|
960
|
+
src: src
|
|
961
|
+
});
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* ListItem component represents a list item in a block editor.
|
|
966
|
+
*
|
|
967
|
+
* @param children - The content of the list item.
|
|
968
|
+
* @returns The rendered list item element.
|
|
969
|
+
*/
|
|
970
|
+
const ListItem = ({
|
|
971
|
+
children
|
|
972
|
+
}) => {
|
|
973
|
+
return jsx("li", {
|
|
974
|
+
children: children
|
|
975
|
+
});
|
|
976
|
+
};
|
|
977
|
+
/**
|
|
978
|
+
* Renders an ordered list component.
|
|
979
|
+
*
|
|
980
|
+
* @param children - The content to be rendered inside the ordered list.
|
|
981
|
+
* @returns The ordered list component.
|
|
982
|
+
*/
|
|
983
|
+
const OrderedList = ({
|
|
984
|
+
children
|
|
985
|
+
}) => {
|
|
986
|
+
return jsx("ol", {
|
|
987
|
+
children: children
|
|
988
|
+
});
|
|
989
|
+
};
|
|
990
|
+
/**
|
|
991
|
+
* Renders a bullet list component.
|
|
992
|
+
*
|
|
993
|
+
* @param children - The content of the bullet list.
|
|
994
|
+
* @returns The rendered bullet list component.
|
|
995
|
+
*/
|
|
996
|
+
const BulletList = ({
|
|
997
|
+
children
|
|
998
|
+
}) => {
|
|
999
|
+
return jsx("ul", {
|
|
1000
|
+
children: children
|
|
1001
|
+
});
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Renders a table component for the Block Editor.
|
|
1006
|
+
*
|
|
1007
|
+
* @param content - The content of the table.
|
|
1008
|
+
* @param blockEditorItem - The Block Editor item component.
|
|
1009
|
+
*/
|
|
1010
|
+
const TableRenderer = ({
|
|
1011
|
+
content,
|
|
1012
|
+
blockEditorItem
|
|
1013
|
+
}) => {
|
|
1014
|
+
const BlockEditorItemComponent = blockEditorItem;
|
|
1015
|
+
const renderTableContent = node => {
|
|
1016
|
+
var _node$content;
|
|
1017
|
+
return jsx(BlockEditorItemComponent, {
|
|
1018
|
+
content: (_node$content = node.content) != null ? _node$content : []
|
|
1019
|
+
});
|
|
1020
|
+
};
|
|
1021
|
+
return jsxs("table", {
|
|
1022
|
+
children: [jsx("thead", {
|
|
1023
|
+
children: content.slice(0, 1).map((rowNode, rowIndex) => {
|
|
1024
|
+
var _rowNode$content;
|
|
1025
|
+
return jsx("tr", {
|
|
1026
|
+
children: (_rowNode$content = rowNode.content) == null ? void 0 : _rowNode$content.map((cellNode, cellIndex) => {
|
|
1027
|
+
var _cellNode$attrs, _cellNode$attrs2;
|
|
1028
|
+
return jsx("th", {
|
|
1029
|
+
colSpan: Number(((_cellNode$attrs = cellNode.attrs) == null ? void 0 : _cellNode$attrs.colspan) || 1),
|
|
1030
|
+
rowSpan: Number(((_cellNode$attrs2 = cellNode.attrs) == null ? void 0 : _cellNode$attrs2.rowspan) || 1),
|
|
1031
|
+
children: renderTableContent(cellNode)
|
|
1032
|
+
}, `${cellNode.type}-${cellIndex}`);
|
|
1033
|
+
})
|
|
1034
|
+
}, `${rowNode.type}-${rowIndex}`);
|
|
1035
|
+
})
|
|
1036
|
+
}), jsx("tbody", {
|
|
1037
|
+
children: content.slice(1).map((rowNode, rowIndex) => {
|
|
1038
|
+
var _rowNode$content2;
|
|
1039
|
+
return jsx("tr", {
|
|
1040
|
+
children: (_rowNode$content2 = rowNode.content) == null ? void 0 : _rowNode$content2.map((cellNode, cellIndex) => {
|
|
1041
|
+
var _cellNode$attrs3, _cellNode$attrs4;
|
|
1042
|
+
return jsx("td", {
|
|
1043
|
+
colSpan: Number(((_cellNode$attrs3 = cellNode.attrs) == null ? void 0 : _cellNode$attrs3.colspan) || 1),
|
|
1044
|
+
rowSpan: Number(((_cellNode$attrs4 = cellNode.attrs) == null ? void 0 : _cellNode$attrs4.rowspan) || 1),
|
|
1045
|
+
children: renderTableContent(cellNode)
|
|
1046
|
+
}, `${cellNode.type}-${cellIndex}`);
|
|
1047
|
+
})
|
|
1048
|
+
}, `${rowNode.type}-${rowIndex}`);
|
|
1049
|
+
})
|
|
1050
|
+
})]
|
|
1051
|
+
});
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Renders the text in bold.
|
|
1056
|
+
*
|
|
1057
|
+
* @param children - The content to be rendered in bold.
|
|
1058
|
+
*/
|
|
1059
|
+
const Bold = ({
|
|
1060
|
+
children
|
|
1061
|
+
}) => jsx("strong", {
|
|
1062
|
+
children: children
|
|
1063
|
+
});
|
|
1064
|
+
/**
|
|
1065
|
+
* Renders the text in italic format.
|
|
1066
|
+
*
|
|
1067
|
+
* @param children - The content to be rendered in italic.
|
|
1068
|
+
*/
|
|
1069
|
+
const Italic = ({
|
|
1070
|
+
children
|
|
1071
|
+
}) => jsx("em", {
|
|
1072
|
+
children: children
|
|
1073
|
+
});
|
|
1074
|
+
/**
|
|
1075
|
+
* Renders a strike-through text.
|
|
1076
|
+
*
|
|
1077
|
+
* @param children - The content to be rendered within the strike-through element.
|
|
1078
|
+
*/
|
|
1079
|
+
const Strike = ({
|
|
1080
|
+
children
|
|
1081
|
+
}) => jsx("s", {
|
|
1082
|
+
children: children
|
|
1083
|
+
});
|
|
1084
|
+
/**
|
|
1085
|
+
* Renders an underline element for the given children.
|
|
1086
|
+
*
|
|
1087
|
+
* @param children - The content to be underlined.
|
|
1088
|
+
*/
|
|
1089
|
+
const Underline = ({
|
|
1090
|
+
children
|
|
1091
|
+
}) => jsx("u", {
|
|
1092
|
+
children: children
|
|
1093
|
+
});
|
|
1094
|
+
/**
|
|
1095
|
+
* Renders a paragraph element.
|
|
1096
|
+
*
|
|
1097
|
+
* @param children - The content of the paragraph.
|
|
1098
|
+
* @param attrs - The style attributes for the paragraph.
|
|
1099
|
+
* @returns The rendered paragraph element.
|
|
1100
|
+
*/
|
|
1101
|
+
const Paragraph = ({
|
|
1102
|
+
children,
|
|
1103
|
+
node
|
|
1104
|
+
}) => {
|
|
1105
|
+
const attrs = (node == null ? void 0 : node.attrs) || {};
|
|
1106
|
+
return jsx("p", {
|
|
1107
|
+
style: attrs,
|
|
1108
|
+
children: children
|
|
1109
|
+
});
|
|
1110
|
+
};
|
|
1111
|
+
/**
|
|
1112
|
+
* Renders a link component.
|
|
1113
|
+
*
|
|
1114
|
+
* @param children - The content of the link.
|
|
1115
|
+
* @param attrs - The attributes to be applied to the link.
|
|
1116
|
+
* @returns The rendered link component.
|
|
1117
|
+
*/
|
|
1118
|
+
const Link = ({
|
|
1119
|
+
children,
|
|
1120
|
+
attrs
|
|
1121
|
+
}) => {
|
|
1122
|
+
return jsx("a", Object.assign({}, attrs, {
|
|
1123
|
+
children: children
|
|
1124
|
+
}));
|
|
1125
|
+
};
|
|
1126
|
+
/**
|
|
1127
|
+
* Renders a heading element with the specified level.
|
|
1128
|
+
*
|
|
1129
|
+
* @param children - The content of the heading.
|
|
1130
|
+
* @param attrs - The attributes for the heading.
|
|
1131
|
+
* @returns The rendered heading element.
|
|
1132
|
+
*/
|
|
1133
|
+
const Heading = ({
|
|
1134
|
+
children,
|
|
1135
|
+
node
|
|
1136
|
+
}) => {
|
|
1137
|
+
const attrs = (node == null ? void 0 : node.attrs) || {};
|
|
1138
|
+
const level = attrs.level || 1;
|
|
1139
|
+
const Tag = `h${level}`;
|
|
1140
|
+
return jsx(Tag, {
|
|
1141
|
+
children: children
|
|
1142
|
+
});
|
|
1143
|
+
};
|
|
1144
|
+
/**
|
|
1145
|
+
* Renders the superscript text.
|
|
1146
|
+
*
|
|
1147
|
+
* @param children - The content to be rendered as superscript.
|
|
1148
|
+
*/
|
|
1149
|
+
const Superscript = ({
|
|
1150
|
+
children
|
|
1151
|
+
}) => jsx("sup", {
|
|
1152
|
+
children: children
|
|
1153
|
+
});
|
|
1154
|
+
/**
|
|
1155
|
+
* Renders a subscript element.
|
|
1156
|
+
*
|
|
1157
|
+
* @param children - The content to be rendered as subscript.
|
|
1158
|
+
*/
|
|
1159
|
+
const Subscript = ({
|
|
1160
|
+
children
|
|
1161
|
+
}) => jsx("sub", {
|
|
1162
|
+
children: children
|
|
1163
|
+
});
|
|
1164
|
+
const nodeMarks = {
|
|
1165
|
+
bold: Bold,
|
|
1166
|
+
link: Link,
|
|
1167
|
+
italic: Italic,
|
|
1168
|
+
strike: Strike,
|
|
1169
|
+
subscript: Subscript,
|
|
1170
|
+
underline: Underline,
|
|
1171
|
+
superscript: Superscript
|
|
1172
|
+
};
|
|
1173
|
+
const defaultMark = {
|
|
1174
|
+
type: '',
|
|
1175
|
+
attrs: {}
|
|
1176
|
+
};
|
|
1177
|
+
/**
|
|
1178
|
+
* Renders a text block with optional marks.
|
|
1179
|
+
*
|
|
1180
|
+
* @param props - The props for the TextBlock component.
|
|
1181
|
+
* @returns The rendered text block.
|
|
1182
|
+
*/
|
|
1183
|
+
const TextBlock = (props = {}) => {
|
|
1184
|
+
const {
|
|
1185
|
+
marks = [],
|
|
1186
|
+
text
|
|
1187
|
+
} = props;
|
|
1188
|
+
const mark = marks[0] || defaultMark;
|
|
1189
|
+
const textProps = Object.assign({}, props, {
|
|
1190
|
+
marks: marks.slice(1)
|
|
1191
|
+
});
|
|
1192
|
+
const Component = nodeMarks[mark == null ? void 0 : mark.type];
|
|
1193
|
+
// In React, class is not a valid attribute name, so we need to rename it to className
|
|
1194
|
+
if (mark.attrs) {
|
|
1195
|
+
mark.attrs.className = mark.attrs.class;
|
|
1196
|
+
delete mark.attrs.class;
|
|
1197
|
+
}
|
|
1198
|
+
if (!Component) {
|
|
1199
|
+
return text;
|
|
1200
|
+
}
|
|
1201
|
+
return jsx(Component, {
|
|
1202
|
+
type: mark.type,
|
|
1203
|
+
attrs: mark.attrs,
|
|
1204
|
+
children: jsx(TextBlock, Object.assign({}, textProps))
|
|
1205
|
+
});
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* Renders a video component for displaying videos.
|
|
1210
|
+
*
|
|
1211
|
+
* @param props - The properties for the video component.
|
|
1212
|
+
* @returns The rendered video component.
|
|
1213
|
+
*/
|
|
1214
|
+
const DotCMSVideo = ({
|
|
1215
|
+
node
|
|
1216
|
+
}) => {
|
|
1217
|
+
const {
|
|
1218
|
+
data,
|
|
1219
|
+
src,
|
|
1220
|
+
mimeType,
|
|
1221
|
+
width,
|
|
1222
|
+
height
|
|
1223
|
+
} = node.attrs;
|
|
1224
|
+
const poster = data == null ? void 0 : data.thumbnail;
|
|
1225
|
+
const posterAttribute = poster ? {
|
|
1226
|
+
poster
|
|
1227
|
+
} : {};
|
|
1228
|
+
return jsxs("video", Object.assign({
|
|
1229
|
+
controls: true,
|
|
1230
|
+
preload: "metadata",
|
|
1231
|
+
width: width,
|
|
1232
|
+
height: height
|
|
1233
|
+
}, posterAttribute, {
|
|
1234
|
+
children: [jsx("track", {
|
|
1235
|
+
default: true,
|
|
1236
|
+
kind: "captions",
|
|
1237
|
+
srcLang: "en"
|
|
1238
|
+
}), jsx("source", {
|
|
1239
|
+
src: src,
|
|
1240
|
+
type: mimeType
|
|
1241
|
+
}), "Your browser does not support the ", jsx("code", {
|
|
1242
|
+
children: "video"
|
|
1243
|
+
}), " element."]
|
|
1244
|
+
}));
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* Renders a block editor item based on the provided content and custom renderers.
|
|
1249
|
+
*
|
|
1250
|
+
* @param content - The content nodes to render.
|
|
1251
|
+
* @param customRenderers - Optional custom renderers for specific node types.
|
|
1252
|
+
* @returns The rendered block editor item.
|
|
1253
|
+
*/
|
|
1254
|
+
const BlockEditorBlock = ({
|
|
1255
|
+
content,
|
|
1256
|
+
customRenderers
|
|
1257
|
+
}) => {
|
|
1258
|
+
if (!content) {
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
return content == null ? void 0 : content.map((node, index) => {
|
|
1262
|
+
var _node$content;
|
|
1263
|
+
const CustomRendererComponent = customRenderers == null ? void 0 : customRenderers[node.type];
|
|
1264
|
+
const key = `${node.type}-${index}`;
|
|
1265
|
+
if (CustomRendererComponent) {
|
|
1266
|
+
return jsx(CustomRendererComponent, {
|
|
1267
|
+
content: node.content,
|
|
1268
|
+
children: jsx(BlockEditorBlock, {
|
|
1269
|
+
content: node.content,
|
|
1270
|
+
customRenderers: customRenderers
|
|
1271
|
+
})
|
|
1272
|
+
}, key);
|
|
1273
|
+
}
|
|
1274
|
+
switch (node.type) {
|
|
1275
|
+
case BlockEditorDefaultBlocks.PARAGRAPH:
|
|
1276
|
+
return jsx(Paragraph, {
|
|
1277
|
+
node: node,
|
|
1278
|
+
children: jsx(BlockEditorBlock, {
|
|
1279
|
+
content: node.content,
|
|
1280
|
+
customRenderers: customRenderers
|
|
1281
|
+
})
|
|
1282
|
+
}, key);
|
|
1283
|
+
case BlockEditorDefaultBlocks.HEADING:
|
|
1284
|
+
return jsx(Heading, {
|
|
1285
|
+
node: node,
|
|
1286
|
+
children: jsx(BlockEditorBlock, {
|
|
1287
|
+
content: node.content,
|
|
1288
|
+
customRenderers: customRenderers
|
|
1289
|
+
})
|
|
1290
|
+
}, key);
|
|
1291
|
+
case BlockEditorDefaultBlocks.TEXT:
|
|
1292
|
+
return jsx(TextBlock, Object.assign({}, node), key);
|
|
1293
|
+
case BlockEditorDefaultBlocks.BULLET_LIST:
|
|
1294
|
+
return jsx(BulletList, {
|
|
1295
|
+
children: jsx(BlockEditorBlock, {
|
|
1296
|
+
content: node.content,
|
|
1297
|
+
customRenderers: customRenderers
|
|
1298
|
+
})
|
|
1299
|
+
}, key);
|
|
1300
|
+
case BlockEditorDefaultBlocks.ORDERED_LIST:
|
|
1301
|
+
return jsx(OrderedList, {
|
|
1302
|
+
children: jsx(BlockEditorBlock, {
|
|
1303
|
+
content: node.content,
|
|
1304
|
+
customRenderers: customRenderers
|
|
1305
|
+
})
|
|
1306
|
+
}, key);
|
|
1307
|
+
case BlockEditorDefaultBlocks.LIST_ITEM:
|
|
1308
|
+
return jsx(ListItem, {
|
|
1309
|
+
children: jsx(BlockEditorBlock, {
|
|
1310
|
+
content: node.content,
|
|
1311
|
+
customRenderers: customRenderers
|
|
1312
|
+
})
|
|
1313
|
+
}, key);
|
|
1314
|
+
case BlockEditorDefaultBlocks.BLOCK_QUOTE:
|
|
1315
|
+
return jsx(BlockQuote, {
|
|
1316
|
+
children: jsx(BlockEditorBlock, {
|
|
1317
|
+
content: node.content,
|
|
1318
|
+
customRenderers: customRenderers
|
|
1319
|
+
})
|
|
1320
|
+
}, key);
|
|
1321
|
+
case BlockEditorDefaultBlocks.CODE_BLOCK:
|
|
1322
|
+
return jsx(CodeBlock, {
|
|
1323
|
+
node: node,
|
|
1324
|
+
children: jsx(BlockEditorBlock, {
|
|
1325
|
+
content: node.content,
|
|
1326
|
+
customRenderers: customRenderers
|
|
1327
|
+
})
|
|
1328
|
+
}, key);
|
|
1329
|
+
case BlockEditorDefaultBlocks.HARDBREAK:
|
|
1330
|
+
return jsx("br", {}, key);
|
|
1331
|
+
case BlockEditorDefaultBlocks.HORIZONTAL_RULE:
|
|
1332
|
+
return jsx("hr", {}, key);
|
|
1333
|
+
case BlockEditorDefaultBlocks.DOT_IMAGE:
|
|
1334
|
+
return jsx(DotCMSImage, {
|
|
1335
|
+
node: node
|
|
1336
|
+
}, key);
|
|
1337
|
+
case BlockEditorDefaultBlocks.DOT_VIDEO:
|
|
1338
|
+
return jsx(DotCMSVideo, {
|
|
1339
|
+
node: node
|
|
1340
|
+
}, key);
|
|
1341
|
+
case BlockEditorDefaultBlocks.TABLE:
|
|
1342
|
+
return jsx(TableRenderer, {
|
|
1343
|
+
content: (_node$content = node.content) != null ? _node$content : [],
|
|
1344
|
+
blockEditorItem: BlockEditorBlock
|
|
1345
|
+
}, key);
|
|
1346
|
+
case BlockEditorDefaultBlocks.DOT_CONTENT:
|
|
1347
|
+
return jsx(DotContent, {
|
|
1348
|
+
customRenderers: customRenderers,
|
|
1349
|
+
node: node
|
|
1350
|
+
}, key);
|
|
1351
|
+
default:
|
|
1352
|
+
return jsx(UnknownBlock, {
|
|
1353
|
+
node: node
|
|
1354
|
+
}, key);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
};
|
|
1358
|
+
/**
|
|
1359
|
+
* Renders an unknown block type with a warning message in development mode.
|
|
1360
|
+
*
|
|
1361
|
+
* @param node - The block editor node to render.
|
|
1362
|
+
* @returns The rendered block or null if in production mode.
|
|
1363
|
+
*/
|
|
1364
|
+
const UnknownBlock = ({
|
|
1365
|
+
node
|
|
1366
|
+
}) => {
|
|
1367
|
+
const style = {
|
|
1368
|
+
backgroundColor: '#fff5f5',
|
|
1369
|
+
color: '#333',
|
|
1370
|
+
padding: '1rem',
|
|
1371
|
+
borderRadius: '0.5rem',
|
|
1372
|
+
marginBottom: '1rem',
|
|
1373
|
+
marginTop: '1rem',
|
|
1374
|
+
border: '1px solid #fc8181'
|
|
1375
|
+
};
|
|
1376
|
+
if (getUVEState()) {
|
|
1377
|
+
return jsxs("div", {
|
|
1378
|
+
style: style,
|
|
1379
|
+
children: [jsx("strong", {
|
|
1380
|
+
style: {
|
|
1381
|
+
color: '#c53030'
|
|
1382
|
+
},
|
|
1383
|
+
children: "Warning:"
|
|
1384
|
+
}), " The block type", ' ', jsx("strong", {
|
|
1385
|
+
children: node.type
|
|
1386
|
+
}), " is not recognized. Please check your", ' ', jsx("a", {
|
|
1387
|
+
href: "https://dev.dotcms.com/docs/block-editor",
|
|
1388
|
+
target: "_blank",
|
|
1389
|
+
rel: "noopener noreferrer",
|
|
1390
|
+
children: "configuration"
|
|
1391
|
+
}), ' ', "or contact support for assistance."]
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
return null;
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* BlockEditorRenderer component for rendering block editor field.
|
|
1399
|
+
*
|
|
1400
|
+
* @component
|
|
1401
|
+
* @param {Object} props - The component props.
|
|
1402
|
+
* @param {BlockEditorContent} props.blocks - The blocks of content to render.
|
|
1403
|
+
* @param {CustomRenderer} [props.customRenderers] - Optional custom renderers for specific block types.
|
|
1404
|
+
* @param {string} [props.className] - Optional CSS class name for the container div.
|
|
1405
|
+
* @param {React.CSSProperties} [props.style] - Optional inline styles for the container div.
|
|
1406
|
+
* @returns {JSX.Element} A div containing the rendered blocks of content.
|
|
1407
|
+
*/
|
|
1408
|
+
const DotCMSBlockEditorRenderer = ({
|
|
1409
|
+
blocks,
|
|
1410
|
+
style,
|
|
1411
|
+
className,
|
|
1412
|
+
customRenderers
|
|
1413
|
+
}) => {
|
|
1414
|
+
const [blockEditorState, setBlockEditorState] = useState({
|
|
1415
|
+
error: null
|
|
1416
|
+
});
|
|
1417
|
+
const isDevMode = useIsDevMode();
|
|
1418
|
+
/**
|
|
1419
|
+
* Validates the blocks structure and updates the block editor state.
|
|
1420
|
+
*
|
|
1421
|
+
* This effect:
|
|
1422
|
+
* 1. Validates that blocks have the correct structure (doc type, content array, etc)
|
|
1423
|
+
* 2. Updates the block editor state with validation result
|
|
1424
|
+
* 3. Logs any validation errors to console
|
|
1425
|
+
*
|
|
1426
|
+
* @dependency {Block} blocks - The content blocks to validate
|
|
1427
|
+
*/
|
|
1428
|
+
useEffect(() => {
|
|
1429
|
+
const validationResult = isValidBlocks(blocks);
|
|
1430
|
+
setBlockEditorState(validationResult);
|
|
1431
|
+
if (validationResult.error) {
|
|
1432
|
+
console.error(validationResult.error);
|
|
1433
|
+
}
|
|
1434
|
+
}, [blocks]);
|
|
1435
|
+
if (blockEditorState.error) {
|
|
1436
|
+
console.error(blockEditorState.error);
|
|
1437
|
+
if (isDevMode) {
|
|
1438
|
+
return jsx("div", {
|
|
1439
|
+
"data-testid": "invalid-blocks-message",
|
|
1440
|
+
children: blockEditorState.error
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
return null;
|
|
1444
|
+
}
|
|
1445
|
+
return jsx("div", {
|
|
1446
|
+
className: className,
|
|
1447
|
+
style: style,
|
|
1448
|
+
"data-testid": "dot-block-editor-container",
|
|
1449
|
+
children: jsx(BlockEditorBlock, {
|
|
1450
|
+
content: blocks == null ? void 0 : blocks.content,
|
|
1451
|
+
customRenderers: customRenderers
|
|
1452
|
+
})
|
|
652
1453
|
});
|
|
653
1454
|
};
|
|
654
1455
|
|
|
655
|
-
export { DotCMSLayoutBody };
|
|
1456
|
+
export { DotCMSBlockEditorRenderer, DotCMSEditableText, DotCMSLayoutBody, DotCMSShow, useDotCMSShowWhen, useEditableDotCMSPage };
|