@elementor/editor-controls 0.4.0 → 0.5.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.
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ import { createContext, useContext } from 'react';
3
+ import { type CreateOptions, type PropKey, type PropType, type PropValue } from '@elementor/editor-props';
4
+
5
+ import { HookOutsideProviderError } from './errors';
6
+
7
+ type SetValueMeta = {
8
+ bind?: PropKey;
9
+ };
10
+
11
+ export type SetValue< T > = ( value: T, options?: CreateOptions, meta?: SetValueMeta ) => void;
12
+
13
+ type PropContext< T extends PropValue, P extends PropType > = {
14
+ setValue: SetValue< T >;
15
+ value: T | null;
16
+ propType: P;
17
+ };
18
+
19
+ const PropContext = createContext< PropContext< PropValue, PropType > | null >( null );
20
+
21
+ export type PropProviderProps< T extends PropValue, P extends PropType > = React.PropsWithChildren<
22
+ PropContext< T, P >
23
+ >;
24
+
25
+ export const PropProvider = < T extends PropValue, P extends PropType >( {
26
+ children,
27
+ value,
28
+ setValue,
29
+ propType,
30
+ }: PropProviderProps< T, P > ) => {
31
+ // @ts-expect-error - figure out how to fix this
32
+ return <PropContext.Provider value={ { value, setValue, propType } }>{ children }</PropContext.Provider>;
33
+ };
34
+
35
+ export const usePropContext = < T extends PropValue, P extends PropType >() => {
36
+ const context = useContext( PropContext ) as PropContext< T, P > | null;
37
+
38
+ if ( ! context ) {
39
+ throw new HookOutsideProviderError( {
40
+ context: {
41
+ hook: 'usePropContext',
42
+ provider: 'PropProvider',
43
+ },
44
+ } );
45
+ }
46
+
47
+ return context;
48
+ };
@@ -0,0 +1,103 @@
1
+ import * as React from 'react';
2
+ import { createContext, useContext } from 'react';
3
+ import {
4
+ type ArrayPropType,
5
+ type ArrayPropValue,
6
+ type CreateOptions,
7
+ type ObjectPropType,
8
+ type ObjectPropValue,
9
+ type PropKey,
10
+ type PropType,
11
+ type PropValue,
12
+ } from '@elementor/editor-props';
13
+
14
+ import { HookOutsideProviderError, MissingPropTypeError, UnsupportedParentError } from './errors';
15
+ import { type SetValue, usePropContext } from './prop-context';
16
+
17
+ export type PropKeyContextValue< T, P > = {
18
+ bind: PropKey;
19
+ setValue: SetValue< T >;
20
+ value: T;
21
+ propType: P;
22
+ };
23
+
24
+ export const PropKeyContext = createContext< PropKeyContextValue< PropValue, PropType > | null >( null );
25
+
26
+ type PropKeyProviderProps = React.PropsWithChildren< {
27
+ bind: PropKey;
28
+ } >;
29
+
30
+ export const PropKeyProvider = ( { children, bind }: PropKeyProviderProps ) => {
31
+ const { propType } = usePropContext();
32
+
33
+ if ( ! propType ) {
34
+ throw new MissingPropTypeError( { context: { bind } } );
35
+ }
36
+
37
+ if ( propType.kind === 'array' ) {
38
+ return <ArrayPropKeyProvider bind={ bind }>{ children }</ArrayPropKeyProvider>;
39
+ }
40
+
41
+ if ( propType.kind === 'object' ) {
42
+ return <ObjectPropKeyProvider bind={ bind }>{ children }</ObjectPropKeyProvider>;
43
+ }
44
+
45
+ throw new UnsupportedParentError( { context: { propType } } );
46
+ };
47
+
48
+ const ObjectPropKeyProvider = ( { children, bind }: PropKeyProviderProps ) => {
49
+ const context = usePropContext< ObjectPropValue[ 'value' ], ObjectPropType >();
50
+
51
+ const setValue: SetValue< PropValue > = ( value, options, meta ) => {
52
+ const newValue = {
53
+ ...context.value,
54
+ [ bind ]: value,
55
+ };
56
+
57
+ return context?.setValue( newValue, options, { ...meta, bind } );
58
+ };
59
+
60
+ const value = context.value?.[ bind ];
61
+
62
+ const propType = context.propType.shape[ bind ];
63
+
64
+ return (
65
+ <PropKeyContext.Provider value={ { ...context, value, setValue, bind, propType } }>
66
+ { children }
67
+ </PropKeyContext.Provider>
68
+ );
69
+ };
70
+
71
+ const ArrayPropKeyProvider = ( { children, bind }: PropKeyProviderProps ) => {
72
+ const context = usePropContext< ArrayPropValue[ 'value' ], ArrayPropType >();
73
+
74
+ const setValue = ( value: PropValue, options?: CreateOptions ) => {
75
+ const newValue = [ ...( context.value ?? [] ) ];
76
+
77
+ newValue[ Number( bind ) ] = value;
78
+
79
+ return context?.setValue( newValue, options, { bind } );
80
+ };
81
+
82
+ const value = context.value?.[ Number( bind ) ];
83
+
84
+ const propType = context.propType.item_prop_type;
85
+
86
+ return (
87
+ <PropKeyContext.Provider value={ { ...context, value, setValue, bind, propType } }>
88
+ { children }
89
+ </PropKeyContext.Provider>
90
+ );
91
+ };
92
+
93
+ export const usePropKeyContext = () => {
94
+ const context = useContext( PropKeyContext );
95
+
96
+ if ( ! context ) {
97
+ throw new HookOutsideProviderError( {
98
+ context: { hook: 'usePropKeyContext', provider: 'PropKeyProvider' },
99
+ } );
100
+ }
101
+
102
+ return context;
103
+ };
@@ -0,0 +1,69 @@
1
+ import {
2
+ type CreateOptions,
3
+ type PropKey,
4
+ type PropType,
5
+ type PropTypeUtil,
6
+ type PropValue,
7
+ } from '@elementor/editor-props';
8
+
9
+ import { MissingPropTypeError } from './errors';
10
+ import { type SetValue } from './prop-context';
11
+ import { type PropKeyContextValue, usePropKeyContext } from './prop-key-context';
12
+
13
+ type UseBoundProp< TValue extends PropValue > = {
14
+ bind: PropKey;
15
+ setValue: SetValue< TValue | null >;
16
+ value: TValue;
17
+ propType: PropType;
18
+ };
19
+
20
+ export function useBoundProp< T extends PropValue = PropValue >(): PropKeyContextValue< T, PropType >;
21
+
22
+ export function useBoundProp< TKey extends string, TValue extends PropValue >(
23
+ propTypeUtil: PropTypeUtil< TKey, TValue >
24
+ ): UseBoundProp< TValue >;
25
+
26
+ export function useBoundProp< TKey extends string, TValue extends PropValue >(
27
+ propTypeUtil?: PropTypeUtil< TKey, TValue >
28
+ ) {
29
+ const propKeyContext = usePropKeyContext();
30
+
31
+ // allow using the hook without a propTypeUtil, with no modifications or validations.
32
+ if ( ! propTypeUtil ) {
33
+ return propKeyContext;
34
+ }
35
+
36
+ function setValue( value: TValue | null, options: CreateOptions, meta: { bind?: PropKey } ) {
37
+ if ( value === null ) {
38
+ return propKeyContext?.setValue( null, options, meta );
39
+ }
40
+
41
+ return propKeyContext?.setValue( propTypeUtil?.create( value, options ), {}, meta );
42
+ }
43
+
44
+ const propType = resolveUnionPropType( propKeyContext.propType, propTypeUtil.key );
45
+
46
+ const value = propTypeUtil.extract( propKeyContext.value ?? propType.default ?? null );
47
+
48
+ return {
49
+ ...propKeyContext,
50
+ setValue,
51
+ value,
52
+ propType,
53
+ };
54
+ }
55
+
56
+ // utils
57
+ const resolveUnionPropType = ( propType: PropType, key: string ): PropType => {
58
+ let resolvedPropType = propType;
59
+
60
+ if ( propType.kind === 'union' ) {
61
+ resolvedPropType = propType.prop_types[ key ];
62
+ }
63
+
64
+ if ( ! resolvedPropType ) {
65
+ throw new MissingPropTypeError( { context: { key } } );
66
+ }
67
+
68
+ return resolvedPropType;
69
+ };
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { useId, useRef, useState } from 'react';
3
+ import { type PropKey } from '@elementor/editor-props';
3
4
  import { CopyIcon, EyeIcon, EyeOffIcon, PlusIcon, XIcon } from '@elementor/icons';
