@dotcms/react 0.0.1-beta.3 → 0.0.1-beta.31

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 (38) hide show
  1. package/README.md +165 -62
  2. package/index.esm.js +49 -2169
  3. package/next.esm.js +1031 -249
  4. package/package.json +7 -4
  5. package/src/lib/deprecated/components/DotEditableText/DotEditableText.d.ts +1 -0
  6. package/src/lib/deprecated/components/DotcmsLayout/DotcmsLayout.d.ts +1 -0
  7. package/src/lib/deprecated/mocks/mockPageContext.d.ts +1 -0
  8. package/src/lib/next/__test__/mock.d.ts +2 -1
  9. package/src/lib/next/components/Column/Column.d.ts +1 -1
  10. package/src/lib/next/components/Container/Container.d.ts +1 -1
  11. package/src/lib/next/components/Container/{ContainerFallbakcs.d.ts → ContainerFallbacks.d.ts} +2 -2
  12. package/src/lib/next/components/Contentlet/Contentlet.d.ts +2 -2
  13. package/src/lib/next/components/DotCMSBlockEditorRenderer/DotCMSBlockEditorRenderer.d.ts +27 -0
  14. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/BlockEditorBlock.d.ts +15 -0
  15. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Code.d.ts +24 -0
  16. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/DotContent.d.ts +14 -0
  17. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Image.d.ts +10 -0
  18. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Lists.d.ts +26 -0
  19. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/NoComponentProvided.d.ts +3 -0
  20. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Table.d.ts +16 -0
  21. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Texts.d.ts +81 -0
  22. package/src/lib/next/components/DotCMSBlockEditorRenderer/components/blocks/Video.d.ts +10 -0
  23. package/src/lib/next/components/DotCMSEditableText/DotCMSEditableText.d.ts +31 -0
  24. package/src/lib/next/components/DotCMSEditableText/utils.d.ts +36 -0
  25. package/src/lib/next/components/DotCMSLayoutBody/DotCMSLayoutBody.d.ts +8 -17
  26. package/src/lib/next/components/DotCMSLayoutBody/components/ErrorMessage.d.ts +1 -4
  27. package/src/lib/next/components/DotCMSShow/DotCMSShow.d.ts +49 -0
  28. package/src/lib/next/components/FallbackComponent/FallbackComponent.d.ts +6 -6
  29. package/src/lib/next/components/Row/Row.d.ts +1 -1
  30. package/src/lib/next/contexts/DotCMSPageContext.d.ts +2 -3
  31. package/src/lib/next/hooks/useDotCMSShowWhen.d.ts +31 -0
  32. package/src/lib/next/hooks/useEditableDotCMSPage.d.ts +90 -0
  33. package/src/lib/next/hooks/useIsDevMode.d.ts +2 -5
  34. package/src/next.d.ts +5 -0
  35. package/web.url-search-params.size.esm.js +4216 -0
  36. package/es.regexp.to-string.esm.js +0 -1878
  37. package/src/lib/next/types.d.ts +0 -421
  38. 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 { s as styleInject } from './es.regexp.to-string.esm.js';
