@elementor/editor-components 3.33.0-99 → 3.35.0-324

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.
Files changed (63) hide show
  1. package/dist/index.js +2225 -128
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +2236 -111
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +23 -12
  6. package/src/api.ts +71 -11
  7. package/src/component-instance-transformer.ts +24 -0
  8. package/src/component-overridable-transformer.ts +28 -0
  9. package/src/components/component-panel-header/component-badge.tsx +62 -0
  10. package/src/components/component-panel-header/component-panel-header.tsx +58 -0
  11. package/src/components/component-panel-header/use-overridable-props.ts +14 -0
  12. package/src/components/components-tab/component-search.tsx +32 -0
  13. package/src/components/components-tab/components-item.tsx +115 -0
  14. package/src/components/components-tab/components-list.tsx +141 -0
  15. package/src/components/components-tab/components.tsx +17 -0
  16. package/src/components/components-tab/loading-components.tsx +43 -0
  17. package/src/components/components-tab/search-provider.tsx +38 -0
  18. package/src/components/consts.ts +1 -0
  19. package/src/components/create-component-form/create-component-form.tsx +109 -100
  20. package/src/components/create-component-form/utils/get-component-event-data.ts +54 -0
  21. package/src/components/create-component-form/utils/replace-element-with-component.ts +28 -10
  22. package/src/components/edit-component/component-modal.tsx +134 -0
  23. package/src/components/edit-component/edit-component.tsx +96 -0
  24. package/src/components/in-edit-mode.tsx +43 -0
  25. package/src/components/overridable-props/indicator.tsx +80 -0
  26. package/src/components/overridable-props/overridable-prop-control.tsx +67 -0
  27. package/src/components/overridable-props/overridable-prop-form.tsx +98 -0
  28. package/src/components/overridable-props/overridable-prop-indicator.tsx +124 -0
  29. package/src/components/overridable-props/utils/get-overridable-prop.ts +20 -0
  30. package/src/create-component-type.ts +194 -0
  31. package/src/hooks/use-canvas-document.ts +6 -0
  32. package/src/hooks/use-components.ts +6 -9
  33. package/src/hooks/use-element-rect.ts +81 -0
  34. package/src/hooks/use-navigate-back.ts +34 -0
  35. package/src/init.ts +100 -3
  36. package/src/mcp/index.ts +14 -0
  37. package/src/mcp/save-as-component-tool.ts +92 -0
  38. package/src/populate-store.ts +12 -0
  39. package/src/prop-types/component-overridable-prop-type.ts +17 -0
  40. package/src/store/actions/archive-component.ts +16 -0
  41. package/src/store/actions/create-unpublished-component.ts +40 -0
  42. package/src/store/actions/load-components-assets.ts +29 -0
  43. package/src/store/actions/load-components-overridable-props.ts +33 -0
  44. package/src/store/actions/load-components-styles.ts +44 -0
  45. package/src/store/actions/remove-component-styles.ts +9 -0
  46. package/src/store/actions/set-overridable-prop.ts +200 -0
  47. package/src/store/actions/update-current-component.ts +33 -0
  48. package/src/store/actions/update-overridable-prop-origin-value.ts +37 -0
  49. package/src/store/components-styles-provider.ts +24 -0
  50. package/src/store/store.ts +193 -0
  51. package/src/store/thunks.ts +10 -0
  52. package/src/sync/before-save.ts +31 -0
  53. package/src/sync/create-components-before-save.ts +102 -0
  54. package/src/sync/set-component-overridable-props-settings-before-save.ts +23 -0
  55. package/src/sync/update-archived-component-before-save.ts +44 -0
  56. package/src/sync/update-components-before-save.ts +35 -0
  57. package/src/types.ts +83 -0
  58. package/src/utils/component-document-data.ts +19 -0
  59. package/src/utils/get-component-ids.ts +36 -0
  60. package/src/utils/get-container-for-new-element.ts +49 -0
  61. package/src/utils/tracking.ts +47 -0
  62. package/src/components/components-tab.tsx +0 -6
  63. package/src/hooks/use-create-component.ts +0 -13
package/dist/index.mjs CHANGED
@@ -1,54 +1,1103 @@
1
1
  // src/init.ts
2
- import { injectIntoTop } from "@elementor/editor";
2
+ import { injectIntoLogic, injectIntoTop } from "@elementor/editor";
3
+ import {
4
+ registerElementType,
5
+ settingsTransformersRegistry as settingsTransformersRegistry2
6
+ } from "@elementor/editor-canvas";
7
+ import { getV1CurrentDocument as getV1CurrentDocument2 } from "@elementor/editor-documents";
8
+ import {
9
+ FIELD_TYPE,
10
+ injectIntoPanelHeaderTop,
11
+ registerControlReplacement,
12
+ registerFieldIndicator
13
+ } from "@elementor/editor-editing-panel";
3
14
  import { injectTab } from "@elementor/editor-elements-panel";
4
- import { __ as __3 } from "@wordpress/i18n";
15
+ import { stylesRepository } from "@elementor/editor-styles-repository";
16
+ import { registerDataHook } from "@elementor/editor-v1-adapters";
17
+ import { __registerSlice as registerSlice } from "@elementor/store";
18
+ import { __ as __15 } from "@wordpress/i18n";
5
19
 
6
- // src/components/components-tab.tsx
7
- import * as React from "react";
8
- import { Box } from "@elementor/ui";
9
- function ComponentsTab() {
10
- return /* @__PURE__ */ React.createElement(Box, { px: 2 }, "This is the Components tab.");
11
- }
20
+ // src/component-instance-transformer.ts
21
+ import { createTransformer } from "@elementor/editor-canvas";
22
+ import { __getState as getState } from "@elementor/store";
12
23
 
13
- // src/components/create-component-form/create-component-form.tsx
14
- import * as React2 from "react";
15
- import { useEffect, useMemo as useMemo2, useState as useState2 } from "react";
16
- import { getElementLabel } from "@elementor/editor-elements";
17
- import { ThemeProvider } from "@elementor/editor-ui";
18
- import { StarIcon } from "@elementor/icons";
19
- import { Alert, Button, FormLabel, Grid, Popover, Snackbar, Stack, TextField, Typography } from "@elementor/ui";
20
- import { __ as __2 } from "@wordpress/i18n";
24
+ // src/store/store.ts
25
+ import {
26
+ __createSelector as createSelector,
27
+ __createSlice as createSlice
28
+ } from "@elementor/store";
21
29
 
22
- // src/hooks/use-components.ts
23
- import { useQuery } from "@elementor/query";
30
+ // src/store/thunks.ts
31
+ import { __createAsyncThunk as createAsyncThunk } from "@elementor/store";
24
32
 
25
33
  // src/api.ts
34
+ import { ajax } from "@elementor/editor-v1-adapters";
26
35
  import { httpService } from "@elementor/http-client";
27
36
  var BASE_URL = "elementor/v1/components";
37
+ var getParams = (id) => ({
38
+ action: "get_document_config",
39
+ unique_id: `document-config-${id}`,
40
+ data: { id }
41
+ });
28
42
  var apiClient = {
29
43
  get: () => httpService().get(`${BASE_URL}`).then((res) => res.data.data),
30
- create: (payload) => httpService().post(`${BASE_URL}`, payload).then((res) => res.data.data)
44
+ create: (payload) => httpService().post(`${BASE_URL}`, payload).then((res) => res.data.data),
45
+ updateStatuses: (ids, status) => httpService().put(`${BASE_URL}/status`, {
46
+ ids,
47
+ status
48
+ }),
49
+ getComponentConfig: (id) => ajax.load(getParams(id)),
50
+ invalidateComponentConfigCache: (id) => ajax.invalidateCache(getParams(id)),
51
+ getComponentLockStatus: async (componentId) => await httpService().get(`${BASE_URL}/lock-status`, {
52
+ params: {
53
+ componentId
54
+ }
55
+ }).then((res) => {
56
+ const { is_current_user_allow_to_edit: isAllowedToSwitchDocument, locked_by: lockedBy } = res.data.data;
57
+ return { isAllowedToSwitchDocument, lockedBy: lockedBy || "" };
58
+ }),
59
+ lockComponent: async (componentId) => await httpService().post(`${BASE_URL}/lock`, {
60
+ componentId
61
+ }).then((res) => res.data),
62
+ unlockComponent: async (componentId) => await httpService().post(`${BASE_URL}/unlock`, {
63
+ componentId
64
+ }).then((res) => res.data),
65
+ getOverridableProps: async (componentId) => await httpService().get(`${BASE_URL}/overridable-props`, {
66
+ params: {
67
+ componentId: componentId.toString()
68
+ }
69
+ }).then((res) => res.data.data),
70
+ updateArchivedComponents: async (componentIds) => await httpService().post(
71
+ `${BASE_URL}/archive`,
72
+ {
73
+ componentIds
74
+ }
75
+ ).then((res) => res.data.data)
76
+ };
77
+
78
+ // src/store/thunks.ts
79
+ var loadComponents = createAsyncThunk("components/load", async () => {
80
+ const response = await apiClient.get();
81
+ return response;
82
+ });
83
+
84
+ // src/store/store.ts
85
+ var initialState = {
86
+ data: [],
87
+ unpublishedData: [],
88
+ loadStatus: "idle",
89
+ styles: {},
90
+ createdThisSession: [],
91
+ archivedData: [],
92
+ path: [],
93
+ currentComponentId: null
94
+ };
95
+ var SLICE_NAME = "components";
96
+ var slice = createSlice({
97
+ name: SLICE_NAME,
98
+ initialState,
99
+ reducers: {
100
+ add: (state, { payload }) => {
101
+ if (Array.isArray(payload)) {
102
+ state.data = [...state.data, ...payload];
103
+ } else {
104
+ state.data.unshift(payload);
105
+ }
106
+ },
107
+ load: (state, { payload }) => {
108
+ state.data = payload;
109
+ },
110
+ addUnpublished: (state, { payload }) => {
111
+ state.unpublishedData.unshift(payload);
112
+ },
113
+ resetUnpublished: (state) => {
114
+ state.unpublishedData = [];
115
+ },
116
+ removeStyles(state, { payload }) {
117
+ const { [payload.id]: _, ...rest } = state.styles;
118
+ state.styles = rest;
119
+ },
120
+ addStyles: (state, { payload }) => {
121
+ state.styles = { ...state.styles, ...payload };
122
+ },
123
+ addCreatedThisSession: (state, { payload }) => {
124
+ state.createdThisSession.push(payload);
125
+ },
126
+ archive: (state, { payload }) => {
127
+ state.data = state.data.filter((component) => {
128
+ const isArchived = component.id === payload;
129
+ if (isArchived) {
130
+ state.archivedData.push(component);
131
+ }
132
+ return !isArchived;
133
+ });
134
+ },
135
+ setCurrentComponentId: (state, { payload }) => {
136
+ state.currentComponentId = payload;
137
+ },
138
+ setPath: (state, { payload }) => {
139
+ state.path = payload;
140
+ },
141
+ setOverridableProps: (state, { payload }) => {
142
+ const component = state.data.find((comp) => comp.id === payload.componentId);
143
+ if (!component) {
144
+ return;
145
+ }
146
+ component.overridableProps = payload.overridableProps;
147
+ }
148
+ },
149
+ extraReducers: (builder) => {
150
+ builder.addCase(loadComponents.fulfilled, (state, { payload }) => {
151
+ state.data = payload;
152
+ state.loadStatus = "idle";
153
+ });
154
+ builder.addCase(loadComponents.pending, (state) => {
155
+ state.loadStatus = "pending";
156
+ });
157
+ builder.addCase(loadComponents.rejected, (state) => {
158
+ state.loadStatus = "error";
159
+ });
160
+ }
161
+ });
162
+ var selectData = (state) => state[SLICE_NAME].data;
163
+ var selectArchivedData = (state) => state[SLICE_NAME].archivedData;
164
+ var selectLoadStatus = (state) => state[SLICE_NAME].loadStatus;
165
+ var selectStylesDefinitions = (state) => state[SLICE_NAME].styles ?? {};
166
+ var selectUnpublishedData = (state) => state[SLICE_NAME].unpublishedData;
167
+ var getCreatedThisSession = (state) => state[SLICE_NAME].createdThisSession;
168
+ var getPath = (state) => state[SLICE_NAME].path;
169
+ var getCurrentComponentId = (state) => state[SLICE_NAME].currentComponentId;
170
+ var selectComponent = (state, componentId) => state[SLICE_NAME].data.find((component) => component.id === componentId);
171
+ var selectComponents = createSelector(
172
+ selectData,
173
+ selectUnpublishedData,
174
+ (data, unpublishedData) => [
175
+ ...unpublishedData.map((item) => ({ uid: item.uid, name: item.name })),
176
+ ...data
177
+ ]
178
+ );
179
+ var selectUnpublishedComponents = createSelector(
180
+ selectUnpublishedData,
181
+ (unpublishedData) => unpublishedData
182
+ );
183
+ var selectLoadIsPending = createSelector(selectLoadStatus, (status) => status === "pending");
184
+ var selectLoadIsError = createSelector(selectLoadStatus, (status) => status === "error");
185
+ var selectStyles = (state) => state[SLICE_NAME].styles ?? {};
186
+ var selectFlatStyles = createSelector(selectStylesDefinitions, (data) => Object.values(data).flat());
187
+ var selectCreatedThisSession = createSelector(
188
+ getCreatedThisSession,
189
+ (createdThisSession) => createdThisSession
190
+ );
191
+ var DEFAULT_OVERRIDABLE_PROPS = {
192
+ props: {},
193
+ groups: {
194
+ items: {},
195
+ order: []
196
+ }
31
197
  };
