@elementor/editor-editing-panel 0.14.2 → 0.16.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 (59) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/index.d.mts +29 -1
  3. package/dist/index.d.ts +29 -1
  4. package/dist/index.js +939 -302
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +944 -294
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +9 -8
  9. package/src/components/editing-panel.tsx +1 -1
  10. package/src/components/settings-tab.tsx +6 -17
  11. package/src/components/style-sections/position-section/position-section.tsx +15 -0
  12. package/src/components/style-sections/position-section/z-index-control.tsx +16 -0
  13. package/src/components/style-sections/size-section.tsx +14 -18
  14. package/src/components/style-sections/spacing-section/linked-dimensions-control.tsx +140 -0
  15. package/src/components/style-sections/spacing-section/spacing-section.tsx +22 -0
  16. package/src/components/style-sections/typography-section/font-size-control.tsx +16 -0
  17. package/src/components/style-sections/typography-section/font-weight-control.tsx +24 -0
  18. package/src/components/style-sections/typography-section/letter-spacing-control.tsx +16 -0
  19. package/src/components/style-sections/typography-section/text-color-control.tsx +16 -0
  20. package/src/{controls/control-types → components/style-sections/typography-section}/text-style-control.tsx +16 -14
  21. package/src/components/style-sections/typography-section/transform-control.tsx +23 -0
  22. package/src/components/style-sections/typography-section/typography-section.tsx +34 -0
  23. package/src/components/style-sections/typography-section/word-spacing-control.tsx +16 -0
  24. package/src/components/style-tab.tsx +30 -6
  25. package/src/contexts/element-context.tsx +5 -3
  26. package/src/contexts/style-context.tsx +8 -2
  27. package/src/controls/components/control-container.tsx +18 -0
  28. package/src/controls/components/control-toggle-button-group.tsx +59 -0
  29. package/src/controls/components/text-field-inner-selection.tsx +79 -0
  30. package/src/controls/control-replacement.ts +26 -0
  31. package/src/controls/control-types/color-control.tsx +24 -0
  32. package/src/controls/control-types/image-control.tsx +3 -18
  33. package/src/controls/control-types/number-control.tsx +25 -0
  34. package/src/controls/control-types/size-control.tsx +22 -34
  35. package/src/controls/control-types/text-area-control.tsx +1 -1
  36. package/src/controls/control-types/toggle-control.tsx +25 -0
  37. package/src/controls/control.tsx +50 -0
  38. package/src/controls/{get-control-by-type.ts → controls-registry.tsx} +13 -9
  39. package/src/controls/hooks/use-style-control.ts +2 -1
  40. package/src/controls/settings-control.tsx +8 -21
  41. package/src/dynamics/components/dynamic-selection-control.tsx +180 -0
  42. package/src/dynamics/components/dynamic-selection.tsx +144 -0
  43. package/src/dynamics/dynamic-control.tsx +42 -0
  44. package/src/dynamics/hooks/use-dynamic-tag.ts +10 -0
  45. package/src/dynamics/hooks/use-prop-dynamic-tags.ts +36 -0
  46. package/src/dynamics/init.ts +10 -0
  47. package/src/dynamics/sync/get-atomic-dynamic-tags.ts +14 -0
  48. package/src/dynamics/sync/get-elementor-config.ts +7 -0
  49. package/src/dynamics/types.ts +32 -0
  50. package/src/dynamics/utils.ts +9 -0
  51. package/src/hooks/use-element-type.ts +5 -0
  52. package/src/index.ts +3 -0
  53. package/src/init.ts +4 -0
  54. package/src/props/is-transformable.ts +14 -0
  55. package/src/sync/types.ts +2 -1
  56. package/src/sync/update-style.ts +2 -2
  57. package/src/types.ts +17 -0
  58. package/LICENSE +0 -674
  59. package/src/components/style-sections/typography-section.tsx +0 -15
package/dist/index.mjs CHANGED
@@ -1,9 +1,34 @@
1
+ // src/controls/control-replacement.ts
2
+ var controlReplacement;
3
+ var replaceControl = ({ component, condition }) => {
4
+ controlReplacement = { component, condition };
5
+ };
6
+ var getControlReplacement = ({ value }) => {
7
+ let shouldReplace = false;
8
+ try {
9
+ shouldReplace = !!controlReplacement?.condition({ value });
10
+ } catch {
11
+ }
12
+ return shouldReplace ? controlReplacement?.component : void 0;
13
+ };
14
+
15
+ // src/controls/control-context.tsx
16
+ import { createContext, useContext } from "react";
17
+ var ControlContext = createContext(null);
18
+ function useControl(defaultValue) {
19
+ const controlContext = useContext(ControlContext);
20
+ if (!controlContext) {
21
+ throw new Error("useControl must be used within a ControlContext");
22
+ }
23
+ return { ...controlContext, value: controlContext.value ?? defaultValue };
24
+ }
25
+
1
26
  // src/panel.ts
2
27
  import { __createPanel as createPanel } from "@elementor/editor-panels";
3
28
 
4
29
  // src/components/editing-panel.tsx
5
- import * as React19 from "react";
6
- import { __ as __7 } from "@wordpress/i18n";
30
+ import * as React36 from "react";
31
+ import { __ as __17 } from "@wordpress/i18n";
7
32
 
8
33
  // src/hooks/use-selected-elements.ts
9
34
  import { __privateUseListenTo as useListenTo, commandEndEvent } from "@elementor/editor-v1-adapters";
@@ -54,9 +79,13 @@ function useElementType(type) {
54
79
  if (!elementType?.atomic_controls) {
55
80
  return null;
56
81
  }
82
+ if (!elementType?.atomic_props_schema) {
83
+ return null;
84
+ }
57
85
  return {
58
86
  key: type,
59
87
  controls: elementType.atomic_controls,
88
+ propsSchema: elementType.atomic_props_schema,
60
89
  title: elementType.title
61
90
  };
62
91
  },
