@elementor/editor-controls 1.5.0 → 3.32.0-21

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 (71) hide show
  1. package/CHANGELOG.md +0 -22
  2. package/dist/index.d.mts +95 -25
  3. package/dist/index.d.ts +95 -25
  4. package/dist/index.js +2045 -1041
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +1962 -964
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +18 -18
  9. package/src/components/control-toggle-button-group.tsx +78 -14
  10. package/src/components/floating-bar.tsx +45 -0
  11. package/src/components/{font-family-selector.tsx → item-selector.tsx} +62 -50
  12. package/src/components/repeater.tsx +1 -1
  13. package/src/components/restricted-link-infotip.tsx +76 -0
  14. package/src/components/size-control/size-input.tsx +8 -7
  15. package/src/components/size-control/text-field-inner-selection.tsx +60 -14
  16. package/src/components/text-field-popover.tsx +30 -7
  17. package/src/components/unstable-repeater/actions/add-item-action.tsx +50 -0
  18. package/src/components/unstable-repeater/actions/disable-item-action.tsx +39 -0
  19. package/src/components/unstable-repeater/actions/duplicate-item-action.tsx +32 -0
  20. package/src/components/unstable-repeater/actions/remove-item-action.tsx +27 -0
  21. package/src/components/unstable-repeater/context/repeater-context.tsx +137 -0
  22. package/src/components/unstable-repeater/header/header.tsx +23 -0
  23. package/src/components/unstable-repeater/index.ts +5 -0
  24. package/src/components/unstable-repeater/items/edit-item-popover.tsx +28 -0
  25. package/src/components/unstable-repeater/items/item.tsx +71 -0
  26. package/src/components/unstable-repeater/items/items-container.tsx +49 -0
  27. package/src/components/unstable-repeater/items/use-popover.tsx +26 -0
  28. package/src/{locations.ts → components/unstable-repeater/locations.ts} +9 -1
  29. package/src/components/unstable-repeater/types.ts +26 -0
  30. package/src/components/unstable-repeater/unstable-repeater.tsx +24 -0
  31. package/src/control-actions/control-actions.tsx +3 -20
  32. package/src/control-replacements.tsx +41 -0
  33. package/src/controls/background-control/background-control.tsx +1 -8
  34. package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +17 -16
  35. package/src/controls/equal-unequal-sizes-control.tsx +2 -9
  36. package/src/controls/filter-control/drop-shadow-item-content.tsx +4 -6
  37. package/src/controls/filter-control/drop-shadow-item-label.tsx +2 -2
  38. package/src/controls/filter-repeater-control.tsx +149 -110
  39. package/src/controls/font-family-control/font-family-control.tsx +22 -10
  40. package/src/controls/key-value-control.tsx +9 -6
  41. package/src/controls/link-control.tsx +8 -91
  42. package/src/controls/linked-dimensions-control.tsx +3 -16
  43. package/src/controls/number-control.tsx +10 -1
  44. package/src/controls/position-control.tsx +4 -16
  45. package/src/controls/repeatable-control.tsx +8 -5
  46. package/src/controls/select-control.tsx +7 -2
  47. package/src/controls/selection-size-control.tsx +74 -0
  48. package/src/controls/size-control.tsx +181 -126
  49. package/src/controls/stroke-control.tsx +2 -2
  50. package/src/controls/toggle-control.tsx +3 -2
  51. package/src/controls/transform-control/functions/axis-row.tsx +4 -2
  52. package/src/controls/transform-control/functions/move.tsx +2 -1
  53. package/src/controls/transform-control/functions/rotate.tsx +48 -0
  54. package/src/controls/transform-control/functions/scale-axis-row.tsx +32 -0
  55. package/src/controls/transform-control/functions/scale.tsx +45 -0
  56. package/src/controls/transform-control/functions/skew.tsx +43 -0
  57. package/src/controls/transform-control/transform-content.tsx +60 -23
  58. package/src/controls/transform-control/transform-icon.tsx +10 -2
  59. package/src/controls/transform-control/transform-label.tsx +39 -2
  60. package/src/controls/transform-control/transform-repeater-control.tsx +2 -10
  61. package/src/controls/transform-control/types.ts +58 -0
  62. package/src/controls/transform-control/use-transform-tabs-history.tsx +107 -0
  63. package/src/controls/transition-control/data.ts +34 -0
  64. package/src/controls/transition-control/transition-repeater-control.tsx +63 -0
  65. package/src/controls/transition-control/transition-selector.tsx +88 -0
  66. package/src/controls/unstable-transform-control/unstable-transform-repeater-control.tsx +35 -0
  67. package/src/hooks/use-filtered-items-list.ts +24 -0
  68. package/src/hooks/use-size-extended-options.ts +1 -6
  69. package/src/index.ts +13 -3
  70. package/src/utils/size-control.ts +12 -6
  71. package/src/hooks/use-filtered-font-families.ts +0 -24
