@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,21 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { type ReactNode, useId, useRef } from 'react';
3
- import {
4
- type PropKey,
5
- type PropTypeUtil,
6
- type PropValue,
7
- sizePropTypeUtil,
8
- type SizePropValue,
9
- } from '@elementor/editor-props';
3
+ import { type PropKey, type PropTypeUtil, sizePropTypeUtil, type SizePropValue } from '@elementor/editor-props';
10
4
  import { bindPopover, bindToggle, Grid, Popover, Stack, ToggleButton, usePopupState } from '@elementor/ui';
11
5
  import { __ } from '@wordpress/i18n';
12
6
 
13
- import { BoundPropProvider, useBoundProp } from '../bound-prop-context';
7
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
14
8
  import { ControlLabel } from '../components/control-label';
15
9
  import { SizeControl } from './size-control';
16
10
 
17
- type SetContextValue = ( v: PropValue ) => void;
18
-
19
11
  type MultiSizePropValue = Record< PropKey, SizePropValue >;
20
12
 
21
13
  type Item = {
@@ -33,7 +25,9 @@ type Props< TMultiPropType extends string, TPropValue extends MultiSizePropValue
33
25
  multiSizePropTypeUtil: PropTypeUtil< TMultiPropType, TPropValue >;
34
26
  };
35
27
 
