@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/CHANGELOG.md +79 -0
- package/dist/index.js +471 -99
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +502 -100
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -11
- package/src/api.ts +6 -12
- package/src/components/class-manager/class-manager-introduction.tsx +51 -0
- package/src/components/class-manager/class-manager-panel.tsx +21 -17
- package/src/components/class-manager/delete-confirmation-dialog.tsx +96 -0
- package/src/components/class-manager/global-classes-list.tsx +250 -14
- package/src/components/class-manager/sortable.tsx +90 -0
- package/src/components/{logic-hooks.tsx → populate-store.tsx} +1 -1
- package/src/errors.ts +5 -0
- package/src/global-classes-styles-provider.ts +34 -23
- package/src/init.ts +10 -4
- package/src/store.ts +49 -13
- package/src/sync-with-document-save.ts +44 -0
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
|
|
10
|
+
import * as React6 from "react";
|
|
10
11
|
import { ColorSwatchIcon as ColorSwatchIcon2 } from "@elementor/icons";
|
|
11
|
-
import { IconButton as
|
|
12
|
-
import { __ as
|
|
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
|
|
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
|
|
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/
|
|
28
|
+
// src/components/class-manager/class-manager-introduction.tsx
|
|
28
29
|
import * as React from "react";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
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/
|
|
33
|
-
import
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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.
|
|
66
|
-
state.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]
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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
|
|
537
|
+
var { panel, usePanelActions } = createPanel({
|
|
111
538
|
id: "class-manager-panel",
|
|
112
539
|
component: ClassManagerPanel
|
|
113
540
|
});
|
|
114
541
|
function ClassManagerPanel() {
|
|
115
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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/
|
|
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:
|
|
138
|
-
|
|
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/
|
|
149
|
-
function
|
|
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/
|
|
161
|
-
import {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
617
|
+
stylesRepository2.register(globalClassesStylesProvider);
|
|
219
618
|
injectIntoLogic({
|
|
220
|
-
id: "global-classes-
|
|
221
|
-
component:
|
|
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
|