@dotcms/uve 0.0.1-beta.11 → 0.0.1-beta.13

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 CHANGED
@@ -1,7 +1,149 @@
1
- # sdk-uve
1
+ # DotCMS UVE SDK
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ A JavaScript library to connect your dotCMS pages with the Universal Visual Editor (UVE) and enable content authors to edit pages in real time.
4
4
 
5
- ## Running unit tests
5
+ ## Installation
6
6
 
7
- Run `nx test sdk-uve` to execute the unit tests via [Jest](https://jestjs.io).
7
+ The UVE SDK is automatically included in DotCMS installations. For external usage:
8
+
9
+ ```bash
10
+ yarn add @dotcms/uve-sdk
11
+ ```
12
+
13
+
14
+ ## Entry Points
15
+
16
+ The library exposes three main entry points:
17
+
18
+ - **`@dotcms/uve`**: Provides everything developers need to communicate with UVE.
19
+
20
+ - **`@dotcms/uve/types`**: Offers TypeScript types, interfaces, and other structures to help users organize their code properly.
21
+
22
+ ---
23
+
24
+ ## Functions
25
+
26
+ ### `createUVESubscription`
27
+
28
+ Subscribe to the pages changes. Receive a callback that will be called with the updated content of the page.
29
+
30
+ **Parameters:**
31
+ - `eventType` - The type of event to subscribe to.
32
+ - `callback` - The callback function that will be called when the event occurs.
33
+
34
+ **Returns:**
35
+ - An event subscription that can be used to unsubscribe.
36
+
37
+ **Example:**
38
+ ***typescript
39
+ // Subscribe to page changes
40
+ const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
41
+ console.log('Content changes:', changes);
42
+ });
43
+
44
+ // Unsubscribe
45
+
46
+ subscription.unsubscribe()
47
+ ```
48
+
49
+ ---
50
+
51
+ ### `getUVEState`
52
+
53
+ Retrieves the current UVE state.
54
+
55
+ **Returns:**
56
+ - A `UVEState` object if running inside the editor, or `undefined` otherwise.
57
+
58
+ The state includes:
59
+ - `mode`: The current editor mode (preview, edit, live).
60
+ - `languageId`: The language ID of the current page set in the UVE.
61
+ - `persona`: The persona of the current page set in the UVE.
62
+ - `variantName`: The name of the current variant.
63
+ - `experimentId`: The ID of the current experiment.
64
+ - `publishDate`: The publish date of the current page set in the UVE.
65
+
66
+ > **Note:** If any of these properties are absent, it means the value is the default one.
67
+
68
+ **Example:**
69
+ ```typescript
70
+ const editorState = getUVEState();
71
+ if (editorState?.mode === 'edit') {
72
+ // Enable editing features
73
+ }
74
+ ```
75
+
76
+ ---
77
+
78
+ ### `editContentlet`
79
+
80
+ Allows you to edit a contentlet in the editor.
81
+
82
+ Calling this function within the editor prompts the UVE to open a dialog to edit the specified contentlet.
83
+
84
+ **Parameters:**
85
+ - `contentlet<T>` - The contentlet to edit.
86
+
87
+ **Example:**
88
+ ```typescript
89
+ editContentlet(myContentlet);
90
+ ```
91
+
92
+ ---
93
+
94
+ ### `reorderMenu`
95
+
96
+ Reorders the menu based on the provided configuration.
97
+
98
+ **Parameters:**
99
+ - `config` (optional): Configuration for reordering the menu.
100
+ - `startLevel` (default: `1`): The starting level of the menu to reorder.
101
+ - `depth` (default: `2`): The depth of the menu to reorder.
102
+
103
+ This function constructs a URL for the reorder menu page with the specified `startLevel` and `depth`, then sends a message to the editor to perform the reorder action.
104
+
105
+ **Example:**
106
+ ```typescript
107
+ reorderMenu({ startLevel: 2, depth: 3 });
108
+ ```
109
+
110
+ ---
111
+
112
+ ### `initInlineEditing`
113
+
114
+ Initializes inline editing in the editor.
115
+
116
+ **Example:**
117
+ ```typescript
118
+ initInlineEditing();
119
+ ```
120
+
121
+ ---
122
+
123
+ ### `sendMessageToUVE`
124
+
125
+ The `sendMessageToUVE` function allows you to send messages to the dotCMS page editor. This is useful for triggering specific actions or updating the editor's state.
126
+
127
+ This function is primarily used within other libraries but can be helpful if you need to trigger specific behavior by sending a message to the UVE.
128
+
129
+ **Example:**
130
+ ```typescript
131
+ sendMessageToEditor({ type: 'CUSTOM_MESSAGE': DotCMSUVEAction, payload: { key: 'value' } });
132
+ ```
133
+
134
+ ### Available Message Types (DotCMSUVEAction)
135
+
136
+ | **Type** | **Description** |
137
+ |--------------------------------------|---------------------------------------------------------------------------------------------------|
138
+ | `set-url` | Notifies the dotCMS editor that the page has changed. |
139
+ | `set-bounds` | Sends the position of rows, columns, containers, and contentlets to the editor. |
140
+ | `set-contentlet` | Sends information about the currently hovered contentlet. |
141
+ | `scroll` | Informs the editor that the page is being scrolled. |
142
+ | `scroll-end` | Notifies the editor that scrolling has stopped.
143
+ | `init-inline-editing` | Initializes the inline editing mode in the editor. |
144
+ | `copy-contentlet-inline-editing` | Opens the "Copy Contentlet" dialog to duplicate and edit a contentlet inline. |
145
+ | `update-contentlet-inline-editing` | Triggers the save action for inline-edited contentlets. |
146
+ | `reorder-menu` | Triggers the menu reorder action with a specified configuration. |
147
+ | `get-page-data` | Requests the current page information from the editor. |
148
+ | `client-ready` | Indicates that the client has sent a GraphQL query to the editor. |
149
+ | `edit-contentlet` | Opens the contentlet editing dialog in the editor. |
package/index.cjs.js CHANGED
@@ -58,209 +58,110 @@ function getUVEState() {
58
58
  /**
59
59
  * Creates a subscription to a UVE event.
60
60
  *
61
- * @param {string} event - The event to subscribe to.
62
- * @param {UVECallback} callback - The callback to call when the event is triggered.
63
- * @return {UnsubscribeUVE | undefined} The unsubscribe function if the event is valid, undefined otherwise.
61
+ * @param eventType - The type of event to subscribe to
62
+ * @param callback - The callback function that will be called when the event occurs
63
+ * @returns An event subscription that can be used to unsubscribe
64
64
  *
65
65
  * @example
66
66
  * ```ts