36
- const isEqualSizes = ( values: SizePropValue[], items: EqualUnequalItems ) => {
28
+ const isEqualSizes = ( propValue: MultiSizePropValue, items: EqualUnequalItems ) => {
29
+ const values = Object.values( propValue );
30
+
37
31
  if ( values.length !== items.length ) {
38
32
  return false;
39
33
  }
@@ -41,7 +35,7 @@ const isEqualSizes = ( values: SizePropValue[], items: EqualUnequalItems ) => {
41
35
  const [ firstValue, ...restValues ] = values;
42
36
 
43
37
  return restValues.every(
44
- ( value ) => value.value?.size === firstValue.value?.size && value.value?.unit === firstValue.value?.unit
38
+ ( value ) => value?.value?.size === firstValue?.value?.size && value?.value?.unit === firstValue?.value?.unit
45
39
  );
46
40
  };
47
41
 
@@ -55,32 +49,48 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
55
49
  const controlRef = useRef< HTMLElement >( null );
56
50
  const popupState = usePopupState( { variant: 'popover', popupId } );
57
51
 
52
+ const {
53
+ propType: multiSizePropType,
54
+ value: multiSizeValue,
55
+ setValue: setMultiSizeValue,
56
+ } = useBoundProp( multiSizePropTypeUtil );
57
+
58
58
  const { value: sizeValue, setValue: setSizeValue } = useBoundProp( sizePropTypeUtil );
59
- const { value: multiSizeValue, setValue: setMultiSizeValue } = useBoundProp( multiSizePropTypeUtil );
60
59
 
61
60
  const splitEqualValue = () => {
62
61
  if ( ! sizeValue ) {
63
- return {};
62
+ return null;
64
63
  }
65
64
 
66
- return items.reduce( ( acc, { bind } ) => ( { ...acc, [ bind ]: sizePropTypeUtil.create( sizeValue ) } ), {} );
65
+ return items.reduce< TPropValue >(
66
+ ( acc, { bind } ) => ( { ...acc, [ bind ]: sizePropTypeUtil.create( sizeValue ) } ),
67
+ {} as TPropValue
68
+ );
67
69
  };
68
70
 
69
- const setNestedProp = ( item: Item, newValue: SizePropValue ) => {
71
+ const setNestedProp = ( newValue: TPropValue ) => {
70
72
  const newMappedValues = {
71
73
  ...( multiSizeValue ?? splitEqualValue() ),
72
- [ item.bind ]: newValue,
74
+ ...newValue,
73
75
  };
74
76
 
75
- const isEqual = isEqualSizes( Object.values( newMappedValues ), items );
77
+ const isEqual = isEqualSizes( newMappedValues, items );
76
78
 
77
79
  if ( isEqual ) {
78
- return setSizeValue( newValue?.value );
80
+ return setSizeValue( Object.values( newMappedValues )[ 0 ].value );
79
81
  }
80
82
 
81
83
  setMultiSizeValue( newMappedValues );
82
84
  };
83
85
 
86
+ const getMultiSizeValues = () => {
87
+ if ( multiSizeValue ) {
88
+ return multiSizeValue;
89
+ }
90
+
91
+ return splitEqualValue() ?? null;
92
+ };
93
+
84
94
  return (
85
95
  <>
86
96
  <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap" ref={ controlRef }>
@@ -88,23 +98,18 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
88
98
  <ControlLabel>{ label }</ControlLabel>
89
99
  </Grid>
90
100
  <Grid item xs={ 6 }>
91
- <EqualSizeControl
92
- items={ items }
93
- value={ sizeValue }
94
- multiSizeValue={ multiSizeValue }
95
- setValue={ setSizeValue }
96
- iconButton={
97
- <ToggleButton
98
- size={ 'tiny' }
99
- value={ 'check' }
100
- sx={ { marginLeft: 'auto' } }
101
- { ...bindToggle( popupState ) }
102
- selected={ popupState.isOpen }
103
- >
104
- { icon }
105
- </ToggleButton>
106
- }
107
- />
101
+ <Stack direction="row" alignItems="center" gap={ 1 }>
102
+ <SizeControl placeholder={ __( 'MIXED', 'elementor' ) } />
103
+ <ToggleButton
104
+ size={ 'tiny' }
105
+ value={ 'check' }
106
+ sx={ { marginLeft: 'auto' } }
107
+ { ...bindToggle( popupState ) }
108
+ selected={ popupState.isOpen }
109
+ >
110
+ { icon }
111
+ </ToggleButton>
112
+ </Stack>
108
113
  </Grid>
109
114
  </Grid>
110
115
  <Popover
@@ -123,113 +128,34 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
123
128
  paper: { sx: { mt: 0.5, p: 2, pt: 1, width: controlRef.current?.getBoundingClientRect().width } },
124
129
  } }
125
130
  >
126
- <Stack gap={ 1.5 }>
127
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
128
- <MultiSizeValueControl
129
- item={ items[ 0 ] }
130
- value={ multiSizeValue }
131
- setNestedProp={ setNestedProp }
132
- splitEqualValue={ splitEqualValue }
133
- />
134
- <MultiSizeValueControl
135
- item={ items[ 1 ] }
136
- value={ multiSizeValue }
137
- setNestedProp={ setNestedProp }
138
- splitEqualValue={ splitEqualValue }
139
- />
140
- </Grid>
141
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
142
- <MultiSizeValueControl
143
- item={ items[ 3 ] }
144
- value={ multiSizeValue }
145
- setNestedProp={ setNestedProp }
146
- splitEqualValue={ splitEqualValue }
147
- />
148
- <MultiSizeValueControl
149
- item={ items[ 2 ] }
150
- value={ multiSizeValue }
151
- setNestedProp={ setNestedProp }
152
- splitEqualValue={ splitEqualValue }
153
- />
154
- </Grid>
155
- </Stack>
131
+ <PropProvider propType={ multiSizePropType } value={ getMultiSizeValues() } setValue={ setNestedProp }>
132
+ <Stack gap={ 1.5 }>
133
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
134
+ <MultiSizeValueControl item={ items[ 0 ] } />
135
+ <MultiSizeValueControl item={ items[ 1 ] } />
136
+ </Grid>
137
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
138
+ <MultiSizeValueControl item={ items[ 3 ] } />
139
+ <MultiSizeValueControl item={ items[ 2 ] } />
140
+ </Grid>
141
+ </Stack>
142
+ </PropProvider>
156
143
  </Popover>
157
144
  </>
158
145
  );
159
146
  }
