@elementor/editor-controls 1.2.0 → 1.5.0

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/index.d.mts +20 -8
  3. package/dist/index.d.ts +20 -8
  4. package/dist/index.js +1092 -714
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +937 -549
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +7 -7
  9. package/src/bound-prop-context/prop-context.tsx +3 -3
  10. package/src/bound-prop-context/prop-key-context.tsx +1 -0
  11. package/src/bound-prop-context/use-bound-prop.ts +5 -1
  12. package/src/components/font-family-selector.tsx +54 -56
  13. package/src/components/repeater.tsx +22 -11
  14. package/src/components/size-control/size-input.tsx +4 -4
  15. package/src/components/text-field-popover.tsx +19 -18
  16. package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +3 -15
  17. package/src/controls/box-shadow-repeater-control.tsx +1 -1
  18. package/src/controls/color-control.tsx +12 -1
  19. package/src/controls/equal-unequal-sizes-control.tsx +1 -1
  20. package/src/controls/filter-control/drop-shadow-item-content.tsx +69 -0
  21. package/src/controls/filter-control/drop-shadow-item-label.tsx +20 -0
  22. package/src/controls/filter-repeater-control.tsx +108 -21
  23. package/src/controls/font-family-control/font-family-control.tsx +14 -2
  24. package/src/controls/image-control.tsx +45 -16
  25. package/src/controls/key-value-control.tsx +57 -46
  26. package/src/controls/link-control.tsx +25 -20
  27. package/src/controls/linked-dimensions-control.tsx +1 -1
  28. package/src/controls/repeatable-control.tsx +100 -21
  29. package/src/controls/select-control.tsx +22 -2
  30. package/src/controls/size-control.tsx +25 -12
  31. package/src/controls/switch-control.tsx +9 -1
  32. package/src/controls/text-control.tsx +33 -18
  33. package/src/controls/transform-control/functions/axis-row.tsx +32 -0
  34. package/src/controls/transform-control/functions/move.tsx +44 -0
  35. package/src/controls/transform-control/transform-content.tsx +36 -0
  36. package/src/controls/transform-control/transform-icon.tsx +12 -0
  37. package/src/controls/transform-control/transform-label.tsx +27 -0
  38. package/src/controls/transform-control/transform-repeater-control.tsx +42 -0
  39. package/src/hooks/use-repeatable-control-context.ts +6 -1
  40. package/src/index.ts +1 -0
  41. package/src/utils/size-control.ts +4 -2
@@ -3,12 +3,20 @@ import { useRef } from 'react';
3
3
  import {
4
4
  blurFilterPropTypeUtil,
5
5
  brightnessFilterPropTypeUtil,
6
+ contrastFilterPropTypeUtil,
7
+ dropShadowFilterPropTypeUtil,
6
8
  type FilterItemPropValue,
7
9
  filterPropTypeUtil,
10
+ grayscaleFilterPropTypeUtil,
11
+ hueRotateFilterPropTypeUtil,
12
+ invertFilterPropTypeUtil,
8
13
  type PropKey,
9
14
  type PropTypeUtil,
15
+ saturateFilterPropTypeUtil,
16
+ sepiaFilterPropTypeUtil,
10
17
  type SizePropValue,
11
18
  } from '@elementor/editor-props';
19
+ import { backdropFilterPropTypeUtil } from '@elementor/editor-props';
12
20
  import { MenuListItem } from '@elementor/editor-ui';
13
21
  import { Box, Grid, Select, type SelectChangeEvent } from '@elementor/ui';
14
22
  import { __ } from '@wordpress/i18n';
@@ -17,9 +25,11 @@ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-conte
17
25
  import { ControlLabel } from '../components/control-label';
18
26
  import { PopoverContent } from '../components/popover-content';
19
27
  import { PopoverGridContainer } from '../components/popover-grid-container';
20
- import { Repeater } from '../components/repeater';
28
+ import { type CollectionPropUtil, Repeater } from '../components/repeater';
21
29
  import { createControl } from '../create-control';
22
- import { defaultUnits } from '../utils/size-control';
30
+ import { defaultUnits, type Unit } from '../utils/size-control';
31
+ import { DropShadowItemContent } from './filter-control/drop-shadow-item-content';
32
+ import { DropShadowItemLabel } from './filter-control/drop-shadow-item-label';
23
33
  import { SizeControl } from './size-control';