4
5
  import {
5
6
  bindPopover,
@@ -32,9 +33,8 @@ export type RepeaterProps< T > = {
32
33
  Label: React.ComponentType< { value: T } >;
33
34
  Icon: React.ComponentType< { value: T } >;
34
35
  Content: React.ComponentType< {
35
- value: T;
36
- setValue: ( newValue: T ) => void;
37
36
  anchorEl: AnchorEl;
37
+ bind: PropKey;
38
38
  } >;
39
39
  };
40
40
  };
@@ -99,17 +99,7 @@ export const Repeater = < T, >( {
99
99
  duplicateItem={ () => duplicateRepeaterItem( index ) }
100
100
  toggleDisableItem={ () => toggleDisableRepeaterItem( index ) }
101
101
  >
102
- { ( props ) => (
103
- <itemSettings.Content
104
- { ...props }
105
- value={ value }
106
- setValue={ ( newValue ) =>
107
- setRepeaterValues(
108
- repeaterValues.map( ( item, i ) => ( i === index ? newValue : item ) )
109
- )
110
- }
111
- />
112
- ) }
102
+ { ( props ) => <itemSettings.Content { ...props } bind={ String( index ) } /> }
113
103
  </RepeaterItem>
114
104
  ) ) }
115
105
  </Stack>