160
147
 
161
- const MultiSizeValueControl = < TPropValue extends MultiSizePropValue >( {
162
- item,
163
- value,
164
- setNestedProp,
165
- splitEqualValue,
166
- }: {
167
- item: Item;
168
- value: TPropValue | undefined;
169
- setNestedProp: ( item: Item, newValue: SizePropValue ) => void;
170
- splitEqualValue: () => TPropValue;
171
- } ) => {
172
- const handleChange = ( val: SizePropValue ) => setNestedProp( item, val );
173
-
174
- const getMultiSizeValues = () => {
175
- if ( value ) {
176
- return value?.[ item.bind ] ?? null;
177
- }
178
-
179
- return splitEqualValue()?.[ item.bind ] ?? null;
180
- };
181
-
182
- return (
183
- <BoundPropProvider bind={ '' } setValue={ handleChange as SetContextValue } value={ getMultiSizeValues() }>
184
- <Grid item xs={ 6 }>
185
- <Grid container gap={ 1 } alignItems="center">
186
- <Grid item xs={ 12 }>
187
- <ControlLabel>{ item.label }</ControlLabel>
188
- </Grid>
189
- <Grid item xs={ 12 }>
190
- <SizeControl startIcon={ item.icon } />
191
- </Grid>
148
+ const MultiSizeValueControl = ( { item }: { item: Item } ) => (
149
+ <PropKeyProvider bind={ item.bind }>
150
+ <Grid item xs={ 6 }>
151
+ <Grid container gap={ 1 } alignItems="center">
152
+ <Grid item xs={ 12 }>
153
+ <ControlLabel>{ item.label }</ControlLabel>
154
+ </Grid>
155
+ <Grid item xs={ 12 }>
156
+ <SizeControl startIcon={ item.icon } />
192
157
  </Grid>
193
158
  </Grid>
194
- </BoundPropProvider>
195
- );
196
- };
197
-
198
- const EqualSizeControl = ( {
199
- value,
200
- items,
201
- setValue,
202
- iconButton,
203
- multiSizeValue,
204
- }: {
205
- value: SizePropValue[ 'value' ] | null;
206
- items: EqualUnequalItems;
207
- setValue: ( newValue: SizePropValue[ 'value' ] ) => void;
208
- iconButton: ReactNode;
209
- multiSizeValue: PropValue;
210
- } ) => {
211
- const handleChange = ( newValue: SizePropValue ) => {
212
- setValue( newValue.value );
213
- };
214
-
215
- const getDisplayValue = () => {
216
- if ( value ) {
217
- return sizePropTypeUtil.create( value );
218
- }
219
-
220
- const multiValues = Object.values( multiSizeValue ?? {} ) as SizePropValue[];
221
-
222
- if ( isEqualSizes( multiValues, items ) ) {
223
- return sizePropTypeUtil.create( multiValues[ 0 ].value );
224
- }
225
- };
226
-
227
- return (
228
- <BoundPropProvider bind={ '' } setValue={ handleChange as SetContextValue } value={ getDisplayValue() ?? null }>
229
- <Stack direction="row" alignItems="center" gap={ 1 }>
230
- <SizeControl placeholder={ __( 'MIXED', 'elementor' ) } />
231
- { iconButton }
232
- </Stack>
233
- </BoundPropProvider>
234
- );
235
- };
159
+ </Grid>
160
+ </PropKeyProvider>
161
+ );
@@ -1,10 +1,10 @@
1
1
  import * as React from 'react';
2
- import { gapPropTypeUtil, type PropValue } from '@elementor/editor-props';
2
+ import { gapPropTypeUtil, type GapPropValue } from '@elementor/editor-props';
3
3
  import { DetachIcon, LinkIcon } 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';
@@ -12,18 +12,21 @@ import { SizeControl } from './size-control';
12
12
  export type Gap = 'row' | 'column';
13
13
 
