@elementor/editor-editing-panel 0.17.0 → 0.18.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 +32 -0
- package/dist/index.d.mts +26 -9
- package/dist/index.d.ts +26 -9
- package/dist/index.js +859 -367
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +830 -329
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/src/components/settings-tab.tsx +5 -2
- package/src/components/style-sections/effects-section/box-shadow-repeater.tsx +224 -0
- package/src/components/style-sections/effects-section/effects-section.tsx +18 -0
- package/src/components/style-sections/position-section/z-index-control.tsx +11 -7
- package/src/components/style-sections/size-section.tsx +23 -20
- package/src/components/style-sections/spacing-section/linked-dimensions-control.tsx +62 -47
- package/src/components/style-sections/typography-section/font-size-control.tsx +10 -6
- package/src/components/style-sections/typography-section/font-weight-control.tsx +16 -12
- package/src/components/style-sections/typography-section/letter-spacing-control.tsx +10 -6
- package/src/components/style-sections/typography-section/text-alignment-control.tsx +12 -8
- package/src/components/style-sections/typography-section/text-color-control.tsx +10 -6
- package/src/components/style-sections/typography-section/text-direction-control.tsx +37 -0
- package/src/components/style-sections/typography-section/text-style-control.tsx +37 -34
- package/src/components/style-sections/typography-section/transform-control.tsx +14 -12
- package/src/components/style-sections/typography-section/typography-section.tsx +2 -0
- package/src/components/style-sections/typography-section/word-spacing-control.tsx +10 -6
- package/src/components/style-tab.tsx +5 -1
- package/src/controls/components/control-type-container.tsx +28 -0
- package/src/controls/components/repeater.tsx +197 -0
- package/src/controls/control-actions/actions/popover-action.tsx +58 -0
- package/src/controls/control-actions/control-actions-menu.ts +8 -0
- package/src/controls/control-actions/control-actions.tsx +43 -0
- package/src/controls/control-replacement.ts +15 -7
- package/src/controls/control-types/color-control.tsx +21 -18
- package/src/controls/control-types/image-control.tsx +56 -59
- package/src/controls/control-types/image-media-control.tsx +73 -0
- package/src/controls/control-types/number-control.tsx +13 -9
- package/src/controls/control-types/select-control.tsx +13 -9
- package/src/controls/control-types/size-control.tsx +17 -13
- package/src/controls/control-types/text-area-control.tsx +15 -11
- package/src/controls/control-types/text-control.tsx +9 -3
- package/src/controls/control-types/toggle-control.tsx +3 -2
- package/src/controls/control.tsx +1 -7
- package/src/controls/controls-registry.tsx +19 -10
- package/src/controls/create-control.tsx +31 -0
- package/src/controls/settings-control.tsx +2 -9
- package/src/dynamics/components/dynamic-selection-control.tsx +1 -1
- package/src/dynamics/components/dynamic-selection.tsx +1 -1
- package/src/dynamics/dynamic-control.tsx +1 -1
- package/src/dynamics/hooks/use-prop-dynamic-action.tsx +23 -0
- package/src/dynamics/hooks/use-prop-dynamic-tags.ts +4 -4
- package/src/dynamics/init.ts +9 -0
- package/src/dynamics/types.ts +6 -3
- package/src/dynamics/utils.ts +16 -3
- package/src/index.ts +2 -0
- package/src/types.ts +35 -14
- package/src/controls/components/control-container.tsx +0 -18
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { UnstableColorPicker } from '@elementor/ui';
|
|
2
|
+
import { UnstableColorPicker, UnstableColorPickerProps } from '@elementor/ui';
|
|
3
3
|
import { useControl } from '../control-context';
|
|
4
|
+
import ControlActions from '../control-actions/control-actions';
|
|
5
|
+
import { createControl } from '../create-control';
|
|
6
|
+
import { TransformablePropValue } from '../../types';
|
|
4
7
|
|
|
5
|
-
export
|
|
6
|
-
const { value, setValue } = useControl< string >();
|
|
8
|
+
export type ColorPropValue = TransformablePropValue< 'color', string >;
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
export const ColorControl = createControl(
|
|
11
|
+
( props: Partial< Omit< UnstableColorPickerProps, 'value' | 'onChange' > > ) => {
|
|
12
|
+
const { value, setValue } = useControl< ColorPropValue >();
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
const handleChange = ( selectedColor: string ) => {
|
|
15
|
+
setValue( {
|
|
16
|
+
$$type: 'color',
|
|
17
|
+
value: selectedColor,
|
|
18
|
+
} );
|
|
19
|
+
};
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
timer = setTimeout( () => func( ...args ), wait );
|
|
23
|
-
};
|
|
24
|
-
};
|
|
21
|
+
return (
|
|
22
|
+
<ControlActions>
|
|
23
|
+
<UnstableColorPicker size="tiny" { ...props } value={ value?.value } onChange={ handleChange } />
|
|
24
|
+
</ControlActions>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
);
|
|
@@ -1,69 +1,66 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { useControl } from '../control-context';
|
|
4
|
-
import { UploadIcon } from '@elementor/icons';
|
|
2
|
+
import { Grid, Stack } from '@elementor/ui';
|
|
5
3
|
import { __ } from '@wordpress/i18n';
|
|
6
|
-
import {
|
|
4
|
+
import { ControlContext, useControl } from '../control-context';
|
|
5
|
+
import { type ImageSrc, ImageMediaControl } from './image-media-control';
|
|
6
|
+
import { SettingsControl } from '../settings-control';
|
|
7
|
+
import { PropValue, TransformablePropValue } from '../../types';
|
|
8
|
+
import { SelectControl } from './select-control';
|
|
9
|
+
import { createControl } from '../create-control';
|
|
7
10
|
|
|
8
|
-
type Image =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
type Image = TransformablePropValue<
|
|
12
|
+
'image',
|
|
13
|
+
{
|
|
14
|
+
src?: ImageSrc;
|
|
15
|
+
size?: string;
|
|
16
|
+
}
|
|
17
|
+
>;
|
|
18
|
+
|
|
19
|
+
type SetContextValue = ( v: PropValue ) => void;
|
|
20
|
+
|
|
21
|
+
export type ImageControlProps = {
|
|
22
|
+
sizes: { label: string; value: string }[];
|
|
19
23
|
};
|
|
20
24
|
|
|
21
|
-
export const ImageControl = () => {
|
|
25
|
+
export const ImageControl = createControl( ( props: ImageControlProps ) => {
|
|
22
26
|
const { value, setValue } = useControl< Image >();
|
|
23
|
-
const {
|
|
24
|
-
const src = attachment?.url ?? value?.value?.url;
|
|
27
|
+
const { src, size } = value?.value || {};
|
|
25
28
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
} );
|
|
37
|
-
},
|
|
38
|
-
} );
|
|
29
|
+
const setImageSrc = ( newValue: ImageSrc ) => {
|
|
30
|
+
setValue( {
|
|
31
|
+
$$type: 'image',
|
|
32
|
+
value: {
|
|
33
|
+
src: newValue,
|
|
34
|
+
size,
|
|
35
|
+
},
|
|
36
|
+
} );
|
|
37
|
+
};
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
open( { mode: 'browse' } );
|
|
50
|
-
} }
|
|
51
|
-
>
|
|
52
|
-
{ __( 'Select Image', 'elementor' ) }
|
|
53
|
-
</Button>
|
|
39
|
+
const setImageSize = ( newValue: string ) => {
|
|
40
|
+
setValue( {
|
|
41
|
+
$$type: 'image',
|
|
42
|
+
value: {
|
|
43
|
+
src,
|
|
44
|
+
size: newValue,
|
|
45
|
+
},
|
|
46
|
+
} );
|
|
47
|
+
};
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
49
|
+
return (
|
|
50
|
+
<Stack gap={ 2 }>
|
|
51
|
+
<ControlContext.Provider value={ { setValue: setImageSrc as SetContextValue, value: src, bind: 'src' } }>
|
|
52
|
+
<ImageMediaControl />
|
|
53
|
+
</ControlContext.Provider>
|
|
54
|
+
<ControlContext.Provider value={ { setValue: setImageSize as SetContextValue, value: size, bind: 'size' } }>
|
|
55
|
+
<Grid container spacing={ 1 } alignItems="center">
|
|
56
|
+
<Grid item xs={ 6 }>
|
|
57
|
+
<SettingsControl.Label> { __( 'Image Resolution', 'elementor' ) }</SettingsControl.Label>
|
|
58
|
+
</Grid>
|
|
59
|
+
<Grid item xs={ 6 }>
|
|
60
|
+
<SelectControl options={ props.sizes } />
|
|
61
|
+
</Grid>
|
|
62
|
+
</Grid>
|
|
63
|
+
</ControlContext.Provider>
|
|
64
|
+
</Stack>
|
|
68
65
|
);
|
|
69
|
-
};
|
|
66
|
+
} );
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Button, Card, CardMedia, CardOverlay, Stack } from '@elementor/ui';
|
|
3
|
+
import { UploadIcon } from '@elementor/icons';
|
|
4
|
+
import { useWpMediaAttachment, useWpMediaFrame } from '@elementor/wp-media';
|
|
5
|
+
import { useControl } from '../control-context';
|
|
6
|
+
import { TransformablePropValue } from '../../types';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
import ControlActions from '../control-actions/control-actions';
|
|
9
|
+
import { createControl } from '../create-control';
|
|
10
|
+
|
|
11
|
+
type ImageAttachmentID = TransformablePropValue< 'image-attachment-id', number >;
|
|
12
|
+
|
|
13
|
+
type Url = TransformablePropValue< 'url', string >;
|
|
14
|
+
|
|
15
|
+
export type ImageSrc = TransformablePropValue<
|
|
16
|
+
'image-src',
|
|
17
|
+
{ id: ImageAttachmentID; url: null } | { url: Url; id: null }
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
export const ImageMediaControl = createControl( () => {
|
|
21
|
+
const { value, setValue } = useControl< ImageSrc >();
|
|
22
|
+
const { id, url } = value?.value ?? {};
|
|
23
|
+
|
|
24
|
+
const { data: attachment } = useWpMediaAttachment( id?.value || null );
|
|
25
|
+
const src = attachment?.url ?? url;
|
|
26
|
+
|
|
27
|
+
const { open } = useWpMediaFrame( {
|
|
28
|
+
types: [ 'image' ],
|
|
29
|
+
multiple: false,
|
|
30
|
+
selected: id?.value || null,
|
|
31
|
+
onSelect: ( selectedAttachment ) => {
|
|
32
|
+
setValue( {
|
|
33
|
+
$$type: 'image-src',
|
|
34
|
+
value: {
|
|
35
|
+
id: {
|
|
36
|
+
$$type: 'image-attachment-id',
|
|
37
|
+
value: selectedAttachment.id,
|
|
38
|
+
},
|
|
39
|
+
url: null,
|
|
40
|
+
},
|
|
41
|
+
} );
|
|
42
|
+
},
|
|
43
|
+
} );
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Card variant="outlined">
|
|
47
|
+
<CardMedia image={ src } sx={ { height: 150 } } />
|
|
48
|
+
<CardOverlay>
|
|
49
|
+
<ControlActions>
|
|
50
|
+
<Stack gap={ 0.5 }>
|
|
51
|
+
<Button
|
|
52
|
+
size="tiny"
|
|
53
|
+
color="inherit"
|
|
54
|
+
variant="outlined"
|
|
55
|
+
onClick={ () => open( { mode: 'browse' } ) }
|
|
56
|
+
>
|
|
57
|
+
{ __( 'Select Image', 'elementor' ) }
|
|
58
|
+
</Button>
|
|
59
|
+
<Button
|
|
60
|
+
size="tiny"
|
|
61
|
+
variant="text"
|
|
62
|
+
color="inherit"
|
|
63
|
+
startIcon={ <UploadIcon /> }
|
|
64
|
+
onClick={ () => open( { mode: 'upload' } ) }
|
|
65
|
+
>
|
|
66
|
+
{ __( 'Upload Image', 'elementor' ) }
|
|
67
|
+
</Button>
|
|
68
|
+
</Stack>
|
|
69
|
+
</ControlActions>
|
|
70
|
+
</CardOverlay>
|
|
71
|
+
</Card>
|
|
72
|
+
);
|
|
73
|
+
} );
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { TextField } from '@elementor/ui';
|
|
3
3
|
import { useControl } from '../control-context';
|
|
4
|
+
import ControlActions from '../control-actions/control-actions';
|
|
5
|
+
import { createControl } from '../create-control';
|
|
4
6
|
|
|
5
7
|
const isEmptyOrNaN = ( value?: string | number ) =>
|
|
6
8
|
value === undefined || value === '' || Number.isNaN( Number( value ) );
|
|
7
9
|
|
|
8
|
-
export const NumberControl = ( { placeholder }: { placeholder?: string } ) => {
|
|
10
|
+
export const NumberControl = createControl( ( { placeholder }: { placeholder?: string } ) => {
|
|
9
11
|
const { value, setValue } = useControl< number | undefined >();
|
|
10
12
|
|
|
11
13
|
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
@@ -14,12 +16,14 @@ export const NumberControl = ( { placeholder }: { placeholder?: string } ) => {
|
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
return (
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
<ControlActions>
|
|
20
|
+
<TextField
|
|
21
|
+
size="tiny"
|
|
22
|
+
type="number"
|
|
23
|
+
value={ isEmptyOrNaN( value ) ? '' : value }
|
|
24
|
+
onChange={ handleChange }
|
|
25
|
+
placeholder={ placeholder }
|
|
26
|
+
/>
|
|
27
|
+
</ControlActions>
|
|
24
28
|
);
|
|
25
|
-
};
|
|
29
|
+
} );
|
|
@@ -2,12 +2,14 @@ import * as React from 'react';
|
|
|
2
2
|
import { MenuItem, Select, SelectChangeEvent } from '@elementor/ui';
|
|
3
3
|
import { useControl } from '../control-context';
|
|
4
4
|
import { PropValue } from '../../types';
|
|
5
|
+
import ControlActions from '../control-actions/control-actions';
|
|
6
|
+
import { createControl } from '../create-control';
|
|
5
7
|
|
|
6
8
|
type Props< T > = {
|
|
7
9
|
options: Array< { label: string; value: T; disabled?: boolean } >;
|
|
8
10
|
};
|
|
9
11
|
|
|
10
|
-
export const SelectControl = < T extends PropValue >( { options }: Props< T > ) => {
|
|
12
|
+
export const SelectControl = createControl( < T extends PropValue >( { options }: Props< T > ) => {
|
|
11
13
|
const { value, setValue } = useControl< T >();
|
|
12
14
|
|
|
13
15
|
const handleChange = ( event: SelectChangeEvent< T > ) => {
|
|
@@ -15,12 +17,14 @@ export const SelectControl = < T extends PropValue >( { options }: Props< T > )
|
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
return (
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
<ControlActions>
|
|
21
|
+
<Select size="tiny" value={ value ?? '' } onChange={ handleChange }>
|
|
22
|
+
{ options.map( ( { label, ...props } ) => (
|
|
23
|
+
<MenuItem key={ props.value } { ...props }>
|
|
24
|
+
{ label }
|
|
25
|
+
</MenuItem>
|
|
26
|
+
) ) }
|
|
27
|
+
</Select>
|
|
28
|
+
</ControlActions>
|
|
25
29
|
);
|
|
26
|
-
};
|
|
30
|
+
} );
|
|
@@ -4,12 +4,14 @@ import { TransformablePropValue } from '../../types';
|
|
|
4
4
|
import { useControl } from '../control-context';
|
|
5
5
|
import { useSyncExternalState } from '../hooks/use-sync-external-state';
|
|
6
6
|
import { SelectionEndAdornment, TextFieldInnerSelection } from '../components/text-field-inner-selection';
|
|
7
|
+
import ControlActions from '../control-actions/control-actions';
|
|
8
|
+
import { createControl } from '../create-control';
|
|
7
9
|
|
|
8
10
|
export type Unit = 'px' | '%' | 'em' | 'rem' | 'vw';
|
|
9
11
|
|
|
10
12
|
const defaultUnits: Unit[] = [ 'px', '%', 'em', 'rem', 'vw' ];
|
|
11
13
|
|
|
12
|
-
export type SizeControlValue = TransformablePropValue< { unit: Unit; size: number } >;
|
|
14
|
+
export type SizeControlValue = TransformablePropValue< 'size', { unit: Unit; size: number } >;
|
|
13
15
|
|
|
14
16
|
export type SizeControlProps = {
|
|
15
17
|
placeholder?: string;
|
|
@@ -17,7 +19,7 @@ export type SizeControlProps = {
|
|
|
17
19
|
units?: Unit[];
|
|
18
20
|
};
|
|
19
21
|
|
|
20
|
-
export const SizeControl = ( { units = defaultUnits, placeholder, startIcon }: SizeControlProps ) => {
|
|
22
|
+
export const SizeControl = createControl( ( { units = defaultUnits, placeholder, startIcon }: SizeControlProps ) => {
|
|
21
23
|
const { value, setValue } = useControl< SizeControlValue >();
|
|
22
24
|
|
|
23
25
|
const [ state, setState ] = useSyncExternalState< SizeControlValue >( {
|
|
@@ -53,15 +55,17 @@ export const SizeControl = ( { units = defaultUnits, placeholder, startIcon }: S
|
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
return (
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
<ControlActions>
|
|
59
|
+
<TextFieldInnerSelection
|
|
60
|
+
endAdornment={
|
|
61
|
+
<SelectionEndAdornment options={ units } onClick={ handleUnitChange } value={ state.value.unit } />
|
|
62
|
+
}
|
|
63
|
+
placeholder={ placeholder }
|
|
64
|
+
startAdornment={ startIcon ?? <InputAdornment position="start">{ startIcon }</InputAdornment> }
|
|
65
|
+
type="number"
|
|
66
|
+
value={ Number.isNaN( state.value.size ) ? '' : state.value.size }
|
|
67
|
+
onChange={ handleSizeChange }
|
|
68
|
+
/>
|
|
69
|
+
</ControlActions>
|
|
66
70
|
);
|
|
67
|
-
};
|
|
71
|
+
} );
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { TextField } from '@elementor/ui';
|
|
3
3
|
import { useControl } from '../control-context';
|
|
4
|
+
import ControlActions from '../control-actions/control-actions';
|
|
5
|
+
import { createControl } from '../create-control';
|
|
4
6
|
|
|
5
7
|
type Props = {
|
|
6
8
|
placeholder?: string;
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
export const TextAreaControl = ( { placeholder }: Props ) => {
|
|
11
|
+
export const TextAreaControl = createControl( ( { placeholder }: Props ) => {
|
|
10
12
|
const { value, setValue } = useControl< string >();
|
|
11
13
|
|
|
12
14
|
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
@@ -14,14 +16,16 @@ export const TextAreaControl = ( { placeholder }: Props ) => {
|
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
return (
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
<ControlActions fullWidth>
|
|
20
|
+
<TextField
|
|
21
|
+
size="tiny"
|
|
22
|
+
multiline
|
|
23
|
+
fullWidth
|
|
24
|
+
rows={ 5 }
|
|
25
|
+
value={ value }
|
|
26
|
+
onChange={ handleChange }
|
|
27
|
+
placeholder={ placeholder }
|
|
28
|
+
/>
|
|
29
|
+
</ControlActions>
|
|
26
30
|
);
|
|
27
|
-
};
|
|
31
|
+
} );
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { TextField } from '@elementor/ui';
|
|
3
3
|
import { useControl } from '../control-context';
|
|
4
|
+
import ControlActions from '../control-actions/control-actions';
|
|
5
|
+
import { createControl } from '../create-control';
|
|
4
6
|
|
|
5
|
-
export const TextControl = ( { placeholder }: { placeholder?: string } ) => {
|
|
7
|
+
export const TextControl = createControl( ( { placeholder }: { placeholder?: string } ) => {
|
|
6
8
|
const { value, setValue } = useControl< string >( '' );
|
|
7
9
|
|
|
8
10
|
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => setValue( event.target.value );
|
|
9
11
|
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
+
return (
|
|
13
|
+
<ControlActions fullWidth>
|
|
14
|
+
<TextField type="text" size="tiny" value={ value } onChange={ handleChange } placeholder={ placeholder } />
|
|
15
|
+
</ControlActions>
|
|
16
|
+
);
|
|
17
|
+
} );
|
|
@@ -2,12 +2,13 @@ import * as React from 'react';
|
|
|
2
2
|
import { useControl } from '../control-context';
|
|
3
3
|
import { ControlToggleButtonGroup, ToggleButtonGroupItem } from '../components/control-toggle-button-group';
|
|
4
4
|
import { PropValue } from '../../types';
|
|
5
|
+
import { createControl } from '../create-control';
|
|
5
6
|
|
|
6
7
|
type ToggleControlProps< T extends PropValue > = {
|
|
7
8
|
options: ToggleButtonGroupItem< T >[];
|
|
8
9
|
};
|
|
9
10
|
|
|
10
|
-
export const ToggleControl = < T extends PropValue >( { options }: ToggleControlProps< T > ) => {
|
|
11
|
+
export const ToggleControl = createControl( < T extends PropValue >( { options }: ToggleControlProps< T > ) => {
|
|
11
12
|
const { value, setValue } = useControl< T >();
|
|
12
13
|
|
|
13
14
|
const handleToggle = ( option: T | null ) => {
|
|
@@ -22,4 +23,4 @@ export const ToggleControl = < T extends PropValue >( { options }: ToggleControl
|
|
|
22
23
|
exclusive={ true }
|
|
23
24
|
/>
|
|
24
25
|
);
|
|
25
|
-
};
|
|
26
|
+
} );
|
package/src/controls/control.tsx
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { ComponentProps } from 'react';
|
|
3
|
-
import { getControlReplacement } from './control-replacement';
|
|
4
|
-
import { useControl } from './control-context';
|
|
5
3
|
import { createError } from '@elementor/utils';
|
|
6
4
|
import { ControlType, ControlTypes, getControlByType } from './controls-registry';
|
|
7
5
|
|
|
@@ -33,8 +31,6 @@ type ControlProps< T extends ControlType > = AnyPropertyRequired< ComponentProps
|
|
|
33
31
|
};
|
|
34
32
|
|
|
35
33
|
export const Control = < T extends ControlType >( { props, type }: ControlProps< T > ) => {
|
|
36
|
-
const { value } = useControl();
|
|
37
|
-
|
|
38
34
|
const ControlByType = getControlByType( type );
|
|
39
35
|
|
|
40
36
|
if ( ! ControlByType ) {
|
|
@@ -43,8 +39,6 @@ export const Control = < T extends ControlType >( { props, type }: ControlProps<
|
|
|
43
39
|
} );
|
|
44
40
|
}
|
|
45
41
|
|
|
46
|
-
const ControlComponent = getControlReplacement( { value } ) || ControlByType;
|
|
47
|
-
|
|
48
42
|
// @ts-expect-error ControlComponent props are inferred from the type (T).
|
|
49
|
-
return <
|
|
43
|
+
return <ControlByType { ...props } />;
|
|
50
44
|
};
|
|
@@ -3,17 +3,26 @@ import { TextControl } from './control-types/text-control';
|
|
|
3
3
|
import { TextAreaControl } from './control-types/text-area-control';
|
|
4
4
|
import { SizeControl } from './control-types/size-control';
|
|
5
5
|
import { SelectControl } from './control-types/select-control';
|
|
6
|
+
import { ControlComponent } from './create-control';
|
|
6
7
|
|
|
7
|
-
export
|
|
8
|
-
image: ImageControl,
|
|
9
|
-
text: TextControl,
|
|
10
|
-
textarea: TextAreaControl,
|
|
11
|
-
size: SizeControl,
|
|
12
|
-
select: SelectControl,
|
|
13
|
-
} as const;
|
|
8
|
+
export type ControlLayout = 'full' | 'two-columns';
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
type ControlRegistry = Record< string, { component: ControlComponent; layout: ControlLayout } >;
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
const controlTypes = {
|
|
13
|
+
image: { component: ImageControl, layout: 'full' },
|
|
14
|
+
text: { component: TextControl, layout: 'two-columns' },
|
|
15
|
+
textarea: { component: TextAreaControl, layout: 'full' },
|
|
16
|
+
size: { component: SizeControl, layout: 'two-columns' },
|
|
17
|
+
select: { component: SelectControl, layout: 'two-columns' },
|
|
18
|
+
} as const satisfies ControlRegistry;
|
|
18
19
|
|
|
19
|
-
export
|
|
20
|
+
export type ControlType = keyof typeof controlTypes;
|
|
21
|
+
|
|
22
|
+
export type ControlTypes = {
|
|
23
|
+
[ key in ControlType ]: ( typeof controlTypes )[ key ][ 'component' ];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getControlByType = ( type: ControlType ) => controlTypes[ type ]?.component;
|
|
27
|
+
|
|
28
|
+
export const getLayoutByType = ( type: ControlType ) => controlTypes[ type ].layout;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ComponentProps, ComponentType } from 'react';
|
|
3
|
+
import { useControlReplacement } from './control-replacement';
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
type AnyComponentType = ComponentType< any >;
|
|
7
|
+
|
|
8
|
+
type Options = {
|
|
9
|
+
supportsReplacements?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const brandSymbol = Symbol( 'control' );
|
|
13
|
+
|
|
14
|
+
export type ControlComponent< TComponent extends AnyComponentType = AnyComponentType > = TComponent & {
|
|
15
|
+
[ brandSymbol ]: true;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function createControl< T extends AnyComponentType >(
|
|
19
|
+
Component: T,
|
|
20
|
+
{ supportsReplacements = true }: Options = {}
|
|
21
|
+
) {
|
|
22
|
+
return ( ( props: ComponentProps< T > ) => {
|
|
23
|
+
const ControlReplacement = useControlReplacement();
|
|
24
|
+
|
|
25
|
+
if ( ControlReplacement && supportsReplacements ) {
|
|
26
|
+
return <ControlReplacement { ...props } />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return <Component { ...props } />;
|
|
30
|
+
} ) as ControlComponent< T >;
|
|
31
|
+
}
|
|
@@ -5,17 +5,16 @@ import { useElementContext } from '../contexts/element-context';
|
|
|
5
5
|
import { useWidgetSettings } from '../hooks/use-widget-settings';
|
|
6
6
|
import { updateSettings } from '../sync/update-settings';
|
|
7
7
|
import { ControlLabel } from '../components/control-label';
|
|
8
|
-
import { ControlContainer } from './components/control-container';
|
|
9
8
|
|
|
10
9
|
type Props = {
|
|
11
10
|
bind: PropKey;
|
|
12
11
|
children: React.ReactNode;
|
|
13
12
|
};
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
const SettingsControl = ( { bind, children }: Props ) => {
|
|
16
15
|
const { element, elementType } = useElementContext();
|
|
17
16
|
|
|
18
|
-
const defaultValue = elementType.propsSchema[ bind ]?.
|
|
17
|
+
const defaultValue = elementType.propsSchema[ bind ]?.default;
|
|
19
18
|
const settingsValue = useWidgetSettings( { id: element.id, bind } );
|
|
20
19
|
const value = settingsValue ?? defaultValue ?? null;
|
|
21
20
|
|
|
@@ -31,12 +30,6 @@ export const SettingsControlProvider = ( { bind, children }: Props ) => {
|
|
|
31
30
|
return <ControlContext.Provider value={ { setValue, value, bind } }>{ children }</ControlContext.Provider>;
|
|
32
31
|
};
|
|
33
32
|
|
|
34
|
-
const SettingsControl = ( { children, bind }: Props ) => (
|
|
35
|
-
<SettingsControlProvider bind={ bind }>
|
|
36
|
-
<ControlContainer flexWrap="wrap">{ children }</ControlContainer>
|
|
37
|
-
</SettingsControlProvider>
|
|
38
|
-
);
|
|
39
|
-
|
|
40
33
|
// TODO: When we start using useControl inside the label component, we should create a new component for it,
|
|
41
34
|
// and keep ControlLabel as a simple label component without context.
|
|
42
35
|
SettingsControl.Label = ControlLabel;
|
|
@@ -54,7 +54,7 @@ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
|
|
|
54
54
|
updatePropValueHistory( currentValue );
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
setValue( { $$type: 'dynamic', value: { name: value } } );
|
|
57
|
+
setValue( { $$type: 'dynamic', value: { name: value, settings: {} } } );
|
|
58
58
|
|
|
59
59
|
onSelect?.();
|
|
60
60
|
};
|
|
@@ -18,7 +18,7 @@ export const DynamicControl = ( { bind, children }: DynamicControlProps ) => {
|
|
|
18
18
|
throw new Error( `Dynamic tag ${ name } not found` );
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const defaultValue = dynamicTag.props_schema[ bind ]?.
|
|
21
|
+
const defaultValue = dynamicTag.props_schema[ bind ]?.default;
|
|
22
22
|
const dynamicValue = settings?.[ bind ] ?? defaultValue;
|
|
23
23
|
|
|
24
24
|
const setDynamicValue = ( newValue: PropValue ) => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useElementContext } from '../../contexts/element-context';
|
|
3
|
+
import { supportsDynamic } from '../utils';
|
|
4
|
+
import { DynamicSelection } from '../components/dynamic-selection';
|
|
5
|
+
import { DatabaseIcon } from '@elementor/icons';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
import { useControl } from '../../controls/control-context';
|
|
8
|
+
import { PopoverActionProps } from '../../controls/control-actions/actions/popover-action';
|
|
9
|
+
|
|
10
|
+
export const usePropDynamicAction = (): PopoverActionProps => {
|
|
11
|
+
const { bind } = useControl();
|
|
12
|
+
const { elementType } = useElementContext();
|
|
13
|
+
|
|
14
|
+
const propType = elementType.propsSchema[ bind ];
|
|
15
|
+
const visible = !! propType && supportsDynamic( propType );
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
visible,
|
|
19
|
+
icon: DatabaseIcon,
|
|
20
|
+
title: __( 'Dynamic Tags', 'elementor' ),
|
|
21
|
+
popoverContent: ( { closePopover } ) => <DynamicSelection onSelect={ closePopover } />,
|
|
22
|
+
};
|
|
23
|
+
};
|