@@ -1,57 +1,34 @@
1
1
  import * as React from 'react';
2
- import {
3
- type BackgroundColorOverlayPropValue,
4
- backgroundPropTypeUtil,
5
- type ColorPropValue,
6
- type PropValue,
7
- } from '@elementor/editor-props';
2
+ import { backgroundPropTypeUtil } from '@elementor/editor-props';
8
3
  import { Grid, Stack } from '@elementor/ui';
9
4
  import { __ } from '@wordpress/i18n';
10
5
 
11
- import { BoundPropProvider, useBoundProp } from '../../bound-prop-context';
6
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../../bound-prop-context';
12
7
  import { ControlLabel } from '../../components/control-label';
13
8
  import { createControl } from '../../create-control';
14
9
  import { ColorControl } from '../color-control';
15
10
  import { BackgroundOverlayRepeaterControl } from './background-overlay/background-overlay-repeater-control';
16
11
 
17
- type SetContextValue = ( v: PropValue ) => void;
18
-
19
12
  export const BackgroundControl = createControl( () => {
20
- const { value, setValue } = useBoundProp( backgroundPropTypeUtil );
21
-
22
- const setColor = ( newValue: ColorPropValue ) => {
23
- setValue( {
24
- ...value,
25
- color: newValue,
26
- } );
27
- };
28
-
29
- const setBackgroundColorOverlay = ( newValue: BackgroundColorOverlayPropValue ) => {
30
- setValue( {
31
- ...value,
32
- 'background-overlay': newValue,
33
- } );
34
- };
13
+ const propContext = useBoundProp( backgroundPropTypeUtil );
35
14
 
36
15
  return (
37
- <Stack gap={ 1.5 }>
38
- <BoundPropProvider
39
- bind="background-overlay"
40
- value={ value?.[ 'background-overlay' ] }
41
- setValue={ setBackgroundColorOverlay as SetContextValue }
42
- >
43
- <BackgroundOverlayRepeaterControl />
44
- </BoundPropProvider>
45
- <BoundPropProvider bind="color" value={ value?.color } setValue={ setColor as SetContextValue }>
46
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
47
- <Grid item xs={ 6 }>
48
- <ControlLabel>{ __( 'Color', 'elementor' ) }</ControlLabel>
49
- </Grid>
50
- <Grid item xs={ 6 }>
51
- <ColorControl />
16
+ <PropProvider { ...propContext }>
17
+ <Stack gap={ 1.5 }>
18
+ <PropKeyProvider bind="background-overlay">
19
+ <BackgroundOverlayRepeaterControl />
20
+ </PropKeyProvider>
21
+ <PropKeyProvider bind="color">
22
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
23
+ <Grid item xs={ 6 }>
24
+ <ControlLabel>{ __( 'Color', 'elementor' ) }</ControlLabel>
25
+ </Grid>
26
+ <Grid item xs={ 6 }>
27
+ <ColorControl />
28
+ </Grid>
52
29
  </Grid>
53
- </Grid>
54
- </BoundPropProvider>
55
- </Stack>
30
+ </PropKeyProvider>
31
+ </Stack>
32
+ </PropProvider>
56
33
  );
57
34
  } );
@@ -1,46 +1,41 @@
1
1
  import * as React from 'react';
2
2
  import {
3
- type BackgroundColorOverlayPropValue,
3
+ backgroundColorOverlayPropTypeUtil,
4
4
  type BackgroundOverlayItemPropValue,
5
5
  backgroundOverlayPropTypeUtil,
6
- type BackgroundOverlayPropValue,
7
- type PropValue,
6
+ type PropKey,
8
7
  } from '@elementor/editor-props';
