@elementor/editor-variables 4.0.0-527 → 4.0.0-528

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-variables",
3
- "version": "4.0.0-527",
3
+ "version": "4.0.0-528",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,22 +39,22 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.0.0-527",
43
- "@elementor/editor-canvas": "4.0.0-527",
44
- "@elementor/editor-controls": "4.0.0-527",
45
- "@elementor/editor-current-user": "4.0.0-527",
46
- "@elementor/editor-mcp": "4.0.0-527",
47
- "@elementor/editor-panels": "4.0.0-527",
48
- "@elementor/editor-props": "4.0.0-527",
49
- "@elementor/editor-ui": "4.0.0-527",
50
- "@elementor/editor-v1-adapters": "4.0.0-527",
51
- "@elementor/menus": "4.0.0-527",
52
- "@elementor/http-client": "4.0.0-527",
42
+ "@elementor/editor": "4.0.0-528",
43
+ "@elementor/editor-canvas": "4.0.0-528",
44
+ "@elementor/editor-controls": "4.0.0-528",
45
+ "@elementor/editor-current-user": "4.0.0-528",
46
+ "@elementor/editor-mcp": "4.0.0-528",
47
+ "@elementor/editor-panels": "4.0.0-528",
48
+ "@elementor/editor-props": "4.0.0-528",
49
+ "@elementor/editor-ui": "4.0.0-528",
50
+ "@elementor/editor-v1-adapters": "4.0.0-528",
51
+ "@elementor/menus": "4.0.0-528",
52
+ "@elementor/http-client": "4.0.0-528",
53
53
  "@elementor/icons": "^1.63.0",
54
- "@elementor/mixpanel": "4.0.0-527",
55
- "@elementor/schema": "4.0.0-527",
54
+ "@elementor/mixpanel": "4.0.0-528",
55
+ "@elementor/schema": "4.0.0-528",
56
56
  "@elementor/ui": "1.37.2",
57
- "@elementor/utils": "4.0.0-527",
57
+ "@elementor/utils": "4.0.0-528",
58
58
  "@wordpress/i18n": "^5.13.0"
59
59
  },
60
60
  "peerDependencies": {
package/src/api.ts CHANGED
@@ -19,6 +19,8 @@ export type BatchOperation = {
19
19
  type?: string;
20
20
  label?: string;
21
21
  value?: string;
22
+ order?: number;
23
+ sync_to_v3?: boolean;
22
24
  };
23
25
  label?: string;
24
26
  value?: string;
@@ -37,6 +37,7 @@ export const buildOperationsArray = (
37
37
  } );