@@ -69,13 +98,13 @@ import { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from "@elementor/edit
69
98
 
70
99
  // src/contexts/element-context.tsx
71
100
  import * as React from "react";
72
- import { createContext, useContext } from "react";
73
- var Context = createContext(null);
74
- function ElementContext({ children, element }) {
75
- return /* @__PURE__ */ React.createElement(Context.Provider, { value: { element } }, children);
101
+ import { createContext as createContext2, useContext as useContext2 } from "react";
102
+ var Context = createContext2(null);
103
+ function ElementContext({ children, element, elementType }) {
104
+ return /* @__PURE__ */ React.createElement(Context.Provider, { value: { element, elementType } }, children);
76
105
  }
77
106
  function useElementContext() {
78
- const context = useContext(Context);
107
+ const context = useContext2(Context);
79
108
  if (!context) {
80
109
  throw new Error("useElementContext must be used within a ElementContextProvider");
81
110
  }
@@ -83,30 +112,16 @@ function useElementContext() {
83
112
  }
84
113
 
85
114
  // src/components/editing-panel-tabs.tsx
86
- import { Stack as Stack10, Tabs, Tab, TabPanel, useTabs } from "@elementor/ui";
87
- import * as React18 from "react";
88
- import { __ as __6 } from "@wordpress/i18n";
115
+ import { Stack as Stack11, Tabs, Tab, TabPanel, useTabs } from "@elementor/ui";
116
+ import * as React35 from "react";
117
+ import { __ as __16 } from "@wordpress/i18n";
89
118
 
90
119
  // src/components/settings-tab.tsx
91
- import * as React9 from "react";
120
+ import * as React13 from "react";
92
121
  import { Stack as Stack3 } from "@elementor/ui";
93
122
 
94
123
  // src/controls/settings-control.tsx
95
- import * as React3 from "react";
96
-
97
- // src/controls/control-context.tsx
98
- import { createContext as createContext2, useContext as useContext2 } from "react";
99
- var ControlContext = createContext2(null);
100
- function useControl(defaultValue) {
101
- const controlContext = useContext2(ControlContext);
102
- if (!controlContext) {
103
- throw new Error("useControl must be used within a ControlContext");
104
- }
105
- return { ...controlContext, value: controlContext.value ?? defaultValue };
106
- }
107
-
108
- // src/controls/settings-control.tsx
109
- import { Stack, styled } from "@elementor/ui";
124
+ import * as React4 from "react";
110
125
 
111
126
  // src/hooks/use-widget-settings.ts
112
127
  import { commandEndEvent as commandEndEvent3, __privateUseListenTo as useListenTo3 } from "@elementor/editor-v1-adapters";
@@ -150,10 +165,28 @@ var ControlLabel = ({ children }) => {
150
165
  return /* @__PURE__ */ React2.createElement(Typography, { component: "label", variant: "caption", color: "text.secondary" }, children);
151
166
  };
152
167
 
168
+ // src/controls/components/control-container.tsx
169
+ import * as React3 from "react";
170
+ import { Stack, styled } from "@elementor/ui";
171
+ var StyledStack = styled(Stack)(({ theme, gap, direction }) => ({
172
+ "> :only-child": {
173
+ width: "100%"
174
+ },
175
+ "&:where( :has( > :nth-child( 2 ):last-child ) ) > :where( * )": {
176
+ width: direction === "column" ? "100%" : `calc( 50% - ${theme.spacing(gap / 2)})`
177
+ },
178
+ "&:where( :has( > :nth-child( 3 ):last-child ) ) > :where( * )": {
179
+ width: direction === "column" ? "100%" : `calc( 33.3333% - ${theme.spacing(gap * 2)} / 3)`
180
+ }
181
+ }));
182
+ var ControlContainer = (props) => /* @__PURE__ */ React3.createElement(StyledStack, { gap: 1, direction: "row", alignItems: "center", justifyContent: "space-between", ...props });
183
+
153
184
  // src/controls/settings-control.tsx
154
185
  var SettingsControlProvider = ({ bind, children }) => {
155
- const { element } = useElementContext();
156
- const value = useWidgetSettings({ id: element.id, bind });
186
+ const { element, elementType } = useElementContext();
187
+ const defaultValue = elementType.propsSchema[bind]?.type.default;
188
+ const settingsValue = useWidgetSettings({ id: element.id, bind });
189
+ const value = settingsValue ?? defaultValue ?? null;
157
190
  const setValue = (newValue) => {
158
191
  updateSettings({
159
192
  id: element.id,
@@ -162,97 +195,36 @@ var SettingsControlProvider = ({ bind, children }) => {
162
195
  }
163
196
  });
164
197
  };
165
- return /* @__PURE__ */ React3.createElement(ControlContext.Provider, { value: { setValue, value, bind } }, children);
198
+ return /* @__PURE__ */ React4.createElement(ControlContext.Provider, { value: { setValue, value, bind } }, children);
166
199
  };
167
- var SettingsControl = ({ children, bind }) => /* @__PURE__ */ React3.createElement(SettingsControlProvider, { bind }, /* @__PURE__ */ React3.createElement(StyledStack, { direction: "row", alignItems: "center", justifyContent: "space-between", flexWrap: "wrap" }, children));
168
- var StyledStack = styled(Stack)(({ theme }) => {
169
- const gap = theme.spacing(1);
170
- return {
171
- gap,
172
- "& > *": {
173
- width: `calc(50% - ${gap} / 2)`
174
- },
175
- "& > label": {
176
- flexShrink: 0
177
- }
178
- };
179
- });
200
+ var SettingsControl = ({ children, bind }) => /* @__PURE__ */ React4.createElement(SettingsControlProvider, { bind }, /* @__PURE__ */ React4.createElement(ControlContainer, { flexWrap: "wrap" }, children));
180
201
  SettingsControl.Label = ControlLabel;
181
202
 
182
203
  // src/components/accordion-section.tsx
183
- import * as React4 from "react";
204
+ import * as React5 from "react";
184
205
  import { useId } from "react";
185
206
  import { Accordion, AccordionSummary, AccordionDetails, AccordionSummaryText, Stack as Stack2 } from "@elementor/ui";
186
207
  var AccordionSection = ({ title, children }) => {
187
208
  const uid = useId();
188
209
  const labelId = `label-${uid}`;
189
210
  const contentId = `content-${uid}`;
190
- return /* @__PURE__ */ React4.createElement(Accordion, { disableGutters: true, defaultExpanded: true }, /* @__PURE__ */ React4.createElement(AccordionSummary, { "aria-controls": contentId, id: labelId }, /* @__PURE__ */ React4.createElement(AccordionSummaryText, { primaryTypographyProps: { variant: "caption" } }, title)), /* @__PURE__ */ React4.createElement(AccordionDetails, { id: contentId, "aria-labelledby": labelId }, /* @__PURE__ */ React4.createElement(Stack2, { gap: 2.5 }, children)));
191
- };
192
-
193
- // src/controls/control-types/select-control.tsx
194
- import * as React5 from "react";
195
- import { MenuItem, Select } from "@elementor/ui";
196
- var SelectControl = ({ options }) => {
197
- const { value, setValue } = useControl();
198
- const handleChange = (event) => {
199
- setValue(event.target.value);
200
- };
201
- return /* @__PURE__ */ React5.createElement(Select, { size: "tiny", value: value ?? "", onChange: handleChange }, options.map(({ label, ...props }) => /* @__PURE__ */ React5.createElement(MenuItem, { key: props.value, ...props }, label)));
202
- };
203
-
204
- // src/controls/control-types/text-area-control.tsx
205
- import * as React6 from "react";
206
- import { TextField } from "@elementor/ui";
207
- var TextAreaControl = ({ placeholder }) => {
208
- const { value, setValue } = useControl("");
209
- const handleChange = (event) => {
210
- setValue(event.target.value);
211
- };
212
- return /* @__PURE__ */ React6.createElement(
213
- TextField,
214
- {
215
- size: "tiny",
216
- multiline: true,
217
- fullWidth: true,
218
- rows: 5,
219
- value,
220
- onChange: handleChange,
221
- placeholder
222
- }
223
- );
211
+ return /* @__PURE__ */ React5.createElement(Accordion, { disableGutters: true, defaultExpanded: true }, /* @__PURE__ */ React5.createElement(AccordionSummary, { "aria-controls": contentId, id: labelId }, /* @__PURE__ */ React5.createElement(AccordionSummaryText, { primaryTypographyProps: { variant: "caption" } }, title)), /* @__PURE__ */ React5.createElement(AccordionDetails, { id: contentId, "aria-labelledby": labelId }, /* @__PURE__ */ React5.createElement(Stack2, { gap: 2.5 }, children)));
224
212
  };
225
213
 
226
- // src/controls/control-types/text-control.tsx
227
- import * as React7 from "react";
228
- import { TextField as TextField2 } from "@elementor/ui";
229
- var TextControl = ({ placeholder }) => {
230
- const { value, setValue } = useControl("");
231
- const handleChange = (event) => setValue(event.target.value);
232
- return /* @__PURE__ */ React7.createElement(TextField2, { type: "text", size: "tiny", value, onChange: handleChange, placeholder });
233
- };
214
+ // src/controls/control.tsx
215
+ import * as React12 from "react";
216
+ import { createError } from "@elementor/utils";
234
217
 
235
218
  // src/controls/control-types/image-control.tsx
236
- import * as React8 from "react";
219
+ import * as React6 from "react";
237
220
  import { Button, Card, CardMedia, CardOverlay } from "@elementor/ui";
238
221
  import { UploadIcon } from "@elementor/icons";
239
222
  import { __ } from "@wordpress/i18n";
240
223
  import { useWpMediaAttachment, useWpMediaFrame } from "@elementor/wp-media";
241
- var defaultState = {
242
- $$type: "image",
243
- value: {
244
- url: "/wp-content/plugins/elementor/assets/images/placeholder.png"
245
- }
246
- };
247
224
  var ImageControl = () => {
248
- const { value, setValue } = useControl(defaultState);
225
+ const { value, setValue } = useControl();
249
226
  const { data: attachment } = useWpMediaAttachment(value?.value?.attachmentId);
250
- const getImageSrc = () => {
251
- if (attachment?.url) {
252
- return attachment.url;
253
- }
254
- return value?.value?.url ?? defaultState.value.url;
255
- };
227
+ const src = attachment?.url ?? value?.value?.url;
256
228
  const { open } = useWpMediaFrame({
257
229
  types: ["image"],
258
230
  multiple: false,
@@ -266,7 +238,7 @@ var ImageControl = () => {
266
238
  });
267
239
  }
268
240
  });
269
- return /* @__PURE__ */ React8.createElement(Card, { variant: "outlined" }, /* @__PURE__ */ React8.createElement(CardMedia, { image: getImageSrc(), sx: { height: 150 } }), /* @__PURE__ */ React8.createElement(CardOverlay, null, /* @__PURE__ */ React8.createElement(
241
+ return /* @__PURE__ */ React6.createElement(Card, { variant: "outlined" }, /* @__PURE__ */ React6.createElement(CardMedia, { image: src, sx: { height: 150 } }), /* @__PURE__ */ React6.createElement(CardOverlay, null, /* @__PURE__ */ React6.createElement(
270
242
  Button,
271
243
  {
272
244
  color: "inherit",
@@ -277,13 +249,13 @@ var ImageControl = () => {
277
249
  }
278
250
  },
279
251
  __("Select Image", "elementor")
280
- ), /* @__PURE__ */ React8.createElement(
252
+ ), /* @__PURE__ */ React6.createElement(
281
253
  Button,
282
254
  {
283
255
  color: "inherit",
284
256
  size: "small",
285
257
  variant: "text",
286
- startIcon: /* @__PURE__ */ React8.createElement(UploadIcon, null),
258
+ startIcon: /* @__PURE__ */ React6.createElement(UploadIcon, null),
287
259
  onClick: () => {
288
260
  open({ mode: "upload" });
289
261
  }
@@ -292,32 +264,220 @@ var ImageControl = () => {
292
264
  )));
293
265
  };
294
266
 
295
- // src/controls/get-control-by-type.ts
267
+ // src/controls/control-types/text-control.tsx
268
+ import * as React7 from "react";
269
+ import { TextField } from "@elementor/ui";
270
+ var TextControl = ({ placeholder }) => {
271
+ const { value, setValue } = useControl("");
272
+ const handleChange = (event) => setValue(event.target.value);
273
+ return /* @__PURE__ */ React7.createElement(TextField, { type: "text", size: "tiny", value, onChange: handleChange, placeholder });
274
+ };
275
+
276
+ // src/controls/control-types/text-area-control.tsx
277
+ import * as React8 from "react";
278
+ import { TextField as TextField2 } from "@elementor/ui";
279
+ var TextAreaControl = ({ placeholder }) => {
280
+ const { value, setValue } = useControl();
281
+ const handleChange = (event) => {
282
+ setValue(event.target.value);
283
+ };
284
+ return /* @__PURE__ */ React8.createElement(
285
+ TextField2,
286
+ {
287
+ size: "tiny",
288
+ multiline: true,
289
+ fullWidth: true,
290
+ rows: 5,
291
+ value,
292
+ onChange: handleChange,
293
+ placeholder
294
+ }
295
+ );
296
+ };
297
+
298
+ // src/controls/control-types/size-control.tsx
299
+ import * as React10 from "react";
300
+ import { InputAdornment as InputAdornment2 } from "@elementor/ui";
301
+
302
+ // src/controls/hooks/use-sync-external-state.tsx
303
+ import { useEffect, useState } from "react";
304
+ var useSyncExternalState = ({
305
+ external,
306
+ setExternal,
307
+ persistWhen,
308
+ fallback
309
+ }) => {
310
+ function toExternal(internalValue) {
311
+ if (persistWhen(internalValue)) {
312
+ return internalValue;
313
+ }
314
+ return void 0;
315
+ }
316
+ function toInternal(externalValue, internalValue) {
317
+ if (!externalValue) {
318
+ return fallback(internalValue);
319
+ }
320
+ return externalValue;
321
+ }
322
+ const [internal, setInternal] = useState(toInternal(external, void 0));
323
+ useEffect(() => {
324
+ setInternal((prevInternal) => toInternal(external, prevInternal));
325
+ }, [external]);
326
+ const setInternalValue = (setter) => {
327
+ const setterFn = typeof setter === "function" ? setter : () => setter;
328
+ const updated = setterFn(internal);
329
+ setInternal(updated);
330
+ setExternal(toExternal(updated));
331
+ };
332
+ return [internal, setInternalValue];
333
+ };
334
+
335
+ // src/controls/components/text-field-inner-selection.tsx
336
+ import * as React9 from "react";
337
+ import { bindMenu, bindTrigger, Button as Button2, InputAdornment, Menu, MenuItem, TextField as TextField3, usePopupState } from "@elementor/ui";
338
+ import { useId as useId2 } from "react";
339
+ var TextFieldInnerSelection = ({
340
+ placeholder,
341
+ type,
342
+ value,
343
+ onChange,
344
+ endAdornment,
345
+ startAdornment
346
+ }) => {
347
+ return /* @__PURE__ */ React9.createElement(
348
+ TextField3,
349
+ {
350
+ size: "tiny",
351
+ type,
352
+ value,
353
+ onChange,
354
+ placeholder,
355
+ InputProps: {
356
+ endAdornment,
357
+ startAdornment
358
+ }
359
+ }
360
+ );
361
+ };
362
+ var SelectionEndAdornment = ({
363
+ options: options2,
364
+ onClick,
365
+ value
366
+ }) => {
367
+ const popupState = usePopupState({
368
+ variant: "popover",
369
+ popupId: useId2()
370
+ });
371
+ const handleMenuItemClick = (index) => {
372
+ onClick(options2[index]);
373
+ popupState.close();
374
+ };
375
+ return /* @__PURE__ */ React9.createElement(InputAdornment, { position: "end" }, /* @__PURE__ */ React9.createElement(
376
+ Button2,
377
+ {
378
+ size: "small",
379
+ color: "inherit",
380
+ sx: { font: "inherit", minWidth: "initial" },
381
+ ...bindTrigger(popupState)
382
+ },
383
+ value.toUpperCase()
384
+ ), /* @__PURE__ */ React9.createElement(Menu, { MenuListProps: { dense: true }, ...bindMenu(popupState) }, options2.map((option, index) => /* @__PURE__ */ React9.createElement(MenuItem, { key: option, onClick: () => handleMenuItemClick(index) }, option.toUpperCase()))));
385
+ };
386
+
387
+ // src/controls/control-types/size-control.tsx
388
+ var defaultUnits = ["px", "%", "em", "rem", "vw"];
389
+ var SizeControl = ({ units = defaultUnits, placeholder, startIcon }) => {
390
+ const { value, setValue } = useControl();
391
+ const [state, setState] = useSyncExternalState({
392
+ external: value,
393
+ setExternal: setValue,
394
+ persistWhen: (controlValue) => !!controlValue?.value.size || controlValue?.value.size === 0,
395
+ fallback: (controlValue) => ({
396
+ $$type: "size",
397
+ value: { unit: controlValue?.value.unit || "px", size: NaN }
398
+ })
399
+ });
400
+ const handleUnitChange = (unit) => {
401
+ setState((prev) => ({
402
+ ...prev,
403
+ value: {
404
+ ...prev.value,
405
+ unit
406
+ }
407
+ }));
408
+ };
409
+ const handleSizeChange = (event) => {
410
+ const { value: size } = event.target;
411
+ setState((prev) => ({
412
+ ...prev,
413
+ value: {
414
+ ...prev.value,
415
+ size: size || size === "0" ? parseFloat(size) : NaN
416
+ }
417
+ }));
418
+ };
419
+ return /* @__PURE__ */ React10.createElement(
420
+ TextFieldInnerSelection,
421
+ {
422
+ endAdornment: /* @__PURE__ */ React10.createElement(SelectionEndAdornment, { options: units, onClick: handleUnitChange, value: state.value.unit }),
423
+ placeholder,
424
+ startAdornment: startIcon ?? /* @__PURE__ */ React10.createElement(InputAdornment2, { position: "start" }, startIcon),
425
+ type: "number",
426
+ value: Number.isNaN(state.value.size) ? "" : state.value.size,
427
+ onChange: handleSizeChange
428
+ }
429
+ );
430
+ };
431
+
432
+ // src/controls/control-types/select-control.tsx
433
+ import * as React11 from "react";
434
+ import { MenuItem as MenuItem2, Select } from "@elementor/ui";
435
+ var SelectControl = ({ options: options2 }) => {
436
+ const { value, setValue } = useControl();
437
+ const handleChange = (event) => {
438
+ setValue(event.target.value);
439
+ };
440
+ return /* @__PURE__ */ React11.createElement(Select, { size: "tiny", value: value ?? "", onChange: handleChange }, options2.map(({ label, ...props }) => /* @__PURE__ */ React11.createElement(MenuItem2, { key: props.value, ...props }, label)));
441
+ };
442
+
443
+ // src/controls/controls-registry.tsx
296
444
  var controlTypes = {
297
445
  image: ImageControl,
298
- select: SelectControl,
299
446
  text: TextControl,
300
- textarea: TextAreaControl
447
+ textarea: TextAreaControl,
448
+ size: SizeControl,
449
+ select: SelectControl
301
450
  };
302
- var getControlByType = (type) => {
303
- return controlTypes[type] ?? null;
451
+ var getControlByType = (type) => controlTypes[type];
452
+
453
+ // src/controls/control.tsx
454
+ var ControlTypeError = createError({
455
+ code: "CONTROL_TYPE_NOT_FOUND",
456
+ message: `Control type not found.`
457
+ });
458
+ var Control = ({ props, type }) => {
459
+ const { value } = useControl();
460
+ const ControlByType = getControlByType(type);
461
+ if (!ControlByType) {
462
+ throw new ControlTypeError({
463
+ context: { type }
464
+ });
465
+ }
466
+ const ControlComponent = getControlReplacement({ value }) || ControlByType;
467
+ return /* @__PURE__ */ React12.createElement(ControlComponent, { ...props });
304
468
  };
305
469
 
306
470
  // src/components/settings-tab.tsx
307
471
  var SettingsTab = () => {
308
- const { element } = useElementContext();
309
- const elementType = useElementType(element?.type);
310
- if (!elementType) {
311
- return null;
312
- }
313
- return /* @__PURE__ */ React9.createElement(Stack3, null, elementType.controls.map(({ type, value }, index) => {
472
+ const { elementType } = useElementContext();
473
+ return /* @__PURE__ */ React13.createElement(Stack3, null, elementType.controls.map(({ type, value }, index) => {
314
474
  if (type === "control") {
315
- return /* @__PURE__ */ React9.createElement(Control, { key: value.bind, control: value });
475
+ return /* @__PURE__ */ React13.createElement(Control2, { key: value.bind, control: value });
316
476
  }
317
477
  if (type === "section") {
318
- return /* @__PURE__ */ React9.createElement(AccordionSection, { key: type + "." + index, title: value.label }, value.items?.map((item) => {
478
+ return /* @__PURE__ */ React13.createElement(AccordionSection, { key: type + "." + index, title: value.label }, value.items?.map((item) => {
319
479
  if (item.type === "control") {
320
- return /* @__PURE__ */ React9.createElement(Control, { key: item.value.bind, control: item.value });
480
+ return /* @__PURE__ */ React13.createElement(Control2, { key: item.value.bind, control: item.value });
321
481
  }
322
482
  return null;
323
483
  }));
@@ -325,31 +485,25 @@ var SettingsTab = () => {
325
485
  return null;
326
486
  }));
327
487
  };
328
- var Control = ({ control }) => {
329
- const ControlComponent = getControlByType(control.type);
330
- if (!ControlComponent) {
488
+ var Control2 = ({ control }) => {
489
+ if (!getControlByType(control.type)) {
331
490
  return null;
332
491
  }
333
- return /* @__PURE__ */ React9.createElement(SettingsControl, { bind: control.bind }, control.label ? /* @__PURE__ */ React9.createElement(SettingsControl.Label, null, control.label) : null, /* @__PURE__ */ React9.createElement(
334
- ControlComponent,
335
- {
336
- ...control.props
337
- }
338
- ));
492
+ return /* @__PURE__ */ React13.createElement(SettingsControl, { bind: control.bind }, control.label ? /* @__PURE__ */ React13.createElement(SettingsControl.Label, null, control.label) : null, /* @__PURE__ */ React13.createElement(Control, { type: control.type, props: control.props }));
339
493
  };
340
494
 
341
495
  // src/components/style-tab.tsx
342
- import * as React17 from "react";
496
+ import * as React34 from "react";
343
497
 
344
498
  // src/contexts/style-context.tsx
345
- import * as React10 from "react";
499
+ import * as React14 from "react";
346
500
  import { createContext as createContext3, useContext as useContext3 } from "react";
347
501
  import { useActiveBreakpoint } from "@elementor/editor-responsive";
348
502
  var Context2 = createContext3(null);
349
- function StyleContext({ children, selectedStyleDef }) {
503
+ function StyleContext({ children, selectedStyleDef, selectedClassesProp }) {
350
504
  const breakpoint = useActiveBreakpoint();
351
505
  const selectedMeta = { breakpoint, state: null };
352
- return /* @__PURE__ */ React10.createElement(Context2.Provider, { value: { selectedStyleDef, selectedMeta } }, children);
506
+ return /* @__PURE__ */ React14.createElement(Context2.Provider, { value: { selectedStyleDef, selectedMeta, selectedClassesProp } }, children);
353
507
  }
354
508
  function useStyleContext() {
355
509
  const context = useContext3(Context2);
@@ -380,13 +534,13 @@ var useElementStyles = (elementID) => {
380
534
  };
381
535
 
382
536
  // src/components/style-tab.tsx
383
- import { Stack as Stack9 } from "@elementor/ui";
537
+ import { Stack as Stack10 } from "@elementor/ui";
384
538
 
385
539
  // src/components/style-sections/size-section.tsx
386
- import * as React14 from "react";
540
+ import * as React16 from "react";
387
541
 
388
542
  // src/controls/style-control.tsx
389
- import * as React11 from "react";
543
+ import * as React15 from "react";
390
544
 
391
545
  // src/hooks/use-element-style-prop.ts
392
546
  import { commandEndEvent as commandEndEvent5, __privateUseListenTo as useListenTo5 } from "@elementor/editor-v1-adapters";
@@ -420,7 +574,7 @@ function getVariantByMeta(styleDef, meta) {
420
574
 
421
575
  // src/sync/update-style.ts
422
576
  import { __privateRunCommand as runCommand2 } from "@elementor/editor-v1-adapters";
423
- var updateStyle = ({ elementID, styleDefID, meta, props, bind = "classes" }) => {
577
+ var updateStyle = ({ elementID, styleDefID, meta, props, bind }) => {
424
578
  const container = getContainer(elementID);
425
579
  runCommand2("document/atomic-widgets/styles", {
426
580
  container,
@@ -434,7 +588,7 @@ var updateStyle = ({ elementID, styleDefID, meta, props, bind = "classes" }) =>
434
588
  // src/controls/hooks/use-style-control.ts
435
589
  var useStyleControl = (propName) => {
436
590
  const { element } = useElementContext();
437
- const { selectedStyleDef, selectedMeta } = useStyleContext();
591
+ const { selectedStyleDef, selectedMeta, selectedClassesProp } = useStyleContext();
438
592
  const value = useElementStyleProp({
439
593
  elementID: element.id,
440
594
  styleDefID: selectedStyleDef?.id,
@@ -446,7 +600,8 @@ var useStyleControl = (propName) => {
446
600
  elementID: element.id,
447
601
  styleDefID: selectedStyleDef?.id,
448
602
  props: { [propName]: newValue },
449
- meta: selectedMeta
603
+ meta: selectedMeta,
604
+ bind: selectedClassesProp
450
605
  });
451
606
  };
452
607
  return [value, setValue];
@@ -455,168 +610,44 @@ var useStyleControl = (propName) => {
455
610
  // src/controls/style-control.tsx
456
611
  var StyleControl = ({ bind, children }) => {
457
612
  const [value, setValue] = useStyleControl(bind);
458
- return /* @__PURE__ */ React11.createElement(ControlContext.Provider, { value: { bind, value, setValue } }, children);
613
+ return /* @__PURE__ */ React15.createElement(ControlContext.Provider, { value: { bind, value, setValue } }, children);
459
614
  };
460
615
  StyleControl.Label = ControlLabel;
461
616
 
462
- // src/components/collapsible-content.tsx
463
- import * as React12 from "react";
464
- import { useState } from "react";
465
- import { ChevronDownIcon } from "@elementor/icons";
466
- import { Button as Button2, Collapse, Stack as Stack4, styled as styled2 } from "@elementor/ui";
617
+ // src/components/style-sections/size-section.tsx
618
+ import { Stack as Stack4 } from "@elementor/ui";
467
619
  import { __ as __2 } from "@wordpress/i18n";
468
- var CollapsibleContent = ({ children, defaultOpen = false }) => {
469
- const [open, setOpen] = useState(defaultOpen);
470
- const handleToggle = () => {
471
- setOpen((prevOpen) => !prevOpen);
472
- };
473
- return /* @__PURE__ */ React12.createElement(Stack4, { sx: { py: 0.5 } }, /* @__PURE__ */ React12.createElement(
474
- Button2,
475
- {
476
- fullWidth: true,
477
- size: "small",
478
- color: "secondary",
479
- variant: "outlined",
480
- onClick: handleToggle,
481
- endIcon: /* @__PURE__ */ React12.createElement(ChevronIcon, { open })
482
- },
483
- open ? __2("Show less", "elementor") : __2("Show more", "elementor")
484
- ), /* @__PURE__ */ React12.createElement(Collapse, { in: open, timeout: "auto" }, children));
620
+ var SizeSection = () => {
621
+ return /* @__PURE__ */ React16.createElement(AccordionSection, { title: __2("Size", "elementor") }, /* @__PURE__ */ React16.createElement(Stack4, { gap: 1.5 }, /* @__PURE__ */ React16.createElement(Stack4, { direction: "row", gap: 2 }, /* @__PURE__ */ React16.createElement(Control3, { bind: "width", label: __2("Width", "elementor") }), /* @__PURE__ */ React16.createElement(Control3, { bind: "height", label: __2("Height", "elementor") })), /* @__PURE__ */ React16.createElement(Stack4, { gap: 1.5, sx: { pt: 1.5 } }, /* @__PURE__ */ React16.createElement(Stack4, { direction: "row", gap: 2 }, /* @__PURE__ */ React16.createElement(Control3, { bind: "minWidth", label: __2("Min. Width", "elementor") }), /* @__PURE__ */ React16.createElement(Control3, { bind: "minHeight", label: __2("Min. Height", "elementor") })), /* @__PURE__ */ React16.createElement(Stack4, { direction: "row", gap: 2 }, /* @__PURE__ */ React16.createElement(Control3, { bind: "maxWidth", label: __2("Max. Width", "elementor") }), /* @__PURE__ */ React16.createElement(Control3, { bind: "maxHeight", label: __2("Max. Height", "elementor") })))));
485
622
  };
486
- var ChevronIcon = styled2(ChevronDownIcon, {
487
- shouldForwardProp: (prop) => prop !== "open"
488
- })(({ theme, open }) => ({
489
- transform: open ? "rotate(180deg)" : "rotate(0)",
490
- transition: theme.transitions.create("transform", {
491
- duration: theme.transitions.duration.standard
492
- })
493
- }));
494
-
495
- // src/controls/control-types/size-control.tsx
496
- import * as React13 from "react";
497
- import { MenuItem as MenuItem2, Select as Select2, Stack as Stack5, TextField as TextField3 } from "@elementor/ui";
498
-
499
- // src/controls/hooks/use-sync-external-state.tsx
500
- import { useEffect, useState as useState2 } from "react";
501
- var useSyncExternalState = ({
502
- external,
503
- setExternal,
504
- persistWhen,
505
- fallback
506
- }) => {
507
- function toExternal(internalValue) {
508
- if (persistWhen(internalValue)) {
509
- return internalValue;
510
- }
511
- return void 0;
512
- }
513
- function toInternal(externalValue, internalValue) {
514
- if (!externalValue) {
515
- return fallback(internalValue);
516
- }
517
- return externalValue;
518
- }
519
- const [internal, setInternal] = useState2(toInternal(external, void 0));
520
- useEffect(() => {
521
- setInternal((prevInternal) => toInternal(external, prevInternal));
522
- }, [external]);
523
- const setInternalValue = (setter) => {
524
- const setterFn = typeof setter === "function" ? setter : () => setter;
525
- const updated = setterFn(internal);
526
- setInternal(updated);
527
- setExternal(toExternal(updated));
528
- };
529
- return [internal, setInternalValue];
623
+ var Control3 = ({ label, bind }) => {
624
+ return /* @__PURE__ */ React16.createElement(StyleControl, { bind }, /* @__PURE__ */ React16.createElement(ControlContainer, { direction: "column" }, /* @__PURE__ */ React16.createElement(StyleControl.Label, null, label), /* @__PURE__ */ React16.createElement(Control, { type: "size" })));
530
625
  };
531
626
 
532
- // src/controls/control-types/size-control.tsx
533
- var SizeControl = ({ units: units2, placeholder }) => {
534
- const { value, setValue } = useControl();
535
- const [state, setState] = useSyncExternalState({
536
- external: value,
537
- setExternal: setValue,
538
- persistWhen: (controlValue) => !!controlValue?.value.size || controlValue?.value.size === 0,
539
- fallback: (controlValue) => ({
540
- $$type: "size",
541
- value: { unit: controlValue?.value.unit || "px", size: NaN }
542
- })
543
- });
544
- const handleUnitChange = (event) => {
545
- const unit = event.target.value;
546
- setState((prev) => ({
547
- ...prev,
548
- value: {
549
- ...prev.value,
550
- unit
551
- }
552
- }));
553
- };
554
- const handleSizeChange = (event) => {
555
- const { value: size } = event.target;
556
- setState((prev) => ({
557
- ...prev,
558
- value: {
559
- ...prev.value,
560
- size: size || size === "0" ? parseFloat(size) : NaN
561
- }
562
- }));
563
- };
564
- return /* @__PURE__ */ React13.createElement(Stack5, { direction: "row" }, /* @__PURE__ */ React13.createElement(
565
- TextField3,
566
- {
567
- size: "tiny",
568
- type: "number",
569
- value: Number.isNaN(state.value.size) ? "" : state.value.size,
570
- onChange: handleSizeChange,
571
- placeholder
572
- }
573
- ), /* @__PURE__ */ React13.createElement(
574
- Select2,
575
- {
576
- size: "tiny",
577
- value: state.value.unit,
578
- onChange: handleUnitChange,
579
- MenuProps: {
580
- anchorOrigin: { vertical: "bottom", horizontal: "right" },
581
- transformOrigin: { vertical: "top", horizontal: "right" }
582
- }
583
- },
584
- units2.map((unit) => /* @__PURE__ */ React13.createElement(MenuItem2, { key: unit, value: unit }, unit.toUpperCase()))
585
- ));
586
- };
627
+ // src/components/style-sections/typography-section/typography-section.tsx
628
+ import * as React28 from "react";
629
+ import { Divider, Stack as Stack6 } from "@elementor/ui";
587
630
 
588
- // src/components/style-sections/size-section.tsx
589
- import { Stack as Stack6 } from "@elementor/ui";
631
+ // src/components/style-sections/typography-section/text-style-control.tsx
632
+ import * as React17 from "react";
633
+ import { ToggleButton as ToggleButtonBase, ToggleButtonGroup } from "@elementor/ui";
634
+ import { ItalicIcon, StrikethroughIcon, UnderlineIcon } from "@elementor/icons";
590
635
  import { __ as __3 } from "@wordpress/i18n";
591
- var SizeSection = () => {
592
- return /* @__PURE__ */ React14.createElement(AccordionSection, { title: __3("Size", "elementor") }, /* @__PURE__ */ React14.createElement(Stack6, { gap: 1.5 }, /* @__PURE__ */ React14.createElement(Stack6, { direction: "row", gap: 2 }, /* @__PURE__ */ React14.createElement(Control2, { bind: "width", label: __3("Width", "elementor") }), /* @__PURE__ */ React14.createElement(Control2, { bind: "height", label: __3("Height", "elementor") })), /* @__PURE__ */ React14.createElement(CollapsibleContent, null, /* @__PURE__ */ React14.createElement(Stack6, { gap: 1.5, sx: { pt: 1.5 } }, /* @__PURE__ */ React14.createElement(Stack6, { direction: "row", gap: 2 }, /* @__PURE__ */ React14.createElement(Control2, { bind: "minWidth", label: __3("Min. Width", "elementor") }), /* @__PURE__ */ React14.createElement(Control2, { bind: "minHeight", label: __3("Min. Height", "elementor") })), /* @__PURE__ */ React14.createElement(Stack6, { direction: "row", gap: 2 }, /* @__PURE__ */ React14.createElement(Control2, { bind: "maxWidth", label: __3("Max. Width", "elementor") }), /* @__PURE__ */ React14.createElement(Control2, { bind: "maxHeight", label: __3("Max. Height", "elementor") }))))));
593
- };
594
- var units = ["px", "%", "em", "rem", "vw"];
595
- var Control2 = ({ label, bind }) => {
596
- return /* @__PURE__ */ React14.createElement(StyleControl, { bind }, /* @__PURE__ */ React14.createElement(Stack6, { gap: 1, sx: { flex: "0 1 50%" } }, /* @__PURE__ */ React14.createElement(StyleControl.Label, null, label), /* @__PURE__ */ React14.createElement(SizeControl, { units })));
597
- };
598
-
599
- // src/components/style-sections/typography-section.tsx
600
- import * as React16 from "react";
601
- import { Stack as Stack8 } from "@elementor/ui";
602
-
603
- // src/controls/control-types/text-style-control.tsx
604
- import * as React15 from "react";
605
- import { Stack as Stack7, ToggleButton as ToggleButtonBase, ToggleButtonGroup } from "@elementor/ui";
606
- import { __ as __4 } from "@wordpress/i18n";
636
+ var buttonSize = "tiny";
607
637
  var TextStyleControl = () => {
608
638
  const [fontStyle, setFontStyle] = useStyleControl("fontStyle");
609
639
  const [textDecoration, setTextDecoration] = useStyleControl("textDecoration");
610
640
  const formats = [fontStyle, ...(textDecoration || "").split(" ")];
611
- return /* @__PURE__ */ React15.createElement(Stack7, { direction: "row", justifyContent: "space-between", alignItems: "center" }, /* @__PURE__ */ React15.createElement(ControlLabel, null, __4("Style", "elementor")), /* @__PURE__ */ React15.createElement(ToggleButtonGroup, { value: formats }, /* @__PURE__ */ React15.createElement(
641
+ return /* @__PURE__ */ React17.createElement(ControlContainer, null, /* @__PURE__ */ React17.createElement(ControlLabel, null, __3("Style", "elementor")), /* @__PURE__ */ React17.createElement(ToggleButtonGroup, { value: formats }, /* @__PURE__ */ React17.createElement(
612
642
  ToggleButton,
613
643
  {
614
644
  value: "italic",
615
645
  onChange: (v) => setFontStyle(fontStyle === v ? null : v),
616
- "aria-label": "italic"
646
+ "aria-label": "italic",
647
+ sx: { marginLeft: "auto" }
617
648
  },
618
- /* @__PURE__ */ React15.createElement("span", { style: { fontStyle: "italic", fontSize: "12px" } }, "I")
619
- ), /* @__PURE__ */ React15.createElement(
649
+ /* @__PURE__ */ React17.createElement(ItalicIcon, { fontSize: buttonSize })
650
+ ), /* @__PURE__ */ React17.createElement(
620
651
  ShorthandControl,
621
652
  {
622
653
  value: "line-through",
@@ -624,8 +655,8 @@ var TextStyleControl = () => {
624
655
  updateValues: setTextDecoration,
625
656
  "aria-label": "line-through"
626
657
  },
627
- /* @__PURE__ */ React15.createElement("span", { style: { textDecoration: "line-through", fontSize: "12px" } }, "S")
628
- ), /* @__PURE__ */ React15.createElement(
658
+ /* @__PURE__ */ React17.createElement(StrikethroughIcon, { fontSize: buttonSize })
659
+ ), /* @__PURE__ */ React17.createElement(
629
660
  ShorthandControl,
630
661
  {
631
662
  value: "underline",
@@ -633,7 +664,7 @@ var TextStyleControl = () => {
633
664
  updateValues: setTextDecoration,
634
665
  "aria-label": "underline"
635
666
  },
636
- /* @__PURE__ */ React15.createElement("span", { style: { textDecoration: "underline", fontSize: "12px" } }, "U")
667
+ /* @__PURE__ */ React17.createElement(UnderlineIcon, { fontSize: buttonSize })
637
668
  )));
638
669
  };
639
670
  var ShorthandControl = ({
@@ -652,33 +683,347 @@ var ShorthandControl = ({
652
683
  updateValues([...valuesArr, newValue].join(" "));
653
684
  }
654
685
  };
655
- return /* @__PURE__ */ React15.createElement(ToggleButton, { value, onChange: toggleValue, selected, "aria-label": ariaLabel }, children);
686
+ return /* @__PURE__ */ React17.createElement(ToggleButton, { value, onChange: toggleValue, selected, "aria-label": ariaLabel }, children);
656
687
  };
657
688
  var ToggleButton = ({ onChange, ...props }) => {
658
689
  const handleChange = (_e, newValue) => {
659
690
  onChange(newValue);
660
691
  };
661
- return /* @__PURE__ */ React15.createElement(ToggleButtonBase, { ...props, onChange: handleChange, size: "tiny", sx: { px: 1.5 } });
692
+ return /* @__PURE__ */ React17.createElement(ToggleButtonBase, { ...props, onChange: handleChange, size: buttonSize });
662
693
  };
663
694
 
664
- // src/components/style-sections/typography-section.tsx
695
+ // src/components/style-sections/typography-section/typography-section.tsx
696
+ import { __ as __11 } from "@wordpress/i18n";
697
+
698
+ // src/components/style-sections/typography-section/font-size-control.tsx
699
+ import * as React18 from "react";
700
+ import { __ as __4 } from "@wordpress/i18n";
701
+ var FontSizeControl = () => {
702
+ return /* @__PURE__ */ React18.createElement(StyleControl, { bind: "font-size" }, /* @__PURE__ */ React18.createElement(ControlContainer, null, /* @__PURE__ */ React18.createElement(StyleControl.Label, null, __4("Font Size", "elementor")), /* @__PURE__ */ React18.createElement(SizeControl, null)));
703
+ };
704
+
705
+ // src/components/style-sections/typography-section/font-weight-control.tsx
706
+ import * as React19 from "react";
665
707
  import { __ as __5 } from "@wordpress/i18n";
708
+ var fontWeightOptions = [
709
+ { label: __5("Light - 400", "elementor"), value: 400 },
710
+ { label: __5("Regular - 500", "elementor"), value: 500 },
711
+ { label: __5("Semi Bold - 600", "elementor"), value: 600 },
712
+ { label: __5("Bold - 700", "elementor"), value: 700 },
713
+ { label: __5("Black - 900", "elementor"), value: 900 }
714
+ ];
715
+ var FontWeightControl = () => {
716
+ return /* @__PURE__ */ React19.createElement(StyleControl, { bind: "fontWeight" }, /* @__PURE__ */ React19.createElement(ControlContainer, null, /* @__PURE__ */ React19.createElement(StyleControl.Label, null, __5("Font Weight", "elementor")), /* @__PURE__ */ React19.createElement(SelectControl, { options: fontWeightOptions })));
717
+ };
718
+
719
+ // src/components/style-sections/typography-section/text-color-control.tsx
720
+ import * as React21 from "react";
721
+ import { __ as __6 } from "@wordpress/i18n";
722
+
723
+ // src/controls/control-types/color-control.tsx
724
+ import * as React20 from "react";
725
+ import { UnstableColorPicker } from "@elementor/ui";
726
+ var ColorControl = () => {
727
+ const { value, setValue } = useControl();
728
+ const handleChange = debounce((selectedColor) => {
729
+ setValue(selectedColor);
730
+ });
731
+ return /* @__PURE__ */ React20.createElement(UnstableColorPicker, { value, onChange: handleChange });
732
+ };
733
+ var debounce = (func, wait = 300) => {
734
+ let timer;
735
+ return (...args) => {
736
+ clearTimeout(timer);
737
+ timer = setTimeout(() => func(...args), wait);
738
+ };
739
+ };
740
+
741
+ // src/components/style-sections/typography-section/text-color-control.tsx
742
+ var TextColorControl = () => {
743
+ return /* @__PURE__ */ React21.createElement(StyleControl, { bind: "color" }, /* @__PURE__ */ React21.createElement(ControlContainer, null, /* @__PURE__ */ React21.createElement(StyleControl.Label, null, __6("Text Color", "elementor")), /* @__PURE__ */ React21.createElement(ColorControl, null)));
744
+ };
745
+
746
+ // src/components/style-sections/typography-section/letter-spacing-control.tsx
747
+ import * as React22 from "react";
748
+ import { __ as __7 } from "@wordpress/i18n";
749
+ var LetterSpacingControl = () => {
750
+ return /* @__PURE__ */ React22.createElement(StyleControl, { bind: "letter-spacing" }, /* @__PURE__ */ React22.createElement(ControlContainer, null, /* @__PURE__ */ React22.createElement(StyleControl.Label, null, __7("Letter Spacing", "elementor")), /* @__PURE__ */ React22.createElement(SizeControl, null)));
751
+ };
752
+
753
+ // src/components/style-sections/typography-section/word-spacing-control.tsx
754
+ import * as React23 from "react";
755
+ import { __ as __8 } from "@wordpress/i18n";
756
+ var WordSpacingControl = () => {
757
+ return /* @__PURE__ */ React23.createElement(StyleControl, { bind: "word-spacing" }, /* @__PURE__ */ React23.createElement(ControlContainer, null, /* @__PURE__ */ React23.createElement(StyleControl.Label, null, __8("Word Spacing", "elementor")), /* @__PURE__ */ React23.createElement(SizeControl, null)));
758
+ };
759
+
760
+ // src/components/collapsible-content.tsx
761
+ import * as React24 from "react";
762
+ import { useState as useState2 } from "react";
763
+ import { ChevronDownIcon } from "@elementor/icons";
764
+ import { Button as Button3, Collapse, Stack as Stack5, styled as styled2 } from "@elementor/ui";
765
+ import { __ as __9 } from "@wordpress/i18n";
766
+ var CollapsibleContent = ({ children, defaultOpen = false }) => {
767
+ const [open, setOpen] = useState2(defaultOpen);
768
+ const handleToggle = () => {
769
+ setOpen((prevOpen) => !prevOpen);
770
+ };
771
+ return /* @__PURE__ */ React24.createElement(Stack5, { sx: { py: 0.5 } }, /* @__PURE__ */ React24.createElement(
772
+ Button3,
773
+ {
774
+ fullWidth: true,
775
+ size: "small",
776
+ color: "secondary",
777
+ variant: "outlined",
778
+ onClick: handleToggle,
779
+ endIcon: /* @__PURE__ */ React24.createElement(ChevronIcon, { open })
780
+ },
781
+ open ? __9("Show less", "elementor") : __9("Show more", "elementor")
782
+ ), /* @__PURE__ */ React24.createElement(Collapse, { in: open, timeout: "auto" }, children));
783
+ };
784
+ var ChevronIcon = styled2(ChevronDownIcon, {
785
+ shouldForwardProp: (prop) => prop !== "open"
786
+ })(({ theme, open }) => ({
787
+ transform: open ? "rotate(180deg)" : "rotate(0)",
788
+ transition: theme.transitions.create("transform", {
789
+ duration: theme.transitions.duration.standard
790
+ })
791
+ }));
792
+
793
+ // src/components/style-sections/typography-section/transform-control.tsx
794
+ import * as React27 from "react";
795
+ import { __ as __10 } from "@wordpress/i18n";
796
+
797
+ // src/controls/control-types/toggle-control.tsx
798
+ import * as React26 from "react";
799
+
800
+ // src/controls/components/control-toggle-button-group.tsx
801
+ import * as React25 from "react";
802
+ import { styled as styled3, ToggleButton as ToggleButton2, ToggleButtonGroup as ToggleButtonGroup2 } from "@elementor/ui";
803
+ var StyledToggleButtonGroup = styled3(ToggleButtonGroup2)`
804
+ ${({ justify }) => `justify-content: ${justify};`}
805
+ `;
806
+ var ControlToggleButtonGroup = ({
807
+ justify = "end",
808
+ size = "tiny",
809
+ value,
810
+ onChange,
811
+ items,
812
+ exclusive = false
813
+ }) => {
814
+ const handleChange = (_, newValue) => {
815
+ onChange(newValue);
816
+ };
817
+ return /* @__PURE__ */ React25.createElement(StyledToggleButtonGroup, { justify, value, onChange: handleChange, exclusive }, items.map(({ label, value: buttonValue, icon: Icon }) => /* @__PURE__ */ React25.createElement(ToggleButton2, { key: buttonValue, value: buttonValue, "aria-label": label, size }, /* @__PURE__ */ React25.createElement(Icon, { fontSize: size }))));
818
+ };
819
+
820
+ // src/controls/control-types/toggle-control.tsx
821
+ var ToggleControl = ({ options: options2 }) => {
822
+ const { value, setValue } = useControl();
823
+ const handleToggle = (option) => {
824
+ setValue(option || void 0);
825
+ };
826
+ return /* @__PURE__ */ React26.createElement(
827
+ ControlToggleButtonGroup,
828
+ {
829
+ items: options2,
830
+ value: value || null,
831
+ onChange: handleToggle,
832
+ exclusive: true
833
+ }
834
+ );
835
+ };
836
+
837
+ // src/components/style-sections/typography-section/transform-control.tsx
838
+ import { LetterCaseIcon, LetterCaseLowerIcon, LetterCaseUpperIcon } from "@elementor/icons";
839
+ var options = [
840
+ { value: "capitalize", label: __10("Capitalize", "elementor"), icon: LetterCaseIcon },
841
+ { value: "uppercase", label: __10("Uppercase", "elementor"), icon: LetterCaseUpperIcon },
842
+ { value: "lowercase", label: __10("Lowercase", "elementor"), icon: LetterCaseLowerIcon }
843
+ ];
844
+ var TransformControl = () => {
845
+ return /* @__PURE__ */ React27.createElement(ControlContainer, null, /* @__PURE__ */ React27.createElement(StyleControl.Label, null, __10("Transform", "elementor")), /* @__PURE__ */ React27.createElement(StyleControl, { bind: "text-transform" }, /* @__PURE__ */ React27.createElement(ToggleControl, { options })));
846
+ };
847
+
848
+ // src/components/style-sections/typography-section/typography-section.tsx
666
849
  var TypographySection = () => {
667
- return /* @__PURE__ */ React16.createElement(AccordionSection, { title: __5("Typography", "elementor") }, /* @__PURE__ */ React16.createElement(Stack8, { gap: 1.5 }, /* @__PURE__ */ React16.createElement(TextStyleControl, null)));
850
+ return /* @__PURE__ */ React28.createElement(AccordionSection, { title: __11("Typography", "elementor") }, /* @__PURE__ */ React28.createElement(Stack6, { gap: 1.5 }, /* @__PURE__ */ React28.createElement(FontWeightControl, null), /* @__PURE__ */ React28.createElement(FontSizeControl, null), /* @__PURE__ */ React28.createElement(Divider, null), /* @__PURE__ */ React28.createElement(TextColorControl, null), /* @__PURE__ */ React28.createElement(CollapsibleContent, null, /* @__PURE__ */ React28.createElement(Stack6, { gap: 1.5, sx: { pt: 1.5 } }, /* @__PURE__ */ React28.createElement(LetterSpacingControl, null), /* @__PURE__ */ React28.createElement(WordSpacingControl, null), /* @__PURE__ */ React28.createElement(Divider, null), /* @__PURE__ */ React28.createElement(TextStyleControl, null), /* @__PURE__ */ React28.createElement(TransformControl, null)))));
851
+ };
852
+
853
+ // src/components/style-sections/position-section/position-section.tsx
854
+ import * as React31 from "react";
855
+ import { Stack as Stack7 } from "@elementor/ui";
856
+
857
+ // src/components/style-sections/position-section/z-index-control.tsx
858
+ import * as React30 from "react";
859
+ import { __ as __12 } from "@wordpress/i18n";
860
+
861
+ // src/controls/control-types/number-control.tsx
862
+ import * as React29 from "react";
863
+ import { TextField as TextField4 } from "@elementor/ui";
864
+ var isEmptyOrNaN = (value) => value === void 0 || value === "" || Number.isNaN(Number(value));
865
+ var NumberControl = ({ placeholder }) => {
866
+ const { value, setValue } = useControl();
867
+ const handleChange = (event) => {
868
+ const eventValue = event.target.value;
869
+ setValue(isEmptyOrNaN(eventValue) ? void 0 : Number(eventValue));
870
+ };
871
+ return /* @__PURE__ */ React29.createElement(
872
+ TextField4,
873
+ {
874
+ size: "tiny",
875
+ type: "number",
876
+ value: isEmptyOrNaN(value) ? "" : value,
877
+ onChange: handleChange,
878
+ placeholder
879
+ }
880
+ );
881
+ };
882
+
883
+ // src/components/style-sections/position-section/z-index-control.tsx
884
+ var ZIndexControl = () => {
885
+ return /* @__PURE__ */ React30.createElement(StyleControl, { bind: "zIndex" }, /* @__PURE__ */ React30.createElement(ControlContainer, null, /* @__PURE__ */ React30.createElement(StyleControl.Label, null, __12("Z-Index", "elementor")), /* @__PURE__ */ React30.createElement(NumberControl, null)));
886
+ };
887
+
888
+ // src/components/style-sections/position-section/position-section.tsx
889
+ import { __ as __13 } from "@wordpress/i18n";
890
+ var PositionSection = () => {
891
+ return /* @__PURE__ */ React31.createElement(AccordionSection, { title: __13("Position", "elementor") }, /* @__PURE__ */ React31.createElement(Stack7, { gap: 1.5 }, /* @__PURE__ */ React31.createElement(ZIndexControl, null)));
892
+ };
893
+
894
+ // src/components/style-sections/spacing-section/spacing-section.tsx
895
+ import * as React33 from "react";
896
+ import { Divider as Divider2, Stack as Stack9 } from "@elementor/ui";
897
+ import { __ as __15 } from "@wordpress/i18n";
898
+
899
+ // src/components/style-sections/spacing-section/linked-dimensions-control.tsx
900
+ import * as React32 from "react";
901
+ import { Stack as Stack8, ToggleButton as ToggleButton3 } from "@elementor/ui";
902
+ import { DetachIcon, LinkIcon, SideBottomIcon, SideLeftIcon, SideRightIcon, SideTopIcon } from "@elementor/icons";
903
+ import { __ as __14 } from "@wordpress/i18n";
904
+ var LinkedDimensionsControl = ({ label }) => {
905
+ const { value, setValue } = useControl();
906
+ const { top, right, bottom, left, isLinked = false } = value?.value || {};
907
+ const setLinkedValue = (position, newValue) => {
908
+ const updatedValue = {
909
+ isLinked,
910
+ top: isLinked ? newValue : top,
911
+ right: isLinked ? newValue : right,
912
+ bottom: isLinked ? newValue : bottom,
913
+ left: isLinked ? newValue : left,
914
+ [position]: newValue
915
+ };
916
+ setValue({
917
+ $$type: "linked-dimensions",
918
+ value: updatedValue
919
+ });
920
+ };
921
+ const toggleLinked = () => {
922
+ const updatedValue = {
923
+ isLinked: !isLinked,
924
+ top,
925
+ right: !isLinked ? top : right,
926
+ bottom: !isLinked ? top : bottom,
927
+ left: !isLinked ? top : left
928
+ };
929
+ setValue({
930
+ $$type: "linked-dimensions",
931
+ value: updatedValue
932
+ });
933
+ };
934
+ const LinkedIcon = isLinked ? LinkIcon : DetachIcon;
935
+ return /* @__PURE__ */ React32.createElement(React32.Fragment, null, /* @__PURE__ */ React32.createElement(Stack8, { direction: "row", gap: 2 }, /* @__PURE__ */ React32.createElement(ControlLabel, null, label), /* @__PURE__ */ React32.createElement(
936
+ ToggleButton3,
937
+ {
938
+ "aria-label": __14("Link Inputs", "elementor"),
939
+ size: "tiny",
940
+ value: "check",
941
+ selected: isLinked,
942
+ sx: { marginLeft: "auto" },
943
+ onChange: toggleLinked
944
+ },
945
+ /* @__PURE__ */ React32.createElement(LinkedIcon, { fontSize: "tiny" })
946
+ )), /* @__PURE__ */ React32.createElement(Stack8, { direction: "row", gap: 2 }, /* @__PURE__ */ React32.createElement(ControlContainer, { direction: "column" }, /* @__PURE__ */ React32.createElement(ControlLabel, null, __14("Top", "elementor")), /* @__PURE__ */ React32.createElement(
947
+ Control4,
948
+ {
949
+ bind: "top",
950
+ value: top,
951
+ setValue: setLinkedValue,
952
+ startIcon: /* @__PURE__ */ React32.createElement(SideTopIcon, { fontSize: "tiny" })
953
+ }
954
+ )), /* @__PURE__ */ React32.createElement(ControlContainer, { direction: "column" }, /* @__PURE__ */ React32.createElement(ControlLabel, null, __14("Right", "elementor")), /* @__PURE__ */ React32.createElement(
955
+ Control4,
956
+ {
957
+ bind: "right",
958
+ value: right,
959
+ setValue: setLinkedValue,
960
+ startIcon: /* @__PURE__ */ React32.createElement(SideRightIcon, { fontSize: "tiny" })
961
+ }
962
+ ))), /* @__PURE__ */ React32.createElement(Stack8, { direction: "row", gap: 2 }, /* @__PURE__ */ React32.createElement(ControlContainer, { direction: "column" }, /* @__PURE__ */ React32.createElement(ControlLabel, null, __14("Bottom", "elementor")), /* @__PURE__ */ React32.createElement(
963
+ Control4,
964
+ {
965
+ bind: "bottom",
966
+ value: bottom,
967
+ setValue: setLinkedValue,
968
+ startIcon: /* @__PURE__ */ React32.createElement(SideBottomIcon, { fontSize: "tiny" })
969
+ }
970
+ )), /* @__PURE__ */ React32.createElement(ControlContainer, { direction: "column" }, /* @__PURE__ */ React32.createElement(ControlLabel, null, __14("Left", "elementor")), /* @__PURE__ */ React32.createElement(
971
+ Control4,
972
+ {
973
+ bind: "left",
974
+ value: left,
975
+ setValue: setLinkedValue,
976
+ startIcon: /* @__PURE__ */ React32.createElement(SideLeftIcon, { fontSize: "tiny" })
977
+ }
978
+ ))));
979
+ };
980
+ var Control4 = ({
981
+ bind,
982
+ startIcon,
983
+ value,
984
+ setValue
985
+ }) => /* @__PURE__ */ React32.createElement(
986
+ ControlContext.Provider,
987
+ {
988
+ value: {
989
+ bind,
990
+ setValue: (newValue) => setValue(bind, newValue),
991
+ value
992
+ }
993
+ },
994
+ /* @__PURE__ */ React32.createElement(SizeControl, { startIcon })
995
+ );
996
+
997
+ // src/components/style-sections/spacing-section/spacing-section.tsx
998
+ var SpacingSection = () => {
999
+ return /* @__PURE__ */ React33.createElement(AccordionSection, { title: __15("Spacing", "elementor") }, /* @__PURE__ */ React33.createElement(Stack9, { gap: 1.5 }, /* @__PURE__ */ React33.createElement(StyleControl, { bind: "padding" }, /* @__PURE__ */ React33.createElement(LinkedDimensionsControl, { label: __15("Padding", "elementor") })), /* @__PURE__ */ React33.createElement(Divider2, null), /* @__PURE__ */ React33.createElement(StyleControl, { bind: "margin" }, /* @__PURE__ */ React33.createElement(LinkedDimensionsControl, { label: __15("Margin", "elementor") }))));
668
1000
  };
669
1001
 
670
1002
  // src/components/style-tab.tsx
1003
+ var CLASSES_PROP_KEY = "classes";
671
1004
  var StyleTab = () => {
1005
+ const styleDefinition = useStyleDefinition();
1006
+ const classesProp = useClassesProp();
1007
+ return /* @__PURE__ */ React34.createElement(StyleContext, { selectedStyleDef: styleDefinition, selectedClassesProp: classesProp }, /* @__PURE__ */ React34.createElement(Stack10, null, /* @__PURE__ */ React34.createElement(SizeSection, null), /* @__PURE__ */ React34.createElement(PositionSection, null), /* @__PURE__ */ React34.createElement(TypographySection, null), /* @__PURE__ */ React34.createElement(SpacingSection, null)));
1008
+ };
1009
+ function useClassesProp() {
1010
+ const { elementType } = useElementContext();
1011
+ const prop = Object.entries(elementType.propsSchema).find(([, { type }]) => type.key === CLASSES_PROP_KEY);
1012
+ if (!prop) {
1013
+ throw new Error("Element does not have a classes prop");
1014
+ }
1015
+ return prop[0];
1016
+ }
1017
+ function useStyleDefinition() {
672
1018
  const { element } = useElementContext();
673
1019
  const elementStyles = useElementStyles(element.id);
674
- const [selectedStyleDef = null] = Object.values(elementStyles || {});
675
- return /* @__PURE__ */ React17.createElement(StyleContext, { selectedStyleDef }, /* @__PURE__ */ React17.createElement(Stack9, null, /* @__PURE__ */ React17.createElement(SizeSection, null), /* @__PURE__ */ React17.createElement(TypographySection, null)));
676
- };
1020
+ return Object.values(elementStyles || {})[0] ?? null;
1021
+ }
677
1022
 
678
1023
  // src/components/editing-panel-tabs.tsx
679
1024
  var EditingPanelTabs = () => {
680
1025
  const { getTabProps, getTabPanelProps, getTabsProps } = useTabs("settings");
681
- return /* @__PURE__ */ React18.createElement(Stack10, { direction: "column", sx: { width: "100%" } }, /* @__PURE__ */ React18.createElement(Tabs, { variant: "fullWidth", indicatorColor: "secondary", textColor: "inherit", ...getTabsProps() }, /* @__PURE__ */ React18.createElement(Tab, { label: __6("General", "elementor"), ...getTabProps("settings") }), /* @__PURE__ */ React18.createElement(Tab, { label: __6("Style", "elementor"), ...getTabProps("style") })), /* @__PURE__ */ React18.createElement(TabPanel, { ...getTabPanelProps("settings"), disablePadding: true }, /* @__PURE__ */ React18.createElement(SettingsTab, null)), /* @__PURE__ */ React18.createElement(TabPanel, { ...getTabPanelProps("style"), disablePadding: true }, /* @__PURE__ */ React18.createElement(StyleTab, null)));
1026
+ return /* @__PURE__ */ React35.createElement(Stack11, { direction: "column", sx: { width: "100%" } }, /* @__PURE__ */ React35.createElement(Tabs, { variant: "fullWidth", indicatorColor: "secondary", textColor: "inherit", ...getTabsProps() }, /* @__PURE__ */ React35.createElement(Tab, { label: __16("General", "elementor"), ...getTabProps("settings") }), /* @__PURE__ */ React35.createElement(Tab, { label: __16("Style", "elementor"), ...getTabProps("style") })), /* @__PURE__ */ React35.createElement(TabPanel, { ...getTabPanelProps("settings"), disablePadding: true }, /* @__PURE__ */ React35.createElement(SettingsTab, null)), /* @__PURE__ */ React35.createElement(TabPanel, { ...getTabPanelProps("style"), disablePadding: true }, /* @__PURE__ */ React35.createElement(StyleTab, null)));
682
1027
  };
683
1028
 
684
1029
  // src/components/editing-panel.tsx
@@ -689,8 +1034,8 @@ var EditingPanel = () => {
689
1034
  if (elements.length !== 1 || !elementType) {
690
1035
  return null;
691
1036
  }
692
- const panelTitle = __7("Edit %s", "elementor").replace("%s", elementType.title);
693
- return /* @__PURE__ */ React19.createElement(Panel, null, /* @__PURE__ */ React19.createElement(PanelHeader, null, /* @__PURE__ */ React19.createElement(PanelHeaderTitle, null, panelTitle)), /* @__PURE__ */ React19.createElement(PanelBody, null, /* @__PURE__ */ React19.createElement(ElementContext, { element: selectedElement }, /* @__PURE__ */ React19.createElement(EditingPanelTabs, null))));
1037
+ const panelTitle = __17("Edit %s", "elementor").replace("%s", elementType.title);
1038
+ return /* @__PURE__ */ React36.createElement(Panel, null, /* @__PURE__ */ React36.createElement(PanelHeader, null, /* @__PURE__ */ React36.createElement(PanelHeaderTitle, null, panelTitle)), /* @__PURE__ */ React36.createElement(PanelBody, null, /* @__PURE__ */ React36.createElement(ElementContext, { element: selectedElement, elementType }, /* @__PURE__ */ React36.createElement(EditingPanelTabs, null))));
694
1039
  };
695
1040
 
696
1041
  // src/panel.ts
@@ -735,13 +1080,314 @@ var EditingPanelHooks = () => {
735
1080
  // src/init.ts
736
1081
  import { __registerPanel as registerPanel } from "@elementor/editor-panels";
737
1082
  import { __privateBlockDataCommand as blockDataCommand } from "@elementor/editor-v1-adapters";
738
- function init() {
1083
+
1084
+ // src/dynamics/components/dynamic-selection-control.tsx
1085
+ import * as React39 from "react";
1086
+ import { useId as useId3 } from "react";
1087
+
1088
+ // src/dynamics/dynamic-control.tsx
1089
+ import * as React37 from "react";
1090
+
1091
+ // src/dynamics/hooks/use-prop-dynamic-tags.ts
1092
+ import { useMemo } from "react";
1093
+
1094
+ // src/dynamics/sync/get-elementor-config.ts
1095
+ var getElementorConfig = () => {
1096
+ const extendedWindow = window;
1097
+ return extendedWindow.elementor?.config ?? {};
1098
+ };
1099
+
1100
+ // src/dynamics/sync/get-atomic-dynamic-tags.ts
1101
+ var getAtomicDynamicTags = () => {
1102
+ const { atomicDynamicTags } = getElementorConfig();
1103
+ if (!atomicDynamicTags) {
1104
+ return null;
1105
+ }
1106
+ return {
1107
+ tags: atomicDynamicTags.tags,
1108
+ groups: atomicDynamicTags.groups
1109
+ };
1110
+ };
1111
+
1112
+ // src/props/is-transformable.ts
1113
+ import { z } from "@elementor/schema";
1114
+ var transformableSchema = z.object({
1115
+ $$type: z.string(),
1116
+ value: z.any()
1117
+ });
1118
+ var isTransformable = (value) => {
1119
+ return transformableSchema.safeParse(value).success;
1120
+ };
1121
+
1122
+ // src/dynamics/utils.ts
1123
+ var isDynamicType = (prop) => prop.key === "dynamic";
1124
+ var isDynamicPropValue = (prop) => {
1125
+ return isTransformable(prop) && prop.$$type === "dynamic";
1126
+ };
1127
+
1128
+ // src/dynamics/hooks/use-prop-dynamic-tags.ts
1129
+ var usePropDynamicTags = (propName) => {
1130
+ let categories = [];
1131
+ const { elementType } = useElementContext();
1132
+ const propSchema = elementType.propsSchema?.[propName];
1133
+ if (propSchema) {
1134
+ const propDynamicType = propSchema.additional_types.find(isDynamicType);
1135
+ categories = propDynamicType?.settings.categories || [];
1136
+ }
1137
+ return useMemo(() => getDynamicTagsByCategories(categories), [categories.join()]);
1138
+ };
1139
+ var getDynamicTagsByCategories = (categories) => {
1140
+ const dynamicTags = getAtomicDynamicTags();
1141
+ if (!categories.length || !dynamicTags?.tags) {
1142
+ return [];
1143
+ }
1144
+ const _categories = new Set(categories);
1145
+ return Object.values(dynamicTags.tags).filter(
1146
+ (dynamicTag) => dynamicTag.categories.some((category) => _categories.has(category))
1147
+ );
1148
+ };
1149
+
1150
+ // src/dynamics/hooks/use-dynamic-tag.ts
1151
+ import { useMemo as useMemo2 } from "react";
1152
+ var useDynamicTag = (propName, tagName) => {
1153
+ const dynamicTags = usePropDynamicTags(propName);
1154
+ return useMemo2(() => dynamicTags.find((tag) => tag.name === tagName) ?? null, [dynamicTags, tagName]);
1155
+ };
1156
+
1157
+ // src/dynamics/dynamic-control.tsx
1158
+ var DynamicControl = ({ bind, children }) => {
1159
+ const { value, setValue, bind: propName } = useControl();
1160
+ const { name = "", settings } = value?.value ?? {};
1161
+ const dynamicTag = useDynamicTag(propName, name);
1162
+ if (!dynamicTag) {
1163
+ throw new Error(`Dynamic tag ${name} not found`);
1164
+ }
1165
+ const defaultValue = dynamicTag.props_schema[bind]?.type.default;
1166
+ const dynamicValue = settings?.[bind] ?? defaultValue;
1167
+ const setDynamicValue = (newValue) => {
1168
+ setValue({
1169
+ $$type: "dynamic",
1170
+ value: {
1171
+ name,
1172
+ settings: {
1173
+ ...settings,
1174
+ [bind]: newValue
1175
+ }
1176
+ }
1177
+ });
1178
+ };
1179
+ return /* @__PURE__ */ React37.createElement(ControlContext.Provider, { value: { setValue: setDynamicValue, value: dynamicValue, bind } }, children);
1180
+ };
1181
+
1182
+ // src/dynamics/components/dynamic-selection-control.tsx
1183
+ import { DatabaseIcon, SettingsIcon, XIcon } from "@elementor/icons";
1184
+
1185
+ // src/dynamics/components/dynamic-selection.tsx
1186
+ import * as React38 from "react";
1187
+ import { useState as useState3, Fragment as Fragment2 } from "react";
1188
+ import { SearchIcon, PhotoIcon } from "@elementor/icons";
1189
+ import {
1190
+ Box,
1191
+ Divider as Divider3,
1192
+ InputAdornment as InputAdornment3,
1193
+ Link,
1194
+ ListSubheader,
1195
+ MenuItem as MenuItem3,
1196
+ MenuList,
1197
+ Stack as Stack12,
1198
+ TextField as TextField5,
1199
+ Typography as Typography2
1200
+ } from "@elementor/ui";
1201
+ import { __ as __18 } from "@wordpress/i18n";
1202
+ var SIZE = "tiny";
1203
+ var DynamicSelection = ({ onSelect }) => {
1204
+ const [searchValue, setSearchValue] = useState3("");
1205
+ const { groups: dynamicGroups } = getAtomicDynamicTags() || {};
1206
+ const { bind, value: dynamicValue, setValue } = useControl();
1207
+ const options2 = useFilteredOptions(bind, searchValue);
1208
+ const handleSearch = (event) => {
1209
+ setSearchValue(event.target.value);
1210
+ };
1211
+ return /* @__PURE__ */ React38.createElement(Stack12, null, /* @__PURE__ */ React38.createElement(Box, { px: 1.5, pb: 1 }, /* @__PURE__ */ React38.createElement(
1212
+ TextField5,
1213
+ {
1214
+ fullWidth: true,
1215
+ size: SIZE,
1216
+ value: searchValue,
1217
+ onChange: handleSearch,
1218
+ placeholder: __18("Search dynamic tag", "elementor"),
1219
+ InputProps: {
1220
+ startAdornment: /* @__PURE__ */ React38.createElement(InputAdornment3, { position: "start" }, /* @__PURE__ */ React38.createElement(SearchIcon, { fontSize: SIZE }))
1221
+ }
1222
+ }
1223
+ )), /* @__PURE__ */ React38.createElement(Divider3, null), /* @__PURE__ */ React38.createElement(Box, { sx: { overflowY: "auto", height: 260, width: 220 } }, options2.length > 0 ? /* @__PURE__ */ React38.createElement(MenuList, { role: "listbox", tabIndex: 0 }, options2.map(([category, items], index) => /* @__PURE__ */ React38.createElement(Fragment2, { key: index }, /* @__PURE__ */ React38.createElement(ListSubheader, { sx: { typography: "caption", color: "text.tertiary" } }, dynamicGroups?.[category]?.title || category), items.map(({ value, label: tagLabel }) => {
1224
+ const isSelected = value === dynamicValue?.value?.name;
1225
+ return /* @__PURE__ */ React38.createElement(
1226
+ MenuItem3,
1227
+ {
1228
+ key: value,
1229
+ selected: isSelected,
1230
+ autoFocus: isSelected,
1231
+ sx: { typography: "caption" },
1232
+ onClick: () => {
1233
+ setValue({ $$type: "dynamic", value: { name: value } });
1234
+ onSelect?.();
1235
+ }
1236
+ },
1237
+ tagLabel
1238
+ );
1239
+ })))) : /* @__PURE__ */ React38.createElement(Stack12, { alignItems: "center", p: 2.5, gap: 1.5 }, /* @__PURE__ */ React38.createElement(PhotoIcon, { fontSize: "large" }), /* @__PURE__ */ React38.createElement(Typography2, { align: "center", variant: "caption", color: "text.secondary" }, __18("Sorry, nothing matched", "elementor"), /* @__PURE__ */ React38.createElement("br", null), "\u201C", searchValue, "\u201D."), /* @__PURE__ */ React38.createElement(Typography2, { align: "center", variant: "caption", color: "text.secondary" }, /* @__PURE__ */ React38.createElement(
1240
+ Link,
1241
+ {
1242
+ color: "secondary",
1243
+ variant: "caption",
1244
+ component: "button",
1245
+ onClick: () => setSearchValue("")
1246
+ },
1247
+ __18("Clear the filters", "elementor")
1248
+ ), "\xA0", __18("and try again.", "elementor")))));
1249
+ };
1250
+ var useFilteredOptions = (bind, searchValue) => {
1251
+ const dynamicTags = usePropDynamicTags(bind);
1252
+ const options2 = dynamicTags.reduce((categories, { name, label, group }) => {
1253
+ const isVisible = label.toLowerCase().includes(searchValue.trim().toLowerCase());
1254
+ if (!isVisible) {
1255
+ return categories;
1256
+ }
1257
+ if (!categories.has(group)) {
1258
+ categories.set(group, []);
1259
+ }
1260
+ categories.get(group)?.push({ label, value: name });
1261
+ return categories;
1262
+ }, /* @__PURE__ */ new Map());
1263
+ return [...options2];
1264
+ };
1265
+
1266
+ // src/dynamics/components/dynamic-selection-control.tsx
1267
+ import {
1268
+ bindPopover,
1269
+ bindTrigger as bindTrigger2,
1270
+ Box as Box2,
1271
+ IconButton,
1272
+ Paper,
1273
+ Popover,
1274
+ Stack as Stack13,
1275
+ Typography as Typography3,
1276
+ UnstableTag as Tag,
1277
+ usePopupState as usePopupState2,
1278
+ Tabs as Tabs2,
1279
+ Divider as Divider4,
1280
+ useTabs as useTabs2,
1281
+ Tab as Tab2,
1282
+ TabPanel as TabPanel2
1283
+ } from "@elementor/ui";
1284
+ import { __ as __19 } from "@wordpress/i18n";
1285
+ var SIZE2 = "tiny";
1286
+ var DynamicSelectionControl = () => {
1287
+ const { bind, value, setValue } = useControl();
1288
+ const { name: tagName = "" } = value?.value || {};
1289
+ const selectionPopoverId = useId3();
1290
+ const selectionPopoverState = usePopupState2({ variant: "popover", popupId: selectionPopoverId });
1291
+ const dynamicTag = useDynamicTag(bind, tagName);
1292
+ const removeDynamicTag = () => {
1293
+ setValue(null);
1294
+ };
1295
+ if (!dynamicTag) {
1296
+ throw new Error(`Dynamic tag ${tagName} not found`);
1297
+ }
1298
+ return /* @__PURE__ */ React39.createElement(Box2, { sx: { width: "100%" } }, /* @__PURE__ */ React39.createElement(
1299
+ Tag,
1300
+ {
1301
+ fullWidth: true,
1302
+ showActionsOnHover: true,
1303
+ label: dynamicTag.label,
1304
+ startIcon: /* @__PURE__ */ React39.createElement(DatabaseIcon, { fontSize: SIZE2 }),
1305
+ ...bindTrigger2(selectionPopoverState),
1306
+ actions: /* @__PURE__ */ React39.createElement(React39.Fragment, null, /* @__PURE__ */ React39.createElement(DynamicSettingsPopover, { dynamicTag }), /* @__PURE__ */ React39.createElement(
1307
+ IconButton,
1308
+ {
1309
+ size: SIZE2,
1310
+ onClick: removeDynamicTag,
1311
+ "aria-label": __19("Remove dynamic value", "elementor")
1312
+ },
1313
+ /* @__PURE__ */ React39.createElement(XIcon, { fontSize: SIZE2 })
1314
+ ))
1315
+ }
1316
+ ), /* @__PURE__ */ React39.createElement(
1317
+ Popover,
1318
+ {
1319
+ disablePortal: true,
1320
+ disableScrollLock: true,
1321
+ anchorOrigin: { vertical: "bottom", horizontal: "left" },
1322
+ ...bindPopover(selectionPopoverState)
1323
+ },
1324
+ /* @__PURE__ */ React39.createElement(Stack13, null, /* @__PURE__ */ React39.createElement(Stack13, { direction: "row", alignItems: "center", pl: 1.5, pr: 0.5, py: 1.5 }, /* @__PURE__ */ React39.createElement(DatabaseIcon, { fontSize: SIZE2, sx: { mr: 0.5 } }), /* @__PURE__ */ React39.createElement(Typography3, { variant: "subtitle2" }, __19("Dynamic Tags", "elementor")), /* @__PURE__ */ React39.createElement(IconButton, { size: SIZE2, sx: { ml: "auto" }, onClick: selectionPopoverState.close }, /* @__PURE__ */ React39.createElement(XIcon, { fontSize: SIZE2 }))), /* @__PURE__ */ React39.createElement(DynamicSelection, { onSelect: selectionPopoverState.close }))
1325
+ ));
1326
+ };
1327
+ var DynamicSettingsPopover = ({ dynamicTag }) => {
1328
+ const popupId = useId3();
1329
+ const settingsPopupState = usePopupState2({ variant: "popover", popupId });
1330
+ const hasDynamicSettings = !!dynamicTag.atomic_controls.length;
1331
+ if (!hasDynamicSettings) {
1332
+ return null;
1333
+ }
1334
+ return /* @__PURE__ */ React39.createElement(React39.Fragment, null, /* @__PURE__ */ React39.createElement(
1335
+ IconButton,
1336
+ {
1337
+ size: SIZE2,
1338
+ ...bindTrigger2(settingsPopupState),
1339
+ "aria-label": __19("Settings", "elementor")
1340
+ },
1341
+ /* @__PURE__ */ React39.createElement(SettingsIcon, { fontSize: SIZE2 })
1342
+ ), /* @__PURE__ */ React39.createElement(
1343
+ Popover,
1344
+ {
1345
+ disableScrollLock: true,
1346
+ anchorOrigin: { vertical: "bottom", horizontal: "center" },
1347
+ ...bindPopover(settingsPopupState)
1348
+ },
1349
+ /* @__PURE__ */ React39.createElement(Paper, { component: Stack13, sx: { minHeight: "300px", width: "220px" } }, /* @__PURE__ */ React39.createElement(Stack13, { direction: "row", alignItems: "center", px: 1.5, pt: 2, pb: 1 }, /* @__PURE__ */ React39.createElement(DatabaseIcon, { fontSize: SIZE2, sx: { mr: 0.5 } }), /* @__PURE__ */ React39.createElement(Typography3, { variant: "subtitle2" }, dynamicTag.label), /* @__PURE__ */ React39.createElement(IconButton, { sx: { ml: "auto" }, size: SIZE2, onClick: settingsPopupState.close }, /* @__PURE__ */ React39.createElement(XIcon, { fontSize: SIZE2 }))), /* @__PURE__ */ React39.createElement(DynamicSettings, { controls: dynamicTag.atomic_controls }))
1350
+ ));
1351
+ };
1352
+ var DynamicSettings = ({ controls }) => {
1353
+ const tabs = controls.filter(({ type }) => type === "section");
1354
+ const { getTabsProps, getTabProps, getTabPanelProps } = useTabs2(0);
1355
+ if (!tabs.length) {
1356
+ return null;
1357
+ }
1358
+ return /* @__PURE__ */ React39.createElement(React39.Fragment, null, /* @__PURE__ */ React39.createElement(Tabs2, { indicatorColor: "secondary", textColor: "secondary", ...getTabsProps() }, tabs.map(({ value }, index) => /* @__PURE__ */ React39.createElement(Tab2, { key: index, label: value.label, sx: { px: 1, py: 0.5 }, ...getTabProps(index) }))), /* @__PURE__ */ React39.createElement(Divider4, null), tabs.map(({ value }, index) => {
1359
+ return /* @__PURE__ */ React39.createElement(TabPanel2, { key: index, sx: { flexGrow: 1 }, ...getTabPanelProps(index) }, /* @__PURE__ */ React39.createElement(Stack13, { gap: 1, px: 2 }, value.items.map((item) => {
1360
+ if (item.type === "control") {
1361
+ return /* @__PURE__ */ React39.createElement(Control5, { key: item.value.bind, control: item.value });
1362
+ }
1363
+ return null;
1364
+ })));
1365
+ }));
1366
+ };
1367
+ var Control5 = ({ control }) => {
1368
+ if (!getControlByType(control.type)) {
1369
+ return null;
1370
+ }
1371
+ return /* @__PURE__ */ React39.createElement(DynamicControl, { bind: control.bind }, control.label ? /* @__PURE__ */ React39.createElement(ControlLabel, null, control.label) : null, /* @__PURE__ */ React39.createElement(Control, { type: control.type, props: control.props }));
1372
+ };
1373
+
1374
+ // src/dynamics/init.ts
1375
+ var init = () => {
1376
+ replaceControl({
1377
+ component: DynamicSelectionControl,
1378
+ condition: ({ value }) => isDynamicPropValue(value)
1379
+ });
1380
+ };
1381
+
1382
+ // src/init.ts
1383
+ function init2() {
739
1384
  registerPanel(panel);
740
1385
  blockV1Panel();
741
1386
  injectIntoLogic({
742
1387
  id: "editing-panel-hooks",
743
1388
  component: EditingPanelHooks
744
1389
  });
1390
+ init();
745
1391
  }
746
1392
  var blockV1Panel = () => {
747
1393
  blockDataCommand({
@@ -751,5 +1397,9 @@ var blockV1Panel = () => {
751
1397
  };
752
1398
 
753
1399
  // src/index.ts
754
- init();
1400
+ init2();
1401
+ export {
1402
+ replaceControl,
1403
+ useControl
1404
+ };
755
1405
  //# sourceMappingURL=index.mjs.map