@dotcms/uve 0.0.1-beta.24 → 0.0.1-beta.26

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.
@@ -1,40 +1,5 @@
1
- import { UVEEventType, DotCMSUVEAction, UVE_MODE } from './types.esm.js';
2
-
3
- /**
4
- * Actions received from the dotcms editor
5
- *
6
- * @export
7
- * @enum {number}
8
- */
9
- var __DOTCMS_UVE_EVENT__;
10
- (function (__DOTCMS_UVE_EVENT__) {
11
- /**
12
- * Request to page to reload
13
- */
14
- __DOTCMS_UVE_EVENT__["UVE_RELOAD_PAGE"] = "uve-reload-page";
15
- /**
16
- * Request the bounds for the elements
17
- */
18
- __DOTCMS_UVE_EVENT__["UVE_REQUEST_BOUNDS"] = "uve-request-bounds";
19
- /**
20
- * Received pong from the editor
21
- */
22
- __DOTCMS_UVE_EVENT__["UVE_EDITOR_PONG"] = "uve-editor-pong";
23
- /**
24
- * Received scroll event trigger from the editor
25
- */
26
- __DOTCMS_UVE_EVENT__["UVE_SCROLL_INSIDE_IFRAME"] = "uve-scroll-inside-iframe";
27
- /**
28
- * TODO:
29
- * Set the page data - This is used to catch the "changes" event.
30
- * We must to re-check the name late.
31
- */
32
- __DOTCMS_UVE_EVENT__["UVE_SET_PAGE_DATA"] = "uve-set-page-data";
33
- /**
34
- * Copy contentlet inline editing success
35
- */
36
- __DOTCMS_UVE_EVENT__["UVE_COPY_CONTENTLET_INLINE_EDITING_SUCCESS"] = "uve-copy-contentlet-inline-editing-success";
37
- })(__DOTCMS_UVE_EVENT__ || (__DOTCMS_UVE_EVENT__ = {}));
1
+ import { UVEEventType, UVE_MODE, DotCMSUVEAction } from '@dotcms/types';
2
+ import { __DOTCMS_UVE_EVENT__ } from '@dotcms/types/internal';
38
3
 
39
4
  /**
40
5
  * Calculates the bounding information for each page element within the given containers.
@@ -253,7 +218,7 @@ const getColumnPositionClasses = column => {
253
218
  *
254
219
  *
255
220
  * Helper function that returns an object containing the dotCMS data attributes.
256
- * @param {DotCMSContentlet} contentlet - The contentlet to get the attributes for
221
+ * @param {DotCMSBasicContentlet} contentlet - The contentlet to get the attributes for
257
222
  * @param {string} container - The container to get the attributes for
258
223
  * @returns {DotContentletAttributes} The dotCMS data attributes
259
224
  */
@@ -265,7 +230,7 @@ function getDotContentletAttributes(contentlet, container) {
265
230
  'data-dot-inode': contentlet?.inode,
266
231
  'data-dot-type': contentlet?.contentType,
267
232
  'data-dot-container': container,
268
- 'data-dot-on-number-of-pages': contentlet?.['onNumberOfPages']
233
+ 'data-dot-on-number-of-pages': contentlet?.['onNumberOfPages'] || '1'
269
234
  };
270
235
  }
271
236
  /**
@@ -317,7 +282,7 @@ const getContainersData = (dotCMSPageAsset, columContainer) => {
317
282
  *
318
283
  * @param {DotCMSPageAsset} dotCMSPageAsset - The page asset containing all containers data
319
284
  * @param {DotCMSColumnContainer} columContainer - The container reference from the layout
320
- * @returns {DotCMSContentlet[]} Array of contentlets in the container
285
+ * @returns {DotCMSBasicContentlet[]} Array of contentlets in the container
321
286
  *
322
287
  * @example
323
288
  * const contentlets = getContentletsInContainer(pageAsset, containerRef);
@@ -370,274 +335,345 @@ function getDotContainerAttributes({
370
335
  };
371
336
  }
372
337
 
373
- /* eslint-disable @typescript-eslint/no-explicit-any */
374
338
  /**
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.
339
+ * Subscribes to content changes in the UVE editor
340
+ *
341
+ * @param {UVEEventHandler} callback - Function to be called when content changes are detected
342
+ * @returns {Object} Object containing unsubscribe function and event type
343
+ * @returns {Function} .unsubscribe - Function to remove the event listener
344
+ * @returns {UVEEventType} .event - The event type being subscribed to
345
+ * @internal
378
346
  */
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
- });
347
+ function onContentChanges(callback) {
348
+ const messageCallback = event => {
349
+ if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_SET_PAGE_DATA) {
350
+ callback(event.data.payload);
351
+ }
389
352
  };
390
- window.addEventListener('scroll', scrollCallback);
391
- window.addEventListener('scrollend', scrollEndCallback);
353
+ window.addEventListener('message', messageCallback);
392
354
  return {
393
- destroyScrollHandler: () => {
394
- window.removeEventListener('scroll', scrollCallback);
395
- window.removeEventListener('scrollend', scrollEndCallback);
396
- }
355
+ unsubscribe: () => {
356
+ window.removeEventListener('message', messageCallback);
357
+ },
358
+ event: UVEEventType.CONTENT_CHANGES
397
359
  };
398
360
  }
399
361
  /**
400
- * Adds 'empty-contentlet' class to contentlet elements that have no height.
401
- * This helps identify and style empty contentlets in the editor view.
362
+ * Subscribes to page reload events in the UVE editor
402
363
  *
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.
364
+ * @param {UVEEventHandler} callback - Function to be called when page reload is triggered
365
+ * @returns {Object} Object containing unsubscribe function and event type
366
+ * @returns {Function} .unsubscribe - Function to remove the event listener
367
+ * @returns {UVEEventType} .event - The event type being subscribed to
368
+ * @internal
407
369
  */
