@elementor/editor-controls 3.33.0-98 → 3.35.0-324

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 (94) hide show
  1. package/dist/index.d.mts +276 -85
  2. package/dist/index.d.ts +276 -85
  3. package/dist/index.js +2491 -1783
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +2304 -1592
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +31 -17
  8. package/src/bound-prop-context/prop-context.tsx +7 -1
  9. package/src/bound-prop-context/use-bound-prop.ts +19 -5
  10. package/src/components/autocomplete.tsx +34 -3
  11. package/src/components/conditional-control-infotip.tsx +64 -0
  12. package/src/components/{unstable-repeater → control-repeater}/actions/disable-item-action.tsx +2 -2
  13. package/src/components/{unstable-repeater → control-repeater}/actions/duplicate-item-action.tsx +10 -4
  14. package/src/components/{unstable-repeater → control-repeater}/actions/remove-item-action.tsx +2 -2
  15. package/src/components/control-repeater/context/item-context.tsx +8 -0
  16. package/src/components/{unstable-repeater → control-repeater}/context/repeater-context.tsx +24 -15
  17. package/src/components/control-repeater/control-repeater.tsx +29 -0
  18. package/src/components/{unstable-repeater → control-repeater}/index.ts +1 -2
  19. package/src/components/{unstable-repeater → control-repeater}/items/edit-item-popover.tsx +6 -20
  20. package/src/components/control-repeater/items/item.tsx +75 -0
  21. package/src/components/{unstable-repeater → control-repeater}/items/items-container.tsx +8 -13
  22. package/src/components/{unstable-repeater → control-repeater}/locations.ts +0 -4
  23. package/src/components/{unstable-repeater → control-repeater}/types.ts +1 -2
  24. package/src/components/control-toggle-button-group.tsx +79 -69
  25. package/src/components/enable-unfiltered-modal.tsx +1 -26
  26. package/src/components/icon-buttons/clear-icon-button.tsx +23 -0
  27. package/src/components/inline-editor-toolbar.tsx +137 -0
  28. package/src/components/inline-editor.tsx +111 -0
  29. package/src/components/item-selector.tsx +10 -4
  30. package/src/components/{unstable-repeater/header/header.tsx → repeater/repeater-header.tsx} +4 -12
  31. package/src/components/repeater/repeater-popover.tsx +19 -0
  32. package/src/components/repeater/repeater-tag.tsx +16 -0
  33. package/src/components/repeater/repeater.tsx +405 -0
  34. package/src/components/{sortable.tsx → repeater/sortable.tsx} +1 -1
  35. package/src/components/size-control/size-input.tsx +20 -14
  36. package/src/components/size-control/text-field-inner-selection.tsx +15 -2
  37. package/src/control-adornments/control-adornments-context.tsx +5 -4
  38. package/src/control-replacements.tsx +12 -47
  39. package/src/controls/background-control/background-control.tsx +43 -12
  40. package/src/controls/background-control/background-gradient-color-control.tsx +5 -8
  41. package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +18 -13
  42. package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +25 -16
  43. package/src/controls/box-shadow-repeater-control.tsx +38 -21
  44. package/src/controls/color-control.tsx +3 -1
  45. package/src/controls/date-time-control.tsx +108 -0
  46. package/src/controls/filter-control/drop-shadow/drop-shadow-item-content.tsx +1 -0
  47. package/src/controls/filter-control/drop-shadow/drop-shadow-item-label.tsx +10 -6
  48. package/src/controls/filter-control/filter-content.tsx +1 -1
  49. package/src/controls/filter-control/filter-repeater-control.tsx +24 -21
  50. package/src/controls/filter-control/single-size/single-size-item-content.tsx +1 -1
  51. package/src/controls/filter-control/single-size/single-size-item-label.tsx +2 -1
  52. package/src/controls/font-family-control/font-family-control.tsx +66 -55
  53. package/src/controls/html-tag-control.tsx +90 -0
  54. package/src/controls/image-media-control.tsx +2 -2
  55. package/src/controls/inline-editing-control.tsx +18 -0
  56. package/src/controls/key-value-control.tsx +8 -2
  57. package/src/controls/link-control.tsx +23 -123
  58. package/src/controls/query-control.tsx +168 -0
  59. package/src/controls/repeatable-control.tsx +62 -27
  60. package/src/controls/select-control-wrapper.tsx +57 -0
  61. package/src/controls/select-control.tsx +9 -5
  62. package/src/controls/selection-size-control.tsx +13 -2
  63. package/src/controls/size-control.tsx +43 -25
  64. package/src/controls/svg-media-control.tsx +33 -10
  65. package/src/controls/text-area-control.tsx +5 -1
  66. package/src/controls/text-control.tsx +5 -0
  67. package/src/controls/toggle-control.tsx +11 -2
  68. package/src/controls/transform-control/functions/axis-row.tsx +1 -0
  69. package/src/controls/transform-control/transform-icon.tsx +2 -2
  70. package/src/controls/transform-control/transform-label.tsx +15 -32
  71. package/src/controls/transform-control/transform-repeater-control.tsx +42 -36
  72. package/src/controls/transform-control/{transform-base-control.tsx → transform-settings-control.tsx} +2 -2
  73. package/src/controls/transform-control/use-transform-tabs-history.tsx +1 -1
  74. package/src/controls/transition-control/data.ts +16 -1
  75. package/src/controls/transition-control/trainsition-events.ts +2 -2
  76. package/src/controls/transition-control/transition-repeater-control.tsx +137 -13
  77. package/src/controls/transition-control/transition-selector.tsx +37 -14
  78. package/src/controls/url-control.tsx +21 -16
  79. package/src/create-control.tsx +3 -2
  80. package/src/hooks/use-filtered-items-list.ts +3 -2
  81. package/src/hooks/use-repeatable-control-context.ts +3 -0
  82. package/src/hooks/use-sync-external-state.tsx +0 -1
  83. package/src/index.ts +21 -5
  84. package/src/utils/convert-toggle-options-to-atomic.tsx +33 -0
  85. package/src/utils/escape-html-attr.ts +11 -0
  86. package/src/components/css-code-editor/css-editor.styles.ts +0 -52
  87. package/src/components/css-code-editor/css-editor.tsx +0 -142
  88. package/src/components/css-code-editor/css-validation.ts +0 -75
  89. package/src/components/css-code-editor/resize-handle.tsx +0 -55
  90. package/src/components/css-code-editor/visual-content-change-protection.ts +0 -69
  91. package/src/components/repeater.tsx +0 -343
  92. package/src/components/unstable-repeater/items/item.tsx +0 -77
  93. package/src/components/unstable-repeater/unstable-repeater.tsx +0 -26
  94. /package/src/components/{unstable-repeater → control-repeater}/actions/tooltip-add-item-action.tsx +0 -0
