@elementor/editor-global-classes 4.0.0-564 → 4.0.0-591

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.0.0-564",
3
+ "version": "4.0.0-591",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,28 +39,28 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.0.0-564",
43
- "@elementor/editor-current-user": "4.0.0-564",
44
- "@elementor/editor-documents": "4.0.0-564",
45
- "@elementor/editor-editing-panel": "4.0.0-564",
46
- "@elementor/editor-mcp": "4.0.0-564",
47
- "@elementor/editor-panels": "4.0.0-564",
48
- "@elementor/editor-props": "4.0.0-564",
49
- "@elementor/editor-variables": "4.0.0-564",
50
- "@elementor/editor-styles": "4.0.0-564",
51
- "@elementor/editor-canvas": "4.0.0-564",
52
- "@elementor/editor-styles-repository": "4.0.0-564",
53
- "@elementor/editor-ui": "4.0.0-564",
54
- "@elementor/editor-v1-adapters": "4.0.0-564",
55
- "@elementor/http-client": "4.0.0-564",
42
+ "@elementor/editor": "4.0.0-591",
43
+ "@elementor/editor-current-user": "4.0.0-591",
44
+ "@elementor/editor-documents": "4.0.0-591",
45
+ "@elementor/editor-editing-panel": "4.0.0-591",
46
+ "@elementor/editor-mcp": "4.0.0-591",
47
+ "@elementor/editor-panels": "4.0.0-591",
48
+ "@elementor/editor-props": "4.0.0-591",
49
+ "@elementor/editor-variables": "4.0.0-591",
50
+ "@elementor/editor-styles": "4.0.0-591",
51
+ "@elementor/editor-canvas": "4.0.0-591",
52
+ "@elementor/editor-styles-repository": "4.0.0-591",
53
+ "@elementor/editor-ui": "4.0.0-591",
54
+ "@elementor/editor-v1-adapters": "4.0.0-591",
55
+ "@elementor/http-client": "4.0.0-591",
56
56
  "@elementor/icons": "^1.63.0",
57
- "@elementor/query": "4.0.0-564",
58
- "@elementor/schema": "4.0.0-564",
59
- "@elementor/store": "4.0.0-564",
57
+ "@elementor/query": "4.0.0-591",
58
+ "@elementor/schema": "4.0.0-591",
59
+ "@elementor/store": "4.0.0-591",
60
60
  "@elementor/ui": "1.36.17",
61
- "@elementor/utils": "4.0.0-564",
61
+ "@elementor/utils": "4.0.0-591",
62
62
  "@wordpress/i18n": "^5.13.0",
63
- "@elementor/events": "4.0.0-564"
63
+ "@elementor/events": "4.0.0-591"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "react": "^18.3.1",
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import { useRef, useState } from 'react';
3
3
  import { validateStyleLabel } from '@elementor/editor-styles-repository';
4
4
  import { EditableField, EllipsisWithTooltip, MenuListItem, useEditable, WarningInfotip } from '@elementor/editor-ui';
5
+ import { isExperimentActive } from '@elementor/editor-v1-adapters';
5
6
  import { DotsVerticalIcon } from '@elementor/icons';