408
- function addClassToEmptyContentlets() {
409
- const contentlets = document.querySelectorAll('[data-dot-object="contentlet"]');
410
- contentlets.forEach(contentlet => {
411
- if (contentlet.clientHeight) {
412
- return;
370
+ function onPageReload(callback) {
371
+ const messageCallback = event => {
372
+ if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_RELOAD_PAGE) {
373
+ callback();
413
374
  }
414
- contentlet.classList.add('empty-contentlet');
415
- });
375
+ };
376
+ window.addEventListener('message', messageCallback);
377
+ return {
378
+ unsubscribe: () => {
379
+ window.removeEventListener('message', messageCallback);
380
+ },
381
+ event: UVEEventType.PAGE_RELOAD
382
+ };
416
383
  }
417
384
  /**
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
385
+ * Subscribes to request bounds events in the UVE editor
425
386
  *
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.
387
+ * @param {UVEEventHandler} callback - Function to be called when bounds are requested
388
+ * @returns {Object} Object containing unsubscribe function and event type
389
+ * @returns {Function} .unsubscribe - Function to remove the event listener
390
+ * @returns {UVEEventType} .event - The event type being subscribed to
391
+ * @internal
430
392
  */
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;
393
+ function onRequestBounds(callback) {
394
+ const messageCallback = event => {
395
+ if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_REQUEST_BOUNDS) {
396
+ const containers = Array.from(document.querySelectorAll('[data-dot-object="container"]'));
397
+ const positionData = getDotCMSPageBounds(containers);
398
+ callback(positionData);
443
399
  }
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
- });
400
+ };
401
+ window.addEventListener('message', messageCallback);
457
402
  return {
458
- subscriptions: [pageReloadSubscription, requestBoundsSubscription, iframeScrollSubscription, contentletHoveredSubscription]
403
+ unsubscribe: () => {
404
+ window.removeEventListener('message', messageCallback);
405
+ },
406
+ event: UVEEventType.REQUEST_BOUNDS
459
407
  };
460
408
  }
461
409
  /**
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.
410
+ * Subscribes to iframe scroll events in the UVE editor
466
411
  *
467
- * @remarks
468
- * This is typically called after all UVE event handlers and DOM listeners
469
- * have been set up successfully.
412
+ * @param {UVEEventHandler} callback - Function to be called when iframe scroll occurs
413
+ * @returns {Object} Object containing unsubscribe function and event type
414
+ * @returns {Function} .unsubscribe - Function to remove the event listener
415
+ * @returns {UVEEventType} .event - The event type being subscribed to
416
+ * @internal
470
417
  */
471
- function setClientIsReady(config) {
472
- sendMessageToUVE({
473
- action: DotCMSUVEAction.CLIENT_READY,
474
- payload: config
475
- });
418
+ function onIframeScroll(callback) {
419
+ const messageCallback = event => {
420
+ if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_SCROLL_INSIDE_IFRAME) {
421
+ const direction = event.data.direction;
422
+ callback(direction);
423
+ }
424
+ };
425
+ window.addEventListener('message', messageCallback);
426
+ return {
427
+ unsubscribe: () => {
428
+ window.removeEventListener('message', messageCallback);
429
+ },
430
+ event: UVEEventType.IFRAME_SCROLL
431
+ };
476
432
  }
477
433
  /**
478
- * Listen for block editor inline event.
434
+ * Subscribes to contentlet hover events in the UVE editor
435
+ *
436
+ * @param {UVEEventHandler} callback - Function to be called when a contentlet is hovered
437
+ * @returns {Object} Object containing unsubscribe function and event type
438
+ * @returns {Function} .unsubscribe - Function to remove the event listener
439
+ * @returns {UVEEventType} .event - The event type being subscribed to
440
+ * @internal
479
441
  */