9
8
  import { Grid, Stack, UnstableColorIndicator } from '@elementor/ui';
10
9
  import { __ } from '@wordpress/i18n';
11
10
 
12
- import { BoundPropProvider, useBoundProp } from '../../../bound-prop-context';
11
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../../../bound-prop-context';
13
12
  import { ControlLabel } from '../../../components/control-label';
14
13
  import { Repeater } from '../../../components/repeater';
15
14
  import { createControl } from '../../../create-control';
16
15
  import { ColorControl } from '../../color-control';
17
16
 
18
- type SetContextValue = ( v: PropValue ) => void;
19
-
20
17
  const initialBackgroundOverlay: BackgroundOverlayItemPropValue = {
21
18
  $$type: 'background-color-overlay',
22
19
  value: 'rgba(0, 0, 0, 0.2)',
23
20
  };
24
21
 
25
22
  export const BackgroundOverlayRepeaterControl = createControl( () => {
26
- const { value: overlayValues, setValue } = useBoundProp( backgroundOverlayPropTypeUtil );
27
-
28
- const setColorOverlay = ( newValue: BackgroundOverlayPropValue[ 'value' ] ) => {
29
- setValue( newValue );
30
- };
23
+ const { propType, value: overlayValues, setValue } = useBoundProp( backgroundOverlayPropTypeUtil );
31
24
 
32
25
  return (
33
- <Repeater
34
- values={ overlayValues ?? [] }
35
- setValues={ setColorOverlay }
36
- label={ __( 'Overlay', 'elementor' ) }
37
- itemSettings={ {
38
- Icon: ItemIcon,
39
- Label: ItemLabel,
40
- Content: ItemContent,
41
- initialValues: initialBackgroundOverlay,
42
- } }
43
- />
26
+ <PropProvider propType={ propType } value={ overlayValues } setValue={ setValue }>
27
+ <Repeater
28
+ values={ overlayValues ?? [] }
29
+ setValues={ setValue }
30
+ label={ __( 'Overlay', 'elementor' ) }
31
+ itemSettings={ {
32
+ Icon: ItemIcon,
33
+ Label: ItemLabel,
34
+ Content: ItemContent,
35
+ initialValues: initialBackgroundOverlay,
36
+ } }
37
+ />
38
+ </PropProvider>
44
39
  );
45
40
  } );
46
41
 
@@ -48,36 +43,25 @@ const ItemIcon = ( { value }: { value: BackgroundOverlayItemPropValue } ) => (
48
43
  <UnstableColorIndicator size="inherit" component="span" value={ value.value } />
49
44
  );
50
45
 
51
- const ItemContent = ( {
52
- value,
53
- setValue,
54
- }: {
55
- value: BackgroundOverlayItemPropValue;
56
- setValue: ( newValue: BackgroundOverlayItemPropValue ) => void;
57
- } ) => {
58
- const setBackgroundColorOverlay = ( newValue: BackgroundColorOverlayPropValue ) => {
59
- setValue( {
60
- $$type: 'background-color-overlay',
61
- value: newValue.value,
62
- } );
63
- };
46
+ const ItemContent = ( { bind }: { bind: PropKey } ) => {
47
+ return (
48
+ <PropKeyProvider bind={ bind }>
49
+ <Content />
50
+ </PropKeyProvider>
51
+ );
52
+ };
64
53
 
54
+ const Content = () => {
65
55
  return (
66
56
  <Stack gap={ 1.5 }>
67
- <BoundPropProvider
68
- bind="background-color-overlay"
69
- value={ value }
70
- setValue={ setBackgroundColorOverlay as SetContextValue }
71
- >
72
- <Grid container spacing={ 1 } alignItems="center">
73
- <Grid item xs={ 12 }>
74
- <ControlLabel>{ __( 'Color', 'elementor' ) }</ControlLabel>
75
- </Grid>
76
- <Grid item xs={ 12 }>
77
- <ColorControl />
78
- </Grid>
57
+ <Grid container spacing={ 1 } alignItems="center">
58
+ <Grid item xs={ 12 }>
59
+ <ControlLabel>{ __( 'Color', 'elementor' ) }</ControlLabel>
60
+ </Grid>
61
+ <Grid item xs={ 12 }>
62
+ <ColorControl propTypeUtil={ backgroundColorOverlayPropTypeUtil } />
79
63
  </Grid>
80
- </BoundPropProvider>
64
+ </Grid>
81
65
  </Stack>
82
66
  );
83
67
  };