@elementor/editor-controls 0.4.1 → 0.6.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.
@@ -1,31 +1,32 @@
1
1
  import * as React from 'react';
2
- import { linkedDimensionsPropTypeUtil, type PropValue } from '@elementor/editor-props';
2
+ import { linkedDimensionsPropTypeUtil, type LinkedDimensionsPropValue, type PropKey } 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';
6
6
 
7
- import { BoundPropProvider, useBoundProp } from '../bound-prop-context';
7
+ import { PropKeyProvider, PropProvider, type SetValue, useBoundProp } from '../bound-prop-context';
8
8
  import { ControlLabel } from '../components/control-label';
9
9
  import { createControl } from '../create-control';
10
10
  import { SizeControl } from './size-control';
11
11
 
12
- export type Position = 'top' | 'right' | 'bottom' | 'left';
13
-
14
12
  export const LinkedDimensionsControl = createControl( ( { label }: { label: string } ) => {
15
- const { value, setValue } = useBoundProp( linkedDimensionsPropTypeUtil );
13
+ const { value, setValue, propType } = useBoundProp( linkedDimensionsPropTypeUtil );
16
14
  const { top, right, bottom, left, isLinked = true } = value || {};
17
15
 
18
- const setLinkedValue = ( position: Position, newValue: PropValue ) => {
19
- const updatedValue = {
20
- isLinked,
21
- top: isLinked ? newValue : top,
22
- right: isLinked ? newValue : right,
23
- bottom: isLinked ? newValue : bottom,
24
- left: isLinked ? newValue : left,
25
- [ position ]: newValue,
26
- };
16
+ const setLinkedValue: SetValue< LinkedDimensionsPropValue[ 'value' ] > = ( newValue, _, meta ) => {
17
+ if ( ! isLinked ) {
18
+ return setValue( newValue );
19
+ }
27
20
 
28
- setValue( updatedValue );
21
+ const newDimension = newValue[ meta?.bind as keyof LinkedDimensionsPropValue[ 'value' ] ];
22
+
23
+ setValue( {
24
+ isLinked,
25
+ top: newDimension,
26
+ right: newDimension,
27
+ bottom: newDimension,
28
+ left: newDimension,
29
+ } );
29
30
  };
30
31
 
31
32
  const toggleLinked = () => {
@@ -43,7 +44,7 @@ export const LinkedDimensionsControl = createControl( ( { label }: { label: stri
43
44
  const LinkedIcon = isLinked ? LinkIcon : DetachIcon;
44
45
 
45
46
  return (
46
- <>
47
+ <PropProvider propType={ propType } value={ value } setValue={ setLinkedValue }>
47
48
  <Stack direction="row" gap={ 2 } flexWrap="nowrap">
48
49
  <ControlLabel>{ label }</ControlLabel>
49
50
  <ToggleButton
@@ -63,12 +64,7 @@ export const LinkedDimensionsControl = createControl( ( { label }: { label: stri
63
64
  <ControlLabel>{ __( 'Top', 'elementor' ) }</ControlLabel>
64
65
  </Grid>
65
66
  <Grid item xs={ 12 }>
66
- <Control
67
- bind={ 'top' }
68
- value={ top }
69
- setValue={ setLinkedValue }
70
- startIcon={ <SideTopIcon fontSize={ 'tiny' } /> }
71
- />
67
+ <Control bind={ 'top' } startIcon={ <SideTopIcon fontSize={ 'tiny' } /> } />
72
68
  </Grid>
73
69
  </Grid>
74
70
  <Grid container gap={ 1 } alignItems="center">
@@ -76,12 +72,7 @@ export const LinkedDimensionsControl = createControl( ( { label }: { label: stri
76
72
  <ControlLabel>{ __( 'Right', 'elementor' ) }</ControlLabel>
77
73
  </Grid>
78
74
  <Grid item xs={ 12 }>
79
- <Control
80
- bind={ 'right' }
81
- value={ right }
82
- setValue={ setLinkedValue }
83
- startIcon={ <SideRightIcon fontSize={ 'tiny' } /> }
84
- />
75
+ <Control bind={ 'right' } startIcon={ <SideRightIcon fontSize={ 'tiny' } /> } />
85
76
  </Grid>
86
77
  </Grid>
87
78
  </Stack>
@@ -91,12 +82,7 @@ export const LinkedDimensionsControl = createControl( ( { label }: { label: stri
91
82
  <ControlLabel>{ __( 'Bottom', 'elementor' ) }</ControlLabel>
92
83
  </Grid>
93
84
  <Grid item xs={ 12 }>
94
- <Control
95
- bind={ 'bottom' }
96
- value={ bottom }
97
- setValue={ setLinkedValue }
98
- startIcon={ <SideBottomIcon fontSize={ 'tiny' } /> }
99
- />
85
+ <Control bind={ 'bottom' } startIcon={ <SideBottomIcon fontSize={ 'tiny' } /> } />
100
86
  </Grid>
101
87
  </Grid>
102
88
  <Grid container gap={ 1 } alignItems="center">
@@ -104,31 +90,16 @@ export const LinkedDimensionsControl = createControl( ( { label }: { label: stri
104
90
  <ControlLabel>{ __( 'Left', 'elementor' ) }</ControlLabel>
105
91
  </Grid>
106
92
  <Grid item xs={ 12 }>
107
- <Control
108
- bind={ 'left' }
109
- value={ left }
110
- setValue={ setLinkedValue }
111
- startIcon={ <SideLeftIcon fontSize={ 'tiny' } /> }
112
- />
93
+ <Control bind={ 'left' } startIcon={ <SideLeftIcon fontSize={ 'tiny' } /> } />
113
94
  </Grid>
114
95
  </Grid>
115
96
  </Stack>
116
- </>
97
+ </PropProvider>
117
98
  );
118
99
  } );
119
100
 
120
- const Control = ( {
121
- bind,
122
- startIcon,
123
- value,
124
- setValue,
125
- }: {
126
- bind: Position;
127
- value: PropValue;
128
- startIcon: React.ReactNode;
129
- setValue: ( bind: Position, newValue: PropValue ) => void;
130
- } ) => (
131
- <BoundPropProvider setValue={ ( newValue ) => setValue( bind, newValue ) } value={ value } bind={ bind }>
101
+ const Control = ( { bind, startIcon }: { bind: PropKey; startIcon: React.ReactNode } ) => (
102
+ <PropKeyProvider bind={ bind }>
132
103
  <SizeControl startIcon={ startIcon } />
133
- </BoundPropProvider>
104
+ </PropKeyProvider>
134
105
  );
@@ -6,7 +6,7 @@ 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
- const isEmptyOrNaN = ( value?: string | number ) =>
9
+ const isEmptyOrNaN = ( value?: string | number | null ) =>
10
10
  value === null || value === undefined || value === '' || Number.isNaN( Number( value ) );
11
11
 
12
12
  export const NumberControl = createControl(
@@ -1,71 +1,41 @@
1
1
  import * as React from 'react';
2
- import { type ColorPropValue, type PropValue, type SizePropValue, strokePropTypeUtil } from '@elementor/editor-props';
2
+ import { strokePropTypeUtil } from '@elementor/editor-props';
3
3
  import { Grid, Stack } from '@elementor/ui';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
- import { BoundPropProvider, useBoundProp } from '../bound-prop-context';
6
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
7
7
  import { ControlLabel } from '../components/control-label';
8
8
  import { createControl } from '../create-control';
9
9
  import { ColorControl } from './color-control';
10
10
  import { SizeControl, type Unit } from './size-control';
11
11
 
12
- type SetContextValue = ( v: PropValue ) => void;
13
-
14
- export type StrokeProps< T > = {
12
+ export type StrokeProps = {
15
13
  bind: string;
16
- value: T;
17
- setValue: ( v: T ) => void;
18
14
  label: string;
19
15
  children: React.ReactNode;
20
16
  };
17
+
21
18
  const units: Unit[] = [ 'px', 'em', 'rem' ];
22
19
 
23
20
  export const StrokeControl = createControl( () => {
24
- const { value, setValue } = useBoundProp( strokePropTypeUtil );
25
-
26
- const setStrokeWidth = ( newValue: SizePropValue ) => {
27
- const updatedValue = {
28
- ...value,
29
- width: newValue,
30
- };
31
-
32
- setValue( updatedValue );
33
- };
34
-
35
- const setStrokeColor = ( newValue: ColorPropValue ) => {
36
- const updatedValue = {
37
- ...value,
38
- color: newValue,
39
- };
40
-
41
- setValue( updatedValue );
42
- };
21
+ const propContext = useBoundProp( strokePropTypeUtil );
43
22
 
44
23
  return (
45
- <Stack gap={ 1.5 }>
46
- <Control
47
- bind="width"
48
- label={ __( 'Stroke Width', 'elementor' ) }
49
- value={ value?.width }
50
- setValue={ setStrokeWidth }
51
- >
52
- <SizeControl units={ units } />
53
- </Control>
54
-
55
- <Control
56
- bind="color"
57
- label={ __( 'Stroke Color', 'elementor' ) }
58
- value={ value?.color }
59
- setValue={ setStrokeColor }
60
- >
61
- <ColorControl />
62
- </Control>
63
- </Stack>
24
+ <PropProvider { ...propContext }>
25
+ <Stack gap={ 1.5 }>
26
+ <Control bind="width" label={ __( 'Stroke Width', 'elementor' ) }>
27
+ <SizeControl units={ units } />
28
+ </Control>
29
+ <Control bind="color" label={ __( 'Stroke Color', 'elementor' ) }>
30
+ <ColorControl />
31
+ </Control>
32
+ </Stack>
33
+ </PropProvider>
64
34
  );
65
35
  } );
66
36
 
67
- const Control = < T extends PropValue >( { bind, value, setValue, label, children }: StrokeProps< T > ) => (
68
- <BoundPropProvider bind={ bind } value={ value } setValue={ setValue as SetContextValue }>
37
+ const Control = ( { bind, label, children }: StrokeProps ) => (
38
+ <PropKeyProvider bind={ bind }>
69
39
  <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
70
40
  <Grid item xs={ 6 }>
71
41
  <ControlLabel>{ label }</ControlLabel>
@@ -74,5 +44,5 @@ const Control = < T extends PropValue >( { bind, value, setValue, label, childre
74
44
  { children }
75
45
  </Grid>
76
46
  </Grid>
77
- </BoundPropProvider>
47
+ </PropKeyProvider>
78
48
  );
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type UrlPropValue } from '@elementor/editor-props';
2
+ import { urlPropTypeUtil } from '@elementor/editor-props';
3
3
  import { TextField } from '@elementor/ui';
4
4
 
5
5
  import { useBoundProp } from '../bound-prop-context';
@@ -7,20 +7,15 @@ import ControlActions from '../control-actions/control-actions';
7
7
  import { createControl } from '../create-control';
8
8
 
9
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
- } );
10
+ const { value, setValue } = useBoundProp( urlPropTypeUtil );
11
+ const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => setValue( event.target.value );
17
12
 
18
13
  return (
19
14
  <ControlActions>
20
15
  <TextField
21
16
  size="tiny"
22
17
  fullWidth
23
- value={ value?.value }
18
+ value={ value ?? '' }
24
19
  onChange={ handleChange }
25
20
  placeholder={ placeholder }
26
21
  />
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // control types
2
2
  export { ImageControl } from './controls/image-control';
3
+ export { AutocompleteControl } from './controls/autocomplete-control';
3
4
  export { TextControl } from './controls/text-control';
4
5
  export { TextAreaControl } from './controls/text-area-control';
5
6
  export { SizeControl } from './controls/size-control';
@@ -26,12 +27,13 @@ export type { ControlComponent } from './create-control';
26
27
  export type { ToggleButtonGroupItem } from './components/control-toggle-button-group';
27
28
  export type { EqualUnequalItems } from './controls/equal-unequal-sizes-control';
28
29
  export type { ControlActionsItems } from './control-actions/control-actions-context';
29
- export type { BoundPropProviderProps } from './bound-prop-context';
30
+ export type { PropProviderProps } from './bound-prop-context';
31
+ export type { SetValue } from './bound-prop-context/prop-context';
30
32
 
31
33
  // providers
32
34
  export { createControlReplacement, ControlReplacementProvider } from './create-control-replacement';
33
- export { useBoundProp, BoundPropProvider } from './bound-prop-context';
34
35
  export { ControlActionsProvider, useControlActions } from './control-actions/control-actions-context';
36
+ export { useBoundProp, PropProvider, PropKeyProvider } from './bound-prop-context';
35
37
 
36
38
  // hooks
37
39
  export { useSyncExternalState } from './hooks/use-sync-external-state';
@@ -1,67 +0,0 @@
1
- import * as React from 'react';
2
- import { createContext, useContext } from 'react';
3
- import { type CreateOptions, type PropKey, type PropTypeUtil, type PropValue } from '@elementor/editor-props';
4
-
5
- // Context
6
- export type BoundPropContext< T extends PropValue > = {
7
- bind: PropKey;
8
- setValue: ( value: T | null ) => void;
9
- value: T;
10
- };
11
-
12
- const BoundPropContext = createContext< BoundPropContext< PropValue > | null >( null );
13
-
14
- // Provider
15
- export type BoundPropProviderProps< T extends PropValue > = BoundPropContext< T > & {
16
- children: React.ReactNode;
17
- setValue: ( value: T | null ) => void;
18
- };
19
-
20
- export const BoundPropProvider = ( { children, value, setValue, bind }: BoundPropProviderProps< PropValue > ) => {
21
- return <BoundPropContext.Provider value={ { value, setValue, bind } }>{ children }</BoundPropContext.Provider>;
22
- };
23
-
24
- // Hook
25
- type SetValue< T > = ( value: T | null, options?: CreateOptions ) => void;
26
-
27
- type UseBoundProp< TValue > = {
28
- bind: PropKey;
29
- setValue: SetValue< TValue >;
30
- value: TValue;
31
- };
32
-
33
- export function useBoundProp< TValue extends PropValue >(): BoundPropContext< TValue >;
34
-
35
- export function useBoundProp< TKey extends string, TValue extends PropValue >(
36
- propTypeUtil: PropTypeUtil< TKey, TValue >
37
- ): UseBoundProp< TValue >;
38
-
39
- export function useBoundProp< TKey extends string, TValue extends PropValue >(
40
- propTypeUtil?: PropTypeUtil< TKey, TValue >
41
- ) {
42
- const boundPropContext = useContext< BoundPropContext< TValue > >( BoundPropContext as never );
43
-
44
- if ( ! boundPropContext ) {
45
- throw new Error( 'useBoundProp must be used within a BoundPropProvider' );
46
- }
47
-
48
- if ( ! propTypeUtil ) {
49
- return boundPropContext;
50
- }
51
-
52
- function setValue( value: TValue | null, options?: CreateOptions ) {
53
- if ( value === null ) {
54
- return boundPropContext.setValue( null );
55
- }
56
-
57
- return boundPropContext.setValue( propTypeUtil?.create( value, options ) as TValue );
58
- }
59
-
60
- const value = propTypeUtil.extract( boundPropContext.value );
61
-
62
- return {
63
- ...boundPropContext,
64
- setValue,
65
- value,
66
- };
67
- }