@elementor/editor-controls 4.1.0-710 → 4.1.0-712

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": "4.1.0-710",
4
+ "version": "4.1.0-712",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,22 +40,22 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor-current-user": "4.1.0-710",
44
- "@elementor/editor-elements": "4.1.0-710",
45
- "@elementor/editor-props": "4.1.0-710",
46
- "@elementor/editor-responsive": "4.1.0-710",
47
- "@elementor/editor-ui": "4.1.0-710",
48
- "@elementor/editor-v1-adapters": "4.1.0-710",
49
- "@elementor/env": "4.1.0-710",
50
- "@elementor/http-client": "4.1.0-710",
43
+ "@elementor/editor-current-user": "4.1.0-712",
44
+ "@elementor/editor-elements": "4.1.0-712",
45
+ "@elementor/editor-props": "4.1.0-712",
46
+ "@elementor/editor-responsive": "4.1.0-712",
47
+ "@elementor/editor-ui": "4.1.0-712",
48
+ "@elementor/editor-v1-adapters": "4.1.0-712",
49
+ "@elementor/env": "4.1.0-712",
50
+ "@elementor/http-client": "4.1.0-712",
51
51
  "@elementor/icons": "^1.68.0",
52
- "@elementor/locations": "4.1.0-710",
53
- "@elementor/events": "4.1.0-710",
54
- "@elementor/query": "4.1.0-710",
55
- "@elementor/session": "4.1.0-710",
52
+ "@elementor/locations": "4.1.0-712",
53
+ "@elementor/events": "4.1.0-712",
54
+ "@elementor/query": "4.1.0-712",
55
+ "@elementor/session": "4.1.0-712",
56
56
  "@elementor/ui": "1.36.17",
57
- "@elementor/utils": "4.1.0-710",
58
- "@elementor/wp-media": "4.1.0-710",
57
+ "@elementor/utils": "4.1.0-712",
58
+ "@elementor/wp-media": "4.1.0-712",
59
59
  "@wordpress/i18n": "^5.13.0",
60
60
  "@monaco-editor/react": "^4.7.0",
61
61
  "dayjs": "^1.11.18",
