@dotcms/uve 0.0.1-beta.17 → 0.0.1-beta.18

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/index.cjs.js CHANGED
@@ -1,94 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var constants = require('./constants.cjs.js');
4
- var types = require('./types.cjs.js');
3
+ var internal = require('./index.cjs2.js');
4
+ require('./types.cjs.js');
5
5
 
6
- /**
7
- * Gets the current state of the Universal Visual Editor (UVE).
8
- *
9
- * This function checks if the code is running inside the DotCMS Universal Visual Editor
10
- * and returns information about its current state, including the editor mode.
11
- *
12
- * @export
13
- * @return {UVEState | undefined} Returns the UVE state object if running inside the editor,
14
- * undefined otherwise.
15
- *
16
- * The state includes:
17
- * - mode: The current editor mode (preview, edit, live)
18
- * - languageId: The language ID of the current page setted on the UVE
19
- * - persona: The persona of the current page setted on the UVE
20
- * - variantName: The name of the current variant
21
- * - experimentId: The ID of the current experiment
22
- * - publishDate: The publish date of the current page setted on the UVE
23
- *
24
- * @note The absence of any of these properties means that the value is the default one.
25
- *
26
- * @example
27
- * ```ts
28
- * const editorState = getUVEState();
29
- * if (editorState?.mode === 'edit') {
30
- * // Enable editing features
31
- * }
32
- * ```
33
- */
34
- function getUVEState() {
35
- if (typeof window === 'undefined' || window.parent === window || !window.location) {
36
- return;
37
- }
38
- const url = new URL(window.location.href);
39
- const possibleModes = Object.values(types.UVE_MODE);
40
- let mode = url.searchParams.get('mode') ?? types.UVE_MODE.EDIT;
41
- const languageId = url.searchParams.get('language_id');
42
- const persona = url.searchParams.get('personaId');
43
- const variantName = url.searchParams.get('variantName');
44
- const experimentId = url.searchParams.get('experimentId');
45
- const publishDate = url.searchParams.get('publishDate');
46
- if (!possibleModes.includes(mode)) {
47
- mode = types.UVE_MODE.EDIT;
48
- }
49
- return {
50
- mode,
51
- languageId,
52
- persona,
53
- variantName,
54
- experimentId,
55
- publishDate
56
- };
57
- }
58
- /**
59
- * Creates a subscription to a UVE event.
60
- *
61
- * @param eventType - The type of event to subscribe to
62
- * @param callback - The callback function that will be called when the event occurs
63
- * @returns An event subscription that can be used to unsubscribe
64
- *
65
- * @example
66
- * ```ts
67
- * // Subscribe to page changes
68
- * const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
69
- * console.log('Content changes:', changes);
70
- * });
71
- *
72
- * // Later, unsubscribe when no longer needed
73
- * subscription.unsubscribe();
74
- * ```
75
- */
76
- function createUVESubscription(eventType, callback) {
77
- if (!getUVEState()) {
78
- console.warn('UVE Subscription: Not running inside UVE');
79
- return constants.__UVE_EVENT_ERROR_FALLBACK__(eventType);
80
- }
81
- const eventCallback = constants.__UVE_EVENTS__[eventType];
82
- if (!eventCallback) {
83
- console.error(`UVE Subscription: Event ${eventType} not found`);
84
- return constants.__UVE_EVENT_ERROR_FALLBACK__(eventType);
85
- }
86
- return eventCallback(callback);
87
- }
88
6
 
89
- exports.editContentlet = constants.editContentlet;
90
- exports.initInlineEditing = constants.initInlineEditing;
91
- exports.reorderMenu = constants.reorderMenu;
92
- exports.sendMessageToUVE = constants.sendMessageToUVE;
93
- exports.createUVESubscription = createUVESubscription;
94
- exports.getUVEState = getUVEState;
7
+
8
+ exports.createUVESubscription = internal.createUVESubscription;
9
+ exports.editContentlet = internal.editContentlet;
10
+ exports.getUVEState = internal.getUVEState;
11
+ exports.initInlineEditing = internal.initInlineEditing;
12
+ exports.initUVE = internal.initUVE;
13
+ exports.reorderMenu = internal.reorderMenu;
14
+ exports.sendMessageToUVE = internal.sendMessageToUVE;
@@ -372,6 +372,161 @@ function getDotContainerAttributes({
372
372
  };
373
373
  }
374
374
 
