@elementor/editor-controls 0.35.0 → 1.0.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 (33) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/index.d.mts +24 -13
  3. package/dist/index.d.ts +24 -13
  4. package/dist/index.js +809 -558
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +748 -491
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +5 -4
  9. package/src/bound-prop-context/use-bound-prop.ts +4 -1
  10. package/src/components/control-form-label.tsx +3 -3
  11. package/src/components/control-label.tsx +1 -1
  12. package/src/components/font-family-selector.tsx +8 -10
  13. package/src/components/popover-grid-container.tsx +7 -10
  14. package/src/components/repeater.tsx +2 -4
  15. package/src/components/size-control/size-input.tsx +125 -0
  16. package/src/components/{text-field-inner-selection.tsx → size-control/text-field-inner-selection.tsx} +33 -16
  17. package/src/components/sortable.tsx +4 -2
  18. package/src/components/text-field-popover.tsx +47 -0
  19. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +14 -5
  20. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-size.tsx +9 -4
  21. package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +1 -1
  22. package/src/controls/box-shadow-repeater-control.tsx +11 -9
  23. package/src/controls/equal-unequal-sizes-control.tsx +38 -18
  24. package/src/controls/font-family-control/font-family-control.tsx +3 -1
  25. package/src/controls/gap-control.tsx +20 -7
  26. package/src/controls/image-control.tsx +2 -2
  27. package/src/controls/link-control.tsx +1 -1
  28. package/src/controls/linked-dimensions-control.tsx +71 -83
  29. package/src/controls/size-control.tsx +179 -149
  30. package/src/controls/stroke-control.tsx +9 -6
  31. package/src/hooks/use-size-extended-options.ts +21 -0
  32. package/src/index.ts +2 -1
  33. package/src/utils/size-control.ts +10 -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": "0.35.0",
4
+ "version": "1.0.0",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -41,9 +41,10 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@elementor/editor-current-user": "0.5.0",
44
- "@elementor/editor-elements": "0.8.4",
45
- "@elementor/editor-props": "0.12.1",
46
- "@elementor/editor-ui": "0.10.1",
44
+ "@elementor/editor-elements": "0.8.5",
45
+ "@elementor/editor-props": "0.13.0",
46
+ "@elementor/editor-responsive": "0.13.5",
47
+ "@elementor/editor-ui": "0.11.0",
47
48
  "@elementor/editor-v1-adapters": "0.12.0",
48
49
  "@elementor/env": "0.3.5",
49
50
  "@elementor/http-client": "0.3.0",
@@ -22,7 +22,10 @@ type UseBoundProp< TValue extends PropValue > = {
22
22
  disabled?: boolean;
23
23
  };
24
24
 
25
- export function useBoundProp< T extends PropValue = PropValue >(): PropKeyContextValue< T, PropType >;
25
+ export function useBoundProp< T extends PropValue = PropValue, P extends PropType = PropType >(): PropKeyContextValue<
26
+ T,
27
+ P
28
+ >;
26
29
 