@@ -85,7 +85,7 @@ const BccField = () => <EmailField bind="bcc" label={ __( 'Bcc', 'elementor' ) }
85
85
  const MetaDataField = () => (
86
86
  <PropKeyProvider bind="meta-data">
87
87
  <Stack gap={ 0.5 }>
88
- <ControlLabel>{ __( 'Metadata', 'elementor' ) }</ControlLabel>
88
+ <ControlFormLabel>{ __( 'Metadata', 'elementor' ) }</ControlFormLabel>
89
89
  <ChipsControl
90
90
  options={ [
91
91
  { label: __( 'Date', 'elementor' ), value: 'date' },
@@ -139,7 +139,7 @@ export const EmailFormActionControl = createControl( () => {
139
139
  return (
140
140
  <PropProvider { ...propContext } value={ value } setValue={ setValue }>
141
141
  <Stack gap={ 2 }>
142
- <ControlFormLabel>{ __( 'Email settings', 'elementor' ) }</ControlFormLabel>
142
+ <ControlLabel>{ __( 'Email settings', 'elementor' ) }</ControlLabel>
143
143
  <SendToField />
144
144
  <SubjectField />
145
145
  <MessageField />
@@ -3,7 +3,6 @@ import type * as React from 'react';
3
3
  import { useTypingBuffer } from '../../../hooks/use-typing-buffer';
4
4
  import { type SizeUnit } from '../types';
5
5
  import { isExtendedUnit } from '../utils/is-extended-unit';
6
- import { isNumericValue } from '../utils/is-numeric-value';
7
6
 
8
7
  const UNIT_KEY_PATTERN = /^[a-zA-Z%]$/;
9
8
 
@@ -53,3 +52,15 @@ export const useSizeUnitKeyboard = ( { unit, units, onUnitChange }: Props ) => {
53
52
 
54
53
  return { onUnitKeyDown };
55
54
  };
55
+
56
+ const isNumericValue = ( value: unknown ): boolean => {
57
+ if ( typeof value === 'number' ) {
58
+ return ! isNaN( value );
59
+ }
60
+
61
+ if ( typeof value === 'string' ) {
62
+ return value.trim() !== '' && ! isNaN( Number( value ) );
63
+ }
64
+
65
+ return false;
66
+ };
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
2
2
  import { type SizePropValue } from '@elementor/editor-props';
3
3
 
4
4
  import { useSyncExternalState } from '../../../hooks/use-sync-external-state';
5
- import { type SizeUnit } from '../types';
5
+ import { type SetSizeValue, type SizeUnit } from '../types';
6
6
  import { isExtendedUnit } from '../utils/is-extended-unit';
7
7
  import { createDefaultSizeValue, resolveSizeOnUnitChange, resolveSizeValue } from '../utils/resolve-size-value';
8
8
 
@@ -10,39 +10,35 @@ type SizeValue = SizePropValue[ 'value' ];
10
10
 
11
11
  type UseSizeValueProps< T, U > = {
12
12
  value: T | null;
13
- onChange: ( value: T ) => void;
13
+ setValue: SetSizeValue< T >;
14
14
  units: U[];
15
15
  defaultUnit?: U;
16
16
  };
17
17
 
18
18
  export const useSizeValue = < T extends SizeValue, U extends SizeUnit >( {
19
19
  value,
20
- onChange,
20
+ setValue,
21
21
  units,
22
22
  defaultUnit,
23
23
  }: UseSizeValueProps< T, U > ) => {
24
24
  const resolvedValue = useMemo(
25
- () => resolveSizeValue( value, { units, defaultUnit } ),
25
+ () => resolveSizeValue( value, { units, defaultUnit } ) as T,
26
26
  // eslint-disable-next-line react-hooks/exhaustive-deps
27
27
  [ value?.size, value?.unit, defaultUnit ]
28
28
  );
29
29
 
30
30
  const [ sizeValue, setSizeValue ] = useSyncExternalState< T >( {
31
- external: resolvedValue as T,
32
- setExternal: ( newState ) => {
33
- // TODO we need to check behaviour that low level doesn't set to null only the high level components size component
34
- // This will fix the issue of if size is empty string '' it gets sends to the model
35
- // but on blur the size component set to null.
36
- // But we need to test this behaviour
31
+ external: resolvedValue,
32
+ setExternal: ( newState, options, meta ) => {
37
33
  if ( newState !== null ) {
38
- onChange( newState );
34
+ setValue( newState, options, meta );
39
35
  }
40
- }, // TODO we will need to handle options, meta if context need them
41
- persistWhen: ( next ) => hasChanged( next, resolvedValue as T ),
42
- fallback: () => createDefaultSizeValue< T >( units, defaultUnit ),
36
+ },
37
+ persistWhen: ( next ) => hasChanged( next, resolvedValue ),
38
+ fallback: () => createDefaultSizeValue( units, defaultUnit ),
43
39
  } );
44
40
 
45
- const setSize = ( newSize: string ) => {
41
+ const setSize = ( newSize: string, isInputValid = true ) => {
46
42
  if ( isExtendedUnit( sizeValue.unit ) ) {
47
43
  return;
48
44
  }
@@ -55,7 +51,7 @@ export const useSizeValue = < T extends SizeValue, U extends SizeUnit >( {
55
51
  size: trimmed && ! isNaN( parsed ) ? parsed : '',
56
52
  };
57
53
 
58
- setSizeValue( newState );
54
+ setSizeValue( newState, undefined, { validation: () => isInputValid } );
59
55
  };
60
56
 
61
57
  const setUnit = ( unit: SizeValue[ 'unit' ] ) => {
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { type RefObject, useEffect } from 'react';
3
3
  import type { SizePropValue } from '@elementor/editor-props';
4
- import { usePopupState } from '@elementor/ui';
4
+ import { useActiveBreakpoint } from '@elementor/editor-responsive';
5
+ import { Box, usePopupState } from '@elementor/ui';
5
6
 
6
7
  import { SizeField, type SizeFieldProps } from './size-field';
7
8
  import { TextFieldPopover } from './ui/text-field-popover';
@@ -13,24 +14,31 @@ type SizeUnit = SizePropValue[ 'value' ][ 'unit' ];
13
14
 
14
15
  type Props = SizeFieldProps< SizeValue, SizeUnit > & {
15
16
  anchorRef?: RefObject< HTMLDivElement | null >;
17
+ isUnitActive?: boolean;
18
+ SizeFieldWrapper?: React.ComponentType< { children: React.ReactNode } >;
16
19
  };
17
20
 
18
- export const SizeComponent = ( { anchorRef, ...sizeFieldProps }: Props ) => {
21
+ export const SizeComponent = ( {
22
+ anchorRef,
23
+ isUnitActive,
24
+ SizeFieldWrapper = React.Fragment,
25
+ ...sizeFieldProps
26
+ }: Props ) => {
19
27
  const popupState = usePopupState( { variant: 'popover' } );
28
+ const activeBreakpoint = useActiveBreakpoint();
20
29
 
21
30
  const isCustomUnit = sizeFieldProps?.value?.unit === EXTENDED_UNITS.custom;
22
31
  const hasCustomUnitOption = sizeFieldProps.units.includes( EXTENDED_UNITS.custom );
23
32
 
24
33
  useEffect( () => {
25
- if ( isCustomUnit && anchorRef?.current && ! popupState.isOpen ) {
26
- popupState.open( anchorRef?.current );
34
+ if ( popupState && popupState.isOpen ) {
35
+ popupState.close();
27
36
  }
28
-
29
37
  // eslint-disable-next-line react-hooks/exhaustive-deps
30
- }, [ anchorRef, isCustomUnit ] );
38
+ }, [ activeBreakpoint ] );
31
39
 
32
40
  const handleCustomSizeChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
33
- sizeFieldProps.onChange( {
41
+ sizeFieldProps.setValue( {
34
42
  size: event.target.value,
35
43
  unit: EXTENDED_UNITS.custom,
36
44
  } );
@@ -42,6 +50,12 @@ export const SizeComponent = ( { anchorRef, ...sizeFieldProps }: Props ) => {
42
50
  }
43
51
  };
44
52
 
53
+ const handleUnitChange = ( unit: SizeUnit ) => {
54
+ if ( unit === EXTENDED_UNITS.custom && anchorRef?.current ) {
55
+ popupState.open( anchorRef.current );
56
+ }
57
+ };
58
+
45
59
  const popupAttributes = {
46
60
  'aria-controls': popupState.isOpen ? popupState.popupId : undefined,
47
61
  'aria-haspopup': true,
@@ -49,16 +63,23 @@ export const SizeComponent = ( { anchorRef, ...sizeFieldProps }: Props ) => {
49
63
 
50
64
  return (
51
65
  <>
52
- <SizeField
53
- { ...sizeFieldProps }
54
- InputProps={ {
55
- ...popupAttributes,
56
- onClick: handleSizeFieldClick,
57
- } }
58
- unitSelectorProps={ {
59
- menuItemsAttributes: hasCustomUnitOption ? { custom: popupAttributes } : undefined,
60
- } }
61
- />
66
+ <SizeFieldWrapper>
67
+ <Box>
68
+ <SizeField
69
+ focused={ popupState.isOpen ? true : undefined }
70
+ onUnitChange={ handleUnitChange }
71
+ InputProps={ {
72
+ ...popupAttributes,
73
+ onClick: handleSizeFieldClick,
74
+ } }
75
+ unitSelectorProps={ {
76
+ menuItemsAttributes: hasCustomUnitOption ? { custom: popupAttributes } : undefined,
77
+ isActive: isUnitActive,
78
+ } }
79
+ { ...sizeFieldProps }
80
+ />
81
+ </Box>
82
+ </SizeFieldWrapper>
62
83
  { popupState.isOpen && anchorRef?.current && (
63
84
  <TextFieldPopover
64
85
  popupState={ popupState }
@@ -1,13 +1,14 @@
1
1
  import * as React from 'react';
2
- import type { PropValue, SizePropValue } from '@elementor/editor-props';
2
+ import type { SizePropValue } from '@elementor/editor-props';
3
3
  import { MathFunctionIcon } from '@elementor/icons';
4
4
  import { InputAdornment, type TextFieldProps } from '@elementor/ui';
5
5
 
6
6
  import { useSizeUnitKeyboard } from './hooks/use-size-unit-keyboard';
7
7
  import { useSizeValue } from './hooks/use-size-value';
8
- import { type SizeUnit } from './types';
8
+ import { type SetSizeValue, type SizeUnit } from './types';
9
9
  import { SizeInput } from './ui/size-input';
10
10
  import { UnitSelector, type UnitSelectorProps } from './ui/unit-selector';
11
+ import { hasSizeValue } from './utils/has-size-value';
11
12
  import { isExtendedUnit } from './utils/is-extended-unit';
12
13
 
13
14
  export type SizeFieldProps< TValue extends SizePropValue[ 'value' ], TUnit extends SizeUnit > = {
@@ -16,10 +17,14 @@ export type SizeFieldProps< TValue extends SizePropValue[ 'value' ], TUnit exten
16
17
  placeholder?: string;
17
18
  defaultUnit?: SizeUnit;
18
19
  startIcon?: React.ReactNode;
19
- onChange: ( value: TValue ) => void;
20
+ setValue: SetSizeValue< TValue >;
20
21
  onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
21
22
  onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
23
+ onUnitChange?: ( unit: SizeUnit ) => void;
22
24
  disabled?: boolean;
25
+ focused?: boolean;
26
+ ariaLabel?: string;
27
+ min?: number;
23
28
  InputProps?: TextFieldProps[ 'InputProps' ];
24
29
  unitSelectorProps?: Partial< UnitSelectorProps< TUnit > >;
25
30
  };
@@ -30,18 +35,30 @@ const UNIT_DISPLAY_LABELS_OVERRIDES: Partial< Record< SizeUnit, React.ReactNode
30
35
 
31
36
  export const SizeField = < T extends SizePropValue[ 'value' ], U extends SizeUnit >( {
32
37
  value,
38
+ focused,
33
39
  disabled,
34
40
  InputProps,
35
41
  defaultUnit,
42
+ placeholder,
43
+ onUnitChange,
36
44
  startIcon,
45
+ ariaLabel,
37
46
  onKeyDown,
38
- onChange,
47
+ setValue,
39
48
  onBlur,
40
49
  units,
50
+ min,
41
51
  unitSelectorProps,
42
52
  }: SizeFieldProps< T, U > ) => {
43
- const { size, unit, setSize, setUnit } = useSizeValue( { value, onChange, units, defaultUnit } );
44
- const { onUnitKeyDown } = useSizeUnitKeyboard( { unit, onUnitChange: setUnit, units } );
53
+ const { size, unit, setSize, setUnit } = useSizeValue( { value, setValue, units, defaultUnit } );
54
+
55
+ const handleUnitChange = ( newUnit: SizeUnit ) => {
56
+ setUnit( newUnit );
57
+
58
+ onUnitChange?.( newUnit );
59
+ };
60
+
61
+ const { onUnitKeyDown } = useSizeUnitKeyboard( { unit, onUnitChange: handleUnitChange, units } );
45
62
 
46
63
  const handleKeyDown = ( event: React.KeyboardEvent< HTMLInputElement > ) => {
47
64
  onUnitKeyDown( event );
@@ -49,15 +66,25 @@ export const SizeField = < T extends SizePropValue[ 'value' ], U extends SizeUni
49
66
  onKeyDown?.( event );
50
67
  };
51
68
 
69
+ const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
70
+ const newSize = event.target.value;
71
+ const isInputValid = event.target.validity.valid;
72
+
73
+ setSize( newSize, isInputValid );
74
+ };
75
+
52
76
  const inputType = isExtendedUnit( unit ) ? 'text' : 'number';
53
77
 
54
78
  return (
55
79
  <SizeInput
80
+ disabled={ disabled }
81
+ focused={ focused }
56
82
  type={ inputType }
57
83
  value={ size }
84
+ placeholder={ placeholder }
58
85
  onBlur={ onBlur }
59
86
  onKeyDown={ handleKeyDown }
60
- onChange={ ( event ) => setSize( event.target.value ) }
87
+ onChange={ handleChange }
61
88
  InputProps={ {
62
89
  ...InputProps,
63
90
  autoComplete: 'off',
@@ -72,18 +99,15 @@ export const SizeField = < T extends SizePropValue[ 'value' ], U extends SizeUni
72
99
  <UnitSelector< U >
73
100
  options={ units }
74
101
  value={ unit as U }
75
- onSelect={ ( newUnit ) => setUnit( newUnit ) }
76
- isActive={ hasValue( size ) }
102
+ onSelect={ handleUnitChange }
103
+ isActive={ unitSelectorProps?.isActive ?? hasSizeValue( size ) }
77
104
  { ...unitSelectorProps }
78
105
  optionLabelOverrides={ UNIT_DISPLAY_LABELS_OVERRIDES }
79
106
  />
80
107
  </InputAdornment>
81
108
  ),
82
109
  } }
110
+ inputProps={ { min, step: 'any', 'arial-label': ariaLabel } }
83
111
  />
84
112
  );
85
113
  };
86
-
87
- const hasValue = ( value: PropValue ): boolean => {
88
- return Boolean( value ) || value === 0;
89
- };
@@ -1,3 +1,17 @@
1
+ import { type SizeUnit } from '../types';
2
+
3
+ export const getLengthUnits = () => {
4
+ return ( window.elementor?.config?.size_units?.length ?? [] ) as SizeUnit[];
5
+ };
6
+
7
+ export const getAngleUnits = () => {
8
+ return ( window.elementor?.config?.size_units?.angle ?? [] ) as SizeUnit[];
9
+ };
10
+
11
+ export const getTimeUnits = () => {
12
+ return ( window.elementor?.config?.size_units?.time ?? [] ) as SizeUnit[];
13
+ };
14
+
1
15
  export const getExtendedUnits = () => {
2
- return window.elementor?.config?.size_units?.extended_units ?? [];
16
+ return ( window.elementor?.config?.size_units?.extended_units ?? [] ) as SizeUnit[];
3
17
  };
@@ -1,3 +1,7 @@
1
+ import type { CreateOptions } from '@elementor/editor-props';
2
+
3
+ import { type SetValueMeta } from '../../bound-prop-context';
4
+
1
5
  type LengthUnit = 'px' | '%' | 'em' | 'rem' | 'vw' | 'vh' | 'ch';
2
6
  type AngleUnit = 'deg' | 'rad' | 'grad' | 'turn';
3
7
  type TimeUnit = 's' | 'ms';
@@ -7,3 +11,7 @@ type ExtendedSizeOption = 'auto' | 'custom';
7
11
  type Unit = LengthUnit | AngleUnit | TimeUnit;
8
12
 
9
13
  export type SizeUnit = Unit | ExtendedSizeOption;
14
+
15
+ export type SizeVariant = 'length' | 'angle' | 'time';
16
+
17
+ export type SetSizeValue< T > = ( value: T, options?: CreateOptions, meta?: SetValueMeta ) => void;
@@ -0,0 +1,86 @@
1
+ import * as React from 'react';
2
+ import type { RefObject } from 'react';
3
+ import { type CreateOptions, sizePropTypeUtil, type SizePropValue } from '@elementor/editor-props';
4
+
5
+ import { type SetValueMeta, useBoundProp } from '../../bound-prop-context';
6
+ import ControlActions from '../../control-actions/control-actions';
7
+ import { createControl } from '../../create-control';
8
+ import { SizeComponent } from './size-component';
9
+ import { type SizeVariant } from './types';
10
+ import { resolveBoundPropValue } from './utils/resolve-bound-prop-value';
11
+ import { getDefaultUnit } from './utils/settings/get-default-unit';
12
+ import { getSizeUnits } from './utils/settings/get-size-units';
13
+ import { shouldNullifyValue } from './utils/should-nullify-value';
14
+
15
+ type Props = {
16
+ placeholder?: string;
17
+ variant?: SizeVariant;
18
+ anchorRef?: RefObject< HTMLDivElement | null >;
19
+ startIcon?: React.ReactNode;
20
+ ariaLabel?: string;
21
+ min?: number;
22
+ id?: string;
23
+ };
24
+
25
+ export const UnstableSizeControl = createControl(
26
+ ( { variant = 'length', placeholder: propPlaceholder, anchorRef, startIcon, ariaLabel, min }: Props ) => {
27
+ const {
28
+ value,
29
+ setValue,
30
+ propType,
31
+ placeholder: boundPropPlaceholder,
32
+ restoreValue,
33
+ } = useBoundProp( sizePropTypeUtil );
34
+
35
+ const { sizeValue, isUnitHighlighted, placeholder } = resolveBoundPropValue(
36
+ value,
37
+ boundPropPlaceholder,
38
+ propPlaceholder
39
+ );
40
+
41
+ const units = getSizeUnits( propType, variant );
42
+ const defaultUnit = getDefaultUnit( propType );
43
+
44
+ const handleBlur = () => {
45
+ const isRequired = propType.settings.required;
46
+
47
+ if ( shouldNullifyValue( value ) && ! isRequired ) {
48
+ setValue( null );
49
+ }
50
+
51
+ if ( isRequired ) {
52
+ restoreValue();
53
+ }
54
+ };
55
+
56
+ const handleChange = ( newValue: SizePropValue[ 'value' ], options?: CreateOptions, meta?: SetValueMeta ) => {
57
+ setValue( newValue, options, {
58
+ ...meta,
59
+ validation: () => {
60
+ if ( propType.settings.required ) {
61
+ return newValue.size !== '';
62
+ }
63
+
64
+ return meta?.validation ? meta.validation( newValue ) : true;
65
+ },
66
+ } );
67
+ };
68
+
69
+ return (
70
+ <SizeComponent
71
+ units={ units }
72
+ value={ sizeValue }
73
+ anchorRef={ anchorRef }
74
+ placeholder={ placeholder }
75
+ defaultUnit={ defaultUnit }
76
+ isUnitActive={ isUnitHighlighted }
77
+ onBlur={ handleBlur }
78
+ setValue={ handleChange }
79
+ SizeFieldWrapper={ ControlActions }
80
+ startIcon={ startIcon }
81
+ ariaLabel={ ariaLabel }
82
+ min={ min }
83
+ />
84
+ );
85
+ }
86
+ );
@@ -0,0 +1,5 @@
1
+ import { type SizePropValue } from '@elementor/editor-props';
2
+
3
+ export const hasSizeValue = ( size: SizePropValue[ 'value' ][ 'size' ] ): boolean => {
4
+ return Boolean( size ) || size === 0;
5
+ };
@@ -0,0 +1,72 @@
1
+ import { sizePropTypeUtil, type SizePropValue } from '@elementor/editor-props';
2
+
3
+ import { hasSizeValue } from './has-size-value';
4
+ import { EXTENDED_UNITS } from './resolve-size-value';
5
+
6
+ type SizeValue = SizePropValue[ 'value' ] | null;
7
+
8
+ export type ResolvedBoundProp = {
9
+ sizeValue: SizeValue;
10
+ isUnitHighlighted: boolean;
11
+ placeholder: string | undefined;
12
+ };
13
+
14
+ export const resolveBoundPropValue = < T extends SizeValue >(
15
+ value?: T | null,
16
+ boundPropPlaceholder?: T | null,
17
+ propPlaceholder?: string
18
+ ): ResolvedBoundProp => {
19
+ let sizeValue: T | null = null;
20
+
21
+ if ( validateSizeValue( value ) ) {
22
+ sizeValue = value;
23
+ } else if ( validateSizeValue( boundPropPlaceholder ) ) {
24
+ sizeValue = { size: '', unit: boundPropPlaceholder?.unit } as T;
25
+ }
26
+
27
+ return {
28
+ sizeValue,
29
+ isUnitHighlighted: shouldHighlightUnit( value ),
30
+ placeholder: resolvePlaceholder( propPlaceholder, boundPropPlaceholder ),
31
+ };
32
+ };
33
+
34
+ const validateSizeValue = ( value?: SizeValue | null ): value is SizeValue => {
35
+ if ( ! value ) {
36
+ return false;
37
+ }
38
+
39
+ const sizePropValue = sizePropTypeUtil.create( value );
40
+
41
+ return sizePropTypeUtil.isValid( sizePropValue );
42
+ };
43
+
44
+ const resolvePlaceholder = ( propPlaceholder?: string, boundPropPlaceholder?: SizeValue ): string | undefined => {
45
+ if ( propPlaceholder ) {
46
+ return propPlaceholder;
47
+ }
48
+
49
+ const size = boundPropPlaceholder?.size;
50
+
51
+ if ( size === undefined ) {
52
+ return undefined;
53
+ }
54
+
55
+ if ( typeof size === 'number' ) {
56
+ return size.toString();
57
+ }
58
+
59
+ return size;
60
+ };
61
+
62
+ const shouldHighlightUnit = ( value?: SizePropValue[ 'value' ] | null ) => {
63
+ if ( ! value ) {
64
+ return false;
65
+ }
66
+
67
+ if ( value.unit === EXTENDED_UNITS.auto ) {
68
+ return true;
69
+ }
70
+
71
+ return hasSizeValue( value.size );
72
+ };
@@ -51,10 +51,10 @@ export const resolveSizeOnUnitChange = (
51
51
  };
52
52
 
53
53
  export const createDefaultSizeValue = < T extends SizeValue >( units: SizeUnit[], defaultUnit?: SizeUnit ): T => {
54
- let unit = units[ 0 ];
54
+ let [ unit ] = units;
55
55
 
56
56
  if ( defaultUnit !== undefined ) {
57
- unit = resolveFallbackUnit( defaultUnit, units ) as SizeUnit;
57
+ unit = resolveFallbackUnit( defaultUnit, units );
58
58
  }
59
59
 
60
60
  return { size: DEFAULT_SIZE, unit } as T;
@@ -64,7 +64,7 @@ const resolveFallbackUnit = < TUnit extends SizeUnit >(
64
64
  unit: TUnit,
65
65
  units: readonly TUnit[],
66
66
  defaultUnit?: TUnit
67
- ): TUnit | string => {
67
+ ): TUnit => {
68
68
  if ( units.includes( unit ) ) {
69
69
  return unit;
70
70
  }
@@ -80,5 +80,6 @@ const sanitizeSize = ( size: SizeValue[ 'size' ] ): SizeValue[ 'size' ] => {
80
80
  if ( typeof size === 'number' && isNaN( size ) ) {
81
81
  return DEFAULT_SIZE;
82
82
  }
83
+
83
84
  return size;
84
85
  };
@@ -0,0 +1,7 @@
1
+ import type { PropType } from '@elementor/editor-props';
2
+
3
+ import { getPropTypeSettings } from './get-prop-type-settings';
4
+
5
+ export const getDefaultUnit = ( propType: PropType ) => {
6
+ return getPropTypeSettings( propType )?.default_unit;
7
+ };
@@ -0,0 +1,12 @@
1
+ import { type PropType } from '@elementor/editor-props';
2
+
3
+ import { type SizeUnit } from '../../types';
4
+
5
+ type Settings = {
6
+ units?: SizeUnit[];
7
+ default_unit?: SizeUnit;
8
+ };
9
+
10
+ export const getPropTypeSettings = ( propType: PropType ) => {
11
+ return propType.settings as Settings;
12
+ };
@@ -0,0 +1,23 @@
1
+ import { type PropType } from '@elementor/editor-props';
2
+
3
+ import { getAngleUnits, getLengthUnits, getTimeUnits } from '../../sync/get-units';
4
+ import { type SizeUnit, type SizeVariant } from '../../types';
5
+ import { getPropTypeSettings } from './get-prop-type-settings';
6
+
7
+ const getVariantUnits = ( variant: SizeVariant ): SizeUnit[] => {
8
+ const map: Record< SizeVariant, () => SizeUnit[] > = {
9
+ length: getLengthUnits,
10
+ angle: getAngleUnits,
11
+ time: getTimeUnits,
12
+ };
13
+
14
+ return map[ variant ]();
15
+ };
16
+
17
+ const getSettingsUnits = ( propType: PropType ) => {
18
+ return getPropTypeSettings( propType )?.units;
19
+ };
20
+
21
+ export const getSizeUnits = ( propType: PropType, variant: SizeVariant ): SizeUnit[] => {
22
+ return getSettingsUnits( propType ) ?? getVariantUnits( variant );
23
+ };
@@ -0,0 +1,15 @@
1
+ import { type SizePropValue } from '@elementor/editor-props';
2
+
3
+ import { EXTENDED_UNITS } from './resolve-size-value';
4
+
5
+ type SizeValue = SizePropValue[ 'value' ] | null;
6
+
7
+ const conditions: Array< ( value: SizeValue ) => boolean > = [
8
+ ( value ) => value?.size === null || value?.size === undefined || value?.size === '',
9
+ ( value ) => value?.unit !== EXTENDED_UNITS.auto,
10
+ ( value ) => value?.unit !== EXTENDED_UNITS.custom,
11
+ ];
12
+
13
+ export const shouldNullifyValue = ( value: SizeValue ): boolean => {
14
+ return conditions.every( ( condition ) => condition( value ) );
15
+ };