@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/CHANGELOG.md +26 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +150 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +125 -56
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/bound-prop-context/prop-context.tsx +3 -0
- package/src/bound-prop-context/prop-key-context.tsx +3 -1
- package/src/bound-prop-context/use-bound-prop.ts +41 -2
- package/src/components/repeater.tsx +7 -1
- package/src/components/text-field-inner-selection.tsx +18 -2
- package/src/controls/font-family-control/font-family-control.tsx +2 -0
- package/src/controls/link-control.tsx +2 -1
- package/src/controls/number-control.tsx +7 -0
- package/src/controls/size-control.tsx +25 -15
- package/src/controls/toggle-control.tsx +3 -3
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.
|
|
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.
|
|
45
|
-
"@elementor/editor-props": "0.
|
|
46
|
-
"@elementor/editor-ui": "0.7.
|
|
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
|
|
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
|
-
(
|
|
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
|
}
|
|
@@ -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
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
/>
|