@elementor/editor-controls 1.2.0 → 1.3.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 +26 -0
- package/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +645 -415
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +570 -341
- 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/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/equal-unequal-sizes-control.tsx +1 -1
- package/src/controls/font-family-control/font-family-control.tsx +14 -2
- package/src/controls/image-control.tsx +45 -16
- package/src/controls/link-control.tsx +25 -20
- package/src/controls/linked-dimensions-control.tsx +1 -1
- package/src/controls/repeatable-control.tsx +77 -17
- package/src/controls/select-control.tsx +22 -2
- package/src/controls/switch-control.tsx +9 -1
- 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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-controls",
|
|
3
3
|
"description": "This package contains the controls model and utils for the Elementor editor",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,19 +40,19 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor-current-user": "0.
|
|
44
|
-
"@elementor/editor-elements": "0.
|
|
45
|
-
"@elementor/editor-props": "0.
|
|
43
|
+
"@elementor/editor-current-user": "0.6.0",
|
|
44
|
+
"@elementor/editor-elements": "0.9.0",
|
|
45
|
+
"@elementor/editor-props": "0.16.0",
|
|
46
46
|
"@elementor/editor-responsive": "0.13.6",
|
|
47
|
-
"@elementor/editor-ui": "0.
|
|
47
|
+
"@elementor/editor-ui": "0.14.0",
|
|
48
48
|
"@elementor/editor-v1-adapters": "0.12.1",
|
|
49
49
|
"@elementor/env": "0.3.5",
|
|
50
50
|
"@elementor/http-client": "0.3.0",
|
|
51
|
-
"@elementor/icons": "1.
|
|
51
|
+
"@elementor/icons": "1.46.0",
|
|
52
52
|
"@elementor/locations": "0.8.0",
|
|
53
53
|
"@elementor/query": "0.2.4",
|
|
54
54
|
"@elementor/session": "0.1.0",
|
|
55
|
-
"@elementor/ui": "1.
|
|
55
|
+
"@elementor/ui": "1.36.0",
|
|
56
56
|
"@elementor/utils": "0.5.0",
|
|
57
57
|
"@elementor/wp-media": "0.6.1",
|
|
58
58
|
"@wordpress/i18n": "^5.13.0"
|
|
@@ -15,7 +15,7 @@ type PropContext< T extends PropValue, P extends PropType > = {
|
|
|
15
15
|
value: T | null;
|
|
16
16
|
propType: P;
|
|
17
17
|
placeholder?: T;
|
|
18
|
-
|
|
18
|
+
isDisabled?: ( propType: PropType ) => boolean | undefined;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const PropContext = createContext< PropContext< PropValue, PropType > | null >( null );
|
|
@@ -30,7 +30,7 @@ export const PropProvider = < T extends PropValue, P extends PropType >( {
|
|
|
30
30
|
setValue,
|
|
31
31
|
propType,
|
|
32
32
|
placeholder,
|
|
33
|
-
|
|
33
|
+
isDisabled,
|
|
34
34
|
}: PropProviderProps< T, P > ) => {
|
|
35
35
|
return (
|
|
36
36
|
<PropContext.Provider
|
|
@@ -39,7 +39,7 @@ export const PropProvider = < T extends PropValue, P extends PropType >( {
|
|
|
39
39
|
propType,
|
|
40
40
|
setValue: setValue as SetValue< PropValue >,
|
|
41
41
|
placeholder,
|
|
42
|
-
|
|
42
|
+
isDisabled,
|
|
43
43
|
} }
|
|
44
44
|
>
|
|
45
45
|
{ children }
|
|
@@ -19,6 +19,7 @@ type UseBoundProp< TValue extends PropValue > = {
|
|
|
19
19
|
placeholder?: TValue;
|
|
20
20
|
path: PropKey[];
|
|
21
21
|
restoreValue: () => void;
|
|
22
|
+
isDisabled?: ( propType: PropType ) => boolean | undefined;
|
|
22
23
|
disabled?: boolean;
|
|
23
24
|
};
|
|
24
25
|
|
|
@@ -38,9 +39,11 @@ export function useBoundProp< TKey extends string, TValue extends PropValue >(
|
|
|
38
39
|
|
|
39
40
|
const { isValid, validate, restoreValue } = useValidation( propKeyContext.propType );
|
|
40
41
|
|
|
42
|
+
const disabled = propKeyContext.isDisabled?.( propKeyContext.propType );
|
|
43
|
+
|
|
41
44
|
// allow using the hook without a propTypeUtil, with no modifications or validations.
|
|
42
45
|
if ( ! propTypeUtil ) {
|
|
43
|
-
return propKeyContext
|
|
46
|
+
return { ...propKeyContext, disabled } as PropKeyContextValue< PropValue, PropType >;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
function setValue( value: TValue | null, options: CreateOptions, meta: { bind?: PropKey } ) {
|
|
@@ -67,6 +70,7 @@ export function useBoundProp< TKey extends string, TValue extends PropValue >(
|
|
|
67
70
|
value: isValid ? value : null,
|
|
68
71
|
restoreValue,
|
|
69
72
|
placeholder,
|
|
73
|
+
disabled,
|
|
70
74
|
};
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { type RefObject } from 'react';
|
|
3
|
-
import { bindPopover,
|
|
3
|
+
import { bindPopover, Popover, type PopupState, TextField } from '@elementor/ui';
|
|
4
4
|
|
|
5
5
|
type Props = {
|
|
6
6
|
popupState: PopupState;
|
|
@@ -16,6 +16,15 @@ export const TextFieldPopover = ( props: Props ) => {
|
|
|
16
16
|
return (
|
|
17
17
|
<Popover
|
|
18
18
|
disablePortal
|
|
19
|
+
slotProps={ {
|
|
20
|
+
paper: {
|
|
21
|
+
sx: {
|
|
22
|
+
borderRadius: 2,
|
|
23
|
+
width: anchorRef.current?.offsetWidth + 'px',
|
|
24
|
+
p: 1.5,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
} }
|
|
19
28
|
{ ...bindPopover( popupState ) }
|
|
20
29
|
anchorOrigin={ { vertical: 'bottom', horizontal: 'center' } }
|
|
21
30
|
transformOrigin={ { vertical: 'top', horizontal: 'center' } }
|
|
@@ -24,24 +33,16 @@ export const TextFieldPopover = ( props: Props ) => {
|
|
|
24
33
|
popupState.close();
|
|
25
34
|
} }
|
|
26
35
|
>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
<TextField
|
|
37
|
+
value={ value }
|
|
38
|
+
onChange={ onChange }
|
|
39
|
+
size="tiny"
|
|
40
|
+
type="text"
|
|
41
|
+
fullWidth
|
|
42
|
+
inputProps={ {
|
|
43
|
+
autoFocus: true,
|
|
32
44
|
} }
|
|
33
|
-
|
|
34
|
-
<TextField
|
|
35
|
-
value={ value }
|
|
36
|
-
onChange={ onChange }
|
|
37
|
-
size="tiny"
|
|
38
|
-
type="text"
|
|
39
|
-
fullWidth
|
|
40
|
-
inputProps={ {
|
|
41
|
-
autoFocus: true,
|
|
42
|
-
} }
|
|
43
|
-
/>
|
|
44
|
-
</Paper>
|
|
45
|
+
/>
|
|
45
46
|
</Popover>
|
|
46
47
|
);
|
|
47
48
|
};
|
package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx
CHANGED
|
@@ -7,12 +7,11 @@ import {
|
|
|
7
7
|
colorPropTypeUtil,
|
|
8
8
|
type PropKey,
|
|
9
9
|
} from '@elementor/editor-props';
|
|
10
|
-
import { Box, CardMedia,
|
|
10
|
+
import { Box, CardMedia, styled, Tab, TabPanel, Tabs, type Theme, UnstableColorIndicator } from '@elementor/ui';
|
|
11
11
|
import { useWpMediaAttachment } from '@elementor/wp-media';
|
|
12
12
|
import { __ } from '@wordpress/i18n';
|
|
13
13
|
|
|
14
14
|
import { PropKeyProvider, PropProvider, useBoundProp } from '../../../bound-prop-context';
|
|
15
|
-
import { ControlFormLabel } from '../../../components/control-form-label';
|
|
16
15
|
import { PopoverContent } from '../../../components/popover-content';
|
|
17
16
|
import { Repeater } from '../../../components/repeater';
|
|
18
17
|
import { createControl } from '../../../create-control';
|
|
@@ -75,7 +74,7 @@ export const BackgroundOverlayRepeaterControl = createControl( () => {
|
|
|
75
74
|
const { propType, value: overlayValues, setValue, disabled } = useBoundProp( backgroundOverlayPropTypeUtil );
|
|
76
75
|
|
|
77
76
|
return (
|
|
78
|
-
<PropProvider propType={ propType } value={ overlayValues } setValue={ setValue }
|
|
77
|
+
<PropProvider propType={ propType } value={ overlayValues } setValue={ setValue } isDisabled={ () => disabled }>
|
|
79
78
|
<Repeater
|
|
80
79
|
openOnAdd
|
|
81
80
|
disabled={ disabled }
|
|
@@ -236,18 +235,7 @@ const ImageOverlayContent = () => {
|
|
|
236
235
|
return (
|
|
237
236
|
<PropProvider { ...propContext }>
|
|
238
237
|
<PropKeyProvider bind={ 'image' }>
|
|
239
|
-
<
|
|
240
|
-
<Grid item xs={ 12 }>
|
|
241
|
-
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
242
|
-
<Grid item xs={ 6 }>
|
|
243
|
-
<ControlFormLabel>{ __( 'Resolution', 'elementor' ) }</ControlFormLabel>
|
|
244
|
-
</Grid>
|
|
245
|
-
<Grid item xs={ 6 } sx={ { overflow: 'hidden' } }>
|
|
246
|
-
<ImageControl sizes={ backgroundResolutionOptions } />
|
|
247
|
-
</Grid>
|
|
248
|
-
</Grid>
|
|
249
|
-
</Grid>
|
|
250
|
-
</Grid>
|
|
238
|
+
<ImageControl sizes={ backgroundResolutionOptions } />
|
|
251
239
|
</PropKeyProvider>
|
|
252
240
|
<PropKeyProvider bind={ 'position' }>
|
|
253
241
|
<BackgroundImageOverlayPosition />
|
|
@@ -17,7 +17,7 @@ export const BoxShadowRepeaterControl = createControl( () => {
|
|
|
17
17
|
const { propType, value, setValue, disabled } = useBoundProp( boxShadowPropTypeUtil );
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
|
-
<PropProvider propType={ propType } value={ value } setValue={ setValue }
|
|
20
|
+
<PropProvider propType={ propType } value={ value } setValue={ setValue } isDisabled={ () => disabled }>
|
|
21
21
|
<Repeater
|
|
22
22
|
openOnAdd
|
|
23
23
|
disabled={ disabled }
|
|
@@ -154,7 +154,7 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
|
|
|
154
154
|
propType={ multiSizePropType }
|
|
155
155
|
value={ getMultiSizeValues() }
|
|
156
156
|
setValue={ setNestedProp }
|
|
157
|
-
|
|
157
|
+
isDisabled={ () => multiSizeDisabled }
|
|
158
158
|
>
|
|
159
159
|
<PopoverContent p={ 1.5 }>
|
|
160
160
|
<PopoverGridContainer ref={ rowRefs[ 1 ] }>
|
|
@@ -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
|
+
};
|
|
@@ -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 ? (
|
|
@@ -47,25 +47,21 @@ export const RepeatableControl = createControl(
|
|
|
47
47
|
[ childPropTypeUtil.key, childPropTypeUtil.schema ]
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const contextValue = useMemo(
|
|
51
|
+
() => ({
|
|
52
|
+
...childControlConfig,
|
|
53
|
+
placeholder: placeholder || '',
|
|
54
|
+
patternLabel: patternLabel || '',
|
|
55
|
+
}),
|
|
56
|
+
[ childControlConfig, placeholder, patternLabel ]
|
|
57
|
+
);
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
return <span>{ finalLabel }</span>;
|
|
57
|
-
}
|
|
59
|
+
const { propType, value, setValue } = useBoundProp( childArrayPropTypeUtil );
|
|
58
60
|
|
|
59
|
-
return (
|
|
60
|
-
<Box component="span" color="text.tertiary">
|
|
61
|
-
{ placeholder }
|
|
62
|
-
</Box>
|
|
63
|
-
);
|
|
64
|
-
};
|
|
65
61
|
|
|
66
62
|
return (
|
|
67
63
|
<PropProvider propType={ propType } value={ value } setValue={ setValue }>
|
|
68
|
-
<RepeatableControlContext.Provider value={
|
|
64
|
+
<RepeatableControlContext.Provider value={ contextValue }>
|
|
69
65
|
<Repeater
|
|
70
66
|
openOnAdd
|
|
71
67
|
values={ value ?? [] }
|
|
@@ -111,9 +107,73 @@ const Content = () => {
|
|
|
111
107
|
};
|
|
112
108
|
|
|
113
109
|
const interpolate = ( template: string, data: object ) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return null;
|
|
110
|
+
if ( ! data ) {
|
|
111
|
+
return template;
|
|
117
112
|
}
|
|
113
|
+
|
|
118
114
|
return new Function( ...Object.keys( data ), `return \`${ template }\`;` )( ...Object.values( data ) );
|
|
119
115
|
};
|
|
116
|
+
|
|
117
|
+
const getNestedValue = ( obj: any, path: string ) => {
|
|
118
|
+
return path.split( '.' ).reduce( ( current, key ) => current?.[key], obj);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const isEmptyValue = (val: unknown) => {
|
|
122
|
+
if ( typeof val === 'string' ) {
|
|
123
|
+
return val.trim() === '';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if ( Number.isNaN( val ) ) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if ( Array.isArray( val ) ) {
|
|
131
|
+
return val.length === 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if ( typeof val === 'object' && val !== null && Object.keys( val ).length === 0 ) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return false;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const shouldShowPlaceholder = ( pattern: string, data: unknown ): boolean => {
|
|
142
|
+
const propertyPaths = getAllProperties( pattern );
|
|
143
|
+
|
|
144
|
+
const values = propertyPaths.map( path => getNestedValue( data, path ) );
|
|
145
|
+
|
|
146
|
+
if ( values.length === 0 ) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if ( values.some( ( value ) => value === null || value === undefined ) ) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if ( values.every( isEmptyValue ) ) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return false;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const ItemLabel = ( { value }: { value: any } ) => {
|
|
162
|
+
const { placeholder, patternLabel } = useRepeatableControlContext();
|
|
163
|
+
|
|
164
|
+
const label = shouldShowPlaceholder( patternLabel, value ) ? placeholder : interpolate( patternLabel, value );
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<Box component="span" color="text.tertiary">
|
|
168
|
+
{ label }
|
|
169
|
+
</Box>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const getAllProperties = ( pattern: string ) => {
|
|
174
|
+
const properties = pattern.match(/\$\{([^}]+)\}/g)?.map(match =>
|
|
175
|
+
match.slice(2, -1)
|
|
176
|
+
) || [];
|
|
177
|
+
|
|
178
|
+
return properties;
|
|
179
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { stringPropTypeUtil, type StringPropValue } from '@elementor/editor-props';
|
|
3
3
|
import { MenuListItem } from '@elementor/editor-ui';
|
|
4
|
-
import { Select, type SelectChangeEvent } from '@elementor/ui';
|
|
4
|
+
import { Select, type SelectChangeEvent, Typography } from '@elementor/ui';
|
|
5
5
|
|
|
6
6
|
import { useBoundProp } from '../bound-prop-context';
|
|
7
7
|
import ControlActions from '../control-actions/control-actions';
|
|
@@ -13,7 +13,7 @@ type Props = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const SelectControl = createControl( ( { options, onChange }: Props ) => {
|
|
16
|
-
const { value, setValue, disabled } = useBoundProp( stringPropTypeUtil );
|
|
16
|
+
const { value, setValue, disabled, placeholder } = useBoundProp( stringPropTypeUtil );
|
|
17
17
|
|
|
18
18
|
const handleChange = ( event: SelectChangeEvent< StringPropValue[ 'value' ] > ) => {
|
|
19
19
|
const newValue = event.target.value || null;
|
|
@@ -28,6 +28,26 @@ export const SelectControl = createControl( ( { options, onChange }: Props ) =>
|
|
|
28
28
|
sx={ { overflow: 'hidden' } }
|
|
29
29
|
displayEmpty
|
|
30
30
|
size="tiny"
|
|
31
|
+
renderValue={ ( selectedValue: string | null ) => {
|
|
32
|
+
const findOptionByValue = ( searchValue: string | null ) =>
|
|
33
|
+
options.find( ( opt ) => opt.value === searchValue );
|
|
34
|
+
|
|
35
|
+
if ( ! selectedValue || selectedValue === '' ) {
|
|
36
|
+
if ( placeholder ) {
|
|
37
|
+
const placeholderOption = findOptionByValue( placeholder );
|
|
38
|
+
const displayText = placeholderOption?.label || placeholder;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Typography component="span" variant="caption" color="text.tertiary">
|
|
42
|
+
{ displayText }
|
|
43
|
+
</Typography>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
const option = findOptionByValue( selectedValue );
|
|
49
|
+
return option?.label || selectedValue;
|
|
50
|
+
} }
|
|
31
51
|
value={ value ?? '' }
|
|
32
52
|
onChange={ handleChange }
|
|
33
53
|
disabled={ disabled }
|
|
@@ -14,7 +14,15 @@ export const SwitchControl = createControl( () => {
|
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<div style={ { display: 'flex', justifyContent: 'flex-end' } }>
|
|
17
|
-
<Switch
|
|
17
|
+
<Switch
|
|
18
|
+
checked={ !! value }
|
|
19
|
+
onChange={ handleChange }
|
|
20
|
+
size="small"
|
|
21
|
+
disabled={ disabled }
|
|
22
|
+
inputProps={ {
|
|
23
|
+
...( disabled ? { style: { opacity: 0 } } : {} ),
|
|
24
|
+
} }
|
|
25
|
+
/>
|
|
18
26
|
</div>
|
|
19
27
|
);
|
|
20
28
|
} );
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Grid } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
import { PropKeyProvider } from '../../../bound-prop-context';
|
|
6
|
+
import { ControlLabel } from '../../../components/control-label';
|
|
7
|
+
import { PopoverGridContainer } from '../../../components/popover-grid-container';
|
|
8
|
+
import { SizeControl } from '../../size-control';
|
|
9
|
+
|
|
10
|
+
type TransformAxisRowProps = {
|
|
11
|
+
label: string;
|
|
12
|
+
bindValue: 'x' | 'y' | 'z';
|
|
13
|
+
startIcon: React.ReactNode;
|
|
14
|
+
anchorRef?: RefObject< HTMLDivElement | null >;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const AxisRow = ( { label, bindValue, startIcon, anchorRef }: TransformAxisRowProps ) => {
|
|
18
|
+
return (
|
|
19
|
+
<Grid item xs={ 12 }>
|
|
20
|
+
<PopoverGridContainer ref={ anchorRef }>
|
|
21
|
+
<Grid item xs={ 6 }>
|
|
22
|
+
<ControlLabel>{ label }</ControlLabel>
|
|
23
|
+
</Grid>
|
|
24
|
+
<Grid item xs={ 6 }>
|
|
25
|
+
<PropKeyProvider bind={ bindValue }>
|
|
26
|
+
<SizeControl anchorRef={ anchorRef } startIcon={ startIcon } />
|
|
27
|
+
</PropKeyProvider>
|
|
28
|
+
</Grid>
|
|
29
|
+
</PopoverGridContainer>
|
|
30
|
+
</Grid>
|
|
31
|
+
);
|
|
32
|
+
};
|