@elementor/editor-controls 4.0.0-manual → 4.0.1
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 +138 -64
- package/dist/index.d.ts +138 -64
- package/dist/index.js +905 -233
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +890 -225
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
- package/src/components/inline-editor.tsx +16 -57
- package/src/components/promotions/attributes-control.tsx +2 -1
- package/src/components/promotions/display-conditions-control.tsx +2 -1
- package/src/components/promotions/promotion-trigger.tsx +14 -4
- package/src/controls/chips-control.tsx +1 -1
- package/src/controls/email-form-action-control.tsx +5 -5
- package/src/controls/number-control.tsx +8 -2
- package/src/controls/size-control/hooks/use-size-unit-keyboard.tsx +66 -0
- package/src/controls/size-control/hooks/use-size-value.ts +71 -0
- package/src/controls/size-control/size-component.tsx +94 -0
- package/src/controls/size-control/size-field.tsx +113 -0
- package/src/controls/size-control/sync/get-units.ts +17 -0
- package/src/controls/size-control/types.ts +17 -0
- package/src/controls/size-control/ui/size-input.tsx +68 -0
- package/src/controls/size-control/ui/text-field-popover.tsx +78 -0
- package/src/controls/size-control/ui/unit-selector.tsx +80 -0
- package/src/controls/size-control/unstable-size-control.tsx +86 -0
- package/src/controls/size-control/utils/has-size-value.ts +5 -0
- package/src/controls/size-control/utils/is-extended-unit.ts +8 -0
- package/src/controls/size-control/utils/resolve-bound-prop-value.ts +72 -0
- package/src/controls/size-control/utils/resolve-size-value.ts +85 -0
- package/src/controls/size-control/utils/settings/get-default-unit.ts +7 -0
- package/src/controls/size-control/utils/settings/get-prop-type-settings.ts +12 -0
- package/src/controls/size-control/utils/settings/get-size-units.ts +23 -0
- package/src/controls/size-control/utils/should-nullify-value.ts +15 -0
- package/src/controls/svg-media-control.tsx +5 -4
- package/src/controls/transition-control/data.ts +3 -3
- package/src/controls/transition-control/transition-repeater-control.tsx +8 -2
- package/src/controls/transition-control/transition-selector.tsx +7 -0
- package/src/hooks/use-font-families.ts +22 -25
- package/src/index.ts +4 -0
- package/src/utils/tracking.ts +61 -0
|
@@ -0,0 +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
|
+
|
|
15
|
+
export const getExtendedUnits = () => {
|
|
16
|
+
return ( window.elementor?.config?.size_units?.extended_units ?? [] ) as SizeUnit[];
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CreateOptions } from '@elementor/editor-props';
|
|
2
|
+
|
|
3
|
+
import { type SetValueMeta } from '../../bound-prop-context';
|
|
4
|
+
|
|
5
|
+
type LengthUnit = 'px' | '%' | 'em' | 'rem' | 'vw' | 'vh' | 'ch';
|
|
6
|
+
type AngleUnit = 'deg' | 'rad' | 'grad' | 'turn';
|
|
7
|
+
type TimeUnit = 's' | 'ms';
|
|
8
|
+
|
|
9
|
+
type ExtendedSizeOption = 'auto' | 'custom';
|
|
10
|
+
|
|
11
|
+
type Unit = LengthUnit | AngleUnit | TimeUnit;
|
|
12
|
+
|
|
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,68 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import type { PropValue } from '@elementor/editor-props';
|
|
4
|
+
import type { TextFieldProps } from '@elementor/ui';
|
|
5
|
+
|
|
6
|
+
import { NumberInput } from '../../../components/number-input';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
type: 'number' | 'text';
|
|
10
|
+
value: PropValue;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
focused?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
id?: string;
|
|
15
|
+
onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
|
|
16
|
+
onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
|
|
17
|
+
onKeyUp?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
|
|
18
|
+
onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
|
|
19
|
+
InputProps: TextFieldProps[ 'InputProps' ] & {
|
|
20
|
+
endAdornment: React.JSX.Element;
|
|
21
|
+
};
|
|
22
|
+
inputProps?: TextFieldProps[ 'inputProps' ];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const SizeInput = forwardRef(
|
|
26
|
+
(
|
|
27
|
+
{
|
|
28
|
+
id,
|
|
29
|
+
type,
|
|
30
|
+
value,
|
|
31
|
+
onBlur,
|
|
32
|
+
onKeyUp,
|
|
33
|
+
focused,
|
|
34
|
+
disabled,
|
|
35
|
+
onChange,
|
|
36
|
+
onKeyDown,
|
|
37
|
+
InputProps,
|
|
38
|
+
inputProps,
|
|
39
|
+
placeholder,
|
|
40
|
+
}: Props,
|
|
41
|
+
ref
|
|
42
|
+
) => {
|
|
43
|
+
return (
|
|
44
|
+
<NumberInput
|
|
45
|
+
id={ id }
|
|
46
|
+
ref={ ref }
|
|
47
|
+
size="tiny"
|
|
48
|
+
fullWidth
|
|
49
|
+
type={ type }
|
|
50
|
+
value={ value }
|
|
51
|
+
placeholder={ placeholder }
|
|
52
|
+
onKeyUp={ onKeyUp }
|
|
53
|
+
focused={ focused }
|
|
54
|
+
disabled={ disabled }
|
|
55
|
+
onKeyDown={ onKeyDown }
|
|
56
|
+
onInput={ onChange }
|
|
57
|
+
onBlur={ onBlur }
|
|
58
|
+
InputProps={ InputProps }
|
|
59
|
+
inputProps={ inputProps }
|
|
60
|
+
sx={ getCursorStyle( InputProps?.readOnly ?? false ) }
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const getCursorStyle = ( readOnly: boolean ) => ( {
|
|
67
|
+
input: { cursor: readOnly ? 'default !important' : undefined },
|
|
68
|
+
} );
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type RefObject, useEffect, useRef } from 'react';
|
|
3
|
+
import { PopoverHeader } from '@elementor/editor-ui';
|
|
4
|
+
import { MathFunctionIcon } from '@elementor/icons';
|
|
5
|
+
import { bindPopover, Popover, type PopupState, TextField } from '@elementor/ui';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
popupState: PopupState;
|
|
10
|
+
anchorRef: RefObject< HTMLDivElement | null >;
|
|
11
|
+
onClose?: () => void;
|
|
12
|
+
value: string;
|
|
13
|
+
onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const SIZE = 'tiny';
|
|
17
|
+
|
|
18
|
+
export const TextFieldPopover = ( { popupState, anchorRef, value, onChange, onClose }: Props ) => {
|
|
19
|
+
const inputRef = useRef< HTMLInputElement >( null );
|
|
20
|
+
|
|
21
|
+
useEffect( () => {
|
|
22
|
+
if ( popupState.isOpen ) {
|
|
23
|
+
requestAnimationFrame( () => {
|
|
24
|
+
if ( inputRef.current ) {
|
|
25
|
+
inputRef.current.focus();
|
|
26
|
+
}
|
|
27
|
+
} );
|
|
28
|
+
}
|
|
29
|
+
}, [ popupState.isOpen ] );
|
|
30
|
+
|
|
31
|
+
const handleKeyDown = ( event: React.KeyboardEvent< HTMLInputElement > ) => {
|
|
32
|
+
if ( event.key.toLowerCase() === 'enter' ) {
|
|
33
|
+
handleClose();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handleClose = () => {
|
|
38
|
+
onClose?.();
|
|
39
|
+
|
|
40
|
+
popupState.close();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Popover
|
|
45
|
+
disablePortal
|
|
46
|
+
slotProps={ {
|
|
47
|
+
paper: {
|
|
48
|
+
sx: {
|
|
49
|
+
borderRadius: 2,
|
|
50
|
+
width: anchorRef.current?.offsetWidth + 'px',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
} }
|
|
54
|
+
{ ...bindPopover( popupState ) }
|
|
55
|
+
anchorOrigin={ { vertical: 'bottom', horizontal: 'center' } }
|
|
56
|
+
transformOrigin={ { vertical: 'top', horizontal: 'center' } }
|
|
57
|
+
onClose={ handleClose }
|
|
58
|
+
>
|
|
59
|
+
<PopoverHeader
|
|
60
|
+
title={ __( 'CSS function', 'elementor' ) }
|
|
61
|
+
onClose={ handleClose }
|
|
62
|
+
icon={ <MathFunctionIcon fontSize={ SIZE } /> }
|
|
63
|
+
/>
|
|
64
|
+
<TextField
|
|
65
|
+
value={ value }
|
|
66
|
+
onChange={ onChange }
|
|
67
|
+
onKeyDown={ handleKeyDown }
|
|
68
|
+
size="tiny"
|
|
69
|
+
type="text"
|
|
70
|
+
fullWidth
|
|
71
|
+
inputProps={ {
|
|
72
|
+
ref: inputRef,
|
|
73
|
+
} }
|
|
74
|
+
sx={ { pt: 0, pr: 1.5, pb: 1.5, pl: 1.5 } }
|
|
75
|
+
/>
|
|
76
|
+
</Popover>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useId } from 'react';
|
|
3
|
+
import { MenuListItem } from '@elementor/editor-ui';
|
|
4
|
+
import { bindMenu, bindTrigger, Button, Menu, styled, usePopupState } from '@elementor/ui';
|
|
5
|
+
|
|
6
|
+
export type UnitSelectorProps< T extends string > = {
|
|
7
|
+
options: T[];
|
|
8
|
+
value: T;
|
|
9
|
+
onSelect: ( value: T ) => void;
|
|
10
|
+
isActive: boolean;
|
|
11
|
+
menuItemsAttributes?: { [ key in T ]?: Record< string, unknown > };
|
|
12
|
+
optionLabelOverrides?: { [ key in T ]?: React.ReactNode };
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const menuItemContentStyles = {
|
|
17
|
+
display: 'flex',
|
|
18
|
+
flexDirection: 'column',
|
|
19
|
+
justifyContent: 'center',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const UnitSelector = < T extends string >( {
|
|
23
|
+
value,
|
|
24
|
+
isActive,
|
|
25
|
+
onSelect,
|
|
26
|
+
options,
|
|
27
|
+
disabled,
|
|
28
|
+
menuItemsAttributes = {},
|
|
29
|
+
optionLabelOverrides = {},
|
|
30
|
+
}: UnitSelectorProps< T > ) => {
|
|
31
|
+
const popupState = usePopupState( {
|
|
32
|
+
variant: 'popover',
|
|
33
|
+
popupId: useId(),
|
|
34
|
+
} );
|
|
35
|
+
|
|
36
|
+
const handleMenuItemClick = ( option: T ) => {
|
|
37
|
+
onSelect( option );
|
|
38
|
+
|
|
39
|
+
popupState.close();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<StyledButton isActive={ isActive } disabled={ disabled } size="small" { ...bindTrigger( popupState ) }>
|
|
45
|
+
{ optionLabelOverrides[ value ] ?? value }
|
|
46
|
+
</StyledButton>
|
|
47
|
+
|
|
48
|
+
<Menu MenuListProps={ { dense: true } } { ...bindMenu( popupState ) }>
|
|
49
|
+
{ options.map( ( option ) => (
|
|
50
|
+
<MenuListItem
|
|
51
|
+
key={ option }
|
|
52
|
+
onClick={ () => handleMenuItemClick( option ) }
|
|
53
|
+
{ ...menuItemsAttributes?.[ option ] }
|
|
54
|
+
primaryTypographyProps={ {
|
|
55
|
+
variant: 'caption',
|
|
56
|
+
sx: {
|
|
57
|
+
...menuItemContentStyles,
|
|
58
|
+
lineHeight: '1',
|
|
59
|
+
},
|
|
60
|
+
} }
|
|
61
|
+
menuItemTextProps={ {
|
|
62
|
+
sx: menuItemContentStyles,
|
|
63
|
+
} }
|
|
64
|
+
>
|
|
65
|
+
{ optionLabelOverrides[ option ] ?? option.toUpperCase() }
|
|
66
|
+
</MenuListItem>
|
|
67
|
+
) ) }
|
|
68
|
+
</Menu>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const StyledButton = styled( Button, {
|
|
74
|
+
shouldForwardProp: ( prop ) => prop !== 'isActive',
|
|
75
|
+
} )( ( { isActive, theme } ) => ( {
|
|
76
|
+
color: isActive ? theme.palette.text.primary : theme.palette.text.tertiary,
|
|
77
|
+
font: 'inherit',
|
|
78
|
+
minWidth: 'initial',
|
|
79
|
+
textTransform: 'uppercase',
|
|
80
|
+
} ) );
|
|
@@ -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,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
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type SizePropValue } from '@elementor/editor-props';
|
|
2
|
+
|
|
3
|
+
import { type SizeUnit } from '../types';
|
|
4
|
+
import { isExtendedUnit } from './is-extended-unit';
|
|
5
|
+
|
|
6
|
+
type SizeValue = SizePropValue[ 'value' ];
|
|
7
|
+
|
|
8
|
+
type ResolverContext< U > = {
|
|
9
|
+
units: U[];
|
|
10
|
+
defaultUnit?: U;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const DEFAULT_SIZE = '';
|
|
14
|
+
|
|
15
|
+
export const EXTENDED_UNITS = {
|
|
16
|
+
auto: 'auto',
|
|
17
|
+
custom: 'custom',
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
export const resolveSizeValue = < TValue extends SizeValue | null, TUnit extends SizeValue[ 'unit' ] >(
|
|
21
|
+
value: TValue,
|
|
22
|
+
context: ResolverContext< TUnit >
|
|
23
|
+
) => {
|
|
24
|
+
if ( ! value ) {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { units, defaultUnit } = context;
|
|
29
|
+
|
|
30
|
+
const unit = resolveFallbackUnit( value.unit as TUnit, units, defaultUnit );
|
|
31
|
+
|
|
32
|
+
if ( unit === EXTENDED_UNITS.auto ) {
|
|
33
|
+
return { size: DEFAULT_SIZE, unit };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if ( unit === EXTENDED_UNITS.custom ) {
|
|
37
|
+
return { size: String( value.size ?? DEFAULT_SIZE ), unit };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
size: sanitizeSize( value.size ) ?? DEFAULT_SIZE,
|
|
42
|
+
unit,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const resolveSizeOnUnitChange = (
|
|
47
|
+
size: SizeValue[ 'size' ],
|
|
48
|
+
unit: SizeValue[ 'unit' ]
|
|
49
|
+
): SizeValue[ 'size' ] => {
|
|
50
|
+
return isExtendedUnit( unit ) ? DEFAULT_SIZE : size;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const createDefaultSizeValue = < T extends SizeValue >( units: SizeUnit[], defaultUnit?: SizeUnit ): T => {
|
|
54
|
+
let [ unit ] = units;
|
|
55
|
+
|
|
56
|
+
if ( defaultUnit !== undefined ) {
|
|
57
|
+
unit = resolveFallbackUnit( defaultUnit, units );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { size: DEFAULT_SIZE, unit } as T;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const resolveFallbackUnit = < TUnit extends SizeUnit >(
|
|
64
|
+
unit: TUnit,
|
|
65
|
+
units: readonly TUnit[],
|
|
66
|
+
defaultUnit?: TUnit
|
|
67
|
+
): TUnit => {
|
|
68
|
+
if ( units.includes( unit ) ) {
|
|
69
|
+
return unit;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if ( defaultUnit && units.includes( defaultUnit ) ) {
|
|
73
|
+
return defaultUnit;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return units[ 0 ] ?? '';
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const sanitizeSize = ( size: SizeValue[ 'size' ] ): SizeValue[ 'size' ] => {
|
|
80
|
+
if ( typeof size === 'number' && isNaN( size ) ) {
|
|
81
|
+
return DEFAULT_SIZE;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return size;
|
|
85
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
|
|
4
|
-
import {
|
|
4
|
+
import { svgSrcPropTypeUtil, urlPropTypeUtil } from '@elementor/editor-props';
|
|
5
5
|
import { UploadIcon } from '@elementor/icons';
|
|
6
6
|
import { Button, Card, CardMedia, CardOverlay, CircularProgress, Stack, styled, ThemeProvider } from '@elementor/ui';
|
|
7
7
|
import { type OpenOptions, useWpMediaAttachment, useWpMediaFrame } from '@elementor/wp-media';
|
|
@@ -43,8 +43,9 @@ const MODE_BROWSE: OpenOptions = { mode: 'browse' };
|
|
|
43
43
|
const MODE_UPLOAD: OpenOptions = { mode: 'upload' };
|
|
44
44
|
|
|
45
45
|
export const SvgMediaControl = createControl( () => {
|
|
46
|
-
const { value, setValue } = useBoundProp(
|
|
47
|
-
const
|
|
46
|
+
const { value, setValue } = useBoundProp( svgSrcPropTypeUtil );
|
|
47
|
+
const id = value?.id;
|
|
48
|
+
const url = value?.url;
|
|
48
49
|
const { data: attachment, isFetching } = useWpMediaAttachment( id?.value || null );
|
|
49
50
|
const src = attachment?.url ?? url?.value ?? null;
|
|
50
51
|
const { data: allowSvgUpload } = useUnfilteredFilesUpload();
|
|
@@ -61,7 +62,7 @@ export const SvgMediaControl = createControl( () => {
|
|
|
61
62
|
$$type: 'image-attachment-id',
|
|
62
63
|
value: selectedAttachment.id,
|
|
63
64
|
},
|
|
64
|
-
url:
|
|
65
|
+
url: urlPropTypeUtil.create( selectedAttachment.url ),
|
|
65
66
|
} );
|
|
66
67
|
},
|
|
67
68
|
} );
|
|
@@ -46,9 +46,9 @@ const getIsSiteRtl = () => {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
// TODO: Remove this after version 4.01 is released
|
|
49
|
-
const
|
|
49
|
+
const shouldShowAllTransitionProperties = (): boolean => {
|
|
50
50
|
if ( ! hasProInstalled() ) {
|
|
51
|
-
return
|
|
51
|
+
return true;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const proVersion = window.elementorPro?.config?.version;
|
|
@@ -191,7 +191,7 @@ const createTransitionPropertiesList = (): TransitionCategory[] => {
|
|
|
191
191
|
},
|
|
192
192
|
];
|
|
193
193
|
|
|
194
|
-
return
|
|
194
|
+
return shouldShowAllTransitionProperties() ? baseProperties : [ baseProperties[ 0 ] ];
|
|
195
195
|
};
|
|
196
196
|
|
|
197
197
|
export const transitionProperties: TransitionCategory[] = createTransitionPropertiesList();
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { type StyleDefinitionState } from '@elementor/editor-styles';
|
|
10
10
|
import { InfoCircleFilledIcon } from '@elementor/icons';
|
|
11
11
|
import { Alert, AlertTitle, Box, Typography } from '@elementor/ui';
|
|
12
|
+
import { hasProInstalled } from '@elementor/utils';
|
|
12
13
|
import { __ } from '@wordpress/i18n';
|
|
13
14
|
|
|
14
15
|
import { useBoundProp } from '../../bound-prop-context';
|
|
@@ -181,6 +182,7 @@ export const TransitionRepeaterControl = createControl(
|
|
|
181
182
|
} ) => {
|
|
182
183
|
const currentStyleIsNormal = currentStyleState === null;
|
|
183
184
|
const [ recentlyUsedList, setRecentlyUsedList ] = useState< string[] >( [] );
|
|
185
|
+
const proInstalled = hasProInstalled();
|
|
184
186
|
|
|
185
187
|
const { value, setValue } = useBoundProp( childArrayPropTypeUtil );
|
|
186
188
|
const { allDisabled: disabledItems, proDisabled: proDisabledItems } = useMemo(
|
|
@@ -191,10 +193,14 @@ export const TransitionRepeaterControl = createControl(
|
|
|
191
193
|
const allowedTransitionSet = useMemo( () => {
|
|
192
194
|
const set = new Set< string >();
|
|
193
195
|
transitionProperties.forEach( ( category ) => {
|
|
194
|
-
category.properties.forEach( ( prop ) =>
|
|
196
|
+
category.properties.forEach( ( prop ) => {
|
|
197
|
+
if ( ! prop.isDisabled || proInstalled ) {
|
|
198
|
+
set.add( prop.value );
|
|
199
|
+
}
|
|
200
|
+
} );
|
|
195
201
|
} );
|
|
196
202
|
return set;
|
|
197
|
-
}, [] );
|
|
203
|
+
}, [ proInstalled ] );
|
|
198
204
|
|
|
199
205
|
useEffect( () => {
|
|
200
206
|
if ( ! value || value.length === 0 ) {
|