@elementor/editor-controls 1.1.0 → 1.2.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 (31) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/index.d.mts +20 -11
  3. package/dist/index.d.ts +20 -11
  4. package/dist/index.js +739 -565
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +548 -372
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +8 -8
  9. package/src/components/font-family-selector.tsx +30 -13
  10. package/src/components/popover-content.tsx +3 -11
  11. package/src/components/repeater.tsx +3 -1
  12. package/src/components/text-field-popover.tsx +3 -3
  13. package/src/controls/aspect-ratio-control.tsx +20 -2
  14. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +2 -2
  15. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-size.tsx +2 -2
  16. package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +9 -4
  17. package/src/controls/box-shadow-repeater-control.tsx +2 -2
  18. package/src/controls/equal-unequal-sizes-control.tsx +3 -9
  19. package/src/controls/filter-repeater-control.tsx +186 -0
  20. package/src/controls/font-family-control/font-family-control.tsx +6 -2
  21. package/src/controls/gap-control.tsx +3 -3
  22. package/src/controls/image-control.tsx +22 -35
  23. package/src/controls/key-value-control.tsx +39 -19
  24. package/src/controls/link-control.tsx +3 -1
  25. package/src/controls/linked-dimensions-control.tsx +3 -3
  26. package/src/controls/number-control.tsx +3 -3
  27. package/src/controls/repeatable-control.tsx +38 -8
  28. package/src/controls/size-control.tsx +3 -3
  29. package/src/controls/stroke-control.tsx +2 -2
  30. package/src/controls/svg-media-control.tsx +0 -2
  31. package/src/index.ts +3 -1
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.1.0",
4
+ "version": "1.2.0",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -41,11 +41,11 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@elementor/editor-current-user": "0.5.0",
44
- "@elementor/editor-elements": "0.8.6",
45
- "@elementor/editor-props": "0.14.0",
46
- "@elementor/editor-responsive": "0.13.5",
47
- "@elementor/editor-ui": "0.12.0",
48
- "@elementor/editor-v1-adapters": "0.12.0",
44
+ "@elementor/editor-elements": "0.8.7",
45
+ "@elementor/editor-props": "0.15.0",
46
+ "@elementor/editor-responsive": "0.13.6",
47
+ "@elementor/editor-ui": "0.13.0",
48
+ "@elementor/editor-v1-adapters": "0.12.1",
49
49
  "@elementor/env": "0.3.5",
50
50
  "@elementor/http-client": "0.3.0",
51
51
  "@elementor/icons": "1.44.0",
@@ -53,8 +53,8 @@
53
53
  "@elementor/query": "0.2.4",
54
54
  "@elementor/session": "0.1.0",
55
55
  "@elementor/ui": "1.35.5",
56
- "@elementor/utils": "0.4.0",
57
- "@elementor/wp-media": "0.6.0",
56
+ "@elementor/utils": "0.5.0",
57
+ "@elementor/wp-media": "0.6.1",
58
58
  "@wordpress/i18n": "^5.13.0"
59
59
  },