@@ -1,127 +1,163 @@
1
1
  import * as React from 'react';
2
2
  import { useRef } from 'react';
3
3
  import {
4
- blurFilterPropTypeUtil,
5
- brightnessFilterPropTypeUtil,
6
- contrastFilterPropTypeUtil,
7
- dropShadowFilterPropTypeUtil,
4
+ type CreateOptions,
5
+ cssFilterFunctionPropUtil,
8
6
  type FilterItemPropValue,
9
7
  filterPropTypeUtil,
10
- grayscaleFilterPropTypeUtil,
11
- hueRotateFilterPropTypeUtil,
12
- invertFilterPropTypeUtil,
13
8
  type PropKey,
14
- type PropTypeUtil,
15
- saturateFilterPropTypeUtil,
16
- sepiaFilterPropTypeUtil,
17
9
  type SizePropValue,
18
10
  } from '@elementor/editor-props';
19
11
  import { backdropFilterPropTypeUtil } from '@elementor/editor-props';
20
- import { MenuListItem } from '@elementor/editor-ui';
21
- import { Box, Grid, Select, type SelectChangeEvent } from '@elementor/ui';
12
+ import { Box, Grid } from '@elementor/ui';
22
13
  import { __ } from '@wordpress/i18n';
23
14
 
24
15
  import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
25
- import { ControlLabel } from '../components/control-label';
16
+ import { ControlFormLabel } from '../components/control-form-label';
26
17
  import { PopoverContent } from '../components/popover-content';
27
18
  import { PopoverGridContainer } from '../components/popover-grid-container';
28
19
  import { type CollectionPropUtil, Repeater } from '../components/repeater';
29
20
  import { createControl } from '../create-control';
30
- import { defaultUnits, type Unit } from '../utils/size-control';
21
+ import { type LengthUnit, lengthUnits, type Unit } from '../utils/size-control';
31
22
  import { DropShadowItemContent } from './filter-control/drop-shadow-item-content';
32
23
  import { DropShadowItemLabel } from './filter-control/drop-shadow-item-label';
33
- import { SizeControl } from './size-control';
24
+ import { SelectControl } from './select-control';
25
+ import { SizeControl, type SizeControlProps } from './size-control';
34
26
 
35
- type FilterType = FilterItemPropValue[ '$$type' ];
36
- type FilterValue = FilterItemPropValue[ 'value' ];
27
+ type FilterType = FilterItemPropValue[ 'value' ][ 'func' ];
37
28
 
38
- const DEFAULT_FILTER_KEY: FilterType = 'blur';
29
+ const DEFAULT_FILTER = 'blur';
39
30
 
40
31
  type FilterItemConfig = {
41
- defaultValue: FilterValue;
32
+ defaultValue: FilterItemPropValue;
42
33
  name: string;
43
34
  valueName: string;
44
- propType: PropTypeUtil< FilterValue, FilterValue >;
45
35
  units?: Exclude< SizePropValue[ 'value' ][ 'unit' ], 'custom' | 'auto' >[];
36
+ sizeVariant?: SizeControlProps[ 'variant' ];
46
37
  };
47
38
 
