@elementor/editor-editing-panel 0.17.0 → 0.19.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/index.d.mts +28 -10
  3. package/dist/index.d.ts +28 -10
  4. package/dist/index.js +941 -398
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +918 -366
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +8 -7
  9. package/src/components/editing-panel-error-fallback.tsx +12 -0
  10. package/src/components/editing-panel.tsx +23 -12
  11. package/src/components/settings-tab.tsx +8 -5
  12. package/src/components/style-sections/background-section/background-color-control.tsx +20 -0
  13. package/src/components/style-sections/background-section/background-section.tsx +15 -0
  14. package/src/components/style-sections/effects-section/box-shadow-repeater.tsx +224 -0
  15. package/src/components/style-sections/effects-section/effects-section.tsx +18 -0
  16. package/src/components/style-sections/position-section/z-index-control.tsx +11 -7
  17. package/src/components/style-sections/size-section.tsx +23 -20
  18. package/src/components/style-sections/spacing-section/linked-dimensions-control.tsx +62 -47
  19. package/src/components/style-sections/typography-section/font-size-control.tsx +10 -6
  20. package/src/components/style-sections/typography-section/font-weight-control.tsx +16 -12
  21. package/src/components/style-sections/typography-section/letter-spacing-control.tsx +10 -6
  22. package/src/components/style-sections/typography-section/text-alignment-control.tsx +12 -8
  23. package/src/components/style-sections/typography-section/text-color-control.tsx +10 -6
  24. package/src/components/style-sections/typography-section/text-direction-control.tsx +37 -0
  25. package/src/components/style-sections/typography-section/text-style-control.tsx +37 -34
  26. package/src/components/style-sections/typography-section/transform-control.tsx +14 -12
  27. package/src/components/style-sections/typography-section/typography-section.tsx +2 -0
  28. package/src/components/style-sections/typography-section/word-spacing-control.tsx +10 -6
  29. package/src/components/style-tab.tsx +10 -4
  30. package/src/control-replacement.tsx +3 -0
  31. package/src/controls/components/control-type-container.tsx +28 -0
  32. package/src/controls/components/repeater.tsx +197 -0
  33. package/src/controls/components/text-field-inner-selection.tsx +2 -2
  34. package/src/controls/control-actions/actions/popover-action.tsx +58 -0
  35. package/src/controls/control-actions/control-actions-menu.ts +8 -0
  36. package/src/controls/control-actions/control-actions.tsx +43 -0
  37. package/src/controls/control-context.tsx +1 -1
  38. package/src/controls/control-replacement.ts +16 -8
  39. package/src/controls/control-types/color-control.tsx +21 -18
  40. package/src/controls/control-types/image-control.tsx +56 -59
  41. package/src/controls/control-types/image-media-control.tsx +73 -0
  42. package/src/controls/control-types/number-control.tsx +13 -9
  43. package/src/controls/control-types/select-control.tsx +14 -10
  44. package/src/controls/control-types/size-control.tsx +18 -14
  45. package/src/controls/control-types/text-area-control.tsx +15 -11
  46. package/src/controls/control-types/text-control.tsx +9 -3
  47. package/src/controls/control-types/toggle-control.tsx +4 -3
  48. package/src/controls/control.tsx +1 -7
  49. package/src/controls/controls-registry.tsx +19 -10
  50. package/src/controls/create-control-replacement.tsx +53 -0
  51. package/src/controls/create-control.tsx +40 -0
  52. package/src/controls/hooks/use-style-control.ts +3 -3
  53. package/src/{hooks → controls/hooks}/use-widget-settings.ts +1 -1
  54. package/src/{props → controls/props}/is-transformable.ts +1 -2
  55. package/src/controls/props/types.ts +51 -0
  56. package/src/{contexts/element-context.tsx → controls/providers/element-provider.tsx} +4 -4
  57. package/src/controls/settings-control.tsx +7 -14
  58. package/src/controls/style-control.tsx +1 -1
  59. package/src/{sync → controls/sync}/get-container.ts +1 -1
  60. package/src/{sync → controls/sync}/update-settings.ts +1 -1
  61. package/src/controls/types.ts +39 -0
  62. package/src/dynamics/components/dynamic-selection-control.tsx +2 -2
  63. package/src/dynamics/components/dynamic-selection.tsx +6 -6
  64. package/src/dynamics/dynamic-control.tsx +2 -2
  65. package/src/dynamics/hooks/use-dynamic-tag.ts +2 -2
  66. package/src/dynamics/hooks/use-prop-dynamic-action.tsx +23 -0
  67. package/src/dynamics/hooks/use-prop-dynamic-tags.ts +7 -7
  68. package/src/dynamics/hooks/use-prop-value-history.ts +3 -3
  69. package/src/dynamics/init.ts +10 -1
  70. package/src/dynamics/types.ts +7 -3
  71. package/src/dynamics/utils.ts +17 -4
  72. package/src/hooks/use-element-style-prop.ts +3 -2
  73. package/src/hooks/use-element-styles.ts +1 -1
  74. package/src/hooks/use-element-type.ts +1 -1
  75. package/src/index.ts +3 -1
  76. package/src/sync/get-element-styles.ts +2 -2
  77. package/src/sync/get-selected-elements.ts +1 -1
  78. package/src/sync/types.ts +2 -1
  79. package/src/sync/update-style.ts +3 -2
  80. package/src/controls/components/control-container.tsx +0 -18
  81. package/src/types.ts +0 -68
