@elementor/editor-global-classes 0.14.1 → 0.15.4
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 +77 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +194 -138
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +173 -134
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/components/class-manager/class-manager-button.tsx +3 -6
- package/src/components/class-manager/class-manager-introduction.tsx +13 -11
- package/src/components/class-manager/class-manager-panel.tsx +83 -91
- package/src/components/class-manager/delete-class.ts +36 -0
- package/src/components/class-manager/delete-confirmation-dialog.tsx +4 -4
- package/src/components/class-manager/global-classes-list.tsx +18 -10
- package/src/components/class-manager/panel-interactions.ts +24 -0
- package/src/global-classes-styles-provider.ts +10 -13
- package/src/index.ts +1 -3
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useEffect
|
|
3
|
-
import {
|
|
4
|
-
__useActiveDocument as useActiveDocument,
|
|
5
|
-
getDocumentModifiedStatus,
|
|
6
|
-
setDocumentModifiedStatus,
|
|
7
|
-
} from '@elementor/editor-documents';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { setDocumentModifiedStatus } from '@elementor/editor-documents';
|
|
8
4
|
import {
|
|
9
5
|
__createPanel as createPanel,
|
|
10
6
|
Panel,
|
|
@@ -23,8 +19,10 @@ import { __ } from '@wordpress/i18n';
|
|
|
23
19
|
import { useDirtyState } from '../../hooks/use-dirty-state';
|
|
24
20
|
import { saveGlobalClasses } from '../../save-global-classes';
|
|
25
21
|
import { ClassManagerIntroduction } from './class-manager-introduction';
|
|
22
|
+
import { hasDeletedItems, onDelete } from './delete-class';
|
|
26
23
|
import { FlippedColorSwatchIcon } from './flipped-color-swatch-icon';
|
|
27
24
|
import { GlobalClassesList } from './global-classes-list';
|
|
25
|
+
import { blockPanelInteractions, unblockPanelInteractions } from './panel-interactions';
|
|
28
26
|
import { SaveChangesDialog, useDialog } from './save-changes-dialog';
|
|
29
27
|
|
|
30
28
|
const id = 'global-classes-manager';
|
|
@@ -40,14 +38,12 @@ export const { panel, usePanelActions } = createPanel( {
|
|
|
40
38
|
onOpen: () => {
|
|
41
39
|
changeEditMode( id );
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
blockPanelInteractions();
|
|
44
42
|
},
|
|
45
|
-
onClose: (
|
|
43
|
+
onClose: () => {
|
|
46
44
|
changeEditMode( 'edit' );
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
setDocumentModifiedStatus( documentModifiedState );
|
|
50
|
-
}
|
|
46
|
+
unblockPanelInteractions();
|
|
51
47
|
},
|
|
52
48
|
} );
|
|
53
49
|
|
|
@@ -62,81 +58,78 @@ export function ClassManagerPanel() {
|
|
|
62
58
|
usePreventUnload();
|
|
63
59
|
|
|
64
60
|
return (
|
|
65
|
-
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
closeSaveChangesDialog();
|
|
131
|
-
closePanel();
|
|
132
|
-
},
|
|
61
|
+
<ThemeProvider>
|
|
62
|
+
<ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
|
|
63
|
+
<Panel>
|
|
64
|
+
<PanelHeader>
|
|
65
|
+
<Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
|
|
66
|
+
<PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
|
|
67
|
+
<FlippedColorSwatchIcon fontSize="inherit" />
|
|
68
|
+
{ __( 'Class Manager', 'elementor' ) }
|
|
69
|
+
</PanelHeaderTitle>
|
|
70
|
+
<CloseButton
|
|
71
|
+
sx={ { marginLeft: 'auto' } }
|
|
72
|
+
disabled={ isPublishing }
|
|
73
|
+
onClose={ () => {
|
|
74
|
+
if ( isDirty ) {
|
|
75
|
+
openSaveChangesDialog();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
closePanel();
|
|
80
|
+
} }
|
|
81
|
+
/>
|
|
82
|
+
</Stack>
|
|
83
|
+
</PanelHeader>
|
|
84
|
+
<PanelBody px={ 2 }>
|
|
85
|
+
<GlobalClassesList disabled={ isPublishing } />
|
|
86
|
+
</PanelBody>
|
|
87
|
+
<PanelFooter>
|
|
88
|
+
<Button
|
|
89
|
+
fullWidth
|
|
90
|
+
size="small"
|
|
91
|
+
color="global"
|
|
92
|
+
variant="contained"
|
|
93
|
+
onClick={ publish }
|
|
94
|
+
disabled={ ! isDirty }
|
|
95
|
+
loading={ isPublishing }
|
|
96
|
+
>
|
|
97
|
+
{ __( 'Save changes', 'elementor' ) }
|
|
98
|
+
</Button>
|
|
99
|
+
</PanelFooter>
|
|
100
|
+
</Panel>
|
|
101
|
+
</ErrorBoundary>
|
|
102
|
+
<ClassManagerIntroduction />
|
|
103
|
+
{ isSaveChangesDialogOpen && (
|
|
104
|
+
<SaveChangesDialog>
|
|
105
|
+
<SaveChangesDialog.Title>{ __( 'You have unsaved changes', 'elementor' ) }</SaveChangesDialog.Title>
|
|
106
|
+
<SaveChangesDialog.Content>
|
|
107
|
+
<SaveChangesDialog.ContentText>
|
|
108
|
+
{ __( 'You have unsaved changes in the Class Manager.', 'elementor' ) }
|
|
109
|
+
</SaveChangesDialog.ContentText>
|
|
110
|
+
<SaveChangesDialog.ContentText>
|
|
111
|
+
{ __( 'To avoid losing your updates, save your changes before leaving.', 'elementor' ) }
|
|
112
|
+
</SaveChangesDialog.ContentText>
|
|
113
|
+
</SaveChangesDialog.Content>
|
|
114
|
+
<SaveChangesDialog.Actions
|
|
115
|
+
actions={ {
|
|
116
|
+
cancel: {
|
|
117
|
+
label: __( 'Cancel', 'elementor' ),
|
|
118
|
+
action: closeSaveChangesDialog,
|
|
119
|
+
},
|
|
120
|
+
confirm: {
|
|
121
|
+
label: __( 'Save & Continue', 'elementor' ),
|
|
122
|
+
action: async () => {
|
|
123
|
+
await publish();
|
|
124
|
+
closeSaveChangesDialog();
|
|
125
|
+
closePanel();
|
|
133
126
|
},
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
127
|
+
},
|
|
128
|
+
} }
|
|
129
|
+
/>
|
|
130
|
+
</SaveChangesDialog>
|
|
131
|
+
) }
|
|
132
|
+
</ThemeProvider>
|
|
140
133
|
);
|
|
141
134
|
}
|
|
142
135
|
|
|
@@ -173,14 +166,13 @@ const usePreventUnload = () => {
|
|
|
173
166
|
};
|
|
174
167
|
|
|
175
168
|
const usePublish = () => {
|
|
176
|
-
const document = useActiveDocument();
|
|
177
|
-
const [ initialDocumentStatus ] = useState( document?.isDirty ?? false );
|
|
178
|
-
|
|
179
169
|
return useMutation( {
|
|
180
170
|
mutationFn: () => saveGlobalClasses( { context: 'frontend' } ),
|
|
181
|
-
onSuccess: () => {
|
|
182
|
-
|
|
183
|
-
|
|
171
|
+
onSuccess: async () => {
|
|
172
|
+
setDocumentModifiedStatus( false );
|
|
173
|
+
|
|
174
|
+
if ( hasDeletedItems() ) {
|
|
175
|
+
await onDelete();
|
|
184
176
|
}
|
|
185
177
|
},
|
|
186
178
|
} );
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getCurrentDocument, getV1DocumentsManager } from '@elementor/editor-documents';
|
|
2
|
+
import { __privateRunCommand as runCommand } from '@elementor/editor-v1-adapters';
|
|
3
|
+
import { __dispatch as dispatch } from '@elementor/store';
|
|
4
|
+
|
|
5
|
+
import { slice } from '../../store';
|
|
6
|
+
|
|
7
|
+
let isDeleted = false;
|
|
8
|
+
|
|
9
|
+
export const deleteClass = ( id: string ) => {
|
|
10
|
+
dispatch( slice.actions.delete( id ) );
|
|
11
|
+
|
|
12
|
+
isDeleted = true;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const onDelete = async () => {
|
|
16
|
+
await reloadDocument();
|
|
17
|
+
|
|
18
|
+
isDeleted = false;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const hasDeletedItems = () => isDeleted;
|
|
22
|
+
|
|
23
|
+
// When deleting a class, we remove it from all the documents that have it applied.
|
|
24
|
+
// In order to reflect the changes in the active document, we need to reload it.
|
|
25
|
+
const reloadDocument = () => {
|
|
26
|
+
const currentDocument = getCurrentDocument();
|
|
27
|
+
const documentsManager = getV1DocumentsManager();
|
|
28
|
+
|
|
29
|
+
documentsManager.invalidateCache();
|
|
30
|
+
|
|
31
|
+
return runCommand( 'editor/documents/switch', {
|
|
32
|
+
id: currentDocument?.id,
|
|
33
|
+
shouldScroll: false,
|
|
34
|
+
shouldNavigateToDefaultRoute: false,
|
|
35
|
+
} );
|
|
36
|
+
};
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '@elementor/ui';
|
|
14
14
|
import { __ } from '@wordpress/i18n';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { deleteClass } from './delete-class';
|
|
17
17
|
|
|
18
18
|
type DeleteConfirmationDialogProps = Pick< StyleDefinition, 'id' | 'label' >;
|
|
19
19
|
|
|
@@ -50,7 +50,7 @@ const DeleteConfirmationDialog = ( { label, id }: DeleteConfirmationDialogProps
|
|
|
50
50
|
const { closeDialog } = useDeleteConfirmation();
|
|
51
51
|
|
|
52
52
|
const onConfirm = () => {
|
|
53
|
-
|
|
53
|
+
deleteClass( id );
|
|
54
54
|
|
|
55
55
|
closeDialog();
|
|
56
56
|
};
|
|
@@ -59,7 +59,7 @@ const DeleteConfirmationDialog = ( { label, id }: DeleteConfirmationDialogProps
|
|
|
59
59
|
<Dialog open onClose={ closeDialog } aria-labelledby={ TITLE_ID } maxWidth="xs">
|
|
60
60
|
<DialogTitle id={ TITLE_ID } display="flex" alignItems="center" gap={ 1 } sx={ { lineHeight: 1 } }>
|
|
61
61
|
<AlertOctagonFilledIcon color="error" />
|
|
62
|
-
{ __( 'Delete
|
|
62
|
+
{ __( 'Delete this class?', 'elementor' ) }
|
|
63
63
|
</DialogTitle>
|
|
64
64
|
<DialogContent>
|
|
65
65
|
<DialogContentText variant="body2" color="textPrimary">
|
|
@@ -75,7 +75,7 @@ const DeleteConfirmationDialog = ( { label, id }: DeleteConfirmationDialogProps
|
|
|
75
75
|
</DialogContent>
|
|
76
76
|
<DialogActions>
|
|
77
77
|
<Button color="secondary" onClick={ closeDialog }>
|
|
78
|
-
{ __( '
|
|
78
|
+
{ __( 'Not now', 'elementor' ) }
|
|
79
79
|
</Button>
|
|
80
80
|
<Button variant="contained" color="error" onClick={ onConfirm }>
|
|
81
81
|
{ __( 'Delete', 'elementor' ) }
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
3
|
-
import {
|
|
3
|
+
import { validateStyleLabel } from '@elementor/editor-styles-repository';
|
|
4
4
|
import { EditableField, EllipsisWithTooltip, MenuListItem, useEditable } from '@elementor/editor-ui';
|
|
5
5
|
import { DotsVerticalIcon } from '@elementor/icons';
|
|
6
|
+
import { __useDispatch as useDispatch } from '@elementor/store';
|
|
6
7
|
import {
|
|
7
8
|
bindMenu,
|
|
8
9
|
bindTrigger,
|
|
@@ -22,15 +23,16 @@ import {
|
|
|
22
23
|
} from '@elementor/ui';
|
|
23
24
|
import { __ } from '@wordpress/i18n';
|
|
24
25
|
|
|
25
|
-
import { globalClassesStylesProvider } from '../../global-classes-styles-provider';
|
|
26
26
|
import { useClassesOrder } from '../../hooks/use-classes-order';
|
|
27
27
|
import { useOrderedClasses } from '../../hooks/use-ordered-classes';
|
|
28
|
+
import { slice } from '../../store';
|
|
28
29
|
import { DeleteConfirmationProvider, useDeleteConfirmation } from './delete-confirmation-dialog';
|
|
29
30
|
import { FlippedColorSwatchIcon } from './flipped-color-swatch-icon';
|
|
30
31
|
import { SortableItem, SortableProvider, SortableTrigger, type SortableTriggerProps } from './sortable';
|
|
31
32
|
|
|
32
33
|
export const GlobalClassesList = ( { disabled }: { disabled?: boolean } ) => {
|
|
33
34
|
const cssClasses = useOrderedClasses();
|
|
35
|
+
const dispatch = useDispatch();
|
|
34
36
|
|
|
35
37
|
const [ classesOrder, reorderClasses ] = useReorder();
|
|
36
38
|
|
|
@@ -44,7 +46,14 @@ export const GlobalClassesList = ( { disabled }: { disabled?: boolean } ) => {
|
|
|
44
46
|
<SortableProvider value={ classesOrder } onChange={ reorderClasses }>
|
|
45
47
|
{ cssClasses?.map( ( { id, label } ) => {
|
|
46
48
|
const renameClass = ( newLabel: string ) => {
|
|
47
|
-
|
|
49
|
+
dispatch(
|
|
50
|
+
slice.actions.update( {
|
|
51
|
+
style: {
|
|
52
|
+
id,
|
|
53
|
+
label: newLabel,
|
|
54
|
+
},
|
|
55
|
+
} )
|
|
56
|
+
);
|
|
48
57
|
};
|
|
49
58
|
|
|
50
59
|
return (
|
|
@@ -69,10 +78,11 @@ export const GlobalClassesList = ( { disabled }: { disabled?: boolean } ) => {
|
|
|
69
78
|
};
|
|
70
79
|
|
|
71
80
|
const useReorder = () => {
|
|
81
|
+
const dispatch = useDispatch();
|
|
72
82
|
const order = useClassesOrder();
|
|
73
83
|
|
|
74
84
|
const reorder = ( newIds: StyleDefinitionID[] ) => {
|
|
75
|
-
|
|
85
|
+
dispatch( slice.actions.setOrder( newIds ) );
|
|
76
86
|
};
|
|
77
87
|
|
|
78
88
|
return [ order, reorder ] as const;
|
|
@@ -257,13 +267,11 @@ const getIndicatorBorder = ( { isActive, isError, theme }: { isActive: boolean;
|
|
|
257
267
|
};
|
|
258
268
|
|
|
259
269
|
const validateLabel = ( newLabel: string ) => {
|
|
260
|
-
|
|
261
|
-
return __( 'Format is not valid', 'elementor' );
|
|
262
|
-
}
|
|
270
|
+
const result = validateStyleLabel( newLabel );
|
|
263
271
|
|
|
264
|
-
if (
|
|
265
|
-
return
|
|
272
|
+
if ( result.isValid ) {
|
|
273
|
+
return null;
|
|
266
274
|
}
|
|
267
275
|
|
|
268
|
-
return
|
|
276
|
+
return result.error;
|
|
269
277
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type ExtendedWindow = Window & {
|
|
2
|
+
$e?: {
|
|
3
|
+
components?: {
|
|
4
|
+
get?: ( name: 'panel' ) =>
|
|
5
|
+
| {
|
|
6
|
+
blockUserInteractions?: () => void;
|
|
7
|
+
unblockUserInteractions?: () => void;
|
|
8
|
+
}
|
|
9
|
+
| undefined;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function blockPanelInteractions() {
|
|
15
|
+
const extendedWindow = window as unknown as ExtendedWindow;
|
|
16
|
+
|
|
17
|
+
extendedWindow.$e?.components?.get?.( 'panel' )?.blockUserInteractions?.();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function unblockPanelInteractions() {
|
|
21
|
+
const extendedWindow = window as unknown as ExtendedWindow;
|
|
22
|
+
|
|
23
|
+
extendedWindow.$e?.components?.get?.( 'panel' )?.unblockUserInteractions?.();
|
|
24
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { generateId } from '@elementor/editor-styles';
|
|
2
|
-
import
|
|
2
|
+
import { createStylesProvider } from '@elementor/editor-styles-repository';
|
|
3
3
|
import {
|
|
4
4
|
__dispatch as dispatch,
|
|
5
5
|
__getState as getState,
|
|
@@ -12,13 +12,18 @@ import { selectClass, selectGlobalClasses, selectOrderedClasses, slice, type Sta
|
|
|
12
12
|
|
|
13
13
|
const MAX_CLASSES = 50;
|
|
14
14
|
|
|
15
|
-
export const globalClassesStylesProvider = {
|
|
15
|
+
export const globalClassesStylesProvider = createStylesProvider( {
|
|
16
16
|
key: 'global-classes',
|
|
17
17
|
priority: 30,
|
|
18
18
|
limit: MAX_CLASSES,
|
|
19
|
+
labels: {
|
|
20
|
+
singular: __( 'class', 'elementor' ),
|
|
21
|
+
plural: __( 'classes', 'elementor' ),
|
|
22
|
+
},
|
|
23
|
+
subscribe: ( cb ) => subscribeWithSelector( ( state: StateWithGlobalClasses ) => state.globalClasses, cb ),
|
|
19
24
|
actions: {
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
all: () => selectOrderedClasses( getState() ),
|
|
26
|
+
get: ( id ) => selectClass( getState(), id ),
|
|
22
27
|
create: ( label ) => {
|
|
23
28
|
const classes = selectGlobalClasses( getState() );
|
|
24
29
|
|
|
@@ -52,9 +57,6 @@ export const globalClassesStylesProvider = {
|
|
|
52
57
|
delete: ( id ) => {
|
|
53
58
|
dispatch( slice.actions.delete( id ) );
|
|
54
59
|
},
|
|
55
|
-
setOrder: ( order ) => {
|
|
56
|
-
dispatch( slice.actions.setOrder( order ) );
|
|
57
|
-
},
|
|
58
60
|
updateProps: ( args ) => {
|
|
59
61
|
dispatch(
|
|
60
62
|
slice.actions.updateProps( {
|
|
@@ -65,9 +67,4 @@ export const globalClassesStylesProvider = {
|
|
|
65
67
|
);
|
|
66
68
|
},
|
|
67
69
|
},
|
|
68
|
-
|
|
69
|
-
labels: {
|
|
70
|
-
singular: __( 'Global class', 'elementor' ),
|
|
71
|
-
plural: __( 'Global CSS classes', 'elementor' ),
|
|
72
|
-
},
|
|
73
|
-
} satisfies StylesProvider;
|
|
70
|
+
} );
|
package/src/index.ts
CHANGED