@elementor/editor-controls 3.32.0-68 → 3.32.0-69
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/dist/index.d.mts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +782 -708
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +562 -489
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -14
- package/src/bound-prop-context/prop-context.tsx +2 -1
- package/src/bound-prop-context/use-bound-prop.ts +8 -4
- package/src/components/number-input.tsx +41 -0
- package/src/components/size-control/size-input.tsx +5 -9
- package/src/components/size-control/text-field-inner-selection.tsx +9 -6
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +2 -0
- package/src/controls/linked-dimensions-control.tsx +19 -2
- package/src/controls/number-control.tsx +20 -21
- package/src/controls/position-control.tsx +8 -2
- package/src/controls/size-control.tsx +16 -7
- package/src/controls/transform-control/functions/axis-row.tsx +1 -0
- package/src/hooks/use-sync-external-state.tsx +6 -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": "3.32.0-
|
|
4
|
+
"version": "3.32.0-69",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,21 +40,21 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor-current-user": "3.32.0-
|
|
44
|
-
"@elementor/editor-elements": "3.32.0-
|
|
45
|
-
"@elementor/editor-props": "3.32.0-
|
|
46
|
-
"@elementor/editor-responsive": "3.32.0-
|
|
47
|
-
"@elementor/editor-ui": "3.32.0-
|
|
48
|
-
"@elementor/editor-v1-adapters": "3.32.0-
|
|
49
|
-
"@elementor/env": "3.32.0-
|
|
50
|
-
"@elementor/http-client": "3.32.0-
|
|
43
|
+
"@elementor/editor-current-user": "3.32.0-69",
|
|
44
|
+
"@elementor/editor-elements": "3.32.0-69",
|
|
45
|
+
"@elementor/editor-props": "3.32.0-69",
|
|
46
|
+
"@elementor/editor-responsive": "3.32.0-69",
|
|
47
|
+
"@elementor/editor-ui": "3.32.0-69",
|
|
48
|
+
"@elementor/editor-v1-adapters": "3.32.0-69",
|
|
49
|
+
"@elementor/env": "3.32.0-69",
|
|
50
|
+
"@elementor/http-client": "3.32.0-69",
|
|
51
51
|
"@elementor/icons": "^1.51.1",
|
|
52
|
-
"@elementor/locations": "3.32.0-
|
|
53
|
-
"@elementor/query": "3.32.0-
|
|
54
|
-
"@elementor/session": "3.32.0-
|
|
52
|
+
"@elementor/locations": "3.32.0-69",
|
|
53
|
+
"@elementor/query": "3.32.0-69",
|
|
54
|
+
"@elementor/session": "3.32.0-69",
|
|
55
55
|
"@elementor/ui": "1.36.8",
|
|
56
|
-
"@elementor/utils": "3.32.0-
|
|
57
|
-
"@elementor/wp-media": "3.32.0-
|
|
56
|
+
"@elementor/utils": "3.32.0-69",
|
|
57
|
+
"@elementor/wp-media": "3.32.0-69",
|
|
58
58
|
"@wordpress/i18n": "^5.13.0",
|
|
59
59
|
"@monaco-editor/react": "^4.7.0"
|
|
60
60
|
},
|
|
@@ -4,8 +4,9 @@ import { type CreateOptions, type PropKey, type PropType, type PropValue } from
|
|
|
4
4
|
|
|
5
5
|
import { HookOutsideProviderError } from './errors';
|
|
6
6
|
|
|
7
|
-
type SetValueMeta = {
|
|
7
|
+
export type SetValueMeta = {
|
|
8
8
|
bind?: PropKey;
|
|
9
|
+
validation?: ( value: PropValue ) => boolean;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export type SetValue< T > = ( value: T, options?: CreateOptions, meta?: SetValueMeta ) => void;
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@elementor/editor-props';
|
|
9
9
|
|
|
10
10
|
import { MissingPropTypeError } from './errors';
|
|
11
|
-
import { type SetValue } from './prop-context';
|
|
11
|
+
import { type SetValue, type SetValueMeta } from './prop-context';
|
|
12
12
|
import { type PropKeyContextValue, usePropKeyContext } from './prop-key-context';
|
|
13
13
|
|
|
14
14
|
type UseBoundProp< TValue extends PropValue > = {
|
|
@@ -46,8 +46,8 @@ export function useBoundProp< TKey extends string, TValue extends PropValue >(
|
|
|
46
46
|
return { ...propKeyContext, disabled } as PropKeyContextValue< PropValue, PropType >;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function setValue( value: TValue | null, options: CreateOptions, meta
|
|
50
|
-
if ( ! validate( value ) ) {
|
|
49
|
+
function setValue( value: TValue | null, options: CreateOptions, meta?: SetValueMeta ) {
|
|
50
|
+
if ( ! validate( value, meta?.validation ) ) {
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -79,13 +79,17 @@ const useValidation = ( propType: PropType ) => {
|
|
|
79
79
|
|
|
80
80
|
// If the value does not pass the prop type validation, set the isValid state to false.
|
|
81
81
|
// This will prevent the value from being set in the model, and its fallback will be used instead.
|
|
82
|
-
const validate = ( value: PropValue | null ) => {
|
|
82
|
+
const validate = ( value: PropValue | null, validation?: ( value: PropValue ) => boolean ) => {
|
|
83
83
|
let valid = true;
|
|
84
84
|
|
|
85
85
|
if ( propType.settings.required && value === null ) {
|
|
86
86
|
valid = false;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
if ( validation && ! validation( value ) ) {
|
|
90
|
+
valid = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
89
93
|
setIsValid( valid );
|
|
90
94
|
|
|
91
95
|
return valid;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { forwardRef, useState } from 'react';
|
|
3
|
+
import { TextField, type TextFieldProps } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
const RESTRICTED_INPUT_KEYS = [ 'e', 'E', '+' ];
|
|
6
|
+
|
|
7
|
+
export const NumberInput = forwardRef( ( props: TextFieldProps, ref ) => {
|
|
8
|
+
const [ key, setKey ] = useState< number >( 0 );
|
|
9
|
+
|
|
10
|
+
const handleKeyDown = ( event: React.KeyboardEvent< HTMLInputElement > ) => {
|
|
11
|
+
blockRestrictedKeys( event, props.inputProps?.min );
|
|
12
|
+
|
|
13
|
+
props.onKeyDown?.( event );
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const handleBlur = ( event: React.FocusEvent< HTMLInputElement > ) => {
|
|
17
|
+
props.onBlur?.( event );
|
|
18
|
+
|
|
19
|
+
const { valid } = event.target.validity;
|
|
20
|
+
|
|
21
|
+
// HTML number input quirk: invalid input (e.g. "-9-") returns value="" but displays "-9-" to user,
|
|
22
|
+
// so when we revert to last valid value we must re-mount the component to actually display it.
|
|
23
|
+
if ( ! valid ) {
|
|
24
|
+
setKey( ( prev ) => prev + 1 );
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return <TextField { ...props } ref={ ref } key={ key } onKeyDown={ handleKeyDown } onBlur={ handleBlur } />;
|
|
29
|
+
} );
|
|
30
|
+
|
|
31
|
+
function blockRestrictedKeys( event: React.KeyboardEvent< HTMLInputElement >, min: number ) {
|
|
32
|
+
const restrictedInputKeys = [ ...RESTRICTED_INPUT_KEYS ];
|
|
33
|
+
|
|
34
|
+
if ( min >= 0 ) {
|
|
35
|
+
restrictedInputKeys.push( '-' );
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if ( restrictedInputKeys.includes( event.key ) ) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -20,10 +20,9 @@ type SizeInputProps = {
|
|
|
20
20
|
handleSizeChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
|
|
21
21
|
popupState: PopupState;
|
|
22
22
|
disabled?: boolean;
|
|
23
|
+
min?: number;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
const RESTRICTED_INPUT_KEYS = [ 'e', 'E', '+', '-' ];
|
|
26
|
-
|
|
27
26
|
export const SizeInput = ( {
|
|
28
27
|
units,
|
|
29
28
|
handleUnitChange,
|
|
@@ -37,6 +36,7 @@ export const SizeInput = ( {
|
|
|
37
36
|
unit,
|
|
38
37
|
popupState,
|
|
39
38
|
disabled,
|
|
39
|
+
min,
|
|
40
40
|
}: SizeInputProps ) => {
|
|
41
41
|
const unitInputBufferRef = useRef( '' );
|
|
42
42
|
const inputType = isUnitExtendedOption( unit ) ? 'text' : 'number';
|
|
@@ -70,7 +70,7 @@ export const SizeInput = ( {
|
|
|
70
70
|
'aria-haspopup': true,
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
const
|
|
73
|
+
const InputProps = {
|
|
74
74
|
...popupAttributes,
|
|
75
75
|
readOnly: isUnitExtendedOption( unit ),
|
|
76
76
|
autoComplete: 'off',
|
|
@@ -110,14 +110,10 @@ export const SizeInput = ( {
|
|
|
110
110
|
type={ inputType }
|
|
111
111
|
value={ inputValue }
|
|
112
112
|
onChange={ handleSizeChange }
|
|
113
|
-
onKeyDown={ ( event ) => {
|
|
114
|
-
if ( RESTRICTED_INPUT_KEYS.includes( event.key ) ) {
|
|
115
|
-
event.preventDefault();
|
|
116
|
-
}
|
|
117
|
-
} }
|
|
118
113
|
onKeyUp={ handleKeyUp }
|
|
119
114
|
onBlur={ onBlur }
|
|
120
|
-
|
|
115
|
+
InputProps={ InputProps }
|
|
116
|
+
inputProps={ { min, step: 'any' } }
|
|
121
117
|
isPopoverOpen={ popupState.isOpen }
|
|
122
118
|
/>
|
|
123
119
|
</Box>
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
InputAdornment,
|
|
10
10
|
Menu,
|
|
11
11
|
styled,
|
|
12
|
-
TextField,
|
|
13
12
|
type TextFieldProps,
|
|
14
13
|
usePopupState,
|
|
15
14
|
} from '@elementor/ui';
|
|
16
15
|
|
|
17
16
|
import { useBoundProp } from '../../bound-prop-context';
|
|
18
17
|
import { DEFAULT_UNIT } from '../../utils/size-control';
|
|
18
|
+
import { NumberInput } from '../number-input';
|
|
19
19
|
|
|
20
20
|
type TextFieldInnerSelectionProps = {
|
|
21
21
|
placeholder?: string;
|
|
@@ -25,9 +25,10 @@ type TextFieldInnerSelectionProps = {
|
|
|
25
25
|
onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
|
|
26
26
|
onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
|
|
27
27
|
onKeyUp?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
|
|
28
|
-
|
|
28
|
+
InputProps: TextFieldProps[ 'InputProps' ] & {
|
|
29
29
|
endAdornment: React.JSX.Element;
|
|
30
30
|
};
|
|
31
|
+
inputProps?: TextFieldProps[ 'inputProps' ];
|
|
31
32
|
disabled?: boolean;
|
|
32
33
|
isPopoverOpen?: boolean;
|
|
33
34
|
};
|
|
@@ -42,6 +43,7 @@ export const TextFieldInnerSelection = forwardRef(
|
|
|
42
43
|
onBlur,
|
|
43
44
|
onKeyDown,
|
|
44
45
|
onKeyUp,
|
|
46
|
+
InputProps,
|
|
45
47
|
inputProps,
|
|
46
48
|
disabled,
|
|
47
49
|
isPopoverOpen,
|
|
@@ -51,25 +53,26 @@ export const TextFieldInnerSelection = forwardRef(
|
|
|
51
53
|
const { placeholder: boundPropPlaceholder } = useBoundProp( sizePropTypeUtil );
|
|
52
54
|
|
|
53
55
|
const getCursorStyle = () => ( {
|
|
54
|
-
input: { cursor:
|
|
56
|
+
input: { cursor: InputProps.readOnly ? 'default !important' : undefined },
|
|
55
57
|
} );
|
|
56
58
|
|
|
57
59
|
return (
|
|
58
|
-
<
|
|
60
|
+
<NumberInput
|
|
59
61
|
ref={ ref }
|
|
60
62
|
sx={ getCursorStyle() }
|
|
61
63
|
size="tiny"
|
|
62
64
|
fullWidth
|
|
63
65
|
type={ type }
|
|
64
66
|
value={ value }
|
|
65
|
-
|
|
67
|
+
onInput={ onChange }
|
|
66
68
|
onKeyDown={ onKeyDown }
|
|
67
69
|
onKeyUp={ onKeyUp }
|
|
68
70
|
disabled={ disabled }
|
|
69
71
|
onBlur={ onBlur }
|
|
70
72
|
focused={ isPopoverOpen ? true : undefined }
|
|
71
73
|
placeholder={ placeholder ?? ( String( boundPropPlaceholder?.size ?? '' ) || undefined ) }
|
|
72
|
-
InputProps={
|
|
74
|
+
InputProps={ InputProps }
|
|
75
|
+
inputProps={ inputProps }
|
|
73
76
|
/>
|
|
74
77
|
);
|
|
75
78
|
}
|
|
@@ -86,6 +86,7 @@ export const BackgroundImageOverlayPosition = () => {
|
|
|
86
86
|
<SizeControl
|
|
87
87
|
startIcon={ <LetterXIcon fontSize={ 'tiny' } /> }
|
|
88
88
|
anchorRef={ rowRef }
|
|
89
|
+
min={ -Number.MAX_SAFE_INTEGER }
|
|
89
90
|
/>
|
|
90
91
|
</PropKeyProvider>
|
|
91
92
|
</Grid>
|
|
@@ -94,6 +95,7 @@ export const BackgroundImageOverlayPosition = () => {
|
|
|
94
95
|
<SizeControl
|
|
95
96
|
startIcon={ <LetterYIcon fontSize={ 'tiny' } /> }
|
|
96
97
|
anchorRef={ rowRef }
|
|
98
|
+
min={ -Number.MAX_SAFE_INTEGER }
|
|
97
99
|
/>
|
|
98
100
|
</PropKeyProvider>
|
|
99
101
|
</Grid>
|
|
@@ -18,10 +18,12 @@ export const LinkedDimensionsControl = createControl(
|
|
|
18
18
|
label,
|
|
19
19
|
isSiteRtl = false,
|
|
20
20
|
extendedOptions,
|
|
21
|
+
min,
|
|
21
22
|
}: {
|
|
22
23
|
label: string;
|
|
23
24
|
isSiteRtl?: boolean;
|
|
24
25
|
extendedOptions?: ExtendedOption[];
|
|
26
|
+
min?: number;
|
|
25
27
|
} ) => {
|
|
26
28
|
const {
|
|
27
29
|
value: sizeValue,
|
|
@@ -110,6 +112,7 @@ export const LinkedDimensionsControl = createControl(
|
|
|
110
112
|
isLinked={ isLinked }
|
|
111
113
|
extendedOptions={ extendedOptions }
|
|
112
114
|
anchorRef={ gridRowRefs[ index ] }
|
|
115
|
+
min={ min }
|
|
113
116
|
/>
|
|
114
117
|
</Grid>
|
|
115
118
|
</Grid>
|
|
@@ -127,20 +130,34 @@ const Control = ( {
|
|
|
127
130
|
isLinked,
|
|
128
131
|
extendedOptions,
|
|
129
132
|
anchorRef,
|
|
133
|
+
min,
|
|
130
134
|
}: {
|
|
131
135
|
bind: PropKey;
|
|
132
136
|
startIcon: React.ReactNode;
|
|
133
137
|
isLinked: boolean;
|
|
134
138
|
extendedOptions?: ExtendedOption[];
|
|
135
139
|
anchorRef: RefObject< HTMLDivElement >;
|
|
140
|
+
min?: number;
|
|
136
141
|
} ) => {
|
|
137
142
|
if ( isLinked ) {
|
|
138
|
-
return
|
|
143
|
+
return (
|
|
144
|
+
<SizeControl
|
|
145
|
+
startIcon={ startIcon }
|
|
146
|
+
extendedOptions={ extendedOptions }
|
|
147
|
+
anchorRef={ anchorRef }
|
|
148
|
+
min={ min }
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
139
151
|
}
|
|
140
152
|
|
|
141
153
|
return (
|
|
142
154
|
<PropKeyProvider bind={ bind }>
|
|
143
|
-
<SizeControl
|
|
155
|
+
<SizeControl
|
|
156
|
+
startIcon={ startIcon }
|
|
157
|
+
extendedOptions={ extendedOptions }
|
|
158
|
+
anchorRef={ anchorRef }
|
|
159
|
+
min={ min }
|
|
160
|
+
/>
|
|
144
161
|
</PropKeyProvider>
|
|
145
162
|
);
|
|
146
163
|
};
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { numberPropTypeUtil } from '@elementor/editor-props';
|
|
3
|
-
import { InputAdornment
|
|
3
|
+
import { InputAdornment } from '@elementor/ui';
|
|
4
4
|
|
|
5
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
6
|
+
import { NumberInput } from '../components/number-input';
|
|
6
7
|
import ControlActions from '../control-actions/control-actions';
|
|
7
8
|
import { createControl } from '../create-control';
|
|
8
9
|
|
|
9
10
|
const isEmptyOrNaN = ( value?: string | number | null ) =>
|
|
10
11
|
value === null || value === undefined || value === '' || Number.isNaN( Number( value ) );
|
|
11
12
|
|
|
12
|
-
const RESTRICTED_INPUT_KEYS = [ 'e', 'E', '+', '-' ];
|
|
13
|
-
|
|
14
13
|
export const NumberControl = createControl(
|
|
15
14
|
( {
|
|
16
15
|
placeholder: labelPlaceholder,
|
|
17
|
-
max = Number.
|
|
18
|
-
min = -Number.
|
|
16
|
+
max = Number.MAX_SAFE_INTEGER,
|
|
17
|
+
min = -Number.MAX_SAFE_INTEGER,
|
|
19
18
|
step = 1,
|
|
20
19
|
shouldForceInt = false,
|
|
21
20
|
startIcon,
|
|
@@ -27,33 +26,38 @@ export const NumberControl = createControl(
|
|
|
27
26
|
shouldForceInt?: boolean;
|
|
28
27
|
startIcon?: React.ReactNode;
|
|
29
28
|
} ) => {
|
|
30
|
-
const { value, setValue, placeholder, disabled } = useBoundProp( numberPropTypeUtil );
|
|
29
|
+
const { value, setValue, placeholder, disabled, restoreValue } = useBoundProp( numberPropTypeUtil );
|
|
31
30
|
|
|
32
31
|
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
33
|
-
const
|
|
32
|
+
const {
|
|
33
|
+
value: eventValue,
|
|
34
|
+
validity: { valid: isInputValid },
|
|
35
|
+
} = event.target;
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
setValue( null );
|
|
37
|
+
let updatedValue;
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
if ( isEmptyOrNaN( eventValue ) ) {
|
|
40
|
+
updatedValue = null;
|
|
41
|
+
} else {
|
|
42
|
+
const formattedValue = shouldForceInt ? +parseInt( eventValue ) : Number( eventValue );
|
|
43
|
+
updatedValue = Math.min( Math.max( formattedValue, min ), max );
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
setValue( Math.min( Math.max( formattedValue, min ), max ) );
|
|
46
|
+
setValue( updatedValue, undefined, { validation: () => isInputValid } );
|
|
44
47
|
};
|
|
45
48
|
|
|
46
49
|
return (
|
|
47
50
|
<ControlActions>
|
|
48
|
-
<
|
|
51
|
+
<NumberInput
|
|
49
52
|
size="tiny"
|
|
50
53
|
type="number"
|
|
51
54
|
fullWidth
|
|
52
55
|
disabled={ disabled }
|
|
53
56
|
value={ isEmptyOrNaN( value ) ? '' : value }
|
|
54
|
-
|
|
57
|
+
onInput={ handleChange }
|
|
58
|
+
onBlur={ restoreValue }
|
|
55
59
|
placeholder={ labelPlaceholder ?? ( isEmptyOrNaN( placeholder ) ? '' : String( placeholder ) ) }
|
|
56
|
-
inputProps={ { step } }
|
|
60
|
+
inputProps={ { step, min } }
|
|
57
61
|
InputProps={ {
|
|
58
62
|
startAdornment: startIcon ? (
|
|
59
63
|
<InputAdornment position="start" disabled={ disabled }>
|
|
@@ -61,11 +65,6 @@ export const NumberControl = createControl(
|
|
|
61
65
|
</InputAdornment>
|
|
62
66
|
) : undefined,
|
|
63
67
|
} }
|
|
64
|
-
onKeyDown={ ( event: KeyboardEvent ) => {
|
|
65
|
-
if ( RESTRICTED_INPUT_KEYS.includes( event.key ) ) {
|
|
66
|
-
event.preventDefault();
|
|
67
|
-
}
|
|
68
|
-
} }
|
|
69
68
|
/>
|
|
70
69
|
</ControlActions>
|
|
71
70
|
);
|
|
@@ -80,12 +80,18 @@ export const PositionControl = () => {
|
|
|
80
80
|
<Grid container spacing={ 1.5 }>
|
|
81
81
|
<Grid item xs={ 6 }>
|
|
82
82
|
<PropKeyProvider bind={ 'x' }>
|
|
83
|
-
<SizeControl
|
|
83
|
+
<SizeControl
|
|
84
|
+
startIcon={ <LetterXIcon fontSize={ 'tiny' } /> }
|
|
85
|
+
min={ -Number.MAX_SAFE_INTEGER }
|
|
86
|
+
/>
|
|
84
87
|
</PropKeyProvider>
|
|
85
88
|
</Grid>
|
|
86
89
|
<Grid item xs={ 6 }>
|
|
87
90
|
<PropKeyProvider bind={ 'y' }>
|
|
88
|
-
<SizeControl
|
|
91
|
+
<SizeControl
|
|
92
|
+
startIcon={ <LetterYIcon fontSize={ 'tiny' } /> }
|
|
93
|
+
min={ -Number.MAX_SAFE_INTEGER }
|
|
94
|
+
/>
|
|
89
95
|
</PropKeyProvider>
|
|
90
96
|
</Grid>
|
|
91
97
|
</Grid>
|
|
@@ -39,6 +39,7 @@ type BaseSizeControlProps = {
|
|
|
39
39
|
extendedOptions?: ExtendedOption[];
|
|
40
40
|
disableCustom?: boolean;
|
|
41
41
|
anchorRef?: RefObject< HTMLDivElement | null >;
|
|
42
|
+
min?: number;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
type LengthSizeControlProps = BaseSizeControlProps &
|
|
@@ -86,6 +87,7 @@ export const SizeControl = createControl(
|
|
|
86
87
|
anchorRef,
|
|
87
88
|
extendedOptions,
|
|
88
89
|
disableCustom,
|
|
90
|
+
min = 0,
|
|
89
91
|
}: Omit< SizeControlProps, 'variant' > & { variant?: SizeVariant } ) => {
|
|
90
92
|
const {
|
|
91
93
|
value: sizeValue,
|
|
@@ -104,7 +106,8 @@ export const SizeControl = createControl(
|
|
|
104
106
|
|
|
105
107
|
const [ state, setState ] = useSyncExternalState( {
|
|
106
108
|
external: internalState,
|
|
107
|
-
setExternal: ( newState: State | null
|
|
109
|
+
setExternal: ( newState: State | null, options, meta ) =>
|
|
110
|
+
setSizeValue( extractValueFromState( newState ), options, meta ),
|
|
108
111
|
persistWhen: ( newState ) => {
|
|
109
112
|
if ( ! newState?.unit ) {
|
|
110
113
|
return false;
|
|
@@ -135,7 +138,8 @@ export const SizeControl = createControl(
|
|
|
135
138
|
};
|
|
136
139
|
|
|
137
140
|
const handleSizeChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
138
|
-
const
|
|
141
|
+
const size = event.target.value;
|
|
142
|
+
const isInputValid = event.target.validity.valid;
|
|
139
143
|
|
|
140
144
|
if ( controlUnit === 'auto' ) {
|
|
141
145
|
setState( ( prev ) => ( { ...prev, unit: controlUnit } ) );
|
|
@@ -143,11 +147,15 @@ export const SizeControl = createControl(
|
|
|
143
147
|
return;
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
setState(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
setState(
|
|
151
|
+
( prev ) => ( {
|
|
152
|
+
...prev,
|
|
153
|
+
[ controlUnit === 'custom' ? 'custom' : 'numeric' ]: formatSize( size, controlUnit ),
|
|
154
|
+
unit: controlUnit,
|
|
155
|
+
} ),
|
|
156
|
+
undefined,
|
|
157
|
+
{ validation: () => isInputValid }
|
|
158
|
+
);
|
|
151
159
|
};
|
|
152
160
|
|
|
153
161
|
const onInputClick = ( event: React.MouseEvent ) => {
|
|
@@ -207,6 +215,7 @@ export const SizeControl = createControl(
|
|
|
207
215
|
onBlur={ restoreValue }
|
|
208
216
|
onClick={ onInputClick }
|
|
209
217
|
popupState={ popupState }
|
|
218
|
+
min={ min }
|
|
210
219
|
/>
|
|
211
220
|
{ anchorRef?.current && (
|
|
212
221
|
<TextFieldPopover
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
+
import { type CreateOptions } from '@elementor/editor-props';
|
|
3
|
+
|
|
4
|
+
import { type SetValueMeta } from '../bound-prop-context';
|
|
2
5
|
|
|
3
6
|
type UseInternalStateOptions< TValue > = {
|
|
4
7
|
external: TValue | null;
|
|
5
|
-
setExternal: ( value: TValue | null ) => void;
|
|
8
|
+
setExternal: ( value: TValue | null, options?: CreateOptions, meta?: SetValueMeta ) => void;
|
|
6
9
|
persistWhen: ( value: TValue | null ) => boolean;
|
|
7
10
|
fallback: ( value: TValue | null ) => TValue;
|
|
8
11
|
};
|
|
@@ -40,12 +43,12 @@ export const useSyncExternalState = < TValue, >( {
|
|
|
40
43
|
|
|
41
44
|
type SetterFunc = ( value: TValue ) => TValue;
|
|
42
45
|
|
|
43
|
-
const setInternalValue = ( setter: SetterFunc | TValue ) => {
|
|
46
|
+
const setInternalValue = ( setter: SetterFunc | TValue, options?: CreateOptions, meta?: SetValueMeta ) => {
|
|
44
47
|
const setterFn = ( typeof setter === 'function' ? setter : () => setter ) as SetterFunc;
|
|
45
48
|
const updated = setterFn( internal );
|
|
46
49
|
|
|
47
50
|
setInternal( updated );
|
|
48
|
-
setExternal( toExternal( updated ) );
|
|
51
|
+
setExternal( toExternal( updated ), options, meta );
|
|
49
52
|
};
|
|
50
53
|
|
|
51
54
|
return [ internal, setInternalValue ] as const;
|