@elementor/editor-global-classes 4.1.0-825 → 4.1.0-826

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-global-classes",
3
- "version": "4.1.0-825",
3
+ "version": "4.1.0-826",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,29 +39,29 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.1.0-825",
43
- "@elementor/editor-current-user": "4.1.0-825",
44
- "@elementor/editor-documents": "4.1.0-825",
45
- "@elementor/editor-editing-panel": "4.1.0-825",
46
- "@elementor/editor-mcp": "4.1.0-825",
47
- "@elementor/editor-panels": "4.1.0-825",
48
- "@elementor/editor-props": "4.1.0-825",
49
- "@elementor/editor-variables": "4.1.0-825",
50
- "@elementor/editor-styles": "4.1.0-825",
51
- "@elementor/editor-canvas": "4.1.0-825",
52
- "@elementor/editor-styles-repository": "4.1.0-825",
53
- "@elementor/editor-ui": "4.1.0-825",
54
- "@elementor/editor-v1-adapters": "4.1.0-825",
55
- "@elementor/http-client": "4.1.0-825",
42
+ "@elementor/editor": "4.1.0-826",
43
+ "@elementor/editor-current-user": "4.1.0-826",
44
+ "@elementor/editor-documents": "4.1.0-826",
45
+ "@elementor/editor-editing-panel": "4.1.0-826",
46
+ "@elementor/editor-mcp": "4.1.0-826",
47
+ "@elementor/editor-panels": "4.1.0-826",
48
+ "@elementor/editor-props": "4.1.0-826",
49
+ "@elementor/editor-variables": "4.1.0-826",
50
+ "@elementor/editor-styles": "4.1.0-826",
51
+ "@elementor/editor-canvas": "4.1.0-826",
52
+ "@elementor/editor-styles-repository": "4.1.0-826",
53
+ "@elementor/editor-ui": "4.1.0-826",
54
+ "@elementor/editor-v1-adapters": "4.1.0-826",
55
+ "@elementor/http-client": "4.1.0-826",
56
56
  "@elementor/icons": "^1.68.0",
57
- "@elementor/query": "4.1.0-825",
58
- "@elementor/schema": "4.1.0-825",
59
- "@elementor/store": "4.1.0-825",
57
+ "@elementor/query": "4.1.0-826",
58
+ "@elementor/schema": "4.1.0-826",
59
+ "@elementor/store": "4.1.0-826",
60
60
  "@elementor/ui": "1.37.5",
61
- "@elementor/utils": "4.1.0-825",
61
+ "@elementor/utils": "4.1.0-826",
62
62
  "@tanstack/react-virtual": "^3.13.24",
63
63
  "@wordpress/i18n": "^5.13.0",
64
- "@elementor/events": "4.1.0-825"
64
+ "@elementor/events": "4.1.0-826"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "react": "^18.3.1",
@@ -5,6 +5,7 @@ import {
5
5
  } from '@elementor/editor-documents';
6
6
  import { useUserStylesCapability } from '@elementor/editor-styles-repository';
7
7
  import { SaveChangesDialog, useDialog } from '@elementor/editor-ui';
8
+ import { isExperimentActive } from '@elementor/editor-v1-adapters';
8
9
  import { IconButton, Tooltip } from '@elementor/ui';
9
10
  import { __ } from '@wordpress/i18n';
10
11
 
@@ -36,18 +37,27 @@ export const ClassManagerButton = () => {
36
37
  return null;
37
38
  }
38
39
 
