@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.mjs CHANGED
@@ -46,56 +46,60 @@ var renameClass = (oldClassName, newClassName) => {
46
46
  };
47
47
 
48
48
  // src/components/elements-overlays.tsx
49
- import * as React2 from "react";
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/components/element-overlay.tsx
59
- import * as React from "react";
60
- import { Box, styled } from "@elementor/ui";
61
- import { FloatingPortal, useHover, useInteractions } from "@floating-ui/react";
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/hooks/use-bind-react-props-to-element.ts
64
- import { useEffect as useEffect2 } from "react";
65
- function useBindReactPropsToElement(element, getProps) {
66
- useEffect2(() => {
67
- const el = element;
68
- const { events, attrs } = groupProps(getProps());
69
- events.forEach(([eventName, listener]) => el.addEventListener(eventName, listener));
70
- attrs.forEach(([attrName, attrValue]) => el.setAttribute(attrName, attrValue));
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 useEffect3, useState } from "react";
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
- useEffect3(() => {
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/element-overlay.tsx
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
- function ElementOverlay({ element, isSelected, id }) {
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 === "" ? "&nbsp;" : 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
- return isActive && elements.map(([id, element]) => /* @__PURE__ */ React2.createElement(ElementOverlay, { key: id, id, element, isSelected: selected.element?.id === id }));
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 React3 from "react";
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 useEffect5, useMemo, useState as useState2 } from "react";
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 useEffect4, useRef } from "react";
331
+ import { useEffect as useEffect5, useRef as useRef2 } from "react";
216
332
  function useOnMount(cb) {
217
- const mounted = useRef(false);
218
- useEffect4(() => {
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 = useMemo(() => {
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
- useEffect5(() => {
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 useMemo(() => {
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__ */ React3.createElement(Portal, { container }, /* @__PURE__ */ React3.createElement(
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 React4 from "react";
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 useEffect6, useMemo as useMemo4, useState as useState3 } from "react";
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 useMemo2 } from "react";
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 useMemo2(() => {
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 useMemo3 } from "react";
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 useMemo3(() => {
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 = useMemo4(() => {
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
- useEffect6(() => {
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 useMemo4(
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__ */ React4.createElement(Portal2, { container }, styleItems.map((item, i) => /* @__PURE__ */ React4.createElement("style", { key: `${item.id}-${i}-${item.breakpoint}` }, item.value)), linksAttrs.map((attrs) => /* @__PURE__ */ React4.createElement("link", { ...attrs, key: attrs.id })));
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 createElement5,
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
- updateElementSettings({
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 = getContainer("document");
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 ? createElement5({
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
- }) : createElement5({
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 getContainer2, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache5 } from "@elementor/editor-elements";
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 = getContainer2(elementId);
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-293",
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-293",
41
- "@elementor/editor-controls": "3.33.0-293",
42
- "@elementor/editor-documents": "3.33.0-293",
43
- "@elementor/editor-elements": "3.33.0-293",
44
- "@elementor/editor-interactions": "3.33.0-293",
45
- "@elementor/editor-notifications": "3.33.0-293",
46
- "@elementor/editor-props": "3.33.0-293",
47
- "@elementor/editor-responsive": "3.33.0-293",
48
- "@elementor/editor-styles": "3.33.0-293",
49
- "@elementor/editor-styles-repository": "3.33.0-293",
50
- "@elementor/editor-v1-adapters": "3.33.0-293",
51
- "@elementor/editor-mcp": "3.33.0-293",
52
- "@elementor/schema": "3.33.0-293",
53
- "@elementor/twing": "3.33.0-293",
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-293",
56
- "@elementor/wp-media": "3.33.0-293",
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 { __privateUseIsRouteActive as useIsRouteActive, useEditMode } from '@elementor/editor-v1-adapters';
5
- import { act, screen } from '@testing-library/react';
6
-
7
- import { CANVAS_WRAPPER_ID } from '../element-overlay';
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
- // eslint-disable-next-line testing-library/no-unnecessary-act
72
- await act( () => renderWithTheme( <ElementsOverlays /> ) );
80
+ renderWithTheme( <ElementsOverlays /> );
73
81
 
74
82
  // Assert.
75
- const overlay = screen.getByRole( 'presentation' );
76
-
77
- expect( overlay ).toHaveAttribute( 'data-element-overlay', 'atomic2' );
78
-
79
- // eslint-disable-next-line testing-library/no-test-id-queries
80
- expect( screen.getByTestId( CANVAS_WRAPPER_ID ) ).toContainElement( overlay );
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
  } );