@elementor/editor-variables 3.35.0-343 → 3.35.0-345

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": "3.35.0-343",
3
+ "version": "3.35.0-345",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,20 +39,20 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "3.35.0-343",
43
- "@elementor/editor-canvas": "3.35.0-343",
44
- "@elementor/editor-controls": "3.35.0-343",
45
- "@elementor/editor-current-user": "3.35.0-343",
46
- "@elementor/editor-editing-panel": "3.35.0-343",
47
- "@elementor/editor-mcp": "3.35.0-343",
48
- "@elementor/editor-panels": "3.35.0-343",
49
- "@elementor/editor-props": "3.35.0-343",
50
- "@elementor/editor-ui": "3.35.0-343",
51
- "@elementor/editor-v1-adapters": "3.35.0-343",
52
- "@elementor/http-client": "3.35.0-343",
42
+ "@elementor/editor": "3.35.0-345",
43
+ "@elementor/editor-canvas": "3.35.0-345",
44
+ "@elementor/editor-controls": "3.35.0-345",
45
+ "@elementor/editor-current-user": "3.35.0-345",
46
+ "@elementor/editor-editing-panel": "3.35.0-345",
47
+ "@elementor/editor-mcp": "3.35.0-345",
48
+ "@elementor/editor-panels": "3.35.0-345",
49
+ "@elementor/editor-props": "3.35.0-345",
50
+ "@elementor/editor-ui": "3.35.0-345",
51
+ "@elementor/editor-v1-adapters": "3.35.0-345",
52
+ "@elementor/http-client": "3.35.0-345",
53
53
  "@elementor/icons": "^1.62.0",
54
- "@elementor/mixpanel": "3.35.0-343",
55
- "@elementor/schema": "3.35.0-343",
54
+ "@elementor/mixpanel": "3.35.0-345",
55
+ "@elementor/schema": "3.35.0-345",
56
56
  "@elementor/ui": "1.36.17",
57
57
  "@wordpress/i18n": "^5.13.0"
58
58
  },
@@ -9,11 +9,36 @@ type Props = {
9
9
  title: string;
10
10
  message: string;
11
11
  onAdd?: () => void;
12
+ children?: React.ReactNode;
12
13
  };
13
14
 