375
+ /* eslint-disable @typescript-eslint/no-explicit-any */
376
+ /**
377
+ * Sets up scroll event handlers for the window to notify the editor about scroll events.
378
+ * Adds listeners for both 'scroll' and 'scrollend' events, sending appropriate messages
379
+ * to the editor when these events occur.
380
+ */
381
+ function scrollHandler() {
382
+ const scrollCallback = () => {
383
+ sendMessageToUVE({
384
+ action: types.DotCMSUVEAction.IFRAME_SCROLL
385
+ });
386
+ };
387
+ const scrollEndCallback = () => {
388
+ sendMessageToUVE({
389
+ action: types.DotCMSUVEAction.IFRAME_SCROLL_END
390
+ });
391
+ };
392
+ window.addEventListener('scroll', scrollCallback);
393
+ window.addEventListener('scrollend', scrollEndCallback);
394
+ return {
395
+ destroyScrollHandler: () => {
396
+ window.removeEventListener('scroll', scrollCallback);
397
+ window.removeEventListener('scrollend', scrollEndCallback);
398
+ }
399
+ };
400
+ }
401
+ /**
402
+ * Adds 'empty-contentlet' class to contentlet elements that have no height.
403
+ * This helps identify and style empty contentlets in the editor view.
404
+ *
405
+ * @remarks
406
+ * The function queries all elements with data-dot-object="contentlet" attribute
407
+ * and checks their clientHeight. If an element has no height (clientHeight = 0),
408
+ * it adds the 'empty-contentlet' class to that element.
409
+ */
410
+ function addClassToEmptyContentlets() {
411
+ const contentlets = document.querySelectorAll('[data-dot-object="contentlet"]');
412
+ contentlets.forEach(contentlet => {
413
+ if (contentlet.clientHeight) {
414
+ return;
415
+ }
416
+ contentlet.classList.add('empty-contentlet');
417
+ });
418
+ }
419
+ /**
420
+ * Registers event handlers for various UVE (Universal Visual Editor) events.
421
+ *
422
+ * This function sets up subscriptions for:
423
+ * - Page reload events that refresh the window
424
+ * - Bounds request events to update editor boundaries
425
+ * - Iframe scroll events to handle smooth scrolling within bounds
426
+ * - Contentlet hover events to notify the editor
427
+ *
428
+ * @remarks
429
+ * For scroll events, the function includes logic to prevent scrolling beyond
430
+ * the top or bottom boundaries of the iframe, which helps maintain proper
431
+ * scroll event handling.
432
+ */
433
+ function registerUVEEvents() {
434
+ const pageReloadSubscription = createUVESubscription(types.UVEEventType.PAGE_RELOAD, () => {
435
+ window.location.reload();
436
+ });
437
+ const requestBoundsSubscription = createUVESubscription(types.UVEEventType.REQUEST_BOUNDS, bounds => {
438
+ setBounds(bounds);
439
+ });
440
+ const iframeScrollSubscription = createUVESubscription(types.UVEEventType.IFRAME_SCROLL, direction => {
441
+ if (window.scrollY === 0 && direction === 'up' || computeScrollIsInBottom() && direction === 'down') {
442
+ // If the iframe scroll is at the top or bottom, do not send anything.
443
+ // This avoids losing the scrollend event.
444
+ return;
445
+ }
446
+ const scrollY = direction === 'up' ? -120 : 120;
447
+ window.scrollBy({
448
+ left: 0,
449
+ top: scrollY,
450
+ behavior: 'smooth'
451
+ });
452
+ });
453
+ const contentletHoveredSubscription = createUVESubscription(types.UVEEventType.CONTENTLET_HOVERED, contentletHovered => {
454
+ sendMessageToUVE({
455
+ action: types.DotCMSUVEAction.SET_CONTENTLET,
456
+ payload: contentletHovered
457
+ });
458
+ });
459
+ return {
460
+ subscriptions: [pageReloadSubscription, requestBoundsSubscription, iframeScrollSubscription, contentletHoveredSubscription]
461
+ };
462
+ }
463
+ /**
464
+ * Notifies the editor that the UVE client is ready to receive messages.
465
+ *
466
+ * This function sends a message to the editor indicating that the client-side
467
+ * initialization is complete and it's ready to handle editor interactions.
468
+ *
469
+ * @remarks
470
+ * This is typically called after all UVE event handlers and DOM listeners
471
+ * have been set up successfully.
472
+ */
473
+ function setClientIsReady() {
474
+ sendMessageToUVE({
475
+ action: types.DotCMSUVEAction.CLIENT_READY
476
+ });
477
+ }
478
+ /**
479
+ * Listen for block editor inline event.
480
+ */
481
+ function listenBlockEditorInlineEvent() {
482
+ if (document.readyState === 'complete') {
483
+ // The page is fully loaded or interactive
484
+ listenBlockEditorClick();
485
+ return {
486
+ destroyListenBlockEditorInlineEvent: () => {
487
+ window.removeEventListener('load', () => listenBlockEditorClick());
488
+ }
489
+ };
490
+ }
491
+ window.addEventListener('load', () => listenBlockEditorClick());
492
+ return {
493
+ destroyListenBlockEditorInlineEvent: () => {
494
+ window.removeEventListener('load', () => listenBlockEditorClick());
495
+ }
496
+ };
497
+ }
498
+ const listenBlockEditorClick = () => {
499
+ const editBlockEditorNodes = document.querySelectorAll('[data-block-editor-content]');
500
+ if (!editBlockEditorNodes.length) {
501
+ return;
502
+ }
503
+ editBlockEditorNodes.forEach(node => {
504
+ const {
505
+ inode,
506
+ language = '1',
507
+ contentType,
508
+ fieldName,
509
+ blockEditorContent
510
+ } = node.dataset;
511
+ const content = JSON.parse(blockEditorContent || '');
512
+ if (!inode || !language || !contentType || !fieldName) {
513
+ console.error('Missing data attributes for block editor inline editing.');
514
+ console.warn('inode, language, contentType and fieldName are required.');
515
+ return;
516
+ }
517
+ node.classList.add('dotcms__inline-edit-field');
518
+ node.addEventListener('click', () => {
519
+ initInlineEditing('BLOCK_EDITOR', {
520
+ inode,
521
+ content,
522
+ language: parseInt(language),
523
+ fieldName,
524
+ contentType
525
+ });
526
+ });
527
+ });
528
+ };
529
+
375
530
  /**
376
531
  * Post message to dotcms page editor
377
532
  *
@@ -444,6 +599,146 @@ function initInlineEditing(type, data) {
444
599
  }
445
600
  });
446
601
  }
602
+ /**
603
+ * Initializes the Universal Visual Editor (UVE) with required handlers and event listeners.
604
+ *
605
+ * This function sets up:
606
+ * - Scroll handling
607
+ * - Empty contentlet styling
608
+ * - Block editor inline event listening
609
+ * - Client ready state
610
+ * - UVE event subscriptions
611
+ *
612
+ * @returns {Object} An object containing the cleanup function
613
+ * @returns {Function} destroyUVESubscriptions - Function to clean up all UVE event subscriptions
614
+ *
615
+ * @example
616
+ * ```typescript
617
+ * const { destroyUVESubscriptions } = initUVE();
618
+ *
619
+ * // When done with UVE
620
+ * destroyUVESubscriptions();
621
+ * ```
622
+ */
623
+ function initUVE() {
624
+ addClassToEmptyContentlets();
625
+ setClientIsReady();
626
+ const {
627
+ subscriptions
628
+ } = registerUVEEvents();
629
+ const {
630
+ destroyScrollHandler
631
+ } = scrollHandler();
632
+ const {
633
+ destroyListenBlockEditorInlineEvent
634
+ } = listenBlockEditorInlineEvent();
635
+ return {
636
+ destroyUVESubscriptions: () => {
637
+ subscriptions.forEach(subscription => subscription.unsubscribe());
638
+ destroyScrollHandler();
639
+ destroyListenBlockEditorInlineEvent();
640
+ }
641
+ };
642
+ }
643
+
644
+ /**
645
+ * Sets the bounds of the containers in the editor.
646
+ * Retrieves the containers from the DOM and sends their position data to the editor.
647
+ * @private
648
+ * @memberof DotCMSPageEditor
649
+ */
650
+ function setBounds(bounds) {
651
+ sendMessageToUVE({
652
+ action: types.DotCMSUVEAction.SET_BOUNDS,
653
+ payload: bounds
654
+ });
655
+ }
656
+ /**
657
+ * Validates the structure of a Block Editor block.
658
+ *
659
+ * This function checks that:
660
+ * 1. The blocks parameter is a valid object
661
+ * 2. The block has a 'doc' type
662
+ * 3. The block has a valid content array that is not empty
663
+ *
664
+ * @param {Block} blocks - The blocks structure to validate
665
+ * @returns {BlockEditorState} Object containing validation state and any error message
666
+ * @property {boolean} BlockEditorState.isValid - Whether the blocks structure is valid
667
+ * @property {string | null} BlockEditorState.error - Error message if invalid, null if valid
668
+ */
669
+ const isValidBlocks = blocks => {
670
+ if (!blocks) {
671
+ return {
672
+ error: `Error: Blocks object is not defined`
673
+ };
674
+ }
675
+ if (typeof blocks !== 'object') {
676
+ return {
677
+ error: `Error: Blocks must be an object, but received: ${typeof blocks}`
678
+ };
679
+ }
680
+ if (blocks.type !== 'doc') {
681
+ return {
682
+ error: `Error: Invalid block type. Expected 'doc' but received: '${blocks.type}'`
683
+ };
684
+ }
685
+ if (!blocks.content) {
686
+ return {
687
+ error: 'Error: Blocks content is missing'
688
+ };
689
+ }
690
+ if (!Array.isArray(blocks.content)) {
691
+ return {
692
+ error: `Error: Blocks content must be an array, but received: ${typeof blocks.content}`
693
+ };
694
+ }
695
+ if (blocks.content.length === 0) {
696
+ return {
697
+ error: 'Error: Blocks content is empty. At least one block is required.'
698
+ };
699
+ }
700
+ // Validate each block in the content array
701
+ for (let i = 0; i < blocks.content.length; i++) {
702
+ const block = blocks.content[i];
703
+ if (!block.type) {
704
+ return {
705
+ error: `Error: Block at index ${i} is missing required 'type' property`
706
+ };
707
+ }
708
+ if (typeof block.type !== 'string') {
709
+ return {
710
+ error: `Error: Block type at index ${i} must be a string, but received: ${typeof block.type}`
711
+ };
712
+ }
713
+ // Validate block attributes if present
714
+ if (block.attrs && typeof block.attrs !== 'object') {
715
+ return {
716
+ error: `Error: Block attributes at index ${i} must be an object, but received: ${typeof block.attrs}`
717
+ };
718
+ }
719
+ // Validate nested content if present
720
+ if (block.content) {
721
+ if (!Array.isArray(block.content)) {
722
+ return {
723
+ error: `Error: Block content at index ${i} must be an array, but received: ${typeof block.content}`
724
+ };
725
+ }
726
+ // Recursively validate nested blocks
727
+ const nestedValidation = isValidBlocks({
728
+ type: 'doc',
729
+ content: block.content
730
+ });
731
+ if (nestedValidation.error) {
732
+ return {
733
+ error: `Error in nested block at index ${i}: ${nestedValidation.error}`
734
+ };
735
+ }
736
+ }
737
+ }
738
+ return {
739
+ error: null
740
+ };
741
+ };
447
742
 
