@elementor/editor-components 3.33.0-99 → 3.34.2

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