4
- import { getUVEState } from '@dotcms/uve';
5
- import { UVE_MODE } from '@dotcms/uve/types';
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 context mode (or the optional `renderMode` argument) is set to 'development', or
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 = renderMode => {
30
+ const useIsDevMode = () => {
30
31
  const {
31
32
  mode
32
33
  } = useContext(DotCMSPageContext);
33
- const effectiveMode = renderMode != null ? renderMode : mode;
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
- const effectiveMode = renderMode != null ? renderMode : mode;
45
- setIsDevMode(effectiveMode === 'development');
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
- mode
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
- const isDevMode = useIsDevMode(mode);
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 ContainerNoFound = ({
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: EMPTY_CONTAINER_STYLE,
120
+ style: EMPTY_CONTAINER_STYLE_REACT,
328
121
  children: ["This container with identifier ", identifier, " was not found."]
329
122
  });
330
123
  };
@@ -342,7 +135,7 @@ const EmptyContainer = dotAttributes => {
342
135
  return null;
343
136
  }
344
137
  return jsx("div", Object.assign({}, dotAttributes, {
345
- style: EMPTY_CONTAINER_STYLE,
138
+ style: EMPTY_CONTAINER_STYLE_REACT,
346
139
  children: jsx("span", {
347
140
  "data-testid": "empty-container-message",
348
141
  children: "This container is empty."
@@ -422,7 +215,7 @@ function FallbackComponent({
422
215
  *
423
216
  * Component to render when there is no component for the content type.
424
217
  *
425
- * @param {DotCMSContentlet} contentType - The content type that couldn't be rendered
218
+ * @param {DotCMSBasicContentlet} contentType - The content type that couldn't be rendered
426
219
  * @return {*}
427
220
  */
428
221
  function NoComponent({
@@ -493,7 +286,7 @@ function CustomComponent({
493
286
  if (UserComponent) {
494
287
  return jsx(UserComponent, Object.assign({}, contentlet));
495
288
  }
496
- const UserNoComponent = userComponents['CustomNoComponent'];
289
+ const UserNoComponent = userComponents[CUSTOM_NO_COMPONENT];
497
290
  return jsx(FallbackComponent, {
498
291
  UserNoComponent: UserNoComponent,
499
292
  contentlet: contentlet
@@ -528,7 +321,7 @@ function Container({
528
321
  const containerData = useMemo(() => getContainersData(pageAsset, container), [pageAsset, container]);
529
322
  const contentlets = useMemo(() => getContentletsInContainer(pageAsset, container), [pageAsset, container]);
530
323
  if (!containerData) {
531
- return jsx(ContainerNoFound, {
324
+ return jsx(ContainerNotFound, {
532
325
  identifier: container.identifier
533
326
  });
534
327
  }
@@ -598,7 +391,7 @@ function Column({
598
391
  const Row = ({
599
392
  row
600
393
  }) => {
601
- const customRowClass = `${row.styleClass || ''} ${styles$1.row}`;
394
+ const customRowClass = combineClasses([row.styleClass || '', styles$1.row]);
602
395
  return jsx("div", {
603
396
  className: "dot-row-container",
604
397
  children: jsx("div", {
@@ -614,28 +407,18 @@ const Row = ({
614
407
  /**
615
408
  * DotCMSLayoutBody component renders the layout body for a DotCMS page.
616
409
  *
617
- * It utilizes the page asset's layout body to render rows using the Row component.
618
- * If the layout body does not exist, it renders an error message.
619
- * It also provides context (DotCMSPageContext) with the page asset, optional user components,
620
- * and the renderer mode to its children.
410
+ * It utilizes the dotCMS page asset's layout body to render the page body.
411
+ * If the layout body does not exist, it renders an error message in the mode is `development`.
621
412
  *
622
413
  * @public
623
414
  * @component
624
415
  * @param {Object} props - Component properties.
625
416
  * @param {DotCMSPageAsset} props.page - The DotCMS page asset containing the layout information.
626
- * @param {Record<string, React.ComponentType<DotCMSContentlet>>} [props.components] - Optional mapping of custom components for content rendering.
417
+ * @param {Record<string, React.ComponentType<DotCMSContentlet>>} [props.components] - mapping of custom components for content rendering.
627
418
  * @param {DotCMSPageRendererMode} [props.mode='production'] - The renderer mode; defaults to 'production'. Alternate modes might trigger different behaviors.
628
419
  *
629
420
  * @returns {JSX.Element} The rendered DotCMS page body or an error message if the layout body is missing.
630
421
  *
631
- * -------------------------------------------------------------------
632
- *
633
- * El componente DotCMSLayoutBody renderiza el cuerpo del layout para una página de DotCMS.
634
- *
635
- * Utiliza el "body" del layout del asset de la página para renderizar las filas mediante el componente Row.
636
- * Si el "body" del layout no está presente, renderiza un mensaje de error.
637
- * También provee un contexto (DotCMSPageContext) con el asset de la página, componentes de usuario opcionales,
638
- * y el modo del renderizado para sus componentes hijos.
639
422
  */
640
423
  const DotCMSLayoutBody = ({
641
424
  page,
@@ -644,11 +427,6 @@ const DotCMSLayoutBody = ({
644
427
  }) => {
645
428
  var _page$layout;
646
429
  const dotCMSPageBody = page == null || (_page$layout = page.layout) == null ? void 0 : _page$layout.body;
647
- if (!dotCMSPageBody) {
648
- return jsx(ErrorMessage, {
649
- mode: _mode
650
- });
651
- }
652
430
  const contextValue = {
653
431
  pageAsset: page,
654
432
  userComponents: _components,
@@ -656,10 +434,1014 @@ const DotCMSLayoutBody = ({
656
434
  };
657
435
  return jsx(DotCMSPageContext.Provider, {
658
436
  value: contextValue,
659
- children: dotCMSPageBody.rows.map((row, index) => jsx(Row, {
437
+ children: dotCMSPageBody ? dotCMSPageBody.rows.map((row, index) => jsx(Row, {
660
438
  row: row
661
- }, index))
439
+ }, index)) : jsx(ErrorMessage, {})
440
+ });
441
+ };
442
+
443
+ /**
444
+ * Custom hook to determine if the current UVE (Universal Visual Editor) mode
445
+ * matches the specified mode. This hook is useful for conditionally rendering
446
+ * components based on the UVE mode.
447
+ *
448
+ * @param {UVE_MODE} when - The UVE mode to check against.
449
+ * @returns {boolean} True if the current UVE mode matches the specified mode, otherwise false.
450
+ *
451
+ * @example
452
+ * // Basic usage: Check if the UVE is in edit mode
453
+ * const showInEditMode = useDotCMSShowWhen(UVE_MODE.EDIT);
454
+ * if (showInEditMode) {
455
+ * // Render edit-specific components
456
+ * }
457
+ *
458
+ * @example
459
+ * // Check if the UVE is in preview mode
460
+ * const showInPreviewMode = useDotCMSShowWhen(UVE_MODE.PREVIEW);
461
+ * if (showInPreviewMode) {
462
+ * // Render preview-specific components
463
+ * }
464
+ *
465
+ * @example
466
+ * // Check if the UVE is in live mode
467
+ * const showInLiveMode = useDotCMSShowWhen(UVE_MODE.LIVE);
468
+ * if (showInLiveMode) {
469
+ * // Render live-specific components
470
+ * }
471
+ */
472
+ const useDotCMSShowWhen = when => {
473
+ const [show, setShow] = useState(false);
474
+ useEffect(() => {
475
+ var _getUVEState;
476
+ setShow(((_getUVEState = getUVEState()) == null ? void 0 : _getUVEState.mode) === when);
477
+ }, [when]);
478
+ return show;
479
+ };
480
+
481
+ /**
482
+ * DotCMSShow component is used to conditionally render its children
483
+ * based on the Universal Visual Editor (UVE) mode. It checks if the UVE
484
+ * is in a specified mode and only renders its children in that case.
485
+ *
486
+ * @param {Object} props - The component props.
487
+ * @param {React.ReactNode} props.children - The children to be rendered when the condition is met.
488
+ * @param {UVE_MODE} [props.when=UVE_MODE.EDIT] - The UVE mode in which the children should be rendered.
489
+ * @returns {React.ReactNode | null} The children if the current UVE mode matches the `when` prop, otherwise null.
490
+ *
491
+ * @example
492
+ * // Basic usage: Render content only in edit mode
493
+ * <DotCMSShow when={UVE_MODE.EDIT}>
494
+ * <div>Edit Mode Content</div>
495
+ * </DotCMSShow>
496
+ *
497
+ * // This will render <div>Edit Mode Content</div> only if the UVE is in edit mode.
498
+ *
499
+ * @example
500
+ * // Render content in preview mode
501
+ * <DotCMSShow when={UVE_MODE.PREVIEW}>
502
+ * <MyCustomPreviewComponent />
503
+ * </DotCMSShow>
504
+ *
505
+ * // MyCustomPreviewComponent will only be rendered if the UVE is in preview mode.
506
+ *
507
+ * @example
508
+ * // Render content in live mode
509
+ * <DotCMSShow when={UVE_MODE.LIVE}>
510
+ * <LiveContentComponent />
511
+ * </DotCMSShow>
512
+ *
513
+ * // LiveContentComponent will only be rendered if the UVE is in live mode.
514
+ */
515
+ const DotCMSShow = ({
516
+ children,
517
+ when: _when = UVE_MODE.EDIT
518
+ }) => {
519
+ const show = useDotCMSShowWhen(_when);
520
+ if (!show) {
521
+ return null;
522
+ }
523
+ return children;
524
+ };
525
+
526
+ /**
527
+ * Custom hook to manage the editable state of a DotCMS page.
528
+ *
529
+ * This hook initializes the Universal Visual Editor (UVE) and subscribes to content changes.
530
+ * It updates the editable page state when content changes are detected in the UVE,
531
+ * ensuring your React components always display the latest content when editing in DotCMS.
532
+ *
533
+ * @example
534
+ * ```ts
535
+ * // Import the hook and the client
536
+ * import { useEditableDotCMSPage } from '@dotcms/react';
537
+ * import { createDotCMSClient } from '@dotcms/client';
538
+ *
539
+ * // Create the client
540
+ * const client = createDotCMSClient({
541
+ * dotcmsURL: 'https://your-dotcms-instance.com',
542
+ * authToken: 'your-auth-token'
543
+ * });
544
+ *
545
+ * // Get the page
546
+ * const page = await client.page.get('/', {
547
+ * languageId: '1',
548
+ * });
549
+ *
550
+ * // Use the hook to get an editable version of the page
551
+ * const editablePage = useEditableDotCMSPage(page);
552
+ *
553
+ * // Then use the page data in your component
554
+ * return (
555
+ * <div>
556
+ * <h1>{editablePage.page.title}</h1>
557
+ * <div dangerouslySetInnerHTML={{ __html: editablePage.page.body }} />
558
+ * </div>
559
+ * );
560
+ * ```
561
+ *
562
+ * @example
563
+ * ```ts
564
+ * // Import the hook and the client
565
+ * import { useEditableDotCMSPage } from '@dotcms/react';
566
+ * import { createDotCMSClient } from '@dotcms/client';
567
+ *
568
+ * // Create the client
569
+ * const client = createDotCMSClient({
570
+ * dotcmsURL: 'https://your-dotcms-instance.com',
571
+ * authToken: 'your-auth-token'
572
+ * });
573
+ *
574
+ * // Get the page with GraphQL content
575
+ * const page = await client.page.get('/', {
576
+ * languageId: '1',
577
+ * graphql: {
578
+ * content: {
579
+ * products: `ProductCollection(query: "+title:snow", limit: 10, offset: 0, sortBy: "score") {
580
+ * title
581
+ * urlMap
582
+ * category {
583
+ * name
584
+ * inode
585
+ * }
586
+ * retailPrice
587
+ * image {
588
+ * versionPath
589
+ * }
590
+ * }`
591
+ * }
592
+ * }
593
+ * });
594
+ *
595
+ * // Use the hook to get an editable version of the page and its content
596
+ * const editablePage = useEditableDotCMSPage(page);
597
+ *
598
+ * // Access both page data and GraphQL content
599
+ * const { page: pageData, content } = editablePage;
600
+ *
601
+ * // Use the products from GraphQL content
602
+ * return (
603
+ * <div>
604
+ * <h1>{pageData.title}</h1>
605
+ * <ProductList products={content.products} />
606
+ * </div>
607
+ * );
608
+ * ```
609
+ * @param {DotCMSPageResponse} pageResponse - The initial editable page data from client.page.get().
610
+ *
611
+ * @returns {DotCMSPageResponse} The updated editable page state that reflects any changes made in the UVE.
612
+ * The structure includes page data and any GraphQL content that was requested.
613
+ */
614
+ const useEditableDotCMSPage = pageResponse => {
615
+ const [updatedPageResponse, setUpdatedPageResponse] = useState(pageResponse);
616
+ useEffect(() => {
617
+ var _pageResponse$pageAss;
618
+ if (!getUVEState()) {
619
+ return;
620
+ }
621
+ if (!pageResponse) {
622
+ console.warn('[useEditableDotCMSPage]: No DotCMSPageResponse provided');
623
+ return;
624
+ }
625
+ const pageURI = pageResponse == null || (_pageResponse$pageAss = pageResponse.pageAsset) == null || (_pageResponse$pageAss = _pageResponse$pageAss.page) == null ? void 0 : _pageResponse$pageAss.pageURI;
626
+ const {
627
+ destroyUVESubscriptions
628
+ } = initUVE(pageResponse);
629
+ // Update the navigation to the pageURI, when we have a pageURI
630
+ // Sometimes the page is null due to permissions, so we don't want to update the navigation
631
+ // And wait for the UVE to resolve the page
632
+ if (pageURI) {
633
+ updateNavigation(pageURI);
634
+ }
635
+ return () => {
636
+ destroyUVESubscriptions();
637
+ };
638
+ }, [pageResponse]);
639
+ useEffect(() => {
640
+ const {
641
+ unsubscribe
642
+ } = createUVESubscription(UVEEventType.CONTENT_CHANGES, payload => {
643
+ setUpdatedPageResponse(payload);
644
+ });
645
+ return () => {
646
+ unsubscribe();
647
+ };
648
+ }, []);
649
+ return updatedPageResponse;
650
+ };
651
+
652
+ const DEFAULT_TINYMCE_CONFIG = Object.assign({}, __DEFAULT_TINYMCE_CONFIG__, {
653
+ licenseKey: 'gpl' // Using self-hosted license key
654
+ });
655
+ const TINYMCE_CONFIG = {
656
+ full: Object.assign({}, DEFAULT_TINYMCE_CONFIG, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.full),
657
+ plain: Object.assign({}, DEFAULT_TINYMCE_CONFIG, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.plain),
658
+ minimal: Object.assign({}, DEFAULT_TINYMCE_CONFIG, __BASE_TINYMCE_CONFIG_WITH_NO_DEFAULT__.minimal)
659
+ };
660
+
661
+ /**
662
+ * Allows inline edit content pulled from dotCMS API using TinyMCE editor
663
+ *
664
+ * @export
665
+ * @component
666
+ * @param {Readonly<DotCMSEditableTextProps>} props {
667
+ * mode = 'plain',
668
+ * format = 'text',
669
+ * contentlet,
670
+ * fieldName = ''
671
+ * }
672
+ * @example
673
+ * ```javascript
674
+ * import { DotCMSEditableText } from '@dotcms/react';
675
+ *
676
+ * const MyContentletWithTitle = ({ contentlet }) => (
677
+ * <h2>
678
+ * <DotCMSEditableText
679
+ * contentlet={contentlet}
680
+ * fieldName="title"
681
+ * mode='full'
682
+ * format='text'/>
683
+ * </h2>
684
+ * );
685
+ * ```
686
+ * @returns {JSX.Element} A component to edit content inline
687
+ */
688
+ function DotCMSEditableText({
689
+ mode = 'plain',
690
+ format = 'text',
691
+ contentlet,
692
+ fieldName = ''
693
+ }) {
694
+ const editorRef = useRef(null);
695
+ const [scriptSrc, setScriptSrc] = useState('');
696
+ const [initEditor, setInitEditor] = useState(false);
697
+ const [content, setContent] = useState((contentlet == null ? void 0 : contentlet[fieldName]) || '');
698
+ useEffect(() => {
699
+ var _state$dotCMSHost, _editorRef$current;
700
+ const state = getUVEState();
701
+ setInitEditor((state == null ? void 0 : state.mode) === UVE_MODE.EDIT && !!(state != null && (_state$dotCMSHost = state.dotCMSHost) != null && _state$dotCMSHost.length));
702
+ if (!contentlet || !fieldName) {
703
+ console.error('DotCMSEditableText: contentlet or fieldName is missing', 'Ensure that all needed props are passed to view and edit the content');
704
+ return;
705
+ }
706
+ if (state && state.mode !== UVE_MODE.EDIT) {
707
+ console.warn('DotCMSEditableText: TinyMCE is not available in the current mode');
708
+ return;
709
+ }
710
+ if (!(state != null && state.dotCMSHost)) {
711
+ console.warn('The `dotCMSHost` parameter is not defined. Check that the UVE is sending the correct parameters.');
712
+ return;
713
+ }
714
+ const createURL = new URL(__TINYMCE_PATH_ON_DOTCMS__, state.dotCMSHost);
715
+ setScriptSrc(createURL.toString());
716
+ const content = (contentlet == null ? void 0 : contentlet[fieldName]) || '';
717
+ (_editorRef$current = editorRef.current) == null || _editorRef$current.setContent(content, {
718
+ format
719
+ });
720
+ setContent(content);
721
+ }, [format, fieldName, contentlet]);
722
+ useEffect(() => {
723
+ var _getUVEState;
724
+ if (((_getUVEState = getUVEState()) == null ? void 0 : _getUVEState.mode) !== UVE_MODE.EDIT) {
725
+ return;
726
+ }
727
+ const onMessage = ({
728
+ data
729
+ }) => {
730
+ const {
731
+ name,
732
+ payload
733
+ } = data;
734
+ if (name !== __DOTCMS_UVE_EVENT__.UVE_COPY_CONTENTLET_INLINE_EDITING_SUCCESS) {
735
+ return;
736
+ }
737
+ const {
738
+ oldInode,
739
+ inode
740
+ } = payload;
741
+ const currentInode = contentlet.inode;
742
+ const shouldFocus = currentInode === oldInode || currentInode === inode;
743
+ if (shouldFocus) {
744
+ var _editorRef$current2;
745
+ (_editorRef$current2 = editorRef.current) == null || _editorRef$current2.focus();
746
+ }
747
+ };
748
+ window.addEventListener('message', onMessage);
749
+ return () => {
750
+ window.removeEventListener('message', onMessage);
751
+ };
752
+ }, [contentlet == null ? void 0 : contentlet.inode]);
753
+ const onMouseDown = event => {
754
+ var _editorRef$current3;
755
+ const {
756
+ onNumberOfPages = 1
757
+ } = contentlet;
758
+ const {
759
+ inode,
760
+ languageId: language
761
+ } = contentlet;
762
+ if (Number(onNumberOfPages) <= 1 || (_editorRef$current3 = editorRef.current) != null && _editorRef$current3.hasFocus()) {
763
+ return;
764
+ }
765
+ event.stopPropagation();
766
+ event.preventDefault();
767
+ sendMessageToUVE({
768
+ action: DotCMSUVEAction.COPY_CONTENTLET_INLINE_EDITING,
769
+ payload: {
770
+ dataset: {
771
+ inode,
772
+ language,
773
+ fieldName
774
+ }
775
+ }
776
+ });
777
+ };
778
+ const onFocusOut = () => {
779
+ var _editorRef$current4, _editorRef$current5;
780
+ const editedContent = ((_editorRef$current4 = editorRef.current) == null ? void 0 : _editorRef$current4.getContent({
781
+ format: format
782
+ })) || '';
783
+ const {
784
+ inode,
785
+ languageId: langId
786
+ } = contentlet;
787
+ if (!((_editorRef$current5 = editorRef.current) != null && _editorRef$current5.isDirty()) || !didContentChange(editedContent)) {
788
+ return;
789
+ }
790
+ sendMessageToUVE({
791
+ action: DotCMSUVEAction.UPDATE_CONTENTLET_INLINE_EDITING,
792
+ payload: {
793
+ content: editedContent,
794
+ dataset: {
795
+ inode,
796
+ langId,
797
+ fieldName
798
+ }
799
+ }
800
+ });
801
+ };
802
+ const didContentChange = editedContent => {
803
+ return content !== editedContent;
804
+ };
805
+ if (!initEditor) {
806
+ // We can let the user pass the Child Component and create a root to get the HTML for the editor
807
+ return jsx("span", {
808
+ dangerouslySetInnerHTML: {
809
+ __html: content
810
+ }
811
+ });
812
+ }
813
+ return jsx(Editor, {
814
+ tinymceScriptSrc: scriptSrc,
815
+ inline: true,
816
+ onInit: (_, editor) => editorRef.current = editor,
817
+ init: TINYMCE_CONFIG[mode],
818
+ initialValue: content,
819
+ onMouseDown: onMouseDown,
820
+ onFocusOut: onFocusOut
821
+ });
822
+ }
823
+
824
+ /**
825
+ * Renders a code block component.
826
+ *
827
+ * @param attrs - The attributes of the code block.
828
+ * @param children - The content of the code block.
829
+ * @returns The rendered code block component.
830
+ */
831
+ const CodeBlock = ({
832
+ node,
833
+ children
834
+ }) => {
835
+ var _node$attrs;
836
+ const language = (node == null || (_node$attrs = node.attrs) == null ? void 0 : _node$attrs.language) || '';
837
+ return jsx("pre", {
838
+ "data-language": language,
839
+ children: jsx("code", {
840
+ children: children
841
+ })
842
+ });
843
+ };
844
+ /**
845
+ * Renders a blockquote component.
846
+ *
847
+ * @param children - The content to be rendered inside the blockquote.
848
+ * @returns The rendered blockquote component.
849
+ */
850
+ const BlockQuote = ({
851
+ children
852
+ }) => {
853
+ return jsx("blockquote", {
854
+ children: children
855
+ });
856
+ };
857
+
858
+ const NoComponentProvided = ({
859
+ contentType
860
+ }) => {
861
+ const style = {
862
+ backgroundColor: '#fffaf0',
863
+ color: '#333',
864
+ padding: '1rem',
865
+ borderRadius: '0.5rem',
866
+ marginBottom: '1rem',
867
+ marginTop: '1rem',
868
+ border: '1px solid #ed8936'
869
+ };
870
+ return jsxs("div", {
871
+ "data-testid": "no-component-provided",
872
+ style: style,
873
+ children: [jsx("strong", {
874
+ style: {
875
+ color: '#c05621'
876
+ },
877
+ children: "Dev Warning"
878
+ }), ": No component or custom renderer provided for content type", jsx("strong", {
879
+ style: {
880
+ color: '#c05621'
881
+ },
882
+ children: contentType || 'Unknown'
883
+ }), ".", jsx("br", {}), "Please refer to the", jsx("a", {
884
+ href: "https://dev.dotcms.com/docs/block-editor",
885
+ target: "_blank",
886
+ rel: "noopener noreferrer",
887
+ style: {
888
+ color: '#c05621'
889
+ },
890
+ children: "Block Editor Custom Renderers Documentation"
891
+ }), ' ', "for guidance."]
892
+ });
893
+ };
894
+
895
+ 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.';
896
+ 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.`;
897
+ /**
898
+ * Renders a DotContent component.
899
+ *
900
+ * @param {DotContentProps} props - The props for the DotContent component.
901
+ * @returns {JSX.Element} The rendered DotContent component.
902
+ */
903
+ const DotContent = ({
904
+ customRenderers,
905
+ node
906
+ }) => {
907
+ const isDevMode = useIsDevMode();
908
+ const {
909
+ attrs = {}
910
+ } = node;
911
+ const {
912
+ data
913
+ } = attrs;
914
+ if (!data) {
915
+ console.error(DOT_CONTENT_NO_DATA_MESSAGE);
916
+ return null;
917
+ }
918
+ const {
919
+ contentType = 'Unknown Content Type'
920
+ } = data;
921
+ const Component = customRenderers[contentType];
922
+ /* In dev mode, show a helpful message for unknown content types */
923
+ if (isDevMode && !Component) {
924
+ return jsx(NoComponentProvided, {
925
+ contentType: contentType
926
+ });
927
+ }
928
+ /* In production, use default component if no matching component found */
929
+ if (!Component) {
930
+ console.warn(DOT_CONTENT_NO_MATCHING_COMPONENT_MESSAGE(contentType));
931
+ return null;
932
+ }
933
+ return jsx(Component, Object.assign({}, node));
934
+ };
935
+
936
+ /**
937
+ * Renders an image component for dotCMS.
938
+ *
939
+ * @param node - The node for the DotCMSImage component.
940
+ * @returns The rendered image component.
941
+ */
942
+ const DotCMSImage = ({
943
+ node
944
+ }) => {
945
+ const {
946
+ src,
947
+ alt
948
+ } = node.attrs;
949
+ return jsx("img", {
950
+ alt: alt,
951
+ src: src
952
+ });
953
+ };
954
+
955
+ /**
956
+ * ListItem component represents a list item in a block editor.
957
+ *
958
+ * @param children - The content of the list item.
959
+ * @returns The rendered list item element.
960
+ */
961
+ const ListItem = ({
962
+ children
963
+ }) => {
964
+ return jsx("li", {
965
+ children: children
966
+ });
967
+ };
968
+ /**
969
+ * Renders an ordered list component.
970
+ *
971
+ * @param children - The content to be rendered inside the ordered list.
972
+ * @returns The ordered list component.
973
+ */
974
+ const OrderedList = ({
975
+ children
976
+ }) => {
977
+ return jsx("ol", {
978
+ children: children
979
+ });
980
+ };
981
+ /**
982
+ * Renders a bullet list component.
983
+ *
984
+ * @param children - The content of the bullet list.
985
+ * @returns The rendered bullet list component.
986
+ */
987
+ const BulletList = ({
988
+ children
989
+ }) => {
990
+ return jsx("ul", {
991
+ children: children
992
+ });
993
+ };
994
+
995
+ /**
996
+ * Renders a table component for the Block Editor.
997
+ *
998
+ * @param content - The content of the table.
999
+ * @param blockEditorItem - The Block Editor item component.
1000
+ */
1001
+ const TableRenderer = ({
1002
+ content,
1003
+ blockEditorItem
1004
+ }) => {
1005
+ const BlockEditorItemComponent = blockEditorItem;
1006
+ const renderTableContent = node => {
1007
+ var _node$content;
1008
+ return jsx(BlockEditorItemComponent, {
1009
+ content: (_node$content = node.content) != null ? _node$content : []
1010
+ });
1011
+ };
1012
+ return jsxs("table", {
1013
+ children: [jsx("thead", {
1014
+ children: content.slice(0, 1).map((rowNode, rowIndex) => {
1015
+ var _rowNode$content;
1016
+ return jsx("tr", {
1017
+ children: (_rowNode$content = rowNode.content) == null ? void 0 : _rowNode$content.map((cellNode, cellIndex) => {
1018
+ var _cellNode$attrs, _cellNode$attrs2;
1019
+ return jsx("th", {
1020
+ colSpan: Number(((_cellNode$attrs = cellNode.attrs) == null ? void 0 : _cellNode$attrs.colspan) || 1),
1021
+ rowSpan: Number(((_cellNode$attrs2 = cellNode.attrs) == null ? void 0 : _cellNode$attrs2.rowspan) || 1),
1022
+ children: renderTableContent(cellNode)
1023
+ }, `${cellNode.type}-${cellIndex}`);
1024
+ })
1025
+ }, `${rowNode.type}-${rowIndex}`);
1026
+ })
1027
+ }), jsx("tbody", {
1028
+ children: content.slice(1).map((rowNode, rowIndex) => {
1029
+ var _rowNode$content2;
1030
+ return jsx("tr", {
1031
+ children: (_rowNode$content2 = rowNode.content) == null ? void 0 : _rowNode$content2.map((cellNode, cellIndex) => {
1032
+ var _cellNode$attrs3, _cellNode$attrs4;
1033
+ return jsx("td", {
1034
+ colSpan: Number(((_cellNode$attrs3 = cellNode.attrs) == null ? void 0 : _cellNode$attrs3.colspan) || 1),
1035
+ rowSpan: Number(((_cellNode$attrs4 = cellNode.attrs) == null ? void 0 : _cellNode$attrs4.rowspan) || 1),
1036
+ children: renderTableContent(cellNode)
1037
+ }, `${cellNode.type}-${cellIndex}`);
1038
+ })
1039
+ }, `${rowNode.type}-${rowIndex}`);
1040
+ })
1041
+ })]
1042
+ });
1043
+ };
1044
+
1045
+ /**
1046
+ * Renders the text in bold.
1047
+ *
1048
+ * @param children - The content to be rendered in bold.
1049
+ */
1050
+ const Bold = ({
1051
+ children
1052
+ }) => jsx("strong", {
1053
+ children: children
1054
+ });
1055
+ /**
1056
+ * Renders the text in italic format.
1057
+ *
1058
+ * @param children - The content to be rendered in italic.
1059
+ */
1060
+ const Italic = ({
1061
+ children
1062
+ }) => jsx("em", {
1063
+ children: children
1064
+ });
1065
+ /**
1066
+ * Renders a strike-through text.
1067
+ *
1068
+ * @param children - The content to be rendered within the strike-through element.
1069
+ */
1070
+ const Strike = ({
1071
+ children
1072
+ }) => jsx("s", {
1073
+ children: children
1074
+ });
1075
+ /**
1076
+ * Renders an underline element for the given children.
1077
+ *
1078
+ * @param children - The content to be underlined.
1079
+ */
1080
+ const Underline = ({
1081
+ children
1082
+ }) => jsx("u", {
1083
+ children: children
1084
+ });
1085
+ /**
1086
+ * Renders a paragraph element.
1087
+ *
1088
+ * @param children - The content of the paragraph.
1089
+ * @param attrs - The style attributes for the paragraph.
1090
+ * @returns The rendered paragraph element.
1091
+ */
1092
+ const Paragraph = ({
1093
+ children,
1094
+ node
1095
+ }) => {
1096
+ const attrs = (node == null ? void 0 : node.attrs) || {};
1097
+ return jsx("p", {
1098
+ style: attrs,
1099
+ children: children
1100
+ });
1101
+ };
1102
+ /**
1103
+ * Renders a link component.
1104
+ *
1105
+ * @param children - The content of the link.
1106
+ * @param attrs - The attributes to be applied to the link.
1107
+ * @returns The rendered link component.
1108
+ */
1109
+ const Link = ({
1110
+ children,
1111
+ attrs
1112
+ }) => {
1113
+ return jsx("a", Object.assign({}, attrs, {
1114
+ children: children
1115
+ }));
1116
+ };
1117
+ /**
1118
+ * Renders a heading element with the specified level.
1119
+ *
1120
+ * @param children - The content of the heading.
1121
+ * @param attrs - The attributes for the heading.
1122
+ * @returns The rendered heading element.
1123
+ */
1124
+ const Heading = ({
1125
+ children,
1126
+ node
1127
+ }) => {
1128
+ const attrs = (node == null ? void 0 : node.attrs) || {};
1129
+ const level = attrs.level || 1;
1130
+ const Tag = `h${level}`;
1131
+ return jsx(Tag, {
1132
+ children: children
1133
+ });
1134
+ };
1135
+ /**
1136
+ * Renders the superscript text.
1137
+ *
1138
+ * @param children - The content to be rendered as superscript.
1139
+ */
1140
+ const Superscript = ({
1141
+ children
1142
+ }) => jsx("sup", {
1143
+ children: children
1144
+ });
1145
+ /**
1146
+ * Renders a subscript element.
1147
+ *
1148
+ * @param children - The content to be rendered as subscript.
1149
+ */
1150
+ const Subscript = ({
1151
+ children
1152
+ }) => jsx("sub", {
1153
+ children: children
1154
+ });
1155
+ const nodeMarks = {
1156
+ bold: Bold,
1157
+ link: Link,
1158
+ italic: Italic,
1159
+ strike: Strike,
1160
+ subscript: Subscript,
1161
+ underline: Underline,
1162
+ superscript: Superscript
1163
+ };
1164
+ const defaultMark = {
1165
+ type: '',
1166
+ attrs: {}
1167
+ };
1168
+ /**
1169
+ * Renders a text block with optional marks.
1170
+ *
1171
+ * @param props - The props for the TextBlock component.
1172
+ * @returns The rendered text block.
1173
+ */
1174
+ const TextBlock = (props = {}) => {
1175
+ const {
1176
+ marks = [],
1177
+ text
1178
+ } = props;
1179
+ const mark = marks[0] || defaultMark;
1180
+ const textProps = Object.assign({}, props, {
1181
+ marks: marks.slice(1)
1182
+ });
1183
+ const Component = nodeMarks[mark == null ? void 0 : mark.type];
1184
+ // In React, class is not a valid attribute name, so we need to rename it to className
1185
+ if (mark.attrs) {
1186
+ mark.attrs.className = mark.attrs.class;
1187
+ delete mark.attrs.class;
1188
+ }
1189
+ if (!Component) {
1190
+ return text;
1191
+ }
1192
+ return jsx(Component, {
1193
+ type: mark.type,
1194
+ attrs: mark.attrs,
1195
+ children: jsx(TextBlock, Object.assign({}, textProps))
1196
+ });
1197
+ };
1198
+
1199
+ /**
1200
+ * Renders a video component for displaying videos.
1201
+ *
1202
+ * @param props - The properties for the video component.
1203
+ * @returns The rendered video component.
1204
+ */
1205
+ const DotCMSVideo = ({
1206
+ node
1207
+ }) => {
1208
+ const {
1209
+ data,
1210
+ src,
1211
+ mimeType,
1212
+ width,
1213
+ height
1214
+ } = node.attrs;
1215
+ const poster = data == null ? void 0 : data.thumbnail;
1216
+ const posterAttribute = poster ? {
1217
+ poster
1218
+ } : {};
1219
+ return jsxs("video", Object.assign({
1220
+ controls: true,
1221
+ preload: "metadata",
1222
+ width: width,
1223
+ height: height
1224
+ }, posterAttribute, {
1225
+ children: [jsx("track", {
1226
+ default: true,
1227
+ kind: "captions",
1228
+ srcLang: "en"
1229
+ }), jsx("source", {
1230
+ src: src,
1231
+ type: mimeType
1232
+ }), "Your browser does not support the ", jsx("code", {
1233
+ children: "video"
1234
+ }), " element."]
1235
+ }));
1236
+ };
1237
+
1238
+ /**
1239
+ * Renders a block editor item based on the provided content and custom renderers.
1240
+ *
1241
+ * @param content - The content nodes to render.
1242
+ * @param customRenderers - Optional custom renderers for specific node types.
1243
+ * @returns The rendered block editor item.
1244
+ */
1245
+ const BlockEditorBlock = ({
1246
+ content,
1247
+ customRenderers
1248
+ }) => {
1249
+ if (!content) {
1250
+ return null;
1251
+ }
1252
+ return content == null ? void 0 : content.map((node, index) => {
1253
+ var _node$content;
1254
+ const CustomRendererComponent = customRenderers == null ? void 0 : customRenderers[node.type];
1255
+ const key = `${node.type}-${index}`;
1256
+ if (CustomRendererComponent) {
1257
+ return jsx(CustomRendererComponent, {
1258
+ content: node.content,
1259
+ children: jsx(BlockEditorBlock, {
1260
+ content: node.content,
1261
+ customRenderers: customRenderers
1262
+ })
1263
+ }, key);
1264
+ }
1265
+ switch (node.type) {
1266
+ case BlockEditorDefaultBlocks.PARAGRAPH:
1267
+ return jsx(Paragraph, {
1268
+ node: node,
1269
+ children: jsx(BlockEditorBlock, {
1270
+ content: node.content,
1271
+ customRenderers: customRenderers
1272
+ })
1273
+ }, key);
1274
+ case BlockEditorDefaultBlocks.HEADING:
1275
+ return jsx(Heading, {
1276
+ node: node,
1277
+ children: jsx(BlockEditorBlock, {
1278
+ content: node.content,
1279
+ customRenderers: customRenderers
1280
+ })
1281
+ }, key);
1282
+ case BlockEditorDefaultBlocks.TEXT:
1283
+ return jsx(TextBlock, Object.assign({}, node), key);
1284
+ case BlockEditorDefaultBlocks.BULLET_LIST:
1285
+ return jsx(BulletList, {
1286
+ children: jsx(BlockEditorBlock, {
1287
+ content: node.content,
1288
+ customRenderers: customRenderers
1289
+ })
1290
+ }, key);
1291
+ case BlockEditorDefaultBlocks.ORDERED_LIST:
1292
+ return jsx(OrderedList, {
1293
+ children: jsx(BlockEditorBlock, {
1294
+ content: node.content,
1295
+ customRenderers: customRenderers
1296
+ })
1297
+ }, key);
1298
+ case BlockEditorDefaultBlocks.LIST_ITEM:
1299
+ return jsx(ListItem, {
1300
+ children: jsx(BlockEditorBlock, {
1301
+ content: node.content,
1302
+ customRenderers: customRenderers
1303
+ })
1304
+ }, key);
1305
+ case BlockEditorDefaultBlocks.BLOCK_QUOTE:
1306
+ return jsx(BlockQuote, {
1307
+ children: jsx(BlockEditorBlock, {
1308
+ content: node.content,
1309
+ customRenderers: customRenderers
1310
+ })
1311
+ }, key);
1312
+ case BlockEditorDefaultBlocks.CODE_BLOCK:
1313
+ return jsx(CodeBlock, {
1314
+ node: node,
1315
+ children: jsx(BlockEditorBlock, {
1316
+ content: node.content,
1317
+ customRenderers: customRenderers
1318
+ })
1319
+ }, key);
1320
+ case BlockEditorDefaultBlocks.HARDBREAK:
1321
+ return jsx("br", {}, key);
1322
+ case BlockEditorDefaultBlocks.HORIZONTAL_RULE:
1323
+ return jsx("hr", {}, key);
1324
+ case BlockEditorDefaultBlocks.DOT_IMAGE:
1325
+ return jsx(DotCMSImage, {
1326
+ node: node
1327
+ }, key);
1328
+ case BlockEditorDefaultBlocks.DOT_VIDEO:
1329
+ return jsx(DotCMSVideo, {
1330
+ node: node
1331
+ }, key);
1332
+ case BlockEditorDefaultBlocks.TABLE:
1333
+ return jsx(TableRenderer, {
1334
+ content: (_node$content = node.content) != null ? _node$content : [],
1335
+ blockEditorItem: BlockEditorBlock
1336
+ }, key);
1337
+ case BlockEditorDefaultBlocks.DOT_CONTENT:
1338
+ return jsx(DotContent, {
1339
+ customRenderers: customRenderers,
1340
+ node: node
1341
+ }, key);
1342
+ default:
1343
+ return jsx(UnknownBlock, {
1344
+ node: node
1345
+ }, key);
1346
+ }
1347
+ });
1348
+ };
1349
+ /**
1350
+ * Renders an unknown block type with a warning message in development mode.
1351
+ *
1352
+ * @param node - The block editor node to render.
1353
+ * @returns The rendered block or null if in production mode.
1354
+ */
1355
+ const UnknownBlock = ({
1356
+ node
1357
+ }) => {
1358
+ const style = {
1359
+ backgroundColor: '#fff5f5',
1360
+ color: '#333',
1361
+ padding: '1rem',
1362
+ borderRadius: '0.5rem',
1363
+ marginBottom: '1rem',
1364
+ marginTop: '1rem',
1365
+ border: '1px solid #fc8181'
1366
+ };
1367
+ if (getUVEState()) {
1368
+ return jsxs("div", {
1369
+ style: style,
1370
+ children: [jsx("strong", {
1371
+ style: {
1372
+ color: '#c53030'
1373
+ },
1374
+ children: "Warning:"
1375
+ }), " The block type", ' ', jsx("strong", {
1376
+ children: node.type
1377
+ }), " is not recognized. Please check your", ' ', jsx("a", {
1378
+ href: "https://dev.dotcms.com/docs/block-editor",
1379
+ target: "_blank",
1380
+ rel: "noopener noreferrer",
1381
+ children: "configuration"
1382
+ }), ' ', "or contact support for assistance."]
1383
+ });
1384
+ }
1385
+ return null;
1386
+ };
1387
+
1388
+ /**
1389
+ * BlockEditorRenderer component for rendering block editor field.
1390
+ *
1391
+ * @component
1392
+ * @param {Object} props - The component props.
1393
+ * @param {BlockEditorContent} props.blocks - The blocks of content to render.
1394
+ * @param {CustomRenderer} [props.customRenderers] - Optional custom renderers for specific block types.
1395
+ * @param {string} [props.className] - Optional CSS class name for the container div.
1396
+ * @param {React.CSSProperties} [props.style] - Optional inline styles for the container div.
1397
+ * @returns {JSX.Element} A div containing the rendered blocks of content.
1398
+ */
1399
+ const DotCMSBlockEditorRenderer = ({
1400
+ blocks,
1401
+ style,
1402
+ className,
1403
+ customRenderers
1404
+ }) => {
1405
+ const [blockEditorState, setBlockEditorState] = useState({
1406
+ error: null
1407
+ });
1408
+ const isDevMode = useIsDevMode();
1409
+ /**
1410
+ * Validates the blocks structure and updates the block editor state.
1411
+ *
1412
+ * This effect:
1413
+ * 1. Validates that blocks have the correct structure (doc type, content array, etc)
1414
+ * 2. Updates the block editor state with validation result
1415
+ * 3. Logs any validation errors to console
1416
+ *
1417
+ * @dependency {Block} blocks - The content blocks to validate
1418
+ */
1419
+ useEffect(() => {
1420
+ const validationResult = isValidBlocks(blocks);
1421
+ setBlockEditorState(validationResult);
1422
+ if (validationResult.error) {
1423
+ console.error(validationResult.error);
1424
+ }
1425
+ }, [blocks]);
1426
+ if (blockEditorState.error) {
1427
+ console.error(blockEditorState.error);
1428
+ if (isDevMode) {
1429
+ return jsx("div", {
1430
+ "data-testid": "invalid-blocks-message",
1431
+ children: blockEditorState.error
1432
+ });
1433
+ }
1434
+ return null;
1435
+ }
1436
+ return jsx("div", {
1437
+ className: className,
1438
+ style: style,
1439
+ "data-testid": "dot-block-editor-container",
1440
+ children: jsx(BlockEditorBlock, {
1441
+ content: blocks == null ? void 0 : blocks.content,
1442
+ customRenderers: customRenderers
1443
+ })
662
1444
  });
663
1445
  };
664
1446
 
665
- export { DotCMSLayoutBody };
1447
+ export { DotCMSBlockEditorRenderer, DotCMSEditableText, DotCMSLayoutBody, DotCMSShow, useDotCMSShowWhen, useEditableDotCMSPage };