48
- const filterConfig: Record< FilterType, FilterItemConfig > = {
39
+ const filterConfig: Record< string, FilterItemConfig > = {
49
40
  blur: {
50
- defaultValue: { $$type: 'radius', radius: { $$type: 'size', value: { size: 0, unit: 'px' } } },
41
+ defaultValue: {
42
+ $$type: 'css-filter-func',
43
+ value: {
44
+ func: { $$type: 'string', value: 'blur' },
45
+ args: { $$type: 'size', value: { size: 0, unit: 'px' } },
46
+ },
47
+ },
51
48
  name: __( 'Blur', 'elementor' ),
52
49
  valueName: __( 'Radius', 'elementor' ),
53
- propType: blurFilterPropTypeUtil,
54
- units: defaultUnits.filter( ( unit ) => unit !== '%' ),
50
+ units: lengthUnits.filter( ( unit ) => unit !== '%' ),
55
51
  },
56
- 'drop-shadow': {
52
+ brightness: {
57
53
  defaultValue: {
58
- $$type: 'drop-shadow',
54
+ $$type: 'css-filter-func',
59
55
  value: {
60
- xAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
61
- yAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
62
- blur: { $$type: 'size', value: { size: 10, unit: 'px' } },
63
- color: { $$type: 'color', value: 'rgba(0, 0, 0, 1)' },
56
+ func: { $$type: 'string', value: 'brightness' },
57
+ args: { $$type: 'size', value: { size: 100, unit: '%' } },
64
58
  },
65
59
  },
66
- name: __( 'Drop shadow', 'elementor' ),
67
- valueName: __( 'Drop-shadow', 'elementor' ),
68
- propType: dropShadowFilterPropTypeUtil,
69
- units: defaultUnits.filter( ( unit ) => unit !== '%' ),
70
- },
71
- brightness: {
72
- defaultValue: { $$type: 'amount', amount: { $$type: 'size', value: { size: 100, unit: '%' } } },
73
60
  name: __( 'Brightness', 'elementor' ),
74
61
  valueName: __( 'Amount', 'elementor' ),
75
- propType: brightnessFilterPropTypeUtil,
76
62
  units: [ '%' ],
77
63
  },
78
64
  contrast: {
79
- defaultValue: { $$type: 'contrast', contrast: { $$type: 'size', value: { size: 100, unit: '%' } } },
65
+ defaultValue: {
66
+ $$type: 'css-filter-func',
67
+ value: {
68
+ func: { $$type: 'string', value: 'contrast' },
69
+ args: { $$type: 'size', value: { size: 100, unit: '%' } },
70
+ },
71
+ },
80
72
  name: __( 'Contrast', 'elementor' ),
81
73
  valueName: __( 'Amount', 'elementor' ),
82
- propType: contrastFilterPropTypeUtil,
83
74
  units: [ '%' ],
84
75
  },
85
76
  'hue-rotate': {
86
- defaultValue: { $$type: 'hue-rotate', 'hue-rotate': { $$type: 'size', value: { size: 0, unit: 'deg' } } },
77
+ defaultValue: {
78
+ $$type: 'css-filter-func',
79
+ value: {
80
+ func: { $$type: 'string', value: 'hue-rotate' },
81
+ args: { $$type: 'size', value: { size: 0, unit: 'deg' } },
82
+ },
83
+ },
87
84
  name: __( 'Hue Rotate', 'elementor' ),
88
85
  valueName: __( 'Angle', 'elementor' ),
89
- propType: hueRotateFilterPropTypeUtil,
90
86
  units: [ 'deg', 'rad', 'grad', 'turn' ],
91
87
  },
92
88
  saturate: {
93
- defaultValue: { $$type: 'saturate', saturate: { $$type: 'size', value: { size: 100, unit: '%' } } },
89
+ defaultValue: {
90
+ $$type: 'css-filter-func',
91
+ value: {
92
+ func: { $$type: 'string', value: 'saturate' },
93
+ args: { $$type: 'size', value: { size: 100, unit: '%' } },
94
+ },
95
+ },
94
96
  name: __( 'Saturate', 'elementor' ),
95
97
  valueName: __( 'Amount', 'elementor' ),
96
- propType: saturateFilterPropTypeUtil,
97
98
  units: [ '%' ],
98
99
  },
99
100
  grayscale: {
100
- defaultValue: { $$type: 'grayscale', grayscale: { $$type: 'size', value: { size: 0, unit: '%' } } },
101
+ defaultValue: {
102
+ $$type: 'css-filter-func',
103
+ value: {
104
+ func: { $$type: 'string', value: 'grayscale' },
105
+ args: { $$type: 'size', value: { size: 0, unit: '%' } },
106
+ },
107
+ },
101
108
  name: __( 'Grayscale', 'elementor' ),
102
109
  valueName: __( 'Amount', 'elementor' ),
103
- propType: grayscaleFilterPropTypeUtil,
104
110
  units: [ '%' ],
105
111
  },
106
112
  invert: {
107
- defaultValue: { $$type: 'invert', invert: { $$type: 'size', value: { size: 0, unit: '%' } } },
113
+ defaultValue: {
114
+ $$type: 'css-filter-func',
115
+ value: {
116
+ func: { $$type: 'string', value: 'invert' },
117
+ args: { $$type: 'size', value: { size: 0, unit: '%' } },
118
+ },
119
+ },
108
120
  name: __( 'Invert', 'elementor' ),
109
121
  valueName: __( 'Amount', 'elementor' ),
110
- propType: invertFilterPropTypeUtil,
111
122
  units: [ '%' ],
112
123
  },
113
124
  sepia: {
114
- defaultValue: { $$type: 'sepia', sepia: { $$type: 'size', value: { size: 0, unit: '%' } } },
125
+ defaultValue: {
126
+ $$type: 'css-filter-func',
127
+ value: {
128
+ func: { $$type: 'string', value: 'sepia' },
129
+ args: { $$type: 'size', value: { size: 0, unit: '%' } },
130
+ },
131
+ },
115
132
  name: __( 'Sepia', 'elementor' ),
116
133
  valueName: __( 'Amount', 'elementor' ),
117
- propType: sepiaFilterPropTypeUtil,
118
134
  units: [ '%' ],
119
135
  },
136
+ 'drop-shadow': {
137
+ defaultValue: {
138
+ $$type: 'css-filter-func',
139
+ value: {
140
+ func: { $$type: 'string', value: 'drop-shadow' },
141
+ args: {
142
+ $$type: 'drop-shadow',
143
+ value: {
144
+ xAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
145
+ yAxis: { $$type: 'size', value: { size: 0, unit: 'px' } },
146
+ blur: { $$type: 'size', value: { size: 10, unit: 'px' } },
147
+ color: { $$type: 'color', value: 'rgba(0, 0, 0, 1)' },
148
+ },
149
+ },
150
+ },
151
+ },
152
+ name: __( 'Drop shadow', 'elementor' ),
153
+ valueName: __( 'Drop-shadow', 'elementor' ),
154
+ units: lengthUnits.filter( ( unit ) => unit !== '%' ),
155
+ },
120
156
  };
121
157
 
122
- const filterKeys = Object.keys( filterConfig ) as FilterType[];
158
+ const filterKeys = Object.keys( filterConfig );
123
159
 
124
- const isSingleSize = ( key: FilterType ): boolean => {
160
+ const isSingleSize = ( key: string ): boolean => {
125
161
  return ! [ 'drop-shadow' ].includes( key );
126
162
  };
127
163
 
@@ -145,10 +181,7 @@ export const FilterRepeaterControl = createControl( ( { filterPropName = 'filter
145
181
  Icon: ItemIcon,
146
182
  Label: ItemLabel,
147
183
  Content: ItemContent,
148
- initialValues: {
149
- $$type: DEFAULT_FILTER_KEY,
150
- value: filterConfig[ DEFAULT_FILTER_KEY ].defaultValue,
151
- } as FilterItemPropValue,
184
+ initialValues: filterConfig[ DEFAULT_FILTER ].defaultValue,
152
185
  } }
153
186
  />
154
187
  </PropProvider>
@@ -158,7 +191,7 @@ export const FilterRepeaterControl = createControl( ( { filterPropName = 'filter
158
191
  const ItemIcon = () => <></>;
159
192
 
160
193
  const ItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
161
- return isSingleSize( value.$$type ) ? (
194
+ return isSingleSize( value.value.func.value ?? '' ) ? (
162
195
  <SingleSizeItemLabel value={ value } />
163
196
  ) : (
164
197
  <DropShadowItemLabel value={ value } />
@@ -166,14 +199,14 @@ const ItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
166
199
  };
167
200
 
168
201
  const SingleSizeItemLabel = ( { value }: { value: FilterItemPropValue } ) => {
169
- const { $$type, value: sizeValue } = value;
170
- const { $$type: key } = filterConfig[ $$type ].defaultValue;
171
- const defaultUnit = filterConfig[ $$type ].defaultValue[ key ].value.unit;
172
- const { unit, size } = sizeValue[ key ]?.value ?? { unit: defaultUnit, size: 0 };
202
+ const { func, args } = value.value;
203
+ const defaultUnit =
204
+ ( filterConfig[ func.value ?? '' ].defaultValue.value.args as SizePropValue ).value.unit ?? lengthUnits[ 0 ];
205
+ const { unit, size } = ( args as SizePropValue ).value ?? { unit: defaultUnit, size: 0 };
173
206
 
174
207
  const label = (
175
208
  <Box component="span" style={ { textTransform: 'capitalize' } }>
176
- { value.$$type }:
209
+ { func.value ?? '' }:
177
210
  </Box>
178
211
  );
179
212
 
@@ -194,80 +227,86 @@ const ItemContent = ( {
194
227
  collectionPropUtil?: CollectionPropUtil< FilterItemPropValue >;
195
228
  anchorEl?: HTMLElement | null;
196
229
  } ) => {
197
- const { value: filterValues, setValue } = useBoundProp( collectionPropUtil ?? filterPropTypeUtil );
230
+ const { value: filterValues = [] } = useBoundProp( collectionPropUtil ?? filterPropTypeUtil );
198
231
  const itemIndex = parseInt( bind, 10 );
199
232
  const item = filterValues?.[ itemIndex ];
233
+ return item ? (
234
+ <PropKeyProvider bind={ bind }>
235
+ <PropContent item={ item } anchorEl={ anchorEl } />
236
+ </PropKeyProvider>
237
+ ) : null;
238
+ };
200
239
 
201
- const handleChange = ( e: SelectChangeEvent< string > ) => {
202
- const newFilterValues = [ ...filterValues ];
203
- const filterType = e.target.value as FilterType;
240
+ const PropContent = ( { item, anchorEl }: { item: FilterItemPropValue; anchorEl?: HTMLElement | null } ) => {
241
+ const propContext = useBoundProp( cssFilterFunctionPropUtil );
204
242
 
205
- newFilterValues[ itemIndex ] = {
206
- $$type: filterType,
207
- value: { ...filterConfig[ filterType ].defaultValue },
208
- } as FilterItemPropValue;
243
+ const handleValueChange = (
244
+ changedValue: FilterItemPropValue[ 'value' ],
245
+ options?: CreateOptions,
246
+ meta?: { bind?: PropKey }
247
+ ) => {
248
+ let newValue = structuredClone( changedValue );
249
+ const newFuncName = newValue?.func.value ?? '';
250
+ if ( meta?.bind === 'func' ) {
251
+ newValue = structuredClone( filterConfig[ newFuncName ].defaultValue.value );
252
+ }
209
253
 
210
- setValue( newFilterValues );
254
+ if ( ! newValue.args ) {
255
+ return;
256
+ }
257
+ propContext.setValue( newValue );
211
258
  };
212
259
 
213
260
  return (
214
- <PropKeyProvider bind={ bind }>
261
+ <PropProvider { ...propContext } setValue={ handleValueChange }>
215
262
  <PopoverContent p={ 1.5 }>
216
263
  <PopoverGridContainer>
217
264
  <Grid item xs={ 6 }>
218
- <ControlLabel>{ __( 'Filter', 'elementor' ) }</ControlLabel>
265
+ <ControlFormLabel>{ __( 'Filter', 'elementor' ) }</ControlFormLabel>
219
266
  </Grid>
220
267
  <Grid item xs={ 6 }>
221
- <Select
222
- sx={ { overflow: 'hidden' } }
223
- size="tiny"
224
- value={ item?.$$type ?? DEFAULT_FILTER_KEY }
225
- onChange={ handleChange }
226
- fullWidth
227
- >
228
- { filterKeys.map( ( filterKey ) => (
229
- <MenuListItem key={ filterKey } value={ filterKey }>
230
- { filterConfig[ filterKey ].name }
231
- </MenuListItem>
232
- ) ) }
233
- </Select>
268
+ <PropKeyProvider bind="func">
269
+ <SelectControl
270
+ options={ filterKeys.map( ( filterKey ) => ( {
271
+ label: filterConfig[ filterKey ].name,
272
+ value: filterKey,
273
+ } ) ) }
274
+ />
275
+ </PropKeyProvider>
234
276
  </Grid>
235
277
  </PopoverGridContainer>
236
- <Content filterType={ item?.$$type } anchorEl={ anchorEl } />
278
+ <PropKeyProvider bind="args">
279
+ <Content filterType={ item?.value.func } anchorEl={ anchorEl } />
280
+ </PropKeyProvider>
237
281
  </PopoverContent>
238
- </PropKeyProvider>
282
+ </PropProvider>
239
283
  );
240
284
  };
241
285
 
242
286
  const Content = ( { filterType, anchorEl }: { filterType: FilterType; anchorEl?: HTMLElement | null } ) => {
243
- const { propType, units = [] } = filterConfig[ filterType ];
287
+ const filterName = filterType?.value || DEFAULT_FILTER;
288
+ const filterItemConfig = filterConfig[ filterName ];
289
+ const { units = [] } = filterItemConfig;
244
290
 
245
- return isSingleSize( filterType ) ? (
246
- <SingleSizeItemContent filterType={ filterType } />
291
+ return isSingleSize( filterName ) ? (
292
+ <SingleSizeItemContent filterType={ filterName } />
247
293
  ) : (
248
- <DropShadowItemContent propType={ propType } units={ units as Unit[] } anchorEl={ anchorEl } />
294
+ <DropShadowItemContent units={ units as LengthUnit[] } anchorEl={ anchorEl } />
249
295
  );
250
296
  };
251
297
 
252
- const SingleSizeItemContent = ( { filterType }: { filterType: FilterType } ) => {
253
- const { propType, valueName, defaultValue, units } = filterConfig[ filterType ];
254
- const { $$type } = defaultValue;
255
- const context = useBoundProp( propType );
298
+ const SingleSizeItemContent = ( { filterType }: { filterType: string } ) => {
299
+ const { valueName, defaultValue, units } = filterConfig[ filterType ];
256
300
  const rowRef = useRef< HTMLDivElement >( null );
257
- const defaultUnit = defaultValue[ $$type ].value.unit;
258
-
301
+ const defaultUnit = ( defaultValue.value.args as SizePropValue ).value.unit;
259
302
  return (
260
- <PropProvider { ...context }>
261
- <PropKeyProvider bind={ $$type }>
262
- <PopoverGridContainer ref={ rowRef }>
263
- <Grid item xs={ 6 }>
264
- <ControlLabel>{ valueName }</ControlLabel>
265
- </Grid>
266
- <Grid item xs={ 6 }>
267
- <SizeControl anchorRef={ rowRef } units={ units } defaultUnit={ defaultUnit } />
268
- </Grid>
269
- </PopoverGridContainer>
270
- </PropKeyProvider>
271
- </PropProvider>
303
+ <PopoverGridContainer ref={ rowRef }>
304
+ <Grid item xs={ 6 }>
305
+ <ControlFormLabel>{ valueName }</ControlFormLabel>
306
+ </Grid>
307
+ <Grid item xs={ 6 }>
308
+ <SizeControl anchorRef={ rowRef } units={ units as LengthUnit[] } defaultUnit={ defaultUnit as Unit } />
309
+ </Grid>
310
+ </PopoverGridContainer>
272
311
  );
273
312
  };
@@ -1,12 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import { stringPropTypeUtil } from '@elementor/editor-props';
3
- import { ChevronDownIcon } from '@elementor/icons';
3
+ import { ChevronDownIcon, TextIcon } from '@elementor/icons';
4
4
  import { bindPopover, bindTrigger, Popover, UnstableTag, usePopupState } from '@elementor/ui';
5
+ import { __ } from '@wordpress/i18n';
5
6
 
6
7
  import { useBoundProp } from '../../bound-prop-context';
7
- import { FontFamilySelector } from '../../components/font-family-selector';
8
+ import { ItemSelector } from '../../components/item-selector';
9
+ import { type Category } from '../../components/item-selector';
8
10
  import ControlActions from '../../control-actions/control-actions';
9
11
  import { createControl } from '../../create-control';
12
+ import { enqueueFont } from './enqueue-font';
10
13
 
11
14
  export type FontCategory = {
12
15
  label: string;
@@ -18,22 +21,26 @@ type FontFamilyControlProps = {
18
21
  sectionWidth: number;
19
22
  };
20
23
 
21
- const SIZE = 'tiny';
22
-
23
24
  export const FontFamilyControl = createControl( ( { fontFamilies, sectionWidth }: FontFamilyControlProps ) => {
24
25
  const { value: fontFamily, setValue: setFontFamily, disabled, placeholder } = useBoundProp( stringPropTypeUtil );
25
26
 
26
27
  const popoverState = usePopupState( { variant: 'popover' } );
27
-
28
28
  const isShowingPlaceholder = ! fontFamily && placeholder;
29
29
 
30
+ const mapFontSubs = React.useMemo< Category[] >( () => {
31
+ return fontFamilies.map( ( { label, fonts } ) => ( {
32
+ label,
33
+ items: fonts,
34
+ } ) );
35
+ }, [ fontFamilies ] );
36
+
30
37
  return (
31
38
  <>
32
39
  <ControlActions>
33
40
  <UnstableTag
34
41
  variant="outlined"
35
42
  label={ fontFamily || placeholder }
36
- endIcon={ <ChevronDownIcon fontSize={ SIZE } /> }
43
+ endIcon={ <ChevronDownIcon fontSize="tiny" /> }
37
44
  { ...bindTrigger( popoverState ) }
38
45
  fullWidth
39
46
  disabled={ disabled }
@@ -49,6 +56,7 @@ export const FontFamilyControl = createControl( ( { fontFamilies, sectionWidth }
49
56
  }
50
57
  />
51
58
  </ControlActions>
59
+
52
60
  <Popover
53
61
  disablePortal
54
62
  disableScrollLock
@@ -57,12 +65,16 @@ export const FontFamilyControl = createControl( ( { fontFamilies, sectionWidth }
57
65
  sx={ { my: 1.5 } }
58
66
  { ...bindPopover( popoverState ) }
59
67
  >
60
- <FontFamilySelector
61
- fontFamilies={ fontFamilies }
62
- fontFamily={ fontFamily }
63
- onFontFamilyChange={ setFontFamily }
68
+ <ItemSelector
69
+ itemsList={ mapFontSubs }
70
+ selectedItem={ fontFamily }
71
+ onItemChange={ setFontFamily }
64
72
  onClose={ popoverState.close }
65
73
  sectionWidth={ sectionWidth }
74
+ title={ __( 'Font Family', 'elementor' ) }
75
+ itemStyle={ ( item ) => ( { fontFamily: item.value } ) }
76
+ onDebounce={ enqueueFont }
77
+ icon={ TextIcon as React.ElementType< { fontSize: string } > }
66
78
  />
67
79
  </Popover>
68
80
  </>
@@ -105,21 +105,24 @@ export const KeyValueControl = createControl( ( props: KeyValueControlProps = {}
105
105
  return (
106
106
  <PropProvider { ...propContext } value={ value } setValue={ handleChange }>
107
107
  <Grid container gap={ 1.5 }>
108
- <Grid item xs={ 12 }>
109
- <FormLabel size="tiny">{ keyLabel }</FormLabel>
108
+ <Grid item xs={ 12 } display="flex" flexDirection="column">
109
+ <FormLabel size="tiny" sx={ { pb: 1 } }>
110
+ { keyLabel }
111
+ </FormLabel>
110
112
  <PropKeyProvider bind={ 'key' }>
111
- <TextControl inputValue={ sessionState.key } error={ !! keyError } sx={ { pt: 1 } } />
113
+ <TextControl inputValue={ sessionState.key } error={ !! keyError } />
112
114
  </PropKeyProvider>
113
115
  { !! keyError && <FormHelperText error>{ keyError }</FormHelperText> }
114
116
  </Grid>
115
- <Grid item xs={ 12 }>
116
- <FormLabel size="tiny">{ valueLabel }</FormLabel>
117
+ <Grid item xs={ 12 } display="flex" flexDirection="column">
118
+ <FormLabel size="tiny" sx={ { pb: 1 } }>
119
+ { valueLabel }
120
+ </FormLabel>
117
121
  <PropKeyProvider bind={ 'value' }>
118
122
  <TextControl
119
123
  inputValue={ sessionState.value }
120
124
  error={ !! valueError }
121
125
  inputDisabled={ !! keyError }
122
- sx={ { pt: 1 } }
123
126
  />
124
127
  </PropKeyProvider>
125
128
  { !! valueError && <FormHelperText error>{ valueError }</FormHelperText> }
@@ -1,20 +1,17 @@
1
1
  import * as React from 'react';
2
- import { type PropsWithChildren, useMemo, useState } from 'react';
3
- import { getLinkInLinkRestriction, type LinkInLinkRestriction, selectElement } from '@elementor/editor-elements';
2
+ import { useMemo, useState } from 'react';
3
+ import { getLinkInLinkRestriction } from '@elementor/editor-elements';
4
4
  import {
5
- booleanPropTypeUtil,
6
5
  linkPropTypeUtil,
7
6
  type LinkPropValue,
8
7
  numberPropTypeUtil,
9
8
  stringPropTypeUtil,
10
9
  urlPropTypeUtil,
11
10
  } from '@elementor/editor-props';
12
- import { InfoTipCard } from '@elementor/editor-ui';
13
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
14
11
  import { type HttpResponse, httpService } from '@elementor/http-client';
15
- import { AlertTriangleIcon, MinusIcon, PlusIcon } from '@elementor/icons';
12
+ import { MinusIcon, PlusIcon } from '@elementor/icons';
16
13
  import { useSessionStorage } from '@elementor/session';
17
- import { Box, Collapse, Grid, IconButton, Infotip, Stack, Switch } from '@elementor/ui';
14
+ import { Collapse, Grid, IconButton, Stack } from '@elementor/ui';
18
15
  import { debounce } from '@elementor/utils';
19
16
  import { __ } from '@wordpress/i18n';
20
17
 
@@ -27,6 +24,7 @@ import {
27
24
  isCategorizedOptionPool,
28
25
  } from '../components/autocomplete';
29
26
  import { ControlFormLabel } from '../components/control-form-label';
27
+ import { RestrictedLinkInfotip } from '../components/restricted-link-infotip';
30
28
  import ControlActions from '../control-actions/control-actions';
31
29
  import { createControl } from '../create-control';
32
30
  import { type ControlProps } from '../utils/types';
@@ -53,10 +51,6 @@ type LinkSessionValue = {
53
51
  type Response = HttpResponse< { value: FlatOption[] | CategorizedOption[] } >;
54
52
 
55
53
  const SIZE = 'tiny';
56
- const learnMoreButton = {
57
- label: __( 'Learn More', 'elementor' ),
58
- href: 'https://go.elementor.com/element-link-inside-link-infotip',
59
- };
60
54
 
61
55
  export const LinkControl = createControl( ( props: Props ) => {
62
56
  const { value, path, setValue, ...propContext } = useBoundProp( linkPropTypeUtil );
@@ -168,14 +162,14 @@ export const LinkControl = createControl( ( props: Props ) => {
168
162
  } }
169
163
  >
170
164
  <ControlFormLabel>{ label }</ControlFormLabel>
171
- <ConditionalInfoTip isVisible={ ! isActive } linkInLinkRestriction={ linkInLinkRestriction }>
165
+ <RestrictedLinkInfotip isVisible={ ! isActive } linkInLinkRestriction={ linkInLinkRestriction }>
172
166
  <ToggleIconControl
173
167
  disabled={ shouldDisableAddingLink }
174
168
  active={ isActive }
175
169
  onIconClick={ onEnabledChange }
176
170
  label={ __( 'Toggle link', 'elementor' ) }
177
171
  />
178
- </ConditionalInfoTip>
172
+ </RestrictedLinkInfotip>
179
173
  </Stack>
180
174
  <Collapse in={ isActive } timeout="auto" unmountOnExit>
181
175
  <Stack gap={ 1.5 }>
@@ -198,7 +192,7 @@ export const LinkControl = createControl( ( props: Props ) => {
198
192
  <ControlFormLabel>{ __( 'Open in a new tab', 'elementor' ) }</ControlFormLabel>
199
193
  </Grid>
200
194
  <Grid item sx={ { marginInlineEnd: -1 } }>
201
- <SwitchControlComponent disabled={ propContext.disabled || ! value } />
195
+ <SwitchControl />
202
196
  </Grid>
203
197
  </Grid>
204
198
  </PropKeyProvider>
@@ -224,30 +218,6 @@ const ToggleIconControl = ( { disabled, active, onIconClick, label }: ToggleIcon
224
218
  );
225
219
  };
226
220
 
227
- const SwitchControlComponent = ( { disabled }: { disabled: boolean } ) => {
228
- const { value, setValue } = useBoundProp( booleanPropTypeUtil );
229
- const isVersion331Active = isExperimentActive( 'e_v_3_31' );
230
-
231
- if ( isVersion331Active ) {
232
- return <SwitchControl />;
233
- }
234
-
235
- const onClick = () => {
236
- setValue( ! value );
237
- };
238
-
239
- return (
240
- <Switch
241
- checked={ value ?? false }
242
- onClick={ onClick }
243
- disabled={ disabled }
244
- inputProps={ {
245
- ...( disabled ? { style: { opacity: 0 } } : {} ),
246
- } }
247
- />
248
- );
249
- };
250
-
251
221
  type FetchOptionsParams = Record< string, unknown > & { term: string };
252
222
 
253
223
  async function fetchOptions( ajaxUrl: string, params: FetchOptionsParams ) {
@@ -286,56 +256,3 @@ function generateFirstLoadedOption( unionValue: LinkPropValue[ 'value' ] | null
286
256
  ]
287
257
  : [];
288
258
  }
289
-
290
- interface ConditionalInfoTipType extends PropsWithChildren {
291
- linkInLinkRestriction: LinkInLinkRestriction;
292
- isVisible: boolean;
293
- }
294
-
295
- const ConditionalInfoTip: React.FC< ConditionalInfoTipType > = ( { linkInLinkRestriction, isVisible, children } ) => {
296
- const { shouldRestrict, reason, elementId } = linkInLinkRestriction;
297
-
298
- const handleTakeMeClick = () => {
299
- if ( elementId ) {
300
- selectElement( elementId );
301
- }
302
- };
303
-
304
- return shouldRestrict && isVisible ? (
305
- <Infotip
306
- placement="right"
307
- content={
308
- <InfoTipCard
309
- content={ INFOTIP_CONTENT[ reason ] }
310
- svgIcon={ <AlertTriangleIcon /> }
311
- learnMoreButton={ learnMoreButton }
312
- ctaButton={ {
313
- label: __( 'Take me there', 'elementor' ),
314
- onClick: handleTakeMeClick,
315
- } }
316
- />
317
- }
318
- >
319
- <Box>{ children }</Box>
320
- </Infotip>
321
- ) : (
322
- <>{ children }</>
323
- );
324
- };
325
-
326
- const INFOTIP_CONTENT = {
327
- descendant: (
328
- <>
329
- { __( 'To add a link to this container,', 'elementor' ) }
330
- <br />
331
- { __( 'first remove the link from the elements inside of it.', 'elementor' ) }
332
- </>
333
- ),
334
- ancestor: (
335
- <>
336
- { __( 'To add a link to this element,', 'elementor' ) }
337
- <br />
338
- { __( 'first remove the link from its parent container.', 'elementor' ) }
339
- </>
340
- ),
341
- };