24
34
 
25
35
  type FilterType = FilterItemPropValue[ '$$type' ];
@@ -43,6 +53,21 @@ const filterConfig: Record< FilterType, FilterItemConfig > = {
43
53
  propType: blurFilterPropTypeUtil,
44
54
  units: defaultUnits.filter( ( unit ) => unit !== '%' ),
45
55
  },
56
+ 'drop-shadow': {
57
+ defaultValue: {
58
+ $$type: 'drop-shadow',
59
+ value: {
60
+ xAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
61
+ yAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
62
+ blur: { $$type: 'size', value: { size: 10, unit: 'px' } },
63
+ color: { $$type: 'color', value: 'rgba(0, 0, 0, 1)' },
64
+ },
65
+ },
66
+ name: __( 'Drop shadow', 'elementor' ),
67
+ valueName: __( 'Drop-shadow', 'elementor' ),
68
+ propType: dropShadowFilterPropTypeUtil,
69
+ units: defaultUnits.filter( ( unit ) => unit !== '%' ),
70
+ },
46
71
  brightness: {
47
72
  defaultValue: { $$type: 'amount', amount: { $$type: 'size', value: { size: 100, unit: '%' } } },
48
73
  name: __( 'Brightness', 'elementor' ),
@@ -50,18 +75,62 @@ const filterConfig: Record< FilterType, FilterItemConfig > = {
50
75
  propType: brightnessFilterPropTypeUtil,
51
76
  units: [ '%' ],
52
77
  },
78
+ contrast: {
79
+ defaultValue: { $$type: 'contrast', contrast: { $$type: 'size', value: { size: 100, unit: '%' } } },
80
+ name: __( 'Contrast', 'elementor' ),
81
+ valueName: __( 'Amount', 'elementor' ),
82
+ propType: contrastFilterPropTypeUtil,
83
+ units: [ '%' ],
84
+ },
85
+ 'hue-rotate': {
86
+ defaultValue: { $$type: 'hue-rotate', 'hue-rotate': { $$type: 'size', value: { size: 0, unit: 'deg' } } },
87
+ name: __( 'Hue Rotate', 'elementor' ),
88
+ valueName: __( 'Angle', 'elementor' ),
89
+ propType: hueRotateFilterPropTypeUtil,
90
+ units: [ 'deg', 'rad', 'grad', 'turn' ],
91
+ },
92
+ saturate: {
93
+ defaultValue: { $$type: 'saturate', saturate: { $$type: 'size', value: { size: 100, unit: '%' } } },
94
+ name: __( 'Saturate', 'elementor' ),
95
+ valueName: __( 'Amount', 'elementor' ),
96
+ propType: saturateFilterPropTypeUtil,
97
+ units: [ '%' ],
98
+ },
99
+ grayscale: {
100
+ defaultValue: { $$type: 'grayscale', grayscale: { $$type: 'size', value: { size: 0, unit: '%' } } },
101
+ name: __( 'Grayscale', 'elementor' ),
102
+ valueName: __( 'Amount', 'elementor' ),
103
+ propType: grayscaleFilterPropTypeUtil,
104
+ units: [ '%' ],
105
+ },
106
+ invert: {
107
+ defaultValue: { $$type: 'invert', invert: { $$type: 'size', value: { size: 0, unit: '%' } } },
108
+ name: __( 'Invert', 'elementor' ),
109
+ valueName: __( 'Amount', 'elementor' ),
110
+ propType: invertFilterPropTypeUtil,
111
+ units: [ '%' ],
112
+ },
113
+ sepia: {
114
+ defaultValue: { $$type: 'sepia', sepia: { $$type: 'size', value: { size: 0, unit: '%' } } },
115
+ name: __( 'Sepia', 'elementor' ),
116
+ valueName: __( 'Amount', 'elementor' ),
117
+ propType: sepiaFilterPropTypeUtil,
118
+ units: [ '%' ],
119
+ },
53
120
  };
54
121
 
55
122
  const filterKeys = Object.keys( filterConfig ) as FilterType[];
56
123
 
57
- const singleSizeFilterNames = filterKeys.filter( ( name ) => {
58
- const filter = filterConfig[ name as FilterType ].defaultValue;
59
-
60
- return filter[ filter.$$type ].$$type === 'size';
61
- } ) as FilterType[];
124
+ const isSingleSize = ( key: FilterType ): boolean => {
125
+ return ! [ 'drop-shadow' ].includes( key );
126
+ };
62
127
 
