@elementor/editor-controls 0.10.0 → 0.12.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/index.d.mts +3 -25
  3. package/dist/index.d.ts +3 -25
  4. package/dist/index.js +427 -403
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +400 -369
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +2 -2
  9. package/src/components/autocomplete.tsx +181 -0
  10. package/src/components/popover-content.tsx +15 -0
  11. package/src/components/popover-grid-container.tsx +20 -0
  12. package/src/components/repeater.tsx +6 -4
  13. package/src/components/section-content.tsx +16 -0
  14. package/src/controls/background-control/background-control.tsx +4 -3
  15. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-attachment.tsx +4 -3
  16. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +15 -14
  17. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-repeat.tsx +3 -2
  18. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-size.tsx +5 -4
  19. package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +4 -3
  20. package/src/controls/box-shadow-repeater-control.tsx +12 -10
  21. package/src/controls/equal-unequal-sizes-control.tsx +9 -7
  22. package/src/controls/font-family-control.tsx +4 -2
  23. package/src/controls/gap-control.tsx +1 -1
  24. package/src/controls/image-control.tsx +2 -2
  25. package/src/controls/image-media-control.tsx +2 -2
  26. package/src/controls/link-control.tsx +65 -49
  27. package/src/controls/linked-dimensions-control.tsx +1 -1
  28. package/src/controls/select-control.tsx +8 -1
  29. package/src/controls/size-control.tsx +3 -1
  30. package/src/controls/stroke-control.tsx +6 -5
  31. package/src/index.ts +0 -1
  32. package/src/controls/autocomplete-control.tsx +0 -195
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.10.0",
4
+ "version": "0.12.0",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,7 +40,7 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor-props": "0.9.1",
43
+ "@elementor/editor-props": "0.9.2",
44
44
  "@elementor/env": "0.3.5",
45
45
  "@elementor/http": "0.1.3",
46
46
  "@elementor/icons": "1.31.0",
