@elementor/editor-ui 4.1.0-822 → 4.1.0-824

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.
package/dist/index.d.mts CHANGED
@@ -88,19 +88,19 @@ type DialogContent = {
88
88
  declare const openDialog: ({ component }: DialogContent) => void;
89
89
  declare const closeDialog: () => void;
90
90
 
91
- type Props$1 = {
91
+ type Props$3 = {
92
92
  value: string;
93
93
  onSearch: (search: string) => void;
94
94
  placeholder: string;
95
95
  id?: string;
96
96
  } & BoxProps;
97
- declare const SearchField: ({ value, onSearch, placeholder, id, sx }: Props$1) => React$1.JSX.Element;
97
+ declare const SearchField: ({ value, onSearch, placeholder, id, sx }: Props$3) => React$1.JSX.Element;
98
98
 
99
- type Props = PropsWithChildren<{
99
+ type Props$2 = PropsWithChildren<{
100
100
  onSubmit?: () => void;
101
101
  'data-testid'?: string;
102
102
  }>;
103
- declare const Form: ({ children, onSubmit, "data-testid": dataTestId }: Props) => React$1.JSX.Element;
103
+ declare const Form: ({ children, onSubmit, "data-testid": dataTestId }: Props$2) => React$1.JSX.Element;
104
104
 
105
105
  type CtaButtonProps = {
106
106
  href: string;
@@ -154,6 +154,24 @@ declare function useFloatingActionsBar(): {
154
154
  setOpen: React$1.Dispatch<React$1.SetStateAction<boolean>>;
155
155
  };
156
156
 
157
+ type Props$1 = {
158
+ onFileSelected: (file: File) => void;
159
+ allowedFileTypes: `${string}/${string}`[];
160
+ accept: string;
161
+ regionLabel?: string;
162
+ primaryLabel?: string;
163
+ secondaryLabel?: string;
164
+ helperText?: string;
165
+ };
166
+ declare const FileUploadDropzone: ({ onFileSelected, allowedFileTypes, accept, regionLabel, primaryLabel, secondaryLabel, helperText, }: Props$1) => React$1.JSX.Element;
167
+
168
+ type Props = {
169
+ file: File;
170
+ onRemove: () => void;
171
+ statusLabel?: string;
172
+ };
173
+ declare const FileUploadRow: ({ file, onRemove, statusLabel }: Props) => React$1.JSX.Element;
174
+
157
175
  type PopoverBodyProps = PropsWithChildren<{
158
176
  height?: number | 'auto';
159
177
  width?: number;
@@ -290,4 +308,4 @@ declare const useTextFieldAutoSelect: () => React$1.RefObject<HTMLInputElement>;
290
308
 
291
309
  declare const useCanvasClickHandler: (isActive: boolean, onClickAway: (e: MouseEvent) => void) => void;
292
310
 
293
- export { CollapseIcon, CollapsibleContent, type CollapsibleValue, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, FloatingActionsBar, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverAction, type PopoverActionProps, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionAlert, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, getCollapsibleValue, openDialog, useCanvasClickHandler, useDialog, useEditable, useFloatingActionsBar, useSectionWidth, useTextFieldAutoSelect };
311
+ export { CollapseIcon, CollapsibleContent, type CollapsibleValue, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, FileUploadDropzone, FileUploadRow, FloatingActionsBar, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverAction, type PopoverActionProps, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionAlert, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, getCollapsibleValue, openDialog, useCanvasClickHandler, useDialog, useEditable, useFloatingActionsBar, useSectionWidth, useTextFieldAutoSelect };
package/dist/index.d.ts CHANGED
@@ -88,19 +88,19 @@ type DialogContent = {
88
88
  declare const openDialog: ({ component }: DialogContent) => void;
89
89
  declare const closeDialog: () => void;
90
90
 
91
- type Props$1 = {
91
+ type Props$3 = {
92
92
  value: string;
93
93
  onSearch: (search: string) => void;
94
94
  placeholder: string;
95
95
  id?: string;
96
96
  } & BoxProps;
97
- declare const SearchField: ({ value, onSearch, placeholder, id, sx }: Props$1) => React$1.JSX.Element;
97
+ declare const SearchField: ({ value, onSearch, placeholder, id, sx }: Props$3) => React$1.JSX.Element;
98
98
 
99
- type Props = PropsWithChildren<{
99
+ type Props$2 = PropsWithChildren<{
100
100
  onSubmit?: () => void;
101
101
  'data-testid'?: string;
102
102
  }>;
103
- declare const Form: ({ children, onSubmit, "data-testid": dataTestId }: Props) => React$1.JSX.Element;
103
+ declare const Form: ({ children, onSubmit, "data-testid": dataTestId }: Props$2) => React$1.JSX.Element;
104
104
 
105
105
  type CtaButtonProps = {
106
106
  href: string;
@@ -154,6 +154,24 @@ declare function useFloatingActionsBar(): {
154
154
  setOpen: React$1.Dispatch<React$1.SetStateAction<boolean>>;
155
155
  };
156
156
 
157
+ type Props$1 = {
158
+ onFileSelected: (file: File) => void;
159
+ allowedFileTypes: `${string}/${string}`[];
160
+ accept: string;
161
+ regionLabel?: string;
162
+ primaryLabel?: string;
163
+ secondaryLabel?: string;
164
+ helperText?: string;
165
+ };
166
+ declare const FileUploadDropzone: ({ onFileSelected, allowedFileTypes, accept, regionLabel, primaryLabel, secondaryLabel, helperText, }: Props$1) => React$1.JSX.Element;
167
+
168
+ type Props = {
169
+ file: File;
170
+ onRemove: () => void;
171
+ statusLabel?: string;
172
+ };
173
+ declare const FileUploadRow: ({ file, onRemove, statusLabel }: Props) => React$1.JSX.Element;
174
+
157
175
  type PopoverBodyProps = PropsWithChildren<{
158
176
  height?: number | 'auto';
159
177
  width?: number;
@@ -290,4 +308,4 @@ declare const useTextFieldAutoSelect: () => React$1.RefObject<HTMLInputElement>;
290
308
 
291
309
  declare const useCanvasClickHandler: (isActive: boolean, onClickAway: (e: MouseEvent) => void) => void;
292
310
 
293
- export { CollapseIcon, CollapsibleContent, type CollapsibleValue, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, FloatingActionsBar, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverAction, type PopoverActionProps, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionAlert, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, getCollapsibleValue, openDialog, useCanvasClickHandler, useDialog, useEditable, useFloatingActionsBar, useSectionWidth, useTextFieldAutoSelect };
311
+ export { CollapseIcon, CollapsibleContent, type CollapsibleValue, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, FileUploadDropzone, FileUploadRow, FloatingActionsBar, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverAction, type PopoverActionProps, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionAlert, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, getCollapsibleValue, openDialog, useCanvasClickHandler, useDialog, useEditable, useFloatingActionsBar, useSectionWidth, useTextFieldAutoSelect };
package/dist/index.js CHANGED
@@ -36,6 +36,8 @@ __export(index_exports, {
36
36
  CtaButton: () => CtaButton,
37
37
  EditableField: () => EditableField,
38
38
  EllipsisWithTooltip: () => EllipsisWithTooltip,
39
+ FileUploadDropzone: () => FileUploadDropzone,
40
+ FileUploadRow: () => FileUploadRow,
39
41
  FloatingActionsBar: () => FloatingActionsBar,
40
42
  Form: () => Form,
41
43
  GlobalDialog: () => GlobalDialog,
@@ -770,15 +772,103 @@ function useFloatingActionsBar() {
770
772
  return context;
771
773
  }
772
774
 
773
- // src/components/popover/body.tsx
775
+ // src/components/file-upload/file-upload-dropzone.tsx
774
776
  var React19 = __toESM(require("react"));
777
+ var import_react16 = require("react");
778
+ var import_icons8 = require("@elementor/icons");
775
779
  var import_ui19 = require("@elementor/ui");
780
+ var import_i18n6 = require("@wordpress/i18n");
781
+ var cardSx = {
782
+ minHeight: 152,
783
+ border: "2px dashed",
784
+ borderColor: "divider",
785
+ borderRadius: 1
786
+ };
787
+ var FileUploadDropzone = ({
788
+ onFileSelected,
789
+ allowedFileTypes,
790
+ accept,
791
+ regionLabel,
792
+ primaryLabel,
793
+ secondaryLabel,
794
+ helperText
795
+ }) => {
796
+ const fileInputRef = (0, import_react16.useRef)(null);
797
+ const { getDropZoneProps } = (0, import_ui19.useUnstableDropZone)({
798
+ allowedFileTypes,
799
+ onChange: ({ valid }) => {
800
+ if (valid[0]) {
801
+ onFileSelected(valid[0]);
802
+ }
803
+ }
804
+ });
805
+ const dropZoneProps = getDropZoneProps();
806
+ const handleBrowseClick = () => fileInputRef.current?.click();
807
+ const handleFileInputChange = (event) => {
808
+ const file = event.target.files?.[0];
809
+ if (file) {
810
+ onFileSelected(file);
811
+ }
812
+ event.target.value = "";
813
+ };
814
+ return /* @__PURE__ */ React19.createElement(
815
+ import_ui19.Card,
816
+ {
817
+ variant: "outlined",
818
+ role: "region",
819
+ "aria-label": regionLabel ?? (0, import_i18n6.__)("File dropzone", "elementor"),
820
+ onDrop: dropZoneProps.onDrop,
821
+ onDragEnter: dropZoneProps.onDragEnter,
822
+ onDragLeave: dropZoneProps.onDragLeave,
823
+ onDragOver: dropZoneProps.onDragOver,
824
+ sx: cardSx
825
+ },
826
+ /* @__PURE__ */ React19.createElement(import_ui19.Stack, { alignItems: "center", spacing: 1, padding: 3 }, /* @__PURE__ */ React19.createElement(import_icons8.UploadIcon, { fontSize: "medium" }), /* @__PURE__ */ React19.createElement(import_ui19.Stack, { direction: "row", spacing: 0.5, alignItems: "center" }, /* @__PURE__ */ React19.createElement(import_ui19.Link, { component: "button", type: "button", underline: "always", onClick: handleBrowseClick }, /* @__PURE__ */ React19.createElement(import_ui19.Typography, { variant: "body1", component: "span" }, primaryLabel ?? (0, import_i18n6.__)("Upload file", "elementor"))), /* @__PURE__ */ React19.createElement(import_ui19.Typography, { variant: "body1" }, secondaryLabel ?? (0, import_i18n6.__)("or drag and drop", "elementor"))), helperText ? /* @__PURE__ */ React19.createElement(import_ui19.Typography, { variant: "caption", color: "text.secondary" }, helperText) : null),
827
+ /* @__PURE__ */ React19.createElement("input", { ref: fileInputRef, type: "file", accept, hidden: true, onChange: handleFileInputChange })
828
+ );
829
+ };
830
+
831
+ // src/components/file-upload/file-upload-row.tsx
832
+ var React20 = __toESM(require("react"));
833
+ var import_icons9 = require("@elementor/icons");
834
+ var import_ui20 = require("@elementor/ui");
835
+ var import_i18n7 = require("@wordpress/i18n");
836
+ var BYTES_PER_KILOBYTE = 1024;
837
+ var cardSx2 = {
838
+ minHeight: 152,
839
+ border: "2px dashed",
840
+ borderColor: "divider",
841
+ borderRadius: 1
842
+ };
843
+ var formatFileSize = (sizeInBytes) => {
844
+ const kilobytes = Math.max(1, Math.round(sizeInBytes / BYTES_PER_KILOBYTE));
845
+ return `${kilobytes}kb`;
846
+ };
847
+ var FileUploadRow = ({ file, onRemove, statusLabel }) => {
848
+ return /* @__PURE__ */ React20.createElement(import_ui20.Card, { variant: "outlined", sx: cardSx2 }, /* @__PURE__ */ React20.createElement(
849
+ import_ui20.Stack,
850
+ {
851
+ direction: "row",
852
+ alignItems: "center",
853
+ justifyContent: "space-between",
854
+ padding: 2,
855
+ spacing: 2,
856
+ minHeight: 152
857
+ },
858
+ /* @__PURE__ */ React20.createElement(import_ui20.Stack, { direction: "column", spacing: 0.5, minWidth: 0, flex: 1 }, /* @__PURE__ */ React20.createElement(import_ui20.Typography, { variant: "subtitle2", noWrap: true }, file.name), /* @__PURE__ */ React20.createElement(import_ui20.Typography, { variant: "caption", color: "text.secondary", noWrap: true }, formatFileSize(file.size), " \xB7 ", statusLabel ?? (0, import_i18n7.__)("Complete", "elementor"))),
859
+ /* @__PURE__ */ React20.createElement(import_ui20.IconButton, { size: "small", onClick: onRemove, "aria-label": (0, import_i18n7.__)("Remove file", "elementor") }, /* @__PURE__ */ React20.createElement(import_icons9.XIcon, { fontSize: "inherit" }))
860
+ ));
861
+ };
862
+
863
+ // src/components/popover/body.tsx
864
+ var React21 = __toESM(require("react"));
865
+ var import_ui21 = require("@elementor/ui");
776
866
  var SECTION_PADDING_INLINE = 32;
777
867
  var DEFAULT_POPOVER_HEIGHT = 348;
778
868
  var FALLBACK_POPOVER_WIDTH = 220;
779
869
  var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) => {
780
- return /* @__PURE__ */ React19.createElement(
781
- import_ui19.Box,
870
+ return /* @__PURE__ */ React21.createElement(
871
+ import_ui21.Box,
782
872
  {
783
873
  display: "flex",
784
874
  flexDirection: "column",
@@ -795,13 +885,13 @@ var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) =>
795
885
  };
796
886
 
797
887
  // src/components/popover/section-popover-body.tsx
798
- var React20 = __toESM(require("react"));
888
+ var React22 = __toESM(require("react"));
799
889
 
800
890
  // src/contexts/section-context.tsx
801
- var import_react16 = require("react");
891
+ var import_react17 = require("react");
802
892
  var FALLBACK_SECTION_WIDTH = 320;
803
- var SectionRefContext = (0, import_react16.createContext)(null);
804
- var useSectionRef = () => (0, import_react16.useContext)(SectionRefContext);
893
+ var SectionRefContext = (0, import_react17.createContext)(null);
894
+ var useSectionRef = () => (0, import_react17.useContext)(SectionRefContext);
805
895
  var useSectionWidth = () => {
806
896
  const sectionRef = useSectionRef();
807
897
  return sectionRef?.current?.offsetWidth ?? FALLBACK_SECTION_WIDTH;
@@ -810,12 +900,12 @@ var useSectionWidth = () => {
810
900
  // src/components/popover/section-popover-body.tsx
811
901
  var SectionPopoverBody = (props) => {
812
902
  const sectionWidth = useSectionWidth();
813
- return /* @__PURE__ */ React20.createElement(PopoverBody, { ...props, width: sectionWidth });
903
+ return /* @__PURE__ */ React22.createElement(PopoverBody, { ...props, width: sectionWidth });
814
904
  };
815
905
 
816
906
  // src/components/popover/header.tsx
817
- var React21 = __toESM(require("react"));
818
- var import_ui20 = require("@elementor/ui");
907
+ var React23 = __toESM(require("react"));
908
+ var import_ui22 = require("@elementor/ui");
819
909
  var SIZE2 = "tiny";
820
910
  var PopoverHeader = ({ title, onClose, icon, actions }) => {
821
911
  const paddingAndSizing = {
@@ -824,13 +914,13 @@ var PopoverHeader = ({ title, onClose, icon, actions }) => {
824
914
  py: 1.5,
825
915
  maxHeight: 36
826
916
  };
827
- return /* @__PURE__ */ React21.createElement(import_ui20.Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React21.createElement(import_ui20.Typography, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React21.createElement(import_ui20.Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React21.createElement(import_ui20.CloseButton, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
917
+ return /* @__PURE__ */ React23.createElement(import_ui22.Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React23.createElement(import_ui22.Typography, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React23.createElement(import_ui22.Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React23.createElement(import_ui22.CloseButton, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
828
918
  };
829
919
 
830
920
  // src/components/popover/menu-list.tsx
831
- var React22 = __toESM(require("react"));
832
- var import_react17 = require("react");
833
- var import_ui21 = require("@elementor/ui");
921
+ var React24 = __toESM(require("react"));
922
+ var import_react18 = require("react");
923
+ var import_ui23 = require("@elementor/ui");
834
924
  var import_react_virtual = require("@tanstack/react-virtual");
835
925
  var ITEM_HEIGHT = 32;
836
926
  var LIST_ITEMS_BUFFER = 6;
@@ -857,11 +947,11 @@ var PopoverMenuList = ({
857
947
  noResultsComponent,
858
948
  menuListTemplate: CustomMenuList
859
949
  }) => {
860
- const containerRef = (0, import_react17.useRef)(null);
950
+ const containerRef = (0, import_react18.useRef)(null);
861
951
  const scrollTop = useScrollTop({ containerRef });
862
- const theme = (0, import_ui21.useTheme)();
952
+ const theme = (0, import_ui23.useTheme)();
863
953
  const MenuListComponent = CustomMenuList || StyledMenuList;
864
- const stickyIndices = (0, import_react17.useMemo)(
954
+ const stickyIndices = (0, import_react18.useMemo)(
865
955
  () => items.reduce((categoryIndices, item, index) => {
866
956
  if (item.type === "category") {
867
957
  categoryIndices.push(index);
@@ -895,12 +985,12 @@ var PopoverMenuList = ({
895
985
  rangeExtractor: getActiveItemIndices,
896
986
  onChange: onChangeCallback
897
987
  });
898
- (0, import_react17.useEffect)(() => {
988
+ (0, import_react18.useEffect)(() => {
899
989
  onChangeCallback(virtualizer);
900
990
  }, [items]);
901
991
  useScrollToSelected({ selectedValue, items, virtualizer });
902
992
  const virtualItems = virtualizer.getVirtualItems();
903
- return /* @__PURE__ */ React22.createElement(import_ui21.Box, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React22.createElement(
993
+ return /* @__PURE__ */ React24.createElement(import_ui23.Box, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React24.createElement(
904
994
  MenuListComponent,
905
995
  {
906
996
  role: "listbox",
@@ -918,8 +1008,8 @@ var PopoverMenuList = ({
918
1008
  }
919
1009
  if (item.type === "category") {
920
1010
  const shouldStick = virtualRow.start + MENU_LIST_PADDING_TOP <= scrollTop;
921
- return /* @__PURE__ */ React22.createElement(
922
- import_ui21.MenuSubheader,
1011
+ return /* @__PURE__ */ React24.createElement(
1012
+ import_ui23.MenuSubheader,
923
1013
  {
924
1014
  key: virtualRow.key,
925
1015
  style: shouldStick ? {} : menuSubHeaderAbsoluteStyling(virtualRow.start),
@@ -929,8 +1019,8 @@ var PopoverMenuList = ({
929
1019
  );
930
1020
  }
931
1021
  const isDisabled = item.disabled;
932
- return /* @__PURE__ */ React22.createElement(
933
- import_ui21.ListItem,
1022
+ return /* @__PURE__ */ React24.createElement(
1023
+ import_ui23.ListItem,
934
1024
  {
935
1025
  key: virtualRow.key,
936
1026
  role: "option",
@@ -969,7 +1059,7 @@ var PopoverMenuList = ({
969
1059
  })
970
1060
  ));
971
1061
  };
972
- var StyledMenuList = (0, import_ui21.styled)(import_ui21.MenuList)(({ theme }) => ({
1062
+ var StyledMenuList = (0, import_ui23.styled)(import_ui23.MenuList)(({ theme }) => ({
973
1063
  "& > li": {
974
1064
  height: ITEM_HEIGHT,
975
1065
  width: "100%",
@@ -999,16 +1089,16 @@ var StyledMenuList = (0, import_ui21.styled)(import_ui21.MenuList)(({ theme }) =
999
1089
  }));
1000
1090
 
1001
1091
  // src/components/popover/popover-action.tsx
1002
- var React23 = __toESM(require("react"));
1003
- var import_ui22 = require("@elementor/ui");
1092
+ var React25 = __toESM(require("react"));
1093
+ var import_ui24 = require("@elementor/ui");
1004
1094
  var SIZE3 = "tiny";
1005
1095
  function PopoverAction({ title, visible = true, icon: Icon, content: PopoverContent }) {
1006
1096
  const { popupState, triggerProps, popoverProps } = useFloatingActionsPopover();
1007
1097
  if (!visible) {
1008
1098
  return null;
1009
1099
  }
1010
- return /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(import_ui22.Tooltip, { placement: "top", title }, /* @__PURE__ */ React23.createElement(import_ui22.IconButton, { "aria-label": title, size: SIZE3, ...triggerProps }, /* @__PURE__ */ React23.createElement(Icon, { fontSize: SIZE3 }))), /* @__PURE__ */ React23.createElement(
1011
- import_ui22.Popover,
1100
+ return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(import_ui24.Tooltip, { placement: "top", title }, /* @__PURE__ */ React25.createElement(import_ui24.IconButton, { "aria-label": title, size: SIZE3, ...triggerProps }, /* @__PURE__ */ React25.createElement(Icon, { fontSize: SIZE3 }))), /* @__PURE__ */ React25.createElement(
1101
+ import_ui24.Popover,
1012
1102
  {
1013
1103
  disableScrollLock: true,
1014
1104
  anchorOrigin: {
@@ -1024,14 +1114,14 @@ function PopoverAction({ title, visible = true, icon: Icon, content: PopoverCont
1024
1114
  },
1025
1115
  ...popoverProps
1026
1116
  },
1027
- /* @__PURE__ */ React23.createElement(PopoverContent, { close: popupState.close })
1117
+ /* @__PURE__ */ React25.createElement(PopoverContent, { close: popupState.close })
1028
1118
  ));
1029
1119
  }
1030
1120
  function useFloatingActionsPopover() {
1031
1121
  const { setOpen } = useFloatingActionsBar();
1032
- const popupState = (0, import_ui22.usePopupState)({ variant: "popover" });
1033
- const triggerProps = (0, import_ui22.bindTrigger)(popupState);
1034
- const popoverProps = (0, import_ui22.bindPopover)(popupState);
1122
+ const popupState = (0, import_ui24.usePopupState)({ variant: "popover" });
1123
+ const triggerProps = (0, import_ui24.bindTrigger)(popupState);
1124
+ const popoverProps = (0, import_ui24.bindPopover)(popupState);
1035
1125
  const onClick = (e) => {
1036
1126
  triggerProps.onClick(e);
1037
1127
  setOpen(true);
@@ -1052,14 +1142,14 @@ function useFloatingActionsPopover() {
1052
1142
  }
1053
1143
 
1054
1144
  // src/components/save-changes-dialog.tsx
1055
- var React24 = __toESM(require("react"));
1056
- var import_react18 = require("react");
1057
- var import_icons8 = require("@elementor/icons");
1058
- var import_ui23 = require("@elementor/ui");
1145
+ var React26 = __toESM(require("react"));
1146
+ var import_react19 = require("react");
1147
+ var import_icons10 = require("@elementor/icons");
1148
+ var import_ui25 = require("@elementor/ui");
1059
1149
  var TITLE_ID = "save-changes-dialog";
1060
- var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React24.createElement(import_ui23.Dialog, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
1061
- var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React24.createElement(
1062
- import_ui23.DialogTitle,
1150
+ var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React26.createElement(import_ui25.Dialog, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
1151
+ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React26.createElement(
1152
+ import_ui25.DialogTitle,
1063
1153
  {
1064
1154
  id: TITLE_ID,
1065
1155
  display: "flex",
@@ -1067,47 +1157,47 @@ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React24.
1067
1157
  gap: 1,
1068
1158
  sx: { lineHeight: 1, justifyContent: "space-between" }
1069
1159
  },
1070
- /* @__PURE__ */ React24.createElement(import_ui23.Stack, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React24.createElement(import_icons8.AlertTriangleFilledIcon, { color: "secondary" }), children),
1071
- onClose && /* @__PURE__ */ React24.createElement(import_ui23.IconButton, { onClick: onClose, size: "small" }, /* @__PURE__ */ React24.createElement(import_icons8.XIcon, null))
1160
+ /* @__PURE__ */ React26.createElement(import_ui25.Stack, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React26.createElement(import_icons10.AlertTriangleFilledIcon, { color: "secondary" }), children),
1161
+ onClose && /* @__PURE__ */ React26.createElement(import_ui25.IconButton, { onClick: onClose, size: "small" }, /* @__PURE__ */ React26.createElement(import_icons10.XIcon, null))
1072
1162
  );
1073
- var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React24.createElement(import_ui23.DialogContent, null, children);
1074
- var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React24.createElement(import_ui23.DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
1163
+ var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React26.createElement(import_ui25.DialogContent, null, children);
1164
+ var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React26.createElement(import_ui25.DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
1075
1165
  var SaveChangesDialogActions = ({ actions }) => {
1076
- const [isConfirming, setIsConfirming] = (0, import_react18.useState)(false);
1166
+ const [isConfirming, setIsConfirming] = (0, import_react19.useState)(false);
1077
1167
  const { cancel, confirm, discard } = actions;
1078
1168
  const onConfirm = async () => {
1079
1169
  setIsConfirming(true);
1080
1170
  await confirm.action();
1081
1171
  setIsConfirming(false);
1082
1172
  };
1083
- return /* @__PURE__ */ React24.createElement(import_ui23.DialogActions, null, cancel && /* @__PURE__ */ React24.createElement(import_ui23.Button, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React24.createElement(import_ui23.Button, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React24.createElement(import_ui23.Button, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
1173
+ return /* @__PURE__ */ React26.createElement(import_ui25.DialogActions, null, cancel && /* @__PURE__ */ React26.createElement(import_ui25.Button, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React26.createElement(import_ui25.Button, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React26.createElement(import_ui25.Button, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
1084
1174
  };
1085
1175
  SaveChangesDialog.Title = SaveChangesDialogTitle;
1086
1176
  SaveChangesDialog.Content = SaveChangesDialogContent;
1087
1177
  SaveChangesDialog.ContentText = SaveChangesDialogContentText;
1088
1178
  SaveChangesDialog.Actions = SaveChangesDialogActions;
1089
1179
  var useDialog = () => {
1090
- const [isOpen, setIsOpen] = (0, import_react18.useState)(false);
1180
+ const [isOpen, setIsOpen] = (0, import_react19.useState)(false);
1091
1181
  const open = () => setIsOpen(true);
1092
1182
  const close = () => setIsOpen(false);
1093
1183
  return { isOpen, open, close };
1094
1184
  };
1095
1185
 
1096
1186
  // src/components/confirmation-dialog.tsx
1097
- var React25 = __toESM(require("react"));
1098
- var import_react19 = require("react");
1099
- var import_icons9 = require("@elementor/icons");
1100
- var import_ui24 = require("@elementor/ui");
1101
- var import_i18n6 = require("@wordpress/i18n");
1187
+ var React27 = __toESM(require("react"));
1188
+ var import_react20 = require("react");
1189
+ var import_icons11 = require("@elementor/icons");
1190
+ var import_ui26 = require("@elementor/ui");
1191
+ var import_i18n8 = require("@wordpress/i18n");
1102
1192
  var TITLE_ID2 = "confirmation-dialog";
1103
- var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React25.createElement(import_ui24.Dialog, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "sm" }, children);
1193
+ var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React27.createElement(import_ui26.Dialog, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "sm" }, children);
1104
1194
  var ConfirmationDialogTitle = ({
1105
1195
  children,
1106
- icon: Icon = import_icons9.AlertOctagonFilledIcon,
1196
+ icon: Icon = import_icons11.AlertOctagonFilledIcon,
1107
1197
  iconColor = "error"
1108
- }) => /* @__PURE__ */ React25.createElement(import_ui24.DialogTitle, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React25.createElement(Icon, { color: iconColor }), children);
1109
- var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React25.createElement(import_ui24.DialogContent, { sx: { mt: 2 } }, children);
1110
- var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React25.createElement(import_ui24.DialogContentText, { variant: "body2", color: "secondary", ...props });
1198
+ }) => /* @__PURE__ */ React27.createElement(import_ui26.DialogTitle, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React27.createElement(Icon, { color: iconColor }), children);
1199
+ var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React27.createElement(import_ui26.DialogContent, { sx: { mt: 2 } }, children);
1200
+ var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React27.createElement(import_ui26.DialogContentText, { variant: "body2", color: "secondary", ...props });
1111
1201
  var ConfirmationDialogActions = ({
1112
1202
  onClose,
1113
1203
  onConfirm,
@@ -1115,20 +1205,20 @@ var ConfirmationDialogActions = ({
1115
1205
  confirmLabel,
1116
1206
  color = "error",
1117
1207
  onSuppressMessage,
1118
- suppressLabel = (0, import_i18n6.__)("Don't show this again", "elementor")
1208
+ suppressLabel = (0, import_i18n8.__)("Don't show this again", "elementor")
1119
1209
  }) => {
1120
- const [dontShowAgain, setDontShowAgain] = (0, import_react19.useState)(false);
1210
+ const [dontShowAgain, setDontShowAgain] = (0, import_react20.useState)(false);
1121
1211
  const handleConfirm = () => {
1122
1212
  if (dontShowAgain && onSuppressMessage) {
1123
1213
  onSuppressMessage();
1124
1214
  }
1125
1215
  onConfirm();
1126
1216
  };
1127
- return /* @__PURE__ */ React25.createElement(import_ui24.DialogActions, { sx: onSuppressMessage ? { justifyContent: "space-between", alignItems: "center" } : void 0 }, onSuppressMessage && /* @__PURE__ */ React25.createElement(
1128
- import_ui24.FormControlLabel,
1217
+ return /* @__PURE__ */ React27.createElement(import_ui26.DialogActions, { sx: onSuppressMessage ? { justifyContent: "space-between", alignItems: "center" } : void 0 }, onSuppressMessage && /* @__PURE__ */ React27.createElement(
1218
+ import_ui26.FormControlLabel,
1129
1219
  {
1130
- control: /* @__PURE__ */ React25.createElement(
1131
- import_ui24.Checkbox,
1220
+ control: /* @__PURE__ */ React27.createElement(
1221
+ import_ui26.Checkbox,
1132
1222
  {
1133
1223
  checked: dontShowAgain,
1134
1224
  onChange: (event) => setDontShowAgain(event.target.checked),
@@ -1136,9 +1226,9 @@ var ConfirmationDialogActions = ({
1136
1226
  color: "secondary"
1137
1227
  }
1138
1228
  ),
1139
- label: /* @__PURE__ */ React25.createElement(import_ui24.Typography, { variant: "body2", color: "text.secondary" }, suppressLabel)
1229
+ label: /* @__PURE__ */ React27.createElement(import_ui26.Typography, { variant: "body2", color: "text.secondary" }, suppressLabel)
1140
1230
  }
1141
- ), /* @__PURE__ */ React25.createElement("div", null, /* @__PURE__ */ React25.createElement(import_ui24.Button, { color: "secondary", onClick: onClose }, cancelLabel ?? (0, import_i18n6.__)("Not now", "elementor")), /* @__PURE__ */ React25.createElement(import_ui24.Button, { autoFocus: true, variant: "contained", color, onClick: handleConfirm, sx: { ml: 1 } }, confirmLabel ?? (0, import_i18n6.__)("Delete", "elementor"))));
1231
+ ), /* @__PURE__ */ React27.createElement("div", null, /* @__PURE__ */ React27.createElement(import_ui26.Button, { color: "secondary", onClick: onClose }, cancelLabel ?? (0, import_i18n8.__)("Not now", "elementor")), /* @__PURE__ */ React27.createElement(import_ui26.Button, { autoFocus: true, variant: "contained", color, onClick: handleConfirm, sx: { ml: 1 } }, confirmLabel ?? (0, import_i18n8.__)("Delete", "elementor"))));
1142
1232
  };
1143
1233
  ConfirmationDialog.Title = ConfirmationDialogTitle;
1144
1234
  ConfirmationDialog.Content = ConfirmationDialogContent;
@@ -1146,10 +1236,10 @@ ConfirmationDialog.ContentText = ConfirmationDialogContentText;
1146
1236
  ConfirmationDialog.Actions = ConfirmationDialogActions;
1147
1237
 
1148
1238
  // src/hooks/use-editable.ts
1149
- var import_react20 = require("react");
1239
+ var import_react21 = require("react");
1150
1240
  var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
1151
- const [isEditing, setIsEditing] = (0, import_react20.useState)(false);
1152
- const [error, setError] = (0, import_react20.useState)(null);
1241
+ const [isEditing, setIsEditing] = (0, import_react21.useState)(false);
1242
+ const [error, setError] = (0, import_react21.useState)(null);
1153
1243
  const ref = useSelection(isEditing);
1154
1244
  const isDirty = (newValue) => newValue !== value;
1155
1245
  const openEditMode = () => {
@@ -1231,8 +1321,8 @@ var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
1231
1321
  };
1232
1322
  };
1233
1323
  var useSelection = (isEditing) => {
1234
- const ref = (0, import_react20.useRef)(null);
1235
- (0, import_react20.useEffect)(() => {
1324
+ const ref = (0, import_react21.useRef)(null);
1325
+ (0, import_react21.useEffect)(() => {
1236
1326
  if (isEditing) {
1237
1327
  selectAll(ref.current);
1238
1328
  }
@@ -1257,6 +1347,8 @@ var selectAll = (el) => {
1257
1347
  CtaButton,
1258
1348
  EditableField,
1259
1349
  EllipsisWithTooltip,
1350
+ FileUploadDropzone,
1351
+ FileUploadRow,
1260
1352
  FloatingActionsBar,
1261
1353
  Form,
1262
1354
  GlobalDialog,
package/dist/index.mjs CHANGED
@@ -732,14 +732,102 @@ function useFloatingActionsBar() {
732
732
  return context;
733
733
  }
734
734
 
735
- // src/components/popover/body.tsx
735
+ // src/components/file-upload/file-upload-dropzone.tsx
736
736
  import * as React19 from "react";
737
+ import { useRef as useRef4 } from "react";
738
+ import { UploadIcon } from "@elementor/icons";
739
+ import { Card as Card3, Link as Link2, Stack as Stack2, Typography as Typography5, useUnstableDropZone } from "@elementor/ui";
740
+ import { __ as __6 } from "@wordpress/i18n";
741
+ var cardSx = {
742
+ minHeight: 152,
743
+ border: "2px dashed",
744
+ borderColor: "divider",
745
+ borderRadius: 1
746
+ };
747
+ var FileUploadDropzone = ({
748
+ onFileSelected,
749
+ allowedFileTypes,
750
+ accept,
751
+ regionLabel,
752
+ primaryLabel,
753
+ secondaryLabel,
754
+ helperText
755
+ }) => {
756
+ const fileInputRef = useRef4(null);
757
+ const { getDropZoneProps } = useUnstableDropZone({
758
+ allowedFileTypes,
759
+ onChange: ({ valid }) => {
760
+ if (valid[0]) {
761
+ onFileSelected(valid[0]);
762
+ }
763
+ }
764
+ });
765
+ const dropZoneProps = getDropZoneProps();
766
+ const handleBrowseClick = () => fileInputRef.current?.click();
767
+ const handleFileInputChange = (event) => {
768
+ const file = event.target.files?.[0];
769
+ if (file) {
770
+ onFileSelected(file);
771
+ }
772
+ event.target.value = "";
773
+ };
774
+ return /* @__PURE__ */ React19.createElement(
775
+ Card3,
776
+ {
777
+ variant: "outlined",
778
+ role: "region",
779
+ "aria-label": regionLabel ?? __6("File dropzone", "elementor"),
780
+ onDrop: dropZoneProps.onDrop,
781
+ onDragEnter: dropZoneProps.onDragEnter,
782
+ onDragLeave: dropZoneProps.onDragLeave,
783
+ onDragOver: dropZoneProps.onDragOver,
784
+ sx: cardSx
785
+ },
786
+ /* @__PURE__ */ React19.createElement(Stack2, { alignItems: "center", spacing: 1, padding: 3 }, /* @__PURE__ */ React19.createElement(UploadIcon, { fontSize: "medium" }), /* @__PURE__ */ React19.createElement(Stack2, { direction: "row", spacing: 0.5, alignItems: "center" }, /* @__PURE__ */ React19.createElement(Link2, { component: "button", type: "button", underline: "always", onClick: handleBrowseClick }, /* @__PURE__ */ React19.createElement(Typography5, { variant: "body1", component: "span" }, primaryLabel ?? __6("Upload file", "elementor"))), /* @__PURE__ */ React19.createElement(Typography5, { variant: "body1" }, secondaryLabel ?? __6("or drag and drop", "elementor"))), helperText ? /* @__PURE__ */ React19.createElement(Typography5, { variant: "caption", color: "text.secondary" }, helperText) : null),
787
+ /* @__PURE__ */ React19.createElement("input", { ref: fileInputRef, type: "file", accept, hidden: true, onChange: handleFileInputChange })
788
+ );
789
+ };
790
+
791
+ // src/components/file-upload/file-upload-row.tsx
792
+ import * as React20 from "react";
793
+ import { XIcon as XIcon2 } from "@elementor/icons";
794
+ import { Card as Card4, IconButton as IconButton2, Stack as Stack3, Typography as Typography6 } from "@elementor/ui";
795
+ import { __ as __7 } from "@wordpress/i18n";
796
+ var BYTES_PER_KILOBYTE = 1024;
797
+ var cardSx2 = {
798
+ minHeight: 152,
799
+ border: "2px dashed",
800
+ borderColor: "divider",
801
+ borderRadius: 1
802
+ };
803
+ var formatFileSize = (sizeInBytes) => {
804
+ const kilobytes = Math.max(1, Math.round(sizeInBytes / BYTES_PER_KILOBYTE));
805
+ return `${kilobytes}kb`;
806
+ };
807
+ var FileUploadRow = ({ file, onRemove, statusLabel }) => {
808
+ return /* @__PURE__ */ React20.createElement(Card4, { variant: "outlined", sx: cardSx2 }, /* @__PURE__ */ React20.createElement(
809
+ Stack3,
810
+ {
811
+ direction: "row",
812
+ alignItems: "center",
813
+ justifyContent: "space-between",
814
+ padding: 2,
815
+ spacing: 2,
816
+ minHeight: 152
817
+ },
818
+ /* @__PURE__ */ React20.createElement(Stack3, { direction: "column", spacing: 0.5, minWidth: 0, flex: 1 }, /* @__PURE__ */ React20.createElement(Typography6, { variant: "subtitle2", noWrap: true }, file.name), /* @__PURE__ */ React20.createElement(Typography6, { variant: "caption", color: "text.secondary", noWrap: true }, formatFileSize(file.size), " \xB7 ", statusLabel ?? __7("Complete", "elementor"))),
819
+ /* @__PURE__ */ React20.createElement(IconButton2, { size: "small", onClick: onRemove, "aria-label": __7("Remove file", "elementor") }, /* @__PURE__ */ React20.createElement(XIcon2, { fontSize: "inherit" }))
820
+ ));
821
+ };
822
+
823
+ // src/components/popover/body.tsx
824
+ import * as React21 from "react";
737
825
  import { Box as Box6 } from "@elementor/ui";
738
826
  var SECTION_PADDING_INLINE = 32;
739
827
  var DEFAULT_POPOVER_HEIGHT = 348;
740
828
  var FALLBACK_POPOVER_WIDTH = 220;
741
829
  var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) => {
742
- return /* @__PURE__ */ React19.createElement(
830
+ return /* @__PURE__ */ React21.createElement(
743
831
  Box6,
744
832
  {
745
833
  display: "flex",
@@ -757,7 +845,7 @@ var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) =>
757
845
  };
758
846
 
759
847
  // src/components/popover/section-popover-body.tsx
760
- import * as React20 from "react";
848
+ import * as React22 from "react";
761
849
 
762
850
  // src/contexts/section-context.tsx
763
851
  import { createContext as createContext2, useContext as useContext2 } from "react";
@@ -772,12 +860,12 @@ var useSectionWidth = () => {
772
860
  // src/components/popover/section-popover-body.tsx
773
861
  var SectionPopoverBody = (props) => {
774
862
  const sectionWidth = useSectionWidth();
775
- return /* @__PURE__ */ React20.createElement(PopoverBody, { ...props, width: sectionWidth });
863
+ return /* @__PURE__ */ React22.createElement(PopoverBody, { ...props, width: sectionWidth });
776
864
  };
777
865
 
778
866
  // src/components/popover/header.tsx
779
- import * as React21 from "react";
780
- import { CloseButton as CloseButton2, Stack as Stack2, Typography as Typography5 } from "@elementor/ui";
867
+ import * as React23 from "react";
868
+ import { CloseButton as CloseButton2, Stack as Stack4, Typography as Typography7 } from "@elementor/ui";
781
869
  var SIZE2 = "tiny";
782
870
  var PopoverHeader = ({ title, onClose, icon, actions }) => {
783
871
  const paddingAndSizing = {
@@ -786,12 +874,12 @@ var PopoverHeader = ({ title, onClose, icon, actions }) => {
786
874
  py: 1.5,
787
875
  maxHeight: 36
788
876
  };
789
- return /* @__PURE__ */ React21.createElement(Stack2, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React21.createElement(Typography5, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React21.createElement(Stack2, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React21.createElement(CloseButton2, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
877
+ return /* @__PURE__ */ React23.createElement(Stack4, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React23.createElement(Typography7, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React23.createElement(Stack4, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React23.createElement(CloseButton2, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
790
878
  };
791
879
 
792
880
  // src/components/popover/menu-list.tsx
793
- import * as React22 from "react";
794
- import { useEffect as useEffect8, useMemo, useRef as useRef4 } from "react";
881
+ import * as React24 from "react";
882
+ import { useEffect as useEffect8, useMemo, useRef as useRef5 } from "react";
795
883
  import { Box as Box7, ListItem, MenuList, MenuSubheader, styled as styled5, useTheme } from "@elementor/ui";
796
884
  import { useVirtualizer } from "@tanstack/react-virtual";
797
885
  var ITEM_HEIGHT = 32;
@@ -819,7 +907,7 @@ var PopoverMenuList = ({
819
907
  noResultsComponent,
820
908
  menuListTemplate: CustomMenuList
821
909
  }) => {
822
- const containerRef = useRef4(null);
910
+ const containerRef = useRef5(null);
823
911
  const scrollTop = useScrollTop({ containerRef });
824
912
  const theme = useTheme();
825
913
  const MenuListComponent = CustomMenuList || StyledMenuList;
@@ -862,7 +950,7 @@ var PopoverMenuList = ({
862
950
  }, [items]);
863
951
  useScrollToSelected({ selectedValue, items, virtualizer });
864
952
  const virtualItems = virtualizer.getVirtualItems();
865
- return /* @__PURE__ */ React22.createElement(Box7, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React22.createElement(
953
+ return /* @__PURE__ */ React24.createElement(Box7, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React24.createElement(
866
954
  MenuListComponent,
867
955
  {
868
956
  role: "listbox",
@@ -880,7 +968,7 @@ var PopoverMenuList = ({
880
968
  }
881
969
  if (item.type === "category") {
882
970
  const shouldStick = virtualRow.start + MENU_LIST_PADDING_TOP <= scrollTop;
883
- return /* @__PURE__ */ React22.createElement(
971
+ return /* @__PURE__ */ React24.createElement(
884
972
  MenuSubheader,
885
973
  {
886
974
  key: virtualRow.key,
@@ -891,7 +979,7 @@ var PopoverMenuList = ({
891
979
  );
892
980
  }
893
981
  const isDisabled = item.disabled;
894
- return /* @__PURE__ */ React22.createElement(
982
+ return /* @__PURE__ */ React24.createElement(
895
983
  ListItem,
896
984
  {
897
985
  key: virtualRow.key,
@@ -961,15 +1049,15 @@ var StyledMenuList = styled5(MenuList)(({ theme }) => ({
961
1049
  }));
962
1050
 
963
1051
  // src/components/popover/popover-action.tsx
964
- import * as React23 from "react";
965
- import { bindPopover, bindTrigger, IconButton as IconButton2, Popover, Tooltip as Tooltip3, usePopupState } from "@elementor/ui";
1052
+ import * as React25 from "react";
1053
+ import { bindPopover, bindTrigger, IconButton as IconButton3, Popover, Tooltip as Tooltip3, usePopupState } from "@elementor/ui";
966
1054
  var SIZE3 = "tiny";
967
1055
  function PopoverAction({ title, visible = true, icon: Icon, content: PopoverContent }) {
968
1056
  const { popupState, triggerProps, popoverProps } = useFloatingActionsPopover();
969
1057
  if (!visible) {
970
1058
  return null;
971
1059
  }
972
- return /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(Tooltip3, { placement: "top", title }, /* @__PURE__ */ React23.createElement(IconButton2, { "aria-label": title, size: SIZE3, ...triggerProps }, /* @__PURE__ */ React23.createElement(Icon, { fontSize: SIZE3 }))), /* @__PURE__ */ React23.createElement(
1060
+ return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(Tooltip3, { placement: "top", title }, /* @__PURE__ */ React25.createElement(IconButton3, { "aria-label": title, size: SIZE3, ...triggerProps }, /* @__PURE__ */ React25.createElement(Icon, { fontSize: SIZE3 }))), /* @__PURE__ */ React25.createElement(
973
1061
  Popover,
974
1062
  {
975
1063
  disableScrollLock: true,
@@ -986,7 +1074,7 @@ function PopoverAction({ title, visible = true, icon: Icon, content: PopoverCont
986
1074
  },
987
1075
  ...popoverProps
988
1076
  },
989
- /* @__PURE__ */ React23.createElement(PopoverContent, { close: popupState.close })
1077
+ /* @__PURE__ */ React25.createElement(PopoverContent, { close: popupState.close })
990
1078
  ));
991
1079
  }
992
1080
  function useFloatingActionsPopover() {
@@ -1014,9 +1102,9 @@ function useFloatingActionsPopover() {
1014
1102
  }
1015
1103
 
1016
1104
  // src/components/save-changes-dialog.tsx
1017
- import * as React24 from "react";
1105
+ import * as React26 from "react";
1018
1106
  import { useState as useState8 } from "react";
1019
- import { AlertTriangleFilledIcon, XIcon as XIcon2 } from "@elementor/icons";
1107
+ import { AlertTriangleFilledIcon, XIcon as XIcon3 } from "@elementor/icons";
1020
1108
  import {
1021
1109
  Button as Button6,
1022
1110
  Dialog as Dialog3,
@@ -1024,12 +1112,12 @@ import {
1024
1112
  DialogContent,
1025
1113
  DialogContentText,
1026
1114
  DialogTitle as DialogTitle2,
1027
- IconButton as IconButton3,
1028
- Stack as Stack3
1115
+ IconButton as IconButton4,
1116
+ Stack as Stack5
1029
1117
  } from "@elementor/ui";
1030
1118
  var TITLE_ID = "save-changes-dialog";
1031
- var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React24.createElement(Dialog3, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
1032
- var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React24.createElement(
1119
+ var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React26.createElement(Dialog3, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
1120
+ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React26.createElement(
1033
1121
  DialogTitle2,
1034
1122
  {
1035
1123
  id: TITLE_ID,
@@ -1038,11 +1126,11 @@ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React24.
1038
1126
  gap: 1,
1039
1127
  sx: { lineHeight: 1, justifyContent: "space-between" }
1040
1128
  },
1041
- /* @__PURE__ */ React24.createElement(Stack3, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React24.createElement(AlertTriangleFilledIcon, { color: "secondary" }), children),
1042
- onClose && /* @__PURE__ */ React24.createElement(IconButton3, { onClick: onClose, size: "small" }, /* @__PURE__ */ React24.createElement(XIcon2, null))
1129
+ /* @__PURE__ */ React26.createElement(Stack5, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React26.createElement(AlertTriangleFilledIcon, { color: "secondary" }), children),
1130
+ onClose && /* @__PURE__ */ React26.createElement(IconButton4, { onClick: onClose, size: "small" }, /* @__PURE__ */ React26.createElement(XIcon3, null))
1043
1131
  );
1044
- var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React24.createElement(DialogContent, null, children);
1045
- var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React24.createElement(DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
1132
+ var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React26.createElement(DialogContent, null, children);
1133
+ var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React26.createElement(DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
1046
1134
  var SaveChangesDialogActions = ({ actions }) => {
1047
1135
  const [isConfirming, setIsConfirming] = useState8(false);
1048
1136
  const { cancel, confirm, discard } = actions;
@@ -1051,7 +1139,7 @@ var SaveChangesDialogActions = ({ actions }) => {
1051
1139
  await confirm.action();
1052
1140
  setIsConfirming(false);
1053
1141
  };
1054
- return /* @__PURE__ */ React24.createElement(DialogActions2, null, cancel && /* @__PURE__ */ React24.createElement(Button6, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React24.createElement(Button6, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React24.createElement(Button6, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
1142
+ return /* @__PURE__ */ React26.createElement(DialogActions2, null, cancel && /* @__PURE__ */ React26.createElement(Button6, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React26.createElement(Button6, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React26.createElement(Button6, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
1055
1143
  };
1056
1144
  SaveChangesDialog.Title = SaveChangesDialogTitle;
1057
1145
  SaveChangesDialog.Content = SaveChangesDialogContent;
@@ -1065,7 +1153,7 @@ var useDialog = () => {
1065
1153
  };
1066
1154
 
1067
1155
  // src/components/confirmation-dialog.tsx
1068
- import * as React25 from "react";
1156
+ import * as React27 from "react";
1069
1157
  import { useState as useState9 } from "react";
1070
1158
  import { AlertOctagonFilledIcon } from "@elementor/icons";
1071
1159
  import {
@@ -1077,18 +1165,18 @@ import {
1077
1165
  DialogContentText as DialogContentText2,
1078
1166
  DialogTitle as DialogTitle3,
1079
1167
  FormControlLabel as FormControlLabel2,
1080
- Typography as Typography6
1168
+ Typography as Typography8
1081
1169
  } from "@elementor/ui";
1082
- import { __ as __6 } from "@wordpress/i18n";
1170
+ import { __ as __8 } from "@wordpress/i18n";
1083
1171
  var TITLE_ID2 = "confirmation-dialog";
1084
- var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React25.createElement(Dialog4, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "sm" }, children);
1172
+ var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React27.createElement(Dialog4, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "sm" }, children);
1085
1173
  var ConfirmationDialogTitle = ({
1086
1174
  children,
1087
1175
  icon: Icon = AlertOctagonFilledIcon,
1088
1176
  iconColor = "error"
1089
- }) => /* @__PURE__ */ React25.createElement(DialogTitle3, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React25.createElement(Icon, { color: iconColor }), children);
1090
- var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React25.createElement(DialogContent2, { sx: { mt: 2 } }, children);
1091
- var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React25.createElement(DialogContentText2, { variant: "body2", color: "secondary", ...props });
1177
+ }) => /* @__PURE__ */ React27.createElement(DialogTitle3, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React27.createElement(Icon, { color: iconColor }), children);
1178
+ var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React27.createElement(DialogContent2, { sx: { mt: 2 } }, children);
1179
+ var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React27.createElement(DialogContentText2, { variant: "body2", color: "secondary", ...props });
1092
1180
  var ConfirmationDialogActions = ({
1093
1181
  onClose,
1094
1182
  onConfirm,
@@ -1096,7 +1184,7 @@ var ConfirmationDialogActions = ({
1096
1184
  confirmLabel,
1097
1185
  color = "error",
1098
1186
  onSuppressMessage,
1099
- suppressLabel = __6("Don't show this again", "elementor")
1187
+ suppressLabel = __8("Don't show this again", "elementor")
1100
1188
  }) => {
1101
1189
  const [dontShowAgain, setDontShowAgain] = useState9(false);
1102
1190
  const handleConfirm = () => {
@@ -1105,10 +1193,10 @@ var ConfirmationDialogActions = ({
1105
1193
  }
1106
1194
  onConfirm();
1107
1195
  };
1108
- return /* @__PURE__ */ React25.createElement(DialogActions3, { sx: onSuppressMessage ? { justifyContent: "space-between", alignItems: "center" } : void 0 }, onSuppressMessage && /* @__PURE__ */ React25.createElement(
1196
+ return /* @__PURE__ */ React27.createElement(DialogActions3, { sx: onSuppressMessage ? { justifyContent: "space-between", alignItems: "center" } : void 0 }, onSuppressMessage && /* @__PURE__ */ React27.createElement(
1109
1197
  FormControlLabel2,
1110
1198
  {
1111
- control: /* @__PURE__ */ React25.createElement(
1199
+ control: /* @__PURE__ */ React27.createElement(
1112
1200
  Checkbox2,
1113
1201
  {
1114
1202
  checked: dontShowAgain,
@@ -1117,9 +1205,9 @@ var ConfirmationDialogActions = ({
1117
1205
  color: "secondary"
1118
1206
  }
1119
1207
  ),
1120
- label: /* @__PURE__ */ React25.createElement(Typography6, { variant: "body2", color: "text.secondary" }, suppressLabel)
1208
+ label: /* @__PURE__ */ React27.createElement(Typography8, { variant: "body2", color: "text.secondary" }, suppressLabel)
1121
1209
  }
1122
- ), /* @__PURE__ */ React25.createElement("div", null, /* @__PURE__ */ React25.createElement(Button7, { color: "secondary", onClick: onClose }, cancelLabel ?? __6("Not now", "elementor")), /* @__PURE__ */ React25.createElement(Button7, { autoFocus: true, variant: "contained", color, onClick: handleConfirm, sx: { ml: 1 } }, confirmLabel ?? __6("Delete", "elementor"))));
1210
+ ), /* @__PURE__ */ React27.createElement("div", null, /* @__PURE__ */ React27.createElement(Button7, { color: "secondary", onClick: onClose }, cancelLabel ?? __8("Not now", "elementor")), /* @__PURE__ */ React27.createElement(Button7, { autoFocus: true, variant: "contained", color, onClick: handleConfirm, sx: { ml: 1 } }, confirmLabel ?? __8("Delete", "elementor"))));
1123
1211
  };
1124
1212
  ConfirmationDialog.Title = ConfirmationDialogTitle;
1125
1213
  ConfirmationDialog.Content = ConfirmationDialogContent;
@@ -1127,7 +1215,7 @@ ConfirmationDialog.ContentText = ConfirmationDialogContentText;
1127
1215
  ConfirmationDialog.Actions = ConfirmationDialogActions;
1128
1216
 
1129
1217
  // src/hooks/use-editable.ts
1130
- import { useEffect as useEffect9, useRef as useRef5, useState as useState10 } from "react";
1218
+ import { useEffect as useEffect9, useRef as useRef6, useState as useState10 } from "react";
1131
1219
  var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
1132
1220
  const [isEditing, setIsEditing] = useState10(false);
1133
1221
  const [error, setError] = useState10(null);
@@ -1212,7 +1300,7 @@ var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
1212
1300
  };
1213
1301
  };
1214
1302
  var useSelection = (isEditing) => {
1215
- const ref = useRef5(null);
1303
+ const ref = useRef6(null);
1216
1304
  useEffect9(() => {
1217
1305
  if (isEditing) {
1218
1306
  selectAll(ref.current);
@@ -1237,6 +1325,8 @@ export {
1237
1325
  CtaButton,
1238
1326
  EditableField,
1239
1327
  EllipsisWithTooltip,
1328
+ FileUploadDropzone,
1329
+ FileUploadRow,
1240
1330
  FloatingActionsBar,
1241
1331
  Form,
1242
1332
  GlobalDialog,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-ui",
3
3
  "description": "Elementor Editor UI",
4
- "version": "4.1.0-822",
4
+ "version": "4.1.0-824",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,7 +37,7 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor-v1-adapters": "4.1.0-822",
40
+ "@elementor/editor-v1-adapters": "4.1.0-824",
41
41
  "@elementor/icons": "^1.68.0",
42
42
  "@elementor/ui": "1.37.5",
43
43
  "@tanstack/react-virtual": "^3.13.3",
@@ -0,0 +1,86 @@
1
+ import * as React from 'react';
2
+ import { useRef } from 'react';
3
+ import { UploadIcon } from '@elementor/icons';
4
+ import { Card, Link, Stack, Typography, useUnstableDropZone } from '@elementor/ui';
5
+ import { __ } from '@wordpress/i18n';
6
+
7
+ const cardSx = {
8
+ minHeight: 152,
9
+ border: '2px dashed',
10
+ borderColor: 'divider',
11
+ borderRadius: 1,
12
+ };
13
+
14
+ type Props = {
15
+ onFileSelected: ( file: File ) => void;
16
+ allowedFileTypes: `${ string }/${ string }`[];
17
+ accept: string;
18
+ regionLabel?: string;
19
+ primaryLabel?: string;
20
+ secondaryLabel?: string;
21
+ helperText?: string;
22
+ };
23
+
24
+ export const FileUploadDropzone = ( {
25
+ onFileSelected,
26
+ allowedFileTypes,
27
+ accept,
28
+ regionLabel,
29
+ primaryLabel,
30
+ secondaryLabel,
31
+ helperText,
32
+ }: Props ) => {
33
+ const fileInputRef = useRef< HTMLInputElement >( null );
34
+
35
+ const { getDropZoneProps } = useUnstableDropZone( {
36
+ allowedFileTypes,
37
+ onChange: ( { valid } ) => {
38
+ if ( valid[ 0 ] ) {
39
+ onFileSelected( valid[ 0 ] );
40
+ }
41
+ },
42
+ } );
43
+
44
+ const dropZoneProps = getDropZoneProps();
45
+
46
+ const handleBrowseClick = () => fileInputRef.current?.click();
47
+
48
+ const handleFileInputChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
49
+ const file = event.target.files?.[ 0 ];
50
+ if ( file ) {
51
+ onFileSelected( file );
52
+ }
53
+ event.target.value = '';
54
+ };
55
+
56
+ return (
57
+ <Card
58
+ variant="outlined"
59
+ role="region"
60
+ aria-label={ regionLabel ?? __( 'File dropzone', 'elementor' ) }
61
+ onDrop={ dropZoneProps.onDrop }
62
+ onDragEnter={ dropZoneProps.onDragEnter }
63
+ onDragLeave={ dropZoneProps.onDragLeave }
64
+ onDragOver={ dropZoneProps.onDragOver }
65
+ sx={ cardSx }
66
+ >
67
+ <Stack alignItems="center" spacing={ 1 } padding={ 3 }>
68
+ <UploadIcon fontSize="medium" />
69
+ <Stack direction="row" spacing={ 0.5 } alignItems="center">
70
+ <Link component="button" type="button" underline="always" onClick={ handleBrowseClick }>
71
+ <Typography variant="body1" component="span">
72
+ { primaryLabel ?? __( 'Upload file', 'elementor' ) }
73
+ </Typography>
74
+ </Link>
75
+ <Typography variant="body1">{ secondaryLabel ?? __( 'or drag and drop', 'elementor' ) }</Typography>
76
+ </Stack>
77
+ { helperText ? (
78
+ <Typography variant="caption" color="text.secondary">
79
+ { helperText }
80
+ </Typography>
81
+ ) : null }
82
+ </Stack>
83
+ <input ref={ fileInputRef } type="file" accept={ accept } hidden onChange={ handleFileInputChange } />
84
+ </Card>
85
+ );
86
+ };
@@ -0,0 +1,51 @@
1
+ import * as React from 'react';
2
+ import { XIcon } from '@elementor/icons';
3
+ import { Card, IconButton, Stack, Typography } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ const BYTES_PER_KILOBYTE = 1024;
7
+
8
+ const cardSx = {
9
+ minHeight: 152,
10
+ border: '2px dashed',
11
+ borderColor: 'divider',
12
+ borderRadius: 1,
13
+ };
14
+
15
+ type Props = {
16
+ file: File;
17
+ onRemove: () => void;
18
+ statusLabel?: string;
19
+ };
20
+
21
+ const formatFileSize = ( sizeInBytes: number ) => {
22
+ const kilobytes = Math.max( 1, Math.round( sizeInBytes / BYTES_PER_KILOBYTE ) );
23
+ return `${ kilobytes }kb`;
24
+ };
25
+
26
+ export const FileUploadRow = ( { file, onRemove, statusLabel }: Props ) => {
27
+ return (
28
+ <Card variant="outlined" sx={ cardSx }>
29
+ <Stack
30
+ direction="row"
31
+ alignItems="center"
32
+ justifyContent="space-between"
33
+ padding={ 2 }
34
+ spacing={ 2 }
35
+ minHeight={ 152 }
36
+ >
37
+ <Stack direction="column" spacing={ 0.5 } minWidth={ 0 } flex={ 1 }>
38
+ <Typography variant="subtitle2" noWrap>
39
+ { file.name }
40
+ </Typography>
41
+ <Typography variant="caption" color="text.secondary" noWrap>
42
+ { formatFileSize( file.size ) } · { statusLabel ?? __( 'Complete', 'elementor' ) }
43
+ </Typography>
44
+ </Stack>
45
+ <IconButton size="small" onClick={ onRemove } aria-label={ __( 'Remove file', 'elementor' ) }>
46
+ <XIcon fontSize="inherit" />
47
+ </IconButton>
48
+ </Stack>
49
+ </Card>
50
+ );
51
+ };
@@ -0,0 +1,2 @@
1
+ export { FileUploadDropzone } from './file-upload-dropzone';
2
+ export { FileUploadRow } from './file-upload-row';
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export { PromotionPopover } from './components/promotions/promotion-popover';
18
18
  export { PromotionChip } from './components/promotions/promotion-chip';
19
19
  export { PromotionAlert } from './components/promotions/promotion-alert';
20
20
  export { FloatingActionsBar, useFloatingActionsBar } from './components/floating-bar';
21
+ export { FileUploadDropzone, FileUploadRow } from './components/file-upload';
21
22
 
22
23
  export * from './components/popover';
23
24
  export * from './components/save-changes-dialog';