infinity-ui-elements 1.8.8 → 1.8.10

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
@@ -5,7 +5,9 @@ import { Slot } from '@radix-ui/react-slot';
5
5
  import { PulseLoader, ClipLoader } from 'react-spinners';
6
6
  import { clsx } from 'clsx';
7
7
  import { twMerge } from 'tailwind-merge';
8
- import { ExternalLink, Loader2, Search, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
8
+ import { ExternalLink, Calendar, Loader2, Search, ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
9
+ import Calendar$1 from 'react-calendar';
10
+ import 'react-calendar/dist/Calendar.css';
9
11
  import { createPortal } from 'react-dom';
10
12
  import { flexRender } from '@tanstack/react-table';
11
13
 
@@ -1688,374 +1690,113 @@ const Counter = React.forwardRef(({ value, max, size = "medium", color = "neutra
1688
1690
  });
1689
1691
  Counter.displayName = "Counter";
1690
1692
 
1691
- const dividerVariants = cva("", {
1693
+ const tooltipVariants = cva("fixed z-50 bg-popup-fill-intense text-action-ink-on-primary-normal rounded-medium border border-popup-outline-subtle flex flex-col p-4 rounded-xlarge min-w-[200px] max-w-[300px] transition-opacity duration-200", {
1692
1694
  variants: {
1693
- orientation: {
1694
- horizontal: "w-full",
1695
- vertical: "h-full",
1696
- },
1697
- thickness: {
1698
- thinner: "",
1699
- thin: "",
1700
- thick: "",
1701
- thicker: "",
1702
- },
1703
- lineStyle: {
1704
- solid: "border-solid",
1705
- dashed: "border-dashed",
1706
- },
1707
- variant: {
1708
- normal: "",
1709
- subtle: "",
1710
- muted: "",
1695
+ isVisible: {
1696
+ true: "opacity-100 pointer-events-auto shadow-[0_4px_20px_rgba(0,0,0,0.15)]",
1697
+ false: "opacity-0 pointer-events-none",
1711
1698
  },
1712
1699
  },
1713
- compoundVariants: [
1714
- // Horizontal orientation with thickness
1715
- {
1716
- orientation: "horizontal",
1717
- thickness: "thinner",
1718
- class: "border-t-[0.5px]",
1719
- },
1720
- {
1721
- orientation: "horizontal",
1722
- thickness: "thin",
1723
- class: "border-t-[1px]",
1724
- },
1725
- {
1726
- orientation: "horizontal",
1727
- thickness: "thick",
1728
- class: "border-t-[2px]",
1729
- },
1730
- {
1731
- orientation: "horizontal",
1732
- thickness: "thicker",
1733
- class: "border-t-[3px]",
1734
- },
1735
- // Vertical orientation with thickness
1736
- {
1737
- orientation: "vertical",
1738
- thickness: "thinner",
1739
- class: "border-l-[0.5px]",
1740
- },
1741
- {
1742
- orientation: "vertical",
1743
- thickness: "thin",
1744
- class: "border-l-[1px]",
1745
- },
1746
- {
1747
- orientation: "vertical",
1748
- thickness: "thick",
1749
- class: "border-l-[2px]",
1750
- },
1751
- {
1752
- orientation: "vertical",
1753
- thickness: "thicker",
1754
- class: "border-l-[3px]",
1755
- },
1756
- // Normal variant colors
1757
- {
1758
- variant: "normal",
1759
- class: "border-surface-outline-neutral-normal",
1760
- },
1761
- // Subtle variant colors
1762
- {
1763
- variant: "subtle",
1764
- class: "border-surface-outline-neutral-subtle",
1765
- },
1766
- // Muted variant colors
1767
- {
1768
- variant: "muted",
1769
- class: "border-surface-outline-neutral-muted",
1770
- },
1771
- ],
1772
1700
  defaultVariants: {
1773
- orientation: "horizontal",
1774
- thickness: "thin",
1775
- lineStyle: "solid",
1776
- variant: "normal",
1701
+ isVisible: false,
1777
1702
  },
1778
1703
  });
1779
- const Divider = React.forwardRef(({ className, orientation = "horizontal", thickness = "thin", lineStyle = "solid", variant = "normal", ...props }, ref) => {
1780
- return (jsx("div", { ref: ref, role: "separator", "aria-orientation": orientation, className: cn(dividerVariants({ orientation, thickness, lineStyle, variant }), className), ...props }));
1781
- });
1782
- Divider.displayName = "Divider";
1783
-
1784
- const listItemVariants = cva("flex items-start gap-3 p-3 rounded-medium transition-colors cursor-pointer", {
1704
+ const tooltipArrowVariants = cva("absolute w-0 h-0 border-solid border-[6px] -translate-x-1/2", {
1785
1705
  variants: {
1786
- variant: {
1787
- default: `hover:bg-action-fill-neutral-faded
1788
- focus:bg-action-fill-neutral-faded
1789
- focus:ring-2
1790
- ring-action-outline-primary-faded-hover
1791
- border border-transparent
1792
- `,
1793
- bordered: "border border-action-outline-primary-faded hover:bg-surface-fill-primary-subtle",
1794
- primary: `hover:bg-action-fill-neutral-faded
1795
- focus:bg-action-fill-neutral-faded
1796
- focus:ring-2
1797
- ring-action-outline-primary-faded-hover
1798
- border border-transparent
1799
- `,
1800
- negative: `hover:bg-action-fill-negative-faded
1801
- focus:bg-action-fill-negative-faded
1802
- focus:ring-2 ring-action-outline-negative-faded-hover
1803
- border border-transparent
1804
- `,
1805
- },
1806
- isDisabled: {
1807
- true: "cursor-not-allowed opacity-60",
1808
- false: "",
1809
- },
1810
- isSelected: {
1811
- true: "bg-action-fill-primary-faded border-action-outline-primary-faded",
1812
- false: "",
1706
+ placement: {
1707
+ "top-start": "top-full border-t-popup-fill-intense border-x-transparent border-b-transparent",
1708
+ top: "top-full border-t-popup-fill-intense border-x-transparent border-b-transparent",
1709
+ "top-end": "top-full border-t-popup-fill-intense border-x-transparent border-b-transparent",
1710
+ "bottom-start": "bottom-full border-b-popup-fill-intense border-x-transparent border-t-transparent",
1711
+ bottom: "bottom-full border-b-popup-fill-intense border-x-transparent border-t-transparent",
1712
+ "bottom-end": "bottom-full border-b-popup-fill-intense border-x-transparent border-t-transparent",
1813
1713
  },
1814
1714
  },
1815
1715
  defaultVariants: {
1816
- variant: "default",
1817
- isDisabled: false,
1818
- isSelected: false,
1716
+ placement: "top",
1819
1717
  },
1820
1718
  });
1821
- const ChevronRightIcon = ({ className }) => (jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, children: jsx("path", { d: "M7.5 15L12.5 10L7.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
1822
- const ListItem = React.forwardRef(({ className, type = "single", leadingIcon, title, description, trailingIcon, showChevron = true, variant = "default", isDisabled = false, isSelected = false, onSelectionChange, checkboxSize = "small", containerClassName, contentClassName, onClick, ...props }, ref) => {
1823
- const [internalSelected, setInternalSelected] = React.useState(isSelected);
1824
- // Sync internal state with prop
1825
- React.useEffect(() => {
1826
- setInternalSelected(isSelected);
1827
- }, [isSelected]);
1828
- const handleClick = (e) => {
1829
- if (isDisabled)
1719
+ const Tooltip = React.forwardRef(({ children, heading, description, placement = "top", showArrow = true, className, delay = 200, disabled = false, }, ref) => {
1720
+ const [isVisible, setIsVisible] = React.useState(false);
1721
+ const [position, setPosition] = React.useState({ top: 0, left: 0 });
1722
+ const [arrowPosition, setArrowPosition] = React.useState({ left: 0 });
1723
+ const [actualPlacement, setActualPlacement] = React.useState(placement);
1724
+ const timeoutRef = React.useRef(null);
1725
+ const triggerRef = React.useRef(null);
1726
+ const tooltipRef = React.useRef(null);
1727
+ const calculatePosition = React.useCallback(() => {
1728
+ if (!triggerRef.current || !tooltipRef.current)
1830
1729
  return;
1831
- if (type === "multiple") {
1832
- const newSelected = !internalSelected;
1833
- setInternalSelected(newSelected);
1834
- onSelectionChange?.(newSelected);
1730
+ const triggerRect = triggerRef.current.getBoundingClientRect();
1731
+ const tooltipRect = tooltipRef.current.getBoundingClientRect();
1732
+ const gap = 8; // 8px gap between trigger and tooltip
1733
+ const arrowSize = 6; // Size of the arrow
1734
+ const viewportPadding = 8; // Minimum padding from viewport edges
1735
+ let top = 0;
1736
+ let left = 0;
1737
+ let currentPlacement = placement;
1738
+ // Calculate initial position based on placement
1739
+ switch (placement) {
1740
+ case "top-start":
1741
+ top = triggerRect.top - tooltipRect.height - gap - arrowSize;
1742
+ left = triggerRect.left;
1743
+ break;
1744
+ case "top":
1745
+ top = triggerRect.top - tooltipRect.height - gap - arrowSize;
1746
+ left =
1747
+ triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
1748
+ break;
1749
+ case "top-end":
1750
+ top = triggerRect.top - tooltipRect.height - gap - arrowSize;
1751
+ left = triggerRect.right - tooltipRect.width;
1752
+ break;
1753
+ case "bottom-start":
1754
+ top = triggerRect.bottom + gap + arrowSize;
1755
+ left = triggerRect.left;
1756
+ break;
1757
+ case "bottom":
1758
+ top = triggerRect.bottom + gap + arrowSize;
1759
+ left =
1760
+ triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
1761
+ break;
1762
+ case "bottom-end":
1763
+ top = triggerRect.bottom + gap + arrowSize;
1764
+ left = triggerRect.right - tooltipRect.width;
1765
+ break;
1835
1766
  }
1836
- onClick?.(e);
1837
- };
1838
- const handleCheckboxChange = (e) => {
1839
- e.stopPropagation();
1840
- if (isDisabled)
1841
- return;
1842
- const newSelected = e.target.checked;
1843
- setInternalSelected(newSelected);
1844
- onSelectionChange?.(newSelected);
1845
- };
1846
- return (jsxs("div", { ref: ref, className: cn(listItemVariants({
1847
- variant,
1848
- isDisabled,
1849
- isSelected: type === "multiple" ? internalSelected : false,
1850
- }), containerClassName), onClick: handleClick, role: type === "multiple" ? "checkbox" : "button", "aria-checked": type === "multiple" ? internalSelected : undefined, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, ...props, children: [type === "multiple" && (jsx(Checkbox, { checked: internalSelected, onChange: handleCheckboxChange, isDisabled: isDisabled, size: checkboxSize, className: "shrink-0 mt-0.5" })), leadingIcon && (jsx("div", { className: cn(`shrink-0 flex items-center justify-center mt-0.5`, variant === "primary"
1851
- ? "text-action-ink-primary-normal"
1852
- : variant === "negative"
1853
- ? "text-action-ink-negative-normal"
1854
- : "text-action-ink-neutral-subtle", isDisabled && "text-surface-ink-neutral-disabled"), children: leadingIcon })), jsxs("div", { className: cn("flex-1 min-w-0 flex flex-col justify-center", contentClassName), children: [jsx("div", { className: cn("text-body-medium-regular truncate", variant === "primary"
1855
- ? "text-action-ink-primary-normal"
1856
- : variant === "negative"
1857
- ? "text-action-ink-negative-normal"
1858
- : "text-action-ink-neutral-normal", isDisabled && "text-surface-ink-neutral-disabled"), children: title }), description && (jsx("div", { className: cn("text-body-small-regular text-surface-ink-neutral-muted mt-0.5 line-clamp-2", isDisabled && "text-surface-ink-neutral-disabled"), children: description }))] }), (trailingIcon || showChevron) && (jsx("div", { className: "shrink-0 self-center text-action-ink-neutral-subtle", children: trailingIcon || jsx(ChevronRightIcon, {}) }))] }));
1859
- });
1860
- ListItem.displayName = "ListItem";
1861
-
1862
- 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) => {
1863
- const renderContent = () => {
1864
- if (isLoading) {
1865
- 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" }) }));
1767
+ // Get viewport dimensions
1768
+ const viewportWidth = window.innerWidth;
1769
+ const viewportHeight = window.innerHeight;
1770
+ // Adjust horizontal position to keep tooltip within viewport
1771
+ if (left < viewportPadding) {
1772
+ // Tooltip would overflow on the left
1773
+ left = viewportPadding;
1866
1774
  }
1867
- if (customContent) {
1868
- 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 })] }));
1775
+ else if (left + tooltipRect.width > viewportWidth - viewportPadding) {
1776
+ // Tooltip would overflow on the right
1777
+ left = viewportWidth - tooltipRect.width - viewportPadding;
1869
1778
  }
1870
- if (isEmpty || items.length === 0) {
1871
- 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 }))] }));
1872
- }
1873
- 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: () => {
1874
- item.onClick?.();
1875
- onClose?.();
1876
- }, containerClassName: cn(index === focusedIndex && "bg-action-fill-primary-faded") }, item.value))) })] }));
1877
- };
1878
- const widthClass = width === "full" ? "w-full" : width === "auto" ? "w-auto" : "";
1879
- const footerVisible = showFooter ?? !disableFooter;
1880
- return (jsxs("div", { ref: ref, className: cn("bg-white rounded-large overflow-hidden", widthClass, className), style: {
1881
- boxShadow: "0 1px 2px rgba(25, 25, 30, 0.1), 0 2px 6px rgba(25, 25, 30, 0.06)",
1882
- ...(width !== "full" && width !== "auto" ? { width } : {}),
1883
- }, children: [renderContent(), footerVisible && (jsxs("div", { className: "flex flex-col", children: [jsx(Divider, { thickness: "thin", variant: "muted" }), jsxs("div", { className: cn("flex gap-3 p-4", footerLayout === "vertical"
1884
- ? "flex-col"
1885
- : "items-center flex-row"), children: [jsx(Button, { variant: "secondary", color: "primary", size: "medium", isFullWidth: true, onClick: onSecondaryClick, children: secondaryButtonText }), jsx(Button, { variant: "primary", color: "primary", size: "medium", isFullWidth: true, onClick: onPrimaryClick, children: primaryButtonText })] })] }))] }));
1886
- });
1887
- DropdownMenu.displayName = "DropdownMenu";
1888
-
1889
- const dropdownVariants = cva("bg-surface-fill-primary-normal border border-surface-outline-neutral-subtle rounded-large", {
1890
- variants: {
1891
- size: {
1892
- small: "w-64",
1893
- medium: "w-80",
1894
- large: "w-96",
1895
- },
1896
- },
1897
- defaultVariants: {
1898
- size: "medium",
1899
- },
1900
- });
1901
- const Dropdown = React.forwardRef(({ className, trigger, 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, size = "medium", open: controlledOpen, defaultOpen = false, onOpenChange, containerClassName, menuClassName, showChevron = false, emptyIcon, disableFooter = false, showFooter, ...props }, ref) => {
1902
- const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
1903
- const isOpen = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen;
1904
- const dropdownRef = React.useRef(null);
1905
- const handleOpenChange = (newOpen) => {
1906
- if (controlledOpen === undefined) {
1907
- setUncontrolledOpen(newOpen);
1908
- }
1909
- onOpenChange?.(newOpen);
1910
- };
1911
- const toggleOpen = () => {
1912
- handleOpenChange(!isOpen);
1913
- };
1914
- // Close dropdown when clicking outside
1915
- React.useEffect(() => {
1916
- const handleClickOutside = (event) => {
1917
- if (dropdownRef.current &&
1918
- !dropdownRef.current.contains(event.target)) {
1919
- handleOpenChange(false);
1920
- }
1921
- };
1922
- if (isOpen) {
1923
- document.addEventListener("mousedown", handleClickOutside);
1924
- return () => {
1925
- document.removeEventListener("mousedown", handleClickOutside);
1926
- };
1927
- }
1928
- }, [isOpen]);
1929
- // Close on escape key
1930
- React.useEffect(() => {
1931
- const handleEscape = (event) => {
1932
- if (event.key === "Escape") {
1933
- handleOpenChange(false);
1934
- }
1935
- };
1936
- if (isOpen) {
1937
- document.addEventListener("keydown", handleEscape);
1938
- return () => {
1939
- document.removeEventListener("keydown", handleEscape);
1940
- };
1941
- }
1942
- }, [isOpen]);
1943
- const sizeMap = {
1944
- small: "w-64",
1945
- medium: "w-80",
1946
- large: "w-96",
1947
- };
1948
- 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] }))] }));
1949
- });
1950
- Dropdown.displayName = "Dropdown";
1951
-
1952
- const tooltipVariants = cva("fixed z-50 bg-popup-fill-intense text-action-ink-on-primary-normal rounded-medium border border-popup-outline-subtle flex flex-col p-4 rounded-xlarge min-w-[200px] max-w-[300px] transition-opacity duration-200", {
1953
- variants: {
1954
- isVisible: {
1955
- true: "opacity-100 pointer-events-auto shadow-[0_4px_20px_rgba(0,0,0,0.15)]",
1956
- false: "opacity-0 pointer-events-none",
1957
- },
1958
- },
1959
- defaultVariants: {
1960
- isVisible: false,
1961
- },
1962
- });
1963
- const tooltipArrowVariants = cva("absolute w-0 h-0 border-solid border-[6px] -translate-x-1/2", {
1964
- variants: {
1965
- placement: {
1966
- "top-start": "top-full border-t-popup-fill-intense border-x-transparent border-b-transparent",
1967
- top: "top-full border-t-popup-fill-intense border-x-transparent border-b-transparent",
1968
- "top-end": "top-full border-t-popup-fill-intense border-x-transparent border-b-transparent",
1969
- "bottom-start": "bottom-full border-b-popup-fill-intense border-x-transparent border-t-transparent",
1970
- bottom: "bottom-full border-b-popup-fill-intense border-x-transparent border-t-transparent",
1971
- "bottom-end": "bottom-full border-b-popup-fill-intense border-x-transparent border-t-transparent",
1972
- },
1973
- },
1974
- defaultVariants: {
1975
- placement: "top",
1976
- },
1977
- });
1978
- const Tooltip = React.forwardRef(({ children, heading, description, placement = "top", showArrow = true, className, delay = 200, disabled = false, }, ref) => {
1979
- const [isVisible, setIsVisible] = React.useState(false);
1980
- const [position, setPosition] = React.useState({ top: 0, left: 0 });
1981
- const [arrowPosition, setArrowPosition] = React.useState({ left: 0 });
1982
- const [actualPlacement, setActualPlacement] = React.useState(placement);
1983
- const timeoutRef = React.useRef(null);
1984
- const triggerRef = React.useRef(null);
1985
- const tooltipRef = React.useRef(null);
1986
- const calculatePosition = React.useCallback(() => {
1987
- if (!triggerRef.current || !tooltipRef.current)
1988
- return;
1989
- const triggerRect = triggerRef.current.getBoundingClientRect();
1990
- const tooltipRect = tooltipRef.current.getBoundingClientRect();
1991
- const gap = 8; // 8px gap between trigger and tooltip
1992
- const arrowSize = 6; // Size of the arrow
1993
- const viewportPadding = 8; // Minimum padding from viewport edges
1994
- let top = 0;
1995
- let left = 0;
1996
- let currentPlacement = placement;
1997
- // Calculate initial position based on placement
1998
- switch (placement) {
1999
- case "top-start":
2000
- top = triggerRect.top - tooltipRect.height - gap - arrowSize;
2001
- left = triggerRect.left;
2002
- break;
2003
- case "top":
2004
- top = triggerRect.top - tooltipRect.height - gap - arrowSize;
2005
- left =
2006
- triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
2007
- break;
2008
- case "top-end":
2009
- top = triggerRect.top - tooltipRect.height - gap - arrowSize;
2010
- left = triggerRect.right - tooltipRect.width;
2011
- break;
2012
- case "bottom-start":
2013
- top = triggerRect.bottom + gap + arrowSize;
2014
- left = triggerRect.left;
2015
- break;
2016
- case "bottom":
2017
- top = triggerRect.bottom + gap + arrowSize;
2018
- left =
2019
- triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
2020
- break;
2021
- case "bottom-end":
2022
- top = triggerRect.bottom + gap + arrowSize;
2023
- left = triggerRect.right - tooltipRect.width;
2024
- break;
2025
- }
2026
- // Get viewport dimensions
2027
- const viewportWidth = window.innerWidth;
2028
- const viewportHeight = window.innerHeight;
2029
- // Adjust horizontal position to keep tooltip within viewport
2030
- if (left < viewportPadding) {
2031
- // Tooltip would overflow on the left
2032
- left = viewportPadding;
2033
- }
2034
- else if (left + tooltipRect.width > viewportWidth - viewportPadding) {
2035
- // Tooltip would overflow on the right
2036
- left = viewportWidth - tooltipRect.width - viewportPadding;
2037
- }
2038
- // Adjust vertical position to keep tooltip within viewport
2039
- if (top < viewportPadding) {
2040
- // Tooltip would overflow at the top
2041
- // Try to flip to bottom if there's more space there
2042
- const spaceBelow = viewportHeight - triggerRect.bottom;
2043
- const spaceAbove = triggerRect.top;
2044
- if (spaceBelow > spaceAbove) {
2045
- // Flip to bottom
2046
- top = triggerRect.bottom + gap + arrowSize;
2047
- // Update placement to reflect the flip
2048
- if (placement === "top-start")
2049
- currentPlacement = "bottom-start";
2050
- else if (placement === "top")
2051
- currentPlacement = "bottom";
2052
- else if (placement === "top-end")
2053
- currentPlacement = "bottom-end";
2054
- }
2055
- else {
2056
- // Keep at top but adjust to stay in viewport
2057
- top = viewportPadding;
2058
- }
1779
+ // Adjust vertical position to keep tooltip within viewport
1780
+ if (top < viewportPadding) {
1781
+ // Tooltip would overflow at the top
1782
+ // Try to flip to bottom if there's more space there
1783
+ const spaceBelow = viewportHeight - triggerRect.bottom;
1784
+ const spaceAbove = triggerRect.top;
1785
+ if (spaceBelow > spaceAbove) {
1786
+ // Flip to bottom
1787
+ top = triggerRect.bottom + gap + arrowSize;
1788
+ // Update placement to reflect the flip
1789
+ if (placement === "top-start")
1790
+ currentPlacement = "bottom-start";
1791
+ else if (placement === "top")
1792
+ currentPlacement = "bottom";
1793
+ else if (placement === "top-end")
1794
+ currentPlacement = "bottom-end";
1795
+ }
1796
+ else {
1797
+ // Keep at top but adjust to stay in viewport
1798
+ top = viewportPadding;
1799
+ }
2059
1800
  }
2060
1801
  else if (top + tooltipRect.height > viewportHeight - viewportPadding) {
2061
1802
  // Tooltip would overflow at the bottom
@@ -2208,6 +1949,497 @@ const FormHeader = React.forwardRef(({ label, size = "medium", isOptional = fals
2208
1949
  });
2209
1950
  FormHeader.displayName = "FormHeader";
2210
1951
 
1952
+ const datePickerVariants = cva("relative flex items-center gap-2 border rounded-large transition-all font-display font-size-100 leading-100", {
1953
+ variants: {
1954
+ size: {
1955
+ small: "h-[28px] px-3 text-xs gap-2",
1956
+ medium: "h-[36px] px-4 text-sm gap-2",
1957
+ large: "h-[44px] px-5 text-base gap-3",
1958
+ },
1959
+ validationState: {
1960
+ none: `
1961
+ border-action-outline-neutral-faded
1962
+ hover:border-action-outline-primary-hover
1963
+ focus-within:border-action-outline-primary-hover
1964
+ focus-within:ring-2
1965
+ ring-action-outline-primary-faded-hover`,
1966
+ positive: `
1967
+ border-action-outline-positive-default
1968
+ focus-within:border-action-outline-positive-hover
1969
+ focus-within:ring-2
1970
+ ring-action-outline-positive-faded-hover`,
1971
+ negative: `border-action-outline-negative-default
1972
+ focus-within:border-action-outline-negative-hover
1973
+ focus-within:ring-2
1974
+ ring-action-outline-negative-faded-hover`,
1975
+ },
1976
+ isDisabled: {
1977
+ true: `
1978
+ border-[var(--border-width-thinner)]
1979
+ hover:border-action-outline-neutral-disabled
1980
+ border-action-outline-neutral-disabled
1981
+ bg-surface-fill-neutral-intense cursor-not-allowed opacity-60`,
1982
+ false: "bg-surface-fill-neutral-intense",
1983
+ },
1984
+ },
1985
+ defaultVariants: {
1986
+ size: "medium",
1987
+ validationState: "none",
1988
+ isDisabled: false,
1989
+ },
1990
+ });
1991
+ // Helper functions
1992
+ const parseDate = (date) => {
1993
+ if (!date)
1994
+ return null;
1995
+ if (date instanceof Date)
1996
+ return date;
1997
+ if (typeof date === "string") {
1998
+ const parsed = new Date(date);
1999
+ return isNaN(parsed.getTime()) ? null : parsed;
2000
+ }
2001
+ return null;
2002
+ };
2003
+ const formatDateDefault = (date) => {
2004
+ return date.toLocaleDateString("en-US", {
2005
+ year: "numeric",
2006
+ month: "short",
2007
+ day: "numeric",
2008
+ });
2009
+ };
2010
+ const DatePicker = React.forwardRef(({ className, value: controlledValue, defaultValue, onChange, placeholder = "Select a date", label, helperText, errorText, successText, validationState = "none", isDisabled = false, isRequired = false, isOptional = false, size = "medium", showClearButton = false, onClear, containerClassName, labelClassName, triggerClassName, calendarClassName, minDate, maxDate, formatDate = formatDateDefault, infoHeading, infoDescription, LinkComponent, linkText, linkHref, onLinkClick, ...props }, ref) => {
2011
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(parseDate(defaultValue));
2012
+ const [isOpen, setIsOpen] = React.useState(false);
2013
+ const datePickerRef = React.useRef(null);
2014
+ const calendarRef = React.useRef(null);
2015
+ const [dropdownPlacement, setDropdownPlacement] = React.useState("bottom");
2016
+ const value = controlledValue !== undefined
2017
+ ? parseDate(controlledValue)
2018
+ : uncontrolledValue;
2019
+ const hasValue = value !== null;
2020
+ // Determine which helper text to show
2021
+ const displayHelperText = errorText || successText || helperText;
2022
+ const currentValidationState = errorText
2023
+ ? "negative"
2024
+ : successText
2025
+ ? "positive"
2026
+ : validationState;
2027
+ const handleOpenChange = (newOpen) => {
2028
+ if (!isDisabled) {
2029
+ setIsOpen(newOpen);
2030
+ }
2031
+ };
2032
+ const toggleOpen = () => {
2033
+ handleOpenChange(!isOpen);
2034
+ };
2035
+ const handleCalendarChange = (value, event) => {
2036
+ // react-calendar can return Date, Date[], or null
2037
+ // We only support single date selection, so we take the first date if it's an array
2038
+ if (!value) {
2039
+ if (controlledValue === undefined) {
2040
+ setUncontrolledValue(null);
2041
+ }
2042
+ onChange?.(null);
2043
+ setIsOpen(false);
2044
+ return;
2045
+ }
2046
+ const selectedDate = Array.isArray(value) ? value[0] : value;
2047
+ // Ensure we have a valid Date object
2048
+ if (selectedDate instanceof Date) {
2049
+ if (controlledValue === undefined) {
2050
+ setUncontrolledValue(selectedDate);
2051
+ }
2052
+ onChange?.(selectedDate);
2053
+ setIsOpen(false);
2054
+ }
2055
+ };
2056
+ const handleClear = (e) => {
2057
+ e.stopPropagation();
2058
+ if (onClear) {
2059
+ onClear();
2060
+ }
2061
+ else {
2062
+ if (controlledValue === undefined) {
2063
+ setUncontrolledValue(null);
2064
+ }
2065
+ onChange?.(null);
2066
+ }
2067
+ };
2068
+ const updateDropdownPlacement = React.useCallback(() => {
2069
+ if (typeof window === "undefined")
2070
+ return;
2071
+ const trigger = datePickerRef.current;
2072
+ if (!trigger)
2073
+ return;
2074
+ const triggerRect = trigger.getBoundingClientRect();
2075
+ const spaceBelow = window.innerHeight - triggerRect.bottom;
2076
+ const spaceAbove = triggerRect.top;
2077
+ const calendarHeight = calendarRef.current
2078
+ ? calendarRef.current.offsetHeight
2079
+ : 0;
2080
+ if (calendarHeight === 0) {
2081
+ setDropdownPlacement(spaceBelow >= spaceAbove ? "bottom" : "top");
2082
+ return;
2083
+ }
2084
+ if (spaceBelow >= calendarHeight || spaceBelow >= spaceAbove) {
2085
+ setDropdownPlacement("bottom");
2086
+ }
2087
+ else {
2088
+ setDropdownPlacement("top");
2089
+ }
2090
+ }, []);
2091
+ React.useEffect(() => {
2092
+ if (!isOpen)
2093
+ return;
2094
+ if (typeof window === "undefined")
2095
+ return;
2096
+ let rafId = requestAnimationFrame(updateDropdownPlacement);
2097
+ const handleUpdate = () => updateDropdownPlacement();
2098
+ window.addEventListener("resize", handleUpdate);
2099
+ window.addEventListener("scroll", handleUpdate, true);
2100
+ return () => {
2101
+ cancelAnimationFrame(rafId);
2102
+ window.removeEventListener("resize", handleUpdate);
2103
+ window.removeEventListener("scroll", handleUpdate, true);
2104
+ };
2105
+ }, [isOpen, updateDropdownPlacement]);
2106
+ React.useEffect(() => {
2107
+ if (isOpen) {
2108
+ updateDropdownPlacement();
2109
+ }
2110
+ }, [isOpen, updateDropdownPlacement]);
2111
+ // Close calendar when clicking outside
2112
+ React.useEffect(() => {
2113
+ const handleClickOutside = (event) => {
2114
+ if (datePickerRef.current &&
2115
+ !datePickerRef.current.contains(event.target)) {
2116
+ handleOpenChange(false);
2117
+ }
2118
+ };
2119
+ if (isOpen) {
2120
+ document.addEventListener("mousedown", handleClickOutside);
2121
+ return () => {
2122
+ document.removeEventListener("mousedown", handleClickOutside);
2123
+ };
2124
+ }
2125
+ }, [isOpen]);
2126
+ // Close on escape key
2127
+ React.useEffect(() => {
2128
+ const handleEscape = (event) => {
2129
+ if (event.key === "Escape") {
2130
+ handleOpenChange(false);
2131
+ }
2132
+ };
2133
+ if (isOpen) {
2134
+ document.addEventListener("keydown", handleEscape);
2135
+ return () => {
2136
+ document.removeEventListener("keydown", handleEscape);
2137
+ };
2138
+ }
2139
+ }, [isOpen]);
2140
+ const minDateParsed = parseDate(minDate);
2141
+ const maxDateParsed = parseDate(maxDate);
2142
+ const sizeConfig = {
2143
+ small: {
2144
+ gap: "gap-2",
2145
+ },
2146
+ medium: {
2147
+ gap: "gap-2",
2148
+ },
2149
+ large: {
2150
+ gap: "gap-3",
2151
+ },
2152
+ };
2153
+ return (jsxs("div", { className: cn("w-full flex flex-col", sizeConfig[size].gap, containerClassName), children: [label && (jsx(FormHeader, { label: label, size: size, isRequired: isRequired, isOptional: isOptional, infoHeading: infoHeading, infoDescription: infoDescription, LinkComponent: LinkComponent, linkText: linkText, linkHref: linkHref, onLinkClick: onLinkClick, htmlFor: props.id, className: "mb-2", labelClassName: labelClassName })), jsxs("div", { ref: datePickerRef, className: cn(datePickerVariants({
2154
+ size,
2155
+ validationState: currentValidationState,
2156
+ isDisabled,
2157
+ }), "relative w-full cursor-pointer", className), onClick: !isDisabled ? toggleOpen : undefined, role: "button", "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-disabled": isDisabled, ...props, children: [jsx(Calendar, { className: cn("shrink-0 w-4 h-4", isDisabled
2158
+ ? "text-surface-ink-neutral-disabled"
2159
+ : currentValidationState === "positive"
2160
+ ? "text-feedback-ink-positive-intense"
2161
+ : currentValidationState === "negative"
2162
+ ? "text-feedback-ink-negative-subtle"
2163
+ : "text-surface-ink-neutral-muted") }), jsx("span", { className: cn("flex-1 text-left truncate", !hasValue && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: hasValue && value ? formatDate(value) : placeholder }), showClearButton && hasValue && !isDisabled && (jsx("button", { type: "button", onClick: handleClear, className: "shrink-0 flex items-center justify-center text-surface-ink-neutral-muted hover:text-surface-ink-neutral-normal transition-colors", tabIndex: -1, children: jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })), isOpen && !isDisabled && (jsx("div", { ref: calendarRef, className: cn("absolute z-50 left-0 bg-surface-fill-neutral-intense rounded-large shadow-lg p-4", dropdownPlacement === "bottom"
2164
+ ? "top-full mt-1"
2165
+ : "bottom-full mb-1", calendarClassName), onClick: (e) => e.stopPropagation(), children: jsx("div", { className: "react-calendar-wrapper w-fit", children: jsx(Calendar$1, { onChange: handleCalendarChange, value: value ?? null, minDate: minDateParsed ?? undefined, maxDate: maxDateParsed ?? undefined, locale: "en-US", formatShortWeekday: (locale, date) => {
2166
+ const weekdayNames = [
2167
+ "Su",
2168
+ "Mo",
2169
+ "Tu",
2170
+ "We",
2171
+ "Th",
2172
+ "Fr",
2173
+ "Sa",
2174
+ ];
2175
+ return weekdayNames[date.getDay()];
2176
+ } }) }) }))] }), jsx(FormFooter, { helperText: displayHelperText, validationState: currentValidationState === "none"
2177
+ ? "default"
2178
+ : currentValidationState, size: size, isDisabled: isDisabled, className: "mt-1" })] }));
2179
+ });
2180
+ DatePicker.displayName = "DatePicker";
2181
+
2182
+ const dividerVariants = cva("", {
2183
+ variants: {
2184
+ orientation: {
2185
+ horizontal: "w-full",
2186
+ vertical: "h-full",
2187
+ },
2188
+ thickness: {
2189
+ thinner: "",
2190
+ thin: "",
2191
+ thick: "",
2192
+ thicker: "",
2193
+ },
2194
+ lineStyle: {
2195
+ solid: "border-solid",
2196
+ dashed: "border-dashed",
2197
+ },
2198
+ variant: {
2199
+ normal: "",
2200
+ subtle: "",
2201
+ muted: "",
2202
+ },
2203
+ },
2204
+ compoundVariants: [
2205
+ // Horizontal orientation with thickness
2206
+ {
2207
+ orientation: "horizontal",
2208
+ thickness: "thinner",
2209
+ class: "border-t-[0.5px]",
2210
+ },
2211
+ {
2212
+ orientation: "horizontal",
2213
+ thickness: "thin",
2214
+ class: "border-t-[1px]",
2215
+ },
2216
+ {
2217
+ orientation: "horizontal",
2218
+ thickness: "thick",
2219
+ class: "border-t-[2px]",
2220
+ },
2221
+ {
2222
+ orientation: "horizontal",
2223
+ thickness: "thicker",
2224
+ class: "border-t-[3px]",
2225
+ },
2226
+ // Vertical orientation with thickness
2227
+ {
2228
+ orientation: "vertical",
2229
+ thickness: "thinner",
2230
+ class: "border-l-[0.5px]",
2231
+ },
2232
+ {
2233
+ orientation: "vertical",
2234
+ thickness: "thin",
2235
+ class: "border-l-[1px]",
2236
+ },
2237
+ {
2238
+ orientation: "vertical",
2239
+ thickness: "thick",
2240
+ class: "border-l-[2px]",
2241
+ },
2242
+ {
2243
+ orientation: "vertical",
2244
+ thickness: "thicker",
2245
+ class: "border-l-[3px]",
2246
+ },
2247
+ // Normal variant colors
2248
+ {
2249
+ variant: "normal",
2250
+ class: "border-surface-outline-neutral-normal",
2251
+ },
2252
+ // Subtle variant colors
2253
+ {
2254
+ variant: "subtle",
2255
+ class: "border-surface-outline-neutral-subtle",
2256
+ },
2257
+ // Muted variant colors
2258
+ {
2259
+ variant: "muted",
2260
+ class: "border-surface-outline-neutral-muted",
2261
+ },
2262
+ ],
2263
+ defaultVariants: {
2264
+ orientation: "horizontal",
2265
+ thickness: "thin",
2266
+ lineStyle: "solid",
2267
+ variant: "normal",
2268
+ },
2269
+ });
2270
+ const Divider = React.forwardRef(({ className, orientation = "horizontal", thickness = "thin", lineStyle = "solid", variant = "normal", ...props }, ref) => {
2271
+ return (jsx("div", { ref: ref, role: "separator", "aria-orientation": orientation, className: cn(dividerVariants({ orientation, thickness, lineStyle, variant }), className), ...props }));
2272
+ });
2273
+ Divider.displayName = "Divider";
2274
+
2275
+ const listItemVariants = cva("flex items-start gap-3 p-3 rounded-medium transition-colors cursor-pointer", {
2276
+ variants: {
2277
+ variant: {
2278
+ default: `hover:bg-action-fill-neutral-faded
2279
+ focus:bg-action-fill-neutral-faded
2280
+ focus:ring-2
2281
+ ring-action-outline-primary-faded-hover
2282
+ border border-transparent
2283
+ `,
2284
+ bordered: "border border-action-outline-primary-faded hover:bg-surface-fill-primary-subtle",
2285
+ primary: `hover:bg-action-fill-neutral-faded
2286
+ focus:bg-action-fill-neutral-faded
2287
+ focus:ring-2
2288
+ ring-action-outline-primary-faded-hover
2289
+ border border-transparent
2290
+ `,
2291
+ negative: `hover:bg-action-fill-negative-faded
2292
+ focus:bg-action-fill-negative-faded
2293
+ focus:ring-2 ring-action-outline-negative-faded-hover
2294
+ border border-transparent
2295
+ `,
2296
+ },
2297
+ isDisabled: {
2298
+ true: "cursor-not-allowed opacity-60",
2299
+ false: "",
2300
+ },
2301
+ isSelected: {
2302
+ true: "bg-action-fill-primary-faded border-action-outline-primary-faded",
2303
+ false: "",
2304
+ },
2305
+ },
2306
+ defaultVariants: {
2307
+ variant: "default",
2308
+ isDisabled: false,
2309
+ isSelected: false,
2310
+ },
2311
+ });
2312
+ const ChevronRightIcon = ({ className }) => (jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, children: jsx("path", { d: "M7.5 15L12.5 10L7.5 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
2313
+ const ListItem = React.forwardRef(({ className, type = "single", leadingIcon, title, description, trailingIcon, showChevron = true, variant = "default", isDisabled = false, isSelected = false, onSelectionChange, checkboxSize = "small", containerClassName, contentClassName, onClick, ...props }, ref) => {
2314
+ const [internalSelected, setInternalSelected] = React.useState(isSelected);
2315
+ // Sync internal state with prop
2316
+ React.useEffect(() => {
2317
+ setInternalSelected(isSelected);
2318
+ }, [isSelected]);
2319
+ const handleClick = (e) => {
2320
+ if (isDisabled)
2321
+ return;
2322
+ if (type === "multiple") {
2323
+ const newSelected = !internalSelected;
2324
+ setInternalSelected(newSelected);
2325
+ onSelectionChange?.(newSelected);
2326
+ }
2327
+ onClick?.(e);
2328
+ };
2329
+ const handleCheckboxChange = (e) => {
2330
+ e.stopPropagation();
2331
+ if (isDisabled)
2332
+ return;
2333
+ const newSelected = e.target.checked;
2334
+ setInternalSelected(newSelected);
2335
+ onSelectionChange?.(newSelected);
2336
+ };
2337
+ return (jsxs("div", { ref: ref, className: cn(listItemVariants({
2338
+ variant,
2339
+ isDisabled,
2340
+ isSelected: type === "multiple" ? internalSelected : false,
2341
+ }), containerClassName), onClick: handleClick, role: type === "multiple" ? "checkbox" : "button", "aria-checked": type === "multiple" ? internalSelected : undefined, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, ...props, children: [type === "multiple" && (jsx(Checkbox, { checked: internalSelected, onChange: handleCheckboxChange, isDisabled: isDisabled, size: checkboxSize, className: "shrink-0 mt-0.5" })), leadingIcon && (jsx("div", { className: cn(`shrink-0 flex items-center justify-center mt-0.5`, variant === "primary"
2342
+ ? "text-action-ink-primary-normal"
2343
+ : variant === "negative"
2344
+ ? "text-action-ink-negative-normal"
2345
+ : "text-action-ink-neutral-subtle", isDisabled && "text-surface-ink-neutral-disabled"), children: leadingIcon })), jsxs("div", { className: cn("flex-1 min-w-0 flex flex-col justify-center", contentClassName), children: [jsx("div", { className: cn("text-body-medium-regular truncate", variant === "primary"
2346
+ ? "text-action-ink-primary-normal"
2347
+ : variant === "negative"
2348
+ ? "text-action-ink-negative-normal"
2349
+ : "text-action-ink-neutral-normal", isDisabled && "text-surface-ink-neutral-disabled"), children: title }), description && (jsx("div", { className: cn("text-body-small-regular text-surface-ink-neutral-muted mt-0.5 line-clamp-2", isDisabled && "text-surface-ink-neutral-disabled"), children: description }))] }), (trailingIcon || showChevron) && (jsx("div", { className: "shrink-0 self-center text-action-ink-neutral-subtle", children: trailingIcon || jsx(ChevronRightIcon, {}) }))] }));
2350
+ });
2351
+ ListItem.displayName = "ListItem";
2352
+
2353
+ 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) => {
2354
+ const renderContent = () => {
2355
+ if (isLoading) {
2356
+ 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" }) }));
2357
+ }
2358
+ if (customContent) {
2359
+ 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 })] }));
2360
+ }
2361
+ if (isEmpty || items.length === 0) {
2362
+ 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 }))] }));
2363
+ }
2364
+ 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: () => {
2365
+ item.onClick?.();
2366
+ onClose?.();
2367
+ }, containerClassName: cn(index === focusedIndex && "bg-action-fill-primary-faded") }, item.value))) })] }));
2368
+ };
2369
+ const widthClass = width === "full" ? "w-full" : width === "auto" ? "w-auto" : "";
2370
+ const footerVisible = showFooter ?? !disableFooter;
2371
+ return (jsxs("div", { ref: ref, className: cn("bg-white rounded-large overflow-hidden", widthClass, className), style: {
2372
+ boxShadow: "0 1px 2px rgba(25, 25, 30, 0.1), 0 2px 6px rgba(25, 25, 30, 0.06)",
2373
+ ...(width !== "full" && width !== "auto" ? { width } : {}),
2374
+ }, children: [renderContent(), footerVisible && (jsxs("div", { className: "flex flex-col", children: [jsx(Divider, { thickness: "thin", variant: "muted" }), jsxs("div", { className: cn("flex gap-3 p-4", footerLayout === "vertical"
2375
+ ? "flex-col"
2376
+ : "items-center flex-row"), children: [jsx(Button, { variant: "secondary", color: "primary", size: "medium", isFullWidth: true, onClick: onSecondaryClick, children: secondaryButtonText }), jsx(Button, { variant: "primary", color: "primary", size: "medium", isFullWidth: true, onClick: onPrimaryClick, children: primaryButtonText })] })] }))] }));
2377
+ });
2378
+ DropdownMenu.displayName = "DropdownMenu";
2379
+
2380
+ const dropdownVariants = cva("bg-surface-fill-primary-normal border border-surface-outline-neutral-subtle rounded-large", {
2381
+ variants: {
2382
+ size: {
2383
+ small: "w-64",
2384
+ medium: "w-80",
2385
+ large: "w-96",
2386
+ },
2387
+ },
2388
+ defaultVariants: {
2389
+ size: "medium",
2390
+ },
2391
+ });
2392
+ const Dropdown = React.forwardRef(({ className, trigger, 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, size = "medium", open: controlledOpen, defaultOpen = false, onOpenChange, containerClassName, menuClassName, showChevron = false, emptyIcon, disableFooter = false, showFooter, ...props }, ref) => {
2393
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
2394
+ const isOpen = controlledOpen !== undefined ? controlledOpen : uncontrolledOpen;
2395
+ const dropdownRef = React.useRef(null);
2396
+ const handleOpenChange = (newOpen) => {
2397
+ if (controlledOpen === undefined) {
2398
+ setUncontrolledOpen(newOpen);
2399
+ }
2400
+ onOpenChange?.(newOpen);
2401
+ };
2402
+ const toggleOpen = () => {
2403
+ handleOpenChange(!isOpen);
2404
+ };
2405
+ // Close dropdown when clicking outside
2406
+ React.useEffect(() => {
2407
+ const handleClickOutside = (event) => {
2408
+ if (dropdownRef.current &&
2409
+ !dropdownRef.current.contains(event.target)) {
2410
+ handleOpenChange(false);
2411
+ }
2412
+ };
2413
+ if (isOpen) {
2414
+ document.addEventListener("mousedown", handleClickOutside);
2415
+ return () => {
2416
+ document.removeEventListener("mousedown", handleClickOutside);
2417
+ };
2418
+ }
2419
+ }, [isOpen]);
2420
+ // Close on escape key
2421
+ React.useEffect(() => {
2422
+ const handleEscape = (event) => {
2423
+ if (event.key === "Escape") {
2424
+ handleOpenChange(false);
2425
+ }
2426
+ };
2427
+ if (isOpen) {
2428
+ document.addEventListener("keydown", handleEscape);
2429
+ return () => {
2430
+ document.removeEventListener("keydown", handleEscape);
2431
+ };
2432
+ }
2433
+ }, [isOpen]);
2434
+ const sizeMap = {
2435
+ small: "w-64",
2436
+ medium: "w-80",
2437
+ large: "w-96",
2438
+ };
2439
+ 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] }))] }));
2440
+ });
2441
+ Dropdown.displayName = "Dropdown";
2442
+
2211
2443
  const Modal = React.forwardRef(({ isOpen, onClose, title, description, footer, children, size = "medium", showCloseButton = true, closeOnOverlayClick = true, closeOnEscape = true, className, contentClassName, headerClassName, bodyClassName, footerClassName, overlayClassName, ariaLabel, ariaDescribedBy, }, ref) => {
2212
2444
  const modalRef = React.useRef(null);
2213
2445
  const contentRef = ref || modalRef;
@@ -3713,5 +3945,5 @@ const TextArea = React.forwardRef(({ label, helperText, errorText, successText,
3713
3945
  });
3714
3946
  TextArea.displayName = "TextArea";
3715
3947
 
3716
- export { Alert, Avatar, AvatarCell, Badge, Button, ButtonGroup, Checkbox, Counter, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, IconButton, IconCell, Link, ListItem, Modal, NumberCell, Pagination, Radio, SearchableDropdown, Select, Skeleton, SlotCell, SpacerCell, Switch, TabItem, Table, TableDetailPanel, Tabs, Text, TextArea, TextField, Tooltip, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, checkboxVariants, cn, counterVariants, dropdownVariants, getAvailableIcons, hasIcon, iconButtonVariants, iconRegistry, linkVariants, listItemVariants, paginationVariants, radioVariants, selectVariants, switchVariants, tableCellVariants, tableHeaderVariants, tableVariants, textAreaVariants, textFieldVariants, tooltipVariants };
3948
+ export { Alert, Avatar, AvatarCell, Badge, Button, ButtonGroup, Checkbox, Counter, DatePicker, Divider, Dropdown, DropdownMenu, FormFooter, FormHeader, Icon, IconButton, IconCell, Link, ListItem, Modal, NumberCell, Pagination, Radio, SearchableDropdown, Select, Skeleton, SlotCell, SpacerCell, Switch, TabItem, Table, TableDetailPanel, Tabs, Text, TextArea, TextField, Tooltip, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, checkboxVariants, cn, counterVariants, datePickerVariants, dropdownVariants, getAvailableIcons, hasIcon, iconButtonVariants, iconRegistry, linkVariants, listItemVariants, paginationVariants, radioVariants, selectVariants, switchVariants, tableCellVariants, tableHeaderVariants, tableVariants, textAreaVariants, textFieldVariants, tooltipVariants };
3717
3949
  //# sourceMappingURL=index.esm.js.map