PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0
Elementor Website Builder – more than just a page builder v4.3.0
4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 All 454 releases
elementor / assets / js / packages / editor-global-classes / editor-global-classes.js

editor-global-classes.js in Elementor Website Builder – more than just a page builder 4.3.0, at assets/js/packages/editor-global-classes/editor-global-classes.js

3,547 lines 136.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function(react, _elementor_editor_current_user, _elementor_editor_documents, _elementor_editor_panels, _elementor_editor_ui, _elementor_query, _elementor_store, _elementor_ui, _wordpress_i18n, _elementor_editor_styles, _elementor_utils, _elementor_http_client, _elementor_icons, _elementor_events, react_dom, _elementor_editor_styles_repository, _elementor_editor, _elementor_editor_editing_panel, _elementor_editor_embedded_documents_manager, _elementor_editor_mcp, _elementor_editor_canvas, _elementor_editor_v1_adapters, _elementor_schema) {
2
3 //#region \0rolldown/runtime.js
4 var __create = Object.create;
5 var __defProp$1 = Object.defineProperty;
6 var __name = (target, value) => __defProp$1(target, "name", {
7 value,
8 configurable: true
9 });
10 var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11 var __getOwnPropNames = Object.getOwnPropertyNames;
12 var __getProtoOf = Object.getPrototypeOf;
13 var __hasOwnProp = Object.prototype.hasOwnProperty;
14 var __exportAll = (all, no_symbols) => {
15 let target = {};
16 for (var name in all) {
17 __defProp$1(target, name, {
18 get: all[name],
19 enumerable: true
20 });
21 }
22 if (!no_symbols) {
23 __defProp$1(target, Symbol.toStringTag, { value: "Module" });
24 }
25 return target;
26 };
27 var __copyProps = (to, from, except, desc) => {
28 if (from && typeof from === "object" || typeof from === "function") {
29 for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
30 key = keys[i];
31 if (!__hasOwnProp.call(to, key) && key !== except) {
32 __defProp$1(to, key, {
33 get: ((k) => from[k]).bind(null, key),
34 enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
35 });
36 }
37 }
38 }
39 return to;
40 };
41 var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp$1(target, "default", {
42 value: mod,
43 enumerable: true
44 }) : target, mod));
45
46 //#endregion
47 let react$1 = __toESM(react, 1);
48 react = __toESM(react);
49
50 //#region packages/packages/core/editor-global-classes/src/errors.ts
51 var GlobalClassNotFoundError = (0, _elementor_utils.createError)({
52 code: "global_class_not_found",
53 message: "Global class not found."
54 });
55 var GlobalClassLabelAlreadyExistsError = (0, _elementor_utils.createError)({
56 code: "global_class_label_already_exists",
57 message: "Class with this name already exists."
58 });
59 var GlobalClassTrackingError = (0, _elementor_utils.createError)({
60 code: "global_class_tracking_error",
61 message: "Error tracking global classes event."
62 });
63
64 //#endregion
65 //#region packages/packages/core/editor-global-classes/src/utils/snapshot-history.ts
66 var __defProp = Object.defineProperty;
67 var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
68 enumerable: true,
69 configurable: true,
70 writable: true,
71 value
72 }) : obj[key] = value;
73 var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
74 function createLink({ value, next, prev }) {
75 return {
76 value,
77 prev: prev || null,
78 next: next || null
79 };
80 }
81 var _SnapshotHistory = class _SnapshotHistory {
82 constructor(namespace) {
83 __publicField(this, "namespace", namespace);
84 __publicField(this, "first", null);
85 __publicField(this, "current", null);
86 }
87 static get(namespace) {
88 if (!_SnapshotHistory.registry[namespace]) _SnapshotHistory.registry[namespace] = new _SnapshotHistory(namespace);
89 return _SnapshotHistory.registry[namespace];
90 }
91 transform(item) {
92 return JSON.parse(JSON.stringify(item));
93 }
94 reset() {
95 this.first = this.current = null;
96 }
97 prev() {
98 if (!this.current || this.current === this.first) return null;
99 this.current = this.current.prev;
100 return this.current?.value || null;
101 }
102 isLast() {
103 return !this.current || !this.current.next;
104 }
105 next(value) {
106 if (value) {
107 if (!this.current) {
108 this.first = createLink({ value: this.transform(value) });
109 this.current = this.first;
110 return this.current.value;
111 }
112 const nextLink = createLink({
113 value: this.transform(value),
114 prev: this.current
115 });
116 this.current.next = nextLink;
117 this.current = nextLink;
118 return this.current.value;
119 }
120 if (!this.current || !this.current.next) return null;
121 this.current = this.current.next;
122 return this.current.value;
123 }
124 };
125 __publicField(_SnapshotHistory, "registry", {});
126 var SnapshotHistory = _SnapshotHistory;
127
128 //#endregion
129 //#region packages/packages/core/editor-global-classes/src/store.ts
130 var localHistory = SnapshotHistory.get("global-classes");
131 var initialState = {
132 data: {
133 items: {},
134 order: []
135 },
136 classLabels: {},
137 initialData: {
138 frontend: {
139 items: {},
140 order: []
141 },
142 preview: {
143 items: {},
144 order: []
145 }
146 },
147 isDirty: false
148 };
149 var SLICE_NAME = "globalClasses";
150 var slice = (0, _elementor_store.__createSlice)({
151 name: SLICE_NAME,
152 initialState,
153 reducers: {
154 load(state, { payload: { frontend, preview, classLabels } }) {
155 state.initialData.frontend = frontend;
156 state.initialData.preview = preview;
157 state.data = preview;
158 state.classLabels = classLabels;
159 state.isDirty = false;
160 },
161 add(state, { payload }) {
162 localHistory.next(state.data);
163 state.data.items[payload.id] = payload;
164 state.data.order.unshift(payload.id);
165 state.classLabels[payload.id] = payload.label;
166 state.isDirty = true;
167 },
168 delete(state, { payload }) {
169 localHistory.next(state.data);
170 state.data.items = Object.fromEntries(Object.entries(state.data.items).filter(([id]) => id !== payload));
171 state.data.order = state.data.order.filter((id) => id !== payload);
172 delete state.classLabels[payload];
173 state.isDirty = true;
174 },
175 setOrder(state, { payload }) {
176 localHistory.next(state.data);
177 state.data.order = payload;
178 state.isDirty = true;
179 },
180 update(state, { payload }) {
181 const oldLabel = state.classLabels[payload.style.id];
182 localHistory.next(state.data);
183 const mergedData = {
184 ...state.data.items[payload.style.id],
185 ...payload.style
186 };
187 if (oldLabel && payload.style.label && payload.style.label !== oldLabel) state.classLabels[payload.style.id] = payload.style.label;
188 state.data.items[payload.style.id] = mergedData;
189 state.isDirty = true;
190 },
191 updateMultiple(state, { payload }) {
192 localHistory.next(state.data);
193 Object.entries(payload).forEach(([id, { modified }]) => {
194 state.data.items[id].label = modified;
195 state.classLabels[id] = modified;
196 });
197 state.isDirty = false;
198 },
199 updateProps(state, { payload }) {
200 const style = state.data.items[payload.id];
201 if (!style) throw new GlobalClassNotFoundError({ context: { styleId: payload.id } });
202 localHistory.next(state.data);
203 const variant = (0, _elementor_editor_styles.getVariantByMeta)(style, payload.meta);
204 let customCss = ("custom_css" in payload ? payload.custom_css : variant?.custom_css) ?? null;
205 customCss = customCss?.raw ? customCss : null;
206 if (variant) {
207 const payloadProps = JSON.parse(JSON.stringify(payload.props));
208 if ((payload.mode ?? "merge") === "replace") variant.props = payloadProps;
209 else {
210 const variantProps = JSON.parse(JSON.stringify(variant.props));
211 variant.props = mergeProps(variantProps, payloadProps);
212 }
213 variant.custom_css = customCss;
214 style.variants = getNonEmptyVariants(style);
215 } else style.variants.push({
216 meta: payload.meta,
217 props: payload.props,
218 custom_css: customCss
219 });
220 state.isDirty = true;
221 },
222 reset(state, { payload: { context } }) {
223 if (context === "frontend") {
224 localHistory.reset();
225 state.initialData.frontend = state.data;
226 state.isDirty = false;
227 }
228 state.initialData.preview = state.data;
229 },
230 undo(state) {
231 if (localHistory.isLast()) localHistory.next(state.data);
232 const data = localHistory.prev();
233 if (data) {
234 state.data = data;
235 state.isDirty = true;
236 } else state.data = state.initialData.preview;
237 },
238 resetToInitialState(state, { payload: { context } }) {
239 localHistory.reset();
240 state.data = state.initialData[context];
241 state.isDirty = false;
242 },
243 redo(state) {
244 const data = localHistory.next();
245 if (localHistory.isLast()) localHistory.prev();
246 if (data) {
247 state.data = data;
248 state.isDirty = true;
249 }
250 },
251 mergeExistingClasses(state, { payload: { preview, frontend } }) {
252 Object.entries(preview).forEach(([id, previewClassData]) => {
253 const frontendClassData = frontend[id];
254 if (previewClassData === null || previewClassData === void 0) return;
255 if (!(id in state.data.items)) state.data.items[id] = previewClassData;
256 if (frontendClassData && !(id in state.initialData.frontend.items)) state.initialData.frontend.items[id] = frontendClassData;
257 if (!(id in state.initialData.preview.items)) state.initialData.preview.items[id] = previewClassData;
258 if (!(id in state.classLabels)) state.classLabels[id] = previewClassData.label;
259 });
260 },
261 setOrderWithoutHistory(state, { payload }) {
262 state.data.order = payload;
263 },
264 updateAfterTemplateImport(state, { payload }) {
265 state.initialData.frontend.items = {
266 ...state.initialData.frontend.items,
267 ...payload.addedItems
268 };
269 state.initialData.frontend.order = [...state.initialData.frontend.order, ...payload.addedIdsOrder];
270 state.initialData.preview.items = {
271 ...state.initialData.preview.items,
272 ...payload.addedItems
273 };
274 state.initialData.preview.order = [...state.initialData.preview.order, ...payload.addedIdsOrder];
275 state.data.items = {
276 ...state.data.items,
277 ...payload.addedItems
278 };
279 state.data.order = [...state.data.order, ...payload.addedIdsOrder];
280 state.classLabels = {
281 ...state.classLabels,
282 ...payload.addedClassLabels
283 };
284 }
285 }
286 });
287 var mergeProps = (current, updates) => {
288 const props = Array.isArray(current) ? {} : current;
289 Object.entries(updates).forEach(([key, value]) => {
290 if (value === null || value === void 0) delete props[key];
291 else props[key] = value;
292 });
293 return props;
294 };
295 var getNonEmptyVariants = (style) => {
296 return style.variants.filter(({ props, custom_css: customCss }) => Object.keys(props).length || customCss?.raw);
297 };
298 var placeholderDefinition = (id, label) => ({
299 id,
300 type: "class",
301 label,
302 variants: []
303 });
304 var selectData = (state) => state[SLICE_NAME].data;
305 var selectClassLabels = (state) => state[SLICE_NAME].classLabels;
306 var selectFrontendInitialData = (state) => state[SLICE_NAME].initialData.frontend;
307 var selectPreviewInitialData = (state) => state[SLICE_NAME].initialData.preview;
308 var selectOrder = (0, _elementor_store.__createSelector)(selectData, ({ order }) => order);
309 var selectGlobalClasses = (0, _elementor_store.__createSelector)(selectData, ({ items }) => items);
310 var selectIsDirty = (state) => state[SLICE_NAME].isDirty;
311 var selectOrderedClasses = (0, _elementor_store.__createSelector)(selectData, selectClassLabels, ({ items, order }, classLabels) => order.map((id) => {
312 const loaded = items[id];
313 if (loaded) return loaded;
314 const label = classLabels[id];
315 return label !== void 0 ? placeholderDefinition(id, label) : null;
316 }).filter((s) => s !== null));
317 var selectClass = (state, id) => state[SLICE_NAME].data.items[id] ?? null;
318 var selectEmptyCssClass = (0, _elementor_store.__createSelector)(selectData, ({ items }) => Object.values(items).filter((cssClass) => (cssClass.variants?.length ?? 0) === 0));
319 var selectIsClassFetched = (state, id) => !!state[SLICE_NAME].initialData.preview.items[id] || !!state[SLICE_NAME].initialData.frontend.items[id] || false;
320
321 //#endregion
322 //#region packages/packages/core/editor-global-classes/src/hooks/use-classes-order.ts
323 var useClassesOrder = () => {
324 return (0, _elementor_store.__useSelector)(selectOrder);
325 };
326
327 //#endregion
328 //#region packages/packages/core/editor-global-classes/src/hooks/use-dirty-state.ts
329 var useDirtyState = () => {
330 return (0, _elementor_store.__useSelector)(selectIsDirty);
331 };
332
333 //#endregion
334 //#region packages/packages/core/editor-global-classes/src/components/search-and-filter/context.tsx
335 var SearchAndFilterContext = (0, react.createContext)(void 0);
336 var INIT_CHECKED_FILTERS = {
337 empty: false,
338 onThisPage: false,
339 unused: false
340 };
341 var SearchAndFilterProvider = ({ children }) => {
342 const [filters, setFilters] = (0, react.useState)(INIT_CHECKED_FILTERS);
343 const getInitialSearchValue = () => {
344 const storedValue = localStorage.getItem("elementor-global-classes-search");
345 if (storedValue) {
346 localStorage.removeItem("elementor-global-classes-search");
347 return storedValue;
348 }
349 return "";
350 };
351 const { debouncedValue, inputValue, handleChange } = (0, _elementor_utils.useDebounceState)({
352 delay: 300,
353 initialValue: getInitialSearchValue()
354 });
355 const onClearSearch = () => {
356 handleChange("");
357 };
358 const onClearFilter = () => {
359 setFilters(INIT_CHECKED_FILTERS);
360 };
361 return /* @__PURE__ */ react.createElement(SearchAndFilterContext.Provider, { value: {
362 search: {
363 debouncedValue,
364 inputValue,
365 handleChange,
366 onClearSearch
367 },
368 filters: {
369 filters,
370 setFilters,
371 onClearFilter
372 }
373 } }, children);
374 };
375 var useSearchAndFilters = () => {
376 const context = (0, react.useContext)(SearchAndFilterContext);
377 if (!context) throw new Error("useSearchContext must be used within a SearchContextProvider");
378 return context;
379 };
380
381 //#endregion
382 //#region packages/packages/core/editor-global-classes/src/api.ts
383 var RESOURCE_URL = "/global-classes";
384 var BASE_URL = "elementor/v1";
385 var RESOURCE_USAGE_URL = `${RESOURCE_URL}/usage`;
386 var RESOURCE_POST_URL = `${RESOURCE_URL}/post`;
387 var RESOURCE_STYLES_URL = `${RESOURCE_URL}/styles`;
388 function saveGlobalClasses$1(context, payload) {
389 return (0, _elementor_http_client.httpService)().put(`${BASE_URL}${RESOURCE_URL}`, payload, { params: { context } });
390 }
391 __name(saveGlobalClasses$1, "saveGlobalClasses");
392 var apiClient = {
393 usage: () => (0, _elementor_http_client.httpService)().get(`${BASE_URL}${RESOURCE_USAGE_URL}`),
394 all: (context = "preview") => (0, _elementor_http_client.httpService)().get(`${BASE_URL}${RESOURCE_URL}`, { params: { context } }),
395 getStylesForPost: (postId, context = "preview") => (0, _elementor_http_client.httpService)().get(`${BASE_URL}${RESOURCE_POST_URL}`, { params: {
396 context,
397 post_id: postId
398 } }),
399 getStylesByIds: (ids, context = "preview") => (0, _elementor_http_client.httpService)().get(`${BASE_URL}${RESOURCE_STYLES_URL}`, { params: {
400 context,
401 ids: ids.join(",")
402 } }),
403 publish: (payload) => saveGlobalClasses$1("frontend", payload),
404 saveDraft: (payload) => saveGlobalClasses$1("preview", payload)
405 };
406 var API_ERROR_CODES = { DUPLICATED_LABEL: "DUPLICATED_LABEL" };
407
408 //#endregion
409 //#region packages/packages/core/editor-global-classes/src/components/css-class-usage/utils.ts
410 var transformData = (data) => Object.entries(data).reduce((acc, [key, value]) => {
411 acc[key] = {
412 content: value || [],
413 total: value.reduce((total, val) => total + (val?.total || 0), 0)
414 };
415 return acc;
416 }, {});
417
418 //#endregion
419 //#region packages/packages/core/editor-global-classes/service/css-class-usage-service.ts
420 var fetchCssClassUsage = async () => {
421 return transformData((await apiClient.usage())?.data?.data || {});
422 };
423
424 //#endregion
425 //#region packages/packages/core/editor-global-classes/src/components/css-class-usage/types.ts
426 var QUERY_KEY = "css-classes-usage";
427
428 //#endregion
429 //#region packages/packages/core/editor-global-classes/src/hooks/use-css-class-usage.ts
430 var useCssClassUsage = () => {
431 return (0, _elementor_query.useQuery)({
432 queryKey: [QUERY_KEY],
433 queryFn: fetchCssClassUsage,
434 refetchOnMount: false,
435 refetchOnWindowFocus: true
436 });
437 };
438
439 //#endregion
440 //#region packages/packages/core/editor-global-classes/src/hooks/use-empty-css-class.ts
441 var useEmptyCssClass = () => {
442 return (0, _elementor_store.__useSelector)(selectEmptyCssClass);
443 };
444 var useAllCssClassesIDs = () => {
445 const cssClasses = (0, _elementor_store.__useSelector)(selectGlobalClasses);
446 return Object.keys(cssClasses);
447 };
448
449 //#endregion
450 //#region packages/packages/core/editor-global-classes/src/hooks/use-filtered-css-class-usage.tsx
451 var findCssClassKeysByPageID = (data, pageId) => {
452 const result = [];
453 for (const key in data) data[key].content.forEach((content) => {
454 if (+content.pageId === pageId) result.push(key);
455 });
456 return result;
457 };
458 var getUnusedClasses = (usedCssClass, potentialUnused) => {
459 const set = new Set(usedCssClass);
460 return potentialUnused.filter((cssClass) => !set.has(cssClass));
461 };
462 var EMPTY_FILTERED_CSS_CLASS_RESPONSE = {
463 empty: [],
464 onThisPage: [],
465 unused: []
466 };
467 var useFilteredCssClassUsage = () => {
468 const document = (0, _elementor_editor_documents.__useActiveDocument)();
469 const emptyCssClasses = useEmptyCssClass();
470 const { data, isLoading } = useCssClassUsage();
471 const listOfCssClasses = useAllCssClassesIDs();
472 const emptyCssClassesIDs = (0, react.useMemo)(() => emptyCssClasses.map(({ id }) => id), [emptyCssClasses]);
473 const onThisPage = (0, react.useMemo)(() => {
474 if (!data || !document) return [];
475 return findCssClassKeysByPageID(data, document.id);
476 }, [data, document]);
477 const unused = (0, react.useMemo)(() => {
478 if (!data) return [];
479 return getUnusedClasses(Object.keys(data), listOfCssClasses);
480 }, [data, listOfCssClasses]);
481 if (isLoading || !data || !document) return EMPTY_FILTERED_CSS_CLASS_RESPONSE;
482 return {
483 onThisPage,
484 unused,
485 empty: emptyCssClassesIDs
486 };
487 };
488
489 //#endregion
490 //#region packages/packages/core/editor-global-classes/src/hooks/use-filters.ts
491 var useFilters = () => {
492 const { filters: { filters } } = useSearchAndFilters();
493 const allFilters = useFilteredCssClassUsage();
494 return (0, react.useMemo)(() => {
495 const activeEntries = Object.entries(filters).filter(([, isActive]) => isActive);
496 if (activeEntries.length === 0) return null;
497 return activeEntries.reduce((acc, [key], index) => {
498 const current = allFilters[key] || [];
499 if (index === 0) return current;
500 return acc.filter((val) => current.includes(val));
501 }, []);
502 }, [filters, allFilters]);
503 };
504
505 //#endregion
506 //#region packages/packages/core/editor-global-classes/src/utils/create-labels-for-classes.ts
507 function createLabelsForClasses(entries) {
508 return Object.fromEntries(entries.map((e) => [e.id, e.label]));
509 }
510
511 //#endregion
512 //#region packages/packages/core/editor-global-classes/src/load-document-classes.ts
513 function styleDefinitionsMapWithoutNull(map) {
514 return Object.fromEntries(Object.entries(map).filter((entry) => entry[1] !== null));
515 }
516 function resetGlobalClassesState(globalOrder, classLabels) {
517 (0, _elementor_store.__dispatch)(slice.actions.load({
518 preview: {
519 items: {},
520 order: globalOrder
521 },
522 frontend: {
523 items: {},
524 order: globalOrder
525 },
526 classLabels
527 }));
528 }
529 async function loadCurrentDocumentClasses() {
530 const [previewIndexRes, frontendIndexRes] = await Promise.all([apiClient.all("preview"), apiClient.all("frontend")]);
531 const previewIndex = previewIndexRes.data.data;
532 const frontendIndex = frontendIndexRes.data.data;
533 const classLabels = createLabelsForClasses(previewIndex);
534 const previewOrder = previewIndex.map((e) => e.id);
535 const frontendOrder = frontendIndex.map((e) => e.id);
536 resetGlobalClassesState(previewOrder, classLabels);
537 const postId = (0, _elementor_editor_documents.getCurrentDocument)()?.id;
538 if (!postId) return;
539 const [previewPostRes, frontendPostRes] = await Promise.all([apiClient.getStylesForPost(postId, "preview"), apiClient.getStylesForPost(postId, "frontend")]);
540 const previewItems = styleDefinitionsMapWithoutNull(previewPostRes.data.data);
541 const frontendItems = styleDefinitionsMapWithoutNull(frontendPostRes.data.data);
542 (0, _elementor_store.__dispatch)(slice.actions.load({
543 preview: {
544 items: previewItems,
545 order: previewOrder
546 },
547 frontend: {
548 items: frontendItems,
549 order: frontendOrder
550 },
551 classLabels
552 }));
553 }
554 async function addDocumentClasses(documentId) {
555 const [previewPostRes, frontendPostRes] = await Promise.all([apiClient.getStylesForPost(documentId, "preview"), apiClient.getStylesForPost(documentId, "frontend")]);
556 const previewItems = styleDefinitionsMapWithoutNull(previewPostRes.data.data);
557 const frontendItems = styleDefinitionsMapWithoutNull(frontendPostRes.data.data);
558 (0, _elementor_store.__dispatch)(slice.actions.mergeExistingClasses({
559 preview: previewItems,
560 frontend: frontendItems
561 }));
562 }
563
564 //#endregion
565 //#region packages/packages/core/editor-global-classes/src/load-existing-classes.ts
566 var pendingLoad = null;
567 var pendingIds = /* @__PURE__ */ new Set();
568 async function loadExistingClasses(classIds) {
569 const existingClasses = selectGlobalClasses((0, _elementor_store.__getState)());
570 const missingIds = classIds.filter((id) => !(id in existingClasses));
571 if (missingIds.length === 0) return;
572 missingIds.forEach((id) => pendingIds.add(id));
573 if (pendingLoad) {
574 await pendingLoad;
575 return loadExistingClasses(classIds);
576 }
577 pendingLoad = fetchAndMergeClasses();
578 try {
579 await pendingLoad;
580 } finally {
581 pendingLoad = null;
582 }
583 }
584 async function fetchAndMergeClasses() {
585 const idsToFetch = Array.from(pendingIds);
586 pendingIds.clear();
587 if (idsToFetch.length === 0) return;
588 const [previewResponse, frontendResponse] = await Promise.all([apiClient.getStylesByIds(idsToFetch, "preview"), apiClient.getStylesByIds(idsToFetch, "frontend")]);
589 const previewItems = styleDefinitionsMapWithoutNull(previewResponse.data.data);
590 const frontendItems = styleDefinitionsMapWithoutNull(frontendResponse.data.data);
591 (0, _elementor_store.__dispatch)(slice.actions.mergeExistingClasses({
592 preview: previewItems,
593 frontend: frontendItems
594 }));
595 }
596
597 //#endregion
598 //#region packages/packages/core/editor-global-classes/src/components/class-manager/duplicate-label-dialog.tsx
599 var DUP_PREFIX = "DUP_";
600 var DuplicateLabelDialog = ({ modifiedLabels, onApprove }) => {
601 const handleButtonClick = () => {
602 localStorage.setItem("elementor-global-classes-search", DUP_PREFIX);
603 onApprove?.();
604 (0, _elementor_editor_ui.closeDialog)();
605 };
606 return /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(_elementor_ui.DialogHeader, { logo: false }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
607 display: "flex",
608 alignItems: "center",
609 gap: 1
610 }, /* @__PURE__ */ react.createElement(_elementor_ui.Icon, { color: "secondary" }, /* @__PURE__ */ react.createElement(_elementor_icons.InfoCircleFilledIcon, { fontSize: "medium" })), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, { variant: "subtitle1" }, (0, _wordpress_i18n.__)("We've published your page and updated class names.", "elementor")))), /* @__PURE__ */ react.createElement(_elementor_ui.DialogContent, null, /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
611 spacing: 2,
612 direction: "column"
613 }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, { variant: "body2" }, (0, _wordpress_i18n.__)("Some new classes used the same names as existing ones. To prevent conflicts, we added the prefix", "elementor"), /* @__PURE__ */ react.createElement("strong", null, " ", DUP_PREFIX)), /* @__PURE__ */ react.createElement(_elementor_ui.Box, null, /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: {
614 width: "100%",
615 display: "flex",
616 gap: 2,
617 alignItems: "flex-start"
618 } }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
619 variant: "subtitle2",
620 sx: {
621 fontWeight: "bold",
622 flex: 1,
623 flexShrink: 1,
624 flexGrow: 1,
625 minWidth: 0
626 }
627 }, (0, _wordpress_i18n.__)("Before", "elementor")), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
628 variant: "subtitle2",
629 sx: {
630 minWidth: "200px",
631 fontWeight: "bold",
632 flexShrink: 0,
633 flexGrow: 0,
634 width: "200px",
635 maxWidth: "200px"
636 }
637 }, (0, _wordpress_i18n.__)("After", "elementor"))), /* @__PURE__ */ react.createElement(_elementor_ui.Divider, { sx: {
638 mt: .5,
639 mb: .5
640 } }), /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
641 direction: "column",
642 gap: .5,
643 sx: { pb: 2 }
644 }, Object.values(modifiedLabels).map(({ original, modified }, index) => /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
645 key: index,
646 sx: {
647 width: "100%",
648 display: "flex",
649 gap: 2,
650 alignItems: "flex-start"
651 }
652 }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: {
653 flex: 1,
654 flexShrink: 1,
655 flexGrow: 1,
656 minWidth: 0
657 } }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.EllipsisWithTooltip, { title: original }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
658 variant: "body2",
659 sx: { color: "text.secondary" }
660 }, original))), /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: {
661 minWidth: "200px",
662 flexShrink: 0,
663 flexGrow: 0,
664 width: "200px",
665 maxWidth: "200px"
666 } }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.EllipsisWithTooltip, { title: modified }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
667 variant: "body2",
668 sx: { color: "text.primary" }
669 }, modified)))))), /* @__PURE__ */ react.createElement(_elementor_ui.Box, null, /* @__PURE__ */ react.createElement(_elementor_ui.Alert, {
670 severity: "info",
671 size: "small",
672 color: "secondary"
673 }, /* @__PURE__ */ react.createElement("strong", null, (0, _wordpress_i18n.__)("Your designs and classes are safe.", "elementor")), (0, _wordpress_i18n.__)("Only the prefixes were added. Find them in Class Manager by searching", "elementor"), /* @__PURE__ */ react.createElement("strong", null, DUP_PREFIX)))))), /* @__PURE__ */ react.createElement(_elementor_ui.DialogActions, null, /* @__PURE__ */ react.createElement(_elementor_ui.Button, {
674 color: "secondary",
675 variant: "text",
676 onClick: handleButtonClick
677 }, (0, _wordpress_i18n.__)("Go to Class Manager", "elementor")), /* @__PURE__ */ react.createElement(_elementor_ui.Button, {
678 color: "secondary",
679 variant: "contained",
680 onClick: _elementor_editor_ui.closeDialog
681 }, (0, _wordpress_i18n.__)("Done", "elementor"))));
682 };
683
684 //#endregion
685 //#region packages/packages/core/editor-global-classes/src/utils/tracking.ts
686 var trackGlobalClasses = async (payload) => {
687 const { runAction } = payload;
688 const data = await getSanitizedData(payload);
689 if (data) {
690 track(data);
691 if (data.event === "classCreated" && "classId" in data) fireClassApplied(data.classId);
692 }
693 runAction?.();
694 };
695 var fireClassApplied = async (classId) => {
696 const appliedInfo = await getAppliedInfo(classId);
697 track({
698 event: "classApplied",
699 classId,
700 ...appliedInfo,
701 totalInstancesAfterApply: 1
702 });
703 };
704 var getSanitizedData = async (payload) => {
705 switch (payload.event) {
706 case "classApplied":
707 if ("classId" in payload && payload.classId) {
708 const appliedInfo = await getAppliedInfo(payload.classId);
709 return {
710 ...payload,
711 ...appliedInfo
712 };
713 }
714 break;
715 case "classRemoved":
716 if ("classId" in payload && payload.classId) {
717 const deleteInfo = getRemovedInfo(payload.classId);
718 return {
719 ...payload,
720 ...deleteInfo
721 };
722 }
723 break;
724 case "classDeleted":
725 if ("classId" in payload && payload.classId) {
726 const deleteInfo = await trackDeleteClass(payload.classId);
727 return {
728 ...payload,
729 ...deleteInfo
730 };
731 }
732 break;
733 case "classCreated":
734 if ("source" in payload && payload.source !== "created") {
735 if ("classId" in payload && payload.classId) return {
736 ...payload,
737 classTitle: getCssClass(payload.classId).label
738 };
739 }
740 return payload;
741 case "classStateClicked":
742 if ("classId" in payload && payload.classId) return {
743 ...payload,
744 classTitle: getCssClass(payload.classId).label
745 };
746 break;
747 case "classSyncToV3PopupShown": return {
748 ...payload,
749 interaction_type: "popup_shown",
750 target_type: "popup",
751 target_name: "sync_to_v3_popup",
752 interaction_result: "popup_viewed",
753 target_location: "widget_panel",
754 location_l1: "class_manager"
755 };
756 case "classSyncToV3": {
757 const classLabel = getCssClass(payload.classId).label;
758 const isSync = payload.action === "sync";
759 return {
760 ...payload,
761 interaction_type: "click",
762 target_type: classLabel,
763 target_name: isSync ? "sync_to_v3" : "unsync_to_v3",
764 interaction_result: isSync ? "class_is_synced_to_V3" : "class_is_unsynced_from_V3",
765 target_location: "widget_panel",
766 location_l1: "class_manager",
767 interaction_description: isSync ? `user_synced_${classLabel}_to_v3` : `user_unsync_${classLabel}_from_v3`
768 };
769 }
770 case "classSyncToV3PopupClick": {
771 const isSyncAction = payload.action === "sync";
772 return {
773 ...payload,
774 interaction_type: "click",
775 target_type: "button",
776 target_name: isSyncAction ? "sync_to_v3" : "cancel",
777 interaction_result: isSyncAction ? "class_is_synced" : "cancel",
778 target_location: "sync_to_v3_popup"
779 };
780 }
781 default: return payload;
782 }
783 };
784 var track = (data) => {
785 const { dispatchEvent, config } = (0, _elementor_events.getMixpanel)();
786 if (!config?.names?.global_classes?.[data.event]) {
787 console.error("Global class tracking event not found", { event: data.event });
788 return;
789 }
790 const name = config.names.global_classes[data.event];
791 const { event, ...eventData } = data;
792 try {
793 dispatchEvent?.(name, {
794 event,
795 ...eventData
796 });
797 } catch (error) {
798 throw new GlobalClassTrackingError({ cause: error });
799 }
800 };
801 var extractCssClassData = (classId) => {
802 return { classTitle: getCssClass(classId).label };
803 };
804 var getCssClass = (classId) => {
805 const state = (0, _elementor_store.__getState)();
806 const cssClass = selectClass(state, classId);
807 if (cssClass) return cssClass;
808 const label = selectClassLabels(state)[classId];
809 if (label !== void 0) return placeholderDefinition(classId, label);
810 throw new Error(`CSS class with ID ${classId} not found`);
811 };
812 var trackDeleteClass = async (classId) => {
813 const classTitle = getCssClass(classId).label;
814 return {
815 totalInstances: await getTotalInstancesByCssClassID(classId),
816 classTitle
817 };
818 };
819 var getTotalInstancesByCssClassID = async (classId) => {
820 return (await fetchCssClassUsage())[classId]?.total ?? 1;
821 };
822 var getAppliedInfo = async (classId) => {
823 const { classTitle } = extractCssClassData(classId);
824 return {
825 classTitle,
826 totalInstancesAfterApply: await getTotalInstancesByCssClassID(classId) + 1
827 };
828 };
829 var getRemovedInfo = (classId) => {
830 const { classTitle } = extractCssClassData(classId);
831 return { classTitle };
832 };
833
834 //#endregion
835 //#region packages/packages/core/editor-global-classes/src/save-global-classes.tsx
836 async function saveGlobalClasses({ context, onApprove }) {
837 const state = selectData((0, _elementor_store.__getState)());
838 const apiAction = context === "preview" ? apiClient.saveDraft : apiClient.publish;
839 const changes = calculateChanges(state, (context === "preview" ? selectPreviewInitialData : selectFrontendInitialData)((0, _elementor_store.__getState)()));
840 const touchedIds = [...changes.added, ...changes.modified];
841 const response = await apiAction({
842 items: Object.fromEntries(touchedIds.map((id) => [id, state.items[id]]).filter(([, v]) => v)),
843 order: state.order,
844 changes
845 });
846 (0, _elementor_store.__dispatch)(slice.actions.reset({ context }));
847 window.dispatchEvent(new CustomEvent("classes:updated", { detail: { context } }));
848 if (response?.data?.data?.code === API_ERROR_CODES.DUPLICATED_LABEL) {
849 (0, _elementor_store.__dispatch)(slice.actions.updateMultiple(response.data.data.modifiedLabels));
850 trackGlobalClasses({
851 event: "classPublishConflict",
852 numOfConflicts: Object.keys(response.data.data.modifiedLabels).length
853 });
854 (0, _elementor_editor_ui.openDialog)({ component: /* @__PURE__ */ react.createElement(DuplicateLabelDialog, {
855 modifiedLabels: response.data.data.modifiedLabels || [],
856 onApprove
857 }) });
858 }
859 }
860 function calculateChanges(state, initialData) {
861 const stateIds = Object.keys(state.items);
862 const initialDataIds = Object.keys(initialData.items);
863 const { order: stateOrder } = state;
864 const { order: initialDataOrder } = initialData;
865 const stateOrderIdSet = new Set(stateOrder);
866 const deleted = initialDataOrder.filter((id) => !stateOrderIdSet.has(id));
867 const order = stateOrder.join(";") !== initialDataOrder.join(";");
868 return {
869 added: stateIds.filter((id) => !initialDataIds.includes(id)),
870 deleted,
871 modified: stateIds.filter((id) => {
872 return id in initialData.items && (0, _elementor_utils.hash)(state.items[id]) !== (0, _elementor_utils.hash)(initialData.items[id]);
873 }),
874 order
875 };
876 }
877
878 //#endregion
879 //#region packages/packages/core/editor-global-classes/src/components/search-and-filter/components/filter/clear-icon-button.tsx
880 var ClearIconButton = ({ tooltipText, sx, trigger }) => {
881 const { filters: { onClearFilter } } = useSearchAndFilters();
882 const handleClearFilters = () => {
883 onClearFilter(trigger);
884 trackGlobalClasses({
885 event: "classManagerFilterCleared",
886 trigger
887 });
888 };
889 return /* @__PURE__ */ react.createElement(_elementor_ui.Tooltip, {
890 title: tooltipText,
891 placement: "top",
892 disableInteractive: true
893 }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, null, /* @__PURE__ */ react.createElement(CustomIconButton$1, {
894 "aria-label": tooltipText,
895 size: "tiny",
896 onClick: handleClearFilters,
897 sx
898 }, /* @__PURE__ */ react.createElement(_elementor_icons.BrushBigIcon, { fontSize: "tiny" }))));
899 };
900 var CustomIconButton$1 = (0, _elementor_ui.styled)(_elementor_ui.IconButton)(({ theme }) => ({ "&.Mui-disabled": {
901 pointerEvents: "auto",
902 "&:hover": { color: theme.palette.action.disabled }
903 } }));
904
905 //#endregion
906 //#region packages/packages/core/editor-global-classes/src/components/search-and-filter/components/filter/filter-list.tsx
907 var filterConfig = {
908 unused: (0, _wordpress_i18n.__)("Unused", "elementor"),
909 empty: (0, _wordpress_i18n.__)("Empty", "elementor"),
910 onThisPage: (0, _wordpress_i18n.__)("On this page", "elementor")
911 };
912 var FilterList = () => {
913 const { filters: { filters, setFilters } } = useSearchAndFilters();
914 const filteredCssClass = useFilteredCssClassUsage();
915 const handleOnClick = (value) => {
916 setFilters((prev) => ({
917 ...prev,
918 [value]: !prev[value]
919 }));
920 trackGlobalClasses({
921 event: "classManagerFilterUsed",
922 action: filters[value] ? "remove" : "apply",
923 type: value,
924 trigger: "menu"
925 });
926 };
927 return /* @__PURE__ */ react.createElement(_elementor_ui.MenuList, null, /* @__PURE__ */ react.createElement(_elementor_ui.MenuItem, { onClick: () => handleOnClick("unused") }, /* @__PURE__ */ react.createElement(LabeledCheckbox, {
928 label: filterConfig.unused,
929 checked: filters.unused,
930 suffix: /* @__PURE__ */ react.createElement(_elementor_ui.Chip, {
931 size: "tiny",
932 sx: { ml: "auto" },
933 label: filteredCssClass.unused.length
934 })
935 })), /* @__PURE__ */ react.createElement(_elementor_ui.MenuItem, { onClick: () => handleOnClick("empty") }, /* @__PURE__ */ react.createElement(LabeledCheckbox, {
936 label: filterConfig.empty,
937 checked: filters.empty,
938 suffix: /* @__PURE__ */ react.createElement(_elementor_ui.Chip, {
939 size: "tiny",
940 sx: { ml: "auto" },
941 label: filteredCssClass.empty.length
942 })
943 })), /* @__PURE__ */ react.createElement(_elementor_ui.MenuItem, { onClick: () => handleOnClick("onThisPage") }, /* @__PURE__ */ react.createElement(LabeledCheckbox, {
944 label: filterConfig.onThisPage,
945 checked: filters.onThisPage,
946 suffix: /* @__PURE__ */ react.createElement(_elementor_ui.Chip, {
947 size: "tiny",
948 sx: { ml: "auto" },
949 label: filteredCssClass.onThisPage.length
950 })
951 })));
952 };
953 var LabeledCheckbox = ({ label, suffix, checked }) => /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
954 direction: "row",
955 alignItems: "center",
956 gap: .5,
957 flex: 1
958 }, /* @__PURE__ */ react.createElement(_elementor_ui.Checkbox, {
959 size: "small",
960 checked,
961 sx: {
962 padding: 0,
963 color: "text.tertiary",
964 "&.Mui-checked": { color: "text.tertiary" }
965 }
966 }), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
967 variant: "caption",
968 sx: { color: "text.secondary" }
969 }, label), suffix);
970
971 //#endregion
972 //#region packages/packages/core/editor-global-classes/src/components/search-and-filter/components/filter/active-filters.tsx
973 var ActiveFilters = () => {
974 const { filters: { filters, setFilters } } = useSearchAndFilters();
975 const handleRemove = (key) => {
976 setFilters((prev) => ({
977 ...prev,
978 [key]: false
979 }));
980 trackGlobalClasses({
981 event: "classManagerFilterUsed",
982 action: "remove",
983 type: key,
984 trigger: "header"
985 });
986 };
987 const activeKeys = Object.keys(filters).filter((key) => filters[key]);
988 const showClearIcon = activeKeys.length > 0;
989 return /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
990 direction: "row",
991 alignItems: "center",
992 justifyContent: "space-between"
993 }, /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
994 direction: "row",
995 gap: .5,
996 alignItems: "center",
997 flexWrap: "wrap"
998 }, activeKeys.map((key) => /* @__PURE__ */ react.createElement(_elementor_ui.Chip, {
999 key,
1000 label: filterConfig[key],
1001 onDelete: () => handleRemove(key),
1002 sx: chipSx,
1003 size: "tiny"
1004 }))), showClearIcon && /* @__PURE__ */ react.createElement(ClearIconButton, {
1005 trigger: "header",
1006 tooltipText: (0, _wordpress_i18n.__)("Clear Filters", "elementor"),
1007 sx: { margin: "0 0 auto auto" }
1008 }));
1009 };
1010 var chipSx = {
1011 "& .MuiChip-deleteIcon": {
1012 display: "none",
1013 transition: "opacity 0.2s"
1014 },
1015 "&:hover .MuiChip-deleteIcon": { display: "block" }
1016 };
1017
1018 //#endregion
1019 //#region packages/packages/core/editor-global-classes/src/components/search-and-filter/components/filter/css-class-filter.tsx
1020 var CssClassFilter = () => {
1021 const { filters: { filters } } = useSearchAndFilters();
1022 const popupState = (0, _elementor_ui.usePopupState)({
1023 variant: "popover",
1024 disableAutoFocus: true
1025 });
1026 (0, react.useEffect)(() => {
1027 if (popupState.isOpen) trackGlobalClasses({ event: "classManagerFiltersOpened" });
1028 }, [popupState.isOpen]);
1029 const showCleanIcon = Object.values(filters).some((value) => value);
1030 return /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(_elementor_ui.Tooltip, {
1031 title: (0, _wordpress_i18n.__)("Filters", "elementor"),
1032 placement: "top"
1033 }, /* @__PURE__ */ react.createElement(_elementor_ui.ToggleButton, {
1034 value: "filter",
1035 size: "tiny",
1036 selected: popupState.isOpen,
1037 ...(0, _elementor_ui.bindToggle)(popupState)
1038 }, /* @__PURE__ */ react.createElement(_elementor_icons.FilterIcon, { fontSize: "tiny" }))), /* @__PURE__ */ react.createElement(_elementor_ui.Popover, {
1039 sx: { maxWidth: "344px" },
1040 anchorOrigin: {
1041 vertical: "top",
1042 horizontal: "right"
1043 },
1044 transformOrigin: {
1045 vertical: "top",
1046 horizontal: -21
1047 },
1048 ...(0, _elementor_ui.bindPopover)(popupState)
1049 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.PopoverHeader, {
1050 actions: showCleanIcon ? [/* @__PURE__ */ react.createElement(ClearIconButton, {
1051 trigger: "menu",
1052 key: "clear-all-button",
1053 tooltipText: (0, _wordpress_i18n.__)("Clear all", "elementor")
1054 })] : [],
1055 onClose: popupState.close,
1056 title: (0, _wordpress_i18n.__)("Filters", "elementor"),
1057 icon: /* @__PURE__ */ react.createElement(_elementor_icons.FilterIcon, { fontSize: "tiny" })
1058 }), /* @__PURE__ */ react.createElement(_elementor_ui.Divider, { sx: { borderWidth: "1px 0 0 0" } }), /* @__PURE__ */ react.createElement(_elementor_editor_ui.PopoverBody, {
1059 width: 344,
1060 height: 125
1061 }, /* @__PURE__ */ react.createElement(FilterList, null))));
1062 };
1063
1064 //#endregion
1065 //#region packages/packages/core/editor-global-classes/src/components/search-and-filter/components/search/class-manager-search.tsx
1066 var ClassManagerSearch = () => {
1067 const { search: { inputValue, handleChange } } = useSearchAndFilters();
1068 return /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
1069 direction: "row",
1070 gap: .5,
1071 sx: { width: "100%" }
1072 }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: { flexGrow: 1 } }, /* @__PURE__ */ react.createElement(_elementor_ui.TextField, {
1073 role: "search",
1074 fullWidth: true,
1075 size: "tiny",
1076 value: inputValue,
1077 onFocus: () => {
1078 trackGlobalClasses({ event: "classManagerSearched" });
1079 },
1080 placeholder: (0, _wordpress_i18n.__)("Search", "elementor"),
1081 onChange: (e) => handleChange(e.target.value),
1082 InputProps: { startAdornment: /* @__PURE__ */ react.createElement(_elementor_ui.InputAdornment, { position: "start" }, /* @__PURE__ */ react.createElement(_elementor_icons.SearchIcon, { fontSize: "tiny" })) }
1083 })));
1084 };
1085
1086 //#endregion
1087 //#region packages/packages/core/editor-global-classes/src/components/class-manager/class-manager-introduction.tsx
1088 var MESSAGE_KEY = "global-class-manager";
1089 var ClassManagerIntroduction = () => {
1090 const [isMessageSuppressed, suppressMessage] = (0, _elementor_editor_current_user.useSuppressedMessage)(MESSAGE_KEY);
1091 const [shouldShowIntroduction, setShouldShowIntroduction] = (0, react.useState)(!isMessageSuppressed);
1092 return /* @__PURE__ */ react.createElement(_elementor_editor_ui.IntroductionModal, {
1093 open: shouldShowIntroduction,
1094 title: (0, _wordpress_i18n.__)("Class Manager", "elementor"),
1095 handleClose: (shouldShowAgain) => {
1096 if (!shouldShowAgain) suppressMessage();
1097 setShouldShowIntroduction(false);
1098 }
1099 }, /* @__PURE__ */ react.createElement(_elementor_ui.Image, {
1100 sx: {
1101 width: "100%",
1102 aspectRatio: "16 / 9"
1103 },
1104 src: "https://assets.elementor.com/packages/v1/images/class-manager-intro.svg",
1105 alt: ""
1106 }), /* @__PURE__ */ react.createElement(IntroductionContent, null));
1107 };
1108 var IntroductionContent = () => {
1109 return /* @__PURE__ */ react.createElement(_elementor_ui.Box, { p: 3 }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, { variant: "body2" }, (0, _wordpress_i18n.__)("The Class Manager lets you see all the classes you've created, plus adjust their priority, rename them, and delete unused classes to keep your CSS structured.", "elementor")), /* @__PURE__ */ react.createElement("br", null), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, { variant: "body2" }, (0, _wordpress_i18n.__)("Remember, when editing an item within a specific class, any changes you make will apply across all elements in that class.", "elementor")));
1110 };
1111
1112 //#endregion
1113 //#region packages/packages/core/editor-global-classes/src/components/class-manager/delete-class.ts
1114 var isDeleted = false;
1115 var deleteClass = (id) => {
1116 trackGlobalClasses({
1117 event: "classDeleted",
1118 classId: id,
1119 runAction: () => {
1120 (0, _elementor_store.__dispatch)(slice.actions.delete(id));
1121 isDeleted = true;
1122 }
1123 });
1124 };
1125 var onDelete = async () => {
1126 isDeleted = false;
1127 };
1128 var hasDeletedItems = () => isDeleted;
1129
1130 //#endregion
1131 //#region packages/packages/core/editor-global-classes/src/components/class-manager/flipped-color-swatch-icon.tsx
1132 var FlippedColorSwatchIcon = ({ sx, ...props }) => /* @__PURE__ */ react.createElement(_elementor_icons.ColorSwatchIcon, {
1133 sx: {
1134 transform: "rotate(90deg)",
1135 ...sx
1136 },
1137 ...props
1138 });
1139
1140 //#endregion
1141 //#region packages/node_modules/@tanstack/virtual-core/dist/esm/utils.js
1142 function memo(getDeps, fn, opts) {
1143 let deps = opts.initialDeps ?? [];
1144 let result;
1145 let isInitial = true;
1146 function memoizedFunction() {
1147 var _a;
1148 var _b;
1149 var _c;
1150 let depTime;
1151 if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
1152 const newDeps = getDeps();
1153 if (!(newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep))) return result;
1154 deps = newDeps;
1155 let resultTime;
1156 if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
1157 result = fn(...newDeps);
1158 if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
1159 const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
1160 const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
1161 const resultFpsPercentage = resultEndTime / 16;
1162 const pad = (str, num) => {
1163 str = String(str);
1164 while (str.length < num) str = " " + str;
1165 return str;
1166 };
1167 console.info(`%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`, `
1168 font-size: .6rem;
1169 font-weight: bold;
1170 color: hsl(${Math.max(0, Math.min(120 - 120 * resultFpsPercentage, 120))}deg 100% 31%);`, opts == null ? void 0 : opts.key);
1171 }
1172 if ((opts == null ? void 0 : opts.onChange) && !(isInitial && opts.skipInitialOnChange)) opts.onChange(result);
1173 isInitial = false;
1174 return result;
1175 }
1176 memoizedFunction.updateDeps = (newDeps) => {
1177 deps = newDeps;
1178 };
1179 return memoizedFunction;
1180 }
1181 function notUndefined(value, msg) {
1182 if (value === void 0) throw new Error(`Unexpected undefined${msg ? `: ${msg}` : ""}`);
1183 else return value;
1184 }
1185 var approxEqual = (a, b) => Math.abs(a - b) < 1.01;
1186 var debounce = (targetWindow, fn, ms) => {
1187 let timeoutId;
1188 return function(...args) {
1189 targetWindow.clearTimeout(timeoutId);
1190 timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
1191 };
1192 };
1193
1194 //#endregion
1195 //#region packages/node_modules/@tanstack/virtual-core/dist/esm/index.js
1196 var getRect = (element) => {
1197 const { offsetWidth, offsetHeight } = element;
1198 return {
1199 width: offsetWidth,
1200 height: offsetHeight
1201 };
1202 };
1203 var defaultKeyExtractor = (index) => index;
1204 var defaultRangeExtractor = (range) => {
1205 const start = Math.max(range.startIndex - range.overscan, 0);
1206 const end = Math.min(range.endIndex + range.overscan, range.count - 1);
1207 const arr = [];
1208 for (let i = start; i <= end; i++) arr.push(i);
1209 return arr;
1210 };
1211 var observeElementRect = (instance, cb) => {
1212 const element = instance.scrollElement;
1213 if (!element) return;
1214 const targetWindow = instance.targetWindow;
1215 if (!targetWindow) return;
1216 const handler = (rect) => {
1217 const { width, height } = rect;
1218 cb({
1219 width: Math.round(width),
1220 height: Math.round(height)
1221 });
1222 };
1223 handler(getRect(element));
1224 if (!targetWindow.ResizeObserver) return () => {};
1225 const observer = new targetWindow.ResizeObserver((entries) => {
1226 const run = () => {
1227 const entry = entries[0];
1228 if (entry == null ? void 0 : entry.borderBoxSize) {
1229 const box = entry.borderBoxSize[0];
1230 if (box) {
1231 handler({
1232 width: box.inlineSize,
1233 height: box.blockSize
1234 });
1235 return;
1236 }
1237 }
1238 handler(getRect(element));
1239 };
1240 instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
1241 });
1242 observer.observe(element, { box: "border-box" });
1243 return () => {
1244 observer.unobserve(element);
1245 };
1246 };
1247 var addEventListenerOptions = { passive: true };
1248 var supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
1249 var observeElementOffset = (instance, cb) => {
1250 const element = instance.scrollElement;
1251 if (!element) return;
1252 const targetWindow = instance.targetWindow;
1253 if (!targetWindow) return;
1254 let offset = 0;
1255 const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(targetWindow, () => {
1256 cb(offset, false);
1257 }, instance.options.isScrollingResetDelay);
1258 const createHandler = (isScrolling) => () => {
1259 const { horizontal, isRtl } = instance.options;
1260 offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
1261 fallback();
1262 cb(offset, isScrolling);
1263 };
1264 const handler = createHandler(true);
1265 const endHandler = createHandler(false);
1266 element.addEventListener("scroll", handler, addEventListenerOptions);
1267 const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
1268 if (registerScrollendEvent) element.addEventListener("scrollend", endHandler, addEventListenerOptions);
1269 return () => {
1270 element.removeEventListener("scroll", handler);
1271 if (registerScrollendEvent) element.removeEventListener("scrollend", endHandler);
1272 };
1273 };
1274 var measureElement = (element, entry, instance) => {
1275 if (entry == null ? void 0 : entry.borderBoxSize) {
1276 const box = entry.borderBoxSize[0];
1277 if (box) return Math.round(box[instance.options.horizontal ? "inlineSize" : "blockSize"]);
1278 }
1279 return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
1280 };
1281 var elementScroll = (offset, { adjustments = 0, behavior }, instance) => {
1282 var _a;
1283 var _b;
1284 const toOffset = offset + adjustments;
1285 (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null || _b.call(_a, {
1286 [instance.options.horizontal ? "left" : "top"]: toOffset,
1287 behavior
1288 });
1289 };
1290 var Virtualizer = class {
1291 constructor(opts) {
1292 this.unsubs = [];
1293 this.scrollElement = null;
1294 this.targetWindow = null;
1295 this.isScrolling = false;
1296 this.scrollState = null;
1297 this.measurementsCache = [];
1298 this.itemSizeCache = /* @__PURE__ */ new Map();
1299 this.laneAssignments = /* @__PURE__ */ new Map();
1300 this.pendingMeasuredCacheIndexes = [];
1301 this.prevLanes = void 0;
1302 this.lanesChangedFlag = false;
1303 this.lanesSettling = false;
1304 this.scrollRect = null;
1305 this.scrollOffset = null;
1306 this.scrollDirection = null;
1307 this.scrollAdjustments = 0;
1308 this.elementsCache = /* @__PURE__ */ new Map();
1309 this.now = () => {
1310 var _a;
1311 var _b;
1312 var _c;
1313 return ((_c = (_b = (_a = this.targetWindow) == null ? void 0 : _a.performance) == null ? void 0 : _b.now) == null ? void 0 : _c.call(_b)) ?? Date.now();
1314 };
1315 this.observer = /* @__PURE__ */ (() => {
1316 let _ro = null;
1317 const get = () => {
1318 if (_ro) return _ro;
1319 if (!this.targetWindow || !this.targetWindow.ResizeObserver) return null;
1320 return _ro = new this.targetWindow.ResizeObserver((entries) => {
1321 entries.forEach((entry) => {
1322 const run = () => {
1323 const node = entry.target;
1324 const index = this.indexFromElement(node);
1325 if (!node.isConnected) {
1326 this.observer.unobserve(node);
1327 return;
1328 }
1329 if (this.shouldMeasureDuringScroll(index)) this.resizeItem(index, this.options.measureElement(node, entry, this));
1330 };
1331 this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
1332 });
1333 });
1334 };
1335 return {
1336 disconnect: () => {
1337 var _a;
1338 (_a = get()) == null || _a.disconnect();
1339 _ro = null;
1340 },
1341 observe: (target) => {
1342 var _a;
1343 return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
1344 },
1345 unobserve: (target) => {
1346 var _a;
1347 return (_a = get()) == null ? void 0 : _a.unobserve(target);
1348 }
1349 };
1350 })();
1351 this.range = null;
1352 this.setOptions = (opts2) => {
1353 Object.entries(opts2).forEach(([key, value]) => {
1354 if (typeof value === "undefined") delete opts2[key];
1355 });
1356 this.options = {
1357 debug: false,
1358 initialOffset: 0,
1359 overscan: 1,
1360 paddingStart: 0,
1361 paddingEnd: 0,
1362 scrollPaddingStart: 0,
1363 scrollPaddingEnd: 0,
1364 horizontal: false,
1365 getItemKey: defaultKeyExtractor,
1366 rangeExtractor: defaultRangeExtractor,
1367 onChange: () => {},
1368 measureElement,
1369 initialRect: {
1370 width: 0,
1371 height: 0
1372 },
1373 scrollMargin: 0,
1374 gap: 0,
1375 indexAttribute: "data-index",
1376 initialMeasurementsCache: [],
1377 lanes: 1,
1378 isScrollingResetDelay: 150,
1379 enabled: true,
1380 isRtl: false,
1381 useScrollendEvent: false,
1382 useAnimationFrameWithResizeObserver: false,
1383 laneAssignmentMode: "estimate",
1384 ...opts2
1385 };
1386 };
1387 this.notify = (sync) => {
1388 var _a;
1389 var _b;
1390 (_b = (_a = this.options).onChange) == null || _b.call(_a, this, sync);
1391 };
1392 this.maybeNotify = memo(() => {
1393 this.calculateRange();
1394 return [
1395 this.isScrolling,
1396 this.range ? this.range.startIndex : null,
1397 this.range ? this.range.endIndex : null
1398 ];
1399 }, (isScrolling) => {
1400 this.notify(isScrolling);
1401 }, {
1402 key: "maybeNotify",
1403 debug: () => this.options.debug,
1404 initialDeps: [
1405 this.isScrolling,
1406 this.range ? this.range.startIndex : null,
1407 this.range ? this.range.endIndex : null
1408 ]
1409 });
1410 this.cleanup = () => {
1411 this.unsubs.filter(Boolean).forEach((d) => d());
1412 this.unsubs = [];
1413 this.observer.disconnect();
1414 if (this.rafId != null && this.targetWindow) {
1415 this.targetWindow.cancelAnimationFrame(this.rafId);
1416 this.rafId = null;
1417 }
1418 this.scrollState = null;
1419 this.scrollElement = null;
1420 this.targetWindow = null;
1421 };
1422 this._didMount = () => {
1423 return () => {
1424 this.cleanup();
1425 };
1426 };
1427 this._willUpdate = () => {
1428 var _a;
1429 const scrollElement = this.options.enabled ? this.options.getScrollElement() : null;
1430 if (this.scrollElement !== scrollElement) {
1431 this.cleanup();
1432 if (!scrollElement) {
1433 this.maybeNotify();
1434 return;
1435 }
1436 this.scrollElement = scrollElement;
1437 if (this.scrollElement && "ownerDocument" in this.scrollElement) this.targetWindow = this.scrollElement.ownerDocument.defaultView;
1438 else this.targetWindow = ((_a = this.scrollElement) == null ? void 0 : _a.window) ?? null;
1439 this.elementsCache.forEach((cached) => {
1440 this.observer.observe(cached);
1441 });
1442 this.unsubs.push(this.options.observeElementRect(this, (rect) => {
1443 this.scrollRect = rect;
1444 this.maybeNotify();
1445 }));
1446 this.unsubs.push(this.options.observeElementOffset(this, (offset, isScrolling) => {
1447 this.scrollAdjustments = 0;
1448 this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
1449 this.scrollOffset = offset;
1450 this.isScrolling = isScrolling;
1451 if (this.scrollState) this.scheduleScrollReconcile();
1452 this.maybeNotify();
1453 }));
1454 this._scrollToOffset(this.getScrollOffset(), {
1455 adjustments: void 0,
1456 behavior: void 0
1457 });
1458 }
1459 };
1460 this.rafId = null;
1461 this.getSize = () => {
1462 if (!this.options.enabled) {
1463 this.scrollRect = null;
1464 return 0;
1465 }
1466 this.scrollRect = this.scrollRect ?? this.options.initialRect;
1467 return this.scrollRect[this.options.horizontal ? "width" : "height"];
1468 };
1469 this.getScrollOffset = () => {
1470 if (!this.options.enabled) {
1471 this.scrollOffset = null;
1472 return 0;
1473 }
1474 this.scrollOffset = this.scrollOffset ?? (typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset);
1475 return this.scrollOffset;
1476 };
1477 this.getFurthestMeasurement = (measurements, index) => {
1478 const furthestMeasurementsFound = /* @__PURE__ */ new Map();
1479 const furthestMeasurements = /* @__PURE__ */ new Map();
1480 for (let m = index - 1; m >= 0; m--) {
1481 const measurement = measurements[m];
1482 if (furthestMeasurementsFound.has(measurement.lane)) continue;
1483 const previousFurthestMeasurement = furthestMeasurements.get(measurement.lane);
1484 if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) furthestMeasurements.set(measurement.lane, measurement);
1485 else if (measurement.end < previousFurthestMeasurement.end) furthestMeasurementsFound.set(measurement.lane, true);
1486 if (furthestMeasurementsFound.size === this.options.lanes) break;
1487 }
1488 return furthestMeasurements.size === this.options.lanes ? Array.from(furthestMeasurements.values()).sort((a, b) => {
1489 if (a.end === b.end) return a.index - b.index;
1490 return a.end - b.end;
1491 })[0] : void 0;
1492 };
1493 this.getMeasurementOptions = memo(() => [
1494 this.options.count,
1495 this.options.paddingStart,
1496 this.options.scrollMargin,
1497 this.options.getItemKey,
1498 this.options.enabled,
1499 this.options.lanes,
1500 this.options.laneAssignmentMode
1501 ], (count, paddingStart, scrollMargin, getItemKey, enabled, lanes, laneAssignmentMode) => {
1502 if (this.prevLanes !== void 0 && this.prevLanes !== lanes) this.lanesChangedFlag = true;
1503 this.prevLanes = lanes;
1504 this.pendingMeasuredCacheIndexes = [];
1505 return {
1506 count,
1507 paddingStart,
1508 scrollMargin,
1509 getItemKey,
1510 enabled,
1511 lanes,
1512 laneAssignmentMode
1513 };
1514 }, { key: false });
1515 this.getMeasurements = memo(() => [this.getMeasurementOptions(), this.itemSizeCache], ({ count, paddingStart, scrollMargin, getItemKey, enabled, lanes, laneAssignmentMode }, itemSizeCache) => {
1516 if (!enabled) {
1517 this.measurementsCache = [];
1518 this.itemSizeCache.clear();
1519 this.laneAssignments.clear();
1520 return [];
1521 }
1522 if (this.laneAssignments.size > count) {
1523 for (const index of this.laneAssignments.keys()) if (index >= count) this.laneAssignments.delete(index);
1524 }
1525 if (this.lanesChangedFlag) {
1526 this.lanesChangedFlag = false;
1527 this.lanesSettling = true;
1528 this.measurementsCache = [];
1529 this.itemSizeCache.clear();
1530 this.laneAssignments.clear();
1531 this.pendingMeasuredCacheIndexes = [];
1532 }
1533 if (this.measurementsCache.length === 0 && !this.lanesSettling) {
1534 this.measurementsCache = this.options.initialMeasurementsCache;
1535 this.measurementsCache.forEach((item) => {
1536 this.itemSizeCache.set(item.key, item.size);
1537 });
1538 }
1539 const min = this.lanesSettling ? 0 : this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
1540 this.pendingMeasuredCacheIndexes = [];
1541 if (this.lanesSettling && this.measurementsCache.length === count) this.lanesSettling = false;
1542 const measurements = this.measurementsCache.slice(0, min);
1543 const laneLastIndex = new Array(lanes).fill(void 0);
1544 for (let m = 0; m < min; m++) {
1545 const item = measurements[m];
1546 if (item) laneLastIndex[item.lane] = m;
1547 }
1548 for (let i = min; i < count; i++) {
1549 const key = getItemKey(i);
1550 const cachedLane = this.laneAssignments.get(i);
1551 let lane;
1552 let start;
1553 const shouldCacheLane = laneAssignmentMode === "estimate" || itemSizeCache.has(key);
1554 if (cachedLane !== void 0 && this.options.lanes > 1) {
1555 lane = cachedLane;
1556 const prevIndex = laneLastIndex[lane];
1557 const prevInLane = prevIndex !== void 0 ? measurements[prevIndex] : void 0;
1558 start = prevInLane ? prevInLane.end + this.options.gap : paddingStart + scrollMargin;
1559 } else {
1560 const furthestMeasurement = this.options.lanes === 1 ? measurements[i - 1] : this.getFurthestMeasurement(measurements, i);
1561 start = furthestMeasurement ? furthestMeasurement.end + this.options.gap : paddingStart + scrollMargin;
1562 lane = furthestMeasurement ? furthestMeasurement.lane : i % this.options.lanes;
1563 if (this.options.lanes > 1 && shouldCacheLane) this.laneAssignments.set(i, lane);
1564 }
1565 const measuredSize = itemSizeCache.get(key);
1566 const size = typeof measuredSize === "number" ? measuredSize : this.options.estimateSize(i);
1567 const end = start + size;
1568 measurements[i] = {
1569 index: i,
1570 start,
1571 size,
1572 end,
1573 key,
1574 lane
1575 };
1576 laneLastIndex[lane] = i;
1577 }
1578 this.measurementsCache = measurements;
1579 return measurements;
1580 }, {
1581 key: "getMeasurements",
1582 debug: () => this.options.debug
1583 });
1584 this.calculateRange = memo(() => [
1585 this.getMeasurements(),
1586 this.getSize(),
1587 this.getScrollOffset(),
1588 this.options.lanes
1589 ], (measurements, outerSize, scrollOffset, lanes) => {
1590 return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
1591 measurements,
1592 outerSize,
1593 scrollOffset,
1594 lanes
1595 }) : null;
1596 }, {
1597 key: "calculateRange",
1598 debug: () => this.options.debug
1599 });
1600 this.getVirtualIndexes = memo(() => {
1601 let startIndex = null;
1602 let endIndex = null;
1603 const range = this.calculateRange();
1604 if (range) {
1605 startIndex = range.startIndex;
1606 endIndex = range.endIndex;
1607 }
1608 this.maybeNotify.updateDeps([
1609 this.isScrolling,
1610 startIndex,
1611 endIndex
1612 ]);
1613 return [
1614 this.options.rangeExtractor,
1615 this.options.overscan,
1616 this.options.count,
1617 startIndex,
1618 endIndex
1619 ];
1620 }, (rangeExtractor, overscan, count, startIndex, endIndex) => {
1621 return startIndex === null || endIndex === null ? [] : rangeExtractor({
1622 startIndex,
1623 endIndex,
1624 overscan,
1625 count
1626 });
1627 }, {
1628 key: "getVirtualIndexes",
1629 debug: () => this.options.debug
1630 });
1631 this.indexFromElement = (node) => {
1632 const attributeName = this.options.indexAttribute;
1633 const indexStr = node.getAttribute(attributeName);
1634 if (!indexStr) {
1635 console.warn(`Missing attribute name '${attributeName}={index}' on measured element.`);
1636 return -1;
1637 }
1638 return parseInt(indexStr, 10);
1639 };
1640 this.shouldMeasureDuringScroll = (index) => {
1641 var _a;
1642 if (!this.scrollState || this.scrollState.behavior !== "smooth") return true;
1643 const scrollIndex = this.scrollState.index ?? ((_a = this.getVirtualItemForOffset(this.scrollState.lastTargetOffset)) == null ? void 0 : _a.index);
1644 if (scrollIndex !== void 0 && this.range) {
1645 const bufferSize = Math.max(this.options.overscan, Math.ceil((this.range.endIndex - this.range.startIndex) / 2));
1646 const minIndex = Math.max(0, scrollIndex - bufferSize);
1647 const maxIndex = Math.min(this.options.count - 1, scrollIndex + bufferSize);
1648 return index >= minIndex && index <= maxIndex;
1649 }
1650 return true;
1651 };
1652 this.measureElement = (node) => {
1653 if (!node) {
1654 this.elementsCache.forEach((cached, key2) => {
1655 if (!cached.isConnected) {
1656 this.observer.unobserve(cached);
1657 this.elementsCache.delete(key2);
1658 }
1659 });
1660 return;
1661 }
1662 const index = this.indexFromElement(node);
1663 const key = this.options.getItemKey(index);
1664 const prevNode = this.elementsCache.get(key);
1665 if (prevNode !== node) {
1666 if (prevNode) this.observer.unobserve(prevNode);
1667 this.observer.observe(node);
1668 this.elementsCache.set(key, node);
1669 }
1670 if ((!this.isScrolling || this.scrollState) && this.shouldMeasureDuringScroll(index)) this.resizeItem(index, this.options.measureElement(node, void 0, this));
1671 };
1672 this.resizeItem = (index, size) => {
1673 var _a;
1674 const item = this.measurementsCache[index];
1675 if (!item) return;
1676 const delta = size - (this.itemSizeCache.get(item.key) ?? item.size);
1677 if (delta !== 0) {
1678 if (((_a = this.scrollState) == null ? void 0 : _a.behavior) !== "smooth" && (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments)) {
1679 if (this.options.debug) console.info("correction", delta);
1680 this._scrollToOffset(this.getScrollOffset(), {
1681 adjustments: this.scrollAdjustments += delta,
1682 behavior: void 0
1683 });
1684 }
1685 this.pendingMeasuredCacheIndexes.push(item.index);
1686 this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size));
1687 this.notify(false);
1688 }
1689 };
1690 this.getVirtualItems = memo(() => [this.getVirtualIndexes(), this.getMeasurements()], (indexes, measurements) => {
1691 const virtualItems = [];
1692 for (let k = 0, len = indexes.length; k < len; k++) {
1693 const measurement = measurements[indexes[k]];
1694 virtualItems.push(measurement);
1695 }
1696 return virtualItems;
1697 }, {
1698 key: "getVirtualItems",
1699 debug: () => this.options.debug
1700 });
1701 this.getVirtualItemForOffset = (offset) => {
1702 const measurements = this.getMeasurements();
1703 if (measurements.length === 0) return;
1704 return notUndefined(measurements[findNearestBinarySearch(0, measurements.length - 1, (index) => notUndefined(measurements[index]).start, offset)]);
1705 };
1706 this.getMaxScrollOffset = () => {
1707 if (!this.scrollElement) return 0;
1708 if ("scrollHeight" in this.scrollElement) return this.options.horizontal ? this.scrollElement.scrollWidth - this.scrollElement.clientWidth : this.scrollElement.scrollHeight - this.scrollElement.clientHeight;
1709 else {
1710 const doc = this.scrollElement.document.documentElement;
1711 return this.options.horizontal ? doc.scrollWidth - this.scrollElement.innerWidth : doc.scrollHeight - this.scrollElement.innerHeight;
1712 }
1713 };
1714 this.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
1715 if (!this.scrollElement) return 0;
1716 const size = this.getSize();
1717 const scrollOffset = this.getScrollOffset();
1718 if (align === "auto") align = toOffset >= scrollOffset + size ? "end" : "start";
1719 if (align === "center") toOffset += (itemSize - size) / 2;
1720 else if (align === "end") toOffset -= size;
1721 const maxOffset = this.getMaxScrollOffset();
1722 return Math.max(Math.min(maxOffset, toOffset), 0);
1723 };
1724 this.getOffsetForIndex = (index, align = "auto") => {
1725 index = Math.max(0, Math.min(index, this.options.count - 1));
1726 const size = this.getSize();
1727 const scrollOffset = this.getScrollOffset();
1728 const item = this.measurementsCache[index];
1729 if (!item) return;
1730 if (align === "auto") if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) align = "end";
1731 else if (item.start <= scrollOffset + this.options.scrollPaddingStart) align = "start";
1732 else return [scrollOffset, align];
1733 if (align === "end" && index === this.options.count - 1) return [this.getMaxScrollOffset(), align];
1734 const toOffset = align === "end" ? item.end + this.options.scrollPaddingEnd : item.start - this.options.scrollPaddingStart;
1735 return [this.getOffsetForAlignment(toOffset, align, item.size), align];
1736 };
1737 this.scrollToOffset = (toOffset, { align = "start", behavior = "auto" } = {}) => {
1738 const offset = this.getOffsetForAlignment(toOffset, align);
1739 const now = this.now();
1740 this.scrollState = {
1741 index: null,
1742 align,
1743 behavior,
1744 startedAt: now,
1745 lastTargetOffset: offset,
1746 stableFrames: 0
1747 };
1748 this._scrollToOffset(offset, {
1749 adjustments: void 0,
1750 behavior
1751 });
1752 this.scheduleScrollReconcile();
1753 };
1754 this.scrollToIndex = (index, { align: initialAlign = "auto", behavior = "auto" } = {}) => {
1755 index = Math.max(0, Math.min(index, this.options.count - 1));
1756 const offsetInfo = this.getOffsetForIndex(index, initialAlign);
1757 if (!offsetInfo) return;
1758 const [offset, align] = offsetInfo;
1759 const now = this.now();
1760 this.scrollState = {
1761 index,
1762 align,
1763 behavior,
1764 startedAt: now,
1765 lastTargetOffset: offset,
1766 stableFrames: 0
1767 };
1768 this._scrollToOffset(offset, {
1769 adjustments: void 0,
1770 behavior
1771 });
1772 this.scheduleScrollReconcile();
1773 };
1774 this.scrollBy = (delta, { behavior = "auto" } = {}) => {
1775 const offset = this.getScrollOffset() + delta;
1776 const now = this.now();
1777 this.scrollState = {
1778 index: null,
1779 align: "start",
1780 behavior,
1781 startedAt: now,
1782 lastTargetOffset: offset,
1783 stableFrames: 0
1784 };
1785 this._scrollToOffset(offset, {
1786 adjustments: void 0,
1787 behavior
1788 });
1789 this.scheduleScrollReconcile();
1790 };
1791 this.getTotalSize = () => {
1792 var _a;
1793 const measurements = this.getMeasurements();
1794 let end;
1795 if (measurements.length === 0) end = this.options.paddingStart;
1796 else if (this.options.lanes === 1) end = ((_a = measurements[measurements.length - 1]) == null ? void 0 : _a.end) ?? 0;
1797 else {
1798 const endByLane = Array(this.options.lanes).fill(null);
1799 let endIndex = measurements.length - 1;
1800 while (endIndex >= 0 && endByLane.some((val) => val === null)) {
1801 const item = measurements[endIndex];
1802 if (endByLane[item.lane] === null) endByLane[item.lane] = item.end;
1803 endIndex--;
1804 }
1805 end = Math.max(...endByLane.filter((val) => val !== null));
1806 }
1807 return Math.max(end - this.options.scrollMargin + this.options.paddingEnd, 0);
1808 };
1809 this._scrollToOffset = (offset, { adjustments, behavior }) => {
1810 this.options.scrollToFn(offset, {
1811 behavior,
1812 adjustments
1813 }, this);
1814 };
1815 this.measure = () => {
1816 this.itemSizeCache = /* @__PURE__ */ new Map();
1817 this.laneAssignments = /* @__PURE__ */ new Map();
1818 this.notify(false);
1819 };
1820 this.setOptions(opts);
1821 }
1822 scheduleScrollReconcile() {
1823 if (!this.targetWindow) {
1824 this.scrollState = null;
1825 return;
1826 }
1827 if (this.rafId != null) return;
1828 this.rafId = this.targetWindow.requestAnimationFrame(() => {
1829 this.rafId = null;
1830 this.reconcileScroll();
1831 });
1832 }
1833 reconcileScroll() {
1834 if (!this.scrollState) return;
1835 if (!this.scrollElement) return;
1836 if (this.now() - this.scrollState.startedAt > 5e3) {
1837 this.scrollState = null;
1838 return;
1839 }
1840 const offsetInfo = this.scrollState.index != null ? this.getOffsetForIndex(this.scrollState.index, this.scrollState.align) : void 0;
1841 const targetOffset = offsetInfo ? offsetInfo[0] : this.scrollState.lastTargetOffset;
1842 const STABLE_FRAMES = 1;
1843 const targetChanged = targetOffset !== this.scrollState.lastTargetOffset;
1844 if (!targetChanged && approxEqual(targetOffset, this.getScrollOffset())) {
1845 this.scrollState.stableFrames++;
1846 if (this.scrollState.stableFrames >= STABLE_FRAMES) {
1847 this.scrollState = null;
1848 return;
1849 }
1850 } else {
1851 this.scrollState.stableFrames = 0;
1852 if (targetChanged) {
1853 this.scrollState.lastTargetOffset = targetOffset;
1854 this.scrollState.behavior = "auto";
1855 this._scrollToOffset(targetOffset, {
1856 adjustments: void 0,
1857 behavior: "auto"
1858 });
1859 }
1860 }
1861 this.scheduleScrollReconcile();
1862 }
1863 };
1864 var findNearestBinarySearch = (low, high, getCurrentValue, value) => {
1865 while (low <= high) {
1866 const middle = (low + high) / 2 | 0;
1867 const currentValue = getCurrentValue(middle);
1868 if (currentValue < value) low = middle + 1;
1869 else if (currentValue > value) high = middle - 1;
1870 else return middle;
1871 }
1872 if (low > 0) return low - 1;
1873 else return 0;
1874 };
1875 function calculateRange({ measurements, outerSize, scrollOffset, lanes }) {
1876 const lastIndex = measurements.length - 1;
1877 const getOffset = (index) => measurements[index].start;
1878 if (measurements.length <= lanes) return {
1879 startIndex: 0,
1880 endIndex: lastIndex
1881 };
1882 let startIndex = findNearestBinarySearch(0, lastIndex, getOffset, scrollOffset);
1883 let endIndex = startIndex;
1884 if (lanes === 1) while (endIndex < lastIndex && measurements[endIndex].end < scrollOffset + outerSize) endIndex++;
1885 else if (lanes > 1) {
1886 const endPerLane = Array(lanes).fill(0);
1887 while (endIndex < lastIndex && endPerLane.some((pos) => pos < scrollOffset + outerSize)) {
1888 const item = measurements[endIndex];
1889 endPerLane[item.lane] = item.end;
1890 endIndex++;
1891 }
1892 const startPerLane = Array(lanes).fill(scrollOffset + outerSize);
1893 while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {
1894 const item = measurements[startIndex];
1895 startPerLane[item.lane] = item.start;
1896 startIndex--;
1897 }
1898 startIndex = Math.max(0, startIndex - startIndex % lanes);
1899 endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - endIndex % lanes));
1900 }
1901 return {
1902 startIndex,
1903 endIndex
1904 };
1905 }
1906
1907 //#endregion
1908 //#region packages/node_modules/@tanstack/react-virtual/dist/esm/index.js
1909 var useIsomorphicLayoutEffect = typeof document !== "undefined" ? react$1.useLayoutEffect : react$1.useEffect;
1910 function useVirtualizerBase({ useFlushSync = true, ...options }) {
1911 const rerender = react$1.useReducer(() => ({}), {})[1];
1912 const resolvedOptions = {
1913 ...options,
1914 onChange: (instance2, sync) => {
1915 var _a;
1916 if (useFlushSync && sync) (0, react_dom.flushSync)(rerender);
1917 else rerender();
1918 (_a = options.onChange) == null || _a.call(options, instance2, sync);
1919 }
1920 };
1921 const [instance] = react$1.useState(() => new Virtualizer(resolvedOptions));
1922 instance.setOptions(resolvedOptions);
1923 useIsomorphicLayoutEffect(() => {
1924 return instance._didMount();
1925 }, []);
1926 useIsomorphicLayoutEffect(() => {
1927 return instance._willUpdate();
1928 });
1929 return instance;
1930 }
1931 function useVirtualizer(options) {
1932 return useVirtualizerBase({
1933 observeElementRect,
1934 observeElementOffset,
1935 scrollToFn: elementScroll,
1936 ...options
1937 });
1938 }
1939
1940 //#endregion
1941 //#region packages/packages/core/editor-global-classes/src/hooks/use-ordered-classes.ts
1942 var useOrderedClasses = () => {
1943 return (0, _elementor_store.__useSelector)(selectOrderedClasses);
1944 };
1945
1946 //#endregion
1947 //#region packages/packages/core/editor-global-classes/src/hooks/use-css-class-usage-by-id.ts
1948 var EMPTY_CLASS_USAGE = {
1949 total: 0,
1950 content: []
1951 };
1952 var useCssClassUsageByID = (id) => {
1953 const { data, ...rest } = useCssClassUsage();
1954 const classData = data?.[id] ?? EMPTY_CLASS_USAGE;
1955 return {
1956 ...rest,
1957 data: classData
1958 };
1959 };
1960
1961 //#endregion
1962 //#region packages/packages/core/editor-global-classes/src/components/css-class-usage/components/css-class-usage-popover.tsx
1963 var iconMapper = {
1964 "wp-post": {
1965 label: (0, _wordpress_i18n.__)("Post", "elementor"),
1966 icon: /* @__PURE__ */ react.createElement(_elementor_icons.PostTypeIcon, { fontSize: "inherit" })
1967 },
1968 "wp-page": {
1969 label: (0, _wordpress_i18n.__)("Page", "elementor"),
1970 icon: /* @__PURE__ */ react.createElement(_elementor_icons.PagesIcon, { fontSize: "inherit" })
1971 },
1972 popup: {
1973 label: (0, _wordpress_i18n.__)("Popup", "elementor"),
1974 icon: /* @__PURE__ */ react.createElement(_elementor_icons.PopupTemplateIcon, { fontSize: "inherit" })
1975 },
1976 header: {
1977 label: (0, _wordpress_i18n.__)("Header", "elementor"),
1978 icon: /* @__PURE__ */ react.createElement(_elementor_icons.HeaderTemplateIcon, { fontSize: "inherit" })
1979 },
1980 footer: {
1981 label: (0, _wordpress_i18n.__)("Footer", "elementor"),
1982 icon: /* @__PURE__ */ react.createElement(_elementor_icons.FooterTemplateIcon, { fontSize: "inherit" })
1983 }
1984 };
1985 var CssClassUsagePopover = ({ cssClassID, onClose }) => {
1986 const { data: classUsage } = useCssClassUsageByID(cssClassID);
1987 const onNavigate = (0, _elementor_editor_documents.__useOpenDocumentInNewTab)();
1988 const cssClassUsageRecords = classUsage?.content.map(({ title, elements, pageId, type }) => ({
1989 type: "item",
1990 value: pageId,
1991 label: title,
1992 secondaryText: elements.length.toString(),
1993 docType: type
1994 })) ?? [];
1995 const handleSelect = (value) => {
1996 onNavigate(+value);
1997 trackGlobalClasses({
1998 event: "classUsageLocate",
1999 classId: cssClassID
2000 });
2001 };
2002 return /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(_elementor_editor_ui.PopoverHeader, {
2003 icon: /* @__PURE__ */ react.createElement(_elementor_icons.CurrentLocationIcon, { fontSize: "tiny" }),
2004 title: /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
2005 flexDirection: "row",
2006 gap: 1,
2007 alignItems: "center"
2008 }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, { "aria-label": "header-title" }, (0, _wordpress_i18n.__)("Locator", "elementor")), /* @__PURE__ */ react.createElement(_elementor_ui.Box, null, /* @__PURE__ */ react.createElement(_elementor_ui.Chip, {
2009 sx: { lineHeight: 1 },
2010 size: "tiny",
2011 label: classUsage.total
2012 }))),
2013 onClose
2014 }), /* @__PURE__ */ react.createElement(_elementor_ui.Divider, null), /* @__PURE__ */ react.createElement(_elementor_editor_ui.PopoverBody, { width: 300 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.PopoverMenuList, {
2015 onSelect: handleSelect,
2016 items: cssClassUsageRecords,
2017 onClose: () => {},
2018 menuListTemplate: StyledCssClassUsageItem,
2019 menuItemContentTemplate: (cssClassUsageRecord) => /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
2020 flexDirection: "row",
2021 flex: 1,
2022 alignItems: "center"
2023 }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2024 display: "flex",
2025 sx: { pr: 1 }
2026 }, /* @__PURE__ */ react.createElement(_elementor_ui.Tooltip, {
2027 disableInteractive: true,
2028 title: iconMapper?.[cssClassUsageRecord.docType]?.label ?? cssClassUsageRecord.docType,
2029 placement: "top"
2030 }, /* @__PURE__ */ react.createElement(_elementor_ui.Icon, { fontSize: "small" }, iconMapper?.[cssClassUsageRecord.docType]?.icon || /* @__PURE__ */ react.createElement(_elementor_icons.PagesIcon, { fontSize: "inherit" })))), /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2031 sx: {
2032 pr: .5,
2033 maxWidth: "173px"
2034 },
2035 display: "flex"
2036 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.EllipsisWithTooltip, {
2037 title: cssClassUsageRecord.label,
2038 as: _elementor_ui.Typography,
2039 variant: "caption",
2040 maxWidth: "173px",
2041 sx: { lineHeight: 1 }
2042 })), /* @__PURE__ */ react.createElement(_elementor_icons.ExternalLinkIcon, {
2043 className: "hover-only-icon",
2044 fontSize: "tiny"
2045 }), /* @__PURE__ */ react.createElement(_elementor_ui.Chip, {
2046 sx: { ml: "auto" },
2047 size: "tiny",
2048 label: cssClassUsageRecord.secondaryText
2049 }))
2050 })));
2051 };
2052 var StyledCssClassUsageItem = (0, _elementor_ui.styled)(_elementor_ui.MenuList)(({ theme }) => ({
2053 "& > li": {
2054 display: "flex",
2055 cursor: "pointer",
2056 height: 32,
2057 width: "100%"
2058 },
2059 "& > [role=\"option\"]": {
2060 ...theme.typography.caption,
2061 lineHeight: "inherit",
2062 padding: theme.spacing(.5, 1, .5, 2),
2063 textOverflow: "ellipsis",
2064 position: "absolute",
2065 top: 0,
2066 left: 0,
2067 opacity: 1,
2068 ".hover-only-icon": {
2069 color: theme.palette.text.disabled,
2070 opacity: 0
2071 },
2072 "&:hover": {
2073 borderRadius: theme.spacing(.5),
2074 backgroundColor: theme.palette.action.hover,
2075 ".hover-only-icon": {
2076 color: theme.palette.text.disabled,
2077 opacity: 1
2078 }
2079 }
2080 },
2081 width: "100%",
2082 position: "relative"
2083 }));
2084
2085 //#endregion
2086 //#region packages/packages/core/editor-global-classes/src/components/css-class-usage/components/css-class-usage-trigger.tsx
2087 var CssClassUsageTrigger = ({ id, onClick }) => {
2088 const { data: { total }, isLoading } = useCssClassUsageByID(id);
2089 const cssClassUsagePopover = (0, _elementor_ui.usePopupState)({
2090 variant: "popover",
2091 popupId: "css-class-usage-popover"
2092 });
2093 if (isLoading) return null;
2094 const WrapperComponent = total !== 0 ? TooltipWrapper : InfoAlertMessage;
2095 const handleMouseEnter = () => {
2096 trackGlobalClasses({
2097 event: "classUsageHovered",
2098 classId: id,
2099 usage: total
2100 });
2101 };
2102 const handleClick = (e) => {
2103 if (total !== 0) {
2104 (0, _elementor_ui.bindTrigger)(cssClassUsagePopover).onClick(e);
2105 onClick(id);
2106 trackGlobalClasses({
2107 event: "classUsageClicked",
2108 classId: id
2109 });
2110 }
2111 };
2112 return /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2113 position: "relative",
2114 onMouseEnter: handleMouseEnter
2115 }, /* @__PURE__ */ react.createElement(WrapperComponent, { total }, /* @__PURE__ */ react.createElement(CustomIconButton, {
2116 disabled: total === 0,
2117 size: "tiny",
2118 ...(0, _elementor_ui.bindTrigger)(cssClassUsagePopover),
2119 onClick: handleClick
2120 }, /* @__PURE__ */ react.createElement(_elementor_icons.CurrentLocationIcon, { fontSize: "tiny" })))), /* @__PURE__ */ react.createElement(_elementor_ui.Box, null, /* @__PURE__ */ react.createElement(_elementor_ui.Popover, {
2121 anchorOrigin: {
2122 vertical: "center",
2123 horizontal: "right"
2124 },
2125 transformOrigin: {
2126 vertical: 15,
2127 horizontal: -50
2128 },
2129 ...(0, _elementor_ui.bindPopover)(cssClassUsagePopover),
2130 onClose: () => {
2131 (0, _elementor_ui.bindPopover)(cssClassUsagePopover).onClose();
2132 onClick("");
2133 }
2134 }, /* @__PURE__ */ react.createElement(CssClassUsagePopover, {
2135 onClose: cssClassUsagePopover.close,
2136 "aria-label": "css-class-usage-popover",
2137 cssClassID: id
2138 }))));
2139 };
2140 var CustomIconButton = (0, _elementor_ui.styled)(_elementor_ui.IconButton)(({ theme }) => ({
2141 "&.Mui-disabled": {
2142 pointerEvents: "auto",
2143 "&:hover": { color: theme.palette.action.disabled }
2144 },
2145 height: "22px",
2146 width: "22px"
2147 }));
2148 var TooltipWrapper = ({ children, total }) => /* @__PURE__ */ react.createElement(_elementor_ui.Tooltip, {
2149 disableInteractive: true,
2150 placement: "top",
2151 title: `${(0, _wordpress_i18n.__)("Show {{number}} {{locations}}", "elementor").replace("{{number}}", total.toString()).replace("{{locations}}", total === 1 ? (0, _wordpress_i18n.__)("location", "elementor") : (0, _wordpress_i18n.__)("locations", "elementor"))}`
2152 }, /* @__PURE__ */ react.createElement("span", null, children));
2153 var InfoAlertMessage = ({ children }) => /* @__PURE__ */ react.createElement(_elementor_ui.Infotip, {
2154 disableInteractive: true,
2155 placement: "top",
2156 color: "secondary",
2157 content: /* @__PURE__ */ react.createElement(_elementor_editor_ui.InfoAlert, { sx: { mt: 1 } }, (0, _wordpress_i18n.__)("This class isn’t being used yet.", "elementor"))
2158 }, /* @__PURE__ */ react.createElement("span", null, children));
2159
2160 //#endregion
2161 //#region packages/packages/core/editor-global-classes/src/components/class-manager/delete-confirmation-dialog.tsx
2162 var context = (0, react.createContext)(null);
2163 var DeleteConfirmationProvider = ({ children }) => {
2164 const [dialogProps, setDialogProps] = (0, react.useState)(null);
2165 const openDialog = (props) => {
2166 setDialogProps(props);
2167 };
2168 const closeDialog = () => {
2169 setDialogProps(null);
2170 };
2171 return /* @__PURE__ */ react.createElement(context.Provider, { value: {
2172 openDialog,
2173 closeDialog,
2174 dialogProps
2175 } }, children, !!dialogProps && /* @__PURE__ */ react.createElement(DeleteClassDialog, { ...dialogProps }));
2176 };
2177 var DeleteClassDialog = ({ label, id }) => {
2178 const { closeDialog } = useDeleteConfirmation();
2179 const { data: { total, content } } = useCssClassUsageByID(id);
2180 const handleConfirm = () => {
2181 closeDialog();
2182 deleteClass(id);
2183 };
2184 const text = total && content.length ? (0, _wordpress_i18n.__)("Will permanently remove it from your project and may affect the design across all elements using it. Used %1 times across %2 pages. This action cannot be undone.", "elementor").replace("%1", total.toString()).replace("%2", content.length.toString()) : (0, _wordpress_i18n.__)("Will permanently remove it from your project and may affect the design across all elements using it. This action cannot be undone.", "elementor");
2185 return /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog, {
2186 open: true,
2187 onClose: closeDialog
2188 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.Title, null, (0, _wordpress_i18n.__)("Delete this class?", "elementor")), /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.Content, null, /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.ContentText, null, (0, _wordpress_i18n.__)("Deleting", "elementor"), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2189 variant: "subtitle2",
2190 component: "span"
2191 }, "\xA0", label, "\xA0"), text)), /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.Actions, {
2192 onClose: closeDialog,
2193 onConfirm: handleConfirm
2194 }));
2195 };
2196 var useDeleteConfirmation = () => {
2197 const contextValue = (0, react.useContext)(context);
2198 if (!contextValue) throw new Error("useDeleteConfirmation must be used within a DeleteConfirmationProvider");
2199 return contextValue;
2200 };
2201
2202 //#endregion
2203 //#region packages/packages/core/editor-global-classes/src/components/class-manager/sortable.tsx
2204 var SortableProvider = (props) => /* @__PURE__ */ react.createElement(_elementor_ui.UnstableSortableProvider, {
2205 restrictAxis: true,
2206 variant: "static",
2207 dragPlaceholderStyle: { visibility: "hidden" },
2208 ...props
2209 });
2210 var SortableTrigger = (props) => /* @__PURE__ */ react.createElement(StyledSortableTrigger, {
2211 ...props,
2212 role: "button",
2213 className: "class-item-sortable-trigger",
2214 "aria-label": "sort"
2215 }, /* @__PURE__ */ react.createElement(_elementor_icons.GripVerticalIcon, { fontSize: "tiny" }));
2216 var SortableItem = ({ children, id, style, ...props }) => {
2217 return /* @__PURE__ */ react.createElement(_elementor_ui.UnstableSortableItem, {
2218 ...props,
2219 id,
2220 render: ({ itemProps, isDragged, triggerProps, itemStyle, triggerStyle, dropIndicationStyle, showDropIndication, isDragOverlay, isDragPlaceholder }) => {
2221 return /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2222 ...itemProps,
2223 style: {
2224 ...itemStyle,
2225 ...!isDragOverlay ? style : null
2226 },
2227 component: "li",
2228 role: "listitem",
2229 sx: { backgroundColor: isDragOverlay ? "background.paper" : void 0 }
2230 }, children({
2231 itemProps,
2232 isDragged,
2233 triggerProps,
2234 itemStyle,
2235 triggerStyle,
2236 isDragPlaceholder
2237 }), showDropIndication && /* @__PURE__ */ react.createElement(SortableItemIndicator, { style: dropIndicationStyle }));
2238 }
2239 });
2240 };
2241 var StyledSortableTrigger = (0, _elementor_ui.styled)("div")(({ theme }) => ({
2242 position: "absolute",
2243 left: 0,
2244 top: "50%",
2245 transform: `translate( -${theme.spacing(1.5)}, -50% )`,
2246 color: theme.palette.action.active
2247 }));
2248 var SortableItemIndicator = (0, _elementor_ui.styled)(_elementor_ui.Box)`
2249 width: 100%;
2250 height: 1px;
2251 background-color: ${({ theme }) => theme.palette.text.primary};
2252 `;
2253
2254 //#endregion
2255 //#region packages/packages/core/editor-global-classes/src/components/class-manager/class-item.tsx
2256 var ClassItem = ({ id, label, renameClass, selected, disabled, sortableTriggerProps, showSortIndicator, syncToV3, onToggleSync }) => {
2257 const itemRef = (0, react.useRef)(null);
2258 const { ref: editableRef, openEditMode, isEditing, error, getProps: getEditableProps } = (0, _elementor_editor_ui.useEditable)({
2259 value: label,
2260 onSubmit: renameClass,
2261 validation: validateLabel
2262 });
2263 const [selectedCssUsage, setSelectedCssUsage] = (0, react.useState)("");
2264 const { openDialog } = useDeleteConfirmation();
2265 const popupState = (0, _elementor_ui.usePopupState)({
2266 variant: "popover",
2267 disableAutoFocus: true
2268 });
2269 const isSelected = (selectedCssUsage === id || selected || popupState.isOpen) && !disabled;
2270 return /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(_elementor_ui.Stack, { p: 0 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.WarningInfotip, {
2271 open: Boolean(error),
2272 text: error ?? "",
2273 placement: "bottom",
2274 width: itemRef.current?.getBoundingClientRect().width,
2275 offset: [0, -15]
2276 }, /* @__PURE__ */ react.createElement(StyledListItemButton, {
2277 ref: itemRef,
2278 dense: true,
2279 disableGutters: true,
2280 showSortIndicator,
2281 showActions: isSelected || isEditing,
2282 shape: "rounded",
2283 onDoubleClick: openEditMode,
2284 selected: isSelected,
2285 disabled,
2286 focusVisibleClassName: "visible-class-item"
2287 }, /* @__PURE__ */ react.createElement(SortableTrigger, { ...sortableTriggerProps }), /* @__PURE__ */ react.createElement(Indicator, {
2288 isActive: isEditing,
2289 isError: !!error
2290 }, isEditing ? /* @__PURE__ */ react.createElement(_elementor_editor_ui.EditableField, {
2291 ref: editableRef,
2292 as: _elementor_ui.Typography,
2293 variant: "caption",
2294 ...getEditableProps()
2295 }) : /* @__PURE__ */ react.createElement(_elementor_editor_ui.EllipsisWithTooltip, {
2296 title: label,
2297 as: _elementor_ui.Typography,
2298 variant: "caption"
2299 })), /* @__PURE__ */ react.createElement(_elementor_ui.Box, { className: "class-item-locator" }, /* @__PURE__ */ react.createElement(CssClassUsageTrigger, {
2300 id,
2301 onClick: setSelectedCssUsage
2302 })), /* @__PURE__ */ react.createElement(_elementor_ui.Tooltip, {
2303 placement: "top",
2304 className: "class-item-more-actions",
2305 title: (0, _wordpress_i18n.__)("More actions", "elementor")
2306 }, /* @__PURE__ */ react.createElement(_elementor_ui.IconButton, {
2307 size: "tiny",
2308 ...(0, _elementor_ui.bindTrigger)(popupState),
2309 "aria-label": "More actions"
2310 }, /* @__PURE__ */ react.createElement(_elementor_icons.DotsVerticalIcon, { fontSize: "tiny" })))))), /* @__PURE__ */ react.createElement(_elementor_ui.Menu, {
2311 ...(0, _elementor_ui.bindMenu)(popupState),
2312 anchorOrigin: {
2313 vertical: "bottom",
2314 horizontal: "right"
2315 },
2316 transformOrigin: {
2317 vertical: "top",
2318 horizontal: "right"
2319 }
2320 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.MenuListItem, {
2321 sx: { minWidth: "160px" },
2322 onClick: () => {
2323 popupState.close();
2324 openEditMode();
2325 }
2326 }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2327 variant: "caption",
2328 sx: { color: "text.primary" }
2329 }, (0, _wordpress_i18n.__)("Rename", "elementor"))), onToggleSync && /* @__PURE__ */ react.createElement(_elementor_editor_ui.MenuListItem, { onClick: () => {
2330 popupState.close();
2331 onToggleSync(id, !syncToV3);
2332 } }, /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
2333 direction: "row",
2334 alignItems: "center",
2335 gap: 1
2336 }, syncToV3 ? /* @__PURE__ */ react.createElement(_elementor_icons.RefreshOffIcon, { fontSize: "tiny" }) : /* @__PURE__ */ react.createElement(_elementor_icons.RefreshIcon, { fontSize: "tiny" }), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2337 variant: "caption",
2338 sx: { color: "text.primary" }
2339 }, syncToV3 ? (0, _wordpress_i18n.__)("Stop syncing to Global Fonts", "elementor") : (0, _wordpress_i18n.__)("Sync to Global Fonts", "elementor")))), /* @__PURE__ */ react.createElement(_elementor_editor_ui.MenuListItem, { onClick: () => {
2340 popupState.close();
2341 openDialog({
2342 id,
2343 label
2344 });
2345 } }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2346 variant: "caption",
2347 sx: { color: "error.light" }
2348 }, (0, _wordpress_i18n.__)("Delete", "elementor")))));
2349 };
2350 var StyledListItemButton = (0, _elementor_ui.styled)(_elementor_ui.ListItemButton, { shouldForwardProp: (prop) => !["showActions", "showSortIndicator"].includes(prop) })(({ showActions, showSortIndicator }) => `
2351 min-height: 36px;
2352
2353 &.visible-class-item {
2354 box-shadow: none !important;
2355 }
2356
2357 .class-item-locator {
2358 visibility: hidden;
2359 }
2360
2361 .class-item-sortable-trigger {
2362 visibility: ${showSortIndicator && showActions ? "visible" : "hidden"};
2363 }
2364
2365 &:hover:not(:disabled) {
2366 .class-item-locator {
2367 visibility: visible;
2368 }
2369
2370 .class-item-sortable-trigger {
2371 visibility: ${showSortIndicator ? "visible" : "hidden"};
2372 }
2373 }
2374 `);
2375 var Indicator = (0, _elementor_ui.styled)(_elementor_ui.Box, { shouldForwardProp: (prop) => !["isActive", "isError"].includes(prop) })(({ theme, isActive, isError }) => ({
2376 display: "flex",
2377 width: "100%",
2378 flexGrow: 1,
2379 borderRadius: theme.spacing(.5),
2380 border: getIndicatorBorder({
2381 isActive,
2382 isError,
2383 theme
2384 }),
2385 padding: `0 ${theme.spacing(1)}`,
2386 marginLeft: isActive ? theme.spacing(1) : 0,
2387 minWidth: 0
2388 }));
2389 var getIndicatorBorder = ({ isActive, isError, theme }) => {
2390 if (isError) return `2px solid ${theme.palette.error.main}`;
2391 if (isActive) return `2px solid ${theme.palette.secondary.main}`;
2392 return "none";
2393 };
2394 var validateLabel = (newLabel) => {
2395 const result = (0, _elementor_editor_styles_repository.validateStyleLabel)(newLabel, "rename");
2396 if (result.isValid) return null;
2397 return result.errorMessage;
2398 };
2399
2400 //#endregion
2401 //#region packages/packages/core/editor-global-classes/src/components/class-manager/not-found.tsx
2402 var getNotFoundType = (searchValue, filters, filteredClasses) => {
2403 const searchNotFound = filteredClasses.length <= 0 && searchValue.length > 1;
2404 const filterNotFound = filters && filters.length === 0;
2405 if (searchNotFound && filterNotFound) return "filterAndSearch";
2406 if (searchNotFound) return "search";
2407 if (filterNotFound) return "filter";
2408 };
2409 var notFound = {
2410 filterAndSearch: {
2411 mainText: (0, _wordpress_i18n.__)("Sorry, nothing matched.", "elementor"),
2412 sceneryText: (0, _wordpress_i18n.__)("Try something else.", "elementor"),
2413 icon: /* @__PURE__ */ react.createElement(_elementor_icons.PhotoIcon, {
2414 color: "inherit",
2415 fontSize: "large"
2416 })
2417 },
2418 search: {
2419 mainText: (0, _wordpress_i18n.__)("Sorry, nothing matched", "elementor"),
2420 sceneryText: (0, _wordpress_i18n.__)("Clear your input and try something else.", "elementor"),
2421 icon: /* @__PURE__ */ react.createElement(_elementor_icons.PhotoIcon, {
2422 color: "inherit",
2423 fontSize: "large"
2424 })
2425 },
2426 filter: {
2427 mainText: (0, _wordpress_i18n.__)("Sorry, nothing matched that search.", "elementor"),
2428 sceneryText: (0, _wordpress_i18n.__)("Clear the filters and try something else.", "elementor"),
2429 icon: /* @__PURE__ */ react.createElement(_elementor_icons.ColorSwatchIcon, {
2430 color: "inherit",
2431 fontSize: "large"
2432 })
2433 }
2434 };
2435 var NotFound = ({ notFoundType }) => {
2436 const { search: { onClearSearch, inputValue }, filters: { onClearFilter } } = useSearchAndFilters();
2437 switch (notFoundType) {
2438 case "filter": return /* @__PURE__ */ react.createElement(NotFoundLayout, {
2439 ...notFound.filter,
2440 onClear: onClearFilter
2441 });
2442 case "search": return /* @__PURE__ */ react.createElement(NotFoundLayout, {
2443 ...notFound.search,
2444 searchValue: inputValue,
2445 onClear: onClearSearch
2446 });
2447 case "filterAndSearch": return /* @__PURE__ */ react.createElement(NotFoundLayout, {
2448 ...notFound.filterAndSearch,
2449 onClear: () => {
2450 onClearFilter();
2451 onClearSearch();
2452 }
2453 });
2454 }
2455 };
2456 var NotFoundLayout = ({ onClear, searchValue, mainText, sceneryText, icon }) => /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
2457 color: "text.secondary",
2458 pt: 5,
2459 alignItems: "center",
2460 gap: 1,
2461 overflow: "hidden",
2462 justifySelf: "center"
2463 }, icon, /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: { width: "100%" } }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2464 align: "center",
2465 variant: "subtitle2",
2466 color: "inherit"
2467 }, mainText), searchValue && /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2468 variant: "subtitle2",
2469 color: "inherit",
2470 sx: {
2471 display: "flex",
2472 width: "100%",
2473 justifyContent: "center"
2474 }
2475 }, /* @__PURE__ */ react.createElement("span", null, "“"), /* @__PURE__ */ react.createElement("span", { style: {
2476 maxWidth: "80%",
2477 overflow: "hidden",
2478 textOverflow: "ellipsis"
2479 } }, searchValue), /* @__PURE__ */ react.createElement("span", null, "”."))), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2480 align: "center",
2481 variant: "caption",
2482 color: "inherit"
2483 }, sceneryText), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2484 align: "center",
2485 variant: "caption",
2486 color: "inherit"
2487 }, /* @__PURE__ */ react.createElement(_elementor_ui.Link, {
2488 color: "secondary",
2489 variant: "caption",
2490 component: "button",
2491 onClick: onClear
2492 }, (0, _wordpress_i18n.__)("Clear & try again", "elementor"))));
2493
2494 //#endregion
2495 //#region packages/packages/core/editor-global-classes/src/components/class-manager/global-classes-list.tsx
2496 var ROW_HEIGHT = 40;
2497 var OVERSCAN = 6;
2498 var GlobalClassesList = ({ disabled, scrollElement, onStopSyncRequest, onStartSyncRequest }) => {
2499 const { search: { debouncedValue: searchValue } } = useSearchAndFilters();
2500 const cssClasses = useOrderedClasses();
2501 const dispatch = (0, _elementor_store.__useDispatch)();
2502 const filters = useFilters();
2503 const [draggedItemId, setDraggedItemId] = (0, react.useState)(null);
2504 const [loading, setLoading] = (0, react.useState)({});
2505 const addLoadingClass = (classId) => setLoading((prev) => ({
2506 ...prev,
2507 [classId]: true
2508 }));
2509 const removeLoadingClass = (classId) => setLoading((prev) => {
2510 const { [classId]: _, ...rest } = prev;
2511 return rest;
2512 });
2513 const draggedItemLabel = cssClasses.find((cssClass) => cssClass.id === draggedItemId)?.label ?? "";
2514 const [classesOrder, reorderClasses] = useReorder(draggedItemId, setDraggedItemId, draggedItemLabel ?? "");
2515 const filteredCssClasses = useFilteredCssClasses();
2516 const virtualizer = useVirtualizer({
2517 count: filteredCssClasses.length,
2518 getScrollElement: () => scrollElement ?? null,
2519 estimateSize: () => ROW_HEIGHT,
2520 overscan: OVERSCAN,
2521 getItemKey: (index) => filteredCssClasses[index].id,
2522 rangeExtractor: (range) => {
2523 const indices = new Set(defaultRangeExtractor(range));
2524 if (draggedItemId) {
2525 const draggedItemIndex = filteredCssClasses.findIndex((cssClass) => cssClass.id === draggedItemId);
2526 if (draggedItemIndex >= 0) indices.add(draggedItemIndex);
2527 }
2528 return [...indices].sort((a, b) => a - b);
2529 }
2530 });
2531 (0, react.useEffect)(() => {
2532 const handler = (event) => {
2533 if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
2534 event.stopImmediatePropagation();
2535 event.preventDefault();
2536 if (event.shiftKey) {
2537 dispatch(slice.actions.redo());
2538 return;
2539 }
2540 dispatch(slice.actions.undo());
2541 }
2542 };
2543 window.addEventListener("keydown", handler, { capture: true });
2544 return () => window.removeEventListener("keydown", handler);
2545 }, [dispatch]);
2546 if (!cssClasses?.length) return /* @__PURE__ */ react.createElement(EmptyState, null);
2547 const notFoundType = getNotFoundType(searchValue, filters, filteredCssClasses);
2548 if (notFoundType) return /* @__PURE__ */ react.createElement(NotFound, { notFoundType });
2549 const isFiltersApplied = filters?.length || searchValue;
2550 const allowSorting = filteredCssClasses.length > 1 && !isFiltersApplied;
2551 return /* @__PURE__ */ react.createElement(DeleteConfirmationProvider, null, /* @__PURE__ */ react.createElement(_elementor_ui.List, { sx: {
2552 position: "relative",
2553 display: "block",
2554 height: virtualizer.getTotalSize(),
2555 padding: 0
2556 } }, /* @__PURE__ */ react.createElement(SortableProvider, {
2557 value: classesOrder,
2558 onChange: reorderClasses,
2559 onDragStart: (event) => setDraggedItemId(event.active.id),
2560 onDragEnd: () => setDraggedItemId(null),
2561 onDragCancel: () => setDraggedItemId(null),
2562 disableDragOverlay: !allowSorting
2563 }, virtualizer.getVirtualItems().map((virtualRow) => {
2564 const cssClass = filteredCssClasses[virtualRow.index];
2565 return /* @__PURE__ */ react.createElement(SortableItem, {
2566 key: virtualRow.key,
2567 id: cssClass.id,
2568 style: {
2569 position: "absolute",
2570 top: virtualRow.start,
2571 left: 0,
2572 width: "100%"
2573 }
2574 }, ({ isDragged, isDragPlaceholder, triggerProps, triggerStyle }) => /* @__PURE__ */ react.createElement(ClassItem, {
2575 id: cssClass.id,
2576 label: cssClass.label,
2577 renameClass: async (newLabel) => {
2578 addLoadingClass(cssClass.id);
2579 try {
2580 trackGlobalClasses({
2581 event: "classRenamed",
2582 classId: cssClass.id,
2583 oldValue: cssClass.label,
2584 newValue: newLabel,
2585 source: "class-manager"
2586 });
2587 await loadExistingClasses([cssClass.id]);
2588 dispatch(slice.actions.update({ style: {
2589 id: cssClass.id,
2590 label: newLabel
2591 } }));
2592 } finally {
2593 removeLoadingClass(cssClass.id);
2594 }
2595 },
2596 selected: isDragged,
2597 disabled: disabled || isDragPlaceholder || loading[cssClass.id],
2598 sortableTriggerProps: {
2599 ...triggerProps,
2600 style: triggerStyle
2601 },
2602 showSortIndicator: allowSorting,
2603 syncToV3: cssClass.sync_to_v3,
2604 onToggleSync: (id, newValue) => {
2605 if (!newValue && onStopSyncRequest) onStopSyncRequest(id);
2606 else if (newValue && onStartSyncRequest) onStartSyncRequest(id);
2607 else dispatch(slice.actions.update({ style: {
2608 id,
2609 sync_to_v3: newValue
2610 } }));
2611 }
2612 }));
2613 }))));
2614 };
2615 var EmptyState = () => /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
2616 alignItems: "center",
2617 gap: 1.5,
2618 pt: 10,
2619 px: .5,
2620 maxWidth: "260px",
2621 margin: "auto"
2622 }, /* @__PURE__ */ react.createElement(FlippedColorSwatchIcon, { fontSize: "large" }), /* @__PURE__ */ react.createElement(StyledHeader, {
2623 variant: "subtitle2",
2624 component: "h2",
2625 color: "text.secondary"
2626 }, (0, _wordpress_i18n.__)("There are no global classes yet.", "elementor")), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2627 align: "center",
2628 variant: "caption",
2629 color: "text.secondary"
2630 }, (0, _wordpress_i18n.__)("CSS classes created in the editor panel will appear here. Once they are available, you can arrange their hierarchy, rename them, or delete them as needed.", "elementor")));
2631 var StyledHeader = (0, _elementor_ui.styled)(_elementor_ui.Typography)(({ theme, variant }) => ({ "&.MuiTypography-root": { ...theme.typography[variant] } }));
2632 var useReorder = (draggedItemId, setDraggedItemId, draggedItemLabel) => {
2633 const dispatch = (0, _elementor_store.__useDispatch)();
2634 const order = useClassesOrder();
2635 const reorder = (newIds) => {
2636 dispatch(slice.actions.setOrder(newIds));
2637 if (draggedItemId) {
2638 trackGlobalClasses({
2639 event: "classManagerReorder",
2640 classId: draggedItemId,
2641 classTitle: draggedItemLabel
2642 });
2643 setDraggedItemId(null);
2644 }
2645 };
2646 return [order, reorder];
2647 };
2648 var useFilteredCssClasses = () => {
2649 const cssClasses = useOrderedClasses();
2650 const { search: { debouncedValue: searchValue } } = useSearchAndFilters();
2651 const filters = useFilters();
2652 const lowercaseLabels = (0, react.useMemo)(() => cssClasses.map((cssClass) => ({
2653 ...cssClass,
2654 lowerLabel: cssClass.label.toLowerCase()
2655 })), [cssClasses]);
2656 const filteredClasses = (0, react.useMemo)(() => {
2657 const normalizedSearch = searchValue.replace(/[^a-zA-Z0-9_-]/g, "").toLowerCase();
2658 if (normalizedSearch.length > 1) return lowercaseLabels.filter((cssClass) => cssClass.lowerLabel.includes(normalizedSearch));
2659 return cssClasses;
2660 }, [
2661 searchValue,
2662 cssClasses,
2663 lowercaseLabels
2664 ]);
2665 return (0, react.useMemo)(() => {
2666 if (filters && filters.length > 0) return filteredClasses.filter((cssClass) => filters.includes(cssClass.id));
2667 return filteredClasses;
2668 }, [filteredClasses, filters]);
2669 };
2670
2671 //#endregion
2672 //#region packages/packages/core/editor-global-classes/src/components/class-manager/panel-interactions.ts
2673 function blockPanelInteractions() {
2674 window.$e?.components?.get?.("panel")?.blockUserInteractions?.();
2675 }
2676 function unblockPanelInteractions() {
2677 window.$e?.components?.get?.("panel")?.unblockUserInteractions?.();
2678 }
2679
2680 //#endregion
2681 //#region packages/packages/core/editor-global-classes/src/components/class-manager/start-sync-to-v3-modal.tsx
2682 var IMAGE_URL = "https://assets.elementor.com/packages/v1/images/class-manager-sync-modal.png";
2683 var StartSyncToV3Modal = ({ externalOpen, classId, onExternalClose, onConfirm } = {}) => {
2684 const [shouldShowAgain, setShouldShowAgain] = (0, react.useState)(true);
2685 const hasTrackedExposure = (0, react.useRef)(false);
2686 (0, react.useEffect)(() => {
2687 if (externalOpen && classId && !hasTrackedExposure.current) {
2688 hasTrackedExposure.current = true;
2689 trackGlobalClasses({
2690 event: "classSyncToV3PopupShown",
2691 classId
2692 });
2693 }
2694 if (!externalOpen) hasTrackedExposure.current = false;
2695 }, [externalOpen, classId]);
2696 const handleClose = () => {
2697 if (classId) trackGlobalClasses({
2698 event: "classSyncToV3PopupClick",
2699 classId,
2700 action: "cancel"
2701 });
2702 onExternalClose?.();
2703 };
2704 const handleConfirm = () => {
2705 if (classId) trackGlobalClasses({
2706 event: "classSyncToV3PopupClick",
2707 classId,
2708 action: "sync"
2709 });
2710 onConfirm?.();
2711 onExternalClose?.();
2712 };
2713 return /* @__PURE__ */ react.createElement(_elementor_ui.Dialog, {
2714 open: !!externalOpen,
2715 onClose: handleClose,
2716 maxWidth: "sm",
2717 fullWidth: true
2718 }, /* @__PURE__ */ react.createElement(_elementor_ui.DialogContent, { sx: { p: 0 } }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2719 component: "img",
2720 src: IMAGE_URL,
2721 alt: "",
2722 sx: {
2723 width: "100%",
2724 display: "block"
2725 }
2726 }), /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: {
2727 px: 3,
2728 pt: 4,
2729 pb: 1
2730 } }, /* @__PURE__ */ react.createElement(_elementor_ui.Typography, { variant: "h6" }, (0, _wordpress_i18n.__)("Sync class to Global Fonts", "elementor")), /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2731 variant: "body2",
2732 color: "secondary",
2733 sx: {
2734 mb: 2,
2735 pt: 1
2736 }
2737 }, (0, _wordpress_i18n.__)("Only typography settings supported in Global Fonts will be applied, including: font family, responsive font sizes, weight, text transform, decoration, line height, letter spacing, and word spacing. Changes made in the class will automatically apply to Global Fonts.", "elementor")))), /* @__PURE__ */ react.createElement(_elementor_ui.DialogActions, { sx: {
2738 justifyContent: "space-between",
2739 px: 3,
2740 pb: 2
2741 } }, /* @__PURE__ */ react.createElement(_elementor_ui.FormControlLabel, {
2742 control: /* @__PURE__ */ react.createElement(_elementor_ui.Checkbox, {
2743 checked: !shouldShowAgain,
2744 onChange: (e) => setShouldShowAgain(!e.target.checked)
2745 }),
2746 label: /* @__PURE__ */ react.createElement(_elementor_ui.Typography, {
2747 variant: "body2",
2748 color: "secondary"
2749 }, (0, _wordpress_i18n.__)("Don't show again", "elementor"))
2750 }), /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: {
2751 display: "flex",
2752 gap: 1
2753 } }, /* @__PURE__ */ react.createElement(_elementor_ui.Button, {
2754 onClick: handleClose,
2755 color: "secondary",
2756 size: "small"
2757 }, (0, _wordpress_i18n.__)("Cancel", "elementor")), /* @__PURE__ */ react.createElement(_elementor_ui.Button, {
2758 onClick: handleConfirm,
2759 variant: "contained",
2760 size: "small"
2761 }, (0, _wordpress_i18n.__)("Sync to Global Fonts", "elementor")))));
2762 };
2763
2764 //#endregion
2765 //#region packages/packages/core/editor-global-classes/src/components/class-manager/class-manager-panel.tsx
2766 var STOP_SYNC_MESSAGE_KEY = "stop-sync-class";
2767 function ClassManagerPanelEmbedded({ onRequestClose, onExposeCloseAttempt, isActive }) {
2768 return /* @__PURE__ */ react.createElement(ClassManagerPanelContent, {
2769 onRequestClose,
2770 onExposeCloseAttempt,
2771 isActive
2772 });
2773 }
2774 function ClassManagerPanelContent({ onRequestClose, onExposeCloseAttempt, isActive = true }) {
2775 const isDirty = useDirtyState();
2776 const { open: openSaveChangesDialog, close: closeSaveChangesDialog, isOpen: isSaveChangesDialogOpen } = (0, _elementor_editor_ui.useDialog)();
2777 const [stopSyncConfirmation, setStopSyncConfirmation] = (0, react.useState)(null);
2778 const [startSyncConfirmation, setStartSyncConfirmation] = (0, react.useState)(null);
2779 const [isStopSyncSuppressed] = (0, _elementor_editor_current_user.useSuppressedMessage)(STOP_SYNC_MESSAGE_KEY);
2780 const [scrollElement, setScrollElement] = (0, react.useState)(null);
2781 const { mutateAsync: publish, isPending: isPublishing } = usePublish();
2782 const resetAndClosePanel = () => {
2783 (0, _elementor_store.__dispatch)(slice.actions.resetToInitialState({ context: "frontend" }));
2784 closeSaveChangesDialog();
2785 };
2786 const handleClosePanel = (0, react.useCallback)(() => {
2787 if (isDirty) {
2788 openSaveChangesDialog();
2789 return;
2790 }
2791 onRequestClose();
2792 }, [
2793 isDirty,
2794 openSaveChangesDialog,
2795 onRequestClose
2796 ]);
2797 (0, react.useEffect)(() => {
2798 if (!onExposeCloseAttempt) return;
2799 onExposeCloseAttempt(() => handleClosePanel());
2800 return () => onExposeCloseAttempt(null);
2801 }, [onExposeCloseAttempt, handleClosePanel]);
2802 (0, react.useEffect)(() => {
2803 blockPanelInteractions();
2804 return () => {
2805 unblockPanelInteractions();
2806 };
2807 }, []);
2808 const handleStopSync = (0, react.useCallback)(async (classId) => {
2809 await loadExistingClasses([classId]);
2810 (0, _elementor_store.__dispatch)(slice.actions.update({ style: {
2811 id: classId,
2812 sync_to_v3: false
2813 } }));
2814 trackGlobalClasses({
2815 event: "classSyncToV3",
2816 classId,
2817 action: "unsync"
2818 });
2819 setStopSyncConfirmation(null);
2820 }, []);
2821 const handleStartSync = (0, react.useCallback)(async (classId) => {
2822 await loadExistingClasses([classId]);
2823 (0, _elementor_store.__dispatch)(slice.actions.update({ style: {
2824 id: classId,
2825 sync_to_v3: true
2826 } }));
2827 trackGlobalClasses({
2828 event: "classSyncToV3",
2829 classId,
2830 action: "sync"
2831 });
2832 setStartSyncConfirmation(null);
2833 }, []);
2834 const handleStopSyncRequest = (0, react.useCallback)((classId) => {
2835 if (!isStopSyncSuppressed) setStopSyncConfirmation(classId);
2836 else handleStopSync(classId);
2837 }, [isStopSyncSuppressed, handleStopSync]);
2838 usePreventUnload();
2839 return /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(_elementor_ui.ErrorBoundary, { fallback: /* @__PURE__ */ react.createElement(ErrorBoundaryFallback, null) }, /* @__PURE__ */ react.createElement(SearchAndFilterProvider, null, /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
2840 direction: "column",
2841 sx: {
2842 height: "100%",
2843 width: "100%",
2844 flex: 1,
2845 minHeight: 0,
2846 overflow: "hidden"
2847 }
2848 }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2849 px: 2,
2850 pb: 1
2851 }, /* @__PURE__ */ react.createElement(_elementor_ui.Stack, {
2852 direction: "row",
2853 alignItems: "center",
2854 justifyContent: "space-between",
2855 gap: .5,
2856 sx: { pb: .5 }
2857 }, /* @__PURE__ */ react.createElement(_elementor_ui.Box, { sx: {
2858 flexGrow: 1,
2859 minWidth: 0
2860 } }, /* @__PURE__ */ react.createElement(ClassManagerSearch, null)), /* @__PURE__ */ react.createElement(CssClassFilter, null), /* @__PURE__ */ react.createElement(TotalCssClassCounter, null)), /* @__PURE__ */ react.createElement(ActiveFilters, null)), /* @__PURE__ */ react.createElement(_elementor_ui.Divider, null), /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2861 ref: setScrollElement,
2862 px: 2,
2863 sx: {
2864 flexGrow: 1,
2865 overflowY: "auto",
2866 minHeight: 0
2867 }
2868 }, /* @__PURE__ */ react.createElement(GlobalClassesList, {
2869 disabled: isPublishing,
2870 scrollElement,
2871 onStopSyncRequest: handleStopSyncRequest,
2872 onStartSyncRequest: (classId) => setStartSyncConfirmation(classId)
2873 })), /* @__PURE__ */ react.createElement(_elementor_editor_panels.PanelFooter, null, /* @__PURE__ */ react.createElement(_elementor_ui.Button, {
2874 fullWidth: true,
2875 size: "small",
2876 color: "global",
2877 variant: "contained",
2878 onClick: publish,
2879 disabled: !isDirty,
2880 loading: isPublishing
2881 }, (0, _wordpress_i18n.__)("Save changes", "elementor")))))), isActive && /* @__PURE__ */ react.createElement(ClassManagerIntroduction, null), startSyncConfirmation && /* @__PURE__ */ react.createElement(StartSyncToV3Modal, {
2882 externalOpen: true,
2883 classId: startSyncConfirmation,
2884 onExternalClose: () => setStartSyncConfirmation(null),
2885 onConfirm: () => handleStartSync(startSyncConfirmation)
2886 }), stopSyncConfirmation && /* @__PURE__ */ react.createElement(StopSyncConfirmationDialog, {
2887 open: true,
2888 onClose: () => setStopSyncConfirmation(null),
2889 onConfirm: () => handleStopSync(stopSyncConfirmation)
2890 }), isSaveChangesDialogOpen && /* @__PURE__ */ react.createElement(_elementor_editor_ui.SaveChangesDialog, null, /* @__PURE__ */ react.createElement(_elementor_ui.DialogHeader, {
2891 onClose: closeSaveChangesDialog,
2892 logo: false
2893 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.SaveChangesDialog.Title, null, (0, _wordpress_i18n.__)("You have unsaved changes", "elementor"))), /* @__PURE__ */ react.createElement(_elementor_editor_ui.SaveChangesDialog.Content, null, /* @__PURE__ */ react.createElement(_elementor_editor_ui.SaveChangesDialog.ContentText, null, (0, _wordpress_i18n.__)("You have unsaved changes in the Class Manager.", "elementor")), /* @__PURE__ */ react.createElement(_elementor_editor_ui.SaveChangesDialog.ContentText, null, (0, _wordpress_i18n.__)("To avoid losing your updates, save your changes before leaving.", "elementor"))), /* @__PURE__ */ react.createElement(_elementor_editor_ui.SaveChangesDialog.Actions, { actions: {
2894 discard: {
2895 label: (0, _wordpress_i18n.__)("Discard", "elementor"),
2896 action: () => {
2897 resetAndClosePanel();
2898 }
2899 },
2900 confirm: {
2901 label: (0, _wordpress_i18n.__)("Save & Continue", "elementor"),
2902 action: async () => {
2903 await publish();
2904 closeSaveChangesDialog();
2905 onRequestClose();
2906 }
2907 }
2908 } })));
2909 }
2910 var ErrorBoundaryFallback = () => /* @__PURE__ */ react.createElement(_elementor_ui.Box, {
2911 role: "alert",
2912 sx: {
2913 minHeight: "100%",
2914 p: 2
2915 }
2916 }, /* @__PURE__ */ react.createElement(_elementor_ui.Alert, {
2917 severity: "error",
2918 sx: {
2919 mb: 2,
2920 maxWidth: 400,
2921 textAlign: "center"
2922 }
2923 }, /* @__PURE__ */ react.createElement("strong", null, (0, _wordpress_i18n.__)("Something went wrong", "elementor"))));
2924 var usePreventUnload = () => {
2925 const isDirty = useDirtyState();
2926 (0, react.useEffect)(() => {
2927 const handleBeforeUnload = (event) => {
2928 if (isDirty) event.preventDefault();
2929 };
2930 window.addEventListener("beforeunload", handleBeforeUnload);
2931 return () => {
2932 window.removeEventListener("beforeunload", handleBeforeUnload);
2933 };
2934 }, [isDirty]);
2935 };
2936 var usePublish = () => {
2937 return (0, _elementor_query.useMutation)({
2938 mutationFn: () => saveGlobalClasses({ context: "frontend" }),
2939 onSuccess: async () => {
2940 (0, _elementor_editor_documents.setDocumentModifiedStatus)(false);
2941 if (hasDeletedItems()) await onDelete();
2942 }
2943 });
2944 };
2945 var TotalCssClassCounter = () => {
2946 const filters = useFilters();
2947 const cssClasses = useClassesOrder();
2948 return /* @__PURE__ */ react.createElement(_elementor_ui.Chip, {
2949 size: "small",
2950 label: filters ? `${filters.length} / ${cssClasses?.length}` : cssClasses?.length
2951 });
2952 };
2953 var StopSyncConfirmationDialog = ({ open, onClose, onConfirm }) => {
2954 const [, suppressStopSyncMessage] = (0, _elementor_editor_current_user.useSuppressedMessage)(STOP_SYNC_MESSAGE_KEY);
2955 return /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog, {
2956 open,
2957 onClose
2958 }, /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.Title, {
2959 icon: FlippedColorSwatchIcon,
2960 iconColor: "primary"
2961 }, (0, _wordpress_i18n.__)("Un-sync typography class", "elementor")), /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.Content, null, /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.ContentText, null, (0, _wordpress_i18n.__)("You're about to stop syncing a typography class to Global Fonts.", "elementor")), /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.ContentText, { sx: { mt: 1 } }, (0, _wordpress_i18n.__)("Note that if it's being used anywhere, the affected elements will inherit the default typography.", "elementor"))), /* @__PURE__ */ react.createElement(_elementor_editor_ui.ConfirmationDialog.Actions, {
2962 onClose,
2963 onConfirm,
2964 cancelLabel: (0, _wordpress_i18n.__)("Cancel", "elementor"),
2965 confirmLabel: (0, _wordpress_i18n.__)("Got it", "elementor"),
2966 color: "primary",
2967 onSuppressMessage: suppressStopSyncMessage,
2968 suppressLabel: (0, _wordpress_i18n.__)("Don't show again", "elementor")
2969 }));
2970 };
2971
2972 //#endregion
2973 //#region packages/packages/core/editor-global-classes/src/capabilities.ts
2974 var UPDATE_CLASS_CAPABILITY_KEY = "elementor_global_classes_update_class";
2975 var getCapabilities = () => {
2976 return {
2977 update: UPDATE_CLASS_CAPABILITY_KEY,
2978 create: UPDATE_CLASS_CAPABILITY_KEY,
2979 delete: UPDATE_CLASS_CAPABILITY_KEY,
2980 updateProps: UPDATE_CLASS_CAPABILITY_KEY
2981 };
2982 };
2983
2984 //#endregion
2985 //#region packages/packages/core/editor-global-classes/src/global-classes-styles-provider.ts
2986 var MAX_CLASSES = 1e3;
2987 var GLOBAL_CLASSES_PROVIDER_KEY = "global-classes";
2988 var PREGENERATED_LINK_PATTERN = /^global-([0-9]+-)?(preview|frontend)-[a-zA-Z_-]+-css$/;
2989 var globalClassesStylesProvider = (0, _elementor_editor_styles_repository.createStylesProvider)({
2990 key: GLOBAL_CLASSES_PROVIDER_KEY,
2991 priority: 30,
2992 limit: MAX_CLASSES,
2993 isPregeneratedLink: ({ id }) => PREGENERATED_LINK_PATTERN.test(id),
2994 labels: {
2995 singular: (0, _wordpress_i18n.__)("class", "elementor"),
2996 plural: (0, _wordpress_i18n.__)("classes", "elementor")
2997 },
2998 subscribe: (cb) => subscribeWithStates(cb),
2999 capabilities: getCapabilities(),
3000 actions: {
3001 all: () => selectOrderedClasses((0, _elementor_store.__getState)()),
3002 get: (id) => {
3003 const state = (0, _elementor_store.__getState)();
3004 const isFetched = selectIsClassFetched(state, id);
3005 const style = selectClass(state, id);
3006 if (isFetched || style) return style;
3007 loadExistingClasses([id]);
3008 return placeholderDefinition(id, selectClassLabels(state)[id] ?? id);
3009 },
3010 resolveCssName: (id) => {
3011 const state = (0, _elementor_store.__getState)();
3012 const loaded = selectClass(state, id);
3013 if (loaded) return loaded.label;
3014 return selectClassLabels(state)[id] ?? id;
3015 },
3016 create: (label, variants = [], id) => {
3017 const existingClasses = Object.entries(selectClassLabels((0, _elementor_store.__getState)()));
3018 if (existingClasses.map(([, classLabel]) => classLabel).includes(label)) throw new GlobalClassLabelAlreadyExistsError({ context: { label } });
3019 const existingIds = existingClasses.map(([existingId]) => existingId);
3020 if (!id) id = (0, _elementor_editor_styles.generateId)("g-", existingIds);
3021 (0, _elementor_store.__dispatch)(slice.actions.add({
3022 id,
3023 type: "class",
3024 label,
3025 variants
3026 }));
3027 return id;
3028 },
3029 update: (payload) => {
3030 (0, _elementor_store.__dispatch)(slice.actions.update({ style: payload }));
3031 },
3032 delete: (id) => {
3033 (0, _elementor_store.__dispatch)(slice.actions.delete(id));
3034 },
3035 updateProps: (args) => {
3036 (0, _elementor_store.__dispatch)(slice.actions.updateProps({
3037 id: args.id,
3038 meta: args.meta,
3039 props: args.props,
3040 mode: args.mode
3041 }));
3042 },
3043 updateCustomCss: (args) => {
3044 (0, _elementor_store.__dispatch)(slice.actions.updateProps({
3045 id: args.id,
3046 meta: args.meta,
3047 custom_css: args.custom_css,
3048 props: {}
3049 }));
3050 },
3051 tracking: (data) => {
3052 trackGlobalClasses(data).catch((error) => {
3053 throw new GlobalClassTrackingError({ cause: error });
3054 });
3055 }
3056 }
3057 });
3058 var subscribeWithStates = (cb) => {
3059 let previousState = selectData((0, _elementor_store.__getState)());
3060 return (0, _elementor_store.__subscribeWithSelector)((state) => selectData(state), (currentState) => {
3061 cb(previousState.items, currentState.items);
3062 previousState = currentState;
3063 });
3064 };
3065
3066 //#endregion
3067 //#region packages/packages/core/editor-global-classes/src/mcp-integration/classes-resource.ts
3068 var GLOBAL_CLASSES_URI = "elementor://global-classes";
3069 var STORAGE_KEY = "elementor-global-classes";
3070 var updateLocalStorageCache = () => {
3071 const classes = selectOrderedClasses((0, _elementor_store.__getState)());
3072 localStorage.setItem(STORAGE_KEY, JSON.stringify(classes));
3073 };
3074 var initClassesResource = (classesMcpEntry, canvasMcpEntry) => {
3075 [canvasMcpEntry, classesMcpEntry].forEach((entry) => {
3076 const { sendResourceUpdated, resource, waitForReady } = entry;
3077 resource("global-classes", GLOBAL_CLASSES_URI, { description: "Global classes list." }, async () => {
3078 return { contents: [{
3079 uri: GLOBAL_CLASSES_URI,
3080 text: localStorage[STORAGE_KEY] ?? "[]"
3081 }] };
3082 });
3083 waitForReady().then(() => {
3084 updateLocalStorageCache();
3085 globalClassesStylesProvider.subscribe(() => {
3086 updateLocalStorageCache();
3087 sendResourceUpdated({ uri: GLOBAL_CLASSES_URI });
3088 });
3089 });
3090 });
3091 };
3092
3093 //#endregion
3094 //#region packages/packages/core/editor-global-classes/src/hooks/use-prefetch-css-class-usage.ts
3095 function usePrefetchCssClassUsage() {
3096 const queryClient = (0, _elementor_query.useQueryClient)();
3097 const prefetchClassesUsage = () => queryClient.prefetchQuery({
3098 queryKey: [QUERY_KEY],
3099 queryFn: fetchCssClassUsage
3100 });
3101 return { prefetchClassesUsage };
3102 }
3103 var PrefetchCssClassUsage = () => {
3104 const { prefetchClassesUsage } = usePrefetchCssClassUsage();
3105 prefetchClassesUsage();
3106 return null;
3107 };
3108
3109 //#endregion
3110 //#region packages/packages/core/editor-global-classes/src/components/class-manager/class-manager-button.tsx
3111 var EVENT_TOGGLE_DESIGN_SYSTEM = "elementor/toggle-design-system";
3112 var ClassManagerButton = () => {
3113 const { prefetchClassesUsage } = usePrefetchCssClassUsage();
3114 const { userCan } = (0, _elementor_editor_styles_repository.useUserStylesCapability)();
3115 if (!userCan(globalClassesStylesProvider.getKey()).update) return null;
3116 const handleOpenPanel = () => {
3117 window.dispatchEvent(new CustomEvent(EVENT_TOGGLE_DESIGN_SYSTEM, { detail: { tab: "classes" } }));
3118 trackGlobalClasses({
3119 event: "classManagerOpened",
3120 source: "style-panel"
3121 });
3122 prefetchClassesUsage();
3123 };
3124 return /* @__PURE__ */ react.createElement(_elementor_ui.Tooltip, {
3125 title: (0, _wordpress_i18n.__)("Class Manager", "elementor"),
3126 placement: "top"
3127 }, /* @__PURE__ */ react.createElement(_elementor_ui.IconButton, {
3128 size: "tiny",
3129 onClick: handleOpenPanel,
3130 sx: { marginInlineEnd: -.75 }
3131 }, /* @__PURE__ */ react.createElement(FlippedColorSwatchIcon, { fontSize: "tiny" })));
3132 };
3133
3134 //#endregion
3135 //#region packages/packages/core/editor-global-classes/src/components/convert-local-class-to-global-class.tsx
3136 var ConvertLocalClassToGlobalClass = (props) => {
3137 const localStyleData = props.styleDef;
3138 const handleConversion = () => {
3139 const newClassName = createClassName(`converted-class-`);
3140 if (!localStyleData) throw new Error("Style definition is required for converting local class to global class.");
3141 const newId = globalClassesStylesProvider.actions.create?.(newClassName, localStyleData.variants);
3142 if (newId) {
3143 props.successCallback(newId);
3144 trackGlobalClasses({
3145 classId: newId,
3146 event: "classCreated",
3147 source: "converted",
3148 classTitle: newClassName
3149 });
3150 }
3151 };
3152 return /* @__PURE__ */ react.createElement(react.Fragment, null, /* @__PURE__ */ react.createElement(_elementor_editor_ui.MenuListItem, {
3153 disabled: !props.canConvert,
3154 onClick: handleConversion,
3155 dense: true,
3156 sx: { "&.Mui-focusVisible": {
3157 border: "none",
3158 boxShadow: "none !important",
3159 backgroundColor: "transparent"
3160 } }
3161 }, (0, _wordpress_i18n.__)("Convert to global class", "elementor")), /* @__PURE__ */ react.createElement(_elementor_ui.Divider, null));
3162 };
3163 function createClassName(prefix) {
3164 let i = 1;
3165 let newClassName = `${prefix}${i}`;
3166 while (!(0, _elementor_editor_styles_repository.validateStyleLabel)(newClassName, "create").isValid) newClassName = `${prefix}${++i}`;
3167 return newClassName;
3168 }
3169
3170 //#endregion
3171 //#region packages/packages/core/editor-global-classes/src/components/global-styles-import-listener.tsx
3172 function GlobalStylesImportListener() {
3173 const dispatch = (0, _elementor_store.__useDispatch)();
3174 (0, react.useEffect)(() => {
3175 const handleGlobalStylesImported = async (event) => {
3176 const globalClasses = event.detail?.global_classes;
3177 if (!globalClasses?.added_items_order || !globalClasses?.added_items || globalClasses?.added_items_order?.length === 0) {
3178 loadCurrentDocumentClasses();
3179 return;
3180 }
3181 dispatch(slice.actions.updateAfterTemplateImport({
3182 addedItems: globalClasses.added_items,
3183 addedIdsOrder: globalClasses.added_items_order,
3184 addedClassLabels: createLabelsForClasses(Object.values(globalClasses.added_items))
3185 }));
3186 };
3187 window.addEventListener(_elementor_editor_canvas.GLOBAL_STYLES_IMPORTED_EVENT, handleGlobalStylesImported);
3188 return () => {
3189 window.removeEventListener(_elementor_editor_canvas.GLOBAL_STYLES_IMPORTED_EVENT, handleGlobalStylesImported);
3190 };
3191 }, [dispatch]);
3192 return null;
3193 }
3194
3195 //#endregion
3196 //#region packages/packages/core/editor-global-classes/src/components/populate-store.tsx
3197 function PopulateStore() {
3198 (0, react.useEffect)(() => {
3199 loadCurrentDocumentClasses();
3200 (0, _elementor_editor_v1_adapters.registerDataHook)("after", "editor/documents/attach-preview", async () => {
3201 await loadCurrentDocumentClasses();
3202 });
3203 }, []);
3204 return null;
3205 }
3206
3207 //#endregion
3208 //#region packages/packages/core/editor-global-classes/src/mcp-integration/manage-classes-tool.ts
3209 var MCP_PROXY_URL = "elementor/v1/mcp-proxy";
3210 var TOOL_NAME = "manage-classes";
3211 var initManageClassesTool = (reg) => {
3212 const { addTool } = reg;
3213 addTool({
3214 name: TOOL_NAME,
3215 description: "Manage V4 global CSS classes on the active kit. Bulk create, update, or delete using raw CSS declarations (up to 50 operations). Duplicate labels are auto-renamed with a DUP_ prefix.",
3216 schema: {
3217 action: _elementor_schema.z.enum([
3218 "create",
3219 "update",
3220 "delete"
3221 ]),
3222 id: _elementor_schema.z.string().optional().describe("Class id — required for update/delete. Get from the global-classes resource."),
3223 label: _elementor_schema.z.string().optional().describe("Class label (lowercase, dash-separated) — required for create/update."),
3224 css: _elementor_schema.z.string().optional().describe("Plain CSS string. Supports &:hover/&:focus/&:active nesting and @media(--breakpoint) blocks. font-family must be a single Google Font name (no fallback stacks). In patch mode: \"prop: null\" removes that prop; \"all: null\" wipes the variant."),
3225 mode: _elementor_schema.z.enum(["patch", "replace"]).optional().describe("Merge strategy for update — patch (default): merge incoming props with existing; replace: discard all existing variants for the affected breakpoints.")
3226 },
3227 outputSchema: {
3228 status: _elementor_schema.z.enum(["ok"]).describe("Operation status"),
3229 id: _elementor_schema.z.string().optional().describe("ID of the affected class — use for subsequent update/delete calls."),
3230 label: _elementor_schema.z.string().optional().describe("Final label of the class after any auto-rename.")
3231 },
3232 requiredResources: [{
3233 uri: GLOBAL_CLASSES_URI,
3234 description: "Current global classes — check before creating to avoid duplicates"
3235 }],
3236 isDestructive: true,
3237 handler: async (params) => {
3238 const { data } = await (0, _elementor_http_client.httpService)().post(MCP_PROXY_URL, {
3239 tool: TOOL_NAME,
3240 input: { operations: [params] }
3241 });
3242 const result = data.data.results?.[0];
3243 const { create, update, delete: del } = globalClassesStylesProvider.actions;
3244 switch (params.action) {
3245 case "create":
3246 if (result && create) create(result.label, result.variants, result.id);
3247 break;
3248 case "update":
3249 if (result && update) update(result);
3250 break;
3251 case "delete":
3252 if (params.id && del) del(params.id);
3253 break;
3254 }
3255 (0, _elementor_store.__dispatch)(slice.actions.reset({ context: "frontend" }));
3256 window.dispatchEvent(new CustomEvent("classes:updated", { detail: { context: "frontend" } }));
3257 return {
3258 status: "ok",
3259 id: result?.id,
3260 label: result?.label
3261 };
3262 }
3263 });
3264 };
3265
3266 //#endregion
3267 //#region packages/packages/core/editor-global-classes/src/mcp-integration/apply-global-class-guide-prompt.ts
3268 var APPLY_GLOBAL_CLASS_GUIDE_URI = "elementor://global-classes/tools/apply-global-class-guide";
3269 var generateApplyGlobalClassGuidePrompt = () => {
3270 const prompt = (0, _elementor_editor_mcp.toolPrompts)("apply-global-class");
3271 prompt.description("Apply a global class to an element, enabling consistent styling through your design system.");
3272 prompt.instruction(`## When to use this tool:
3273 **ALWAYS use this IMMEDIATELY AFTER creating or updating elements** to apply the global classes you created beforehand:
3274 - After adding elements to the canvas, apply semantic classes to them
3275 - When applying consistent typography styles (heading-primary, text-body, etc.)
3276 - When applying theme colors or brand styles (bg-brand, button-cta, etc.)
3277 - When ensuring spacing consistency (spacing-section-large, etc.)
3278
3279 **DO NOT use this tool** for:
3280 - Elements that don't share styles with other elements (use inline styles instead)
3281 - Layout-specific properties (those should remain inline in stylesConfig)`);
3282 prompt.instruction(`## Prerequisites:
3283 - **REQUIRED**: Get the list of available global classes from 'elementor://global-classes' resource
3284 - **REQUIRED**: Get element IDs from the page structure or selected element resources
3285 - Ensure you have the most up-to-date list of classes applied to the element to avoid duplicates
3286 - Make sure you have the correct class ID that you want to apply`);
3287 prompt.instruction(`## Best Practices:
3288 1. Apply multiple classes to a single element if needed (typography + color + spacing)
3289 2. After applying, the tool will remind you to remove duplicate inline styles from elementConfig
3290 3. Classes should describe purpose, not implementation (e.g., "heading-primary" not "big-red-text")`);
3291 return prompt.prompt();
3292 };
3293
3294 //#endregion
3295 //#region packages/packages/core/editor-global-classes/src/mcp-integration/mcp-apply-unapply-global-classes.ts
3296 function initMcpApplyUnapplyGlobalClasses(server) {
3297 const { addTool, resource } = server;
3298 const applyGlobalClassGuideText = generateApplyGlobalClassGuidePrompt();
3299 resource("apply-global-class-guide", APPLY_GLOBAL_CLASS_GUIDE_URI, {
3300 description: "Workflow, prerequisites, and best practices for apply-global-class",
3301 mimeType: "text/plain",
3302 title: "Apply global class tool guide"
3303 }, async (uri) => ({ contents: [{
3304 mimeType: "text/plain",
3305 text: applyGlobalClassGuideText,
3306 uri: uri.href
3307 }] }));
3308 addTool({
3309 schema: {
3310 classId: _elementor_schema.z.string().describe("The ID of the class to apply"),
3311 elementId: _elementor_schema.z.string().describe("The ID of the element to which the class will be applied")
3312 },
3313 outputSchema: {
3314 result: _elementor_schema.z.string().describe("Result message indicating the success of the apply operation"),
3315 llm_instructions: _elementor_schema.z.string().describe("Instructions what to do next, Important to follow these instructions!")
3316 },
3317 name: "apply-global-class",
3318 description: `Apply a global class to an element for shared design-system styling. Read the full guide at [${APPLY_GLOBAL_CLASS_GUIDE_URI}].`,
3319 requiredResources: [{
3320 description: "Apply global class tool guide",
3321 uri: APPLY_GLOBAL_CLASS_GUIDE_URI
3322 }, {
3323 description: "Global classes list",
3324 uri: GLOBAL_CLASSES_URI
3325 }],
3326 handler: async (params) => {
3327 const { classId, elementId } = params;
3328 (0, _elementor_editor_editing_panel.doApplyClasses)(elementId, [...(0, _elementor_editor_editing_panel.doGetAppliedClasses)(elementId), classId]);
3329 globalClassesStylesProvider.actions.tracking?.({
3330 event: "classApplied",
3331 executedBy: "mcp_tool",
3332 classId
3333 });
3334 return {
3335 llm_instructions: "Please check the element configuration, find inline styles duplicated by the applied global class, and remove them",
3336 result: `Class ${classId} applied to element ${elementId} successfully.`
3337 };
3338 }
3339 });
3340 addTool({
3341 name: "unapply-global-class",
3342 schema: {
3343 classId: _elementor_schema.z.string().describe("The ID of the class to unapply"),
3344 elementId: _elementor_schema.z.string().describe("The ID of the element from which the class will be unapplied")
3345 },
3346 outputSchema: { result: _elementor_schema.z.string().describe("Result message indicating the success of the unapply operation") },
3347 description: `Unapply a global class from an element by class ID. Resolve class names to IDs via [${GLOBAL_CLASSES_URI}].`,
3348 requiredResources: [{
3349 description: "Global classes list",
3350 uri: GLOBAL_CLASSES_URI
3351 }],
3352 handler: async (params) => {
3353 const { classId, elementId } = params;
3354 if (!(0, _elementor_editor_editing_panel.doUnapplyClass)(elementId, classId)) throw new Error(`Class ${classId} is not applied to element ${elementId}, cannot unapply it.`);
3355 return { result: `Class ${classId} unapplied from element ${elementId} successfully.` };
3356 }
3357 });
3358 }
3359
3360 //#endregion
3361 //#region packages/packages/core/editor-global-classes/src/mcp-integration/mcp-get-global-class-usages.ts
3362 function initMcpApplyGetGlobalClassUsages(reg) {
3363 const { addTool } = reg;
3364 const globalClassesUsageSchema = { usages: _elementor_schema.z.array(_elementor_schema.z.object({
3365 classId: _elementor_schema.z.string().describe("The ID of the class, not visible to the user. To retrieve the name of the class, use the \"list-global-classes\" tool"),
3366 usages: _elementor_schema.z.array(_elementor_schema.z.object({
3367 pageId: _elementor_schema.z.string().describe("The ID of the page where the class is used"),
3368 title: _elementor_schema.z.string().describe("The title of the page where the class is used"),
3369 total: _elementor_schema.z.number().describe("The number of times the class is used on this page"),
3370 elements: _elementor_schema.z.array(_elementor_schema.z.string()).describe("List of element IDs using this class on the page")
3371 }))
3372 })) };
3373 addTool({
3374 name: "get-global-class-usages",
3375 description: `Retrieve usages of global classes across all Elementor pages. Heavy operation \u2014 scans every page in the site.
3376
3377 ## When to use:
3378 - Before deleting or radically changing a class \u2014 to understand cross-page side effects and decide whether to consult the user.
3379 - To identify unused global classes for cleanup.
3380
3381 ## When NOT to use:
3382 - To list global classes themselves \u2014 use the global-classes resource instead (this tool returns usages, not the class list).`,
3383 requiredResources: [{
3384 description: "Global classes list",
3385 uri: GLOBAL_CLASSES_URI
3386 }],
3387 outputSchema: globalClassesUsageSchema,
3388 handler: async () => {
3389 const data = await fetchCssClassUsage();
3390 const result = { usages: [] };
3391 Object.entries(data).forEach(([classId, usageDetails]) => {
3392 const newEntry = {
3393 classId,
3394 usages: []
3395 };
3396 if (typeof usageDetails !== "number") {
3397 const { content } = usageDetails;
3398 content.forEach((detail) => {
3399 newEntry.usages.push({
3400 pageId: String(detail.pageId),
3401 title: detail.title,
3402 total: detail.total,
3403 elements: detail.elements
3404 });
3405 });
3406 result.usages.push(newEntry);
3407 }
3408 });
3409 return result;
3410 }
3411 });
3412 }
3413
3414 //#endregion
3415 //#region packages/packages/core/editor-global-classes/src/mcp-integration/index.ts
3416 var initMcpIntegration = (reg, canvasMcpEntry) => {
3417 initMcpApplyUnapplyGlobalClasses(reg);
3418 initMcpApplyGetGlobalClassUsages(reg);
3419 initManageClassesTool(reg);
3420 initClassesResource(reg, canvasMcpEntry);
3421 };
3422
3423 //#endregion
3424 //#region packages/packages/core/editor-global-classes/src/sync-with-document-save.ts
3425 var pendingSave = null;
3426 function syncWithDocumentSave(panelActions) {
3427 const unsubscribe = syncDirtyState();
3428 bindSaveAction(panelActions);
3429 bindBeforeSaveTemplateAction();
3430 return unsubscribe;
3431 }
3432 function syncDirtyState() {
3433 return (0, _elementor_store.__subscribeWithSelector)(selectIsDirty, () => {
3434 if (!isDirty()) return;
3435 (0, _elementor_editor_documents.setDocumentModifiedStatus)(true);
3436 });
3437 }
3438 function triggerSave(panelActions, context = "preview") {
3439 if (!(0, _elementor_editor_current_user.getCurrentUser)()?.capabilities.includes("elementor_global_classes_update_class")) return null;
3440 if (pendingSave) return pendingSave;
3441 const promise = saveGlobalClasses({
3442 context,
3443 onApprove: panelActions?.open
3444 });
3445 pendingSave = promise;
3446 promise.finally(() => {
3447 pendingSave = null;
3448 });
3449 return promise;
3450 }
3451 function bindSaveAction(panelActions) {
3452 (0, _elementor_editor_v1_adapters.registerDataHook)("dependency", "document/save/save", (args) => {
3453 triggerSave(panelActions, args.status === "publish" ? "frontend" : "preview");
3454 return true;
3455 });
3456 }
3457 function bindBeforeSaveTemplateAction() {
3458 window.addEventListener("elementor/global-styles/before-save", (event) => {
3459 if (!pendingSave && isDirty()) triggerSave();
3460 if (pendingSave) event.detail.promises.push(pendingSave);
3461 });
3462 }
3463 function isDirty() {
3464 return selectIsDirty((0, _elementor_store.__getState)());
3465 }
3466
3467 //#endregion
3468 //#region packages/packages/core/editor-global-classes/src/sync-with-document.tsx
3469 function SyncWithDocumentSave() {
3470 (0, react.useEffect)(() => {
3471 return (0, _elementor_editor_v1_adapters.__privateListenTo)((0, _elementor_editor_v1_adapters.v1ReadyEvent)(), () => {
3472 const open = () => {
3473 window.dispatchEvent(new CustomEvent("elementor/open-global-classes-manager"));
3474 };
3475 syncWithDocumentSave({ open });
3476 });
3477 }, []);
3478 return null;
3479 }
3480
3481 //#endregion
3482 //#region packages/packages/core/editor-global-classes/src/init.ts
3483 function init() {
3484 (0, _elementor_store.__registerSlice)(slice);
3485 _elementor_editor_embedded_documents_manager.embeddedDocumentsManager.onDocumentLoad((documentId) => {
3486 addDocumentClasses(documentId);
3487 });
3488 _elementor_editor_styles_repository.stylesRepository.register(globalClassesStylesProvider);
3489 (0, _elementor_editor.injectIntoLogic)({
3490 id: "global-classes-populate-store",
3491 component: PopulateStore
3492 });
3493 (0, _elementor_editor.injectIntoLogic)({
3494 id: "global-classes-sync-with-document",
3495 component: SyncWithDocumentSave
3496 });
3497 (0, _elementor_editor.injectIntoLogic)({
3498 id: "global-classes-import-listener",
3499 component: GlobalStylesImportListener
3500 });
3501 (0, _elementor_editor.injectIntoLogic)({
3502 id: "global-classes-prefetch-css-class-usage",
3503 component: PrefetchCssClassUsage
3504 });
3505 (0, _elementor_editor_editing_panel.injectIntoCssClassConvert)({
3506 id: "global-classes-convert-from-local-class",
3507 component: ConvertLocalClassToGlobalClass
3508 });
3509 (0, _elementor_editor_editing_panel.injectIntoClassSelectorActions)({
3510 id: "global-classes-manager-button",
3511 component: ClassManagerButton
3512 });
3513 (0, _elementor_editor_editing_panel.registerStyleProviderToColors)(GLOBAL_CLASSES_PROVIDER_KEY, {
3514 name: "global",
3515 getThemeColor: (theme) => theme.palette.global.dark
3516 });
3517 initMcpIntegration((0, _elementor_editor_mcp.getMCPByDomain)("classes", {
3518 instructions: "MCP server for management of Elementor global classes",
3519 docs: `Everything related to V4 ( Atomic ) global classes.
3520 # Global classes
3521 - Create/update/delete global classes
3522 - Get list of global classes
3523 - Get details of a global class
3524 `
3525 }), (0, _elementor_editor_mcp.getMCPByDomain)("canvas"));
3526 }
3527
3528 //#endregion
3529 //#region packages/packages/core/editor-global-classes/src/index.ts
3530 var src_exports = /* @__PURE__ */ __exportAll({
3531 ClassManagerPanelEmbedded: () => ClassManagerPanelEmbedded,
3532 GLOBAL_CLASSES_URI: () => GLOBAL_CLASSES_URI,
3533 addDocumentClasses: () => addDocumentClasses,
3534 createLabelsForClasses: () => createLabelsForClasses,
3535 init: () => init,
3536 loadExistingClasses: () => loadExistingClasses,
3537 trackGlobalClasses: () => trackGlobalClasses
3538 });
3539
3540 //#endregion
3541 //#region \0elementor-package-library-entry
3542 (window.elementorV2 = window.elementorV2 || {}).editorGlobalClasses = src_exports;
3543
3544 //#endregion
3545 })(React, elementorV2.editorCurrentUser, elementorV2.editorDocuments, elementorV2.editorPanels, elementorV2.editorUi, elementorV2.query, elementorV2.store, elementorV2.ui, wp.i18n, elementorV2.editorStyles, elementorV2.utils, elementorV2.httpClient, elementorV2.icons, elementorV2.events, ReactDOM, elementorV2.editorStylesRepository, elementorV2.editor, elementorV2.editorEditingPanel, elementorV2.editorEmbeddedDocumentsManager, elementorV2.editorMcp, elementorV2.editorCanvas, elementorV2.editorV1Adapters, elementorV2.schema);
3546 window.elementorV2.editorGlobalClasses?.init?.();
3547 //# sourceMappingURL=editor-global-classes.js.map