448
743
  /**
449
744
  * Enum representing the different types of blocks available in the Block Editor
@@ -739,6 +1034,89 @@ const EMPTY_CONTAINER_STYLE_ANGULAR = {
739
1034
  */
740
1035
  const CUSTOM_NO_COMPONENT = 'CustomNoComponent';
741
1036
 
1037
+ /**
1038
+ * Gets the current state of the Universal Visual Editor (UVE).
1039
+ *
1040
+ * This function checks if the code is running inside the DotCMS Universal Visual Editor
1041
+ * and returns information about its current state, including the editor mode.
1042
+ *
1043
+ * @export
1044
+ * @return {UVEState | undefined} Returns the UVE state object if running inside the editor,
1045
+ * undefined otherwise.
1046
+ *
1047
+ * The state includes:
1048
+ * - mode: The current editor mode (preview, edit, live)
1049
+ * - languageId: The language ID of the current page setted on the UVE
1050
+ * - persona: The persona of the current page setted on the UVE
1051
+ * - variantName: The name of the current variant
1052
+ * - experimentId: The ID of the current experiment
1053
+ * - publishDate: The publish date of the current page setted on the UVE
1054
+ *
1055
+ * @note The absence of any of these properties means that the value is the default one.
1056
+ *
1057
+ * @example
1058
+ * ```ts
1059
+ * const editorState = getUVEState();
1060
+ * if (editorState?.mode === 'edit') {
1061
+ * // Enable editing features
1062
+ * }
1063
+ * ```
1064
+ */
1065
+ function getUVEState() {
1066
+ if (typeof window === 'undefined' || window.parent === window || !window.location) {
1067
+ return undefined;
1068
+ }
1069
+ const url = new URL(window.location.href);
1070
+ const possibleModes = Object.values(types.UVE_MODE);
1071
+ let mode = url.searchParams.get('mode') ?? types.UVE_MODE.EDIT;
1072
+ const languageId = url.searchParams.get('language_id');
1073
+ const persona = url.searchParams.get('personaId');
1074
+ const variantName = url.searchParams.get('variantName');
1075
+ const experimentId = url.searchParams.get('experimentId');
1076
+ const publishDate = url.searchParams.get('publishDate');
1077
+ if (!possibleModes.includes(mode)) {
1078
+ mode = types.UVE_MODE.EDIT;
1079
+ }
1080
+ return {
1081
+ mode,
1082
+ languageId,
1083
+ persona,
1084
+ variantName,
1085
+ experimentId,
1086
+ publishDate
1087
+ };
1088
+ }
1089
+ /**
1090
+ * Creates a subscription to a UVE event.
1091
+ *
1092
+ * @param eventType - The type of event to subscribe to
1093
+ * @param callback - The callback function that will be called when the event occurs
1094
+ * @returns An event subscription that can be used to unsubscribe
1095
+ *
1096
+ * @example
1097
+ * ```ts
1098
+ * // Subscribe to page changes
1099
+ * const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
1100
+ * console.log('Content changes:', changes);
1101
+ * });
1102
+ *
1103
+ * // Later, unsubscribe when no longer needed
1104
+ * subscription.unsubscribe();
1105
+ * ```
1106
+ */
1107
+ function createUVESubscription(eventType, callback) {
1108
+ if (!getUVEState()) {
1109
+ console.warn('UVE Subscription: Not running inside UVE');
1110
+ return __UVE_EVENT_ERROR_FALLBACK__(eventType);
1111
+ }
1112
+ const eventCallback = __UVE_EVENTS__[eventType];
1113
+ if (!eventCallback) {
1114
+ console.error(`UVE Subscription: Event ${eventType} not found`);
1115
+ return __UVE_EVENT_ERROR_FALLBACK__(eventType);
1116
+ }
1117
+ return eventCallback(callback);
1118
+ }
1119
+
742
1120
  exports.CUSTOM_NO_COMPONENT = CUSTOM_NO_COMPONENT;
743
1121
  exports.DEVELOPMENT_MODE = DEVELOPMENT_MODE;
744
1122
  exports.EMPTY_CONTAINER_STYLE_ANGULAR = EMPTY_CONTAINER_STYLE_ANGULAR;
@@ -750,6 +1128,7 @@ exports.__UVE_EVENTS__ = __UVE_EVENTS__;
750
1128
  exports.__UVE_EVENT_ERROR_FALLBACK__ = __UVE_EVENT_ERROR_FALLBACK__;
751
1129
  exports.combineClasses = combineClasses;
752
1130
  exports.computeScrollIsInBottom = computeScrollIsInBottom;
1131
+ exports.createUVESubscription = createUVESubscription;
753
1132
  exports.editContentlet = editContentlet;
754
1133
  exports.findDotCMSElement = findDotCMSElement;
755
1134
  exports.findDotCMSVTLData = findDotCMSVTLData;
@@ -762,6 +1141,10 @@ exports.getDotCMSContentletsBound = getDotCMSContentletsBound;
762
1141
  exports.getDotCMSPageBounds = getDotCMSPageBounds;
763
1142
  exports.getDotContainerAttributes = getDotContainerAttributes;
764
1143
  exports.getDotContentletAttributes = getDotContentletAttributes;
1144
+ exports.getUVEState = getUVEState;
765
1145
  exports.initInlineEditing = initInlineEditing;
1146
+ exports.initUVE = initUVE;
1147
+ exports.isValidBlocks = isValidBlocks;
766
1148
  exports.reorderMenu = reorderMenu;
767
1149
  exports.sendMessageToUVE = sendMessageToUVE;
