@elementor/editor-ui 4.0.0-501 → 4.0.0-503

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
@@ -1,7 +1,7 @@
1
1
  import * as _emotion_styled from '@emotion/styled';
2
2
  import * as _mui_system from '@mui/system';
3
3
  import * as React$1 from 'react';
4
- import { ReactNode, PropsWithChildren, ReactElement, ComponentProps, RefObject } from 'react';
4
+ import { ReactNode, PropsWithChildren, ReactElement, ComponentProps, ElementType, ComponentType, RefObject } from 'react';
5
5
  import * as _mui_material from '@mui/material';
6
6
  import { MenuItemProps, MenuItemTextProps, TypographyProps, AlertProps, InfotipProps, BoxProps, ButtonProps, MenuList, DialogProps, DialogContentTextProps } from '@elementor/ui';
7
7
 
@@ -125,6 +125,14 @@ declare const PromotionPopover: ({ children, open, placement, slotProps, ...card
125
125
 
126
126
  declare const PromotionChip: React$1.ForwardRefExoticComponent<React$1.RefAttributes<HTMLDivElement>>;
127
127
 
128
+ declare function FloatingActionsBar({ actions, children }: PropsWithChildren<{
129
+ actions: ReactElement[];
130
+ }>): React$1.JSX.Element;
131
+ declare function useFloatingActionsBar(): {
132
+ open: boolean;
133
+ setOpen: React$1.Dispatch<React$1.SetStateAction<boolean>>;
134
+ };
135
+
128
136
  type PopoverBodyProps = PropsWithChildren<{
129
137
  height?: number | 'auto';
130
138
  width?: number;
@@ -170,6 +178,16 @@ declare const StyledMenuList: _emotion_styled.StyledComponent<any, {}, {
170
178
  ref?: React$1.Ref<any> | undefined;
171
179
  }>;
172
180
 
181
+ type PopoverActionProps = {
182
+ title: string;
183
+ visible?: boolean;
184
+ icon: ElementType;
185
+ content: ComponentType<{
186
+ close: () => void;
187
+ }>;
188
+ };
189
+ declare function PopoverAction({ title, visible, icon: Icon, content: PopoverContent }: PopoverActionProps): React$1.JSX.Element | null;
190
+
173
191
  declare const SaveChangesDialog: {
174
192
  ({ children, onClose }: Pick<DialogProps, "children" | "onClose">): React$1.JSX.Element;
175
193
  Title: ({ children, onClose }: React$1.PropsWithChildren & {
@@ -240,4 +258,4 @@ declare const useEditable: ({ value, onSubmit, validation, onClick, onError }: U
240
258
  };
241
259
  };
242
260
 
243
- export { CollapseIcon, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, openDialog, useDialog, useEditable, useSectionWidth };
261
+ export { CollapseIcon, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, FloatingActionsBar, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverAction, type PopoverActionProps, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, openDialog, useDialog, useEditable, useFloatingActionsBar, useSectionWidth };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _emotion_styled from '@emotion/styled';
2
2
  import * as _mui_system from '@mui/system';
3
3
  import * as React$1 from 'react';
4
- import { ReactNode, PropsWithChildren, ReactElement, ComponentProps, RefObject } from 'react';
4
+ import { ReactNode, PropsWithChildren, ReactElement, ComponentProps, ElementType, ComponentType, RefObject } from 'react';
5
5
  import * as _mui_material from '@mui/material';
6
6
  import { MenuItemProps, MenuItemTextProps, TypographyProps, AlertProps, InfotipProps, BoxProps, ButtonProps, MenuList, DialogProps, DialogContentTextProps } from '@elementor/ui';
7
7
 
@@ -125,6 +125,14 @@ declare const PromotionPopover: ({ children, open, placement, slotProps, ...card
125
125
 
126
126
  declare const PromotionChip: React$1.ForwardRefExoticComponent<React$1.RefAttributes<HTMLDivElement>>;
127
127
 
128
+ declare function FloatingActionsBar({ actions, children }: PropsWithChildren<{
129
+ actions: ReactElement[];
130
+ }>): React$1.JSX.Element;
131
+ declare function useFloatingActionsBar(): {
132
+ open: boolean;
133
+ setOpen: React$1.Dispatch<React$1.SetStateAction<boolean>>;
134
+ };
135
+
128
136
  type PopoverBodyProps = PropsWithChildren<{
129
137
  height?: number | 'auto';
130
138
  width?: number;
@@ -170,6 +178,16 @@ declare const StyledMenuList: _emotion_styled.StyledComponent<any, {}, {
170
178
  ref?: React$1.Ref<any> | undefined;
171
179
  }>;
172
180
 
181
+ type PopoverActionProps = {
182
+ title: string;
183
+ visible?: boolean;
184
+ icon: ElementType;
185
+ content: ComponentType<{
186
+ close: () => void;
187
+ }>;
188
+ };
189
+ declare function PopoverAction({ title, visible, icon: Icon, content: PopoverContent }: PopoverActionProps): React$1.JSX.Element | null;
190
+
173
191
  declare const SaveChangesDialog: {
174
192
  ({ children, onClose }: Pick<DialogProps, "children" | "onClose">): React$1.JSX.Element;
175
193
  Title: ({ children, onClose }: React$1.PropsWithChildren & {
@@ -240,4 +258,4 @@ declare const useEditable: ({ value, onSubmit, validation, onClick, onError }: U
240
258
  };
241
259
  };
242
260
 
243
- export { CollapseIcon, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, openDialog, useDialog, useEditable, useSectionWidth };
261
+ export { CollapseIcon, ConfirmationDialog, CtaButton, EditableField, EllipsisWithTooltip, FloatingActionsBar, Form, GlobalDialog, ITEM_HEIGHT, InfoAlert, InfoTipCard, IntroductionModal, MenuItemInfotip, MenuListItem, PopoverAction, type PopoverActionProps, PopoverBody, PopoverHeader, PopoverMenuList, type PopoverMenuListProps, PromotionChip, PromotionInfotip, PromotionPopover, SaveChangesDialog, SearchField, SectionPopoverBody, SectionRefContext, StyledMenuList, ThemeProvider, type VirtualizedItem, WarningInfotip, closeDialog, openDialog, useDialog, useEditable, useFloatingActionsBar, useSectionWidth };
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  CtaButton: () => CtaButton,
36
36
  EditableField: () => EditableField,
37
37
  EllipsisWithTooltip: () => EllipsisWithTooltip,
38
+ FloatingActionsBar: () => FloatingActionsBar,
38
39
  Form: () => Form,
39
40
  GlobalDialog: () => GlobalDialog,
40
41
  ITEM_HEIGHT: () => ITEM_HEIGHT,
@@ -43,6 +44,7 @@ __export(index_exports, {
43
44
  IntroductionModal: () => IntroductionModal,
44
45
  MenuItemInfotip: () => MenuItemInfotip,
45
46
  MenuListItem: () => MenuListItem,
47
+ PopoverAction: () => PopoverAction,
46
48
  PopoverBody: () => PopoverBody,
47
49
  PopoverHeader: () => PopoverHeader,
48
50
  PopoverMenuList: () => PopoverMenuList,
@@ -60,6 +62,7 @@ __export(index_exports, {
60
62
  openDialog: () => openDialog,
61
63
  useDialog: () => useDialog,
62
64
  useEditable: () => useEditable,
65
+ useFloatingActionsBar: () => useFloatingActionsBar,
63
66
  useSectionWidth: () => useSectionWidth
64
67
  });
65
68
  module.exports = __toCommonJS(index_exports);
@@ -580,15 +583,43 @@ var PromotionChip = React15.forwardRef(({ ...props }, ref) => {
580
583
  );
581
584
  });
582
585
 
583
- // src/components/popover/body.tsx
586
+ // src/components/floating-bar.tsx
584
587
  var React16 = __toESM(require("react"));
588
+ var import_react11 = require("react");
585
589
  var import_ui16 = require("@elementor/ui");
590
+ var FloatingBarContainer = (0, import_ui16.styled)("span")`
591
+ display: contents;
592
+
593
+ .MuiFloatingActionBar-popper:has( .MuiFloatingActionBar-actions:empty ) {
594
+ display: none;
595
+ }
596
+
597
+ .MuiFloatingActionBar-popper {
598
+ z-index: 1000;
599
+ }
600
+ `;
601
+ var FloatingActionsContext = (0, import_react11.createContext)(null);
602
+ function FloatingActionsBar({ actions, children }) {
603
+ const [open, setOpen] = (0, import_react11.useState)(false);
604
+ return /* @__PURE__ */ React16.createElement(FloatingActionsContext.Provider, { value: { open, setOpen } }, /* @__PURE__ */ React16.createElement(FloatingBarContainer, null, /* @__PURE__ */ React16.createElement(import_ui16.UnstableFloatingActionBar, { actions, open: open || void 0 }, children)));
605
+ }
606
+ function useFloatingActionsBar() {
607
+ const context = (0, import_react11.useContext)(FloatingActionsContext);
608
+ if (!context) {
609
+ throw new Error("useFloatingActions must be used within a FloatingActionsBar");
610
+ }
611
+ return context;
612
+ }
613
+
614
+ // src/components/popover/body.tsx
615
+ var React17 = __toESM(require("react"));
616
+ var import_ui17 = require("@elementor/ui");
586
617
  var SECTION_PADDING_INLINE = 32;
587
618
  var DEFAULT_POPOVER_HEIGHT = 348;
588
619
  var FALLBACK_POPOVER_WIDTH = 220;
589
620
  var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) => {
590
- return /* @__PURE__ */ React16.createElement(
591
- import_ui16.Box,
621
+ return /* @__PURE__ */ React17.createElement(
622
+ import_ui17.Box,
592
623
  {
593
624
  display: "flex",
594
625
  flexDirection: "column",
@@ -605,13 +636,13 @@ var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) =>
605
636
  };
606
637
 
607
638
  // src/components/popover/section-popover-body.tsx
608
- var React17 = __toESM(require("react"));
639
+ var React18 = __toESM(require("react"));
609
640
 
610
641
  // src/contexts/section-context.tsx
611
- var import_react11 = require("react");
642
+ var import_react12 = require("react");
612
643
  var FALLBACK_SECTION_WIDTH = 320;
613
- var SectionRefContext = (0, import_react11.createContext)(null);
614
- var useSectionRef = () => (0, import_react11.useContext)(SectionRefContext);
644
+ var SectionRefContext = (0, import_react12.createContext)(null);
645
+ var useSectionRef = () => (0, import_react12.useContext)(SectionRefContext);
615
646
  var useSectionWidth = () => {
616
647
  const sectionRef = useSectionRef();
617
648
  return sectionRef?.current?.offsetWidth ?? FALLBACK_SECTION_WIDTH;
@@ -620,12 +651,12 @@ var useSectionWidth = () => {
620
651
  // src/components/popover/section-popover-body.tsx
621
652
  var SectionPopoverBody = (props) => {
622
653
  const sectionWidth = useSectionWidth();
623
- return /* @__PURE__ */ React17.createElement(PopoverBody, { ...props, width: sectionWidth });
654
+ return /* @__PURE__ */ React18.createElement(PopoverBody, { ...props, width: sectionWidth });
624
655
  };
625
656
 
626
657
  // src/components/popover/header.tsx
627
- var React18 = __toESM(require("react"));
628
- var import_ui17 = require("@elementor/ui");
658
+ var React19 = __toESM(require("react"));
659
+ var import_ui18 = require("@elementor/ui");
629
660
  var SIZE2 = "tiny";
630
661
  var PopoverHeader = ({ title, onClose, icon, actions }) => {
631
662
  const paddingAndSizing = {
@@ -634,23 +665,23 @@ var PopoverHeader = ({ title, onClose, icon, actions }) => {
634
665
  py: 1.5,
635
666
  maxHeight: 36
636
667
  };
637
- return /* @__PURE__ */ React18.createElement(import_ui17.Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React18.createElement(import_ui17.Typography, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React18.createElement(import_ui17.Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React18.createElement(import_ui17.CloseButton, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
668
+ return /* @__PURE__ */ React19.createElement(import_ui18.Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React19.createElement(import_ui18.Typography, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React19.createElement(import_ui18.Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React19.createElement(import_ui18.CloseButton, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
638
669
  };
639
670
 
640
671
  // src/components/popover/menu-list.tsx
641
- var React19 = __toESM(require("react"));
642
- var import_react14 = require("react");
643
- var import_ui18 = require("@elementor/ui");
672
+ var React20 = __toESM(require("react"));
673
+ var import_react15 = require("react");
674
+ var import_ui19 = require("@elementor/ui");
644
675
  var import_react_virtual = require("@tanstack/react-virtual");
645
676
 
646
677
  // src/hooks/use-scroll-to-selected.ts
647
- var import_react12 = require("react");
678
+ var import_react13 = require("react");
648
679
  var useScrollToSelected = ({
649
680
  selectedValue,
650
681
  items,
651
682
  virtualizer
652
683
  }) => {
653
- (0, import_react12.useEffect)(() => {
684
+ (0, import_react13.useEffect)(() => {
654
685
  if (!selectedValue || items.length === 0) {
655
686
  return;
656
687
  }
@@ -662,10 +693,10 @@ var useScrollToSelected = ({
662
693
  };
663
694
 
664
695
  // src/hooks/use-scroll-top.ts
665
- var import_react13 = require("react");
696
+ var import_react14 = require("react");
666
697
  var useScrollTop = ({ containerRef }) => {
667
- const [scrollTop, setScrollTop] = (0, import_react13.useState)(0);
668
- (0, import_react13.useEffect)(() => {
698
+ const [scrollTop, setScrollTop] = (0, import_react14.useState)(0);
699
+ (0, import_react14.useEffect)(() => {
669
700
  const container = containerRef.current;
670
701
  if (!container) {
671
702
  return;
@@ -705,11 +736,11 @@ var PopoverMenuList = ({
705
736
  noResultsComponent,
706
737
  menuListTemplate: CustomMenuList
707
738
  }) => {
708
- const containerRef = (0, import_react14.useRef)(null);
739
+ const containerRef = (0, import_react15.useRef)(null);
709
740
  const scrollTop = useScrollTop({ containerRef });
710
- const theme = (0, import_ui18.useTheme)();
741
+ const theme = (0, import_ui19.useTheme)();
711
742
  const MenuListComponent = CustomMenuList || StyledMenuList;
712
- const stickyIndices = (0, import_react14.useMemo)(
743
+ const stickyIndices = (0, import_react15.useMemo)(
713
744
  () => items.reduce((categoryIndices, item, index) => {
714
745
  if (item.type === "category") {
715
746
  categoryIndices.push(index);
@@ -743,12 +774,12 @@ var PopoverMenuList = ({
743
774
  rangeExtractor: getActiveItemIndices,
744
775
  onChange: onChangeCallback
745
776
  });
746
- (0, import_react14.useEffect)(() => {
777
+ (0, import_react15.useEffect)(() => {
747
778
  onChangeCallback(virtualizer);
748
779
  }, [items]);
749
780
  useScrollToSelected({ selectedValue, items, virtualizer });
750
781
  const virtualItems = virtualizer.getVirtualItems();
751
- return /* @__PURE__ */ React19.createElement(import_ui18.Box, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React19.createElement(
782
+ return /* @__PURE__ */ React20.createElement(import_ui19.Box, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React20.createElement(
752
783
  MenuListComponent,
753
784
  {
754
785
  role: "listbox",
@@ -766,8 +797,8 @@ var PopoverMenuList = ({
766
797
  }
767
798
  if (item.type === "category") {
768
799
  const shouldStick = virtualRow.start + MENU_LIST_PADDING_TOP <= scrollTop;
769
- return /* @__PURE__ */ React19.createElement(
770
- import_ui18.MenuSubheader,
800
+ return /* @__PURE__ */ React20.createElement(
801
+ import_ui19.MenuSubheader,
771
802
  {
772
803
  key: virtualRow.key,
773
804
  style: shouldStick ? {} : menuSubHeaderAbsoluteStyling(virtualRow.start),
@@ -777,8 +808,8 @@ var PopoverMenuList = ({
777
808
  );
778
809
  }
779
810
  const isDisabled = item.disabled;
780
- return /* @__PURE__ */ React19.createElement(
781
- import_ui18.ListItem,
811
+ return /* @__PURE__ */ React20.createElement(
812
+ import_ui19.ListItem,
782
813
  {
783
814
  key: virtualRow.key,
784
815
  role: "option",
@@ -817,7 +848,7 @@ var PopoverMenuList = ({
817
848
  })
818
849
  ));
819
850
  };
820
- var StyledMenuList = (0, import_ui18.styled)(import_ui18.MenuList)(({ theme }) => ({
851
+ var StyledMenuList = (0, import_ui19.styled)(import_ui19.MenuList)(({ theme }) => ({
821
852
  "& > li": {
822
853
  height: ITEM_HEIGHT,
823
854
  width: "100%",
@@ -846,15 +877,68 @@ var StyledMenuList = (0, import_ui18.styled)(import_ui18.MenuList)(({ theme }) =
846
877
  position: "relative"
847
878
  }));
848
879
 
880
+ // src/components/popover/popover-action.tsx
881
+ var React21 = __toESM(require("react"));
882
+ var import_ui20 = require("@elementor/ui");
883
+ var SIZE3 = "tiny";
884
+ function PopoverAction({ title, visible = true, icon: Icon, content: PopoverContent }) {
885
+ const { popupState, triggerProps, popoverProps } = useFloatingActionsPopover();
886
+ if (!visible) {
887
+ return null;
888
+ }
889
+ return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(import_ui20.Tooltip, { placement: "top", title }, /* @__PURE__ */ React21.createElement(import_ui20.IconButton, { "aria-label": title, size: SIZE3, ...triggerProps }, /* @__PURE__ */ React21.createElement(Icon, { fontSize: SIZE3 }))), /* @__PURE__ */ React21.createElement(
890
+ import_ui20.Popover,
891
+ {
892
+ disableScrollLock: true,
893
+ anchorOrigin: {
894
+ vertical: "bottom",
895
+ horizontal: "right"
896
+ },
897
+ transformOrigin: {
898
+ vertical: "top",
899
+ horizontal: "right"
900
+ },
901
+ PaperProps: {
902
+ sx: { my: 2.5 }
903
+ },
904
+ ...popoverProps
905
+ },
906
+ /* @__PURE__ */ React21.createElement(PopoverContent, { close: popupState.close })
907
+ ));
908
+ }
909
+ function useFloatingActionsPopover() {
910
+ const { setOpen } = useFloatingActionsBar();
911
+ const popupState = (0, import_ui20.usePopupState)({ variant: "popover" });
912
+ const triggerProps = (0, import_ui20.bindTrigger)(popupState);
913
+ const popoverProps = (0, import_ui20.bindPopover)(popupState);
914
+ const onClick = (e) => {
915
+ triggerProps.onClick(e);
916
+ setOpen(true);
917
+ };
918
+ const onClose = () => {
919
+ popoverProps.onClose();
920
+ setOpen(false);
921
+ };
922
+ const close = () => {
923
+ popupState.close();
924
+ setOpen(false);
925
+ };
926
+ return {
927
+ popupState: { ...popupState, close },
928
+ triggerProps: { ...triggerProps, onClick },
929
+ popoverProps: { ...popoverProps, onClose }
930
+ };
931
+ }
932
+
849
933
  // src/components/save-changes-dialog.tsx
850
- var React20 = __toESM(require("react"));
851
- var import_react15 = require("react");
934
+ var React22 = __toESM(require("react"));
935
+ var import_react16 = require("react");
852
936
  var import_icons7 = require("@elementor/icons");
853
- var import_ui19 = require("@elementor/ui");
937
+ var import_ui21 = require("@elementor/ui");
854
938
  var TITLE_ID = "save-changes-dialog";
855
- var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React20.createElement(import_ui19.Dialog, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
856
- var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React20.createElement(
857
- import_ui19.DialogTitle,
939
+ var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React22.createElement(import_ui21.Dialog, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
940
+ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React22.createElement(
941
+ import_ui21.DialogTitle,
858
942
  {
859
943
  id: TITLE_ID,
860
944
  display: "flex",
@@ -862,58 +946,58 @@ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React20.
862
946
  gap: 1,
863
947
  sx: { lineHeight: 1, justifyContent: "space-between" }
864
948
  },
865
- /* @__PURE__ */ React20.createElement(import_ui19.Stack, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React20.createElement(import_icons7.AlertTriangleFilledIcon, { color: "secondary" }), children),
866
- onClose && /* @__PURE__ */ React20.createElement(import_ui19.IconButton, { onClick: onClose, size: "small" }, /* @__PURE__ */ React20.createElement(import_icons7.XIcon, null))
949
+ /* @__PURE__ */ React22.createElement(import_ui21.Stack, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React22.createElement(import_icons7.AlertTriangleFilledIcon, { color: "secondary" }), children),
950
+ onClose && /* @__PURE__ */ React22.createElement(import_ui21.IconButton, { onClick: onClose, size: "small" }, /* @__PURE__ */ React22.createElement(import_icons7.XIcon, null))
867
951
  );
868
- var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React20.createElement(import_ui19.DialogContent, null, children);
869
- var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React20.createElement(import_ui19.DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
952
+ var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React22.createElement(import_ui21.DialogContent, null, children);
953
+ var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React22.createElement(import_ui21.DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
870
954
  var SaveChangesDialogActions = ({ actions }) => {
871
- const [isConfirming, setIsConfirming] = (0, import_react15.useState)(false);
955
+ const [isConfirming, setIsConfirming] = (0, import_react16.useState)(false);
872
956
  const { cancel, confirm, discard } = actions;
873
957
  const onConfirm = async () => {
874
958
  setIsConfirming(true);
875
959
  await confirm.action();
876
960
  setIsConfirming(false);
877
961
  };
878
- return /* @__PURE__ */ React20.createElement(import_ui19.DialogActions, null, cancel && /* @__PURE__ */ React20.createElement(import_ui19.Button, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React20.createElement(import_ui19.Button, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React20.createElement(import_ui19.Button, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
962
+ return /* @__PURE__ */ React22.createElement(import_ui21.DialogActions, null, cancel && /* @__PURE__ */ React22.createElement(import_ui21.Button, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React22.createElement(import_ui21.Button, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React22.createElement(import_ui21.Button, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
879
963
  };
880
964
  SaveChangesDialog.Title = SaveChangesDialogTitle;
881
965
  SaveChangesDialog.Content = SaveChangesDialogContent;
882
966
  SaveChangesDialog.ContentText = SaveChangesDialogContentText;
883
967
  SaveChangesDialog.Actions = SaveChangesDialogActions;
884
968
  var useDialog = () => {
885
- const [isOpen, setIsOpen] = (0, import_react15.useState)(false);
969
+ const [isOpen, setIsOpen] = (0, import_react16.useState)(false);
886
970
  const open = () => setIsOpen(true);
887
971
  const close = () => setIsOpen(false);
888
972
  return { isOpen, open, close };
889
973
  };
890
974
 
891
975
  // src/components/confirmation-dialog.tsx
892
- var React21 = __toESM(require("react"));
976
+ var React23 = __toESM(require("react"));
893
977
  var import_icons8 = require("@elementor/icons");
894
- var import_ui20 = require("@elementor/ui");
978
+ var import_ui22 = require("@elementor/ui");
895
979
  var import_i18n4 = require("@wordpress/i18n");
896
980
  var TITLE_ID2 = "confirmation-dialog";
897
- var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React21.createElement(import_ui20.Dialog, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "xs" }, children);
898
- var ConfirmationDialogTitle = ({ children }) => /* @__PURE__ */ React21.createElement(import_ui20.DialogTitle, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React21.createElement(import_icons8.AlertOctagonFilledIcon, { color: "error" }), children);
899
- var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React21.createElement(import_ui20.DialogContent, null, children);
900
- var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React21.createElement(import_ui20.DialogContentText, { variant: "body2", color: "textPrimary", ...props });
981
+ var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React23.createElement(import_ui22.Dialog, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "xs" }, children);
982
+ var ConfirmationDialogTitle = ({ children }) => /* @__PURE__ */ React23.createElement(import_ui22.DialogTitle, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React23.createElement(import_icons8.AlertOctagonFilledIcon, { color: "error" }), children);
983
+ var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React23.createElement(import_ui22.DialogContent, null, children);
984
+ var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React23.createElement(import_ui22.DialogContentText, { variant: "body2", color: "textPrimary", ...props });
901
985
  var ConfirmationDialogActions = ({
902
986
  onClose,
903
987
  onConfirm,
904
988
  cancelLabel,
905
989
  confirmLabel
906
- }) => /* @__PURE__ */ React21.createElement(import_ui20.DialogActions, null, /* @__PURE__ */ React21.createElement(import_ui20.Button, { color: "secondary", onClick: onClose }, cancelLabel ?? (0, import_i18n4.__)("Not now", "elementor")), /* @__PURE__ */ React21.createElement(import_ui20.Button, { autoFocus: true, variant: "contained", color: "error", onClick: onConfirm }, confirmLabel ?? (0, import_i18n4.__)("Delete", "elementor")));
990
+ }) => /* @__PURE__ */ React23.createElement(import_ui22.DialogActions, null, /* @__PURE__ */ React23.createElement(import_ui22.Button, { color: "secondary", onClick: onClose }, cancelLabel ?? (0, import_i18n4.__)("Not now", "elementor")), /* @__PURE__ */ React23.createElement(import_ui22.Button, { autoFocus: true, variant: "contained", color: "error", onClick: onConfirm }, confirmLabel ?? (0, import_i18n4.__)("Delete", "elementor")));
907
991
  ConfirmationDialog.Title = ConfirmationDialogTitle;
908
992
  ConfirmationDialog.Content = ConfirmationDialogContent;
909
993
  ConfirmationDialog.ContentText = ConfirmationDialogContentText;
910
994
  ConfirmationDialog.Actions = ConfirmationDialogActions;
911
995
 
912
996
  // src/hooks/use-editable.ts
913
- var import_react16 = require("react");
997
+ var import_react17 = require("react");
914
998
  var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
915
- const [isEditing, setIsEditing] = (0, import_react16.useState)(false);
916
- const [error, setError] = (0, import_react16.useState)(null);
999
+ const [isEditing, setIsEditing] = (0, import_react17.useState)(false);
1000
+ const [error, setError] = (0, import_react17.useState)(null);
917
1001
  const ref = useSelection(isEditing);
918
1002
  const isDirty = (newValue) => newValue !== value;
919
1003
  const openEditMode = () => {
@@ -995,8 +1079,8 @@ var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
995
1079
  };
996
1080
  };
997
1081
  var useSelection = (isEditing) => {
998
- const ref = (0, import_react16.useRef)(null);
999
- (0, import_react16.useEffect)(() => {
1082
+ const ref = (0, import_react17.useRef)(null);
1083
+ (0, import_react17.useEffect)(() => {
1000
1084
  if (isEditing) {
1001
1085
  selectAll(ref.current);
1002
1086
  }
@@ -1020,6 +1104,7 @@ var selectAll = (el) => {
1020
1104
  CtaButton,
1021
1105
  EditableField,
1022
1106
  EllipsisWithTooltip,
1107
+ FloatingActionsBar,
1023
1108
  Form,
1024
1109
  GlobalDialog,
1025
1110
  ITEM_HEIGHT,
@@ -1028,6 +1113,7 @@ var selectAll = (el) => {
1028
1113
  IntroductionModal,
1029
1114
  MenuItemInfotip,
1030
1115
  MenuListItem,
1116
+ PopoverAction,
1031
1117
  PopoverBody,
1032
1118
  PopoverHeader,
1033
1119
  PopoverMenuList,
@@ -1045,6 +1131,7 @@ var selectAll = (el) => {
1045
1131
  openDialog,
1046
1132
  useDialog,
1047
1133
  useEditable,
1134
+ useFloatingActionsBar,
1048
1135
  useSectionWidth
1049
1136
  });
1050
1137
  //# sourceMappingURL=index.js.map
package/dist/index.mjs CHANGED
@@ -550,14 +550,42 @@ var PromotionChip = React15.forwardRef(({ ...props }, ref) => {
550
550
  );
551
551
  });
552
552
 
553
- // src/components/popover/body.tsx
553
+ // src/components/floating-bar.tsx
554
554
  import * as React16 from "react";
555
+ import { createContext, useContext, useState as useState5 } from "react";
556
+ import { styled as styled3, UnstableFloatingActionBar } from "@elementor/ui";
557
+ var FloatingBarContainer = styled3("span")`
558
+ display: contents;
559
+
560
+ .MuiFloatingActionBar-popper:has( .MuiFloatingActionBar-actions:empty ) {
561
+ display: none;
562
+ }
563
+
564
+ .MuiFloatingActionBar-popper {
565
+ z-index: 1000;
566
+ }
567
+ `;
568
+ var FloatingActionsContext = createContext(null);
569
+ function FloatingActionsBar({ actions, children }) {
570
+ const [open, setOpen] = useState5(false);
571
+ return /* @__PURE__ */ React16.createElement(FloatingActionsContext.Provider, { value: { open, setOpen } }, /* @__PURE__ */ React16.createElement(FloatingBarContainer, null, /* @__PURE__ */ React16.createElement(UnstableFloatingActionBar, { actions, open: open || void 0 }, children)));
572
+ }
573
+ function useFloatingActionsBar() {
574
+ const context = useContext(FloatingActionsContext);
575
+ if (!context) {
576
+ throw new Error("useFloatingActions must be used within a FloatingActionsBar");
577
+ }
578
+ return context;
579
+ }
580
+
581
+ // src/components/popover/body.tsx
582
+ import * as React17 from "react";
555
583
  import { Box as Box6 } from "@elementor/ui";
556
584
  var SECTION_PADDING_INLINE = 32;
557
585
  var DEFAULT_POPOVER_HEIGHT = 348;
558
586
  var FALLBACK_POPOVER_WIDTH = 220;
559
587
  var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) => {
560
- return /* @__PURE__ */ React16.createElement(
588
+ return /* @__PURE__ */ React17.createElement(
561
589
  Box6,
562
590
  {
563
591
  display: "flex",
@@ -575,13 +603,13 @@ var PopoverBody = ({ children, height = DEFAULT_POPOVER_HEIGHT, width, id }) =>
575
603
  };
576
604
 
577
605
  // src/components/popover/section-popover-body.tsx
578
- import * as React17 from "react";
606
+ import * as React18 from "react";
579
607
 
580
608
  // src/contexts/section-context.tsx
581
- import { createContext, useContext } from "react";
609
+ import { createContext as createContext2, useContext as useContext2 } from "react";
582
610
  var FALLBACK_SECTION_WIDTH = 320;
583
- var SectionRefContext = createContext(null);
584
- var useSectionRef = () => useContext(SectionRefContext);
611
+ var SectionRefContext = createContext2(null);
612
+ var useSectionRef = () => useContext2(SectionRefContext);
585
613
  var useSectionWidth = () => {
586
614
  const sectionRef = useSectionRef();
587
615
  return sectionRef?.current?.offsetWidth ?? FALLBACK_SECTION_WIDTH;
@@ -590,11 +618,11 @@ var useSectionWidth = () => {
590
618
  // src/components/popover/section-popover-body.tsx
591
619
  var SectionPopoverBody = (props) => {
592
620
  const sectionWidth = useSectionWidth();
593
- return /* @__PURE__ */ React17.createElement(PopoverBody, { ...props, width: sectionWidth });
621
+ return /* @__PURE__ */ React18.createElement(PopoverBody, { ...props, width: sectionWidth });
594
622
  };
595
623
 
596
624
  // src/components/popover/header.tsx
597
- import * as React18 from "react";
625
+ import * as React19 from "react";
598
626
  import { CloseButton as CloseButton2, Stack, Typography as Typography5 } from "@elementor/ui";
599
627
  var SIZE2 = "tiny";
600
628
  var PopoverHeader = ({ title, onClose, icon, actions }) => {
@@ -604,13 +632,13 @@ var PopoverHeader = ({ title, onClose, icon, actions }) => {
604
632
  py: 1.5,
605
633
  maxHeight: 36
606
634
  };
607
- return /* @__PURE__ */ React18.createElement(Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React18.createElement(Typography5, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React18.createElement(Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React18.createElement(CloseButton2, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
635
+ return /* @__PURE__ */ React19.createElement(Stack, { direction: "row", alignItems: "center", ...paddingAndSizing, sx: { columnGap: 0.5 } }, icon, /* @__PURE__ */ React19.createElement(Typography5, { variant: "subtitle2", sx: { fontSize: "12px", mt: 0.25 } }, title), /* @__PURE__ */ React19.createElement(Stack, { direction: "row", sx: { ml: "auto" } }, actions, /* @__PURE__ */ React19.createElement(CloseButton2, { slotProps: { icon: { fontSize: SIZE2 } }, sx: { ml: "auto" }, onClick: onClose })));
608
636
  };
609
637
 
610
638
  // src/components/popover/menu-list.tsx
611
- import * as React19 from "react";
639
+ import * as React20 from "react";
612
640
  import { useEffect as useEffect7, useMemo, useRef as useRef3 } from "react";
613
- import { Box as Box7, ListItem, MenuList, MenuSubheader, styled as styled3, useTheme } from "@elementor/ui";
641
+ import { Box as Box7, ListItem, MenuList, MenuSubheader, styled as styled4, useTheme } from "@elementor/ui";
614
642
  import { useVirtualizer } from "@tanstack/react-virtual";
615
643
 
616
644
  // src/hooks/use-scroll-to-selected.ts
@@ -632,9 +660,9 @@ var useScrollToSelected = ({
632
660
  };
633
661
 
634
662
  // src/hooks/use-scroll-top.ts
635
- import { useEffect as useEffect6, useState as useState5 } from "react";
663
+ import { useEffect as useEffect6, useState as useState6 } from "react";
636
664
  var useScrollTop = ({ containerRef }) => {
637
- const [scrollTop, setScrollTop] = useState5(0);
665
+ const [scrollTop, setScrollTop] = useState6(0);
638
666
  useEffect6(() => {
639
667
  const container = containerRef.current;
640
668
  if (!container) {
@@ -718,7 +746,7 @@ var PopoverMenuList = ({
718
746
  }, [items]);
719
747
  useScrollToSelected({ selectedValue, items, virtualizer });
720
748
  const virtualItems = virtualizer.getVirtualItems();
721
- return /* @__PURE__ */ React19.createElement(Box7, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React19.createElement(
749
+ return /* @__PURE__ */ React20.createElement(Box7, { ref: containerRef, sx: { height: "100%", overflowY: "auto" } }, items.length === 0 && noResultsComponent ? noResultsComponent : /* @__PURE__ */ React20.createElement(
722
750
  MenuListComponent,
723
751
  {
724
752
  role: "listbox",
@@ -736,7 +764,7 @@ var PopoverMenuList = ({
736
764
  }
737
765
  if (item.type === "category") {
738
766
  const shouldStick = virtualRow.start + MENU_LIST_PADDING_TOP <= scrollTop;
739
- return /* @__PURE__ */ React19.createElement(
767
+ return /* @__PURE__ */ React20.createElement(
740
768
  MenuSubheader,
741
769
  {
742
770
  key: virtualRow.key,
@@ -747,7 +775,7 @@ var PopoverMenuList = ({
747
775
  );
748
776
  }
749
777
  const isDisabled = item.disabled;
750
- return /* @__PURE__ */ React19.createElement(
778
+ return /* @__PURE__ */ React20.createElement(
751
779
  ListItem,
752
780
  {
753
781
  key: virtualRow.key,
@@ -787,7 +815,7 @@ var PopoverMenuList = ({
787
815
  })
788
816
  ));
789
817
  };
790
- var StyledMenuList = styled3(MenuList)(({ theme }) => ({
818
+ var StyledMenuList = styled4(MenuList)(({ theme }) => ({
791
819
  "& > li": {
792
820
  height: ITEM_HEIGHT,
793
821
  width: "100%",
@@ -816,9 +844,62 @@ var StyledMenuList = styled3(MenuList)(({ theme }) => ({
816
844
  position: "relative"
817
845
  }));
818
846
 
847
+ // src/components/popover/popover-action.tsx
848
+ import * as React21 from "react";
849
+ import { bindPopover, bindTrigger, IconButton as IconButton2, Popover, Tooltip as Tooltip3, usePopupState } from "@elementor/ui";
850
+ var SIZE3 = "tiny";
851
+ function PopoverAction({ title, visible = true, icon: Icon, content: PopoverContent }) {
852
+ const { popupState, triggerProps, popoverProps } = useFloatingActionsPopover();
853
+ if (!visible) {
854
+ return null;
855
+ }
856
+ return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(Tooltip3, { placement: "top", title }, /* @__PURE__ */ React21.createElement(IconButton2, { "aria-label": title, size: SIZE3, ...triggerProps }, /* @__PURE__ */ React21.createElement(Icon, { fontSize: SIZE3 }))), /* @__PURE__ */ React21.createElement(
857
+ Popover,
858
+ {
859
+ disableScrollLock: true,
860
+ anchorOrigin: {
861
+ vertical: "bottom",
862
+ horizontal: "right"
863
+ },
864
+ transformOrigin: {
865
+ vertical: "top",
866
+ horizontal: "right"
867
+ },
868
+ PaperProps: {
869
+ sx: { my: 2.5 }
870
+ },
871
+ ...popoverProps
872
+ },
873
+ /* @__PURE__ */ React21.createElement(PopoverContent, { close: popupState.close })
874
+ ));
875
+ }
876
+ function useFloatingActionsPopover() {
877
+ const { setOpen } = useFloatingActionsBar();
878
+ const popupState = usePopupState({ variant: "popover" });
879
+ const triggerProps = bindTrigger(popupState);
880
+ const popoverProps = bindPopover(popupState);
881
+ const onClick = (e) => {
882
+ triggerProps.onClick(e);
883
+ setOpen(true);
884
+ };
885
+ const onClose = () => {
886
+ popoverProps.onClose();
887
+ setOpen(false);
888
+ };
889
+ const close = () => {
890
+ popupState.close();
891
+ setOpen(false);
892
+ };
893
+ return {
894
+ popupState: { ...popupState, close },
895
+ triggerProps: { ...triggerProps, onClick },
896
+ popoverProps: { ...popoverProps, onClose }
897
+ };
898
+ }
899
+
819
900
  // src/components/save-changes-dialog.tsx
820
- import * as React20 from "react";
821
- import { useState as useState6 } from "react";
901
+ import * as React22 from "react";
902
+ import { useState as useState7 } from "react";
822
903
  import { AlertTriangleFilledIcon, XIcon as XIcon2 } from "@elementor/icons";
823
904
  import {
824
905
  Button as Button4,
@@ -827,12 +908,12 @@ import {
827
908
  DialogContent,
828
909
  DialogContentText,
829
910
  DialogTitle as DialogTitle2,
830
- IconButton as IconButton2,
911
+ IconButton as IconButton3,
831
912
  Stack as Stack2
832
913
  } from "@elementor/ui";
833
914
  var TITLE_ID = "save-changes-dialog";
834
- var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React20.createElement(Dialog3, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
835
- var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React20.createElement(
915
+ var SaveChangesDialog = ({ children, onClose }) => /* @__PURE__ */ React22.createElement(Dialog3, { open: true, onClose, "aria-labelledby": TITLE_ID, maxWidth: "xs" }, children);
916
+ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React22.createElement(
836
917
  DialogTitle2,
837
918
  {
838
919
  id: TITLE_ID,
@@ -841,34 +922,34 @@ var SaveChangesDialogTitle = ({ children, onClose }) => /* @__PURE__ */ React20.
841
922
  gap: 1,
842
923
  sx: { lineHeight: 1, justifyContent: "space-between" }
843
924
  },
844
- /* @__PURE__ */ React20.createElement(Stack2, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React20.createElement(AlertTriangleFilledIcon, { color: "secondary" }), children),
845
- onClose && /* @__PURE__ */ React20.createElement(IconButton2, { onClick: onClose, size: "small" }, /* @__PURE__ */ React20.createElement(XIcon2, null))
925
+ /* @__PURE__ */ React22.createElement(Stack2, { direction: "row", alignItems: "center", gap: 1 }, /* @__PURE__ */ React22.createElement(AlertTriangleFilledIcon, { color: "secondary" }), children),
926
+ onClose && /* @__PURE__ */ React22.createElement(IconButton3, { onClick: onClose, size: "small" }, /* @__PURE__ */ React22.createElement(XIcon2, null))
846
927
  );
847
- var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React20.createElement(DialogContent, null, children);
848
- var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React20.createElement(DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
928
+ var SaveChangesDialogContent = ({ children }) => /* @__PURE__ */ React22.createElement(DialogContent, null, children);
929
+ var SaveChangesDialogContentText = (props) => /* @__PURE__ */ React22.createElement(DialogContentText, { variant: "body2", color: "textPrimary", display: "flex", flexDirection: "column", ...props });
849
930
  var SaveChangesDialogActions = ({ actions }) => {
850
- const [isConfirming, setIsConfirming] = useState6(false);
931
+ const [isConfirming, setIsConfirming] = useState7(false);
851
932
  const { cancel, confirm, discard } = actions;
852
933
  const onConfirm = async () => {
853
934
  setIsConfirming(true);
854
935
  await confirm.action();
855
936
  setIsConfirming(false);
856
937
  };
857
- return /* @__PURE__ */ React20.createElement(DialogActions2, null, cancel && /* @__PURE__ */ React20.createElement(Button4, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React20.createElement(Button4, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React20.createElement(Button4, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
938
+ return /* @__PURE__ */ React22.createElement(DialogActions2, null, cancel && /* @__PURE__ */ React22.createElement(Button4, { variant: "text", color: "secondary", onClick: cancel.action }, cancel.label), discard && /* @__PURE__ */ React22.createElement(Button4, { variant: "text", color: "secondary", onClick: discard.action }, discard.label), /* @__PURE__ */ React22.createElement(Button4, { variant: "contained", color: "secondary", onClick: onConfirm, loading: isConfirming }, confirm.label));
858
939
  };
859
940
  SaveChangesDialog.Title = SaveChangesDialogTitle;
860
941
  SaveChangesDialog.Content = SaveChangesDialogContent;
861
942
  SaveChangesDialog.ContentText = SaveChangesDialogContentText;
862
943
  SaveChangesDialog.Actions = SaveChangesDialogActions;
863
944
  var useDialog = () => {
864
- const [isOpen, setIsOpen] = useState6(false);
945
+ const [isOpen, setIsOpen] = useState7(false);
865
946
  const open = () => setIsOpen(true);
866
947
  const close = () => setIsOpen(false);
867
948
  return { isOpen, open, close };
868
949
  };
869
950
 
870
951
  // src/components/confirmation-dialog.tsx
871
- import * as React21 from "react";
952
+ import * as React23 from "react";
872
953
  import { AlertOctagonFilledIcon } from "@elementor/icons";
873
954
  import {
874
955
  Button as Button5,
@@ -880,26 +961,26 @@ import {
880
961
  } from "@elementor/ui";
881
962
  import { __ as __4 } from "@wordpress/i18n";
882
963
  var TITLE_ID2 = "confirmation-dialog";
883
- var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React21.createElement(Dialog4, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "xs" }, children);
884
- var ConfirmationDialogTitle = ({ children }) => /* @__PURE__ */ React21.createElement(DialogTitle3, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React21.createElement(AlertOctagonFilledIcon, { color: "error" }), children);
885
- var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React21.createElement(DialogContent2, null, children);
886
- var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React21.createElement(DialogContentText2, { variant: "body2", color: "textPrimary", ...props });
964
+ var ConfirmationDialog = ({ open, onClose, children }) => /* @__PURE__ */ React23.createElement(Dialog4, { open, onClose, "aria-labelledby": TITLE_ID2, maxWidth: "xs" }, children);
965
+ var ConfirmationDialogTitle = ({ children }) => /* @__PURE__ */ React23.createElement(DialogTitle3, { id: TITLE_ID2, display: "flex", alignItems: "center", gap: 1, sx: { lineHeight: 1 } }, /* @__PURE__ */ React23.createElement(AlertOctagonFilledIcon, { color: "error" }), children);
966
+ var ConfirmationDialogContent = ({ children }) => /* @__PURE__ */ React23.createElement(DialogContent2, null, children);
967
+ var ConfirmationDialogContentText = (props) => /* @__PURE__ */ React23.createElement(DialogContentText2, { variant: "body2", color: "textPrimary", ...props });
887
968
  var ConfirmationDialogActions = ({
888
969
  onClose,
889
970
  onConfirm,
890
971
  cancelLabel,
891
972
  confirmLabel
892
- }) => /* @__PURE__ */ React21.createElement(DialogActions3, null, /* @__PURE__ */ React21.createElement(Button5, { color: "secondary", onClick: onClose }, cancelLabel ?? __4("Not now", "elementor")), /* @__PURE__ */ React21.createElement(Button5, { autoFocus: true, variant: "contained", color: "error", onClick: onConfirm }, confirmLabel ?? __4("Delete", "elementor")));
973
+ }) => /* @__PURE__ */ React23.createElement(DialogActions3, null, /* @__PURE__ */ React23.createElement(Button5, { color: "secondary", onClick: onClose }, cancelLabel ?? __4("Not now", "elementor")), /* @__PURE__ */ React23.createElement(Button5, { autoFocus: true, variant: "contained", color: "error", onClick: onConfirm }, confirmLabel ?? __4("Delete", "elementor")));
893
974
  ConfirmationDialog.Title = ConfirmationDialogTitle;
894
975
  ConfirmationDialog.Content = ConfirmationDialogContent;
895
976
  ConfirmationDialog.ContentText = ConfirmationDialogContentText;
896
977
  ConfirmationDialog.Actions = ConfirmationDialogActions;
897
978
 
898
979
  // src/hooks/use-editable.ts
899
- import { useEffect as useEffect8, useRef as useRef4, useState as useState7 } from "react";
980
+ import { useEffect as useEffect8, useRef as useRef4, useState as useState8 } from "react";
900
981
  var useEditable = ({ value, onSubmit, validation, onClick, onError }) => {
901
- const [isEditing, setIsEditing] = useState7(false);
902
- const [error, setError] = useState7(null);
982
+ const [isEditing, setIsEditing] = useState8(false);
983
+ const [error, setError] = useState8(null);
903
984
  const ref = useSelection(isEditing);
904
985
  const isDirty = (newValue) => newValue !== value;
905
986
  const openEditMode = () => {
@@ -1005,6 +1086,7 @@ export {
1005
1086
  CtaButton,
1006
1087
  EditableField,
1007
1088
  EllipsisWithTooltip,
1089
+ FloatingActionsBar,
1008
1090
  Form,
1009
1091
  GlobalDialog,
1010
1092
  ITEM_HEIGHT,
@@ -1013,6 +1095,7 @@ export {
1013
1095
  IntroductionModal,
1014
1096
  MenuItemInfotip,
1015
1097
  MenuListItem,
1098
+ PopoverAction,
1016
1099
  PopoverBody,
1017
1100
  PopoverHeader,
1018
1101
  PopoverMenuList,
@@ -1030,6 +1113,7 @@ export {
1030
1113
  openDialog,
1031
1114
  useDialog,
1032
1115
  useEditable,
1116
+ useFloatingActionsBar,
1033
1117
  useSectionWidth
1034
1118
  };
1035
1119
  //# sourceMappingURL=index.mjs.map
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.0.0-501",
4
+ "version": "4.0.0-503",
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.0.0-501",
40
+ "@elementor/editor-v1-adapters": "4.0.0-503",
41
41
  "@elementor/icons": "^1.63.0",
42
42
  "@elementor/ui": "1.36.17",
43
43
  "@tanstack/react-virtual": "^3.13.3",
@@ -0,0 +1,45 @@
1
+ import * as React from 'react';
2
+ import { createContext, type PropsWithChildren, type ReactElement, useContext, useState } from 'react';
3
+ import { styled, UnstableFloatingActionBar } from '@elementor/ui';
4
+
5
+ // CSS hack to hide empty floating bars.
6
+ const FloatingBarContainer = styled( 'span' )`
7
+ display: contents;
8
+
9
+ .MuiFloatingActionBar-popper:has( .MuiFloatingActionBar-actions:empty ) {
10
+ display: none;
11
+ }
12
+
13
+ .MuiFloatingActionBar-popper {
14
+ z-index: 1000;
15
+ }
16
+ `;
17
+
18
+ const FloatingActionsContext = createContext< null | {
19
+ open: boolean;
20
+ setOpen: React.Dispatch< React.SetStateAction< boolean > >;
21
+ } >( null );
22
+
23
+ export function FloatingActionsBar( { actions, children }: PropsWithChildren< { actions: ReactElement[] } > ) {
24
+ const [ open, setOpen ] = useState< boolean >( false );
25
+
26
+ return (
27
+ <FloatingActionsContext.Provider value={ { open, setOpen } }>
28
+ <FloatingBarContainer>
29
+ <UnstableFloatingActionBar actions={ actions } open={ open || undefined }>
30
+ { children as ReactElement }
31
+ </UnstableFloatingActionBar>
32
+ </FloatingBarContainer>
33
+ </FloatingActionsContext.Provider>
34
+ );
35
+ }
36
+
37
+ export function useFloatingActionsBar() {
38
+ const context = useContext( FloatingActionsContext );
39
+
40
+ if ( ! context ) {
41
+ throw new Error( 'useFloatingActions must be used within a FloatingActionsBar' );
42
+ }
43
+
44
+ return context;
45
+ }
@@ -0,0 +1,120 @@
1
+ import * as React from 'react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+
4
+ import { useFloatingActionsBar } from '../../floating-bar';
5
+ import { PopoverAction } from '../popover-action';
6
+
7
+ jest.mock( '../../floating-bar' );
8
+
9
+ describe( 'PopoverAction', () => {
10
+ beforeEach( () => {
11
+ jest.mocked( useFloatingActionsBar ).mockReturnValue( {
12
+ setOpen: jest.fn(),
13
+ open: false,
14
+ } );
15
+ } );
16
+
17
+ it( 'renders the icon button with the correct title', () => {
18
+ // Arrange.
19
+ const title = 'Test Action';
20
+ const Icon = () => <span>Icon</span>;
21
+ const content = () => <div>Content</div>;
22
+
23
+ // Act.
24
+ render( <PopoverAction title={ title } icon={ Icon } content={ content } /> );
25
+
26
+ // Assert.
27
+ const button = screen.getByRole( 'button', { name: title } );
28
+ expect( button ).toBeInTheDocument();
29
+ expect( screen.getByText( 'Icon' ) ).toBeInTheDocument();
30
+ } );
31
+
32
+ it( 'does not render when visible is false', () => {
33
+ // Arrange & Act.
34
+ const { container } = render(
35
+ <PopoverAction
36
+ title="Hidden Action"
37
+ icon={ () => <span>Icon</span> }
38
+ content={ () => <div>Content</div> }
39
+ visible={ false }
40
+ />
41
+ );
42
+
43
+ // Assert.
44
+ expect( container ).toBeEmptyDOMElement();
45
+ } );
46
+
47
+ it( 'renders the popover with the content', () => {
48
+ // Arrange.
49
+ const title = 'Test Action';
50
+ const Icon = () => <span>Icon</span>;
51
+ const content = () => <div> Content </div>;
52
+
53
+ // Act.
54
+ render( <PopoverAction title={ title } icon={ Icon } content={ content } /> );
55
+
56
+ fireEvent.click( screen.getByRole( 'button', { name: title } ) );
57
+
58
+ // Assert.
59
+ expect( screen.getByText( 'Content' ) ).toBeInTheDocument();
60
+ } );
61
+
62
+ it( 'calls setOpen when the button is clicked', () => {
63
+ // Arrange.
64
+ const setOpenMock = jest.fn();
65
+ jest.mocked( useFloatingActionsBar ).mockReturnValue( {
66
+ setOpen: setOpenMock,
67
+ open: false,
68
+ } );
69
+
70
+ const title = 'Test Action';
71
+ const Icon = () => <span>Icon</span>;
72
+ const content = () => <div>Content</div>;
73
+
74
+ // Act.
75
+ render( <PopoverAction title={ title } icon={ Icon } content={ content } /> );
76
+
77
+ fireEvent.click( screen.getByRole( 'button', { name: title } ) );
78
+
79
+ // Assert.
80
+ expect( setOpenMock ).toHaveBeenCalledWith( true );
81
+ } );
82
+
83
+ it( 'closes the popover when the close function is called', () => {
84
+ // Arrange.
85
+ const setOpenMock = jest.fn();
86
+ jest.mocked( useFloatingActionsBar ).mockReturnValue( {
87
+ setOpen: setOpenMock,
88
+ open: true,
89
+ } );
90
+
91
+ const title = 'Test Action';
92
+ const Icon = () => <span>Icon</span>;
93
+
94
+ // Act.
95
+ render(
96
+ <PopoverAction
97
+ title={ title }
98
+ icon={ Icon }
99
+ content={ ( { close } ) => (
100
+ <div>
101
+ Content
102
+ <button onClick={ close }>Close</button>
103
+ </div>
104
+ ) }
105
+ />
106
+ );
107
+
108
+ fireEvent.click( screen.getByRole( 'button', { name: title } ) );
109
+
110
+ // Assert.
111
+ expect( setOpenMock ).toHaveBeenCalledWith( true );
112
+
113
+ setOpenMock.mockClear();
114
+
115
+ // Act.
116
+ fireEvent.click( screen.getByText( 'Close' ) );
117
+
118
+ expect( setOpenMock ).toHaveBeenCalledWith( false );
119
+ } );
120
+ } );
@@ -3,3 +3,4 @@ export { SectionPopoverBody } from './section-popover-body';
3
3
  export { PopoverHeader } from './header';
4
4
  export { ITEM_HEIGHT, PopoverMenuList, StyledMenuList } from './menu-list';
5
5
  export type { PopoverMenuListProps, VirtualizedItem } from './menu-list';
6
+ export { PopoverAction, type PopoverActionProps } from './popover-action';
@@ -0,0 +1,78 @@
1
+ import * as React from 'react';
2
+ import { type ComponentType, type ElementType as ReactElementType } from 'react';
3
+ import { bindPopover, bindTrigger, IconButton, Popover, Tooltip, usePopupState } from '@elementor/ui';
4
+
5
+ import { useFloatingActionsBar } from '../floating-bar';
6
+
7
+ const SIZE = 'tiny';
8
+
9
+ export type PopoverActionProps = {
10
+ title: string;
11
+ visible?: boolean;
12
+ icon: ReactElementType;
13
+ content: ComponentType< { close: () => void } >;
14
+ };
15
+
16
+ export function PopoverAction( { title, visible = true, icon: Icon, content: PopoverContent }: PopoverActionProps ) {
17
+ const { popupState, triggerProps, popoverProps } = useFloatingActionsPopover();
18
+
19
+ if ( ! visible ) {
20
+ return null;
21
+ }
22
+
23
+ return (
24
+ <>
25
+ <Tooltip placement="top" title={ title }>
26
+ <IconButton aria-label={ title } size={ SIZE } { ...triggerProps }>
27
+ <Icon fontSize={ SIZE } />
28
+ </IconButton>
29
+ </Tooltip>
30
+ <Popover
31
+ disableScrollLock
32
+ anchorOrigin={ {
33
+ vertical: 'bottom',
34
+ horizontal: 'right',
35
+ } }
36
+ transformOrigin={ {
37
+ vertical: 'top',
38
+ horizontal: 'right',
39
+ } }
40
+ PaperProps={ {
41
+ sx: { my: 2.5 },
42
+ } }
43
+ { ...popoverProps }
44
+ >
45
+ <PopoverContent close={ popupState.close } />
46
+ </Popover>
47
+ </>
48
+ );
49
+ }
50
+
51
+ export function useFloatingActionsPopover() {
52
+ const { setOpen } = useFloatingActionsBar();
53
+ const popupState = usePopupState( { variant: 'popover' } );
54
+
55
+ const triggerProps = bindTrigger( popupState );
56
+ const popoverProps = bindPopover( popupState );
57
+
58
+ const onClick = ( e: React.MouseEvent ) => {
59
+ triggerProps.onClick( e );
60
+ setOpen( true );
61
+ };
62
+
63
+ const onClose = () => {
64
+ popoverProps.onClose();
65
+ setOpen( false );
66
+ };
67
+
68
+ const close = () => {
69
+ popupState.close();
70
+ setOpen( false );
71
+ };
72
+
73
+ return {
74
+ popupState: { ...popupState, close },
75
+ triggerProps: { ...triggerProps, onClick },
76
+ popoverProps: { ...popoverProps, onClose },
77
+ };
78
+ }
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export { CtaButton } from './components/cta-button';
15
15
  export { PromotionInfotip } from './components/promotion-infotip';
16
16
  export { PromotionPopover } from './components/promotion-popover';
17
17
  export { PromotionChip } from './components/promotion-chip';
18
+ export { FloatingActionsBar, useFloatingActionsBar } from './components/floating-bar';
18
19
 
19
20
  export * from './components/popover';
20
21
  export * from './components/save-changes-dialog';