@elementor/editor-components 3.33.0-200 → 3.33.0-202

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
@@ -10,76 +10,7 @@ import { __ as __6 } from "@wordpress/i18n";
10
10
 
11
11
  // src/component-id-transformer.ts
12
12
  import { createTransformer } from "@elementor/editor-canvas";
13
- var componentIdTransformer = createTransformer(async (id) => {
14
- const extendedWindow = window;
15
- const documentManager = extendedWindow.elementor?.documents;
16
- if (!documentManager) {
17
- throw new Error("Elementor documents manager not found");
18
- }
19
- const data = await documentManager.request(id);
20
- return data.elements ?? [];
21
- });
22
-
23
- // src/components/components-tab/components.tsx
24
- import * as React6 from "react";
25
- import { ThemeProvider } from "@elementor/editor-ui";
26
-
27
- // src/components/components-tab/component-search.tsx
28
- import * as React2 from "react";
29
- import { SearchIcon } from "@elementor/icons";
30
- import { Box, InputAdornment, Stack, TextField } from "@elementor/ui";
31
- import { __ } from "@wordpress/i18n";
32
-
33
- // src/components/components-tab/search-provider.tsx
34
- import * as React from "react";
35
- import { createContext, useContext } from "react";
36
- import { useSearchState } from "@elementor/utils";
37
- var SearchContext = createContext(void 0);
38
- var SearchProvider = ({
39
- children,
40
- localStorageKey
41
- }) => {
42
- const { debouncedValue, handleChange, inputValue } = useSearchState({ localStorageKey });
43
- const clearSearch = () => {
44
- handleChange("");
45
- };
46
- return /* @__PURE__ */ React.createElement(SearchContext.Provider, { value: { handleChange, clearSearch, searchValue: debouncedValue, inputValue } }, children);
47
- };
48
- var useSearch = () => {
49
- const context = useContext(SearchContext);
50
- if (!context) {
51
- throw new Error("useSearch must be used within a SearchProvider");
52
- }
53
- return context;
54
- };
55
-
56
- // src/components/components-tab/component-search.tsx
57
- var ComponentSearch = () => {
58
- const { inputValue, handleChange } = useSearch();
59
- return /* @__PURE__ */ React2.createElement(Stack, { direction: "row", gap: 0.5, sx: { width: "100%", px: 2, py: 1.5 } }, /* @__PURE__ */ React2.createElement(Box, { sx: { flexGrow: 1 } }, /* @__PURE__ */ React2.createElement(
60
- TextField,
61
- {
62
- role: "search",
63
- fullWidth: true,
64
- size: "tiny",
65
- value: inputValue,
66
- placeholder: __("Search", "elementor"),
67
- onChange: (e) => handleChange(e.target.value),
68
- InputProps: {
69
- startAdornment: /* @__PURE__ */ React2.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React2.createElement(SearchIcon, { fontSize: "tiny" }))
70
- }
71
- }
72
- )));
73
- };
74
-
75
- // src/components/components-tab/components-list.tsx
76
- import * as React5 from "react";
77
- import { ComponentsIcon as ComponentsIcon2, EyeIcon } from "@elementor/icons";
78
- import { Box as Box4, Divider, Icon, Link, List, Stack as Stack3, Typography as Typography2 } from "@elementor/ui";
79
- import { __ as __2 } from "@wordpress/i18n";
80
-
81
- // src/hooks/use-components.ts
82
- import { __useSelector as useSelector } from "@elementor/store";
13
+ import { __getState as getState } from "@elementor/store";
83
14
 
84
15
  // src/store/store.ts