1150
+ exports.setBounds = setBounds;
package/index.esm.js CHANGED
@@ -1,88 +1,2 @@
1
- import { _ as __UVE_EVENT_ERROR_FALLBACK__, a as __UVE_EVENTS__ } from './constants.esm.js';
2
- export { e as editContentlet, i as initInlineEditing, r as reorderMenu, s as sendMessageToUVE } from './constants.esm.js';
3
- import { UVE_MODE } from './types.esm.js';
4
-
5
- /**
6
- * Gets the current state of the Universal Visual Editor (UVE).
7
- *
8
- * This function checks if the code is running inside the DotCMS Universal Visual Editor
9
- * and returns information about its current state, including the editor mode.
10
- *
11
- * @export
12
- * @return {UVEState | undefined} Returns the UVE state object if running inside the editor,
13
- * undefined otherwise.
14
- *
15
- * The state includes:
16
- * - mode: The current editor mode (preview, edit, live)
17
- * - languageId: The language ID of the current page setted on the UVE
18
- * - persona: The persona of the current page setted on the UVE
19
- * - variantName: The name of the current variant
20
- * - experimentId: The ID of the current experiment
21
- * - publishDate: The publish date of the current page setted on the UVE
22
- *
23
- * @note The absence of any of these properties means that the value is the default one.
24
- *
25
- * @example
26
- * ```ts
27
- * const editorState = getUVEState();
28
- * if (editorState?.mode === 'edit') {
29
- * // Enable editing features
30
- * }
31
- * ```
32
- */
33
- function getUVEState() {
34
- if (typeof window === 'undefined' || window.parent === window || !window.location) {
35
- return;
36
- }
37
- const url = new URL(window.location.href);
38
- const possibleModes = Object.values(UVE_MODE);
39
- let mode = url.searchParams.get('mode') ?? UVE_MODE.EDIT;
40
- const languageId = url.searchParams.get('language_id');
41
- const persona = url.searchParams.get('personaId');
42
- const variantName = url.searchParams.get('variantName');
43
- const experimentId = url.searchParams.get('experimentId');
44
- const publishDate = url.searchParams.get('publishDate');
45
- if (!possibleModes.includes(mode)) {
46
- mode = UVE_MODE.EDIT;
47
- }
48
- return {
49
- mode,
50
- languageId,
51
- persona,
52
- variantName,
53
- experimentId,
54
- publishDate
55
- };
56
- }
57
- /**
58
- * Creates a subscription to a UVE event.
59
- *
60
- * @param eventType - The type of event to subscribe to
61
- * @param callback - The callback function that will be called when the event occurs
62
- * @returns An event subscription that can be used to unsubscribe
63
- *
64
- * @example
65
- * ```ts
66
- * // Subscribe to page changes
67
- * const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
68
- * console.log('Content changes:', changes);
69
- * });
70
- *
71
- * // Later, unsubscribe when no longer needed
72
- * subscription.unsubscribe();
73
- * ```
74
- */
75
- function createUVESubscription(eventType, callback) {
76
- if (!getUVEState()) {
77
- console.warn('UVE Subscription: Not running inside UVE');
78
- return __UVE_EVENT_ERROR_FALLBACK__(eventType);
79
- }
80
- const eventCallback = __UVE_EVENTS__[eventType];
81
- if (!eventCallback) {
82
- console.error(`UVE Subscription: Event ${eventType} not found`);
83
- return __UVE_EVENT_ERROR_FALLBACK__(eventType);
84
- }
85
- return eventCallback(callback);
86
- }
87
-
88
- export { createUVESubscription, getUVEState };
1
+ export { u as createUVESubscription, w as editContentlet, t as getUVEState, y as initInlineEditing, z as initUVE, x as reorderMenu, v as sendMessageToUVE } from './index.esm2.js';
2
+ import './types.esm.js';
@@ -1,4 +1,4 @@
1
- import { DotCMSUVEAction, UVEEventType } from './types.esm.js';
1
+ import { UVEEventType, DotCMSUVEAction, UVE_MODE } from './types.esm.js';
2
2
 
