@elementor/editor-controls 1.2.0 → 1.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.
- package/CHANGELOG.md +66 -0
- package/dist/index.d.mts +20 -8
- package/dist/index.d.ts +20 -8
- package/dist/index.js +1092 -714
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +937 -549
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/bound-prop-context/prop-context.tsx +3 -3
- package/src/bound-prop-context/prop-key-context.tsx +1 -0
- package/src/bound-prop-context/use-bound-prop.ts +5 -1
- package/src/components/font-family-selector.tsx +54 -56
- package/src/components/repeater.tsx +22 -11
- package/src/components/size-control/size-input.tsx +4 -4
- package/src/components/text-field-popover.tsx +19 -18
- package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +3 -15
- package/src/controls/box-shadow-repeater-control.tsx +1 -1
- package/src/controls/color-control.tsx +12 -1
- package/src/controls/equal-unequal-sizes-control.tsx +1 -1
- package/src/controls/filter-control/drop-shadow-item-content.tsx +69 -0
- package/src/controls/filter-control/drop-shadow-item-label.tsx +20 -0
- package/src/controls/filter-repeater-control.tsx +108 -21
- package/src/controls/font-family-control/font-family-control.tsx +14 -2
- package/src/controls/image-control.tsx +45 -16
- package/src/controls/key-value-control.tsx +57 -46
- package/src/controls/link-control.tsx +25 -20
- package/src/controls/linked-dimensions-control.tsx +1 -1
- package/src/controls/repeatable-control.tsx +100 -21
- package/src/controls/select-control.tsx +22 -2
- package/src/controls/size-control.tsx +25 -12
- package/src/controls/switch-control.tsx +9 -1
- package/src/controls/text-control.tsx +33 -18
- package/src/controls/transform-control/functions/axis-row.tsx +32 -0
- package/src/controls/transform-control/functions/move.tsx +44 -0
- package/src/controls/transform-control/transform-content.tsx +36 -0
- package/src/controls/transform-control/transform-icon.tsx +12 -0
- package/src/controls/transform-control/transform-label.tsx +27 -0
- package/src/controls/transform-control/transform-repeater-control.tsx +42 -0
- package/src/hooks/use-repeatable-control-context.ts +6 -1
- package/src/index.ts +1 -0
- package/src/utils/size-control.ts +4 -2
|
@@ -3,12 +3,20 @@ import { useRef } from 'react';
|
|
|
3
3
|
import {
|
|
4
4
|
blurFilterPropTypeUtil,
|
|
5
5
|
brightnessFilterPropTypeUtil,
|
|
6
|
+
contrastFilterPropTypeUtil,
|
|
7
|
+
dropShadowFilterPropTypeUtil,
|
|
6
8
|
type FilterItemPropValue,
|
|
7
9
|
filterPropTypeUtil,
|
|
10
|
+
grayscaleFilterPropTypeUtil,
|
|
11
|
+
hueRotateFilterPropTypeUtil,
|
|
12
|
+
invertFilterPropTypeUtil,
|
|
8
13
|
type PropKey,
|
|
9
14
|
type PropTypeUtil,
|
|
15
|
+
saturateFilterPropTypeUtil,
|
|
16
|
+
sepiaFilterPropTypeUtil,
|
|
10
17
|
type SizePropValue,
|
|
11
18
|
} from '@elementor/editor-props';
|
|
19
|
+
import { backdropFilterPropTypeUtil } from '@elementor/editor-props';
|
|
12
20
|
import { MenuListItem } from '@elementor/editor-ui';
|
|
13
21
|
import { Box, Grid, Select, type SelectChangeEvent } from '@elementor/ui';
|
|
14
22
|
import { __ } from '@wordpress/i18n';
|
|
@@ -17,9 +25,11 @@ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-conte
|
|
|
17
25
|
import { ControlLabel } from '../components/control-label';
|
|
18
26
|
import { PopoverContent } from '../components/popover-content';
|
|
19
27
|
import { PopoverGridContainer } from '../components/popover-grid-container';
|
|
20
|
-
import { Repeater } from '../components/repeater';
|
|
28
|
+
import { type CollectionPropUtil, Repeater } from '../components/repeater';
|
|
21
29
|
import { createControl } from '../create-control';
|
|
22
|
-
import { defaultUnits } from '../utils/size-control';
|
|
30
|
+
import { defaultUnits, type Unit } from '../utils/size-control';
|
|
31
|
+
import { DropShadowItemContent } from './filter-control/drop-shadow-item-content';
|
|
32
|
+
import { DropShadowItemLabel } from './filter-control/drop-shadow-item-label';
|
|
23
33
|
import { SizeControl } from './size-control';
|
|
24
34
|
|
|
25
35
|
type FilterType = FilterItemPropValue[ '$$type' ];
|
|
@@ -43,6 +53,21 @@ const filterConfig: Record< FilterType, FilterItemConfig > = {
|
|
|
43
53
|
propType: blurFilterPropTypeUtil,
|
|
44
54
|
units: defaultUnits.filter( ( unit ) => unit !== '%' ),
|
|
45
55
|
},
|
|
56
|
+
'drop-shadow': {
|
|
57
|
+
defaultValue: {
|
|
58
|
+
$$type: 'drop-shadow',
|
|
59
|
+
value: {
|
|
60
|
+
xAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
|
|
61
|
+
yAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
|
|
62
|
+
blur: { $$type: 'size', value: { size: 10, unit: 'px' } },
|
|
63
|
+
color: { $$type: 'color', value: 'rgba(0, 0, 0, 1)' },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
name: __( 'Drop shadow', 'elementor' ),
|
|
67
|
+
valueName: __( 'Drop-shadow', 'elementor' ),
|
|
68
|
+
propType: dropShadowFilterPropTypeUtil,
|
|
69
|
+
units: defaultUnits.filter( ( unit ) => unit !== '%' ),
|
|
70
|
+
},
|
|
46
71
|
brightness: {
|
|
47
72
|
defaultValue: { $$type: 'amount', amount: { $$type: 'size', value: { size: 100, unit: '%' } } },
|
|
48
73
|
name: __( 'Brightness', 'elementor' ),
|
|
@@ -50,18 +75,62 @@ const filterConfig: Record< FilterType, FilterItemConfig > = {
|
|
|
50
75
|
propType: brightnessFilterPropTypeUtil,
|
|
51
76
|
units: [ '%' ],
|
|
52
77
|
},
|
|
78
|
+
contrast: {
|
|
79
|
+
defaultValue: { $$type: 'contrast', contrast: { $$type: 'size', value: { size: 100, unit: '%' } } },
|
|
80
|
+
name: __( 'Contrast', 'elementor' ),
|
|
81
|
+
valueName: __( 'Amount', 'elementor' ),
|
|
82
|
+
propType: contrastFilterPropTypeUtil,
|
|
83
|
+
units: [ '%' ],
|
|
84
|
+
},
|
|
85
|
+
'hue-rotate': {
|
|
86
|
+
defaultValue: { $$type: 'hue-rotate', 'hue-rotate': { $$type: 'size', value: { size: 0, unit: 'deg' } } },
|
|
87
|
+
name: __( 'Hue Rotate', 'elementor' ),
|
|
88
|
+
valueName: __( 'Angle', 'elementor' ),
|
|
89
|
+
propType: hueRotateFilterPropTypeUtil,
|
|
90
|
+
units: [ 'deg', 'rad', 'grad', 'turn' ],
|
|
91
|
+
},
|
|
92
|
+
saturate: {
|
|
93
|
+
defaultValue: { $$type: 'saturate', saturate: { $$type: 'size', value: { size: 100, unit: '%' } } },
|
|
94
|
+
name: __( 'Saturate', 'elementor' ),
|
|
95
|
+
valueName: __( 'Amount', 'elementor' ),
|
|
96
|
+
propType: saturateFilterPropTypeUtil,
|
|
97
|
+
units: [ '%' ],
|
|
98
|
+
},
|
|
99
|
+
grayscale: {
|
|
100
|
+
defaultValue: { $$type: 'grayscale', grayscale: { $$type: 'size', value: { size: 0, unit: '%' } } },
|
|
101
|
+
name: __( 'Grayscale', 'elementor' ),
|
|
102
|
+
valueName: __( 'Amount', 'elementor' ),
|
|
103
|
+
propType: grayscaleFilterPropTypeUtil,
|
|
104
|
+
units: [ '%' ],
|
|
105
|
+
},
|
|
106
|
+
invert: {
|
|
107
|
+
defaultValue: { $$type: 'invert', invert: { $$type: 'size', value: { size: 0, unit: '%' } } },
|
|
108
|
+
name: __( 'Invert', 'elementor' ),
|
|
109
|
+
valueName: __( 'Amount', 'elementor' ),
|
|
110
|
+
propType: invertFilterPropTypeUtil,
|
|
111
|
+
units: [ '%' ],
|
|
112
|
+
},
|
|
113
|
+
sepia: {
|
|
114
|
+
defaultValue: { $$type: 'sepia', sepia: { $$type: 'size', value: { size: 0, unit: '%' } } },
|
|
115
|
+
name: __( 'Sepia', 'elementor' ),
|
|
116
|
+
valueName: __( 'Amount', 'elementor' ),
|
|
117
|
+
propType: sepiaFilterPropTypeUtil,
|
|
118
|
+
units: [ '%' ],
|
|
119
|
+
},
|
|
53
120
|
};
|
|
54
121
|
|
|
55
122
|
const filterKeys = Object.keys( filterConfig ) as FilterType[];
|
|
56
123
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return filter[ filter.$$type ].$$type === 'size';
|
|
61
|
-
} ) as FilterType[];
|
|
124
|
+
const isSingleSize = ( key: FilterType ): boolean => {
|
|
125
|
+
return ! [ 'drop-shadow' ].includes( key );
|
|
126
|
+
};
|
|
62
127
|
|
|
63
|
-
export const FilterRepeaterControl = createControl( () => {
|
|
64
|
-
const
|
|
128
|
+
export const FilterRepeaterControl = createControl( ( { filterPropName = 'filter' }: { filterPropName?: string } ) => {
|
|
129
|
+
const [ propUtil, label ] =
|
|
130
|
+
filterPropName === 'backdrop-filter'
|
|
131
|
+
? [ backdropFilterPropTypeUtil, __( 'Backdrop Filters', 'elementor' ) ]
|
|
132
|
+
: [ filterPropTypeUtil, __( 'Filters', 'elementor' ) ];
|
|
133
|
+
const { propType, value: filterValues, setValue, disabled } = useBoundProp( propUtil );
|
|
65
134
|
|
|
66
135
|
return (
|
|
67
136
|
<PropProvider propType={ propType } value={ filterValues } setValue={ setValue }>
|
|
@@ -70,7 +139,8 @@ export const FilterRepeaterControl = createControl( () => {
|
|
|
70
139
|
disabled={ disabled }
|
|
71
140
|
values={ filterValues ?? [] }
|
|
72
141
|
setValues={ setValue }
|
|
73
|
-
label={
|
|
142
|
+
label={ label }
|
|
143
|
+
collectionPropUtil={ propUtil }
|
|
74
144
|
itemSettings={ {
|
|
75
145
|
Icon: ItemIcon,
|
|
76
146
|
Label: ItemLabel,
|
|
@@ -87,10 +157,12 @@ export const FilterRepeaterControl = createControl( () => {
|
|
|
87
157
|
|
|
88
158
|
const ItemIcon = () => <></>;
|
|
89
159
|
|
|
90
|
-
const ItemLabel = (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
160
|
+
const ItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
|
|
161
|
+
return isSingleSize( value.$$type ) ? (
|
|
162
|
+
<SingleSizeItemLabel value={ value } />
|
|
163
|
+
) : (
|
|
164
|
+
<DropShadowItemLabel value={ value } />
|
|
165
|
+
);
|
|
94
166
|
};
|
|
95
167
|
|
|
96
168
|
const SingleSizeItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
|
|
@@ -113,8 +185,16 @@ const SingleSizeItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
|
|
|
113
185
|
);
|
|
114
186
|
};
|
|
115
187
|
|
|
116
|
-
const ItemContent = ( {
|
|
117
|
-
|
|
188
|
+
const ItemContent = ( {
|
|
189
|
+
bind,
|
|
190
|
+
collectionPropUtil,
|
|
191
|
+
anchorEl,
|
|
192
|
+
}: {
|
|
193
|
+
bind: PropKey;
|
|
194
|
+
collectionPropUtil?: CollectionPropUtil< FilterItemPropValue >;
|
|
195
|
+
anchorEl?: HTMLElement | null;
|
|
196
|
+
} ) => {
|
|
197
|
+
const { value: filterValues, setValue } = useBoundProp( collectionPropUtil ?? filterPropTypeUtil );
|
|
118
198
|
const itemIndex = parseInt( bind, 10 );
|
|
119
199
|
const item = filterValues?.[ itemIndex ];
|
|
120
200
|
|
|
@@ -124,7 +204,7 @@ const ItemContent = ( { bind }: { bind: PropKey } ) => {
|
|
|
124
204
|
|
|
125
205
|
newFilterValues[ itemIndex ] = {
|
|
126
206
|
$$type: filterType,
|
|
127
|
-
value: filterConfig[ filterType ].defaultValue,
|
|
207
|
+
value: { ...filterConfig[ filterType ].defaultValue },
|
|
128
208
|
} as FilterItemPropValue;
|
|
129
209
|
|
|
130
210
|
setValue( newFilterValues );
|
|
@@ -153,14 +233,20 @@ const ItemContent = ( { bind }: { bind: PropKey } ) => {
|
|
|
153
233
|
</Select>
|
|
154
234
|
</Grid>
|
|
155
235
|
</PopoverGridContainer>
|
|
156
|
-
<Content filterType={ item?.$$type } />
|
|
236
|
+
<Content filterType={ item?.$$type } anchorEl={ anchorEl } />
|
|
157
237
|
</PopoverContent>
|
|
158
238
|
</PropKeyProvider>
|
|
159
239
|
);
|
|
160
240
|
};
|
|
161
241
|
|
|
162
|
-
const Content = ( { filterType }: { filterType: FilterType } ) => {
|
|
163
|
-
|
|
242
|
+
const Content = ( { filterType, anchorEl }: { filterType: FilterType; anchorEl?: HTMLElement | null } ) => {
|
|
243
|
+
const { propType, units = [] } = filterConfig[ filterType ];
|
|
244
|
+
|
|
245
|
+
return isSingleSize( filterType ) ? (
|
|
246
|
+
<SingleSizeItemContent filterType={ filterType } />
|
|
247
|
+
) : (
|
|
248
|
+
<DropShadowItemContent propType={ propType } units={ units as Unit[] } anchorEl={ anchorEl } />
|
|
249
|
+
);
|
|
164
250
|
};
|
|
165
251
|
|
|
166
252
|
const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) => {
|
|
@@ -168,6 +254,7 @@ const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) =>
|
|
|
168
254
|
const { $$type } = defaultValue;
|
|
169
255
|
const context = useBoundProp( propType );
|
|
170
256
|
const rowRef = useRef< HTMLDivElement >( null );
|
|
257
|
+
const defaultUnit = defaultValue[ $$type ].value.unit;
|
|
171
258
|
|
|
172
259
|
return (
|
|
173
260
|
<PropProvider { ...context }>
|
|
@@ -177,7 +264,7 @@ const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) =>
|
|
|
177
264
|
<ControlLabel>{ valueName }</ControlLabel>
|
|
178
265
|
</Grid>
|
|
179
266
|
<Grid item xs={ 6 }>
|
|
180
|
-
<SizeControl anchorRef={ rowRef } units={ units } />
|
|
267
|
+
<SizeControl anchorRef={ rowRef } units={ units } defaultUnit={ defaultUnit } />
|
|
181
268
|
</Grid>
|
|
182
269
|
</PopoverGridContainer>
|
|
183
270
|
</PropKeyProvider>
|
|
@@ -21,20 +21,32 @@ type FontFamilyControlProps = {
|
|
|
21
21
|
const SIZE = 'tiny';
|
|
22
22
|
|
|
23
23
|
export const FontFamilyControl = createControl( ( { fontFamilies, sectionWidth }: FontFamilyControlProps ) => {
|
|
24
|
-
const { value: fontFamily, setValue: setFontFamily, disabled } = useBoundProp( stringPropTypeUtil );
|
|
24
|
+
const { value: fontFamily, setValue: setFontFamily, disabled, placeholder } = useBoundProp( stringPropTypeUtil );
|
|
25
25
|
|
|
26
26
|
const popoverState = usePopupState( { variant: 'popover' } );
|
|
27
27
|
|
|
28
|
+
const isShowingPlaceholder = ! fontFamily && placeholder;
|
|
29
|
+
|
|
28
30
|
return (
|
|
29
31
|
<>
|
|
30
32
|
<ControlActions>
|
|
31
33
|
<UnstableTag
|
|
32
34
|
variant="outlined"
|
|
33
|
-
label={ fontFamily }
|
|
35
|
+
label={ fontFamily || placeholder }
|
|
34
36
|
endIcon={ <ChevronDownIcon fontSize={ SIZE } /> }
|
|
35
37
|
{ ...bindTrigger( popoverState ) }
|
|
36
38
|
fullWidth
|
|
37
39
|
disabled={ disabled }
|
|
40
|
+
sx={
|
|
41
|
+
isShowingPlaceholder
|
|
42
|
+
? {
|
|
43
|
+
'& .MuiTag-label': {
|
|
44
|
+
color: ( theme ) => theme.palette.text.tertiary,
|
|
45
|
+
},
|
|
46
|
+
textTransform: 'capitalize',
|
|
47
|
+
}
|
|
48
|
+
: undefined
|
|
49
|
+
}
|
|
38
50
|
/>
|
|
39
51
|
</ControlActions>
|
|
40
52
|
<Popover
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { imagePropTypeUtil } from '@elementor/editor-props';
|
|
3
|
-
import { Stack } from '@elementor/ui';
|
|
3
|
+
import { Grid, Stack } from '@elementor/ui';
|
|
4
4
|
import { type MediaType } from '@elementor/wp-media';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
5
6
|
|
|
6
7
|
import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
|
|
8
|
+
import { ControlFormLabel } from '../components/control-form-label';
|
|
7
9
|
import { createControl } from '../create-control';
|
|
8
10
|
import { useUnfilteredFilesUpload } from '../hooks/use-unfiltered-files-upload';
|
|
9
11
|
import { ImageMediaControl } from './image-media-control';
|
|
@@ -17,23 +19,50 @@ type ImageControlProps = {
|
|
|
17
19
|
export const ImageControl = createControl( ( { sizes, showMode = 'all' }: ImageControlProps ) => {
|
|
18
20
|
const propContext = useBoundProp( imagePropTypeUtil );
|
|
19
21
|
|
|
22
|
+
let componentToRender;
|
|
23
|
+
switch ( showMode ) {
|
|
24
|
+
case 'media':
|
|
25
|
+
componentToRender = <ImageSrcControl />;
|
|
26
|
+
break;
|
|
27
|
+
case 'sizes':
|
|
28
|
+
componentToRender = <ImageSizeControl sizes={ sizes } />;
|
|
29
|
+
break;
|
|
30
|
+
case 'all':
|
|
31
|
+
default:
|
|
32
|
+
componentToRender = (
|
|
33
|
+
<Stack gap={ 1.5 }>
|
|
34
|
+
<ControlFormLabel>{ __( 'Image', 'elementor' ) }</ControlFormLabel>
|
|
35
|
+
<ImageSrcControl />
|
|
36
|
+
<Grid container gap={ 1.5 } alignItems="center" flexWrap="nowrap">
|
|
37
|
+
<Grid item xs={ 6 }>
|
|
38
|
+
<ControlFormLabel>{ __( 'Resolution', 'elementor' ) }</ControlFormLabel>
|
|
39
|
+
</Grid>
|
|
40
|
+
<Grid item xs={ 6 } sx={ { overflow: 'hidden' } }>
|
|
41
|
+
<ImageSizeControl sizes={ sizes } />
|
|
42
|
+
</Grid>
|
|
43
|
+
</Grid>
|
|
44
|
+
</Stack>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return <PropProvider { ...propContext }>{ componentToRender }</PropProvider>;
|
|
49
|
+
} );
|
|
50
|
+
|
|
51
|
+
const ImageSrcControl = () => {
|
|
20
52
|
const { data: allowSvgUpload } = useUnfilteredFilesUpload();
|
|
21
53
|
const mediaTypes: MediaType[] = allowSvgUpload ? [ 'image', 'svg' ] : [ 'image' ];
|
|
22
54
|
|
|
23
55
|
return (
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
<PropKeyProvider bind={ 'src' }>
|
|
28
|
-
<ImageMediaControl mediaTypes={ mediaTypes } />
|
|
29
|
-
</PropKeyProvider>
|
|
30
|
-
) : null }
|
|
31
|
-
{ [ 'all', 'sizes' ].includes( showMode ) ? (
|
|
32
|
-
<PropKeyProvider bind={ 'size' }>
|
|
33
|
-
<SelectControl options={ sizes } />
|
|
34
|
-
</PropKeyProvider>
|
|
35
|
-
) : null }
|
|
36
|
-
</Stack>
|
|
37
|
-
</PropProvider>
|
|
56
|
+
<PropKeyProvider bind={ 'src' }>
|
|
57
|
+
<ImageMediaControl mediaTypes={ mediaTypes } />
|
|
58
|
+
</PropKeyProvider>
|
|
38
59
|
);
|
|
39
|
-
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const ImageSizeControl = ( { sizes }: { sizes: ImageControlProps[ 'sizes' ] } ) => {
|
|
63
|
+
return (
|
|
64
|
+
<PropKeyProvider bind={ 'size' }>
|
|
65
|
+
<SelectControl options={ sizes } />
|
|
66
|
+
</PropKeyProvider>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
type CreateOptions,
|
|
5
|
+
isTransformable,
|
|
6
|
+
keyValuePropTypeUtil,
|
|
7
|
+
type PropKey,
|
|
8
|
+
type Props,
|
|
9
|
+
stringPropTypeUtil,
|
|
10
|
+
} from '@elementor/editor-props';
|
|
11
|
+
import { FormHelperText, FormLabel, Grid } from '@elementor/ui';
|
|
5
12
|
import { __ } from '@wordpress/i18n';
|
|
6
13
|
|
|
7
|
-
import { useBoundProp } from '../bound-prop-context';
|
|
8
|
-
import ControlActions from '../control-actions/control-actions';
|
|
14
|
+
import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
|
|
9
15
|
import { createControl } from '../create-control';
|
|
10
|
-
|
|
11
|
-
type FieldType = 'key' | 'value';
|
|
16
|
+
import { TextControl } from './text-control';
|
|
12
17
|
|
|
13
18
|
type KeyValueControlProps = {
|
|
14
19
|
keyName?: string;
|
|
@@ -19,9 +24,9 @@ type KeyValueControlProps = {
|
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
export const KeyValueControl = createControl( ( props: KeyValueControlProps = {} ) => {
|
|
22
|
-
const { value, setValue } = useBoundProp( keyValuePropTypeUtil );
|
|
23
|
-
const [ keyError, setKeyError ] = useState< string
|
|
24
|
-
const [ valueError, setValueError ] = useState< string
|
|
27
|
+
const { value, setValue, ...propContext } = useBoundProp( keyValuePropTypeUtil );
|
|
28
|
+
const [ keyError, setKeyError ] = useState< string >( '' );
|
|
29
|
+
const [ valueError, setValueError ] = useState< string >( '' );
|
|
25
30
|
|
|
26
31
|
const [ sessionState, setSessionState ] = useState( {
|
|
27
32
|
key: value?.key?.value || '',
|
|
@@ -43,31 +48,48 @@ export const KeyValueControl = createControl( ( props: KeyValueControlProps = {}
|
|
|
43
48
|
const validate = ( newValue: string, fieldType: string ): boolean => {
|
|
44
49
|
if ( fieldType === 'key' && keyRegex ) {
|
|
45
50
|
const isValid = keyRegex.test( newValue );
|
|
46
|
-
setKeyError( isValid ?
|
|
51
|
+
setKeyError( isValid ? '' : errMsg );
|
|
52
|
+
|
|
47
53
|
return isValid;
|
|
48
54
|
} else if ( fieldType === 'value' && valueRegex ) {
|
|
49
55
|
const isValid = valueRegex.test( newValue );
|
|
50
|
-
setValueError( isValid ?
|
|
56
|
+
setValueError( isValid ? '' : errMsg );
|
|
57
|
+
|
|
51
58
|
return isValid;
|
|
52
59
|
}
|
|
60
|
+
|
|
53
61
|
return true;
|
|
54
62
|
};
|
|
55
63
|
|
|
56
|
-
const handleChange = (
|
|
57
|
-
const
|
|
64
|
+
const handleChange = ( newValue: Props, options?: CreateOptions, meta?: { bind?: PropKey } ) => {
|
|
65
|
+
const fieldType = meta?.bind;
|
|
66
|
+
|
|
67
|
+
if ( ! fieldType ) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const newChangedValue = newValue[ fieldType ];
|
|
72
|
+
|
|
73
|
+
if ( isTransformable( newChangedValue ) && newChangedValue.$$type === 'dynamic' ) {
|
|
74
|
+
setValue( {
|
|
75
|
+
...value,
|
|
76
|
+
[ fieldType ]: newChangedValue,
|
|
77
|
+
} );
|
|
78
|
+
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const extractedValue = stringPropTypeUtil.extract( newChangedValue );
|
|
58
83
|
|
|
59
84
|
setSessionState( ( prev ) => ( {
|
|
60
85
|
...prev,
|
|
61
|
-
[ fieldType ]:
|
|
86
|
+
[ fieldType ]: extractedValue,
|
|
62
87
|
} ) );
|
|
63
88
|
|
|
64
|
-
if ( validate(
|
|
89
|
+
if ( extractedValue && validate( extractedValue, fieldType ) ) {
|
|
65
90
|
setValue( {
|
|
66
91
|
...value,
|
|
67
|
-
[ fieldType ]:
|
|
68
|
-
value: newValue,
|
|
69
|
-
$$type: 'string',
|
|
70
|
-
},
|
|
92
|
+
[ fieldType ]: newChangedValue,
|
|
71
93
|
} );
|
|
72
94
|
} else {
|
|
73
95
|
setValue( {
|
|
@@ -80,40 +102,29 @@ export const KeyValueControl = createControl( ( props: KeyValueControlProps = {}
|
|
|
80
102
|
}
|
|
81
103
|
};
|
|
82
104
|
|
|
83
|
-
const isKeyInvalid = keyError !== null;
|
|
84
|
-
const isValueInvalid = valueError !== null;
|
|
85
|
-
|
|
86
105
|
return (
|
|
87
|
-
<
|
|
106
|
+
<PropProvider { ...propContext } value={ value } setValue={ handleChange }>
|
|
88
107
|
<Grid container gap={ 1.5 }>
|
|
89
108
|
<Grid item xs={ 12 }>
|
|
90
109
|
<FormLabel size="tiny">{ keyLabel }</FormLabel>
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
size="tiny"
|
|
96
|
-
fullWidth
|
|
97
|
-
value={ sessionState.key }
|
|
98
|
-
onChange={ ( e: ChangeEvent< HTMLInputElement > ) => handleChange( e, 'key' ) }
|
|
99
|
-
error={ isKeyInvalid }
|
|
100
|
-
/>
|
|
101
|
-
{ isKeyInvalid && <FormHelperText error>{ keyError }</FormHelperText> }
|
|
110
|
+
<PropKeyProvider bind={ 'key' }>
|
|
111
|
+
<TextControl inputValue={ sessionState.key } error={ !! keyError } sx={ { pt: 1 } } />
|
|
112
|
+
</PropKeyProvider>
|
|
113
|
+
{ !! keyError && <FormHelperText error>{ keyError }</FormHelperText> }
|
|
102
114
|
</Grid>
|
|
103
115
|
<Grid item xs={ 12 }>
|
|
104
116
|
<FormLabel size="tiny">{ valueLabel }</FormLabel>
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{ isValueInvalid && <FormHelperText error>{ valueError }</FormHelperText> }
|
|
117
|
+
<PropKeyProvider bind={ 'value' }>
|
|
118
|
+
<TextControl
|
|
119
|
+
inputValue={ sessionState.value }
|
|
120
|
+
error={ !! valueError }
|
|
121
|
+
inputDisabled={ !! keyError }
|
|
122
|
+
sx={ { pt: 1 } }
|
|
123
|
+
/>
|
|
124
|
+
</PropKeyProvider>
|
|
125
|
+
{ !! valueError && <FormHelperText error>{ valueError }</FormHelperText> }
|
|
115
126
|
</Grid>
|
|
116
127
|
</Grid>
|
|
117
|
-
</
|
|
128
|
+
</PropProvider>
|
|
118
129
|
);
|
|
119
130
|
} );
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
urlPropTypeUtil,
|
|
11
11
|
} from '@elementor/editor-props';
|
|
12
12
|
import { InfoTipCard } from '@elementor/editor-ui';
|
|
13
|
+
import { isExperimentActive } from '@elementor/editor-v1-adapters';
|
|
13
14
|
import { type HttpResponse, httpService } from '@elementor/http-client';
|
|
14
15
|
import { AlertTriangleIcon, MinusIcon, PlusIcon } from '@elementor/icons';
|
|
15
16
|
import { useSessionStorage } from '@elementor/session';
|
|
@@ -29,6 +30,7 @@ import { ControlFormLabel } from '../components/control-form-label';
|
|
|
29
30
|
import ControlActions from '../control-actions/control-actions';
|
|
30
31
|
import { createControl } from '../create-control';
|
|
31
32
|
import { type ControlProps } from '../utils/types';
|
|
33
|
+
import { SwitchControl } from './switch-control';
|
|
32
34
|
|
|
33
35
|
type Props = ControlProps< {
|
|
34
36
|
queryOptions: {
|
|
@@ -191,7 +193,14 @@ export const LinkControl = createControl( ( props: Props ) => {
|
|
|
191
193
|
</ControlActions>
|
|
192
194
|
</PropKeyProvider>
|
|
193
195
|
<PropKeyProvider bind={ 'isTargetBlank' }>
|
|
194
|
-
<
|
|
196
|
+
<Grid container alignItems="center" flexWrap="nowrap" justifyContent="space-between">
|
|
197
|
+
<Grid item>
|
|
198
|
+
<ControlFormLabel>{ __( 'Open in a new tab', 'elementor' ) }</ControlFormLabel>
|
|
199
|
+
</Grid>
|
|
200
|
+
<Grid item sx={ { marginInlineEnd: -1 } }>
|
|
201
|
+
<SwitchControlComponent disabled={ propContext.disabled || ! value } />
|
|
202
|
+
</Grid>
|
|
203
|
+
</Grid>
|
|
195
204
|
</PropKeyProvider>
|
|
196
205
|
</Stack>
|
|
197
206
|
</Collapse>
|
|
@@ -215,31 +224,27 @@ const ToggleIconControl = ( { disabled, active, onIconClick, label }: ToggleIcon
|
|
|
215
224
|
);
|
|
216
225
|
};
|
|
217
226
|
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
const
|
|
227
|
+
const SwitchControlComponent = ( { disabled }: { disabled: boolean } ) => {
|
|
228
|
+
const { value, setValue } = useBoundProp( booleanPropTypeUtil );
|
|
229
|
+
const isVersion331Active = isExperimentActive( 'e_v_3_31' );
|
|
230
|
+
|
|
231
|
+
if ( isVersion331Active ) {
|
|
232
|
+
return <SwitchControl />;
|
|
233
|
+
}
|
|
221
234
|
|
|
222
235
|
const onClick = () => {
|
|
223
236
|
setValue( ! value );
|
|
224
237
|
};
|
|
225
238
|
|
|
226
|
-
const inputProps = disabled
|
|
227
|
-
? {
|
|
228
|
-
style: {
|
|
229
|
-
opacity: 0,
|
|
230
|
-
},
|
|
231
|
-
}
|
|
232
|
-
: {};
|
|
233
|
-
|
|
234
239
|
return (
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
<Switch
|
|
241
|
+
checked={ value ?? false }
|
|
242
|
+
onClick={ onClick }
|
|
243
|
+
disabled={ disabled }
|
|
244
|
+
inputProps={ {
|
|
245
|
+
...( disabled ? { style: { opacity: 0 } } : {} ),
|
|
246
|
+
} }
|
|
247
|
+
/>
|
|
243
248
|
);
|
|
244
249
|
};
|
|
245
250
|
|
|
@@ -68,7 +68,7 @@ export const LinkedDimensionsControl = createControl(
|
|
|
68
68
|
propType={ propType }
|
|
69
69
|
value={ dimensionsValue }
|
|
70
70
|
setValue={ setDimensionsValue }
|
|
71
|
-
|
|
71
|
+
isDisabled={ () => disabled }
|
|
72
72
|
>
|
|
73
73
|
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
74
74
|
{ isUsingNestedProps ? (
|