85
16
  import {
@@ -122,6 +53,7 @@ var loadComponents = createAsyncThunk("components/load", async () => {
122
53
  // src/store/store.ts
123
54
  var initialState = {
124
55
  data: [],
56
+ unpublishedData: [],
125
57
  loadStatus: "idle",
126
58
  createStatus: "idle",
127
59
  styles: {}
@@ -132,11 +64,21 @@ var slice = createSlice({
132
64
  initialState,
133
65
  reducers: {
134
66
  add: (state, { payload }) => {
135
- state.data = { ...payload };
67
+ if (Array.isArray(payload)) {
68
+ state.data = [...state.data, ...payload];
69
+ } else {
70
+ state.data.unshift(payload);
71
+ }
136
72
  },
137
73
  load: (state, { payload }) => {
138
74
  state.data = payload;
139
75
  },
76
+ addUnpublished: (state, { payload }) => {
77
+ state.unpublishedData.unshift(payload);
78
+ },
79
+ resetUnpublished: (state) => {
80
+ state.unpublishedData = [];
81
+ },
140
82
  removeStyles(state, { payload }) {
141
83
  const { [payload.id]: _, ...rest } = state.styles;
142
84
  state.styles = rest;
@@ -175,7 +117,19 @@ var selectData = (state) => state[SLICE_NAME].data;
175
117
  var selectLoadStatus = (state) => state[SLICE_NAME].loadStatus;
176
118
  var selectCreateStatus = (state) => state[SLICE_NAME].createStatus;
177
119
  var selectStylesDefinitions = (state) => state[SLICE_NAME].styles ?? {};
178
- var selectComponents = createSelector(selectData, (data) => data);
120
+ var selectUnpublishedData = (state) => state[SLICE_NAME].unpublishedData;
121
+ var selectComponents = createSelector(
122
+ selectData,
123
+ selectUnpublishedData,
124
+ (data, unpublishedData) => [
125
+ ...unpublishedData.map((item) => ({ id: item.id, name: item.name })),
126
+ ...data
127
+ ]
128
+ );
129
+ var selectUnpublishedComponents = createSelector(
130
+ selectUnpublishedData,
131
+ (unpublishedData) => unpublishedData
132
+ );
179
133
  var selectLoadIsPending = createSelector(selectLoadStatus, (status) => status === "pending");
180
134
  var selectLoadIsError = createSelector(selectLoadStatus, (status) => status === "error");
181
135
  var selectCreateIsPending = createSelector(selectCreateStatus, (status) => status === "pending");
@@ -183,7 +137,82 @@ var selectCreateIsError = createSelector(selectCreateStatus, (status) => status
183
137
  var selectStyles = (state) => state[SLICE_NAME].styles ?? {};
184
138
  var selectFlatStyles = createSelector(selectStylesDefinitions, (data) => Object.values(data).flat());
185
139
 
140
+ // src/component-id-transformer.ts
141
+ var componentIdTransformer = createTransformer(async (id) => {
142
+ const unpublishedComponents = selectUnpublishedComponents(getState());
143
+ const unpublishedComponent = unpublishedComponents.find((component) => component.id === id);
144
+ if (unpublishedComponent) {
145
+ return structuredClone(unpublishedComponent.content);
146
+ }
147
+ const extendedWindow = window;
148
+ const documentManager = extendedWindow.elementor?.documents;
149
+ if (!documentManager) {
150
+ throw new Error("Elementor documents manager not found");
151
+ }
152
+ const data = await documentManager.request(id);
153
+ return data.elements ?? [];
154
+ });
155
+
156
+ // src/components/components-tab/components.tsx
157
+ import * as React6 from "react";
158
+ import { ThemeProvider } from "@elementor/editor-ui";
159
+
160
+ // src/components/components-tab/component-search.tsx
161
+ import * as React2 from "react";
162
+ import { SearchIcon } from "@elementor/icons";
163
+ import { Box, InputAdornment, Stack, TextField } from "@elementor/ui";
164
+ import { __ } from "@wordpress/i18n";
165
+
166
+ // src/components/components-tab/search-provider.tsx
167
+ import * as React from "react";
168
+ import { createContext, useContext } from "react";
169
+ import { useSearchState } from "@elementor/utils";
170
+ var SearchContext = createContext(void 0);
171
+ var SearchProvider = ({
172
+ children,
173
+ localStorageKey
174
+ }) => {
175
+ const { debouncedValue, handleChange, inputValue } = useSearchState({ localStorageKey });
176
+ const clearSearch = () => {
177
+ handleChange("");
178
+ };
179
+ return /* @__PURE__ */ React.createElement(SearchContext.Provider, { value: { handleChange, clearSearch, searchValue: debouncedValue, inputValue } }, children);
180
+ };
181
+ var useSearch = () => {
182
+ const context = useContext(SearchContext);
183
+ if (!context) {
184
+ throw new Error("useSearch must be used within a SearchProvider");
185
+ }
186
+ return context;
187
+ };
188
+
189
+ // src/components/components-tab/component-search.tsx
190
+ var ComponentSearch = () => {
191
+ const { inputValue, handleChange } = useSearch();
192
+ return /* @__PURE__ */ React2.createElement(Stack, { direction: "row", gap: 0.5, sx: { width: "100%", px: 2, py: 1.5 } }, /* @__PURE__ */ React2.createElement(Box, { sx: { flexGrow: 1 } }, /* @__PURE__ */ React2.createElement(
193
+ TextField,
194
+ {
195
+ role: "search",
196
+ fullWidth: true,
197
+ size: "tiny",
198
+ value: inputValue,
199
+ placeholder: __("Search", "elementor"),
200
+ onChange: (e) => handleChange(e.target.value),
201
+ InputProps: {
202
+ startAdornment: /* @__PURE__ */ React2.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React2.createElement(SearchIcon, { fontSize: "tiny" }))
203
+ }
204
+ }
205
+ )));
206
+ };
207
+
208
+ // src/components/components-tab/components-list.tsx
209
+ import * as React5 from "react";
210
+ import { ComponentsIcon as ComponentsIcon2, EyeIcon } from "@elementor/icons";
211
+ import { Box as Box4, Divider, Icon, Link, List, Stack as Stack3, Typography as Typography2 } from "@elementor/ui";
212
+ import { __ as __2 } from "@wordpress/i18n";
213
+
186
214
  // src/hooks/use-components.ts
215
+ import { __useSelector as useSelector } from "@elementor/store";
187
216
  var useComponents = () => {
188
217
  const components = useSelector(selectComponents);
189
218
  const isLoading = useSelector(selectLoadIsPending);
@@ -239,7 +268,7 @@ function getSelectedElementContainer() {
239
268
 
240
269
  // src/components/create-component-form/utils/replace-element-with-component.ts
241
270
  import { replaceElement } from "@elementor/editor-elements";
242
- var replaceElementWithComponent = async (element, component) => {
271
+ var replaceElementWithComponent = (element, component) => {
243
272
  replaceElement({
244
273
  currentElement: element,
245
274
  newElement: createComponentModel(component),
@@ -459,26 +488,10 @@ import { useEffect, useMemo as useMemo2, useState as useState2 } from "react";
459
488
  import { getElementLabel } from "@elementor/editor-elements";
460
489
  import { ThemeProvider as ThemeProvider2 } from "@elementor/editor-ui";
461
490
  import { StarIcon } from "@elementor/icons";
491
+ import { __useDispatch as useDispatch } from "@elementor/store";
462
492
  import { Alert, Button, FormLabel, Grid, Popover, Snackbar, Stack as Stack4, TextField as TextField2, Typography as Typography3 } from "@elementor/ui";
463
493
  import { __ as __4 } from "@wordpress/i18n";
464
494
 
465
- // src/hooks/use-create-component.ts
466
- import { __useDispatch as useDispatch, __useSelector as useSelector2 } from "@elementor/store";
467
- var useCreateComponent = () => {
468
- const dispatch4 = useDispatch();
469
- const isPending = useSelector2(selectCreateIsPending);
470
- const isError = useSelector2(selectCreateIsError);
471
- const createComponentAction = async (payload) => {
472
- const result = await dispatch4(createComponent(payload));
473
- return result.payload;
474
- };
475
- return {
476
- createComponent: createComponentAction,
477
- isPending,
478
- isError
479
- };
480
- };
481
-
482
495
  // src/components/create-component-form/hooks/use-form.ts
483
496
  import { useMemo, useState } from "react";
484
497
  var useForm = (initialValues) => {
@@ -559,7 +572,7 @@ function CreateComponentForm() {
559
572
  const [element, setElement] = useState2(null);
560
573
  const [anchorPosition, setAnchorPosition] = useState2();
561
574
  const [resultNotification, setResultNotification] = useState2(null);
562
- const { createComponent: createComponent2, isPending } = useCreateComponent();
575
+ const dispatch5 = useDispatch();
563
576
  useEffect(() => {
564
577
  const OPEN_SAVE_AS_COMPONENT_FORM_EVENT = "elementor/editor/open-save-as-component-form";
565
578
  const openPopup = (event) => {
@@ -572,25 +585,26 @@ function CreateComponentForm() {
572
585
  };
573
586
  }, []);
574
587
  const handleSave = async (values) => {
575
- if (!element) {
576
- throw new Error(`Can't save element as component: element not found`);
577
- }
578
588
  try {
579
- const result = await createComponent2({
580
- name: values.componentName,
581
- content: [element.element.model.toJSON({ remove: ["default"] })]
582
- });
583
589
  if (!element) {
584
- throw new Error(`Can't replace element with component: element not found`);
590
+ throw new Error(`Can't save element as component: element not found`);
585
591
  }
592
+ const tempId = generateTempId();
593
+ dispatch5(
594
+ slice.actions.addUnpublished({
595
+ id: tempId,
596
+ name: values.componentName,
597
+ content: [element.element.model.toJSON({ remove: ["default"] })]
598
+ })
599
+ );
586
600
  replaceElementWithComponent(element.element, {
587
- id: result.component_id,
601
+ id: tempId,
588
602
  name: values.componentName
589
603
  });
590
604
  setResultNotification({
591
605
  show: true,
592
- // Translators: %1$s: Component name, %2$s: Component ID
593
- message: __4("Component saved successfully as: %1$s (ID: %2$s)", "elementor").replace("%1$s", values.componentName).replace("%2$s", result.component_id.toString()),
606
+ // Translators: %1$s: Component name, %2$s: Component temp ID
607
+ message: __4("Component saved successfully as: %1$s (temp ID: %2$s)", "elementor").replace("%1$s", values.componentName).replace("%2$s", tempId.toString()),
594
608
  type: "success"
595
609
  });
596
610
  resetAndClosePopup();
@@ -620,7 +634,6 @@ function CreateComponentForm() {
620
634
  {
621
635
  initialValues: { componentName: element.elementLabel },
622
636
  handleSave,
623
- isSubmitting: isPending,
624
637
  closePopup: resetAndClosePopup
625
638
  }
626
639
  )
@@ -638,7 +651,6 @@ var FONT_SIZE = "tiny";
638
651
  var Form = ({
639
652
  initialValues,
640
653
  handleSave,
641
- isSubmitting,
642
654
  closePopup
643
655
  }) => {
644
656
  const { values, errors, isValid, handleChange, validateForm: validateForm2 } = useForm(initialValues);
@@ -683,18 +695,21 @@ var Form = ({
683
695
  error: Boolean(errors.componentName),
684
696
  helperText: errors.componentName
685
697
  }
686
- ))), /* @__PURE__ */ React7.createElement(Stack4, { direction: "row", justifyContent: "flex-end", alignSelf: "end", py: 1, px: 1.5 }, /* @__PURE__ */ React7.createElement(Button, { onClick: closePopup, disabled: isSubmitting, color: "secondary", variant: "text", size: "small" }, __4("Cancel", "elementor")), /* @__PURE__ */ React7.createElement(
698
+ ))), /* @__PURE__ */ React7.createElement(Stack4, { direction: "row", justifyContent: "flex-end", alignSelf: "end", py: 1, px: 1.5 }, /* @__PURE__ */ React7.createElement(Button, { onClick: closePopup, color: "secondary", variant: "text", size: "small" }, __4("Cancel", "elementor")), /* @__PURE__ */ React7.createElement(
687
699
  Button,
688
700
  {
689
701
  onClick: handleSubmit,
690
- disabled: isSubmitting || !isValid,
702
+ disabled: !isValid,
691
703
  variant: "contained",
692
704
  color: "primary",
693
705
  size: "small"
694
706
  },
695
- isSubmitting ? __4("Creating\u2026", "elementor") : __4("Create", "elementor")
707
+ __4("Create", "elementor")
696
708
  )));
697
709
  };
710
+ var generateTempId = () => {
711
+ return Date.now() + Math.floor(Math.random() * 1e6);
712
+ };
698
713
 
699
714
  // src/create-component-type.ts
700
715
  import {
@@ -789,7 +804,7 @@ function PopulateStore() {
789
804
 
790
805
  // src/store/components-styles-provider.ts
791
806
  import { createStylesProvider } from "@elementor/editor-styles-repository";
792
- import { __getState as getState, __subscribeWithSelector as subscribeWithSelector } from "@elementor/store";
807
+ import { __getState as getState2, __subscribeWithSelector as subscribeWithSelector } from "@elementor/store";
793
808
  var componentsStylesProvider = createStylesProvider({
794
809
  key: "components-styles",
795
810
  priority: 100,
@@ -801,16 +816,16 @@ var componentsStylesProvider = createStylesProvider({
801
816
  ),
802
817
  actions: {
803
818
  all: () => {
804
- return selectFlatStyles(getState());
819
+ return selectFlatStyles(getState2());
805
820
  },
806
821
  get: (id) => {
807
- return selectFlatStyles(getState()).find((style) => style.id === id) ?? null;
822
+ return selectFlatStyles(getState2()).find((style) => style.id === id) ?? null;
808
823
  }
809
824
  }
810
825
  });
811
826
 
812
827
  // src/store/load-components-styles.ts
813
- import { __dispatch as dispatch2, __getState as getState2 } from "@elementor/store";
828
+ import { __dispatch as dispatch2, __getState as getState3 } from "@elementor/store";
814
829
 
815
830
  // src/utils/get-component-ids.ts
816
831
  import { isTransformable } from "@elementor/editor-props";
@@ -834,7 +849,7 @@ async function loadComponentsStyles(elements) {
834
849
  if (!componentIds.length) {
835
850
  return;
836
851
  }
837
- const knownComponents = selectStyles(getState2());
852
+ const knownComponents = selectStyles(getState3());
838
853
  const unknownComponentIds = componentIds.filter((id) => !knownComponents[id]);
839
854
  if (!unknownComponentIds.length) {
840
855
  return;
@@ -868,6 +883,74 @@ function removeComponentStyles(id) {
868
883
  dispatch3(slice.actions.removeStyles({ id }));
869
884
  }
870
885
 
886
+ // src/utils/before-save.ts
887
+ import { updateElementSettings } from "@elementor/editor-elements";
888
+ import { __dispatch as dispatch4, __getState as getState4 } from "@elementor/store";
889
+ var beforeSave = async ({ container, status }) => {
890
+ const unpublishedComponents = selectUnpublishedComponents(getState4());
891
+ if (!unpublishedComponents.length) {
892
+ return;
893
+ }
894
+ try {
895
+ const tempIdToComponentId = await createComponents(unpublishedComponents, status);
896
+ const elements = container.model.get("elements").toJSON();
897
+ updateComponentInstances(elements, tempIdToComponentId);
898
+ dispatch4(
899
+ slice.actions.add(
900
+ unpublishedComponents.map((component) => ({
901
+ id: tempIdToComponentId.get(component.id),
902
+ name: component.name
903
+ }))
904
+ )
905
+ );
906
+ dispatch4(slice.actions.resetUnpublished());
907
+ } catch (error) {
908
+ throw new Error(`Failed to publish components and update component instances: ${error}`);
909
+ }
910
+ };
911
+ async function createComponents(components, status) {
912
+ const tempIdToComponentId = /* @__PURE__ */ new Map();
913
+ const promises = components.map((component) => {
914
+ return apiClient.create({ name: component.name, content: component.content, status }).then((response) => {
915
+ tempIdToComponentId.set(component.id, response.component_id);
916
+ });
917
+ });
918
+ await Promise.all(promises);
919
+ return tempIdToComponentId;
920
+ }
921
+ function updateComponentInstances(elements, tempIdToComponentId) {
922
+ elements.forEach((element) => {
923
+ const { shouldUpdate, newComponentId } = shouldUpdateElement(element, tempIdToComponentId);
924
+ if (shouldUpdate) {
925
+ updateElementComponentId(element.id, newComponentId);
926
+ }
927
+ if (element.elements) {
928
+ updateComponentInstances(element.elements, tempIdToComponentId);
929
+ }
930
+ });
931
+ }
932
+ function shouldUpdateElement(element, tempIdToComponentId) {
933
+ if (element.widgetType === "e-component") {
934
+ const currentComponentId = element.settings?.component?.value;
935
+ if (currentComponentId && tempIdToComponentId.has(currentComponentId)) {
936
+ return { shouldUpdate: true, newComponentId: tempIdToComponentId.get(currentComponentId) };
937
+ }
938
+ }
939
+ return { shouldUpdate: false, newComponentId: null };
940
+ }
941
+ function updateElementComponentId(elementId, componentId) {
942
+ updateElementSettings({
943
+ id: elementId,
944
+ props: {
945
+ component: {
946
+ $$type: "component-id",
947
+ value: componentId
948
+ }
949
+ },
950
+ withHistory: false
951
+ });
952
+ }
953
+
871
954
  // src/init.ts
872
955
  var COMPONENT_DOCUMENT_TYPE = "elementor_component";
873
956
  function init() {
@@ -881,6 +964,7 @@ function init() {
881
964
  }
882
965
  return true;
883
966
  });
967
+ window.elementorCommon.__beforeSave = beforeSave;
884
968
  injectTab({
885
969
  id: "components",
886
970
  label: __6("Components", "elementor"),