6
7
  import {
7
8
  bindMenu,
@@ -32,6 +33,8 @@ type ClassItemProps = React.PropsWithChildren< {
32
33
  disabled?: boolean;
33
34
  sortableTriggerProps: SortableTriggerProps;
34
35
  showSortIndicator?: boolean;
36
+ syncToV3?: boolean;
37
+ onToggleSync?: ( id: string, newValue: boolean ) => void;
35
38
  } >;
36
39
 
37
40
  export const ClassItem = ( {
@@ -42,6 +45,8 @@ export const ClassItem = ( {
42
45
  disabled,
43
46
  sortableTriggerProps,
44
47
  showSortIndicator,
48
+ syncToV3,
49
+ onToggleSync,
45
50
  }: ClassItemProps ) => {
46
51
  const itemRef = useRef< HTMLElement >( null );
47
52
  const {
@@ -137,6 +142,20 @@ export const ClassItem = ( {
137
142
  { __( 'Rename', 'elementor' ) }
138
143
  </Typography>
139
144
  </MenuListItem>
145
+ { isExperimentActive( 'e_design_system_sync' ) && onToggleSync && (
146
+ <MenuListItem
147
+ onClick={ () => {
148
+ popupState.close();
149
+ onToggleSync( id, ! syncToV3 );
150
+ } }
151
+ >
152
+ <Typography variant="caption" sx={ { color: 'text.primary' } }>
153
+ { syncToV3
154
+ ? __( 'Stop syncing to Version 3', 'elementor' )
155
+ : __( 'Sync to Version 3', 'elementor' ) }
156
+ </Typography>
157
+ </MenuListItem>
158
+ ) }
140
159
  <MenuListItem
141
160
  onClick={ () => {
142
161
  popupState.close();
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
- import { useEffect } from 'react';
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { useSuppressedMessage } from '@elementor/editor-current-user';
3
4
  import { getCurrentDocument, getV1DocumentsManager, setDocumentModifiedStatus } from '@elementor/editor-documents';
4
5
  import {
5
6
  __createPanel as createPanel,
@@ -9,7 +10,7 @@ import {
9
10
  PanelHeader,
10
11
  PanelHeaderTitle,
11
12
  } from '@elementor/editor-panels';
12
- import { SaveChangesDialog, ThemeProvider, useDialog } from '@elementor/editor-ui';
13
+ import { ConfirmationDialog, SaveChangesDialog, ThemeProvider, useDialog } from '@elementor/editor-ui';
13
14
  import { __privateRunCommand as runCommand, changeEditMode } from '@elementor/editor-v1-adapters';
14
15
  import { XIcon } from '@elementor/icons';
15
16
  import { useMutation } from '@elementor/query';
@@ -42,6 +43,15 @@ import { hasDeletedItems, onDelete } from './delete-class';
42
43
  import { FlippedColorSwatchIcon } from './flipped-color-swatch-icon';
43
44
  import { GlobalClassesList } from './global-classes-list';
44
45
  import { blockPanelInteractions, unblockPanelInteractions } from './panel-interactions';
46
+ import { StartSyncToV3Modal } from './start-sync-to-v3-modal';
47
+
48
+ const STOP_SYNC_MESSAGE_KEY = 'stop-sync-class';
49
+
50
+ type StopSyncConfirmationDialogProps = {
51
+ open: boolean;
52
+ onClose: () => void;
53
+ onConfirm: () => void;
54
+ };
45
55
 
46
56
  const id = 'global-classes-manager';
47
57
 
@@ -83,6 +93,9 @@ export function ClassManagerPanel() {
83
93
  const isDirty = useDirtyState();
84
94
  const { close: closePanel } = usePanelActions();
85
95
  const { open: openSaveChangesDialog, close: closeSaveChangesDialog, isOpen: isSaveChangesDialogOpen } = useDialog();
96
+ const [ stopSyncConfirmation, setStopSyncConfirmation ] = useState< string | null >( null );
97
+ const [ startSyncConfirmation, setStartSyncConfirmation ] = useState< string | null >( null );
98
+ const [ isStopSyncSuppressed ] = useSuppressedMessage( STOP_SYNC_MESSAGE_KEY );
86
99
 
87
100
  const { mutateAsync: publish, isPending: isPublishing } = usePublish();
88
101
 
@@ -91,6 +104,41 @@ export function ClassManagerPanel() {
91
104
  closeSaveChangesDialog();
92
105
  };
93
106
 
107
+ const handleStopSync = useCallback( ( classId: string ) => {
108
+ dispatch(
109
+ slice.actions.update( {
110
+ style: {
111
+ id: classId,
112
+ sync_to_v3: false,
113
+ },
114
+ } )
115
+ );
116
+ setStopSyncConfirmation( null );
117
+ }, [] );
118
+
119
+ const handleStartSync = useCallback( ( classId: string ) => {
120
+ dispatch(
121
+ slice.actions.update( {
122
+ style: {
123
+ id: classId,
124
+ sync_to_v3: true,
125
+ },
126
+ } )
127
+ );
128
+ setStartSyncConfirmation( null );
129
+ }, [] );
130
+
131
+ const handleStopSyncRequest = useCallback(
132
+ ( classId: string ) => {
133
+ if ( ! isStopSyncSuppressed ) {
134
+ setStopSyncConfirmation( classId );
135
+ } else {
136
+ handleStopSync( classId );
137
+ }
138
+ },
139
+ [ isStopSyncSuppressed, handleStopSync ]
140
+ );
141
+
94
142
  usePreventUnload();
95
143
 
96
144
  return (
@@ -145,7 +193,11 @@ export function ClassManagerPanel() {
145
193
  overflowY: 'auto',
146
194
  } }
147
195
  >
148
- <GlobalClassesList disabled={ isPublishing } />
196
+ <GlobalClassesList
197
+ disabled={ isPublishing }
198
+ onStopSyncRequest={ handleStopSyncRequest }
199
+ onStartSyncRequest={ ( classId ) => setStartSyncConfirmation( classId ) }
200
+ />
149
201
  </Box>
150
202
  </PanelBody>
151
203
 
@@ -166,6 +218,20 @@ export function ClassManagerPanel() {
166
218
  </Panel>
167
219
  </ErrorBoundary>
168
220
  <ClassManagerIntroduction />
221
+ { startSyncConfirmation && (
222
+ <StartSyncToV3Modal
223
+ externalOpen
224
+ onExternalClose={ () => setStartSyncConfirmation( null ) }
225
+ onConfirm={ () => handleStartSync( startSyncConfirmation ) }
226
+ />
227
+ ) }
228
+ { stopSyncConfirmation && (
229
+ <StopSyncConfirmationDialog
230
+ open
231
+ onClose={ () => setStopSyncConfirmation( null ) }
232
+ onConfirm={ () => handleStopSync( stopSyncConfirmation ) }
233
+ />
234
+ ) }
169
235
  { isSaveChangesDialogOpen && (
170
236
  <SaveChangesDialog>
171
237
  <DialogHeader onClose={ closeSaveChangesDialog } logo={ false }>
@@ -261,3 +327,35 @@ const TotalCssClassCounter = () => {
261
327
  />
262
328
  );
263
329
  };
330
+
331
+ const StopSyncConfirmationDialog = ( { open, onClose, onConfirm }: StopSyncConfirmationDialogProps ) => {
332
+ const [ , suppressStopSyncMessage ] = useSuppressedMessage( STOP_SYNC_MESSAGE_KEY );
333
+
334
+ return (
335
+ <ConfirmationDialog open={ open } onClose={ onClose }>
336
+ <ConfirmationDialog.Title icon={ FlippedColorSwatchIcon } iconColor="primary">
337
+ { __( 'Un-sync typography class', 'elementor' ) }
338
+ </ConfirmationDialog.Title>
339
+ <ConfirmationDialog.Content>
340
+ <ConfirmationDialog.ContentText>
341
+ { __( "You're about to stop syncing a typography class to version 3.", 'elementor' ) }
342
+ </ConfirmationDialog.ContentText>
343
+ <ConfirmationDialog.ContentText sx={ { mt: 1 } }>
344
+ { __(
345
+ "Note that if it's being used anywhere, the affected elements will inherit the default typography.",
346
+ 'elementor'
347
+ ) }
348
+ </ConfirmationDialog.ContentText>
349
+ </ConfirmationDialog.Content>
350
+ <ConfirmationDialog.Actions
351
+ onClose={ onClose }
352
+ onConfirm={ onConfirm }
353
+ cancelLabel={ __( 'Cancel', 'elementor' ) }
354
+ confirmLabel={ __( 'Got it', 'elementor' ) }
355
+ color="primary"
356
+ onSuppressMessage={ suppressStopSyncMessage }
357
+ suppressLabel={ __( "Don't show again", 'elementor' ) }
358
+ />
359
+ </ConfirmationDialog>
360
+ );
361
+ };
@@ -19,9 +19,11 @@ import { SortableItem, SortableProvider } from './sortable';
19
19
 
20
20
  type GlobalClassesListProps = {
21
21
  disabled?: boolean;
22
+ onStopSyncRequest?: ( id: string ) => void;
23
+ onStartSyncRequest?: ( id: string ) => void;
22
24
  };
23
25
 
24
- export const GlobalClassesList = ( { disabled }: GlobalClassesListProps ) => {
26
+ export const GlobalClassesList = ( { disabled, onStopSyncRequest, onStartSyncRequest }: GlobalClassesListProps ) => {
25
27
  const {
26
28
  search: { debouncedValue: searchValue },
27
29
  } = useSearchAndFilters();
@@ -73,28 +75,28 @@ export const GlobalClassesList = ( { disabled }: GlobalClassesListProps ) => {
73
75
  onChange={ reorderClasses }
74
76
  disableDragOverlay={ ! allowSorting }
75
77
  >
76
- { filteredCssClasses?.map( ( { id, label } ) => (
77
- <SortableItem key={ id } id={ id }>
78
+ { filteredCssClasses?.map( ( cssClass ) => (
79
+ <SortableItem key={ cssClass.id } id={ cssClass.id }>
78
80
  { ( { isDragged, isDragPlaceholder, triggerProps, triggerStyle } ) => {
79
81
  if ( isDragged && ! draggedItemId ) {
80
- setDraggedItemId( id );
82
+ setDraggedItemId( cssClass.id );
81
83
  }
82
84
  return (
83
85
  <ClassItem
84
- id={ id }
85
- label={ label }
86
+ id={ cssClass.id }
87
+ label={ cssClass.label }
86
88
  renameClass={ ( newLabel: string ) => {
87
89
  trackGlobalClasses( {
88
90
  event: 'classRenamed',
89
- classId: id,
90
- oldValue: label,
91
+ classId: cssClass.id,
92
+ oldValue: cssClass.label,
91
93
  newValue: newLabel,
92
94
  source: 'class-manager',
93
95
  } );
94
96
  dispatch(
95
97
  slice.actions.update( {
96
98
  style: {
97
- id,
99
+ id: cssClass.id,
98
100
  label: newLabel,
99
101
  },
100
102
  } )
@@ -107,6 +109,23 @@ export const GlobalClassesList = ( { disabled }: GlobalClassesListProps ) => {
107
109
  style: triggerStyle,
108
110
  } }
109
111
  showSortIndicator={ allowSorting }
112
+ syncToV3={ cssClass.sync_to_v3 }
113
+ onToggleSync={ ( id, newValue ) => {
114
+ if ( ! newValue && onStopSyncRequest ) {
115
+ onStopSyncRequest( id );
116
+ } else if ( newValue && onStartSyncRequest ) {
117
+ onStartSyncRequest( id );
118
+ } else {
119
+ dispatch(
120
+ slice.actions.update( {
121
+ style: {
122
+ id,
123
+ sync_to_v3: newValue,
124
+ },
125
+ } )
126
+ );
127
+ }
128
+ } }
110
129
  />
111
130
  );
112
131
  } }
@@ -0,0 +1,76 @@
1
+ import * as React from 'react';
2
+ import { useState } from 'react';
3
+ import {
4
+ Box,
5
+ Button,
6
+ Checkbox,
7
+ Dialog,
8
+ DialogActions,
9
+ DialogContent,
10
+ FormControlLabel,
11
+ Typography,
12
+ } from '@elementor/ui';
13
+ import { __ } from '@wordpress/i18n';
14
+
15
+ const IMAGE_URL = 'https://assets.elementor.com/packages/v1/images/class-manager-sync-modal.png';
16
+
17
+ type StartSyncToV3ModalProps = {
18
+ externalOpen?: boolean;
19
+ onExternalClose?: () => void;
20
+ onConfirm?: () => void;
21
+ };
22
+
23
+ export const StartSyncToV3Modal = ( { externalOpen, onExternalClose, onConfirm }: StartSyncToV3ModalProps = {} ) => {
24
+ const [ shouldShowAgain, setShouldShowAgain ] = useState( true );
25
+
26
+ const handleClose = () => {
27
+ onExternalClose?.();
28
+ };
29
+
30
+ const handleConfirm = () => {
31
+ onConfirm?.();
32
+ onExternalClose?.();
33
+ };
34
+
35
+ return (
36
+ <Dialog open={ !! externalOpen } onClose={ handleClose } maxWidth="sm" fullWidth>
37
+ <DialogContent sx={ { p: 0 } }>
38
+ <Box component="img" src={ IMAGE_URL } alt="" sx={ { width: '100%', display: 'block' } } />
39
+ <Box sx={ { px: 3, pt: 4, pb: 1 } }>
40
+ <Typography variant="h6">{ __( 'Sync class to version 3 Global Fonts', 'elementor' ) }</Typography>
41
+ <Typography variant="body2" color="secondary" sx={ { mb: 2, pt: 1 } }>
42
+ { __(
43
+ 'Only typography settings supported in version 3 will be applied, including: font family, responsive font sizes, weight, text transform, decoration, line height, letter spacing, and word spacing. Changes made in the class will automatically apply to version 3.',
44
+ 'elementor'
45
+ ) }
46
+ </Typography>
47
+ </Box>
48
+ </DialogContent>
49
+ <DialogActions sx={ { justifyContent: 'space-between', px: 3, pb: 2 } }>
50
+ <FormControlLabel
51
+ control={
52
+ <Checkbox
53
+ checked={ ! shouldShowAgain }
54
+ onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) =>
55
+ setShouldShowAgain( ! e.target.checked )
56
+ }
57
+ />
58
+ }
59
+ label={
60
+ <Typography variant="body2" color="secondary">
61
+ { __( "Don't show again", 'elementor' ) }
62
+ </Typography>
63
+ }
64
+ />
65
+ <Box sx={ { display: 'flex', gap: 1 } }>
66
+ <Button onClick={ handleClose } color="secondary" size="small">
67
+ { __( 'Cancel', 'elementor' ) }
68
+ </Button>
69
+ <Button onClick={ handleConfirm } variant="contained" size="small">
70
+ { __( 'Sync to version 3', 'elementor' ) }
71
+ </Button>
72
+ </Box>
73
+ </DialogActions>
74
+ </Dialog>
75
+ );
76
+ };