infinity-ui-elements 1.8.45 → 1.8.46

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.esm.js CHANGED
@@ -2604,18 +2604,18 @@ const ListItem = React.forwardRef(({ className, type = "single", leadingIcon, ti
2604
2604
  });
2605
2605
  ListItem.displayName = "ListItem";
2606
2606
 
2607
- const DropdownMenu = React.forwardRef(({ items = [], customContent, sectionHeading, isLoading = false, isEmpty = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, showChevron = false, emptyIcon, disableFooter = false, showFooter, footerLayout = "horizontal", onClose, focusedIndex = -1, className, width = "auto", }, ref) => {
2607
+ const DropdownMenu = React.forwardRef(({ items = [], customContent, sectionHeading, isLoading = false, isEmpty = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, showChevron = false, emptyIcon, disableFooter = false, showFooter, footerLayout = "horizontal", onClose, focusedIndex = -1, className, width = "auto", maxHeight = "400px", }, ref) => {
2608
2608
  const renderContent = () => {
2609
2609
  if (isLoading) {
2610
2610
  return (jsx("div", { className: "flex flex-col items-center justify-center py-12 px-6", children: jsx(Loader2, { className: "w-12 h-12 text-action-ink-primary-normal mb-4 animate-spin" }) }));
2611
2611
  }
2612
2612
  if (customContent) {
2613
- return (jsxs("div", { className: "py-3 px-3 max-h-[400px] overflow-y-auto", children: [sectionHeading && (jsx(Text, { as: "div", variant: "body", size: "small", weight: "medium", className: "text-body-small-medium text-surface-ink-neutral-muted px-3 py-2 mb-1", children: sectionHeading })), jsx("div", { className: "px-1", children: customContent })] }));
2613
+ return (jsxs("div", { className: "py-3 px-3 overflow-y-auto", style: { maxHeight }, children: [sectionHeading && (jsx(Text, { as: "div", variant: "body", size: "small", weight: "medium", className: "text-body-small-medium text-surface-ink-neutral-muted px-3 py-2 mb-1", children: sectionHeading })), jsx("div", { className: "px-1", children: customContent })] }));
2614
2614
  }
2615
2615
  if (isEmpty || items.length === 0) {
2616
2616
  return (jsxs("div", { className: "flex flex-col items-center justify-center py-8 px-6 text-center", children: [emptyIcon || (jsx(Search, { className: "w-12 h-12 text-surface-ink-neutral-muted mb-4" })), jsx(Text, { as: "h3", variant: "body", size: "small", weight: "semibold", className: "text-surface-ink-neutral-normal mb-2", children: emptyTitle }), jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", className: "text-surface-ink-neutral-muted mb-3", children: emptyDescription }), emptyLinkText && (jsx(Link, { type: "anchor", color: "primary", size: "small", onClick: onEmptyLinkClick, children: emptyLinkText }))] }));
2617
2617
  }
2618
- return (jsxs("div", { className: "py-3 px-3 max-h-[400px] overflow-y-auto", children: [sectionHeading && (jsx(Text, { as: "div", variant: "body", size: "small", weight: "medium", className: "text-surface-ink-neutral-muted px-3 py-2 mb-1", children: sectionHeading })), jsx("div", { className: "flex flex-col gap-1", children: items.map((item, index) => (jsx(ListItem, { title: item.label, description: item.description, leadingIcon: item.leadingIcon, trailingIcon: item.trailingIcon, showChevron: showChevron, isDisabled: item.isDisabled, isSelected: index === focusedIndex, variant: item.variant, onClick: () => {
2618
+ return (jsxs("div", { className: "py-3 px-3 overflow-y-auto", style: { maxHeight }, children: [sectionHeading && (jsx(Text, { as: "div", variant: "body", size: "small", weight: "medium", className: "text-surface-ink-neutral-muted px-3 py-2 mb-1", children: sectionHeading })), jsx("div", { className: "flex flex-col gap-1", children: items.map((item, index) => (jsx(ListItem, { title: item.label, description: item.description, leadingIcon: item.leadingIcon, trailingIcon: item.trailingIcon, showChevron: showChevron, isDisabled: item.isDisabled, isSelected: index === focusedIndex, variant: item.variant, onClick: () => {
2619
2619
  item.onClick?.();
2620
2620
  onClose?.();
2621
2621
  }, containerClassName: cn(index === focusedIndex && "bg-action-fill-primary-faded") }, item.value))) })] }));
@@ -2647,6 +2647,14 @@ const Dropdown = React.forwardRef(({ className, trigger, items = [], customConte
2647
2647
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
2648
2648
  const isOpen = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen;
2649
2649
  const dropdownRef = React.useRef(null);
2650
+ const menuRef = React.useRef(null);
2651
+ const [menuPosition, setMenuPosition] = React.useState({
2652
+ top: "100%",
2653
+ bottom: "auto",
2654
+ left: "0",
2655
+ right: "auto",
2656
+ maxHeight: "400px",
2657
+ });
2650
2658
  const handleOpenChange = (newOpen) => {
2651
2659
  if (controlledOpen === undefined) {
2652
2660
  setUncontrolledOpen(newOpen);
@@ -2685,12 +2693,92 @@ const Dropdown = React.forwardRef(({ className, trigger, items = [], customConte
2685
2693
  };
2686
2694
  }
2687
2695
  }, [isOpen]);
2696
+ // Calculate and adjust dropdown position to keep it within viewport
2697
+ React.useEffect(() => {
2698
+ if (!isOpen || !dropdownRef.current || !menuRef.current)
2699
+ return;
2700
+ const calculatePosition = () => {
2701
+ const triggerRect = dropdownRef.current.getBoundingClientRect();
2702
+ const menuElement = menuRef.current;
2703
+ // Get menu dimensions (use a temporary measurement if needed)
2704
+ const menuRect = menuElement.getBoundingClientRect();
2705
+ const menuHeight = menuRect.height || 400; // fallback to max-height
2706
+ const menuWidth = menuRect.width;
2707
+ const viewportHeight = window.innerHeight;
2708
+ const viewportWidth = window.innerWidth;
2709
+ const spaceBelow = viewportHeight - triggerRect.bottom;
2710
+ const spaceAbove = triggerRect.top;
2711
+ const spaceRight = viewportWidth - triggerRect.left;
2712
+ const spaceLeft = triggerRect.right;
2713
+ const position = {
2714
+ top: "auto",
2715
+ bottom: "auto",
2716
+ left: "auto",
2717
+ right: "auto",
2718
+ maxHeight: "400px",
2719
+ };
2720
+ // Vertical positioning
2721
+ if (spaceBelow >= menuHeight || spaceBelow >= spaceAbove) {
2722
+ // Position below trigger
2723
+ position.top = "100%";
2724
+ position.bottom = "auto";
2725
+ position.maxHeight = `${Math.min(400, spaceBelow - 16)}px`;
2726
+ }
2727
+ else {
2728
+ // Position above trigger
2729
+ position.top = "auto";
2730
+ position.bottom = "100%";
2731
+ position.maxHeight = `${Math.min(400, spaceAbove - 16)}px`;
2732
+ }
2733
+ // Horizontal positioning
2734
+ if (spaceRight >= menuWidth) {
2735
+ // Align to left edge of trigger
2736
+ position.left = "0";
2737
+ position.right = "auto";
2738
+ }
2739
+ else if (spaceLeft >= menuWidth) {
2740
+ // Align to right edge of trigger
2741
+ position.left = "auto";
2742
+ position.right = "0";
2743
+ }
2744
+ else {
2745
+ // Not enough space on either side, try to center or align based on available space
2746
+ if (triggerRect.left + menuWidth > viewportWidth) {
2747
+ position.left = "auto";
2748
+ position.right = "0";
2749
+ }
2750
+ else {
2751
+ position.left = "0";
2752
+ position.right = "auto";
2753
+ }
2754
+ }
2755
+ setMenuPosition(position);
2756
+ };
2757
+ // Calculate position after menu is rendered
2758
+ calculatePosition();
2759
+ // Recalculate on window resize or scroll
2760
+ const handleResize = () => calculatePosition();
2761
+ const handleScroll = () => calculatePosition();
2762
+ window.addEventListener("resize", handleResize);
2763
+ window.addEventListener("scroll", handleScroll, true);
2764
+ return () => {
2765
+ window.removeEventListener("resize", handleResize);
2766
+ window.removeEventListener("scroll", handleScroll, true);
2767
+ };
2768
+ }, [isOpen]);
2688
2769
  const sizeMap = {
2689
2770
  small: "w-64",
2690
2771
  medium: "w-80",
2691
2772
  large: "w-96",
2692
2773
  };
2693
- return (jsxs("div", { ref: dropdownRef, className: cn("relative inline-block", containerClassName), ...props, children: [trigger && (jsx("div", { onClick: toggleOpen, className: "cursor-pointer", children: trigger })), isOpen && (jsx(DropdownMenu, { ref: ref, items: items, customContent: customContent, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: isEmpty, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, showFooter: showFooter, onClose: () => handleOpenChange(false), className: cn("absolute z-50 mt-2", menuClassName, className), width: sizeMap[size] }))] }));
2774
+ return (jsxs("div", { ref: dropdownRef, className: cn("relative inline-block", containerClassName), ...props, children: [trigger && (jsx("div", { onClick: toggleOpen, className: "cursor-pointer", children: trigger })), isOpen && (jsx("div", { ref: menuRef, className: cn("absolute z-50", menuClassName), style: {
2775
+ top: menuPosition.top,
2776
+ bottom: menuPosition.bottom,
2777
+ left: menuPosition.left,
2778
+ right: menuPosition.right,
2779
+ marginTop: menuPosition.top === "100%" ? "0.5rem" : "0",
2780
+ marginBottom: menuPosition.bottom === "100%" ? "0.5rem" : "0",
2781
+ }, children: jsx(DropdownMenu, { ref: ref, items: items, customContent: customContent, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: isEmpty, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, showFooter: showFooter, onClose: () => handleOpenChange(false), className: className, width: sizeMap[size], maxHeight: menuPosition.maxHeight }) }))] }));
2694
2782
  });
2695
2783
  Dropdown.displayName = "Dropdown";
2696
2784