67
- * const unsubscribeChanges = createUVESubscription('changes', (payload) => {
68
- * console.log(payload);
67
+ * // Subscribe to page changes
68
+ * const subscription = createUVESubscription(UVEEventType.CONTENT_CHANGES, (changes) => {
69
+ * console.log('Content changes:', changes);
69
70
  * });
71
+ *
72
+ * // Later, unsubscribe when no longer needed
73
+ * subscription.unsubscribe();
70
74
  * ```
71
75
  */
72
- function createUVESubscription(event, callback) {
76
+ function createUVESubscription(eventType, callback) {
73
77
  if (!getUVEState()) {
74
78
  console.warn('UVE Subscription: Not running inside UVE');
75
- return internal.__UVE_EVENT_ERROR_FALLBACK__(event);
79
+ return internal.__UVE_EVENT_ERROR_FALLBACK__(eventType);
76
80
  }
77
- const eventCallback = internal.__UVE_EVENTS__[event];
81
+ const eventCallback = internal.__UVE_EVENTS__[eventType];
78
82
  if (!eventCallback) {
79
- console.error(`UVE Subscription: Event ${event} not found`);
80
- return internal.__UVE_EVENT_ERROR_FALLBACK__(event);
83
+ console.error(`UVE Subscription: Event ${eventType} not found`);
84
+ return internal.__UVE_EVENT_ERROR_FALLBACK__(eventType);
81
85
  }
82
86
  return eventCallback(callback);
83
87
  }
84
88
 
85
89
  /**
86
- * Calculates the bounding information for each page element within the given containers.
90
+ * Post message to dotcms page editor
87
91
  *
88
92
  * @export
89
- * @param {HTMLDivElement[]} containers - An array of HTMLDivElement representing the containers.
90
- * @return {DotCMSContainerBound[]} An array of objects containing the bounding information for each page element.
91
- * @example
92
- * ```ts
93
- * const containers = document.querySelectorAll('.container');
94
- * const bounds = getDotCMSPageBounds(containers);
95
- * console.log(bounds);
96
- * ```
93
+ * @template T
94
+ * @param {DotCMSUVEMessage<T>} message
97
95
  */
98
- function getDotCMSPageBounds(containers) {
99
- return containers.map(container => {
100
- const containerRect = container.getBoundingClientRect();
101
- const contentlets = Array.from(container.querySelectorAll('[data-dot-object="contentlet"]'));
102
- return {
103
- x: containerRect.x,
104
- y: containerRect.y,
105
- width: containerRect.width,
106
- height: containerRect.height,
107
- payload: JSON.stringify({
108
- container: getDotCMSContainerData(container)
109
- }),
110
- contentlets: getDotCMSContentletsBound(containerRect, contentlets)
111
- };
112
- });
96
+ function sendMessageToUVE(message) {
97
+ window.parent.postMessage(message, '*');
113
98
  }
114
99
  /**
115
- * Calculates the bounding information for each contentlet inside a container.
100
+ * You can use this function to edit a contentlet in the editor.
101
+ *
102
+ * Calling this function inside the editor, will prompt the UVE to open a dialog to edit the contentlet.
116
103
  *
117
104
  * @export
118
- * @param {DOMRect} containerRect - The bounding rectangle of the container.
119
- * @param {HTMLDivElement[]} contentlets - An array of HTMLDivElement representing the contentlets.
120
- * @return {DotCMSContentletBound[]} An array of objects containing the bounding information for each contentlet.
121
- * @example
122
- * ```ts
123
- * const containerRect = container.getBoundingClientRect();
124
- * const contentlets = container.querySelectorAll('.contentlet');
125
- * const bounds = getDotCMSContentletsBound(containerRect, contentlets);
126
- * console.log(bounds); // Element bounds within the container
127
- * ```
105
+ * @template T
106
+ * @param {Contentlet<T>} contentlet - The contentlet to edit.
128
107
  */
129
- function getDotCMSContentletsBound(containerRect, contentlets) {
130
- return contentlets.map(contentlet => {
131
- const contentletRect = contentlet.getBoundingClientRect();
132
- return {
133
- x: 0,
134
- y: contentletRect.y - containerRect.y,
135
- width: contentletRect.width,
136
- height: contentletRect.height,
137
- payload: JSON.stringify({
138
- container: contentlet.dataset?.['dotContainer'] ? JSON.parse(contentlet.dataset?.['dotContainer']) : getClosestDotCMSContainerData(contentlet),
139
- contentlet: {
140
- identifier: contentlet.dataset?.['dotIdentifier'],
141
- title: contentlet.dataset?.['dotTitle'],
142
- inode: contentlet.dataset?.['dotInode'],
143
- contentType: contentlet.dataset?.['dotType']
144
- }
145
- })
146
- };
108
+ function editContentlet(contentlet) {
109
+ sendMessageToUVE({
110
+ action: types.DotCMSUVEAction.EDIT_CONTENTLET,
111
+ payload: contentlet
147
112
  });
148
113
  }
149
- /**
150
- * Get container data from VTLS.
114
+ /*
115
+ * Reorders the menu based on the provided configuration.
151
116
  *
152
- * @export
153
- * @param {HTMLElement} container - The container element.
154
- * @return {object} An object containing the container data.
155
- * @example
156
- * ```ts
157
- * const container = document.querySelector('.container');
158
- * const data = getContainerData(container);
159
- * console.log(data);
160
- * ```
161
- */
162
- function getDotCMSContainerData(container) {
163
- return {
164
- acceptTypes: container.dataset?.['dotAcceptTypes'] || '',
165
- identifier: container.dataset?.['dotIdentifier'] || '',
166
- maxContentlets: container.dataset?.['maxContentlets'] || '',
167
- uuid: container.dataset?.['dotUuid'] || ''
168
- };
169
- }
170
- /**
171
- * Get the closest container data from the contentlet.
117
+ * @param {ReorderMenuConfig} [config] - Optional configuration for reordering the menu.
118
+ * @param {number} [config.startLevel=1] - The starting level of the menu to reorder.
119
+ * @param {number} [config.depth=2] - The depth of the menu to reorder.
172
120
  *
173
- * @export
174
- * @param {Element} element - The contentlet element.
175
- * @return {object | null} An object containing the closest container data or null if no container is found.
176
- * @example
177
- * ```ts
178
- * const contentlet = document.querySelector('.contentlet');
179
- * const data = getClosestDotCMSContainerData(contentlet);
180
- * console.log(data);
181
- * ```
121
+ * This function constructs a URL for the reorder menu page with the specified
122
+ * start level and depth, and sends a message to the editor to perform the reorder action.
182
123
  */
183
- function getClosestDotCMSContainerData(element) {
184
- // Find the closest ancestor element with data-dot-object="container" attribute
185
- const container = element.closest('[data-dot-object="container"]');
186
- // If a container element is found
187
- if (container) {
188
- // Return the dataset of the container element
189
- return getDotCMSContainerData(container);
190
- } else {
191
- // If no container element is found, return null
192
- console.warn('No container found for the contentlet');
193
- return null;
194
- }
124
+ function reorderMenu(config) {
125
+ const {
126
+ startLevel = 1,
127
+ depth = 2
128
+ } = config || {};
129
+ sendMessageToUVE({
130
+ action: types.DotCMSUVEAction.REORDER_MENU,
131
+ payload: {
132
+ startLevel,
133
+ depth
134
+ }
135
+ });
195
136
  }
196
137
  /**
197
- * Find the closest contentlet element based on HTMLElement.
138
+ * Initializes the inline editing in the editor.
198
139
  *
199
140
  * @export
200
- * @param {HTMLElement | null} element - The starting element.
201
- * @return {HTMLElement | null} The closest contentlet element or null if not found.
202
- * @example
203
- * const element = document.querySelector('.some-element');
204
- * const contentlet = findDotCMSElement(element);
205
- * console.log(contentlet);
206
- */
207
- function findDotCMSElement(element) {
208
- if (!element) return null;
209
- if (element?.dataset?.['dotObject'] === 'contentlet' || element?.dataset?.['dotObject'] === 'container' && element.children.length === 0) {
210
- return element;
211
- }
212
- return findDotCMSElement(element?.['parentElement']);
213
- }
214
- /**
215
- * Find VTL data within a target element.
141
+ * @param {INLINE_EDITING_EVENT_KEY} type
142
+ * @param {InlineEditEventData} eventData
143
+ * @return {*}
216
144
  *
217
- * @export
218
- * @param {HTMLElement} target - The target element to search within.
219
- * @return {Array<{ inode: string, name: string }> | null} An array of objects containing VTL data or null if none found.
220
- * @example
221
- * ```ts
222
- * const target = document.querySelector('.target-element');
223
- * const vtlData = findDotCMSVTLData(target);
224
- * console.log(vtlData);
145
+ * * @example
146
+ * ```html
147
+ * <div onclick="initInlineEditing('BLOCK_EDITOR', { inode, languageId, contentType, fieldName, content })">
148
+ * ${My Content}
149
+ * </div>
225
150
  * ```
226
151
  */
227
- function findDotCMSVTLData(target) {
228
- const vltElements = target.querySelectorAll('[data-dot-object="vtl-file"]');
229
- if (!vltElements.length) {
230
- return null;
231
- }
232
- return Array.from(vltElements).map(vltElement => {
233
- return {
234
- inode: vltElement.dataset?.['dotInode'],
235
- name: vltElement.dataset?.['dotUrl']
236
- };
152
+ function initInlineEditing(type, data) {
153
+ sendMessageToUVE({
154
+ action: types.DotCMSUVEAction.INIT_INLINE_EDITING,
155
+ payload: {
156
+ type,
157
+ data
158
+ }
237
159
  });
238
160
  }
239
- /**
240
- * Check if the scroll position is at the bottom of the page.
241
- *
242
- * @export
243
- * @return {boolean} True if the scroll position is at the bottom, otherwise false.
244
- * @example
245
- * ```ts
246
- * if (dotCMSScrollIsInBottom()) {
247
- * console.log('Scrolled to the bottom');
248
- * }
249
- * ```
250
- */
251
- function computeScrollIsInBottom() {
252
- const documentHeight = document.documentElement.scrollHeight;
253
- const viewportHeight = window.innerHeight;
254
- const scrollY = window.scrollY;
255
- return scrollY + viewportHeight >= documentHeight;
256
- }
257
161
 
258
- exports.computeScrollIsInBottom = computeScrollIsInBottom;
259
162
  exports.createUVESubscription = createUVESubscription;
260
- exports.findDotCMSElement = findDotCMSElement;
261
- exports.findDotCMSVTLData = findDotCMSVTLData;
262
- exports.getClosestDotCMSContainerData = getClosestDotCMSContainerData;
263
- exports.getDotCMSContainerData = getDotCMSContainerData;
264
- exports.getDotCMSContentletsBound = getDotCMSContentletsBound;
265
- exports.getDotCMSPageBounds = getDotCMSPageBounds;
163
+ exports.editContentlet = editContentlet;
266
164
  exports.getUVEState = getUVEState;
165
+ exports.initInlineEditing = initInlineEditing;
166
+ exports.reorderMenu = reorderMenu;
167
+ exports.sendMessageToUVE = sendMessageToUVE;