@@ -7,28 +7,59 @@ import { PropKeyProvider, PropProvider, useBoundProp } from '../../bound-prop-co
7
7
  import { ControlLabel } from '../../components/control-label';
8
8
  import { createControl } from '../../create-control';
9
9
  import { ColorControl } from '../color-control';
10
+ import { SelectControl } from '../select-control';
10
11
  import { BackgroundOverlayRepeaterControl } from './background-overlay/background-overlay-repeater-control';
11
12
 
13
+ const clipOptions = [
14
+ { label: __( 'Full element', 'elementor' ), value: 'border-box' },
15
+ { label: __( 'Padding edges', 'elementor' ), value: 'padding-box' },
16
+ { label: __( 'Content edges', 'elementor' ), value: 'content-box' },
17
+ { label: __( 'Text', 'elementor' ), value: 'text' },
18
+ ];
19
+
20
+ const colorLabel = __( 'Color', 'elementor' );
21
+ const clipLabel = __( 'Clipping', 'elementor' );
22
+
12
23
  export const BackgroundControl = createControl( () => {
13
24
  const propContext = useBoundProp( backgroundPropTypeUtil );
14
25
 
15
- const colorLabel = __( 'Color', 'elementor' );
16
-
17
26
  return (
18
27
  <PropProvider { ...propContext }>
19
28
  <PropKeyProvider bind="background-overlay">
20
29
  <BackgroundOverlayRepeaterControl />
21
30
  </PropKeyProvider>
22
- <PropKeyProvider bind="color">
23
- <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
24
- <Grid item xs={ 6 }>
25
- <ControlLabel>{ colorLabel }</ControlLabel>
26
- </Grid>
27
- <Grid item xs={ 6 }>
28
- <ColorControl />
29
- </Grid>
30
- </Grid>
31
- </PropKeyProvider>
31
+ <BackgroundColorField />
32
+ <BackgroundClipField />
32
33
  </PropProvider>
33
34
  );
34
35
  } );
