@elementor/editor-global-classes 0.4.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,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 React5 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 __5 } from "@wordpress/i18n";
13
14
 
14
15
  // src/components/class-manager/class-manager-panel.tsx
15
- import * as React2 from "react";
16
+ import * as React4 from "react";
16
17
  import {
17
18
  __createPanel as createPanel,
18
19
  Panel,
@@ -21,24 +22,41 @@ 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 Box3, ErrorBoundary, IconButton as IconButton2, Stack as Stack2 } from "@elementor/ui";
26
+ import { __ as __4 } from "@wordpress/i18n";
26
27
 
27
28
  // src/components/class-manager/global-classes-list.tsx
28
- import * as React from "react";
29
- import { EllipsisWithTooltip } from "@elementor/editor-ui";
30
- import { List, ListItemButton, Stack, Typography } from "@elementor/ui";
31
-
32
- // src/store.ts
33
- import { mergeProps } from "@elementor/editor-props";
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";
34
33
  import {
35
- getVariantByMeta
36
- } from "@elementor/editor-styles";
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";
51
+
52
+ // src/global-classes-styles-provider.ts
53
+ import { generateId } from "@elementor/editor-styles";
37
54
  import {
38
- __createSelector as createSelector,
39
- __createSlice as createSlice,
40
- __useSelector as useSelector
55
+ __dispatch as dispatch,
56
+ __getState as getState,
57
+ __subscribeWithSelector as subscribeWithSelector
41
58
  } from "@elementor/store";
59
+ import { __ } from "@wordpress/i18n";
42
60
 
43
61
  // src/errors.ts
44
62
  import { createError } from "@elementor/utils";
@@ -46,11 +64,25 @@ var GlobalClassNotFoundError = createError({
46
64
  code: "global_class_not_found",
47
65
  message: "Global class not found."
48
66
  });
67
+ var GlobalClassLabelAlreadyExistsError = createError({
68
+ code: "global_class_label_already_exists",
69
+ message: "Class with this name already exists."
70
+ });
49
71
 
50
72
  // src/store.ts
73
+ import { mergeProps } from "@elementor/editor-props";
74
+ import {
75
+ getVariantByMeta
76
+ } from "@elementor/editor-styles";
77
+ import {
78
+ __createSelector as createSelector,
79
+ __createSlice as createSlice,
80
+ __useSelector as useSelector
81
+ } from "@elementor/store";
51
82
  var initialState = {
52
83
  items: {},
53
- order: []
84
+ order: [],
85
+ isDirty: false
54
86
  };
55
87
  var SLICE_NAME = "globalClasses";