40
+ const toggleClassesManagerPanel = () => {
41
+ if ( isExperimentActive( 'e_editor_design_system_panel' ) ) {
42
+ window.dispatchEvent(
43
+ new CustomEvent( 'elementor/toggle-design-system', {
44
+ detail: { tab: 'classes' as const },
45
+ } )
46
+ );
47
+ } else {
48
+ openPanel();
49
+ }
50
+ };
51
+
39
52
  const handleOpenPanel = () => {
40
53
  if ( document?.isDirty ) {
41
54
  openSaveChangesDialog();
42
55
  return;
43
56
  }
44
57
 
45
- openPanel();
58
+ toggleClassesManagerPanel();
59
+
46
60
  trackGlobalClassesButton();
47
- trackGlobalClasses( {
48
- event: 'classManagerOpened',
49
- source: 'style-panel',
50
- } );
51
61
  prefetchClassesUsage();
52
62
  };
53
63
 
@@ -80,7 +90,7 @@ export const ClassManagerButton = () => {
80
90
  action: async () => {
81
91
  await saveDocument();
82
92
  closeSaveChangesDialog();
83
- openPanel();
93
+ toggleClassesManagerPanel();
84
94
  trackGlobalClassesButton();
85
95
  prefetchClassesUsage();
86
96
  },
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useCallback, useEffect, useState } from 'react';
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { useSuppressedMessage } from '@elementor/editor-current-user';
4
4
  import { reloadCurrentDocument, setDocumentModifiedStatus } from '@elementor/editor-documents';