14
14
  export const GapControl = createControl( ( { label }: { label: string } ) => {
15
- const { value, setValue } = useBoundProp( gapPropTypeUtil );
15
+ const { propType, value, setValue } = useBoundProp( gapPropTypeUtil );
16
16
  const { column, row, isLinked = true } = value || {};
17
17
 
18
- const setLinkedValue = ( gap: Gap, newValue: PropValue ) => {
19
- const updatedValue = {
20
- isLinked,
21
- column: isLinked ? newValue : column,
22
- row: isLinked ? newValue : row,
23
- [ gap ]: newValue,
24
- };
18
+ const setLinkedValue: SetValue< GapPropValue[ 'value' ] > = ( newValue, _, meta ) => {
19
+ if ( ! isLinked ) {
20
+ return setValue( newValue );
21
+ }
25
22
 
26
- setValue( updatedValue );
23
+ const newDimension = newValue[ meta?.bind as Gap ];
24
+
25
+ setValue( {
26
+ isLinked,
27
+ column: newDimension,
28
+ row: newDimension,
29
+ } );
27
30
  };
28
31
 
29
32
  const toggleLinked = () => {
@@ -39,7 +42,7 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
39
42
  const LinkedIcon = isLinked ? LinkIcon : DetachIcon;
40
43
 
41
44
  return (
42
- <>
45
+ <PropProvider propType={ propType } value={ value } setValue={ setLinkedValue }>
43
46
  <Stack direction="row" gap={ 2 } flexWrap="nowrap">
44
47
  <ControlLabel>{ label }</ControlLabel>
45
48
  <ToggleButton
@@ -59,13 +62,9 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
59
62
  <ControlLabel>{ __( 'Column', 'elementor' ) }</ControlLabel>
60
63
  </Grid>
61
64
  <Grid item xs={ 12 }>
62
- <BoundPropProvider
63
- setValue={ ( newValue ) => setLinkedValue( 'column', newValue ) }
64
- value={ column }
65
- bind="column"
66
- >
65
+ <PropKeyProvider bind="column">
67
66
  <SizeControl />
68
- </BoundPropProvider>
67
+ </PropKeyProvider>
69
68
  </Grid>
70
69
  </Grid>
71
70
  <Grid container gap={ 1 } alignItems="center">
@@ -73,16 +72,12 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
73
72
  <ControlLabel>{ __( 'Row', 'elementor' ) }</ControlLabel>
74
73
  </Grid>
75
74
  <Grid item xs={ 12 }>
76
- <BoundPropProvider
77
- setValue={ ( newValue ) => setLinkedValue( 'row', newValue ) }
78
- value={ row }
79
- bind="row"
80
- >
75
+ <PropKeyProvider bind="row">
81
76
  <SizeControl />
82
- </BoundPropProvider>
77
+ </PropKeyProvider>
83
78
  </Grid>
84
79
  </Grid>
85
80
  </Stack>
86
- </>
81
+ </PropProvider>
87
82
  );
88
83
  } );
@@ -1,53 +1,38 @@
1
1
  import * as React from 'react';
2
- import { imagePropTypeUtil, type ImageSrcPropValue, type PropValue, type SizePropValue } from '@elementor/editor-props';
2
+ import { imagePropTypeUtil } 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 { ImageMediaControl } from './image-media-control';
10
10
  import { SelectControl } from './select-control';
11
11
 
12
- type SetContextValue = ( v: PropValue ) => void;
13
-
14
12
  export type ImageControlProps = {
15
13
  sizes: { label: string; value: string }[];
16
14
  };
17
15
 
18
16
  export const ImageControl = createControl( ( props: ImageControlProps ) => {
19
- const { value, setValue } = useBoundProp( imagePropTypeUtil );
20
- const { src, size } = value || {};
21
-
22
- const setImageSrc = ( newValue: ImageSrcPropValue ) => {
23
- setValue( {
24
- src: newValue,
25
- size: size as SizePropValue,
26
- } );
27
- };
28
-
29
- const setImageSize = ( newValue: SizePropValue ) => {
30
- setValue( {
31
- src: src as ImageSrcPropValue,
32
- size: newValue,
33
- } );
34
- };
17
+ const propContext = useBoundProp( imagePropTypeUtil );
35
18
 
36
19
  return (
37
- <Stack gap={ 1.5 }>
38
- <BoundPropProvider value={ src } setValue={ setImageSrc as SetContextValue } bind={ 'src' }>
39
- <ImageMediaControl />
40
- </BoundPropProvider>
41
- <BoundPropProvider value={ size } setValue={ setImageSize as SetContextValue } bind={ 'size' }>
42
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
43
- <Grid item xs={ 6 }>
44
- <ControlLabel> { __( 'Image Resolution', 'elementor' ) }</ControlLabel>
45
- </Grid>
46
- <Grid item xs={ 6 }>
47
- <SelectControl options={ props.sizes } />
20
+ <PropProvider { ...propContext }>
21
+ <Stack gap={ 1.5 }>
22
+ <PropKeyProvider bind={ 'src' }>
23
+ <ImageMediaControl />
24
+ </PropKeyProvider>
25
+ <PropKeyProvider bind={ 'size' }>
26
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
27
+ <Grid item xs={ 6 }>
28
+ <ControlLabel> { __( 'Image Resolution', 'elementor' ) }</ControlLabel>
29
+ </Grid>
30
+ <Grid item xs={ 6 }>
31
+ <SelectControl options={ props.sizes } />
32
+ </Grid>
48
33
  </Grid>
49
- </Grid>
50
- </BoundPropProvider>
51
- </Stack>
34
+ </PropKeyProvider>
35
+ </Stack>
36
+ </PropProvider>
52
37
  );
53
38
  } );
@@ -14,7 +14,7 @@ export const ImageMediaControl = createControl( () => {
14
14
  const { id, url } = value ?? {};
15
15
 
16
16
  const { data: attachment, isFetching } = useWpMediaAttachment( id?.value || null );
17
- const src = attachment?.url ?? url;
17
+ const src = attachment?.url ?? url?.value ?? null;
18
18
 
19
19
  const { open } = useWpMediaFrame( {
20
20
  types: [ 'image' ],
@@ -1,89 +1,118 @@
1
1
  import * as React from 'react';
2
- import { type LinkPropValue, type UrlPropValue } from '@elementor/editor-props';
2
+ import { booleanPropTypeUtil, linkPropTypeUtil, type LinkPropValue, stringPropTypeUtil } from '@elementor/editor-props';
3
3
  import { MinusIcon, PlusIcon } from '@elementor/icons';
4
+ import { useSessionStorage } from '@elementor/session';
4
5
  import { Collapse, Divider, Grid, IconButton, Stack, Switch } from '@elementor/ui';
5
6
  import { __ } from '@wordpress/i18n';
6
7
 
7
- import { BoundPropProvider, useBoundProp } from '../bound-prop-context';
8
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
8
9
  import { ControlLabel } from '../components/control-label';
9
10
  import { createControl } from '../create-control';
10
- import { UrlControl } from './url-control';
11
+ import { AutocompleteControl, type GroupedOption, type Option } from './autocomplete-control';
11
12
 
12
- const SIZE = 'tiny';
13
+ type Props = {
14
+ options?: Record< string, Option > | Record< string, GroupedOption >;
15
+ allowCustomValues?: boolean;
16
+ placeholder?: string;
17
+ };
13
18
 
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
- },
19
+ type LinkSessionValue = {
20
+ value?: LinkPropValue[ 'value' ];
21
+ meta?: {
22
+ isEnabled?: boolean;
23
+ };
24
24
  };
25
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
- } );
26
+ const SIZE = 'tiny';
27
+
28
+ export const LinkControl = createControl( ( props?: Props ) => {
29
+ const { value, path, setValue, ...propContext } = useBoundProp( linkPropTypeUtil );
30
+
31
+ const [ linkSessionValue, setLinkSessionValue ] = useSessionStorage< LinkSessionValue >( path.join( '/' ) );
32
+
33
+ const { allowCustomValues = false, options = {}, placeholder } = props || {};
34
+
35
+ const onEnabledChange = () => {
36
+ const { meta } = linkSessionValue ?? {};
37
+ const { isEnabled } = meta ?? {};
38
+
39
+ if ( isEnabled && value ) {
40
+ setValue( null );
41
+ } else if ( linkSessionValue?.value ) {
42
+ setValue( linkSessionValue?.value ?? null );
43
+ }
44
+
45
+ setLinkSessionValue( { value, meta: { isEnabled: ! isEnabled } } );
41
46
  };
42
47
 
43
48
  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 ) }
49
+ <PropProvider { ...propContext } value={ value } setValue={ setValue }>
50
+ <Stack gap={ 1.5 }>
51
+ <Divider />
52
+ <Stack
53
+ direction="row"
54
+ sx={ {
55
+ justifyContent: 'space-between',
56
+ alignItems: 'center',
57
+ } }
58
+ >
59
+ <ControlLabel>{ __( 'Link', 'elementor' ) }</ControlLabel>
60
+ <ToggleIconControl
61
+ enabled={ linkSessionValue?.meta?.isEnabled ?? false }
62
+ onIconClick={ onEnabledChange }
63
+ label={ __( 'Toggle Link', 'elementor' ) }
71
64
  />
72
65
  </Stack>
73
- </Collapse>
74
- </Stack>
66
+ <Collapse in={ linkSessionValue?.meta?.isEnabled } timeout="auto" unmountOnExit>
67
+ <Stack gap={ 1.5 }>
68
+ <PropKeyProvider bind={ 'href' }>
69
+ <AutocompleteControl
70
+ allowCustomValues={ Object.keys( options ).length ? allowCustomValues : true }
71
+ options={ options }
72
+ propType={ stringPropTypeUtil }
73
+ placeholder={ placeholder }
74
+ />
75
+ </PropKeyProvider>
76
+
77
+ <PropKeyProvider bind={ 'isTargetBlank' }>
78
+ <SwitchControl />
79
+ </PropKeyProvider>
80
+ </Stack>
81
+ </Collapse>
82
+ </Stack>
83
+ </PropProvider>
75
84
  );
76
85
  } );