14
- export const EmptyState = ( { icon, title, message, onAdd }: Props ) => {
15
+ export const EmptyState = ( { icon, title, message, onAdd, children }: Props ) => {
15
16
  const canAdd = usePermissions().canAdd();
17
+ const displayTitle = canAdd ? title : __( 'There are no variables', 'elementor' );
18
+ const displayMessage = canAdd
19
+ ? message
20
+ : __( 'With your current role, you can only connect and detach variables.', 'elementor' );
16
21
 
22
+ return (
23
+ <Content title={ displayTitle } message={ displayMessage } icon={ icon }>
24
+ { children ||
25
+ ( onAdd && (
26
+ <Button variant="outlined" color="secondary" size="small" onClick={ onAdd }>
27
+ { __( 'Create a variable', 'elementor' ) }
28
+ </Button>
29
+ ) ) }
30
+ </Content>
31
+ );
32
+ };
33
+
34
+ type NoVariablesContentProps = {
35
+ title: string;
36
+ message: string;
37
+ icon?: React.ReactNode;
38
+ children?: React.ReactNode;
39
+ };
40
+
41
+ function Content( { title, message, icon, children }: NoVariablesContentProps ) {
17
42
  return (
18
43
  <Stack
19
44
  gap={ 1 }
@@ -25,33 +50,6 @@ export const EmptyState = ( { icon, title, message, onAdd }: Props ) => {
25
50
  >
26
51
  { icon }
27
52
 
28
- { canAdd ? (
29
- <>
30
- <Content title={ title } message={ message } />
31
- { onAdd && (
32
- <Button variant="outlined" color="secondary" size="small" onClick={ onAdd }>
33
- { __( 'Create a variable', 'elementor' ) }
34
- </Button>
35
- ) }
36
- </>
37
- ) : (
38
- <Content
39
- title={ __( 'There are no variables', 'elementor' ) }
40
- message={ __( 'With your current role, you can only connect and detach variables.', 'elementor' ) }
41
- />
42
- ) }
43
- </Stack>
44
- );
45
- };
46
-
47
- type NoVariablesContentProps = {
48
- title: string;
49
- message: string;
50
- };
51
-
52
- function Content( { title, message }: NoVariablesContentProps ) {
53
- return (
54
- <>
55
53
  <Typography align="center" variant="subtitle2">
56
54
  { title }
57
55
  </Typography>
@@ -59,6 +57,8 @@ function Content( { title, message }: NoVariablesContentProps ) {
59
57
  <Typography align="center" variant="caption" maxWidth="180px">
60
58
  { message }
61
59
  </Typography>
62
- </>
60
+
61
+ { children }
62
+ </Stack>
63
63
  );
64
64
  }
@@ -142,21 +142,23 @@ export const VariableCreation = ( { onGoBack, onClose }: Props ) => {
142
142
  } }
143
143
  />
144
144
  </FormField>
145
- <FormField errorMsg={ valueFieldError } label={ __( 'Value', 'elementor' ) }>
146
- <Typography variant="h5" id="variable-value-wrapper">
147
- <ValueField
148
- value={ value }
149
- onPropTypeKeyChange={ ( key: string ) => setPropTypeKey( key ) }
150
- onChange={ ( newValue ) => {
151
- setValue( newValue );
152
- setErrorMessage( '' );
153
- setValueFieldError( '' );
154
- } }
155
- onValidationChange={ setValueFieldError }
156
- propType={ propType }
157
- />
158
- </Typography>
159
- </FormField>
145
+ { ValueField && (
146
+ <FormField errorMsg={ valueFieldError } label={ __( 'Value', 'elementor' ) }>
147
+ <Typography variant="h5" id="variable-value-wrapper">
148
+ <ValueField
149
+ value={ value }
150
+ onPropTypeKeyChange={ ( key: string ) => setPropTypeKey( key ) }
151
+ onChange={ ( newValue ) => {
152
+ setValue( newValue );
153
+ setErrorMessage( '' );
154
+ setValueFieldError( '' );
155
+ } }
156
+ onValidationChange={ setValueFieldError }
157
+ propType={ propType }
158
+ />
159
+ </Typography>
160
+ </FormField>
161
+ ) }
160
162
 
161
163
  { errorMessage && <FormHelperText error>{ errorMessage }</FormHelperText> }
162
164
  </PopoverContent>
@@ -206,21 +206,23 @@ export const VariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) =
206
206
  } }
207
207
  />
208
208
  </FormField>
209
- <FormField errorMsg={ valueFieldError } label={ __( 'Value', 'elementor' ) }>
210
- <Typography variant="h5">
211
- <ValueField
212
- propTypeKey={ variable.type }
213
- value={ value }
214
- onChange={ ( newValue ) => {
215
- setValue( newValue );
216
- setErrorMessage( '' );
217
- setValueFieldError( '' );
218
- } }
219
- onValidationChange={ setValueFieldError }
220
- propType={ propType }
221
- />
222
- </Typography>
223
- </FormField>
209
+ { ValueField && (
210
+ <FormField errorMsg={ valueFieldError } label={ __( 'Value', 'elementor' ) }>
211
+ <Typography variant="h5">
212
+ <ValueField
213
+ propTypeKey={ variable.type }
214
+ value={ value }
215
+ onChange={ ( newValue ) => {
216
+ setValue( newValue );
217
+ setErrorMessage( '' );
218
+ setValueFieldError( '' );
219
+ } }
220
+ onValidationChange={ setValueFieldError }
221
+ propType={ propType }
222
+ />
223
+ </Typography>
224
+ </FormField>
225
+ ) }
224
226
 
225
227
  { errorMessage && <FormHelperText error>{ errorMessage }</FormHelperText> }
226
228
  </PopoverContent>
@@ -121,20 +121,22 @@ export const VariableRestore = ( { variableId, onClose, onSubmit }: Props ) => {
121
121
  } }
122
122
  />
123
123
  </FormField>