38
38
  } else if ( originalVariables[ id ] ) {
39
39
  const original = originalVariables[ id ];
40
+ const syncChanged = original.sync_to_v3 !== variable.sync_to_v3;
40
41
 
41
42
  if ( original.deleted && ! variable.deleted ) {
42
43
  operations.push( {
@@ -49,7 +50,8 @@ export const buildOperationsArray = (
49
50
  ! variable.deleted &&
50
51
  ( original.label !== variable.label ||
51
52
  original.value !== variable.value ||
52
- original.order !== variable.order )
53
+ original.order !== variable.order ||
54
+ syncChanged )
53
55
  ) {
54
56
  operations.push( {
55
57
  type: 'update',
@@ -58,6 +60,7 @@ export const buildOperationsArray = (
58
60
  ...( original.label !== variable.label && { label: variable.label } ),
59
61
  ...( original.value !== variable.value && { value: variable.value } ),
60
62
  ...( original.order !== variable.order && { order: variable.order } ),
63
+ ...( syncChanged && { sync_to_v3: variable.sync_to_v3 } ),
61
64
  },
62
65
  } );
63
66
  }
@@ -0,0 +1,77 @@
1
+ import * as React from 'react';
2
+ import { useState } from 'react';
3
+ import { BrushIcon } from '@elementor/icons';
4
+ import {
5
+ Button,
6
+ Checkbox,
7
+ Dialog,
8
+ DialogActions,
9
+ DialogContent,
10
+ DialogContentText,
11
+ DialogTitle,
12
+ FormControlLabel,
13
+ Typography,
14
+ } from '@elementor/ui';
15
+ import { __ } from '@wordpress/i18n';
16
+
17
+ type StopSyncConfirmationDialogProps = {
18
+ open: boolean;
19
+ closeDialog: () => void;
20
+ onConfirm: () => void;
21
+ onSuppressMessage?: () => void;
22
+ };
23
+
24
+ export const StopSyncConfirmationDialog = ( {
25
+ open,
26
+ closeDialog,
27
+ onConfirm,
28
+ onSuppressMessage,
29
+ }: StopSyncConfirmationDialogProps ) => {
30
+ const [ dontShowAgain, setDontShowAgain ] = useState( false );
31
+
32
+ const handleConfirm = () => {
33
+ if ( dontShowAgain ) {
34
+ onSuppressMessage?.();
35
+ }
36
+ onConfirm();
37
+ };
38
+
39
+ return (
40
+ <Dialog open={ open } onClose={ closeDialog } maxWidth="xs">
41
+ <DialogTitle display="flex" alignItems="center" gap={ 1 }>
42
+ <BrushIcon color="secondary" />
43
+ { __( 'Stop syncing variable color', 'elementor' ) }
44
+ </DialogTitle>
45
+ <DialogContent>
46
+ <DialogContentText variant="body2" color="textPrimary">
47
+ { __(
48
+ 'This will disconnect the variable color from version 3. Existing uses on your site will automatically switch to a default color.',
49
+ 'elementor'
50
+ ) }
51
+ </DialogContentText>
52
+ </DialogContent>
53
+ <DialogActions sx={ { justifyContent: 'space-between', alignItems: 'center' } }>
54
+ <FormControlLabel
55
+ control={
56
+ <Checkbox
57
+ checked={ dontShowAgain }
58
+ onChange={ ( event: React.ChangeEvent< HTMLInputElement > ) =>
59
+ setDontShowAgain( event.target.checked )
60
+ }
61
+ size="small"
62
+ />
63
+ }
64
+ label={ <Typography variant="body2">{ __( "Don't show this again", 'elementor' ) }</Typography> }
65
+ />
66
+ <div>
67
+ <Button color="secondary" onClick={ closeDialog }>
68
+ { __( 'Cancel', 'elementor' ) }
69
+ </Button>
70
+ <Button variant="contained" color="secondary" onClick={ handleConfirm } sx={ { ml: 1 } }>
71
+ { __( 'Got it', 'elementor' ) }
72
+ </Button>
73
+ </div>
74
+ </DialogActions>
75
+ </Dialog>
76
+ );
77
+ };
@@ -45,6 +45,22 @@ export const useVariablesManagerState = () => {
45
45
  setIsDirty( true );
46
46
  }, [] );
47
47
 
48
+ const handleStartSync = useCallback( ( itemId: string ) => {
49
+ setVariables( ( prev ) => ( {
50
+ ...prev,
51
+ [ itemId ]: { ...prev[ itemId ], sync_to_v3: true },
52
+ } ) );
53
+ setIsDirty( true );
54
+ }, [] );
55
+
56
+ const handleStopSync = useCallback( ( itemId: string ) => {
57
+ setVariables( ( prev ) => ( {
58
+ ...prev,
59
+ [ itemId ]: { ...prev[ itemId ], sync_to_v3: false },
60
+ } ) );
61
+ setIsDirty( true );
62
+ }, [] );
63
+
48
64
  const handleSearch = ( searchTerm: string ) => {
49
65
  setSearchValue( searchTerm );
50
66
  };
@@ -82,6 +98,8 @@ export const useVariablesManagerState = () => {
82
98
  handleOnChange,
83
99
  createVariable,
84
100
  handleDeleteVariable,
101
+ handleStartSync,
102
+ handleStopSync,
85
103
  handleSave,
86
104
  isSaving,
87
105
  handleSearch,
@@ -26,10 +26,11 @@ import { __ } from '@wordpress/i18n';
26
26
 
27
27
  import { trackVariablesManagerEvent } from '../../utils/tracking';
28
28
  import { type ErrorResponse, type MappedError, mapServerError } from '../../utils/validations';
29
- import { getVariableType } from '../../variables-registry/variable-type-registry';
29
+ import { getMenuActionsForVariable, getVariableType } from '../../variables-registry/variable-type-registry';
30
30
  import { DeleteConfirmationDialog } from '../ui/delete-confirmation-dialog';
31
31
  import { EmptyState } from '../ui/empty-state';
32
32
  import { NoSearchResults } from '../ui/no-search-results';
33
+ import { StopSyncConfirmationDialog } from '../ui/stop-sync-confirmation-dialog';
33
34
  import { useAutoEdit } from './hooks/use-auto-edit';
34
35
  import { useErrorNavigation } from './hooks/use-error-navigation';
35
36
  import { useVariablesManagerState } from './hooks/use-variables-manager-state';
@@ -67,6 +68,8 @@ export function VariablesManagerPanel() {
67
68
  handleOnChange,
68
69
  createVariable,
69
70
  handleDeleteVariable,
71
+ handleStartSync,
72
+ handleStopSync,
70
73
  handleSave,
71
74
  isSaving,
72
75
  handleSearch,
@@ -78,6 +81,7 @@ export function VariablesManagerPanel() {
78
81
  const { createNavigationCallback, resetNavigation } = useErrorNavigation();
79
82
 
80
83
  const [ deleteConfirmation, setDeleteConfirmation ] = useState< { id: string; label: string } | null >( null );
84
+ const [ stopSyncConfirmation, setStopSyncConfirmation ] = useState< string | null >( null );
81
85
  const [ serverError, setServerError ] = useState< MappedError | null >( null );
82
86
 
83
87
  usePreventUnload( isDirty );
@@ -139,22 +143,49 @@ export function VariablesManagerPanel() {
139
143
  [ handleDeleteVariable ]
140
144
  );
141
145
 
142
- const menuActions = [
143
- {
144
- name: __( 'Delete', 'elementor' ),
145
- icon: TrashIcon,
146
- color: 'error.main',
147
- onClick: ( itemId: string ) => {
148
- const variable = variables[ itemId ];
149
- if ( variable ) {
150
- setDeleteConfirmation( { id: itemId, label: variable.label } );
151
-
152
- const variableTypeOptions = getVariableType( variable.type );
153
- trackVariablesManagerEvent( { action: 'delete', varType: variableTypeOptions?.variableType } );
154
- }
155
- },
146
+ const handleStopSyncWithConfirmation = useCallback(
147
+ ( itemId: string ) => {
148
+ handleStopSync( itemId );
149
+ setStopSyncConfirmation( null );
156
150
  },
157
- ];
151
+ [ handleStopSync ]
152
+ );
153
+
154
+ const buildMenuActions = useCallback(
155
+ ( variableId: string ) => {
156
+ const variable = variables[ variableId ];
157
+ if ( ! variable ) {
158
+ return [];
159
+ }
160
+
161
+ const typeActions = getMenuActionsForVariable( variable.type, {
162
+ variable,
163
+ variableId,
164
+ handlers: {
165
+ onStartSync: handleStartSync,
166
+ onStopSync: ( itemId: string ) => setStopSyncConfirmation( itemId ),
167
+ },
168
+ } );
169
+
170
+ const deleteAction = {
171
+ name: __( 'Delete', 'elementor' ),
172
+ icon: TrashIcon,
173
+ color: 'error.main',
174
+ onClick: ( itemId: string ) => {
175
+ const v = variables[ itemId ];
176
+ if ( v ) {
177
+ setDeleteConfirmation( { id: itemId, label: v.label } );
178
+
179
+ const variableTypeOptions = getVariableType( v.type );
180
+ trackVariablesManagerEvent( { action: 'delete', varType: variableTypeOptions?.variableType } );
181
+ }
182
+ },
183
+ };
184
+
185
+ return [ ...typeActions, deleteAction ];
186
+ },
187
+ [ variables, handleStartSync ]
188
+ );
158
189
 
159
190
  const hasVariables = Object.keys( variables ).length > 0;
160
191
 
@@ -212,7 +243,7 @@ export function VariablesManagerPanel() {
212
243
  >
213
244
  { hasVariables && (
214
245
  <VariablesManagerTable
215
- menuActions={ menuActions }
246
+ menuActions={ buildMenuActions }
216
247
  variables={ variables }
217
248
  onChange={ handleOnChange }
218
249
  autoEditVariableId={ autoEditVariableId }
@@ -314,6 +345,14 @@ export function VariablesManagerPanel() {
314
345
  />
315
346
  ) }
316
347
 
348
+ { stopSyncConfirmation && (
349
+ <StopSyncConfirmationDialog
350
+ open
351
+ closeDialog={ () => setStopSyncConfirmation( null ) }
352
+ onConfirm={ () => handleStopSyncWithConfirmation( stopSyncConfirmation ) }
353
+ />
354
+ ) }
355
+
317
356
  { isSaveChangesDialogOpen && (
318
357
  <SaveChangesDialog>
319
358
  <SaveChangesDialog.Title onClose={ closeSaveChangesDialog }>
@@ -25,7 +25,7 @@ import { VariableTableCell } from './ui/variable-table-cell';
25
25
  import { VariableEditableCell } from './variable-editable-cell';
26
26
 
27
27
  type Props = {
28
- menuActions: VariableManagerMenuAction[];
28
+ menuActions: ( variableId: string ) => VariableManagerMenuAction[];
29
29
  variables: TVariablesList;
30
30
  onChange: ( variables: TVariablesList ) => void;
31
31
  autoEditVariableId?: string;
@@ -306,7 +306,7 @@ export const VariablesManagerTable = ( {
306
306
  >
307
307
  <Stack role="toolbar" direction="row" justifyContent="flex-end">
308
308
  <VariableEditMenu
309
- menuActions={ menuActions }
309
+ menuActions={ menuActions( row.id ) }
310
310
  disabled={ isSorting }
311
311
  itemId={ row.id }
312
312
  />
package/src/index.ts CHANGED
@@ -3,7 +3,13 @@ export { GLOBAL_VARIABLES_URI } from './mcp/variables-resource';
3
3
  export { sizeVariablePropTypeUtil } from './prop-types/size-variable-prop-type';
4
4
  export { registerVariableTypes } from './register-variable-types';
5
5
  export { service } from './service';
6
- export { registerVariableType } from './variables-registry/variable-type-registry';
6
+ export {
7
+ getMenuActionsForVariable,
8
+ registerVariableType,
9
+ type MenuActionContext,
10
+ type MenuActionsFactory,
11
+ type VariableManagerMenuAction,
12
+ } from './variables-registry/variable-type-registry';
7
13
  export { hasVariable } from './hooks/use-prop-variables';
8
14
 
9
15
  import { globalVariablesLLMResolvers } from './utils/llm-propvalue-label-resolver';
@@ -1,7 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { colorPropTypeUtil, sizePropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
3
3
  import { CtaButton } from '@elementor/editor-ui';
4
- import { BrushIcon, ExpandDiagonalIcon, TextIcon } from '@elementor/icons';
4
+ import { isExperimentActive } from '@elementor/editor-v1-adapters';
5
+ import { BrushIcon, ExpandDiagonalIcon, ResetIcon, TextIcon } from '@elementor/icons';
6
+ import { __ } from '@wordpress/i18n';
5
7
 
6
8
  import { ColorField } from './components/fields/color-field';
7
9
  import { FontField } from './components/fields/font-field';
@@ -22,6 +24,31 @@ export function registerVariableTypes() {
22
24
  variableType: 'color',
23
25
  startIcon: ( { value } ) => <ColorIndicator size="inherit" component="span" value={ value } />,
24
26
  defaultValue: '#ffffff',
27
+ menuActionsFactory: ( { variable, variableId, handlers } ) => {
28
+ const actions = [];
29
+
30
+ if ( ! isExperimentActive( 'e_design_system_sync' ) ) {
31
+ return [];
32
+ }
33
+
34
+ if ( variable.sync_to_v3 ) {
35
+ actions.push( {
36
+ name: __( 'Stop syncing to Version 3', 'elementor' ),
37
+ icon: ResetIcon,
38
+ color: 'text.primary',
39
+ onClick: () => handlers.onStopSync( variableId ),
40
+ } );
41
+ } else {
42
+ actions.push( {
43
+ name: __( 'Sync to Version 3', 'elementor' ),
44
+ icon: ResetIcon,
45
+ color: 'text.primary',
46
+ onClick: () => handlers.onStartSync( variableId ),
47
+ } );
48
+ }
49
+
50
+ return actions;
51
+ },
25
52
  } );
26
53
 
27
54
  registerVariableType( {
package/src/storage.ts CHANGED
@@ -5,6 +5,7 @@ export type TVariable = {
5
5
  order?: number;
6
6
  deleted?: boolean;
7
7
  deleted_at?: string;
8
+ sync_to_v3?: boolean;
8
9
  };
9
10
 
10
11
  export type TVariablesList = Record< string, TVariable >;
package/src/types.ts CHANGED
@@ -8,6 +8,7 @@ export type Variable = {
8
8
  type: string;
9
9
  deleted?: boolean;
10
10
  deleted_at?: string;
11
+ sync_to_v3?: boolean;
11
12
  };
12
13
 
13
14
  export type StyleVariables = Record< string, Variable >;
@@ -23,4 +24,5 @@ export type NormalizedVariable = {
23
24
  label: string;
24
25
  value: string;
25
26
  order?: number;
27
+ sync_to_v3?: boolean;
26
28
  };
@@ -8,11 +8,18 @@ export const variablesToList = ( variables: TVariablesList ): VariableWithKey[]
8
8
  return Object.entries( variables ).map( ( [ key, variable ] ) => ( { key, ...variable } ) );
9
9
  };
10
10
 
11
- export const toNormalizedVariable = ( { key, label, value, order }: VariableWithKey ): NormalizedVariable => ( {
11
+ export const toNormalizedVariable = ( {
12
12
  key,
13
13
  label,
14
14
  value,
15
15
  order,
16
+ sync_to_v3: syncToV3,
17
+ }: VariableWithKey ): NormalizedVariable => ( {
18
+ key,
19
+ label,
20
+ value,
21
+ order,
22
+ sync_to_v3: syncToV3,
16
23
  } );
17
24
 
18
25
  type VariableWithType = NormalizedVariable & { type: string };
@@ -13,10 +13,22 @@ import {
13
13
  } from '@elementor/editor-props';
14
14
  import { type SvgIconProps } from '@elementor/ui';
15
15
 
16
+ import { type VariableManagerMenuAction } from '../components/variables-manager/ui/variable-edit-menu';
16
17
  import { inheritanceTransformer } from '../transformers/inheritance-transformer';
17
18
  import { variableTransformer } from '../transformers/variable-transformer';
18
19
  import { type NormalizedVariable, type Variable } from '../types';
19
20
 
21
+ export type MenuActionContext = {
22
+ variable: Variable;
23
+ variableId: string;
24
+ handlers: {
25
+ onStartSync: ( id: string ) => void;
26
+ onStopSync: ( id: string ) => void;
27
+ };
28
+ };
29
+
30
+ export type MenuActionsFactory = ( context: MenuActionContext ) => VariableManagerMenuAction[];
31
+
20
32
  export type ValueFieldProps = {
21
33
  value: string;
22
34
  onChange: ( value: string ) => void;
@@ -45,6 +57,7 @@ type VariableTypeOptions = {
45
57
  isCompatible?: ( propType: PropType, variable: Variable ) => boolean;
46
58
  emptyState?: JSX.Element;
47
59
  isActive?: boolean;
60
+ menuActionsFactory?: MenuActionsFactory;
48
61
  };
49
62
 
50
63
  export type VariableTypesMap = Record< string, Omit< VariableTypeOptions, 'key' > >;
@@ -67,6 +80,7 @@ export function createVariableTypeRegistry() {
67
80
  isCompatible,
68
81
  emptyState,
69
82
  isActive = true,
83
+ menuActionsFactory,
70
84
  }: VariableTypeOptions ) => {
71
85
  const variableTypeKey = key ?? propTypeUtil.key;
72
86
 
@@ -94,6 +108,7 @@ export function createVariableTypeRegistry() {
94
108
  isCompatible,
95
109
  emptyState,
96
110
  isActive,
111
+ menuActionsFactory,
97
112
  };
98
113
 
99
114
  registerTransformer( propTypeUtil.key, styleTransformer );
@@ -1,4 +1,22 @@
1
- import { createVariableTypeRegistry } from './create-variable-type-registry';
1
+ import { type VariableManagerMenuAction } from '../components/variables-manager/ui/variable-edit-menu';
2
+ import {
3
+ createVariableTypeRegistry,
4
+ type MenuActionContext,
5
+ type MenuActionsFactory,
6
+ } from './create-variable-type-registry';
2
7
 
3
8
  export const { registerVariableType, getVariableType, getVariableTypes, hasVariableType } =
4
9
  createVariableTypeRegistry();
10
+
11
+ export function getMenuActionsForVariable(
12
+ variableType: string,
13
+ context: MenuActionContext
14
+ ): VariableManagerMenuAction[] {
15
+ const typeOptions = getVariableType( variableType );
16
+ if ( typeOptions?.menuActionsFactory ) {
17
+ return typeOptions.menuActionsFactory( context );
18
+ }
19
+ return [];
20
+ }
21
+
22
+ export type { MenuActionContext, MenuActionsFactory, VariableManagerMenuAction };