3
3
  /**
4
4
  * Actions received from the dotcms editor
@@ -370,6 +370,161 @@ function getDotContainerAttributes({
370
370
  };
371
371
  }
372
372
 
373
+ /* eslint-disable @typescript-eslint/no-explicit-any */
374
+ /**
375
+ * Sets up scroll event handlers for the window to notify the editor about scroll events.
376
+ * Adds listeners for both 'scroll' and 'scrollend' events, sending appropriate messages
377
+ * to the editor when these events occur.
378
+ */
379
+ function scrollHandler() {
380
+ const scrollCallback = () => {
381
+ sendMessageToUVE({
382
+ action: DotCMSUVEAction.IFRAME_SCROLL
383
+ });
384
+ };
385
+ const scrollEndCallback = () => {
386
+ sendMessageToUVE({
387
+ action: DotCMSUVEAction.IFRAME_SCROLL_END
388
+ });
389
+ };
390
+ window.addEventListener('scroll', scrollCallback);
391
+ window.addEventListener('scrollend', scrollEndCallback);
392
+ return {
393
+ destroyScrollHandler: () => {
394
+ window.removeEventListener('scroll', scrollCallback);
395
+ window.removeEventListener('scrollend', scrollEndCallback);
396
+ }
397
+ };
398
+ }
399
+ /**
400
+ * Adds 'empty-contentlet' class to contentlet elements that have no height.
401
+ * This helps identify and style empty contentlets in the editor view.
402
+ *
403
+ * @remarks
404
+ * The function queries all elements with data-dot-object="contentlet" attribute
405
+ * and checks their clientHeight. If an element has no height (clientHeight = 0),
406
+ * it adds the 'empty-contentlet' class to that element.
407
+ */
408
+ function addClassToEmptyContentlets() {
409
+ const contentlets = document.querySelectorAll('[data-dot-object="contentlet"]');
410
+ contentlets.forEach(contentlet => {
411
+ if (contentlet.clientHeight) {
412
+ return;
413
+ }
414
+ contentlet.classList.add('empty-contentlet');
415
+ });
416
+ }
417
+ /**
418
+ * Registers event handlers for various UVE (Universal Visual Editor) events.
419
+ *
420
+ * This function sets up subscriptions for:
421
+ * - Page reload events that refresh the window
422
+ * - Bounds request events to update editor boundaries
423
+ * - Iframe scroll events to handle smooth scrolling within bounds
424
+ * - Contentlet hover events to notify the editor
425
+ *
426
+ * @remarks
427
+ * For scroll events, the function includes logic to prevent scrolling beyond
428
+ * the top or bottom boundaries of the iframe, which helps maintain proper
429
+ * scroll event handling.
430
+ */
431
+ function registerUVEEvents() {
432
+ const pageReloadSubscription = createUVESubscription(UVEEventType.PAGE_RELOAD, () => {
433
+ window.location.reload();
434
+ });
435
+ const requestBoundsSubscription = createUVESubscription(UVEEventType.REQUEST_BOUNDS, bounds => {
436
+ setBounds(bounds);
437
+ });
438
+ const iframeScrollSubscription = createUVESubscription(UVEEventType.IFRAME_SCROLL, direction => {
439
+ if (window.scrollY === 0 && direction === 'up' || computeScrollIsInBottom() && direction === 'down') {
440
+ // If the iframe scroll is at the top or bottom, do not send anything.
441
+ // This avoids losing the scrollend event.
442
+ return;
443
+ }
444
+ const scrollY = direction === 'up' ? -120 : 120;
445
+ window.scrollBy({
446
+ left: 0,
447
+ top: scrollY,
448
+ behavior: 'smooth'
449
+ });
450
+ });
451
+ const contentletHoveredSubscription = createUVESubscription(UVEEventType.CONTENTLET_HOVERED, contentletHovered => {
452
+ sendMessageToUVE({
453
+ action: DotCMSUVEAction.SET_CONTENTLET,
454
+ payload: contentletHovered
455
+ });
456
+ });
457
+ return {
458
+ subscriptions: [pageReloadSubscription, requestBoundsSubscription, iframeScrollSubscription, contentletHoveredSubscription]
459
+ };
460
+ }
461
+ /**
462
+ * Notifies the editor that the UVE client is ready to receive messages.
463
+ *
464
+ * This function sends a message to the editor indicating that the client-side
465
+ * initialization is complete and it's ready to handle editor interactions.
466
+ *
467
+ * @remarks
468
+ * This is typically called after all UVE event handlers and DOM listeners
469
+ * have been set up successfully.
470
+ */
471
+ function setClientIsReady() {
472
+ sendMessageToUVE({
473
+ action: DotCMSUVEAction.CLIENT_READY
474
+ });
475
+ }
476
+ /**
477
+ * Listen for block editor inline event.
478
+ */
479
+ function listenBlockEditorInlineEvent() {
480
+ if (document.readyState === 'complete') {
481
+ // The page is fully loaded or interactive
482
+ listenBlockEditorClick();
483
+ return {
484
+ destroyListenBlockEditorInlineEvent: () => {
485
+ window.removeEventListener('load', () => listenBlockEditorClick());
486
+ }
487
+ };
488
+ }
489
+ window.addEventListener('load', () => listenBlockEditorClick());
490
+ return {
491
+ destroyListenBlockEditorInlineEvent: () => {
492
+ window.removeEventListener('load', () => listenBlockEditorClick());
493
+ }
494
+ };
495
+ }
496
+ const listenBlockEditorClick = () => {
497
+ const editBlockEditorNodes = document.querySelectorAll('[data-block-editor-content]');
498
+ if (!editBlockEditorNodes.length) {
499
+ return;
500
+ }
501
+ editBlockEditorNodes.forEach(node => {
502
+ const {
503
+ inode,
504
+ language = '1',
505
+ contentType,
506
+ fieldName,
507
+ blockEditorContent
508
+ } = node.dataset;
509
+ const content = JSON.parse(blockEditorContent || '');
510
+ if (!inode || !language || !contentType || !fieldName) {
511
+ console.error('Missing data attributes for block editor inline editing.');
512
+ console.warn('inode, language, contentType and fieldName are required.');
513
+ return;
514
+ }
515
+ node.classList.add('dotcms__inline-edit-field');
516
+ node.addEventListener('click', () => {
517
+ initInlineEditing('BLOCK_EDITOR', {
518
+ inode,
519
+ content,
520
+ language: parseInt(language),
521
+ fieldName,
522
+ contentType
523
+ });
524
+ });
525
+ });
526
+ };
527
+
373
528
  /**
374
529
  * Post message to dotcms page editor
375
530
  *
@@ -442,6 +597,146 @@ function initInlineEditing(type, data) {
442
597
  }
443
598
  });
444
599
  }
600
+ /**
601
+ * Initializes the Universal Visual Editor (UVE) with required handlers and event listeners.
602
+ *
603
+ * This function sets up:
604
+ * - Scroll handling
605
+ * - Empty contentlet styling
606
+ * - Block editor inline event listening
607
+ * - Client ready state
608
+ * - UVE event subscriptions
609
+ *
610
+ * @returns {Object} An object containing the cleanup function
611
+ * @returns {Function} destroyUVESubscriptions - Function to clean up all UVE event subscriptions
612
+ *
613
+ * @example
614
+ * ```typescript
615
+ * const { destroyUVESubscriptions } = initUVE();
616
+ *
617
+ * // When done with UVE
618
+ * destroyUVESubscriptions();
619
+ * ```
620
+ */
621
+ function initUVE() {
622
+ addClassToEmptyContentlets();
623
+ setClientIsReady();
624
+ const {
625
+ subscriptions
626
+ } = registerUVEEvents();
627
+ const {
628
+ destroyScrollHandler
629
+ } = scrollHandler();
630
+ const {
631
+ destroyListenBlockEditorInlineEvent
632
+ } = listenBlockEditorInlineEvent();
633
+ return {
634
+ destroyUVESubscriptions: () => {
635
+ subscriptions.forEach(subscription => subscription.unsubscribe());
636
+ destroyScrollHandler();
637
+ destroyListenBlockEditorInlineEvent();
638
+ }
639
+ };
640
+ }
641
+
642
+ /**
643
+ * Sets the bounds of the containers in the editor.
644
+ * Retrieves the containers from the DOM and sends their position data to the editor.
645
+ * @private
646
+ * @memberof DotCMSPageEditor
647
+ */
648
+ function setBounds(bounds) {
649
+ sendMessageToUVE({
650
+ action: DotCMSUVEAction.SET_BOUNDS,
651
+ payload: bounds
652
+ });
653
+ }
654
+ /**
655
+ * Validates the structure of a Block Editor block.
656
+ *
657
+ * This function checks that:
658
+ * 1. The blocks parameter is a valid object
659
+ * 2. The block has a 'doc' type
660
+ * 3. The block has a valid content array that is not empty
661
+ *
662
+ * @param {Block} blocks - The blocks structure to validate
663
+ * @returns {BlockEditorState} Object containing validation state and any error message
664
+ * @property {boolean} BlockEditorState.isValid - Whether the blocks structure is valid
665
+ * @property {string | null} BlockEditorState.error - Error message if invalid, null if valid
666
+ */
667
+ const isValidBlocks = blocks => {
668
+ if (!blocks) {
669
+ return {
670
+ error: `Error: Blocks object is not defined`
671
+ };
672
+ }
673
+ if (typeof blocks !== 'object') {
674
+ return {
675
+ error: `Error: Blocks must be an object, but received: ${typeof blocks}`
676
+ };
677
+ }
678
+ if (blocks.type !== 'doc') {
679
+ return {
680
+ error: `Error: Invalid block type. Expected 'doc' but received: '${blocks.type}'`
681
+ };
682
+ }
683
+ if (!blocks.content) {
684
+ return {
685
+ error: 'Error: Blocks content is missing'
686
+ };
687
+ }
688
+ if (!Array.isArray(blocks.content)) {
689
+ return {
690
+ error: `Error: Blocks content must be an array, but received: ${typeof blocks.content}`
691
+ };
692
+ }
693
+ if (blocks.content.length === 0) {
694
+ return {
695
+ error: 'Error: Blocks content is empty. At least one block is required.'
696
+ };
697
+ }
698
+ // Validate each block in the content array
699
+ for (let i = 0; i < blocks.content.length; i++) {
700
+ const block = blocks.content[i];
701
+ if (!block.type) {
702
+ return {
703
+ error: `Error: Block at index ${i} is missing required 'type' property`
704
+ };
705
+ }
706
+ if (typeof block.type !== 'string') {
707
+ return {
708
+ error: `Error: Block type at index ${i} must be a string, but received: ${typeof block.type}`
709
+ };
710
+ }
711
+ // Validate block attributes if present
712
+ if (block.attrs && typeof block.attrs !== 'object') {
713
+ return {
714
+ error: `Error: Block attributes at index ${i} must be an object, but received: ${typeof block.attrs}`
715
+ };
716
+ }
717
+ // Validate nested content if present
718
+ if (block.content) {
719
+ if (!Array.isArray(block.content)) {
720
+ return {
721
+ error: `Error: Block content at index ${i} must be an array, but received: ${typeof block.content}`
722
+ };
723
+ }
724
+ // Recursively validate nested blocks
725
+ const nestedValidation = isValidBlocks({
726
+ type: 'doc',
727
+ content: block.content
728
+ });
729
+ if (nestedValidation.error) {
730
+ return {
731
+ error: `Error in nested block at index ${i}: ${nestedValidation.error}`
732
+ };
733
+ }
734
+ }
735
+ }
736
+ return {
737
+ error: null
738
+ };
739
+ };
445
740
 
