@contentful/experiences-visual-editor-react 1.39.0-alpha-20250528T1549-bd210e1.0 → 1.39.0-alpha-20250603T1549-6807858.0
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/dist/index.js +147 -4
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +450 -8
- package/dist/renderApp.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import styleInject from 'style-inject';
|
|
|
2
2
|
import React, { useState, useEffect, useCallback, forwardRef, useMemo, useLayoutEffect, useRef } from 'react';
|
|
3
3
|
import { create } from 'zustand';
|
|
4
4
|
import { produce } from 'immer';
|
|
5
|
-
import { isEqual, omit, isArray, get as get$1 } from 'lodash-es';
|
|
5
|
+
import { isEqual, omit, isArray, get as get$1, debounce } from 'lodash-es';
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import md5 from 'md5';
|
|
8
8
|
import { BLOCKS } from '@contentful/rich-text-types';
|
|
@@ -228,6 +228,7 @@ const OUTGOING_EVENTS = {
|
|
|
228
228
|
OutsideCanvasClick: 'outsideCanvasClick',
|
|
229
229
|
SDKFeatures: 'sdkFeatures',
|
|
230
230
|
RequestEntities: 'REQUEST_ENTITIES',
|
|
231
|
+
CanvasGeometryUpdated: 'canvasGeometryUpdated',
|
|
231
232
|
};
|
|
232
233
|
const INCOMING_EVENTS$1 = {
|
|
233
234
|
RequestEditorMode: 'requestEditorMode',
|
|
@@ -1398,6 +1399,51 @@ let DebugLogger$1 = class DebugLogger {
|
|
|
1398
1399
|
DebugLogger$1.instance = null;
|
|
1399
1400
|
DebugLogger$1.getInstance();
|
|
1400
1401
|
|
|
1402
|
+
const findOutermostCoordinates = (first, second) => {
|
|
1403
|
+
return {
|
|
1404
|
+
top: Math.min(first.top, second.top),
|
|
1405
|
+
right: Math.max(first.right, second.right),
|
|
1406
|
+
bottom: Math.max(first.bottom, second.bottom),
|
|
1407
|
+
left: Math.min(first.left, second.left),
|
|
1408
|
+
};
|
|
1409
|
+
};
|
|
1410
|
+
const getElementCoordinates = (element) => {
|
|
1411
|
+
const rect = element.getBoundingClientRect();
|
|
1412
|
+
/**
|
|
1413
|
+
* If element does not have children, or element has it's own width or height,
|
|
1414
|
+
* return the element's coordinates.
|
|
1415
|
+
*/
|
|
1416
|
+
if (element.children.length === 0 || rect.width !== 0 || rect.height !== 0) {
|
|
1417
|
+
return rect;
|
|
1418
|
+
}
|
|
1419
|
+
const rects = [];
|
|
1420
|
+
/**
|
|
1421
|
+
* If element has children, or element does not have it's own width and height,
|
|
1422
|
+
* we find the cordinates of the children, and assume the outermost coordinates of the children
|
|
1423
|
+
* as the coordinate of the element.
|
|
1424
|
+
*
|
|
1425
|
+
* E.g child1 => {top: 2, bottom: 3, left: 4, right: 6} & child2 => {top: 1, bottom: 8, left: 12, right: 24}
|
|
1426
|
+
* The final assumed coordinates of the element would be => { top: 1, right: 24, bottom: 8, left: 4 }
|
|
1427
|
+
*/
|
|
1428
|
+
for (const child of element.children) {
|
|
1429
|
+
const childRect = getElementCoordinates(child);
|
|
1430
|
+
if (childRect.width !== 0 || childRect.height !== 0) {
|
|
1431
|
+
const { top, right, bottom, left } = childRect;
|
|
1432
|
+
rects.push({ top, right, bottom, left });
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
if (rects.length === 0) {
|
|
1436
|
+
return rect;
|
|
1437
|
+
}
|
|
1438
|
+
const { top, right, bottom, left } = rects.reduce(findOutermostCoordinates);
|
|
1439
|
+
return DOMRect.fromRect({
|
|
1440
|
+
x: left,
|
|
1441
|
+
y: top,
|
|
1442
|
+
height: bottom - top,
|
|
1443
|
+
width: right - left,
|
|
1444
|
+
});
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1401
1447
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1402
1448
|
const isLinkToAsset = (variable) => {
|
|
1403
1449
|
if (!variable)
|
|
@@ -4073,18 +4119,115 @@ const EmptyCanvasMessage = () => {
|
|
|
4073
4119
|
React.createElement("span", { className: styles['empty-canvas-label'] }, "Add components to begin")));
|
|
4074
4120
|
};
|
|
4075
4121
|
|
|
4122
|
+
/**
|
|
4123
|
+
* This function gets the element co-ordinates of a specified component in the DOM and its parent
|
|
4124
|
+
* and sends the DOM Rect to the client app.
|
|
4125
|
+
*/
|
|
4126
|
+
const sendCanvasGeometryUpdatedMessage = async (tree, sourceEvent) => {
|
|
4127
|
+
const nodeToCoordinatesMap = {};
|
|
4128
|
+
await waitForAllImagesToBeLoaded();
|
|
4129
|
+
collectNodeCoordinates(tree.root, nodeToCoordinatesMap);
|
|
4130
|
+
sendMessage(OUTGOING_EVENTS.CanvasGeometryUpdated, {
|
|
4131
|
+
size: {
|
|
4132
|
+
width: document.documentElement.offsetWidth,
|
|
4133
|
+
height: document.documentElement.offsetHeight,
|
|
4134
|
+
},
|
|
4135
|
+
nodes: nodeToCoordinatesMap,
|
|
4136
|
+
sourceEvent,
|
|
4137
|
+
});
|
|
4138
|
+
};
|
|
4139
|
+
const collectNodeCoordinates = (node, nodeToCoordinatesMap) => {
|
|
4140
|
+
const selectedElement = document.querySelector(`[data-cf-node-id="${node.data.id}"]`);
|
|
4141
|
+
if (selectedElement) {
|
|
4142
|
+
const rect = getElementCoordinates(selectedElement);
|
|
4143
|
+
nodeToCoordinatesMap[node.data.id] = {
|
|
4144
|
+
coordinates: {
|
|
4145
|
+
x: rect.x + window.scrollX,
|
|
4146
|
+
y: rect.y + window.scrollY,
|
|
4147
|
+
width: rect.width,
|
|
4148
|
+
height: rect.height,
|
|
4149
|
+
},
|
|
4150
|
+
};
|
|
4151
|
+
}
|
|
4152
|
+
node.children.forEach((child) => collectNodeCoordinates(child, nodeToCoordinatesMap));
|
|
4153
|
+
};
|
|
4154
|
+
const waitForAllImagesToBeLoaded = () => {
|
|
4155
|
+
// If the document contains an image, wait for this image to be loaded before collecting & sending all geometry data.
|
|
4156
|
+
const allImageNodes = document.querySelectorAll('img');
|
|
4157
|
+
return Promise.all(Array.from(allImageNodes).map((imageNode) => {
|
|
4158
|
+
if (imageNode.complete) {
|
|
4159
|
+
return Promise.resolve();
|
|
4160
|
+
}
|
|
4161
|
+
return new Promise((resolve, reject) => {
|
|
4162
|
+
const handleImageLoad = (event) => {
|
|
4163
|
+
imageNode.removeEventListener('load', handleImageLoad);
|
|
4164
|
+
imageNode.removeEventListener('error', handleImageLoad);
|
|
4165
|
+
if (event.type === 'error') {
|
|
4166
|
+
console.warn('Image failed to load:', imageNode);
|
|
4167
|
+
reject();
|
|
4168
|
+
}
|
|
4169
|
+
else {
|
|
4170
|
+
resolve();
|
|
4171
|
+
}
|
|
4172
|
+
};
|
|
4173
|
+
imageNode.addEventListener('load', handleImageLoad);
|
|
4174
|
+
imageNode.addEventListener('error', handleImageLoad);
|
|
4175
|
+
});
|
|
4176
|
+
}));
|
|
4177
|
+
};
|
|
4178
|
+
|
|
4179
|
+
const useCanvasGeometryUpdates = ({ tree, rootContainerRef, }) => {
|
|
4180
|
+
const debouncedUpdateGeometry = useMemo(() => debounce((tree, sourceEvent) => {
|
|
4181
|
+
// When the DOM changed, we still need to wait for the next frame to ensure that
|
|
4182
|
+
// rendering is complete (e.g. this is required when deleting a node).
|
|
4183
|
+
window.requestAnimationFrame(() => {
|
|
4184
|
+
sendCanvasGeometryUpdatedMessage(tree, sourceEvent);
|
|
4185
|
+
});
|
|
4186
|
+
}, 100, {
|
|
4187
|
+
leading: true,
|
|
4188
|
+
// To be sure, we recalculate it at the end of the frame again. Though, we couldn't
|
|
4189
|
+
// yet show the need for this. So we might be able to drop this later to boost performance.
|
|
4190
|
+
trailing: true,
|
|
4191
|
+
}), []);
|
|
4192
|
+
// Store tree in a ref to avoid the need to deactivate & reactivate the mutation observer
|
|
4193
|
+
// when the tree changes. This is important to avoid missing out on some mutation events.
|
|
4194
|
+
const treeRef = useRef(tree);
|
|
4195
|
+
useEffect(() => {
|
|
4196
|
+
treeRef.current = tree;
|
|
4197
|
+
}, [tree]);
|
|
4198
|
+
// Handling window resize events
|
|
4199
|
+
useEffect(() => {
|
|
4200
|
+
const resizeEventListener = () => debouncedUpdateGeometry(treeRef.current, 'resize');
|
|
4201
|
+
window.addEventListener('resize', resizeEventListener);
|
|
4202
|
+
return () => window.removeEventListener('resize', resizeEventListener);
|
|
4203
|
+
}, [debouncedUpdateGeometry]);
|
|
4204
|
+
// Handling DOM mutations
|
|
4205
|
+
useEffect(() => {
|
|
4206
|
+
if (!rootContainerRef.current)
|
|
4207
|
+
return;
|
|
4208
|
+
const observer = new MutationObserver(() => debouncedUpdateGeometry(treeRef.current, 'mutation'));
|
|
4209
|
+
observer.observe(rootContainerRef.current, {
|
|
4210
|
+
childList: true,
|
|
4211
|
+
subtree: true,
|
|
4212
|
+
attributes: true,
|
|
4213
|
+
});
|
|
4214
|
+
return () => observer.disconnect();
|
|
4215
|
+
}, [debouncedUpdateGeometry, rootContainerRef]);
|
|
4216
|
+
};
|
|
4217
|
+
|
|
4076
4218
|
const RootRenderer = () => {
|
|
4219
|
+
const rootContainerRef = useRef(null);
|
|
4220
|
+
const tree = useTreeStore((state) => state.tree);
|
|
4221
|
+
useCanvasGeometryUpdates({ tree, rootContainerRef });
|
|
4077
4222
|
useEditorSubscriber();
|
|
4078
4223
|
const breakpoints = useTreeStore((state) => state.breakpoints);
|
|
4079
|
-
const containerRef = useRef(null);
|
|
4080
4224
|
const { resolveDesignValue } = useBreakpoints(breakpoints);
|
|
4081
|
-
const tree = useTreeStore((state) => state.tree);
|
|
4082
4225
|
// If the root blockId is defined but not the default string, it is the entry ID
|
|
4083
4226
|
// of the experience/ pattern to properly detect circular dependencies.
|
|
4084
4227
|
const rootBlockId = tree.root.data.blockId ?? ROOT_ID;
|
|
4085
4228
|
const wrappingPatternIds = rootBlockId !== ROOT_ID ? new Set([rootBlockId]) : new Set();
|
|
4086
4229
|
return (React.createElement(React.Fragment, null,
|
|
4087
|
-
React.createElement("div", { "data-ctfl-root": true, className: styles$2.rootContainer, ref:
|
|
4230
|
+
React.createElement("div", { "data-ctfl-root": true, className: styles$2.rootContainer, ref: rootContainerRef }, !tree.root.children.length ? (React.createElement(EmptyCanvasMessage, null)) : (tree.root.children.map((topLevelChildNode) => (React.createElement(EditorBlock, { key: topLevelChildNode.data.id, node: topLevelChildNode, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds })))))));
|
|
4088
4231
|
};
|
|
4089
4232
|
|
|
4090
4233
|
const useInitializeEditor = () => {
|