@elementor/editor-global-classes 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,16 +2,18 @@
2
2
  import { injectIntoLogic } from "@elementor/editor";
3
3
  import { injectIntoClassSelectorActions } from "@elementor/editor-editing-panel";
4
4
  import { __registerPanel as registerPanel } from "@elementor/editor-panels";
5
- import { stylesRepository } from "@elementor/editor-styles-repository";
5
+ import { stylesRepository as stylesRepository2 } from "@elementor/editor-styles-repository";
6
+ import { __privateListenTo as listenTo, v1ReadyEvent } from "@elementor/editor-v1-adapters";
6
7
  import { __registerSlice as registerSlice } from "@elementor/store";
7
8
 
8
9
  // src/components/class-manager/class-manager-button.tsx
9
- import * as React2 from "react";
10
- import { AppsIcon } from "@elementor/icons";
11
- import { IconButton as IconButton2 } from "@elementor/ui";
10
+ import * as React5 from "react";
11
+ import { ColorSwatchIcon as ColorSwatchIcon2 } from "@elementor/icons";
12
+ import { IconButton as IconButton3, Tooltip as Tooltip2 } from "@elementor/ui";
13
+ import { __ as __5 } from "@wordpress/i18n";
12
14
 
13
15
  // src/components/class-manager/class-manager-panel.tsx
