@elementor/editor-canvas 3.33.0-293 → 3.33.0-295
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 +253 -139
- package/dist/index.mjs +189 -74
- package/package.json +17 -17
- package/src/components/__tests__/elements-overlays.test.tsx +96 -12
- package/src/components/__tests__/inline-editor-overlay.test.tsx +245 -0
- package/src/components/elements-overlays.tsx +33 -10
- package/src/components/inline-editor-overlay.tsx +79 -0
- package/src/components/{element-overlay.tsx → outline-overlay.tsx} +4 -6
- package/src/hooks/use-style-items.ts +0 -1
- package/src/types/element-overlay.ts +18 -0
- package/src/utils/inline-editing-utils.ts +43 -0
package/dist/index.mjs
CHANGED
|
@@ -46,56 +46,60 @@ var renameClass = (oldClassName, newClassName) => {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
// src/components/elements-overlays.tsx
|
|
49
|
-
import * as
|
|
49
|
+
import * as React3 from "react";
|
|
50
50
|
import { getElements, useSelectedElement } from "@elementor/editor-elements";
|
|
51
51
|
import {
|
|
52
52
|
__privateUseIsRouteActive as useIsRouteActive,
|
|
53
53
|
__privateUseListenTo as useListenTo,
|
|
54
|
+
isExperimentActive,
|
|
54
55
|
useEditMode,
|
|
55
56
|
windowEvent
|
|
56
57
|
} from "@elementor/editor-v1-adapters";
|
|
57
58
|
|
|
58
|
-
// src/
|
|
59
|
-
import
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
// src/utils/inline-editing-utils.ts
|
|
60
|
+
import { getContainer, getElementType } from "@elementor/editor-elements";
|
|
61
|
+
var WIDGET_PROPERTY_MAP = {
|
|
62
|
+
"e-heading": "title",
|
|
63
|
+
"e-paragraph": "paragraph"
|
|
64
|
+
};
|
|
65
|
+
var getHtmlPropertyName = (container) => {
|
|
66
|
+
const widgetType = container?.model?.get("widgetType") ?? container?.model?.get("elType");
|
|
67
|
+
if (!widgetType) {
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
if (WIDGET_PROPERTY_MAP[widgetType]) {
|
|
71
|
+
return WIDGET_PROPERTY_MAP[widgetType];
|
|
72
|
+
}
|
|
73
|
+
const propsSchema = getElementType(widgetType)?.propsSchema;
|
|
74
|
+
if (!propsSchema) {
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
const entry = Object.entries(propsSchema).find(([, propType]) => propType.key === "html");
|
|
78
|
+
return entry?.[0] ?? "";
|
|
79
|
+
};
|
|
80
|
+
var hasInlineEditableProperty = (containerId) => {
|
|
81
|
+
const container = getContainer(containerId);
|
|
82
|
+
const widgetType = container?.model?.get("widgetType") ?? container?.model?.get("elType");
|
|
83
|
+
if (!widgetType) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return widgetType in WIDGET_PROPERTY_MAP;
|
|
87
|
+
};
|
|
88
|
+
var getInlineEditablePropertyName = (container) => {
|
|
89
|
+
return getHtmlPropertyName(container);
|
|
90
|
+
};
|
|
62
91
|
|
|
63
|
-
// src/
|
|
64
|
-
import
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return () => {
|
|
72
|
-
events.forEach(([eventName, listener]) => el.removeEventListener(eventName, listener));
|
|
73
|
-
attrs.forEach(([attrName]) => el.removeAttribute(attrName));
|
|
74
|
-
};
|
|
75
|
-
}, [getProps, element]);
|
|
76
|
-
}
|
|
77
|
-
function groupProps(props) {
|
|
78
|
-
const eventRegex = /^on(?=[A-Z])/;
|
|
79
|
-
return Object.entries(props).reduce(
|
|
80
|
-
(acc, [propName, propValue]) => {
|
|
81
|
-
if (!eventRegex.test(propName)) {
|
|
82
|
-
acc.attrs.push([propName, propValue]);
|
|
83
|
-
return acc;
|
|
84
|
-
}
|
|
85
|
-
const eventName = propName.replace(eventRegex, "").toLowerCase();
|
|
86
|
-
const listener = propValue;
|
|
87
|
-
acc.events.push([eventName, listener]);
|
|
88
|
-
return acc;
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
events: [],
|
|
92
|
-
attrs: []
|
|
93
|
-
}
|
|
94
|
-
);
|
|
95
|
-
}
|
|
92
|
+
// src/components/inline-editor-overlay.tsx
|
|
93
|
+
import * as React2 from "react";
|
|
94
|
+
import { InlineEditor } from "@elementor/editor-controls";
|
|
95
|
+
import { getContainer as getContainer2, updateElementSettings, useElementSetting } from "@elementor/editor-elements";
|
|
96
|
+
import { htmlPropTypeUtil } from "@elementor/editor-props";
|
|
97
|
+
import { Box as Box2 } from "@elementor/ui";
|
|
98
|
+
import { debounce } from "@elementor/utils";
|
|
99
|
+
import { FloatingPortal as FloatingPortal2 } from "@floating-ui/react";
|
|
96
100
|
|
|
97
101
|
// src/hooks/use-floating-on-element.ts
|
|
98
|
-
import { useEffect as
|
|
102
|
+
import { useEffect as useEffect2, useState } from "react";
|
|
99
103
|
import { autoUpdate, offset, size, useFloating } from "@floating-ui/react";
|
|
100
104
|
function useFloatingOnElement({ element, isSelected }) {
|
|
101
105
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -121,7 +125,7 @@ function useFloatingOnElement({ element, isSelected }) {
|
|
|
121
125
|
offset(({ rects }) => -rects.reference.height / 2 - rects.floating.height / 2)
|
|
122
126
|
]
|
|
123
127
|
});
|
|
124
|
-
|
|
128
|
+
useEffect2(() => {
|
|
125
129
|
refs.setReference(element);
|
|
126
130
|
}, [element, refs]);
|
|
127
131
|
return {
|
|
@@ -135,6 +139,45 @@ function useFloatingOnElement({ element, isSelected }) {
|
|
|
135
139
|
};
|
|
136
140
|
}
|
|
137
141
|
|
|
142
|
+
// src/components/outline-overlay.tsx
|
|
143
|
+
import * as React from "react";
|
|
144
|
+
import { Box, styled } from "@elementor/ui";
|
|
145
|
+
import { FloatingPortal, useHover, useInteractions } from "@floating-ui/react";
|
|
146
|
+
|
|
147
|
+
// src/hooks/use-bind-react-props-to-element.ts
|
|
148
|
+
import { useEffect as useEffect3 } from "react";
|
|
149
|
+
function useBindReactPropsToElement(element, getProps) {
|
|
150
|
+
useEffect3(() => {
|
|
151
|
+
const el = element;
|
|
152
|
+
const { events, attrs } = groupProps(getProps());
|
|
153
|
+
events.forEach(([eventName, listener]) => el.addEventListener(eventName, listener));
|
|
154
|
+
attrs.forEach(([attrName, attrValue]) => el.setAttribute(attrName, attrValue));
|
|
155
|
+
return () => {
|
|
156
|
+
events.forEach(([eventName, listener]) => el.removeEventListener(eventName, listener));
|
|
157
|
+
attrs.forEach(([attrName]) => el.removeAttribute(attrName));
|
|
158
|
+
};
|
|
159
|
+
}, [getProps, element]);
|
|
160
|
+
}
|
|
161
|
+
function groupProps(props) {
|
|
162
|
+
const eventRegex = /^on(?=[A-Z])/;
|
|
163
|
+
return Object.entries(props).reduce(
|
|
164
|
+
(acc, [propName, propValue]) => {
|
|
165
|
+
if (!eventRegex.test(propName)) {
|
|
166
|
+
acc.attrs.push([propName, propValue]);
|
|
167
|
+
return acc;
|
|
168
|
+
}
|
|
169
|
+
const eventName = propName.replace(eventRegex, "").toLowerCase();
|
|
170
|
+
const listener = propValue;
|
|
171
|
+
acc.events.push([eventName, listener]);
|
|
172
|
+
return acc;
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
events: [],
|
|
176
|
+
attrs: []
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
138
181
|
// src/hooks/use-has-overlapping.ts
|
|
139
182
|
var possibleOverlappingSelectors = [".e-off-canvas"];
|
|
140
183
|
var useHasOverlapping = () => {
|
|
@@ -152,7 +195,7 @@ var useHasOverlapping = () => {
|
|
|
152
195
|
return hasOverlapping;
|
|
153
196
|
};
|
|
154
197
|
|
|
155
|
-
// src/components/
|
|
198
|
+
// src/components/outline-overlay.tsx
|
|
156
199
|
var CANVAS_WRAPPER_ID = "elementor-preview-responsive-wrapper";
|
|
157
200
|
var OverlayBox = styled(Box, {
|
|
158
201
|
shouldForwardProp: (prop) => prop !== "isSelected" && prop !== "isSmallerOffset"
|
|
@@ -161,7 +204,7 @@ var OverlayBox = styled(Box, {
|
|
|
161
204
|
outlineOffset: isSelected && !isSmallerOffset ? "-2px" : "-1px",
|
|
162
205
|
pointerEvents: "none"
|
|
163
206
|
}));
|
|
164
|
-
|
|
207
|
+
var OutlineOverlay = ({ element, isSelected, id }) => {
|
|
165
208
|
const { context, floating, isVisible } = useFloatingOnElement({ element, isSelected });
|
|
166
209
|
const { getFloatingProps, getReferenceProps } = useInteractions([useHover(context)]);
|
|
167
210
|
const hasOverlapping = useHasOverlapping();
|
|
@@ -179,9 +222,75 @@ function ElementOverlay({ element, isSelected, id }) {
|
|
|
179
222
|
...getFloatingProps()
|
|
180
223
|
}
|
|
181
224
|
));
|
|
182
|
-
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/components/inline-editor-overlay.tsx
|
|
228
|
+
var OVERLAY_Z_INDEX = 1e3;
|
|
229
|
+
var DEBOUNCE_DELAY = 100;
|
|
230
|
+
var InlineEditorOverlay = ({ element, isSelected, id }) => {
|
|
231
|
+
const { floating, isVisible } = useFloatingOnElement({ element, isSelected });
|
|
232
|
+
const propertyName = React2.useMemo(() => {
|
|
233
|
+
const container = getContainer2(id);
|
|
234
|
+
return getInlineEditablePropertyName(container);
|
|
235
|
+
}, [id]);
|
|
236
|
+
const contentProp = useElementSetting(id, propertyName);
|
|
237
|
+
const value = React2.useMemo(() => htmlPropTypeUtil.extract(contentProp) || "", [contentProp]);
|
|
238
|
+
const debouncedUpdateRef = React2.useRef(null);
|
|
239
|
+
const lastValueRef = React2.useRef("");
|
|
240
|
+
React2.useEffect(() => {
|
|
241
|
+
debouncedUpdateRef.current = debounce((newValue) => {
|
|
242
|
+
const textContent = newValue.replace(/<[^>]*>/g, "").trim();
|
|
243
|
+
const valueToSave = textContent === "" ? " " : newValue;
|
|
244
|
+
updateElementSettings({
|
|
245
|
+
id,
|
|
246
|
+
props: {
|
|
247
|
+
[propertyName]: htmlPropTypeUtil.create(valueToSave)
|
|
248
|
+
},
|
|
249
|
+
withHistory: true
|
|
250
|
+
});
|
|
251
|
+
}, DEBOUNCE_DELAY);
|
|
252
|
+
return () => {
|
|
253
|
+
debouncedUpdateRef.current?.cancel?.();
|
|
254
|
+
};
|
|
255
|
+
}, [id, propertyName]);
|
|
256
|
+
const handleValueChange = React2.useCallback((newValue) => {
|
|
257
|
+
lastValueRef.current = newValue;
|
|
258
|
+
debouncedUpdateRef.current?.(newValue);
|
|
259
|
+
}, []);
|
|
260
|
+
React2.useEffect(() => {
|
|
261
|
+
if (!isVisible && debouncedUpdateRef.current?.pending?.()) {
|
|
262
|
+
debouncedUpdateRef.current.flush(lastValueRef.current);
|
|
263
|
+
}
|
|
264
|
+
}, [isVisible]);
|
|
265
|
+
if (!isVisible) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
return /* @__PURE__ */ React2.createElement(FloatingPortal2, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React2.createElement(
|
|
269
|
+
Box2,
|
|
270
|
+
{
|
|
271
|
+
ref: floating.setRef,
|
|
272
|
+
style: {
|
|
273
|
+
...floating.styles,
|
|
274
|
+
zIndex: OVERLAY_Z_INDEX,
|
|
275
|
+
pointerEvents: "auto"
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
/* @__PURE__ */ React2.createElement(InlineEditor, { value, setValue: handleValueChange })
|
|
279
|
+
));
|
|
280
|
+
};
|
|
183
281
|
|
|
184
282
|
// src/components/elements-overlays.tsx
|
|
283
|
+
var ELEMENTS_DATA_ATTR = "atomic";
|
|
284
|
+
var overlayRegistry = [
|
|
285
|
+
{
|
|
286
|
+
component: OutlineOverlay,
|
|
287
|
+
shouldRender: () => true
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
component: InlineEditorOverlay,
|
|
291
|
+
shouldRender: ({ id, isSelected }) => isSelected && hasInlineEditableProperty(id) && isExperimentActive("v4-inline-text-editing")
|
|
292
|
+
}
|
|
293
|
+
];
|
|
185
294
|
function ElementsOverlays() {
|
|
186
295
|
const selected = useSelectedElement();
|
|
187
296
|
const elements = useElementsDom();
|
|
@@ -189,9 +298,16 @@ function ElementsOverlays() {
|
|
|
189
298
|
const isEditMode = currentEditMode === "edit";
|
|
190
299
|
const isKitRouteActive = useIsRouteActive("panel/global");
|
|
191
300
|
const isActive = isEditMode && !isKitRouteActive;
|
|
192
|
-
|
|
301
|
+
if (!isActive) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
return elements.map(([id, element]) => {
|
|
305
|
+
const isSelected = selected.element?.id === id;
|
|
306
|
+
return overlayRegistry.map(
|
|
307
|
+
({ shouldRender, component: Overlay }, index) => shouldRender({ id, element, isSelected }) && /* @__PURE__ */ React3.createElement(Overlay, { key: `${id}-${index}`, id, element, isSelected })
|
|
308
|
+
);
|
|
309
|
+
});
|
|
193
310
|
}
|
|
194
|
-
var ELEMENTS_DATA_ATTR = "atomic";
|
|
195
311
|
function useElementsDom() {
|
|
196
312
|
return useListenTo(
|
|
197
313
|
[windowEvent("elementor/editor/element-rendered"), windowEvent("elementor/editor/element-destroyed")],
|
|
@@ -202,20 +318,20 @@ function useElementsDom() {
|
|
|
202
318
|
}
|
|
203
319
|
|
|
204
320
|
// src/components/interactions-renderer.tsx
|
|
205
|
-
import * as
|
|
321
|
+
import * as React4 from "react";
|
|
206
322
|
import { __privateUseListenTo as useListenTo2, commandEndEvent } from "@elementor/editor-v1-adapters";
|
|
207
323
|
import { Portal } from "@elementor/ui";
|
|
208
324
|
|
|
209
325
|
// src/hooks/use-interactions-items.ts
|
|
210
|
-
import { useEffect as
|
|
326
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useState as useState2 } from "react";
|
|
211
327
|
import { interactionsRepository } from "@elementor/editor-interactions";
|
|
212
328
|
import { registerDataHook } from "@elementor/editor-v1-adapters";
|
|
213
329
|
|
|
214
330
|
// src/hooks/use-on-mount.ts
|
|
215
|
-
import { useEffect as
|
|
331
|
+
import { useEffect as useEffect5, useRef as useRef2 } from "react";
|
|
216
332
|
function useOnMount(cb) {
|
|
217
|
-
const mounted =
|
|
218
|
-
|
|
333
|
+
const mounted = useRef2(false);
|
|
334
|
+
useEffect5(() => {
|
|
219
335
|
if (!mounted.current) {
|
|
220
336
|
mounted.current = true;
|
|
221
337
|
cb();
|
|
@@ -226,7 +342,7 @@ function useOnMount(cb) {
|
|
|
226
342
|
// src/hooks/use-interactions-items.ts
|
|
227
343
|
function useInteractionsItems() {
|
|
228
344
|
const [interactionItems, setInteractionItems] = useState2({});
|
|
229
|
-
const providerAndSubscribers =
|
|
345
|
+
const providerAndSubscribers = useMemo2(() => {
|
|
230
346
|
try {
|
|
231
347
|
const providers = interactionsRepository.getProviders();
|
|
232
348
|
const mapped = providers.map((provider) => {
|
|
@@ -243,7 +359,7 @@ function useInteractionsItems() {
|
|
|
243
359
|
return [];
|
|
244
360
|
}
|
|
245
361
|
}, []);
|
|
246
|
-
|
|
362
|
+
useEffect6(() => {
|
|
247
363
|
if (providerAndSubscribers.length === 0) {
|
|
248
364
|
return;
|
|
249
365
|
}
|
|
@@ -274,7 +390,7 @@ function useInteractionsItems() {
|
|
|
274
390
|
});
|
|
275
391
|
});
|
|
276
392
|
});
|
|
277
|
-
return
|
|
393
|
+
return useMemo2(() => {
|
|
278
394
|
const result = Object.values(interactionItems).sort(sortByProviderPriority).flatMap(({ items }) => items);
|
|
279
395
|
return result;
|
|
280
396
|
}, [interactionItems]);
|
|
@@ -310,7 +426,7 @@ function InteractionsRenderer() {
|
|
|
310
426
|
return null;
|
|
311
427
|
}
|
|
312
428
|
const interactionsData = JSON.stringify(Array.isArray(interactionItems) ? interactionItems : []);
|
|
313
|
-
return /* @__PURE__ */
|
|
429
|
+
return /* @__PURE__ */ React4.createElement(Portal, { container }, /* @__PURE__ */ React4.createElement(
|
|
314
430
|
"script",
|
|
315
431
|
{
|
|
316
432
|
type: "application/json",
|
|
@@ -326,7 +442,7 @@ function usePortalContainer() {
|
|
|
326
442
|
}
|
|
327
443
|
|
|
328
444
|
// src/components/style-renderer.tsx
|
|
329
|
-
import * as
|
|
445
|
+
import * as React5 from "react";
|
|
330
446
|
import { __privateUseListenTo as useListenTo4, commandEndEvent as commandEndEvent3 } from "@elementor/editor-v1-adapters";
|
|
331
447
|
import { Portal as Portal2 } from "@elementor/ui";
|
|
332
448
|
|
|
@@ -378,7 +494,7 @@ function getLinkAttrs(el) {
|
|
|
378
494
|
}
|
|
379
495
|
|
|
380
496
|
// src/hooks/use-style-items.ts
|
|
381
|
-
import { useEffect as
|
|
497
|
+
import { useEffect as useEffect7, useMemo as useMemo5, useState as useState3 } from "react";
|
|
382
498
|
import { getBreakpoints } from "@elementor/editor-responsive";
|
|
383
499
|
import { isClassState as isClassState2 } from "@elementor/editor-styles";
|
|
384
500
|
import { stylesRepository as stylesRepository2 } from "@elementor/editor-styles-repository";
|
|
@@ -416,7 +532,7 @@ function signalizedProcess(signal, steps = []) {
|
|
|
416
532
|
}
|
|
417
533
|
|
|
418
534
|
// src/hooks/use-style-prop-resolver.ts
|
|
419
|
-
import { useMemo as
|
|
535
|
+
import { useMemo as useMemo3 } from "react";
|
|
420
536
|
import { getStylesSchema } from "@elementor/editor-styles";
|
|
421
537
|
|
|
422
538
|
// src/renderers/create-props-resolver.ts
|
|
@@ -540,7 +656,7 @@ var enqueueFont = (fontFamily, context = "preview") => {
|
|
|
540
656
|
|
|
541
657
|
// src/hooks/use-style-prop-resolver.ts
|
|
542
658
|
function useStylePropResolver() {
|
|
543
|
-
return
|
|
659
|
+
return useMemo3(() => {
|
|
544
660
|
return createPropsResolver({
|
|
545
661
|
transformers: styleTransformersRegistry,
|
|
546
662
|
schema: getStylesSchema(),
|
|
@@ -555,7 +671,7 @@ function useStylePropResolver() {
|
|
|
555
671
|
}
|
|
556
672
|
|
|
557
673
|
// src/hooks/use-style-renderer.ts
|
|
558
|
-
import { useMemo as
|
|
674
|
+
import { useMemo as useMemo4 } from "react";
|
|
559
675
|
import { useBreakpointsMap } from "@elementor/editor-responsive";
|
|
560
676
|
|
|
561
677
|
// src/renderers/create-styles-renderer.ts
|
|
@@ -659,7 +775,7 @@ function customCssToString(customCss) {
|
|
|
659
775
|
var SELECTOR_PREFIX = ".elementor";
|
|
660
776
|
function useStyleRenderer(resolve) {
|
|
661
777
|
const breakpoints = useBreakpointsMap();
|
|
662
|
-
return
|
|
778
|
+
return useMemo4(() => {
|
|
663
779
|
return createStylesRenderer({
|
|
664
780
|
selectorPrefix: SELECTOR_PREFIX,
|
|
665
781
|
breakpoints,
|
|
@@ -673,7 +789,7 @@ function useStyleItems() {
|
|
|
673
789
|
const resolve = useStylePropResolver();
|
|
674
790
|
const renderStyles = useStyleRenderer(resolve);
|
|
675
791
|
const [styleItems, setStyleItems] = useState3({});
|
|
676
|
-
const providerAndSubscribers =
|
|
792
|
+
const providerAndSubscribers = useMemo5(() => {
|
|
677
793
|
return stylesRepository2.getProviders().map((provider) => {
|
|
678
794
|
return {
|
|
679
795
|
provider,
|
|
@@ -685,7 +801,7 @@ function useStyleItems() {
|
|
|
685
801
|
};
|
|
686
802
|
});
|
|
687
803
|
}, [renderStyles]);
|
|
688
|
-
|
|
804
|
+
useEffect7(() => {
|
|
689
805
|
const unsubscribes = providerAndSubscribers.map(
|
|
690
806
|
({ provider, subscriber }) => provider.subscribe(subscriber)
|
|
691
807
|
);
|
|
@@ -700,9 +816,8 @@ function useStyleItems() {
|
|
|
700
816
|
});
|
|
701
817
|
});
|
|
702
818
|
const breakpointsOrder = getBreakpoints().map((breakpoint) => breakpoint.id);
|
|
703
|
-
return
|
|
819
|
+
return useMemo5(
|
|
704
820
|
() => Object.values(styleItems).sort(sortByProviderPriority2).flatMap(({ items }) => items).sort(sortByStateType).sort(sortByBreakpoint(breakpointsOrder)),
|
|
705
|
-
// eslint-disable-next-line
|
|
706
821
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
707
822
|
[styleItems, breakpointsOrder.join("-")]
|
|
708
823
|
);
|
|
@@ -774,7 +889,7 @@ function StyleRenderer() {
|
|
|
774
889
|
if (!container) {
|
|
775
890
|
return null;
|
|
776
891
|
}
|
|
777
|
-
return /* @__PURE__ */
|
|
892
|
+
return /* @__PURE__ */ React5.createElement(Portal2, { container }, styleItems.map((item, i) => /* @__PURE__ */ React5.createElement("style", { key: `${item.id}-${i}-${item.breakpoint}` }, item.value)), linksAttrs.map((attrs) => /* @__PURE__ */ React5.createElement("link", { ...attrs, key: attrs.id })));
|
|
778
893
|
}
|
|
779
894
|
function usePortalContainer2() {
|
|
780
895
|
return useListenTo4(commandEndEvent3("editor/documents/attach-preview"), () => getCanvasIframeDocument()?.head);
|
|
@@ -1621,10 +1736,10 @@ function cleanupPropType(propType) {
|
|
|
1621
1736
|
|
|
1622
1737
|
// src/mcp/tools/build-composition/tool.ts
|
|
1623
1738
|
import {
|
|
1624
|
-
createElement as
|
|
1739
|
+
createElement as createElement6,
|
|
1625
1740
|
deleteElement,
|
|
1626
1741
|
generateElementId,
|
|
1627
|
-
getContainer,
|
|
1742
|
+
getContainer as getContainer3,
|
|
1628
1743
|
getWidgetsCache as getWidgetsCache4
|
|
1629
1744
|
} from "@elementor/editor-elements";
|
|
1630
1745
|
|
|
@@ -1633,7 +1748,7 @@ import {
|
|
|
1633
1748
|
createElementStyle,
|
|
1634
1749
|
getElementStyles,
|
|
1635
1750
|
getWidgetsCache as getWidgetsCache3,
|
|
1636
|
-
updateElementSettings,
|
|
1751
|
+
updateElementSettings as updateElementSettings2,
|
|
1637
1752
|
updateElementStyle
|
|
1638
1753
|
} from "@elementor/editor-elements";
|
|
1639
1754
|
import {
|
|
@@ -1734,7 +1849,7 @@ var doUpdateElementProperty = (params) => {
|
|
|
1734
1849
|
);
|
|
1735
1850
|
}
|
|
1736
1851
|
const value = resolvePropValue(propertyValue);
|
|
1737
|
-
|
|
1852
|
+
updateElementSettings2({
|
|
1738
1853
|
id: elementId,
|
|
1739
1854
|
props: {
|
|
1740
1855
|
[propertyName]: value
|
|
@@ -1892,7 +2007,7 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
1892
2007
|
const softErrors = [];
|
|
1893
2008
|
const rootContainers = [];
|
|
1894
2009
|
const widgetsCache = getWidgetsCache4() || {};
|
|
1895
|
-
const documentContainer =
|
|
2010
|
+
const documentContainer = getContainer3("document");
|
|
1896
2011
|
try {
|
|
1897
2012
|
const parser = new DOMParser();
|
|
1898
2013
|
xml = parser.parseFromString(xmlStructure, "application/xml");
|
|
@@ -1907,14 +2022,14 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
1907
2022
|
errors.push(new Error(`Unknown widget type: ${elementTag}`));
|
|
1908
2023
|
}
|
|
1909
2024
|
const isContainer = elementTag === "e-flexbox" || elementTag === "e-div-block";
|
|
1910
|
-
const newElement = isContainer ?
|
|
2025
|
+
const newElement = isContainer ? createElement6({
|
|
1911
2026
|
containerId: containerElement.id,
|
|
1912
2027
|
model: {
|
|
1913
2028
|
elType: elementTag,
|
|
1914
2029
|
id: generateElementId()
|
|
1915
2030
|
},
|
|
1916
2031
|
options: { useHistory: false }
|
|
1917
|
-
}) :
|
|
2032
|
+
}) : createElement6({
|
|
1918
2033
|
containerId: containerElement.id,
|
|
1919
2034
|
model: {
|
|
1920
2035
|
elType: "widget",
|
|
@@ -2178,7 +2293,7 @@ Now that you have this information, ensure you have the schema and try again.`;
|
|
|
2178
2293
|
}
|
|
2179
2294
|
|
|
2180
2295
|
// src/mcp/tools/get-element-config/tool.ts
|
|
2181
|
-
import { getContainer as
|
|
2296
|
+
import { getContainer as getContainer4, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache5 } from "@elementor/editor-elements";
|
|
2182
2297
|
import { Schema as Schema3 } from "@elementor/editor-props";
|
|
2183
2298
|
import { z as z3 } from "@elementor/schema";
|
|
2184
2299
|
import { decodeString as decodeString2 } from "@elementor/utils";
|
|
@@ -2199,7 +2314,7 @@ var initGetElementConfigTool = (reg) => {
|
|
|
2199
2314
|
schema,
|
|
2200
2315
|
outputSchema: outputSchema3,
|
|
2201
2316
|
handler: async ({ elementId }) => {
|
|
2202
|
-
const element =
|
|
2317
|
+
const element = getContainer4(elementId);
|
|
2203
2318
|
if (!element) {
|
|
2204
2319
|
throw new Error(`Element with ID ${elementId} not found.`);
|
|
2205
2320
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-canvas",
|
|
3
3
|
"description": "Elementor Editor Canvas",
|
|
4
|
-
"version": "3.33.0-
|
|
4
|
+
"version": "3.33.0-295",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -37,23 +37,23 @@
|
|
|
37
37
|
"react-dom": "^18.3.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@elementor/editor": "3.33.0-
|
|
41
|
-
"@elementor/editor-controls": "3.33.0-
|
|
42
|
-
"@elementor/editor-documents": "3.33.0-
|
|
43
|
-
"@elementor/editor-elements": "3.33.0-
|
|
44
|
-
"@elementor/editor-interactions": "3.33.0-
|
|
45
|
-
"@elementor/editor-notifications": "3.33.0-
|
|
46
|
-
"@elementor/editor-props": "3.33.0-
|
|
47
|
-
"@elementor/editor-responsive": "3.33.0-
|
|
48
|
-
"@elementor/editor-styles": "3.33.0-
|
|
49
|
-
"@elementor/editor-styles-repository": "3.33.0-
|
|
50
|
-
"@elementor/editor-v1-adapters": "3.33.0-
|
|
51
|
-
"@elementor/editor-mcp": "3.33.0-
|
|
52
|
-
"@elementor/schema": "3.33.0-
|
|
53
|
-
"@elementor/twing": "3.33.0-
|
|
40
|
+
"@elementor/editor": "3.33.0-295",
|
|
41
|
+
"@elementor/editor-controls": "3.33.0-295",
|
|
42
|
+
"@elementor/editor-documents": "3.33.0-295",
|
|
43
|
+
"@elementor/editor-elements": "3.33.0-295",
|
|
44
|
+
"@elementor/editor-interactions": "3.33.0-295",
|
|
45
|
+
"@elementor/editor-notifications": "3.33.0-295",
|
|
46
|
+
"@elementor/editor-props": "3.33.0-295",
|
|
47
|
+
"@elementor/editor-responsive": "3.33.0-295",
|
|
48
|
+
"@elementor/editor-styles": "3.33.0-295",
|
|
49
|
+
"@elementor/editor-styles-repository": "3.33.0-295",
|
|
50
|
+
"@elementor/editor-v1-adapters": "3.33.0-295",
|
|
51
|
+
"@elementor/editor-mcp": "3.33.0-295",
|
|
52
|
+
"@elementor/schema": "3.33.0-295",
|
|
53
|
+
"@elementor/twing": "3.33.0-295",
|
|
54
54
|
"@elementor/ui": "1.36.17",
|
|
55
|
-
"@elementor/utils": "3.33.0-
|
|
56
|
-
"@elementor/wp-media": "3.33.0-
|
|
55
|
+
"@elementor/utils": "3.33.0-295",
|
|
56
|
+
"@elementor/wp-media": "3.33.0-295",
|
|
57
57
|
"@floating-ui/react": "^0.27.5",
|
|
58
58
|
"@wordpress/i18n": "^5.13.0"
|
|
59
59
|
},
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { createDOMElement, createMockElement, createMockElementType, renderWithTheme } from 'test-utils';
|
|
3
3
|
import { getElements, useSelectedElement } from '@elementor/editor-elements';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import {
|
|
5
|
+
__privateUseIsRouteActive as useIsRouteActive,
|
|
6
|
+
isExperimentActive,
|
|
7
|
+
useEditMode,
|
|
8
|
+
} from '@elementor/editor-v1-adapters';
|
|
9
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
10
|
+
|
|
11
|
+
import { hasInlineEditableProperty } from '../../utils/inline-editing-utils';
|
|
8
12
|
import { ElementsOverlays } from '../elements-overlays';
|
|
13
|
+
import { CANVAS_WRAPPER_ID } from '../outline-overlay';
|
|
9
14
|
|
|
10
15
|
jest.mock( '@elementor/editor-elements' );
|
|
11
16
|
jest.mock( '@elementor/editor-v1-adapters', () => ( {
|
|
12
17
|
...jest.requireActual( '@elementor/editor-v1-adapters' ),
|
|
13
18
|
useEditMode: jest.fn(),
|
|
14
19
|
__privateUseIsRouteActive: jest.fn(),
|
|
20
|
+
isExperimentActive: jest.fn(),
|
|
15
21
|
} ) );
|
|
22
|
+
jest.mock( '../../utils/inline-editing-utils' );
|
|
16
23
|
|
|
17
24
|
describe( '<ElementsOverlays />', () => {
|
|
18
25
|
beforeEach( () => {
|
|
@@ -23,6 +30,8 @@ describe( '<ElementsOverlays />', () => {
|
|
|
23
30
|
|
|
24
31
|
jest.mocked( useEditMode ).mockReturnValue( 'edit' );
|
|
25
32
|
jest.mocked( useIsRouteActive ).mockReturnValue( false );
|
|
33
|
+
jest.mocked( hasInlineEditableProperty ).mockReturnValue( false );
|
|
34
|
+
jest.mocked( isExperimentActive ).mockReturnValue( true );
|
|
26
35
|
|
|
27
36
|
jest.mocked( getElements ).mockReturnValue( [
|
|
28
37
|
createMockElement( {
|
|
@@ -68,16 +77,15 @@ describe( '<ElementsOverlays />', () => {
|
|
|
68
77
|
} );
|
|
69
78
|
|
|
70
79
|
// Act.
|
|
71
|
-
|
|
72
|
-
await act( () => renderWithTheme( <ElementsOverlays /> ) );
|
|
80
|
+
renderWithTheme( <ElementsOverlays /> );
|
|
73
81
|
|
|
74
82
|
// Assert.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
await waitFor( () => {
|
|
84
|
+
const overlay = screen.getByRole( 'presentation' );
|
|
85
|
+
expect( overlay ).toHaveAttribute( 'data-element-overlay', 'atomic2' );
|
|
86
|
+
// eslint-disable-next-line testing-library/no-test-id-queries
|
|
87
|
+
expect( screen.getByTestId( CANVAS_WRAPPER_ID ) ).toContainElement( overlay );
|
|
88
|
+
} );
|
|
81
89
|
} );
|
|
82
90
|
|
|
83
91
|
it.each( [
|
|
@@ -128,4 +136,80 @@ describe( '<ElementsOverlays />', () => {
|
|
|
128
136
|
// Assert.
|
|
129
137
|
expect( screen.queryByRole( 'presentation' ) ).not.toBeInTheDocument();
|
|
130
138
|
} );
|
|
139
|
+
|
|
140
|
+
it( 'should return null when editor is not in edit mode', () => {
|
|
141
|
+
// Arrange
|
|
142
|
+
jest.mocked( useEditMode ).mockReturnValue( 'preview' );
|
|
143
|
+
jest.mocked( useIsRouteActive ).mockReturnValue( false );
|
|
144
|
+
jest.mocked( useSelectedElement ).mockReturnValue( {
|
|
145
|
+
element: { id: 'atomic1', type: 'widget' },
|
|
146
|
+
elementType: createMockElementType(),
|
|
147
|
+
} );
|
|
148
|
+
|
|
149
|
+
// Act
|
|
150
|
+
const { container } = renderWithTheme( <ElementsOverlays /> );
|
|
151
|
+
|
|
152
|
+
// Assert
|
|
153
|
+
expect( container ).toBeEmptyDOMElement();
|
|
154
|
+
expect( screen.queryByRole( 'presentation' ) ).not.toBeInTheDocument();
|
|
155
|
+
} );
|
|
156
|
+
|
|
157
|
+
it( 'should return null when panel/global route is active', () => {
|
|
158
|
+
// Arrange
|
|
159
|
+
jest.mocked( useEditMode ).mockReturnValue( 'edit' );
|
|
160
|
+
jest.mocked( useIsRouteActive ).mockReturnValue( true );
|
|
161
|
+
jest.mocked( useSelectedElement ).mockReturnValue( {
|
|
162
|
+
element: { id: 'atomic1', type: 'widget' },
|
|
163
|
+
elementType: createMockElementType(),
|
|
164
|
+
} );
|
|
165
|
+
|
|
166
|
+
// Act
|
|
167
|
+
const { container } = renderWithTheme( <ElementsOverlays /> );
|
|
168
|
+
|
|
169
|
+
// Assert
|
|
170
|
+
expect( container ).toBeEmptyDOMElement();
|
|
171
|
+
expect( screen.queryByRole( 'presentation' ) ).not.toBeInTheDocument();
|
|
172
|
+
} );
|
|
173
|
+
|
|
174
|
+
it( 'should render OutlineOverlay for atomic elements', async () => {
|
|
175
|
+
// Arrange
|
|
176
|
+
jest.mocked( useSelectedElement ).mockReturnValue( {
|
|
177
|
+
element: { id: 'atomic1', type: 'widget' },
|
|
178
|
+
elementType: createMockElementType(),
|
|
179
|
+
} );
|
|
180
|
+
|
|
181
|
+
// Act
|
|
182
|
+
renderWithTheme( <ElementsOverlays /> );
|
|
183
|
+
|
|
184
|
+
// Assert
|
|
185
|
+
const overlay = await screen.findByRole( 'presentation' );
|
|
186
|
+
expect( overlay ).toBeInTheDocument();
|
|
187
|
+
expect( overlay ).toHaveAttribute( 'data-element-overlay', 'atomic1' );
|
|
188
|
+
} );
|
|
189
|
+
|
|
190
|
+
it( 'should render InlineEditorOverlay only for selected elements that support inline editing', async () => {
|
|
191
|
+
// Arrange
|
|
192
|
+
const headingEl = createDOMElement( { tag: 'div', attrs: { 'data-atomic': '', id: '50' } } );
|
|
193
|
+
|
|
194
|
+
jest.mocked( getElements ).mockReturnValue( [
|
|
195
|
+
createMockElement( {
|
|
196
|
+
model: { id: 'heading-element' },
|
|
197
|
+
view: { el: headingEl, getDomElement: () => ( { get: () => headingEl } ) },
|
|
198
|
+
} ),
|
|
199
|
+
] );
|
|
200
|
+
|
|
201
|
+
jest.mocked( useSelectedElement ).mockReturnValue( {
|
|
202
|
+
element: { id: 'heading-element', type: 'widget' },
|
|
203
|
+
elementType: createMockElementType(),
|
|
204
|
+
} );
|
|
205
|
+
|
|
206
|
+
// Act
|
|
207
|
+
renderWithTheme( <ElementsOverlays /> );
|
|
208
|
+
|
|
209
|
+
// Assert
|
|
210
|
+
await waitFor( () => {
|
|
211
|
+
const overlay = screen.getByRole( 'presentation' );
|
|
212
|
+
expect( overlay ).toHaveAttribute( 'data-element-overlay', 'heading-element' );
|
|
213
|
+
} );
|
|
214
|
+
} );
|
|
131
215
|
} );
|