63
- export const FilterRepeaterControl = createControl( () => {
64
- const { propType, value: filterValues, setValue, disabled } = useBoundProp( filterPropTypeUtil );
128
+ export const FilterRepeaterControl = createControl( ( { filterPropName = 'filter' }: { filterPropName?: string } ) => {
129
+ const [ propUtil, label ] =
130
+ filterPropName === 'backdrop-filter'
131
+ ? [ backdropFilterPropTypeUtil, __( 'Backdrop Filters', 'elementor' ) ]
132
+ : [ filterPropTypeUtil, __( 'Filters', 'elementor' ) ];
133
+ const { propType, value: filterValues, setValue, disabled } = useBoundProp( propUtil );
65
134
 
66
135
  return (
67
136
  <PropProvider propType={ propType } value={ filterValues } setValue={ setValue }>
@@ -70,7 +139,8 @@ export const FilterRepeaterControl = createControl( () => {
70
139
  disabled={ disabled }
71
140
  values={ filterValues ?? [] }
72
141
  setValues={ setValue }
73
- label={ __( 'Filter', 'elementor' ) }
142
+ label={ label }
143
+ collectionPropUtil={ propUtil }
74
144
  itemSettings={ {
75
145
  Icon: ItemIcon,
76
146
  Label: ItemLabel,
@@ -87,10 +157,12 @@ export const FilterRepeaterControl = createControl( () => {
87
157
 
88
158
  const ItemIcon = () => <></>;
89
159
 
90
- const ItemLabel = ( props: { value: FilterItemPropValue } ) => {
91
- const { $$type } = props.value;
92
-
93
- return singleSizeFilterNames.includes( $$type ) && <SingleSizeItemLabel value={ props.value } />;
160
+ const ItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
161
+ return isSingleSize( value.$$type ) ? (
162
+ <SingleSizeItemLabel value={ value } />
163
+ ) : (
164
+ <DropShadowItemLabel value={ value } />
165
+ );
94
166
  };
95
167
 
96
168
  const SingleSizeItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
@@ -113,8 +185,16 @@ const SingleSizeItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
113
185
  );
114
186
  };
115
187
 
116
- const ItemContent = ( { bind }: { bind: PropKey } ) => {
117
- const { value: filterValues, setValue } = useBoundProp( filterPropTypeUtil );
188
+ const ItemContent = ( {
189
+ bind,
190
+ collectionPropUtil,
191
+ anchorEl,
192
+ }: {
193
+ bind: PropKey;
194
+ collectionPropUtil?: CollectionPropUtil< FilterItemPropValue >;
195
+ anchorEl?: HTMLElement | null;
196
+ } ) => {
197
+ const { value: filterValues, setValue } = useBoundProp( collectionPropUtil ?? filterPropTypeUtil );
118
198
  const itemIndex = parseInt( bind, 10 );
119
199
  const item = filterValues?.[ itemIndex ];
120
200
 
@@ -124,7 +204,7 @@ const ItemContent = ( { bind }: { bind: PropKey } ) => {
124
204
 
125
205
  newFilterValues[ itemIndex ] = {
126
206
  $$type: filterType,
127
- value: filterConfig[ filterType ].defaultValue,
207
+ value: { ...filterConfig[ filterType ].defaultValue },
128
208
  } as FilterItemPropValue;
129
209
 
130
210
  setValue( newFilterValues );
@@ -153,14 +233,20 @@ const ItemContent = ( { bind }: { bind: PropKey } ) => {
153
233
  </Select>
154
234
  </Grid>
155
235
  </PopoverGridContainer>
156
- <Content filterType={ item?.$$type } />
236
+ <Content filterType={ item?.$$type } anchorEl={ anchorEl } />
157
237
  </PopoverContent>
158
238
  </PropKeyProvider>
159
239
  );
160
240
  };
161
241
 
162
- const Content = ( { filterType }: { filterType: FilterType } ) => {
163
- return singleSizeFilterNames.includes( filterType ) && <SingleSizeItemContent filterType={ filterType } />;
242
+ const Content = ( { filterType, anchorEl }: { filterType: FilterType; anchorEl?: HTMLElement | null } ) => {
243
+ const { propType, units = [] } = filterConfig[ filterType ];
244
+
245
+ return isSingleSize( filterType ) ? (
246
+ <SingleSizeItemContent filterType={ filterType } />
247
+ ) : (
248
+ <DropShadowItemContent propType={ propType } units={ units as Unit[] } anchorEl={ anchorEl } />
249
+ );
164
250
  };
165
251
 
166
252
  const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) => {
@@ -168,6 +254,7 @@ const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) =>
168
254
  const { $$type } = defaultValue;
169
255
  const context = useBoundProp( propType );
170
256
  const rowRef = useRef< HTMLDivElement >( null );
257
+ const defaultUnit = defaultValue[ $$type ].value.unit;
171
258
 
172
259
  return (
173
260
  <PropProvider { ...context }>
@@ -177,7 +264,7 @@ const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) =>
177
264
  <ControlLabel>{ valueName }</ControlLabel>
178
265
  </Grid>
179
266
  <Grid item xs={ 6 }>
180
- <SizeControl anchorRef={ rowRef } units={ units } />
267
+ <SizeControl anchorRef={ rowRef } units={ units } defaultUnit={ defaultUnit } />
181
268
  </Grid>
182
269
  </PopoverGridContainer>
183
270
  </PropKeyProvider>
@@ -21,20 +21,32 @@ type FontFamilyControlProps = {
21
21
  const SIZE = 'tiny';
22
22
 
23
23
  export const FontFamilyControl = createControl( ( { fontFamilies, sectionWidth }: FontFamilyControlProps ) => {
24
- const { value: fontFamily, setValue: setFontFamily, disabled } = useBoundProp( stringPropTypeUtil );
24
+ const { value: fontFamily, setValue: setFontFamily, disabled, placeholder } = useBoundProp( stringPropTypeUtil );
25
25
 
26
26
  const popoverState = usePopupState( { variant: 'popover' } );
27
27
 
28
+ const isShowingPlaceholder = ! fontFamily && placeholder;
29
+
28
30
  return (
29
31
  <>
30
32
  <ControlActions>
31
33
  <UnstableTag
32
34
  variant="outlined"
33
- label={ fontFamily }
35
+ label={ fontFamily || placeholder }
34
36
  endIcon={ <ChevronDownIcon fontSize={ SIZE } /> }
35
37
  { ...bindTrigger( popoverState ) }
36
38
  fullWidth
37
39
  disabled={ disabled }
40
+ sx={
41
+ isShowingPlaceholder
42
+ ? {
43
+ '& .MuiTag-label': {
44
+ color: ( theme ) => theme.palette.text.tertiary,
45
+ },
46
+ textTransform: 'capitalize',
47
+ }
48
+ : undefined
49
+ }
38
50
  />
39
51
  </ControlActions>
40
52
  <Popover
@@ -1,9 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { imagePropTypeUtil } from '@elementor/editor-props';
3
- import { Stack } from '@elementor/ui';
3
+ import { Grid, Stack } from '@elementor/ui';
4
4
  import { type MediaType } from '@elementor/wp-media';
5
+ import { __ } from '@wordpress/i18n';
5
6
 
6
7
  import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
8
+ import { ControlFormLabel } from '../components/control-form-label';
7
9
  import { createControl } from '../create-control';
8
10
  import { useUnfilteredFilesUpload } from '../hooks/use-unfiltered-files-upload';
9
11
  import { ImageMediaControl } from './image-media-control';
@@ -17,23 +19,50 @@ type ImageControlProps = {
17
19
  export const ImageControl = createControl( ( { sizes, showMode = 'all' }: ImageControlProps ) => {
18
20
  const propContext = useBoundProp( imagePropTypeUtil );
19
21
 
22
+ let componentToRender;
23
+ switch ( showMode ) {
24
+ case 'media':
25
+ componentToRender = <ImageSrcControl />;
26
+ break;
27
+ case 'sizes':
28
+ componentToRender = <ImageSizeControl sizes={ sizes } />;
29
+ break;
30
+ case 'all':
31
+ default:
32
+ componentToRender = (
33
+ <Stack gap={ 1.5 }>
34
+ <ControlFormLabel>{ __( 'Image', 'elementor' ) }</ControlFormLabel>
35
+ <ImageSrcControl />
36
+ <Grid container gap={ 1.5 } alignItems="center" flexWrap="nowrap">
37
+ <Grid item xs={ 6 }>
38
+ <ControlFormLabel>{ __( 'Resolution', 'elementor' ) }</ControlFormLabel>
39
+ </Grid>
40
+ <Grid item xs={ 6 } sx={ { overflow: 'hidden' } }>
41
+ <ImageSizeControl sizes={ sizes } />
42
+ </Grid>
43
+ </Grid>
44
+ </Stack>
45
+ );
46
+ }
47
+
48
+ return <PropProvider { ...propContext }>{ componentToRender }</PropProvider>;
49
+ } );
50
+
51
+ const ImageSrcControl = () => {
20
52
  const { data: allowSvgUpload } = useUnfilteredFilesUpload();
21
53
  const mediaTypes: MediaType[] = allowSvgUpload ? [ 'image', 'svg' ] : [ 'image' ];
22
54
 
23
55
  return (
24
- <PropProvider { ...propContext }>
25
- <Stack gap={ 1.5 }>
26
- { [ 'all', 'media' ].includes( showMode ) ? (
27
- <PropKeyProvider bind={ 'src' }>
28
- <ImageMediaControl mediaTypes={ mediaTypes } />
29
- </PropKeyProvider>
30
- ) : null }
31
- { [ 'all', 'sizes' ].includes( showMode ) ? (
32
- <PropKeyProvider bind={ 'size' }>
33
- <SelectControl options={ sizes } />
34
- </PropKeyProvider>
35
- ) : null }
36
- </Stack>
37
- </PropProvider>
56
+ <PropKeyProvider bind={ 'src' }>
57
+ <ImageMediaControl mediaTypes={ mediaTypes } />
58
+ </PropKeyProvider>
38
59
  );
39
- } );
60
+ };
61
+
62
+ const ImageSizeControl = ( { sizes }: { sizes: ImageControlProps[ 'sizes' ] } ) => {
63
+ return (
64
+ <PropKeyProvider bind={ 'size' }>
65
+ <SelectControl options={ sizes } />
66
+ </PropKeyProvider>
67
+ );
68
+ };
@@ -1,14 +1,19 @@
1
1
  import * as React from 'react';
2
- import { type ChangeEvent, useMemo, useState } from 'react';
3
- import { keyValuePropTypeUtil } from '@elementor/editor-props';
4
- import { FormHelperText, FormLabel, Grid, TextField } from '@elementor/ui';
2
+ import { useMemo, useState } from 'react';
3
+ import {
4
+ type CreateOptions,
5
+ isTransformable,
6
+ keyValuePropTypeUtil,
7
+ type PropKey,
8
+ type Props,
9
+ stringPropTypeUtil,
10
+ } from '@elementor/editor-props';
11
+ import { FormHelperText, FormLabel, Grid } from '@elementor/ui';
5
12
  import { __ } from '@wordpress/i18n';
6
13
 
7
- import { useBoundProp } from '../bound-prop-context';
8
- import ControlActions from '../control-actions/control-actions';
14
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
9
15
  import { createControl } from '../create-control';
10
-
11
- type FieldType = 'key' | 'value';
16
+ import { TextControl } from './text-control';
12
17
 
13
18
  type KeyValueControlProps = {
14
19
  keyName?: string;
@@ -19,9 +24,9 @@ type KeyValueControlProps = {
19
24
  };
20
25
 
21
26
  export const KeyValueControl = createControl( ( props: KeyValueControlProps = {} ) => {
22
- const { value, setValue } = useBoundProp( keyValuePropTypeUtil );
23
- const [ keyError, setKeyError ] = useState< string | null >( null );
24
- const [ valueError, setValueError ] = useState< string | null >( null );
27
+ const { value, setValue, ...propContext } = useBoundProp( keyValuePropTypeUtil );
28
+ const [ keyError, setKeyError ] = useState< string >( '' );
29
+ const [ valueError, setValueError ] = useState< string >( '' );
25
30
 
26
31
  const [ sessionState, setSessionState ] = useState( {
27
32
  key: value?.key?.value || '',
@@ -43,31 +48,48 @@ export const KeyValueControl = createControl( ( props: KeyValueControlProps = {}
43
48
  const validate = ( newValue: string, fieldType: string ): boolean => {
44
49
  if ( fieldType === 'key' && keyRegex ) {
45
50
  const isValid = keyRegex.test( newValue );
46
- setKeyError( isValid ? null : errMsg );
51
+ setKeyError( isValid ? '' : errMsg );
52
+
47
53
  return isValid;
48
54
  } else if ( fieldType === 'value' && valueRegex ) {
49
55
  const isValid = valueRegex.test( newValue );
50
- setValueError( isValid ? null : errMsg );
56
+ setValueError( isValid ? '' : errMsg );
57
+
51
58
  return isValid;
52
59
  }
60
+
53
61
  return true;
54
62
  };
55
63
 
56
- const handleChange = ( event: ChangeEvent< HTMLInputElement >, fieldType: FieldType ) => {
57
- const newValue = event.target.value;
64
+ const handleChange = ( newValue: Props, options?: CreateOptions, meta?: { bind?: PropKey } ) => {
65
+ const fieldType = meta?.bind;
66
+
67
+ if ( ! fieldType ) {
68
+ return;
69
+ }
70
+
71
+ const newChangedValue = newValue[ fieldType ];
72
+
73
+ if ( isTransformable( newChangedValue ) && newChangedValue.$$type === 'dynamic' ) {
74
+ setValue( {
75
+ ...value,
76
+ [ fieldType ]: newChangedValue,
77
+ } );
78
+
79
+ return;
80
+ }
81
+
82
+ const extractedValue = stringPropTypeUtil.extract( newChangedValue );
58
83
 
59
84
  setSessionState( ( prev ) => ( {
60
85
  ...prev,
61
- [ fieldType ]: newValue,
86
+ [ fieldType ]: extractedValue,
62
87
  } ) );
63
88
 
64
- if ( validate( newValue, fieldType ) ) {
89
+ if ( extractedValue && validate( extractedValue, fieldType ) ) {
65
90
  setValue( {
66
91
  ...value,
67
- [ fieldType ]: {
68
- value: newValue,
69
- $$type: 'string',
70
- },
92
+ [ fieldType ]: newChangedValue,
71
93
  } );
72
94
  } else {
73
95
  setValue( {
@@ -80,40 +102,29 @@ export const KeyValueControl = createControl( ( props: KeyValueControlProps = {}
80
102
  }
81
103
  };
82
104
 
83
- const isKeyInvalid = keyError !== null;
84
- const isValueInvalid = valueError !== null;
85
-
86
105
  return (
87
- <ControlActions>
106
+ <PropProvider { ...propContext } value={ value } setValue={ handleChange }>
88
107
  <Grid container gap={ 1.5 }>
89
108
  <Grid item xs={ 12 }>
90
109
  <FormLabel size="tiny">{ keyLabel }</FormLabel>
91
- <TextField
92
- // eslint-disable-next-line jsx-a11y/no-autofocus
93
- autoFocus
94
- sx={ { pt: 1 } }
95
- size="tiny"
96
- fullWidth
97
- value={ sessionState.key }
98
- onChange={ ( e: ChangeEvent< HTMLInputElement > ) => handleChange( e, 'key' ) }
99
- error={ isKeyInvalid }
100
- />
101
- { isKeyInvalid && <FormHelperText error>{ keyError }</FormHelperText> }
110
+ <PropKeyProvider bind={ 'key' }>
111
+ <TextControl inputValue={ sessionState.key } error={ !! keyError } sx={ { pt: 1 } } />
112
+ </PropKeyProvider>
113
+ { !! keyError && <FormHelperText error>{ keyError }</FormHelperText> }
102
114
  </Grid>
103
115
  <Grid item xs={ 12 }>
104
116
  <FormLabel size="tiny">{ valueLabel }</FormLabel>
105
- <TextField
106
- sx={ { pt: 1 } }
107
- size="tiny"
108
- fullWidth
109
- value={ sessionState.value }
110
- onChange={ ( e: ChangeEvent< HTMLInputElement > ) => handleChange( e, 'value' ) }
111
- disabled={ isKeyInvalid }
112
- error={ isValueInvalid }
113
- />
114
- { isValueInvalid && <FormHelperText error>{ valueError }</FormHelperText> }
117
+ <PropKeyProvider bind={ 'value' }>
118
+ <TextControl
119
+ inputValue={ sessionState.value }
120
+ error={ !! valueError }
121
+ inputDisabled={ !! keyError }
122
+ sx={ { pt: 1 } }
123
+ />
124
+ </PropKeyProvider>
125
+ { !! valueError && <FormHelperText error>{ valueError }</FormHelperText> }
115
126
  </Grid>
116
127
  </Grid>
117
- </ControlActions>
128
+ </PropProvider>
118
129
  );
119
130
  } );
@@ -10,6 +10,7 @@ import {
10
10
  urlPropTypeUtil,
11
11
  } from '@elementor/editor-props';
12
12
  import { InfoTipCard } from '@elementor/editor-ui';
13
+ import { isExperimentActive } from '@elementor/editor-v1-adapters';
13
14
  import { type HttpResponse, httpService } from '@elementor/http-client';
14
15
  import { AlertTriangleIcon, MinusIcon, PlusIcon } from '@elementor/icons';
15
16
  import { useSessionStorage } from '@elementor/session';
@@ -29,6 +30,7 @@ import { ControlFormLabel } from '../components/control-form-label';
29
30
  import ControlActions from '../control-actions/control-actions';
30
31
  import { createControl } from '../create-control';
31
32
  import { type ControlProps } from '../utils/types';
33
+ import { SwitchControl } from './switch-control';
32
34
 
33
35
  type Props = ControlProps< {
34
36
  queryOptions: {
@@ -191,7 +193,14 @@ export const LinkControl = createControl( ( props: Props ) => {
191
193
  </ControlActions>
192
194
  </PropKeyProvider>
193
195
  <PropKeyProvider bind={ 'isTargetBlank' }>
194
- <SwitchControl disabled={ propContext.disabled || ! value } />
196
+ <Grid container alignItems="center" flexWrap="nowrap" justifyContent="space-between">
197
+ <Grid item>
198
+ <ControlFormLabel>{ __( 'Open in a new tab', 'elementor' ) }</ControlFormLabel>
199
+ </Grid>
200
+ <Grid item sx={ { marginInlineEnd: -1 } }>
201
+ <SwitchControlComponent disabled={ propContext.disabled || ! value } />
202
+ </Grid>
203
+ </Grid>
195
204
  </PropKeyProvider>
196
205
  </Stack>
197
206
  </Collapse>
@@ -215,31 +224,27 @@ const ToggleIconControl = ( { disabled, active, onIconClick, label }: ToggleIcon
215
224
  );
216
225
  };
217
226
 
218
- // @TODO Should be refactored in ED-16323
219
- const SwitchControl = ( { disabled }: { disabled: boolean } ) => {
220
- const { value = false, setValue } = useBoundProp( booleanPropTypeUtil );
227
+ const SwitchControlComponent = ( { disabled }: { disabled: boolean } ) => {
228
+ const { value, setValue } = useBoundProp( booleanPropTypeUtil );
229
+ const isVersion331Active = isExperimentActive( 'e_v_3_31' );
230
+
231
+ if ( isVersion331Active ) {
232
+ return <SwitchControl />;
233
+ }
221
234
 
222
235
  const onClick = () => {
223
236
  setValue( ! value );
224
237
  };
225
238
 
226
- const inputProps = disabled
227
- ? {
228
- style: {
229
- opacity: 0,
230
- },
231
- }
232
- : {};
233
-
234
239
  return (
235
- <Grid container alignItems="center" flexWrap="nowrap" justifyContent="space-between">
236
- <Grid item>
237
- <ControlFormLabel>{ __( 'Open in a new tab', 'elementor' ) }</ControlFormLabel>
238
- </Grid>
239
- <Grid item sx={ { marginInlineEnd: -1 } }>
240
- <Switch checked={ value } onClick={ onClick } disabled={ disabled } inputProps={ inputProps } />
241
- </Grid>
242
- </Grid>
240
+ <Switch
241
+ checked={ value ?? false }
242
+ onClick={ onClick }
243
+ disabled={ disabled }
244
+ inputProps={ {
245
+ ...( disabled ? { style: { opacity: 0 } } : {} ),
246
+ } }
247
+ />
243
248
  );
244
249
  };
245
250
 
@@ -68,7 +68,7 @@ export const LinkedDimensionsControl = createControl(
68
68
  propType={ propType }
69
69
  value={ dimensionsValue }
70
70
  setValue={ setDimensionsValue }
71
- disabled={ disabled }
71
+ isDisabled={ () => disabled }
72
72
  >
73
73
  <Stack direction="row" gap={ 2 } flexWrap="nowrap">
74
74
  { isUsingNestedProps ? (