446
741
  /**
447
742
  * Enum representing the different types of blocks available in the Block Editor
@@ -737,4 +1032,87 @@ const EMPTY_CONTAINER_STYLE_ANGULAR = {
737
1032
  */
738
1033
  const CUSTOM_NO_COMPONENT = 'CustomNoComponent';
739
1034
 
740
- export { Blocks as B, CUSTOM_NO_COMPONENT as C, DEVELOPMENT_MODE as D, END_CLASS as E, PRODUCTION_MODE as P, START_CLASS as S, __UVE_EVENT_ERROR_FALLBACK__ as _, __UVE_EVENTS__ as a, EMPTY_CONTAINER_STYLE_REACT as b, EMPTY_CONTAINER_STYLE_ANGULAR as c, __DOTCMS_UVE_EVENT__ as d, editContentlet as e, getDotCMSContentletsBound as f, getDotCMSPageBounds as g, getDotCMSContainerData as h, initInlineEditing as i, getClosestDotCMSContainerData as j, findDotCMSElement as k, findDotCMSVTLData as l, computeScrollIsInBottom as m, combineClasses as n, getColumnPositionClasses as o, getDotContentletAttributes as p, getContainersData as q, reorderMenu as r, sendMessageToUVE as s, getContentletsInContainer as t, getDotContainerAttributes as u };
1035
+ /**
1036
+ * Gets the current state of the Universal Visual Editor (UVE).
1037
+ *
1038
+ * This function checks if the code is running inside the DotCMS Universal Visual Editor
1039
+ * and returns information about its current state, including the editor mode.
1040
+ *
1041
+ * @export
1042
+ * @return {UVEState | undefined} Returns the UVE state object if running inside the editor,
1043
+ * undefined otherwise.
1044
+ *
1045
+ * The state includes:
1046
+ * - mode: The current editor mode (preview, edit, live)
1047
+ * - languageId: The language ID of the current page setted on the UVE
1048
+ * - persona: The persona of the current page setted on the UVE
1049
+ * - variantName: The name of the current variant
1050
+ * - experimentId: The ID of the current experiment
1051
+ * - publishDate: The publish date of the current page setted on the UVE
1052
+ *
1053
+ * @note The absence of any of these properties means that the value is the default one.
1054
+ *
1055
+ * @example
1056
+ * ```ts
1057
+ * const editorState = getUVEState();
1058
+ * if (editorState?.mode === 'edit') {
1059
+ * // Enable editing features
1060
+ * }
1061
+ * ```
1062
+ */
1063
+ function getUVEState() {
1064
+ if (typeof window === 'undefined' || window.parent === window || !window.location) {
1065
+ return undefined;
1066
+ }
1067
+ const url = new URL(window.location.href);
1068
+ const possibleModes = Object.values(UVE_MODE);
1069
+ let mode = url.searchParams.get('mode') ?? UVE_MODE.EDIT;
1070
+ const languageId = url.searchParams.get('language_id');
1071
+ const persona = url.searchParams.get('personaId');
1072
+ const variantName = url.searchParams.get('variantName');
1073
+ const experimentId = url.searchParams.get('experimentId');
1074
+ const publishDate = url.searchParams.get('publishDate');
1075
+ if (!possibleModes.includes(mode)) {
1076
+ mode = UVE_MODE.EDIT;
1077
+ }
1078
+ return {
1079
+ mode,
1080
+ languageId,
1081
+ persona,
1082
+ variantName,
1083
+ experimentId,
1084
+ publishDate
1085
+ };
1086
+ }
1087
+ /**
1088
+ * Creates a subscription to a UVE event.
1089
+ *
1090
+ * @param eventType - The type of event to subscribe to
1091
+ * @param callback - The callback function that will be called when the event occurs
1092
+ * @returns An event subscription that can be used to unsubscribe
1093
+ *
1094
+ * @example
1095
+ * ```ts
1096
+ * // Subscribe to page changes
1097
+ * const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
1098
+ * console.log('Content changes:', changes);
1099
+ * });
1100
+ *
1101
+ * // Later, unsubscribe when no longer needed
1102
+ * subscription.unsubscribe();
1103
+ * ```
1104
+ */
1105
+ function createUVESubscription(eventType, callback) {
1106
+ if (!getUVEState()) {
1107
+ console.warn('UVE Subscription: Not running inside UVE');
1108
+ return __UVE_EVENT_ERROR_FALLBACK__(eventType);
1109
+ }
1110
+ const eventCallback = __UVE_EVENTS__[eventType];
1111
+ if (!eventCallback) {
1112
+ console.error(`UVE Subscription: Event ${eventType} not found`);
1113
+ return __UVE_EVENT_ERROR_FALLBACK__(eventType);
1114
+ }
1115
+ return eventCallback(callback);
1116
+ }
1117
+
1118
+ export { Blocks as B, CUSTOM_NO_COMPONENT as C, DEVELOPMENT_MODE as D, END_CLASS as E, PRODUCTION_MODE as P, START_CLASS as S, __UVE_EVENTS__ as _, __UVE_EVENT_ERROR_FALLBACK__ as a, EMPTY_CONTAINER_STYLE_REACT as b, EMPTY_CONTAINER_STYLE_ANGULAR as c, __DOTCMS_UVE_EVENT__ as d, getDotCMSContentletsBound as e, getDotCMSContainerData as f, getDotCMSPageBounds as g, getClosestDotCMSContainerData as h, findDotCMSElement as i, findDotCMSVTLData as j, computeScrollIsInBottom as k, combineClasses as l, getColumnPositionClasses as m, getDotContentletAttributes as n, getContainersData as o, getContentletsInContainer as p, getDotContainerAttributes as q, isValidBlocks as r, setBounds as s, getUVEState as t, createUVESubscription as u, sendMessageToUVE as v, editContentlet as w, reorderMenu as x, initInlineEditing as y, initUVE as z };
package/internal.cjs.js CHANGED
@@ -1,136 +1,39 @@
1
1
  'use strict';
2
2
 
3
- var constants = require('./constants.cjs.js');
4
- var types = require('./types.cjs.js');
3
+ var internal = require('./index.cjs2.js');
4
+ require('./types.cjs.js');
5
+
5
6
 