480
- function listenBlockEditorInlineEvent() {
481
- if (document.readyState === 'complete') {
482
- // The page is fully loaded or interactive
483
- listenBlockEditorClick();
484
- return {
485
- destroyListenBlockEditorInlineEvent: () => {
486
- window.removeEventListener('load', () => listenBlockEditorClick());
487
- }
442
+ function onContentletHovered(callback) {
443
+ const pointerMoveCallback = event => {
444
+ const foundElement = findDotCMSElement(event.target);
445
+ if (!foundElement) return;
446
+ const {
447
+ x,
448
+ y,
449
+ width,
450
+ height
451
+ } = foundElement.getBoundingClientRect();
452
+ const isContainer = foundElement.dataset?.['dotObject'] === 'container';
453
+ const contentletForEmptyContainer = {
454
+ identifier: 'TEMP_EMPTY_CONTENTLET',
455
+ title: 'TEMP_EMPTY_CONTENTLET',
456
+ contentType: 'TEMP_EMPTY_CONTENTLET_TYPE',
457
+ inode: 'TEMPY_EMPTY_CONTENTLET_INODE',
458
+ widgetTitle: 'TEMP_EMPTY_CONTENTLET',
459
+ baseType: 'TEMP_EMPTY_CONTENTLET',
460
+ onNumberOfPages: 1
488
461
  };
489
- }
490
- window.addEventListener('load', () => listenBlockEditorClick());
462
+ const contentlet = {
463
+ identifier: foundElement.dataset?.['dotIdentifier'],
464
+ title: foundElement.dataset?.['dotTitle'],
465
+ inode: foundElement.dataset?.['dotInode'],
466
+ contentType: foundElement.dataset?.['dotType'],
467
+ baseType: foundElement.dataset?.['dotBasetype'],
468
+ widgetTitle: foundElement.dataset?.['dotWidgetTitle'],
469
+ onNumberOfPages: foundElement.dataset?.['dotOnNumberOfPages']
470
+ };
471
+ const vtlFiles = findDotCMSVTLData(foundElement);
472
+ const contentletPayload = {
473
+ container:
474
+ // Here extract dot-container from contentlet if it is Headless
475
+ // or search in parent container if it is VTL
476
+ foundElement.dataset?.['dotContainer'] ? JSON.parse(foundElement.dataset?.['dotContainer']) : getClosestDotCMSContainerData(foundElement),
477
+ contentlet: isContainer ? contentletForEmptyContainer : contentlet,
478
+ vtlFiles
479
+ };
480
+ const contentletHoveredPayload = {
481
+ x,
482
+ y,
483
+ width,
484
+ height,
485
+ payload: contentletPayload
486
+ };
487
+ callback(contentletHoveredPayload);
488
+ };
489
+ document.addEventListener('pointermove', pointerMoveCallback);
491
490
  return {
492
- destroyListenBlockEditorInlineEvent: () => {
493
- window.removeEventListener('load', () => listenBlockEditorClick());
494
- }
491
+ unsubscribe: () => {
492
+ document.removeEventListener('pointermove', pointerMoveCallback);
493
+ },
494
+ event: UVEEventType.CONTENTLET_HOVERED
495
495
  };
496
496
  }
497
- const listenBlockEditorClick = () => {
498
- const editBlockEditorNodes = document.querySelectorAll('[data-block-editor-content]');
499
- if (!editBlockEditorNodes.length) {
500
- return;
501
- }
502
- editBlockEditorNodes.forEach(node => {
503
- const {
504
- inode,
505
- language = '1',
506
- contentType,
507
- fieldName,
508
- blockEditorContent
509
- } = node.dataset;
510
- const content = JSON.parse(blockEditorContent || '');
511
- if (!inode || !language || !contentType || !fieldName) {
512
- console.error('Missing data attributes for block editor inline editing.');
513
- console.warn('inode, language, contentType and fieldName are required.');
514
- return;
515
- }
516
- node.classList.add('dotcms__inline-edit-field');
517
- node.addEventListener('click', () => {
518
- initInlineEditing('BLOCK_EDITOR', {
519
- inode,
520
- content,
521
- language: parseInt(language),
522
- fieldName,
523
- contentType
524
- });
525
- });
526
- });
527
- };
528
497
 
529
498
  /**
530
- * Post message to dotcms page editor
499
+ * Events that can be subscribed to in the UVE
531
500
  *
532
- * @export
533
- * @template T
534
- * @param {DotCMSUVEMessage<T>} message
501
+ * @internal
502
+ * @type {Record<UVEEventType, UVEEventSubscriber>}
535
503
  */
536
- function sendMessageToUVE(message) {
537
- window.parent.postMessage(message, '*');
538
- }
504
+ const __UVE_EVENTS__ = {
505
+ [UVEEventType.CONTENT_CHANGES]: callback => {
506
+ return onContentChanges(callback);
507
+ },
508
+ [UVEEventType.PAGE_RELOAD]: callback => {
509
+ return onPageReload(callback);
510
+ },
511
+ [UVEEventType.REQUEST_BOUNDS]: callback => {
512
+ return onRequestBounds(callback);
513
+ },
514
+ [UVEEventType.IFRAME_SCROLL]: callback => {
515
+ return onIframeScroll(callback);
516
+ },
517
+ [UVEEventType.CONTENTLET_HOVERED]: callback => {
518
+ return onContentletHovered(callback);
519
+ }
520
+ };
539
521
  /**
540
- * You can use this function to edit a contentlet in the editor.
522
+ * Default UVE event
541
523
  *
542
- * Calling this function inside the editor, will prompt the UVE to open a dialog to edit the contentlet.
524
+ * @param {string} event - The event to subscribe to.
525
+ * @internal
526
+ */
527
+ const __UVE_EVENT_ERROR_FALLBACK__ = event => {
528
+ return {
529
+ unsubscribe: () => {
530
+ /* do nothing */
531
+ },
532
+ event
533
+ };
534
+ };
535
+ /**
536
+ * Development mode
543
537
  *
544
- * @export
545
- * @template T
546
- * @param {Contentlet<T>} contentlet - The contentlet to edit.
538
+ * @internal
547
539
  */
548
- function editContentlet(contentlet) {
549
- sendMessageToUVE({
550
- action: DotCMSUVEAction.EDIT_CONTENTLET,
551
- payload: contentlet
552
- });
553
- }
554
- /*
555
- * Reorders the menu based on the provided configuration.
540
+ const DEVELOPMENT_MODE = 'development';
541
+ /**
542
+ * Production mode
556
543
  *
557
- * @param {ReorderMenuConfig} [config] - Optional configuration for reordering the menu.
558
- * @param {number} [config.startLevel=1] - The starting level of the menu to reorder.
559
- * @param {number} [config.depth=2] - The depth of the menu to reorder.
544
+ * @internal
545
+ */
546
+ const PRODUCTION_MODE = 'production';
547
+ /**
548
+ * End class
560
549
  *
561
- * This function constructs a URL for the reorder menu page with the specified
562
- * start level and depth, and sends a message to the editor to perform the reorder action.
550
+ * @internal
563
551
  */
564
- function reorderMenu(config) {
565
- const {
566
- startLevel = 1,
567
- depth = 2
568
- } = config || {};
569
- sendMessageToUVE({
570
- action: DotCMSUVEAction.REORDER_MENU,
571
- payload: {
572
- startLevel,
573
- depth
574
- }
575
- });
576
- }
552
+ const END_CLASS = 'col-end-';
577
553
  /**
578
- * Initializes the inline editing in the editor.
554
+ * Start class
555
+ *
556
+ * @internal
557
+ */
558
+ const START_CLASS = 'col-start-';
559
+ /**
560
+ * Empty container style for React
561
+ *
562
+ * @internal
563
+ */
564
+ const EMPTY_CONTAINER_STYLE_REACT = {
565
+ width: '100%',
566
+ backgroundColor: '#ECF0FD',
567
+ display: 'flex',
568
+ justifyContent: 'center',
569
+ alignItems: 'center',
570
+ color: '#030E32',
571
+ height: '10rem'
572
+ };
573
+ /**
574
+ * Empty container style for Angular
575
+ *
576
+ * @internal
577
+ */
578
+ const EMPTY_CONTAINER_STYLE_ANGULAR = {
579
+ width: '100%',
580
+ 'background-color': '#ECF0FD',
581
+ display: 'flex',
582
+ 'justify-content': 'center',
583
+ 'align-items': 'center',
584
+ color: '#030E32',
585
+ height: '10rem'
586
+ };
587
+ /**
588
+ * Custom no component
589
+ *
590
+ * @internal
591
+ */
592
+ const CUSTOM_NO_COMPONENT = 'CustomNoComponent';
593
+
594
+ /**
595
+ * Gets the current state of the Universal Visual Editor (UVE).
596
+ *
597
+ * This function checks if the code is running inside the DotCMS Universal Visual Editor
598
+ * and returns information about its current state, including the editor mode.
579
599
  *
580
600
  * @export
581
- * @param {INLINE_EDITING_EVENT_KEY} type
582
- * @param {InlineEditEventData} eventData
583
- * @return {*}
601
+ * @return {UVEState | undefined} Returns the UVE state object if running inside the editor,
602
+ * undefined otherwise.
584
603
  *
585
- * * @example
586
- * ```html
587
- * <div onclick="initInlineEditing('BLOCK_EDITOR', { inode, languageId, contentType, fieldName, content })">
588
- * ${My Content}
589
- * </div>
604
+ * The state includes:
605
+ * - mode: The current editor mode (preview, edit, live)
606
+ * - languageId: The language ID of the current page setted on the UVE
607
+ * - persona: The persona of the current page setted on the UVE
608
+ * - variantName: The name of the current variant
609
+ * - experimentId: The ID of the current experiment
610
+ * - publishDate: The publish date of the current page setted on the UVE
611
+ *
612
+ * @note The absence of any of these properties means that the value is the default one.
613
+ *
614
+ * @example
615
+ * ```ts
616
+ * const editorState = getUVEState();
617
+ * if (editorState?.mode === 'edit') {
618
+ * // Enable editing features
619
+ * }
590
620
  * ```
591
621
  */
592
- function initInlineEditing(type, data) {
593
- sendMessageToUVE({
594
- action: DotCMSUVEAction.INIT_INLINE_EDITING,
595
- payload: {
596
- type,
597
- data
598
- }
599
- });
622
+ function getUVEState() {
623
+ if (typeof window === 'undefined' || window.parent === window || !window.location) {
624
+ return undefined;
625
+ }
626
+ const url = new URL(window.location.href);
627
+ const possibleModes = Object.values(UVE_MODE);
628
+ let mode = url.searchParams.get('mode') ?? UVE_MODE.EDIT;
629
+ const languageId = url.searchParams.get('language_id');
630
+ const persona = url.searchParams.get('personaId');
631
+ const variantName = url.searchParams.get('variantName');
632
+ const experimentId = url.searchParams.get('experimentId');
633
+ const publishDate = url.searchParams.get('publishDate');
634
+ const dotCMSHost = url.searchParams.get('dotCMSHost');
635
+ if (!possibleModes.includes(mode)) {
636
+ mode = UVE_MODE.EDIT;
637
+ }
638
+ return {
639
+ mode,
640
+ languageId,
641
+ persona,
642
+ variantName,
643
+ experimentId,
644
+ publishDate,
645
+ dotCMSHost
646
+ };
600
647
  }
601
648
  /**
602
- * Initializes the Universal Visual Editor (UVE) with required handlers and event listeners.
603
- *
604
- * This function sets up:
605
- * - Scroll handling
606
- * - Empty contentlet styling
607
- * - Block editor inline event listening
608
- * - Client ready state
609
- * - UVE event subscriptions
649
+ * Creates a subscription to a UVE event.
610
650
  *
611
- * @returns {Object} An object containing the cleanup function
612
- * @returns {Function} destroyUVESubscriptions - Function to clean up all UVE event subscriptions
651
+ * @param eventType - The type of event to subscribe to
652
+ * @param callback - The callback function that will be called when the event occurs
653
+ * @returns An event subscription that can be used to unsubscribe
613
654
  *
614
655
  * @example
615
- * ```typescript
616
- * const { destroyUVESubscriptions } = initUVE();
656
+ * ```ts
657
+ * // Subscribe to page changes
658
+ * const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
659
+ * console.log('Content changes:', changes);
660
+ * });
617
661
  *
618
- * // When done with UVE
619
- * destroyUVESubscriptions();
662
+ * // Later, unsubscribe when no longer needed
663
+ * subscription.unsubscribe();
620
664
  * ```
621
665
  */
622
- function initUVE(config = {}) {
623
- addClassToEmptyContentlets();
624
- setClientIsReady(config);
625
- const {
626
- subscriptions
627
- } = registerUVEEvents();
628
- const {
629
- destroyScrollHandler
630
- } = scrollHandler();
631
- const {
632
- destroyListenBlockEditorInlineEvent
633
- } = listenBlockEditorInlineEvent();
634
- return {
635
- destroyUVESubscriptions: () => {
636
- subscriptions.forEach(subscription => subscription.unsubscribe());
637
- destroyScrollHandler();
638
- destroyListenBlockEditorInlineEvent();
639
- }
640
- };
666
+ function createUVESubscription(eventType, callback) {
667
+ if (!getUVEState()) {
668
+ console.warn('UVE Subscription: Not running inside UVE');
669
+ return __UVE_EVENT_ERROR_FALLBACK__(eventType);
670
+ }
671
+ const eventCallback = __UVE_EVENTS__[eventType];
672
+ if (!eventCallback) {
673
+ console.error(`UVE Subscription: Event ${eventType} not found`);
674
+ return __UVE_EVENT_ERROR_FALLBACK__(eventType);
675
+ }
676
+ return eventCallback(callback);
641
677
  }
642
678
 
643
679
  /**
@@ -739,383 +775,274 @@ const isValidBlocks = blocks => {
739
775
  };
740
776
  };
741
777
 
778
+ /* eslint-disable @typescript-eslint/no-explicit-any */
742
779
  /**
743
- * Enum representing the different types of blocks available in the Block Editor
744
- *
745
- * @export
746
- * @enum {string}
780
+ * Sets up scroll event handlers for the window to notify the editor about scroll events.
781
+ * Adds listeners for both 'scroll' and 'scrollend' events, sending appropriate messages
782
+ * to the editor when these events occur.
747
783
  */
748
- var Blocks;
749
- (function (Blocks) {
750
- /** Represents a paragraph block */
751
- Blocks["PARAGRAPH"] = "paragraph";
752
- /** Represents a heading block */
753
- Blocks["HEADING"] = "heading";
754
- /** Represents a text block */
755
- Blocks["TEXT"] = "text";
756
- /** Represents a bullet/unordered list block */
757
- Blocks["BULLET_LIST"] = "bulletList";
758
- /** Represents an ordered/numbered list block */
759
- Blocks["ORDERED_LIST"] = "orderedList";
760
- /** Represents a list item within a list block */
761
- Blocks["LIST_ITEM"] = "listItem";
762
- /** Represents a blockquote block */
763
- Blocks["BLOCK_QUOTE"] = "blockquote";
764
- /** Represents a code block */
765
- Blocks["CODE_BLOCK"] = "codeBlock";
766
- /** Represents a hard break (line break) */
767
- Blocks["HARDBREAK"] = "hardBreak";
768
- /** Represents a horizontal rule/divider */
769
- Blocks["HORIZONTAL_RULE"] = "horizontalRule";
770
- /** Represents a DotCMS image block */
771
- Blocks["DOT_IMAGE"] = "dotImage";
772
- /** Represents a DotCMS video block */
773
- Blocks["DOT_VIDEO"] = "dotVideo";
774
- /** Represents a table block */
775
- Blocks["TABLE"] = "table";
776
- /** Represents a DotCMS content block */
777
- Blocks["DOT_CONTENT"] = "dotContent";
778
- })(Blocks || (Blocks = {}));
779
-
780
- /**
781
- * Subscribes to content changes in the UVE editor
782
- *
783
- * @param {UVEEventHandler} callback - Function to be called when content changes are detected
784
- * @returns {Object} Object containing unsubscribe function and event type
785
- * @returns {Function} .unsubscribe - Function to remove the event listener
786
- * @returns {UVEEventType} .event - The event type being subscribed to
787
- * @internal
788
- */
789
- function onContentChanges(callback) {
790
- const messageCallback = event => {
791
- if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_SET_PAGE_DATA) {
792
- callback(event.data.payload);
793
- }
784
+ function scrollHandler() {
785
+ const scrollCallback = () => {
786
+ sendMessageToUVE({
787
+ action: DotCMSUVEAction.IFRAME_SCROLL
788
+ });
794
789
  };
795
- window.addEventListener('message', messageCallback);
790
+ const scrollEndCallback = () => {
791
+ sendMessageToUVE({
792
+ action: DotCMSUVEAction.IFRAME_SCROLL_END
793
+ });
794
+ };
795
+ window.addEventListener('scroll', scrollCallback);
796
+ window.addEventListener('scrollend', scrollEndCallback);
796
797
  return {
797
- unsubscribe: () => {
798
- window.removeEventListener('message', messageCallback);
799
- },
800
- event: UVEEventType.CONTENT_CHANGES
798
+ destroyScrollHandler: () => {
799
+ window.removeEventListener('scroll', scrollCallback);
800
+ window.removeEventListener('scrollend', scrollEndCallback);
801
+ }
801
802
  };
802
803
  }
803
804
  /**
804
- * Subscribes to page reload events in the UVE editor
805
+ * Adds 'empty-contentlet' class to contentlet elements that have no height.
806
+ * This helps identify and style empty contentlets in the editor view.
805
807
  *
806
- * @param {UVEEventHandler} callback - Function to be called when page reload is triggered
807
- * @returns {Object} Object containing unsubscribe function and event type
808
- * @returns {Function} .unsubscribe - Function to remove the event listener
809
- * @returns {UVEEventType} .event - The event type being subscribed to
810
- * @internal
808
+ * @remarks
809
+ * The function queries all elements with data-dot-object="contentlet" attribute
810
+ * and checks their clientHeight. If an element has no height (clientHeight = 0),
811
+ * it adds the 'empty-contentlet' class to that element.
811
812
  */
812
- function onPageReload(callback) {
813
- const messageCallback = event => {
814
- if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_RELOAD_PAGE) {
815
- callback();
813
+ function addClassToEmptyContentlets() {
814
+ const contentlets = document.querySelectorAll('[data-dot-object="contentlet"]');
815
+ contentlets.forEach(contentlet => {
816
+ if (contentlet.clientHeight) {
817
+ return;
816
818
  }
817
- };
818
- window.addEventListener('message', messageCallback);
819
- return {
820
- unsubscribe: () => {
821
- window.removeEventListener('message', messageCallback);
822
- },
823
- event: UVEEventType.PAGE_RELOAD
824
- };
819
+ contentlet.classList.add('empty-contentlet');
820
+ });
825
821
  }
826
822
  /**
827
- * Subscribes to request bounds events in the UVE editor
823
+ * Registers event handlers for various UVE (Universal Visual Editor) events.
828
824
  *
829
- * @param {UVEEventHandler} callback - Function to be called when bounds are requested
830
- * @returns {Object} Object containing unsubscribe function and event type
831
- * @returns {Function} .unsubscribe - Function to remove the event listener
832
- * @returns {UVEEventType} .event - The event type being subscribed to
833
- * @internal
825
+ * This function sets up subscriptions for:
826
+ * - Page reload events that refresh the window
827
+ * - Bounds request events to update editor boundaries
828
+ * - Iframe scroll events to handle smooth scrolling within bounds
829
+ * - Contentlet hover events to notify the editor
830
+ *
831
+ * @remarks
832
+ * For scroll events, the function includes logic to prevent scrolling beyond
833
+ * the top or bottom boundaries of the iframe, which helps maintain proper
834
+ * scroll event handling.
834
835
  */
835
- function onRequestBounds(callback) {
836
- const messageCallback = event => {
837
- if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_REQUEST_BOUNDS) {
838
- const containers = Array.from(document.querySelectorAll('[data-dot-object="container"]'));
839
- const positionData = getDotCMSPageBounds(containers);
840
- callback(positionData);
836
+ function registerUVEEvents() {
837
+ const pageReloadSubscription = createUVESubscription(UVEEventType.PAGE_RELOAD, () => {
838
+ window.location.reload();
839
+ });
840
+ const requestBoundsSubscription = createUVESubscription(UVEEventType.REQUEST_BOUNDS, bounds => {
841
+ setBounds(bounds);
842
+ });
843
+ const iframeScrollSubscription = createUVESubscription(UVEEventType.IFRAME_SCROLL, direction => {
844
+ if (window.scrollY === 0 && direction === 'up' || computeScrollIsInBottom() && direction === 'down') {
845
+ // If the iframe scroll is at the top or bottom, do not send anything.
846
+ // This avoids losing the scrollend event.
847
+ return;
841
848
  }
842
- };
843
- window.addEventListener('message', messageCallback);
849
+ const scrollY = direction === 'up' ? -120 : 120;
850
+ window.scrollBy({
851
+ left: 0,
852
+ top: scrollY,
853
+ behavior: 'smooth'
854
+ });
855
+ });
856
+ const contentletHoveredSubscription = createUVESubscription(UVEEventType.CONTENTLET_HOVERED, contentletHovered => {
857
+ sendMessageToUVE({
858
+ action: DotCMSUVEAction.SET_CONTENTLET,
859
+ payload: contentletHovered
860
+ });
861
+ });
844
862
  return {
845
- unsubscribe: () => {
846
- window.removeEventListener('message', messageCallback);
847
- },
848
- event: UVEEventType.REQUEST_BOUNDS
863
+ subscriptions: [pageReloadSubscription, requestBoundsSubscription, iframeScrollSubscription, contentletHoveredSubscription]
849
864
  };
850
865
  }
851
866
  /**
852
- * Subscribes to iframe scroll events in the UVE editor
867
+ * Notifies the editor that the UVE client is ready to receive messages.
853
868
  *
854
- * @param {UVEEventHandler} callback - Function to be called when iframe scroll occurs
855
- * @returns {Object} Object containing unsubscribe function and event type
856
- * @returns {Function} .unsubscribe - Function to remove the event listener
857
- * @returns {UVEEventType} .event - The event type being subscribed to
858
- * @internal
869
+ * This function sends a message to the editor indicating that the client-side
870
+ * initialization is complete and it's ready to handle editor interactions.
871
+ *
872
+ * @remarks
873
+ * This is typically called after all UVE event handlers and DOM listeners
874
+ * have been set up successfully.
859
875
  */
860
- function onIframeScroll(callback) {
861
- const messageCallback = event => {
862
- if (event.data.name === __DOTCMS_UVE_EVENT__.UVE_SCROLL_INSIDE_IFRAME) {
863
- const direction = event.data.direction;
864
- callback(direction);
865
- }
866
- };
867
- window.addEventListener('message', messageCallback);
868
- return {
869
- unsubscribe: () => {
870
- window.removeEventListener('message', messageCallback);
871
- },
872
- event: UVEEventType.IFRAME_SCROLL
873
- };
876
+ function setClientIsReady(config) {
877
+ sendMessageToUVE({
878
+ action: DotCMSUVEAction.CLIENT_READY,
879
+ payload: config
880
+ });
874
881
  }
875
882
  /**
876
- * Subscribes to contentlet hover events in the UVE editor
877
- *
878
- * @param {UVEEventHandler} callback - Function to be called when a contentlet is hovered
879
- * @returns {Object} Object containing unsubscribe function and event type
880
- * @returns {Function} .unsubscribe - Function to remove the event listener
881
- * @returns {UVEEventType} .event - The event type being subscribed to
882
- * @internal
883
+ * Listen for block editor inline event.
883
884
  */
884
- function onContentletHovered(callback) {
885
- const pointerMoveCallback = event => {
886
- const foundElement = findDotCMSElement(event.target);
887
- if (!foundElement) return;
888
- const {
889
- x,
890
- y,
891
- width,
892
- height
893
- } = foundElement.getBoundingClientRect();
894
- const isContainer = foundElement.dataset?.['dotObject'] === 'container';
895
- const contentletForEmptyContainer = {
896
- identifier: 'TEMP_EMPTY_CONTENTLET',
897
- title: 'TEMP_EMPTY_CONTENTLET',
898
- contentType: 'TEMP_EMPTY_CONTENTLET_TYPE',
899
- inode: 'TEMPY_EMPTY_CONTENTLET_INODE',
900
- widgetTitle: 'TEMP_EMPTY_CONTENTLET',
901
- baseType: 'TEMP_EMPTY_CONTENTLET',
902
- onNumberOfPages: 1
903
- };
904
- const contentlet = {
905
- identifier: foundElement.dataset?.['dotIdentifier'],
906
- title: foundElement.dataset?.['dotTitle'],
907
- inode: foundElement.dataset?.['dotInode'],
908
- contentType: foundElement.dataset?.['dotType'],
909
- baseType: foundElement.dataset?.['dotBasetype'],
910
- widgetTitle: foundElement.dataset?.['dotWidgetTitle'],
911
- onNumberOfPages: foundElement.dataset?.['dotOnNumberOfPages']
912
- };
913
- const vtlFiles = findDotCMSVTLData(foundElement);
914
- const contentletPayload = {
915
- container:
916
- // Here extract dot-container from contentlet if it is Headless
917
- // or search in parent container if it is VTL
918
- foundElement.dataset?.['dotContainer'] ? JSON.parse(foundElement.dataset?.['dotContainer']) : getClosestDotCMSContainerData(foundElement),
919
- contentlet: isContainer ? contentletForEmptyContainer : contentlet,
920
- vtlFiles
921
- };
922
- const contentletHoveredPayload = {
923
- x,
924
- y,
925
- width,
926
- height,
927
- payload: contentletPayload
885
+ function listenBlockEditorInlineEvent() {
886
+ if (document.readyState === 'complete') {
887
+ // The page is fully loaded or interactive
888
+ listenBlockEditorClick();
889
+ return {
890
+ destroyListenBlockEditorInlineEvent: () => {
891
+ window.removeEventListener('load', () => listenBlockEditorClick());
892
+ }
928
893
  };
929
- callback(contentletHoveredPayload);
930
- };
931
- document.addEventListener('pointermove', pointerMoveCallback);
894
+ }
895
+ window.addEventListener('load', () => listenBlockEditorClick());
932
896
  return {
933
- unsubscribe: () => {
934
- document.removeEventListener('pointermove', pointerMoveCallback);
935
- },
936
- event: UVEEventType.CONTENTLET_HOVERED
897
+ destroyListenBlockEditorInlineEvent: () => {
898
+ window.removeEventListener('load', () => listenBlockEditorClick());
899
+ }
937
900
  };
938
901
  }
939
-
940
- /**
941
- * Events that can be subscribed to in the UVE
942
- *
943
- * @internal
944
- * @type {Record<UVEEventType, UVEEventSubscriber>}
945
- */
946
- const __UVE_EVENTS__ = {
947
- [UVEEventType.CONTENT_CHANGES]: callback => {
948
- return onContentChanges(callback);
949
- },
950
- [UVEEventType.PAGE_RELOAD]: callback => {
951
- return onPageReload(callback);
952
- },
953
- [UVEEventType.REQUEST_BOUNDS]: callback => {
954
- return onRequestBounds(callback);
955
- },
956
- [UVEEventType.IFRAME_SCROLL]: callback => {
957
- return onIframeScroll(callback);
958
- },
959
- [UVEEventType.CONTENTLET_HOVERED]: callback => {
960
- return onContentletHovered(callback);
902
+ const listenBlockEditorClick = () => {
903
+ const editBlockEditorNodes = document.querySelectorAll('[data-block-editor-content]');
904
+ if (!editBlockEditorNodes.length) {
905
+ return;
961
906
  }
907
+ editBlockEditorNodes.forEach(node => {
908
+ const {
909
+ inode,
910
+ language = '1',
911
+ contentType,
912
+ fieldName,
913
+ blockEditorContent
914
+ } = node.dataset;
915
+ const content = JSON.parse(blockEditorContent || '');
916
+ if (!inode || !language || !contentType || !fieldName) {
917
+ console.error('Missing data attributes for block editor inline editing.');
918
+ console.warn('inode, language, contentType and fieldName are required.');
919
+ return;
920
+ }
921
+ node.classList.add('dotcms__inline-edit-field');
922
+ node.addEventListener('click', () => {
923
+ initInlineEditing('BLOCK_EDITOR', {
924
+ inode,
925
+ content,
926
+ language: parseInt(language),
927
+ fieldName,
928
+ contentType
929
+ });
930
+ });
931
+ });
962
932
  };
933
+
963
934
  /**
964
- * Default UVE event
965
- *
966
- * @param {string} event - The event to subscribe to.
967
- * @internal
968
- */
969
- const __UVE_EVENT_ERROR_FALLBACK__ = event => {
970
- return {
971
- unsubscribe: () => {
972
- /* do nothing */
973
- },
974
- event
975
- };
976
- };
977
- /**
978
- * Development mode
979
- *
980
- * @internal
981
- */
982
- const DEVELOPMENT_MODE = 'development';
983
- /**
984
- * Production mode
985
- *
986
- * @internal
987
- */
988
- const PRODUCTION_MODE = 'production';
989
- /**
990
- * End class
935
+ * Post message to dotcms page editor
991
936
  *
992
- * @internal
937
+ * @export
938
+ * @template T
939
+ * @param {DotCMSUVEMessage<T>} message
993
940
  */
994
- const END_CLASS = 'col-end-';
941
+ function sendMessageToUVE(message) {
942
+ window.parent.postMessage(message, '*');
943
+ }
995
944
  /**
996
- * Start class
945
+ * You can use this function to edit a contentlet in the editor.
997
946
  *
998
- * @internal
999
- */
1000
- const START_CLASS = 'col-start-';
1001
- /**
1002
- * Empty container style for React
947
+ * Calling this function inside the editor, will prompt the UVE to open a dialog to edit the contentlet.
1003
948
  *
1004
- * @internal
949
+ * @export
950
+ * @template T
951
+ * @param {Contentlet<T>} contentlet - The contentlet to edit.
1005
952
  */
1006
- const EMPTY_CONTAINER_STYLE_REACT = {
1007
- width: '100%',
1008
- backgroundColor: '#ECF0FD',
1009
- display: 'flex',
1010
- justifyContent: 'center',
1011
- alignItems: 'center',
1012
- color: '#030E32',
1013
- height: '10rem'
1014
- };
1015
- /**
1016
- * Empty container style for Angular
953
+ function editContentlet(contentlet) {
954
+ sendMessageToUVE({
955
+ action: DotCMSUVEAction.EDIT_CONTENTLET,
956
+ payload: contentlet
957
+ });
958
+ }
959
+ /*
960
+ * Reorders the menu based on the provided configuration.
1017
961
  *
1018
- * @internal
1019
- */
1020
- const EMPTY_CONTAINER_STYLE_ANGULAR = {
1021
- width: '100%',
1022
- 'background-color': '#ECF0FD',
1023
- display: 'flex',
1024
- 'justify-content': 'center',
1025
- 'align-items': 'center',
1026
- color: '#030E32',
1027
- height: '10rem'
1028
- };
1029
- /**
1030
- * Custom no component
962
+ * @param {ReorderMenuConfig} [config] - Optional configuration for reordering the menu.
963
+ * @param {number} [config.startLevel=1] - The starting level of the menu to reorder.
964
+ * @param {number} [config.depth=2] - The depth of the menu to reorder.
1031
965
  *
1032
- * @internal
966
+ * This function constructs a URL for the reorder menu page with the specified
967
+ * start level and depth, and sends a message to the editor to perform the reorder action.
1033
968
  */
1034
- const CUSTOM_NO_COMPONENT = 'CustomNoComponent';
1035
-
969
+ function reorderMenu(config) {
970
+ const {
971
+ startLevel = 1,
972
+ depth = 2
973
+ } = config || {};
974
+ sendMessageToUVE({
975
+ action: DotCMSUVEAction.REORDER_MENU,
976
+ payload: {
977
+ startLevel,
978
+ depth
979
+ }
980
+ });
981
+ }
1036
982
  /**
1037
- * Gets the current state of the Universal Visual Editor (UVE).
1038
- *
1039
- * This function checks if the code is running inside the DotCMS Universal Visual Editor
1040
- * and returns information about its current state, including the editor mode.
983
+ * Initializes the inline editing in the editor.
1041
984
  *
1042
985
  * @export
1043
- * @return {UVEState | undefined} Returns the UVE state object if running inside the editor,
1044
- * undefined otherwise.
1045
- *
1046
- * The state includes:
1047
- * - mode: The current editor mode (preview, edit, live)
1048
- * - languageId: The language ID of the current page setted on the UVE
1049
- * - persona: The persona of the current page setted on the UVE
1050
- * - variantName: The name of the current variant
1051
- * - experimentId: The ID of the current experiment
1052
- * - publishDate: The publish date of the current page setted on the UVE
1053
- *
1054
- * @note The absence of any of these properties means that the value is the default one.
986
+ * @param {INLINE_EDITING_EVENT_KEY} type
987
+ * @param {InlineEditEventData} eventData
988
+ * @return {*}
1055
989
  *
1056
- * @example
1057
- * ```ts
1058
- * const editorState = getUVEState();
1059
- * if (editorState?.mode === 'edit') {
1060
- * // Enable editing features
1061
- * }
990
+ * * @example
991
+ * ```html
992
+ * <div onclick="initInlineEditing('BLOCK_EDITOR', { inode, languageId, contentType, fieldName, content })">
993
+ * ${My Content}
994
+ * </div>
1062
995
  * ```
1063
996
  */
1064
- function getUVEState() {
1065
- if (typeof window === 'undefined' || window.parent === window || !window.location) {
1066
- return undefined;
1067
- }
1068
- const url = new URL(window.location.href);
1069
- const possibleModes = Object.values(UVE_MODE);
1070
- let mode = url.searchParams.get('mode') ?? UVE_MODE.EDIT;
1071
- const languageId = url.searchParams.get('language_id');
1072
- const persona = url.searchParams.get('personaId');
1073
- const variantName = url.searchParams.get('variantName');
1074
- const experimentId = url.searchParams.get('experimentId');
1075
- const publishDate = url.searchParams.get('publishDate');
1076
- const dotCMSHost = url.searchParams.get('dotCMSHost');
1077
- if (!possibleModes.includes(mode)) {
1078
- mode = UVE_MODE.EDIT;
1079
- }
1080
- return {
1081
- mode,
1082
- languageId,
1083
- persona,
1084
- variantName,
1085
- experimentId,
1086
- publishDate,
1087
- dotCMSHost
1088
- };
997
+ function initInlineEditing(type, data) {
998
+ sendMessageToUVE({
999
+ action: DotCMSUVEAction.INIT_INLINE_EDITING,
1000
+ payload: {
1001
+ type,
1002
+ data
1003
+ }
1004
+ });
1089
1005
  }
1090
1006
  /**
1091
- * Creates a subscription to a UVE event.
1007
+ * Initializes the Universal Visual Editor (UVE) with required handlers and event listeners.
1092
1008
  *
1093
- * @param eventType - The type of event to subscribe to
1094
- * @param callback - The callback function that will be called when the event occurs
1095
- * @returns An event subscription that can be used to unsubscribe
1009
+ * This function sets up:
1010
+ * - Scroll handling
1011
+ * - Empty contentlet styling
1012
+ * - Block editor inline event listening
1013
+ * - Client ready state
1014
+ * - UVE event subscriptions
1015
+ *
1016
+ * @returns {Object} An object containing the cleanup function
1017
+ * @returns {Function} destroyUVESubscriptions - Function to clean up all UVE event subscriptions
1096
1018
  *
1097
1019
  * @example
1098
- * ```ts
1099
- * // Subscribe to page changes
1100
- * const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
1101
- * console.log('Content changes:', changes);
1102
- * });
1020
+ * ```typescript
1021
+ * const { destroyUVESubscriptions } = initUVE();
1103
1022
  *
1104
- * // Later, unsubscribe when no longer needed
1105
- * subscription.unsubscribe();
1023
+ * // When done with UVE
1024
+ * destroyUVESubscriptions();
1106
1025
  * ```
1107
1026
  */
1108
- function createUVESubscription(eventType, callback) {
1109
- if (!getUVEState()) {
1110
- console.warn('UVE Subscription: Not running inside UVE');
1111
- return __UVE_EVENT_ERROR_FALLBACK__(eventType);
1112
- }
1113
- const eventCallback = __UVE_EVENTS__[eventType];
1114
- if (!eventCallback) {
1115
- console.error(`UVE Subscription: Event ${eventType} not found`);
1116
- return __UVE_EVENT_ERROR_FALLBACK__(eventType);
1117
- }
1118
- return eventCallback(callback);
1027
+ function initUVE(config = {}) {
1028
+ addClassToEmptyContentlets();
1029
+ setClientIsReady(config);
1030
+ const {
1031
+ subscriptions
1032
+ } = registerUVEEvents();
1033
+ const {
1034
+ destroyScrollHandler
1035
+ } = scrollHandler();
1036
+ const {
1037
+ destroyListenBlockEditorInlineEvent
1038
+ } = listenBlockEditorInlineEvent();
1039
+ return {
1040
+ destroyUVESubscriptions: () => {
1041
+ subscriptions.forEach(subscription => subscription.unsubscribe());
1042
+ destroyScrollHandler();
1043
+ destroyListenBlockEditorInlineEvent();
1044
+ }
1045
+ };
1119
1046
  }
1120
1047
 
1121
- 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 };
1048
+ export { CUSTOM_NO_COMPONENT as C, DEVELOPMENT_MODE as D, END_CLASS as E, PRODUCTION_MODE as P, START_CLASS as S, __UVE_EVENTS__ as _, initUVE as a, __UVE_EVENT_ERROR_FALLBACK__ as b, createUVESubscription as c, EMPTY_CONTAINER_STYLE_REACT as d, editContentlet as e, EMPTY_CONTAINER_STYLE_ANGULAR as f, getUVEState as g, getDotCMSPageBounds as h, initInlineEditing as i, getDotCMSContentletsBound as j, getDotCMSContainerData as k, getClosestDotCMSContainerData as l, findDotCMSElement as m, findDotCMSVTLData as n, computeScrollIsInBottom as o, combineClasses as p, getColumnPositionClasses as q, reorderMenu as r, sendMessageToUVE as s, getDotContentletAttributes as t, getContainersData as u, getContentletsInContainer as v, getDotContainerAttributes as w, setBounds as x, isValidBlocks as y };