27
30
  export function useBoundProp< TKey extends string, TValue extends PropValue >(
28
31
  propTypeUtil: PropTypeUtil< TKey, TValue >
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import { FormLabel } from '@elementor/ui';
2
+ import { FormLabel, type FormLabelProps } from '@elementor/ui';
3
3
 
4
- export const ControlFormLabel = ( { children }: { children: React.ReactNode } ) => {
5
- return <FormLabel size="tiny">{ children }</FormLabel>;
4
+ export const ControlFormLabel = ( props: FormLabelProps ) => {
5
+ return <FormLabel size="tiny" { ...props } />;
6
6
  };
@@ -7,7 +7,7 @@ import { ControlFormLabel } from './control-form-label';
7
7
 
8
8
  export const ControlLabel = ( { children }: PropsWithChildren< object > ) => {
9
9
  return (
10
- <Stack direction="row" alignItems="center" justifyItems="start" gap={ 1 }>
10
+ <Stack direction="row" alignItems="center" justifyItems="start" gap={ 0.25 }>
11
11
  <ControlFormLabel>{ children }</ControlFormLabel>
12
12
  <ControlAdornments />
13
13
  </Stack>
@@ -1,11 +1,10 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
  import * as React from 'react';
3
- import { type FontCategory } from '@elementor/editor-controls';
4
- import { SearchIcon, TextIcon, XIcon } from '@elementor/icons';
3
+ import { PopoverHeader } from '@elementor/editor-ui';
4
+ import { SearchIcon, TextIcon } from '@elementor/icons';
5
5
  import {
6
6
  Box,
7
7
  Divider,
8
- IconButton,
9
8
  InputAdornment,
10
9
  Link,
11
10
  MenuList,
@@ -20,6 +19,7 @@ import { useVirtualizer } from '@tanstack/react-virtual';
20
19
  import { __ } from '@wordpress/i18n';
21
20
 
22
21
  import { enqueueFont } from '../controls/font-family-control/enqueue-font';
22
+ import { type FontCategory } from '../controls/font-family-control/font-family-control';
23
23
  import { type FontListItem, useFilteredFontFamilies } from '../hooks/use-filtered-font-families';
24
24
 
25
25
  const SIZE = 'tiny';
@@ -52,13 +52,11 @@ export const FontFamilySelector = ( {
52
52
 
53
53
  return (
54
54
  <Stack>
55
- <Stack direction="row" alignItems="center" pl={ 1.5 } pr={ 0.5 } py={ 1.5 }>
56
- <TextIcon fontSize={ SIZE } sx={ { mr: 0.5 } } />
57
- <Typography variant="subtitle2">{ __( 'Font Family', 'elementor' ) }</Typography>
58
- <IconButton size={ SIZE } sx={ { ml: 'auto' } } onClick={ handleClose }>
59
- <XIcon fontSize={ SIZE } />
60
- </IconButton>
61
- </Stack>
55
+ <PopoverHeader
56
+ title={ __( 'Font Family', 'elementor' ) }
57
+ onClose={ handleClose }
58
+ icon={ <TextIcon fontSize={ SIZE } /> }
59
+ />
62
60
 
63
61
  <Box px={ 1.5 } pb={ 1 }>
64
62
  <TextField
@@ -1,4 +1,4 @@
1
- import { type FC, type PropsWithChildren } from 'react';
1
+ import { forwardRef, type PropsWithChildren } from 'react';
2
2
  import * as React from 'react';
3
3
  import { Grid } from '@elementor/ui';
4
4
 
@@ -8,13 +8,10 @@ type PopoverGridContainerProps = PropsWithChildren< {
8
8
  flexWrap?: React.ComponentProps< typeof Grid >[ 'flexWrap' ];
9
9
  } >;
10
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>
11
+ export const PopoverGridContainer = forwardRef(
12
+ ( { gap = 1.5, alignItems = 'center', flexWrap = 'nowrap', children }: PopoverGridContainerProps, ref ) => (
13
+ <Grid container gap={ gap } alignItems={ alignItems } flexWrap={ flexWrap } ref={ ref }>
14
+ { children }
15
+ </Grid>
16
+ )
20
17
  );
@@ -143,8 +143,6 @@ export const Repeater = < T, >( {
143
143
  } );
144
144
  };
145
145
 
146
- const ItemWrapper = disabled ? React.Fragment : SortableItem;
147
-
148
146
  return (
149
147
  <SectionContent>
150
148
  <Stack
@@ -178,7 +176,7 @@ export const Repeater = < T, >( {
178
176
  }
179
177
 
180
178
  return (
181
- <ItemWrapper id={ key } key={ `sortable-${ key }` }>
179
+ <SortableItem id={ key } key={ `sortable-${ key }` } disabled={ disabled }>
182
180
  <RepeaterItem
183
181
  disabled={ disabled }
184
182
  propDisabled={ value?.disabled }
@@ -202,7 +200,7 @@ export const Repeater = < T, >( {
202
200
  <itemSettings.Content { ...props } value={ value } bind={ String( index ) } />
203
201
  ) }
204
202
  </RepeaterItem>
205
- </ItemWrapper>
203
+ </SortableItem>
206
204
  );
207
205
  } ) }
208
206
  </SortableProvider>
@@ -0,0 +1,125 @@
1
+ import * as React from 'react';
2
+ import { useRef } from 'react';
3
+ import { PencilIcon } from '@elementor/icons';
4
+ import { Box, InputAdornment, type PopupState } from '@elementor/ui';
5
+
6
+ import ControlActions from '../../control-actions/control-actions';
7
+ import { type ExtendedOption, isUnitExtendedOption, type Unit } from '../../utils/size-control';
8
+ import { SelectionEndAdornment, TextFieldInnerSelection } from '../size-control/text-field-inner-selection';
9
+
10
+ type SizeInputProps = {
11
+ unit: Unit | ExtendedOption;
12
+ size: number | string;
13
+ placeholder?: string;
14
+ startIcon?: React.ReactNode;
15
+ units: ( Unit | ExtendedOption )[];
16
+ onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
17
+ onFocus?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
18
+ onClick?: ( event: React.MouseEvent< HTMLInputElement > ) => void;
19
+ handleUnitChange: ( unit: Unit | ExtendedOption ) => void;
20
+ handleSizeChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
21
+ popupState: PopupState;
22
+ disabled?: boolean;
23
+ };
24
+
25
+ const RESTRICTED_INPUT_KEYS = [ 'e', 'E', '+', '-' ];
26
+
27
+ export const SizeInput = ( {
28
+ units,
29
+ handleUnitChange,
30
+ handleSizeChange,
31
+ placeholder,
32
+ startIcon,
33
+ onBlur,
34
+ onFocus,
35
+ onClick,
36
+ size,
37
+ unit,
38
+ popupState,
39
+ disabled,
40
+ }: SizeInputProps ) => {
41
+ const unitInputBufferRef = useRef( '' );
42
+ const inputType = isUnitExtendedOption( unit ) ? 'text' : 'number';
43
+ const inputValue = ! isUnitExtendedOption( unit ) && Number.isNaN( size ) ? '' : size ?? '';
44
+
45
+ const handleKeyUp = ( event: React.KeyboardEvent< HTMLInputElement > ) => {
46
+ const { key } = event;
47
+
48
+ if ( ! /^[a-zA-Z%]$/.test( key ) ) {
49
+ return;
50
+ }
51
+
52
+ event.preventDefault();
53
+
54
+ const newChar = key.toLowerCase();
55
+ const updatedBuffer = ( unitInputBufferRef.current + newChar ).slice( -3 );
56
+ unitInputBufferRef.current = updatedBuffer;
57
+
58
+ const matchedUnit =
59
+ units.find( ( u ) => u.includes( updatedBuffer ) ) ||
60
+ units.find( ( u ) => u.startsWith( newChar ) ) ||
61
+ units.find( ( u ) => u.includes( newChar ) );
62
+
63
+ if ( matchedUnit ) {
64
+ handleUnitChange( matchedUnit );
65
+ }
66
+ };
67
+
68
+ const popupAttributes = {
69
+ 'aria-controls': popupState.isOpen ? popupState.popupId : undefined,
70
+ 'aria-haspopup': true,
71
+ };
72
+
73
+ const inputProps = {
74
+ ...popupAttributes,
75
+ autoComplete: 'off',
76
+ onClick,
77
+ onFocus,
78
+ startAdornment: startIcon ? (
79
+ <InputAdornment position="start" disabled={ disabled }>
80
+ { startIcon }
81
+ </InputAdornment>
82
+ ) : undefined,
83
+ endAdornment: (
84
+ <SelectionEndAdornment
85
+ disabled={ disabled }
86
+ options={ units }
87
+ onClick={ handleUnitChange }
88
+ value={ unit }
89
+ alternativeOptionLabels={ {
90
+ custom: <PencilIcon fontSize="small" />,
91
+ } }
92
+ menuItemsAttributes={
93
+ units.includes( 'custom' )
94
+ ? {
95
+ custom: popupAttributes,
96
+ }
97
+ : undefined
98
+ }
99
+ />
100
+ ),
101
+ };
102
+
103
+ return (
104
+ <ControlActions>
105
+ <Box>
106
+ <TextFieldInnerSelection
107
+ disabled={ disabled }
108
+ placeholder={ placeholder }
109
+ type={ inputType }
110
+ value={ inputValue }
111
+ onChange={ handleSizeChange }
112
+ onKeyDown={ ( event ) => {
113
+ if ( RESTRICTED_INPUT_KEYS.includes( event.key ) ) {
114
+ event.preventDefault();
115
+ }
116
+ } }
117
+ onKeyUp={ handleKeyUp }
118
+ onBlur={ onBlur }
119
+ shouldBlockInput={ isUnitExtendedOption( unit ) }
120
+ inputProps={ inputProps }
121
+ />
122
+ </Box>
123
+ </ControlActions>
124
+ );
125
+ };
@@ -2,7 +2,16 @@ import * as React from 'react';
2
2
  import { forwardRef, useId } from 'react';
3
3
  import { type PropValue } from '@elementor/editor-props';
4
4
  import { MenuListItem } from '@elementor/editor-ui';
5
- import { bindMenu, bindTrigger, Button, InputAdornment, Menu, TextField, usePopupState } from '@elementor/ui';
5
+ import {
6
+ bindMenu,
7
+ bindTrigger,
8
+ Button,
9
+ InputAdornment,
10
+ Menu,
11
+ TextField,
12
+ type TextFieldProps,
13
+ usePopupState,
14
+ } from '@elementor/ui';
6
15
 
7
16
  type TextFieldInnerSelectionProps = {
8
17
  placeholder?: string;
@@ -12,8 +21,10 @@ type TextFieldInnerSelectionProps = {
12
21
  onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
13
22
  onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
14
23
  onKeyUp?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
15
- endAdornment: React.ReactNode;
16
- startAdornment?: React.ReactNode;
24
+ shouldBlockInput?: boolean;
25
+ inputProps: TextFieldProps[ 'InputProps' ] & {
26
+ endAdornment: React.JSX.Element;
27
+ };
17
28
  disabled?: boolean;
18
29
  };
19
30
 
@@ -27,8 +38,8 @@ export const TextFieldInnerSelection = forwardRef(
27
38
  onBlur,
28
39
  onKeyDown,
29
40
  onKeyUp,
30
- endAdornment,
31
- startAdornment,
41
+ shouldBlockInput = false,
42
+ inputProps,
32
43
  disabled,
33
44
  }: TextFieldInnerSelectionProps,
34
45
  ref
@@ -36,20 +47,18 @@ export const TextFieldInnerSelection = forwardRef(
36
47
  return (
37
48
  <TextField
38
49
  ref={ ref }
50
+ sx={ { input: { cursor: shouldBlockInput ? 'default !important' : undefined } } }
39
51
  size="tiny"
40
52
  fullWidth
41
- type={ type }
53
+ type={ shouldBlockInput ? undefined : type }
42
54
  value={ value }
55
+ onChange={ shouldBlockInput ? undefined : onChange }
56
+ onKeyDown={ shouldBlockInput ? undefined : onKeyDown }
57
+ onKeyUp={ shouldBlockInput ? undefined : onKeyUp }
43
58
  disabled={ disabled }
44
- onChange={ onChange }
45
- onKeyDown={ onKeyDown }
46
- onKeyUp={ onKeyUp }
47
59
  onBlur={ onBlur }
48
60
  placeholder={ placeholder }
49
- InputProps={ {
50
- endAdornment,
51
- startAdornment,
52
- } }
61
+ InputProps={ inputProps }
53
62
  />
54
63
  );
55
64
  }
@@ -59,13 +68,17 @@ type SelectionEndAdornmentProps< T extends string > = {
59
68
  options: T[];
60
69
  onClick: ( value: T ) => void;
61
70
  value: T;
71
+ alternativeOptionLabels?: { [ key in T ]?: React.ReactNode };
72
+ menuItemsAttributes?: { [ key in T ]?: Record< string, unknown > };
62
73
  disabled?: boolean;
63
74
  };
64
75
 
65
76
  export const SelectionEndAdornment = < T extends string >( {
66
77
  options,
78
+ alternativeOptionLabels = {} as Record< T, React.ReactNode >,
67
79
  onClick,
68
80
  value,
81
+ menuItemsAttributes = {},
69
82
  disabled,
70
83
  }: SelectionEndAdornmentProps< T > ) => {
71
84
  const popupState = usePopupState( {
@@ -87,13 +100,17 @@ export const SelectionEndAdornment = < T extends string >( {
87
100
  sx={ { font: 'inherit', minWidth: 'initial', textTransform: 'uppercase' } }
88
101
  { ...bindTrigger( popupState ) }
89
102
  >
90
- { value }
103
+ { alternativeOptionLabels[ value ] ?? value }
91
104
  </Button>
92
105
 
93
106
  <Menu MenuListProps={ { dense: true } } { ...bindMenu( popupState ) }>
94
107
  { options.map( ( option, index ) => (
95
- <MenuListItem key={ option } onClick={ () => handleMenuItemClick( index ) }>
96
- { option.toUpperCase() }
108
+ <MenuListItem
109
+ key={ option }
110
+ onClick={ () => handleMenuItemClick( index ) }
111
+ { ...menuItemsAttributes?.[ option ] }
112
+ >
113
+ { alternativeOptionLabels[ option ] ?? option.toUpperCase() }
97
114
  </MenuListItem>
98
115
  ) ) }
99
116
  </Menu>
@@ -23,12 +23,14 @@ export const SortableProvider = < T extends number >( props: UnstableSortablePro
23
23
  type SortableItemProps = {
24
24
  id: UnstableSortableItemProps[ 'id' ];
25
25
  children: React.ReactNode;
26
+ disabled?: boolean;
26
27
  };
27
28
 
28
- export const SortableItem = ( { id, children }: SortableItemProps ): React.ReactNode => {
29
+ export const SortableItem = ( { id, children, disabled }: SortableItemProps ): React.ReactNode => {
29
30
  return (
30
31
  <UnstableSortableItem
31
32
  id={ id }
33
+ disabled={ disabled }
32
34
  render={ ( {
33
35
  itemProps,
34
36
  triggerProps,
@@ -39,7 +41,7 @@ export const SortableItem = ( { id, children }: SortableItemProps ): React.React
39
41
  }: UnstableSortableItemRenderProps ) => {
40
42
  return (
41
43
  <StyledListItem { ...itemProps } style={ itemStyle }>
42
- <SortableTrigger { ...triggerProps } style={ triggerStyle } />
44
+ { ! disabled && <SortableTrigger { ...triggerProps } style={ triggerStyle } /> }
43
45
  { children }
44
46
  { showDropIndication && <StyledDivider style={ dropIndicationStyle } /> }
45
47
  </StyledListItem>
@@ -0,0 +1,47 @@
1
+ import * as React from 'react';
2
+ import { type MutableRefObject } from 'react';
3
+ import { bindPopover, Paper, Popover, type PopupState, TextField } from '@elementor/ui';
4
+
5
+ type Props = {
6
+ popupState: PopupState;
7
+ anchorRef: MutableRefObject< HTMLElement >;
8
+ restoreValue: () => void;
9
+ value: string;
10
+ onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
11
+ };
12
+
13
+ export const TextFieldPopover = ( props: Props ) => {
14
+ const { popupState, restoreValue, anchorRef, value, onChange } = props;
15
+
16
+ return (
17
+ <Popover
18
+ disablePortal
19
+ { ...bindPopover( popupState ) }
20
+ anchorOrigin={ { vertical: 'bottom', horizontal: 'center' } }
21
+ transformOrigin={ { vertical: 'top', horizontal: 'center' } }
22
+ onClose={ () => {
23
+ restoreValue();
24
+ popupState.close();
25
+ } }
26
+ >
27
+ <Paper
28
+ sx={ {
29
+ width: anchorRef.current.offsetWidth + 'px',
30
+ borderRadius: 2,
31
+ p: 1.5,
32
+ } }
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
+ </Popover>
46
+ );
47
+ };
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { type MutableRefObject, useRef } from 'react';
2
3
  import { backgroundImagePositionOffsetPropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
3
4
  import { MenuListItem } from '@elementor/editor-ui';
4
5
  import { LetterXIcon, LetterYIcon } from '@elementor/icons';
@@ -40,6 +41,7 @@ export const BackgroundImageOverlayPosition = () => {
40
41
  const stringPropContext = useBoundProp( stringPropTypeUtil );
41
42
 
42
43
  const isCustom = !! backgroundImageOffsetContext.value;
44
+ const rowRef: MutableRefObject< HTMLElement | undefined > = useRef();
43
45
 
44
46
  const handlePositionChange = ( event: SelectChangeEvent< Positions > ) => {
45
47
  const value = event.target.value || null;
@@ -60,10 +62,11 @@ export const BackgroundImageOverlayPosition = () => {
60
62
  </Grid>
61
63
  <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end', overflow: 'hidden' } }>
62
64
  <Select
65
+ fullWidth
63
66
  size="tiny"
64
- value={ ( backgroundImageOffsetContext.value ? 'custom' : stringPropContext.value ) ?? '' }
65
67
  onChange={ handlePositionChange }
66
- fullWidth
68
+ disabled={ stringPropContext.disabled }
69
+ value={ ( backgroundImageOffsetContext.value ? 'custom' : stringPropContext.value ) ?? '' }
67
70
  >
68
71
  { backgroundPositionOptions.map( ( { label, value } ) => (
69
72
  <MenuListItem key={ value } value={ value ?? '' }>
@@ -77,15 +80,21 @@ export const BackgroundImageOverlayPosition = () => {
77
80
  { isCustom ? (
78
81
  <PropProvider { ...backgroundImageOffsetContext }>
79
82
  <Grid item xs={ 12 }>
80
- <Grid container spacing={ 1.5 }>
83
+ <Grid container spacing={ 1.5 } ref={ rowRef }>
81
84
  <Grid item xs={ 6 }>
82
85
  <PropKeyProvider bind={ 'x' }>
83
- <SizeControl startIcon={ <LetterXIcon fontSize={ 'tiny' } /> } />
86
+ <SizeControl
87
+ startIcon={ <LetterXIcon fontSize={ 'tiny' } /> }
88
+ anchorRef={ rowRef }
89
+ />
84
90
  </PropKeyProvider>
85
91
  </Grid>
86
92
  <Grid item xs={ 6 }>
87
93
  <PropKeyProvider bind={ 'y' }>
88
- <SizeControl startIcon={ <LetterYIcon fontSize={ 'tiny' } /> } />
94
+ <SizeControl
95
+ startIcon={ <LetterYIcon fontSize={ 'tiny' } /> }
96
+ anchorRef={ rowRef }
97
+ />
89
98
  </PropKeyProvider>
90
99
  </Grid>
91
100
  </Grid>
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { type MutableRefObject, useRef } from 'react';
2
3
  import { backgroundImageSizeScalePropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
3
4
  import {
4
5
  ArrowBarBothIcon,
@@ -54,6 +55,7 @@ export const BackgroundImageOverlaySize = () => {
54
55
  const stringPropContext = useBoundProp( stringPropTypeUtil );
55
56
 
56
57
  const isCustom = !! backgroundImageScaleContext.value;
58
+ const rowRef: MutableRefObject< HTMLElement | undefined > = useRef();
57
59
 
58
60
  const handleSizeChange = ( size: Sizes | null ) => {
59
61
  if ( size === 'custom' ) {
@@ -74,23 +76,25 @@ export const BackgroundImageOverlaySize = () => {
74
76
  <ControlToggleButtonGroup
75
77
  exclusive
76
78
  items={ sizeControlOptions }
79
+ onChange={ handleSizeChange }
80
+ disabled={ stringPropContext.disabled }
77
81
  value={
78
82
  ( backgroundImageScaleContext.value ? 'custom' : stringPropContext.value ) as Sizes
79
83
  }
80
- onChange={ handleSizeChange }
81
84
  />
82
85
  </Grid>
83
86
  </PopoverGridContainer>
84
87
  </Grid>
85
88
  { isCustom ? (
86
89
  <PropProvider { ...backgroundImageScaleContext }>
87
- <Grid item xs={ 12 }>
90
+ <Grid item xs={ 12 } ref={ rowRef }>
88
91
  <PopoverGridContainer>
89
92
  <Grid item xs={ 6 }>
90
93
  <PropKeyProvider bind={ 'width' }>
91
94
  <SizeControl
92
95
  startIcon={ <ArrowsMoveHorizontalIcon fontSize={ 'tiny' } /> }
93
- extendedValues={ [ 'auto' ] }
96
+ extendedOptions={ [ 'auto' ] }
97
+ anchorRef={ rowRef }
94
98
  />
95
99
  </PropKeyProvider>
96
100
  </Grid>
@@ -98,7 +102,8 @@ export const BackgroundImageOverlaySize = () => {
98
102
  <PropKeyProvider bind={ 'height' }>
99
103
  <SizeControl
100
104
  startIcon={ <ArrowsMoveVerticalIcon fontSize={ 'tiny' } /> }
101
- extendedValues={ [ 'auto' ] }
105
+ extendedOptions={ [ 'auto' ] }
106
+ anchorRef={ rowRef }
102
107
  />
103
108
  </PropKeyProvider>
104
109
  </Grid>
@@ -74,7 +74,7 @@ export const BackgroundOverlayRepeaterControl = createControl( () => {
74
74
  const { propType, value: overlayValues, setValue, disabled } = useBoundProp( backgroundOverlayPropTypeUtil );
75
75
 
76
76
  return (
77
- <PropProvider propType={ propType } value={ overlayValues } setValue={ setValue }>
77
+ <PropProvider propType={ propType } value={ overlayValues } setValue={ setValue } disabled={ disabled }>
78
78
  <Repeater
79
79
  openOnAdd
80
80
  disabled={ disabled }
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { type MutableRefObject, useRef } from 'react';
2
3
  import { boxShadowPropTypeUtil, type PropKey, shadowPropTypeUtil, type ShadowPropValue } from '@elementor/editor-props';
3
4
  import { FormLabel, Grid, type SxProps, type Theme, UnstableColorIndicator } from '@elementor/ui';
4
5
  import { __ } from '@wordpress/i18n';
@@ -16,7 +17,7 @@ export const BoxShadowRepeaterControl = createControl( () => {
16
17
  const { propType, value, setValue, disabled } = useBoundProp( boxShadowPropTypeUtil );
17
18
 
18
19
  return (
19
- <PropProvider propType={ propType } value={ value } setValue={ setValue }>
20
+ <PropProvider propType={ propType } value={ value } setValue={ setValue } disabled={ disabled }>
20
21
  <Repeater
21
22
  openOnAdd
22
23
  disabled={ disabled }
@@ -47,10 +48,11 @@ const ItemContent = ( { anchorEl, bind }: { anchorEl: HTMLElement | null; bind:
47
48
  };
48
49
 
49
50
  const Content = ( { anchorEl }: { anchorEl: HTMLElement | null } ) => {
50
- const { propType, value, setValue } = useBoundProp( shadowPropTypeUtil );
51
+ const context = useBoundProp( shadowPropTypeUtil );
52
+ const rowRef: MutableRefObject< HTMLElement | undefined >[] = [ useRef(), useRef() ];
51
53
 
52
54
  return (
53
- <PropProvider propType={ propType } value={ value } setValue={ setValue }>
55
+ <PropProvider { ...context }>
54
56
  <PopoverContent p={ 1.5 }>
55
57
  <PopoverGridContainer>
56
58
  <Control bind="color" label={ __( 'Color', 'elementor' ) }>
@@ -65,20 +67,20 @@ const Content = ( { anchorEl }: { anchorEl: HTMLElement | null } ) => {
65
67
  />
66
68
  </Control>
67
69
  </PopoverGridContainer>
68
- <PopoverGridContainer>
70
+ <PopoverGridContainer ref={ rowRef[ 0 ] }>
69
71
  <Control bind="hOffset" label={ __( 'Horizontal', 'elementor' ) }>
70
- <SizeControl />
72
+ <SizeControl anchorRef={ rowRef[ 0 ] } />
71
73
  </Control>
72
74
  <Control bind="vOffset" label={ __( 'Vertical', 'elementor' ) }>
73
- <SizeControl />
75
+ <SizeControl anchorRef={ rowRef[ 0 ] } />
74
76
  </Control>
75
77
  </PopoverGridContainer>
76
- <PopoverGridContainer>
78
+ <PopoverGridContainer ref={ rowRef[ 1 ] }>
77
79
  <Control bind="blur" label={ __( 'Blur', 'elementor' ) }>
78
- <SizeControl />
80
+ <SizeControl anchorRef={ rowRef[ 1 ] } />
79
81
  </Control>
80
82
  <Control bind="spread" label={ __( 'Spread', 'elementor' ) }>
81
- <SizeControl />
83
+ <SizeControl anchorRef={ rowRef[ 1 ] } />
82
84
  </Control>
83
85
  </PopoverGridContainer>
84
86
  </PopoverContent>