5
5
  import {
@@ -56,10 +56,25 @@ type StopSyncConfirmationDialogProps = {
56
56
 
57
57
  const id = 'global-classes-manager';
58
58
 
59
- // We need to disable the app-bar buttons, and the elements overlays when opening the classes manager panel.
60
- // The buttons and overlays are enabled only in edit mode, so we're creating a custom new edit mode that
61
- // will force them to be disabled. We can't use the `preview` edit mode in this case since it'll force
62
- // the panel to be closed.
59
+ export type ClassManagerPanelEmbeddedProps = {
60
+ onRequestClose: () => void | Promise< void >;
61
+ onExposeCloseAttempt?: ( attemptClose: ( () => void ) | null ) => void;
62
+ };
63
+
64
+ export function ClassManagerPanelEmbedded( { onRequestClose, onExposeCloseAttempt }: ClassManagerPanelEmbeddedProps ) {
65
+ return (
66
+ <ClassManagerPanelRoot
67
+ embedded
68
+ onRequestClose={ onRequestClose }
69
+ onExposeCloseAttempt={ onExposeCloseAttempt }
70
+ />
71
+ );
72
+ }
73
+
74
+ export function ClassManagerPanel() {
75
+ return <ClassManagerPanelRoot />;
76
+ }
77
+
63
78
  export const { panel, usePanelActions } = createPanel( {
64
79
  id,
65
80
  component: ClassManagerPanel,
@@ -77,9 +92,24 @@ export const { panel, usePanelActions } = createPanel( {
77
92
  isOpenPreviousElement: true,
78
93
  } );
79
94
 
80
- export function ClassManagerPanel() {
95
+ type ClassManagerPanelRootProps = {
96
+ embedded?: boolean;
97
+ onRequestClose?: () => void | Promise< void >;
98
+ onExposeCloseAttempt?: ( attemptClose: ( () => void ) | null ) => void;
99
+ };
100
+
101
+ function ClassManagerPanelRoot( {
102
+ embedded = false,
103
+ onRequestClose,
104
+ onExposeCloseAttempt,
105
+ }: ClassManagerPanelRootProps = {} ) {
81
106
  const isDirty = useDirtyState();
82
- const { close: closePanel } = usePanelActions();
107
+ const { close: closeStandalonePanel } = usePanelActions();
108
+ const closePanel = useMemo(
109
+ () => ( embedded ? onRequestClose ?? ( async () => {} ) : closeStandalonePanel ),
110
+ [ embedded, onRequestClose, closeStandalonePanel ]
111
+ );
112
+
83
113
  const { open: openSaveChangesDialog, close: closeSaveChangesDialog, isOpen: isSaveChangesDialogOpen } = useDialog();
84
114
  const [ stopSyncConfirmation, setStopSyncConfirmation ] = useState< string | null >( null );
85
115
  const [ startSyncConfirmation, setStartSyncConfirmation ] = useState< string | null >( null );
@@ -93,6 +123,37 @@ export function ClassManagerPanel() {
93
123
  closeSaveChangesDialog();
94
124
  };
95
125
 
126
+ const handleClosePanel = useCallback( () => {
127
+ if ( isDirty ) {
128
+ openSaveChangesDialog();
129
+ return;
130
+ }
131
+
132
+ void closePanel();
133
+ }, [ isDirty, openSaveChangesDialog, closePanel ] );
134
+
135
+ useEffect( () => {
136
+ if ( ! embedded || ! onExposeCloseAttempt ) {
137
+ return;
138
+ }
139
+
140
+ onExposeCloseAttempt( () => handleClosePanel() );
141
+
142
+ return () => onExposeCloseAttempt( null );
143
+ }, [ embedded, onExposeCloseAttempt, handleClosePanel ] );
144
+
145
+ useEffect( () => {
146
+ if ( ! embedded ) {
147
+ return;
148
+ }
149
+
150
+ blockPanelInteractions();
151
+
152
+ return () => {
153
+ unblockPanelInteractions();
154
+ };
155
+ }, [ embedded ] );
156
+
96
157
  const handleStopSync = useCallback( ( classId: string ) => {
97
158
  dispatch(
98
159
  slice.actions.update( {
@@ -132,85 +193,56 @@ export function ClassManagerPanel() {
132
193
 
133
194
  usePreventUnload();
134
195
 
135
- return (
136
- <ThemeProvider>
137
- <ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
138
- <Panel>
139
- <SearchAndFilterProvider>
140
- <PanelHeader>
141
- <Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
142
- <Stack width="100%" direction="row" gap={ 1 }>
143
- <PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
144
- <FlippedColorSwatchIcon fontSize="inherit" />
145
- { __( 'Class Manager', 'elementor' ) }
146
- </PanelHeaderTitle>
147
- <TotalCssClassCounter />
148
- </Stack>
149
- <CloseButton
150
- sx={ { marginLeft: 'auto' } }
151
- disabled={ isPublishing }
152
- onClose={ () => {
153
- if ( isDirty ) {
154
- openSaveChangesDialog();
155
- return;
156
- }
157
-
158
- closePanel();
159
- } }
160
- />
161
- </Stack>
162
- </PanelHeader>
163
- <PanelBody
164
- sx={ {
165
- display: 'flex',
166
- flexDirection: 'column',
167
- height: '100%',
168
- } }
169
- >
170
- <Box px={ 2 } pb={ 1 }>
171
- <Stack direction="row" justifyContent="spaceBetween" gap={ 0.5 } sx={ { pb: 0.5 } }>
172
- <Box sx={ { flexGrow: 1 } }>
173
- <ClassManagerSearch />
174
- </Box>
175
- <CssClassFilter />
176
- </Stack>
177
- <ActiveFilters />
178
- </Box>
179
- <Divider />
180
- <Box
181
- ref={ setScrollElement }
182
- px={ 2 }
183
- sx={ {
184
- flexGrow: 1,
185
- overflowY: 'auto',
186
- } }
187
- >
188
- <GlobalClassesList
189
- disabled={ isPublishing }
190
- scrollElement={ scrollElement }
191
- onStopSyncRequest={ handleStopSyncRequest }
192
- onStartSyncRequest={ ( classId ) => setStartSyncConfirmation( classId ) }
193
- />
194
- </Box>
195
- </PanelBody>
196
-
197
- <PanelFooter>
198
- <Button
199
- fullWidth
200
- size="small"
201
- color="global"
202
- variant="contained"
203
- onClick={ publish }
204
- disabled={ ! isDirty }
205
- loading={ isPublishing }
206
- >
207
- { __( 'Save changes', 'elementor' ) }
208
- </Button>
209
- </PanelFooter>
210
- </SearchAndFilterProvider>
211
- </Panel>
212
- </ErrorBoundary>
213
- <ClassManagerIntroduction />
196
+ const searchFiltersBlock = (
197
+ <Box px={ 2 } pb={ 1 }>
198
+ <Stack direction="row" alignItems="center" justifyContent="space-between" gap={ 0.5 } sx={ { pb: 0.5 } }>
199
+ <Box sx={ embedded ? { flexGrow: 1, minWidth: 0 } : { flexGrow: 1 } }>
200
+ <ClassManagerSearch />
201
+ </Box>
202
+ <CssClassFilter />
203
+ { embedded && <TotalCssClassCounter /> }
204
+ </Stack>
205
+ <ActiveFilters />
206
+ </Box>
207
+ );
208
+
209
+ const listArea = (
210
+ <Box
211
+ ref={ setScrollElement }
212
+ px={ 2 }
213
+ sx={ {
214
+ flexGrow: 1,
215
+ overflowY: 'auto',
216
+ ...( embedded ? { minHeight: 0 } : {} ),
217
+ } }
218
+ >
219
+ <GlobalClassesList
220
+ disabled={ isPublishing }
221
+ scrollElement={ scrollElement }
222
+ onStopSyncRequest={ handleStopSyncRequest }
223
+ onStartSyncRequest={ ( classId ) => setStartSyncConfirmation( classId ) }
224
+ />
225
+ </Box>
226
+ );
227
+
228
+ const saveFooter = (
229
+ <PanelFooter>
230
+ <Button
231
+ fullWidth
232
+ size="small"
233
+ color="global"
234
+ variant="contained"
235
+ onClick={ publish }
236
+ disabled={ ! isDirty }
237
+ loading={ isPublishing }
238
+ >
239
+ { __( 'Save changes', 'elementor' ) }
240
+ </Button>
241
+ </PanelFooter>
242
+ );
243
+
244
+ const dialogs = (
245
+ <>
214
246
  { startSyncConfirmation && (
215
247
  <StartSyncToV3Modal
216
248
  externalOpen
@@ -254,19 +286,93 @@ export function ClassManagerPanel() {
254
286
  action: async () => {
255
287
  await publish();
256
288
  closeSaveChangesDialog();
257
- closePanel();
289
+ void closePanel();
258
290
  },
259
291
  },
260
292
  } }
261
293
  />
262
294
  </SaveChangesDialog>
263
295
  ) }
264
- </ThemeProvider>
296
+ </>
297
+ );
298
+
299
+ const classManagerLayout = embedded ? (
300
+ <Stack
301
+ direction="column"
302
+ sx={ {
303
+ height: '100%',
304
+ width: '100%',
305
+ flex: 1,
306
+ minHeight: 0,
307
+ overflow: 'hidden',
308
+ } }
309
+ >
310
+ { searchFiltersBlock }
311
+ <Divider />
312
+ { listArea }
313
+ { saveFooter }
314
+ </Stack>
315
+ ) : (
316
+ <Panel>
317
+ <PanelHeader>
318
+ <Stack p={ 1 } pl={ 2 } width="100%" direction="row" alignItems="center">
319
+ <Stack width="100%" direction="row" gap={ 1 }>
320
+ <PanelHeaderTitle sx={ { display: 'flex', alignItems: 'center', gap: 0.5 } }>
321
+ <FlippedColorSwatchIcon fontSize="inherit" />
322
+ { __( 'Class Manager', 'elementor' ) }
323
+ </PanelHeaderTitle>
324
+ <TotalCssClassCounter />
325
+ </Stack>
326
+ <ClassPanelCloseButton
327
+ disabled={ isPublishing }
328
+ onClose={ () => {
329
+ if ( isDirty ) {
330
+ openSaveChangesDialog();
331
+ return;
332
+ }
333
+
334
+ void closeStandalonePanel();
335
+ } }
336
+ />
337
+ </Stack>
338
+ </PanelHeader>
339
+ <PanelBody
340
+ sx={ {
341
+ display: 'flex',
342
+ flexDirection: 'column',
343
+ height: '100%',
344
+ } }
345
+ >
346
+ { searchFiltersBlock }
347
+ <Divider />
348
+ { listArea }
349
+ </PanelBody>
350
+ { saveFooter }
351
+ </Panel>
265
352
  );
353
+
354
+ const core = (
355
+ <>
356
+ <ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
357
+ <SearchAndFilterProvider>{ classManagerLayout }</SearchAndFilterProvider>
358
+ </ErrorBoundary>
359
+ <ClassManagerIntroduction />
360
+ { dialogs }
361
+ </>
362
+ );
363
+
364
+ return embedded ? core : <ThemeProvider>{ core }</ThemeProvider>;
266
365
  }
267
366
 
268
- const CloseButton = ( { onClose, ...props }: IconButtonProps & { onClose: () => void } ) => (
269
- <IconButton size="small" color="secondary" onClick={ onClose } aria-label="Close" { ...props }>
367
+ const ClassPanelCloseButton = ( { onClose, sx, ...props }: IconButtonProps & { onClose: () => void } ) => (
368
+ <IconButton
369
+ size="small"
370
+ color="secondary"
371
+ onClick={ onClose }
372
+ aria-label="Close"
373
+ sx={ { marginLeft: 'auto', ...sx } }
374
+ { ...props }
375
+ >
270
376
  <XIcon fontSize="small" />
271
377
  </IconButton>
272
378
  );
package/src/index.ts CHANGED
@@ -1,3 +1,7 @@
1
+ export {
2
+ ClassManagerPanelEmbedded,
3
+ type ClassManagerPanelEmbeddedProps,
4
+ } from './components/class-manager/class-manager-panel';
1
5
  export { GLOBAL_CLASSES_URI } from './mcp-integration/classes-resource';
2
6
 
3
7
  export { init } from './init';
package/src/init.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  import { getMCPByDomain } from '@elementor/editor-mcp';
8
8
  import { __registerPanel as registerPanel } from '@elementor/editor-panels';
9
9
  import { stylesRepository } from '@elementor/editor-styles-repository';
10
+ import { isExperimentActive } from '@elementor/editor-v1-adapters';
10
11
  import { __registerSlice as registerSlice } from '@elementor/store';
11
12
 
12
13
  import { ClassManagerButton } from './components/class-manager/class-manager-button';
@@ -23,7 +24,10 @@ import { SyncWithDocumentSave } from './sync-with-document';
23
24
 
24
25
  export function init() {
25
26
  registerSlice( slice );
26
- registerPanel( panel );
27
+
28
+ if ( ! isExperimentActive( 'e_editor_design_system_panel' ) ) {
29
+ registerPanel( panel );
30
+ }
27
31
 
28
32
  stylesRepository.register( globalClassesStylesProvider );
29
33
 
@@ -47,10 +51,12 @@ export function init() {
47
51
  component: PrefetchCssClassUsage,
48
52
  } );
49
53
 
50
- injectIntoLogic( {
51
- id: 'global-classes-open-panel-from-url',
52
- component: OpenPanelFromUrl,
53
- } );
54
+ if ( ! isExperimentActive( 'e_editor_design_system_panel' ) ) {
55
+ injectIntoLogic( {
56
+ id: 'global-classes-open-panel-from-url',
57
+ component: OpenPanelFromUrl,
58
+ } );
59
+ }
54
60
 
55
61
  injectIntoCssClassConvert( {
56
62
  id: 'global-classes-convert-from-local-class',
@@ -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 panelActions = usePanelActions();
8
+ const { open: openClassPanel } = usePanelActions();
9
9
 
10
10
  useEffect( () => {
11
- listenTo( v1ReadyEvent(), () => {
12
- syncWithDocumentSave( panelActions );
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
- // eslint-disable-next-line react-hooks/exhaustive-deps
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;