77
86
 
87
+ type ToggleIconControlProps = {
88
+ enabled: boolean;
89
+ onIconClick: () => void;
90
+ label?: string;
91
+ };
92
+
93
+ const ToggleIconControl = ( { enabled, onIconClick, label }: ToggleIconControlProps ) => {
94
+ return (
95
+ <IconButton size={ SIZE } onClick={ onIconClick } aria-label={ label }>
96
+ { enabled ? <MinusIcon fontSize={ SIZE } /> : <PlusIcon fontSize={ SIZE } /> }
97
+ </IconButton>
98
+ );
99
+ };
100
+
78
101
  // @TODO Should be refactored in ED-16323
79
- const SwitchControl = ( { value, onSwitch }: { value: boolean; onSwitch: () => void } ) => {
102
+ const SwitchControl = () => {
103
+ const { value = false, setValue } = useBoundProp( booleanPropTypeUtil );
104
+
105
+ const onChange = () => {
106
+ setValue( ! value );
107
+ };
108
+
80
109
  return (
81
110
  <Grid container alignItems="center" flexWrap="nowrap" justifyContent="space-between">
82
111
  <Grid item>
83
112
  <ControlLabel>{ __( 'Open in new tab', 'elementor' ) }</ControlLabel>
84
113
  </Grid>
85
114
  <Grid item>
86
- <Switch checked={ value } onChange={ onSwitch } />
115
+ <Switch checked={ value } onChange={ onChange } />
87
116
  </Grid>
88
117
  </Grid>
89
118
  );