124
- <FormField errorMsg={ valueFieldError } label={ __( 'Value', 'elementor' ) }>
125
- <Typography variant="h5">
126
- <ValueField
127
- value={ value }
128
- onChange={ ( newValue ) => {
129
- setValue( newValue );
130
- setErrorMessage( '' );
131
- setValueFieldError( '' );
132
- } }
133
- onValidationChange={ setValueFieldError }
134
- propType={ propType }
135
- />
136
- </Typography>
137
- </FormField>
124
+ { ValueField && (
125
+ <FormField errorMsg={ valueFieldError } label={ __( 'Value', 'elementor' ) }>
126
+ <Typography variant="h5">
127
+ <ValueField
128
+ value={ value }
129
+ onChange={ ( newValue ) => {
130
+ setValue( newValue );
131
+ setErrorMessage( '' );
132
+ setValueFieldError( '' );
133
+ } }
134
+ onValidationChange={ setValueFieldError }
135
+ propType={ propType }
136
+ />
137
+ </Typography>
138
+ </FormField>
139
+ ) }
138
140
 
139
141
  { errorMessage && <FormHelperText error>{ errorMessage }</FormHelperText> }
140
142
  </PopoverContent>
@@ -6,6 +6,7 @@ import { isExperimentActive } from '@elementor/editor-v1-adapters';
6
6
  import { PopoverContentRefContextProvider } from '../context/variable-selection-popover.context';
7
7
  import { VariableTypeProvider } from '../context/variable-type-context';
8
8
  import { usePermissions } from '../hooks/use-permissions';
9
+ import { useQuotaPermissions } from '../hooks/use-quota-permissions';
9
10
  import { type Variable } from '../types';
10
11
  import { VariableCreation } from './variable-creation';
11
12
  import { VariableEdit } from './variable-edit';
