@elementor/editor-global-classes 4.1.0-manual → 4.2.0-839
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.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +1008 -700
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1011 -705
- package/dist/index.mjs.map +1 -1
- package/package.json +22 -21
- package/src/api.ts +32 -15
- package/src/components/class-manager/class-manager-button.tsx +16 -6
- package/src/components/class-manager/class-manager-panel.tsx +197 -101
- package/src/components/class-manager/global-classes-list.tsx +62 -14
- package/src/components/class-manager/sortable.tsx +9 -3
- package/src/components/global-styles-import-listener.tsx +10 -37
- package/src/components/populate-store.tsx +10 -24
- package/src/global-classes-styles-provider.ts +48 -15
- package/src/index.ts +9 -0
- package/src/init.ts +11 -5
- package/src/load-document-classes.ts +76 -0
- package/src/load-existing-classes.ts +49 -0
- package/src/mcp-integration/classes-resource.ts +3 -1
- package/src/mcp-integration/mcp-apply-unapply-global-classes.ts +1 -1
- package/src/mcp-integration/mcp-get-global-class-usages.ts +4 -4
- package/src/save-global-classes.tsx +17 -2
- package/src/store.ts +73 -7
- package/src/sync-with-document.tsx +13 -5
- package/src/utils/create-labels-for-classes.ts +7 -0
- package/src/utils/tracking.ts +14 -6
package/src/store.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
getVariantByMeta,
|
|
5
5
|
type StyleDefinition,
|
|
6
6
|
type StyleDefinitionID,
|
|
7
|
+
type StyleDefinitionsMap,
|
|
7
8
|
type StyleDefinitionVariant,
|
|
8
9
|
} from '@elementor/editor-styles';
|
|
9
10
|
import { type UpdateActionPayload } from '@elementor/editor-styles-repository';
|
|
@@ -19,12 +20,13 @@ import { GlobalClassNotFoundError } from './errors';
|
|
|
19
20
|
import { SnapshotHistory } from './utils/snapshot-history';
|
|
20
21
|
|
|
21
22
|
export type GlobalClasses = {
|
|
22
|
-
items:
|
|
23
|
+
items: StyleDefinitionsMap;
|
|
23
24
|
order: StyleDefinitionID[];
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
type GlobalClassesState = {
|
|
27
28
|
data: GlobalClasses;
|
|
29
|
+
classLabels: Record< StyleDefinitionID, string >;
|
|
28
30
|
initialData: {
|
|
29
31
|
frontend: GlobalClasses;
|
|
30
32
|
preview: GlobalClasses;
|
|
@@ -43,6 +45,7 @@ const localHistory = SnapshotHistory.get< GlobalClasses >( 'global-classes' );
|
|
|
43
45
|
|
|
44
46
|
const initialState: GlobalClassesState = {
|
|
45
47
|
data: { items: {}, order: [] },
|
|
48
|
+
classLabels: {},
|
|
46
49
|
initialData: {
|
|
47
50
|
frontend: { items: {}, order: [] },
|
|
48
51
|
preview: { items: {}, order: [] },
|
|
@@ -62,15 +65,17 @@ export const slice = createSlice( {
|
|
|
62
65
|
load(
|
|
63
66
|
state,
|
|
64
67
|
{
|
|
65
|
-
payload: { frontend, preview },
|
|
68
|
+
payload: { frontend, preview, classLabels },
|
|
66
69
|
}: PayloadAction< {
|
|
67
70
|
frontend: GlobalClasses;
|
|
68
71
|
preview: GlobalClasses;
|
|
72
|
+
classLabels: Record< StyleDefinitionID, string >;
|
|
69
73
|
} >
|
|
70
74
|
) {
|
|
71
75
|
state.initialData.frontend = frontend;
|
|
72
76
|
state.initialData.preview = preview;
|
|
73
77
|
state.data = preview;
|
|
78
|
+
state.classLabels = classLabels;
|
|
74
79
|
|
|
75
80
|
state.isDirty = false;
|
|
76
81
|
},
|
|
@@ -79,6 +84,7 @@ export const slice = createSlice( {
|
|
|
79
84
|
localHistory.next( state.data );
|
|
80
85
|
state.data.items[ payload.id ] = payload;
|
|
81
86
|
state.data.order.unshift( payload.id );
|
|
87
|
+
state.classLabels[ payload.id ] = payload.label;
|
|
82
88
|
|
|
83
89
|
state.isDirty = true;
|
|
84
90
|
},
|
|
@@ -90,6 +96,8 @@ export const slice = createSlice( {
|
|
|
90
96
|
);
|
|
91
97
|
|
|
92
98
|
state.data.order = state.data.order.filter( ( id ) => id !== payload );
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
100
|
+
delete state.classLabels[ payload ];
|
|
93
101
|
|
|
94
102
|
state.isDirty = true;
|
|
95
103
|
},
|
|
@@ -119,6 +127,7 @@ export const slice = createSlice( {
|
|
|
119
127
|
localHistory.next( state.data );
|
|
120
128
|
Object.entries( payload ).forEach( ( [ id, { modified } ] ) => {
|
|
121
129
|
state.data.items[ id ].label = modified;
|
|
130
|
+
state.classLabels[ id ] = modified;
|
|
122
131
|
} );
|
|
123
132
|
|
|
124
133
|
state.isDirty = false;
|
|
@@ -132,6 +141,7 @@ export const slice = createSlice( {
|
|
|
132
141
|
meta: StyleDefinitionVariant[ 'meta' ];
|
|
133
142
|
props: Props;
|
|
134
143
|
custom_css?: CustomCss | null;
|
|
144
|
+
mode?: 'merge' | 'replace';
|
|
135
145
|
} >
|
|
136
146
|
) {
|
|
137
147
|
const style = state.data.items[ payload.id ];
|
|
@@ -147,10 +157,16 @@ export const slice = createSlice( {
|
|
|
147
157
|
customCss = customCss?.raw ? customCss : null;
|
|
148
158
|
|
|
149
159
|
if ( variant ) {
|
|
150
|
-
// mergeProps fails with Proxy objects from store, manually re-create clones
|
|
151
|
-
const variantProps = JSON.parse( JSON.stringify( variant.props ) ) as Props;
|
|
152
160
|
const payloadProps = JSON.parse( JSON.stringify( payload.props ) ) as Props;
|
|
153
|
-
|
|
161
|
+
const mode = payload.mode ?? 'merge';
|
|
162
|
+
|
|
163
|
+
if ( mode === 'replace' ) {
|
|
164
|
+
variant.props = payloadProps;
|
|
165
|
+
} else {
|
|
166
|
+
const variantProps = JSON.parse( JSON.stringify( variant.props ) ) as Props;
|
|
167
|
+
variant.props = mergeProps( variantProps, payloadProps );
|
|
168
|
+
}
|
|
169
|
+
|
|
154
170
|
variant.custom_css = customCss;
|
|
155
171
|
|
|
156
172
|
style.variants = getNonEmptyVariants( style );
|
|
@@ -201,6 +217,33 @@ export const slice = createSlice( {
|
|
|
201
217
|
state.isDirty = true;
|
|
202
218
|
}
|
|
203
219
|
},
|
|
220
|
+
|
|
221
|
+
mergeExistingClasses(
|
|
222
|
+
state,
|
|
223
|
+
{
|
|
224
|
+
payload: { preview, frontend },
|
|
225
|
+
}: PayloadAction< { preview: GlobalClasses[ 'items' ]; frontend: GlobalClasses[ 'items' ] } >
|
|
226
|
+
) {
|
|
227
|
+
Object.entries( preview ).forEach( ( [ id, previewClassData ] ) => {
|
|
228
|
+
const frontendClassData = frontend[ id ];
|
|
229
|
+
|
|
230
|
+
if ( previewClassData === null || previewClassData === undefined ) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if ( ! ( id in state.data.items ) ) {
|
|
234
|
+
state.data.items[ id ] = previewClassData;
|
|
235
|
+
}
|
|
236
|
+
if ( ! ( id in state.initialData.frontend.items ) ) {
|
|
237
|
+
state.initialData.frontend.items[ id ] = frontendClassData;
|
|
238
|
+
}
|
|
239
|
+
if ( ! ( id in state.initialData.preview.items ) ) {
|
|
240
|
+
state.initialData.preview.items[ id ] = previewClassData;
|
|
241
|
+
}
|
|
242
|
+
if ( ! ( id in state.classLabels ) ) {
|
|
243
|
+
state.classLabels[ id ] = previewClassData.label;
|
|
244
|
+
}
|
|
245
|
+
} );
|
|
246
|
+
},
|
|
204
247
|
},
|
|
205
248
|
} );
|
|
206
249
|
|
|
@@ -226,9 +269,18 @@ const getNonEmptyVariants = ( style: StyleDefinition ) => {
|
|
|
226
269
|
);
|
|
227
270
|
};
|
|
228
271
|
|
|
272
|
+
export const placeholderDefinition = ( id: StyleDefinitionID, label: string ): StyleDefinition => ( {
|
|
273
|
+
id,
|
|
274
|
+
type: 'class',
|
|
275
|
+
label,
|
|
276
|
+
variants: [],
|
|
277
|
+
} );
|
|
278
|
+
|
|
229
279
|
// Selectors
|
|
230
280
|
export const selectData = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].data;
|
|
231
281
|
|
|
282
|
+
export const selectClassLabels = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].classLabels;
|
|
283
|
+
|
|
232
284
|
export const selectFrontendInitialData = ( state: SliceState< typeof slice > ) =>
|
|
233
285
|
state[ SLICE_NAME ].initialData.frontend;
|
|
234
286
|
|
|
@@ -241,8 +293,17 @@ export const selectGlobalClasses = createSelector( selectData, ( { items } ) =>
|
|
|
241
293
|
|
|
242
294
|
export const selectIsDirty = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].isDirty;
|
|
243
295
|
|
|
244
|
-
export const selectOrderedClasses = createSelector(
|
|
245
|
-
order
|
|
296
|
+
export const selectOrderedClasses = createSelector( selectData, selectClassLabels, ( { items, order }, classLabels ) =>
|
|
297
|
+
order
|
|
298
|
+
.map( ( id ) => {
|
|
299
|
+
const loaded = items[ id ];
|
|
300
|
+
if ( loaded ) {
|
|
301
|
+
return loaded;
|
|
302
|
+
}
|
|
303
|
+
const label = classLabels[ id ];
|
|
304
|
+
return label !== undefined ? placeholderDefinition( id, label ) : null;
|
|
305
|
+
} )
|
|
306
|
+
.filter( ( s ): s is StyleDefinition => s !== null )
|
|
246
307
|
);
|
|
247
308
|
|
|
248
309
|
export const selectClass = ( state: SliceState< typeof slice >, id: StyleDefinitionID ) =>
|
|
@@ -251,3 +312,8 @@ export const selectClass = ( state: SliceState< typeof slice >, id: StyleDefinit
|
|
|
251
312
|
export const selectEmptyCssClass = createSelector( selectData, ( { items } ) =>
|
|
252
313
|
Object.values( items ).filter( ( cssClass ) => cssClass.variants.length === 0 )
|
|
253
314
|
);
|
|
315
|
+
|
|
316
|
+
export const selectIsClassFetched = ( state: SliceState< typeof slice >, id: StyleDefinitionID ) =>
|
|
317
|
+
!! state[ SLICE_NAME ].initialData.preview.items[ id ] ||
|
|
318
|
+
!! state[ SLICE_NAME ].initialData.frontend.items[ id ] ||
|
|
319
|
+
false;
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
-
import { __privateListenTo as listenTo, v1ReadyEvent } from '@elementor/editor-v1-adapters';
|
|
2
|
+
import { __privateListenTo as listenTo, isExperimentActive, v1ReadyEvent } from '@elementor/editor-v1-adapters';
|
|
3
3
|
|
|
4
4
|
import { usePanelActions } from './components/class-manager/class-manager-panel';
|
|
5
5
|
import { syncWithDocumentSave } from './sync-with-document-save';
|
|
6
6
|
|
|
7
7
|
export function SyncWithDocumentSave() {
|
|
8
|
-
const
|
|
8
|
+
const { open: openClassPanel } = usePanelActions();
|
|
9
9
|
|
|
10
10
|
useEffect( () => {
|
|
11
|
-
listenTo( v1ReadyEvent(), () => {
|
|
12
|
-
|
|
11
|
+
const unsubscribe = listenTo( v1ReadyEvent(), () => {
|
|
12
|
+
const open = isExperimentActive( 'e_editor_design_system_panel' )
|
|
13
|
+
? () => {
|
|
14
|
+
window.dispatchEvent( new CustomEvent( 'elementor/open-global-classes-manager' ) );
|
|
15
|
+
}
|
|
16
|
+
: openClassPanel;
|
|
17
|
+
|
|
18
|
+
syncWithDocumentSave( { open } );
|
|
13
19
|
} );
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
return unsubscribe;
|
|
22
|
+
|
|
23
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- bind once at v1 ready; openClassPanel from createPanel is stable
|
|
16
24
|
}, [] );
|
|
17
25
|
|
|
18
26
|
return null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type StyleDefinitionID } from '@elementor/editor-styles';
|
|
2
|
+
|
|
3
|
+
import { type GlobalClassIndexEntry } from '../api';
|
|
4
|
+
|
|
5
|
+
export function createLabelsForClasses( entries: GlobalClassIndexEntry[] ): Record< StyleDefinitionID, string > {
|
|
6
|
+
return Object.fromEntries( entries.map( ( e ) => [ e.id, e.label ] ) );
|
|
7
|
+
}
|
package/src/utils/tracking.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { __getState as getState } from '@elementor/store';
|
|
|
5
5
|
import { fetchCssClassUsage } from '../../service/css-class-usage-service';
|
|
6
6
|
import { GlobalClassTrackingError } from '../errors';
|
|
7
7
|
import { type FilterKey } from '../hooks/use-filtered-css-class-usage';
|
|
8
|
-
import { selectClass } from '../store';
|
|
8
|
+
import { placeholderDefinition, selectClass, selectClassLabels } from '../store';
|
|
9
9
|
|
|
10
10
|
type EventMap = {
|
|
11
11
|
classCreated: {
|
|
@@ -220,16 +220,24 @@ const extractCssClassData = ( classId: StyleDefinitionID ) => {
|
|
|
220
220
|
};
|
|
221
221
|
|
|
222
222
|
const getCssClass = ( classId: StyleDefinitionID ) => {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
const state = getState();
|
|
224
|
+
const cssClass = selectClass( state, classId );
|
|
225
|
+
|
|
226
|
+
if ( cssClass ) {
|
|
227
|
+
return cssClass;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const label = selectClassLabels( state )[ classId ];
|
|
231
|
+
if ( label !== undefined ) {
|
|
232
|
+
return placeholderDefinition( classId, label );
|
|
226
233
|
}
|
|
227
|
-
|
|
234
|
+
|
|
235
|
+
throw new Error( `CSS class with ID ${ classId } not found` );
|
|
228
236
|
};
|
|
229
237
|
|
|
230
238
|
const trackDeleteClass = async ( classId: StyleDefinitionID ) => {
|
|
231
|
-
const totalInstances = await getTotalInstancesByCssClassID( classId );
|
|
232
239
|
const classTitle = getCssClass( classId ).label;
|
|
240
|
+
const totalInstances = await getTotalInstancesByCssClassID( classId );
|
|
233
241
|
return { totalInstances, classTitle };
|
|
234
242
|
};
|
|
235
243
|
|