@elementor/editor-controls 0.6.0 → 0.7.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 +42 -0
- package/dist/index.js +287 -112
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +281 -106
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/components/repeater.tsx +2 -1
- package/src/controls/autocomplete-control.tsx +19 -24
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-attachment.tsx +38 -0
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +31 -0
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-repeat.tsx +50 -0
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-resolution.tsx +27 -0
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-size.tsx +44 -0
- package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +88 -26
- package/src/controls/box-shadow-repeater-control.tsx +3 -2
- package/src/controls/equal-unequal-sizes-control.tsx +4 -2
- package/src/controls/gap-control.tsx +33 -31
- package/src/controls/image-media-control.tsx +23 -15
- package/src/controls/link-control.tsx +2 -2
- package/src/controls/linked-dimensions-control.tsx +48 -36
- package/src/controls/select-control.tsx +5 -3
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": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor-props": "0.
|
|
44
|
-
"@elementor/icons": "
|
|
43
|
+
"@elementor/editor-props": "0.8.0",
|
|
44
|
+
"@elementor/icons": "1.24.0",
|
|
45
45
|
"@elementor/session": "0.1.0",
|
|
46
|
-
"@elementor/ui": "
|
|
46
|
+
"@elementor/ui": "1.23.3",
|
|
47
47
|
"@elementor/utils": "0.3.0",
|
|
48
|
-
"@elementor/wp-media": "0.
|
|
48
|
+
"@elementor/wp-media": "0.4.0",
|
|
49
49
|
"@wordpress/i18n": "^5.13.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
@@ -35,6 +35,7 @@ export type RepeaterProps< T > = {
|
|
|
35
35
|
Content: React.ComponentType< {
|
|
36
36
|
anchorEl: AnchorEl;
|
|
37
37
|
bind: PropKey;
|
|
38
|
+
value: T;
|
|
38
39
|
} >;
|
|
39
40
|
};
|
|
40
41
|
};
|
|
@@ -100,7 +101,7 @@ export const Repeater = < T, >( {
|
|
|
100
101
|
duplicateItem={ () => duplicateRepeaterItem( index ) }
|
|
101
102
|
toggleDisableItem={ () => toggleDisableRepeaterItem( index ) }
|
|
102
103
|
>
|
|
103
|
-
{ ( props ) => <itemSettings.Content { ...props } bind={ String( index ) } /> }
|
|
104
|
+
{ ( props ) => <itemSettings.Content { ...props } value={ value } bind={ String( index ) } /> }
|
|
104
105
|
</RepeaterItem>
|
|
105
106
|
) ) }
|
|
106
107
|
</Stack>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useState } from 'react';
|
|
3
2
|
import { stringPropTypeUtil, type urlPropTypeUtil } from '@elementor/editor-props';
|
|
4
3
|
import { XIcon } from '@elementor/icons';
|
|
5
4
|
import {
|
|
@@ -42,30 +41,23 @@ export const AutocompleteControl = createControl(
|
|
|
42
41
|
minInputLength = 2,
|
|
43
42
|
}: Props ) => {
|
|
44
43
|
const { value = '', setValue } = useBoundProp( propType );
|
|
45
|
-
const [ inputValue, setInputValue ] = useState(
|
|
46
|
-
value && options[ value ]?.label ? options[ value ]?.label : value
|
|
47
|
-
);
|
|
48
44
|
|
|
49
45
|
const hasSelectedValue = !! (
|
|
50
|
-
|
|
51
|
-
( options[
|
|
46
|
+
value &&
|
|
47
|
+
( options[ value ] || Object.values( options ).find( ( { label } ) => label === value ) )
|
|
52
48
|
);
|
|
53
|
-
const allowClear = !!
|
|
49
|
+
const allowClear = !! value;
|
|
54
50
|
const formattedOptions = Object.keys( options );
|
|
55
51
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
setInputValue( formattedInputValue || '' );
|
|
60
|
-
|
|
61
|
-
if ( ! allowCustomValues && newValue && ! options[ newValue ] ) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
52
|
+
const onOptionSelect = ( _: React.SyntheticEvent | null, newValue: string | null ) => {
|
|
53
|
+
setValue( newValue );
|
|
54
|
+
};
|
|
64
55
|
|
|
56
|
+
const handleChange = ( newValue: string | null ) => {
|
|
65
57
|
setValue( newValue );
|
|
66
58
|
};
|
|
67
59
|
|
|
68
|
-
const filterOptions = () => {
|
|
60
|
+
const filterOptions = ( _: string[], { inputValue }: { inputValue: string | null } ) => {
|
|
69
61
|
const formattedValue = inputValue?.toLowerCase() || '';
|
|
70
62
|
|
|
71
63
|
if ( formattedValue.length < minInputLength ) {
|
|
@@ -84,7 +76,7 @@ export const AutocompleteControl = createControl(
|
|
|
84
76
|
};
|
|
85
77
|
|
|
86
78
|
// Prevents MUI warning when freeSolo/allowCustomValues is false
|
|
87
|
-
const muiWarningPreventer = () => allowCustomValues || !! filterOptions().length;
|
|
79
|
+
const muiWarningPreventer = () => allowCustomValues || !! filterOptions( [], { inputValue: value } ).length;
|
|
88
80
|
|
|
89
81
|
return (
|
|
90
82
|
<ControlActions>
|
|
@@ -92,11 +84,9 @@ export const AutocompleteControl = createControl(
|
|
|
92
84
|
forcePopupIcon={ false }
|
|
93
85
|
disableClearable={ true } // Disabled component's auto clear icon to use our custom one instead
|
|
94
86
|
freeSolo={ muiWarningPreventer() }
|
|
95
|
-
value={
|
|
87
|
+
value={ value || '' }
|
|
96
88
|
size={ 'tiny' }
|
|
97
|
-
onChange={
|
|
98
|
-
onInputChange={ handleChange }
|
|
99
|
-
onBlur={ allowCustomValues ? undefined : () => handleChange( null, value ) }
|
|
89
|
+
onChange={ onOptionSelect }
|
|
100
90
|
readOnly={ hasSelectedValue }
|
|
101
91
|
options={ formattedOptions }
|
|
102
92
|
getOptionKey={ ( option ) => option }
|
|
@@ -135,14 +125,19 @@ const TextInput = ( {
|
|
|
135
125
|
}: {
|
|
136
126
|
params: AutocompleteRenderInputParams;
|
|
137
127
|
allowClear: boolean;
|
|
138
|
-
handleChange: (
|
|
128
|
+
handleChange: ( newValue: string | null ) => void;
|
|
139
129
|
placeholder: string;
|
|
140
130
|
hasSelectedValue: boolean;
|
|
141
131
|
} ) => {
|
|
132
|
+
const onChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
133
|
+
handleChange( event.target.value );
|
|
134
|
+
};
|
|
135
|
+
|
|
142
136
|
return (
|
|
143
137
|
<TextField
|
|
144
138
|
{ ...params }
|
|
145
139
|
placeholder={ placeholder }
|
|
140
|
+
onChange={ onChange }
|
|
146
141
|
sx={ {
|
|
147
142
|
'& .MuiInputBase-input': {
|
|
148
143
|
cursor: hasSelectedValue ? 'default' : undefined,
|
|
@@ -163,11 +158,11 @@ const ClearButton = ( {
|
|
|
163
158
|
}: {
|
|
164
159
|
params: AutocompleteRenderInputParams;
|
|
165
160
|
allowClear: boolean;
|
|
166
|
-
handleChange: (
|
|
161
|
+
handleChange: ( newValue: string | null ) => void;
|
|
167
162
|
} ) => (
|
|
168
163
|
<InputAdornment position="end">
|
|
169
164
|
{ allowClear && (
|
|
170
|
-
<IconButton size={ params.size } onClick={ handleChange } sx={ { cursor: 'pointer' } }>
|
|
165
|
+
<IconButton size={ params.size } onClick={ () => handleChange( null ) } sx={ { cursor: 'pointer' } }>
|
|
171
166
|
<XIcon fontSize={ params.size } />
|
|
172
167
|
</IconButton>
|
|
173
168
|
) }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { PinIcon, PinnedOffIcon } from '@elementor/icons';
|
|
3
|
+
import { Grid } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
import { ControlLabel } from '../../../../components/control-label';
|
|
7
|
+
import { type ToggleButtonGroupItem } from '../../../../components/control-toggle-button-group';
|
|
8
|
+
import { ToggleControl } from '../../../toggle-control';
|
|
9
|
+
|
|
10
|
+
type Attachment = 'fixed' | 'scroll';
|
|
11
|
+
|
|
12
|
+
const attachmentControlOptions: ToggleButtonGroupItem< Attachment >[] = [
|
|
13
|
+
{
|
|
14
|
+
value: 'fixed',
|
|
15
|
+
label: __( 'Fixed', 'elementor' ),
|
|
16
|
+
renderContent: ( { size } ) => <PinIcon fontSize={ size } />,
|
|
17
|
+
showTooltip: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: 'scroll',
|
|
21
|
+
label: __( 'Scroll', 'elementor' ),
|
|
22
|
+
renderContent: ( { size } ) => <PinnedOffIcon fontSize={ size } />,
|
|
23
|
+
showTooltip: true,
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export const BackgroundImageOverlayAttachment = () => {
|
|
28
|
+
return (
|
|
29
|
+
<Grid container gap={ 8 } alignItems="center" flexWrap="nowrap">
|
|
30
|
+
<Grid item xs={ 2 }>
|
|
31
|
+
<ControlLabel>{ __( 'Attachment', 'elementor' ) }</ControlLabel>
|
|
32
|
+
</Grid>
|
|
33
|
+
<Grid item justifyContent="flex-end" xs={ 8 } sx={ { display: 'flex' } }>
|
|
34
|
+
<ToggleControl options={ attachmentControlOptions } />
|
|
35
|
+
</Grid>
|
|
36
|
+
</Grid>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Grid } from '@elementor/ui';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
|
|
5
|
+
import { ControlLabel } from '../../../../components/control-label';
|
|
6
|
+
import { SelectControl } from '../../../select-control';
|
|
7
|
+
|
|
8
|
+
const backgroundPositionOptions = [
|
|
9
|
+
{ label: __( 'Center Center', 'elementor' ), value: 'center center' },
|
|
10
|
+
{ label: __( 'Center Left', 'elementor' ), value: 'center left' },
|
|
11
|
+
{ label: __( 'Center Right', 'elementor' ), value: 'center right' },
|
|
12
|
+
{ label: __( 'Top Center', 'elementor' ), value: 'top center' },
|
|
13
|
+
{ label: __( 'Top Left', 'elementor' ), value: 'top left' },
|
|
14
|
+
{ label: __( 'Top Right', 'elementor' ), value: 'top right' },
|
|
15
|
+
{ label: __( 'Bottom Center', 'elementor' ), value: 'bottom center' },
|
|
16
|
+
{ label: __( 'Bottom Left', 'elementor' ), value: 'bottom left' },
|
|
17
|
+
{ label: __( 'Bottom Right', 'elementor' ), value: 'bottom right' },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
export const BackgroundImageOverlayPosition = () => {
|
|
21
|
+
return (
|
|
22
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
23
|
+
<Grid item xs={ 6 }>
|
|
24
|
+
<ControlLabel>{ __( 'Position', 'elementor' ) }</ControlLabel>
|
|
25
|
+
</Grid>
|
|
26
|
+
<Grid item xs={ 6 }>
|
|
27
|
+
<SelectControl options={ backgroundPositionOptions } />
|
|
28
|
+
</Grid>
|
|
29
|
+
</Grid>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { DotsHorizontalIcon, DotsVerticalIcon, GridDotsIcon, XIcon } from '@elementor/icons';
|
|
3
|
+
import { Grid } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
import { ControlLabel } from '../../../../components/control-label';
|
|
7
|
+
import { type ToggleButtonGroupItem } from '../../../../components/control-toggle-button-group';
|
|
8
|
+
import { ToggleControl } from '../../../toggle-control';
|
|
9
|
+
|
|
10
|
+
type Repeaters = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';
|
|
11
|
+
|
|
12
|
+
const repeatControlOptions: ToggleButtonGroupItem< Repeaters >[] = [
|
|
13
|
+
{
|
|
14
|
+
value: 'repeat',
|
|
15
|
+
label: __( 'Repeat', 'elementor' ),
|
|
16
|
+
renderContent: ( { size } ) => <GridDotsIcon fontSize={ size } />,
|
|
17
|
+
showTooltip: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: 'repeat-x',
|
|
21
|
+
label: __( 'Repeat-x', 'elementor' ),
|
|
22
|
+
renderContent: ( { size } ) => <DotsHorizontalIcon fontSize={ size } />,
|
|
23
|
+
showTooltip: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: 'repeat-y',
|
|
27
|
+
label: __( 'Repeat-y', 'elementor' ),
|
|
28
|
+
renderContent: ( { size } ) => <DotsVerticalIcon fontSize={ size } />,
|
|
29
|
+
showTooltip: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
value: 'no-repeat',
|
|
33
|
+
label: __( 'No-Repeat', 'elementor' ),
|
|
34
|
+
renderContent: ( { size } ) => <XIcon fontSize={ size } />,
|
|
35
|
+
showTooltip: true,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export const BackgroundImageOverlayRepeat = () => {
|
|
40
|
+
return (
|
|
41
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
42
|
+
<Grid item xs={ 6 }>
|
|
43
|
+
<ControlLabel>{ __( 'Repeat', 'elementor' ) }</ControlLabel>
|
|
44
|
+
</Grid>
|
|
45
|
+
<Grid item xs={ 6 }>
|
|
46
|
+
<ToggleControl options={ repeatControlOptions } />
|
|
47
|
+
</Grid>
|
|
48
|
+
</Grid>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Grid } from '@elementor/ui';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
|
|
5
|
+
import { ControlLabel } from '../../../../components/control-label';
|
|
6
|
+
import { SelectControl } from '../../../select-control';
|
|
7
|
+
|
|
8
|
+
const backgroundResolutionOptions = [
|
|
9
|
+
{ label: __( 'Thumbnail - 150 x 150', 'elementor' ), value: 'thumbnail' },
|
|
10
|
+
{ label: __( 'Medium - 300 x 300', 'elementor' ), value: 'medium' },
|
|
11
|
+
{ label: __( 'Medium Large - 768 x 768' ), value: 'medium_large' },
|
|
12
|
+
{ label: __( 'Large 1024 x 1024', 'elementor' ), value: 'large' },
|
|
13
|
+
{ label: __( 'Full', 'elementor' ), value: 'full' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export const BackgroundImageOverlayResolution = () => {
|
|
17
|
+
return (
|
|
18
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
19
|
+
<Grid item xs={ 6 }>
|
|
20
|
+
<ControlLabel>{ __( 'Resolution', 'elementor' ) }</ControlLabel>
|
|
21
|
+
</Grid>
|
|
22
|
+
<Grid item xs={ 6 }>
|
|
23
|
+
<SelectControl options={ backgroundResolutionOptions } />
|
|
24
|
+
</Grid>
|
|
25
|
+
</Grid>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ArrowBarBothIcon, ArrowsMaximizeIcon } from '@elementor/icons';
|
|
3
|
+
import { Grid } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
import { ControlLabel } from '../../../../components/control-label';
|
|
7
|
+
import { type ToggleButtonGroupItem } from '../../../../components/control-toggle-button-group';
|
|
8
|
+
import { ToggleControl } from '../../../toggle-control';
|
|
9
|
+
|
|
10
|
+
type Sizes = 'auto' | 'cover' | 'contain';
|
|
11
|
+
|
|
12
|
+
const sizeControlOptions: ToggleButtonGroupItem< Sizes >[] = [
|
|
13
|
+
{
|
|
14
|
+
value: 'auto',
|
|
15
|
+
label: __( 'Auto', 'elementor' ),
|
|
16
|
+
renderContent: () => 'Auto',
|
|
17
|
+
showTooltip: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: 'cover',
|
|
21
|
+
label: __( 'Cover', 'elementor' ),
|
|
22
|
+
renderContent: ( { size } ) => <ArrowsMaximizeIcon fontSize={ size } />,
|
|
23
|
+
showTooltip: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: 'contain',
|
|
27
|
+
label: __( 'Contain', 'elementor' ),
|
|
28
|
+
renderContent: ( { size } ) => <ArrowBarBothIcon fontSize={ size } />,
|
|
29
|
+
showTooltip: true,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export const BackgroundImageOverlaySize = () => {
|
|
34
|
+
return (
|
|
35
|
+
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
36
|
+
<Grid item xs={ 6 }>
|
|
37
|
+
<ControlLabel>{ __( 'Size', 'elementor' ) }</ControlLabel>
|
|
38
|
+
</Grid>
|
|
39
|
+
<Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end' } }>
|
|
40
|
+
<ToggleControl options={ sizeControlOptions } />
|
|
41
|
+
</Grid>
|
|
42
|
+
</Grid>
|
|
43
|
+
);
|
|
44
|
+
};
|
package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx
CHANGED
|
@@ -6,22 +6,39 @@ import {
|
|
|
6
6
|
backgroundOverlayPropTypeUtil,
|
|
7
7
|
type PropKey,
|
|
8
8
|
} from '@elementor/editor-props';
|
|
9
|
-
import { Grid, Stack, UnstableColorIndicator } from '@elementor/ui';
|
|
9
|
+
import { Box, Grid, Stack, Tab, TabPanel, Tabs, UnstableColorIndicator, useTabs } from '@elementor/ui';
|
|
10
10
|
import { useWpMediaAttachment } from '@elementor/wp-media';
|
|
11
11
|
import { __ } from '@wordpress/i18n';
|
|
12
12
|
|
|
13
13
|
import { PropKeyProvider, PropProvider, useBoundProp } from '../../../bound-prop-context';
|
|
14
|
-
import { ControlLabel } from '../../../components/control-label';
|
|
15
14
|
import { Repeater } from '../../../components/repeater';
|
|
16
15
|
import { createControl } from '../../../create-control';
|
|
17
16
|
import { ColorControl } from '../../color-control';
|
|
18
17
|
import { ImageMediaControl } from '../../image-media-control';
|
|
18
|
+
import { BackgroundImageOverlayAttachment } from './background-image-overlay/background-image-overlay-attachment';
|
|
19
|
+
import { BackgroundImageOverlayPosition } from './background-image-overlay/background-image-overlay-position';
|
|
20
|
+
import { BackgroundImageOverlayRepeat } from './background-image-overlay/background-image-overlay-repeat';
|
|
21
|
+
import { BackgroundImageOverlayResolution } from './background-image-overlay/background-image-overlay-resolution';
|
|
22
|
+
import { BackgroundImageOverlaySize } from './background-image-overlay/background-image-overlay-size';
|
|
19
23
|
|
|
24
|
+
const defaultImagePlaceholderId = 1;
|
|
20
25
|
const initialBackgroundOverlay: BackgroundOverlayItemPropValue = {
|
|
21
|
-
$$type: 'background-
|
|
22
|
-
value:
|
|
26
|
+
$$type: 'background-image-overlay',
|
|
27
|
+
value: {
|
|
28
|
+
'image-src': {
|
|
29
|
+
$$type: 'image-src',
|
|
30
|
+
value: {
|
|
31
|
+
id: {
|
|
32
|
+
$$type: 'image-attachment-id',
|
|
33
|
+
value: defaultImagePlaceholderId,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
23
38
|
};
|
|
24
39
|
|
|
40
|
+
type OverlayType = 'image' | 'color';
|
|
41
|
+
|
|
25
42
|
export const BackgroundOverlayRepeaterControl = createControl( () => {
|
|
26
43
|
const { propType, value: overlayValues, setValue } = useBoundProp( backgroundOverlayPropTypeUtil );
|
|
27
44
|
|
|
@@ -46,37 +63,39 @@ const ItemIcon = ( { value }: { value: BackgroundOverlayItemPropValue } ) => (
|
|
|
46
63
|
<UnstableColorIndicator size="inherit" component="span" value={ value.value } />
|
|
47
64
|
);
|
|
48
65
|
|
|
49
|
-
const ItemContent = ( { bind }: { bind: PropKey } ) => {
|
|
66
|
+
const ItemContent = ( { bind, value }: { bind: PropKey; value: BackgroundOverlayItemPropValue } ) => {
|
|
50
67
|
return (
|
|
51
68
|
<PropKeyProvider bind={ bind }>
|
|
52
|
-
<Content />
|
|
69
|
+
<Content value={ value } />
|
|
53
70
|
</PropKeyProvider>
|
|
54
71
|
);
|
|
55
72
|
};
|
|
56
73
|
|
|
57
|
-
const Content = () => {
|
|
58
|
-
const
|
|
74
|
+
const Content = ( { value }: { value: BackgroundOverlayItemPropValue } ) => {
|
|
75
|
+
const activeTab = deriveOverlayType( value.$$type );
|
|
76
|
+
const { getTabsProps, getTabProps, getTabPanelProps } = useTabs< OverlayType >( activeTab );
|
|
59
77
|
|
|
60
78
|
return (
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
<Box sx={ { width: '100%' } }>
|
|
80
|
+
<Box sx={ { borderBottom: 1, borderColor: 'divider' } }>
|
|
81
|
+
<Tabs { ...getTabsProps() } aria-label={ __( 'Background Overlay', 'elementor' ) }>
|
|
82
|
+
<Tab label={ __( 'Image', 'elementor' ) } { ...getTabProps( 'image' ) } />
|
|
83
|
+
<Tab label={ __( 'Color', 'elementor' ) } { ...getTabProps( 'color' ) } />
|
|
84
|
+
</Tabs>
|
|
85
|
+
</Box>
|
|
86
|
+
<TabPanel { ...getTabPanelProps( 'image' ) }>
|
|
87
|
+
<Stack gap={ 1.5 }>
|
|
88
|
+
<ImageOverlayContent />
|
|
89
|
+
</Stack>
|
|
90
|
+
</TabPanel>
|
|
91
|
+
<TabPanel { ...getTabPanelProps( 'color' ) }>
|
|
92
|
+
<Grid container spacing={ 1 } alignItems="center">
|
|
93
|
+
<Grid item xs={ 12 }>
|
|
94
|
+
<ColorControl propTypeUtil={ backgroundColorOverlayPropTypeUtil } />
|
|
76
95
|
</Grid>
|
|
77
|
-
</
|
|
78
|
-
</
|
|
79
|
-
</
|
|
96
|
+
</Grid>
|
|
97
|
+
</TabPanel>
|
|
98
|
+
</Box>
|
|
80
99
|
);
|
|
81
100
|
};
|
|
82
101
|
|
|
@@ -101,3 +120,46 @@ const ItemLabelImage = ( { value }: { value: BackgroundOverlayItemPropValue } )
|
|
|
101
120
|
|
|
102
121
|
return <span>{ imageTitle }</span>;
|
|
103
122
|
};
|
|
123
|
+
|
|
124
|
+
const ImageOverlayContent = () => {
|
|
125
|
+
const propContext = useBoundProp( backgroundImageOverlayPropTypeUtil );
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<PropProvider { ...propContext }>
|
|
129
|
+
<PropKeyProvider bind={ 'image-src' }>
|
|
130
|
+
<Grid container spacing={ 1 } alignItems="center">
|
|
131
|
+
<Grid item xs={ 12 }>
|
|
132
|
+
<ImageMediaControl />
|
|
133
|
+
</Grid>
|
|
134
|
+
</Grid>
|
|
135
|
+
</PropKeyProvider>
|
|
136
|
+
<PropKeyProvider bind={ 'resolution' }>
|
|
137
|
+
<BackgroundImageOverlayResolution />
|
|
138
|
+
</PropKeyProvider>
|
|
139
|
+
<PropKeyProvider bind={ 'position' }>
|
|
140
|
+
<BackgroundImageOverlayPosition />
|
|
141
|
+
</PropKeyProvider>
|
|
142
|
+
<PropKeyProvider bind={ 'repeat' }>
|
|
143
|
+
<BackgroundImageOverlayRepeat />
|
|
144
|
+
</PropKeyProvider>
|
|
145
|
+
<PropKeyProvider bind={ 'size' }>
|
|
146
|
+
<BackgroundImageOverlaySize />
|
|
147
|
+
</PropKeyProvider>
|
|
148
|
+
<PropKeyProvider bind={ 'attachment' }>
|
|
149
|
+
<BackgroundImageOverlayAttachment />
|
|
150
|
+
</PropKeyProvider>
|
|
151
|
+
</PropProvider>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const deriveOverlayType = ( type: string ): OverlayType => {
|
|
156
|
+
if ( type === 'background-color-overlay' ) {
|
|
157
|
+
return 'color';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if ( type === 'background-image-overlay' ) {
|
|
161
|
+
return 'image';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
throw new Error( `Invalid overlay type: ${ type }` );
|
|
165
|
+
};
|
|
@@ -70,7 +70,7 @@ const Content = ( { anchorEl }: { anchorEl: HTMLElement | null } ) => {
|
|
|
70
70
|
<SelectControl
|
|
71
71
|
options={ [
|
|
72
72
|
{ label: __( 'Inset', 'elementor' ), value: 'inset' },
|
|
73
|
-
{ label: __( 'Outset', 'elementor' ), value:
|
|
73
|
+
{ label: __( 'Outset', 'elementor' ), value: null },
|
|
74
74
|
] }
|
|
75
75
|
/>
|
|
76
76
|
</Control>
|
|
@@ -120,6 +120,7 @@ const ItemLabel = ( { value }: { value: ShadowPropValue } ) => {
|
|
|
120
120
|
const { size: spreadSize = '', unit: spreadUnit = '' } = spread?.value || {};
|
|
121
121
|
const { size: hOffsetSize = 'unset', unit: hOffsetUnit = '' } = hOffset?.value || {};
|
|
122
122
|
const { size: vOffsetSize = 'unset', unit: vOffsetUnit = '' } = vOffset?.value || {};
|
|
123
|
+
const positionLabel = position?.value || 'outset';
|
|
123
124
|
|
|
124
125
|
const sizes = [
|
|
125
126
|
hOffsetSize + hOffsetUnit,
|
|
@@ -130,7 +131,7 @@ const ItemLabel = ( { value }: { value: ShadowPropValue } ) => {
|
|
|
130
131
|
|
|
131
132
|
return (
|
|
132
133
|
<span style={ { textTransform: 'capitalize' } }>
|
|
133
|
-
{
|
|
134
|
+
{ positionLabel }: { sizes }
|
|
134
135
|
</span>
|
|
135
136
|
);
|
|
136
137
|
};
|
|
@@ -77,7 +77,7 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
|
|
|
77
77
|
const isEqual = isEqualSizes( newMappedValues, items );
|
|
78
78
|
|
|
79
79
|
if ( isEqual ) {
|
|
80
|
-
return setSizeValue( Object.values( newMappedValues )[ 0 ]
|
|
80
|
+
return setSizeValue( Object.values( newMappedValues )[ 0 ]?.value );
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
setMultiSizeValue( newMappedValues );
|
|
@@ -91,6 +91,8 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
|
|
|
91
91
|
return splitEqualValue() ?? null;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
+
const isMixed = !! multiSizeValue;
|
|
95
|
+
|
|
94
96
|
return (
|
|
95
97
|
<>
|
|
96
98
|
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap" ref={ controlRef }>
|
|
@@ -99,7 +101,7 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
|
|
|
99
101
|
</Grid>
|
|
100
102
|
<Grid item xs={ 6 }>
|
|
101
103
|
<Stack direction="row" alignItems="center" gap={ 1 }>
|
|
102
|
-
<SizeControl placeholder={ __( '
|
|
104
|
+
<SizeControl placeholder={ isMixed ? __( 'Mixed', 'elementor' ) : undefined } />
|
|
103
105
|
<ToggleButton
|
|
104
106
|
size={ 'tiny' }
|
|
105
107
|
value={ 'check' }
|
|
@@ -1,48 +1,42 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { layoutDirectionPropTypeUtil, type PropKey, sizePropTypeUtil } 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 { PropKeyProvider, PropProvider,
|
|
7
|
+
import { PropKeyProvider, PropProvider, 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 Gap = 'row' | 'column';
|
|
13
|
-
|
|
14
12
|
export const GapControl = createControl( ( { label }: { label: string } ) => {
|
|
15
|
-
const {
|
|
16
|
-
|
|
13
|
+
const {
|
|
14
|
+
value: directionValue,
|
|
15
|
+
setValue: setDirectionValue,
|
|
16
|
+
propType,
|
|
17
|
+
} = useBoundProp( layoutDirectionPropTypeUtil );
|
|
18
|
+
const { value: sizeValue, setValue: setSizeValue } = useBoundProp( sizePropTypeUtil );
|
|
19
|
+
|
|
20
|
+
const isLinked = ! directionValue && ! sizeValue ? true : !! sizeValue;
|
|
17
21
|
|
|
18
|
-
const
|
|
22
|
+
const onLinkToggle = () => {
|
|
19
23
|
if ( ! isLinked ) {
|
|
20
|
-
|
|
24
|
+
setSizeValue( directionValue?.column.value );
|
|
25
|
+
return;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
const
|
|
28
|
+
const value = sizeValue ? sizePropTypeUtil.create( sizeValue ) : null;
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
column:
|
|
28
|
-
row: newDimension,
|
|
30
|
+
setDirectionValue( {
|
|
31
|
+
row: value,
|
|
32
|
+
column: value,
|
|
29
33
|
} );
|
|
30
34
|
};
|
|
31
35
|
|
|
32
|
-
const toggleLinked = () => {
|
|
33
|
-
const updatedValue = {
|
|
34
|
-
isLinked: ! isLinked,
|
|
35
|
-
column,
|
|
36
|
-
row: ! isLinked ? column : row,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
setValue( updatedValue );
|
|
40
|
-
};
|
|
41
|
-
|
|
42
36
|
const LinkedIcon = isLinked ? LinkIcon : DetachIcon;
|
|
43
37
|
|
|
44
38
|
return (
|
|
45
|
-
<PropProvider propType={ propType } value={
|
|
39
|
+
<PropProvider propType={ propType } value={ directionValue } setValue={ setDirectionValue }>
|
|
46
40
|
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
47
41
|
<ControlLabel>{ label }</ControlLabel>
|
|
48
42
|
<ToggleButton
|
|
@@ -51,7 +45,7 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
|
|
|
51
45
|
value={ 'check' }
|
|
52
46
|
selected={ isLinked }
|
|
53
47
|
sx={ { marginLeft: 'auto' } }
|
|
54
|
-
onChange={
|
|
48
|
+
onChange={ onLinkToggle }
|
|
55
49
|
>
|
|
56
50
|
<LinkedIcon fontSize={ 'tiny' } />
|
|
57
51
|
</ToggleButton>
|
|
@@ -62,9 +56,7 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
|
|
|
62
56
|
<ControlLabel>{ __( 'Column', 'elementor' ) }</ControlLabel>
|
|
63
57
|
</Grid>
|
|
64
58
|
<Grid item xs={ 12 }>
|
|
65
|
-
<
|
|
66
|
-
<SizeControl />
|
|
67
|
-
</PropKeyProvider>
|
|
59
|
+
<Control bind={ 'column' } isLinked={ isLinked } />
|
|
68
60
|
</Grid>
|
|
69
61
|
</Grid>
|
|
70
62
|
<Grid container gap={ 1 } alignItems="center">
|
|
@@ -72,12 +64,22 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
|
|
|
72
64
|
<ControlLabel>{ __( 'Row', 'elementor' ) }</ControlLabel>
|
|
73
65
|
</Grid>
|
|
74
66
|
<Grid item xs={ 12 }>
|
|
75
|
-
<
|
|
76
|
-
<SizeControl />
|
|
77
|
-
</PropKeyProvider>
|
|
67
|
+
<Control bind={ 'row' } isLinked={ isLinked } />
|
|
78
68
|
</Grid>
|
|
79
69
|
</Grid>
|
|
80
70
|
</Stack>
|
|
81
71
|
</PropProvider>
|
|
82
72
|
);
|
|
83
73
|
} );
|
|
74
|
+
|
|
75
|
+
const Control = ( { bind, isLinked }: { bind: PropKey; isLinked: boolean } ) => {
|
|
76
|
+
if ( isLinked ) {
|
|
77
|
+
return <SizeControl />;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<PropKeyProvider bind={ bind }>
|
|
82
|
+
<SizeControl />
|
|
83
|
+
</PropKeyProvider>
|
|
84
|
+
);
|
|
85
|
+
};
|