@elementor/editor-controls 0.1.0 → 0.2.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 +22 -0
- package/dist/index.d.mts +61 -32
- package/dist/index.d.ts +61 -32
- package/dist/index.js +299 -225
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +304 -226
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/bound-prop-context.tsx +47 -10
- package/src/components/control-toggle-button-group.tsx +14 -3
- package/src/controls/background-overlay-repeater-control.tsx +3 -7
- package/src/controls/box-shadow-repeater-control.tsx +9 -9
- package/src/controls/color-control.tsx +4 -13
- package/src/controls/equal-unequal-sizes-control.tsx +100 -100
- package/src/controls/font-family-control.tsx +2 -1
- package/src/controls/image-control.tsx +7 -18
- package/src/controls/image-media-control.tsx +7 -10
- package/src/controls/link-control.tsx +90 -0
- package/src/controls/linked-dimensions-control.tsx +5 -11
- package/src/controls/number-control.tsx +48 -23
- package/src/controls/select-control.tsx +7 -7
- package/src/controls/size-control.tsx +10 -19
- package/src/controls/stroke-control.tsx +17 -45
- package/src/controls/text-area-control.tsx +2 -1
- package/src/controls/text-control.tsx +2 -1
- package/src/controls/toggle-control.tsx +5 -5
- package/src/controls/url-control.tsx +29 -0
- package/src/hooks/use-sync-external-state.tsx +8 -8
- package/src/index.ts +3 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type LinkPropValue, type UrlPropValue } from '@elementor/editor-props';
|
|
3
|
+
import { MinusIcon, PlusIcon } from '@elementor/icons';
|
|
4
|
+
import { Collapse, Divider, Grid, IconButton, Stack, Switch } from '@elementor/ui';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
import { BoundPropProvider, useBoundProp } from '../bound-prop-context';
|
|
8
|
+
import { ControlLabel } from '../components/control-label';
|
|
9
|
+
import { createControl } from '../create-control';
|
|
10
|
+
import { UrlControl } from './url-control';
|
|
11
|
+
|
|
12
|
+
const SIZE = 'tiny';
|
|
13
|
+
|
|
14
|
+
const DEFAULT_LINK_CONTROL_VALUE: LinkPropValue = {
|
|
15
|
+
$$type: 'link',
|
|
16
|
+
value: {
|
|
17
|
+
enabled: false,
|
|
18
|
+
href: {
|
|
19
|
+
$$type: 'url',
|
|
20
|
+
value: '',
|
|
21
|
+
},
|
|
22
|
+
isTargetBlank: false,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const LinkControl = createControl( () => {
|
|
27
|
+
const { value = DEFAULT_LINK_CONTROL_VALUE, setValue } = useBoundProp< LinkPropValue >();
|
|
28
|
+
const { enabled, href, isTargetBlank } = value?.value || {};
|
|
29
|
+
|
|
30
|
+
const handleOnChange = < T extends keyof LinkPropValue[ 'value' ] >(
|
|
31
|
+
key: T,
|
|
32
|
+
newValue: LinkPropValue[ 'value' ][ T ]
|
|
33
|
+
) => {
|
|
34
|
+
setValue( {
|
|
35
|
+
$$type: 'link',
|
|
36
|
+
value: {
|
|
37
|
+
...( value?.value ?? DEFAULT_LINK_CONTROL_VALUE.value ),
|
|
38
|
+
[ key ]: newValue,
|
|
39
|
+
},
|
|
40
|
+
} );
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Stack gap={ 1.5 }>
|
|
45
|
+
<Divider />
|
|
46
|
+
<Stack
|
|
47
|
+
direction="row"
|
|
48
|
+
sx={ {
|
|
49
|
+
justifyContent: 'space-between',
|
|
50
|
+
alignItems: 'center',
|
|
51
|
+
} }
|
|
52
|
+
>
|
|
53
|
+
<ControlLabel>{ __( 'Link', 'elementor' ) }</ControlLabel>
|
|
54
|
+
<IconButton size={ SIZE } onClick={ () => handleOnChange( 'enabled', ! enabled ) }>
|
|
55
|
+
{ enabled ? <MinusIcon fontSize={ SIZE } /> : <PlusIcon fontSize={ SIZE } /> }
|
|
56
|
+
</IconButton>
|
|
57
|
+
</Stack>
|
|
58
|
+
<Collapse in={ enabled } timeout="auto" unmountOnExit>
|
|
59
|
+
<Stack gap={ 1.5 }>
|
|
60
|
+
<BoundPropProvider
|
|
61
|
+
value={ href }
|
|
62
|
+
setValue={ ( newHref ) => handleOnChange( 'href', newHref as UrlPropValue ) }
|
|
63
|
+
bind={ 'href' }
|
|
64
|
+
>
|
|
65
|
+
<UrlControl placeholder={ __( 'Paste URL or type', 'elementor' ) } />
|
|
66
|
+
</BoundPropProvider>
|
|
67
|
+
|
|
68
|
+
<SwitchControl
|
|
69
|
+
value={ isTargetBlank }
|
|
70
|
+
onSwitch={ () => handleOnChange( 'isTargetBlank', ! isTargetBlank ) }
|
|
71
|
+
/>
|
|
72
|
+
</Stack>
|
|
73
|
+
</Collapse>
|
|
74
|
+
</Stack>
|
|
75
|
+
);
|
|
76
|
+
} );
|
|
77
|
+
|
|
78
|
+
// @TODO Should be refactored in ED-16323
|
|
79
|
+
const SwitchControl = ( { value, onSwitch }: { value: boolean; onSwitch: () => void } ) => {
|
|
80
|
+
return (
|
|
81
|
+
<Grid container alignItems="center" flexWrap="nowrap" justifyContent="space-between">
|
|
82
|
+
<Grid item>
|
|
83
|
+
<ControlLabel>{ __( 'Open in new tab', 'elementor' ) }</ControlLabel>
|
|
84
|
+
</Grid>
|
|
85
|
+
<Grid item>
|
|
86
|
+
<Switch checked={ value } onChange={ onSwitch } />
|
|
87
|
+
</Grid>
|
|
88
|
+
</Grid>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { linkedDimensionsPropTypeUtil, type PropValue } from '@elementor/editor-props';
|
|
3
3
|
import { DetachIcon, LinkIcon, SideBottomIcon, SideLeftIcon, SideRightIcon, SideTopIcon } from '@elementor/icons';
|
|
4
4
|
import { Grid, Stack, ToggleButton } from '@elementor/ui';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
@@ -12,8 +12,8 @@ import { SizeControl } from './size-control';
|
|
|
12
12
|
export type Position = 'top' | 'right' | 'bottom' | 'left';
|
|
13
13
|
|
|
14
14
|
export const LinkedDimensionsControl = createControl( ( { label }: { label: string } ) => {
|
|
15
|
-
const { value, setValue } = useBoundProp
|
|
16
|
-
const { top, right, bottom, left, isLinked = true } = value
|
|
15
|
+
const { value, setValue } = useBoundProp( linkedDimensionsPropTypeUtil );
|
|
16
|
+
const { top, right, bottom, left, isLinked = true } = value || {};
|
|
17
17
|
|
|
18
18
|
const setLinkedValue = ( position: Position, newValue: PropValue ) => {
|
|
19
19
|
const updatedValue = {
|
|
@@ -25,10 +25,7 @@ export const LinkedDimensionsControl = createControl( ( { label }: { label: stri
|
|
|
25
25
|
[ position ]: newValue,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
setValue(
|
|
29
|
-
$$type: 'linked-dimensions',
|
|
30
|
-
value: updatedValue,
|
|
31
|
-
} );
|
|
28
|
+
setValue( updatedValue );
|
|
32
29
|
};
|
|
33
30
|
|
|
34
31
|
const toggleLinked = () => {
|
|
@@ -40,10 +37,7 @@ export const LinkedDimensionsControl = createControl( ( { label }: { label: stri
|
|
|
40
37
|
left: ! isLinked ? top : left,
|
|
41
38
|
};
|
|
42
39
|
|
|
43
|
-
setValue(
|
|
44
|
-
$$type: 'linked-dimensions',
|
|
45
|
-
value: updatedValue,
|
|
46
|
-
} );
|
|
40
|
+
setValue( updatedValue );
|
|
47
41
|
};
|
|
48
42
|
|
|
49
43
|
const LinkedIcon = isLinked ? LinkIcon : DetachIcon;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { numberPropTypeUtil } from '@elementor/editor-props';
|
|
2
3
|
import { TextField } from '@elementor/ui';
|
|
3
4
|
|
|
4
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
@@ -6,26 +7,50 @@ import ControlActions from '../control-actions/control-actions';
|
|
|
6
7
|
import { createControl } from '../create-control';
|
|
7
8
|
|
|
8
9
|
const isEmptyOrNaN = ( value?: string | number ) =>
|
|
9
|
-
value === undefined || value === '' || Number.isNaN( Number( value ) );
|
|
10
|
-
|
|
11
|
-
export const NumberControl = createControl(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
value === null || value === undefined || value === '' || Number.isNaN( Number( value ) );
|
|
11
|
+
|
|
12
|
+
export const NumberControl = createControl(
|
|
13
|
+
( {
|
|
14
|
+
placeholder,
|
|
15
|
+
max = Number.MAX_VALUE,
|
|
16
|
+
min = -Number.MAX_VALUE,
|
|
17
|
+
step = 1,
|
|
18
|
+
shouldForceInt = false,
|
|
19
|
+
}: {
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
max?: number;
|
|
22
|
+
min?: number;
|
|
23
|
+
step?: number;
|
|
24
|
+
shouldForceInt?: boolean;
|
|
25
|
+
} ) => {
|
|
26
|
+
const { value, setValue } = useBoundProp( numberPropTypeUtil );
|
|
27
|
+
|
|
28
|
+
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
29
|
+
const eventValue: string = event.target.value;
|
|
30
|
+
|
|
31
|
+
if ( isEmptyOrNaN( eventValue ) ) {
|
|
32
|
+
setValue( null );
|
|
33
|
+
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const formattedValue = shouldForceInt ? +parseInt( eventValue ) : Number( eventValue );
|
|
38
|
+
|
|
39
|
+
setValue( Math.min( Math.max( formattedValue, min ), max ) );
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<ControlActions>
|
|
44
|
+
<TextField
|
|
45
|
+
size="tiny"
|
|
46
|
+
type="number"
|
|
47
|
+
fullWidth
|
|
48
|
+
value={ isEmptyOrNaN( value ) ? '' : value }
|
|
49
|
+
onChange={ handleChange }
|
|
50
|
+
placeholder={ placeholder }
|
|
51
|
+
inputProps={ { step } }
|
|
52
|
+
/>
|
|
53
|
+
</ControlActions>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type
|
|
2
|
+
import { stringPropTypeUtil, type StringPropValue } from '@elementor/editor-props';
|
|
3
3
|
import { MenuItem, Select, type SelectChangeEvent } from '@elementor/ui';
|
|
4
4
|
|
|
5
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
6
6
|
import ControlActions from '../control-actions/control-actions';
|
|
7
7
|
import { createControl } from '../create-control';
|
|
8
8
|
|
|
9
|
-
type Props
|
|
10
|
-
options: Array< { label: string; value:
|
|
9
|
+
type Props = {
|
|
10
|
+
options: Array< { label: string; value: StringPropValue[ 'value' ]; disabled?: boolean } >;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export const SelectControl = createControl(
|
|
14
|
-
const { value, setValue } = useBoundProp
|
|
13
|
+
export const SelectControl = createControl( ( { options }: Props ) => {
|
|
14
|
+
const { value, setValue } = useBoundProp( stringPropTypeUtil );
|
|
15
15
|
|
|
16
|
-
const handleChange = ( event: SelectChangeEvent<
|
|
17
|
-
setValue( event.target.value
|
|
16
|
+
const handleChange = ( event: SelectChangeEvent< StringPropValue[ 'value' ] > ) => {
|
|
17
|
+
setValue( event.target.value );
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
return (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { sizePropTypeUtil } from '@elementor/editor-props';
|
|
3
3
|
import { InputAdornment } from '@elementor/ui';
|
|
4
4
|
|
|
5
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
@@ -22,25 +22,19 @@ export type SizeControlProps = {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
export const SizeControl = createControl( ( { units = defaultUnits, placeholder, startIcon }: SizeControlProps ) => {
|
|
25
|
-
const { value, setValue } = useBoundProp
|
|
25
|
+
const { value, setValue } = useBoundProp( sizePropTypeUtil );
|
|
26
26
|
|
|
27
|
-
const [ state, setState ] = useSyncExternalState
|
|
27
|
+
const [ state, setState ] = useSyncExternalState( {
|
|
28
28
|
external: value,
|
|
29
29
|
setExternal: setValue,
|
|
30
|
-
persistWhen: ( controlValue ) => !! controlValue?.
|
|
31
|
-
fallback: ( controlValue ) => ( {
|
|
32
|
-
$$type: 'size',
|
|
33
|
-
value: { unit: controlValue?.value?.unit || defaultUnit, size: defaultSize },
|
|
34
|
-
} ),
|
|
30
|
+
persistWhen: ( controlValue ) => !! controlValue?.size || controlValue?.size === 0,
|
|
31
|
+
fallback: ( controlValue ) => ( { unit: controlValue?.unit || defaultUnit, size: defaultSize } ),
|
|
35
32
|
} );
|
|
36
33
|
|
|
37
34
|
const handleUnitChange = ( unit: Unit ) => {
|
|
38
35
|
setState( ( prev ) => ( {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
...prev.value,
|
|
42
|
-
unit,
|
|
43
|
-
},
|
|
36
|
+
size: prev?.size ?? defaultSize,
|
|
37
|
+
unit,
|
|
44
38
|
} ) );
|
|
45
39
|
};
|
|
46
40
|
|
|
@@ -49,10 +43,7 @@ export const SizeControl = createControl( ( { units = defaultUnits, placeholder,
|
|
|
49
43
|
|
|
50
44
|
setState( ( prev ) => ( {
|
|
51
45
|
...prev,
|
|
52
|
-
|
|
53
|
-
...prev.value,
|
|
54
|
-
size: size || size === '0' ? parseFloat( size ) : defaultSize,
|
|
55
|
-
},
|
|
46
|
+
size: size || size === '0' ? parseFloat( size ) : defaultSize,
|
|
56
47
|
} ) );
|
|
57
48
|
};
|
|
58
49
|
|
|
@@ -63,13 +54,13 @@ export const SizeControl = createControl( ( { units = defaultUnits, placeholder,
|
|
|
63
54
|
<SelectionEndAdornment
|
|
64
55
|
options={ units }
|
|
65
56
|
onClick={ handleUnitChange }
|
|
66
|
-
value={ state
|
|
57
|
+
value={ state?.unit ?? defaultUnit }
|
|
67
58
|
/>
|
|
68
59
|
}
|
|
69
60
|
placeholder={ placeholder }
|
|
70
61
|
startAdornment={ startIcon ?? <InputAdornment position="start">{ startIcon }</InputAdornment> }
|
|
71
62
|
type="number"
|
|
72
|
-
value={ Number.isNaN( state
|
|
63
|
+
value={ Number.isNaN( state?.size ) ? '' : state?.size }
|
|
73
64
|
onChange={ handleSizeChange }
|
|
74
65
|
/>
|
|
75
66
|
</ControlActions>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type
|
|
2
|
+
import { type ColorPropValue, type PropValue, type SizePropValue, strokePropTypeUtil } from '@elementor/editor-props';
|
|
3
3
|
import { Grid, Stack } from '@elementor/ui';
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
5
|
|
|
@@ -11,50 +11,34 @@ import { SizeControl, type Unit } from './size-control';
|
|
|
11
11
|
|
|
12
12
|
type SetContextValue = ( v: PropValue ) => void;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
value:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
width: {
|
|
22
|
-
$$type: 'size',
|
|
23
|
-
value: {
|
|
24
|
-
unit: 'px',
|
|
25
|
-
size: NaN,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
14
|
+
export type StrokeProps< T > = {
|
|
15
|
+
bind: string;
|
|
16
|
+
value: T;
|
|
17
|
+
setValue: ( v: T ) => void;
|
|
18
|
+
label: string;
|
|
19
|
+
children: React.ReactNode;
|
|
29
20
|
};
|
|
30
|
-
|
|
31
21
|
const units: Unit[] = [ 'px', 'em', 'rem' ];
|
|
32
22
|
|
|
33
23
|
export const StrokeControl = createControl( () => {
|
|
34
|
-
const { value, setValue } = useBoundProp
|
|
24
|
+
const { value, setValue } = useBoundProp( strokePropTypeUtil );
|
|
35
25
|
|
|
36
|
-
const setStrokeWidth = ( newValue:
|
|
26
|
+
const setStrokeWidth = ( newValue: SizePropValue ) => {
|
|
37
27
|
const updatedValue = {
|
|
38
|
-
...
|
|
28
|
+
...value,
|
|
39
29
|
width: newValue,
|
|
40
30
|
};
|
|
41
31
|
|
|
42
|
-
setValue(
|
|
43
|
-
$$type: 'stroke',
|
|
44
|
-
value: updatedValue,
|
|
45
|
-
} );
|
|
32
|
+
setValue( updatedValue );
|
|
46
33
|
};
|
|
47
34
|
|
|
48
|
-
const setStrokeColor = ( newValue:
|
|
35
|
+
const setStrokeColor = ( newValue: ColorPropValue ) => {
|
|
49
36
|
const updatedValue = {
|
|
50
|
-
...
|
|
37
|
+
...value,
|
|
51
38
|
color: newValue,
|
|
52
39
|
};
|
|
53
40
|
|
|
54
|
-
setValue(
|
|
55
|
-
$$type: 'stroke',
|
|
56
|
-
value: updatedValue,
|
|
57
|
-
} );
|
|
41
|
+
setValue( updatedValue );
|
|
58
42
|
};
|
|
59
43
|
|
|
60
44
|
return (
|
|
@@ -62,7 +46,7 @@ export const StrokeControl = createControl( () => {
|
|
|
62
46
|
<Control
|
|
63
47
|
bind="width"
|
|
64
48
|
label={ __( 'Stroke Width', 'elementor' ) }
|
|
65
|
-
value={ value?.
|
|
49
|
+
value={ value?.width }
|
|
66
50
|
setValue={ setStrokeWidth }
|
|
67
51
|
>
|
|
68
52
|
<SizeControl units={ units } />
|
|
@@ -71,7 +55,7 @@ export const StrokeControl = createControl( () => {
|
|
|
71
55
|
<Control
|
|
72
56
|
bind="color"
|
|
73
57
|
label={ __( 'Stroke Color', 'elementor' ) }
|
|
74
|
-
value={ value?.
|
|
58
|
+
value={ value?.color }
|
|
75
59
|
setValue={ setStrokeColor }
|
|
76
60
|
>
|
|
77
61
|
<ColorControl />
|
|
@@ -80,19 +64,7 @@ export const StrokeControl = createControl( () => {
|
|
|
80
64
|
);
|
|
81
65
|
} );
|
|
82
66
|
|
|
83
|
-
const Control = < T extends PropValue >( {
|
|
84
|
-
bind,
|
|
85
|
-
value,
|
|
86
|
-
setValue,
|
|
87
|
-
label,
|
|
88
|
-
children,
|
|
89
|
-
}: {
|
|
90
|
-
bind: string;
|
|
91
|
-
value: T;
|
|
92
|
-
setValue: ( v: T ) => void;
|
|
93
|
-
label: string;
|
|
94
|
-
children: React.ReactNode;
|
|
95
|
-
} ) => (
|
|
67
|
+
const Control = < T extends PropValue >( { bind, value, setValue, label, children }: StrokeProps< T > ) => (
|
|
96
68
|
<BoundPropProvider bind={ bind } value={ value } setValue={ setValue as SetContextValue }>
|
|
97
69
|
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
98
70
|
<Grid item xs={ 6 }>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { stringPropTypeUtil } from '@elementor/editor-props';
|
|
2
3
|
import { TextField } from '@elementor/ui';
|
|
3
4
|
|
|
4
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
@@ -10,7 +11,7 @@ type Props = {
|
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export const TextAreaControl = createControl( ( { placeholder }: Props ) => {
|
|
13
|
-
const { value, setValue } = useBoundProp
|
|
14
|
+
const { value, setValue } = useBoundProp( stringPropTypeUtil );
|
|
14
15
|
|
|
15
16
|
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
16
17
|
setValue( event.target.value );
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { stringPropTypeUtil } from '@elementor/editor-props';
|
|
2
3
|
import { TextField } from '@elementor/ui';
|
|
3
4
|
|
|
4
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
@@ -6,7 +7,7 @@ import ControlActions from '../control-actions/control-actions';
|
|
|
6
7
|
import { createControl } from '../create-control';
|
|
7
8
|
|
|
8
9
|
export const TextControl = createControl( ( { placeholder }: { placeholder?: string } ) => {
|
|
9
|
-
const { value, setValue } = useBoundProp
|
|
10
|
+
const { value, setValue } = useBoundProp( stringPropTypeUtil );
|
|
10
11
|
|
|
11
12
|
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => setValue( event.target.value );
|
|
12
13
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type PropValue } from '@elementor/editor-props';
|
|
2
|
+
import { type PropValue, stringPropTypeUtil, type StringPropValue } from '@elementor/editor-props';
|
|
3
3
|
import { type ToggleButtonProps } from '@elementor/ui';
|
|
4
4
|
|
|
5
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
@@ -13,17 +13,17 @@ type ToggleControlProps< T extends PropValue > = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const ToggleControl = createControl(
|
|
16
|
-
|
|
17
|
-
const { value, setValue } = useBoundProp
|
|
16
|
+
( { options, fullWidth = false, size = 'tiny' }: ToggleControlProps< StringPropValue[ 'value' ] > ) => {
|
|
17
|
+
const { value, setValue } = useBoundProp( stringPropTypeUtil );
|
|
18
18
|
|
|
19
|
-
const handleToggle = ( option:
|
|
19
|
+
const handleToggle = ( option: StringPropValue[ 'value' ] | null ) => {
|
|
20
20
|
setValue( option );
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
24
|
<ControlToggleButtonGroup
|
|
25
25
|
items={ options }
|
|
26
|
-
value={ value
|
|
26
|
+
value={ value ?? null }
|
|
27
27
|
onChange={ handleToggle }
|
|
28
28
|
exclusive={ true }
|
|
29
29
|
fullWidth={ fullWidth }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type UrlPropValue } from '@elementor/editor-props';
|
|
3
|
+
import { TextField } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
import { useBoundProp } from '../bound-prop-context';
|
|
6
|
+
import ControlActions from '../control-actions/control-actions';
|
|
7
|
+
import { createControl } from '../create-control';
|
|
8
|
+
|
|
9
|
+
export const UrlControl = createControl( ( { placeholder }: { placeholder?: string } ) => {
|
|
10
|
+
const { value, setValue } = useBoundProp< UrlPropValue >();
|
|
11
|
+
|
|
12
|
+
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) =>
|
|
13
|
+
setValue( {
|
|
14
|
+
$$type: 'url',
|
|
15
|
+
value: event.target.value,
|
|
16
|
+
} );
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<ControlActions>
|
|
20
|
+
<TextField
|
|
21
|
+
size="tiny"
|
|
22
|
+
fullWidth
|
|
23
|
+
value={ value?.value }
|
|
24
|
+
onChange={ handleChange }
|
|
25
|
+
placeholder={ placeholder }
|
|
26
|
+
/>
|
|
27
|
+
</ControlActions>
|
|
28
|
+
);
|
|
29
|
+
} );
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
type UseInternalStateOptions< TValue > = {
|
|
4
|
-
external: TValue |
|
|
5
|
-
setExternal: ( value: TValue |
|
|
6
|
-
persistWhen: ( value: TValue |
|
|
7
|
-
fallback: ( value: TValue |
|
|
4
|
+
external: TValue | null;
|
|
5
|
+
setExternal: ( value: TValue | null ) => void;
|
|
6
|
+
persistWhen: ( value: TValue | null ) => boolean;
|
|
7
|
+
fallback: ( value: TValue | null ) => TValue;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
export const useSyncExternalState = < TValue, >( {
|
|
@@ -13,15 +13,15 @@ export const useSyncExternalState = < TValue, >( {
|
|
|
13
13
|
persistWhen,
|
|
14
14
|
fallback,
|
|
15
15
|
}: UseInternalStateOptions< TValue > ) => {
|
|
16
|
-
function toExternal( internalValue: TValue |
|
|
16
|
+
function toExternal( internalValue: TValue | null ) {
|
|
17
17
|
if ( persistWhen( internalValue ) ) {
|
|
18
18
|
return internalValue;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
return
|
|
21
|
+
return null;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function toInternal( externalValue: TValue |
|
|
24
|
+
function toInternal( externalValue: TValue | null, internalValue: TValue | null ) {
|
|
25
25
|
if ( ! externalValue ) {
|
|
26
26
|
return fallback( internalValue );
|
|
27
27
|
}
|
|
@@ -29,7 +29,7 @@ export const useSyncExternalState = < TValue, >( {
|
|
|
29
29
|
return externalValue;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const [ internal, setInternal ] = useState< TValue >( toInternal( external,
|
|
32
|
+
const [ internal, setInternal ] = useState< TValue >( toInternal( external, null ) );
|
|
33
33
|
|
|
34
34
|
useEffect( () => {
|
|
35
35
|
setInternal( ( prevInternal ) => toInternal( external, prevInternal ) );
|
package/src/index.ts
CHANGED
|
@@ -13,9 +13,12 @@ export { NumberControl } from './controls/number-control';
|
|
|
13
13
|
export { EqualUnequalSizesControl } from './controls/equal-unequal-sizes-control';
|
|
14
14
|
export { LinkedDimensionsControl } from './controls/linked-dimensions-control';
|
|
15
15
|
export { FontFamilyControl } from './controls/font-family-control';
|
|
16
|
+
export { UrlControl } from './controls/url-control';
|
|
17
|
+
export { LinkControl } from './controls/link-control';
|
|
16
18
|
|
|
17
19
|
// components
|
|
18
20
|
export { ControlLabel } from './components/control-label';
|
|
21
|
+
export { ControlToggleButtonGroup } from './components/control-toggle-button-group';
|
|
19
22
|
|
|
20
23
|
// types
|
|
21
24
|
export type { ControlComponent } from './create-control';
|