60
60
  "devDependencies": {
@@ -17,6 +17,7 @@ type FontFamilySelectorProps = {
17
17
  fontFamily: string | null;
18
18
  onFontFamilyChange: ( fontFamily: string ) => void;
19
19
  onClose: () => void;
20
+ sectionWidth: number;
20
21
  };
21
22
 
22
23
  export const FontFamilySelector = ( {
@@ -24,6 +25,7 @@ export const FontFamilySelector = ( {
24
25
  fontFamily,
25
26
  onFontFamilyChange,
26
27
  onClose,
28
+ sectionWidth,
27
29
  }: FontFamilySelectorProps ) => {
28
30
  const [ searchValue, setSearchValue ] = useState( '' );
29
31
 
@@ -45,22 +47,32 @@ export const FontFamilySelector = ( {
45
47
  onClose={ handleClose }
46
48
  icon={ <TextIcon fontSize={ SIZE } /> }
47
49
  />
50
+
48
51
  <PopoverSearch
49
52
  value={ searchValue }
50
53
  onSearch={ handleSearch }
51
54
  placeholder={ __( 'Search', 'elementor' ) }
52
55
  />
56
+
53
57
  <Divider />
54
- { filteredFontFamilies.length > 0 ? (
55
- <FontList
56
- fontListItems={ filteredFontFamilies }
57
- setFontFamily={ onFontFamilyChange }
58
- handleClose={ handleClose }
59
- fontFamily={ fontFamily }
60
- />
61
- ) : (
62
- <PopoverScrollableContent>
63
- <Stack alignItems="center" p={ 2.5 } gap={ 1.5 } overflow={ 'hidden' }>
58
+
59
+ <PopoverScrollableContent width={ sectionWidth }>
60
+ { filteredFontFamilies.length > 0 ? (
61
+ <FontList
62
+ fontListItems={ filteredFontFamilies }
63
+ setFontFamily={ onFontFamilyChange }
64
+ handleClose={ handleClose }
65
+ fontFamily={ fontFamily }
66
+ />
67
+ ) : (
68
+ <Stack
69
+ alignItems="center"
70
+ justifyContent="center"
71
+ height="100%"
72
+ p={ 2.5 }
73
+ gap={ 1.5 }
74
+ overflow={ 'hidden' }
75
+ >
64
76
  <TextIcon fontSize="large" />
65
77
  <Box sx={ { maxWidth: 160, overflow: 'hidden' } }>
66
78
  <Typography align="center" variant="subtitle2" color="text.secondary">
@@ -82,7 +94,12 @@ export const FontFamilySelector = ( {
82
94
  <span>&rdquo;.</span>
83
95
  </Typography>
84
96
  </Box>
85
- <Typography align="center" variant="caption" color="text.secondary">
97
+ <Typography
98
+ align="center"
99
+ variant="caption"
100
+ color="text.secondary"
101
+ sx={ { display: 'flex', flexDirection: 'column' } }
102
+ >
86
103
  { __( 'Try something else.', 'elementor' ) }
87
104
  <Link
88
105
  color="secondary"
@@ -94,8 +111,8 @@ export const FontFamilySelector = ( {
94
111
  </Link>
95
112
  </Typography>
96
113
  </Stack>
97
- </PopoverScrollableContent>
98
- ) }
114
+ ) }
115
+ </PopoverScrollableContent>
99
116
  </Stack>
100
117
  );
101
118
  };
@@ -1,17 +1,9 @@
1
1
  import { type FC, type PropsWithChildren } from 'react';
2
2
  import * as React from 'react';
3
- import { Stack } from '@elementor/ui';
3
+ import { Stack, type StackProps } from '@elementor/ui';
4
4
 
5
- type PopoverContentProps = PropsWithChildren< {
6
- alignItems?: 'center';
7
- gap?: number;
8
- p?: 1.5 | 2 | 2.5;
9
- pt?: 2.5;
10
- pb?: 3;
11
- } >;
12
-
13
- export const PopoverContent: FC< PopoverContentProps > = ( { alignItems, gap = 1.5, p, pt, pb, children } ) => (
14
- <Stack alignItems={ alignItems } gap={ gap } p={ p } pt={ pt } pb={ pb }>
5
+ export const PopoverContent: FC< PropsWithChildren< StackProps > > = ( { gap = 1.5, children, ...props } ) => (
6
+ <Stack { ...props } gap={ gap }>
15
7
  { children }
16
8
  </Stack>
17
9
  );
@@ -50,6 +50,7 @@ type RepeaterProps< T > = {
50
50
  };
51
51
  showDuplicate?: boolean;
52
52
  showToggle?: boolean;
53
+ isSortable?: boolean;
53
54
  };
54
55
 
55
56
  const EMPTY_OPEN_ITEM = -1;
@@ -64,6 +65,7 @@ export const Repeater = < T, >( {
64
65
  setValues: setRepeaterValues,
65
66
  showDuplicate = true,
66
67
  showToggle = true,
68
+ isSortable = true,
67
69
  }: RepeaterProps< Item< T > > ) => {
68
70
  const [ openItem, setOpenItem ] = useState( EMPTY_OPEN_ITEM );
69
71
 
@@ -180,7 +182,7 @@ export const Repeater = < T, >( {
180
182
  }
181
183
 
182
184
  return (
183
- <SortableItem id={ key } key={ `sortable-${ key }` } disabled={ disabled }>
185
+ <SortableItem id={ key } key={ `sortable-${ key }` } disabled={ ! isSortable }>
184
186
  <RepeaterItem
185
187
  disabled={ disabled }
186
188
  propDisabled={ value?.disabled }
@@ -1,10 +1,10 @@
1
1
  import * as React from 'react';
2
- import { type MutableRefObject } from 'react';
2
+ import { type RefObject } from 'react';
3
3
  import { bindPopover, Paper, Popover, type PopupState, TextField } from '@elementor/ui';
4
4
 
5
5
  type Props = {
6
6
  popupState: PopupState;
7
- anchorRef: MutableRefObject< HTMLElement >;
7
+ anchorRef: RefObject< HTMLDivElement | null >;
8
8
  restoreValue: () => void;
9
9
  value: string;
10
10
  onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
@@ -26,7 +26,7 @@ export const TextFieldPopover = ( props: Props ) => {
26
26
  >
27
27
  <Paper
28
28
  sx={ {
29
- width: anchorRef.current.offsetWidth + 'px',
29
+ width: anchorRef.current?.offsetWidth + 'px',
30
30
  borderRadius: 2,
31
31
  p: 1.5,
32
32
  } }
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useState } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { stringPropTypeUtil } from '@elementor/editor-props';
4
4
  import { MenuListItem } from '@elementor/editor-ui';
5
5
  import { ArrowsMoveHorizontalIcon, ArrowsMoveVerticalIcon } from '@elementor/icons';
@@ -38,6 +38,24 @@ export const AspectRatioControl = createControl( ( { label }: { label: string }
38
38
  isCustomSelected ? CUSTOM_RATIO : aspectRatioValue || ''
39
39
  );
40
40
 
41
+ useEffect( () => {
42
+ const isCustomValue =
43
+ aspectRatioValue && ! RATIO_OPTIONS.some( ( option ) => option.value === aspectRatioValue );
44
+
45
+ if ( isCustomValue ) {
46
+ const [ width, height ] = aspectRatioValue.split( '/' );
47
+ setCustomWidth( width || '' );
48
+ setCustomHeight( height || '' );
49
+ setSelectedValue( CUSTOM_RATIO );
50
+ setIsCustom( true );
51
+ } else {
52
+ setSelectedValue( aspectRatioValue || '' );
53
+ setIsCustom( false );
54
+ setCustomWidth( '' );
55
+ setCustomHeight( '' );
56
+ }
57
+ }, [ aspectRatioValue ] );
58
+
41
59
  const handleSelectChange = ( event: SelectChangeEvent< string > ) => {
42
60
  const newValue = event.target.value;
43
61
  const isCustomRatio = newValue === CUSTOM_RATIO;
@@ -71,7 +89,7 @@ export const AspectRatioControl = createControl( ( { label }: { label: string }
71
89
 
72
90
  return (
73
91
  <ControlActions>
74
- <Stack direction="column" pt={ 2 } gap={ 2 }>
92
+ <Stack direction="column" gap={ 2 }>
75
93
  <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
76
94
  <Grid item xs={ 6 }>
77
95
  <ControlLabel>{ label }</ControlLabel>
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type MutableRefObject, useRef } from 'react';
2
+ import { useRef } from 'react';
3
3
  import { backgroundImagePositionOffsetPropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
4
4
  import { MenuListItem } from '@elementor/editor-ui';
5
5
  import { LetterXIcon, LetterYIcon } from '@elementor/icons';
@@ -41,7 +41,7 @@ export const BackgroundImageOverlayPosition = () => {
41
41
  const stringPropContext = useBoundProp( stringPropTypeUtil );
42
42
 
43
43
  const isCustom = !! backgroundImageOffsetContext.value;
44
- const rowRef: MutableRefObject< HTMLElement | undefined > = useRef();
44
+ const rowRef = useRef< HTMLDivElement >( null );
45
45
 
46
46
  const handlePositionChange = ( event: SelectChangeEvent< Positions > ) => {
47
47
  const value = event.target.value || null;
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type MutableRefObject, useRef } from 'react';
2
+ import { useRef } from 'react';
3
3
  import { backgroundImageSizeScalePropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
4
4
  import {
5
5
  ArrowBarBothIcon,
@@ -55,7 +55,7 @@ export const BackgroundImageOverlaySize = () => {
55
55
  const stringPropContext = useBoundProp( stringPropTypeUtil );
56
56
 
57
57
  const isCustom = !! backgroundImageScaleContext.value;
58
- const rowRef: MutableRefObject< HTMLElement | undefined > = useRef();
58
+ const rowRef = useRef< HTMLDivElement >( null );
59
59
 
60
60
  const handleSizeChange = ( size: Sizes | null ) => {
61
61
  if ( size === 'custom' ) {
@@ -12,6 +12,7 @@ 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';
15
16
  import { PopoverContent } from '../../../components/popover-content';
16
17
  import { Repeater } from '../../../components/repeater';
17
18
  import { createControl } from '../../../create-control';
@@ -237,10 +238,14 @@ const ImageOverlayContent = () => {
237
238
  <PropKeyProvider bind={ 'image' }>
238
239
  <Grid container spacing={ 1 } alignItems="center">
239
240
  <Grid item xs={ 12 }>
240
- <ImageControl
241
- resolutionLabel={ __( 'Resolution', 'elementor' ) }
242
- sizes={ backgroundResolutionOptions }
243
- />
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>
244
249
  </Grid>
245
250
  </Grid>
246
251
  </PropKeyProvider>
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type MutableRefObject, useRef } from 'react';
2
+ import { type RefObject, useRef } from 'react';
3
3
  import { boxShadowPropTypeUtil, type PropKey, shadowPropTypeUtil, type ShadowPropValue } from '@elementor/editor-props';
4
4
  import { FormLabel, Grid, type SxProps, type Theme, UnstableColorIndicator } from '@elementor/ui';
5
5
  import { __ } from '@wordpress/i18n';
@@ -49,7 +49,7 @@ const ItemContent = ( { anchorEl, bind }: { anchorEl: HTMLElement | null; bind:
49
49
 
50
50
  const Content = ( { anchorEl }: { anchorEl: HTMLElement | null } ) => {
51
51
  const context = useBoundProp( shadowPropTypeUtil );
52
- const rowRef: MutableRefObject< HTMLElement | undefined >[] = [ useRef(), useRef() ];
52
+ const rowRef: RefObject< HTMLDivElement >[] = [ useRef( null ), useRef( null ) ];
53
53
 
54
54
  return (
55
55
  <PropProvider { ...context }>
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type MutableRefObject, type ReactNode, useId, useRef } from 'react';
2
+ import { type ReactNode, type RefObject, useId, useRef } from 'react';
3
3
  import { type PropKey, type PropTypeUtil, sizePropTypeUtil, type SizePropValue } from '@elementor/editor-props';
4
4
  import { isExperimentActive } from '@elementor/editor-v1-adapters';
5
5
  import { bindPopover, bindToggle, Grid, Popover, Stack, ToggleButton, Tooltip, usePopupState } from '@elementor/ui';
@@ -63,7 +63,7 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
63
63
 
64
64
  const { value: sizeValue, setValue: setSizeValue } = useBoundProp( sizePropTypeUtil );
65
65
 
66
- const rowRefs: MutableRefObject< HTMLElement | undefined >[] = [ useRef(), useRef() ];
66
+ const rowRefs: RefObject< HTMLDivElement >[] = [ useRef( null ), useRef( null ) ];
67
67
 
68
68
  const splitEqualValue = () => {
69
69
  if ( ! sizeValue ) {
@@ -172,13 +172,7 @@ export function EqualUnequalSizesControl< TMultiPropType extends string, TPropVa
172
172
  );
173
173
  }
174
174
 
175
- const MultiSizeValueControl = ( {
176
- item,
177
- rowRef,
178
- }: {
179
- item: Item;
180
- rowRef: MutableRefObject< HTMLElement | undefined >;
181
- } ) => {
175
+ const MultiSizeValueControl = ( { item, rowRef }: { item: Item; rowRef: RefObject< HTMLDivElement > } ) => {
182
176
  const isUsingNestedProps = isExperimentActive( 'e_v_3_30' );
183
177
 
184
178
  return (
@@ -0,0 +1,186 @@
1
+ import * as React from 'react';
2
+ import { useRef } from 'react';
3
+ import {
4
+ blurFilterPropTypeUtil,
5
+ brightnessFilterPropTypeUtil,
6
+ type FilterItemPropValue,
7
+ filterPropTypeUtil,
8
+ type PropKey,
9
+ type PropTypeUtil,
10
+ type SizePropValue,
11
+ } from '@elementor/editor-props';
12
+ import { MenuListItem } from '@elementor/editor-ui';
13
+ import { Box, Grid, Select, type SelectChangeEvent } from '@elementor/ui';
14
+ import { __ } from '@wordpress/i18n';
15
+
16
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
17
+ import { ControlLabel } from '../components/control-label';
18
+ import { PopoverContent } from '../components/popover-content';
19
+ import { PopoverGridContainer } from '../components/popover-grid-container';
20
+ import { Repeater } from '../components/repeater';
21
+ import { createControl } from '../create-control';
22
+ import { defaultUnits } from '../utils/size-control';
23
+ import { SizeControl } from './size-control';
24
+
25
+ type FilterType = FilterItemPropValue[ '$$type' ];
26
+ type FilterValue = FilterItemPropValue[ 'value' ];
27
+
28
+ const DEFAULT_FILTER_KEY: FilterType = 'blur';
29
+
30
+ type FilterItemConfig = {
31
+ defaultValue: FilterValue;
32
+ name: string;
33
+ valueName: string;
34
+ propType: PropTypeUtil< FilterValue, FilterValue >;
35
+ units?: Exclude< SizePropValue[ 'value' ][ 'unit' ], 'custom' | 'auto' >[];
36
+ };
37
+
38
+ const filterConfig: Record< FilterType, FilterItemConfig > = {
39
+ blur: {
40
+ defaultValue: { $$type: 'radius', radius: { $$type: 'size', value: { size: 0, unit: 'px' } } },
41
+ name: __( 'Blur', 'elementor' ),
42
+ valueName: __( 'Radius', 'elementor' ),
43
+ propType: blurFilterPropTypeUtil,
44
+ units: defaultUnits.filter( ( unit ) => unit !== '%' ),
45
+ },
46
+ brightness: {
47
+ defaultValue: { $$type: 'amount', amount: { $$type: 'size', value: { size: 100, unit: '%' } } },
48
+ name: __( 'Brightness', 'elementor' ),
49
+ valueName: __( 'Amount', 'elementor' ),
50
+ propType: brightnessFilterPropTypeUtil,
51
+ units: [ '%' ],
52
+ },
53
+ };
54
+
55
+ const filterKeys = Object.keys( filterConfig ) as FilterType[];
56
+
57
+ const singleSizeFilterNames = filterKeys.filter( ( name ) => {
58
+ const filter = filterConfig[ name as FilterType ].defaultValue;
59
+
60
+ return filter[ filter.$$type ].$$type === 'size';
61
+ } ) as FilterType[];
62
+
63
+ export const FilterRepeaterControl = createControl( () => {
64
+ const { propType, value: filterValues, setValue, disabled } = useBoundProp( filterPropTypeUtil );
65
+
66
+ return (
67
+ <PropProvider propType={ propType } value={ filterValues } setValue={ setValue }>
68
+ <Repeater
69
+ openOnAdd
70
+ disabled={ disabled }
71
+ values={ filterValues ?? [] }
72
+ setValues={ setValue }
73
+ label={ __( 'Filter', 'elementor' ) }
74
+ itemSettings={ {
75
+ Icon: ItemIcon,
76
+ Label: ItemLabel,
77
+ Content: ItemContent,
78
+ initialValues: {
79
+ $$type: DEFAULT_FILTER_KEY,
80
+ value: filterConfig[ DEFAULT_FILTER_KEY ].defaultValue,
81
+ } as FilterItemPropValue,
82
+ } }
83
+ />
84
+ </PropProvider>
85
+ );
86
+ } );
87
+
88
+ const ItemIcon = () => <></>;
89
+
90
+ const ItemLabel = ( props: { value: FilterItemPropValue } ) => {
91
+ const { $$type } = props.value;
92
+
93
+ return singleSizeFilterNames.includes( $$type ) && <SingleSizeItemLabel value={ props.value } />;
94
+ };
95
+
96
+ const SingleSizeItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
97
+ const { $$type, value: sizeValue } = value;
98
+ const { $$type: key } = filterConfig[ $$type ].defaultValue;
99
+ const defaultUnit = filterConfig[ $$type ].defaultValue[ key ].value.unit;
100
+ const { unit, size } = sizeValue[ key ]?.value ?? { unit: defaultUnit, size: 0 };
101
+
102
+ const label = (
103
+ <Box component="span" style={ { textTransform: 'capitalize' } }>
104
+ { value.$$type }:
105
+ </Box>
106
+ );
107
+
108
+ return (
109
+ <Box component="span">
110
+ { label }
111
+ { unit !== 'custom' ? ` ${ size ?? 0 }${ unit ?? defaultUnit }` : size }
112
+ </Box>
113
+ );
114
+ };
115
+
116
+ const ItemContent = ( { bind }: { bind: PropKey } ) => {
117
+ const { value: filterValues, setValue } = useBoundProp( filterPropTypeUtil );
118
+ const itemIndex = parseInt( bind, 10 );
119
+ const item = filterValues?.[ itemIndex ];
120
+
121
+ const handleChange = ( e: SelectChangeEvent< string > ) => {
122
+ const newFilterValues = [ ...filterValues ];
123
+ const filterType = e.target.value as FilterType;
124
+
125
+ newFilterValues[ itemIndex ] = {
126
+ $$type: filterType,
127
+ value: filterConfig[ filterType ].defaultValue,
128
+ } as FilterItemPropValue;
129
+
130
+ setValue( newFilterValues );
131
+ };
132
+
133
+ return (
134
+ <PropKeyProvider bind={ bind }>
135
+ <PopoverContent p={ 1.5 }>
136
+ <PopoverGridContainer>
137
+ <Grid item xs={ 6 }>
138
+ <ControlLabel>{ __( 'Filter', 'elementor' ) }</ControlLabel>
139
+ </Grid>
140
+ <Grid item xs={ 6 }>
141
+ <Select
142
+ sx={ { overflow: 'hidden' } }
143
+ size="tiny"
144
+ value={ item?.$$type ?? DEFAULT_FILTER_KEY }
145
+ onChange={ handleChange }
146
+ fullWidth
147
+ >
148
+ { filterKeys.map( ( filterKey ) => (
149
+ <MenuListItem key={ filterKey } value={ filterKey }>
150
+ { filterConfig[ filterKey ].name }
151
+ </MenuListItem>
152
+ ) ) }
153
+ </Select>
154
+ </Grid>
155
+ </PopoverGridContainer>
156
+ <Content filterType={ item?.$$type } />
157
+ </PopoverContent>
158
+ </PropKeyProvider>
159
+ );
160
+ };
161
+
162
+ const Content = ( { filterType }: { filterType: FilterType } ) => {
163
+ return singleSizeFilterNames.includes( filterType ) && <SingleSizeItemContent filterType={ filterType } />;
164
+ };
165
+
166
+ const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) => {
167
+ const { propType, valueName, defaultValue, units } = filterConfig[ filterType ];
168
+ const { $$type } = defaultValue;
169
+ const context = useBoundProp( propType );
170
+ const rowRef = useRef< HTMLDivElement >( null );
171
+
172
+ return (
173
+ <PropProvider { ...context }>
174
+ <PropKeyProvider bind={ $$type }>
175
+ <PopoverGridContainer ref={ rowRef }>
176
+ <Grid item xs={ 6 }>
177
+ <ControlLabel>{ valueName }</ControlLabel>
178
+ </Grid>
179
+ <Grid item xs={ 6 }>
180
+ <SizeControl anchorRef={ rowRef } units={ units } />
181
+ </Grid>
182
+ </PopoverGridContainer>
183
+ </PropKeyProvider>
184
+ </PropProvider>
185
+ );
186
+ };
@@ -15,11 +15,12 @@ export type FontCategory = {
15
15
 
16
16
  type FontFamilyControlProps = {
17
17
  fontFamilies: FontCategory[];
18
+ sectionWidth: number;
18
19
  };
19
20
 
20
21
  const SIZE = 'tiny';
21
22
 
22
- export const FontFamilyControl = createControl( ( { fontFamilies }: FontFamilyControlProps ) => {
23
+ export const FontFamilyControl = createControl( ( { fontFamilies, sectionWidth }: FontFamilyControlProps ) => {
23
24
  const { value: fontFamily, setValue: setFontFamily, disabled } = useBoundProp( stringPropTypeUtil );
24
25
 
25
26
  const popoverState = usePopupState( { variant: 'popover' } );
@@ -39,7 +40,9 @@ export const FontFamilyControl = createControl( ( { fontFamilies }: FontFamilyCo
39
40
  <Popover
40
41
  disablePortal
41
42
  disableScrollLock
42
- anchorOrigin={ { vertical: 'bottom', horizontal: 'left' } }
43
+ anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
44
+ transformOrigin={ { vertical: 'top', horizontal: 'right' } }
45
+ sx={ { my: 1.5 } }
43
46
  { ...bindPopover( popoverState ) }
44
47
  >
45
48
  <FontFamilySelector
@@ -47,6 +50,7 @@ export const FontFamilyControl = createControl( ( { fontFamilies }: FontFamilyCo
47
50
  fontFamily={ fontFamily }
48
51
  onFontFamilyChange={ setFontFamily }
49
52
  onClose={ popoverState.close }
53
+ sectionWidth={ sectionWidth }
50
54
  />
51
55
  </Popover>
52
56
  </>
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { type MutableRefObject, useRef } from 'react';
2
+ import { type RefObject, useRef } from 'react';
3
3
  import { layoutDirectionPropTypeUtil, type PropKey, sizePropTypeUtil } from '@elementor/editor-props';
4
4
  import { DetachIcon, LinkIcon } from '@elementor/icons';
5
5
  import { Grid, Stack, ToggleButton, Tooltip } from '@elementor/ui';
@@ -19,7 +19,7 @@ export const GapControl = createControl( ( { label }: { label: string } ) => {
19
19
  disabled: directionDisabled,
20
20
  } = useBoundProp( layoutDirectionPropTypeUtil );
21
21
 
22
- const stackRef: MutableRefObject< HTMLElement | undefined > = useRef();
22
+ const stackRef = useRef< HTMLDivElement >( null );
23
23
 
24
24
  const { value: sizeValue, setValue: setSizeValue, disabled: sizeDisabled } = useBoundProp( sizePropTypeUtil );
25
25
 
@@ -96,7 +96,7 @@ const Control = ( {
96
96
  }: {
97
97
  bind: PropKey;
98
98
  isLinked: boolean;
99
- anchorRef: MutableRefObject< HTMLElement | undefined >;
99
+ anchorRef: RefObject< HTMLDivElement >;
100
100
  } ) => {
101
101
  if ( isLinked ) {
102
102
  return <SizeControl anchorRef={ anchorRef } />;
@@ -1,11 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { imagePropTypeUtil } from '@elementor/editor-props';
3
- import { Grid, Stack } from '@elementor/ui';
3
+ import { Stack } from '@elementor/ui';
4
4
  import { type MediaType } from '@elementor/wp-media';
5
- import { __ } from '@wordpress/i18n';
6
5
 
7
6
  import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
8
- import { ControlFormLabel } from '../components/control-form-label';
9
7
  import { createControl } from '../create-control';
10
8
  import { useUnfilteredFilesUpload } from '../hooks/use-unfiltered-files-upload';
11
9
  import { ImageMediaControl } from './image-media-control';
@@ -13,40 +11,29 @@ import { SelectControl } from './select-control';
13
11
 
14
12
  type ImageControlProps = {
15
13
  sizes: { label: string; value: string }[];
16
- resolutionLabel?: string;
17
14
  showMode?: 'all' | 'media' | 'sizes';
18
15
  };
19
16
 
20
- export const ImageControl = createControl(
21
- ( { sizes, resolutionLabel = __( 'Image resolution', 'elementor' ), showMode = 'all' }: ImageControlProps ) => {
22
- const propContext = useBoundProp( imagePropTypeUtil );
17
+ export const ImageControl = createControl( ( { sizes, showMode = 'all' }: ImageControlProps ) => {
18
+ const propContext = useBoundProp( imagePropTypeUtil );
23
19
 
24
- const { data: allowSvgUpload } = useUnfilteredFilesUpload();
25
- const mediaTypes: MediaType[] = allowSvgUpload ? [ 'image', 'svg' ] : [ 'image' ];
20
+ const { data: allowSvgUpload } = useUnfilteredFilesUpload();
21
+ const mediaTypes: MediaType[] = allowSvgUpload ? [ 'image', 'svg' ] : [ 'image' ];
26
22
 
27
- return (
28
- <PropProvider { ...propContext }>
29
- <Stack gap={ 1.5 }>
30
- { [ 'all', 'media' ].includes( showMode ) ? (
31
- <PropKeyProvider bind={ 'src' }>
32
- <ControlFormLabel>{ __( 'Image', 'elementor' ) }</ControlFormLabel>
33
- <ImageMediaControl mediaTypes={ mediaTypes } />
34
- </PropKeyProvider>
35
- ) : null }
36
- { [ 'all', 'sizes' ].includes( showMode ) ? (
37
- <PropKeyProvider bind={ 'size' }>
38
- <Grid container gap={ 1.5 } alignItems="center" flexWrap="nowrap">
39
- <Grid item xs={ 6 }>
40
- <ControlFormLabel>{ resolutionLabel }</ControlFormLabel>
41
- </Grid>
42
- <Grid item xs={ 6 } sx={ { overflow: 'hidden' } }>
43
- <SelectControl options={ sizes } />
44
- </Grid>
45
- </Grid>
46
- </PropKeyProvider>
47
- ) : null }
48
- </Stack>
49
- </PropProvider>
50
- );
51
- }
52
- );
23
+ return (
24
+ <PropProvider { ...propContext }>
25
+ <Stack gap={ 1.5 }>
26
+ { [ 'all', 'media' ].includes( showMode ) ? (
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>
38
+ );
39
+ } );