@elementor/editor-canvas 3.33.0-294 → 3.33.0-296
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 -138
- package/dist/index.mjs +189 -73
- 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/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,7 +816,7 @@ 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
821
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
706
822
|
[styleItems, breakpointsOrder.join("-")]
|
|
@@ -773,7 +889,7 @@ function StyleRenderer() {
|
|
|
773
889
|
if (!container) {
|
|
774
890
|
return null;
|
|
775
891
|
}
|
|
776
|
-
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 })));
|
|
777
893
|
}
|
|
778
894
|
function usePortalContainer2() {
|
|
779
895
|
return useListenTo4(commandEndEvent3("editor/documents/attach-preview"), () => getCanvasIframeDocument()?.head);
|
|
@@ -1620,10 +1736,10 @@ function cleanupPropType(propType) {
|
|
|
1620
1736
|
|
|
1621
1737
|
// src/mcp/tools/build-composition/tool.ts
|
|
1622
1738
|
import {
|
|
1623
|
-
createElement as
|
|
1739
|
+
createElement as createElement6,
|
|
1624
1740
|
deleteElement,
|
|
1625
1741
|
generateElementId,
|
|
1626
|
-
getContainer,
|
|
1742
|
+
getContainer as getContainer3,
|
|
1627
1743
|
getWidgetsCache as getWidgetsCache4
|
|
1628
1744
|
} from "@elementor/editor-elements";
|
|
1629
1745
|
|
|
@@ -1632,7 +1748,7 @@ import {
|
|
|
1632
1748
|
createElementStyle,
|
|
1633
1749
|
getElementStyles,
|
|
1634
1750
|
getWidgetsCache as getWidgetsCache3,
|
|
1635
|
-
updateElementSettings,
|
|
1751
|
+
updateElementSettings as updateElementSettings2,
|
|
1636
1752
|
updateElementStyle
|
|
1637
1753
|
} from "@elementor/editor-elements";
|
|
1638
1754
|
import {
|
|
@@ -1733,7 +1849,7 @@ var doUpdateElementProperty = (params) => {
|
|
|
1733
1849
|
);
|
|
1734
1850
|
}
|
|
1735
1851
|
const value = resolvePropValue(propertyValue);
|
|
1736
|
-
|
|
1852
|
+
updateElementSettings2({
|
|
1737
1853
|
id: elementId,
|
|
1738
1854
|
props: {
|
|
1739
1855
|
[propertyName]: value
|
|
@@ -1891,7 +2007,7 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
1891
2007
|
const softErrors = [];
|
|
1892
2008
|
const rootContainers = [];
|
|
1893
2009
|
const widgetsCache = getWidgetsCache4() || {};
|
|
1894
|
-
const documentContainer =
|
|
2010
|
+
const documentContainer = getContainer3("document");
|
|
1895
2011
|
try {
|
|
1896
2012
|
const parser = new DOMParser();
|
|
1897
2013
|
xml = parser.parseFromString(xmlStructure, "application/xml");
|
|
@@ -1906,14 +2022,14 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
1906
2022
|
errors.push(new Error(`Unknown widget type: ${elementTag}`));
|
|
1907
2023
|
}
|
|
1908
2024
|
const isContainer = elementTag === "e-flexbox" || elementTag === "e-div-block";
|
|
1909
|
-
const newElement = isContainer ?
|
|
2025
|
+
const newElement = isContainer ? createElement6({
|
|
1910
2026
|
containerId: containerElement.id,
|
|
1911
2027
|
model: {
|
|
1912
2028
|
elType: elementTag,
|
|
1913
2029
|
id: generateElementId()
|
|
1914
2030
|
},
|
|
1915
2031
|
options: { useHistory: false }
|
|
1916
|
-
}) :
|
|
2032
|
+
}) : createElement6({
|
|
1917
2033
|
containerId: containerElement.id,
|
|
1918
2034
|
model: {
|
|
1919
2035
|
elType: "widget",
|
|
@@ -2177,7 +2293,7 @@ Now that you have this information, ensure you have the schema and try again.`;
|
|
|
2177
2293
|
}
|
|
2178
2294
|
|
|
2179
2295
|
// src/mcp/tools/get-element-config/tool.ts
|
|
2180
|
-
import { getContainer as
|
|
2296
|
+
import { getContainer as getContainer4, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache5 } from "@elementor/editor-elements";
|
|
2181
2297
|
import { Schema as Schema3 } from "@elementor/editor-props";
|
|
2182
2298
|
import { z as z3 } from "@elementor/schema";
|
|
2183
2299
|
import { decodeString as decodeString2 } from "@elementor/utils";
|
|
@@ -2198,7 +2314,7 @@ var initGetElementConfigTool = (reg) => {
|
|
|
2198
2314
|
schema,
|
|
2199
2315
|
outputSchema: outputSchema3,
|
|
2200
2316
|
handler: async ({ elementId }) => {
|
|
2201
|
-
const element =
|
|
2317
|
+
const element = getContainer4(elementId);
|
|
2202
2318
|
if (!element) {
|
|
2203
2319
|
throw new Error(`Element with ID ${elementId} not found.`);
|
|
2204
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-296",
|
|
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-296",
|
|
41
|
+
"@elementor/editor-controls": "3.33.0-296",
|
|
42
|
+
"@elementor/editor-documents": "3.33.0-296",
|
|
43
|
+
"@elementor/editor-elements": "3.33.0-296",
|
|
44
|
+
"@elementor/editor-interactions": "3.33.0-296",
|
|
45
|
+
"@elementor/editor-notifications": "3.33.0-296",
|
|
46
|
+
"@elementor/editor-props": "3.33.0-296",
|
|
47
|
+
"@elementor/editor-responsive": "3.33.0-296",
|
|
48
|
+
"@elementor/editor-styles": "3.33.0-296",
|
|
49
|
+
"@elementor/editor-styles-repository": "3.33.0-296",
|
|
50
|
+
"@elementor/editor-v1-adapters": "3.33.0-296",
|
|
51
|
+
"@elementor/editor-mcp": "3.33.0-296",
|
|
52
|
+
"@elementor/schema": "3.33.0-296",
|
|
53
|
+
"@elementor/twing": "3.33.0-296",
|
|
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-296",
|
|
56
|
+
"@elementor/wp-media": "3.33.0-296",
|
|
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
|
} );
|