14
- import * as React from "react";
16
+ import * as React4 from "react";
15
17
  import {
16
18
  __createPanel as createPanel,
17
19
  Panel,
@@ -19,55 +21,68 @@ import {
19
21
  PanelHeader,
20
22
  PanelHeaderTitle
21
23
  } from "@elementor/editor-panels";
22
- import { XIcon } from "@elementor/icons";
23
- import { Alert, Box, ErrorBoundary, IconButton, Stack } from "@elementor/ui";
24
- import { __ } from "@wordpress/i18n";
25
- var { panel, usePanelActions, usePanelStatus } = createPanel({
26
- id: "class-manager-panel",
27
- component: ClassManagerPanel
28
- });
29
- function ClassManagerPanel() {
30
- return /* @__PURE__ */ React.createElement(ErrorBoundary, { fallback: /* @__PURE__ */ React.createElement(ErrorBoundaryFallback, null) }, /* @__PURE__ */ React.createElement(Panel, null, /* @__PURE__ */ React.createElement(PanelHeader, null, /* @__PURE__ */ React.createElement(Stack, { p: 1, width: "100%", direction: "row", justifyContent: "space-between", alignItems: "center" }, /* @__PURE__ */ React.createElement(PanelHeaderTitle, null, __("CSS Class manager")), /* @__PURE__ */ React.createElement(CloseButton, null))), /* @__PURE__ */ React.createElement(PanelBody, null)));
31
- }
32
- var CloseButton = () => {
33
- const { close } = usePanelActions();
34
- return /* @__PURE__ */ React.createElement(IconButton, { size: "small", color: "secondary", onClick: close }, /* @__PURE__ */ React.createElement(XIcon, { fontSize: "small" }));
35
- };
36
- var ErrorBoundaryFallback = () => /* @__PURE__ */ React.createElement(Box, { role: "alert", sx: { minHeight: "100%", p: 2 } }, /* @__PURE__ */ React.createElement(Alert, { severity: "error", sx: { mb: 2, maxWidth: 400, textAlign: "center" } }, /* @__PURE__ */ React.createElement("strong", null, __("Something went wrong", "elementor"))));
24
+ import { ColorSwatchIcon, XIcon } from "@elementor/icons";
25
+ import { Alert, Box as Box3, ErrorBoundary, IconButton as IconButton2, Stack as Stack2 } from "@elementor/ui";
26
+ import { __ as __4 } from "@wordpress/i18n";
37
27
 
38
- // src/components/class-manager/class-manager-button.tsx
39
- var ClassManagerButton = () => {
40
- const { open } = usePanelActions();
41
- return /* @__PURE__ */ React2.createElement(IconButton2, { onClick: open }, /* @__PURE__ */ React2.createElement(AppsIcon, { fontSize: "tiny" }));
42
- };
28
+ // src/components/class-manager/global-classes-list.tsx
29
+ import * as React3 from "react";
30
+ import { stylesRepository } from "@elementor/editor-styles-repository";
31
+ import { EditableField, EllipsisWithTooltip, useEditable } from "@elementor/editor-ui";
32
+ import { DotsVerticalIcon, PhotoIcon } from "@elementor/icons";
33
+ import {
34
+ bindMenu,
35
+ bindTrigger,
36
+ Box as Box2,
37
+ IconButton,
38
+ List,
39
+ ListItem,
40
+ ListItemButton,
41
+ ListItemText,
42
+ Menu,
43
+ MenuItem,
44
+ Stack,
45
+ styled as styled2,
46
+ Tooltip,
47
+ Typography as Typography2,
48
+ usePopupState
49
+ } from "@elementor/ui";
50
+ import { __ as __3 } from "@wordpress/i18n";
43
51
 
44
- // src/components/logic-hooks.tsx
45
- import { useEffect } from "react";
46
- import { __useDispatch as useDispatch } from "@elementor/store";
52
+ // src/global-classes-styles-provider.ts
53
+ import { generateId } from "@elementor/editor-styles";
54
+ import {
55
+ __dispatch as dispatch,
56
+ __getState as getState,
57
+ __subscribeWithSelector as subscribeWithSelector
58
+ } from "@elementor/store";
59
+ import { __ } from "@wordpress/i18n";
47
60
 
48
- // src/api.ts
49
- import { httpService } from "@elementor/http";
50
- var RESOURCE_URL = "/global-classes";
51
- var apiClient = {
52
- all: async () => {
53
- return httpService().get(RESOURCE_URL);
54
- },
55
- post: async (data) => {
56
- return httpService().post(RESOURCE_URL, data);
57
- },
58
- put: async (id, data) => {
59
- return httpService().put(`${RESOURCE_URL}/${id}`, data);
60
- }
61
- };
61
+ // src/errors.ts
62
+ import { createError } from "@elementor/utils";
63
+ var GlobalClassNotFoundError = createError({
64
+ code: "global_class_not_found",
65
+ message: "Global class not found."
66
+ });
67
+ var GlobalClassLabelAlreadyExistsError = createError({
68
+ code: "global_class_label_already_exists",
69
+ message: "Class with this name already exists."
70
+ });
62
71
 
63
72
  // src/store.ts
73
+ import { mergeProps } from "@elementor/editor-props";
74
+ import {
75
+ getVariantByMeta
76
+ } from "@elementor/editor-styles";
64
77
  import {
65
78
  __createSelector as createSelector,
66
- __createSlice as createSlice
79
+ __createSlice as createSlice,
80
+ __useSelector as useSelector
67
81
  } from "@elementor/store";
68
82
  var initialState = {
69
83
  items: {},
70
- order: []
84
+ order: [],
85
+ isDirty: false
71
86
  };
72
87
  var SLICE_NAME = "globalClasses";
73
88
  var slice = createSlice({
@@ -77,91 +92,504 @@ var slice = createSlice({
77
92
  init(state, { payload }) {
78
93
  state.items = payload.items;
79
94
  state.order = payload.order;
95
+ state.isDirty = false;
80
96
  },
81
97
  add(state, { payload }) {
82
- state.items[payload.style.id] = payload.style;
83
- state.order = payload.order;
98
+ state.items[payload.id] = payload;
99
+ state.order.push(payload.id);
100
+ state.isDirty = true;
101
+ },
102
+ delete(state, { payload }) {
103
+ state.items = Object.fromEntries(Object.entries(state.items).filter(([id]) => id !== payload));
104
+ state.order = state.order.filter((id) => id !== payload);
105
+ state.isDirty = true;
106
+ },
107
+ setOrder(state, { payload }) {
108
+ state.order = payload;
84
109
  },
85
110
  update(state, { payload }) {
86
- state.items[payload.style.id] = payload.style;
87
- state.order = payload.order;
111
+ const style = state.items[payload.style.id];
112
+ const mergedData = {
113
+ ...style,
114
+ ...payload.style
115
+ };
116
+ state.items[payload.style.id] = mergedData;
117
+ state.isDirty = true;
118
+ },
119
+ updateProps(state, {
120
+ payload
121
+ }) {
122
+ const style = state.items[payload.id];
123
+ if (!style) {
124
+ throw new GlobalClassNotFoundError({ context: { styleId: payload.id } });
125
+ }
126
+ const variant = getVariantByMeta(style, payload.meta);
127
+ if (variant) {
128
+ variant.props = mergeProps(variant.props, payload.props);
129
+ } else {
130
+ style.variants.push({ meta: payload.meta, props: payload.props });
131
+ }
132
+ state.isDirty = true;
133
+ },
134
+ setPristine(state) {
135
+ state.isDirty = false;
88
136
  }
89
137
  }
90
138
  });
91
- var selectItems = (state) => state[SLICE_NAME].items;
92
139
  var selectOrder = (state) => state[SLICE_NAME].order;
140
+ var selectGlobalClasses = (state) => state[SLICE_NAME].items;
93
141
  var selectOrderedGlobalClasses = createSelector(
94
- selectItems,
142
+ selectGlobalClasses,
95
143
  selectOrder,
96
144
  (items, order) => order.map((id) => items[id])
97
145
  );
98
- var selectClass = (state, id) => state[SLICE_NAME].items[id];
99
-
100
- // src/components/logic-hooks.tsx
101
- function LogicHooks() {
102
- const dispatch2 = useDispatch();
103
- useEffect(() => {
104
- apiClient.all().then((res) => {
105
- const { data, meta } = res.data;
106
- dispatch2(slice.actions.init({ items: data, order: meta.order }));
107
- });
108
- }, [dispatch2]);
109
- return null;
110
- }
146
+ var selectClass = (state, id) => state[SLICE_NAME].items[id] ?? null;
147
+ var selectIsDirty = (state) => state.globalClasses.isDirty;
148
+ var useOrderedGlobalClasses = () => {
149
+ const items = useSelector(selectOrderedGlobalClasses);
150
+ return items;
151
+ };
152
+ var useGlobalClassesOrder = () => useSelector(selectOrder);
111
153
 
112
154
  // src/global-classes-styles-provider.ts
113
- import { __dispatch as dispatch, __getState as getState, __subscribe as subscribe } from "@elementor/store";
114
- import { __ as __2 } from "@wordpress/i18n";
115
155
  var globalClassesStylesProvider = {
116
156
  key: "global-classes",
117
157
  priority: 30,
118
158
  actions: {
119
159
  get: () => selectOrderedGlobalClasses(getState()),
120
- create: async (style) => {
121
- const res = await apiClient.post(style);
122
- const { data, meta } = res.data;
160
+ getById: (id) => selectClass(getState(), id),
161
+ create: (label) => {
162
+ const classes = selectGlobalClasses(getState());
163
+ const existingLabels = Object.values(classes).map((style) => style.label);
164
+ if (existingLabels.includes(label)) {
165
+ throw new GlobalClassLabelAlreadyExistsError({ context: { label } });
166
+ }
167
+ const existingIds = Object.keys(classes);
168
+ const id = generateId("g-", existingIds);
123
169
  dispatch(
124
170
  slice.actions.add({
125
- style: data,
126
- order: meta.order
171
+ id,
172
+ type: "class",
173
+ label,
174
+ variants: []
127
175
  })
128
176
  );
129
- return data;
177
+ return id;
130
178
  },
131
- update: async (payload) => {
132
- const style = selectClass(getState(), payload.id);
133
- const mergedData = { ...style, ...payload };
134
- const res = await apiClient.put(payload.id, mergedData);
135
- const { data, meta } = res.data;
179
+ update: (payload) => {
136
180
  dispatch(
137
181
  slice.actions.update({
138
- style: data,
139
- order: meta.order
182
+ style: payload
183
+ })
184
+ );
185
+ },
186
+ delete: (id) => {
187
+ dispatch(slice.actions.delete(id));
188
+ },
189
+ setOrder: (order) => {
190
+ dispatch(slice.actions.setOrder(order));
191
+ },
192
+ updateProps: (args) => {
193
+ dispatch(
194
+ slice.actions.updateProps({
195
+ id: args.id,
196
+ meta: args.meta,
197
+ props: args.props
140
198
  })
141
199
  );
142
- return data;
143
200
  }
144
201
  },
145
- subscribe: (cb) => subscribe(cb),
202
+ subscribe: (cb) => subscribeWithSelector((state) => state.globalClasses, cb),
146
203
  labels: {
147
- singular: __2("Global CSS Class", "elementor"),
148
- plural: __2("Global CSS Classes", "elementor")
204
+ singular: __("Global class", "elementor"),
205
+ plural: __("Global CSS Classes", "elementor")
206
+ }
207
+ };
208
+
209
+ // src/components/class-manager/delete-confirmation-dialog.tsx
210
+ import * as React from "react";
211
+ import { createContext, useContext, useState } from "react";
212
+ import { AlertOctagonFilledIcon } from "@elementor/icons";
213
+ import {
214
+ Button,
215
+ Dialog,
216
+ DialogActions,
217
+ DialogContent,
218
+ DialogContentText,
219
+ DialogTitle,
220
+ Typography
221
+ } from "@elementor/ui";
222
+ import { __ as __2 } from "@wordpress/i18n";
223
+ var context = createContext(null);
224
+ var DeleteConfirmationProvider = ({ children }) => {
225
+ const [dialogProps, setDialogProps] = useState(null);
226
+ const openDialog = (props) => {
227
+ setDialogProps(props);
228
+ };
229
+ const closeDialog = () => {
230
+ setDialogProps(null);
231
+ };
232
+ return /* @__PURE__ */ React.createElement(context.Provider, { value: { openDialog, closeDialog, dialogProps } }, children, !!dialogProps && /* @__PURE__ */ React.createElement(DeleteConfirmationDialog, { ...dialogProps }));
233
+ };
234
+ var TITLE_ID = "delete-class-dialog";
235
+ var DeleteConfirmationDialog = ({ label, id }) => {
236
+ const { closeDialog } = useDeleteConfirmation();
237
+ const onConfirm = () => {
238
+ globalClassesStylesProvider.actions.delete(id);
239
+ closeDialog();
240
+ };
241
+ return /* @__PURE__ */ React.createElement(Dialog, { open: true, onClose: closeDialog, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, /* @__PURE__ */ React.createElement(DialogTitle, { id: TITLE_ID, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React.createElement(AlertOctagonFilledIcon, { color: "error" }), __2("Delete global class", "elementor")), /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(DialogContentText, { variant: "body2", color: "textPrimary" }, __2("Deleting", "elementor"), /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2", component: "span" }, "\xA0", label, "\xA0"), __2(
242
+ "will permanently remove it from your project and may affect the design across all elements using it. This action cannot be undone.",
243
+ "elementor"
244
+ ))), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { color: "secondary", onClick: closeDialog }, __2("Cancel", "elementor")), /* @__PURE__ */ React.createElement(Button, { variant: "contained", color: "error", onClick: onConfirm }, __2("Delete", "elementor"))));
245
+ };
246
+ var useDeleteConfirmation = () => {
247
+ const contextValue = useContext(context);
248
+ if (!contextValue) {
249
+ throw new Error("useDeleteConfirmation must be used within a DeleteConfirmationProvider");
250
+ }
251
+ return contextValue;
252
+ };
253
+
254
+ // src/components/class-manager/sortable.tsx
255
+ import * as React2 from "react";
256
+ import { GripVerticalIcon } from "@elementor/icons";
257
+ import {
258
+ Box,
259
+ Paper,
260
+ styled,
261
+ UnstableSortableItem,
262
+ UnstableSortableProvider
263
+ } from "@elementor/ui";
264
+ var SortableProvider = (props) => /* @__PURE__ */ React2.createElement(UnstableSortableProvider, { restrictAxis: true, variant: "static", dragPlaceholderStyle: { opacity: "1" }, ...props });
265
+ var SortableTrigger = (props) => /* @__PURE__ */ React2.createElement("div", { ...props, role: "button", className: "class-item-sortable-trigger" }, /* @__PURE__ */ React2.createElement(GripVerticalIcon, { fontSize: "tiny" }));
266
+ var SortableItem = ({ children, id }) => {
267
+ return /* @__PURE__ */ React2.createElement(
268
+ UnstableSortableItem,
269
+ {
270
+ id,
271
+ render: ({
272
+ itemProps,
273
+ isDragged,
274
+ triggerProps,
275
+ itemStyle,
276
+ triggerStyle,
277
+ dropIndicationStyle,
278
+ showDropIndication
279
+ }) => {
280
+ return /* @__PURE__ */ React2.createElement(StyledSortableItem, { ...itemProps, elevation: 0, sx: itemStyle, role: "listitem" }, /* @__PURE__ */ React2.createElement(SortableTrigger, { ...triggerProps, style: triggerStyle }), children({
281
+ itemProps,
282
+ isDragged,
283
+ triggerProps,
284
+ itemStyle,
285
+ triggerStyle,
286
+ dropIndicationStyle,
287
+ showDropIndication
288
+ }));
289
+ }
290
+ }
291
+ );
292
+ };
293
+ var StyledSortableItem = styled(Paper)`
294
+ position: relative;
295
+
296
+ &:hover {
297
+ & .class-item-sortable-trigger {
298
+ visibility: visible;
299
+ }
300
+ }
301
+
302
+ & .class-item-sortable-trigger {
303
+ visibility: hidden;
304
+ position: absolute;
305
+ left: 0;
306
+ top: 50%;
307
+ transform: translate( -75%, -50% );
308
+ }
309
+ `;
310
+ var SortableItemIndicator = styled(Box)`
311
+ width: 100%;
312
+ height: 3px;
313
+ border-radius: ${({ theme }) => theme.spacing(0.5)};
314
+ background-color: ${({ theme }) => theme.palette.text.primary};
315
+ `;
316
+
317
+ // src/components/class-manager/global-classes-list.tsx
318
+ var GlobalClassesList = () => {
319
+ const cssClasses = useOrderedGlobalClasses();
320
+ const [classesOrder, reorderClasses] = useClassesOrder();
321
+ if (!cssClasses?.length) {
322
+ return /* @__PURE__ */ React3.createElement(EmptyState, null);
323
+ }
324
+ return /* @__PURE__ */ React3.createElement(DeleteConfirmationProvider, null, /* @__PURE__ */ React3.createElement(List, { sx: { display: "flex", flexDirection: "column", gap: 0.5 } }, /* @__PURE__ */ React3.createElement(SortableProvider, { value: classesOrder, onChange: reorderClasses }, cssClasses?.map(({ id, label }) => {
325
+ const renameClass = (newLabel) => {
326
+ globalClassesStylesProvider.actions.update({ label: newLabel, id });
327
+ };
328
+ return /* @__PURE__ */ React3.createElement(SortableItem, { key: id, id }, ({ isDragged, showDropIndication, dropIndicationStyle }) => /* @__PURE__ */ React3.createElement(
329
+ ClassItem,
330
+ {
331
+ id,
332
+ label,
333
+ renameClass,
334
+ selected: isDragged
335
+ },
336
+ showDropIndication && /* @__PURE__ */ React3.createElement(SortableItemIndicator, { style: dropIndicationStyle })
337
+ ));
338
+ }))));
339
+ };
340
+ var useClassesOrder = () => {
341
+ const order = useGlobalClassesOrder();
342
+ const reorder = (newIds) => {
343
+ globalClassesStylesProvider.actions.setOrder(newIds);
344
+ };
345
+ return [order, reorder];
346
+ };
347
+ var ClassItem = ({
348
+ id,
349
+ label,
350
+ renameClass,
351
+ selected,
352
+ children
353
+ }) => {
354
+ const {
355
+ ref: editableRef,
356
+ openEditMode,
357
+ isEditing,
358
+ error,
359
+ getProps: getEditableProps
360
+ } = useEditable({
361
+ value: label,
362
+ onSubmit: renameClass,
363
+ validation: validateLabel
364
+ });
365
+ const { openDialog } = useDeleteConfirmation();
366
+ const popupState = usePopupState({
367
+ variant: "popover",
368
+ disableAutoFocus: true
369
+ });
370
+ return /* @__PURE__ */ React3.createElement(Stack, { direction: "row", alignItems: "center", gap: 1, flexGrow: 1, flexShrink: 0 }, /* @__PURE__ */ React3.createElement(
371
+ StyledListItem,
372
+ {
373
+ component: "div",
374
+ disablePadding: true,
375
+ disableGutters: true,
376
+ secondaryAction: /* @__PURE__ */ React3.createElement(
377
+ Tooltip,
378
+ {
379
+ placement: "top",
380
+ className: "class-item-more-actions",
381
+ title: __3("More actions", "elementor")
382
+ },
383
+ /* @__PURE__ */ React3.createElement(IconButton, { size: "tiny", ...bindTrigger(popupState), "aria-label": "More actions" }, /* @__PURE__ */ React3.createElement(DotsVerticalIcon, { fontSize: "tiny" }))
384
+ )
385
+ },
386
+ /* @__PURE__ */ React3.createElement(
387
+ ListItemButton,
388
+ {
389
+ dense: true,
390
+ disableGutters: true,
391
+ shape: "rounded",
392
+ onDoubleClick: openEditMode,
393
+ selected: selected || popupState.isOpen,
394
+ focusVisibleClassName: "visible-class-item",
395
+ sx: {
396
+ minHeight: "36px",
397
+ display: "flex",
398
+ "&.visible-class-item": {
399
+ boxShadow: "none !important"
400
+ }
401
+ }
402
+ },
403
+ /* @__PURE__ */ React3.createElement(Indicator, { isActive: isEditing, isError: !!error }, isEditing ? /* @__PURE__ */ React3.createElement(
404
+ EditableField,
405
+ {
406
+ ref: editableRef,
407
+ error,
408
+ as: Typography2,
409
+ variant: "caption",
410
+ ...getEditableProps()
411
+ }
412
+ ) : /* @__PURE__ */ React3.createElement(EllipsisWithTooltip, { title: label, as: Typography2, variant: "caption" }))
413
+ ),
414
+ children,
415
+ /* @__PURE__ */ React3.createElement(
416
+ Menu,
417
+ {
418
+ ...bindMenu(popupState),
419
+ anchorOrigin: {
420
+ vertical: "bottom",
421
+ horizontal: "right"
422
+ },
423
+ transformOrigin: {
424
+ vertical: "top",
425
+ horizontal: "right"
426
+ }
427
+ },
428
+ /* @__PURE__ */ React3.createElement(
429
+ MenuItem,
430
+ {
431
+ sx: { minWidth: "160px" },
432
+ onClick: () => {
433
+ popupState.close();
434
+ openEditMode();
435
+ }
436
+ },
437
+ /* @__PURE__ */ React3.createElement(ListItemText, { primary: __3("Rename", "elementor") })
438
+ ),
439
+ /* @__PURE__ */ React3.createElement(
440
+ MenuItem,
441
+ {
442
+ onClick: () => {
443
+ popupState.close();
444
+ openDialog({ id, label });
445
+ }
446
+ },
447
+ /* @__PURE__ */ React3.createElement(ListItemText, { primary: __3("Delete", "elementor"), sx: { color: "error.light" } })
448
+ )
449
+ )
450
+ ));
451
+ };
452
+ var StyledListItem = styled2(ListItem)`
453
+ .class-item-more-actions {
454
+ visibility: hidden;
455
+ }
456
+ &:hover {
457
+ .class-item-more-actions {
458
+ visibility: visible;
459
+ }
460
+ }
461
+ `;
462
+ var EmptyState = () => /* @__PURE__ */ React3.createElement(Stack, { alignItems: "center", gap: 3, pt: 4, px: 0.5 }, /* @__PURE__ */ React3.createElement(PhotoIcon, { fontSize: "large" }), /* @__PURE__ */ React3.createElement(StyledHeader, { variant: "subtitle2", component: "h2", color: "text.secondary" }, __3("No CSS classes created yet", "elementor")), /* @__PURE__ */ React3.createElement(Typography2, { align: "center", variant: "caption", color: "text.secondary" }, __3(
463
+ "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.",
464
+ "elementor"
465
+ )));
466
+ var StyledHeader = styled2(Typography2)(({ theme, variant }) => ({
467
+ "&.MuiTypography-root": {
468
+ ...theme.typography[variant]
469
+ }
470
+ }));
471
+ var Indicator = styled2(Box2, {
472
+ shouldForwardProp: (prop) => !["isActive", "isError"].includes(prop)
473
+ })(({ theme, isActive, isError }) => ({
474
+ display: "flex",
475
+ width: "100%",
476
+ flexGrow: 1,
477
+ borderRadius: theme.spacing(0.5),
478
+ border: getIndicatorBorder({ isActive, isError, theme }),
479
+ padding: `0 ${theme.spacing(1)}`,
480
+ marginLeft: isActive ? theme.spacing(1) : 0
481
+ }));
482
+ var getIndicatorBorder = ({ isActive, isError, theme }) => {
483
+ if (isError) {
484
+ return `2px solid ${theme.palette.error.main}`;
485
+ }
486
+ if (isActive) {
487
+ return `2px solid ${theme.palette.secondary.main}`;
488
+ }
489
+ return "none";
490
+ };
491
+ var validateLabel = (newLabel) => {
492
+ if (!stylesRepository.isLabelValid(newLabel)) {
493
+ return __3("Format is not valid", "elementor");
494
+ }
495
+ if (stylesRepository.isLabelExist(newLabel)) {
496
+ return __3("Existing name", "elementor");
149
497
  }
150
498
  };
151
499
 
500
+ // src/components/class-manager/class-manager-panel.tsx
501
+ var { panel, usePanelActions } = createPanel({
502
+ id: "class-manager-panel",
503
+ component: ClassManagerPanel
504
+ });
505
+ function ClassManagerPanel() {
506
+ return /* @__PURE__ */ React4.createElement(ErrorBoundary, { fallback: /* @__PURE__ */ React4.createElement(ErrorBoundaryFallback, null) }, /* @__PURE__ */ React4.createElement(Panel, null, /* @__PURE__ */ React4.createElement(PanelHeader, null, /* @__PURE__ */ React4.createElement(Stack2, { p: 1, pl: 2, width: "100%", direction: "row", alignItems: "center" }, /* @__PURE__ */ React4.createElement(PanelHeaderTitle, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React4.createElement(ColorSwatchIcon, { fontSize: "inherit", sx: { transform: "rotate(90deg)" } }), __4("CSS Class manager")), /* @__PURE__ */ React4.createElement(CloseButton, { sx: { marginLeft: "auto" } }))), /* @__PURE__ */ React4.createElement(PanelBody, { px: 2 }, /* @__PURE__ */ React4.createElement(GlobalClassesList, null))));
507
+ }
508
+ var CloseButton = (props) => {
509
+ const { close } = usePanelActions();
510
+ return /* @__PURE__ */ React4.createElement(IconButton2, { size: "small", color: "secondary", onClick: close, ...props }, /* @__PURE__ */ React4.createElement(XIcon, { fontSize: "small" }));
511
+ };
512
+ var ErrorBoundaryFallback = () => /* @__PURE__ */ React4.createElement(Box3, { role: "alert", sx: { minHeight: "100%", p: 2 } }, /* @__PURE__ */ React4.createElement(Alert, { severity: "error", sx: { mb: 2, maxWidth: 400, textAlign: "center" } }, /* @__PURE__ */ React4.createElement("strong", null, __4("Something went wrong", "elementor"))));
513
+
514
+ // src/components/class-manager/class-manager-button.tsx
515
+ var ClassManagerButton = () => {
516
+ const { open } = usePanelActions();
517
+ return /* @__PURE__ */ React5.createElement(Tooltip2, { title: __5("Class manager"), placement: "top" }, /* @__PURE__ */ React5.createElement(IconButton3, { onClick: open }, /* @__PURE__ */ React5.createElement(ColorSwatchIcon2, { fontSize: "tiny" })));
518
+ };
519
+
520
+ // src/components/populate-store.tsx
521
+ import { useEffect } from "react";
522
+ import { __useDispatch as useDispatch } from "@elementor/store";
523
+
524
+ // src/api.ts
525
+ import { httpService } from "@elementor/http";
526
+ var RESOURCE_URL = "/global-classes";
527
+ var apiClient = {
528
+ all: () => httpService().get("elementor/v1" + RESOURCE_URL),
529
+ update: (payload) => httpService().put("elementor/v1" + RESOURCE_URL, payload)
530
+ };
531
+
532
+ // src/components/populate-store.tsx
533
+ function PopulateStore() {
534
+ const dispatch2 = useDispatch();
535
+ useEffect(() => {
536
+ apiClient.all().then((res) => {
537
+ const { data, meta } = res.data;
538
+ dispatch2(slice.actions.init({ items: data, order: meta.order }));
539
+ });
540
+ }, [dispatch2]);
541
+ return null;
542
+ }
543
+
544
+ // src/sync-with-document-save.ts
545
+ import { __privateRunCommandSync as runCommandSync, registerDataHook } from "@elementor/editor-v1-adapters";
546
+ import { __dispatch, __getState as getState2, __subscribeWithSelector as subscribeWithSelector2 } from "@elementor/store";
547
+ function syncWithDocumentSave() {
548
+ const unsubscribe = syncDirtyState();
549
+ bindSaveAction();
550
+ return unsubscribe;
551
+ }
552
+ function syncDirtyState() {
553
+ return subscribeWithSelector2(selectIsDirty, () => {
554
+ if (!isDirty()) {
555
+ return;
556
+ }
557
+ runCommandSync("document/save/set-is-modified", { status: true }, { internal: true });
558
+ });
559
+ }
560
+ function bindSaveAction() {
561
+ registerDataHook("after", "document/save/save", async () => {
562
+ if (!isDirty()) {
563
+ return;
564
+ }
565
+ const state = getState2().globalClasses;
566
+ await apiClient.update({
567
+ items: state.items,
568
+ order: state.order
569
+ });
570
+ __dispatch(slice.actions.setPristine());
571
+ });
572
+ }
573
+ function isDirty() {
574
+ return selectIsDirty(getState2());
575
+ }
576
+
152
577
  // src/init.ts
153
578
  function init() {
154
579
  registerSlice(slice);
155
580
  registerPanel(panel);
156
- stylesRepository.register(globalClassesStylesProvider);
581
+ stylesRepository2.register(globalClassesStylesProvider);
157
582
  injectIntoLogic({
158
- id: "global-classes-hooks",
159
- component: LogicHooks
583
+ id: "global-classes-populate-store",
584
+ component: PopulateStore
160
585
  });
161
586
  injectIntoClassSelectorActions({
162
- id: "global-classes",
587
+ id: "global-classes-manager-button",
163
588
  component: ClassManagerButton
164
589
  });
590
+ listenTo(v1ReadyEvent(), () => {
591
+ syncWithDocumentSave();
592
+ });
165
593
  }
166
594
 
167
595
  // src/index.ts