@dotcms/uve 1.5.6 → 1.6.0-next.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -19
- package/index.esm.js +1 -1
- package/internal.cjs.js +5 -0
- package/internal.esm.js +1 -1
- package/package.json +2 -2
- package/public.cjs.js +157 -39
- package/public.esm.js +153 -40
- package/src/internal/constants.d.ts +23 -0
- package/src/lib/dom/dom.utils.d.ts +44 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The `@dotcms/uve` SDK adds live editing to your JavaScript app using the dotCMS Universal Visual Editor (UVE). It provides low-level tools that power our framework-specific SDKs, such as [@dotcms/react](https://github.com/dotCMS/core/blob/main/core-web/libs/sdk/react/README.md) and [@dotcms/angular](https://github.com/dotCMS/core/blob/main/core-web/libs/sdk/angular/README.md).
|
|
4
4
|
|
|
5
|
-
> ⚠️ We **do not recommend using this SDK directly** for most use cases
|
|
5
|
+
> ⚠️ We **do not recommend using this SDK directly** for most use cases; you should use a [framework SDK that handles setup](#getting-started-recommended-examples), rendering, and event wiring for you.
|
|
6
6
|
|
|
7
7
|
With `@dotcms/uve`, framework SDKs are able to:
|
|
8
8
|
- Make pages and contentlets editable
|
|
@@ -17,11 +17,11 @@ With `@dotcms/uve`, framework SDKs are able to:
|
|
|
17
17
|
- [🚩 Custom Setup: Manual Rendering (Not Recommended)](#-custom-setup-manual-rendering-not-recommended)
|
|
18
18
|
- [Prerequisites & Setup](#prerequisites--setup)
|
|
19
19
|
- [Get a dotCMS Environment](#get-a-dotcms-environment)
|
|
20
|
-
- [
|
|
20
|
+
- [Configure The Universal Visual Editor App](#configure-the-universal-visual-editor-app)
|
|
21
21
|
- [Installation](#installation)
|
|
22
22
|
- [Using the SDK with TypeScript](#using-the-sdk-with-typescript)
|
|
23
23
|
- [SDK Reference](#sdk-reference)
|
|
24
|
-
- [`initUVE()`](#inituveconfig-
|
|
24
|
+
- [`initUVE()`](#inituveconfig-dotcmspageresponse)
|
|
25
25
|
- [`getUVEState()`](#getuvestate)
|
|
26
26
|
- [`createUVESubscription()`](#createuvesubscriptioneventtype-callback)
|
|
27
27
|
- [`editContentlet()`](#editcontentletcontentlet)
|
|
@@ -49,7 +49,7 @@ With `@dotcms/uve`, framework SDKs are able to:
|
|
|
49
49
|
We strongly recommend using one of our official framework SDKs, which are designed to handle UVE integration, routing, rendering, and more—out of the box. These examples are the best way to get started:
|
|
50
50
|
|
|
51
51
|
- [dotCMS Angular SDK: Angular Example](https://github.com/dotCMS/core/tree/main/examples/angular) – Ideal for Angular apps 🅰️
|
|
52
|
-
- [dotCMS React SDK: NextJS Example](https://github.com/dotCMS/core/tree/main/examples/
|
|
52
|
+
- [dotCMS React SDK: NextJS Example](https://github.com/dotCMS/core/tree/main/examples/nextjs) – Ideal for NextJS projects ⚛️
|
|
53
53
|
- [dotCMS React SDK: Astro Example](https://github.com/dotCMS/core/tree/main/examples/astro) – Ideal for Astro projects 🌌
|
|
54
54
|
|
|
55
55
|
These examples handle UVE integration, routing, rendering, and more—out of the box. **If you're building a headless dotCMS front-end, start there.**
|
|
@@ -66,7 +66,7 @@ You can use `@dotcms/uve` directly, but **it’s not recommended or supported**
|
|
|
66
66
|
|
|
67
67
|
Here's a minimal setup using `@dotcms/client` and `@dotcms/uve`:
|
|
68
68
|
|
|
69
|
-
1.
|
|
69
|
+
1. Initialize the Client and get the page response:
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
72
|
// getPage.ts
|
|
@@ -94,12 +94,13 @@ const getPage = async () => {
|
|
|
94
94
|
|
|
95
95
|
```ts
|
|
96
96
|
import { initUVE, createUVESubscription } from '@dotcms/uve';
|
|
97
|
+
import { UVEEventType } from '@dotcms/types';
|
|
97
98
|
import { getPage } from './getPage';
|
|
98
99
|
|
|
99
100
|
const pageResponse = await getPage();
|
|
100
101
|
|
|
101
102
|
initUVE(pageResponse);
|
|
102
|
-
createUVESubscription(
|
|
103
|
+
createUVESubscription(UVEEventType.CONTENT_CHANGES, (newPageResponse) => {
|
|
103
104
|
// Handle page updates (e.g. re-render)
|
|
104
105
|
});
|
|
105
106
|
```
|
|
@@ -118,7 +119,7 @@ createUVESubscription('changes', (newPageResponse) => {
|
|
|
118
119
|
#### 🔄 How to Render a dotCMS Page
|
|
119
120
|
|
|
120
121
|
> 📚 For a complete guide, here is a full tutorial:
|
|
121
|
-
> 👉 [dotCMS Page Rendering Architecture](
|
|
122
|
+
> 👉 [dotCMS Page Rendering Architecture](https://dev.dotcms.com/docs/dotcms-page-rendering-architecture)
|
|
122
123
|
|
|
123
124
|
dotCMS pages are structured as nested layout objects:
|
|
124
125
|
|
|
@@ -218,7 +219,6 @@ The SDK uses several key types from `@dotcms/types`:
|
|
|
218
219
|
import {
|
|
219
220
|
DotCMSBasicContentlet,
|
|
220
221
|
DotCMSPageResponse,
|
|
221
|
-
DotCMSUVEConfig,
|
|
222
222
|
DotCMSInlineEditingType,
|
|
223
223
|
UVEEventType,
|
|
224
224
|
UVEState
|
|
@@ -229,7 +229,7 @@ For a complete reference of all available types and interfaces, please refer to
|
|
|
229
229
|
|
|
230
230
|
## SDK Reference
|
|
231
231
|
|
|
232
|
-
### `initUVE(config?:
|
|
232
|
+
### `initUVE(config?: DotCMSPageResponse)`
|
|
233
233
|
|
|
234
234
|
`initUVE` is a function that initializes the Universal Visual Editor (UVE). It sets up the necessary communication between your app and the editor, enabling seamless integration and interaction.
|
|
235
235
|
|
|
@@ -243,7 +243,7 @@ For a complete reference of all available types and interfaces, please refer to
|
|
|
243
243
|
const { destroyUVESubscriptions } = initUVE(pageResponse);
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
-
> ⚠️ If you don't provide a `pageResponse`, we
|
|
246
|
+
> ⚠️ If you don't provide a `pageResponse`, we cannot guarantee that the UVE will be initialized correctly.
|
|
247
247
|
|
|
248
248
|
### `getUVEState()`
|
|
249
249
|
|
|
@@ -303,14 +303,16 @@ sub.unsubscribe();
|
|
|
303
303
|
|
|
304
304
|
- `UVEEventType.CONTENT_CHANGES`: Triggered when the content of the page changes.
|
|
305
305
|
- `UVEEventType.PAGE_RELOAD`: Triggered when the page is reloaded.
|
|
306
|
-
- `UVEEventType.
|
|
307
|
-
- `UVEEventType.IFRAME_SCROLL`: Triggered when the iframe is scrolled.
|
|
308
|
-
- `UVEEventType.IFRAME_SCROLL_END`: Triggered when the iframe has stopped scrolling.
|
|
306
|
+
- `UVEEventType.IFRAME_SCROLL`: Triggered when scroll action is needed inside the iframe (`'up'` or `'down'`).
|
|
309
307
|
- `UVEEventType.CONTENTLET_HOVERED`: Triggered when a contentlet is hovered.
|
|
308
|
+
- `UVEEventType.CONTENTLET_CLICKED`: Triggered when a contentlet is clicked.
|
|
309
|
+
- `UVEEventType.AUTO_BOUNDS`: Triggered when the SDK syncs page bounds after layout changes (internal SDK use).
|
|
310
|
+
- `UVEEventType.SCROLL_TO_SECTION`: Triggered when the editor requests a scroll to a specific page section (internal).
|
|
311
|
+
- `UVEEventType.SELECTION_CLEARED`: Triggered when the editor clears its selection (internal).
|
|
310
312
|
|
|
311
313
|
### `editContentlet(contentlet)`
|
|
312
314
|
|
|
313
|
-
`editContentlet` is a function that opens the dotCMS modal editor for any contentlet in or out of page area.
|
|
315
|
+
`editContentlet` is a function that opens the dotCMS modal editor for any contentlet in or out of the page area.
|
|
314
316
|
|
|
315
317
|
| Input | Type | Required | Description |
|
|
316
318
|
| ------------ | ------------------ | -------- | -------------------------------- |
|
|
@@ -340,7 +342,7 @@ const myEditButton = ({ contentlet }) => {
|
|
|
340
342
|
| Input | Type | Required | Description |
|
|
341
343
|
| ----------- | ---------------------------- | -------- | ------------------------------------------------------------------------------ |
|
|
342
344
|
| `type` | `DotCMSInlineEditingType` | ✅ | `'BLOCK_EDITOR'` or `'WYSIWYG'` |
|
|
343
|
-
| `
|
|
345
|
+
| `data` | `DotCMSInlineEditingPayload` | ✅ | [Field content required to enable inline editing](#dotcmsinlineeditingpayload) |
|
|
344
346
|
|
|
345
347
|
#### Usage
|
|
346
348
|
|
|
@@ -449,7 +451,7 @@ reorderMenu({ startLevel: 2, depth: 3 });
|
|
|
449
451
|
|
|
450
452
|
| Input | Type | Required | Description |
|
|
451
453
|
| --------- | ------------------------------------------ | -------- | ---------------------------- |
|
|
452
|
-
| `message` | [`DotCMSUVEMessage<T>`](#
|
|
454
|
+
| `message` | [`DotCMSUVEMessage<T>`](#dotcmsuvemessaget) | ✅ | Object with action + payload |
|
|
453
455
|
|
|
454
456
|
#### Usage
|
|
455
457
|
|
|
@@ -488,7 +490,7 @@ Style editor schemas are configured in the DotCMS admin UI under **Content Types
|
|
|
488
490
|
**Key Benefits:**
|
|
489
491
|
|
|
490
492
|
- **Real-Time Visual Editing**: Modify component styles and see changes instantly in the editor
|
|
491
|
-
- **Content-Specific Customization**: Different content types can have unique style schemas, and the same contentlet could have different styles depending on
|
|
493
|
+
- **Content-Specific Customization**: Different content types can have unique style schemas, and the same contentlet could have different styles depending on whether it is located in a different container or page
|
|
492
494
|
- **Admin-Managed**: Style schemas are defined in the DotCMS admin UI under Content Types and fetched automatically by the SDK
|
|
493
495
|
|
|
494
496
|
**Use Cases:**
|
|
@@ -627,7 +629,7 @@ When **defining styles** for a contentlet within a page using **Style Editor**,
|
|
|
627
629
|
|
|
628
630
|
> **NOTE:** (🎨 Styles are different) means the capability to define distinct styles, even when utilizing the identical Contentlet.
|
|
629
631
|
|
|
630
|
-
The only known limitation is that moving a contentlet with defined styles between different container types (5th scenario)
|
|
632
|
+
The only known limitation is that moving a contentlet with defined styles between different container types (5th scenario) results in the loss of those styles. A fix for this scenario is on our roadmap.
|
|
631
633
|
|
|
632
634
|
## Troubleshooting
|
|
633
635
|
|
|
@@ -713,7 +715,7 @@ We offer multiple channels to get help with the dotCMS UVE SDK:
|
|
|
713
715
|
- **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository
|
|
714
716
|
- **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions
|
|
715
717
|
- **Stack Overflow**: Use the tag `dotcms-uve` when posting questions
|
|
716
|
-
- **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://
|
|
718
|
+
- **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://www.dotcms.com/support)
|
|
717
719
|
|
|
718
720
|
When reporting issues, please include:
|
|
719
721
|
|
package/index.esm.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { b as createContentlet, c as createUVESubscription, e as editContentlet, a as enableBlockEditorInline, g as getUVEState, i as initInlineEditing, d as initUVE, r as reorderMenu, s as sendMessageToUVE, u as updateNavigation } from './public.esm.js';
|
|
2
2
|
import '@dotcms/types';
|
|
3
3
|
import '@dotcms/types/internal';
|
package/internal.cjs.js
CHANGED
|
@@ -370,6 +370,8 @@ const styleEditorField = {
|
|
|
370
370
|
})
|
|
371
371
|
};
|
|
372
372
|
|
|
373
|
+
exports.ANALYTICS_ACTIVE_WINDOW_KEY = _public.ANALYTICS_ACTIVE_WINDOW_KEY;
|
|
374
|
+
exports.ANALYTICS_READY_EVENT = _public.ANALYTICS_READY_EVENT;
|
|
373
375
|
exports.CUSTOM_NO_COMPONENT = _public.CUSTOM_NO_COMPONENT;
|
|
374
376
|
exports.DEVELOPMENT_MODE = _public.DEVELOPMENT_MODE;
|
|
375
377
|
exports.DOT_SECTION_ID_PREFIX = _public.DOT_SECTION_ID_PREFIX;
|
|
@@ -387,6 +389,7 @@ exports.computeScrollIsInBottom = _public.computeScrollIsInBottom;
|
|
|
387
389
|
exports.createUVESubscription = _public.createUVESubscription;
|
|
388
390
|
exports.findDotCMSElement = _public.findDotCMSElement;
|
|
389
391
|
exports.findDotCMSVTLData = _public.findDotCMSVTLData;
|
|
392
|
+
exports.getAnalyticsContentletAttributes = _public.getAnalyticsContentletAttributes;
|
|
390
393
|
exports.getClosestDotCMSContainerData = _public.getClosestDotCMSContainerData;
|
|
391
394
|
exports.getColumnPositionClasses = _public.getColumnPositionClasses;
|
|
392
395
|
exports.getContainersData = _public.getContainersData;
|
|
@@ -396,7 +399,9 @@ exports.getDotCMSContentletsBound = _public.getDotCMSContentletsBound;
|
|
|
396
399
|
exports.getDotCMSPageBounds = _public.getDotCMSPageBounds;
|
|
397
400
|
exports.getDotContainerAttributes = _public.getDotContainerAttributes;
|
|
398
401
|
exports.getDotContentletAttributes = _public.getDotContentletAttributes;
|
|
402
|
+
exports.getNativeEventBinder = _public.getNativeEventBinder;
|
|
399
403
|
exports.getUVEState = _public.getUVEState;
|
|
404
|
+
exports.isDotAnalyticsActive = _public.isDotAnalyticsActive;
|
|
400
405
|
exports.isValidBlocks = _public.isValidBlocks;
|
|
401
406
|
exports.readContentletDataset = _public.readContentletDataset;
|
|
402
407
|
exports.setBounds = _public.setBounds;
|
package/internal.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { g as getUVEState, s as sendMessageToUVE } from './public.esm.js';
|
|
2
|
-
export { C as CUSTOM_NO_COMPONENT, D as DEVELOPMENT_MODE,
|
|
2
|
+
export { A as ANALYTICS_ACTIVE_WINDOW_KEY, l as ANALYTICS_READY_EVENT, C as CUSTOM_NO_COMPONENT, D as DEVELOPMENT_MODE, k as DOT_SECTION_ID_PREFIX, j as EMPTY_CONTAINER_STYLE_ANGULAR, h as EMPTY_CONTAINER_STYLE_REACT, E as END_CLASS, P as PRODUCTION_MODE, S as START_CLASS, T as TEMP_EMPTY_CONTENTLET, m as TEMP_EMPTY_CONTENTLET_TYPE, _ as __UVE_EVENTS__, f as __UVE_EVENT_ERROR_FALLBACK__, x as combineClasses, w as computeScrollIsInBottom, c as createUVESubscription, t as findDotCMSElement, v as findDotCMSVTLData, B as getAnalyticsContentletAttributes, q as getClosestDotCMSContainerData, y as getColumnPositionClasses, G as getContainersData, H as getContentletsInContainer, p as getDotCMSContainerData, o as getDotCMSContentletsBound, n as getDotCMSPageBounds, I as getDotContainerAttributes, z as getDotContentletAttributes, K as getNativeEventBinder, F as isDotAnalyticsActive, M as isValidBlocks, J as readContentletDataset, L as setBounds } from './public.esm.js';
|
|
3
3
|
import { UVE_MODE, DotCMSUVEAction } from '@dotcms/types';
|
|
4
4
|
import '@dotcms/types/internal';
|
|
5
5
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotcms/uve",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-next.36",
|
|
4
4
|
"description": "Official JavaScript library for interacting with Universal Visual Editor (UVE)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -50,4 +50,4 @@
|
|
|
50
50
|
"module": "./index.esm.js",
|
|
51
51
|
"main": "./index.cjs.js",
|
|
52
52
|
"types": "./index.d.ts"
|
|
53
|
-
}
|
|
53
|
+
}
|
package/public.cjs.js
CHANGED
|
@@ -257,6 +257,38 @@ function getDotContentletAttributes(contentlet, container) {
|
|
|
257
257
|
})
|
|
258
258
|
};
|
|
259
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
*
|
|
262
|
+
* Returns the minimal set of contentlet data attributes required by DotCMS
|
|
263
|
+
* Analytics (impression & click tracking) to identify a contentlet.
|
|
264
|
+
*
|
|
265
|
+
* Used in live mode where the full editor metadata is stripped but Analytics
|
|
266
|
+
* still needs to resolve the contentlet behind an impression/click.
|
|
267
|
+
*
|
|
268
|
+
* @param {DotCMSBasicContentlet} contentlet - The contentlet to get the attributes for
|
|
269
|
+
* @returns {DotAnalyticsContentletAttributes} The Analytics-required data attributes
|
|
270
|
+
*/
|
|
271
|
+
function getAnalyticsContentletAttributes(contentlet) {
|
|
272
|
+
return {
|
|
273
|
+
'data-dot-identifier': contentlet?.identifier,
|
|
274
|
+
'data-dot-inode': contentlet?.inode,
|
|
275
|
+
'data-dot-title': contentlet?.['widgetTitle'] || contentlet?.title,
|
|
276
|
+
'data-dot-type': contentlet?.contentType,
|
|
277
|
+
'data-dot-basetype': contentlet?.baseType
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
*
|
|
282
|
+
* Checks whether DotCMS Analytics is initialized and active on the page.
|
|
283
|
+
*
|
|
284
|
+
* The SDKs use this in live mode to decide whether to keep the minimal
|
|
285
|
+
* contentlet attributes Analytics depends on.
|
|
286
|
+
*
|
|
287
|
+
* @returns {boolean} `true` when analytics is active, otherwise `false`
|
|
288
|
+
*/
|
|
289
|
+
function isDotAnalyticsActive() {
|
|
290
|
+
return typeof window !== 'undefined' && window[ANALYTICS_ACTIVE_WINDOW_KEY] === true;
|
|
291
|
+
}
|
|
260
292
|
/**
|
|
261
293
|
*
|
|
262
294
|
*
|
|
@@ -377,6 +409,38 @@ function readContentletDataset(element) {
|
|
|
377
409
|
})
|
|
378
410
|
};
|
|
379
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* Returns Zone.js's *unpatched* native `addEventListener` / `removeEventListener`
|
|
414
|
+
* bound to `target`, falling back to the standard methods when Zone.js is absent.
|
|
415
|
+
*
|
|
416
|
+
* UVE reuses a single iframe and rewrites it with `document.open()/write()/close()`
|
|
417
|
+
* on every in-editor navigation. When Zone.js is loaded inside that iframe it runs
|
|
418
|
+
* in global-events mode: one native "gateway" listener per (target, eventType)
|
|
419
|
+
* plus a JS-level task list stored on the target node. `document.open()` tears down
|
|
420
|
+
* the native gateway on the persistent `window`/`document` nodes, but the task list
|
|
421
|
+
* survives on them, so Zone sees "already registered" on re-init and skips
|
|
422
|
+
* re-installing the native gateway — the listener silently goes dead after the
|
|
423
|
+
* first navigation.
|
|
424
|
+
*
|
|
425
|
+
* Hover/click dodge this by binding to `document.documentElement`, a node that
|
|
426
|
+
* `write()` recreates fresh. `scroll`/`message` (on `window`) and
|
|
427
|
+
* `DOMContentLoaded` (on `document`) can't: none of them fire on `<html>`, so
|
|
428
|
+
* there is no fresh node to rebind to. Going through Zone's native (untracked)
|
|
429
|
+
* methods sidesteps the dedup entirely, so the listener rebinds cleanly after
|
|
430
|
+
* every rewrite.
|
|
431
|
+
*/
|
|
432
|
+
function getNativeEventBinder(target) {
|
|
433
|
+
const zone = globalThis.Zone;
|
|
434
|
+
const symbolFor = name => typeof zone?.__symbol__ === 'function' ? zone.__symbol__(name) : `__zone_symbol__${name}`;
|
|
435
|
+
const source = target;
|
|
436
|
+
const base = target;
|
|
437
|
+
const nativeAdd = source[symbolFor('addEventListener')];
|
|
438
|
+
const nativeRemove = source[symbolFor('removeEventListener')];
|
|
439
|
+
return {
|
|
440
|
+
addEventListener: (nativeAdd ?? base.addEventListener).bind(base),
|
|
441
|
+
removeEventListener: (nativeRemove ?? base.removeEventListener).bind(base)
|
|
442
|
+
};
|
|
443
|
+
}
|
|
380
444
|
|
|
381
445
|
/**
|
|
382
446
|
* Subscribes to content changes in the UVE editor
|
|
@@ -393,10 +457,12 @@ function onContentChanges(callback) {
|
|
|
393
457
|
callback(event.data.payload);
|
|
394
458
|
}
|
|
395
459
|
};
|
|
396
|
-
|
|
460
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
461
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
462
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
397
463
|
return {
|
|
398
464
|
unsubscribe: () => {
|
|
399
|
-
|
|
465
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
400
466
|
},
|
|
401
467
|
event: types.UVEEventType.CONTENT_CHANGES
|
|
402
468
|
};
|
|
@@ -416,10 +482,12 @@ function onPageReload(callback) {
|
|
|
416
482
|
callback();
|
|
417
483
|
}
|
|
418
484
|
};
|
|
419
|
-
|
|
485
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
486
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
487
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
420
488
|
return {
|
|
421
489
|
unsubscribe: () => {
|
|
422
|
-
|
|
490
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
423
491
|
},
|
|
424
492
|
event: types.UVEEventType.PAGE_RELOAD
|
|
425
493
|
};
|
|
@@ -507,13 +575,13 @@ function onAutoBounds(callback) {
|
|
|
507
575
|
childList: true,
|
|
508
576
|
subtree: true
|
|
509
577
|
});
|
|
510
|
-
// Scrolling
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
//
|
|
514
|
-
|
|
578
|
+
// Scrolling doesn't change layout (ResizeObserver won't fire) but moves
|
|
579
|
+
// every contentlet, so re-emit bounds after the scroll settles to re-anchor
|
|
580
|
+
// the selected overlay. Native binder so it survives the iframe rewrite
|
|
581
|
+
// under Zone.js. See getNativeEventBinder.
|
|
582
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
515
583
|
const onScroll = () => scheduleEmit();
|
|
516
|
-
|
|
584
|
+
nativeWindow.addEventListener('scroll', onScroll, {
|
|
517
585
|
passive: true
|
|
518
586
|
});
|
|
519
587
|
// Flush channel: the editor occasionally needs an immediate snapshot
|
|
@@ -528,7 +596,8 @@ function onAutoBounds(callback) {
|
|
|
528
596
|
}
|
|
529
597
|
emit();
|
|
530
598
|
};
|
|
531
|
-
|
|
599
|
+
// Native binder (reused from `scroll` above): survives the iframe rewrite too.
|
|
600
|
+
nativeWindow.addEventListener('message', onFlush);
|
|
532
601
|
return {
|
|
533
602
|
unsubscribe: () => {
|
|
534
603
|
if (debounceTimer !== null) {
|
|
@@ -537,8 +606,8 @@ function onAutoBounds(callback) {
|
|
|
537
606
|
}
|
|
538
607
|
resizeObserver.disconnect();
|
|
539
608
|
mutationObserver.disconnect();
|
|
540
|
-
|
|
541
|
-
|
|
609
|
+
nativeWindow.removeEventListener('scroll', onScroll);
|
|
610
|
+
nativeWindow.removeEventListener('message', onFlush);
|
|
542
611
|
observed = [];
|
|
543
612
|
},
|
|
544
613
|
event: types.UVEEventType.AUTO_BOUNDS
|
|
@@ -560,10 +629,12 @@ function onIframeScroll(callback) {
|
|
|
560
629
|
callback(direction);
|
|
561
630
|
}
|
|
562
631
|
};
|
|
563
|
-
|
|
632
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
633
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
634
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
564
635
|
return {
|
|
565
636
|
unsubscribe: () => {
|
|
566
|
-
|
|
637
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
567
638
|
},
|
|
568
639
|
event: types.UVEEventType.IFRAME_SCROLL
|
|
569
640
|
};
|
|
@@ -593,10 +664,12 @@ function onScrollToSection(callback) {
|
|
|
593
664
|
offsetTop: el.offsetTop
|
|
594
665
|
});
|
|
595
666
|
};
|
|
596
|
-
|
|
667
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
668
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
669
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
597
670
|
return {
|
|
598
671
|
unsubscribe: () => {
|
|
599
|
-
|
|
672
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
600
673
|
},
|
|
601
674
|
event: types.UVEEventType.SCROLL_TO_SECTION
|
|
602
675
|
};
|
|
@@ -666,16 +739,18 @@ function onContentletHovered(callback) {
|
|
|
666
739
|
hasHover = true;
|
|
667
740
|
callback(contentletHoveredPayload);
|
|
668
741
|
};
|
|
669
|
-
// We intentionally do not fire null on
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
//
|
|
673
|
-
//
|
|
674
|
-
//
|
|
675
|
-
|
|
742
|
+
// We intentionally do not fire null on `pointerleave`: the editor's hover
|
|
743
|
+
// toolbar lives in the parent window, so leaving the iframe usually means
|
|
744
|
+
// the user is reaching for it — killing the overlay would yank it away.
|
|
745
|
+
//
|
|
746
|
+
// Bind to `document.documentElement` (a fresh node on each iframe rewrite)
|
|
747
|
+
// rather than `document`, which goes dead under Zone.js after a rewrite.
|
|
748
|
+
// `pointermove` bubbles to <html>, so no-Zone behavior is identical. See
|
|
749
|
+
// getNativeEventBinder for the full rationale.
|
|
750
|
+
document.documentElement.addEventListener('pointermove', pointerMoveCallback);
|
|
676
751
|
return {
|
|
677
752
|
unsubscribe: () => {
|
|
678
|
-
document.removeEventListener('pointermove', pointerMoveCallback);
|
|
753
|
+
document.documentElement.removeEventListener('pointermove', pointerMoveCallback);
|
|
679
754
|
},
|
|
680
755
|
event: types.UVEEventType.CONTENTLET_HOVERED
|
|
681
756
|
};
|
|
@@ -749,18 +824,22 @@ function onContentletClicked(callback) {
|
|
|
749
824
|
lastSelectedInode = undefined;
|
|
750
825
|
}
|
|
751
826
|
};
|
|
752
|
-
// Capture phase so we run BEFORE the page's own click handlers
|
|
753
|
-
//
|
|
754
|
-
document.
|
|
827
|
+
// Capture phase so we run BEFORE the page's own click handlers. Bound to
|
|
828
|
+
// `document.documentElement` (a fresh node on each iframe rewrite) rather
|
|
829
|
+
// than `document`, which goes dead under Zone.js after a rewrite; capture on
|
|
830
|
+
// <html> still runs first. See getNativeEventBinder for the full rationale.
|
|
831
|
+
document.documentElement.addEventListener('click', clickCallback, {
|
|
755
832
|
capture: true
|
|
756
833
|
});
|
|
757
|
-
|
|
834
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
835
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
836
|
+
nativeWindow.addEventListener('message', selectionClearedCallback);
|
|
758
837
|
return {
|
|
759
838
|
unsubscribe: () => {
|
|
760
|
-
document.removeEventListener('click', clickCallback, {
|
|
839
|
+
document.documentElement.removeEventListener('click', clickCallback, {
|
|
761
840
|
capture: true
|
|
762
841
|
});
|
|
763
|
-
|
|
842
|
+
nativeWindow.removeEventListener('message', selectionClearedCallback);
|
|
764
843
|
},
|
|
765
844
|
event: types.UVEEventType.CONTENTLET_CLICKED
|
|
766
845
|
};
|
|
@@ -885,6 +964,29 @@ const CUSTOM_NO_COMPONENT = 'CustomNoComponent';
|
|
|
885
964
|
* @internal
|
|
886
965
|
*/
|
|
887
966
|
const DOT_SECTION_ID_PREFIX = 'dot-section-';
|
|
967
|
+
/**
|
|
968
|
+
* Window flag set by `@dotcms/analytics` when content analytics is initialized
|
|
969
|
+
* and active on the page.
|
|
970
|
+
*
|
|
971
|
+
* @important This value is intentionally duplicated from `@dotcms/analytics`
|
|
972
|
+
* (`ANALYTICS_WINDOWS_ACTIVE_KEY` in dot-analytics.constants.ts). The SDKs read
|
|
973
|
+
* it in live mode to decide whether to keep the minimal contentlet attributes
|
|
974
|
+
* Analytics depends on. Both constants MUST stay in sync.
|
|
975
|
+
*
|
|
976
|
+
* @internal
|
|
977
|
+
*/
|
|
978
|
+
const ANALYTICS_ACTIVE_WINDOW_KEY = '__dotAnalyticsActive__';
|
|
979
|
+
/**
|
|
980
|
+
* Event dispatched by `@dotcms/analytics` once analytics is ready. The SDKs
|
|
981
|
+
* listen for it so live-mode contentlets can re-render with the attributes
|
|
982
|
+
* Analytics needs, regardless of initialization order.
|
|
983
|
+
*
|
|
984
|
+
* @important Kept in sync with the `dotcms:analytics:ready` event dispatched by
|
|
985
|
+
* `@dotcms/analytics` (initializeContentAnalytics).
|
|
986
|
+
*
|
|
987
|
+
* @internal
|
|
988
|
+
*/
|
|
989
|
+
const ANALYTICS_READY_EVENT = 'dotcms:analytics:ready';
|
|
888
990
|
|
|
889
991
|
/**
|
|
890
992
|
* Gets the current state of the Universal Visual Editor (UVE).
|
|
@@ -1073,7 +1175,6 @@ const isValidBlocks = blocks => {
|
|
|
1073
1175
|
};
|
|
1074
1176
|
};
|
|
1075
1177
|
|
|
1076
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1077
1178
|
/**
|
|
1078
1179
|
* Sets up scroll event handlers for the window to notify the editor about scroll events.
|
|
1079
1180
|
* Adds listeners for both 'scroll' and 'scrollend' events, sending appropriate messages
|
|
@@ -1090,12 +1191,19 @@ function scrollHandler() {
|
|
|
1090
1191
|
action: types.DotCMSUVEAction.IFRAME_SCROLL_END
|
|
1091
1192
|
});
|
|
1092
1193
|
};
|
|
1093
|
-
|
|
1094
|
-
|
|
1194
|
+
// Native binder: scroll survives the iframe rewrite under Zone.js. It can't
|
|
1195
|
+
// move to a fresh `documentElement` like hover/click (viewport scroll only
|
|
1196
|
+
// fires on `window`). See getNativeEventBinder.
|
|
1197
|
+
const {
|
|
1198
|
+
addEventListener,
|
|
1199
|
+
removeEventListener
|
|
1200
|
+
} = getNativeEventBinder(window);
|
|
1201
|
+
addEventListener('scroll', scrollCallback);
|
|
1202
|
+
addEventListener('scrollend', scrollEndCallback);
|
|
1095
1203
|
return {
|
|
1096
1204
|
destroyScrollHandler: () => {
|
|
1097
|
-
|
|
1098
|
-
|
|
1205
|
+
removeEventListener('scroll', scrollCallback);
|
|
1206
|
+
removeEventListener('scrollend', scrollEndCallback);
|
|
1099
1207
|
}
|
|
1100
1208
|
};
|
|
1101
1209
|
}
|
|
@@ -1209,14 +1317,19 @@ function listenBlockEditorInlineEvent() {
|
|
|
1209
1317
|
}
|
|
1210
1318
|
};
|
|
1211
1319
|
}
|
|
1212
|
-
//
|
|
1320
|
+
// Native binder: `DOMContentLoaded` fires on `document` (not `<html>`), so
|
|
1321
|
+
// it survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
1213
1322
|
const handleDOMContentLoaded = () => {
|
|
1214
1323
|
listenBlockEditorClick();
|
|
1215
1324
|
};
|
|
1216
|
-
|
|
1325
|
+
const {
|
|
1326
|
+
addEventListener,
|
|
1327
|
+
removeEventListener
|
|
1328
|
+
} = getNativeEventBinder(document);
|
|
1329
|
+
addEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1217
1330
|
return {
|
|
1218
1331
|
destroyListenBlockEditorInlineEvent: () => {
|
|
1219
|
-
|
|
1332
|
+
removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1220
1333
|
}
|
|
1221
1334
|
};
|
|
1222
1335
|
}
|
|
@@ -1434,6 +1547,8 @@ function initUVE(config = {}) {
|
|
|
1434
1547
|
};
|
|
1435
1548
|
}
|
|
1436
1549
|
|
|
1550
|
+
exports.ANALYTICS_ACTIVE_WINDOW_KEY = ANALYTICS_ACTIVE_WINDOW_KEY;
|
|
1551
|
+
exports.ANALYTICS_READY_EVENT = ANALYTICS_READY_EVENT;
|
|
1437
1552
|
exports.CUSTOM_NO_COMPONENT = CUSTOM_NO_COMPONENT;
|
|
1438
1553
|
exports.DEVELOPMENT_MODE = DEVELOPMENT_MODE;
|
|
1439
1554
|
exports.DOT_SECTION_ID_PREFIX = DOT_SECTION_ID_PREFIX;
|
|
@@ -1454,6 +1569,7 @@ exports.editContentlet = editContentlet;
|
|
|
1454
1569
|
exports.enableBlockEditorInline = enableBlockEditorInline;
|
|
1455
1570
|
exports.findDotCMSElement = findDotCMSElement;
|
|
1456
1571
|
exports.findDotCMSVTLData = findDotCMSVTLData;
|
|
1572
|
+
exports.getAnalyticsContentletAttributes = getAnalyticsContentletAttributes;
|
|
1457
1573
|
exports.getClosestDotCMSContainerData = getClosestDotCMSContainerData;
|
|
1458
1574
|
exports.getColumnPositionClasses = getColumnPositionClasses;
|
|
1459
1575
|
exports.getContainersData = getContainersData;
|
|
@@ -1463,9 +1579,11 @@ exports.getDotCMSContentletsBound = getDotCMSContentletsBound;
|
|
|
1463
1579
|
exports.getDotCMSPageBounds = getDotCMSPageBounds;
|
|
1464
1580
|
exports.getDotContainerAttributes = getDotContainerAttributes;
|
|
1465
1581
|
exports.getDotContentletAttributes = getDotContentletAttributes;
|
|
1582
|
+
exports.getNativeEventBinder = getNativeEventBinder;
|
|
1466
1583
|
exports.getUVEState = getUVEState;
|
|
1467
1584
|
exports.initInlineEditing = initInlineEditing;
|
|
1468
1585
|
exports.initUVE = initUVE;
|
|
1586
|
+
exports.isDotAnalyticsActive = isDotAnalyticsActive;
|
|
1469
1587
|
exports.isValidBlocks = isValidBlocks;
|
|
1470
1588
|
exports.readContentletDataset = readContentletDataset;
|
|
1471
1589
|
exports.reorderMenu = reorderMenu;
|
package/public.esm.js
CHANGED
|
@@ -255,6 +255,38 @@ function getDotContentletAttributes(contentlet, container) {
|
|
|
255
255
|
})
|
|
256
256
|
};
|
|
257
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
*
|
|
260
|
+
* Returns the minimal set of contentlet data attributes required by DotCMS
|
|
261
|
+
* Analytics (impression & click tracking) to identify a contentlet.
|
|
262
|
+
*
|
|
263
|
+
* Used in live mode where the full editor metadata is stripped but Analytics
|
|
264
|
+
* still needs to resolve the contentlet behind an impression/click.
|
|
265
|
+
*
|
|
266
|
+
* @param {DotCMSBasicContentlet} contentlet - The contentlet to get the attributes for
|
|
267
|
+
* @returns {DotAnalyticsContentletAttributes} The Analytics-required data attributes
|
|
268
|
+
*/
|
|
269
|
+
function getAnalyticsContentletAttributes(contentlet) {
|
|
270
|
+
return {
|
|
271
|
+
'data-dot-identifier': contentlet?.identifier,
|
|
272
|
+
'data-dot-inode': contentlet?.inode,
|
|
273
|
+
'data-dot-title': contentlet?.['widgetTitle'] || contentlet?.title,
|
|
274
|
+
'data-dot-type': contentlet?.contentType,
|
|
275
|
+
'data-dot-basetype': contentlet?.baseType
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
*
|
|
280
|
+
* Checks whether DotCMS Analytics is initialized and active on the page.
|
|
281
|
+
*
|
|
282
|
+
* The SDKs use this in live mode to decide whether to keep the minimal
|
|
283
|
+
* contentlet attributes Analytics depends on.
|
|
284
|
+
*
|
|
285
|
+
* @returns {boolean} `true` when analytics is active, otherwise `false`
|
|
286
|
+
*/
|
|
287
|
+
function isDotAnalyticsActive() {
|
|
288
|
+
return typeof window !== 'undefined' && window[ANALYTICS_ACTIVE_WINDOW_KEY] === true;
|
|
289
|
+
}
|
|
258
290
|
/**
|
|
259
291
|
*
|
|
260
292
|
*
|
|
@@ -375,6 +407,38 @@ function readContentletDataset(element) {
|
|
|
375
407
|
})
|
|
376
408
|
};
|
|
377
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Returns Zone.js's *unpatched* native `addEventListener` / `removeEventListener`
|
|
412
|
+
* bound to `target`, falling back to the standard methods when Zone.js is absent.
|
|
413
|
+
*
|
|
414
|
+
* UVE reuses a single iframe and rewrites it with `document.open()/write()/close()`
|
|
415
|
+
* on every in-editor navigation. When Zone.js is loaded inside that iframe it runs
|
|
416
|
+
* in global-events mode: one native "gateway" listener per (target, eventType)
|
|
417
|
+
* plus a JS-level task list stored on the target node. `document.open()` tears down
|
|
418
|
+
* the native gateway on the persistent `window`/`document` nodes, but the task list
|
|
419
|
+
* survives on them, so Zone sees "already registered" on re-init and skips
|
|
420
|
+
* re-installing the native gateway — the listener silently goes dead after the
|
|
421
|
+
* first navigation.
|
|
422
|
+
*
|
|
423
|
+
* Hover/click dodge this by binding to `document.documentElement`, a node that
|
|
424
|
+
* `write()` recreates fresh. `scroll`/`message` (on `window`) and
|
|
425
|
+
* `DOMContentLoaded` (on `document`) can't: none of them fire on `<html>`, so
|
|
426
|
+
* there is no fresh node to rebind to. Going through Zone's native (untracked)
|
|
427
|
+
* methods sidesteps the dedup entirely, so the listener rebinds cleanly after
|
|
428
|
+
* every rewrite.
|
|
429
|
+
*/
|
|
430
|
+
function getNativeEventBinder(target) {
|
|
431
|
+
const zone = globalThis.Zone;
|
|
432
|
+
const symbolFor = name => typeof zone?.__symbol__ === 'function' ? zone.__symbol__(name) : `__zone_symbol__${name}`;
|
|
433
|
+
const source = target;
|
|
434
|
+
const base = target;
|
|
435
|
+
const nativeAdd = source[symbolFor('addEventListener')];
|
|
436
|
+
const nativeRemove = source[symbolFor('removeEventListener')];
|
|
437
|
+
return {
|
|
438
|
+
addEventListener: (nativeAdd ?? base.addEventListener).bind(base),
|
|
439
|
+
removeEventListener: (nativeRemove ?? base.removeEventListener).bind(base)
|
|
440
|
+
};
|
|
441
|
+
}
|
|
378
442
|
|
|
379
443
|
/**
|
|
380
444
|
* Subscribes to content changes in the UVE editor
|
|
@@ -391,10 +455,12 @@ function onContentChanges(callback) {
|
|
|
391
455
|
callback(event.data.payload);
|
|
392
456
|
}
|
|
393
457
|
};
|
|
394
|
-
|
|
458
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
459
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
460
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
395
461
|
return {
|
|
396
462
|
unsubscribe: () => {
|
|
397
|
-
|
|
463
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
398
464
|
},
|
|
399
465
|
event: UVEEventType.CONTENT_CHANGES
|
|
400
466
|
};
|
|
@@ -414,10 +480,12 @@ function onPageReload(callback) {
|
|
|
414
480
|
callback();
|
|
415
481
|
}
|
|
416
482
|
};
|
|
417
|
-
|
|
483
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
484
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
485
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
418
486
|
return {
|
|
419
487
|
unsubscribe: () => {
|
|
420
|
-
|
|
488
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
421
489
|
},
|
|
422
490
|
event: UVEEventType.PAGE_RELOAD
|
|
423
491
|
};
|
|
@@ -505,13 +573,13 @@ function onAutoBounds(callback) {
|
|
|
505
573
|
childList: true,
|
|
506
574
|
subtree: true
|
|
507
575
|
});
|
|
508
|
-
// Scrolling
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
|
|
576
|
+
// Scrolling doesn't change layout (ResizeObserver won't fire) but moves
|
|
577
|
+
// every contentlet, so re-emit bounds after the scroll settles to re-anchor
|
|
578
|
+
// the selected overlay. Native binder so it survives the iframe rewrite
|
|
579
|
+
// under Zone.js. See getNativeEventBinder.
|
|
580
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
513
581
|
const onScroll = () => scheduleEmit();
|
|
514
|
-
|
|
582
|
+
nativeWindow.addEventListener('scroll', onScroll, {
|
|
515
583
|
passive: true
|
|
516
584
|
});
|
|
517
585
|
// Flush channel: the editor occasionally needs an immediate snapshot
|
|
@@ -526,7 +594,8 @@ function onAutoBounds(callback) {
|
|
|
526
594
|
}
|
|
527
595
|
emit();
|
|
528
596
|
};
|
|
529
|
-
|
|
597
|
+
// Native binder (reused from `scroll` above): survives the iframe rewrite too.
|
|
598
|
+
nativeWindow.addEventListener('message', onFlush);
|
|
530
599
|
return {
|
|
531
600
|
unsubscribe: () => {
|
|
532
601
|
if (debounceTimer !== null) {
|
|
@@ -535,8 +604,8 @@ function onAutoBounds(callback) {
|
|
|
535
604
|
}
|
|
536
605
|
resizeObserver.disconnect();
|
|
537
606
|
mutationObserver.disconnect();
|
|
538
|
-
|
|
539
|
-
|
|
607
|
+
nativeWindow.removeEventListener('scroll', onScroll);
|
|
608
|
+
nativeWindow.removeEventListener('message', onFlush);
|
|
540
609
|
observed = [];
|
|
541
610
|
},
|
|
542
611
|
event: UVEEventType.AUTO_BOUNDS
|
|
@@ -558,10 +627,12 @@ function onIframeScroll(callback) {
|
|
|
558
627
|
callback(direction);
|
|
559
628
|
}
|
|
560
629
|
};
|
|
561
|
-
|
|
630
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
631
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
632
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
562
633
|
return {
|
|
563
634
|
unsubscribe: () => {
|
|
564
|
-
|
|
635
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
565
636
|
},
|
|
566
637
|
event: UVEEventType.IFRAME_SCROLL
|
|
567
638
|
};
|
|
@@ -591,10 +662,12 @@ function onScrollToSection(callback) {
|
|
|
591
662
|
offsetTop: el.offsetTop
|
|
592
663
|
});
|
|
593
664
|
};
|
|
594
|
-
|
|
665
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
666
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
667
|
+
nativeWindow.addEventListener('message', messageCallback);
|
|
595
668
|
return {
|
|
596
669
|
unsubscribe: () => {
|
|
597
|
-
|
|
670
|
+
nativeWindow.removeEventListener('message', messageCallback);
|
|
598
671
|
},
|
|
599
672
|
event: UVEEventType.SCROLL_TO_SECTION
|
|
600
673
|
};
|
|
@@ -664,16 +737,18 @@ function onContentletHovered(callback) {
|
|
|
664
737
|
hasHover = true;
|
|
665
738
|
callback(contentletHoveredPayload);
|
|
666
739
|
};
|
|
667
|
-
// We intentionally do not fire null on
|
|
668
|
-
//
|
|
669
|
-
//
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
//
|
|
673
|
-
|
|
740
|
+
// We intentionally do not fire null on `pointerleave`: the editor's hover
|
|
741
|
+
// toolbar lives in the parent window, so leaving the iframe usually means
|
|
742
|
+
// the user is reaching for it — killing the overlay would yank it away.
|
|
743
|
+
//
|
|
744
|
+
// Bind to `document.documentElement` (a fresh node on each iframe rewrite)
|
|
745
|
+
// rather than `document`, which goes dead under Zone.js after a rewrite.
|
|
746
|
+
// `pointermove` bubbles to <html>, so no-Zone behavior is identical. See
|
|
747
|
+
// getNativeEventBinder for the full rationale.
|
|
748
|
+
document.documentElement.addEventListener('pointermove', pointerMoveCallback);
|
|
674
749
|
return {
|
|
675
750
|
unsubscribe: () => {
|
|
676
|
-
document.removeEventListener('pointermove', pointerMoveCallback);
|
|
751
|
+
document.documentElement.removeEventListener('pointermove', pointerMoveCallback);
|
|
677
752
|
},
|
|
678
753
|
event: UVEEventType.CONTENTLET_HOVERED
|
|
679
754
|
};
|
|
@@ -747,18 +822,22 @@ function onContentletClicked(callback) {
|
|
|
747
822
|
lastSelectedInode = undefined;
|
|
748
823
|
}
|
|
749
824
|
};
|
|
750
|
-
// Capture phase so we run BEFORE the page's own click handlers
|
|
751
|
-
//
|
|
752
|
-
document.
|
|
825
|
+
// Capture phase so we run BEFORE the page's own click handlers. Bound to
|
|
826
|
+
// `document.documentElement` (a fresh node on each iframe rewrite) rather
|
|
827
|
+
// than `document`, which goes dead under Zone.js after a rewrite; capture on
|
|
828
|
+
// <html> still runs first. See getNativeEventBinder for the full rationale.
|
|
829
|
+
document.documentElement.addEventListener('click', clickCallback, {
|
|
753
830
|
capture: true
|
|
754
831
|
});
|
|
755
|
-
|
|
832
|
+
// Native binder: survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
833
|
+
const nativeWindow = getNativeEventBinder(window);
|
|
834
|
+
nativeWindow.addEventListener('message', selectionClearedCallback);
|
|
756
835
|
return {
|
|
757
836
|
unsubscribe: () => {
|
|
758
|
-
document.removeEventListener('click', clickCallback, {
|
|
837
|
+
document.documentElement.removeEventListener('click', clickCallback, {
|
|
759
838
|
capture: true
|
|
760
839
|
});
|
|
761
|
-
|
|
840
|
+
nativeWindow.removeEventListener('message', selectionClearedCallback);
|
|
762
841
|
},
|
|
763
842
|
event: UVEEventType.CONTENTLET_CLICKED
|
|
764
843
|
};
|
|
@@ -883,6 +962,29 @@ const CUSTOM_NO_COMPONENT = 'CustomNoComponent';
|
|
|
883
962
|
* @internal
|
|
884
963
|
*/
|
|
885
964
|
const DOT_SECTION_ID_PREFIX = 'dot-section-';
|
|
965
|
+
/**
|
|
966
|
+
* Window flag set by `@dotcms/analytics` when content analytics is initialized
|
|
967
|
+
* and active on the page.
|
|
968
|
+
*
|
|
969
|
+
* @important This value is intentionally duplicated from `@dotcms/analytics`
|
|
970
|
+
* (`ANALYTICS_WINDOWS_ACTIVE_KEY` in dot-analytics.constants.ts). The SDKs read
|
|
971
|
+
* it in live mode to decide whether to keep the minimal contentlet attributes
|
|
972
|
+
* Analytics depends on. Both constants MUST stay in sync.
|
|
973
|
+
*
|
|
974
|
+
* @internal
|
|
975
|
+
*/
|
|
976
|
+
const ANALYTICS_ACTIVE_WINDOW_KEY = '__dotAnalyticsActive__';
|
|
977
|
+
/**
|
|
978
|
+
* Event dispatched by `@dotcms/analytics` once analytics is ready. The SDKs
|
|
979
|
+
* listen for it so live-mode contentlets can re-render with the attributes
|
|
980
|
+
* Analytics needs, regardless of initialization order.
|
|
981
|
+
*
|
|
982
|
+
* @important Kept in sync with the `dotcms:analytics:ready` event dispatched by
|
|
983
|
+
* `@dotcms/analytics` (initializeContentAnalytics).
|
|
984
|
+
*
|
|
985
|
+
* @internal
|
|
986
|
+
*/
|
|
987
|
+
const ANALYTICS_READY_EVENT = 'dotcms:analytics:ready';
|
|
886
988
|
|
|
887
989
|
/**
|
|
888
990
|
* Gets the current state of the Universal Visual Editor (UVE).
|
|
@@ -1071,7 +1173,6 @@ const isValidBlocks = blocks => {
|
|
|
1071
1173
|
};
|
|
1072
1174
|
};
|
|
1073
1175
|
|
|
1074
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1075
1176
|
/**
|
|
1076
1177
|
* Sets up scroll event handlers for the window to notify the editor about scroll events.
|
|
1077
1178
|
* Adds listeners for both 'scroll' and 'scrollend' events, sending appropriate messages
|
|
@@ -1088,12 +1189,19 @@ function scrollHandler() {
|
|
|
1088
1189
|
action: DotCMSUVEAction.IFRAME_SCROLL_END
|
|
1089
1190
|
});
|
|
1090
1191
|
};
|
|
1091
|
-
|
|
1092
|
-
|
|
1192
|
+
// Native binder: scroll survives the iframe rewrite under Zone.js. It can't
|
|
1193
|
+
// move to a fresh `documentElement` like hover/click (viewport scroll only
|
|
1194
|
+
// fires on `window`). See getNativeEventBinder.
|
|
1195
|
+
const {
|
|
1196
|
+
addEventListener,
|
|
1197
|
+
removeEventListener
|
|
1198
|
+
} = getNativeEventBinder(window);
|
|
1199
|
+
addEventListener('scroll', scrollCallback);
|
|
1200
|
+
addEventListener('scrollend', scrollEndCallback);
|
|
1093
1201
|
return {
|
|
1094
1202
|
destroyScrollHandler: () => {
|
|
1095
|
-
|
|
1096
|
-
|
|
1203
|
+
removeEventListener('scroll', scrollCallback);
|
|
1204
|
+
removeEventListener('scrollend', scrollEndCallback);
|
|
1097
1205
|
}
|
|
1098
1206
|
};
|
|
1099
1207
|
}
|
|
@@ -1207,14 +1315,19 @@ function listenBlockEditorInlineEvent() {
|
|
|
1207
1315
|
}
|
|
1208
1316
|
};
|
|
1209
1317
|
}
|
|
1210
|
-
//
|
|
1318
|
+
// Native binder: `DOMContentLoaded` fires on `document` (not `<html>`), so
|
|
1319
|
+
// it survives the iframe rewrite under Zone.js. See getNativeEventBinder.
|
|
1211
1320
|
const handleDOMContentLoaded = () => {
|
|
1212
1321
|
listenBlockEditorClick();
|
|
1213
1322
|
};
|
|
1214
|
-
|
|
1323
|
+
const {
|
|
1324
|
+
addEventListener,
|
|
1325
|
+
removeEventListener
|
|
1326
|
+
} = getNativeEventBinder(document);
|
|
1327
|
+
addEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1215
1328
|
return {
|
|
1216
1329
|
destroyListenBlockEditorInlineEvent: () => {
|
|
1217
|
-
|
|
1330
|
+
removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
1218
1331
|
}
|
|
1219
1332
|
};
|
|
1220
1333
|
}
|
|
@@ -1432,4 +1545,4 @@ function initUVE(config = {}) {
|
|
|
1432
1545
|
};
|
|
1433
1546
|
}
|
|
1434
1547
|
|
|
1435
|
-
export {
|
|
1548
|
+
export { ANALYTICS_ACTIVE_WINDOW_KEY as A, getAnalyticsContentletAttributes as B, CUSTOM_NO_COMPONENT as C, DEVELOPMENT_MODE as D, END_CLASS as E, isDotAnalyticsActive as F, getContainersData as G, getContentletsInContainer as H, getDotContainerAttributes as I, readContentletDataset as J, getNativeEventBinder as K, setBounds as L, isValidBlocks as M, PRODUCTION_MODE as P, START_CLASS as S, TEMP_EMPTY_CONTENTLET as T, __UVE_EVENTS__ as _, enableBlockEditorInline as a, createContentlet as b, createUVESubscription as c, initUVE as d, editContentlet as e, __UVE_EVENT_ERROR_FALLBACK__ as f, getUVEState as g, EMPTY_CONTAINER_STYLE_REACT as h, initInlineEditing as i, EMPTY_CONTAINER_STYLE_ANGULAR as j, DOT_SECTION_ID_PREFIX as k, ANALYTICS_READY_EVENT as l, TEMP_EMPTY_CONTENTLET_TYPE as m, getDotCMSPageBounds as n, getDotCMSContentletsBound as o, getDotCMSContainerData as p, getClosestDotCMSContainerData as q, reorderMenu as r, sendMessageToUVE as s, findDotCMSElement as t, updateNavigation as u, findDotCMSVTLData as v, computeScrollIsInBottom as w, combineClasses as x, getColumnPositionClasses as y, getDotContentletAttributes as z };
|
|
@@ -81,3 +81,26 @@ export declare const CUSTOM_NO_COMPONENT = "CustomNoComponent";
|
|
|
81
81
|
* @internal
|
|
82
82
|
*/
|
|
83
83
|
export declare const DOT_SECTION_ID_PREFIX = "dot-section-";
|
|
84
|
+
/**
|
|
85
|
+
* Window flag set by `@dotcms/analytics` when content analytics is initialized
|
|
86
|
+
* and active on the page.
|
|
87
|
+
*
|
|
88
|
+
* @important This value is intentionally duplicated from `@dotcms/analytics`
|
|
89
|
+
* (`ANALYTICS_WINDOWS_ACTIVE_KEY` in dot-analytics.constants.ts). The SDKs read
|
|
90
|
+
* it in live mode to decide whether to keep the minimal contentlet attributes
|
|
91
|
+
* Analytics depends on. Both constants MUST stay in sync.
|
|
92
|
+
*
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
export declare const ANALYTICS_ACTIVE_WINDOW_KEY = "__dotAnalyticsActive__";
|
|
96
|
+
/**
|
|
97
|
+
* Event dispatched by `@dotcms/analytics` once analytics is ready. The SDKs
|
|
98
|
+
* listen for it so live-mode contentlets can re-render with the attributes
|
|
99
|
+
* Analytics needs, regardless of initialization order.
|
|
100
|
+
*
|
|
101
|
+
* @important Kept in sync with the `dotcms:analytics:ready` event dispatched by
|
|
102
|
+
* `@dotcms/analytics` (initializeContentAnalytics).
|
|
103
|
+
*
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
export declare const ANALYTICS_READY_EVENT = "dotcms:analytics:ready";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DotCMSBasicContentlet, DotCMSColumnContainer, DotCMSPageAsset, DotPageAssetLayoutColumn, EditableContainerData } from '@dotcms/types';
|
|
2
|
-
import { DotCMSContainerBound, DotCMSContentletBound, DotContainerAttributes, DotContentletAttributes } from '@dotcms/types/internal';
|
|
2
|
+
import { DotAnalyticsContentletAttributes, DotCMSContainerBound, DotCMSContentletBound, DotContainerAttributes, DotContentletAttributes } from '@dotcms/types/internal';
|
|
3
3
|
/**
|
|
4
4
|
* Calculates the bounding information for each page element within the given containers.
|
|
5
5
|
*
|
|
@@ -152,6 +152,28 @@ export declare const getColumnPositionClasses: (column: DotPageAssetLayoutColumn
|
|
|
152
152
|
* @returns {DotContentletAttributes} The dotCMS data attributes
|
|
153
153
|
*/
|
|
154
154
|
export declare function getDotContentletAttributes(contentlet: DotCMSBasicContentlet, container: string): DotContentletAttributes;
|
|
155
|
+
/**
|
|
156
|
+
*
|
|
157
|
+
* Returns the minimal set of contentlet data attributes required by DotCMS
|
|
158
|
+
* Analytics (impression & click tracking) to identify a contentlet.
|
|
159
|
+
*
|
|
160
|
+
* Used in live mode where the full editor metadata is stripped but Analytics
|
|
161
|
+
* still needs to resolve the contentlet behind an impression/click.
|
|
162
|
+
*
|
|
163
|
+
* @param {DotCMSBasicContentlet} contentlet - The contentlet to get the attributes for
|
|
164
|
+
* @returns {DotAnalyticsContentletAttributes} The Analytics-required data attributes
|
|
165
|
+
*/
|
|
166
|
+
export declare function getAnalyticsContentletAttributes(contentlet: DotCMSBasicContentlet): DotAnalyticsContentletAttributes;
|
|
167
|
+
/**
|
|
168
|
+
*
|
|
169
|
+
* Checks whether DotCMS Analytics is initialized and active on the page.
|
|
170
|
+
*
|
|
171
|
+
* The SDKs use this in live mode to decide whether to keep the minimal
|
|
172
|
+
* contentlet attributes Analytics depends on.
|
|
173
|
+
*
|
|
174
|
+
* @returns {boolean} `true` when analytics is active, otherwise `false`
|
|
175
|
+
*/
|
|
176
|
+
export declare function isDotAnalyticsActive(): boolean;
|
|
155
177
|
/**
|
|
156
178
|
*
|
|
157
179
|
*
|
|
@@ -219,3 +241,24 @@ export declare function readContentletDataset(element: HTMLElement): {
|
|
|
219
241
|
widgetTitle: string | undefined;
|
|
220
242
|
onNumberOfPages: string | undefined;
|
|
221
243
|
};
|
|
244
|
+
/**
|
|
245
|
+
* Returns Zone.js's *unpatched* native `addEventListener` / `removeEventListener`
|
|
246
|
+
* bound to `target`, falling back to the standard methods when Zone.js is absent.
|
|
247
|
+
*
|
|
248
|
+
* UVE reuses a single iframe and rewrites it with `document.open()/write()/close()`
|
|
249
|
+
* on every in-editor navigation. When Zone.js is loaded inside that iframe it runs
|
|
250
|
+
* in global-events mode: one native "gateway" listener per (target, eventType)
|
|
251
|
+
* plus a JS-level task list stored on the target node. `document.open()` tears down
|
|
252
|
+
* the native gateway on the persistent `window`/`document` nodes, but the task list
|
|
253
|
+
* survives on them, so Zone sees "already registered" on re-init and skips
|
|
254
|
+
* re-installing the native gateway — the listener silently goes dead after the
|
|
255
|
+
* first navigation.
|
|
256
|
+
*
|
|
257
|
+
* Hover/click dodge this by binding to `document.documentElement`, a node that
|
|
258
|
+
* `write()` recreates fresh. `scroll`/`message` (on `window`) and
|
|
259
|
+
* `DOMContentLoaded` (on `document`) can't: none of them fire on `<html>`, so
|
|
260
|
+
* there is no fresh node to rebind to. Going through Zone's native (untracked)
|
|
261
|
+
* methods sidesteps the dedup entirely, so the listener rebinds cleanly after
|
|
262
|
+
* every rewrite.
|
|
263
|
+
*/
|
|
264
|
+
export declare function getNativeEventBinder<T extends Window | Document>(target: T): Pick<T, 'addEventListener' | 'removeEventListener'>;
|