@@ -0,0 +1,58 @@
1
+ import * as React from 'react';
2
+ import { bindPopover, bindToggle, IconButton, Popover, Stack, Tooltip, Typography, usePopupState } from '@elementor/ui';
3
+ import { ComponentType, ElementType as ReactElementType, useId } from 'react';
4
+ import { XIcon } from '@elementor/icons';
5
+
6
+ const SIZE = 'tiny';
7
+
8
+ export type PopoverActionProps = {
9
+ title: string;
10
+ visible?: boolean;
11
+ icon: ReactElementType;
12
+ popoverContent: ComponentType< { closePopover: () => void } >;
13
+ };
14
+
15
+ export default function PopoverAction( {
16
+ title,
17
+ visible = true,
18
+ icon: Icon,
19
+ popoverContent: PopoverContent,
20
+ }: PopoverActionProps ) {
21
+ const id = useId();
22
+ const popupState = usePopupState( {
23
+ variant: 'popover',
24
+ popupId: `elementor-popover-action-${ id }`,
25
+ } );
26
+
27
+ if ( ! visible ) {
28
+ return null;
29
+ }
30
+
31
+ return (
32
+ <>
33
+ <Tooltip placement="top" title={ title }>
34
+ <IconButton aria-label={ title } key={ id } size={ SIZE } { ...bindToggle( popupState ) }>
35
+ <Icon fontSize={ SIZE } />
36
+ </IconButton>
37
+ </Tooltip>
38
+ <Popover
39
+ disablePortal
40
+ disableScrollLock
41
+ anchorOrigin={ {
42
+ vertical: 'bottom',
43
+ horizontal: 'center',
44
+ } }
45
+ { ...bindPopover( popupState ) }
46
+ >
47
+ <Stack direction="row" alignItems="center" pl={ 1.5 } pr={ 0.5 } py={ 1.5 }>
48
+ <Icon fontSize={ SIZE } sx={ { mr: 0.5 } } />
49
+ <Typography variant="subtitle2">{ title }</Typography>
50
+ <IconButton sx={ { ml: 'auto' } } size={ SIZE } onClick={ popupState.close }>
51
+ <XIcon fontSize={ SIZE } />
52
+ </IconButton>
53
+ </Stack>
54
+ <PopoverContent closePopover={ popupState.close } />
55
+ </Popover>
56
+ </>
57
+ );
58
+ }
@@ -0,0 +1,8 @@
1
+ import PopoverAction from './actions/popover-action';
2
+ import { createMenu } from '@elementor/menus';
3
+
4
+ export const controlActionsMenu = createMenu( {
5
+ components: {
6
+ PopoverAction,
7
+ },
8
+ } );
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { styled, UnstableFloatingActionBar } from '@elementor/ui';
3
+ import { PropsWithChildren } from 'react';
4
+ import { controlActionsMenu } from './control-actions-menu';
5
+
6
+ const { useMenuItems } = controlActionsMenu;
7
+
8
+ // CSS hack to hide empty floating bars.
9
+ const FloatingBar = styled( UnstableFloatingActionBar )`
10
+ & .MuiPaper-root:empty {
11
+ display: none;
12
+ }
13
+
14
+ // this is for a fix which would be added later on - to force the width externally
15
+ width: 100%;
16
+ & > :first-of-type {
17
+ width: 100%;
18
+ }
19
+ `;
20
+
21
+ export type ControlActionsProps = PropsWithChildren< {
22
+ fullWidth?: boolean;
23
+ } >;
24
+
25
+ export default function ControlActions( { fullWidth = false, children }: ControlActionsProps ) {
26
+ const items = useMenuItems().default;
27
+
28
+ if ( items.length === 0 ) {
29
+ return children;
30
+ }
31
+
32
+ return (
33
+ <FloatingBar
34
+ actions={ items.map( ( { MenuItem, id } ) => (
35
+ <MenuItem key={ id } />
36
+ ) ) }
37
+ // TODO - work on a general layouting solution instead
38
+ sx={ fullWidth ? { width: '100%' } : undefined }
39
+ >
40
+ { children }
41
+ </FloatingBar>
42
+ );
43
+ }
@@ -1,5 +1,5 @@
1
1
  import { createContext, useContext } from 'react';