@@ -56,6 +57,7 @@ type ViewProps = {
56
57
  propTypeKey: string;
57
58
  currentView: View;
58
59
  selectedVariable?: Variable;
60
+ disabled?: boolean;
59
61
  editId: string;
60
62
  setEditId: ( id: string ) => void;
61
63
  setCurrentView: ( stage: View ) => void;
@@ -73,6 +75,7 @@ type Handlers = {
73
75
 
74
76
  function RenderView( props: ViewProps ): React.ReactNode {
75
77
  const userPermissions = usePermissions();
78
+ const userQuotaPremissions = useQuotaPermissions( props.propTypeKey );
76
79
 
77
80
  const handlers: Handlers = {
78
81
  onClose: () => {
@@ -118,6 +121,7 @@ function RenderView( props: ViewProps ): React.ReactNode {
118
121
  onAdd={ handlers.onAdd }
119
122
  onEdit={ handlers.onEdit }
120
123
  onSettings={ handlers.onSettings }
124
+ disabled={ ! userQuotaPremissions.canAdd() }
121
125
  />
122
126
  );
123
127
  }
@@ -264,7 +264,7 @@ export const VariablesManagerTable = ( {
264
264
  onValidationChange,
265
265
  error,
266
266
  } ) =>
267
- row.valueField( {
267
+ row.valueField?.( {
268
268
  ref: {
269
269
  current: variableRowRefs.current.get(
270
270
  'table-ref-' + row.id
@@ -284,7 +284,7 @@ export const VariablesManagerTable = ( {
284
284
  onFieldError?.( !! errorMsg );
285
285
  },
286
286
  error,
287
- } )
287
+ } ) ?? <></>
288
288
  }
289
289
  onRowRef={ handleRowRef( row.id ) }
290
290
  gap={ 0.25 }
@@ -25,10 +25,11 @@ type Props = {
25
25
  onAdd?: () => void;
26
26
  onEdit?: ( key: string ) => void;
27
27
  onSettings?: () => void;
28
+ disabled?: boolean;
28
29
  };
29
30
 
30
- export const VariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }: Props ) => {
31
- const { icon: VariableIcon, startIcon, variableType, propTypeUtil } = useVariableType();
31
+ export const VariablesSelection = ( { closePopover, onAdd, onEdit, onSettings, disabled = false }: Props ) => {
32
+ const { icon: VariableIcon, startIcon, variableType, propTypeUtil, emptyState } = useVariableType();
32
33
 
33
34
  const { value: variable, setValue: setVariable, path } = useVariableBoundProp();
34
35
  const [ searchValue, setSearchValue ] = useState( '' );
@@ -64,14 +65,17 @@ export const VariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }:
64
65
  if ( onAdd ) {
65
66
  actions.push(
66
67
  <Tooltip key="add" placement="top" title={ CREATE_LABEL }>
67
- <IconButton
68
- id="add-variable-button"
69
- size={ SIZE }
70
- onClick={ onAddAndTrack }
71
- aria-label={ CREATE_LABEL }
72
- >
73
- <PlusIcon fontSize={ SIZE } />
74
- </IconButton>
68
+ <span>
69
+ <IconButton
70
+ id="add-variable-button"
71
+ size={ SIZE }
72
+ onClick={ onAddAndTrack }
73
+ aria-label={ CREATE_LABEL }
74
+ disabled={ disabled }
75
+ >
76
+ <PlusIcon fontSize={ SIZE } />
77
+ </IconButton>
78
+ </span>
75
79
  </Tooltip>
76
80
  );
77
81
  }
@@ -119,11 +123,28 @@ export const VariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }:
119
123
  setSearchValue( '' );
120
124
  };
121
125
 
122
- const noVariableTitle = sprintf(
123
- /* translators: %s: Variable Type. */
124
- __( 'Create your first %s variable', 'elementor' ),
125
- variableType
126
- );
126
+ const noVariableTitle = disabled
127
+ ? sprintf(
128
+ /* translators: %s: Variable Type. */
129
+ __( 'No %s variables yet', 'elementor' ),
130
+ variableType
131
+ )
132
+ : sprintf(
133
+ /* translators: %s: Variable Type. */
134
+ __( 'Create your first %s variable', 'elementor' ),
135
+ variableType
136
+ );
137
+
138
+ const noVariableMessage = disabled
139
+ ? sprintf(
140
+ /* translators: %s: Variable Type. */
141
+ __(
142
+ 'Start by creating your first %s variable to apply consistent sizing across elements.',
143
+ 'elementor'
144
+ ),
145
+ variableType
146
+ )
147
+ : __( 'Variables are saved attributes that you can apply anywhere on your site.', 'elementor' );
127
148
 
128
149
  return (
129
150
  <PopoverBody>
@@ -169,13 +190,12 @@ export const VariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }:
169
190
  { ! hasVariables && ! hasNoCompatibleVariables && (
170
191
  <EmptyState
171
192
  title={ noVariableTitle }
172
- message={ __(
173
- 'Variables are saved attributes that you can apply anywhere on your site.',
174
- 'elementor'
175
- ) }
193
+ message={ noVariableMessage }
176
194
  icon={ <VariableIcon fontSize="large" /> }
177
- onAdd={ onAdd }
178
- />
195
+ onAdd={ disabled ? undefined : onAdd }
196
+ >
197
+ { emptyState }
198
+ </EmptyState>
179
199
  ) }
180
200
 
181
201
  { hasNoCompatibleVariables && (
@@ -186,8 +206,10 @@ export const VariablesSelection = ( { closePopover, onAdd, onEdit, onSettings }:
186
206
  'elementor'
187
207
  ) }
188
208
  icon={ <VariableIcon fontSize="large" /> }
189
- onAdd={ onAdd }
190
- />
209
+ onAdd={ disabled ? undefined : onAdd }
210
+ >
211
+ { emptyState }
212
+ </EmptyState>
191
213
  ) }
192
214
  </PopoverBody>
193
215
  );
@@ -0,0 +1,18 @@
1
+ import { getLicenseInfo } from '../sync/license-info';
2
+
3
+ declare global {
4
+ interface Window {
5
+ ElementorVariablesQuotaConfig: Record< string, number >;
6
+ }
7
+ }
8
+
9
+ export const useQuotaPermissions = ( variableType: string ) => {
10
+ const quotaConfig = window.ElementorVariablesQuotaConfig || {};
11
+ const limit = quotaConfig[ variableType ] || 0;
12
+ const hasQuota = limit > 0;
13
+
14
+ return {
15
+ canAdd: () => hasQuota || getLicenseInfo().hasPro,
16
+ canEdit: () => hasQuota || getLicenseInfo().hasPro,
17
+ };
18
+ };
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { init } from './init';
2
2
  export { registerVariableTypes } from './register-variable-types';
3
+ export { sizeVariablePropTypeUtil } from './prop-types/size-variable-prop-type';
3
4
  export { service } from './service';
4
5
  export { registerVariableType } from './variables-registry/variable-type-registry';
5
6
  export { GLOBAL_VARIABLES_URI } from './mcp/variables-resource';
@@ -0,0 +1,4 @@
1
+ import { createPropUtils } from '@elementor/editor-props';
2
+ import { z } from '@elementor/schema';
3
+
4
+ export const sizeVariablePropTypeUtil = createPropUtils( 'global-size-variable', z.string() );
@@ -1,12 +1,14 @@
1
1
  import * as React from 'react';
2
- import { colorPropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
3
- import { BrushIcon, TextIcon } from '@elementor/icons';
2
+ import { colorPropTypeUtil, sizePropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
3
+ import { UpgradeButton } from '@elementor/editor-ui';
4
+ import { BrushIcon, ExpandDiagonalIcon, TextIcon } from '@elementor/icons';
4
5
 
5
6
  import { ColorField } from './components/fields/color-field';
6
7
  import { FontField } from './components/fields/font-field';
7
8
  import { ColorIndicator } from './components/ui/color-indicator';
8
9
  import { colorVariablePropTypeUtil } from './prop-types/color-variable-prop-type';
9
10
  import { fontVariablePropTypeUtil } from './prop-types/font-variable-prop-type';
11
+ import { sizeVariablePropTypeUtil } from './prop-types/size-variable-prop-type';
10
12
  import { registerVariableType } from './variables-registry/variable-type-registry';
11
13
 
12
14
  export function registerVariableTypes() {
@@ -30,4 +32,14 @@ export function registerVariableTypes() {
30
32
  variableType: 'font',
31
33
  defaultValue: 'Roboto',
32
34
  } );
35
+
36
+ registerVariableType( {
37
+ key: sizeVariablePropTypeUtil.key,
38
+ icon: ExpandDiagonalIcon,
39
+ propTypeUtil: sizeVariablePropTypeUtil,
40
+ fallbackPropTypeUtil: sizePropTypeUtil,
41
+ variableType: 'size',
42
+ selectionFilter: () => [],
43
+ emptyState: <UpgradeButton size="small" href={ 'https://go.elementor.com/go-pro-panel-size-variable/' } />,
44
+ } );
33
45
  }
@@ -0,0 +1,9 @@
1
+ import { type CanvasExtendedWindow } from './types';
2
+
3
+ export function getLicenseInfo() {
4
+ const extendedWindow = window as unknown as CanvasExtendedWindow;
5
+
6
+ return {
7
+ hasPro: !! extendedWindow.elementorPro,
8
+ };
9
+ }
package/src/sync/types.ts CHANGED
@@ -7,4 +7,5 @@ export type CanvasExtendedWindow = Window & {
7
7
  enqueueFont?: EnqueueFont;
8
8
  };
9
9
  };
10
+ elementorPro?: object;
10
11
  };