56
88
  var slice = createSlice({
@@ -60,14 +92,29 @@ var slice = createSlice({
60
92
  init(state, { payload }) {
61
93
  state.items = payload.items;
62
94
  state.order = payload.order;
95
+ state.isDirty = false;
63
96
  },
64
97
  add(state, { payload }) {
65
- state.items[payload.style.id] = payload.style;
66
- 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;
67
109
  },
68
110
  update(state, { payload }) {
69
- state.items[payload.style.id] = payload.style;
70
- 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;
71
118
  },
72
119
  updateProps(state, {
73
120
  payload
@@ -82,51 +129,395 @@ var slice = createSlice({
82
129
  } else {
83
130
  style.variants.push({ meta: payload.meta, props: payload.props });
84
131
  }
132
+ state.isDirty = true;
133
+ },
134
+ setPristine(state) {
135
+ state.isDirty = false;
85
136
  }
86
137
  }
87
138
  });
88
- var selectItems = (state) => state[SLICE_NAME].items;
89
139
  var selectOrder = (state) => state[SLICE_NAME].order;
140
+ var selectGlobalClasses = (state) => state[SLICE_NAME].items;
90
141
  var selectOrderedGlobalClasses = createSelector(
91
- selectItems,
142
+ selectGlobalClasses,
92
143
  selectOrder,
93
144
  (items, order) => order.map((id) => items[id])
94
145
  );
95
146
  var selectClass = (state, id) => state[SLICE_NAME].items[id] ?? null;
147
+ var selectIsDirty = (state) => state.globalClasses.isDirty;
96
148
  var useOrderedGlobalClasses = () => {
97
149
  const items = useSelector(selectOrderedGlobalClasses);
98
150
  return items;
99
151
  };
152
+ var useGlobalClassesOrder = () => useSelector(selectOrder);
153
+
154
+ // src/global-classes-styles-provider.ts
155
+ var globalClassesStylesProvider = {
156
+ key: "global-classes",
157
+ priority: 30,
158
+ actions: {
159
+ get: () => selectOrderedGlobalClasses(getState()),
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);
169
+ dispatch(
170
+ slice.actions.add({
171
+ id,
172
+ type: "class",
173
+ label,
174
+ variants: []
175
+ })
176
+ );
177
+ return id;
178
+ },
179
+ update: (payload) => {
180
+ dispatch(
181
+ slice.actions.update({
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
198
+ })
199
+ );
200
+ }
201
+ },
202
+ subscribe: (cb) => subscribeWithSelector((state) => state.globalClasses, cb),
203
+ labels: {
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
+ `;
100
316
 
101
317
  // src/components/class-manager/global-classes-list.tsx
102
318
  var GlobalClassesList = () => {
103
319
  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
- })));
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");
497
+ }
107
498
  };
108
499
 
109
500
  // src/components/class-manager/class-manager-panel.tsx
110
- var { panel, usePanelActions, usePanelStatus } = createPanel({
501
+ var { panel, usePanelActions } = createPanel({
111
502
  id: "class-manager-panel",
112
503
  component: ClassManagerPanel
113
504
  });
114
505
  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))));
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))));
116
507
  }
117
508
  var CloseButton = (props) => {
118
509
  const { close } = usePanelActions();
119
- return /* @__PURE__ */ React2.createElement(IconButton, { size: "small", color: "secondary", onClick: close, ...props }, /* @__PURE__ */ React2.createElement(XIcon, { fontSize: "small" }));
510
+ return /* @__PURE__ */ React4.createElement(IconButton2, { size: "small", color: "secondary", onClick: close, ...props }, /* @__PURE__ */ React4.createElement(XIcon, { fontSize: "small" }));
120
511
  };
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"))));
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"))));
122
513
 
123
514
  // src/components/class-manager/class-manager-button.tsx
124
515
  var ClassManagerButton = () => {
125
516
  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" })));
517
+ return /* @__PURE__ */ React5.createElement(Tooltip2, { title: __5("Class manager"), placement: "top" }, /* @__PURE__ */ React5.createElement(IconButton3, { onClick: open }, /* @__PURE__ */ React5.createElement(ColorSwatchIcon2, { fontSize: "tiny" })));
127
518
  };
128
519
 
129
- // src/components/logic-hooks.tsx
520
+ // src/components/populate-store.tsx
130
521
  import { useEffect } from "react";
131
522
  import { __useDispatch as useDispatch } from "@elementor/store";
132
523
 
@@ -134,19 +525,12 @@ import { __useDispatch as useDispatch } from "@elementor/store";
134
525
  import { httpService } from "@elementor/http";
135
526
  var RESOURCE_URL = "/global-classes";
136
527
  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
- }
528
+ all: () => httpService().get("elementor/v1" + RESOURCE_URL),
529
+ update: (payload) => httpService().put("elementor/v1" + RESOURCE_URL, payload)
146
530
  };
147
531
 
148
- // src/components/logic-hooks.tsx
149
- function LogicHooks() {
532
+ // src/components/populate-store.tsx
533
+ function PopulateStore() {
150
534
  const dispatch2 = useDispatch();
151
535
  useEffect(() => {
152
536
  apiClient.all().then((res) => {
@@ -157,73 +541,55 @@ function LogicHooks() {
157
541
  return null;
158
542
  }
159
543
 
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
- );
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;
205
556
  }
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
- };
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
+ }
213
576
 
214
577
  // src/init.ts
215
578
  function init() {
216
579
  registerSlice(slice);
217
580
  registerPanel(panel);
218
- stylesRepository.register(globalClassesStylesProvider);
581
+ stylesRepository2.register(globalClassesStylesProvider);
219
582
  injectIntoLogic({
220
- id: "global-classes-hooks",
221
- component: LogicHooks
583
+ id: "global-classes-populate-store",
584
+ component: PopulateStore
222
585
  });
223
586
  injectIntoClassSelectorActions({
224
- id: "global-classes",
587
+ id: "global-classes-manager-button",
225
588
  component: ClassManagerButton
226
589
  });
590
+ listenTo(v1ReadyEvent(), () => {
591
+ syncWithDocumentSave();
592
+ });
227
593
  }
228
594
 
229
595
  // src/index.ts