@contentful/experiences-visual-editor-react 2.0.0-prerelease-20250704T1603-11d76eb.0 → 2.0.1-beta.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 +2070 -425
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +12069 -751
- package/dist/renderApp.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,41 +1,35 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useEffect, useRef, useState, useCallback, forwardRef, useLayoutEffect, useMemo } from 'react';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { omit, isArray, isEqual, get as get$2
|
|
4
|
+
import { omit, isArray, isEqual, get as get$2 } from 'lodash-es';
|
|
5
5
|
import md5 from 'md5';
|
|
6
6
|
import { BLOCKS } from '@contentful/rich-text-types';
|
|
7
7
|
import { create, useStore } from 'zustand';
|
|
8
|
+
import { Droppable, Draggable, DragDropContext } from '@hello-pangea/dnd';
|
|
8
9
|
import { produce } from 'immer';
|
|
9
10
|
import '@contentful/rich-text-react-renderer';
|
|
11
|
+
import { v4 } from 'uuid';
|
|
12
|
+
import { createPortal } from 'react-dom';
|
|
13
|
+
import classNames from 'classnames';
|
|
10
14
|
|
|
11
15
|
var css_248z$b = "html,\nbody {\n margin: 0;\n padding: 0;\n}\n\n/*\n * All of these variables are tokens from Forma-36 and should not be adjusted as these\n * are global variables that may affect multiple places.\n * As our customers may use other design libraries, we try to avoid overlapping global\n * variables by always using the prefix `--exp-builder-` inside this SDK.\n */\n\n:root {\n /* Color tokens from Forma 36: https://f36.contentful.com/tokens/color-system */\n --exp-builder-blue100: #e8f5ff;\n --exp-builder-blue200: #ceecff;\n --exp-builder-blue300: #98cbff;\n --exp-builder-blue400: #40a0ff;\n --exp-builder-blue500: #036fe3;\n --exp-builder-blue600: #0059c8;\n --exp-builder-blue700: #0041ab;\n --exp-builder-blue800: #003298;\n --exp-builder-blue900: #002a8e;\n --exp-builder-gray100: #f7f9fa;\n --exp-builder-gray200: #e7ebee;\n --exp-builder-gray300: #cfd9e0;\n --exp-builder-gray400: #aec1cc;\n --exp-builder-gray500: #67728a;\n --exp-builder-gray600: #5a657c;\n --exp-builder-gray700: #414d63;\n --exp-builder-gray800: #1b273a;\n --exp-builder-gray900: #111b2b;\n --exp-builder-purple600: #6c3ecf;\n --exp-builder-red200: #ffe0e0;\n --exp-builder-red800: #7f0010;\n --exp-builder-color-white: #ffffff;\n --exp-builder-glow-primary: 0px 0px 0px 3px #e8f5ff;\n\n /* RGB colors for applying opacity */\n --exp-builder-blue100-rgb: 232, 245, 255;\n --exp-builder-blue300-rgb: 152, 203, 255;\n\n /* Spacing tokens from Forma 36: https://f36.contentful.com/tokens/spacing */\n --exp-builder-spacing-s: 0.75rem;\n --exp-builder-spacing-2xs: 0.25rem;\n\n /* Typography tokens from Forma 36: https://f36.contentful.com/tokens/typography */\n --exp-builder-font-size-l: 1rem;\n --exp-builder-font-size-m: 0.875rem;\n --exp-builder-font-stack-primary: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;\n --exp-builder-line-height-condensed: 1.25;\n}\n";
|
|
12
16
|
styleInject(css_248z$b);
|
|
13
17
|
|
|
14
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
15
18
|
const INCOMING_EVENTS$1 = {
|
|
16
19
|
RequestEditorMode: 'requestEditorMode',
|
|
17
20
|
RequestReadOnlyMode: 'requestReadOnlyMode',
|
|
18
21
|
ExperienceUpdated: 'componentTreeUpdated',
|
|
19
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
20
22
|
ComponentDraggingChanged: 'componentDraggingChanged',
|
|
21
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
22
23
|
ComponentDragCanceled: 'componentDragCanceled',
|
|
23
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
24
24
|
ComponentDragStarted: 'componentDragStarted',
|
|
25
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
26
25
|
ComponentDragEnded: 'componentDragEnded',
|
|
27
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
28
26
|
ComponentMoveEnded: 'componentMoveEnded',
|
|
29
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
30
27
|
CanvasResized: 'canvasResized',
|
|
31
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
32
28
|
SelectComponent: 'selectComponent',
|
|
33
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
34
29
|
HoverComponent: 'hoverComponent',
|
|
35
30
|
UpdatedEntity: 'updatedEntity',
|
|
36
31
|
AssembliesAdded: 'assembliesAdded',
|
|
37
32
|
AssembliesRegistered: 'assembliesRegistered',
|
|
38
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
39
33
|
MouseMove: 'mouseMove',
|
|
40
34
|
RequestedEntities: 'REQUESTED_ENTITIES',
|
|
41
35
|
};
|
|
@@ -50,7 +44,7 @@ var StudioCanvasMode$3;
|
|
|
50
44
|
StudioCanvasMode["EDITOR"] = "editorMode";
|
|
51
45
|
StudioCanvasMode["NONE"] = "none";
|
|
52
46
|
})(StudioCanvasMode$3 || (StudioCanvasMode$3 = {}));
|
|
53
|
-
const CONTENTFUL_COMPONENTS$
|
|
47
|
+
const CONTENTFUL_COMPONENTS$2 = {
|
|
54
48
|
section: {
|
|
55
49
|
id: 'contentful-section',
|
|
56
50
|
name: 'Section',
|
|
@@ -96,6 +90,9 @@ const CONTENTFUL_COMPONENTS$1 = {
|
|
|
96
90
|
name: 'Carousel',
|
|
97
91
|
},
|
|
98
92
|
};
|
|
93
|
+
const ASSEMBLY_NODE_TYPE$1 = 'assembly';
|
|
94
|
+
const ASSEMBLY_DEFAULT_CATEGORY$1 = 'Assemblies';
|
|
95
|
+
const ASSEMBLY_BLOCK_NODE_TYPE$1 = 'assemblyBlock';
|
|
99
96
|
const CF_STYLE_ATTRIBUTES = [
|
|
100
97
|
'cfVisibility',
|
|
101
98
|
'cfHorizontalAlignment',
|
|
@@ -130,7 +127,7 @@ const CF_STYLE_ATTRIBUTES = [
|
|
|
130
127
|
'cfTextItalic',
|
|
131
128
|
'cfTextUnderline',
|
|
132
129
|
];
|
|
133
|
-
const
|
|
130
|
+
const EMPTY_CONTAINER_HEIGHT$1 = '80px';
|
|
134
131
|
const DEFAULT_IMAGE_WIDTH = '500px';
|
|
135
132
|
var PostMessageMethods$3;
|
|
136
133
|
(function (PostMessageMethods) {
|
|
@@ -140,14 +137,20 @@ var PostMessageMethods$3;
|
|
|
140
137
|
const SUPPORTED_IMAGE_FORMATS = ['jpg', 'png', 'webp', 'gif', 'avif'];
|
|
141
138
|
|
|
142
139
|
const structureComponentIds = new Set([
|
|
143
|
-
CONTENTFUL_COMPONENTS$
|
|
144
|
-
CONTENTFUL_COMPONENTS$
|
|
145
|
-
CONTENTFUL_COMPONENTS$
|
|
146
|
-
CONTENTFUL_COMPONENTS$
|
|
140
|
+
CONTENTFUL_COMPONENTS$2.section.id,
|
|
141
|
+
CONTENTFUL_COMPONENTS$2.columns.id,
|
|
142
|
+
CONTENTFUL_COMPONENTS$2.container.id,
|
|
143
|
+
CONTENTFUL_COMPONENTS$2.singleColumn.id,
|
|
147
144
|
]);
|
|
148
|
-
const
|
|
145
|
+
const patternTypes = new Set([ASSEMBLY_NODE_TYPE$1, ASSEMBLY_BLOCK_NODE_TYPE$1]);
|
|
146
|
+
const allContentfulComponentIds = new Set(Object.values(CONTENTFUL_COMPONENTS$2).map((component) => component.id));
|
|
147
|
+
const isPatternComponent = (type) => patternTypes.has(type ?? '');
|
|
149
148
|
const isContentfulStructureComponent = (componentId) => structureComponentIds.has((componentId ?? ''));
|
|
150
149
|
const isContentfulComponent = (componentId) => allContentfulComponentIds.has((componentId ?? ''));
|
|
150
|
+
const isComponentAllowedOnRoot = ({ type, category, componentId }) => isPatternComponent(type) ||
|
|
151
|
+
category === ASSEMBLY_DEFAULT_CATEGORY$1 ||
|
|
152
|
+
isContentfulStructureComponent(componentId) ||
|
|
153
|
+
componentId === CONTENTFUL_COMPONENTS$2.divider.id;
|
|
151
154
|
const isStructureWithRelativeHeight = (componentId, height) => {
|
|
152
155
|
return isContentfulStructureComponent(componentId) && !height?.toString().endsWith('px');
|
|
153
156
|
};
|
|
@@ -1190,10 +1193,6 @@ const findOutermostCoordinates = (first, second) => {
|
|
|
1190
1193
|
left: Math.min(first.left, second.left),
|
|
1191
1194
|
};
|
|
1192
1195
|
};
|
|
1193
|
-
const isElementHidden = (rect) => {
|
|
1194
|
-
/** if the rect has no size and position, its element is not rendered in the DOM */
|
|
1195
|
-
return rect.width === 0 && rect.height === 0 && rect.x === 0 && rect.y === 0;
|
|
1196
|
-
};
|
|
1197
1196
|
const getElementCoordinates = (element) => {
|
|
1198
1197
|
const rect = element.getBoundingClientRect();
|
|
1199
1198
|
/**
|
|
@@ -1433,17 +1432,21 @@ function buildTemplate({ template, context, }) {
|
|
|
1433
1432
|
|
|
1434
1433
|
const stylesToKeep = ['cfImageAsset'];
|
|
1435
1434
|
const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.includes(style));
|
|
1436
|
-
|
|
1437
|
-
const propsToRemove = [
|
|
1438
|
-
'cfHyperlink',
|
|
1439
|
-
'cfOpenInNewTab',
|
|
1440
|
-
'cfSsrClassName',
|
|
1441
|
-
'cfWrapColumns',
|
|
1442
|
-
'cfWrapColumnsCount',
|
|
1443
|
-
];
|
|
1435
|
+
const propsToRemove = ['cfHyperlink', 'cfOpenInNewTab', 'cfSsrClassName'];
|
|
1444
1436
|
const sanitizeNodeProps = (nodeProps) => {
|
|
1445
1437
|
return omit(nodeProps, stylesToRemove, propsToRemove);
|
|
1446
1438
|
};
|
|
1439
|
+
|
|
1440
|
+
/** Turn the visibility value into a style object that can be used for inline styles in React */
|
|
1441
|
+
const transformVisibility = (value) => {
|
|
1442
|
+
if (value === false) {
|
|
1443
|
+
return {
|
|
1444
|
+
display: 'none !important',
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
// Don't explicitly set anything when visible to not overwrite values like `grid` or `flex`.
|
|
1448
|
+
return {};
|
|
1449
|
+
};
|
|
1447
1450
|
const transformGridColumn = (span) => {
|
|
1448
1451
|
if (!span) {
|
|
1449
1452
|
return {};
|
|
@@ -1602,7 +1605,7 @@ const buildCfStyles = (values) => {
|
|
|
1602
1605
|
};
|
|
1603
1606
|
/**
|
|
1604
1607
|
* Container/section default behavior:
|
|
1605
|
-
* Default height => height:
|
|
1608
|
+
* Default height => height: EMPTY_CONTAINER_HEIGHT
|
|
1606
1609
|
* If a container component has children => height: 'fit-content'
|
|
1607
1610
|
*/
|
|
1608
1611
|
const calculateNodeDefaultHeight = ({ blockId, children, value, }) => {
|
|
@@ -1612,7 +1615,7 @@ const calculateNodeDefaultHeight = ({ blockId, children, value, }) => {
|
|
|
1612
1615
|
if (children.length) {
|
|
1613
1616
|
return '100%';
|
|
1614
1617
|
}
|
|
1615
|
-
return
|
|
1618
|
+
return EMPTY_CONTAINER_HEIGHT$1;
|
|
1616
1619
|
};
|
|
1617
1620
|
|
|
1618
1621
|
function getOptimizedImageUrl(url, width, quality, format) {
|
|
@@ -2645,7 +2648,180 @@ function gatherDeepReferencesFromTree(startingNode, dataSource) {
|
|
|
2645
2648
|
return deepReferences;
|
|
2646
2649
|
}
|
|
2647
2650
|
|
|
2651
|
+
const useDraggedItemStore = create((set) => ({
|
|
2652
|
+
draggedItem: undefined,
|
|
2653
|
+
hoveredComponentId: undefined,
|
|
2654
|
+
domRect: undefined,
|
|
2655
|
+
componentId: '',
|
|
2656
|
+
isDraggingOnCanvas: false,
|
|
2657
|
+
onBeforeCaptureId: '',
|
|
2658
|
+
mouseX: 0,
|
|
2659
|
+
mouseY: 0,
|
|
2660
|
+
scrollY: 0,
|
|
2661
|
+
setComponentId(id) {
|
|
2662
|
+
set({ componentId: id });
|
|
2663
|
+
},
|
|
2664
|
+
setHoveredComponentId(id) {
|
|
2665
|
+
set({ hoveredComponentId: id });
|
|
2666
|
+
},
|
|
2667
|
+
updateItem: (item) => {
|
|
2668
|
+
set({ draggedItem: item });
|
|
2669
|
+
},
|
|
2670
|
+
setDraggingOnCanvas: (isDraggingOnCanvas) => {
|
|
2671
|
+
set({ isDraggingOnCanvas });
|
|
2672
|
+
},
|
|
2673
|
+
setOnBeforeCaptureId: (onBeforeCaptureId) => {
|
|
2674
|
+
set({ onBeforeCaptureId });
|
|
2675
|
+
},
|
|
2676
|
+
setMousePosition(x, y) {
|
|
2677
|
+
set({ mouseX: x, mouseY: y });
|
|
2678
|
+
},
|
|
2679
|
+
setDomRect(domRect) {
|
|
2680
|
+
set({ domRect });
|
|
2681
|
+
},
|
|
2682
|
+
setScrollY(y) {
|
|
2683
|
+
set({ scrollY: y });
|
|
2684
|
+
},
|
|
2685
|
+
}));
|
|
2686
|
+
|
|
2687
|
+
const SCROLL_STATES = {
|
|
2688
|
+
Start: 'scrollStart',
|
|
2689
|
+
IsScrolling: 'isScrolling',
|
|
2690
|
+
End: 'scrollEnd',
|
|
2691
|
+
};
|
|
2692
|
+
const OUTGOING_EVENTS = {
|
|
2693
|
+
Connected: 'connected',
|
|
2694
|
+
DesignTokens: 'registerDesignTokens',
|
|
2695
|
+
RegisteredBreakpoints: 'registeredBreakpoints',
|
|
2696
|
+
MouseMove: 'mouseMove',
|
|
2697
|
+
NewHoveredElement: 'newHoveredElement',
|
|
2698
|
+
ComponentSelected: 'componentSelected',
|
|
2699
|
+
RegisteredComponents: 'registeredComponents',
|
|
2700
|
+
RequestComponentTreeUpdate: 'requestComponentTreeUpdate',
|
|
2701
|
+
ComponentDragCanceled: 'componentDragCanceled',
|
|
2702
|
+
ComponentDropped: 'componentDropped',
|
|
2703
|
+
ComponentMoved: 'componentMoved',
|
|
2704
|
+
CanvasReload: 'canvasReload',
|
|
2705
|
+
UpdateSelectedComponentCoordinates: 'updateSelectedComponentCoordinates',
|
|
2706
|
+
CanvasScroll: 'canvasScrolling',
|
|
2707
|
+
CanvasError: 'canvasError',
|
|
2708
|
+
ComponentMoveStarted: 'componentMoveStarted',
|
|
2709
|
+
ComponentMoveEnded: 'componentMoveEnded',
|
|
2710
|
+
OutsideCanvasClick: 'outsideCanvasClick',
|
|
2711
|
+
SDKFeatures: 'sdkFeatures',
|
|
2712
|
+
RequestEntities: 'REQUEST_ENTITIES',
|
|
2713
|
+
};
|
|
2714
|
+
const INCOMING_EVENTS = {
|
|
2715
|
+
RequestEditorMode: 'requestEditorMode',
|
|
2716
|
+
RequestReadOnlyMode: 'requestReadOnlyMode',
|
|
2717
|
+
ExperienceUpdated: 'componentTreeUpdated',
|
|
2718
|
+
ComponentDraggingChanged: 'componentDraggingChanged',
|
|
2719
|
+
ComponentDragCanceled: 'componentDragCanceled',
|
|
2720
|
+
ComponentDragStarted: 'componentDragStarted',
|
|
2721
|
+
ComponentDragEnded: 'componentDragEnded',
|
|
2722
|
+
ComponentMoveEnded: 'componentMoveEnded',
|
|
2723
|
+
CanvasResized: 'canvasResized',
|
|
2724
|
+
SelectComponent: 'selectComponent',
|
|
2725
|
+
HoverComponent: 'hoverComponent',
|
|
2726
|
+
UpdatedEntity: 'updatedEntity',
|
|
2727
|
+
AssembliesAdded: 'assembliesAdded',
|
|
2728
|
+
AssembliesRegistered: 'assembliesRegistered',
|
|
2729
|
+
MouseMove: 'mouseMove',
|
|
2730
|
+
RequestedEntities: 'REQUESTED_ENTITIES',
|
|
2731
|
+
};
|
|
2732
|
+
const INTERNAL_EVENTS = {
|
|
2733
|
+
ComponentsRegistered: 'cfComponentsRegistered',
|
|
2734
|
+
VisualEditorInitialize: 'cfVisualEditorInitialize',
|
|
2735
|
+
};
|
|
2736
|
+
const VISUAL_EDITOR_EVENTS = {
|
|
2737
|
+
Ready: 'cfVisualEditorReady',
|
|
2738
|
+
};
|
|
2739
|
+
/**
|
|
2740
|
+
* These modes are ONLY intended to be internally used within the context of
|
|
2741
|
+
* editing an experience inside of Contentful Studio. i.e. these modes
|
|
2742
|
+
* intentionally do not include preview/delivery modes.
|
|
2743
|
+
*/
|
|
2744
|
+
var StudioCanvasMode$2;
|
|
2745
|
+
(function (StudioCanvasMode) {
|
|
2746
|
+
StudioCanvasMode["READ_ONLY"] = "readOnlyMode";
|
|
2747
|
+
StudioCanvasMode["EDITOR"] = "editorMode";
|
|
2748
|
+
StudioCanvasMode["NONE"] = "none";
|
|
2749
|
+
})(StudioCanvasMode$2 || (StudioCanvasMode$2 = {}));
|
|
2750
|
+
const CONTENTFUL_COMPONENTS$1 = {
|
|
2751
|
+
section: {
|
|
2752
|
+
id: 'contentful-section',
|
|
2753
|
+
name: 'Section',
|
|
2754
|
+
},
|
|
2755
|
+
container: {
|
|
2756
|
+
id: 'contentful-container',
|
|
2757
|
+
name: 'Container',
|
|
2758
|
+
},
|
|
2759
|
+
columns: {
|
|
2760
|
+
id: 'contentful-columns',
|
|
2761
|
+
name: 'Columns',
|
|
2762
|
+
},
|
|
2763
|
+
singleColumn: {
|
|
2764
|
+
id: 'contentful-single-column',
|
|
2765
|
+
name: 'Column',
|
|
2766
|
+
},
|
|
2767
|
+
button: {
|
|
2768
|
+
id: 'contentful-button',
|
|
2769
|
+
name: 'Button',
|
|
2770
|
+
},
|
|
2771
|
+
heading: {
|
|
2772
|
+
id: 'contentful-heading',
|
|
2773
|
+
name: 'Heading',
|
|
2774
|
+
},
|
|
2775
|
+
image: {
|
|
2776
|
+
id: 'contentful-image',
|
|
2777
|
+
name: 'Image',
|
|
2778
|
+
},
|
|
2779
|
+
richText: {
|
|
2780
|
+
id: 'contentful-richText',
|
|
2781
|
+
name: 'Rich Text',
|
|
2782
|
+
},
|
|
2783
|
+
text: {
|
|
2784
|
+
id: 'contentful-text',
|
|
2785
|
+
name: 'Text',
|
|
2786
|
+
},
|
|
2787
|
+
divider: {
|
|
2788
|
+
id: 'contentful-divider',
|
|
2789
|
+
name: 'Divider',
|
|
2790
|
+
},
|
|
2791
|
+
carousel: {
|
|
2792
|
+
id: 'contentful-carousel',
|
|
2793
|
+
name: 'Carousel',
|
|
2794
|
+
},
|
|
2795
|
+
};
|
|
2796
|
+
const ASSEMBLY_NODE_TYPE = 'assembly';
|
|
2797
|
+
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
2798
|
+
const ASSEMBLY_BLOCK_NODE_TYPE = 'assemblyBlock';
|
|
2799
|
+
const ASSEMBLY_NODE_TYPES = [ASSEMBLY_NODE_TYPE, ASSEMBLY_BLOCK_NODE_TYPE];
|
|
2800
|
+
const EMPTY_CONTAINER_HEIGHT = '80px';
|
|
2801
|
+
const HYPERLINK_DEFAULT_PATTERN = `/{locale}/{entry.fields.slug}/`;
|
|
2802
|
+
var PostMessageMethods$2;
|
|
2803
|
+
(function (PostMessageMethods) {
|
|
2804
|
+
PostMessageMethods["REQUEST_ENTITIES"] = "REQUEST_ENTITIES";
|
|
2805
|
+
PostMessageMethods["REQUESTED_ENTITIES"] = "REQUESTED_ENTITIES";
|
|
2806
|
+
})(PostMessageMethods$2 || (PostMessageMethods$2 = {}));
|
|
2807
|
+
|
|
2808
|
+
const DRAGGABLE_HEIGHT = 30;
|
|
2809
|
+
const DRAGGABLE_WIDTH = 50;
|
|
2810
|
+
const DRAG_PADDING = 4;
|
|
2648
2811
|
const ROOT_ID = 'root';
|
|
2812
|
+
const COMPONENT_LIST_ID = 'component-list';
|
|
2813
|
+
const NEW_COMPONENT_ID = 'ctfl-new-draggable';
|
|
2814
|
+
const CTFL_ZONE_ID = 'data-ctfl-zone-id';
|
|
2815
|
+
const CTFL_DRAGGING_ELEMENT = 'data-ctfl-dragging-element';
|
|
2816
|
+
const HITBOX = {
|
|
2817
|
+
WIDTH: 70,
|
|
2818
|
+
HEIGHT: 20,
|
|
2819
|
+
INITIAL_OFFSET: 10,
|
|
2820
|
+
OFFSET_INCREMENT: 8,
|
|
2821
|
+
MIN_HEIGHT: 45,
|
|
2822
|
+
MIN_DEPTH_HEIGHT: 20,
|
|
2823
|
+
DEEP_ZONE: 5,
|
|
2824
|
+
};
|
|
2649
2825
|
var TreeAction;
|
|
2650
2826
|
(function (TreeAction) {
|
|
2651
2827
|
TreeAction[TreeAction["REMOVE_NODE"] = 0] = "REMOVE_NODE";
|
|
@@ -2655,6 +2831,139 @@ var TreeAction;
|
|
|
2655
2831
|
TreeAction[TreeAction["REORDER_NODE"] = 4] = "REORDER_NODE";
|
|
2656
2832
|
TreeAction[TreeAction["REPLACE_NODE"] = 5] = "REPLACE_NODE";
|
|
2657
2833
|
})(TreeAction || (TreeAction = {}));
|
|
2834
|
+
var HitboxDirection;
|
|
2835
|
+
(function (HitboxDirection) {
|
|
2836
|
+
HitboxDirection[HitboxDirection["TOP"] = 0] = "TOP";
|
|
2837
|
+
HitboxDirection[HitboxDirection["LEFT"] = 1] = "LEFT";
|
|
2838
|
+
HitboxDirection[HitboxDirection["RIGHT"] = 2] = "RIGHT";
|
|
2839
|
+
HitboxDirection[HitboxDirection["BOTTOM"] = 3] = "BOTTOM";
|
|
2840
|
+
HitboxDirection[HitboxDirection["SELF_VERTICAL"] = 4] = "SELF_VERTICAL";
|
|
2841
|
+
HitboxDirection[HitboxDirection["SELF_HORIZONTAL"] = 5] = "SELF_HORIZONTAL";
|
|
2842
|
+
})(HitboxDirection || (HitboxDirection = {}));
|
|
2843
|
+
var DraggablePosition;
|
|
2844
|
+
(function (DraggablePosition) {
|
|
2845
|
+
DraggablePosition[DraggablePosition["CENTERED"] = 0] = "CENTERED";
|
|
2846
|
+
DraggablePosition[DraggablePosition["MOUSE_POSITION"] = 1] = "MOUSE_POSITION";
|
|
2847
|
+
})(DraggablePosition || (DraggablePosition = {}));
|
|
2848
|
+
|
|
2849
|
+
function useDraggablePosition({ draggableId, draggableRef, position }) {
|
|
2850
|
+
const isDraggingOnCanvas = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
2851
|
+
const draggingId = useDraggedItemStore((state) => state.onBeforeCaptureId);
|
|
2852
|
+
const preDragDomRect = useDraggedItemStore((state) => state.domRect);
|
|
2853
|
+
useEffect(() => {
|
|
2854
|
+
const el = draggableRef?.current ??
|
|
2855
|
+
document.querySelector(`[${CTFL_DRAGGING_ELEMENT}][data-cf-node-id="${draggableId}"]`);
|
|
2856
|
+
if (!isDraggingOnCanvas || draggingId !== draggableId || !el) {
|
|
2857
|
+
return;
|
|
2858
|
+
}
|
|
2859
|
+
const isCentered = position === DraggablePosition.CENTERED || !preDragDomRect;
|
|
2860
|
+
const domRect = isCentered ? el.getBoundingClientRect() : preDragDomRect;
|
|
2861
|
+
const { mouseX, mouseY } = useDraggedItemStore.getState();
|
|
2862
|
+
const top = isCentered ? mouseY - domRect.height / 2 : domRect.top;
|
|
2863
|
+
const left = isCentered ? mouseX - domRect.width / 2 : domRect.left;
|
|
2864
|
+
el.style.position = 'fixed';
|
|
2865
|
+
el.style.left = `${left}px`;
|
|
2866
|
+
el.style.top = `${top}px`;
|
|
2867
|
+
el.style.width = `${domRect.width}px`;
|
|
2868
|
+
el.style.height = `${domRect.height}px`;
|
|
2869
|
+
}, [draggableRef, draggableId, isDraggingOnCanvas, draggingId, position, preDragDomRect]);
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
function getStyle$2(style, snapshot) {
|
|
2873
|
+
if (!snapshot.isDropAnimating) {
|
|
2874
|
+
return style;
|
|
2875
|
+
}
|
|
2876
|
+
return {
|
|
2877
|
+
...style,
|
|
2878
|
+
// cannot be 0, but make it super tiny
|
|
2879
|
+
transitionDuration: `0.001s`,
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
const DraggableContainer = ({ id }) => {
|
|
2883
|
+
const ref = useRef(null);
|
|
2884
|
+
useDraggablePosition({
|
|
2885
|
+
draggableId: id,
|
|
2886
|
+
draggableRef: ref,
|
|
2887
|
+
position: DraggablePosition.CENTERED,
|
|
2888
|
+
});
|
|
2889
|
+
return (React.createElement("div", { id: COMPONENT_LIST_ID, style: {
|
|
2890
|
+
position: 'absolute',
|
|
2891
|
+
top: 0,
|
|
2892
|
+
left: 0,
|
|
2893
|
+
pointerEvents: 'none',
|
|
2894
|
+
zIndex: -1,
|
|
2895
|
+
} },
|
|
2896
|
+
React.createElement(Droppable, { droppableId: COMPONENT_LIST_ID, isDropDisabled: true }, (provided) => (React.createElement("div", { ...provided.droppableProps, ref: provided.innerRef },
|
|
2897
|
+
React.createElement(Draggable, { draggableId: id, key: id, index: 0 }, (provided, snapshot) => (React.createElement("div", { id: NEW_COMPONENT_ID, "data-ctfl-dragging-element": true, ref: (node) => {
|
|
2898
|
+
provided.innerRef(node);
|
|
2899
|
+
ref.current = node;
|
|
2900
|
+
}, ...provided.draggableProps, ...provided.dragHandleProps, style: {
|
|
2901
|
+
...getStyle$2(provided.draggableProps.style, snapshot),
|
|
2902
|
+
width: DRAGGABLE_WIDTH,
|
|
2903
|
+
height: DRAGGABLE_HEIGHT,
|
|
2904
|
+
pointerEvents: 'none',
|
|
2905
|
+
} }))),
|
|
2906
|
+
provided.placeholder)))));
|
|
2907
|
+
};
|
|
2908
|
+
|
|
2909
|
+
function getItemFromTree(id, node) {
|
|
2910
|
+
// Check if the current node's id matches the search id
|
|
2911
|
+
if (node.data.id === id) {
|
|
2912
|
+
return node;
|
|
2913
|
+
}
|
|
2914
|
+
// Recursively search through each child
|
|
2915
|
+
for (const child of node.children) {
|
|
2916
|
+
const foundNode = getItemFromTree(id, child);
|
|
2917
|
+
if (foundNode) {
|
|
2918
|
+
// Node found in children
|
|
2919
|
+
return foundNode;
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
// If the node is not found in this branch of the tree, return undefined
|
|
2923
|
+
return undefined;
|
|
2924
|
+
}
|
|
2925
|
+
function findDepthById(node, id, currentDepth = 1) {
|
|
2926
|
+
if (node.data.id === id) {
|
|
2927
|
+
return currentDepth;
|
|
2928
|
+
}
|
|
2929
|
+
// If the node has children, check each one
|
|
2930
|
+
for (const child of node.children) {
|
|
2931
|
+
const childDepth = findDepthById(child, id, currentDepth + 1);
|
|
2932
|
+
if (childDepth !== -1) {
|
|
2933
|
+
return childDepth; // Found the node in a child
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
return -1; // Node not found in this branch
|
|
2937
|
+
}
|
|
2938
|
+
const getChildFromTree = (parentId, index, node) => {
|
|
2939
|
+
// Check if the current node's id matches the search id
|
|
2940
|
+
if (node.data.id === parentId) {
|
|
2941
|
+
return node.children[index];
|
|
2942
|
+
}
|
|
2943
|
+
// Recursively search through each child
|
|
2944
|
+
for (const child of node.children) {
|
|
2945
|
+
const foundNode = getChildFromTree(parentId, index, child);
|
|
2946
|
+
if (foundNode) {
|
|
2947
|
+
// Node found in children
|
|
2948
|
+
return foundNode;
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
// If the node is not found in this branch of the tree, return undefined
|
|
2952
|
+
return undefined;
|
|
2953
|
+
};
|
|
2954
|
+
const getItem = (selector, tree) => {
|
|
2955
|
+
return getItemFromTree(selector.id, {
|
|
2956
|
+
type: 'block',
|
|
2957
|
+
data: {
|
|
2958
|
+
id: ROOT_ID,
|
|
2959
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2960
|
+
},
|
|
2961
|
+
children: tree.root.children,
|
|
2962
|
+
});
|
|
2963
|
+
};
|
|
2964
|
+
const getItemDepthFromNode = (selector, node) => {
|
|
2965
|
+
return findDepthById(node, selector.id);
|
|
2966
|
+
};
|
|
2658
2967
|
|
|
2659
2968
|
function updateNode(nodeId, updatedNode, node) {
|
|
2660
2969
|
if (node.data.id === nodeId) {
|
|
@@ -2693,33 +3002,24 @@ function addChildNode(indexToAdd, parentNodeId, nodeToAdd, node) {
|
|
|
2693
3002
|
}
|
|
2694
3003
|
node.children.forEach((childNode) => addChildNode(indexToAdd, parentNodeId, nodeToAdd, childNode));
|
|
2695
3004
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
3005
|
+
function reorderChildNode(oldIndex, newIndex, parentNodeId, node) {
|
|
3006
|
+
if (node.data.id === parentNodeId) {
|
|
3007
|
+
// Remove the child from the old position
|
|
3008
|
+
const [childToMove] = node.children.splice(oldIndex, 1);
|
|
3009
|
+
// Insert the child at the new position
|
|
3010
|
+
node.children.splice(newIndex, 0, childToMove);
|
|
3011
|
+
return;
|
|
2701
3012
|
}
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
}
|
|
3013
|
+
node.children.forEach((childNode) => reorderChildNode(oldIndex, newIndex, parentNodeId, childNode));
|
|
3014
|
+
}
|
|
3015
|
+
function reparentChildNode(oldIndex, newIndex, sourceNodeId, destinationNodeId, node) {
|
|
3016
|
+
const nodeToMove = getChildFromTree(sourceNodeId, oldIndex, node);
|
|
3017
|
+
if (!nodeToMove) {
|
|
3018
|
+
return;
|
|
2709
3019
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
3020
|
+
removeChildNode(oldIndex, nodeToMove.data.id, sourceNodeId, node);
|
|
3021
|
+
addChildNode(newIndex, destinationNodeId, nodeToMove, node);
|
|
2712
3022
|
}
|
|
2713
|
-
const getItem = (selector, tree) => {
|
|
2714
|
-
return getItemFromTree(selector.id, {
|
|
2715
|
-
type: 'block',
|
|
2716
|
-
data: {
|
|
2717
|
-
id: ROOT_ID,
|
|
2718
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2719
|
-
},
|
|
2720
|
-
children: tree.root.children,
|
|
2721
|
-
});
|
|
2722
|
-
};
|
|
2723
3023
|
|
|
2724
3024
|
function missingNodeAction({ index, nodeAdded, child, tree, parentNodeId, currentNode, }) {
|
|
2725
3025
|
if (nodeAdded) {
|
|
@@ -2815,11 +3115,6 @@ function compareNodes({ currentNode, updatedNode, originalTree, differences = []
|
|
|
2815
3115
|
});
|
|
2816
3116
|
});
|
|
2817
3117
|
map.forEach((index, key) => {
|
|
2818
|
-
// If the node count of the entire tree doesn't signify
|
|
2819
|
-
// a node was removed, don't add that as a diff
|
|
2820
|
-
if (!nodeRemoved) {
|
|
2821
|
-
return;
|
|
2822
|
-
}
|
|
2823
3118
|
// Remaining nodes in the map are removed in the second tree
|
|
2824
3119
|
differences.push({
|
|
2825
3120
|
type: TreeAction.REMOVE_NODE,
|
|
@@ -2841,85 +3136,6 @@ function getTreeDiffs(tree1, tree2, originalTree) {
|
|
|
2841
3136
|
return differences.filter((diff) => diff);
|
|
2842
3137
|
}
|
|
2843
3138
|
|
|
2844
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2845
|
-
const OUTGOING_EVENTS = {
|
|
2846
|
-
Connected: 'connected',
|
|
2847
|
-
DesignTokens: 'registerDesignTokens',
|
|
2848
|
-
RegisteredBreakpoints: 'registeredBreakpoints',
|
|
2849
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2850
|
-
MouseMove: 'mouseMove',
|
|
2851
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2852
|
-
ComponentSelected: 'componentSelected',
|
|
2853
|
-
RegisteredComponents: 'registeredComponents',
|
|
2854
|
-
RequestComponentTreeUpdate: 'requestComponentTreeUpdate',
|
|
2855
|
-
CanvasReload: 'canvasReload',
|
|
2856
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2857
|
-
UpdateSelectedComponentCoordinates: 'updateSelectedComponentCoordinates',
|
|
2858
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2859
|
-
CanvasScroll: 'canvasScrolling',
|
|
2860
|
-
CanvasError: 'canvasError',
|
|
2861
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2862
|
-
OutsideCanvasClick: 'outsideCanvasClick',
|
|
2863
|
-
SDKFeatures: 'sdkFeatures',
|
|
2864
|
-
RequestEntities: 'REQUEST_ENTITIES',
|
|
2865
|
-
CanvasGeometryUpdated: 'canvasGeometryUpdated',
|
|
2866
|
-
};
|
|
2867
|
-
const INCOMING_EVENTS = {
|
|
2868
|
-
RequestEditorMode: 'requestEditorMode',
|
|
2869
|
-
RequestReadOnlyMode: 'requestReadOnlyMode',
|
|
2870
|
-
ExperienceUpdated: 'componentTreeUpdated',
|
|
2871
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2872
|
-
ComponentDraggingChanged: 'componentDraggingChanged',
|
|
2873
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2874
|
-
ComponentDragCanceled: 'componentDragCanceled',
|
|
2875
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2876
|
-
ComponentDragStarted: 'componentDragStarted',
|
|
2877
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2878
|
-
ComponentDragEnded: 'componentDragEnded',
|
|
2879
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2880
|
-
ComponentMoveEnded: 'componentMoveEnded',
|
|
2881
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2882
|
-
CanvasResized: 'canvasResized',
|
|
2883
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2884
|
-
SelectComponent: 'selectComponent',
|
|
2885
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2886
|
-
HoverComponent: 'hoverComponent',
|
|
2887
|
-
UpdatedEntity: 'updatedEntity',
|
|
2888
|
-
AssembliesAdded: 'assembliesAdded',
|
|
2889
|
-
AssembliesRegistered: 'assembliesRegistered',
|
|
2890
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
2891
|
-
MouseMove: 'mouseMove',
|
|
2892
|
-
RequestedEntities: 'REQUESTED_ENTITIES',
|
|
2893
|
-
};
|
|
2894
|
-
const INTERNAL_EVENTS = {
|
|
2895
|
-
ComponentsRegistered: 'cfComponentsRegistered',
|
|
2896
|
-
VisualEditorInitialize: 'cfVisualEditorInitialize',
|
|
2897
|
-
};
|
|
2898
|
-
const VISUAL_EDITOR_EVENTS = {
|
|
2899
|
-
Ready: 'cfVisualEditorReady',
|
|
2900
|
-
};
|
|
2901
|
-
/**
|
|
2902
|
-
* These modes are ONLY intended to be internally used within the context of
|
|
2903
|
-
* editing an experience inside of Contentful Studio. i.e. these modes
|
|
2904
|
-
* intentionally do not include preview/delivery modes.
|
|
2905
|
-
*/
|
|
2906
|
-
var StudioCanvasMode$2;
|
|
2907
|
-
(function (StudioCanvasMode) {
|
|
2908
|
-
StudioCanvasMode["READ_ONLY"] = "readOnlyMode";
|
|
2909
|
-
StudioCanvasMode["EDITOR"] = "editorMode";
|
|
2910
|
-
StudioCanvasMode["NONE"] = "none";
|
|
2911
|
-
})(StudioCanvasMode$2 || (StudioCanvasMode$2 = {}));
|
|
2912
|
-
const ASSEMBLY_NODE_TYPE = 'assembly';
|
|
2913
|
-
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
2914
|
-
const ASSEMBLY_BLOCK_NODE_TYPE = 'assemblyBlock';
|
|
2915
|
-
const EMPTY_CONTAINER_SIZE = '80px';
|
|
2916
|
-
const HYPERLINK_DEFAULT_PATTERN = `/{locale}/{entry.fields.slug}/`;
|
|
2917
|
-
var PostMessageMethods$2;
|
|
2918
|
-
(function (PostMessageMethods) {
|
|
2919
|
-
PostMessageMethods["REQUEST_ENTITIES"] = "REQUEST_ENTITIES";
|
|
2920
|
-
PostMessageMethods["REQUESTED_ENTITIES"] = "REQUESTED_ENTITIES";
|
|
2921
|
-
})(PostMessageMethods$2 || (PostMessageMethods$2 = {}));
|
|
2922
|
-
|
|
2923
3139
|
const useTreeStore = create((set, get) => ({
|
|
2924
3140
|
tree: {
|
|
2925
3141
|
root: {
|
|
@@ -2951,6 +3167,20 @@ const useTreeStore = create((set, get) => ({
|
|
|
2951
3167
|
});
|
|
2952
3168
|
}));
|
|
2953
3169
|
},
|
|
3170
|
+
/**
|
|
3171
|
+
* NOTE: this is for debugging purposes only as it causes ugly canvas flash.
|
|
3172
|
+
*
|
|
3173
|
+
* Force updates entire tree. Usually shouldn't be used as updateTree()
|
|
3174
|
+
* uses smart update algorithm based on diffs. But for troubleshooting
|
|
3175
|
+
* you may want to force update the tree so leaving this in.
|
|
3176
|
+
*/
|
|
3177
|
+
updateTreeForced: (tree) => {
|
|
3178
|
+
set({
|
|
3179
|
+
tree,
|
|
3180
|
+
// Breakpoints must be updated, as we receive completely new tree with possibly new breakpoints
|
|
3181
|
+
breakpoints: tree?.root?.data?.breakpoints || [],
|
|
3182
|
+
});
|
|
3183
|
+
},
|
|
2954
3184
|
updateTree: (tree) => {
|
|
2955
3185
|
const currentTree = get().tree;
|
|
2956
3186
|
/**
|
|
@@ -2996,6 +3226,21 @@ const useTreeStore = create((set, get) => ({
|
|
|
2996
3226
|
state.breakpoints = tree?.root?.data?.breakpoints || [];
|
|
2997
3227
|
}));
|
|
2998
3228
|
},
|
|
3229
|
+
addChild: (index, parentId, node) => {
|
|
3230
|
+
set(produce((state) => {
|
|
3231
|
+
addChildNode(index, parentId, node, state.tree.root);
|
|
3232
|
+
}));
|
|
3233
|
+
},
|
|
3234
|
+
reorderChildren: (destinationIndex, destinationParentId, sourceIndex) => {
|
|
3235
|
+
set(produce((state) => {
|
|
3236
|
+
reorderChildNode(sourceIndex, destinationIndex, destinationParentId, state.tree.root);
|
|
3237
|
+
}));
|
|
3238
|
+
},
|
|
3239
|
+
reparentChild: (destinationIndex, destinationParentId, sourceIndex, sourceParentId) => {
|
|
3240
|
+
set(produce((state) => {
|
|
3241
|
+
reparentChildNode(sourceIndex, destinationIndex, sourceParentId, destinationParentId, state.tree.root);
|
|
3242
|
+
}));
|
|
3243
|
+
},
|
|
2999
3244
|
// breadth first search
|
|
3000
3245
|
findNodeById(nodeId) {
|
|
3001
3246
|
if (!nodeId) {
|
|
@@ -3033,8 +3278,8 @@ const cloneDeepAsPOJO = (obj) => {
|
|
|
3033
3278
|
return JSON.parse(JSON.stringify(obj));
|
|
3034
3279
|
};
|
|
3035
3280
|
|
|
3036
|
-
var css_248z$a = ".
|
|
3037
|
-
var styles$
|
|
3281
|
+
var css_248z$a = ".render-module_hitbox__l4ysJ {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 10px;\n z-index: 1000000;\n}\n\n.render-module_hitboxLower__tgsA1 {\n position: absolute;\n bottom: -20px;\n left: 0;\n width: 100%;\n height: 20px;\n z-index: 1000000;\n}\n\n.render-module_canvasBottomSpacer__JuxVh {\n position: absolute;\n width: 100%;\n height: 50px;\n}\n\n.render-module_container__-C3d7 {\n position: relative;\n display: flex;\n flex-direction: column;\n}\n\nbody {\n margin: 0;\n}\n\nhtml {\n -ms-overflow-style: none; /* Internet Explorer 10+ */\n scrollbar-width: none; /* Firefox */\n}\n\nhtml::-webkit-scrollbar {\n display: none;\n}\n";
|
|
3282
|
+
var styles$3 = {"hitbox":"render-module_hitbox__l4ysJ","hitboxLower":"render-module_hitboxLower__tgsA1","canvasBottomSpacer":"render-module_canvasBottomSpacer__JuxVh","container":"render-module_container__-C3d7"};
|
|
3038
3283
|
styleInject(css_248z$a);
|
|
3039
3284
|
|
|
3040
3285
|
// TODO: In order to support integrations without React, we should extract this heavy logic into simple
|
|
@@ -3073,6 +3318,66 @@ const useBreakpoints = (breakpoints) => {
|
|
|
3073
3318
|
return { resolveDesignValue };
|
|
3074
3319
|
};
|
|
3075
3320
|
|
|
3321
|
+
/**
|
|
3322
|
+
* This function gets the element co-ordinates of a specified component in the DOM and its parent
|
|
3323
|
+
* and sends the DOM Rect to the client app
|
|
3324
|
+
*/
|
|
3325
|
+
const sendSelectedComponentCoordinates = (instanceId) => {
|
|
3326
|
+
const selection = getSelectionNodes(instanceId);
|
|
3327
|
+
if (selection?.target) {
|
|
3328
|
+
const sendUpdateSelectedComponentCoordinates = () => {
|
|
3329
|
+
sendMessage(OUTGOING_EVENTS.UpdateSelectedComponentCoordinates, {
|
|
3330
|
+
selectedNodeCoordinates: getElementCoordinates(selection.target),
|
|
3331
|
+
selectedAssemblyChildCoordinates: selection.patternChild
|
|
3332
|
+
? getElementCoordinates(selection.patternChild)
|
|
3333
|
+
: undefined,
|
|
3334
|
+
parentCoordinates: selection.parent ? getElementCoordinates(selection.parent) : undefined,
|
|
3335
|
+
});
|
|
3336
|
+
};
|
|
3337
|
+
// If the target contains an image, wait for this image to be loaded before sending the coordinates
|
|
3338
|
+
const childImage = selection.target.querySelector('img');
|
|
3339
|
+
if (childImage) {
|
|
3340
|
+
const handleImageLoad = () => {
|
|
3341
|
+
sendUpdateSelectedComponentCoordinates();
|
|
3342
|
+
childImage.removeEventListener('load', handleImageLoad);
|
|
3343
|
+
};
|
|
3344
|
+
childImage.addEventListener('load', handleImageLoad);
|
|
3345
|
+
}
|
|
3346
|
+
sendUpdateSelectedComponentCoordinates();
|
|
3347
|
+
}
|
|
3348
|
+
};
|
|
3349
|
+
const getSelectionNodes = (instanceId) => {
|
|
3350
|
+
if (!instanceId)
|
|
3351
|
+
return;
|
|
3352
|
+
let selectedNode = document.querySelector(`[data-cf-node-id="${instanceId}"]`);
|
|
3353
|
+
let selectedPatternChild = null;
|
|
3354
|
+
let selectedParent = null;
|
|
3355
|
+
// Use RegEx instead of split to match the last occurrence of '---' in the instanceId instead of the first one
|
|
3356
|
+
const idMatch = instanceId.match(/(.*)---(.*)/);
|
|
3357
|
+
const rootNodeId = idMatch?.[1] ?? instanceId;
|
|
3358
|
+
const nodeLocation = idMatch?.[2];
|
|
3359
|
+
const isNestedPattern = nodeLocation && selectedNode?.dataset?.cfNodeBlockType === ASSEMBLY_NODE_TYPE;
|
|
3360
|
+
const isPatternChild = !isNestedPattern && nodeLocation;
|
|
3361
|
+
if (isPatternChild) {
|
|
3362
|
+
// For pattern child nodes, render the pattern itself as selected component
|
|
3363
|
+
selectedPatternChild = selectedNode;
|
|
3364
|
+
selectedNode = document.querySelector(`[data-cf-node-id="${rootNodeId}"]`);
|
|
3365
|
+
}
|
|
3366
|
+
else if (isNestedPattern) {
|
|
3367
|
+
// For nested patterns, return the upper pattern as parent
|
|
3368
|
+
selectedParent = document.querySelector(`[data-cf-node-id="${rootNodeId}"]`);
|
|
3369
|
+
}
|
|
3370
|
+
else {
|
|
3371
|
+
// Find the next valid parent of the selected element
|
|
3372
|
+
selectedParent = selectedNode?.parentElement ?? null;
|
|
3373
|
+
// Ensure that the selection parent is a VisualEditorBlock
|
|
3374
|
+
while (selectedParent && !selectedParent.dataset?.cfNodeId) {
|
|
3375
|
+
selectedParent = selectedParent?.parentElement;
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
return { target: selectedNode, patternChild: selectedPatternChild, parent: selectedParent };
|
|
3379
|
+
};
|
|
3380
|
+
|
|
3076
3381
|
// Note: During development, the hot reloading might empty this and it
|
|
3077
3382
|
// stays empty leading to not rendering assemblies. Ideally, this is
|
|
3078
3383
|
// integrated into the state machine to keep track of its state.
|
|
@@ -3106,10 +3411,14 @@ const useEditorStore = create((set, get) => ({
|
|
|
3106
3411
|
dataSource: {},
|
|
3107
3412
|
hyperLinkPattern: undefined,
|
|
3108
3413
|
unboundValues: {},
|
|
3414
|
+
selectedNodeId: null,
|
|
3109
3415
|
locale: null,
|
|
3110
3416
|
setHyperLinkPattern: (pattern) => {
|
|
3111
3417
|
set({ hyperLinkPattern: pattern });
|
|
3112
3418
|
},
|
|
3419
|
+
setSelectedNodeId: (id) => {
|
|
3420
|
+
set({ selectedNodeId: id });
|
|
3421
|
+
},
|
|
3113
3422
|
setDataSource(data) {
|
|
3114
3423
|
const dataSource = get().dataSource;
|
|
3115
3424
|
const newDataSource = { ...dataSource, ...data };
|
|
@@ -3141,7 +3450,6 @@ const useEditorStore = create((set, get) => ({
|
|
|
3141
3450
|
var css_248z$8 = "@import url(https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,400;0,500;0,600;1,400;1,600&display=swap);:root{--cf-color-white:#fff;--cf-color-black:#000;--cf-color-gray100:#f7f9fa;--cf-color-gray400:#aec1cc;--cf-color-gray400-rgb:174,193,204;--cf-spacing-0:0rem;--cf-spacing-1:0.125rem;--cf-spacing-2:0.25rem;--cf-spacing-3:0.375rem;--cf-spacing-4:0.5rem;--cf-spacing-5:0.625rem;--cf-spacing-6:0.75rem;--cf-spacing-7:0.875rem;--cf-spacing-8:1rem;--cf-spacing-9:1.25rem;--cf-spacing-10:1.5rem;--cf-spacing-11:1.75rem;--cf-spacing-12:2rem;--cf-spacing-13:2.25rem;--cf-text-xs:0.75rem;--cf-text-sm:0.875rem;--cf-text-base:1rem;--cf-text-lg:1.125rem;--cf-text-xl:1.25rem;--cf-text-2xl:1.5rem;--cf-text-3xl:2rem;--cf-text-4xl:2.75rem;--cf-font-light:300;--cf-font-normal:400;--cf-font-medium:500;--cf-font-semibold:600;--cf-font-bold:700;--cf-font-extra-bold:800;--cf-font-black:900;--cf-border-radius-none:0px;--cf-border-radius-sm:0.125rem;--cf-border-radius:0.25rem;--cf-border-radius-md:0.375rem;--cf-border-radius-lg:0.5rem;--cf-border-radius-xl:0.75rem;--cf-border-radius-2xl:1rem;--cf-border-radius-3xl:1.5rem;--cf-border-radius-full:9999px;--cf-font-family-sans:Archivo,Helvetica,Arial,sans-serif;--cf-font-family-serif:Georgia,Cambria,Times New Roman,Times,serif;--cf-max-width-full:100%;--cf-button-bg:var(--cf-color-black);--cf-button-color:var(--cf-color-white);--cf-text-color:var(--cf-color-black)}*{box-sizing:border-box}";
|
|
3142
3451
|
styleInject(css_248z$8);
|
|
3143
3452
|
|
|
3144
|
-
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3145
3453
|
/**
|
|
3146
3454
|
* These modes are ONLY intended to be internally used within the context of
|
|
3147
3455
|
* editing an experience inside of Contentful Studio. i.e. these modes
|
|
@@ -4146,8 +4454,8 @@ var VisualEditorMode;
|
|
|
4146
4454
|
VisualEditorMode["InjectScript"] = "injectScript";
|
|
4147
4455
|
})(VisualEditorMode || (VisualEditorMode = {}));
|
|
4148
4456
|
|
|
4149
|
-
var css_248z$2 = ".contentful-container{display:flex;pointer-events:all;position:relative}.contentful-container::-webkit-scrollbar{display:none}.cf-container-wrapper{position:relative;width:100%}.contentful-container:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.contentful-section-label:after{content:\"Section\"}.contentful-container-label:after{content:\"Container\"}.contentful-container-link,.contentful-container-link:active,.contentful-container-link:focus-visible,.contentful-container-link:hover,.contentful-container-link:read-write,.contentful-container-link:visited{color:inherit;outline:unset;text-decoration:unset}";
|
|
4150
|
-
styleInject(css_248z$2);
|
|
4457
|
+
var css_248z$2$1 = ".contentful-container{display:flex;pointer-events:all;position:relative}.contentful-container::-webkit-scrollbar{display:none}.cf-single-column-wrapper{position:relative}.cf-container-wrapper{position:relative;width:100%}.contentful-container:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.contentful-section-label:after{content:\"Section\"}.contentful-container-label:after{content:\"Container\"}.contentful-container-link,.contentful-container-link:active,.contentful-container-link:focus-visible,.contentful-container-link:hover,.contentful-container-link:read-write,.contentful-container-link:visited{color:inherit;outline:unset;text-decoration:unset}";
|
|
4458
|
+
styleInject(css_248z$2$1);
|
|
4151
4459
|
|
|
4152
4460
|
const Flex = forwardRef(({ id, children, onMouseEnter, onMouseUp, onMouseLeave, onMouseDown, onClick, flex, flexBasis, flexShrink, flexDirection, gap, justifyContent, justifyItems, justifySelf, alignItems, alignSelf, alignContent, order, flexWrap, flexGrow, className, cssStyles, ...props }, ref) => {
|
|
4153
4461
|
return (React.createElement("div", { id: id, ref: ref, style: {
|
|
@@ -4171,24 +4479,113 @@ const Flex = forwardRef(({ id, children, onMouseEnter, onMouseUp, onMouseLeave,
|
|
|
4171
4479
|
});
|
|
4172
4480
|
Flex.displayName = 'Flex';
|
|
4173
4481
|
|
|
4174
|
-
var css_248z$1$1 = ".cf-divider{display:contents;height:100%;position:relative;width:100%}.cf-divider hr{border:none}";
|
|
4482
|
+
var css_248z$1$1 = ".cf-divider{display:contents;height:100%;position:relative;width:100%}.cf-divider hr{border:none}[data-ctfl-zone-id=root] .cf-divider:before{bottom:-5px;content:\"\";left:-5px;pointer-events:all;position:absolute;right:-5px;top:-5px}";
|
|
4175
4483
|
styleInject(css_248z$1$1);
|
|
4176
4484
|
|
|
4177
|
-
var css_248z$9 = ".cf-columns{display:flex;flex-direction:column;gap:24px;grid-template-columns:repeat(12,1fr);min-height:0;min-width:0}@media (min-width:768px){.cf-columns{display:grid}}.cf-single-column-wrapper{position:relative}.cf-single-column-wrapper:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.cf-single-column-label:after{content:\"Column\"}";
|
|
4485
|
+
var css_248z$9 = ".cf-columns{display:flex;flex-direction:column;gap:24px;grid-template-columns:repeat(12,1fr);min-height:0;min-width:0}@media (min-width:768px){.cf-columns{display:grid}}.cf-single-column-wrapper{display:flex;position:relative}.cf-single-column{pointer-events:all}.cf-single-column-wrapper:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.cf-single-column-label:after{content:\"Column\"}";
|
|
4178
4486
|
styleInject(css_248z$9);
|
|
4179
4487
|
|
|
4488
|
+
const ColumnWrapper = forwardRef((props, ref) => {
|
|
4489
|
+
return (React.createElement("div", { ref: ref, ...props, style: {
|
|
4490
|
+
...(props.style || {}),
|
|
4491
|
+
display: 'grid',
|
|
4492
|
+
gridTemplateColumns: 'repeat(12, [col-start] 1fr)',
|
|
4493
|
+
} }, props.children));
|
|
4494
|
+
});
|
|
4495
|
+
ColumnWrapper.displayName = 'ColumnWrapper';
|
|
4496
|
+
|
|
4180
4497
|
const assemblyStyle = { display: 'contents' };
|
|
4498
|
+
// Feel free to do any magic as regards variable definitions for assemblies
|
|
4499
|
+
// Or if this isn't necessary by the time we figure that part out, we can bid this part farewell
|
|
4181
4500
|
const Assembly = (props) => {
|
|
4501
|
+
if (props.editorMode) {
|
|
4502
|
+
const { node, dragProps, ...editorModeProps } = props;
|
|
4503
|
+
return props.renderDropzone(node, {
|
|
4504
|
+
...editorModeProps,
|
|
4505
|
+
['data-test-id']: 'contentful-assembly',
|
|
4506
|
+
className: props.className,
|
|
4507
|
+
dragProps,
|
|
4508
|
+
});
|
|
4509
|
+
}
|
|
4182
4510
|
// Using a display contents so assembly content/children
|
|
4183
4511
|
// can appear as if they are direct children of the div wrapper's parent
|
|
4184
4512
|
return React.createElement("div", { "data-test-id": "assembly", ...props, style: assemblyStyle });
|
|
4185
4513
|
};
|
|
4186
4514
|
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4515
|
+
class DragState {
|
|
4516
|
+
constructor() {
|
|
4517
|
+
this.isDragStartedOnParent = false;
|
|
4518
|
+
this.isDraggingItem = false;
|
|
4519
|
+
}
|
|
4520
|
+
get isDragging() {
|
|
4521
|
+
return this.isDraggingItem;
|
|
4522
|
+
}
|
|
4523
|
+
get isDraggingOnParent() {
|
|
4524
|
+
return this.isDragStartedOnParent;
|
|
4525
|
+
}
|
|
4526
|
+
updateIsDragging(isDraggingItem) {
|
|
4527
|
+
this.isDraggingItem = isDraggingItem;
|
|
4528
|
+
}
|
|
4529
|
+
updateIsDragStartedOnParent(isDragStartedOnParent) {
|
|
4530
|
+
this.isDragStartedOnParent = isDragStartedOnParent;
|
|
4531
|
+
}
|
|
4532
|
+
resetState() {
|
|
4533
|
+
this.isDraggingItem = false;
|
|
4534
|
+
this.isDragStartedOnParent = false;
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
|
|
4538
|
+
class SimulateDnD extends DragState {
|
|
4539
|
+
constructor() {
|
|
4540
|
+
super();
|
|
4541
|
+
this.draggingElement = null;
|
|
4542
|
+
}
|
|
4543
|
+
setupDrag() {
|
|
4544
|
+
this.updateIsDragStartedOnParent(true);
|
|
4545
|
+
}
|
|
4546
|
+
startDrag(coordX, coordY) {
|
|
4547
|
+
this.draggingElement = document.getElementById(NEW_COMPONENT_ID);
|
|
4548
|
+
this.updateIsDragging(true);
|
|
4549
|
+
this.simulateMouseEvent(coordX, coordY, 'mousedown');
|
|
4550
|
+
}
|
|
4551
|
+
updateDrag(coordX, coordY) {
|
|
4552
|
+
if (!this.draggingElement) {
|
|
4553
|
+
this.draggingElement = document.querySelector(`[${CTFL_DRAGGING_ELEMENT}]`);
|
|
4554
|
+
}
|
|
4555
|
+
this.simulateMouseEvent(coordX, coordY);
|
|
4556
|
+
}
|
|
4557
|
+
endDrag(coordX, coordY) {
|
|
4558
|
+
this.simulateMouseEvent(coordX, coordY, 'mouseup');
|
|
4559
|
+
this.reset();
|
|
4560
|
+
}
|
|
4561
|
+
reset() {
|
|
4562
|
+
this.draggingElement = null;
|
|
4563
|
+
this.resetState();
|
|
4564
|
+
}
|
|
4565
|
+
simulateMouseEvent(coordX, coordY, eventName = 'mousemove') {
|
|
4566
|
+
if (!this.draggingElement) {
|
|
4567
|
+
return;
|
|
4568
|
+
}
|
|
4569
|
+
const options = {
|
|
4570
|
+
bubbles: true,
|
|
4571
|
+
cancelable: true,
|
|
4572
|
+
view: window,
|
|
4573
|
+
pageX: 0,
|
|
4574
|
+
pageY: 0,
|
|
4575
|
+
clientX: coordX,
|
|
4576
|
+
clientY: coordY,
|
|
4577
|
+
};
|
|
4578
|
+
const event = new MouseEvent(eventName, options);
|
|
4579
|
+
this.draggingElement.dispatchEvent(event);
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
var SimulateDnD$1 = new SimulateDnD();
|
|
4583
|
+
|
|
4584
|
+
function useEditorSubscriber(entityCache) {
|
|
4585
|
+
const entityStore = entityCache((state) => state.entityStore);
|
|
4586
|
+
const areEntitiesFetched = entityCache((state) => state.areEntitiesFetched);
|
|
4587
|
+
const setEntitiesFetched = entityCache((state) => state.setEntitiesFetched);
|
|
4588
|
+
const resetEntityStore = entityCache((state) => state.resetEntityStore);
|
|
4192
4589
|
const { updateTree, updateNodesByUpdatedEntity } = useTreeStore((state) => ({
|
|
4193
4590
|
updateTree: state.updateTree,
|
|
4194
4591
|
updateNodesByUpdatedEntity: state.updateNodesByUpdatedEntity,
|
|
@@ -4198,6 +4595,13 @@ function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
|
4198
4595
|
const setLocale = useEditorStore((state) => state.setLocale);
|
|
4199
4596
|
const setUnboundValues = useEditorStore((state) => state.setUnboundValues);
|
|
4200
4597
|
const setDataSource = useEditorStore((state) => state.setDataSource);
|
|
4598
|
+
const setSelectedNodeId = useEditorStore((state) => state.setSelectedNodeId);
|
|
4599
|
+
const selectedNodeId = useEditorStore((state) => state.selectedNodeId);
|
|
4600
|
+
const setComponentId = useDraggedItemStore((state) => state.setComponentId);
|
|
4601
|
+
const setHoveredComponentId = useDraggedItemStore((state) => state.setHoveredComponentId);
|
|
4602
|
+
const setDraggingOnCanvas = useDraggedItemStore((state) => state.setDraggingOnCanvas);
|
|
4603
|
+
const setMousePosition = useDraggedItemStore((state) => state.setMousePosition);
|
|
4604
|
+
const setScrollY = useDraggedItemStore((state) => state.setScrollY);
|
|
4201
4605
|
const reloadApp = () => {
|
|
4202
4606
|
sendMessage(OUTGOING_EVENTS.CanvasReload, undefined);
|
|
4203
4607
|
// Wait a moment to ensure that the message was sent
|
|
@@ -4375,6 +4779,27 @@ function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
|
4375
4779
|
}
|
|
4376
4780
|
break;
|
|
4377
4781
|
}
|
|
4782
|
+
case INCOMING_EVENTS.CanvasResized: {
|
|
4783
|
+
const { selectedNodeId } = eventData.payload;
|
|
4784
|
+
if (selectedNodeId) {
|
|
4785
|
+
sendSelectedComponentCoordinates(selectedNodeId);
|
|
4786
|
+
}
|
|
4787
|
+
break;
|
|
4788
|
+
}
|
|
4789
|
+
case INCOMING_EVENTS.HoverComponent: {
|
|
4790
|
+
const { hoveredNodeId } = eventData.payload;
|
|
4791
|
+
setHoveredComponentId(hoveredNodeId);
|
|
4792
|
+
break;
|
|
4793
|
+
}
|
|
4794
|
+
case INCOMING_EVENTS.ComponentDraggingChanged: {
|
|
4795
|
+
const { isDragging } = eventData.payload;
|
|
4796
|
+
if (!isDragging) {
|
|
4797
|
+
setComponentId('');
|
|
4798
|
+
setDraggingOnCanvas(false);
|
|
4799
|
+
SimulateDnD$1.reset();
|
|
4800
|
+
}
|
|
4801
|
+
break;
|
|
4802
|
+
}
|
|
4378
4803
|
case INCOMING_EVENTS.UpdatedEntity: {
|
|
4379
4804
|
const { entity: updatedEntity, shouldRerender } = eventData.payload;
|
|
4380
4805
|
if (updatedEntity) {
|
|
@@ -4391,6 +4816,51 @@ function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
|
4391
4816
|
case INCOMING_EVENTS.RequestEditorMode: {
|
|
4392
4817
|
break;
|
|
4393
4818
|
}
|
|
4819
|
+
case INCOMING_EVENTS.ComponentDragCanceled: {
|
|
4820
|
+
if (SimulateDnD$1.isDragging) {
|
|
4821
|
+
//simulate a mouseup event to cancel the drag
|
|
4822
|
+
SimulateDnD$1.endDrag(0, 0);
|
|
4823
|
+
}
|
|
4824
|
+
break;
|
|
4825
|
+
}
|
|
4826
|
+
case INCOMING_EVENTS.ComponentDragStarted: {
|
|
4827
|
+
const { id, isAssembly } = eventData.payload;
|
|
4828
|
+
SimulateDnD$1.setupDrag();
|
|
4829
|
+
setComponentId(`${id}:${isAssembly}` || '');
|
|
4830
|
+
setDraggingOnCanvas(true);
|
|
4831
|
+
sendMessage(OUTGOING_EVENTS.ComponentSelected, {
|
|
4832
|
+
nodeId: '',
|
|
4833
|
+
});
|
|
4834
|
+
break;
|
|
4835
|
+
}
|
|
4836
|
+
case INCOMING_EVENTS.ComponentDragEnded: {
|
|
4837
|
+
SimulateDnD$1.reset();
|
|
4838
|
+
setComponentId('');
|
|
4839
|
+
setDraggingOnCanvas(false);
|
|
4840
|
+
break;
|
|
4841
|
+
}
|
|
4842
|
+
case INCOMING_EVENTS.SelectComponent: {
|
|
4843
|
+
const { selectedNodeId: nodeId } = eventData.payload;
|
|
4844
|
+
setSelectedNodeId(nodeId);
|
|
4845
|
+
sendSelectedComponentCoordinates(nodeId);
|
|
4846
|
+
break;
|
|
4847
|
+
}
|
|
4848
|
+
case INCOMING_EVENTS.MouseMove: {
|
|
4849
|
+
const { mouseX, mouseY } = eventData.payload;
|
|
4850
|
+
setMousePosition(mouseX, mouseY);
|
|
4851
|
+
if (SimulateDnD$1.isDraggingOnParent && !SimulateDnD$1.isDragging) {
|
|
4852
|
+
SimulateDnD$1.startDrag(mouseX, mouseY);
|
|
4853
|
+
}
|
|
4854
|
+
else {
|
|
4855
|
+
SimulateDnD$1.updateDrag(mouseX, mouseY);
|
|
4856
|
+
}
|
|
4857
|
+
break;
|
|
4858
|
+
}
|
|
4859
|
+
case INCOMING_EVENTS.ComponentMoveEnded: {
|
|
4860
|
+
const { mouseX, mouseY } = eventData.payload;
|
|
4861
|
+
SimulateDnD$1.endDrag(mouseX, mouseY);
|
|
4862
|
+
break;
|
|
4863
|
+
}
|
|
4394
4864
|
default:
|
|
4395
4865
|
console.error(`[experiences-sdk-react::onMessage] Logic error, unsupported eventType: [${eventData.eventType}]`);
|
|
4396
4866
|
}
|
|
@@ -4401,8 +4871,11 @@ function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
|
4401
4871
|
};
|
|
4402
4872
|
}, [
|
|
4403
4873
|
entityStore,
|
|
4874
|
+
setComponentId,
|
|
4875
|
+
setDraggingOnCanvas,
|
|
4404
4876
|
setDataSource,
|
|
4405
4877
|
setLocale,
|
|
4878
|
+
setSelectedNodeId,
|
|
4406
4879
|
dataSource,
|
|
4407
4880
|
areEntitiesFetched,
|
|
4408
4881
|
fetchMissingEntities,
|
|
@@ -4410,123 +4883,360 @@ function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
|
4410
4883
|
unboundValues,
|
|
4411
4884
|
updateTree,
|
|
4412
4885
|
updateNodesByUpdatedEntity,
|
|
4886
|
+
setMousePosition,
|
|
4413
4887
|
resetEntityStore,
|
|
4888
|
+
setHoveredComponentId,
|
|
4414
4889
|
]);
|
|
4890
|
+
/*
|
|
4891
|
+
* Handles on scroll business
|
|
4892
|
+
*/
|
|
4893
|
+
useEffect(() => {
|
|
4894
|
+
let timeoutId = 0;
|
|
4895
|
+
let isScrolling = false;
|
|
4896
|
+
const onScroll = () => {
|
|
4897
|
+
setScrollY(window.scrollY);
|
|
4898
|
+
if (isScrolling === false) {
|
|
4899
|
+
sendMessage(OUTGOING_EVENTS.CanvasScroll, SCROLL_STATES.Start);
|
|
4900
|
+
}
|
|
4901
|
+
sendMessage(OUTGOING_EVENTS.CanvasScroll, SCROLL_STATES.IsScrolling);
|
|
4902
|
+
isScrolling = true;
|
|
4903
|
+
clearTimeout(timeoutId);
|
|
4904
|
+
timeoutId = window.setTimeout(() => {
|
|
4905
|
+
if (isScrolling === false) {
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
isScrolling = false;
|
|
4909
|
+
sendMessage(OUTGOING_EVENTS.CanvasScroll, SCROLL_STATES.End);
|
|
4910
|
+
/**
|
|
4911
|
+
* On scroll end, send new co-ordinates of selected node
|
|
4912
|
+
*/
|
|
4913
|
+
if (selectedNodeId) {
|
|
4914
|
+
sendSelectedComponentCoordinates(selectedNodeId);
|
|
4915
|
+
}
|
|
4916
|
+
}, 150);
|
|
4917
|
+
};
|
|
4918
|
+
window.addEventListener('scroll', onScroll, { capture: true, passive: true });
|
|
4919
|
+
return () => {
|
|
4920
|
+
window.removeEventListener('scroll', onScroll, { capture: true });
|
|
4921
|
+
clearTimeout(timeoutId);
|
|
4922
|
+
};
|
|
4923
|
+
}, [selectedNodeId, setScrollY]);
|
|
4415
4924
|
}
|
|
4416
4925
|
|
|
4417
|
-
const
|
|
4418
|
-
|
|
4419
|
-
border: '1px solid red',
|
|
4420
|
-
background: 'rgba(255, 0, 0, 0.1)',
|
|
4421
|
-
padding: '1rem 1rem 0 1rem',
|
|
4422
|
-
width: '100%',
|
|
4423
|
-
height: '100%',
|
|
4424
|
-
} },
|
|
4425
|
-
"Circular usage of patterns detected:",
|
|
4426
|
-
React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
|
|
4427
|
-
const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
|
|
4428
|
-
const entry = inMemoryEntities.maybeResolveLink(entryLink);
|
|
4429
|
-
const entryTitle = entry?.fields?.title;
|
|
4430
|
-
const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
|
|
4431
|
-
return React.createElement("li", { key: patternId }, text);
|
|
4432
|
-
}))));
|
|
4926
|
+
const onComponentMoved = (options) => {
|
|
4927
|
+
sendMessage(OUTGOING_EVENTS.ComponentMoved, options);
|
|
4433
4928
|
};
|
|
4434
4929
|
|
|
4435
|
-
|
|
4436
|
-
constructor(message) {
|
|
4437
|
-
super(message);
|
|
4438
|
-
this.name = 'ImportedComponentError';
|
|
4439
|
-
}
|
|
4440
|
-
}
|
|
4441
|
-
class ExperienceSDKError extends Error {
|
|
4442
|
-
constructor(message) {
|
|
4443
|
-
super(message);
|
|
4444
|
-
this.name = 'ExperienceSDKError';
|
|
4445
|
-
}
|
|
4446
|
-
}
|
|
4447
|
-
class ImportedComponentErrorBoundary extends React.Component {
|
|
4448
|
-
componentDidCatch(error, _errorInfo) {
|
|
4449
|
-
if (error.name === 'ImportedComponentError' || error.name === 'ExperienceSDKError') {
|
|
4450
|
-
// This error was already handled by a nested error boundary and should be passed upwards
|
|
4451
|
-
// We have to do this as we wrap every component on every layer with this error boundary and
|
|
4452
|
-
// thus an error deep in the tree bubbles through many layers of error boundaries.
|
|
4453
|
-
throw error;
|
|
4454
|
-
}
|
|
4455
|
-
// Differentiate between custom and SDK-provided components for error tracking
|
|
4456
|
-
const ErrorClass = isContentfulComponent(this.props.componentId)
|
|
4457
|
-
? ExperienceSDKError
|
|
4458
|
-
: ImportedComponentError;
|
|
4459
|
-
const err = new ErrorClass(error.message);
|
|
4460
|
-
err.stack = error.stack;
|
|
4461
|
-
throw err;
|
|
4462
|
-
}
|
|
4463
|
-
render() {
|
|
4464
|
-
return this.props.children;
|
|
4465
|
-
}
|
|
4466
|
-
}
|
|
4930
|
+
const generateId = (type) => `${type}-${v4()}`;
|
|
4467
4931
|
|
|
4468
|
-
const
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4932
|
+
const createTreeNode = ({ blockId, parentId, slotId }) => {
|
|
4933
|
+
const node = {
|
|
4934
|
+
type: 'block',
|
|
4935
|
+
data: {
|
|
4936
|
+
id: generateId(blockId),
|
|
4937
|
+
blockId,
|
|
4938
|
+
slotId,
|
|
4939
|
+
props: {},
|
|
4940
|
+
dataSource: {},
|
|
4941
|
+
breakpoints: [],
|
|
4942
|
+
unboundValues: {},
|
|
4943
|
+
},
|
|
4944
|
+
parentId,
|
|
4945
|
+
children: [],
|
|
4946
|
+
};
|
|
4947
|
+
return node;
|
|
4477
4948
|
};
|
|
4478
4949
|
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4950
|
+
const onComponentDropped = ({ node, index, parentBlockId, parentType, parentId, }) => {
|
|
4951
|
+
sendMessage(OUTGOING_EVENTS.ComponentDropped, {
|
|
4952
|
+
node,
|
|
4953
|
+
index: index ?? node.children.length,
|
|
4954
|
+
parentNode: {
|
|
4955
|
+
type: parentType,
|
|
4956
|
+
data: {
|
|
4957
|
+
blockId: parentBlockId,
|
|
4958
|
+
id: parentId,
|
|
4959
|
+
},
|
|
4960
|
+
},
|
|
4961
|
+
});
|
|
4962
|
+
};
|
|
4482
4963
|
|
|
4483
|
-
const
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4964
|
+
const onDrop = ({ destinationIndex, componentType, destinationZoneId, data, slotId, }) => {
|
|
4965
|
+
const parentId = destinationZoneId;
|
|
4966
|
+
const parentNode = getItem({ id: parentId }, data);
|
|
4967
|
+
const parentIsRoot = parentId === ROOT_ID;
|
|
4968
|
+
const emptyComponentData = {
|
|
4969
|
+
type: 'block',
|
|
4970
|
+
parentId,
|
|
4971
|
+
children: [],
|
|
4972
|
+
data: {
|
|
4973
|
+
blockId: componentType,
|
|
4974
|
+
id: generateId(componentType),
|
|
4975
|
+
slotId,
|
|
4976
|
+
breakpoints: [],
|
|
4977
|
+
dataSource: {},
|
|
4978
|
+
props: {},
|
|
4979
|
+
unboundValues: {},
|
|
4980
|
+
},
|
|
4981
|
+
};
|
|
4982
|
+
onComponentDropped({
|
|
4983
|
+
node: emptyComponentData,
|
|
4984
|
+
index: destinationIndex,
|
|
4985
|
+
parentType: parentIsRoot ? 'root' : parentNode?.type,
|
|
4986
|
+
parentBlockId: parentNode?.data.blockId,
|
|
4987
|
+
parentId: parentIsRoot ? 'root' : parentId,
|
|
4988
|
+
});
|
|
4498
4989
|
};
|
|
4499
4990
|
|
|
4500
4991
|
/**
|
|
4501
|
-
*
|
|
4502
|
-
*
|
|
4503
|
-
*
|
|
4504
|
-
*
|
|
4505
|
-
*
|
|
4992
|
+
* Parses a droppable zone ID into a node ID and slot ID.
|
|
4993
|
+
*
|
|
4994
|
+
* The slot ID is optional and only present if the component implements multiple drop zones.
|
|
4995
|
+
*
|
|
4996
|
+
* @param zoneId - Expected formats are `nodeId` or `nodeId|slotId`.
|
|
4506
4997
|
*/
|
|
4507
|
-
const
|
|
4508
|
-
const [
|
|
4509
|
-
|
|
4510
|
-
|
|
4998
|
+
const parseZoneId = (zoneId) => {
|
|
4999
|
+
const [nodeId, slotId] = zoneId.includes('|') ? zoneId.split('|') : [zoneId, undefined];
|
|
5000
|
+
return { nodeId, slotId };
|
|
5001
|
+
};
|
|
5002
|
+
|
|
5003
|
+
function useCanvasInteractions() {
|
|
5004
|
+
const tree = useTreeStore((state) => state.tree);
|
|
5005
|
+
const reorderChildren = useTreeStore((state) => state.reorderChildren);
|
|
5006
|
+
const reparentChild = useTreeStore((state) => state.reparentChild);
|
|
5007
|
+
const addChild = useTreeStore((state) => state.addChild);
|
|
5008
|
+
const onAddComponent = (droppedItem) => {
|
|
5009
|
+
const { destination, draggableId } = droppedItem;
|
|
5010
|
+
if (!destination) {
|
|
4511
5011
|
return;
|
|
4512
5012
|
}
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
5013
|
+
/**
|
|
5014
|
+
* We only have the draggableId as information about the new component being dropped.
|
|
5015
|
+
* So we need to split it to get the blockId and the isAssembly flag.
|
|
5016
|
+
*/
|
|
5017
|
+
const [blockId, isAssembly] = draggableId.split(':');
|
|
5018
|
+
const { nodeId: parentId, slotId } = parseZoneId(destination.droppableId);
|
|
5019
|
+
const droppingOnRoot = parentId === ROOT_ID;
|
|
5020
|
+
const isValidRootComponent = blockId === CONTENTFUL_COMPONENTS$1.container.id;
|
|
5021
|
+
let node = createTreeNode({ blockId, parentId, slotId });
|
|
5022
|
+
if (droppingOnRoot && !isValidRootComponent) {
|
|
5023
|
+
const wrappingContainer = createTreeNode({
|
|
5024
|
+
blockId: CONTENTFUL_COMPONENTS$1.container.id,
|
|
5025
|
+
parentId,
|
|
5026
|
+
});
|
|
5027
|
+
const childNode = createTreeNode({
|
|
5028
|
+
blockId,
|
|
5029
|
+
parentId: wrappingContainer.data.id,
|
|
5030
|
+
});
|
|
5031
|
+
node = wrappingContainer;
|
|
5032
|
+
node.children = [childNode];
|
|
4519
5033
|
}
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
5034
|
+
/**
|
|
5035
|
+
* isAssembly comes from a string ID so we need to check if it's 'true' or 'false'
|
|
5036
|
+
* in string format.
|
|
5037
|
+
*/
|
|
5038
|
+
if (isAssembly === 'false') {
|
|
5039
|
+
addChild(destination.index, parentId, node);
|
|
5040
|
+
}
|
|
5041
|
+
onDrop({
|
|
5042
|
+
data: tree,
|
|
5043
|
+
componentType: blockId,
|
|
5044
|
+
destinationIndex: destination.index,
|
|
5045
|
+
destinationZoneId: parentId,
|
|
5046
|
+
slotId,
|
|
5047
|
+
});
|
|
5048
|
+
};
|
|
5049
|
+
const onMoveComponent = (droppedItem) => {
|
|
5050
|
+
const { destination, source, draggableId } = droppedItem;
|
|
5051
|
+
if (!destination || !source) {
|
|
5052
|
+
return;
|
|
5053
|
+
}
|
|
5054
|
+
if (destination.droppableId === source.droppableId) {
|
|
5055
|
+
reorderChildren(destination.index, destination.droppableId, source.index);
|
|
5056
|
+
}
|
|
5057
|
+
if (destination.droppableId !== source.droppableId) {
|
|
5058
|
+
reparentChild(destination.index, destination.droppableId, source.index, source.droppableId);
|
|
5059
|
+
}
|
|
5060
|
+
onComponentMoved({
|
|
5061
|
+
nodeId: draggableId,
|
|
5062
|
+
destinationIndex: destination.index,
|
|
5063
|
+
destinationParentId: destination.droppableId,
|
|
5064
|
+
sourceIndex: source.index,
|
|
5065
|
+
sourceParentId: source.droppableId,
|
|
5066
|
+
});
|
|
5067
|
+
};
|
|
5068
|
+
return { onAddComponent, onMoveComponent };
|
|
5069
|
+
}
|
|
5070
|
+
|
|
5071
|
+
const TestDNDContainer = ({ onDragEnd, onBeforeDragStart, onDragStart, onDragUpdate, children, }) => {
|
|
5072
|
+
const handleDragStart = (event) => {
|
|
5073
|
+
const draggedItem = event.nativeEvent;
|
|
5074
|
+
const start = {
|
|
5075
|
+
mode: draggedItem.mode,
|
|
5076
|
+
draggableId: draggedItem.draggableId,
|
|
5077
|
+
type: draggedItem.type,
|
|
5078
|
+
source: draggedItem.source,
|
|
5079
|
+
};
|
|
5080
|
+
onBeforeDragStart(start);
|
|
5081
|
+
onDragStart(start, {});
|
|
5082
|
+
};
|
|
5083
|
+
const handleDrag = (event) => {
|
|
5084
|
+
const draggedItem = event.nativeEvent;
|
|
5085
|
+
const update = {
|
|
5086
|
+
mode: draggedItem.mode,
|
|
5087
|
+
draggableId: draggedItem.draggableId,
|
|
5088
|
+
type: draggedItem.type,
|
|
5089
|
+
source: draggedItem.source,
|
|
5090
|
+
destination: draggedItem.destination,
|
|
5091
|
+
combine: draggedItem.combine,
|
|
5092
|
+
};
|
|
5093
|
+
onDragUpdate(update, {});
|
|
5094
|
+
};
|
|
5095
|
+
const handleDragEnd = (event) => {
|
|
5096
|
+
const draggedItem = event.nativeEvent;
|
|
5097
|
+
const result = {
|
|
5098
|
+
mode: draggedItem.mode,
|
|
5099
|
+
draggableId: draggedItem.draggableId,
|
|
5100
|
+
type: draggedItem.type,
|
|
5101
|
+
source: draggedItem.source,
|
|
5102
|
+
destination: draggedItem.destination,
|
|
5103
|
+
combine: draggedItem.combine,
|
|
5104
|
+
reason: draggedItem.reason,
|
|
5105
|
+
};
|
|
5106
|
+
onDragEnd(result, {});
|
|
5107
|
+
};
|
|
5108
|
+
return (React.createElement("div", { "data-test-id": "dnd-context-substitute", onDragStart: handleDragStart, onDrag: handleDrag, onDragEnd: handleDragEnd }, children));
|
|
4528
5109
|
};
|
|
4529
|
-
|
|
5110
|
+
|
|
5111
|
+
const DNDProvider = ({ children }) => {
|
|
5112
|
+
const setSelectedNodeId = useEditorStore((state) => state.setSelectedNodeId);
|
|
5113
|
+
const draggedItem = useDraggedItemStore((state) => state.draggedItem);
|
|
5114
|
+
const setOnBeforeCaptureId = useDraggedItemStore((state) => state.setOnBeforeCaptureId);
|
|
5115
|
+
const setDraggingOnCanvas = useDraggedItemStore((state) => state.setDraggingOnCanvas);
|
|
5116
|
+
const updateItem = useDraggedItemStore((state) => state.updateItem);
|
|
5117
|
+
const { onAddComponent, onMoveComponent } = useCanvasInteractions();
|
|
5118
|
+
const selectedNodeId = useEditorStore((state) => state.selectedNodeId);
|
|
5119
|
+
const prevSelectedNodeId = useRef(null);
|
|
5120
|
+
const isTestRun = typeof window !== 'undefined' && Object.prototype.hasOwnProperty.call(window, 'Cypress');
|
|
5121
|
+
const beforeDragStart = ({ source }) => {
|
|
5122
|
+
prevSelectedNodeId.current = selectedNodeId;
|
|
5123
|
+
// Unselect the current node when dragging and remove the outline
|
|
5124
|
+
setSelectedNodeId('');
|
|
5125
|
+
// Set dragging state here to make sure that DnD capture phase has completed
|
|
5126
|
+
setDraggingOnCanvas(true);
|
|
5127
|
+
sendMessage(OUTGOING_EVENTS.ComponentSelected, {
|
|
5128
|
+
nodeId: '',
|
|
5129
|
+
});
|
|
5130
|
+
if (source.droppableId !== COMPONENT_LIST_ID) {
|
|
5131
|
+
sendMessage(OUTGOING_EVENTS.ComponentMoveStarted, undefined);
|
|
5132
|
+
}
|
|
5133
|
+
};
|
|
5134
|
+
const beforeCapture = ({ draggableId }) => {
|
|
5135
|
+
setOnBeforeCaptureId(draggableId);
|
|
5136
|
+
};
|
|
5137
|
+
const dragStart = (start) => {
|
|
5138
|
+
updateItem(start);
|
|
5139
|
+
};
|
|
5140
|
+
const dragUpdate = (update) => {
|
|
5141
|
+
updateItem(update);
|
|
5142
|
+
};
|
|
5143
|
+
const dragEnd = (dropResult) => {
|
|
5144
|
+
setDraggingOnCanvas(false);
|
|
5145
|
+
setOnBeforeCaptureId('');
|
|
5146
|
+
updateItem();
|
|
5147
|
+
SimulateDnD$1.reset();
|
|
5148
|
+
// If the component is being dropped onto itself, do nothing
|
|
5149
|
+
// This can happen from an apparent race condition where the hovering zone gets set
|
|
5150
|
+
// to the component after its dropped.
|
|
5151
|
+
if (dropResult.destination?.droppableId === dropResult.draggableId) {
|
|
5152
|
+
return;
|
|
5153
|
+
}
|
|
5154
|
+
if (!dropResult.destination) {
|
|
5155
|
+
if (!draggedItem?.destination) {
|
|
5156
|
+
// User cancel drag
|
|
5157
|
+
sendMessage(OUTGOING_EVENTS.ComponentDragCanceled, undefined);
|
|
5158
|
+
//select the previously selected node if drag was canceled
|
|
5159
|
+
if (prevSelectedNodeId.current) {
|
|
5160
|
+
setSelectedNodeId(prevSelectedNodeId.current);
|
|
5161
|
+
sendMessage(OUTGOING_EVENTS.ComponentSelected, {
|
|
5162
|
+
nodeId: prevSelectedNodeId.current,
|
|
5163
|
+
});
|
|
5164
|
+
prevSelectedNodeId.current = null;
|
|
5165
|
+
}
|
|
5166
|
+
return;
|
|
5167
|
+
}
|
|
5168
|
+
// Use the destination from the draggedItem (when clicking the canvas)
|
|
5169
|
+
dropResult.destination = draggedItem.destination;
|
|
5170
|
+
}
|
|
5171
|
+
// New component added to canvas
|
|
5172
|
+
if (dropResult.source.droppableId.startsWith('component-list')) {
|
|
5173
|
+
onAddComponent(dropResult);
|
|
5174
|
+
}
|
|
5175
|
+
else {
|
|
5176
|
+
onMoveComponent(dropResult);
|
|
5177
|
+
}
|
|
5178
|
+
// If a node was previously selected prior to dragging, re-select it
|
|
5179
|
+
setSelectedNodeId(dropResult.draggableId);
|
|
5180
|
+
sendMessage(OUTGOING_EVENTS.ComponentMoveEnded, undefined);
|
|
5181
|
+
sendMessage(OUTGOING_EVENTS.ComponentSelected, {
|
|
5182
|
+
nodeId: dropResult.draggableId,
|
|
5183
|
+
});
|
|
5184
|
+
};
|
|
5185
|
+
return (React.createElement(DragDropContext, { onBeforeCapture: beforeCapture, onDragUpdate: dragUpdate, onBeforeDragStart: beforeDragStart, onDragStart: dragStart, onDragEnd: dragEnd }, isTestRun ? (React.createElement(TestDNDContainer, { onDragEnd: dragEnd, onBeforeDragStart: beforeDragStart, onDragStart: dragStart, onDragUpdate: dragUpdate }, children)) : (children)));
|
|
5186
|
+
};
|
|
5187
|
+
|
|
5188
|
+
/**
|
|
5189
|
+
* This hook gets the element co-ordinates of a specified element in the DOM
|
|
5190
|
+
* and sends the DOM Rect to the client app
|
|
5191
|
+
*/
|
|
5192
|
+
const useSelectedInstanceCoordinates = ({ node }) => {
|
|
5193
|
+
const selectedNodeId = useEditorStore((state) => state.selectedNodeId);
|
|
5194
|
+
useEffect(() => {
|
|
5195
|
+
if (selectedNodeId !== node.data.id) {
|
|
5196
|
+
return;
|
|
5197
|
+
}
|
|
5198
|
+
// Allows the drop animation to finish before
|
|
5199
|
+
// calculating the components coordinates
|
|
5200
|
+
setTimeout(() => {
|
|
5201
|
+
sendSelectedComponentCoordinates(node.data.id);
|
|
5202
|
+
}, 10);
|
|
5203
|
+
}, [node, selectedNodeId]);
|
|
5204
|
+
const selectedElement = node.data.id
|
|
5205
|
+
? document.querySelector(`[data-cf-node-id="${selectedNodeId}"]`)
|
|
5206
|
+
: undefined;
|
|
5207
|
+
return selectedElement ? getElementCoordinates(selectedElement) : null;
|
|
5208
|
+
};
|
|
5209
|
+
|
|
5210
|
+
/**
|
|
5211
|
+
* @param styles: the list of styles to apply
|
|
5212
|
+
* @param nodeId: the id of node that these styles will be applied to
|
|
5213
|
+
* @returns className: the className that was used
|
|
5214
|
+
* Builds and adds a style tag in the document. Returns the className to be attached to the element.
|
|
5215
|
+
* In editor mode the nodeId is used as the identifier in order to avoid creating endless tags as the styles are tweeked
|
|
5216
|
+
*/
|
|
5217
|
+
const useEditorModeClassName = ({ styles, nodeId, }) => {
|
|
5218
|
+
const [className, setClassName] = useState('');
|
|
5219
|
+
useLayoutEffect(() => {
|
|
5220
|
+
if (Object.keys(styles).length === 0) {
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5223
|
+
const [newClassName, styleRules] = buildStyleTag({ styles, nodeId });
|
|
5224
|
+
addStylesTag(newClassName, styleRules);
|
|
5225
|
+
if (className !== newClassName) {
|
|
5226
|
+
setClassName(newClassName);
|
|
5227
|
+
// Clean up: remove outdated styles from DOM
|
|
5228
|
+
removeStylesTag(className);
|
|
5229
|
+
}
|
|
5230
|
+
}, [styles, nodeId, className]);
|
|
5231
|
+
return className;
|
|
5232
|
+
};
|
|
5233
|
+
const removeStylesTag = (className) => {
|
|
5234
|
+
const existingTag = document.querySelector(`[data-cf-styles="${className}"]`);
|
|
5235
|
+
if (existingTag) {
|
|
5236
|
+
document.head.removeChild(existingTag);
|
|
5237
|
+
}
|
|
5238
|
+
};
|
|
5239
|
+
const addStylesTag = (className, styleRules) => {
|
|
4530
5240
|
const existingTag = document.querySelector(`[data-cf-styles="${className}"]`);
|
|
4531
5241
|
if (existingTag) {
|
|
4532
5242
|
existingTag.innerHTML = styleRules;
|
|
@@ -4574,12 +5284,15 @@ const maybeMergePatternDefaultDesignValues = ({ variableName, variableMapping, n
|
|
|
4574
5284
|
return variableMapping.valuesByBreakpoint;
|
|
4575
5285
|
};
|
|
4576
5286
|
|
|
4577
|
-
const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesignValue, definition, options, }) => {
|
|
5287
|
+
const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesignValue, renderDropzone, definition, options, userIsDragging, requiresDragWrapper, }) => {
|
|
4578
5288
|
const unboundValues = useEditorStore((state) => state.unboundValues);
|
|
4579
5289
|
const hyperlinkPattern = useEditorStore((state) => state.hyperLinkPattern);
|
|
4580
5290
|
const locale = useEditorStore((state) => state.locale);
|
|
4581
5291
|
const dataSource = useEditorStore((state) => state.dataSource);
|
|
5292
|
+
const draggingId = useDraggedItemStore((state) => state.onBeforeCaptureId);
|
|
5293
|
+
const nodeRect = useDraggedItemStore((state) => state.domRect);
|
|
4582
5294
|
const findNodeById = useTreeStore((state) => state.findNodeById);
|
|
5295
|
+
const isEmptyZone = !node.children.length;
|
|
4583
5296
|
const props = useMemo(() => {
|
|
4584
5297
|
const propsBase = {
|
|
4585
5298
|
cfSsrClassName: node.data.props.cfSsrClassName
|
|
@@ -4675,9 +5388,18 @@ const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesig
|
|
|
4675
5388
|
return { ...acc };
|
|
4676
5389
|
}
|
|
4677
5390
|
}, {});
|
|
5391
|
+
const slotProps = {};
|
|
5392
|
+
if (definition.slots) {
|
|
5393
|
+
for (const slotId in definition.slots) {
|
|
5394
|
+
slotProps[slotId] = renderDropzone(node, {
|
|
5395
|
+
zoneId: [node.data.id, slotId].join('|'),
|
|
5396
|
+
});
|
|
5397
|
+
}
|
|
5398
|
+
}
|
|
4678
5399
|
return {
|
|
4679
5400
|
...propsBase,
|
|
4680
5401
|
...extractedProps,
|
|
5402
|
+
...slotProps,
|
|
4681
5403
|
};
|
|
4682
5404
|
}, [
|
|
4683
5405
|
hyperlinkPattern,
|
|
@@ -4689,224 +5411,1120 @@ const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesig
|
|
|
4689
5411
|
areEntitiesFetched,
|
|
4690
5412
|
unboundValues,
|
|
4691
5413
|
entityStore,
|
|
5414
|
+
renderDropzone,
|
|
4692
5415
|
findNodeById,
|
|
4693
5416
|
]);
|
|
4694
|
-
const cfStyles = useMemo(() =>
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
5417
|
+
const cfStyles = useMemo(() => ({
|
|
5418
|
+
...buildCfStyles(props),
|
|
5419
|
+
// This is not handled by buildCfStyles as it requires separate disjunct media queries in preview mode
|
|
5420
|
+
...transformVisibility(props.cfVisibility),
|
|
5421
|
+
}), [props]);
|
|
5422
|
+
const cfVisibility = props['cfVisibility'];
|
|
5423
|
+
const isAssemblyBlock = node.type === ASSEMBLY_BLOCK_NODE_TYPE;
|
|
5424
|
+
const isSingleColumn = node?.data.blockId === CONTENTFUL_COMPONENTS$1.columns.id;
|
|
5425
|
+
const isStructureComponent = isContentfulStructureComponent(node?.data.blockId);
|
|
5426
|
+
const isPatternNode = node.type === ASSEMBLY_NODE_TYPE;
|
|
5427
|
+
const { overrideStyles, wrapperStyles } = useMemo(() => {
|
|
5428
|
+
// Move size styles to the wrapping div and override the component styles
|
|
5429
|
+
const overrideStyles = {};
|
|
5430
|
+
const wrapperStyles = { width: options?.wrapContainerWidth };
|
|
5431
|
+
if (requiresDragWrapper) {
|
|
5432
|
+
// when element is marked by user as not-visible, on that element the node `display: none !important`
|
|
5433
|
+
// will be set and it will disappear. However, when such a node has a wrapper div, the wrapper
|
|
5434
|
+
// should not have any css properties (at least not ones which force size), as such div should
|
|
5435
|
+
// simply be a zero height wrapper around element with `display: none !important`.
|
|
5436
|
+
// Hence we guard all wrapperStyles with `cfVisibility` check.
|
|
5437
|
+
if (cfVisibility && cfStyles.width)
|
|
5438
|
+
wrapperStyles.width = cfStyles.width;
|
|
5439
|
+
if (cfVisibility && cfStyles.height)
|
|
5440
|
+
wrapperStyles.height = cfStyles.height;
|
|
5441
|
+
if (cfVisibility && cfStyles.maxWidth)
|
|
5442
|
+
wrapperStyles.maxWidth = cfStyles.maxWidth;
|
|
5443
|
+
if (cfVisibility && cfStyles.margin)
|
|
5444
|
+
wrapperStyles.margin = cfStyles.margin;
|
|
5445
|
+
}
|
|
5446
|
+
// Override component styles to fill the wrapper
|
|
5447
|
+
if (wrapperStyles.width)
|
|
5448
|
+
overrideStyles.width = '100%';
|
|
5449
|
+
if (wrapperStyles.height)
|
|
5450
|
+
overrideStyles.height = '100%';
|
|
5451
|
+
if (wrapperStyles.margin)
|
|
5452
|
+
overrideStyles.margin = '0';
|
|
5453
|
+
if (wrapperStyles.maxWidth)
|
|
5454
|
+
overrideStyles.maxWidth = 'none';
|
|
5455
|
+
// Prevent the dragging element from changing sizes when it has a percentage width or height
|
|
5456
|
+
if (draggingId === node.data.id && nodeRect) {
|
|
5457
|
+
if (requiresDragWrapper) {
|
|
5458
|
+
if (isPercentValue(cfStyles.width))
|
|
5459
|
+
wrapperStyles.maxWidth = nodeRect.width;
|
|
5460
|
+
if (isPercentValue(cfStyles.height))
|
|
5461
|
+
wrapperStyles.maxHeight = nodeRect.height;
|
|
5462
|
+
}
|
|
5463
|
+
else {
|
|
5464
|
+
if (isPercentValue(cfStyles.width))
|
|
5465
|
+
overrideStyles.maxWidth = nodeRect.width;
|
|
5466
|
+
if (isPercentValue(cfStyles.height))
|
|
5467
|
+
overrideStyles.maxHeight = nodeRect.height;
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
return { overrideStyles, wrapperStyles };
|
|
5471
|
+
}, [
|
|
5472
|
+
cfStyles,
|
|
5473
|
+
options?.wrapContainerWidth,
|
|
5474
|
+
requiresDragWrapper,
|
|
5475
|
+
node.data.id,
|
|
5476
|
+
draggingId,
|
|
5477
|
+
nodeRect,
|
|
5478
|
+
cfVisibility,
|
|
5479
|
+
]);
|
|
4705
5480
|
// Styles that will be applied to the component element
|
|
5481
|
+
// This has to be memoized to avoid recreating the styles in useEditorModeClassName on every render
|
|
4706
5482
|
const componentStyles = useMemo(() => ({
|
|
4707
5483
|
...cfStyles,
|
|
4708
|
-
...
|
|
4709
|
-
|
|
4710
|
-
|
|
5484
|
+
...overrideStyles,
|
|
5485
|
+
...(isEmptyZone &&
|
|
5486
|
+
isStructureWithRelativeHeight(node?.data.blockId, cfStyles.height) && {
|
|
5487
|
+
minHeight: EMPTY_CONTAINER_HEIGHT,
|
|
4711
5488
|
}),
|
|
4712
|
-
|
|
4713
|
-
|
|
5489
|
+
...(userIsDragging &&
|
|
5490
|
+
isStructureComponent &&
|
|
5491
|
+
!isSingleColumn &&
|
|
5492
|
+
!isAssemblyBlock && {
|
|
5493
|
+
padding: addExtraDropzonePadding(cfStyles.padding?.toString() || '0 0 0 0'),
|
|
5494
|
+
}),
|
|
5495
|
+
}), [
|
|
5496
|
+
cfStyles,
|
|
5497
|
+
isAssemblyBlock,
|
|
5498
|
+
isEmptyZone,
|
|
5499
|
+
isSingleColumn,
|
|
5500
|
+
isStructureComponent,
|
|
5501
|
+
node?.data.blockId,
|
|
5502
|
+
overrideStyles,
|
|
5503
|
+
userIsDragging,
|
|
5504
|
+
]);
|
|
5505
|
+
const componentClass = useEditorModeClassName({
|
|
4714
5506
|
styles: componentStyles,
|
|
4715
5507
|
nodeId: node.data.id,
|
|
4716
5508
|
});
|
|
4717
|
-
const
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
5509
|
+
const sharedProps = {
|
|
5510
|
+
'data-cf-node-id': node.data.id,
|
|
5511
|
+
'data-cf-node-block-id': node.data.blockId,
|
|
5512
|
+
'data-cf-node-block-type': node.type,
|
|
5513
|
+
className: props.cfSsrClassName ?? componentClass,
|
|
5514
|
+
...(definition?.children ? { children: renderDropzone(node) } : {}),
|
|
5515
|
+
};
|
|
5516
|
+
const customComponentProps = {
|
|
5517
|
+
...sharedProps,
|
|
5518
|
+
// Allows custom components to render differently in the editor. This needs to be activated
|
|
5519
|
+
// through options as the component has to be aware of this prop to not cause any React warnings.
|
|
5520
|
+
...(options?.enableCustomEditorView ? { isInExpEditorMode: true } : {}),
|
|
5521
|
+
...sanitizeNodeProps(props),
|
|
5522
|
+
};
|
|
5523
|
+
const structuralOrPatternComponentProps = {
|
|
5524
|
+
...sharedProps,
|
|
5525
|
+
editorMode: true,
|
|
5526
|
+
node,
|
|
5527
|
+
renderDropzone,
|
|
5528
|
+
};
|
|
5529
|
+
return {
|
|
5530
|
+
componentProps: isStructureComponent || isPatternNode
|
|
5531
|
+
? structuralOrPatternComponentProps
|
|
5532
|
+
: customComponentProps,
|
|
5533
|
+
componentStyles,
|
|
5534
|
+
wrapperStyles,
|
|
5535
|
+
};
|
|
5536
|
+
};
|
|
5537
|
+
const addExtraDropzonePadding = (padding) => padding
|
|
5538
|
+
.split(' ')
|
|
5539
|
+
.map((value) => parseFloat(value) === 0 ? `${DRAG_PADDING}px` : `calc(${value} + ${DRAG_PADDING}px)`)
|
|
5540
|
+
.join(' ');
|
|
5541
|
+
const isPercentValue = (value) => typeof value === 'string' && value.endsWith('%');
|
|
5542
|
+
|
|
5543
|
+
class ImportedComponentError extends Error {
|
|
5544
|
+
constructor(message) {
|
|
5545
|
+
super(message);
|
|
5546
|
+
this.name = 'ImportedComponentError';
|
|
5547
|
+
}
|
|
5548
|
+
}
|
|
5549
|
+
class ExperienceSDKError extends Error {
|
|
5550
|
+
constructor(message) {
|
|
5551
|
+
super(message);
|
|
5552
|
+
this.name = 'ExperienceSDKError';
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
class ImportedComponentErrorBoundary extends React.Component {
|
|
5556
|
+
componentDidCatch(error, _errorInfo) {
|
|
5557
|
+
if (error.name === 'ImportedComponentError' || error.name === 'ExperienceSDKError') {
|
|
5558
|
+
// This error was already handled by a nested error boundary and should be passed upwards
|
|
5559
|
+
// We have to do this as we wrap every component on every layer with this error boundary and
|
|
5560
|
+
// thus an error deep in the tree bubbles through many layers of error boundaries.
|
|
5561
|
+
throw error;
|
|
5562
|
+
}
|
|
5563
|
+
// Differentiate between custom and SDK-provided components for error tracking
|
|
5564
|
+
const ErrorClass = isContentfulComponent(this.props.componentId)
|
|
5565
|
+
? ExperienceSDKError
|
|
5566
|
+
: ImportedComponentError;
|
|
5567
|
+
const err = new ErrorClass(error.message);
|
|
5568
|
+
err.stack = error.stack;
|
|
5569
|
+
throw err;
|
|
5570
|
+
}
|
|
5571
|
+
render() {
|
|
5572
|
+
return this.props.children;
|
|
5573
|
+
}
|
|
5574
|
+
}
|
|
5575
|
+
|
|
5576
|
+
const MissingComponentPlaceholder = ({ blockId }) => {
|
|
5577
|
+
return (React.createElement("div", { style: {
|
|
5578
|
+
border: '1px solid red',
|
|
5579
|
+
width: '100%',
|
|
5580
|
+
height: '100%',
|
|
5581
|
+
} },
|
|
5582
|
+
"Missing component '",
|
|
5583
|
+
blockId,
|
|
5584
|
+
"'"));
|
|
5585
|
+
};
|
|
5586
|
+
|
|
5587
|
+
const CircularDependencyErrorPlaceholder = forwardRef(({ wrappingPatternIds, ...props }, ref) => {
|
|
5588
|
+
return (React.createElement("div", { ...props,
|
|
5589
|
+
// Pass through ref to avoid DND errors being logged
|
|
5590
|
+
ref: ref, "data-cf-node-error": "circular-pattern-dependency", style: {
|
|
5591
|
+
border: '1px solid red',
|
|
5592
|
+
background: 'rgba(255, 0, 0, 0.1)',
|
|
5593
|
+
padding: '1rem 1rem 0 1rem',
|
|
5594
|
+
width: '100%',
|
|
5595
|
+
height: '100%',
|
|
5596
|
+
} },
|
|
5597
|
+
"Circular usage of patterns detected:",
|
|
5598
|
+
React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
|
|
5599
|
+
const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
|
|
5600
|
+
const entry = inMemoryEntities.maybeResolveLink(entryLink);
|
|
5601
|
+
const entryTitle = entry?.fields?.title;
|
|
5602
|
+
const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
|
|
5603
|
+
return React.createElement("li", { key: patternId }, text);
|
|
5604
|
+
}))));
|
|
5605
|
+
});
|
|
5606
|
+
CircularDependencyErrorPlaceholder.displayName = 'CircularDependencyErrorPlaceholder';
|
|
5607
|
+
|
|
5608
|
+
const useComponent = ({ node, entityStore, areEntitiesFetched, resolveDesignValue, renderDropzone, userIsDragging, wrappingPatternIds, }) => {
|
|
5609
|
+
const tree = useTreeStore((state) => state.tree);
|
|
5610
|
+
const componentRegistration = useMemo(() => {
|
|
5611
|
+
let registration = componentRegistry.get(node.data.blockId);
|
|
5612
|
+
if (node.type === ASSEMBLY_NODE_TYPE && !registration) {
|
|
5613
|
+
registration = createAssemblyRegistration({
|
|
5614
|
+
definitionId: node.data.blockId,
|
|
5615
|
+
component: Assembly,
|
|
5616
|
+
});
|
|
5617
|
+
}
|
|
5618
|
+
if (!registration) {
|
|
5619
|
+
console.warn(`Component registration not found for component with id: "${node.data.blockId}". The registered component might have been removed from the code. To proceed, remove the component manually from the layers tab.`);
|
|
5620
|
+
return undefined;
|
|
5621
|
+
}
|
|
5622
|
+
return registration;
|
|
5623
|
+
}, [node]);
|
|
5624
|
+
const componentId = node.data.id;
|
|
5625
|
+
const isPatternNode = node.type === ASSEMBLY_NODE_TYPE;
|
|
5626
|
+
const isPatternComponent = node.type === ASSEMBLY_BLOCK_NODE_TYPE;
|
|
5627
|
+
const parentComponentNode = getItem({ id: node.parentId }, tree);
|
|
5628
|
+
const isNestedPattern = isPatternNode &&
|
|
5629
|
+
[ASSEMBLY_BLOCK_NODE_TYPE, ASSEMBLY_NODE_TYPE].includes(parentComponentNode?.type ?? '');
|
|
5630
|
+
const isStructureComponent = isContentfulStructureComponent(node.data.blockId);
|
|
5631
|
+
const requiresDragWrapper = !isPatternNode && !isStructureComponent && !componentRegistration?.options?.wrapComponent;
|
|
5632
|
+
const { componentProps, wrapperStyles } = useComponentProps({
|
|
5633
|
+
node,
|
|
5634
|
+
entityStore,
|
|
5635
|
+
areEntitiesFetched,
|
|
5636
|
+
resolveDesignValue,
|
|
5637
|
+
renderDropzone,
|
|
5638
|
+
definition: componentRegistration?.definition,
|
|
5639
|
+
options: componentRegistration?.options,
|
|
5640
|
+
userIsDragging,
|
|
5641
|
+
requiresDragWrapper,
|
|
5642
|
+
});
|
|
5643
|
+
const elementToRender = (props) => {
|
|
5644
|
+
const { dragProps = {} } = props || {};
|
|
5645
|
+
const { children, innerRef, Tag = 'div', ToolTipAndPlaceholder, style, ...rest } = dragProps;
|
|
5646
|
+
const { 'data-cf-node-block-id': dataCfNodeBlockId, 'data-cf-node-block-type': dataCfNodeBlockType, 'data-cf-node-id': dataCfNodeId, } = componentProps;
|
|
5647
|
+
const refCallback = (refNode) => {
|
|
5648
|
+
if (innerRef && refNode)
|
|
5649
|
+
innerRef(refNode);
|
|
4723
5650
|
};
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
if (isStructureComponent) {
|
|
4727
|
-
return {
|
|
4728
|
-
...sharedProps,
|
|
4729
|
-
editorMode: true,
|
|
4730
|
-
node,
|
|
4731
|
-
};
|
|
5651
|
+
if (!componentRegistration) {
|
|
5652
|
+
return React.createElement(MissingComponentPlaceholder, { blockId: node.data.blockId });
|
|
4732
5653
|
}
|
|
5654
|
+
if (node.data.blockId && wrappingPatternIds.has(node.data.blockId)) {
|
|
5655
|
+
return (React.createElement(CircularDependencyErrorPlaceholder, { ref: refCallback, "data-cf-node-id": dataCfNodeId, "data-cf-node-block-id": dataCfNodeBlockId, "data-cf-node-block-type": dataCfNodeBlockType, wrappingPatternIds: wrappingPatternIds }));
|
|
5656
|
+
}
|
|
5657
|
+
const element = React.createElement(ImportedComponentErrorBoundary, { componentId: node.data.blockId }, React.createElement(componentRegistration.component, {
|
|
5658
|
+
...componentProps,
|
|
5659
|
+
dragProps,
|
|
5660
|
+
}));
|
|
5661
|
+
if (!requiresDragWrapper) {
|
|
5662
|
+
return element;
|
|
5663
|
+
}
|
|
5664
|
+
return (React.createElement(Tag, { ...rest, style: { ...style, ...wrapperStyles }, ref: refCallback, "data-cf-node-id": dataCfNodeId, "data-cf-node-block-id": dataCfNodeBlockId, "data-cf-node-block-type": dataCfNodeBlockType },
|
|
5665
|
+
ToolTipAndPlaceholder,
|
|
5666
|
+
element));
|
|
5667
|
+
};
|
|
5668
|
+
return {
|
|
5669
|
+
node,
|
|
5670
|
+
parentComponentNode,
|
|
5671
|
+
isAssembly: isPatternNode,
|
|
5672
|
+
isPatternNode,
|
|
5673
|
+
isPatternComponent,
|
|
5674
|
+
isNestedPattern,
|
|
5675
|
+
componentId,
|
|
5676
|
+
elementToRender,
|
|
5677
|
+
definition: componentRegistration?.definition,
|
|
5678
|
+
};
|
|
5679
|
+
};
|
|
5680
|
+
|
|
5681
|
+
const calcOffsetLeft = (parentElement, placeholderWidth, nodeWidth) => {
|
|
5682
|
+
if (!parentElement) {
|
|
5683
|
+
return 0;
|
|
5684
|
+
}
|
|
5685
|
+
const alignItems = window.getComputedStyle(parentElement).alignItems;
|
|
5686
|
+
if (alignItems === 'center') {
|
|
5687
|
+
return -(placeholderWidth - nodeWidth) / 2;
|
|
5688
|
+
}
|
|
5689
|
+
if (alignItems === 'end') {
|
|
5690
|
+
return -placeholderWidth + nodeWidth + 2;
|
|
5691
|
+
}
|
|
5692
|
+
return 0;
|
|
5693
|
+
};
|
|
5694
|
+
const calcOffsetTop = (parentElement, placeholderHeight, nodeHeight) => {
|
|
5695
|
+
if (!parentElement) {
|
|
5696
|
+
return 0;
|
|
5697
|
+
}
|
|
5698
|
+
const alignItems = window.getComputedStyle(parentElement).alignItems;
|
|
5699
|
+
if (alignItems === 'center') {
|
|
5700
|
+
return -(placeholderHeight - nodeHeight) / 2;
|
|
5701
|
+
}
|
|
5702
|
+
if (alignItems === 'end') {
|
|
5703
|
+
return -placeholderHeight + nodeHeight + 2;
|
|
5704
|
+
}
|
|
5705
|
+
return 0;
|
|
5706
|
+
};
|
|
5707
|
+
const getPaddingOffset = (element) => {
|
|
5708
|
+
const paddingLeft = parseFloat(window.getComputedStyle(element).paddingLeft);
|
|
5709
|
+
const paddingRight = parseFloat(window.getComputedStyle(element).paddingRight);
|
|
5710
|
+
const paddingTop = parseFloat(window.getComputedStyle(element).paddingTop);
|
|
5711
|
+
const paddingBottom = parseFloat(window.getComputedStyle(element).paddingBottom);
|
|
5712
|
+
const horizontalOffset = paddingLeft + paddingRight;
|
|
5713
|
+
const verticalOffset = paddingTop + paddingBottom;
|
|
5714
|
+
return [horizontalOffset, verticalOffset];
|
|
5715
|
+
};
|
|
5716
|
+
/**
|
|
5717
|
+
* Calculate the size and position of the dropzone indicator
|
|
5718
|
+
* when dragging a new component onto the canvas
|
|
5719
|
+
*/
|
|
5720
|
+
const calcNewComponentStyles = (params) => {
|
|
5721
|
+
const { destinationIndex, elementIndex, dropzoneElementId, id, direction, totalIndexes } = params;
|
|
5722
|
+
const isEnd = destinationIndex === totalIndexes && elementIndex === totalIndexes - 1;
|
|
5723
|
+
const isHorizontal = direction === 'horizontal';
|
|
5724
|
+
const isRightAlign = isHorizontal && isEnd;
|
|
5725
|
+
const isBottomAlign = !isHorizontal && isEnd;
|
|
5726
|
+
const dropzone = document.querySelector(`[data-rfd-droppable-id="${dropzoneElementId}"]`);
|
|
5727
|
+
const element = document.querySelector(`[data-ctfl-draggable-id="${id}"]`);
|
|
5728
|
+
if (!dropzone || !element) {
|
|
5729
|
+
return emptyStyles;
|
|
5730
|
+
}
|
|
5731
|
+
const elementSizes = element.getBoundingClientRect();
|
|
5732
|
+
const dropzoneSizes = dropzone.getBoundingClientRect();
|
|
5733
|
+
const [horizontalPadding, verticalPadding] = getPaddingOffset(dropzone);
|
|
5734
|
+
const width = isHorizontal ? DRAGGABLE_WIDTH : dropzoneSizes.width - horizontalPadding;
|
|
5735
|
+
const height = isHorizontal ? dropzoneSizes.height - verticalPadding : DRAGGABLE_HEIGHT;
|
|
5736
|
+
const top = isHorizontal
|
|
5737
|
+
? calcOffsetTop(element.parentElement, height, elementSizes.height)
|
|
5738
|
+
: -height;
|
|
5739
|
+
const left = isHorizontal
|
|
5740
|
+
? -width
|
|
5741
|
+
: calcOffsetLeft(element.parentElement, width, elementSizes.width);
|
|
5742
|
+
return {
|
|
5743
|
+
width,
|
|
5744
|
+
height,
|
|
5745
|
+
top: !isBottomAlign ? top : 'unset',
|
|
5746
|
+
right: isRightAlign ? -width : 'unset',
|
|
5747
|
+
bottom: isBottomAlign ? -height : 'unset',
|
|
5748
|
+
left: !isRightAlign ? left : 'unset',
|
|
5749
|
+
};
|
|
5750
|
+
};
|
|
5751
|
+
/**
|
|
5752
|
+
* Calculate the size and position of the dropzone indicator
|
|
5753
|
+
* when moving an existing component on the canvas
|
|
5754
|
+
*/
|
|
5755
|
+
const calcMovementStyles = (params) => {
|
|
5756
|
+
const { destinationIndex, sourceIndex, destinationId, sourceId, elementIndex, dropzoneElementId, id, direction, totalIndexes, draggableId, } = params;
|
|
5757
|
+
const isEnd = destinationIndex === totalIndexes && elementIndex === totalIndexes - 1;
|
|
5758
|
+
const isHorizontal = direction === 'horizontal';
|
|
5759
|
+
const isSameZone = destinationId === sourceId;
|
|
5760
|
+
const isBelowSourceIndex = destinationIndex > sourceIndex;
|
|
5761
|
+
const isRightAlign = isHorizontal && (isEnd || (isSameZone && isBelowSourceIndex));
|
|
5762
|
+
const isBottomAlign = !isHorizontal && (isEnd || (isSameZone && isBelowSourceIndex));
|
|
5763
|
+
const dropzone = document.querySelector(`[data-rfd-droppable-id="${dropzoneElementId}"]`);
|
|
5764
|
+
const draggable = document.querySelector(`[data-rfd-draggable-id="${draggableId}"]`);
|
|
5765
|
+
const element = document.querySelector(`[data-ctfl-draggable-id="${id}"]`);
|
|
5766
|
+
if (!dropzone || !element || !draggable) {
|
|
5767
|
+
return emptyStyles;
|
|
5768
|
+
}
|
|
5769
|
+
const elementSizes = element.getBoundingClientRect();
|
|
5770
|
+
const dropzoneSizes = dropzone.getBoundingClientRect();
|
|
5771
|
+
const draggableSizes = draggable.getBoundingClientRect();
|
|
5772
|
+
const [horizontalPadding, verticalPadding] = getPaddingOffset(dropzone);
|
|
5773
|
+
const width = isHorizontal ? draggableSizes.width : dropzoneSizes.width - horizontalPadding;
|
|
5774
|
+
const height = isHorizontal ? dropzoneSizes.height - verticalPadding : draggableSizes.height;
|
|
5775
|
+
const top = isHorizontal
|
|
5776
|
+
? calcOffsetTop(element.parentElement, height, elementSizes.height)
|
|
5777
|
+
: -height;
|
|
5778
|
+
const left = isHorizontal
|
|
5779
|
+
? -width
|
|
5780
|
+
: calcOffsetLeft(element.parentElement, width, elementSizes.width);
|
|
5781
|
+
return {
|
|
5782
|
+
width,
|
|
5783
|
+
height,
|
|
5784
|
+
top: !isBottomAlign ? top : 'unset',
|
|
5785
|
+
right: isRightAlign ? -width : 'unset',
|
|
5786
|
+
bottom: isBottomAlign ? -height : 'unset',
|
|
5787
|
+
left: !isRightAlign ? left : 'unset',
|
|
5788
|
+
};
|
|
5789
|
+
};
|
|
5790
|
+
const emptyStyles = { width: 0, height: 0 };
|
|
5791
|
+
const calcPlaceholderStyles = (params) => {
|
|
5792
|
+
const { isDraggingOver, sourceId } = params;
|
|
5793
|
+
if (!isDraggingOver) {
|
|
5794
|
+
return emptyStyles;
|
|
5795
|
+
}
|
|
5796
|
+
if (sourceId === COMPONENT_LIST_ID) {
|
|
5797
|
+
return calcNewComponentStyles(params);
|
|
5798
|
+
}
|
|
5799
|
+
return calcMovementStyles(params);
|
|
5800
|
+
};
|
|
5801
|
+
const Placeholder = (props) => {
|
|
5802
|
+
const sourceIndex = useDraggedItemStore((state) => state.draggedItem?.source.index) ?? -1;
|
|
5803
|
+
const draggableId = useDraggedItemStore((state) => state.draggedItem?.draggableId) ?? '';
|
|
5804
|
+
const sourceId = useDraggedItemStore((state) => state.draggedItem?.source.droppableId) ?? '';
|
|
5805
|
+
const destinationIndex = useDraggedItemStore((state) => state.draggedItem?.destination?.index) ?? -1;
|
|
5806
|
+
const destinationId = useDraggedItemStore((state) => state.draggedItem?.destination?.droppableId) ?? '';
|
|
5807
|
+
const { elementIndex, totalIndexes, isDraggingOver } = props;
|
|
5808
|
+
const isActive = destinationIndex === elementIndex;
|
|
5809
|
+
const isEnd = destinationIndex === totalIndexes && elementIndex === totalIndexes - 1;
|
|
5810
|
+
const isVisible = isEnd || isActive;
|
|
5811
|
+
const isComponentList = destinationId === COMPONENT_LIST_ID;
|
|
5812
|
+
return (!isComponentList &&
|
|
5813
|
+
isDraggingOver &&
|
|
5814
|
+
isVisible && (React.createElement("div", { style: {
|
|
5815
|
+
...calcPlaceholderStyles({
|
|
5816
|
+
...props,
|
|
5817
|
+
sourceId,
|
|
5818
|
+
sourceIndex,
|
|
5819
|
+
destinationId,
|
|
5820
|
+
destinationIndex,
|
|
5821
|
+
draggableId,
|
|
5822
|
+
}),
|
|
5823
|
+
backgroundColor: 'rgba(var(--exp-builder-blue300-rgb), 0.5)',
|
|
5824
|
+
position: 'absolute',
|
|
5825
|
+
pointerEvents: 'none',
|
|
5826
|
+
} })));
|
|
5827
|
+
};
|
|
5828
|
+
|
|
5829
|
+
var css_248z$2 = ".styles-module_hitbox__i3wKV {\n position: fixed;\n pointer-events: all;\n}\n";
|
|
5830
|
+
var styles$2 = {"hitbox":"styles-module_hitbox__i3wKV"};
|
|
5831
|
+
styleInject(css_248z$2);
|
|
5832
|
+
|
|
5833
|
+
const useZoneStore = create()((set) => ({
|
|
5834
|
+
zones: {},
|
|
5835
|
+
hoveringZone: '',
|
|
5836
|
+
setHoveringZone(zoneId) {
|
|
5837
|
+
set({
|
|
5838
|
+
hoveringZone: zoneId,
|
|
5839
|
+
});
|
|
5840
|
+
},
|
|
5841
|
+
upsertZone(id, data) {
|
|
5842
|
+
set(produce((state) => {
|
|
5843
|
+
state.zones[id] = { ...(state.zones[id] || {}), ...data };
|
|
5844
|
+
}));
|
|
5845
|
+
},
|
|
5846
|
+
}));
|
|
5847
|
+
|
|
5848
|
+
const { WIDTH, HEIGHT, INITIAL_OFFSET, OFFSET_INCREMENT, MIN_HEIGHT, MIN_DEPTH_HEIGHT, DEEP_ZONE } = HITBOX;
|
|
5849
|
+
const calcOffsetDepth = (depth) => {
|
|
5850
|
+
return INITIAL_OFFSET - OFFSET_INCREMENT * depth;
|
|
5851
|
+
};
|
|
5852
|
+
const getHitboxStyles = ({ direction, zoneDepth, domRect, scrollY, offsetRect, }) => {
|
|
5853
|
+
if (!domRect) {
|
|
4733
5854
|
return {
|
|
4734
|
-
|
|
4735
|
-
// Allows custom components to render differently in the editor. This needs to be activated
|
|
4736
|
-
// through options as the component has to be aware of this prop to not cause any React warnings.
|
|
4737
|
-
...(options?.enableCustomEditorView ? { isInExpEditorMode: true } : {}),
|
|
4738
|
-
...sanitizeNodeProps(props),
|
|
5855
|
+
display: 'none',
|
|
4739
5856
|
};
|
|
4740
|
-
}
|
|
4741
|
-
|
|
5857
|
+
}
|
|
5858
|
+
const { width, height, top, left, bottom, right } = domRect;
|
|
5859
|
+
const { height: offsetHeight, width: offsetWidth } = offsetRect || { height: 0, width: 0 };
|
|
5860
|
+
const MAX_SELF_HEIGHT = DRAGGABLE_HEIGHT * 2;
|
|
5861
|
+
const isDeepZone = zoneDepth > DEEP_ZONE;
|
|
5862
|
+
const isAboveMaxHeight = height > MAX_SELF_HEIGHT;
|
|
5863
|
+
switch (direction) {
|
|
5864
|
+
case HitboxDirection.TOP:
|
|
5865
|
+
return {
|
|
5866
|
+
width,
|
|
5867
|
+
height: HEIGHT,
|
|
5868
|
+
top: top + offsetHeight - calcOffsetDepth(zoneDepth) - scrollY,
|
|
5869
|
+
left,
|
|
5870
|
+
zIndex: 100 + zoneDepth,
|
|
5871
|
+
};
|
|
5872
|
+
case HitboxDirection.BOTTOM:
|
|
5873
|
+
return {
|
|
5874
|
+
width,
|
|
5875
|
+
height: HEIGHT,
|
|
5876
|
+
top: bottom + offsetHeight + calcOffsetDepth(zoneDepth) - scrollY,
|
|
5877
|
+
left,
|
|
5878
|
+
zIndex: 100 + zoneDepth,
|
|
5879
|
+
};
|
|
5880
|
+
case HitboxDirection.LEFT:
|
|
5881
|
+
return {
|
|
5882
|
+
width: WIDTH,
|
|
5883
|
+
height: height - HEIGHT,
|
|
5884
|
+
left: left + offsetWidth - calcOffsetDepth(zoneDepth) - WIDTH / 2,
|
|
5885
|
+
top: top + HEIGHT / 2 - scrollY,
|
|
5886
|
+
zIndex: 100 + zoneDepth,
|
|
5887
|
+
};
|
|
5888
|
+
case HitboxDirection.RIGHT:
|
|
5889
|
+
return {
|
|
5890
|
+
width: WIDTH,
|
|
5891
|
+
height: height - HEIGHT,
|
|
5892
|
+
left: right + offsetWidth + calcOffsetDepth(zoneDepth) - WIDTH / 2,
|
|
5893
|
+
top: top + HEIGHT / 2 - scrollY,
|
|
5894
|
+
zIndex: 100 + zoneDepth,
|
|
5895
|
+
};
|
|
5896
|
+
case HitboxDirection.SELF_VERTICAL: {
|
|
5897
|
+
if (isAboveMaxHeight && !isDeepZone) {
|
|
5898
|
+
return { display: 'none' };
|
|
5899
|
+
}
|
|
5900
|
+
const selfHeight = isDeepZone ? MIN_DEPTH_HEIGHT : MIN_HEIGHT;
|
|
5901
|
+
return {
|
|
5902
|
+
width,
|
|
5903
|
+
height: selfHeight,
|
|
5904
|
+
left,
|
|
5905
|
+
top: top + height / 2 - selfHeight / 2 - scrollY,
|
|
5906
|
+
zIndex: 1000 + zoneDepth,
|
|
5907
|
+
};
|
|
5908
|
+
}
|
|
5909
|
+
case HitboxDirection.SELF_HORIZONTAL: {
|
|
5910
|
+
if (width > DRAGGABLE_WIDTH) {
|
|
5911
|
+
return { display: 'none' };
|
|
5912
|
+
}
|
|
5913
|
+
return {
|
|
5914
|
+
width: width - DRAGGABLE_WIDTH * 2,
|
|
5915
|
+
height,
|
|
5916
|
+
left: left + DRAGGABLE_WIDTH,
|
|
5917
|
+
top: top - scrollY,
|
|
5918
|
+
zIndex: 1000 + zoneDepth,
|
|
5919
|
+
};
|
|
5920
|
+
}
|
|
5921
|
+
default:
|
|
5922
|
+
return {};
|
|
5923
|
+
}
|
|
4742
5924
|
};
|
|
4743
5925
|
|
|
4744
|
-
|
|
4745
|
-
const
|
|
4746
|
-
const
|
|
4747
|
-
|
|
4748
|
-
|
|
5926
|
+
const Hitboxes = ({ zoneId, parentZoneId, isEmptyZone }) => {
|
|
5927
|
+
const tree = useTreeStore((state) => state.tree);
|
|
5928
|
+
const isDraggingOnCanvas = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
5929
|
+
const scrollY = useDraggedItemStore((state) => state.scrollY);
|
|
5930
|
+
const zoneDepth = useMemo(() => getItemDepthFromNode({ id: parentZoneId }, tree.root), [tree, parentZoneId]);
|
|
5931
|
+
const zones = useZoneStore((state) => state.zones);
|
|
5932
|
+
const hoveringZone = useZoneStore((state) => state.hoveringZone);
|
|
5933
|
+
const isHoveringZone = hoveringZone === zoneId;
|
|
5934
|
+
const hitboxContainer = useMemo(() => {
|
|
5935
|
+
return document.querySelector('[data-ctfl-hitboxes]');
|
|
5936
|
+
}, []);
|
|
5937
|
+
const domRect = useMemo(() => {
|
|
5938
|
+
if (!isDraggingOnCanvas)
|
|
5939
|
+
return;
|
|
5940
|
+
return document.querySelector(`[${CTFL_ZONE_ID}="${zoneId}"]`)?.getBoundingClientRect();
|
|
5941
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5942
|
+
}, [zoneId, isDraggingOnCanvas]);
|
|
5943
|
+
// Use the size of the cloned dragging element to offset the position of the hitboxes
|
|
5944
|
+
// So that when dragging causes a dropzone to expand, the hitboxes will be in the correct position
|
|
5945
|
+
const offsetRect = useMemo(() => {
|
|
5946
|
+
if (!isDraggingOnCanvas || isEmptyZone || !isHoveringZone)
|
|
5947
|
+
return;
|
|
5948
|
+
return document.querySelector(`[${CTFL_DRAGGING_ELEMENT}]`)?.getBoundingClientRect();
|
|
5949
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5950
|
+
}, [isEmptyZone, isHoveringZone, isDraggingOnCanvas]);
|
|
5951
|
+
const zoneDirection = zones[parentZoneId]?.direction || 'vertical';
|
|
5952
|
+
const isVertical = zoneDirection === 'vertical';
|
|
5953
|
+
const isRoot = parentZoneId === ROOT_ID;
|
|
5954
|
+
const { slotId: parentSlotId } = parseZoneId(parentZoneId);
|
|
5955
|
+
const getStyles = useCallback((direction) => getHitboxStyles({
|
|
5956
|
+
direction,
|
|
5957
|
+
zoneDepth,
|
|
5958
|
+
domRect,
|
|
5959
|
+
scrollY,
|
|
5960
|
+
offsetRect,
|
|
5961
|
+
}), [zoneDepth, domRect, scrollY, offsetRect]);
|
|
5962
|
+
const renderFinalRootHitbox = () => {
|
|
5963
|
+
if (!isRoot)
|
|
5964
|
+
return null;
|
|
5965
|
+
return (React.createElement("div", { "data-ctfl-zone-id": parentZoneId, className: styles$2.hitbox, style: getStyles(HitboxDirection.BOTTOM) }));
|
|
5966
|
+
};
|
|
5967
|
+
const renderSurroundingHitboxes = () => {
|
|
5968
|
+
if (isRoot || parentSlotId)
|
|
5969
|
+
return null;
|
|
5970
|
+
return (React.createElement(React.Fragment, null,
|
|
5971
|
+
React.createElement("div", { "data-ctfl-zone-id": parentZoneId, className: styles$2.hitbox, style: getStyles(isVertical ? HitboxDirection.TOP : HitboxDirection.LEFT) }),
|
|
5972
|
+
React.createElement("div", { "data-ctfl-zone-id": parentZoneId, className: styles$2.hitbox, style: getStyles(isVertical ? HitboxDirection.BOTTOM : HitboxDirection.RIGHT) })));
|
|
5973
|
+
};
|
|
5974
|
+
const ActiveHitboxes = (React.createElement(React.Fragment, null,
|
|
5975
|
+
React.createElement("div", { "data-ctfl-zone-id": zoneId, className: styles$2.hitbox, style: getStyles(isVertical ? HitboxDirection.SELF_VERTICAL : HitboxDirection.SELF_HORIZONTAL) }),
|
|
5976
|
+
renderSurroundingHitboxes(),
|
|
5977
|
+
renderFinalRootHitbox()));
|
|
5978
|
+
if (!hitboxContainer) {
|
|
5979
|
+
return null;
|
|
5980
|
+
}
|
|
5981
|
+
return createPortal(ActiveHitboxes, hitboxContainer);
|
|
5982
|
+
};
|
|
5983
|
+
|
|
5984
|
+
const isRelativePreviewSize = (width) => {
|
|
5985
|
+
// For now, we solely allow 100% as relative value
|
|
5986
|
+
return width === '100%';
|
|
5987
|
+
};
|
|
5988
|
+
const getTooltipPositions = ({ previewSize, tooltipRect, coordinates, }) => {
|
|
5989
|
+
if (!coordinates || !tooltipRect) {
|
|
5990
|
+
return { display: 'none' };
|
|
5991
|
+
}
|
|
5992
|
+
/**
|
|
5993
|
+
* By default, the tooltip floats to the left of the element
|
|
5994
|
+
*/
|
|
5995
|
+
const newTooltipStyles = { display: 'flex' };
|
|
5996
|
+
// If the preview size is relative, we don't change the floating direction
|
|
5997
|
+
if (!isRelativePreviewSize(previewSize)) {
|
|
5998
|
+
const previewSizeMatch = previewSize.match(/(\d{1,})px/);
|
|
5999
|
+
if (!previewSizeMatch) {
|
|
6000
|
+
return { display: 'none' };
|
|
6001
|
+
}
|
|
6002
|
+
const previewSizePx = parseInt(previewSizeMatch[1]);
|
|
6003
|
+
/**
|
|
6004
|
+
* If the element is at the right edge of the canvas, and the element isn't wide enough to fit the tooltip width,
|
|
6005
|
+
* we float the tooltip to the right of the element.
|
|
6006
|
+
*/
|
|
6007
|
+
if (tooltipRect.width > previewSizePx - coordinates.right &&
|
|
6008
|
+
tooltipRect.width > coordinates.width) {
|
|
6009
|
+
newTooltipStyles['float'] = 'right';
|
|
4749
6010
|
}
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
if (
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
6011
|
+
}
|
|
6012
|
+
const tooltipHeight = tooltipRect.height === 0 ? 32 : tooltipRect.height;
|
|
6013
|
+
/**
|
|
6014
|
+
* For elements with small heights, we don't want the tooltip covering the content in the element,
|
|
6015
|
+
* so we show the tooltip at the top or bottom.
|
|
6016
|
+
*/
|
|
6017
|
+
if (tooltipHeight * 2 > coordinates.height) {
|
|
6018
|
+
/**
|
|
6019
|
+
* If there's enough space for the tooltip at the top of the element, we show the tooltip at the top of the element,
|
|
6020
|
+
* else we show the tooltip at the bottom.
|
|
6021
|
+
*/
|
|
6022
|
+
if (tooltipHeight < coordinates.top) {
|
|
6023
|
+
newTooltipStyles['bottom'] = coordinates.height;
|
|
6024
|
+
}
|
|
6025
|
+
else {
|
|
6026
|
+
newTooltipStyles['top'] = coordinates.height;
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
/**
|
|
6030
|
+
* If the component draws outside of the borders of the canvas to the left we move the tooltip to the right
|
|
6031
|
+
* so that it is fully visible.
|
|
6032
|
+
*/
|
|
6033
|
+
if (coordinates.left < 0) {
|
|
6034
|
+
newTooltipStyles['left'] = -coordinates.left;
|
|
6035
|
+
}
|
|
6036
|
+
/**
|
|
6037
|
+
* If for any reason, the element's top is negative, we show the tooltip at the bottom
|
|
6038
|
+
*/
|
|
6039
|
+
if (coordinates.top < 0) {
|
|
6040
|
+
newTooltipStyles['top'] = coordinates.height;
|
|
6041
|
+
}
|
|
6042
|
+
return newTooltipStyles;
|
|
6043
|
+
};
|
|
6044
|
+
|
|
6045
|
+
var css_248z$1 = ".styles-module_DraggableComponent__oyE7Q,\n.styles-module_Dropzone__3R-sm:not(.styles-module_isSlot__HI9yO) {\n position: relative;\n transition: background-color 0.2s;\n pointer-events: all;\n box-sizing: border-box;\n cursor: grab;\n}\n\n.styles-module_DraggableComponent__oyE7Q:before,\n.styles-module_Dropzone__3R-sm:not(.styles-module_isSlot__HI9yO):before {\n content: '';\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n outline-offset: -2px;\n outline: 2px solid transparent;\n z-index: 1;\n transition: outline 0.2s;\n pointer-events: none;\n}\n\n.styles-module_DraggableComponent__oyE7Q.styles-module_isDragging__hldL4.styles-module_Dropzone__3R-sm:before {\n outline-offset: -1px;\n}\n\n.styles-module_DraggableComponent__oyE7Q.styles-module_isDragging__hldL4.styles-module_Dropzone__3R-sm {\n pointer-events: all;\n}\n\n.styles-module_Dropzone__3R-sm.styles-module_fullHeight__afMfT {\n height: 100%;\n}\n\n.styles-module_Dropzone__3R-sm.styles-module_fullWidth__Od117 {\n width: 100%;\n}\n\n.styles-module_isRoot__c-c-x,\n.styles-module_isEmptyCanvas__Mm6Al {\n flex: 1;\n}\n\n.styles-module_isEmptyZone__XZ1Ej {\n min-height: 80px;\n min-width: 80px;\n}\n\n.styles-module_isDragging__hldL4:not(.styles-module_isRoot__c-c-x):not(.styles-module_DraggableClone__CdKIH):before {\n outline: 2px dashed var(--exp-builder-gray300);\n}\n\n.styles-module_Dropzone__3R-sm.styles-module_isDestination__sE70P:not(.styles-module_isRoot__c-c-x):before {\n transition:\n outline 0.2s,\n background-color 0.2s;\n outline: 2px dashed var(--exp-builder-blue400);\n background-color: rgba(var(--exp-builder-blue100-rgb), 0.5);\n z-index: 2;\n}\n\n.styles-module_DraggableClone__CdKIH:before {\n outline: 2px solid var(--exp-builder-blue500);\n}\n\n.styles-module_DropzoneClone__xiT8j,\n.styles-module_DraggableClone__CdKIH,\n.styles-module_DropzoneClone__xiT8j *,\n.styles-module_DraggableClone__CdKIH * {\n pointer-events: none !important;\n}\n\n.styles-module_DraggableComponent__oyE7Q:not(.styles-module_isDragging__hldL4) :not(.styles-module_DraggableComponent__oyE7Q) {\n pointer-events: none;\n}\n\n.styles-module_isDraggingThisComponent__yCZTp {\n overflow: hidden;\n}\n\n.styles-module_isSelected__c2QEJ:before {\n outline: 2px solid transparent !important;\n}\n\n.styles-module_tooltipWrapper__kqvmR {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n z-index: 10;\n pointer-events: none;\n}\n\n.styles-module_DraggableComponent__oyE7Q.styles-module_isDragging__hldL4 .styles-module_tooltipWrapper__kqvmR {\n display: none;\n}\n\n.styles-module_overlay__knwhE {\n position: absolute;\n display: flex;\n align-items: center;\n min-width: max-content;\n height: 24px;\n z-index: 2;\n font-family: var(--exp-builder-font-stack-primary);\n font-size: 14px;\n font-weight: 500;\n background-color: var(--exp-builder-gray500);\n color: var(--exp-builder-color-white);\n padding: 4px 12px 4px 12px;\n transition: opacity 0.1s;\n opacity: 0;\n text-wrap: nowrap;\n}\n\n.styles-module_overlayContainer__lUsiC {\n opacity: 0;\n}\n\n.styles-module_overlayAssembly__3BKl4 {\n background-color: var(--exp-builder-purple600);\n}\n\n.styles-module_isDragging__hldL4 > .styles-module_overlay__knwhE,\n.styles-module_isDragging__hldL4 > .styles-module_overlayContainer__lUsiC {\n opacity: 0 !important;\n}\n\n.styles-module_isDragging__hldL4:not(.styles-module_Dropzone__3R-sm):before {\n outline: 2px solid transparent !important;\n}\n\n.styles-module_isHoveringComponent__f7G5m > div > .styles-module_overlay__knwhE,\n.styles-module_DraggableComponent__oyE7Q:hover:not(:has(.styles-module_DraggableComponent__oyE7Q:hover)) > div > .styles-module_overlay__knwhE {\n opacity: 1;\n}\n\n/* hovering related component in layers tab */\n\n.styles-module_DraggableComponent__oyE7Q:has(.styles-module_isHoveringComponent__f7G5m):not(.styles-module_isAssemblyBlock__goT9z):before,\n.styles-module_DraggableComponent__oyE7Q:has(.styles-module_isHoveringComponent__f7G5m):not(.styles-module_isAssemblyBlock__goT9z) .styles-module_DraggableComponent__oyE7Q:not(.styles-module_isHoveringComponent__f7G5m):not(.styles-module_isAssemblyBlock__goT9z):before,\n.styles-module_isHoveringComponent__f7G5m:not(.styles-module_isAssemblyBlock__goT9z) .styles-module_DraggableComponent__oyE7Q:not(.styles-module_isAssemblyBlock__goT9z):before,\n\n.styles-module_DraggableComponent__oyE7Q:not(.styles-module_isAssemblyBlock__goT9z):not(.styles-module_isDragging__hldL4):hover:before,\n.styles-module_DraggableComponent__oyE7Q:not(.styles-module_isDragging__hldL4):hover .styles-module_DraggableComponent__oyE7Q:before {\n outline: 2px dashed var(--exp-builder-gray500);\n}\n\n/* hovering component in layers tab */\n\n.styles-module_isHoveringComponent__f7G5m:not(.styles-module_isAssemblyBlock__goT9z):before,\n\n.styles-module_DraggableComponent__oyE7Q:not(.styles-module_isAssemblyBlock__goT9z):not(.styles-module_isDragging__hldL4):hover:not(:has(.styles-module_DraggableComponent__oyE7Q:hover)):before {\n outline: 2px solid var(--exp-builder-gray500);\n}\n\n/* hovering related pattern in layers tab */\n\n.styles-module_isAssemblyBlock__goT9z:has(.styles-module_isHoveringComponent__f7G5m):before,\n.styles-module_isAssemblyBlock__goT9z:has(.styles-module_isHoveringComponent__f7G5m) .styles-module_isAssemblyBlock__goT9z:not(.styles-module_isHoveringComponent__f7G5m):before,\n.styles-module_isHoveringComponent__f7G5m .styles-module_isAssemblyBlock__goT9z:before,\n\n.styles-module_isAssemblyBlock__goT9z:hover:before,\n.styles-module_isAssemblyBlock__goT9z:hover .styles-module_DraggableComponent__oyE7Q:before,\n.styles-module_DraggableComponent__oyE7Q:not(.styles-module_isDragging__hldL4):hover .styles-module_isAssemblyBlock__goT9z:before {\n outline: 2px dashed var(--exp-builder-purple600);\n}\n\n/* hovering pattern in layers tab */\n\n.styles-module_isAssemblyBlock__goT9z.styles-module_isHoveringComponent__f7G5m:before,\n\n.styles-module_isAssemblyBlock__goT9z:hover:not(:has(.styles-module_DraggableComponent__oyE7Q:hover)):before {\n outline: 2px solid var(--exp-builder-purple600);\n}\n";
|
|
6046
|
+
var styles$1 = {"DraggableComponent":"styles-module_DraggableComponent__oyE7Q","Dropzone":"styles-module_Dropzone__3R-sm","isSlot":"styles-module_isSlot__HI9yO","isDragging":"styles-module_isDragging__hldL4","fullHeight":"styles-module_fullHeight__afMfT","fullWidth":"styles-module_fullWidth__Od117","isRoot":"styles-module_isRoot__c-c-x","isEmptyCanvas":"styles-module_isEmptyCanvas__Mm6Al","isEmptyZone":"styles-module_isEmptyZone__XZ1Ej","DraggableClone":"styles-module_DraggableClone__CdKIH","isDestination":"styles-module_isDestination__sE70P","DropzoneClone":"styles-module_DropzoneClone__xiT8j","isDraggingThisComponent":"styles-module_isDraggingThisComponent__yCZTp","isSelected":"styles-module_isSelected__c2QEJ","tooltipWrapper":"styles-module_tooltipWrapper__kqvmR","overlay":"styles-module_overlay__knwhE","overlayContainer":"styles-module_overlayContainer__lUsiC","overlayAssembly":"styles-module_overlayAssembly__3BKl4","isHoveringComponent":"styles-module_isHoveringComponent__f7G5m","isAssemblyBlock":"styles-module_isAssemblyBlock__goT9z"};
|
|
6047
|
+
styleInject(css_248z$1);
|
|
6048
|
+
|
|
6049
|
+
const Tooltip = ({ coordinates, id, label, isAssemblyBlock, isContainer, isSelected, }) => {
|
|
6050
|
+
const tooltipRef = useRef(null);
|
|
6051
|
+
const previewSize = '100%'; // This should be based on breakpoints and added to usememo dependency array
|
|
6052
|
+
const tooltipStyles = useMemo(() => {
|
|
6053
|
+
const tooltipRect = tooltipRef.current?.getBoundingClientRect();
|
|
6054
|
+
const draggableRect = document
|
|
6055
|
+
.querySelector(`[data-ctfl-draggable-id="${id}"]`)
|
|
6056
|
+
?.getBoundingClientRect();
|
|
6057
|
+
const newTooltipStyles = getTooltipPositions({
|
|
6058
|
+
previewSize,
|
|
6059
|
+
tooltipRect,
|
|
6060
|
+
coordinates: draggableRect,
|
|
6061
|
+
});
|
|
6062
|
+
return newTooltipStyles;
|
|
6063
|
+
// Ignore eslint because we intentionally want to trigger this whenever a user clicks on a container/component which is tracked by these coordinates of the component being clicked being changed
|
|
6064
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6065
|
+
}, [coordinates, id, tooltipRef.current]);
|
|
6066
|
+
if (isSelected) {
|
|
6067
|
+
return null;
|
|
6068
|
+
}
|
|
6069
|
+
return (React.createElement("div", { "data-tooltip": true, className: styles$1.tooltipWrapper },
|
|
6070
|
+
React.createElement("div", { "data-tooltip": true, ref: tooltipRef, style: tooltipStyles, className: classNames(styles$1.overlay, {
|
|
6071
|
+
[styles$1.overlayContainer]: isContainer,
|
|
6072
|
+
[styles$1.overlayAssembly]: isAssemblyBlock,
|
|
6073
|
+
}) }, label)));
|
|
6074
|
+
};
|
|
6075
|
+
|
|
6076
|
+
function useSingleColumn(node, resolveDesignValue) {
|
|
6077
|
+
const tree = useTreeStore((store) => store.tree);
|
|
6078
|
+
const isSingleColumn = node.data.blockId === CONTENTFUL_COMPONENTS$1.singleColumn.id;
|
|
6079
|
+
const isWrapped = useMemo(() => {
|
|
6080
|
+
if (!node.parentId || !isSingleColumn) {
|
|
6081
|
+
return false;
|
|
6082
|
+
}
|
|
6083
|
+
const parentNode = getItem({ id: node.parentId }, tree);
|
|
6084
|
+
if (!parentNode || parentNode.data.blockId !== CONTENTFUL_COMPONENTS$1.columns.id) {
|
|
6085
|
+
return false;
|
|
6086
|
+
}
|
|
6087
|
+
const { cfWrapColumns } = parentNode.data.props;
|
|
6088
|
+
if (cfWrapColumns?.type !== 'DesignValue') {
|
|
6089
|
+
return false;
|
|
6090
|
+
}
|
|
6091
|
+
return resolveDesignValue(cfWrapColumns.valuesByBreakpoint);
|
|
6092
|
+
}, [tree, node, isSingleColumn, resolveDesignValue]);
|
|
6093
|
+
return {
|
|
6094
|
+
isSingleColumn,
|
|
6095
|
+
isWrapped,
|
|
6096
|
+
};
|
|
4771
6097
|
}
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
6098
|
+
|
|
6099
|
+
function getStyle$1(style, snapshot) {
|
|
6100
|
+
if (!snapshot.isDropAnimating) {
|
|
6101
|
+
return style;
|
|
6102
|
+
}
|
|
6103
|
+
return {
|
|
6104
|
+
...style,
|
|
6105
|
+
// cannot be 0, but make it super tiny
|
|
6106
|
+
transitionDuration: `0.001s`,
|
|
6107
|
+
};
|
|
6108
|
+
}
|
|
6109
|
+
const EditorBlock = ({ node: rawNode, entityStore, areEntitiesFetched, resolveDesignValue, renderDropzone, index, zoneId, userIsDragging, placeholder, wrappingPatternIds, }) => {
|
|
6110
|
+
const { slotId } = parseZoneId(zoneId);
|
|
6111
|
+
const ref = useRef(null);
|
|
6112
|
+
const setSelectedNodeId = useEditorStore((state) => state.setSelectedNodeId);
|
|
6113
|
+
const selectedNodeId = useEditorStore((state) => state.selectedNodeId);
|
|
6114
|
+
const { node, componentId, elementToRender, definition, isPatternNode, isPatternComponent, isNestedPattern, } = useComponent({
|
|
6115
|
+
node: rawNode,
|
|
4778
6116
|
entityStore,
|
|
4779
6117
|
areEntitiesFetched,
|
|
6118
|
+
resolveDesignValue,
|
|
6119
|
+
renderDropzone,
|
|
6120
|
+
userIsDragging,
|
|
6121
|
+
wrappingPatternIds,
|
|
6122
|
+
});
|
|
6123
|
+
const { isSingleColumn, isWrapped } = useSingleColumn(node, resolveDesignValue);
|
|
6124
|
+
const setDomRect = useDraggedItemStore((state) => state.setDomRect);
|
|
6125
|
+
const isHoveredComponent = useDraggedItemStore((state) => state.hoveredComponentId === componentId);
|
|
6126
|
+
const coordinates = useSelectedInstanceCoordinates({ node });
|
|
6127
|
+
const displayName = node.data.displayName || rawNode.data.displayName || definition?.name;
|
|
6128
|
+
const testId = `draggable-${node.data.blockId ?? 'node'}`;
|
|
6129
|
+
const isSelected = node.data.id === selectedNodeId;
|
|
6130
|
+
const isContainer = node.data.blockId === CONTENTFUL_COMPONENTS$1.container.id;
|
|
6131
|
+
const isSlotComponent = Boolean(node.data.slotId);
|
|
6132
|
+
const isDragDisabled = isNestedPattern || isPatternComponent || (isSingleColumn && isWrapped) || isSlotComponent;
|
|
6133
|
+
const isEmptyZone = useMemo(() => {
|
|
6134
|
+
return !node.children.filter((node) => node.data.slotId === slotId).length;
|
|
6135
|
+
}, [node.children, slotId]);
|
|
6136
|
+
useDraggablePosition({
|
|
6137
|
+
draggableId: componentId,
|
|
6138
|
+
draggableRef: ref,
|
|
6139
|
+
position: DraggablePosition.MOUSE_POSITION,
|
|
4780
6140
|
});
|
|
4781
|
-
|
|
6141
|
+
const onClick = (e) => {
|
|
6142
|
+
e.stopPropagation();
|
|
6143
|
+
if (!userIsDragging) {
|
|
6144
|
+
setSelectedNodeId(node.data.id);
|
|
6145
|
+
// if it is the assembly directly we just want to select it as a normal component
|
|
6146
|
+
if (isPatternNode) {
|
|
6147
|
+
sendMessage(OUTGOING_EVENTS.ComponentSelected, {
|
|
6148
|
+
nodeId: node.data.id,
|
|
6149
|
+
});
|
|
6150
|
+
return;
|
|
6151
|
+
}
|
|
6152
|
+
sendMessage(OUTGOING_EVENTS.ComponentSelected, {
|
|
6153
|
+
assembly: node.data.assembly,
|
|
6154
|
+
nodeId: node.data.id,
|
|
6155
|
+
});
|
|
6156
|
+
}
|
|
6157
|
+
};
|
|
6158
|
+
const onMouseOver = (e) => {
|
|
6159
|
+
e.stopPropagation();
|
|
6160
|
+
if (userIsDragging)
|
|
6161
|
+
return;
|
|
6162
|
+
sendMessage(OUTGOING_EVENTS.NewHoveredElement, {
|
|
6163
|
+
nodeId: componentId,
|
|
6164
|
+
});
|
|
6165
|
+
};
|
|
6166
|
+
const onMouseDown = (e) => {
|
|
6167
|
+
if (isDragDisabled) {
|
|
6168
|
+
return;
|
|
6169
|
+
}
|
|
6170
|
+
e.stopPropagation();
|
|
6171
|
+
setDomRect(e.currentTarget.getBoundingClientRect());
|
|
6172
|
+
};
|
|
6173
|
+
const ToolTipAndPlaceholder = (React.createElement(React.Fragment, null,
|
|
6174
|
+
React.createElement(Tooltip, { id: componentId, coordinates: coordinates, isAssemblyBlock: isPatternNode || isPatternComponent, isContainer: isContainer, isSelected: isSelected, label: displayName || 'No label specified' }),
|
|
6175
|
+
React.createElement(Placeholder, { ...placeholder, id: componentId }),
|
|
6176
|
+
userIsDragging && !isPatternComponent && (React.createElement(Hitboxes, { parentZoneId: zoneId, zoneId: componentId, isEmptyZone: isEmptyZone }))));
|
|
6177
|
+
return (React.createElement(Draggable, { key: componentId, draggableId: componentId, index: index, isDragDisabled: isDragDisabled, disableInteractiveElementBlocking: true }, (provided, snapshot) => elementToRender({
|
|
6178
|
+
dragProps: {
|
|
6179
|
+
...provided.draggableProps,
|
|
6180
|
+
...provided.dragHandleProps,
|
|
6181
|
+
'data-ctfl-draggable-id': componentId,
|
|
6182
|
+
'data-test-id': testId,
|
|
6183
|
+
innerRef: (refNode) => {
|
|
6184
|
+
provided?.innerRef(refNode);
|
|
6185
|
+
ref.current = refNode;
|
|
6186
|
+
},
|
|
6187
|
+
className: classNames(styles$1.DraggableComponent, {
|
|
6188
|
+
[styles$1.isAssemblyBlock]: isPatternComponent || isPatternNode,
|
|
6189
|
+
[styles$1.isDragging]: snapshot?.isDragging || userIsDragging,
|
|
6190
|
+
[styles$1.isSelected]: isSelected,
|
|
6191
|
+
[styles$1.isHoveringComponent]: isHoveredComponent,
|
|
6192
|
+
}),
|
|
6193
|
+
style: getStyle$1(provided.draggableProps.style, snapshot),
|
|
6194
|
+
onMouseDown,
|
|
6195
|
+
onMouseOver,
|
|
6196
|
+
onClick,
|
|
6197
|
+
ToolTipAndPlaceholder,
|
|
6198
|
+
},
|
|
6199
|
+
})));
|
|
4782
6200
|
};
|
|
4783
6201
|
|
|
4784
|
-
var css_248z = ".
|
|
4785
|
-
var styles = {"
|
|
6202
|
+
var css_248z = ".EmptyContainer-module_container__XPH5b {\n height: 200px;\n display: flex;\n width: 100%;\n position: absolute;\n align-items: center;\n justify-content: center;\n flex-direction: row;\n transition: all 0.2s;\n color: var(--exp-builder-gray400);\n font-size: var(--exp-builder-font-size-l);\n font-family: var(--exp-builder-font-stack-primary);\n outline: 2px dashed var(--exp-builder-gray400);\n outline-offset: -2px;\n}\n\n.EmptyContainer-module_highlight__lcICy:hover {\n outline: 2px dashed var(--exp-builder-blue500);\n background-color: rgba(var(--exp-builder-blue100-rgb), 0.5);\n cursor: grabbing;\n}\n\n.EmptyContainer-module_icon__82-2O rect {\n fill: var(--exp-builder-gray400);\n}\n\n.EmptyContainer-module_label__4TxRa {\n margin-left: var(--exp-builder-spacing-s);\n}\n";
|
|
6203
|
+
var styles = {"container":"EmptyContainer-module_container__XPH5b","highlight":"EmptyContainer-module_highlight__lcICy","icon":"EmptyContainer-module_icon__82-2O","label":"EmptyContainer-module_label__4TxRa"};
|
|
4786
6204
|
styleInject(css_248z);
|
|
4787
6205
|
|
|
4788
|
-
const
|
|
4789
|
-
return (React.createElement("div", { className: styles
|
|
4790
|
-
|
|
6206
|
+
const EmptyContainer = ({ isDragging }) => {
|
|
6207
|
+
return (React.createElement("div", { className: classNames(styles.container, {
|
|
6208
|
+
[styles.highlight]: isDragging,
|
|
6209
|
+
}), "data-type": "empty-container" },
|
|
6210
|
+
React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "37", height: "36", fill: "none", className: styles.icon },
|
|
4791
6211
|
React.createElement("rect", { width: "11.676", height: "11.676", x: "18.512", y: ".153", rx: "1.621", transform: "rotate(45 18.512 .153)" }),
|
|
4792
6212
|
React.createElement("rect", { width: "11.676", height: "11.676", x: "9.254", y: "9.139", rx: "1.621", transform: "rotate(45 9.254 9.139)" }),
|
|
4793
6213
|
React.createElement("rect", { width: "11.676", height: "11.676", x: "18.011", y: "18.625", rx: "1.621", transform: "rotate(45 18.01 18.625)" }),
|
|
4794
6214
|
React.createElement("rect", { width: "11.676", height: "11.676", x: "30.557", y: "10.131", rx: "1.621", transform: "rotate(60 30.557 10.13)" }),
|
|
4795
6215
|
React.createElement("path", { fill: "#fff", stroke: "#fff", strokeWidth: ".243", d: "M31.113 17.038a.463.463 0 0 0-.683-.517l-1.763 1.032-1.033-1.763a.464.464 0 0 0-.8.469l1.034 1.763-1.763 1.033a.463.463 0 1 0 .468.8l1.763-1.033 1.033 1.763a.463.463 0 1 0 .8-.469l-1.033-1.763 1.763-1.033a.463.463 0 0 0 .214-.282Z" })),
|
|
4796
|
-
React.createElement("span", { className: styles
|
|
6216
|
+
React.createElement("span", { className: styles.label }, "Add components to begin")));
|
|
4797
6217
|
};
|
|
4798
6218
|
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
6219
|
+
const useDropzoneDirection = ({ resolveDesignValue, node, zoneId }) => {
|
|
6220
|
+
const zone = useZoneStore((state) => state.zones);
|
|
6221
|
+
const upsertZone = useZoneStore((state) => state.upsertZone);
|
|
6222
|
+
useEffect(() => {
|
|
6223
|
+
function getDirection() {
|
|
6224
|
+
if (!node || !node.data.blockId) {
|
|
6225
|
+
return 'vertical';
|
|
6226
|
+
}
|
|
6227
|
+
if (!isContentfulStructureComponent(node.data.blockId)) {
|
|
6228
|
+
return 'vertical';
|
|
6229
|
+
}
|
|
6230
|
+
if (node.data.blockId === CONTENTFUL_COMPONENTS$1.columns.id) {
|
|
6231
|
+
return 'horizontal';
|
|
6232
|
+
}
|
|
6233
|
+
const designValues = node.data.props['cfFlexDirection'];
|
|
6234
|
+
if (!designValues || !resolveDesignValue || designValues.type !== 'DesignValue') {
|
|
6235
|
+
return 'vertical';
|
|
6236
|
+
}
|
|
6237
|
+
const direction = resolveDesignValue(designValues.valuesByBreakpoint);
|
|
6238
|
+
if (direction === 'row') {
|
|
6239
|
+
return 'horizontal';
|
|
6240
|
+
}
|
|
6241
|
+
return 'vertical';
|
|
6242
|
+
}
|
|
6243
|
+
upsertZone(zoneId, { direction: getDirection() });
|
|
6244
|
+
}, [node, resolveDesignValue, zoneId, upsertZone]);
|
|
6245
|
+
return zone[zoneId]?.direction || 'vertical';
|
|
6246
|
+
};
|
|
6247
|
+
|
|
6248
|
+
function getStyle(style = {}, snapshot) {
|
|
6249
|
+
if (!snapshot?.isDropAnimating) {
|
|
6250
|
+
return style;
|
|
6251
|
+
}
|
|
6252
|
+
return {
|
|
6253
|
+
...style,
|
|
6254
|
+
// cannot be 0, but make it super tiny
|
|
6255
|
+
transitionDuration: `0.001s`,
|
|
6256
|
+
};
|
|
6257
|
+
}
|
|
6258
|
+
const EditorBlockClone = ({ node: rawNode, entityStore, areEntitiesFetched, resolveDesignValue, snapshot, provided, renderDropzone, wrappingPatternIds, }) => {
|
|
6259
|
+
const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
6260
|
+
const { node, elementToRender } = useComponent({
|
|
6261
|
+
node: rawNode,
|
|
6262
|
+
entityStore,
|
|
6263
|
+
areEntitiesFetched,
|
|
6264
|
+
resolveDesignValue,
|
|
6265
|
+
renderDropzone,
|
|
6266
|
+
userIsDragging,
|
|
6267
|
+
wrappingPatternIds,
|
|
6268
|
+
});
|
|
6269
|
+
const isAssemblyBlock = node.type === ASSEMBLY_BLOCK_NODE_TYPE;
|
|
6270
|
+
return elementToRender({
|
|
6271
|
+
dragProps: {
|
|
6272
|
+
...provided?.draggableProps,
|
|
6273
|
+
...provided?.dragHandleProps,
|
|
6274
|
+
'data-ctfl-dragging-element': 'true',
|
|
6275
|
+
innerRef: provided?.innerRef,
|
|
6276
|
+
className: classNames(styles$1.DraggableComponent, styles$1.DraggableClone, {
|
|
6277
|
+
[styles$1.isAssemblyBlock]: isAssemblyBlock,
|
|
6278
|
+
[styles$1.isDragging]: snapshot?.isDragging,
|
|
6279
|
+
}),
|
|
6280
|
+
style: getStyle(provided?.draggableProps.style, snapshot),
|
|
4811
6281
|
},
|
|
4812
|
-
nodes: nodeToCoordinatesMap,
|
|
4813
|
-
sourceEvent,
|
|
4814
6282
|
});
|
|
4815
6283
|
};
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
if (
|
|
4819
|
-
const
|
|
4820
|
-
|
|
6284
|
+
|
|
6285
|
+
const getHtmlDragProps = (dragProps) => {
|
|
6286
|
+
if (dragProps) {
|
|
6287
|
+
const { ToolTipAndPlaceholder, Tag, innerRef, wrapComponent, ...htmlDragProps } = dragProps;
|
|
6288
|
+
return htmlDragProps;
|
|
6289
|
+
}
|
|
6290
|
+
return {};
|
|
6291
|
+
};
|
|
6292
|
+
const getHtmlComponentProps = (props) => {
|
|
6293
|
+
if (props) {
|
|
6294
|
+
const { editorMode, renderDropzone, node, ...htmlProps } = props;
|
|
6295
|
+
return htmlProps;
|
|
6296
|
+
}
|
|
6297
|
+
return {};
|
|
6298
|
+
};
|
|
6299
|
+
|
|
6300
|
+
function DropzoneClone({ node, entityStore, zoneId, resolveDesignValue, WrapperComponent = 'div', renderDropzone, dragProps, wrappingPatternIds, areEntitiesFetched, ...rest }) {
|
|
6301
|
+
const tree = useTreeStore((state) => state.tree);
|
|
6302
|
+
const content = node?.children || tree.root?.children || [];
|
|
6303
|
+
const { slotId } = parseZoneId(zoneId);
|
|
6304
|
+
const htmlDraggableProps = getHtmlDragProps(dragProps);
|
|
6305
|
+
const htmlProps = getHtmlComponentProps(rest);
|
|
6306
|
+
const isRootZone = zoneId === ROOT_ID;
|
|
6307
|
+
if (!resolveDesignValue) {
|
|
6308
|
+
return null;
|
|
6309
|
+
}
|
|
6310
|
+
return (React.createElement(WrapperComponent, { ...htmlDraggableProps, ...htmlProps, className: classNames(dragProps?.className, styles$1.Dropzone, styles$1.DropzoneClone, rest.className, {
|
|
6311
|
+
[styles$1.isRoot]: isRootZone,
|
|
6312
|
+
[styles$1.isEmptyZone]: !content.length,
|
|
6313
|
+
}), "data-ctfl-slot-id": slotId, ref: (refNode) => {
|
|
6314
|
+
if (dragProps?.innerRef) {
|
|
6315
|
+
dragProps.innerRef(refNode);
|
|
6316
|
+
}
|
|
6317
|
+
} }, content
|
|
6318
|
+
.filter((node) => node.data.slotId === slotId)
|
|
6319
|
+
.map((item) => {
|
|
6320
|
+
const componentId = item.data.id;
|
|
6321
|
+
return (React.createElement(EditorBlockClone, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, key: componentId, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone, wrappingPatternIds: wrappingPatternIds }));
|
|
6322
|
+
})));
|
|
6323
|
+
}
|
|
6324
|
+
|
|
6325
|
+
function Dropzone({ node, zoneId, resolveDesignValue, className, WrapperComponent = 'div', dragProps, entityStore, areEntitiesFetched, wrappingPatternIds: parentWrappingPatternIds = new Set(), ...rest }) {
|
|
6326
|
+
const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
6327
|
+
const draggedItem = useDraggedItemStore((state) => state.draggedItem);
|
|
6328
|
+
const isDraggingNewComponent = useDraggedItemStore((state) => Boolean(state.componentId));
|
|
6329
|
+
const isHoveringZone = useZoneStore((state) => state.hoveringZone === zoneId);
|
|
6330
|
+
const tree = useTreeStore((state) => state.tree);
|
|
6331
|
+
const content = node?.children || tree.root?.children || [];
|
|
6332
|
+
const { slotId } = parseZoneId(zoneId);
|
|
6333
|
+
const direction = useDropzoneDirection({ resolveDesignValue, node, zoneId });
|
|
6334
|
+
const draggedDestinationId = draggedItem && draggedItem.destination?.droppableId;
|
|
6335
|
+
const draggedNode = useMemo(() => {
|
|
6336
|
+
if (!draggedItem)
|
|
4821
6337
|
return;
|
|
6338
|
+
return getItem({ id: draggedItem.draggableId }, tree);
|
|
6339
|
+
}, [draggedItem, tree]);
|
|
6340
|
+
const isRootZone = zoneId === ROOT_ID;
|
|
6341
|
+
const isDestination = draggedDestinationId === zoneId;
|
|
6342
|
+
const isEmptyCanvas = isRootZone && !content.length;
|
|
6343
|
+
const isAssembly = ASSEMBLY_NODE_TYPES.includes(node?.type || '');
|
|
6344
|
+
const isRootAssembly = node?.type === ASSEMBLY_NODE_TYPE;
|
|
6345
|
+
const htmlDraggableProps = getHtmlDragProps(dragProps);
|
|
6346
|
+
const htmlProps = getHtmlComponentProps(rest);
|
|
6347
|
+
const wrappingPatternIds = useMemo(() => {
|
|
6348
|
+
// On the top level, the node is not defined. If the root blockId is not the default string,
|
|
6349
|
+
// we assume that it is the entry ID of the experience/ pattern to properly detect circular dependencies
|
|
6350
|
+
if (!node && tree.root.data.blockId && tree.root.data.blockId !== ROOT_ID) {
|
|
6351
|
+
return new Set([tree.root.data.blockId, ...parentWrappingPatternIds]);
|
|
4822
6352
|
}
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
};
|
|
6353
|
+
if (isRootAssembly && node?.data.blockId) {
|
|
6354
|
+
return new Set([node.data.blockId, ...parentWrappingPatternIds]);
|
|
6355
|
+
}
|
|
6356
|
+
return parentWrappingPatternIds;
|
|
6357
|
+
}, [isRootAssembly, node, parentWrappingPatternIds, tree.root.data.blockId]);
|
|
6358
|
+
// To avoid a circular dependency, we create the recursive rendering function here and trickle it down
|
|
6359
|
+
const renderDropzone = useCallback((node, props) => {
|
|
6360
|
+
return (React.createElement(Dropzone, { zoneId: node.data.id, entityStore: entityStore, node: node, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds, areEntitiesFetched: areEntitiesFetched, ...props }));
|
|
6361
|
+
}, [wrappingPatternIds, resolveDesignValue, entityStore, areEntitiesFetched]);
|
|
6362
|
+
const renderClonedDropzone = useCallback((node, props) => {
|
|
6363
|
+
return (React.createElement(DropzoneClone, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, zoneId: node.data.id, node: node, resolveDesignValue: resolveDesignValue, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds, ...props }));
|
|
6364
|
+
}, [resolveDesignValue, wrappingPatternIds, entityStore, areEntitiesFetched]);
|
|
6365
|
+
const isDropzoneEnabled = useMemo(() => {
|
|
6366
|
+
const isColumns = node?.data.blockId === CONTENTFUL_COMPONENTS$1.columns.id;
|
|
6367
|
+
const isDraggingSingleColumn = draggedNode?.data.blockId === CONTENTFUL_COMPONENTS$1.singleColumn.id;
|
|
6368
|
+
const isParentOfDraggedNode = node?.data.id === draggedNode?.parentId;
|
|
6369
|
+
// If dragging a single column, only enable the dropzone of the parent
|
|
6370
|
+
// columns component
|
|
6371
|
+
if (isDraggingSingleColumn && isColumns && isParentOfDraggedNode) {
|
|
6372
|
+
return true;
|
|
6373
|
+
}
|
|
6374
|
+
// If dragging a single column, disable dropzones for any component besides
|
|
6375
|
+
// the parent of the dragged single column
|
|
6376
|
+
if (isDraggingSingleColumn && !isParentOfDraggedNode) {
|
|
6377
|
+
return false;
|
|
6378
|
+
}
|
|
6379
|
+
// Disable dropzone for Columns component
|
|
6380
|
+
if (isColumns) {
|
|
6381
|
+
return false;
|
|
6382
|
+
}
|
|
6383
|
+
// Disable dropzone for Assembly
|
|
6384
|
+
if (isAssembly) {
|
|
6385
|
+
return false;
|
|
6386
|
+
}
|
|
6387
|
+
// Enable dropzone for the non-root hovered zones if component is not allowed on root
|
|
6388
|
+
if (!isDraggingNewComponent &&
|
|
6389
|
+
!isComponentAllowedOnRoot({ type: draggedNode?.type, componentId: draggedNode?.data.blockId })) {
|
|
6390
|
+
return isHoveringZone && !isRootZone;
|
|
6391
|
+
}
|
|
6392
|
+
// Enable dropzone for the hovered zone only
|
|
6393
|
+
return isHoveringZone;
|
|
6394
|
+
}, [isAssembly, isHoveringZone, isRootZone, isDraggingNewComponent, draggedNode, node]);
|
|
6395
|
+
if (!resolveDesignValue) {
|
|
6396
|
+
return null;
|
|
4831
6397
|
}
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
const
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
return
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
imageNode.removeEventListener('error', handleImageLoad);
|
|
4845
|
-
if (event.type === 'error') {
|
|
4846
|
-
console.warn('Image failed to load:', imageNode);
|
|
4847
|
-
reject();
|
|
6398
|
+
const isPatternWrapperComponentFullHeight = isRootAssembly
|
|
6399
|
+
? node.children.length === 1 &&
|
|
6400
|
+
resolveDesignValue(node?.children[0]?.data.props.cfHeight?.valuesByBreakpoint ?? {}, 'cfHeight') === '100%'
|
|
6401
|
+
: false;
|
|
6402
|
+
const isPatternWrapperComponentFullWidth = isRootAssembly
|
|
6403
|
+
? node.children.length === 1 &&
|
|
6404
|
+
resolveDesignValue(node?.children[0]?.data.props.cfWidth?.valuesByBreakpoint ?? {}, 'cfWidth') === '100%'
|
|
6405
|
+
: false;
|
|
6406
|
+
return (React.createElement(Droppable, { droppableId: zoneId, direction: direction, isDropDisabled: !isDropzoneEnabled, renderClone: (provided, snapshot, rubic) => (React.createElement(EditorBlockClone, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, node: content[rubic.source.index], resolveDesignValue: resolveDesignValue, provided: provided, snapshot: snapshot, renderDropzone: renderClonedDropzone, wrappingPatternIds: wrappingPatternIds })) }, (provided, snapshot) => {
|
|
6407
|
+
return (React.createElement(WrapperComponent, { ...(provided || { droppableProps: {} }).droppableProps, ...htmlDraggableProps, ...htmlProps, ref: (refNode) => {
|
|
6408
|
+
if (dragProps?.innerRef) {
|
|
6409
|
+
dragProps.innerRef(refNode);
|
|
4848
6410
|
}
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
6411
|
+
provided?.innerRef(refNode);
|
|
6412
|
+
}, id: zoneId, "data-ctfl-zone-id": zoneId, "data-ctfl-slot-id": slotId, className: classNames(dragProps?.className, styles$1.Dropzone, className, {
|
|
6413
|
+
[styles$1.isEmptyCanvas]: isEmptyCanvas,
|
|
6414
|
+
[styles$1.isDragging]: userIsDragging,
|
|
6415
|
+
[styles$1.isDestination]: isDestination && !isAssembly,
|
|
6416
|
+
[styles$1.isRoot]: isRootZone,
|
|
6417
|
+
[styles$1.isEmptyZone]: !content.length,
|
|
6418
|
+
[styles$1.isSlot]: Boolean(slotId),
|
|
6419
|
+
[styles$1.fullHeight]: isPatternWrapperComponentFullHeight,
|
|
6420
|
+
[styles$1.fullWidth]: isPatternWrapperComponentFullWidth,
|
|
6421
|
+
}) },
|
|
6422
|
+
isEmptyCanvas ? (React.createElement(EmptyContainer, { isDragging: isRootZone && userIsDragging })) : (content
|
|
6423
|
+
.filter((node) => node.data.slotId === slotId)
|
|
6424
|
+
.map((item, i) => (React.createElement(EditorBlock, { entityStore: entityStore, areEntitiesFetched: areEntitiesFetched, placeholder: {
|
|
6425
|
+
isDraggingOver: snapshot?.isDraggingOver,
|
|
6426
|
+
totalIndexes: content.length,
|
|
6427
|
+
elementIndex: i,
|
|
6428
|
+
dropzoneElementId: zoneId,
|
|
6429
|
+
direction,
|
|
6430
|
+
}, index: i, zoneId: zoneId, key: item.data.id, userIsDragging: userIsDragging, draggingNewComponent: isDraggingNewComponent, node: item, resolveDesignValue: resolveDesignValue, renderDropzone: renderDropzone, wrappingPatternIds: wrappingPatternIds })))),
|
|
6431
|
+
provided?.placeholder,
|
|
6432
|
+
dragProps?.ToolTipAndPlaceholder));
|
|
4856
6433
|
}));
|
|
4857
|
-
}
|
|
6434
|
+
}
|
|
4858
6435
|
|
|
4859
|
-
const
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
6436
|
+
const RootRenderer = ({ onChange, inMemoryEntitiesStore, }) => {
|
|
6437
|
+
useEditorSubscriber(inMemoryEntitiesStore);
|
|
6438
|
+
const dragItem = useDraggedItemStore((state) => state.componentId);
|
|
6439
|
+
const userIsDragging = useDraggedItemStore((state) => state.isDraggingOnCanvas);
|
|
6440
|
+
const setHoveredComponentId = useDraggedItemStore((state) => state.setHoveredComponentId);
|
|
6441
|
+
const breakpoints = useTreeStore((state) => state.breakpoints);
|
|
6442
|
+
const setSelectedNodeId = useEditorStore((state) => state.setSelectedNodeId);
|
|
6443
|
+
const containerRef = useRef(null);
|
|
6444
|
+
const { resolveDesignValue } = useBreakpoints(breakpoints);
|
|
6445
|
+
const [containerStyles, setContainerStyles] = useState({});
|
|
6446
|
+
const tree = useTreeStore((state) => state.tree);
|
|
6447
|
+
const handleMouseOver = useCallback(() => {
|
|
6448
|
+
// Remove hover state set by UI when mouse is over canvas
|
|
6449
|
+
setHoveredComponentId();
|
|
6450
|
+
// Remove hover styling from components in the layers tab
|
|
6451
|
+
sendMessage(OUTGOING_EVENTS.NewHoveredElement, {});
|
|
6452
|
+
}, [setHoveredComponentId]);
|
|
6453
|
+
const handleClickOutside = useCallback((e) => {
|
|
6454
|
+
const element = e.target;
|
|
6455
|
+
const isRoot = element.getAttribute('data-ctfl-zone-id') === ROOT_ID;
|
|
6456
|
+
const clickedOnCanvas = element.closest(`[data-ctfl-root]`);
|
|
6457
|
+
if (clickedOnCanvas && !isRoot) {
|
|
6458
|
+
return;
|
|
6459
|
+
}
|
|
6460
|
+
sendMessage(OUTGOING_EVENTS.OutsideCanvasClick, {
|
|
6461
|
+
outsideCanvasClick: true,
|
|
6462
|
+
});
|
|
6463
|
+
sendMessage(OUTGOING_EVENTS.ComponentSelected, {
|
|
6464
|
+
nodeId: '',
|
|
4865
6465
|
});
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
6466
|
+
setSelectedNodeId('');
|
|
6467
|
+
}, [setSelectedNodeId]);
|
|
6468
|
+
const handleResizeCanvas = useCallback(() => {
|
|
6469
|
+
const parentElement = containerRef.current?.parentElement;
|
|
6470
|
+
if (!parentElement) {
|
|
6471
|
+
return;
|
|
6472
|
+
}
|
|
6473
|
+
let siblingHeight = 0;
|
|
6474
|
+
for (const child of parentElement.children) {
|
|
6475
|
+
if (!child.hasAttribute('data-ctfl-root')) {
|
|
6476
|
+
siblingHeight += child.getBoundingClientRect().height;
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
if (!siblingHeight) {
|
|
6480
|
+
/**
|
|
6481
|
+
* DRAGGABLE_HEIGHT is subtracted here due to an uninteded scrolling effect
|
|
6482
|
+
* when dragging a new component onto the canvas
|
|
6483
|
+
*
|
|
6484
|
+
* The DRAGGABLE_HEIGHT is then added as margin bottom to offset this value
|
|
6485
|
+
* so that visually there is no difference to the user.
|
|
6486
|
+
*/
|
|
6487
|
+
setContainerStyles({
|
|
6488
|
+
minHeight: `${window.innerHeight - DRAGGABLE_HEIGHT}px`,
|
|
6489
|
+
});
|
|
6490
|
+
return;
|
|
6491
|
+
}
|
|
6492
|
+
setContainerStyles({
|
|
6493
|
+
minHeight: `${window.innerHeight - siblingHeight}px`,
|
|
6494
|
+
});
|
|
6495
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6496
|
+
}, [containerRef.current]);
|
|
4875
6497
|
useEffect(() => {
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
6498
|
+
if (onChange)
|
|
6499
|
+
onChange(tree);
|
|
6500
|
+
}, [tree, onChange]);
|
|
4879
6501
|
useEffect(() => {
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
6502
|
+
window.addEventListener('mouseover', handleMouseOver);
|
|
6503
|
+
return () => {
|
|
6504
|
+
window.removeEventListener('mouseover', handleMouseOver);
|
|
6505
|
+
};
|
|
6506
|
+
}, [handleMouseOver]);
|
|
4885
6507
|
useEffect(() => {
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
};
|
|
4895
|
-
|
|
4896
|
-
const RootRenderer = ({ inMemoryEntitiesStore }) => {
|
|
4897
|
-
useEditorSubscriber(inMemoryEntitiesStore);
|
|
4898
|
-
const tree = useTreeStore((state) => state.tree);
|
|
4899
|
-
useCanvasGeometryUpdates({ tree });
|
|
4900
|
-
const breakpoints = useTreeStore((state) => state.breakpoints);
|
|
4901
|
-
const { resolveDesignValue } = useBreakpoints(breakpoints);
|
|
4902
|
-
// If the root blockId is defined but not the default string, it is the entry ID
|
|
4903
|
-
// of the experience/ pattern to properly detect circular dependencies.
|
|
4904
|
-
const rootBlockId = tree.root.data.blockId ?? ROOT_ID;
|
|
4905
|
-
const wrappingPatternIds = rootBlockId !== ROOT_ID ? new Set([rootBlockId]) : new Set();
|
|
6508
|
+
document.addEventListener('click', handleClickOutside);
|
|
6509
|
+
return () => {
|
|
6510
|
+
document.removeEventListener('click', handleClickOutside);
|
|
6511
|
+
};
|
|
6512
|
+
}, [handleClickOutside]);
|
|
6513
|
+
useEffect(() => {
|
|
6514
|
+
handleResizeCanvas();
|
|
6515
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6516
|
+
}, [containerRef.current]);
|
|
4906
6517
|
const entityStore = inMemoryEntitiesStore((state) => state.entityStore);
|
|
4907
6518
|
const areEntitiesFetched = inMemoryEntitiesStore((state) => state.areEntitiesFetched);
|
|
4908
|
-
return (React.createElement(
|
|
4909
|
-
|
|
6519
|
+
return (React.createElement(DNDProvider, null,
|
|
6520
|
+
dragItem && React.createElement(DraggableContainer, { id: dragItem }),
|
|
6521
|
+
React.createElement("div", { "data-ctfl-root": true, className: styles$3.container, ref: containerRef, style: containerStyles },
|
|
6522
|
+
userIsDragging && React.createElement("div", { "data-ctfl-zone-id": ROOT_ID, className: styles$3.hitbox }),
|
|
6523
|
+
React.createElement(Dropzone, { zoneId: ROOT_ID, resolveDesignValue: resolveDesignValue, entityStore: entityStore, areEntitiesFetched: areEntitiesFetched }),
|
|
6524
|
+
userIsDragging && (React.createElement(React.Fragment, null,
|
|
6525
|
+
React.createElement("div", { "data-ctfl-zone-id": ROOT_ID, className: styles$3.hitboxLower }),
|
|
6526
|
+
React.createElement("div", { "data-ctfl-zone-id": ROOT_ID, className: styles$3.canvasBottomSpacer })))),
|
|
6527
|
+
React.createElement("div", { "data-ctfl-hitboxes": true })));
|
|
4910
6528
|
};
|
|
4911
6529
|
|
|
4912
6530
|
const useInitializeEditor = (inMemoryEntitiesStore) => {
|
|
@@ -4948,11 +6566,38 @@ const useInitializeEditor = (inMemoryEntitiesStore) => {
|
|
|
4948
6566
|
const VisualEditorRoot = ({ experience, inMemoryEntitiesStore: inMemoryEntitiesStore$1 = inMemoryEntitiesStore, }) => {
|
|
4949
6567
|
const initialized = useInitializeEditor(inMemoryEntitiesStore$1);
|
|
4950
6568
|
const setHyperLinkPattern = useEditorStore((state) => state.setHyperLinkPattern);
|
|
6569
|
+
const setMousePosition = useDraggedItemStore((state) => state.setMousePosition);
|
|
6570
|
+
const setHoveringZone = useZoneStore((state) => state.setHoveringZone);
|
|
4951
6571
|
useEffect(() => {
|
|
4952
6572
|
if (experience?.hyperlinkPattern) {
|
|
4953
6573
|
setHyperLinkPattern(experience.hyperlinkPattern);
|
|
4954
6574
|
}
|
|
4955
6575
|
}, [experience?.hyperlinkPattern, setHyperLinkPattern]);
|
|
6576
|
+
useEffect(() => {
|
|
6577
|
+
const onMouseMove = (e) => {
|
|
6578
|
+
setMousePosition(e.clientX, e.clientY);
|
|
6579
|
+
const target = e.target;
|
|
6580
|
+
const zoneId = target.closest(`[${CTFL_ZONE_ID}]`)?.getAttribute(CTFL_ZONE_ID);
|
|
6581
|
+
if (zoneId) {
|
|
6582
|
+
setHoveringZone(zoneId);
|
|
6583
|
+
}
|
|
6584
|
+
if (!SimulateDnD$1.isDragging) {
|
|
6585
|
+
return;
|
|
6586
|
+
}
|
|
6587
|
+
if (target.id === NEW_COMPONENT_ID) {
|
|
6588
|
+
return;
|
|
6589
|
+
}
|
|
6590
|
+
SimulateDnD$1.updateDrag(e.clientX, e.clientY);
|
|
6591
|
+
sendMessage(OUTGOING_EVENTS.MouseMove, {
|
|
6592
|
+
clientX: e.pageX - window.scrollX,
|
|
6593
|
+
clientY: e.pageY - window.scrollY,
|
|
6594
|
+
});
|
|
6595
|
+
};
|
|
6596
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
6597
|
+
return () => {
|
|
6598
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
6599
|
+
};
|
|
6600
|
+
}, [setHoveringZone, setMousePosition]);
|
|
4956
6601
|
if (!initialized)
|
|
4957
6602
|
return null;
|
|
4958
6603
|
return React.createElement(RootRenderer, { inMemoryEntitiesStore: inMemoryEntitiesStore$1 });
|