@@ -0,0 +1,181 @@
1
+ import * as React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import { XIcon } from '@elementor/icons';
4
+ import {
5
+ Autocomplete as AutocompleteBase,
6
+ type AutocompleteRenderInputParams,
7
+ Box,
8
+ IconButton,
9
+ InputAdornment,
10
+ TextField,
11
+ } from '@elementor/ui';
12
+
13
+ export type FlatOption = {
14
+ id: string;
15
+ label: string;
16
+ groupLabel?: never;
17
+ };
18
+
19
+ export type CategorizedOption = {
20
+ id: string;
21
+ label: string;
22
+ groupLabel: string;
23
+ };
24
+
25
+ export type Props = {
26
+ options: FlatOption[] | CategorizedOption[];
27
+ value?: number | string | null;
28
+ onOptionChange: ( newValue: number | null ) => void;
29
+ onTextChange?: ( newValue: string | null ) => void;
30
+ allowCustomValues?: boolean;
31
+ placeholder?: string;
32
+ minInputLength?: number;
33
+ };
34
+
35
+ export const Autocomplete = forwardRef( ( props: Props, ref ) => {
36
+ const {
37
+ options,
38
+ onOptionChange,
39
+ onTextChange,
40
+ allowCustomValues = false,
41
+ placeholder = '',
42
+ minInputLength = 2,
43
+ value = '',
44
+ ...restProps
45
+ } = props;
46
+
47
+ const optionKeys = _factoryFilter( value, options, minInputLength ).map( ( { id } ) => id );
48
+ const allowClear = !! value;
49
+
50
+ // Prevents MUI warning when freeSolo/allowCustomValues is false
51
+ const muiWarningPreventer = allowCustomValues || !! value?.toString()?.length;
52
+
53
+ const isOptionEqualToValue = muiWarningPreventer ? undefined : () => true;
54
+
55
+ const isValueFromOptions = typeof value === 'number' && !! findMatchingOption( options, value );
56
+
57
+ return (
58
+ <AutocompleteBase
59
+ { ...restProps }
60
+ ref={ ref }
61
+ forcePopupIcon={ false }
62
+ disableClearable={ true } // Disabled component's auto clear icon to use our custom one instead
63
+ freeSolo={ allowCustomValues }
64
+ value={ value?.toString() || '' }
65
+ size={ 'tiny' }
66
+ onChange={ ( _, newValue ) => onOptionChange( Number( newValue ) ) }
67
+ readOnly={ isValueFromOptions }
68
+ options={ optionKeys }
69
+ getOptionKey={ ( optionId ) => findMatchingOption( options, optionId )?.id || optionId }
70
+ getOptionLabel={ ( optionId ) => findMatchingOption( options, optionId )?.label || optionId.toString() }
71
+ groupBy={
72
+ isCategorizedOptionPool( options )
73
+ ? ( optionId: string ) => findMatchingOption( options, optionId )?.groupLabel || optionId
74
+ : undefined
75
+ }
76
+ isOptionEqualToValue={ isOptionEqualToValue }
77
+ filterOptions={ () => optionKeys }
78
+ renderOption={ ( optionProps, optionId ) => (
79
+ <Box component="li" { ...optionProps } key={ optionProps.id }>
80
+ { findMatchingOption( options, optionId )?.label ?? optionId }
81
+ </Box>
82
+ ) }
83
+ renderInput={ ( params ) => (
84
+ <TextInput
85
+ params={ params }
86
+ handleChange={ ( newValue ) => onTextChange?.( newValue ) }
87
+ allowClear={ allowClear }
88
+ placeholder={ placeholder }
89
+ hasSelectedValue={ isValueFromOptions }
90
+ />
91
+ ) }
92
+ />
93
+ );
94
+ } );
95
+
96
+ const TextInput = ( {
97
+ params,
98
+ allowClear,
99
+ placeholder,
100
+ handleChange,
101
+ hasSelectedValue,
102
+ }: {
103
+ params: AutocompleteRenderInputParams;
104
+ allowClear: boolean;
105
+ handleChange: ( newValue: string | null ) => void;
106
+ placeholder: string;
107
+ hasSelectedValue: boolean;
108
+ } ) => {
109
+ const onChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
110
+ handleChange( event.target.value );
111
+ };
112
+
113
+ return (
114
+ <TextField
115
+ { ...params }
116
+ placeholder={ placeholder }
117
+ onChange={ onChange }
118
+ sx={ {
119
+ '& .MuiInputBase-input': {
120
+ cursor: hasSelectedValue ? 'default' : undefined,
121
+ },
122
+ } }
123
+ InputProps={ {
124
+ ...params.InputProps,
125
+ endAdornment: <ClearButton params={ params } allowClear={ allowClear } handleChange={ handleChange } />,
126
+ } }
127
+ />
128
+ );
129
+ };
130
+
131
+ const ClearButton = ( {
132
+ allowClear,
133
+ handleChange,
134
+ params,
135
+ }: {
136
+ params: AutocompleteRenderInputParams;
137
+ allowClear: boolean;
138
+ handleChange: ( newValue: string | null ) => void;
139
+ } ) => (
140
+ <InputAdornment position="end">
141
+ { allowClear && (
142
+ <IconButton size={ params.size } onClick={ () => handleChange( null ) } sx={ { cursor: 'pointer' } }>
143
+ <XIcon fontSize={ params.size } />
144
+ </IconButton>
145
+ ) }
146
+ </InputAdornment>
147
+ );
148
+
149
+ export function findMatchingOption(
150
+ options: FlatOption[] | CategorizedOption[],
151
+ optionId: string | number | null = null
152
+ ) {
153
+ const formattedOption = ( optionId || '' ).toString();
154
+
155
+ return options.find( ( { id } ) => formattedOption === id.toString() );
156
+ }
157
+
158
+ export function isCategorizedOptionPool( options: FlatOption[] | CategorizedOption[] ): options is CategorizedOption[] {
159
+ return options.every( ( option ) => 'groupLabel' in option );
160
+ }
161
+ function _factoryFilter< T extends FlatOption[] | CategorizedOption[] >(
162
+ newValue: string | number | null,
163
+ options: T,
164
+ minInputLength: number
165
+ ): T {
166
+ if ( null === newValue ) {
167
+ return options;
168
+ }
169
+
170
+ const formattedValue = String( newValue || '' )?.toLowerCase();
171
+
172
+ if ( formattedValue.length < minInputLength ) {
173
+ return new Array( 0 ) as T;
174
+ }
175
+
176
+ return options.filter(
177
+ ( option ) =>
178
+ String( option.id ).toLowerCase().includes( formattedValue ) ||
179
+ option.label.toLowerCase().includes( formattedValue )
180
+ ) as T;
181
+ }
@@ -0,0 +1,15 @@
1
+ import { type FC, type PropsWithChildren } from 'react';
2
+ import * as React from 'react';
3
+ import { Stack } from '@elementor/ui';
4
+
5
+ type PopoverContentProps = PropsWithChildren< {
6
+ alignItems?: 'center';
7
+ gap?: number;
8
+ p?: 1.5 | 2 | 2.5;
9
+ } >;
10
+
11
+ export const PopoverContent: FC< PopoverContentProps > = ( { alignItems, gap = 1.5, p, children } ) => (
12
+ <Stack alignItems={ alignItems } gap={ gap } p={ p }>
13
+ { children }
14
+ </Stack>
15
+ );
@@ -0,0 +1,20 @@
1
+ import { type FC, type PropsWithChildren } from 'react';
2
+ import * as React from 'react';
3
+ import { Grid } from '@elementor/ui';
4
+
5
+ type PopoverGridContainerProps = PropsWithChildren< {
6
+ gap?: number;
7
+ alignItems?: React.ComponentProps< typeof Grid >[ 'alignItems' ];
8
+ flexWrap?: React.ComponentProps< typeof Grid >[ 'flexWrap' ];
9
+ } >;
10
+
11
+ export const PopoverGridContainer: FC< PopoverGridContainerProps > = ( {
12
+ gap = 1.5,
13
+ alignItems = 'center',
14
+ flexWrap = 'nowrap',
15
+ children,
16
+ } ) => (
17
+ <Grid container gap={ gap } alignItems={ alignItems } flexWrap={ flexWrap }>
18
+ { children }
19
+ </Grid>
20
+ );
@@ -16,6 +16,8 @@ import {
16
16
  } from '@elementor/ui';