36
+
37
+ const BackgroundColorField = () => {
38
+ return (
39
+ <PropKeyProvider bind="color">
40
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
41
+ <Grid item xs={ 6 }>
42
+ <ControlLabel>{ colorLabel }</ControlLabel>
43
+ </Grid>
44
+ <Grid item xs={ 6 }>
45
+ <ColorControl />
46
+ </Grid>
47
+ </Grid>
48
+ </PropKeyProvider>
49
+ );
50
+ };
51
+
52
+ const BackgroundClipField = () => {
53
+ return (
54
+ <PropKeyProvider bind="clip">
55
+ <Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
56
+ <Grid item xs={ 6 }>
57
+ <ControlLabel>{ clipLabel }</ControlLabel>
58
+ </Grid>
59
+ <Grid item xs={ 6 }>
60
+ <SelectControl options={ clipOptions } />
61
+ </Grid>
62
+ </Grid>
63
+ </PropKeyProvider>
64
+ );
65
+ };
@@ -15,7 +15,6 @@ import {
15
15
  import { UnstableGradientBox } from '@elementor/ui';
16
16
 
17
17
  import { useBoundProp } from '../../bound-prop-context';
18
- import ControlActions from '../../control-actions/control-actions';
19
18
  import { createControl } from '../../create-control';
20
19
 
21
20
  export type ColorStop = TransformablePropValue<
@@ -74,13 +73,11 @@ export const BackgroundGradientColorControl = createControl( () => {
74
73
  };
75
74
 
76
75
  return (
77
- <ControlActions>
78
- <UnstableGradientBox
79
- sx={ { width: 'auto', padding: 1.5 } }
80
- value={ normalizeValue() }
81
- onChange={ handleChange }
82
- />
83
- </ControlActions>
76
+ <UnstableGradientBox
77
+ sx={ { width: 'auto', padding: 1.5 } }
78
+ value={ normalizeValue() }
79
+ onChange={ handleChange }
80
+ />
84
81
  );
85
82
  } );
86
83
 
@@ -9,6 +9,7 @@ import { __ } from '@wordpress/i18n';
9
9
  import { PropKeyProvider, PropProvider, useBoundProp } from '../../../../bound-prop-context';
10
10
  import { ControlFormLabel } from '../../../../components/control-form-label';
11
11
  import { PopoverGridContainer } from '../../../../components/popover-grid-container';
12
+ import ControlActions from '../../../../control-actions/control-actions';
12
13
  import { SizeControl } from '../../../size-control';
13
14
 
14
15
  type Positions =
