@elementor/editor-canvas 3.32.0-38 → 3.32.0-40

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 CHANGED
@@ -42,6 +42,48 @@ module.exports = __toCommonJS(index_exports);
42
42
  // src/init.tsx
43
43
  var import_editor = require("@elementor/editor");
44
44
 
45
+ // src/components/classes-rename.tsx
46
+ var import_react = require("react");
47
+ var import_editor_documents = require("@elementor/editor-documents");
48
+ var import_editor_styles_repository = require("@elementor/editor-styles-repository");
49
+ var import_utils = require("@elementor/utils");
50
+ var ClassesRename = () => {
51
+ (0, import_react.useEffect)(() => {
52
+ const unsubscribe = subscribeToStylesRepository();
53
+ return () => {
54
+ unsubscribe();
55
+ };
56
+ }, []);
57
+ return null;
58
+ };
59
+ var subscribeToStylesRepository = () => {
60
+ return import_editor_styles_repository.stylesRepository.subscribe((previous, current) => {
61
+ if (!previous || !current) {
62
+ return;
63
+ }
64
+ const currentIds = Object.keys(current);
65
+ currentIds.forEach((id) => {
66
+ const isStyleChanged = previous[id] && (0, import_utils.hash)(previous[id]) !== (0, import_utils.hash)(current[id]);
67
+ if (!isStyleChanged) {
68
+ return;
69
+ }
70
+ const previousStyle = previous[id];
71
+ const currentStyle = current[id];
72
+ if (previousStyle.label !== currentStyle.label) {
73
+ renameClass(previousStyle.label, currentStyle.label);
74
+ }
75
+ });
76
+ });
77
+ };
78
+ var renameClass = (oldClassName, newClassName) => {
79
+ Object.values((0, import_editor_documents.getV1DocumentsManager)().documents).forEach((document) => {
80
+ const container = document.container;
81
+ container.view?.el?.querySelectorAll(`.elementor .${oldClassName}`).forEach((element) => {
82
+ element.classList.replace(oldClassName, newClassName);
83
+ });
84
+ });
85
+ };
86
+
45
87
  // src/components/elements-overlays.tsx
46
88
  var React2 = __toESM(require("react"));
47
89
  var import_editor_elements = require("@elementor/editor-elements");
@@ -50,12 +92,12 @@ var import_editor_v1_adapters = require("@elementor/editor-v1-adapters");
50
92
  // src/components/element-overlay.tsx
51
93
  var React = __toESM(require("react"));
52
94
  var import_ui = require("@elementor/ui");
53
- var import_react4 = require("@floating-ui/react");
95
+ var import_react5 = require("@floating-ui/react");
54
96
 
55
97
  // src/hooks/use-bind-react-props-to-element.ts