198
+ var selectOverridableProps = createSelector(
199
+ selectComponent,
200
+ (component) => {
201
+ if (!component) {
202
+ return void 0;
203
+ }
204
+ return component.overridableProps ?? DEFAULT_OVERRIDABLE_PROPS;
205
+ }
206
+ );
207
+ var selectIsOverridablePropsLoaded = createSelector(
208
+ selectComponent,
209
+ (component) => {
210
+ return !!component?.overridableProps;
211
+ }
212
+ );
213
+ var selectPath = createSelector(getPath, (path) => path);
214
+ var selectCurrentComponentId = createSelector(
215
+ getCurrentComponentId,
216
+ (currentComponentId) => currentComponentId
217
+ );
218
+ var selectArchivedComponents = createSelector(
219
+ selectArchivedData,
220
+ (archivedData) => archivedData
221
+ );
222
+
223
+ // src/utils/component-document-data.ts
224
+ import { getV1DocumentsManager } from "@elementor/editor-documents";
225
+ var getComponentDocumentData = async (id) => {
226
+ const documentManager = getV1DocumentsManager();
227
+ try {
228
+ return await documentManager.request(id);
229
+ } catch {
230
+ return null;
231
+ }
232
+ };
233
+ var invalidateComponentDocumentData = (id) => {
234
+ const documentManager = getV1DocumentsManager();
235
+ documentManager.invalidateCache(id);
236
+ };
237
+
238
+ // src/component-instance-transformer.ts
239
+ var componentInstanceTransformer = createTransformer(
240
+ async ({ component_id: id }) => {
241
+ const unpublishedComponents = selectUnpublishedComponents(getState());
242
+ const unpublishedComponent = unpublishedComponents.find(({ uid }) => uid === id);
243
+ if (unpublishedComponent) {
244
+ return structuredClone(unpublishedComponent.elements);
245
+ }
246
+ if (typeof id !== "number") {
247
+ throw new Error(`Component ID "${id}" not found.`);
248
+ }
249
+ const data = await getComponentDocumentData(id);
250
+ return data?.elements ?? [];
251
+ }
252
+ );
253
+
254
+ // src/component-overridable-transformer.ts
255
+ import { createTransformer as createTransformer2, settingsTransformersRegistry } from "@elementor/editor-canvas";
256
+ var componentOverridableTransformer = createTransformer2(
257
+ async (value, options) => {
258
+ return await transformOriginValue(value, options);
259
+ }
260
+ );
261
+ async function transformOriginValue(value, options) {
262
+ if (!value.origin_value || !value.origin_value.value || !value.origin_value.$$type) {
263
+ return null;
264
+ }
265
+ const transformer = settingsTransformersRegistry.get(value.origin_value.$$type);
266
+ if (!transformer) {
267
+ return null;
268
+ }
269
+ try {
270
+ return await transformer(value.origin_value.value, options);
271
+ } catch {
272
+ return null;
273
+ }
274
+ }
275
+
276
+ // src/components/component-panel-header/component-panel-header.tsx
277
+ import * as React2 from "react";
278
+ import { getV1DocumentsManager as getV1DocumentsManager3 } from "@elementor/editor-documents";
279
+ import { ArrowLeftIcon, ComponentsIcon } from "@elementor/icons";
280
+ import { __useSelector as useSelector3 } from "@elementor/store";
281
+ import { Box as Box2, Divider, IconButton, Stack, Tooltip, Typography } from "@elementor/ui";
282
+ import { __ as __2 } from "@wordpress/i18n";
283
+
284
+ // src/hooks/use-navigate-back.ts
285
+ import { useCallback } from "react";
286
+ import { getV1DocumentsManager as getV1DocumentsManager2 } from "@elementor/editor-documents";
287
+ import { __privateRunCommand as runCommand } from "@elementor/editor-v1-adapters";
288
+ import { __useSelector as useSelector } from "@elementor/store";
289
+ function useNavigateBack() {
290
+ const path = useSelector(selectPath);
291
+ const documentsManager = getV1DocumentsManager2();
292
+ return useCallback(() => {
293
+ const { componentId: prevComponentId, instanceId: prevComponentInstanceId } = path.at(-2) ?? {};
294
+ const switchToDocument = (id, selector) => {
295
+ runCommand("editor/documents/switch", {
296
+ id,
297
+ selector,
298
+ mode: "autosave",
299
+ setAsInitial: false,
300
+ shouldScroll: false
301
+ });
302
+ };
303
+ if (prevComponentId && prevComponentInstanceId) {
304
+ switchToDocument(prevComponentId, `[data-id="${prevComponentInstanceId}"]`);
305
+ return;
306
+ }
307
+ switchToDocument(documentsManager.getInitialId());
308
+ }, [path, documentsManager]);
309
+ }
310
+
311
+ // src/components/component-panel-header/component-badge.tsx
312
+ import * as React from "react";
313
+ import { useEffect, useRef } from "react";
314
+ import { SettingsIcon } from "@elementor/icons";
315
+ import { Badge, Box, keyframes, styled, ToggleButton } from "@elementor/ui";
316
+ import { __ } from "@wordpress/i18n";
317
+ var ComponentsBadge = ({ overridesCount }) => {
318
+ const prevCount = usePrevious(overridesCount);
319
+ const isFirstOverride = prevCount === 0 && overridesCount === 1;
320
+ return /* @__PURE__ */ React.createElement(
321
+ StyledBadge,
322
+ {
323
+ color: "primary",
324
+ key: overridesCount,
325
+ invisible: overridesCount === 0,
326
+ animate: isFirstOverride,
327
+ anchorOrigin: { vertical: "top", horizontal: "right" },
328
+ badgeContent: /* @__PURE__ */ React.createElement(Box, { sx: { animation: !isFirstOverride ? `${slideUp} 300ms ease-out` : "none" } }, overridesCount)
329
+ },
330
+ /* @__PURE__ */ React.createElement(ToggleButton, { value: "overrides", size: "tiny", "aria-label": __("View overrides", "elementor") }, /* @__PURE__ */ React.createElement(SettingsIcon, { fontSize: "tiny" }))
331
+ );
332
+ };
333
+ var StyledBadge = styled(Badge, { shouldForwardProp: (prop) => prop !== "animate" })(
334
+ ({ theme, animate }) => ({
335
+ "& .MuiBadge-badge": {
336
+ minWidth: theme.spacing(2),
337
+ height: theme.spacing(2),
338
+ minHeight: theme.spacing(2),
339
+ maxWidth: theme.spacing(2),
340
+ fontSize: theme.typography.caption.fontSize,
341
+ animation: animate ? `${bounceIn} 300ms ease-out` : "none"
342
+ }
343
+ })
344
+ );
345
+ function usePrevious(value) {
346
+ const ref = useRef(value);
347
+ useEffect(() => {
348
+ ref.current = value;
349
+ }, [value]);
350
+ return ref.current;
351
+ }
352
+ var bounceIn = keyframes`
353
+ 0% { transform: scale(0) translate(50%, 50%); opacity: 0; }
354
+ 70% { transform: scale(1.1) translate(50%, -50%); opacity: 1; }
355
+ 100% { transform: scale(1) translate(50%, -50%); opacity: 1; }
356
+ `;
357
+ var slideUp = keyframes`
358
+ from { transform: translateY(100%); opacity: 0; }
359
+ to { transform: translateY(0); opacity: 1; }
360
+ `;
361
+
362
+ // src/components/component-panel-header/use-overridable-props.ts
363
+ import { __useSelector as useSelector2 } from "@elementor/store";
364
+ function useOverridableProps(componentId) {
365
+ return useSelector2((state) => {
366
+ if (!componentId) {
367
+ return void 0;
368
+ }
369
+ return selectOverridableProps(state, componentId);
370
+ });
371
+ }
372
+
373
+ // src/components/component-panel-header/component-panel-header.tsx
374
+ var ComponentPanelHeader = () => {
375
+ const currentComponentId = useSelector3(selectCurrentComponentId);
376
+ const overridableProps = useOverridableProps(currentComponentId);
377
+ const onBack = useNavigateBack();
378
+ const componentName = getComponentName();
379
+ const overridesCount = overridableProps ? Object.keys(overridableProps.props).length : 0;
380
+ if (!currentComponentId) {
381
+ return null;
382
+ }
383
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(
384
+ Stack,
385
+ {
386
+ direction: "row",
387
+ alignItems: "center",
388
+ justifyContent: "space-between",
389
+ sx: { height: 48, pl: 1.5, pr: 2, py: 1 }
390
+ },
391
+ /* @__PURE__ */ React2.createElement(Stack, { direction: "row", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(Tooltip, { title: __2("Back", "elementor") }, /* @__PURE__ */ React2.createElement(IconButton, { size: "tiny", onClick: onBack, "aria-label": __2("Back", "elementor") }, /* @__PURE__ */ React2.createElement(ArrowLeftIcon, null))), /* @__PURE__ */ React2.createElement(Stack, { direction: "row", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(ComponentsIcon, { color: "secondary", fontSize: "tiny" }), /* @__PURE__ */ React2.createElement(Typography, { variant: "caption", sx: { fontWeight: 500 } }, componentName))),
392
+ /* @__PURE__ */ React2.createElement(ComponentsBadge, { overridesCount })
393
+ ), /* @__PURE__ */ React2.createElement(Divider, null));
394
+ };
395
+ function getComponentName() {
396
+ const documentsManager = getV1DocumentsManager3();
397
+ const currentDocument = documentsManager.getCurrent();
398
+ return currentDocument?.container?.settings?.get("post_title") ?? "";
399
+ }
400
+
401
+ // src/components/components-tab/components.tsx
402
+ import * as React8 from "react";
403
+ import { ThemeProvider } from "@elementor/editor-ui";
404
+
405
+ // src/components/components-tab/component-search.tsx
406
+ import * as React4 from "react";
407
+ import { SearchIcon } from "@elementor/icons";
408
+ import { Box as Box3, InputAdornment, Stack as Stack2, TextField } from "@elementor/ui";
409
+ import { __ as __3 } from "@wordpress/i18n";
410
+
411
+ // src/components/components-tab/search-provider.tsx
412
+ import * as React3 from "react";
413
+ import { createContext, useContext } from "react";
414
+ import { useSearchState } from "@elementor/utils";
415
+ var SearchContext = createContext(void 0);
416
+ var SearchProvider = ({
417
+ children,
418
+ localStorageKey
419
+ }) => {
420
+ const { debouncedValue, handleChange, inputValue } = useSearchState({ localStorageKey });
421
+ const clearSearch = () => {
422
+ handleChange("");
423
+ };
424
+ return /* @__PURE__ */ React3.createElement(SearchContext.Provider, { value: { handleChange, clearSearch, searchValue: debouncedValue, inputValue } }, children);
425
+ };
426
+ var useSearch = () => {
427
+ const context = useContext(SearchContext);
428
+ if (!context) {
429
+ throw new Error("useSearch must be used within a SearchProvider");
430
+ }
431
+ return context;
432
+ };
433
+
434
+ // src/components/components-tab/component-search.tsx
435
+ var ComponentSearch = () => {
436
+ const { inputValue, handleChange } = useSearch();
437
+ return /* @__PURE__ */ React4.createElement(Stack2, { direction: "row", gap: 0.5, sx: { width: "100%", px: 2, py: 1.5 } }, /* @__PURE__ */ React4.createElement(Box3, { sx: { flexGrow: 1 } }, /* @__PURE__ */ React4.createElement(
438
+ TextField,
439
+ {
440
+ role: "search",
441
+ fullWidth: true,
442
+ size: "tiny",
443
+ value: inputValue,
444
+ placeholder: __3("Search", "elementor"),
445
+ onChange: (e) => handleChange(e.target.value),
446
+ InputProps: {
447
+ startAdornment: /* @__PURE__ */ React4.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React4.createElement(SearchIcon, { fontSize: "tiny" }))
448
+ }
449
+ }
450
+ )));
451
+ };
452
+
453
+ // src/components/components-tab/components-list.tsx
454
+ import * as React7 from "react";
455
+ import { ComponentsIcon as ComponentsIcon3, EyeIcon } from "@elementor/icons";
456
+ import { Box as Box6, Divider as Divider2, Icon, Link, List, Stack as Stack4, Typography as Typography3 } from "@elementor/ui";
457
+ import { __ as __6 } from "@wordpress/i18n";
32
458
 
33
459
  // src/hooks/use-components.ts