@@ -61,19 +62,23 @@ export const BackgroundImageOverlayPosition = () => {
61
62
  <ControlFormLabel>{ __( 'Position', 'elementor' ) }</ControlFormLabel>
62
63
  </Grid>
63
64
  <Grid item xs={ 6 } sx={ { display: 'flex', justifyContent: 'flex-end', overflow: 'hidden' } }>
64
- <Select
65
- fullWidth
66
- size="tiny"
67
- onChange={ handlePositionChange }
68
- disabled={ stringPropContext.disabled }
69
- value={ ( backgroundImageOffsetContext.value ? 'custom' : stringPropContext.value ) ?? '' }
70
- >
71
- { backgroundPositionOptions.map( ( { label, value } ) => (
72
- <MenuListItem key={ value } value={ value ?? '' }>
73
- { label }
74
- </MenuListItem>
75
- ) ) }
76
- </Select>
65
+ <ControlActions>
66
+ <Select
67
+ fullWidth
68
+ size="tiny"
69
+ onChange={ handlePositionChange }
70
+ disabled={ stringPropContext.disabled }
71
+ value={
72
+ ( backgroundImageOffsetContext.value ? 'custom' : stringPropContext.value ) ?? ''
73
+ }
74
+ >
75
+ { backgroundPositionOptions.map( ( { label, value } ) => (
76
+ <MenuListItem key={ value } value={ value ?? '' }>
77
+ { label }
78
+ </MenuListItem>
79
+ ) ) }
80
+ </Select>
81
+ </ControlActions>
77
82
  </Grid>
78
83
  </PopoverGridContainer>
79
84
  </Grid>
@@ -11,15 +11,16 @@ 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 { ControlRepeater, ItemsContainer, TooltipAddItemAction } from '../../../components/control-repeater';
15
+ import { DisableItemAction } from '../../../components/control-repeater/actions/disable-item-action';
16
+ import { DuplicateItemAction } from '../../../components/control-repeater/actions/duplicate-item-action';
17
+ import { RemoveItemAction } from '../../../components/control-repeater/actions/remove-item-action';
18
+ import { useRepeaterContext } from '../../../components/control-repeater/context/repeater-context';
19
+ import { EditItemPopover } from '../../../components/control-repeater/items/edit-item-popover';
20
+ import { Item } from '../../../components/control-repeater/items/item';
21
+ import { type CollectionPropUtil, type RepeatablePropValue } from '../../../components/control-repeater/types';
14
22
  import { PopoverContent } from '../../../components/popover-content';
15
- import { Header, ItemsContainer, TooltipAddItemAction, UnstableRepeater } from '../../../components/unstable-repeater';
16
- import { DisableItemAction } from '../../../components/unstable-repeater/actions/disable-item-action';
17
- import { DuplicateItemAction } from '../../../components/unstable-repeater/actions/duplicate-item-action';
18
- import { RemoveItemAction } from '../../../components/unstable-repeater/actions/remove-item-action';
19
- import { useRepeaterContext } from '../../../components/unstable-repeater/context/repeater-context';
20
- import { EditItemPopover } from '../../../components/unstable-repeater/items/edit-item-popover';
21
- import { Item } from '../../../components/unstable-repeater/items/item';
22
- import { type CollectionPropUtil, type RepeatablePropValue } from '../../../components/unstable-repeater/types';
23
+ import { RepeaterHeader } from '../../../components/repeater/repeater-header';
23
24
  import { createControl } from '../../../create-control';
24
25
  import { env } from '../../../env';
25
26
  import { ColorControl } from '../../color-control';
@@ -81,22 +82,30 @@ export const BackgroundOverlayRepeaterControl = createControl( () => {
81
82
 
82
83
  return (
83
84
  <PropProvider propType={ propType } value={ overlayValues } setValue={ setValue }>
84
- <UnstableRepeater
85
+ <ControlRepeater
85
86
  initial={ getInitialBackgroundOverlay() as RepeatablePropValue }
86
87
  propTypeUtil={ backgroundOverlayPropTypeUtil as CollectionPropUtil< RepeatablePropValue > }
87
88
  >
88
- <Header label={ __( 'Overlay', 'elementor' ) }>
89
+ <RepeaterHeader label={ __( 'Overlay', 'elementor' ) }>
89
90
  <TooltipAddItemAction newItemIndex={ 0 } />
90
- </Header>
91
- <ItemsContainer itemTemplate={ <Item Icon={ ItemIcon } Label={ ItemLabel } /> }>
92
- <DuplicateItemAction />
93
- <DisableItemAction />
94
- <RemoveItemAction />
91
+ </RepeaterHeader>
92
+ <ItemsContainer>
93
+ <Item
94
+ Icon={ ItemIcon }
95
+ Label={ ItemLabel }
96
+ actions={
97
+ <>
98
+ <DuplicateItemAction />
99
+ <DisableItemAction />
100
+ <RemoveItemAction />
101
+ </>
102
+ }
103
+ />
95
104
  </ItemsContainer>
96
105
  <EditItemPopover>
97
106
  <ItemContent />
98
107
  </EditItemPopover>
99
- </UnstableRepeater>
108
+ </ControlRepeater>
100
109
  </PropProvider>
101
110
  );
102
111
  } );
@@ -5,37 +5,46 @@ import { FormLabel, Grid, styled, type SxProps, type Theme, UnstableColorIndicat
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
7
  import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
8
+ import { ControlRepeater, Item, ItemsContainer, TooltipAddItemAction } from '../components/control-repeater';
9
+ import { DisableItemAction } from '../components/control-repeater/actions/disable-item-action';
10
+ import { DuplicateItemAction } from '../components/control-repeater/actions/duplicate-item-action';
11
+ import { RemoveItemAction } from '../components/control-repeater/actions/remove-item-action';
12
+ import { useRepeaterContext } from '../components/control-repeater/context/repeater-context';
13
+ import { EditItemPopover } from '../components/control-repeater/items/edit-item-popover';
8
14
  import { PopoverContent } from '../components/popover-content';
9
15
  import { PopoverGridContainer } from '../components/popover-grid-container';
10
- import { Header, Item, ItemsContainer, TooltipAddItemAction, UnstableRepeater } from '../components/unstable-repeater';
11
- import { DisableItemAction } from '../components/unstable-repeater/actions/disable-item-action';
12
- import { DuplicateItemAction } from '../components/unstable-repeater/actions/duplicate-item-action';
13
- import { RemoveItemAction } from '../components/unstable-repeater/actions/remove-item-action';
14
- import { useRepeaterContext } from '../components/unstable-repeater/context/repeater-context';
15
- import { EditItemPopover } from '../components/unstable-repeater/items/edit-item-popover';
16
+ import { RepeaterHeader } from '../components/repeater/repeater-header';
16
17
  import { createControl } from '../create-control';
17
18
  import { ColorControl } from './color-control';
18
19
  import { SelectControl } from './select-control';
19
- import { SizeControl } from './size-control';
20
+ import { CUSTOM_SIZE_LABEL, SizeControl } from './size-control';
20
21
 
21
22
  export const BoxShadowRepeaterControl = createControl( () => {
22
23
  const { propType, value, setValue, disabled } = useBoundProp( boxShadowPropTypeUtil );
23
24
 
24
25
  return (
25
26
  <PropProvider propType={ propType } value={ value } setValue={ setValue } isDisabled={ () => disabled }>
26
- <UnstableRepeater initial={ initialShadow } propTypeUtil={ boxShadowPropTypeUtil }>
27
- <Header label={ __( 'Box shadow', 'elementor' ) }>
28
- <TooltipAddItemAction newItemIndex={ 0 } disabled={ disabled } />
29
- </Header>
30
- <ItemsContainer itemTemplate={ <Item Icon={ ItemIcon } Label={ ItemLabel } /> }>
31
- <DuplicateItemAction />
32
- <DisableItemAction />
33
- <RemoveItemAction />
27
+ <ControlRepeater initial={ initialShadow } propTypeUtil={ boxShadowPropTypeUtil }>
28
+ <RepeaterHeader label={ __( 'Box shadow', 'elementor' ) }>
29
+ <TooltipAddItemAction newItemIndex={ 0 } disabled={ disabled } ariaLabel={ 'Box shadow' } />
30
+ </RepeaterHeader>
31
+ <ItemsContainer>
32
+ <Item
33
+ Icon={ ItemIcon }
34
+ Label={ ItemLabel }
35
+ actions={
36
+ <>
37
+ <DuplicateItemAction />
38
+ <DisableItemAction />
39
+ <RemoveItemAction />
40
+ </>
41
+ }
42
+ />
34
43
  </ItemsContainer>
35
44
  <EditItemPopover>
36
45
  <Content />
37
46
  </EditItemPopover>
38
- </UnstableRepeater>
47
+ </ControlRepeater>
39
48
  </PropProvider>
40
49
  );
41
50
  } );
@@ -127,11 +136,19 @@ const ItemLabel = ( { value }: { value: ShadowPropValue } ) => {
127
136
  const positionLabel = position?.value || 'outset';
128
137
 
129
138
  const sizes = [
130
- hOffsetSize + hOffsetUnit,
131
- vOffsetSize + vOffsetUnit,
132
- blurSize + blurUnit,
133
- spreadSize + spreadUnit,
134
- ].join( ' ' );
139
+ [ hOffsetSize, hOffsetUnit ],
140
+ [ vOffsetSize, vOffsetUnit ],
141
+ [ blurSize, blurUnit ],
142
+ [ spreadSize, spreadUnit ],
143
+ ]
144
+ .map( ( [ size, unit ] ) => {
145
+ if ( unit !== 'custom' ) {
146
+ return size + unit;
147
+ }
148
+
149
+ return ! size ? CUSTOM_SIZE_LABEL : size;
150
+ } )
151
+ .join( ' ' );
135
152
 
136
153
  return (
137
154
  <span style={ { textTransform: 'capitalize' } }>
@@ -9,10 +9,11 @@ import { createControl } from '../create-control';
9
9
  type Props = Partial< Omit< UnstableColorFieldProps, 'value' | 'onChange' > > & {
10
10
  propTypeUtil?: PropTypeUtil< string, string >;
11
11
  anchorEl?: HTMLElement | null;
12
+ id?: string;
12
13
  };
13
14
 
14
15
  export const ColorControl = createControl(
15
- ( { propTypeUtil = colorPropTypeUtil, anchorEl, slotProps = {}, ...props }: Props ) => {
16
+ ( { propTypeUtil = colorPropTypeUtil, anchorEl, slotProps = {}, id, ...props }: Props ) => {
16
17
  const { value, setValue, placeholder: boundPropPlaceholder, disabled } = useBoundProp( propTypeUtil );
17
18
 
18
19
  const placeholder = props.placeholder ?? boundPropPlaceholder;
@@ -24,6 +25,7 @@ export const ColorControl = createControl(
24
25
  return (
25
26
  <ControlActions>
26
27
  <UnstableColorField
28
+ id={ id }
27
29
  size="tiny"
28
30
  fullWidth
29
31
  value={ value ?? '' }
@@ -0,0 +1,108 @@
1
+ import * as React from 'react';
2
+ import type { Dayjs } from 'dayjs';
3
+ import * as dayjs from 'dayjs';
4
+ import { isTransformable, type Props, stringPropTypeUtil } from '@elementor/editor-props';
5
+ import { DateTimePropTypeUtil } from '@elementor/editor-props';
6
+ import { Box, DatePicker, LocalizationProvider, TimePicker } from '@elementor/ui';
7
+
8
+ import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
9
+ import ControlActions from '../control-actions/control-actions';
10
+ import { createControl } from '../create-control';
11
+
12
+ const DATE_FORMAT = 'YYYY-MM-DD';
13
+ const TIME_FORMAT = 'HH:mm';
14
+
15
+ export const DateTimeControl = createControl( ( { inputDisabled }: { inputDisabled?: boolean } ) => {
16
+ const { value, setValue, ...propContext } = useBoundProp( DateTimePropTypeUtil );
17
+
18
+ const handleChange = ( newValue: Props, meta: { bind: 'date' | 'time' } ) => {
19
+ const field = meta.bind;
20
+ const fieldValue = newValue[ field as 'date' | 'time' ];
21
+
22
+ if ( isTransformable( fieldValue ) ) {
23
+ return setValue( { ...value, [ field ]: fieldValue } );
24
+ }
25
+
26
+ let formattedValue = '';
27
+
28
+ if ( fieldValue ) {
29
+ const dayjsValue = fieldValue as Dayjs;
30
+ formattedValue = field === 'date' ? dayjsValue.format( DATE_FORMAT ) : dayjsValue.format( TIME_FORMAT );
31
+ }
32
+
33
+ setValue( {
34
+ ...value,
35
+ [ field ]: {
36
+ $$type: 'string',
37
+ value: formattedValue,
38
+ },
39
+ } );
40
+ };
41
+
42
+ const parseDateValue = ( dateStr?: string | null ): Dayjs | null => {
43
+ if ( ! dateStr ) {
44
+ return null;
45
+ }
46
+
47
+ const d = ( dayjs as unknown as { default: ( s?: string | number | Date ) => Dayjs } ).default( dateStr );
48
+
49
+ return d && typeof d.isValid === 'function' && d.isValid() ? d : null;
50
+ };
51
+
52
+ const parseTimeValue = ( timeStr?: string | null ): Dayjs | null => {
53
+ if ( ! timeStr ) {
54
+ return null;
55
+ }
56
+
57
+ const [ hours, minutes ] = timeStr.split( ':' );
58
+ const h = Number.parseInt( hours ?? '', 10 );
59
+ const m = Number.parseInt( minutes ?? '', 10 );
60
+
61
+ if ( Number.isNaN( h ) || Number.isNaN( m ) ) {
62
+ return null;
63
+ }
64
+
65
+ const base = ( dayjs as unknown as { default: () => Dayjs } ).default();
66
+ return base.hour( h ).minute( m ).second( 0 ).millisecond( 0 );
67
+ };
68
+
69
+ return (
70
+ <PropProvider { ...propContext } value={ value } setValue={ setValue }>
71
+ <ControlActions>
72
+ <LocalizationProvider>
73
+ <Box display="flex" gap={ 1 } alignItems="center">
74
+ <PropKeyProvider bind="date">
75
+ <DatePicker
76
+ value={ parseDateValue( stringPropTypeUtil.extract( value?.date ) ) }
77
+ onChange={ ( v: Dayjs | null ) =>
78
+ handleChange( { date: v } as Props, { bind: 'date' } )
79
+ }
80
+ disabled={ inputDisabled }
81
+ slotProps={ {
82
+ textField: { size: 'tiny' },
83
+ openPickerButton: { size: 'tiny' },
84
+ openPickerIcon: { fontSize: 'tiny' },
85
+ } }
86
+ />
87
+ </PropKeyProvider>
88
+
89
+ <PropKeyProvider bind="time">
90
+ <TimePicker
91
+ value={ parseTimeValue( stringPropTypeUtil.extract( value?.time ) ) }
92
+ onChange={ ( v: Dayjs | null ) =>
93
+ handleChange( { time: v } as Props, { bind: 'time' } )
94
+ }
95
+ disabled={ inputDisabled }
96
+ slotProps={ {
97
+ textField: { size: 'tiny' },
98
+ openPickerButton: { size: 'tiny' },
99
+ openPickerIcon: { fontSize: 'tiny' },
100
+ } }
101
+ />
102
+ </PropKeyProvider>
103
+ </Box>
104
+ </LocalizationProvider>
105
+ </ControlActions>
106
+ </PropProvider>
107
+ );
108
+ } );
@@ -53,6 +53,7 @@ export const DropShadowItemContent = ( { anchorEl }: { anchorEl?: HTMLElement |
53
53
  anchorRef={ rowRefs[ item.rowIndex ] }
54
54
  enablePropTypeUnits
55
55
  defaultUnit="px"
56
+ isRepeaterControl
56
57
  />
57
58
  ) }
58
59
  </Grid>
@@ -2,19 +2,23 @@ import * as React from 'react';
2
2
  import { type DropShadowFilterPropValue, type FilterItemPropValue } from '@elementor/editor-props';
3
3
  import { Box } from '@elementor/ui';
4
4
 
5
- export const DropShadowItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
6
- const { xAxis, yAxis, blur } = value.value.args.value as DropShadowFilterPropValue[ 'value' ];
5
+ import { CUSTOM_SIZE_LABEL } from '../../size-control';
7
6
 
8
- const xValue = `${ xAxis?.value?.size ?? 0 }${ xAxis?.value?.unit ?? 'px' }`;
9
- const yValue = `${ yAxis?.value?.size ?? 0 }${ yAxis?.value?.unit ?? 'px' }`;
10
- const blurValue = `${ blur?.value?.size ?? 10 }${ blur?.value?.unit ?? 'px' }`;
7
+ export const DropShadowItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
8
+ const values = value.value.args.value as DropShadowFilterPropValue[ 'value' ];
9
+ const keys = [ 'xAxis', 'yAxis', 'blur' ] as Array< keyof typeof values >;
10
+ const labels: string[] = keys.map( ( key ) =>
11
+ values[ key ]?.value?.unit !== 'custom'
12
+ ? `${ values[ key ]?.value?.size ?? 0 }${ values[ key ]?.value?.unit ?? 'px' }`
13
+ : values[ key ]?.value?.size || CUSTOM_SIZE_LABEL
14
+ );
11
15
 
12
16
  return (
13
17
  <Box component="span">
14
18
  <Box component="span" style={ { textTransform: 'capitalize' } }>
15
19
  Drop shadow:
16
20
  </Box>
17
- { `${ xValue } ${ yValue } ${ blurValue }` }
21
+ { ` ${ labels.join( ' ' ) }` }
18
22
  </Box>
19
23
  );
20
24
  };
@@ -10,9 +10,9 @@ import { __ } from '@wordpress/i18n';
10
10
 
11
11
  import { PropKeyProvider, PropProvider, useBoundProp } from '../../bound-prop-context';
12
12
  import { ControlFormLabel } from '../../components/control-form-label';
13
+ import { useRepeaterContext } from '../../components/control-repeater/context/repeater-context';
13
14
  import { PopoverContent } from '../../components/popover-content';
14
15
  import { PopoverGridContainer } from '../../components/popover-grid-container';
15
- import { useRepeaterContext } from '../../components/unstable-repeater/context/repeater-context';
16
16
  import { SelectControl } from '../select-control';
17
17
  import { type FilterFunction } from './configs';
18
18
  import { useFilterConfig } from './context/filter-config-context';
@@ -8,18 +8,13 @@ import {
8
8
  import { __ } from '@wordpress/i18n';
9
9
 
10
10
  import { PropProvider, useBoundProp } from '../../bound-prop-context';
11
- import {
12
- Header,
13
- Item,
14
- ItemsContainer,
15
- TooltipAddItemAction,
16
- UnstableRepeater,
17
- } from '../../components/unstable-repeater';
18
- import { DisableItemAction } from '../../components/unstable-repeater/actions/disable-item-action';
19
- import { DuplicateItemAction } from '../../components/unstable-repeater/actions/duplicate-item-action';
20
- import { RemoveItemAction } from '../../components/unstable-repeater/actions/remove-item-action';
21
- import { EditItemPopover } from '../../components/unstable-repeater/items/edit-item-popover';
22
- import type { RepeatablePropValue } from '../../components/unstable-repeater/types';
11
+ import { ControlRepeater, Item, ItemsContainer, TooltipAddItemAction } from '../../components/control-repeater';
12
+ import { DisableItemAction } from '../../components/control-repeater/actions/disable-item-action';
13
+ import { DuplicateItemAction } from '../../components/control-repeater/actions/duplicate-item-action';
14
+ import { RemoveItemAction } from '../../components/control-repeater/actions/remove-item-action';
15
+ import { EditItemPopover } from '../../components/control-repeater/items/edit-item-popover';
16
+ import type { RepeatablePropValue } from '../../components/control-repeater/types';
17
+ import { RepeaterHeader } from '../../components/repeater/repeater-header';
23
18
  import { createControl } from '../../create-control';
24
19
  import { FilterConfigProvider, useFilterConfig } from './context/filter-config-context';
25
20
  import { FilterContent } from './filter-content';
@@ -42,7 +37,7 @@ const FILTER_CONFIG: Record< string, Config > = {
42
37
  },
43
38
  'backdrop-filter': {
44
39
  propTypeUtil: backdropFilterPropTypeUtil,
45
- label: __( 'Backdrop Filters', 'elementor' ),
40
+ label: __( 'Backdrop filters', 'elementor' ),
46
41
  },
47
42
  } as const;
48
43
 
@@ -73,22 +68,30 @@ const Repeater = ( { propTypeUtil, label, filterPropName }: RepeaterProps ) => {
73
68
  const { getInitialValue } = useFilterConfig();
74
69
 
75
70
  return (
76
- <UnstableRepeater initial={ getInitialValue() as RepeatablePropValue } propTypeUtil={ propTypeUtil }>
77
- <Header label={ label }>
71
+ <ControlRepeater initial={ getInitialValue() as RepeatablePropValue } propTypeUtil={ propTypeUtil }>
72
+ <RepeaterHeader label={ label }>
78
73
  <TooltipAddItemAction
79
74
  newItemIndex={ 0 }
80
75
  ariaLabel={ filterPropName === 'backdrop-filter' ? 'backdrop filter' : 'filter' }
81
76
  />
82
- </Header>
83
- <ItemsContainer itemTemplate={ <Item Label={ FilterLabel } Icon={ FilterIcon } /> }>
84
- <DuplicateItemAction />
85
- <DisableItemAction />
86
- <RemoveItemAction />
77
+ </RepeaterHeader>
78
+ <ItemsContainer>
79
+ <Item
80
+ Label={ FilterLabel }
81
+ Icon={ FilterIcon }
82
+ actions={
83
+ <>
84
+ <DuplicateItemAction />
85
+ <DisableItemAction />
86
+ <RemoveItemAction />
87
+ </>
88
+ }
89
+ />
87
90
  </ItemsContainer>
88
91
  <EditItemPopover>
89
92
  <FilterContent />
90
93
  </EditItemPopover>
91
- </UnstableRepeater>
94
+ </ControlRepeater>
92
95
  );
93
96
  };
94
97
 
@@ -38,7 +38,7 @@ export const SingleSizeItemContent = ( { filterFunc }: { filterFunc: FilterFunct
38
38
  <ControlFormLabel>{ valueName }</ControlFormLabel>
39
39
  </Grid>
40
40
  <Grid item xs={ 6 }>
41
- <SizeControl anchorRef={ rowRef } enablePropTypeUnits />
41
+ <SizeControl anchorRef={ rowRef } enablePropTypeUnits isRepeaterControl />
42
42
  </Grid>
43
43
  </PopoverGridContainer>
44
44
  </PropKeyProvider>
@@ -3,6 +3,7 @@ import type { FilterItemPropValue, SizePropValue } from '@elementor/editor-props
3
3
  import { Box } from '@elementor/ui';
4
4
 
5
5
  import { lengthUnits } from '../../../utils/size-control';
6
+ import { CUSTOM_SIZE_LABEL } from '../../size-control';
6
7
  import { type FilterFunction } from '../configs';
7
8
  import { useFilterConfig } from '../context/filter-config-context';
8
9
 
@@ -24,7 +25,7 @@ export const SingleSizeItemLabel = ( { value }: { value: FilterItemPropValue } )
24
25
  return (
25
26
  <Box component="span">
26
27
  { label }
27
- { unit !== 'custom' ? ` ${ size ?? 0 }${ unit ?? defaultUnit }` : size }
28
+ { ' ' + ( unit !== 'custom' ? `${ size ?? 0 }${ unit ?? defaultUnit }` : size || CUSTOM_SIZE_LABEL ) }
28
29
  </Box>
29
30
  );
30
31
  };