17
17
  import { __ } from '@wordpress/i18n';
18
18
 
19
+ import { SectionContent } from './section-content';
20
+
19
21
  const SIZE = 'tiny';
20
22
 
21
23
  type AnchorEl = HTMLElement | null;
@@ -86,8 +88,8 @@ export const Repeater = < T, >( {
86
88
  };
87
89
 
88
90
  return (
89
- <Stack>
90
- <Stack direction="row" justifyContent="space-between" alignItems="center" sx={ { pb: 1 } }>
91
+ <SectionContent>
92
+ <Stack direction="row" justifyContent="space-between" alignItems="center">
91
93
  <Typography component="label" variant="caption" color="text.secondary">
92
94
  { label }
93
95
  </Typography>
@@ -95,7 +97,7 @@ export const Repeater = < T, >( {
95
97
  <PlusIcon fontSize={ SIZE } />
96
98
  </IconButton>
97
99
  </Stack>
98
- <Stack gap={ 1 }>
100
+ <Stack gap={ 1 } sx={ { '&:empty': { display: 'none' } } }>
99
101
  { repeaterValues.map( ( value, index ) => (
100
102
  <RepeaterItem
101
103
  key={ index }
@@ -111,7 +113,7 @@ export const Repeater = < T, >( {
111
113
  </RepeaterItem>
112
114
  ) ) }
113
115
  </Stack>
114
- </Stack>
116
+ </SectionContent>
115
117
  );
116
118
  };
117
119
 
@@ -0,0 +1,16 @@
1
+ import { type FC, type PropsWithChildren } from 'react';
2
+ import * as React from 'react';
3
+ import { Stack } from '@elementor/ui';
4
+
5
+ type SectionContentProps = PropsWithChildren< {
6
+ gap?: number;
7
+ sx?: {
8
+ pt?: number;
9
+ };
10
+ } >;
11
+
12
+ export const SectionContent: FC< SectionContentProps > = ( { gap = 2, sx, children } ) => (
13
+ <Stack gap={ gap } sx={ { ...sx } }>
14
+ { children }
15
+ </Stack>
16
+ );
@@ -1,10 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { backgroundPropTypeUtil } from '@elementor/editor-props';
3
- import { Grid, Stack } from '@elementor/ui';
3
+ import { Grid } from '@elementor/ui';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
6
  import { PropKeyProvider, PropProvider, useBoundProp } from '../../bound-prop-context';
7
7
  import { ControlLabel } from '../../components/control-label';
8
+ import { SectionContent } from '../../components/section-content';
8
9
  import { createControl } from '../../create-control';
9
10
  import { ColorControl } from '../color-control';
10
11
  import { BackgroundOverlayRepeaterControl } from './background-overlay/background-overlay-repeater-control';
@@ -14,7 +15,7 @@ export const BackgroundControl = createControl( () => {
14
15
 
15
16
  return (
16
17
  <PropProvider { ...propContext }>
17
- <Stack gap={ 1.5 }>
18
+ <SectionContent>
18
19
  <PropKeyProvider bind="background-overlay">
19
20
  <BackgroundOverlayRepeaterControl />
20
21
  </PropKeyProvider>
@@ -28,7 +29,7 @@ export const BackgroundControl = createControl( () => {
28
29
  </Grid>
29
30
  </Grid>
30
31
  </PropKeyProvider>
31
- </Stack>
32
+ </SectionContent>
32
33
  </PropProvider>
33
34
  );
34
35
  } );
@@ -5,6 +5,7 @@ import { __ } from '@wordpress/i18n';
5
5
 
6
6
  import { ControlLabel } from '../../../../components/control-label';
7
7
  import { type ToggleButtonGroupItem } from '../../../../components/control-toggle-button-group';
8
+ import { PopoverGridContainer } from '../../../../components/popover-grid-container';
8
9
  import { ToggleControl } from '../../../toggle-control';
9
10
 
10
11
  type Attachment = 'fixed' | 'scroll';
@@ -26,13 +27,13 @@ const attachmentControlOptions: ToggleButtonGroupItem< Attachment >[] = [
26
27
 
27
28
  export const BackgroundImageOverlayAttachment = () => {
28
29
  return (
29
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
30
+ <PopoverGridContainer>
30
31
  <Grid item xs={ 6 }>
31
32
  <ControlLabel>{ __( 'Attachment', 'elementor' ) }</ControlLabel>
32
33
  </Grid>
33
- <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end' } }>
34
+ <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end', overflow: 'hidden' } }>
34
35
  <ToggleControl options={ attachmentControlOptions } />
35
36
  </Grid>
36
- </Grid>
37
+ </PopoverGridContainer>
37
38
  );
38
39
  };
@@ -6,6 +6,7 @@ import { __ } from '@wordpress/i18n';
6
6
 
7
7
  import { PropKeyProvider, PropProvider, useBoundProp } from '../../../../bound-prop-context';
8
8
  import { ControlLabel } from '../../../../components/control-label';
9
+ import { PopoverGridContainer } from '../../../../components/popover-grid-container';
9
10
  import { SizeControl } from '../../../size-control';
10
11
 
11
12
  type Positions =
@@ -21,15 +22,15 @@ type Positions =
21
22
  | 'custom';
22
23
 
23
24
  const backgroundPositionOptions = [
24
- { label: __( 'Center Center', 'elementor' ), value: 'center center' },
25
- { label: __( 'Center Left', 'elementor' ), value: 'center left' },
26
- { label: __( 'Center Right', 'elementor' ), value: 'center right' },
27
- { label: __( 'Top Center', 'elementor' ), value: 'top center' },
28
- { label: __( 'Top Left', 'elementor' ), value: 'top left' },
29
- { label: __( 'Top Right', 'elementor' ), value: 'top right' },
30
- { label: __( 'Bottom Center', 'elementor' ), value: 'bottom center' },
31
- { label: __( 'Bottom Left', 'elementor' ), value: 'bottom left' },
32
- { label: __( 'Bottom Right', 'elementor' ), value: 'bottom right' },
25
+ { label: __( 'Center center', 'elementor' ), value: 'center center' },
26
+ { label: __( 'Center left', 'elementor' ), value: 'center left' },
27
+ { label: __( 'Center right', 'elementor' ), value: 'center right' },
28
+ { label: __( 'Top center', 'elementor' ), value: 'top center' },
29
+ { label: __( 'Top left', 'elementor' ), value: 'top left' },
30
+ { label: __( 'Top right', 'elementor' ), value: 'top right' },
31
+ { label: __( 'Bottom center', 'elementor' ), value: 'bottom center' },
32
+ { label: __( 'Bottom left', 'elementor' ), value: 'bottom left' },
33
+ { label: __( 'Bottom right', 'elementor' ), value: 'bottom right' },
33
34
  { label: __( 'Custom', 'elementor' ), value: 'custom' },
34
35
  ];
35
36
 
@@ -50,13 +51,13 @@ export const BackgroundImageOverlayPosition = () => {
50
51
  };
51
52
 
52
53
  return (
53
- <Grid container spacing={ 2 }>
54
+ <Grid container spacing={ 1.5 }>
54
55
  <Grid item xs={ 12 }>
55
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
56
+ <PopoverGridContainer>
56
57
  <Grid item xs={ 6 }>
57
58
  <ControlLabel>{ __( 'Position', 'elementor' ) }</ControlLabel>
58
59
  </Grid>
59
- <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end' } }>
60
+ <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end', overflow: 'hidden' } }>
60
61
  <Select
61
62
  size="tiny"
62
63
  value={ ( backgroundImageOffsetContext.value ? 'custom' : stringPropContext.value ) ?? '' }
@@ -70,12 +71,12 @@ export const BackgroundImageOverlayPosition = () => {
70
71
  ) ) }
71
72
  </Select>
72
73
  </Grid>
73
- </Grid>
74
+ </PopoverGridContainer>
74
75
  </Grid>
75
76
  { isCustom ? (
76
77
  <PropProvider { ...backgroundImageOffsetContext }>
77
78
  <Grid item xs={ 12 }>
78
- <Grid container spacing={ 2 }>
79
+ <Grid container spacing={ 1.5 }>
79
80
  <Grid item xs={ 6 }>
80
81
  <PropKeyProvider bind={ 'x' }>
81
82
  <SizeControl startIcon={ <LetterXIcon fontSize={ 'tiny' } /> } />
@@ -5,6 +5,7 @@ import { __ } from '@wordpress/i18n';
5
5
 
6
6
  import { ControlLabel } from '../../../../components/control-label';
7
7
  import { type ToggleButtonGroupItem } from '../../../../components/control-toggle-button-group';
8
+ import { PopoverGridContainer } from '../../../../components/popover-grid-container';
8
9
  import { ToggleControl } from '../../../toggle-control';
9
10
 
10
11
  type Repeaters = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';
@@ -38,13 +39,13 @@ const repeatControlOptions: ToggleButtonGroupItem< Repeaters >[] = [
38
39
 
39
40
  export const BackgroundImageOverlayRepeat = () => {
40
41
  return (
41
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
42
+ <PopoverGridContainer>
42
43
  <Grid item xs={ 6 }>
43
44
  <ControlLabel>{ __( 'Repeat', 'elementor' ) }</ControlLabel>
44
45
  </Grid>
45
46
  <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end' } }>
46
47
  <ToggleControl options={ repeatControlOptions } />
47
48
  </Grid>
48
- </Grid>
49
+ </PopoverGridContainer>
49
50
  );
50
51
  };
@@ -17,6 +17,7 @@ import {
17
17
  ControlToggleButtonGroup,
18
18
  type ToggleButtonGroupItem,
19
19
  } from '../../../../components/control-toggle-button-group';
20
+ import { PopoverGridContainer } from '../../../../components/popover-grid-container';
20
21
  import { SizeControl } from '../../../size-control';
21
22
 
22
23
  type Sizes = 'auto' | 'cover' | 'contain' | 'custom';
@@ -65,7 +66,7 @@ export const BackgroundImageOverlaySize = () => {
65
66
  return (
66
67
  <Grid container spacing={ 1.5 }>
67
68
  <Grid item xs={ 12 }>
68
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
69
+ <PopoverGridContainer>
69
70
  <Grid item xs={ 6 }>
70
71
  <ControlLabel>{ __( 'Size', 'elementor' ) }</ControlLabel>
71
72
  </Grid>
@@ -79,12 +80,12 @@ export const BackgroundImageOverlaySize = () => {
79
80
  onChange={ handleSizeChange }
80
81
  />
81
82
  </Grid>
82
- </Grid>
83
+ </PopoverGridContainer>
83
84
  </Grid>
84
85
  { isCustom ? (
85
86
  <PropProvider { ...backgroundImageScaleContext }>
86
87
  <Grid item xs={ 12 }>
87
- <Grid container spacing={ 2 }>
88
+ <PopoverGridContainer>
88
89
  <Grid item xs={ 6 }>
89
90
  <PropKeyProvider bind={ 'width' }>
90
91
  <SizeControl startIcon={ <ArrowsMoveHorizontalIcon fontSize={ 'tiny' } /> } />
@@ -95,7 +96,7 @@ export const BackgroundImageOverlaySize = () => {
95
96
  <SizeControl startIcon={ <ArrowsMoveVerticalIcon fontSize={ 'tiny' } /> } />
96
97
  </PropKeyProvider>
97
98
  </Grid>
98
- </Grid>
99
+ </PopoverGridContainer>
99
100
  </Grid>
100
101
  </PropProvider>
101
102
  ) : null }
@@ -6,11 +6,12 @@ import {
6
6
  backgroundOverlayPropTypeUtil,
7
7
  type PropKey,
8
8
  } from '@elementor/editor-props';
9
- import { Box, CardMedia, Grid, Stack, Tab, TabPanel, Tabs, UnstableColorIndicator } from '@elementor/ui';
9
+ import { Box, CardMedia, Grid, Tab, TabPanel, Tabs, UnstableColorIndicator } 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 { PopoverContent } from '../../../components/popover-content';
14
15
  import { Repeater } from '../../../components/repeater';
15
16
  import { createControl } from '../../../create-control';
16
17
  import { env } from '../../../env';
@@ -103,9 +104,9 @@ const Content = () => {
103
104
  </Tabs>
104
105
  </Box>
105
106
  <TabPanel sx={ { p: 1.5 } } { ...getTabPanelProps( 'image' ) }>
106
- <Stack gap={ 1.5 }>
107
+ <PopoverContent>
107
108
  <ImageOverlayContent />
108
- </Stack>
109
+ </PopoverContent>
109
110
  </TabPanel>
110
111
  <TabPanel { ...getTabPanelProps( 'color' ) } sx={ { p: 1.5 } }>
111
112
  <Grid container spacing={ 1 } alignItems="center">
@@ -1,9 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { boxShadowPropTypeUtil, type PropKey, shadowPropTypeUtil, type ShadowPropValue } from '@elementor/editor-props';
3
- import { Grid, Stack, Typography, UnstableColorIndicator } from '@elementor/ui';
3
+ import { Grid, Typography, UnstableColorIndicator } from '@elementor/ui';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
6
  import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
7
+ import { PopoverContent } from '../components/popover-content';
8
+ import { PopoverGridContainer } from '../components/popover-grid-container';
7
9
  import { Repeater } from '../components/repeater';
8
10
  import { createControl } from '../create-control';
9
11
  import { ColorControl } from './color-control';
@@ -47,8 +49,8 @@ const Content = ( { anchorEl }: { anchorEl: HTMLElement | null } ) => {
47
49
 
48
50
  return (
49
51
  <PropProvider propType={ propType } value={ value } setValue={ setValue }>
50
- <Stack gap={ 1.5 } sx={ { p: 1.5 } }>
51
- <Grid container gap={ 2 } flexWrap="nowrap">
52
+ <PopoverContent p={ 1.5 }>
53
+ <PopoverGridContainer>
52
54
  <Control bind="color" label={ __( 'Color', 'elementor' ) }>
53
55
  <ColorControl
54
56
  slotProps={ {
@@ -74,31 +76,31 @@ const Content = ( { anchorEl }: { anchorEl: HTMLElement | null } ) => {
74
76
  ] }
75
77
  />
76
78
  </Control>
77
- </Grid>
78
- <Grid container gap={ 2 } flexWrap="nowrap">
79
+ </PopoverGridContainer>
80
+ <PopoverGridContainer>
79
81
  <Control bind="hOffset" label={ __( 'Horizontal', 'elementor' ) }>
80
82
  <SizeControl />
81
83
  </Control>
82
84
  <Control bind="vOffset" label={ __( 'Vertical', 'elementor' ) }>
83
85
  <SizeControl />
84
86
  </Control>
85
- </Grid>
86
- <Grid container gap={ 2 } flexWrap="nowrap">
87
+ </PopoverGridContainer>
88
+ <PopoverGridContainer>
87
89
  <Control bind="blur" label={ __( 'Blur', 'elementor' ) }>
88
90
  <SizeControl />
89
91
  </Control>
90
92
  <Control bind="spread" label={ __( 'Spread', 'elementor' ) }>
91
93
  <SizeControl />
92
94
  </Control>
93
- </Grid>
94
- </Stack>
95
+ </PopoverGridContainer>
96
+ </PopoverContent>
95
97
  </PropProvider>
96
98
  );
97
99
  };
98
100
 
99
101
  const Control = ( { label, bind, children }: { bind: string; label: string; children: React.ReactNode } ) => (
100
102
  <PropKeyProvider bind={ bind }>
101
- <Grid item xs={ 6 }>
103
+ <Grid item xs={ 6 } sx={ { overflow: 'hidden' } }>
102
104
  <Grid container gap={ 1 } alignItems="center">
103
105
  <Grid item xs={ 12 }>
104
106
  <Typography component="label" variant="caption" color="text.secondary">
@@ -6,6 +6,8 @@ import { __ } from '@wordpress/i18n';
6
6
 
7
7
  import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
8
8
  import { ControlLabel } from '../components/control-label';
9
+ import { PopoverContent } from '../components/popover-content';
10
+ import { PopoverGridContainer } from '../components/popover-grid-container';
9
11
  import { SizeControl } from './size-control';
10
12
 
11
13
  type MultiSizePropValue = Record< PropKey, SizePropValue >;
@@ -127,20 +129,20 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
127
129
  } }
128
130
  { ...bindPopover( popupState ) }
129
131
  slotProps={ {
130
- paper: { sx: { mt: 0.5, p: 2, pt: 1, width: controlRef.current?.getBoundingClientRect().width } },
132
+ paper: { sx: { mt: 0.5, width: controlRef.current?.getBoundingClientRect().width } },
131
133
  } }
132
134
  >
133
135
  <PropProvider propType={ multiSizePropType } value={ getMultiSizeValues() } setValue={ setNestedProp }>
134
- <Stack gap={ 1.5 }>
135
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
136
+ <PopoverContent p={ 1.5 }>
137
+ <PopoverGridContainer>
136
138
  <MultiSizeValueControl item={ items[ 0 ] } />
137
139
  <MultiSizeValueControl item={ items[ 1 ] } />
138
- </Grid>
139
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
140
+ </PopoverGridContainer>
141
+ <PopoverGridContainer>
140
142
  <MultiSizeValueControl item={ items[ 3 ] } />
141
143
  <MultiSizeValueControl item={ items[ 2 ] } />
142
- </Grid>
143
- </Stack>
144
+ </PopoverGridContainer>
145
+ </PopoverContent>
144
146
  </PropProvider>
145
147
  </Popover>
146
148
  </>
@@ -99,7 +99,9 @@ export const FontFamilyControl = createControl( ( { fontFamilies } ) => {
99
99
  <MenuList role="listbox" tabIndex={ 0 }>
100
100
  { filteredFontFamilies.map( ( [ category, items ], index ) => (
101
101
  <Fragment key={ index }>
102
- <ListSubheader sx={ { typography: 'caption', color: 'text.tertiary' } }>
102
+ <ListSubheader
103
+ sx={ { px: 1.5, typography: 'caption', color: 'text.tertiary' } }
104
+ >
103
105
  { category }
104
106
  </ListSubheader>
105
107
  { items.map( ( item ) => {
@@ -115,7 +117,7 @@ export const FontFamilyControl = createControl( ( { fontFamilies } ) => {
115
117
  setFontFamily( item );
116
118
  handleClose();
117
119
  } }
118
- sx={ { typography: 'caption' } }
120
+ sx={ { px: 1.5, typography: 'caption' } }
119
121
  style={ { fontFamily: item } }
120
122
  >
121
123
  { item }
@@ -40,7 +40,7 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
40
40
  <Stack direction="row" gap={ 2 } flexWrap="nowrap">
41
41
  <ControlLabel>{ label }</ControlLabel>
42
42
  <ToggleButton
43
- aria-label={ __( 'Link Inputs', 'elementor' ) }
43
+ aria-label={ __( 'Link inputs', 'elementor' ) }
44
44
  size={ 'tiny' }
45
45
  value={ 'check' }
46
46
  selected={ isLinked }
@@ -26,11 +26,11 @@ export const ImageControl = createControl(
26
26
  <ImageMediaControl />
27
27
  </PropKeyProvider>
28
28
  <PropKeyProvider bind={ 'size' }>
29
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
29
+ <Grid container gap={ 1.5 } alignItems="center" flexWrap="nowrap">
30
30
  <Grid item xs={ 6 }>
31
31
  <ControlLabel> { resolutionLabel } </ControlLabel>
32
32
  </Grid>
33
- <Grid item xs={ 6 }>
33
+ <Grid item xs={ 6 } sx={ { overflow: 'hidden' } }>
34
34
  <SelectControl options={ sizes } />
35
35
  </Grid>
36
36
  </Grid>