@elementor/editor-controls 0.24.0 → 0.26.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-controls",
3
3
  "description": "This package contains the controls model and utils for the Elementor editor",
4
- "version": "0.24.0",
4
+ "version": "0.26.0",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -41,9 +41,9 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@elementor/editor-current-user": "0.3.0",
44
- "@elementor/editor-elements": "0.8.0",
45
- "@elementor/editor-props": "0.11.1",
46
- "@elementor/editor-ui": "0.7.0",
44
+ "@elementor/editor-elements": "0.8.1",
45
+ "@elementor/editor-props": "0.12.0",
46
+ "@elementor/editor-ui": "0.7.1",
47
47
  "@elementor/env": "0.3.5",
48
48
  "@elementor/http": "0.1.4",
49
49
  "@elementor/icons": "1.37.0",
@@ -14,6 +14,7 @@ type PropContext< T extends PropValue, P extends PropType > = {
14
14
  setValue: SetValue< T >;
15
15
  value: T | null;
16
16
  propType: P;
17
+ placeholder?: T;
17
18
  };
18
19
 
19
20
  const PropContext = createContext< PropContext< PropValue, PropType > | null >( null );
@@ -27,6 +28,7 @@ export const PropProvider = < T extends PropValue, P extends PropType >( {
27
28
  value,
28
29
  setValue,
29
30
  propType,
31
+ placeholder,
30
32
  }: PropProviderProps< T, P > ) => {
31
33
  return (
32
34
  <PropContext.Provider
@@ -34,6 +36,7 @@ export const PropProvider = < T extends PropValue, P extends PropType >( {
34
36
  value,
35
37
  propType,
36
38
  setValue: setValue as SetValue< PropValue >,
39
+ placeholder,
37
40
  } }
38
41
  >
39
42
  { children }
@@ -19,6 +19,7 @@ export type PropKeyContextValue< T, P > = {
19
19
  setValue: SetValue< T >;
20
20
  value: T;
21
21
  propType: P;
22
+ placeholder?: T;
22
23
  path: PropKey[];
23
24
  };
24
25
 
@@ -60,12 +61,13 @@ const ObjectPropKeyProvider = ( { children, bind }: PropKeyProviderProps ) => {
60
61
  };
61
62
 
62
63
  const value = context.value?.[ bind ];
64
+ const placeholder = context.placeholder?.[ bind ];
63
65
 
64
66
  const propType = context.propType.shape[ bind ];
65
67
 
66
68
  return (
67
69
  <PropKeyContext.Provider
68
- value={ { ...context, value, setValue, bind, propType, path: [ ...( path ?? [] ), bind ] } }
70
+ value={ { ...context, value, setValue, placeholder, bind, propType, path: [ ...( path ?? [] ), bind ] } }
69
71
  >
70
72
  { children }
71
73
  </PropKeyContext.Provider>
@@ -1,3 +1,4 @@
1
+ import { useState } from 'react';
1
2
  import {
2
3
  type CreateOptions,
3
4
  type PropKey,
@@ -15,7 +16,9 @@ type UseBoundProp< TValue extends PropValue > = {
15
16
  setValue: SetValue< TValue | null >;
16
17
  value: TValue;
17
18
  propType: PropType;
19
+ placeholder?: TValue;
18
20
  path: PropKey[];
21
+ restoreValue: () => void;
19
22
  };
20
23
 
21
24
  export function useBoundProp< T extends PropValue = PropValue >(): PropKeyContextValue< T, PropType >;
@@ -29,12 +32,18 @@ export function useBoundProp< TKey extends string, TValue extends PropValue >(
29
32
  ) {
30
33
  const propKeyContext = usePropKeyContext();
31
34
 
35
+ const { isValid, validate, restoreValue } = useValidation( propKeyContext.propType );
36
+
32
37
  // allow using the hook without a propTypeUtil, with no modifications or validations.
33
38
  if ( ! propTypeUtil ) {
34
39
  return propKeyContext;
35
40
  }
36
41
 
37
42
  function setValue( value: TValue | null, options: CreateOptions, meta: { bind?: PropKey } ) {
43
+ if ( ! validate( value ) ) {
44
+ return;
45
+ }
46
+
38
47
  if ( value === null ) {
39
48
  return propKeyContext?.setValue( null, options, meta );
40
49
  }
@@ -45,15 +54,45 @@ export function useBoundProp< TKey extends string, TValue extends PropValue >(
45
54
  const propType = resolveUnionPropType( propKeyContext.propType, propTypeUtil.key );
46
55
 
47
56
  const value = propTypeUtil.extract( propKeyContext.value ?? propType.default ?? null );
57
+ const placeholder = propTypeUtil.extract( propKeyContext.placeholder ?? null );
48
58
 
49
59
  return {
50
60
  ...propKeyContext,
51
- setValue,
52
- value,
53
61
  propType,
62
+ setValue,
63
+ value: isValid ? value : null,
64
+ restoreValue,
65
+ placeholder,
54
66
  };
55
67
  }
56
68
 
69
+ const useValidation = ( propType: PropType ) => {
70
+ const [ isValid, setIsValid ] = useState( true );
71
+
72
+ // If the value does not pass the prop type validation, set the isValid state to false.
73
+ // This will prevent the value from being set in the model, and its fallback will be used instead.
74
+ const validate = ( value: PropValue | null ) => {
75
+ let valid = true;
76
+
77
+ if ( propType.settings.required && value === null ) {
78
+ valid = false;
79
+ }
80
+
81
+ setIsValid( valid );
82
+
83
+ return valid;
84
+ };
85
+
86
+ const restoreValue = () => setIsValid( true );
87
+
88
+ return {
89
+ isValid,
90
+ setIsValid,
91
+ validate,
92
+ restoreValue,
93
+ };
94
+ };
95
+
57
96
  // utils
58
97
  const resolveUnionPropType = ( propType: PropType, key: string ): PropType => {
59
98
  let resolvedPropType = propType;
@@ -142,7 +142,13 @@ export const Repeater = < T, >( {
142
142
 
143
143
  return (
144
144
  <SectionContent>
145
- <Stack direction="row" justifyContent="start" alignItems="center" gap={ 1 }>
145
+ <Stack
146
+ direction="row"
147
+ justifyContent="start"
148
+ alignItems="center"
149
+ gap={ 1 }
150
+ sx={ { marginInlineEnd: -0.75 } }
151
+ >
146
152
  <Typography component="label" variant="caption" color="text.secondary">
147
153
  { label }
148
154
  </Typography>
@@ -9,25 +9,41 @@ type TextFieldInnerSelectionProps = {
9
9
  type: string;
10
10
  value: PropValue;
11
11
  onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
12
+ onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
13
+ onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
12
14
  endAdornment: React.ReactNode;
13
15
  startAdornment?: React.ReactNode;
14
16
  };
15
17
 
16
18
  export const TextFieldInnerSelection = forwardRef(
17
- ( { placeholder, type, value, onChange, endAdornment, startAdornment }: TextFieldInnerSelectionProps, ref ) => {
19
+ (
20
+ {
21
+ placeholder,
22
+ type,
23
+ value,
24
+ onChange,
25
+ onBlur,
26
+ onKeyDown,
27
+ endAdornment,
28
+ startAdornment,
29
+ }: TextFieldInnerSelectionProps,
30
+ ref
31
+ ) => {
18
32
  return (
19
33
  <TextField
34
+ ref={ ref }
20
35
  size="tiny"
21
36
  fullWidth
22
37
  type={ type }
23
38
  value={ value }
24
39
  onChange={ onChange }
40
+ onKeyDown={ onKeyDown }
41
+ onBlur={ onBlur }
25
42
  placeholder={ placeholder }
26
43
  InputProps={ {
27
44
  endAdornment,
28
45
  startAdornment,
29
46
  } }
30
- ref={ ref }
31
47
  />
32
48
  );
33
49
  }
@@ -86,6 +86,8 @@ export const FontFamilyControl = createControl( ( { fontFamilies }: FontFamilyCo
86
86
 
87
87
  <Box px={ 1.5 } pb={ 1 }>
88
88
  <TextField
89
+ // eslint-disable-next-line jsx-a11y/no-autofocus
90
+ autoFocus
89
91
  fullWidth
90
92
  size={ SIZE }
91
93
  value={ searchValue }
@@ -149,6 +149,7 @@ export const LinkControl = createControl( ( props: Props ) => {
149
149
  sx={ {
150
150
  justifyContent: 'space-between',
151
151
  alignItems: 'center',
152
+ marginInlineEnd: -0.75,
152
153
  } }
153
154
  >
154
155
  <ControlFormLabel>{ __( 'Link', 'elementor' ) }</ControlFormLabel>
@@ -222,7 +223,7 @@ const SwitchControl = ( { disabled }: { disabled: boolean } ) => {
222
223
  <Grid item>
223
224
  <ControlFormLabel>{ __( 'Open in a new tab', 'elementor' ) }</ControlFormLabel>
224
225
  </Grid>
225
- <Grid item>
226
+ <Grid item sx={ { marginInlineEnd: -1 } }>
226
227
  <Switch checked={ value } onClick={ onClick } disabled={ disabled } inputProps={ inputProps } />
227
228
  </Grid>
228
229
  </Grid>
@@ -9,6 +9,8 @@ import { createControl } from '../create-control';
9
9
  const isEmptyOrNaN = ( value?: string | number | null ) =>
10
10
  value === null || value === undefined || value === '' || Number.isNaN( Number( value ) );
11
11
 
12
+ const RESTRICTED_INPUT_KEYS = [ 'e', 'E', '+', '-' ];
13
+
12
14
  export const NumberControl = createControl(
13
15
  ( {
14
16
  placeholder,
@@ -49,6 +51,11 @@ export const NumberControl = createControl(
49
51
  onChange={ handleChange }
50
52
  placeholder={ placeholder }
51
53
  inputProps={ { step } }
54
+ onKeyDown={ ( event: KeyboardEvent ) => {
55
+ if ( RESTRICTED_INPUT_KEYS.includes( event.key ) ) {
56
+ event.preventDefault();
57
+ }
58
+ } }
52
59
  />
53
60
  </ControlActions>
54
61
  );
@@ -25,7 +25,7 @@ type SizeControlProps = {
25
25
 
26
26
  export const SizeControl = createControl(
27
27
  ( { units = defaultUnits, extendedValues = [], placeholder, startIcon }: SizeControlProps ) => {
28
- const { value: sizeValue, setValue: setSizeValue } = useBoundProp( sizePropTypeUtil );
28
+ const { value: sizeValue, setValue: setSizeValue, restoreValue } = useBoundProp( sizePropTypeUtil );
29
29
 
30
30
  const [ state, setState ] = useSyncExternalState( {
31
31
  external: sizeValue,
@@ -50,21 +50,21 @@ export const SizeControl = createControl(
50
50
  } ) );
51
51
  };
52
52
 
53
- const inputProps = {
54
- size: state.size,
55
- unit: state.unit,
56
- placeholder,
57
- startIcon,
58
- units,
59
- extendedValues,
60
- handleSizeChange,
61
- handleUnitChange,
62
- };
53
+ const Input = extendedValues?.length ? ExtendedSizeInput : SizeInput;
63
54
 
64
- if ( extendedValues?.length ) {
65
- return <ExtendedSizeInput { ...inputProps } />;
66
- }
67
- return <SizeInput { ...inputProps } />;
55
+ return (
56
+ <Input
57
+ size={ state.size }
58
+ unit={ state.unit }
59
+ placeholder={ placeholder }
60
+ startIcon={ startIcon }
61
+ units={ units }
62
+ extendedValues={ extendedValues }
63
+ handleSizeChange={ handleSizeChange }
64
+ handleUnitChange={ handleUnitChange }
65
+ onBlur={ restoreValue }
66
+ />
67
+ );
68
68
  }
69
69
  );
70
70
 
@@ -99,16 +99,20 @@ type SizeInputProps = {
99
99
  startIcon?: React.ReactNode;
100
100
  units: Unit[];
101
101
  extendedValues?: ExtendedValue[];
102
+ onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
102
103
  handleUnitChange: ( unit: Unit ) => void;
103
104
  handleSizeChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
104
105
  };
105
106
 
107
+ const RESTRICTED_INPUT_KEYS = [ 'e', 'E', '+', '-' ];
108
+
106
109
  const SizeInput = ( {
107
110
  units,
108
111
  handleUnitChange,
109
112
  handleSizeChange,
110
113
  placeholder,
111
114
  startIcon,
115
+ onBlur,
112
116
  size,
113
117
  unit,
114
118
  }: SizeInputProps ) => {
@@ -129,6 +133,12 @@ const SizeInput = ( {
129
133
  type="number"
130
134
  value={ Number.isNaN( size ) ? '' : size }
131
135
  onChange={ handleSizeChange }
136
+ onBlur={ onBlur }
137
+ onKeyDown={ ( event ) => {
138
+ if ( RESTRICTED_INPUT_KEYS.includes( event.key ) ) {
139
+ event.preventDefault();
140
+ }
141
+ } }
132
142
  />
133
143
  </ControlActions>
134
144
  );
@@ -20,7 +20,7 @@ export const ToggleControl = createControl(
20
20
  size = 'tiny',
21
21
  exclusive = true,
22
22
  }: ToggleControlProps< StringPropValue[ 'value' ] > ) => {
23
- const { value, setValue } = useBoundProp( stringPropTypeUtil );
23
+ const { value, setValue, placeholder } = useBoundProp( stringPropTypeUtil );
24
24
 
25
25
  const exclusiveValues = options.filter( ( option ) => option.exclusive ).map( ( option ) => option.value );
26
26
 
@@ -44,14 +44,14 @@ export const ToggleControl = createControl(
44
44
  return exclusive ? (
45
45
  <ControlToggleButtonGroup
46
46
  { ...toggleButtonGroupProps }
47
- value={ value ?? null }
47
+ value={ value ?? placeholder ?? null }
48
48
  onChange={ setValue }
49
49
  exclusive={ true }
50
50
  />
51
51
  ) : (
52
52
  <ControlToggleButtonGroup
53
53
  { ...toggleButtonGroupProps }
54
- value={ value?.split( ' ' ) ?? [] }
54
+ value={ ( value ?? placeholder )?.split( ' ' ) ?? [] }
55
55
  onChange={ handleNonExclusiveToggle }
56
56
  exclusive={ false }
57
57
  />