56
- var import_react = require("react");
98
+ var import_react2 = require("react");
57
99
  function useBindReactPropsToElement(element, getProps) {
58
- (0, import_react.useEffect)(() => {
100
+ (0, import_react2.useEffect)(() => {
59
101
  const el = element;
60
102
  const { events, attrs } = groupProps(getProps());
61
103
  events.forEach(([eventName, listener]) => el.addEventListener(eventName, listener));
@@ -87,18 +129,18 @@ function groupProps(props) {
87
129
  }
88
130
 
89
131
  // src/hooks/use-floating-on-element.ts
90
- var import_react2 = require("react");
91
- var import_react3 = require("@floating-ui/react");
132
+ var import_react3 = require("react");
133
+ var import_react4 = require("@floating-ui/react");
92
134
  function useFloatingOnElement({ element, isSelected }) {
93
- const [isOpen, setIsOpen] = (0, import_react2.useState)(false);
94
- const { refs, floatingStyles, context } = (0, import_react3.useFloating)({
135
+ const [isOpen, setIsOpen] = (0, import_react3.useState)(false);
136
+ const { refs, floatingStyles, context } = (0, import_react4.useFloating)({
95
137
  // Must be controlled for interactions (like hover) to work.
96
138
  open: isOpen || isSelected,
97
139
  onOpenChange: setIsOpen,
98
- whileElementsMounted: import_react3.autoUpdate,
140
+ whileElementsMounted: import_react4.autoUpdate,
99
141
  middleware: [
100
142
  // Match the floating element's size to the reference element.
101
- (0, import_react3.size)({
143
+ (0, import_react4.size)({
102
144
  apply({ elements, rects }) {
103
145
  Object.assign(elements.floating.style, {
104
146
  width: `${rects.reference.width + 2}px`,
@@ -107,10 +149,10 @@ function useFloatingOnElement({ element, isSelected }) {
107
149
  }
108
150
  }),
109
151
  // Center the floating element on the reference element.
110
- (0, import_react3.offset)(({ rects }) => -rects.reference.height / 2 - rects.floating.height / 2)
152
+ (0, import_react4.offset)(({ rects }) => -rects.reference.height / 2 - rects.floating.height / 2)
111
153
  ]
112
154
  });
113
- (0, import_react2.useEffect)(() => {
155
+ (0, import_react3.useEffect)(() => {
114
156
  refs.setReference(element);
115
157
  }, [element, refs]);
116
158
  return {
@@ -135,10 +177,10 @@ var OverlayBox = (0, import_ui.styled)(import_ui.Box, {
135
177
  }));
136
178
  function ElementOverlay({ element, isSelected, id }) {
137
179
  const { context, floating, isVisible } = useFloatingOnElement({ element, isSelected });
138
- const { getFloatingProps, getReferenceProps } = (0, import_react4.useInteractions)([(0, import_react4.useHover)(context)]);
180
+ const { getFloatingProps, getReferenceProps } = (0, import_react5.useInteractions)([(0, import_react5.useHover)(context)]);
139
181
  useBindReactPropsToElement(element, getReferenceProps);
140
182
  const isSmallerOffset = element.offsetHeight <= 1;
141
- return isVisible && /* @__PURE__ */ React.createElement(import_react4.FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React.createElement(
183
+ return isVisible && /* @__PURE__ */ React.createElement(import_react5.FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React.createElement(
142
184
  OverlayBox,
143
185
  {
144
186
  ref: floating.setRef,
@@ -233,9 +275,9 @@ function getLinkAttrs(el) {
233
275
  }
234
276
 
235
277
  // src/hooks/use-style-items.ts
236
- var import_react8 = require("react");
278
+ var import_react9 = require("react");
237
279
  var import_editor_responsive2 = require("@elementor/editor-responsive");
238
- var import_editor_styles_repository = require("@elementor/editor-styles-repository");
280
+ var import_editor_styles_repository2 = require("@elementor/editor-styles-repository");
239
281
  var import_editor_v1_adapters4 = require("@elementor/editor-v1-adapters");
240
282
 
241
283
  // src/utils/abort-previous-runs.ts
@@ -270,10 +312,10 @@ function signalizedProcess(signal, steps = []) {
270
312
  }
271
313
 
272
314
  // src/hooks/use-on-mount.ts
273
- var import_react5 = require("react");
315
+ var import_react6 = require("react");
274
316
  function useOnMount(cb) {
275
- const mounted = (0, import_react5.useRef)(false);
276
- (0, import_react5.useEffect)(() => {
317
+ const mounted = (0, import_react6.useRef)(false);
318
+ (0, import_react6.useEffect)(() => {
277
319
  if (!mounted.current) {
278
320
  mounted.current = true;
279
321
  cb();
@@ -282,7 +324,7 @@ function useOnMount(cb) {
282
324
  }
283
325
 
284
326
  // src/hooks/use-style-prop-resolver.ts
285
- var import_react6 = require("react");
327
+ var import_react7 = require("react");
286
328
  var import_editor_styles = require("@elementor/editor-styles");
287
329
 
288
330
  // src/renderers/create-props-resolver.ts
@@ -404,7 +446,7 @@ var enqueueFont = (fontFamily, context = "preview") => {
404
446
 
405
447
  // src/hooks/use-style-prop-resolver.ts
406
448
  function useStylePropResolver() {
407
- return (0, import_react6.useMemo)(() => {
449
+ return (0, import_react7.useMemo)(() => {
408
450
  return createPropsResolver({
409
451
  transformers: styleTransformersRegistry,
410
452
  schema: (0, import_editor_styles.getStylesSchema)(),
@@ -419,16 +461,16 @@ function useStylePropResolver() {
419
461
  }
420
462
 
421
463
  // src/hooks/use-style-renderer.ts
422
- var import_react7 = require("react");
464
+ var import_react8 = require("react");
423
465
  var import_editor_responsive = require("@elementor/editor-responsive");
424
466
 
425
467
  // src/renderers/create-styles-renderer.ts
426
468
  var import_editor_v1_adapters3 = require("@elementor/editor-v1-adapters");
427
- var import_utils2 = require("@elementor/utils");
469
+ var import_utils3 = require("@elementor/utils");
428
470
 
429
471
  // src/renderers/errors.ts
430
- var import_utils = require("@elementor/utils");
431
- var UnknownStyleTypeError = (0, import_utils.createError)({
472
+ var import_utils2 = require("@elementor/utils");
473
+ var UnknownStyleTypeError = (0, import_utils2.createError)({
432
474
  code: "unknown_style_type",
433
475
  message: "Unknown style type"
434
476
  });
@@ -496,7 +538,7 @@ function customCssToString(customCss) {
496
538
  if (!(0, import_editor_v1_adapters3.isExperimentActive)(import_editor_v1_adapters3.EXPERIMENTAL_FEATURES.CUSTOM_CSS) || !customCss?.raw) {
497
539
  return "";
498
540
  }
499
- const decoded = (0, import_utils2.decodeString)(customCss.raw);
541
+ const decoded = (0, import_utils3.decodeString)(customCss.raw);
500
542
  if (!decoded.trim()) {
501
543
  return "";
502
544
  }
@@ -507,7 +549,7 @@ function customCssToString(customCss) {
507
549
  var SELECTOR_PREFIX = ".elementor";
508
550
  function useStyleRenderer(resolve) {
509
551
  const breakpoints = (0, import_editor_responsive.useBreakpointsMap)();
510
- return (0, import_react7.useMemo)(() => {
552
+ return (0, import_react8.useMemo)(() => {
511
553
  return createStylesRenderer({
512
554
  selectorPrefix: SELECTOR_PREFIX,
513
555
  breakpoints,
@@ -520,9 +562,9 @@ function useStyleRenderer(resolve) {
520
562
  function useStyleItems() {
521
563
  const resolve = useStylePropResolver();
522
564
  const renderStyles = useStyleRenderer(resolve);
523
- const [styleItems, setStyleItems] = (0, import_react8.useState)({});
524
- const providerAndSubscribers = (0, import_react8.useMemo)(() => {
525
- return import_editor_styles_repository.stylesRepository.getProviders().map((provider) => {
565
+ const [styleItems, setStyleItems] = (0, import_react9.useState)({});
566
+ const providerAndSubscribers = (0, import_react9.useMemo)(() => {
567
+ return import_editor_styles_repository2.stylesRepository.getProviders().map((provider) => {
526
568
  return {
527
569
  provider,
528
570
  subscriber: createProviderSubscriber({
@@ -533,7 +575,7 @@ function useStyleItems() {
533
575
  };
534
576
  });
535
577
  }, [renderStyles]);
536
- (0, import_react8.useEffect)(() => {
578
+ (0, import_react9.useEffect)(() => {
537
579
  const unsubscribes = providerAndSubscribers.map(
538
580
  ({ provider, subscriber }) => provider.subscribe(subscriber)
539
581
  );
@@ -548,7 +590,7 @@ function useStyleItems() {
548
590
  });
549
591
  });
550
592
  const breakpointsOrder = (0, import_editor_responsive2.getBreakpoints)().map((breakpoint) => breakpoint.id);
551
- return (0, import_react8.useMemo)(
593
+ return (0, import_react9.useMemo)(
552
594
  () => Object.values(styleItems).sort(({ provider: providerA }, { provider: providerB }) => providerA.priority - providerB.priority).flatMap(({ items }) => items).sort(({ breakpoint: breakpointA }, { breakpoint: breakpointB }) => {
553
595
  return breakpointsOrder.indexOf(breakpointA) - breakpointsOrder.indexOf(breakpointB);
554
596
  }),
@@ -645,7 +687,7 @@ var attributesTransformer = createTransformer((values) => {
645
687
  });
646
688
 
647
689
  // src/transformers/settings/classes-transformer.ts
648
- var import_editor_styles_repository2 = require("@elementor/editor-styles-repository");
690
+ var import_editor_styles_repository3 = require("@elementor/editor-styles-repository");
649
691
  function createClassesTransformer() {
650
692
  const cache = /* @__PURE__ */ new Map();
651
693
  return createTransformer((value) => {
@@ -658,7 +700,7 @@ function createClassesTransformer() {
658
700
  });
659
701
  }
660
702
  function getCssName(id) {
661
- const provider = import_editor_styles_repository2.stylesRepository.getProviders().find((p) => {
703
+ const provider = import_editor_styles_repository3.stylesRepository.getProviders().find((p) => {
662
704
  return p.actions.all().find((style) => style.id === id);
663
705
  });
664
706
  if (!provider) {
@@ -1310,7 +1352,7 @@ var import_editor_v1_adapters9 = require("@elementor/editor-v1-adapters");
1310
1352
 
1311
1353
  // src/style-commands/undoable-actions/paste-element-style.ts
1312
1354
  var import_editor_elements5 = require("@elementor/editor-elements");
1313
- var import_editor_styles_repository3 = require("@elementor/editor-styles-repository");
1355
+ var import_editor_styles_repository4 = require("@elementor/editor-styles-repository");
1314
1356
  var import_editor_v1_adapters8 = require("@elementor/editor-v1-adapters");
1315
1357
  var import_i18n3 = require("@wordpress/i18n");
1316
1358
 
@@ -1389,7 +1431,7 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters8.undoable)(
1389
1431
  revertData.styleId = (0, import_editor_elements5.createElementStyle)({
1390
1432
  elementId,
1391
1433
  classesProp,
1392
- label: import_editor_styles_repository3.ELEMENTS_STYLES_RESERVED_LABEL,
1434
+ label: import_editor_styles_repository4.ELEMENTS_STYLES_RESERVED_LABEL,
1393
1435
  ...firstVariant,
1394
1436
  additionalVariants
1395
1437
  });
@@ -1416,7 +1458,7 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters8.undoable)(
1416
1458
  (0, import_editor_elements5.createElementStyle)({
1417
1459
  elementId: container.id,
1418
1460
  classesProp,
1419
- label: import_editor_styles_repository3.ELEMENTS_STYLES_RESERVED_LABEL,
1461
+ label: import_editor_styles_repository4.ELEMENTS_STYLES_RESERVED_LABEL,
1420
1462
  styleId: revertData.styleId,
1421
1463
  ...firstVariant,
1422
1464
  additionalVariants
@@ -1466,7 +1508,7 @@ var import_editor_v1_adapters11 = require("@elementor/editor-v1-adapters");
1466
1508
 
1467
1509
  // src/style-commands/undoable-actions/reset-element-style.ts
1468
1510
  var import_editor_elements6 = require("@elementor/editor-elements");
1469
- var import_editor_styles_repository4 = require("@elementor/editor-styles-repository");
1511
+ var import_editor_styles_repository5 = require("@elementor/editor-styles-repository");
1470
1512
  var import_editor_v1_adapters10 = require("@elementor/editor-v1-adapters");
1471
1513
  var import_i18n4 = require("@wordpress/i18n");
1472
1514
  var undoableResetElementStyle = () => (0, import_editor_v1_adapters10.undoable)(
@@ -1496,7 +1538,7 @@ var undoableResetElementStyle = () => (0, import_editor_v1_adapters10.undoable)(
1496
1538
  elementId,
1497
1539
  classesProp,
1498
1540
  styleId,
1499
- label: import_editor_styles_repository4.ELEMENTS_STYLES_RESERVED_LABEL,
1541
+ label: import_editor_styles_repository5.ELEMENTS_STYLES_RESERVED_LABEL,
1500
1542
  ...firstVariant,
1501
1543
  additionalVariants
1502
1544
  });
@@ -1552,6 +1594,10 @@ function init() {
1552
1594
  id: "canvas-style-render",
1553
1595
  component: StyleRenderer
1554
1596
  });
1597
+ (0, import_editor.injectIntoLogic)({
1598
+ id: "classes-rename",
1599
+ component: ClassesRename
1600
+ });
1555
1601
  }
1556
1602
  // Annotate the CommonJS export names for ESM import in node:
1557
1603
  0 && (module.exports = {
package/dist/index.mjs CHANGED
@@ -1,5 +1,47 @@
1
1
  // src/init.tsx
2
- import { injectIntoTop } from "@elementor/editor";
2
+ import { injectIntoLogic, injectIntoTop } from "@elementor/editor";
3
+
4
+ // src/components/classes-rename.tsx
5
+ import { useEffect } from "react";
6
+ import { getV1DocumentsManager } from "@elementor/editor-documents";
7
+ import { stylesRepository } from "@elementor/editor-styles-repository";
8
+ import { hash } from "@elementor/utils";
9
+ var ClassesRename = () => {
10
+ useEffect(() => {
11
+ const unsubscribe = subscribeToStylesRepository();
12
+ return () => {
13
+ unsubscribe();
14
+ };
15
+ }, []);
16
+ return null;
17
+ };
18
+ var subscribeToStylesRepository = () => {
19
+ return stylesRepository.subscribe((previous, current) => {
20
+ if (!previous || !current) {
21
+ return;
22
+ }
23
+ const currentIds = Object.keys(current);
24
+ currentIds.forEach((id) => {
25
+ const isStyleChanged = previous[id] && hash(previous[id]) !== hash(current[id]);
26
+ if (!isStyleChanged) {
27
+ return;
28
+ }
29
+ const previousStyle = previous[id];
30
+ const currentStyle = current[id];
31
+ if (previousStyle.label !== currentStyle.label) {
32
+ renameClass(previousStyle.label, currentStyle.label);
33
+ }
34
+ });
35
+ });
36
+ };
37
+ var renameClass = (oldClassName, newClassName) => {
38
+ Object.values(getV1DocumentsManager().documents).forEach((document) => {
39
+ const container = document.container;
40
+ container.view?.el?.querySelectorAll(`.elementor .${oldClassName}`).forEach((element) => {
41
+ element.classList.replace(oldClassName, newClassName);
42
+ });
43
+ });
44
+ };
3
45
 
4
46
  // src/components/elements-overlays.tsx
5
47
  import * as React2 from "react";
@@ -17,9 +59,9 @@ import { Box, styled } from "@elementor/ui";
17
59
  import { FloatingPortal, useHover, useInteractions } from "@floating-ui/react";
18
60
 
19
61
  // src/hooks/use-bind-react-props-to-element.ts
20
- import { useEffect } from "react";
62
+ import { useEffect as useEffect2 } from "react";
21
63
  function useBindReactPropsToElement(element, getProps) {
22
- useEffect(() => {
64
+ useEffect2(() => {
23
65
  const el = element;
24
66
  const { events, attrs } = groupProps(getProps());
25
67
  events.forEach(([eventName, listener]) => el.addEventListener(eventName, listener));
@@ -51,7 +93,7 @@ function groupProps(props) {
51
93
  }
52
94
 
53
95
  // src/hooks/use-floating-on-element.ts
54
- import { useEffect as useEffect2, useState } from "react";
96
+ import { useEffect as useEffect3, useState } from "react";
55
97
  import { autoUpdate, offset, size, useFloating } from "@floating-ui/react";
56
98
  function useFloatingOnElement({ element, isSelected }) {
57
99
  const [isOpen, setIsOpen] = useState(false);
@@ -74,7 +116,7 @@ function useFloatingOnElement({ element, isSelected }) {
74
116
  offset(({ rects }) => -rects.reference.height / 2 - rects.floating.height / 2)
75
117
  ]
76
118
  });
77
- useEffect2(() => {
119
+ useEffect3(() => {
78
120
  refs.setReference(element);
79
121
  }, [element, refs]);
80
122
  return {
@@ -197,9 +239,9 @@ function getLinkAttrs(el) {
197
239
  }
198
240
 
199
241
  // src/hooks/use-style-items.ts
200
- import { useEffect as useEffect4, useMemo as useMemo3, useState as useState2 } from "react";
242
+ import { useEffect as useEffect5, useMemo as useMemo3, useState as useState2 } from "react";
201
243
  import { getBreakpoints } from "@elementor/editor-responsive";
202
- import { stylesRepository } from "@elementor/editor-styles-repository";
244
+ import { stylesRepository as stylesRepository2 } from "@elementor/editor-styles-repository";
203
245
  import { registerDataHook } from "@elementor/editor-v1-adapters";
204
246
 
205
247
  // src/utils/abort-previous-runs.ts
@@ -234,10 +276,10 @@ function signalizedProcess(signal, steps = []) {
234
276
  }
235
277
 
236
278
  // src/hooks/use-on-mount.ts
237
- import { useEffect as useEffect3, useRef } from "react";
279
+ import { useEffect as useEffect4, useRef } from "react";
238
280
  function useOnMount(cb) {
239
281
  const mounted = useRef(false);
240
- useEffect3(() => {
282
+ useEffect4(() => {
241
283
  if (!mounted.current) {
242
284
  mounted.current = true;
243
285
  cb();
@@ -488,7 +530,7 @@ function useStyleItems() {
488
530
  const renderStyles = useStyleRenderer(resolve);
489
531
  const [styleItems, setStyleItems] = useState2({});
490
532
  const providerAndSubscribers = useMemo3(() => {
491
- return stylesRepository.getProviders().map((provider) => {
533
+ return stylesRepository2.getProviders().map((provider) => {
492
534
  return {
493
535
  provider,
494
536
  subscriber: createProviderSubscriber({
@@ -499,7 +541,7 @@ function useStyleItems() {
499
541
  };
500
542
  });
501
543
  }, [renderStyles]);
502
- useEffect4(() => {
544
+ useEffect5(() => {
503
545
  const unsubscribes = providerAndSubscribers.map(
504
546
  ({ provider, subscriber }) => provider.subscribe(subscriber)
505
547
  );
@@ -611,7 +653,7 @@ var attributesTransformer = createTransformer((values) => {
611
653
  });
612
654
 
613
655
  // src/transformers/settings/classes-transformer.ts
614
- import { stylesRepository as stylesRepository2 } from "@elementor/editor-styles-repository";
656
+ import { stylesRepository as stylesRepository3 } from "@elementor/editor-styles-repository";
615
657
  function createClassesTransformer() {
616
658
  const cache = /* @__PURE__ */ new Map();
617
659
  return createTransformer((value) => {
@@ -624,7 +666,7 @@ function createClassesTransformer() {
624
666
  });
625
667
  }
626
668
  function getCssName(id) {
627
- const provider = stylesRepository2.getProviders().find((p) => {
669
+ const provider = stylesRepository3.getProviders().find((p) => {
628
670
  return p.actions.all().find((style) => style.id === id);
629
671
  });
630
672
  if (!provider) {
@@ -1535,6 +1577,10 @@ function init() {
1535
1577
  id: "canvas-style-render",
1536
1578
  component: StyleRenderer
1537
1579
  });
1580
+ injectIntoLogic({
1581
+ id: "classes-rename",
1582
+ component: ClassesRename
1583
+ });
1538
1584
  }
1539
1585
  export {
1540
1586
  createPropsResolver,
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.32.0-38",
4
+ "version": "3.32.0-40",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,18 +37,19 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "3.32.0-38",
41
- "@elementor/editor-notifications": "3.32.0-38",
42
- "@elementor/editor-elements": "3.32.0-38",
43
- "@elementor/editor-props": "3.32.0-38",
44
- "@elementor/editor-responsive": "3.32.0-38",
45
- "@elementor/editor-styles": "3.32.0-38",
46
- "@elementor/editor-styles-repository": "3.32.0-38",
47
- "@elementor/editor-v1-adapters": "3.32.0-38",
48
- "@elementor/twing": "3.32.0-38",
40
+ "@elementor/editor": "3.32.0-40",
41
+ "@elementor/editor-notifications": "3.32.0-40",
42
+ "@elementor/editor-documents": "3.32.0-40",
43
+ "@elementor/editor-elements": "3.32.0-40",
44
+ "@elementor/editor-props": "3.32.0-40",
45
+ "@elementor/editor-responsive": "3.32.0-40",
46
+ "@elementor/editor-styles": "3.32.0-40",
47
+ "@elementor/editor-styles-repository": "3.32.0-40",
48
+ "@elementor/editor-v1-adapters": "3.32.0-40",
49
+ "@elementor/twing": "3.32.0-40",
49
50
  "@elementor/ui": "1.36.8",
50
- "@elementor/utils": "3.32.0-38",
51
- "@elementor/wp-media": "3.32.0-38",
51
+ "@elementor/utils": "3.32.0-40",
52
+ "@elementor/wp-media": "3.32.0-40",
52
53
  "@floating-ui/react": "^0.27.5",
53
54
  "@wordpress/i18n": "^5.13.0"
54
55
  },
@@ -0,0 +1,311 @@
1
+ import * as React from 'react';
2
+ import { getV1DocumentsManager } from '@elementor/editor-documents';
3
+ import { type StyleDefinition } from '@elementor/editor-styles';
4
+ import { render } from '@testing-library/react';
5
+
6
+ import { ClassesRename } from '../classes-rename';
7
+
8
+ jest.mock( '@elementor/editor-documents', () => ( {
9
+ getV1DocumentsManager: jest.fn(),
10
+ } ) );
11
+
12
+ jest.mock( '@elementor/editor-styles-repository', () => ( {
13
+ stylesRepository: {
14
+ subscribe: jest.fn(),
15
+ register: jest.fn(),
16
+ all: jest.fn(),
17
+ },
18
+ createStylesProvider: jest.fn(),
19
+ } ) );
20
+
21
+ const createMockDocument = () => {
22
+ const mockElement = {
23
+ classList: {
24
+ replace: jest.fn(),
25
+ },
26
+ };
27
+
28
+ const mockQuerySelectorAll = jest.fn().mockReturnValue( [ mockElement ] );
29
+
30
+ const mockContainer = {
31
+ view: {
32
+ el: {
33
+ querySelectorAll: mockQuerySelectorAll,
34
+ },
35
+ },
36
+ };
37
+
38
+ const mockDocument = {
39
+ container: mockContainer,
40
+ };
41
+
42
+ return { mockDocument, mockContainer, mockElement };
43
+ };
44
+
45
+ interface StylesMap {
46
+ [ key: string ]: StyleDefinition;
47
+ }
48
+
49
+ describe( 'ClassesRename', () => {
50
+ const mockGetV1DocumentsManager = jest.mocked( getV1DocumentsManager );
51
+ const { stylesRepository } = jest.requireMock( '@elementor/editor-styles-repository' );
52
+ const mockSubscribe = jest.mocked( stylesRepository.subscribe ).mockReturnValue( jest.fn() );
53
+
54
+ const triggerStylesChange = ( previousStyles: StylesMap, currentStyles: StylesMap ) => {
55
+ const subscriptionCallback = mockSubscribe.mock.calls[ mockSubscribe.mock.calls.length - 1 ][ 0 ];
56
+ subscriptionCallback( previousStyles as never, currentStyles as never );
57
+ };
58
+
59
+ beforeEach( () => {
60
+ jest.clearAllMocks();
61
+ } );
62
+
63
+ it( 'should not rename classes when no classes have been changed', () => {
64
+ // Arrange.
65
+ const { mockElement } = createMockDocument();
66
+ mockGetV1DocumentsManager.mockReturnValue( {
67
+ documents: {},
68
+ } as never );
69
+
70
+ const previousStyles: StylesMap = {
71
+ style1: { id: 'style1', label: 'old-class', type: 'class', variants: [] },
72
+ };
73
+
74
+ const currentStyles: StylesMap = {
75
+ style1: { id: 'style1', label: 'old-class', type: 'class', variants: [] },
76
+ };
77
+
78
+ // Act.
79
+ render( <ClassesRename /> );
80
+ triggerStylesChange( previousStyles, currentStyles );
81
+
82
+ // Assert.
83
+ expect( mockElement.classList.replace ).not.toHaveBeenCalled();
84
+ } );
85
+
86
+ it( 'should rename classes when label changes', () => {
87
+ // Arrange.
88
+ const { mockDocument, mockElement } = createMockDocument();
89
+ mockGetV1DocumentsManager.mockReturnValue( {
90
+ documents: {
91
+ 1: mockDocument,
92
+ },
93
+ } as never );
94
+
95
+ const previousStyles: StylesMap = {
96
+ style1: {
97
+ id: 'style1',
98
+ label: 'old-class',
99
+ type: 'class',
100
+ variants: [],
101
+ },
102
+ };
103
+
104
+ const currentStyles: StylesMap = {
105
+ style1: { id: 'style1', label: 'new-class', type: 'class', variants: [] },
106
+ };
107
+
108
+ // Act.
109
+ render( <ClassesRename /> );
110
+ triggerStylesChange( previousStyles, currentStyles );
111
+
112
+ // Assert.
113
+ expect( mockElement.classList.replace ).toHaveBeenCalledWith( 'old-class', 'new-class' );
114
+ } );
115
+
116
+ it( 'should not rename classes when only non-label properties change', () => {
117
+ // Arrange.
118
+ const { mockDocument, mockElement } = createMockDocument();
119
+ mockGetV1DocumentsManager.mockReturnValue( {
120
+ documents: {
121
+ 1: mockDocument,
122
+ },
123
+ } as never );
124
+
125
+ const previousStyles: StylesMap = {
126
+ style1: {
127
+ id: 'style1',
128
+ label: 'same-class',
129
+ type: 'class',
130
+ variants: [ { meta: { breakpoint: null, state: null }, props: {}, custom_css: null } ],
131
+ },
132
+ };
133
+
134
+ const currentStyles: StylesMap = {
135
+ style1: { id: 'style1', label: 'same-class', type: 'class', variants: [] },
136
+ };
137
+
138
+ // Act.
139
+ render( <ClassesRename /> );
140
+ triggerStylesChange( previousStyles, currentStyles );
141
+
142
+ // Assert.
143
+ expect( mockElement.classList.replace ).not.toHaveBeenCalled();
144
+ } );
145
+
146
+ it( 'should handle multiple style changes', () => {
147
+ // Arrange.
148
+ const { mockDocument, mockElement } = createMockDocument();
149
+ mockGetV1DocumentsManager.mockReturnValue( {
150
+ documents: {
151
+ 1: mockDocument,
152
+ },
153
+ } as never );
154
+
155
+ const previousStyles: StylesMap = {
156
+ style1: { id: 'style1', label: 'old-class-1', type: 'class', variants: [] },
157
+ style2: { id: 'style2', label: 'old-class-2', type: 'class', variants: [] },
158
+ style3: { id: 'style3', label: 'unchanged-class', type: 'class', variants: [] },
159
+ };
160
+
161
+ const currentStyles: StylesMap = {
162
+ style1: { id: 'style1', label: 'new-class-1', type: 'class', variants: [] },
163
+ style2: { id: 'style2', label: 'new-class-2', type: 'class', variants: [] },
164
+ style3: { id: 'style3', label: 'unchanged-class', type: 'class', variants: [] },
165
+ };
166
+
167
+ // Act.
168
+ render( <ClassesRename /> );
169
+ triggerStylesChange( previousStyles, currentStyles );
170
+
171
+ // Assert.
172
+ expect( mockElement.classList.replace ).toHaveBeenCalledTimes( 2 );
173
+ expect( mockElement.classList.replace ).toHaveBeenCalledWith( 'old-class-1', 'new-class-1' );
174
+ expect( mockElement.classList.replace ).toHaveBeenCalledWith( 'old-class-2', 'new-class-2' );
175
+ } );
176
+
177
+ it( 'should rename classes when new label is an empty string', () => {
178
+ // Arrange.
179
+ const { mockDocument, mockElement } = createMockDocument();
180
+ mockGetV1DocumentsManager.mockReturnValue( {
181
+ documents: {
182
+ 1: mockDocument,
183
+ },
184
+ } as never );
185
+
186
+ const previousStyles: StylesMap = {
187
+ style1: { id: 'style1', label: 'old-class', type: 'class', variants: [] },
188
+ };
189
+
190
+ const currentStyles: StylesMap = {
191
+ style1: { id: 'style1', label: '', type: 'class', variants: [] },
192
+ };
193
+
194
+ // Act.
195
+ render( <ClassesRename /> );
196
+ triggerStylesChange( previousStyles, currentStyles );
197
+
198
+ // Assert.
199
+ expect( mockElement.classList.replace ).toHaveBeenCalledWith( 'old-class', '' );
200
+ } );
201
+
202
+ it( 'should handle new styles (not in previous)', () => {
203
+ // Arrange.
204
+ const { mockDocument, mockElement } = createMockDocument();
205
+ mockGetV1DocumentsManager.mockReturnValue( {
206
+ documents: {
207
+ 1: mockDocument,
208
+ },
209
+ } as never );
210
+
211
+ const previousStyles: StylesMap = {
212
+ style1: { id: 'style1', label: 'existing-class', type: 'class', variants: [] },
213
+ };
214
+
215
+ const currentStyles: StylesMap = {
216
+ style1: { id: 'style1', label: 'existing-class', type: 'class', variants: [] },
217
+ style2: { id: 'style2', label: 'new-class', type: 'class', variants: [] },
218
+ };
219
+
220
+ // Act.
221
+ render( <ClassesRename /> );
222
+ triggerStylesChange( previousStyles, currentStyles );
223
+
224
+ // Assert.
225
+ expect( mockElement.classList.replace ).not.toHaveBeenCalled();
226
+ } );
227
+
228
+ it( 'should handle multiple documents', () => {
229
+ // Arrange.
230
+ const { mockDocument, mockElement } = createMockDocument();
231
+ const { mockDocument: mockDocument2, mockElement: mockElement2 } = createMockDocument();
232
+
233
+ mockGetV1DocumentsManager.mockReturnValue( {
234
+ documents: {
235
+ 1: mockDocument,
236
+ 2: mockDocument2,
237
+ },
238
+ } as never );
239
+
240
+ const previousStyles: StylesMap = {
241
+ style1: { id: 'style1', label: 'old-class', type: 'class', variants: [] },
242
+ };
243
+
244
+ const currentStyles: StylesMap = {
245
+ style1: { id: 'style1', label: 'new-class', type: 'class', variants: [] },
246
+ };
247
+
248
+ // Act.
249
+ render( <ClassesRename /> );
250
+ triggerStylesChange( previousStyles, currentStyles );
251
+
252
+ // Assert.
253
+ expect( mockElement.classList.replace ).toHaveBeenCalledWith( 'old-class', 'new-class' );
254
+ expect( mockElement2.classList.replace ).toHaveBeenCalledWith( 'old-class', 'new-class' );
255
+ } );
256
+
257
+ it( 'should handle documents without view or el', () => {
258
+ // Arrange.
259
+ const incompleteDocument = {
260
+ container: {
261
+ view: null,
262
+ },
263
+ };
264
+
265
+ mockGetV1DocumentsManager.mockReturnValue( {
266
+ documents: {
267
+ 1: incompleteDocument as never,
268
+ },
269
+ } as never );
270
+
271
+ const previousStyles: StylesMap = {
272
+ style1: { id: 'style1', label: 'old-class', type: 'class', variants: [] },
273
+ };
274
+
275
+ const currentStyles: StylesMap = {
276
+ style1: { id: 'style1', label: 'new-class', type: 'class', variants: [] },
277
+ };
278
+
279
+ // Act & Assert - should not throw an error.
280
+ expect( () => {
281
+ render( <ClassesRename /> );
282
+ triggerStylesChange( previousStyles, currentStyles );
283
+ } ).not.toThrow();
284
+ } );
285
+
286
+ it( 'should use correct CSS selector format', () => {
287
+ // Arrange.
288
+ const { mockDocument, mockContainer } = createMockDocument();
289
+ mockGetV1DocumentsManager.mockReturnValue( {
290
+ documents: {
291
+ 1: mockDocument,
292
+ },
293
+ } as never );
294
+
295
+ const previousStyles: StylesMap = {
296
+ style1: { id: 'style1', label: 'test-class', type: 'class', variants: [] },
297
+ };
298
+
299
+ const currentStyles: StylesMap = {
300
+ style1: { id: 'style1', label: 'new-test-class', type: 'class', variants: [] },
301
+ };
302
+
303
+ // Act.
304
+ render( <ClassesRename /> );
305
+ triggerStylesChange( previousStyles, currentStyles );
306
+
307
+ // Assert.
308
+ // eslint-disable-next-line testing-library/no-node-access
309
+ expect( mockContainer.view.el.querySelectorAll ).toHaveBeenCalledWith( '.elementor .test-class' );
310
+ } );
311
+ } );
@@ -0,0 +1,51 @@
1
+ import { useEffect } from 'react';
2
+ import { getV1DocumentsManager, type V1Document } from '@elementor/editor-documents';
3
+ import { type V1Element } from '@elementor/editor-elements';
4
+ import { stylesRepository } from '@elementor/editor-styles-repository';
5
+ import { hash } from '@elementor/utils';
6
+
7
+ export const ClassesRename = () => {
8
+ useEffect( () => {
9
+ const unsubscribe = subscribeToStylesRepository();
10
+
11
+ return () => {
12
+ unsubscribe();
13
+ };
14
+ }, [] );
15
+
16
+ return null;
17
+ };
18
+
19
+ const subscribeToStylesRepository = () => {
20
+ return stylesRepository.subscribe( ( previous, current ) => {
21
+ if ( ! previous || ! current ) {
22
+ return;
23
+ }
24
+ const currentIds = Object.keys( current );
25
+
26
+ currentIds.forEach( ( id ) => {
27
+ const isStyleChanged = previous[ id ] && hash( previous[ id ] ) !== hash( current[ id ] );
28
+
29
+ if ( ! isStyleChanged ) {
30
+ return;
31
+ }
32
+
33
+ const previousStyle = previous[ id ];
34
+ const currentStyle = current[ id ];
35
+
36
+ if ( previousStyle.label !== currentStyle.label ) {
37
+ renameClass( previousStyle.label, currentStyle.label );
38
+ }
39
+ } );
40
+ } );
41
+ };
42
+
43
+ const renameClass = ( oldClassName: string, newClassName: string ) => {
44
+ Object.values< V1Document >( getV1DocumentsManager().documents ).forEach( ( document ) => {
45
+ const container = document.container as V1Element;
46
+
47
+ container.view?.el?.querySelectorAll( `.elementor .${ oldClassName }` ).forEach( ( element ) => {
48
+ element.classList.replace( oldClassName, newClassName );
49
+ } );
50
+ } );
51
+ };
package/src/init.tsx CHANGED
@@ -1,5 +1,6 @@
1
- import { injectIntoTop } from '@elementor/editor';
1
+ import { injectIntoLogic, injectIntoTop } from '@elementor/editor';
2
2
 
3
+ import { ClassesRename } from './components/classes-rename';
3
4
  import { ElementsOverlays } from './components/elements-overlays';
4
5
  import { StyleRenderer } from './components/style-renderer';
5
6
  import { initSettingsTransformers } from './init-settings-transformers';
@@ -27,4 +28,9 @@ export function init() {
27
28
  id: 'canvas-style-render',
28
29
  component: StyleRenderer,
29
30
  } );
31
+
32
+ injectIntoLogic( {
33
+ id: 'classes-rename',
34
+ component: ClassesRename,
35
+ } );
30
36
  }