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