@@ -30,7 +30,7 @@ type FallbackPropTypeUtil = ReturnType< typeof createPropUtils >;
30
30
  type VariableTypeOptions = {
31
31
  icon: ForwardRefExoticComponent< Omit< SvgIconProps, 'ref' > & RefAttributes< SVGSVGElement > >;
32
32
  startIcon?: ( { value }: { value: string } ) => JSX.Element;
33
- valueField: ( props: ValueFieldProps ) => JSX.Element;
33
+ valueField?: ( props: ValueFieldProps ) => JSX.Element;
34
34
  variableType: string;
35
35
  key?: string;
36
36
  defaultValue?: string;
@@ -39,6 +39,7 @@ type VariableTypeOptions = {
39
39
  selectionFilter?: ( variables: NormalizedVariable[], propType: PropType ) => NormalizedVariable[];
40
40
  valueTransformer?: ( value: string, type?: string ) => PropValue;
41
41
  isCompatible?: ( propType: PropType, variable: Variable ) => boolean;
42
+ emptyState?: JSX.Element;
42
43
  };
43
44
 
44
45
  export type VariableTypesMap = Record< string, Omit< VariableTypeOptions, 'key' > >;
@@ -58,6 +59,7 @@ export function createVariableTypeRegistry() {
58
59
  valueTransformer,
59
60
  fallbackPropTypeUtil,
60
61
  isCompatible,
62
+ emptyState,
61
63
  }: VariableTypeOptions ) => {
62
64
  const variableTypeKey = key ?? propTypeUtil.key;
63
65
 
@@ -83,6 +85,7 @@ export function createVariableTypeRegistry() {
83
85
  valueTransformer,
84
86
  fallbackPropTypeUtil,
85
87
  isCompatible,
88
+ emptyState,
86
89
  };
87
90
 
88
91
  registerTransformer( propTypeUtil.key );