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