2
- import { PropKey, PropValue } from '../types';
2
+ import { PropKey, PropValue } from './props/types';
3
3
 
4
4
  export type ControlContext< T extends PropValue > = {
5
5
  bind: PropKey;
@@ -1,26 +1,34 @@
1
- import { PropValue } from '../types';
1
+ import { ComponentType } from 'react';
2
+ import { PropValue } from './props/types';
3
+ import { useControl } from './control-context';
2
4
 
3
5
  type ReplaceWhenParams = {
4
6
  value: PropValue;
5
7
  };
6
8
 
7
9
  type ControlReplacement = {
8
- component: React.ComponentType;
10
+ component: ComponentType;
9
11
  condition: ( { value }: ReplaceWhenParams ) => boolean;
10
12
  };
11
13
 
12
14
  let controlReplacement: ControlReplacement | undefined;
13
15
 
14
- export const replaceControl = ( { component, condition }: ControlReplacement ) => {
15
- controlReplacement = { component, condition };
16
- };
17
-
18
- export const getControlReplacement = ( { value }: ReplaceWhenParams ) => {
16
+ const getControlReplacement = ( { value }: ReplaceWhenParams ) => {
19
17
  let shouldReplace = false;
20
18
 
21
19
  try {
22
- shouldReplace = !! controlReplacement?.condition( { value } );
20
+ shouldReplace = !! controlReplacement?.condition( { value } ) && !! controlReplacement?.component;
23
21
  } catch {}
24
22
 
25
23
  return shouldReplace ? controlReplacement?.component : undefined;
26
24
  };
25
+
26
+ export function replaceControl( { component, condition }: ControlReplacement ) {
27
+ controlReplacement = { component, condition };
28
+ }
29
+
30
+ export function useControlReplacement() {
31
+ const { value } = useControl();
32
+
33
+ return getControlReplacement( { value } );
34
+ }
@@ -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 '../props/types';
4
7
 
5
- export const ColorControl = () => {
6
- const { value, setValue } = useControl< string >();
8
+ export type ColorPropValue = TransformablePropValue< 'color', string >;
7
9
 
8
- const handleChange = debounce( ( selectedColor: string ) => {
9
- setValue( selectedColor );
10
- } );
10
+ export const ColorControl = createControl(
11
+ ( props: Partial< Omit< UnstableColorPickerProps, 'value' | 'onChange' > > ) => {
12
+ const { value, setValue } = useControl< ColorPropValue >();
11
13
 
12
- return <UnstableColorPicker value={ value } onChange={ handleChange }></UnstableColorPicker>;
13
- };
14
+ const handleChange = ( selectedColor: string ) => {
15
+ setValue( {
16
+ $$type: 'color',
17
+ value: selectedColor,
18
+ } );
19
+ };
14
20
 
15
- // TODO: Remove this when the color picker component sends one event per color change [DES-422].
16
- const debounce = < TArgs extends unknown[], TReturn >( func: ( ...args: TArgs ) => TReturn, wait = 300 ) => {
17
- let timer: ReturnType< typeof setTimeout >;
18
-
19
- return ( ...args: TArgs ) => {
20
- clearTimeout( timer );
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 { Button, Card, CardMedia, CardOverlay } from '@elementor/ui';
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 { useWpMediaAttachment, useWpMediaFrame } from '@elementor/wp-media';
4
+ import { ControlContext, useControl } from '../control-context';
5
+ import { type ImageSrc, ImageMediaControl } from './image-media-control';
6
+ import { PropValue, TransformablePropValue } from '../props/types';
7
+ import { SettingsControl } from '../settings-control';
8
+ import { SelectControl } from './select-control';
9
+ import { createControl } from '../create-control';
7
10
 
8
- type Image = {
9
- $$type: 'image';
10
- value:
11
- | {
12
- attachmentId: number;
13
- url?: never;
14
- }
15
- | {
16
- attachmentId?: never;
17
- url: string;
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 { data: attachment } = useWpMediaAttachment( value?.value?.attachmentId );
24
- const src = attachment?.url ?? value?.value?.url;
27
+ const { src, size } = value?.value || {};
25
28
 
26
- const { open } = useWpMediaFrame( {
27
- types: [ 'image' ],
28
- multiple: false,
29
- selected: value?.value?.attachmentId,
30
- onSelect: ( selectedAttachment ) => {
31
- setValue( {
32
- $$type: 'image',
33
- value: {
34
- attachmentId: selectedAttachment.id,
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
- return (
41
- <Card variant="outlined">
42
- <CardMedia image={ src } sx={ { height: 150 } } />
43
- <CardOverlay>
44
- <Button
45
- color="inherit"
46
- size="small"
47
- variant="outlined"
48
- onClick={ () => {
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
- <Button
56
- color="inherit"
57
- size="small"
58
- variant="text"
59
- startIcon={ <UploadIcon /> }
60
- onClick={ () => {
61
- open( { mode: 'upload' } );
62
- } }
63
- >
64
- { __( 'Upload Image', 'elementor' ) }
65
- </Button>
66
- </CardOverlay>
67
- </Card>
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 { __ } from '@wordpress/i18n';
6
+ import { useControl } from '../control-context';
7
+ import ControlActions from '../control-actions/control-actions';
8
+ import { createControl } from '../create-control';
9
+ import { TransformablePropValue } from '../props/types';
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
- <TextField
18
- size="tiny"
19
- type="number"
20
- value={ isEmptyOrNaN( value ) ? '' : value }
21
- onChange={ handleChange }
22
- placeholder={ placeholder }
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
+ } );
@@ -1,13 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import { MenuItem, Select, SelectChangeEvent } from '@elementor/ui';
3
3
  import { useControl } from '../control-context';
4
- import { PropValue } from '../../types';
4
+ import { PropValue } from '../props/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
- <Select size="tiny" value={ value ?? '' } onChange={ handleChange }>
19
- { options.map( ( { label, ...props } ) => (
20
- <MenuItem key={ props.value } { ...props }>
21
- { label }
22
- </MenuItem>
23
- ) ) }
24
- </Select>
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
+ } );
@@ -1,15 +1,17 @@
1
1
  import * as React from 'react';
2
2
  import { InputAdornment } from '@elementor/ui';
3
- import { TransformablePropValue } from '../../types';
3
+ import { TransformablePropValue } from '../props/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
- <TextFieldInnerSelection
57
- endAdornment={
58
- <SelectionEndAdornment options={ units } onClick={ handleUnitChange } value={ state.value.unit } />
59
- }
60
- placeholder={ placeholder }
61
- startAdornment={ startIcon ?? <InputAdornment position="start">{ startIcon }</InputAdornment> }
62
- type="number"
63
- value={ Number.isNaN( state.value.size ) ? '' : state.value.size }
64
- onChange={ handleSizeChange }
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
- <TextField
18
- size="tiny"
19
- multiline
20
- fullWidth
21
- rows={ 5 }
22
- value={ value }
23
- onChange={ handleChange }
24
- placeholder={ placeholder }
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 <TextField type="text" size="tiny" value={ value } onChange={ handleChange } placeholder={ placeholder } />;
11
- };
12
+ return (
13
+ <ControlActions fullWidth>
14
+ <TextField type="text" size="tiny" value={ value } onChange={ handleChange } placeholder={ placeholder } />
15
+ </ControlActions>
16
+ );
17
+ } );
@@ -1,13 +1,14 @@
1
1
  import * as React from 'react';
2
2
  import { useControl } from '../control-context';
3
3
  import { ControlToggleButtonGroup, ToggleButtonGroupItem } from '../components/control-toggle-button-group';
4
- import { PropValue } from '../../types';
4
+ import { PropValue } from '../props/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
+ } );
@@ -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 <ControlComponent { ...props } />;
43
+ return <ControlByType { ...props } />;
50
44
  };