6
- /**
7
- * Sets the bounds of the containers in the editor.
8
- * Retrieves the containers from the DOM and sends their position data to the editor.
9
- * @private
10
- * @memberof DotCMSPageEditor
11
- */
12
- function setBounds(bounds) {
13
- constants.sendMessageToUVE({
14
- action: types.DotCMSUVEAction.SET_BOUNDS,
15
- payload: bounds
16
- });
17
- }
18
- /**
19
- * Validates the structure of a Block Editor block.
20
- *
21
- * This function checks that:
22
- * 1. The blocks parameter is a valid object
23
- * 2. The block has a 'doc' type
24
- * 3. The block has a valid content array that is not empty
25
- *
26
- * @param {Block} blocks - The blocks structure to validate
27
- * @returns {BlockEditorState} Object containing validation state and any error message
28
- * @property {boolean} BlockEditorState.isValid - Whether the blocks structure is valid
29
- * @property {string | null} BlockEditorState.error - Error message if invalid, null if valid
30
- */
31
- const isValidBlocks = blocks => {
32
- if (!blocks) {
33
- return {
34
- error: `Error: Blocks object is not defined`
35
- };
36
- }
37
- if (typeof blocks !== 'object') {
38
- return {
39
- error: `Error: Blocks must be an object, but received: ${typeof blocks}`
40
- };
41
- }
42
- if (blocks.type !== 'doc') {
43
- return {
44
- error: `Error: Invalid block type. Expected 'doc' but received: '${blocks.type}'`
45
- };
46
- }
47
- if (!blocks.content) {
48
- return {
49
- error: 'Error: Blocks content is missing'
50
- };
51
- }
52
- if (!Array.isArray(blocks.content)) {
53
- return {
54
- error: `Error: Blocks content must be an array, but received: ${typeof blocks.content}`
55
- };
56
- }
57
- if (blocks.content.length === 0) {
58
- return {
59
- error: 'Error: Blocks content is empty. At least one block is required.'
60
- };
61
- }
62
- // Validate each block in the content array
63
- for (let i = 0; i < blocks.content.length; i++) {
64
- const block = blocks.content[i];
65
- if (!block.type) {
66
- return {
67
- error: `Error: Block at index ${i} is missing required 'type' property`
68
- };
69
- }
70
- if (typeof block.type !== 'string') {
71
- return {
72
- error: `Error: Block type at index ${i} must be a string, but received: ${typeof block.type}`
73
- };
74
- }
75
- // Validate block attributes if present
76
- if (block.attrs && typeof block.attrs !== 'object') {
77
- return {
78
- error: `Error: Block attributes at index ${i} must be an object, but received: ${typeof block.attrs}`
79
- };
80
- }
81
- // Validate nested content if present
82
- if (block.content) {
83
- if (!Array.isArray(block.content)) {
84
- return {
85
- error: `Error: Block content at index ${i} must be an array, but received: ${typeof block.content}`
86
- };
87
- }
88
- // Recursively validate nested blocks
89
- const nestedValidation = isValidBlocks({
90
- type: 'doc',
91
- content: block.content
92
- });
93
- if (nestedValidation.error) {
94
- return {
95
- error: `Error in nested block at index ${i}: ${nestedValidation.error}`
96
- };
97
- }
98
- }
99
- }
100
- return {
101
- error: null
102
- };
103
- };
104
7
 
105
8
  Object.defineProperty(exports, "Blocks", {
106
- enumerable: true,
107
- get: function () { return constants.Blocks; }
9
+ enumerable: true,
10
+ get: function () { return internal.Blocks; }
108
11
  });
109
- exports.CUSTOM_NO_COMPONENT = constants.CUSTOM_NO_COMPONENT;
110
- exports.DEVELOPMENT_MODE = constants.DEVELOPMENT_MODE;
111
- exports.EMPTY_CONTAINER_STYLE_ANGULAR = constants.EMPTY_CONTAINER_STYLE_ANGULAR;
112
- exports.EMPTY_CONTAINER_STYLE_REACT = constants.EMPTY_CONTAINER_STYLE_REACT;
113
- exports.END_CLASS = constants.END_CLASS;
114
- exports.PRODUCTION_MODE = constants.PRODUCTION_MODE;
115
- exports.START_CLASS = constants.START_CLASS;
12
+ exports.CUSTOM_NO_COMPONENT = internal.CUSTOM_NO_COMPONENT;
13
+ exports.DEVELOPMENT_MODE = internal.DEVELOPMENT_MODE;
14
+ exports.EMPTY_CONTAINER_STYLE_ANGULAR = internal.EMPTY_CONTAINER_STYLE_ANGULAR;
15
+ exports.EMPTY_CONTAINER_STYLE_REACT = internal.EMPTY_CONTAINER_STYLE_REACT;
16
+ exports.END_CLASS = internal.END_CLASS;
17
+ exports.PRODUCTION_MODE = internal.PRODUCTION_MODE;
18
+ exports.START_CLASS = internal.START_CLASS;
116
19
  Object.defineProperty(exports, "__DOTCMS_UVE_EVENT__", {
117
- enumerable: true,
118
- get: function () { return constants.__DOTCMS_UVE_EVENT__; }
20
+ enumerable: true,
21
+ get: function () { return internal.__DOTCMS_UVE_EVENT__; }
119
22
  });