34
- var COMPONENTS_QUERY_KEY = "components";
460
+ import { __useSelector as useSelector4 } from "@elementor/store";
35
461
  var useComponents = () => {
36
- return useQuery({
37
- queryKey: [COMPONENTS_QUERY_KEY],
38
- queryFn: apiClient.get,
39
- staleTime: Infinity
462
+ const components = useSelector4(selectComponents);
463
+ const isLoading = useSelector4(selectLoadIsPending);
464
+ return { components, isLoading };
465
+ };
466
+
467
+ // src/components/components-tab/components-item.tsx
468
+ import * as React5 from "react";
469
+ import { endDragElementFromPanel, startDragElementFromPanel } from "@elementor/editor-canvas";
470
+ import { dropElement } from "@elementor/editor-elements";
471
+ import { MenuListItem } from "@elementor/editor-ui";
472
+ import { ComponentsIcon as ComponentsIcon2, DotsVerticalIcon } from "@elementor/icons";
473
+ import {
474
+ bindMenu,
475
+ bindTrigger,
476
+ Box as Box4,
477
+ IconButton as IconButton2,
478
+ ListItemButton,
479
+ ListItemIcon,
480
+ ListItemText,
481
+ Menu,
482
+ Typography as Typography2,
483
+ usePopupState
484
+ } from "@elementor/ui";
485
+ import { __ as __5 } from "@wordpress/i18n";
486
+
487
+ // src/store/actions/archive-component.ts
488
+ import { setDocumentModifiedStatus } from "@elementor/editor-documents";
489
+ import { __getStore as getStore } from "@elementor/store";
490
+ var archiveComponent = (componentId) => {
491
+ const store = getStore();
492
+ const dispatch9 = store?.dispatch;
493
+ if (!dispatch9) {
494
+ return;
495
+ }
496
+ dispatch9(slice.actions.archive(componentId));
497
+ setDocumentModifiedStatus(true);
498
+ };
499
+
500
+ // src/store/actions/load-components-assets.ts
501
+ import { isDocumentDirty, setDocumentModifiedStatus as setDocumentModifiedStatus2 } from "@elementor/editor-documents";
502
+
503
+ // src/create-component-type.ts
504
+ import {
505
+ createTemplatedElementView
506
+ } from "@elementor/editor-canvas";
507
+ import { getCurrentDocument } from "@elementor/editor-documents";
508
+ import { __privateRunCommand as runCommand2 } from "@elementor/editor-v1-adapters";
509
+ import { __ as __4 } from "@wordpress/i18n";
510
+
511
+ // src/utils/tracking.ts
512
+ import { getMixpanel } from "@elementor/mixpanel";
513
+ import { __getState as getState2 } from "@elementor/store";
514
+ var trackComponentEvent = ({ action, ...data }) => {
515
+ const { dispatchEvent, config } = getMixpanel();
516
+ if (!config?.names?.components?.[action]) {
517
+ return;
518
+ }
519
+ const name = config.names.components[action];
520
+ dispatchEvent?.(name, data);
521
+ };
522
+ var onElementDrop = (_args, element) => {
523
+ if (!(element.model.get("widgetType") === "e-component")) {
524
+ return;
525
+ }
526
+ const editorSettings = element.model.get("editor_settings");
527
+ const componentName = editorSettings?.title;
528
+ const componentUID = editorSettings?.component_uid;
529
+ const instanceId = element.id;
530
+ const createdThisSession = selectCreatedThisSession(getState2());
531
+ const isSameSessionReuse = componentUID && createdThisSession.includes(componentUID);
532
+ const eventsManagerConfig = window.elementorCommon.eventsManager.config;
533
+ const { locations, secondaryLocations } = eventsManagerConfig;
534
+ trackComponentEvent({
535
+ action: "instanceAdded",
536
+ instance_id: instanceId,
537
+ component_uid: componentUID,
538
+ component_name: componentName,
539
+ is_same_session_reuse: isSameSessionReuse,
540
+ location: locations.widgetPanel,
541
+ secondary_location: secondaryLocations.componentsTab
542
+ });
543
+ };
544
+
545
+ // src/create-component-type.ts
546
+ var TYPE = "e-component";
547
+ function createComponentType(options) {
548
+ const legacyWindow = window;
549
+ return class extends legacyWindow.elementor.modules.elements.types.Widget {
550
+ getType() {
551
+ return options.type;
552
+ }
553
+ getView() {
554
+ return createComponentView(options);
555
+ }
556
+ };
557
+ }
558
+ function createComponentView(options) {
559
+ return class extends createTemplatedElementView(options) {
560
+ legacyWindow = window;
561
+ eventsManagerConfig = this.legacyWindow.elementorCommon.eventsManager.config;
562
+ isComponentCurrentlyEdited() {
563
+ const currentDocument = getCurrentDocument();
564
+ return currentDocument?.id === this.getComponentId();
565
+ }
566
+ afterSettingsResolve(settings) {
567
+ if (settings.component_instance) {
568
+ this.collection = this.legacyWindow.elementor.createBackboneElementsCollection(
569
+ settings.component_instance
570
+ );
571
+ this.collection.models.forEach(setInactiveRecursively);
572
+ settings.component_instance = "<template data-children-placeholder></template>";
573
+ }
574
+ return settings;
575
+ }
576
+ getDomElement() {
577
+ return this.children.findByIndex(0)?.getDomElement() ?? this.$el;
578
+ }
579
+ attachBuffer(collectionView, buffer) {
580
+ const childrenPlaceholder = collectionView.$el.find("[data-children-placeholder]").get(0);
581
+ if (!childrenPlaceholder) {
582
+ super.attachBuffer(collectionView, buffer);
583
+ return;
584
+ }
585
+ childrenPlaceholder.replaceWith(buffer);
586
+ }
587
+ getComponentId() {
588
+ const componentInstance = this.options?.model?.get("settings")?.get("component_instance")?.value;
589
+ return componentInstance.component_id;
590
+ }
591
+ getContextMenuGroups() {
592
+ const filteredGroups = super.getContextMenuGroups().filter((group) => group.name !== "save");
593
+ const componentId = this.getComponentId();
594
+ if (!componentId) {
595
+ return filteredGroups;
596
+ }
597
+ const newGroup = [
598
+ {
599
+ name: "edit component",
600
+ actions: [
601
+ {
602
+ name: "edit component",
603
+ icon: "eicon-edit",
604
+ title: () => __4("Edit Component", "elementor"),
605
+ isEnabled: () => true,
606
+ callback: (_, eventData) => this.editComponent(eventData)
607
+ }
608
+ ]
609
+ }
610
+ ];
611
+ return [...filteredGroups, ...newGroup];
612
+ }
613
+ async switchDocument() {
614
+ const { isAllowedToSwitchDocument, lockedBy } = await apiClient.getComponentLockStatus(
615
+ this.getComponentId()
616
+ );
617
+ if (!isAllowedToSwitchDocument) {
618
+ options.showLockedByModal?.(lockedBy || "");
619
+ } else {
620
+ runCommand2("editor/documents/switch", {
621
+ id: this.getComponentId(),
622
+ mode: "autosave",
623
+ selector: `[data-id="${this.model.get("id")}"]`,
624
+ shouldScroll: false
625
+ });
626
+ }
627
+ }
628
+ editComponent({ trigger, location, secondaryLocation }) {
629
+ if (this.isComponentCurrentlyEdited()) {
630
+ return;
631
+ }
632
+ this.switchDocument();
633
+ const editorSettings = this.model.get("editor_settings");
634
+ trackComponentEvent({
635
+ action: "edited",
636
+ component_uid: editorSettings?.component_uid,
637
+ component_name: editorSettings?.title,
638
+ location,
639
+ secondary_location: secondaryLocation,
640
+ trigger
641
+ });
642
+ }
643
+ handleDblClick(e) {
644
+ e.stopPropagation();
645
+ const { triggers, locations, secondaryLocations } = this.eventsManagerConfig;
646
+ this.editComponent({
647
+ trigger: triggers.doubleClick,
648
+ location: locations.canvas,
649
+ secondaryLocation: secondaryLocations.canvasElement
650
+ });
651
+ }
652
+ events() {
653
+ return {
654
+ ...super.events(),
655
+ dblclick: this.handleDblClick
656
+ };
657
+ }
658
+ attributes() {
659
+ return {
660
+ ...super.attributes(),
661
+ "data-elementor-id": this.getComponentId()
662
+ };
663
+ }
664
+ };
665
+ }
666
+ function setInactiveRecursively(model) {
667
+ const editSettings = model.get("editSettings");
668
+ if (editSettings) {
669
+ editSettings.set("inactive", true);
670
+ }
671
+ const elements = model.get("elements");
672
+ if (elements) {
673
+ elements.forEach((childModel) => {
674
+ setInactiveRecursively(childModel);
675
+ });
676
+ }
677
+ }
678
+
679
+ // src/utils/get-component-ids.ts
680
+ var getComponentIds = async (elements) => {
681
+ const components = elements.map(async ({ widgetType, elType, elements: childElements, settings }) => {
682
+ const ids = [];
683
+ const isComponent = [widgetType, elType].includes(TYPE);
684
+ if (isComponent) {
685
+ const componentId = settings?.component_instance?.value?.component_id;
686
+ const document = await getComponentDocumentData(componentId);
687
+ childElements = document?.elements;
688
+ if (Boolean(componentId)) {
689
+ ids.push(componentId);
690
+ }
691
+ }
692
+ if (!!childElements?.length) {
693
+ ids.push(...await getComponentIds(childElements));
694
+ }
695
+ return ids;
40
696
  });
697
+ const result = (await Promise.all(components)).flat();
698
+ return Array.from(new Set(result));
699
+ };
700
+
701
+ // src/store/actions/load-components-overridable-props.ts
702
+ import { __dispatch as dispatch, __getState as getState3 } from "@elementor/store";
703
+ function loadComponentsOverridableProps(componentIds) {
704
+ if (!componentIds.length) {
705
+ return;
706
+ }
707
+ componentIds.forEach(loadComponentOverrides);
708
+ }
709
+ async function loadComponentOverrides(componentId) {
710
+ const isOverridablePropsLoaded = selectIsOverridablePropsLoaded(getState3(), componentId);
711
+ if (isOverridablePropsLoaded) {
712
+ return;
713
+ }
714
+ const overridableProps = await apiClient.getOverridableProps(componentId);
715
+ if (!overridableProps) {
716
+ return;
717
+ }
718
+ dispatch(
719
+ slice.actions.setOverridableProps({
720
+ componentId,
721
+ overridableProps
722
+ })
723
+ );
724
+ }
725
+
726
+ // src/store/actions/load-components-styles.ts
727
+ import { __dispatch as dispatch2, __getState as getState4 } from "@elementor/store";
728
+ async function loadComponentsStyles(componentIds) {
729
+ if (!componentIds.length) {
730
+ return;
731
+ }
732
+ const knownComponents = selectStyles(getState4());
733
+ const unknownComponentIds = componentIds.filter((id) => !knownComponents[id]);
734
+ if (!unknownComponentIds.length) {
735
+ return;
736
+ }
737
+ addComponentStyles(unknownComponentIds);
738
+ }
739
+ async function addComponentStyles(ids) {
740
+ const newComponents = await loadStyles(ids);
741
+ addStyles(newComponents);
742
+ }
743
+ async function loadStyles(ids) {
744
+ return Promise.all(ids.map(async (id) => [id, await apiClient.getComponentConfig(id)]));
745
+ }
746
+ function addStyles(data) {
747
+ const styles = Object.fromEntries(
748
+ data.map(([componentId, componentData]) => [componentId, extractStyles(componentData)])
749
+ );
750
+ dispatch2(slice.actions.addStyles(styles));
751
+ }
752
+ function extractStyles(element) {
753
+ return [...Object.values(element.styles ?? {}), ...(element.elements ?? []).flatMap(extractStyles)];
754
+ }
755
+
756
+ // src/store/actions/load-components-assets.ts
757
+ async function loadComponentsAssets(elements) {
758
+ const componentIds = await getComponentIds(elements);
759
+ return Promise.all([
760
+ updateDocumentState(componentIds),
761
+ loadComponentsOverridableProps(componentIds),
762
+ loadComponentsStyles(componentIds)
763
+ ]);
764
+ }
765
+ async function updateDocumentState(componentIds) {
766
+ const components = (await Promise.all(componentIds.map(getComponentDocumentData))).filter(
767
+ (document) => !!document
768
+ );
769
+ const isDrafted = components.some(isDocumentDirty);
770
+ if (isDrafted) {
771
+ setDocumentModifiedStatus2(true);
772
+ }
773
+ }
774
+
775
+ // src/utils/get-container-for-new-element.ts
776
+ import {
777
+ getContainer,
778
+ getCurrentDocumentContainer,
779
+ getSelectedElements
780
+ } from "@elementor/editor-elements";
781
+ var getContainerForNewElement = () => {
782
+ const currentDocumentContainer = getCurrentDocumentContainer();
783
+ const selectedElement = getSelectedElementContainer();
784
+ let container, options;
785
+ if (selectedElement) {
786
+ switch (selectedElement.model.get("elType")) {
787
+ case "widget": {
788
+ container = selectedElement?.parent;
789
+ const selectedElIndex = selectedElement.view?._index ?? -1;
790
+ if (selectedElIndex > -1) {
791
+ options = { at: selectedElIndex + 1 };
792
+ }
793
+ break;
794
+ }
795
+ case "section": {
796
+ container = selectedElement?.children?.[0];
797
+ break;
798
+ }
799
+ default: {
800
+ container = selectedElement;
801
+ break;
802
+ }
803
+ }
804
+ }
805
+ return { container: container ?? currentDocumentContainer, options };
41
806
  };
807
+ function getSelectedElementContainer() {
808
+ const selectedElements = getSelectedElements();
809
+ if (selectedElements.length !== 1) {
810
+ return void 0;
811
+ }
812
+ return getContainer(selectedElements[0].id);
813
+ }
42
814
 
43
- // src/hooks/use-create-component.ts
44
- import { useMutation, useQueryClient } from "@elementor/query";
45
- var useCreateComponentMutation = () => {
46
- const queryClient = useQueryClient();
47
- return useMutation({
48
- mutationFn: apiClient.create,
49
- onSuccess: () => queryClient.invalidateQueries({ queryKey: [COMPONENTS_QUERY_KEY] })
815
+ // src/components/create-component-form/utils/replace-element-with-component.ts
816
+ import { replaceElement } from "@elementor/editor-elements";
817
+ var replaceElementWithComponent = (element, component) => {
818
+ replaceElement({
819
+ currentElement: element,
820
+ newElement: createComponentModel(component),
821
+ withHistory: false
50
822
  });
51
823
  };
824
+ var createComponentModel = (component) => {
825
+ return {
826
+ elType: "widget",
827
+ widgetType: "e-component",
828
+ settings: {
829
+ component_instance: {
830
+ $$type: "component-instance",
831
+ value: {
832
+ component_id: component.id ?? component.uid
833
+ }
834
+ }
835
+ },
836
+ editor_settings: {
837
+ title: component.name,
838
+ component_uid: component.uid
839
+ }
840
+ };
841
+ };
842
+
843
+ // src/components/components-tab/components-item.tsx
844
+ var ComponentItem = ({ component }) => {
845
+ const componentModel = createComponentModel(component);
846
+ const popupState = usePopupState({
847
+ variant: "popover",
848
+ disableAutoFocus: true
849
+ });
850
+ const handleClick = () => {
851
+ addComponentToPage(componentModel);
852
+ };
853
+ const handleDragEnd = () => {
854
+ loadComponentsAssets([componentModel]);
855
+ endDragElementFromPanel();
856
+ };
857
+ const handleArchiveClick = () => {
858
+ popupState.close();
859
+ if (!component.id) {
860
+ throw new Error("Component ID is required");
861
+ }
862
+ archiveComponent(component.id);
863
+ };
864
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(
865
+ ListItemButton,
866
+ {
867
+ draggable: true,
868
+ onDragStart: () => startDragElementFromPanel(componentModel),
869
+ onDragEnd: handleDragEnd,
870
+ shape: "rounded",
871
+ sx: { border: "solid 1px", borderColor: "divider", py: 0.5, px: 1 }
872
+ },
873
+ /* @__PURE__ */ React5.createElement(Box4, { sx: { display: "flex", width: "100%", alignItems: "center", gap: 1 }, onClick: handleClick }, /* @__PURE__ */ React5.createElement(ListItemIcon, { size: "tiny" }, /* @__PURE__ */ React5.createElement(ComponentsIcon2, { fontSize: "tiny" })), /* @__PURE__ */ React5.createElement(
874
+ ListItemText,
875
+ {
876
+ primary: /* @__PURE__ */ React5.createElement(Typography2, { variant: "caption", sx: { color: "text.primary" } }, component.name)
877
+ }
878
+ )),
879
+ /* @__PURE__ */ React5.createElement(IconButton2, { size: "tiny", ...bindTrigger(popupState), "aria-label": "More actions" }, /* @__PURE__ */ React5.createElement(DotsVerticalIcon, { fontSize: "tiny" }))
880
+ ), /* @__PURE__ */ React5.createElement(
881
+ Menu,
882
+ {
883
+ ...bindMenu(popupState),
884
+ anchorOrigin: {
885
+ vertical: "bottom",
886
+ horizontal: "right"
887
+ },
888
+ transformOrigin: {
889
+ vertical: "top",
890
+ horizontal: "right"
891
+ }
892
+ },
893
+ /* @__PURE__ */ React5.createElement(MenuListItem, { sx: { minWidth: "160px" }, onClick: handleArchiveClick }, __5("Archive", "elementor"))
894
+ ));
895
+ };
896
+ var addComponentToPage = (model) => {
897
+ const { container, options } = getContainerForNewElement();
898
+ if (!container) {
899
+ throw new Error(`Can't find container to drop new component instance at`);
900
+ }
901
+ loadComponentsAssets([model]);
902
+ dropElement({
903
+ containerId: container.id,
904
+ model,
905
+ options: { ...options, useHistory: false, scrollIntoView: true }
906
+ });
907
+ };
908
+
909
+ // src/components/components-tab/loading-components.tsx
910
+ import * as React6 from "react";
911
+ import { Box as Box5, ListItemButton as ListItemButton2, Skeleton, Stack as Stack3 } from "@elementor/ui";
912
+ var ROWS_COUNT = 6;
913
+ var rows = Array.from({ length: ROWS_COUNT }, (_, index) => index);
914
+ var LoadingComponents = () => {
915
+ return /* @__PURE__ */ React6.createElement(
916
+ Stack3,
917
+ {
918
+ "aria-label": "Loading components",
919
+ gap: 1,
920
+ sx: {
921
+ pointerEvents: "none",
922
+ position: "relative",
923
+ maxHeight: "300px",
924
+ overflow: "hidden",
925
+ "&:after": {
926
+ position: "absolute",
927
+ top: 0,
928
+ content: '""',
929
+ left: 0,
930
+ width: "100%",
931
+ height: "300px",
932
+ background: "linear-gradient(to top, white, transparent)",
933
+ pointerEvents: "none"
934
+ }
935
+ }
936
+ },
937
+ rows.map((row) => /* @__PURE__ */ React6.createElement(
938
+ ListItemButton2,
939
+ {
940
+ key: row,
941
+ sx: { border: "solid 1px", borderColor: "divider", py: 0.5, px: 1 },
942
+ shape: "rounded"
943
+ },
944
+ /* @__PURE__ */ React6.createElement(Box5, { display: "flex", gap: 1, width: "100%" }, /* @__PURE__ */ React6.createElement(Skeleton, { variant: "text", width: "24px", height: "36px" }), /* @__PURE__ */ React6.createElement(Skeleton, { variant: "text", width: "100%", height: "36px" }))
945
+ ))
946
+ );
947
+ };
948
+
949
+ // src/components/components-tab/components-list.tsx
950
+ function ComponentsList() {
951
+ const { components, isLoading, searchValue } = useFilteredComponents();
952
+ if (isLoading) {
953
+ return /* @__PURE__ */ React7.createElement(LoadingComponents, null);
954
+ }
955
+ const isEmpty = !components || components.length === 0;
956
+ if (isEmpty) {
957
+ if (searchValue.length > 0) {
958
+ return /* @__PURE__ */ React7.createElement(EmptySearchResult, null);
959
+ }
960
+ return /* @__PURE__ */ React7.createElement(EmptyState, null);
961
+ }
962
+ return /* @__PURE__ */ React7.createElement(List, { sx: { display: "flex", flexDirection: "column", gap: 1, px: 2 } }, components.map((component) => /* @__PURE__ */ React7.createElement(ComponentItem, { key: component.uid, component })));
963
+ }
964
+ var EmptyState = () => {
965
+ return /* @__PURE__ */ React7.createElement(
966
+ Stack4,
967
+ {
968
+ alignItems: "center",
969
+ justifyContent: "center",
970
+ height: "100%",
971
+ sx: { px: 2.5, pt: 10 },
972
+ gap: 1.75,
973
+ overflow: "hidden"
974
+ },
975
+ /* @__PURE__ */ React7.createElement(Icon, { fontSize: "large" }, /* @__PURE__ */ React7.createElement(EyeIcon, { fontSize: "large" })),
976
+ /* @__PURE__ */ React7.createElement(Typography3, { align: "center", variant: "subtitle2", color: "text.secondary", fontWeight: "bold" }, __6("Text that explains that there are no Components yet.", "elementor")),
977
+ /* @__PURE__ */ React7.createElement(Typography3, { variant: "caption", align: "center", color: "text.secondary" }, __6(
978
+ "Once you have Components, this is where you can manage them\u2014rearrange, duplicate, rename and delete irrelevant classes.",
979
+ "elementor"
980
+ )),
981
+ /* @__PURE__ */ React7.createElement(Divider2, { sx: { width: "100%" }, color: "text.secondary" }),
982
+ /* @__PURE__ */ React7.createElement(Typography3, { align: "left", variant: "caption", color: "text.secondary" }, __6("To create a component, first design it, then choose one of three options:", "elementor")),
983
+ /* @__PURE__ */ React7.createElement(
984
+ Typography3,
985
+ {
986
+ align: "left",
987
+ variant: "caption",
988
+ color: "text.secondary",
989
+ sx: { display: "flex", flexDirection: "column" }
990
+ },
991
+ /* @__PURE__ */ React7.createElement("span", null, __6("1. Right-click and select Create Component", "elementor")),
992
+ /* @__PURE__ */ React7.createElement("span", null, __6("2. Use the component icon in the Structure panel", "elementor")),
993
+ /* @__PURE__ */ React7.createElement("span", null, __6("3. Use the component icon in the Edit panel header", "elementor"))
994
+ )
995
+ );
996
+ };
997
+ var EmptySearchResult = () => {
998
+ const { searchValue, clearSearch } = useSearch();
999
+ return /* @__PURE__ */ React7.createElement(
1000
+ Stack4,
1001
+ {
1002
+ color: "text.secondary",
1003
+ pt: 5,
1004
+ alignItems: "center",
1005
+ gap: 1,
1006
+ overflow: "hidden",
1007
+ justifySelf: "center"
1008
+ },
1009
+ /* @__PURE__ */ React7.createElement(ComponentsIcon3, null),
1010
+ /* @__PURE__ */ React7.createElement(
1011
+ Box6,
1012
+ {
1013
+ sx: {
1014
+ width: "100%"
1015
+ }
1016
+ },
1017
+ /* @__PURE__ */ React7.createElement(Typography3, { align: "center", variant: "subtitle2", color: "inherit" }, __6("Sorry, nothing matched", "elementor")),
1018
+ searchValue && /* @__PURE__ */ React7.createElement(
1019
+ Typography3,
1020
+ {
1021
+ variant: "subtitle2",
1022
+ color: "inherit",
1023
+ sx: {
1024
+ display: "flex",
1025
+ width: "100%",
1026
+ justifyContent: "center"
1027
+ }
1028
+ },
1029
+ /* @__PURE__ */ React7.createElement("span", null, "\u201C"),
1030
+ /* @__PURE__ */ React7.createElement(
1031
+ "span",
1032
+ {
1033
+ style: {
1034
+ maxWidth: "80%",
1035
+ overflow: "hidden",
1036
+ textOverflow: "ellipsis"
1037
+ }
1038
+ },
1039
+ searchValue
1040
+ ),
1041
+ /* @__PURE__ */ React7.createElement("span", null, "\u201D.")
1042
+ )
1043
+ ),
1044
+ /* @__PURE__ */ React7.createElement(Typography3, { align: "center", variant: "caption", color: "inherit" }, __6("Try something else.", "elementor")),
1045
+ /* @__PURE__ */ React7.createElement(Typography3, { align: "center", variant: "caption", color: "inherit" }, /* @__PURE__ */ React7.createElement(Link, { color: "secondary", variant: "caption", component: "button", onClick: clearSearch }, __6("Clear & try again", "elementor")))
1046
+ );
1047
+ };
1048
+ var useFilteredComponents = () => {
1049
+ const { components, isLoading } = useComponents();
1050
+ const { searchValue } = useSearch();
1051
+ return {
1052
+ components: components.filter(
1053
+ (component) => component.name.toLowerCase().includes(searchValue.toLowerCase())
1054
+ ),
1055
+ isLoading,
1056
+ searchValue
1057
+ };
1058
+ };
1059
+
1060
+ // src/components/components-tab/components.tsx
1061
+ var Components = () => {
1062
+ return /* @__PURE__ */ React8.createElement(ThemeProvider, null, /* @__PURE__ */ React8.createElement(SearchProvider, { localStorageKey: "elementor-components-search" }, /* @__PURE__ */ React8.createElement(ComponentSearch, null), /* @__PURE__ */ React8.createElement(ComponentsList, null)));
1063
+ };
1064
+
1065
+ // src/components/consts.ts
1066
+ var COMPONENT_DOCUMENT_TYPE = "elementor_component";
1067
+
1068
+ // src/components/create-component-form/create-component-form.tsx
1069
+ import * as React9 from "react";
1070
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
1071
+ import { getElementLabel } from "@elementor/editor-elements";
1072
+ import { Form as FormElement, ThemeProvider as ThemeProvider2 } from "@elementor/editor-ui";
1073
+ import { StarIcon } from "@elementor/icons";
1074
+ import { Alert, Button, FormLabel, Grid, Popover, Snackbar, Stack as Stack5, TextField as TextField2, Typography as Typography4 } from "@elementor/ui";
1075
+ import { __ as __8 } from "@wordpress/i18n";
1076
+
1077
+ // src/store/actions/create-unpublished-component.ts
1078
+ import { __privateRunCommand as runCommand3 } from "@elementor/editor-v1-adapters";
1079
+ import { __dispatch as dispatch3 } from "@elementor/store";
1080
+ import { generateUniqueId } from "@elementor/utils";
1081
+ function createUnpublishedComponent(name, element, eventData) {
1082
+ const uid = generateUniqueId("component");
1083
+ const componentBase = { uid, name };
1084
+ dispatch3(
1085
+ slice.actions.addUnpublished({
1086
+ ...componentBase,
1087
+ elements: [element]
1088
+ })
1089
+ );
1090
+ dispatch3(slice.actions.addCreatedThisSession(uid));
1091
+ replaceElementWithComponent(element, componentBase);
1092
+ trackComponentEvent({
1093
+ action: "created",
1094
+ component_uid: uid,
1095
+ component_name: name,
1096
+ ...eventData
1097
+ });
1098
+ runCommand3("document/save/auto");
1099
+ return uid;
1100
+ }
52
1101
 
53
1102
  // src/components/create-component-form/hooks/use-form.ts
54
1103
  import { useMemo, useState } from "react";
@@ -101,16 +1150,16 @@ var validateForm = (values, schema) => {
101
1150
 
102
1151
  // src/components/create-component-form/utils/component-form-schema.ts
103
1152
  import { z } from "@elementor/schema";
104
- import { __ } from "@wordpress/i18n";
1153
+ import { __ as __7 } from "@wordpress/i18n";
105
1154
  var MIN_NAME_LENGTH = 2;
106
1155
  var MAX_NAME_LENGTH = 50;
107
1156
  var createBaseComponentSchema = (existingNames) => {
108
1157
  return z.object({
109
1158
  componentName: z.string().trim().max(
110
1159
  MAX_NAME_LENGTH,
111
- __("Component name is too long. Please keep it under 50 characters.", "elementor")
1160
+ __7("Component name is too long. Please keep it under 50 characters.", "elementor")
112
1161
  ).refine((value) => !existingNames.includes(value), {
113
- message: __("Component name already exists", "elementor")
1162
+ message: __7("Component name already exists", "elementor")
114
1163
  })
115
1164
  });
116
1165
  };
@@ -118,103 +1167,114 @@ var createSubmitComponentSchema = (existingNames) => {
118
1167
  const baseSchema = createBaseComponentSchema(existingNames);
119
1168
  return baseSchema.extend({
120
1169
  componentName: baseSchema.shape.componentName.refine((value) => value.length > 0, {
121
- message: __("Component name is required.", "elementor")
1170
+ message: __7("Component name is required.", "elementor")
122
1171
  }).refine((value) => value.length >= MIN_NAME_LENGTH, {
123
- message: __("Component name is too short. Please enter at least 2 characters.", "elementor")
1172
+ message: __7("Component name is too short. Please enter at least 2 characters.", "elementor")
124
1173
  })
125
1174
  });
126
1175
  };
127
1176
 
128
- // src/components/create-component-form/utils/replace-element-with-component.ts
129
- import { replaceElement } from "@elementor/editor-elements";
130
- import { numberPropTypeUtil } from "@elementor/editor-props";
131
- var replaceElementWithComponent = async (element, componentId) => {
132
- replaceElement({
133
- currentElement: element,
134
- newElement: {
135
- elType: "widget",
136
- widgetType: "e-component",
137
- settings: {
138
- component_id: numberPropTypeUtil.create(componentId)
139
- }
140
- },
141
- withHistory: false
142
- });
1177
+ // src/components/create-component-form/utils/get-component-event-data.ts
1178
+ var getComponentEventData = (containerElement, options) => {
1179
+ const { elementsCount, componentsCount } = countNestedElements(containerElement);
1180
+ return {
1181
+ nested_elements_count: elementsCount,
1182
+ nested_components_count: componentsCount,
1183
+ top_element_type: containerElement.elType,
1184
+ location: options?.location,
1185
+ secondary_location: options?.secondaryLocation,
1186
+ trigger: options?.trigger
1187
+ };
143
1188
  };
1189
+ function countNestedElements(container) {
1190
+ if (!container.elements || container.elements.length === 0) {
1191
+ return { elementsCount: 0, componentsCount: 0 };
1192
+ }
1193
+ let elementsCount = container.elements.length;
1194
+ let componentsCount = 0;
1195
+ for (const element of container.elements) {
1196
+ if (element.widgetType === "e-component") {
1197
+ componentsCount++;
1198
+ }
1199
+ const { elementsCount: nestedElementsCount, componentsCount: nestedComponentsCount } = countNestedElements(element);
1200
+ elementsCount += nestedElementsCount;
1201
+ componentsCount += nestedComponentsCount;
1202
+ }
1203
+ return { elementsCount, componentsCount };
1204
+ }
144
1205
 
145
1206
  // src/components/create-component-form/create-component-form.tsx
146
1207
  function CreateComponentForm() {
147
1208
  const [element, setElement] = useState2(null);
148
1209
  const [anchorPosition, setAnchorPosition] = useState2();
149
1210
  const [resultNotification, setResultNotification] = useState2(null);
150
- const { mutate: createComponent, isPending } = useCreateComponentMutation();
151
- useEffect(() => {
1211
+ const eventData = useRef2(null);
1212
+ useEffect2(() => {
152
1213
  const OPEN_SAVE_AS_COMPONENT_FORM_EVENT = "elementor/editor/open-save-as-component-form";
153
1214
  const openPopup = (event) => {
154
1215
  setElement({ element: event.detail.element, elementLabel: getElementLabel(event.detail.element.id) });
155
1216
  setAnchorPosition(event.detail.anchorPosition);
1217
+ eventData.current = getComponentEventData(event.detail.element, event.detail.options);
1218
+ trackComponentEvent({
1219
+ action: "createClicked",
1220
+ ...eventData.current
1221
+ });
156
1222
  };
157
1223
  window.addEventListener(OPEN_SAVE_AS_COMPONENT_FORM_EVENT, openPopup);
158
1224
  return () => {
159
1225
  window.removeEventListener(OPEN_SAVE_AS_COMPONENT_FORM_EVENT, openPopup);
160
1226
  };
161
1227
  }, []);
162
- const handleSave = async (values) => {
163
- if (!element) {
164
- throw new Error(`Can't save element as component: element not found`);
165
- }
166
- createComponent(
167
- {
168
- name: values.componentName,
169
- content: [element.element.model.toJSON({ remove: ["default"] })]
170
- },
171
- {
172
- onSuccess: (result) => {
173
- if (!element) {
174
- throw new Error(`Can't replace element with component: element not found`);
175
- }
176
- replaceElementWithComponent(element.element, result.component_id);
177
- setResultNotification({
178
- show: true,
179
- // Translators: %1$s: Component name, %2$s: Component ID
180
- message: __2("Component saved successfully as: %1$s (ID: %2$s)", "elementor").replace("%1$s", values.componentName).replace("%2$s", result.component_id.toString()),
181
- type: "success"
182
- });
183
- resetAndClosePopup();
184
- },
185
- onError: () => {
186
- const errorMessage = __2("Failed to save component. Please try again.", "elementor");
187
- setResultNotification({
188
- show: true,
189
- message: errorMessage,
190
- type: "error"
191
- });
192
- }
1228
+ const handleSave = (values) => {
1229
+ try {
1230
+ if (!element) {
1231
+ throw new Error(`Can't save element as component: element not found`);
193
1232
  }
194
- );
1233
+ const uid = createUnpublishedComponent(values.componentName, element.element, eventData.current);
1234
+ setResultNotification({
1235
+ show: true,
1236
+ // Translators: %1$s: Component name, %2$s: Component UID
1237
+ message: __8("Component saved successfully as: %1$s (UID: %2$s)", "elementor").replace("%1$s", values.componentName).replace("%2$s", uid),
1238
+ type: "success"
1239
+ });
1240
+ resetAndClosePopup();
1241
+ } catch {
1242
+ const errorMessage = __8("Failed to save component. Please try again.", "elementor");
1243
+ setResultNotification({
1244
+ show: true,
1245
+ message: errorMessage,
1246
+ type: "error"
1247
+ });
1248
+ }
195
1249
  };
196
1250
  const resetAndClosePopup = () => {
197
1251
  setElement(null);
198
1252
  setAnchorPosition(void 0);
199
1253
  };
200
- return /* @__PURE__ */ React2.createElement(ThemeProvider, null, /* @__PURE__ */ React2.createElement(
1254
+ const cancelSave = () => {
1255
+ resetAndClosePopup();
1256
+ trackComponentEvent({
1257
+ action: "createCancelled",
1258
+ ...eventData.current
1259
+ });
1260
+ };
1261
+ return /* @__PURE__ */ React9.createElement(ThemeProvider2, null, /* @__PURE__ */ React9.createElement(
201
1262
  Popover,
202
1263
  {
203
1264
  open: element !== null,
204
- onClose: resetAndClosePopup,
1265
+ onClose: cancelSave,
205
1266
  anchorReference: "anchorPosition",
206
1267
  anchorPosition
207
1268
  },
208
- element !== null && /* @__PURE__ */ React2.createElement(
1269
+ element !== null && /* @__PURE__ */ React9.createElement(
209
1270
  Form,
210
1271
  {
211
1272
  initialValues: { componentName: element.elementLabel },
212
1273
  handleSave,
213
- isSubmitting: isPending,
214
- closePopup: resetAndClosePopup
1274
+ closePopup: cancelSave
215
1275
  }
216
1276
  )
217
- ), /* @__PURE__ */ React2.createElement(Snackbar, { open: resultNotification?.show, onClose: () => setResultNotification(null) }, /* @__PURE__ */ React2.createElement(
1277
+ ), /* @__PURE__ */ React9.createElement(Snackbar, { open: resultNotification?.show, onClose: () => setResultNotification(null) }, /* @__PURE__ */ React9.createElement(
218
1278
  Alert,
219
1279
  {
220
1280
  onClose: () => setResultNotification(null),
@@ -228,11 +1288,10 @@ var FONT_SIZE = "tiny";
228
1288
  var Form = ({
229
1289
  initialValues,
230
1290
  handleSave,
231
- isSubmitting,
232
1291
  closePopup
233
1292
  }) => {
234
1293
  const { values, errors, isValid, handleChange, validateForm: validateForm2 } = useForm(initialValues);
235
- const { data: components } = useComponents();
1294
+ const { components } = useComponents();
236
1295
  const existingComponentNames = useMemo2(() => {
237
1296
  return components?.map((component) => component.name) ?? [];
238
1297
  }, [components]);
@@ -250,8 +1309,15 @@ var Form = ({
250
1309
  handleSave(parsedValues);
251
1310
  }
252
1311
  };
253
- return /* @__PURE__ */ React2.createElement(Stack, { alignItems: "start", width: "268px" }, /* @__PURE__ */ React2.createElement(
254
- Stack,
1312
+ const texts = {
1313
+ heading: __8("Save as a component", "elementor"),
1314
+ name: __8("Name", "elementor"),
1315
+ cancel: __8("Cancel", "elementor"),
1316
+ create: __8("Create", "elementor")
1317
+ };
1318
+ const nameInputId = "component-name";
1319
+ return /* @__PURE__ */ React9.createElement(FormElement, { onSubmit: handleSubmit }, /* @__PURE__ */ React9.createElement(Stack5, { alignItems: "start", width: "268px" }, /* @__PURE__ */ React9.createElement(
1320
+ Stack5,
255
1321
  {
256
1322
  direction: "row",
257
1323
  alignItems: "center",
@@ -259,12 +1325,12 @@ var Form = ({
259
1325
  px: 1.5,
260
1326
  sx: { columnGap: 0.5, borderBottom: "1px solid", borderColor: "divider", width: "100%" }
261
1327
  },
262
- /* @__PURE__ */ React2.createElement(StarIcon, { fontSize: FONT_SIZE }),
263
- /* @__PURE__ */ React2.createElement(Typography, { variant: "caption", sx: { color: "text.primary", fontWeight: "500", lineHeight: 1 } }, __2("Save as a component", "elementor"))
264
- ), /* @__PURE__ */ React2.createElement(Grid, { container: true, gap: 0.75, alignItems: "start", p: 1.5 }, /* @__PURE__ */ React2.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React2.createElement(FormLabel, { htmlFor: "component-name", size: "tiny" }, __2("Name", "elementor"))), /* @__PURE__ */ React2.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React2.createElement(
265
- TextField,
1328
+ /* @__PURE__ */ React9.createElement(StarIcon, { fontSize: FONT_SIZE }),
1329
+ /* @__PURE__ */ React9.createElement(Typography4, { variant: "caption", sx: { color: "text.primary", fontWeight: "500", lineHeight: 1 } }, texts.heading)
1330
+ ), /* @__PURE__ */ React9.createElement(Grid, { container: true, gap: 0.75, alignItems: "start", p: 1.5 }, /* @__PURE__ */ React9.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React9.createElement(FormLabel, { htmlFor: nameInputId, size: "tiny" }, texts.name)), /* @__PURE__ */ React9.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React9.createElement(
1331
+ TextField2,
266
1332
  {
267
- id: "component-name",
1333
+ id: nameInputId,
268
1334
  size: FONT_SIZE,
269
1335
  fullWidth: true,
270
1336
  value: values.componentName,
@@ -273,30 +1339,1089 @@ var Form = ({
273
1339
  error: Boolean(errors.componentName),
274
1340
  helperText: errors.componentName
275
1341
  }
276
- ))), /* @__PURE__ */ React2.createElement(Stack, { direction: "row", justifyContent: "flex-end", alignSelf: "end", py: 1, px: 1.5 }, /* @__PURE__ */ React2.createElement(Button, { onClick: closePopup, disabled: isSubmitting, color: "secondary", variant: "text", size: "small" }, __2("Cancel", "elementor")), /* @__PURE__ */ React2.createElement(
277
- Button,
1342
+ ))), /* @__PURE__ */ React9.createElement(Stack5, { direction: "row", justifyContent: "flex-end", alignSelf: "end", py: 1, px: 1.5 }, /* @__PURE__ */ React9.createElement(Button, { onClick: closePopup, color: "secondary", variant: "text", size: "small" }, texts.cancel), /* @__PURE__ */ React9.createElement(Button, { type: "submit", disabled: !isValid, variant: "contained", color: "primary", size: "small" }, texts.create))));
1343
+ };
1344
+
1345
+ // src/components/edit-component/edit-component.tsx
1346
+ import * as React11 from "react";
1347
+ import { useEffect as useEffect5 } from "react";
1348
+ import { getV1DocumentsManager as getV1DocumentsManager4 } from "@elementor/editor-documents";
1349
+ import { __privateListenTo as listenTo, commandEndEvent as commandEndEvent2 } from "@elementor/editor-v1-adapters";
1350
+ import { __useSelector as useSelector5 } from "@elementor/store";
1351
+
1352
+ // src/store/actions/update-current-component.ts
1353
+ import { setDocumentModifiedStatus as setDocumentModifiedStatus3 } from "@elementor/editor-documents";
1354
+ import { __getStore as getStore2 } from "@elementor/store";
1355
+ function updateCurrentComponent({
1356
+ path,
1357
+ currentComponentId
1358
+ }) {
1359
+ const dispatch9 = getStore2()?.dispatch;
1360
+ if (!dispatch9) {
1361
+ return;
1362
+ }
1363
+ dispatch9(slice.actions.setPath(path));
1364
+ dispatch9(slice.actions.setCurrentComponentId(currentComponentId));
1365
+ }
1366
+
1367
+ // src/components/edit-component/component-modal.tsx
1368
+ import * as React10 from "react";
1369
+ import { useEffect as useEffect4 } from "react";
1370
+ import { createPortal } from "react-dom";
1371
+ import { __ as __9 } from "@wordpress/i18n";
1372
+
1373
+ // src/hooks/use-canvas-document.ts
1374
+ import { getCanvasIframeDocument } from "@elementor/editor-canvas";
1375
+ import { __privateUseListenTo as useListenTo, commandEndEvent } from "@elementor/editor-v1-adapters";
1376
+ function useCanvasDocument() {
1377
+ return useListenTo(commandEndEvent("editor/documents/attach-preview"), () => getCanvasIframeDocument());
1378
+ }
1379
+
1380
+ // src/hooks/use-element-rect.ts
1381
+ import { useEffect as useEffect3, useState as useState3 } from "react";
1382
+ import { throttle } from "@elementor/utils";
1383
+ function useElementRect(element) {
1384
+ const [rect, setRect] = useState3(new DOMRect(0, 0, 0, 0));
1385
+ const onChange = throttle(
1386
+ () => {
1387
+ setRect(element?.getBoundingClientRect() ?? new DOMRect(0, 0, 0, 0));
1388
+ },
1389
+ 20,
1390
+ true
1391
+ );
1392
+ useScrollListener({ element, onChange });
1393
+ useResizeListener({ element, onChange });
1394
+ useMutationsListener({ element, onChange });
1395
+ useEffect3(
1396
+ () => () => {
1397
+ onChange.cancel();
1398
+ },
1399
+ [onChange]
1400
+ );
1401
+ return rect;
1402
+ }
1403
+ function useScrollListener({ element, onChange }) {
1404
+ useEffect3(() => {
1405
+ if (!element) {
1406
+ return;
1407
+ }
1408
+ const win = element.ownerDocument?.defaultView;
1409
+ win?.addEventListener("scroll", onChange, { passive: true });
1410
+ return () => {
1411
+ win?.removeEventListener("scroll", onChange);
1412
+ };
1413
+ }, [element, onChange]);
1414
+ }
1415
+ function useResizeListener({ element, onChange }) {
1416
+ useEffect3(() => {
1417
+ if (!element) {
1418
+ return;
1419
+ }
1420
+ const resizeObserver = new ResizeObserver(onChange);
1421
+ resizeObserver.observe(element);
1422
+ const win = element.ownerDocument?.defaultView;
1423
+ win?.addEventListener("resize", onChange, { passive: true });
1424
+ return () => {
1425
+ resizeObserver.disconnect();
1426
+ win?.removeEventListener("resize", onChange);
1427
+ };
1428
+ }, [element, onChange]);
1429
+ }
1430
+ function useMutationsListener({ element, onChange }) {
1431
+ useEffect3(() => {
1432
+ if (!element) {
1433
+ return;
1434
+ }
1435
+ const mutationObserver = new MutationObserver(onChange);
1436
+ mutationObserver.observe(element, { childList: true, subtree: true });
1437
+ return () => {
1438
+ mutationObserver.disconnect();
1439
+ };
1440
+ }, [element, onChange]);
1441
+ }
1442
+
1443
+ // src/components/edit-component/component-modal.tsx
1444
+ function ComponentModal({ element, onClose }) {
1445
+ const canvasDocument = useCanvasDocument();
1446
+ useEffect4(() => {
1447
+ const handleEsc = (event) => {
1448
+ if (event.key === "Escape") {
1449
+ onClose();
1450
+ }
1451
+ };
1452
+ canvasDocument?.body.addEventListener("keydown", handleEsc);
1453
+ return () => {
1454
+ canvasDocument?.body.removeEventListener("keydown", handleEsc);
1455
+ };
1456
+ }, [canvasDocument, onClose]);
1457
+ if (!canvasDocument?.body) {
1458
+ return null;
1459
+ }
1460
+ return createPortal(
1461
+ /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(BlockEditPage, null), /* @__PURE__ */ React10.createElement(Backdrop, { canvas: canvasDocument, element, onClose })),
1462
+ canvasDocument.body
1463
+ );
1464
+ }
1465
+ function Backdrop({ canvas, element, onClose }) {
1466
+ const rect = useElementRect(element);
1467
+ const backdropStyle = {
1468
+ position: "fixed",
1469
+ top: 0,
1470
+ left: 0,
1471
+ width: "100vw",
1472
+ height: "100vh",
1473
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
1474
+ zIndex: 999,
1475
+ pointerEvents: "painted",
1476
+ cursor: "pointer",
1477
+ clipPath: getRoundedRectPath(rect, canvas.defaultView, 5)
1478
+ };
1479
+ const handleKeyDown = (event) => {
1480
+ if (event.key === "Enter" || event.key === " ") {
1481
+ event.preventDefault();
1482
+ onClose();
1483
+ }
1484
+ };
1485
+ return /* @__PURE__ */ React10.createElement(
1486
+ "div",
278
1487
  {
279
- onClick: handleSubmit,
280
- disabled: isSubmitting || !isValid,
281
- variant: "contained",
282
- color: "primary",
283
- size: "small"
1488
+ style: backdropStyle,
1489
+ onClick: onClose,
1490
+ onKeyDown: handleKeyDown,
1491
+ role: "button",
1492
+ tabIndex: 0,
1493
+ "aria-label": __9("Exit component editing mode", "elementor")
1494
+ }
1495
+ );
1496
+ }
1497
+ function getRoundedRectPath(rect, viewport, borderRadius) {
1498
+ const padding = borderRadius / 2;
1499
+ const { x: originalX, y: originalY, width: originalWidth, height: originalHeight } = rect;
1500
+ const x = originalX - padding;
1501
+ const y = originalY - padding;
1502
+ const width = originalWidth + 2 * padding;
1503
+ const height = originalHeight + 2 * padding;
1504
+ const radius = Math.min(borderRadius, width / 2, height / 2);
1505
+ const { innerWidth: vw, innerHeight: vh } = viewport;
1506
+ const path = `path(evenodd, 'M 0 0
1507
+ L ${vw} 0
1508
+ L ${vw} ${vh}
1509
+ L 0 ${vh}
1510
+ Z
1511
+ M ${x + radius} ${y}
1512
+ L ${x + width - radius} ${y}
1513
+ A ${radius} ${radius} 0 0 1 ${x + width} ${y + radius}
1514
+ L ${x + width} ${y + height - radius}
1515
+ A ${radius} ${radius} 0 0 1 ${x + width - radius} ${y + height}
1516
+ L ${x + radius} ${y + height}
1517
+ A ${radius} ${radius} 0 0 1 ${x} ${y + height - radius}
1518
+ L ${x} ${y + radius}
1519
+ A ${radius} ${radius} 0 0 1 ${x + radius} ${y}
1520
+ Z'
1521
+ )`;
1522
+ return path.replace(/\s{2,}/g, " ");
1523
+ }
1524
+ function BlockEditPage() {
1525
+ const blockV3DocumentHandlesStyles = `
1526
+ .elementor-editor-active {
1527
+ & .elementor-section-wrap.ui-sortable {
1528
+ display: contents;
1529
+ }
1530
+
1531
+ & *[data-editable-elementor-document]:not(.elementor-edit-mode):hover {
1532
+ & .elementor-document-handle:not(.elementor-document-save-back-handle) {
1533
+ display: none;
1534
+
1535
+ &::before,
1536
+ & .elementor-document-handle__inner {
1537
+ display: none;
1538
+ }
1539
+ }
1540
+ }
1541
+ }
1542
+ `;
1543
+ return /* @__PURE__ */ React10.createElement("style", { "data-e-style-id": "e-block-v3-document-handles-styles" }, blockV3DocumentHandlesStyles);
1544
+ }
1545
+
1546
+ // src/components/edit-component/edit-component.tsx
1547
+ function EditComponent() {
1548
+ const currentComponentId = useSelector5(selectCurrentComponentId);
1549
+ useHandleDocumentSwitches();
1550
+ const onBack = useNavigateBack();
1551
+ const elementDom = getComponentDOMElement(currentComponentId ?? void 0);
1552
+ if (!elementDom) {
1553
+ return null;
1554
+ }
1555
+ return /* @__PURE__ */ React11.createElement(ComponentModal, { element: elementDom, onClose: onBack });
1556
+ }
1557
+ function useHandleDocumentSwitches() {
1558
+ const documentsManager = getV1DocumentsManager4();
1559
+ const currentComponentId = useSelector5(selectCurrentComponentId);
1560
+ const path = useSelector5(selectPath);
1561
+ useEffect5(() => {
1562
+ return listenTo(commandEndEvent2("editor/documents/attach-preview"), () => {
1563
+ const nextDocument = documentsManager.getCurrent();
1564
+ if (nextDocument.id === currentComponentId) {
1565
+ return;
1566
+ }
1567
+ if (currentComponentId) {
1568
+ apiClient.unlockComponent(currentComponentId);
1569
+ }
1570
+ const isComponent = nextDocument.config.type === COMPONENT_DOCUMENT_TYPE;
1571
+ if (!isComponent) {
1572
+ updateCurrentComponent({ path: [], currentComponentId: null });
1573
+ return;
1574
+ }
1575
+ updateCurrentComponent({
1576
+ path: getUpdatedComponentPath(path, nextDocument),
1577
+ currentComponentId: nextDocument.id
1578
+ });
1579
+ });
1580
+ }, [path, documentsManager, currentComponentId]);
1581
+ }
1582
+ function getUpdatedComponentPath(path, nextDocument) {
1583
+ const componentIndex = path.findIndex(({ componentId }) => componentId === nextDocument.id);
1584
+ if (componentIndex >= 0) {
1585
+ return path.slice(0, componentIndex + 1);
1586
+ }
1587
+ return [
1588
+ ...path,
1589
+ {
1590
+ instanceId: nextDocument?.container.view?.el?.dataset.id,
1591
+ componentId: nextDocument.id
1592
+ }
1593
+ ];
1594
+ }
1595
+ function getComponentDOMElement(id) {
1596
+ if (!id) {
1597
+ return null;
1598
+ }
1599
+ const documentsManager = getV1DocumentsManager4();
1600
+ const currentComponent = documentsManager.get(id);
1601
+ const widget = currentComponent?.container;
1602
+ const container = widget?.view?.el?.children?.[0] ?? null;
1603
+ const elementDom = container?.children[0];
1604
+ return elementDom ?? null;
1605
+ }
1606
+
1607
+ // src/components/in-edit-mode.tsx
1608
+ import * as React12 from "react";
1609
+ import { closeDialog, openDialog } from "@elementor/editor-ui";
1610
+ import { InfoCircleFilledIcon } from "@elementor/icons";
1611
+ import { Box as Box7, Button as Button2, DialogActions, DialogContent, DialogHeader, Icon as Icon2, Stack as Stack6, Typography as Typography5 } from "@elementor/ui";
1612
+ import { __ as __10 } from "@wordpress/i18n";
1613
+ var openEditModeDialog = (lockedBy) => {
1614
+ openDialog({
1615
+ component: /* @__PURE__ */ React12.createElement(EditModeDialog, { lockedBy })
1616
+ });
1617
+ };
1618
+ var EditModeDialog = ({ lockedBy }) => {
1619
+ const content = __10("%s is currently editing this document", "elementor").replace("%s", lockedBy);
1620
+ return /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(DialogHeader, { logo: false }, /* @__PURE__ */ React12.createElement(Box7, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React12.createElement(Icon2, { color: "secondary" }, /* @__PURE__ */ React12.createElement(InfoCircleFilledIcon, { fontSize: "medium" })), /* @__PURE__ */ React12.createElement(Typography5, { variant: "subtitle1" }, content))), /* @__PURE__ */ React12.createElement(DialogContent, null, /* @__PURE__ */ React12.createElement(Stack6, { spacing: 2, direction: "column" }, /* @__PURE__ */ React12.createElement(Typography5, { variant: "body2" }, __10(
1621
+ "You can wait for them to finish or reach out to coordinate your changes together.",
1622
+ "elementor"
1623
+ )), /* @__PURE__ */ React12.createElement(DialogActions, null, /* @__PURE__ */ React12.createElement(Button2, { color: "secondary", variant: "contained", onClick: closeDialog }, __10("Close", "elementor"))))));
1624
+ };
1625
+
1626
+ // src/components/overridable-props/overridable-prop-control.tsx
1627
+ import * as React13 from "react";
1628
+ import { ControlReplacementsProvider, PropKeyProvider, PropProvider, useBoundProp } from "@elementor/editor-controls";
1629
+ import { createTopLevelObjectType, useElement } from "@elementor/editor-editing-panel";
1630
+ import { __getState as getState6 } from "@elementor/store";
1631
+
1632
+ // src/prop-types/component-overridable-prop-type.ts
1633
+ import { createPropUtils } from "@elementor/editor-props";
1634
+ import { z as z2 } from "@elementor/schema";
1635
+ var componentOverridablePropTypeUtil = createPropUtils(
1636
+ "overridable",
1637
+ z2.object({
1638
+ override_key: z2.string(),
1639
+ origin_value: z2.object({
1640
+ $$type: z2.string(),
1641
+ value: z2.unknown()
1642
+ }).nullable()
1643
+ })
1644
+ );
1645
+
1646
+ // src/store/actions/update-overridable-prop-origin-value.ts
1647
+ import { __dispatch as dispatch4, __getState as getState5 } from "@elementor/store";
1648
+ function updateOverridablePropOriginValue(componentId, propValue) {
1649
+ const overridableProps = selectOverridableProps(getState5(), componentId);
1650
+ if (!overridableProps) {
1651
+ return;
1652
+ }
1653
+ const existingOverridableProp = overridableProps.props[propValue.override_key];
1654
+ if (!existingOverridableProp) {
1655
+ return;
1656
+ }
1657
+ const newOverridableProps = {
1658
+ ...overridableProps,
1659
+ props: {
1660
+ ...overridableProps.props,
1661
+ [existingOverridableProp.overrideKey]: {
1662
+ ...existingOverridableProp,
1663
+ originValue: propValue.origin_value
1664
+ }
1665
+ }
1666
+ };
1667
+ dispatch4(
1668
+ slice.actions.setOverridableProps({
1669
+ componentId,
1670
+ overridableProps: newOverridableProps
1671
+ })
1672
+ );
1673
+ }
1674
+
1675
+ // src/components/overridable-props/overridable-prop-control.tsx
1676
+ function OverridablePropControl({
1677
+ OriginalControl,
1678
+ ...props
1679
+ }) {
1680
+ const { elementType } = useElement();
1681
+ const { value, bind, setValue, placeholder, ...propContext } = useBoundProp(componentOverridablePropTypeUtil);
1682
+ const componentId = selectCurrentComponentId(getState6());
1683
+ if (!componentId) {
1684
+ throw new Error("Component ID is required");
1685
+ }
1686
+ if (!value?.override_key) {
1687
+ throw new Error("Override key is required");
1688
+ }
1689
+ const setOverridableValue = (newValue) => {
1690
+ const propValue = {
1691
+ ...value,
1692
+ origin_value: newValue[bind]
1693
+ };
1694
+ setValue(propValue);
1695
+ updateOverridablePropOriginValue(componentId, propValue);
1696
+ };
1697
+ const propType = createTopLevelObjectType({
1698
+ schema: {
1699
+ [bind]: elementType.propsSchema[bind]
1700
+ }
1701
+ });
1702
+ const objectPlaceholder = placeholder ? { [bind]: placeholder } : void 0;
1703
+ return /* @__PURE__ */ React13.createElement(
1704
+ PropProvider,
1705
+ {
1706
+ ...propContext,
1707
+ propType,
1708
+ setValue: setOverridableValue,
1709
+ value: { [bind]: value.origin_value },
1710
+ placeholder: objectPlaceholder
284
1711
  },
285
- isSubmitting ? __2("Creating\u2026", "elementor") : __2("Create", "elementor")
286
- )));
1712
+ /* @__PURE__ */ React13.createElement(PropKeyProvider, { bind }, /* @__PURE__ */ React13.createElement(ControlReplacementsProvider, { replacements: [] }, /* @__PURE__ */ React13.createElement(OriginalControl, { ...props })))
1713
+ );
1714
+ }
1715
+
1716
+ // src/components/overridable-props/overridable-prop-indicator.tsx
1717
+ import * as React16 from "react";
1718
+ import { useBoundProp as useBoundProp2 } from "@elementor/editor-controls";
1719
+ import { getV1CurrentDocument } from "@elementor/editor-documents";
1720
+ import { useElement as useElement2 } from "@elementor/editor-editing-panel";
1721
+ import { getWidgetsCache } from "@elementor/editor-elements";
1722
+ import { __getState as getState9 } from "@elementor/store";
1723
+ import { bindPopover, bindTrigger as bindTrigger2, Popover as Popover2, Tooltip as Tooltip2, usePopupState as usePopupState2 } from "@elementor/ui";
1724
+ import { __ as __14 } from "@wordpress/i18n";
1725
+
1726
+ // src/store/actions/set-overridable-prop.ts
1727
+ import { __dispatch as dispatch5, __getState as getState7 } from "@elementor/store";
1728
+ import { generateUniqueId as generateUniqueId2 } from "@elementor/utils";
1729
+ import { __ as __11 } from "@wordpress/i18n";
1730
+ function setOverridableProp({
1731
+ componentId,
1732
+ overrideKey,
1733
+ elementId,
1734
+ label,
1735
+ groupId,
1736
+ propKey,
1737
+ elType,
1738
+ widgetType,
1739
+ originValue
1740
+ }) {
1741
+ const overridableProps = selectOverridableProps(getState7(), componentId);
1742
+ if (!overridableProps) {
1743
+ return;
1744
+ }
1745
+ const existingOverridableProp = overrideKey ? overridableProps.props[overrideKey] : null;
1746
+ const duplicatedTargetProps = Object.values(overridableProps.props).filter(
1747
+ (prop) => prop.elementId === elementId && prop.propKey === propKey && prop !== existingOverridableProp
1748
+ );
1749
+ const { props: prevProps, groups: prevGroups } = { ...overridableProps };
1750
+ const { groups: updatedGroups, currentGroupId } = getUpdatedGroups(
1751
+ prevGroups,
1752
+ groupId || existingOverridableProp?.groupId
1753
+ );
1754
+ const overridableProp = {
1755
+ overrideKey: existingOverridableProp?.overrideKey || generateUniqueId2("prop"),
1756
+ label,
1757
+ elementId,
1758
+ propKey,
1759
+ widgetType,
1760
+ elType,
1761
+ originValue,
1762
+ groupId: currentGroupId
1763
+ };
1764
+ const { props: propsWithoutDuplicates, groups: groupsWithoutDuplicates } = removeProps({
1765
+ props: prevProps,
1766
+ groups: updatedGroups,
1767
+ propsToRemove: duplicatedTargetProps
1768
+ });
1769
+ const props = {
1770
+ ...propsWithoutDuplicates,
1771
+ [overridableProp.overrideKey]: overridableProp
1772
+ };
1773
+ const groups = {
1774
+ items: {
1775
+ ...groupsWithoutDuplicates.items,
1776
+ [currentGroupId]: getGroupWithProp(groupsWithoutDuplicates, currentGroupId, overridableProp)
1777
+ },
1778
+ order: groupsWithoutDuplicates.order.includes(currentGroupId) ? groupsWithoutDuplicates.order : [...groupsWithoutDuplicates.order, currentGroupId]
1779
+ };
1780
+ const isChangingGroups = existingOverridableProp && existingOverridableProp.groupId !== currentGroupId;
1781
+ if (isChangingGroups) {
1782
+ groups.items[existingOverridableProp.groupId] = getGroupWithoutProp(
1783
+ groupsWithoutDuplicates,
1784
+ existingOverridableProp.groupId,
1785
+ overridableProp
1786
+ );
1787
+ }
1788
+ dispatch5(
1789
+ slice.actions.setOverridableProps({
1790
+ componentId,
1791
+ overridableProps: {
1792
+ props,
1793
+ groups
1794
+ }
1795
+ })
1796
+ );
1797
+ return overridableProp;
1798
+ }
1799
+ function getUpdatedGroups(groups, groupId) {
1800
+ if (!groupId) {
1801
+ if (groups.order.length > 0) {
1802
+ return { groups, currentGroupId: groups.order[0] };
1803
+ }
1804
+ return addNewGroup(groups);
1805
+ }
1806
+ if (!groups.items[groupId]) {
1807
+ return addNewGroup(groups, groupId);
1808
+ }
1809
+ return { groups, currentGroupId: groupId };
1810
+ }
1811
+ function addNewGroup(groups, groupId) {
1812
+ const currentGroupId = groupId || generateUniqueId2("group");
1813
+ const updatedGroups = {
1814
+ ...groups,
1815
+ items: {
1816
+ ...groups.items,
1817
+ [currentGroupId]: {
1818
+ id: currentGroupId,
1819
+ label: __11("Default", "elementor"),
1820
+ props: []
1821
+ }
1822
+ },
1823
+ order: [...groups.order, currentGroupId]
1824
+ };
1825
+ return { groups: updatedGroups, currentGroupId };
1826
+ }
1827
+ function getGroupWithProp(groups, groupId, overridableProp) {
1828
+ const group = { ...groups.items[groupId] };
1829
+ if (!group.props.includes(overridableProp.overrideKey)) {
1830
+ group.props = [...group.props, overridableProp.overrideKey];
1831
+ }
1832
+ return group;
1833
+ }
1834
+ function getGroupWithoutProp(groups, groupId, overridableProp) {
1835
+ const group = { ...groups.items[groupId] };
1836
+ if (group) {
1837
+ group.props = group.props.filter((key) => key !== overridableProp.overrideKey);
1838
+ }
1839
+ return group;
1840
+ }
1841
+ function removeProps({
1842
+ props,
1843
+ groups,
1844
+ propsToRemove
1845
+ }) {
1846
+ const allProps = Object.fromEntries(
1847
+ Object.entries(props).filter(([, prop]) => !propsToRemove.includes(prop))
1848
+ );
1849
+ const overrideKeysToRemove = propsToRemove.map((prop) => prop.overrideKey);
1850
+ const allGroupItems = Object.fromEntries(
1851
+ Object.entries(groups.items).map(([groupId, group]) => [
1852
+ groupId,
1853
+ {
1854
+ ...group,
1855
+ props: group.props.filter((prop) => !overrideKeysToRemove.includes(prop))
1856
+ }
1857
+ ])
1858
+ );
1859
+ return {
1860
+ props: allProps,
1861
+ groups: {
1862
+ items: allGroupItems,
1863
+ order: groups.order.filter((groupId) => !overrideKeysToRemove.includes(groupId))
1864
+ }
1865
+ };
1866
+ }
1867
+
1868
+ // src/components/overridable-props/indicator.tsx
1869
+ import * as React14 from "react";
1870
+ import { forwardRef } from "react";
1871
+ import { CheckIcon, PlusIcon } from "@elementor/icons";
1872
+ import { Box as Box8, styled as styled2 } from "@elementor/ui";
1873
+ import { __ as __12 } from "@wordpress/i18n";
1874
+ var SIZE = "tiny";
1875
+ var IconContainer = styled2(Box8)`
1876
+ pointer-events: none;
1877
+ opacity: 0;
1878
+ transition: opacity 0.2s ease-in-out;
1879
+
1880
+ & > svg {
1881
+ position: absolute;
1882
+ top: 50%;
1883
+ left: 50%;
1884
+ transform: translate( -50%, -50% );
1885
+ width: 10px;
1886
+ height: 10px;
1887
+ fill: ${({ theme }) => theme.palette.primary.contrastText};
1888
+ stroke: ${({ theme }) => theme.palette.primary.contrastText};
1889
+ stroke-width: 2px;
1890
+ }
1891
+ `;
1892
+ var Content = styled2(Box8)`
1893
+ position: relative;
1894
+ display: flex;
1895
+ align-items: center;
1896
+ justify-content: center;
1897
+ cursor: pointer;
1898
+ width: 16px;
1899
+ height: 16px;
1900
+ margin-inline: ${({ theme }) => theme.spacing(0.5)};
1901
+
1902
+ &:before {
1903
+ content: '';
1904
+ display: block;
1905
+ position: absolute;
1906
+ top: 50%;
1907
+ left: 50%;
1908
+ transform: translate( -50%, -50% ) rotate( 45deg );
1909
+ width: 5px;
1910
+ height: 5px;
1911
+ border-radius: 1px;
1912
+ background-color: ${({ theme }) => theme.palette.primary.main};
1913
+ transition: all 0.1s ease-in-out;
1914
+ }
1915
+
1916
+ &:hover,
1917
+ &.enlarged {
1918
+ &:before {
1919
+ width: 12px;
1920
+ height: 12px;
1921
+ border-radius: 2px;
1922
+ }
1923
+
1924
+ .icon {
1925
+ opacity: 1;
1926
+ }
1927
+ }
1928
+ `;
1929
+ var Indicator = forwardRef(({ isOpen, isOverridable, ...props }, ref) => /* @__PURE__ */ React14.createElement(Content, { ref, ...props, className: isOpen || isOverridable ? "enlarged" : "" }, /* @__PURE__ */ React14.createElement(
1930
+ IconContainer,
1931
+ {
1932
+ className: "icon",
1933
+ "aria-label": isOverridable ? __12("Overridable property", "elementor") : __12("Make prop overridable", "elementor")
1934
+ },
1935
+ isOverridable ? /* @__PURE__ */ React14.createElement(CheckIcon, { fontSize: SIZE }) : /* @__PURE__ */ React14.createElement(PlusIcon, { fontSize: SIZE })
1936
+ )));
1937
+
1938
+ // src/components/overridable-props/overridable-prop-form.tsx
1939
+ import * as React15 from "react";
1940
+ import { useState as useState4 } from "react";
1941
+ import { Form as Form2, MenuListItem as MenuListItem2 } from "@elementor/editor-ui";
1942
+ import { Button as Button3, FormLabel as FormLabel2, Grid as Grid2, Select, Stack as Stack7, TextField as TextField3, Typography as Typography6 } from "@elementor/ui";
1943
+ import { __ as __13 } from "@wordpress/i18n";
1944
+ var SIZE2 = "tiny";
1945
+ var DEFAULT_GROUP = { value: null, label: __13("Default", "elementor") };
1946
+ function OverridablePropForm({ onSubmit, groups, currentValue }) {
1947
+ const [propLabel, setPropLabel] = useState4(currentValue?.label ?? null);
1948
+ const [group, setGroup] = useState4(currentValue?.groupId ?? groups?.[0]?.value ?? null);
1949
+ const name = __13("Name", "elementor");
1950
+ const groupName = __13("Group Name", "elementor");
1951
+ const isCreate = currentValue === void 0;
1952
+ const title = isCreate ? __13("Create new property", "elementor") : __13("Update property", "elementor");
1953
+ const ctaLabel = isCreate ? __13("Create", "elementor") : __13("Update", "elementor");
1954
+ return /* @__PURE__ */ React15.createElement(Form2, { onSubmit: () => onSubmit({ label: propLabel ?? "", group }) }, /* @__PURE__ */ React15.createElement(Stack7, { alignItems: "start", width: "268px" }, /* @__PURE__ */ React15.createElement(
1955
+ Stack7,
1956
+ {
1957
+ direction: "row",
1958
+ alignItems: "center",
1959
+ py: 1,
1960
+ px: 1.5,
1961
+ sx: { columnGap: 0.5, borderBottom: "1px solid", borderColor: "divider", width: "100%", mb: 1.5 }
1962
+ },
1963
+ /* @__PURE__ */ React15.createElement(Typography6, { variant: "caption", sx: { color: "text.primary", fontWeight: "500", lineHeight: 1 } }, title)
1964
+ ), /* @__PURE__ */ React15.createElement(Grid2, { container: true, gap: 0.75, alignItems: "start", px: 1.5, mb: 1.5 }, /* @__PURE__ */ React15.createElement(Grid2, { item: true, xs: 12 }, /* @__PURE__ */ React15.createElement(FormLabel2, { size: "tiny" }, name)), /* @__PURE__ */ React15.createElement(Grid2, { item: true, xs: 12 }, /* @__PURE__ */ React15.createElement(
1965
+ TextField3,
1966
+ {
1967
+ name,
1968
+ size: SIZE2,
1969
+ fullWidth: true,
1970
+ placeholder: __13("Enter value", "elementor"),
1971
+ value: propLabel ?? "",
1972
+ onChange: (e) => setPropLabel(e.target.value)
1973
+ }
1974
+ ))), /* @__PURE__ */ React15.createElement(Grid2, { container: true, gap: 0.75, alignItems: "start", px: 1.5, mb: 1.5 }, /* @__PURE__ */ React15.createElement(Grid2, { item: true, xs: 12 }, /* @__PURE__ */ React15.createElement(FormLabel2, { size: "tiny" }, groupName)), /* @__PURE__ */ React15.createElement(Grid2, { item: true, xs: 12 }, /* @__PURE__ */ React15.createElement(
1975
+ Select,
1976
+ {
1977
+ name: groupName,
1978
+ size: SIZE2,
1979
+ fullWidth: true,
1980
+ value: group ?? null,
1981
+ onChange: setGroup,
1982
+ displayEmpty: true,
1983
+ renderValue: (selectedValue) => {
1984
+ if (!selectedValue || selectedValue === "") {
1985
+ const [firstGroup = DEFAULT_GROUP] = groups ?? [];
1986
+ return firstGroup.label;
1987
+ }
1988
+ return groups?.find(({ value }) => value === selectedValue)?.label ?? selectedValue;
1989
+ }
1990
+ },
1991
+ (groups ?? [DEFAULT_GROUP]).map(({ label: groupLabel, ...props }) => /* @__PURE__ */ React15.createElement(MenuListItem2, { key: props.value, ...props, value: props.value ?? "" }, groupLabel))
1992
+ ))), /* @__PURE__ */ React15.createElement(Stack7, { direction: "row", justifyContent: "flex-end", alignSelf: "end", mt: 1.5, py: 1, px: 1.5 }, /* @__PURE__ */ React15.createElement(Button3, { type: "submit", disabled: !propLabel, variant: "contained", color: "primary", size: "small" }, ctaLabel))));
1993
+ }
1994
+
1995
+ // src/components/overridable-props/utils/get-overridable-prop.ts
1996
+ import { __getState as getState8 } from "@elementor/store";
1997
+ function getOverridableProp({
1998
+ componentId,
1999
+ overrideKey
2000
+ }) {
2001
+ const overridableProps = selectOverridableProps(getState8(), componentId);
2002
+ if (!overridableProps) {
2003
+ return void 0;
2004
+ }
2005
+ return overridableProps.props[overrideKey];
2006
+ }
2007
+
2008
+ // src/components/overridable-props/overridable-prop-indicator.tsx
2009
+ var FORBIDDEN_KEYS = ["_cssid", "attributes"];
2010
+ function OverridablePropIndicator() {
2011
+ const { bind } = useBoundProp2();
2012
+ const currentDocument = getV1CurrentDocument();
2013
+ if (currentDocument.config.type !== COMPONENT_DOCUMENT_TYPE || !currentDocument.id) {
2014
+ return null;
2015
+ }
2016
+ if (!isPropAllowed(bind)) {
2017
+ return null;
2018
+ }
2019
+ const overridableProps = selectOverridableProps(getState9(), currentDocument.id);
2020
+ return /* @__PURE__ */ React16.createElement(Content2, { componentId: currentDocument.id, overridableProps });
2021
+ }
2022
+ function Content2({ componentId, overridableProps }) {
2023
+ const {
2024
+ element: { id: elementId },
2025
+ elementType
2026
+ } = useElement2();
2027
+ const { value, bind, propType } = useBoundProp2();
2028
+ const { value: overridableValue, setValue: setOverridableValue } = useBoundProp2(componentOverridablePropTypeUtil);
2029
+ const popupState = usePopupState2({
2030
+ variant: "popover"
2031
+ });
2032
+ const triggerProps = bindTrigger2(popupState);
2033
+ const popoverProps = bindPopover(popupState);
2034
+ const { elType } = getWidgetsCache()?.[elementType.key] ?? { elType: "widget" };
2035
+ const handleSubmit = ({ label, group }) => {
2036
+ const originValue = !overridableValue ? value ?? propType.default : overridableValue?.origin_value ?? {};
2037
+ const overridablePropConfig = setOverridableProp({
2038
+ componentId,
2039
+ overrideKey: overridableValue?.override_key ?? null,
2040
+ elementId,
2041
+ label,
2042
+ groupId: group,
2043
+ propKey: bind,
2044
+ elType: elType ?? "widget",
2045
+ widgetType: elementType.key,
2046
+ originValue
2047
+ });
2048
+ if (!overridableValue && overridablePropConfig) {
2049
+ setOverridableValue({
2050
+ override_key: overridablePropConfig.overrideKey,
2051
+ origin_value: originValue
2052
+ });
2053
+ }
2054
+ popupState.close();
2055
+ };
2056
+ const overridableConfig = overridableValue ? getOverridableProp({ componentId, overrideKey: overridableValue.override_key }) : void 0;
2057
+ return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(Tooltip2, { placement: "top", title: __14("Override Property", "elementor") }, /* @__PURE__ */ React16.createElement(Indicator, { ...triggerProps, isOpen: !!popoverProps.open, isOverridable: !!overridableValue })), /* @__PURE__ */ React16.createElement(
2058
+ Popover2,
2059
+ {
2060
+ disableScrollLock: true,
2061
+ anchorOrigin: {
2062
+ vertical: "bottom",
2063
+ horizontal: "right"
2064
+ },
2065
+ transformOrigin: {
2066
+ vertical: "top",
2067
+ horizontal: "right"
2068
+ },
2069
+ PaperProps: {
2070
+ sx: { my: 2.5 }
2071
+ },
2072
+ ...popoverProps
2073
+ },
2074
+ /* @__PURE__ */ React16.createElement(
2075
+ OverridablePropForm,
2076
+ {
2077
+ onSubmit: handleSubmit,
2078
+ groups: overridableProps?.groups.order.map((groupId) => ({
2079
+ value: groupId,
2080
+ label: overridableProps.groups.items[groupId].label
2081
+ })),
2082
+ currentValue: overridableConfig
2083
+ }
2084
+ )
2085
+ ));
2086
+ }
2087
+ function isPropAllowed(bind) {
2088
+ return !FORBIDDEN_KEYS.includes(bind);
2089
+ }
2090
+
2091
+ // src/mcp/index.ts
2092
+ import { getMCPByDomain as getMCPByDomain2 } from "@elementor/editor-mcp";
2093
+
2094
+ // src/mcp/save-as-component-tool.ts
2095
+ import { getContainer as getContainer2 } from "@elementor/editor-elements";
2096
+ import { getMCPByDomain } from "@elementor/editor-mcp";
2097
+ import { z as z3 } from "@elementor/schema";
2098
+ var InputSchema = {
2099
+ element_id: z3.string().describe(
2100
+ 'The unique identifier of the element to save as a component. Use the "list-elements" tool to find available element IDs in the current document.'
2101
+ ),
2102
+ component_name: z3.string().describe("The name for the new component. Should be descriptive and unique among existing components.")
2103
+ };
2104
+ var OutputSchema = {
2105
+ message: z3.string().optional().describe("Additional information about the operation result"),
2106
+ component_uid: z3.string().optional().describe("The unique identifier of the newly created component (only present on success)")
2107
+ };
2108
+ var VALID_ELEMENT_TYPES = ["e-div-block", "e-flexbox", "e-tabs"];
2109
+ var ERROR_MESSAGES = {
2110
+ ELEMENT_NOT_FOUND: "Element not found. Use 'list-elements' to get valid element IDs.",
2111
+ ELEMENT_NOT_ONE_OF_TYPES: `Element is not one of the following types: ${VALID_ELEMENT_TYPES.join(", ")}`,
2112
+ ELEMENT_IS_LOCKED: "Cannot save a locked element as a component."
2113
+ };
2114
+ var handleSaveAsComponent = async (params) => {
2115
+ const { element_id: elementId, component_name: componentName } = params;
2116
+ const container = getContainer2(elementId);
2117
+ if (!container) {
2118
+ throw new Error(ERROR_MESSAGES.ELEMENT_NOT_FOUND);
2119
+ }
2120
+ const elType = container.model.get("elType");
2121
+ if (!VALID_ELEMENT_TYPES.includes(elType)) {
2122
+ throw new Error(ERROR_MESSAGES.ELEMENT_NOT_ONE_OF_TYPES);
2123
+ }
2124
+ const element = container.model.toJSON({ remove: ["default"] });
2125
+ if (element?.isLocked) {
2126
+ throw new Error(ERROR_MESSAGES.ELEMENT_IS_LOCKED);
2127
+ }
2128
+ const uid = createUnpublishedComponent(componentName, element, null);
2129
+ return {
2130
+ status: "ok",
2131
+ message: `Component "${componentName}" created successfully.`,
2132
+ component_uid: uid
2133
+ };
2134
+ };
2135
+ var initSaveAsComponentTool = () => {
2136
+ return getMCPByDomain("components").addTool({
2137
+ name: "save-as-component",
2138
+ schema: InputSchema,
2139
+ outputSchema: OutputSchema,
2140
+ description: `Save an existing element as a reusable component in the Elementor editor.
2141
+
2142
+ ## When NOT to use this tool:
2143
+ - Do not use for elements that are already components (widgetType: 'e-component').
2144
+ - Do not use for locked elements.
2145
+ - Do not guess element IDs. Always use "list-elements" first to get valid IDs.
2146
+
2147
+ ## Prerequisites:
2148
+ - **Verify element type**: Ensure the element is not already a component (widgetType should not be 'e-component').
2149
+ - **Check if element is unlocked**: Locked elements cannot be saved as components.
2150
+ - **Check that the element is one of the following types**: ${VALID_ELEMENT_TYPES.join(", ")}
2151
+
2152
+ ## Required parameters:
2153
+ - **element_id**: The unique ID of the element to save.
2154
+ - **component_name**: A descriptive name for the component (2-50 characters).
2155
+
2156
+ ## Example tool call:
2157
+ \`\`\`json
2158
+ { "element_id": "abc123", "component_name": "Hero Section" }
2159
+ \`\`\`
2160
+ `,
2161
+ handler: handleSaveAsComponent
2162
+ });
2163
+ };
2164
+
2165
+ // src/mcp/index.ts
2166
+ function initMcp() {
2167
+ const { setMCPDescription } = getMCPByDomain2("components");
2168
+ setMCPDescription(
2169
+ `Elementor Editor Components MCP - Tools for creating and managing reusable components.
2170
+ Components are reusable blocks of content that can be used multiple times across the pages, its a widget which contains a set of elements and styles.`
2171
+ );
2172
+ initSaveAsComponentTool();
2173
+ }
2174
+
2175
+ // src/populate-store.ts
2176
+ import { useEffect as useEffect6 } from "react";
2177
+ import { __dispatch as dispatch6 } from "@elementor/store";
2178
+ function PopulateStore() {
2179
+ useEffect6(() => {
2180
+ dispatch6(loadComponents());
2181
+ }, []);
2182
+ return null;
2183
+ }
2184
+
2185
+ // src/store/actions/remove-component-styles.ts
2186
+ import { __dispatch as dispatch7 } from "@elementor/store";
2187
+ function removeComponentStyles(id) {
2188
+ apiClient.invalidateComponentConfigCache(id);
2189
+ dispatch7(slice.actions.removeStyles({ id }));
2190
+ }
2191
+
2192
+ // src/store/components-styles-provider.ts
2193
+ import { createStylesProvider } from "@elementor/editor-styles-repository";
2194
+ import { __getState as getState10, __subscribeWithSelector as subscribeWithSelector } from "@elementor/store";
2195
+ var componentsStylesProvider = createStylesProvider({
2196
+ key: "components-styles",
2197
+ priority: 100,
2198
+ subscribe: (cb) => subscribeWithSelector(
2199
+ (state) => state[SLICE_NAME],
2200
+ () => {
2201
+ cb();
2202
+ }
2203
+ ),
2204
+ actions: {
2205
+ all: () => {
2206
+ return selectFlatStyles(getState10());
2207
+ },
2208
+ get: (id) => {
2209
+ return selectFlatStyles(getState10()).find((style) => style.id === id) ?? null;
2210
+ }
2211
+ }
2212
+ });
2213
+
2214
+ // src/sync/create-components-before-save.ts
2215
+ import { updateElementSettings } from "@elementor/editor-elements";
2216
+ import { __dispatch as dispatch8, __getState as getState11 } from "@elementor/store";
2217
+ async function createComponentsBeforeSave({
2218
+ elements,
2219
+ status
2220
+ }) {
2221
+ const unpublishedComponents = selectUnpublishedComponents(getState11());
2222
+ if (!unpublishedComponents.length) {
2223
+ return;
2224
+ }
2225
+ try {
2226
+ const uidToComponentId = await createComponents(unpublishedComponents, status);
2227
+ updateComponentInstances(elements, uidToComponentId);
2228
+ dispatch8(
2229
+ slice.actions.add(
2230
+ unpublishedComponents.map((component) => ({
2231
+ id: uidToComponentId.get(component.uid),
2232
+ name: component.name,
2233
+ uid: component.uid
2234
+ }))
2235
+ )
2236
+ );
2237
+ dispatch8(slice.actions.resetUnpublished());
2238
+ } catch (error) {
2239
+ throw new Error(`Failed to publish components and update component instances: ${error}`);
2240
+ }
2241
+ }
2242
+ async function createComponents(components, status) {
2243
+ const response = await apiClient.create({
2244
+ status,
2245
+ items: components.map((component) => ({
2246
+ uid: component.uid,
2247
+ title: component.name,
2248
+ elements: component.elements
2249
+ }))
2250
+ });
2251
+ const map = /* @__PURE__ */ new Map();
2252
+ Object.entries(response).forEach(([key, value]) => {
2253
+ map.set(key, value);
2254
+ });
2255
+ return map;
2256
+ }
2257
+ function updateComponentInstances(elements, uidToComponentId) {
2258
+ elements.forEach((element) => {
2259
+ const { shouldUpdate, newComponentId } = shouldUpdateElement(element, uidToComponentId);
2260
+ if (shouldUpdate) {
2261
+ updateElementComponentId(element.id, newComponentId);
2262
+ }
2263
+ if (element.elements) {
2264
+ updateComponentInstances(element.elements, uidToComponentId);
2265
+ }
2266
+ });
2267
+ }
2268
+ function shouldUpdateElement(element, uidToComponentId) {
2269
+ if (element.widgetType === "e-component") {
2270
+ const currentComponentId = element.settings?.component_instance?.value?.component_id;
2271
+ if (currentComponentId && uidToComponentId.has(currentComponentId)) {
2272
+ return { shouldUpdate: true, newComponentId: uidToComponentId.get(currentComponentId) };
2273
+ }
2274
+ }
2275
+ return { shouldUpdate: false, newComponentId: null };
2276
+ }
2277
+ function updateElementComponentId(elementId, componentId) {
2278
+ updateElementSettings({
2279
+ id: elementId,
2280
+ props: {
2281
+ component_instance: {
2282
+ $$type: "component-instance",
2283
+ value: { component_id: componentId }
2284
+ }
2285
+ },
2286
+ withHistory: false
2287
+ });
2288
+ }
2289
+
2290
+ // src/sync/set-component-overridable-props-settings-before-save.ts
2291
+ import { __getState as getState12 } from "@elementor/store";
2292
+ var setComponentOverridablePropsSettingsBeforeSave = ({
2293
+ container
2294
+ }) => {
2295
+ const currentDocument = container.document;
2296
+ if (!currentDocument || currentDocument.config.type !== COMPONENT_DOCUMENT_TYPE) {
2297
+ return;
2298
+ }
2299
+ const overridableProps = selectOverridableProps(getState12(), currentDocument.id);
2300
+ if (overridableProps) {
2301
+ container.settings.set("overridable_props", overridableProps);
2302
+ }
2303
+ };
2304
+
2305
+ // src/sync/update-archived-component-before-save.ts
2306
+ import { notify } from "@elementor/editor-notifications";
2307
+ import { __getState as getState13 } from "@elementor/store";
2308
+ var failedNotification = (message) => ({
2309
+ type: "error",
2310
+ message: `Failed to archive components: ${message}`,
2311
+ id: "failed-archived-components-notification"
2312
+ });
2313
+ var successNotification = (message) => ({
2314
+ type: "success",
2315
+ message: `Successfully archived components: ${message}`,
2316
+ id: "success-archived-components-notification"
2317
+ });
2318
+ var updateArchivedComponentBeforeSave = async () => {
2319
+ try {
2320
+ const archivedComponents = selectArchivedComponents(getState13());
2321
+ if (!archivedComponents.length) {
2322
+ return;
2323
+ }
2324
+ const result = await apiClient.updateArchivedComponents(
2325
+ archivedComponents.map((component) => component.id)
2326
+ );
2327
+ const failedIds = result.failedIds.join(", ");
2328
+ const successIds = result.successIds.join(", ");
2329
+ if (failedIds) {
2330
+ notify(failedNotification(failedIds));
2331
+ }
2332
+ if (successIds) {
2333
+ notify(successNotification(successIds));
2334
+ }
2335
+ } catch (error) {
2336
+ throw new Error(`Failed to update archived components: ${error}`);
2337
+ }
2338
+ };
2339
+
2340
+ // src/sync/update-components-before-save.ts
2341
+ import { isDocumentDirty as isDocumentDirty2 } from "@elementor/editor-documents";
2342
+ async function updateComponentsBeforeSave({ status, elements }) {
2343
+ if (status !== "publish") {
2344
+ return;
2345
+ }
2346
+ const componentIds = await getComponentIds(elements);
2347
+ const componentDocumentData = await Promise.all(componentIds.map(getComponentDocumentData));
2348
+ const draftIds = componentDocumentData.filter((document) => !!document).filter(isDocumentDirty2).map((document) => document.id);
2349
+ if (draftIds.length === 0) {
2350
+ return;
2351
+ }
2352
+ await apiClient.updateStatuses(draftIds, "publish");
2353
+ draftIds.forEach((id) => invalidateComponentDocumentData(id));
2354
+ }
2355
+
2356
+ // src/sync/before-save.ts
2357
+ var beforeSave = ({ container, status }) => {
2358
+ const elements = container.model.get("elements")?.toJSON() ?? [];
2359
+ return Promise.all([
2360
+ updateArchivedComponentBeforeSave(),
2361
+ createComponentsBeforeSave({ elements, status }),
2362
+ updateComponentsBeforeSave({ elements, status }),
2363
+ setComponentOverridablePropsSettingsBeforeSave({ container })
2364
+ ]);
287
2365
  };
288
2366
 
289
2367
  // src/init.ts
290
2368
  function init() {
2369
+ stylesRepository.register(componentsStylesProvider);
2370
+ registerSlice(slice);
2371
+ registerElementType(
2372
+ TYPE,
2373
+ (options) => createComponentType({ ...options, showLockedByModal: openEditModeDialog })
2374
+ );
2375
+ registerDataHook("dependency", "editor/documents/close", (args) => {
2376
+ const document = getV1CurrentDocument2();
2377
+ if (document.config.type === COMPONENT_DOCUMENT_TYPE) {
2378
+ args.mode = "autosave";
2379
+ }
2380
+ return true;
2381
+ });
2382
+ registerDataHook("after", "preview/drop", onElementDrop);
2383
+ window.elementorCommon.__beforeSave = beforeSave;
291
2384
  injectTab({
292
2385
  id: "components",
293
- label: __3("Components", "elementor"),
294
- component: ComponentsTab
2386
+ label: __15("Components", "elementor"),
2387
+ component: Components
295
2388
  });
296
2389
  injectIntoTop({
297
2390
  id: "create-component-popup",
298
2391
  component: CreateComponentForm
299
2392
  });
2393
+ injectIntoLogic({
2394
+ id: "components-populate-store",
2395
+ component: PopulateStore
2396
+ });
2397
+ injectIntoTop({
2398
+ id: "edit-component",
2399
+ component: EditComponent
2400
+ });
2401
+ injectIntoPanelHeaderTop({
2402
+ id: "component-panel-header",
2403
+ component: ComponentPanelHeader
2404
+ });
2405
+ registerDataHook("after", "editor/documents/attach-preview", async () => {
2406
+ const { id, config } = getV1CurrentDocument2();
2407
+ if (id) {
2408
+ removeComponentStyles(id);
2409
+ }
2410
+ await loadComponentsAssets(config?.elements ?? []);
2411
+ });
2412
+ registerFieldIndicator({
2413
+ fieldType: FIELD_TYPE.SETTINGS,
2414
+ id: "component-overridable-prop",
2415
+ priority: 1,
2416
+ indicator: OverridablePropIndicator
2417
+ });
2418
+ registerControlReplacement({
2419
+ component: OverridablePropControl,
2420
+ condition: ({ value }) => componentOverridablePropTypeUtil.isValid(value)
2421
+ });
2422
+ settingsTransformersRegistry2.register("component-instance", componentInstanceTransformer);
2423
+ settingsTransformersRegistry2.register("overridable", componentOverridableTransformer);
2424
+ initMcp();
300
2425
  }
301
2426
  export {
302
2427
  init