120
- exports.__UVE_EVENTS__ = constants.__UVE_EVENTS__;
121
- exports.__UVE_EVENT_ERROR_FALLBACK__ = constants.__UVE_EVENT_ERROR_FALLBACK__;
122
- exports.combineClasses = constants.combineClasses;
123
- exports.computeScrollIsInBottom = constants.computeScrollIsInBottom;
124
- exports.findDotCMSElement = constants.findDotCMSElement;
125
- exports.findDotCMSVTLData = constants.findDotCMSVTLData;
126
- exports.getClosestDotCMSContainerData = constants.getClosestDotCMSContainerData;
127
- exports.getColumnPositionClasses = constants.getColumnPositionClasses;
128
- exports.getContainersData = constants.getContainersData;
129
- exports.getContentletsInContainer = constants.getContentletsInContainer;
130
- exports.getDotCMSContainerData = constants.getDotCMSContainerData;
131
- exports.getDotCMSContentletsBound = constants.getDotCMSContentletsBound;
132
- exports.getDotCMSPageBounds = constants.getDotCMSPageBounds;
133
- exports.getDotContainerAttributes = constants.getDotContainerAttributes;
134
- exports.getDotContentletAttributes = constants.getDotContentletAttributes;
135
- exports.isValidBlocks = isValidBlocks;
136
- exports.setBounds = setBounds;
23
+ exports.__UVE_EVENTS__ = internal.__UVE_EVENTS__;
24
+ exports.__UVE_EVENT_ERROR_FALLBACK__ = internal.__UVE_EVENT_ERROR_FALLBACK__;
25
+ exports.combineClasses = internal.combineClasses;
26
+ exports.computeScrollIsInBottom = internal.computeScrollIsInBottom;
27
+ exports.findDotCMSElement = internal.findDotCMSElement;
28
+ exports.findDotCMSVTLData = internal.findDotCMSVTLData;
29
+ exports.getClosestDotCMSContainerData = internal.getClosestDotCMSContainerData;
30
+ exports.getColumnPositionClasses = internal.getColumnPositionClasses;
31
+ exports.getContainersData = internal.getContainersData;
32
+ exports.getContentletsInContainer = internal.getContentletsInContainer;
33
+ exports.getDotCMSContainerData = internal.getDotCMSContainerData;
34
+ exports.getDotCMSContentletsBound = internal.getDotCMSContentletsBound;
35
+ exports.getDotCMSPageBounds = internal.getDotCMSPageBounds;
36
+ exports.getDotContainerAttributes = internal.getDotContainerAttributes;
37
+ exports.getDotContentletAttributes = internal.getDotContentletAttributes;
38
+ exports.isValidBlocks = internal.isValidBlocks;
39
+ exports.setBounds = internal.setBounds;
package/internal.esm.js CHANGED
@@ -1,104 +1,2 @@
1
- import { s as sendMessageToUVE } from './constants.esm.js';
2
- export { B as Blocks, C as CUSTOM_NO_COMPONENT, D as DEVELOPMENT_MODE, c as EMPTY_CONTAINER_STYLE_ANGULAR, b as EMPTY_CONTAINER_STYLE_REACT, E as END_CLASS, P as PRODUCTION_MODE, S as START_CLASS, d as __DOTCMS_UVE_EVENT__, a as __UVE_EVENTS__, _ as __UVE_EVENT_ERROR_FALLBACK__, n as combineClasses, m as computeScrollIsInBottom, k as findDotCMSElement, l as findDotCMSVTLData, j as getClosestDotCMSContainerData, o as getColumnPositionClasses, q as getContainersData, t as getContentletsInContainer, h as getDotCMSContainerData, f as getDotCMSContentletsBound, g as getDotCMSPageBounds, u as getDotContainerAttributes, p as getDotContentletAttributes } from './constants.esm.js';
3
- import { DotCMSUVEAction } from './types.esm.js';
4
-
5
- /**
6
- * Sets the bounds of the containers in the editor.
7
- * Retrieves the containers from the DOM and sends their position data to the editor.
8
- * @private
9
- * @memberof DotCMSPageEditor
10
- */
11
- function setBounds(bounds) {
12
- sendMessageToUVE({
13
- action: DotCMSUVEAction.SET_BOUNDS,
14
- payload: bounds
15
- });
16
- }
17
- /**
18
- * Validates the structure of a Block Editor block.
19
- *
20
- * This function checks that:
21
- * 1. The blocks parameter is a valid object
22
- * 2. The block has a 'doc' type
23
- * 3. The block has a valid content array that is not empty
24
- *
25
- * @param {Block} blocks - The blocks structure to validate
26
- * @returns {BlockEditorState} Object containing validation state and any error message
27
- * @property {boolean} BlockEditorState.isValid - Whether the blocks structure is valid
28
- * @property {string | null} BlockEditorState.error - Error message if invalid, null if valid
29
- */
30
- const isValidBlocks = blocks => {
31
- if (!blocks) {
32
- return {
33
- error: `Error: Blocks object is not defined`
34
- };
35
- }
36
- if (typeof blocks !== 'object') {
37
- return {
38
- error: `Error: Blocks must be an object, but received: ${typeof blocks}`
39
- };
40
- }
41
- if (blocks.type !== 'doc') {
42
- return {
43
- error: `Error: Invalid block type. Expected 'doc' but received: '${blocks.type}'`
44
- };
45
- }
46
- if (!blocks.content) {
47
- return {
48
- error: 'Error: Blocks content is missing'
49
- };
50
- }
51
- if (!Array.isArray(blocks.content)) {
52
- return {
53
- error: `Error: Blocks content must be an array, but received: ${typeof blocks.content}`
54
- };
55
- }
56
- if (blocks.content.length === 0) {
57
- return {
58
- error: 'Error: Blocks content is empty. At least one block is required.'
59
- };
60
- }
61
- // Validate each block in the content array
62
- for (let i = 0; i < blocks.content.length; i++) {
63
- const block = blocks.content[i];
64
- if (!block.type) {
65
- return {
66
- error: `Error: Block at index ${i} is missing required 'type' property`
67
- };
68
- }
69
- if (typeof block.type !== 'string') {
70
- return {
71
- error: `Error: Block type at index ${i} must be a string, but received: ${typeof block.type}`
72
- };
73
- }
74
- // Validate block attributes if present
75
- if (block.attrs && typeof block.attrs !== 'object') {
76
- return {
77
- error: `Error: Block attributes at index ${i} must be an object, but received: ${typeof block.attrs}`
78
- };
79
- }
80
- // Validate nested content if present
81
- if (block.content) {
82
- if (!Array.isArray(block.content)) {
83
- return {
84
- error: `Error: Block content at index ${i} must be an array, but received: ${typeof block.content}`
85
- };
86
- }
87
- // Recursively validate nested blocks
88
- const nestedValidation = isValidBlocks({
89
- type: 'doc',
90
- content: block.content
91
- });
92
- if (nestedValidation.error) {
93
- return {
94
- error: `Error in nested block at index ${i}: ${nestedValidation.error}`
95
- };
96
- }
97
- }
98
- }
99
- return {
100
- error: null
101
- };
102
- };
103
-
104
- export { isValidBlocks, setBounds };
1
+ export { B as Blocks, C as CUSTOM_NO_COMPONENT, D as DEVELOPMENT_MODE, c as EMPTY_CONTAINER_STYLE_ANGULAR, b as EMPTY_CONTAINER_STYLE_REACT, E as END_CLASS, P as PRODUCTION_MODE, S as START_CLASS, d as __DOTCMS_UVE_EVENT__, _ as __UVE_EVENTS__, a as __UVE_EVENT_ERROR_FALLBACK__, l as combineClasses, k as computeScrollIsInBottom, i as findDotCMSElement, j as findDotCMSVTLData, h as getClosestDotCMSContainerData, m as getColumnPositionClasses, o as getContainersData, p as getContentletsInContainer, f as getDotCMSContainerData, e as getDotCMSContentletsBound, g as getDotCMSPageBounds, q as getDotContainerAttributes, n as getDotContentletAttributes, r as isValidBlocks, s as setBounds } from './index.esm2.js';
2
+ import './types.esm.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotcms/uve",
3
- "version": "0.0.1-beta.17",
3
+ "version": "0.0.1-beta.18",
4
4
  "description": "Official JavaScript library for interacting with Universal Visual Editor (UVE)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,3 +36,27 @@ export declare function reorderMenu(config?: DotCMSReorderMenuConfig): void;
36
36
  * ```
37
37
  */
38
38
  export declare function initInlineEditing(type: DotCMSInlineEditingType, data?: DotCMSInlineEditingPayload): void;
39
+ /**
40
+ * Initializes the Universal Visual Editor (UVE) with required handlers and event listeners.
41
+ *
42
+ * This function sets up:
43
+ * - Scroll handling
44
+ * - Empty contentlet styling
45
+ * - Block editor inline event listening
46
+ * - Client ready state
47
+ * - UVE event subscriptions
48
+ *
49
+ * @returns {Object} An object containing the cleanup function
50
+ * @returns {Function} destroyUVESubscriptions - Function to clean up all UVE event subscriptions
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const { destroyUVESubscriptions } = initUVE();
55
+ *
56
+ * // When done with UVE
57
+ * destroyUVESubscriptions();
58
+ * ```
59
+ */
60
+ export declare function initUVE(): {
61
+ destroyUVESubscriptions: () => void;
62
+ };
@@ -3,7 +3,9 @@
3
3
  * Adds listeners for both 'scroll' and 'scrollend' events, sending appropriate messages
4
4
  * to the editor when these events occur.
5
5
  */
6
- export declare function scrollHandler(): void;
6
+ export declare function scrollHandler(): {
7
+ destroyScrollHandler: () => void;
8
+ };
7
9
  /**
8
10
  * Adds 'empty-contentlet' class to contentlet elements that have no height.
9
11
  * This helps identify and style empty contentlets in the editor view.
@@ -28,7 +30,9 @@ export declare function addClassToEmptyContentlets(): void;
28
30
  * the top or bottom boundaries of the iframe, which helps maintain proper
29
31
  * scroll event handling.
30
32
  */
31
- export declare function registerUVEEvents(): void;
33
+ export declare function registerUVEEvents(): {
34
+ subscriptions: import("../lib/types/editor/public").UVEEventSubscription[];
35
+ };
32
36
  /**
33
37
  * Notifies the editor that the UVE client is ready to receive messages.
34
38
  *
@@ -43,4 +47,6 @@ export declare function setClientIsReady(): void;
43
47
  /**
44
48
  * Listen for block editor inline event.
45
49
  */
46
- export declare const listenBlockEditorInlineEvent: () => void;
50
+ export declare function listenBlockEditorInlineEvent(): {
51
+ destroyListenBlockEditorInlineEvent: () => void;
52
+ };