infinity-ui-elements 1.14.5 → 1.14.7

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.js CHANGED
@@ -1431,102 +1431,12 @@ const Divider = React__namespace.forwardRef(({ className, orientation = "horizon
1431
1431
  });
1432
1432
  Divider.displayName = "Divider";
1433
1433
 
1434
- // Detect iOS
1435
- const isIOS = () => {
1436
- if (typeof window === "undefined" || typeof navigator === "undefined")
1437
- return false;
1438
- return /iPad|iPhone|iPod/.test(navigator.userAgent) ||
1439
- (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
1440
- };
1441
- // Detect Android
1442
- const isAndroid = () => {
1443
- if (typeof navigator === "undefined")
1444
- return false;
1445
- return /Android/.test(navigator.userAgent);
1446
- };
1447
- // Get visual viewport height (accounts for keyboard)
1448
- const getVisualViewportHeight = () => {
1449
- if (typeof window === "undefined")
1450
- return 0;
1451
- // Use visualViewport API if available (better keyboard detection)
1452
- if (window.visualViewport) {
1453
- return window.visualViewport.height;
1454
- }
1455
- return window.innerHeight;
1456
- };
1457
- // Get safe area inset bottom (for devices with home indicator)
1458
- const getSafeAreaInsetBottom = () => {
1459
- if (typeof window === "undefined" || typeof document === "undefined")
1460
- return 0;
1461
- const root = document.documentElement;
1462
- const safeAreaInset = getComputedStyle(root).getPropertyValue("--safe-area-inset-bottom");
1463
- if (safeAreaInset) {
1464
- return parseInt(safeAreaInset, 10) || 0;
1465
- }
1466
- // Fallback: try to read env() value
1467
- const testDiv = document.createElement("div");
1468
- testDiv.style.paddingBottom = "env(safe-area-inset-bottom, 0px)";
1469
- document.body.appendChild(testDiv);
1470
- const paddingBottom = parseInt(getComputedStyle(testDiv).paddingBottom, 10) || 0;
1471
- document.body.removeChild(testDiv);
1472
- return paddingBottom;
1473
- };
1474
- const BottomSheet = React__namespace.forwardRef(({ isOpen, onClose, title, description, footer, children, variant = "default", showCloseButton = true, showDragHandle = true, closeOnOverlayClick = true, closeOnEscape = true, closeOnSwipeDown = true, maxHeight = "90vh", className, contentClassName, headerClassName, bodyClassName, footerClassName, overlayClassName, ariaLabel, ariaDescribedBy, adjustForKeyboard = true, scrollToFocusedInput = true, stickyContent, stickyContentClassName, }, ref) => {
1434
+ const BottomSheet = React__namespace.forwardRef(({ isOpen, onClose, title, description, footer, children, variant = "default", showCloseButton = true, showDragHandle = true, closeOnOverlayClick = true, closeOnEscape = true, closeOnSwipeDown = true, maxHeight = "90vh", className, contentClassName, headerClassName, bodyClassName, footerClassName, overlayClassName, ariaLabel, ariaDescribedBy, }, ref) => {
1475
1435
  const sheetRef = React__namespace.useRef(null);
1476
1436
  const contentRef = ref || sheetRef;
1477
- const bodyRef = React__namespace.useRef(null);
1478
- const dragHandleRef = React__namespace.useRef(null);
1479
- const overlayRef = React__namespace.useRef(null);
1480
1437
  const [isClosing, setIsClosing] = React__namespace.useState(false);
1481
1438
  const [touchStart, setTouchStart] = React__namespace.useState(null);
1482
1439
  const [touchEnd, setTouchEnd] = React__namespace.useState(null);
1483
- const [touchStartX, setTouchStartX] = React__namespace.useState(null);
1484
- const [isSwipeGesture, setIsSwipeGesture] = React__namespace.useState(false);
1485
- const [keyboardHeight, setKeyboardHeight] = React__namespace.useState(0);
1486
- const [viewportHeight, setViewportHeight] = React__namespace.useState(typeof window !== "undefined" ? window.innerHeight : 0);
1487
- const [safeAreaBottom, setSafeAreaBottom] = React__namespace.useState(0);
1488
- // Track if user started dragging from the drag handle
1489
- const isDraggingFromHandle = React__namespace.useRef(false);
1490
- // Store original body styles for restoration
1491
- const originalBodyStyles = React__namespace.useRef(null);
1492
- // Track scroll position before locking
1493
- const scrollPosition = React__namespace.useRef(0);
1494
- // Initialize safe area inset
1495
- React__namespace.useEffect(() => {
1496
- setSafeAreaBottom(getSafeAreaInsetBottom());
1497
- }, []);
1498
- // Handle Visual Viewport API for keyboard detection
1499
- React__namespace.useEffect(() => {
1500
- if (!isOpen || !adjustForKeyboard)
1501
- return;
1502
- if (typeof window === "undefined")
1503
- return;
1504
- const updateViewport = () => {
1505
- const newHeight = getVisualViewportHeight();
1506
- const fullHeight = window.innerHeight;
1507
- const newKeyboardHeight = Math.max(0, fullHeight - newHeight);
1508
- setViewportHeight(newHeight);
1509
- setKeyboardHeight(newKeyboardHeight);
1510
- };
1511
- // Use visualViewport API if available
1512
- if (window.visualViewport) {
1513
- window.visualViewport.addEventListener("resize", updateViewport);
1514
- window.visualViewport.addEventListener("scroll", updateViewport);
1515
- updateViewport();
1516
- return () => {
1517
- window.visualViewport?.removeEventListener("resize", updateViewport);
1518
- window.visualViewport?.removeEventListener("scroll", updateViewport);
1519
- };
1520
- }
1521
- else {
1522
- // Fallback for browsers without visualViewport API
1523
- window.addEventListener("resize", updateViewport);
1524
- updateViewport();
1525
- return () => {
1526
- window.removeEventListener("resize", updateViewport);
1527
- };
1528
- }
1529
- }, [isOpen, adjustForKeyboard]);
1530
1440
  // Handle escape key
1531
1441
  React__namespace.useEffect(() => {
1532
1442
  if (!isOpen || !closeOnEscape || !onClose)
@@ -1539,297 +1449,69 @@ const BottomSheet = React__namespace.forwardRef(({ isOpen, onClose, title, descr
1539
1449
  document.addEventListener("keydown", handleEscape);
1540
1450
  return () => document.removeEventListener("keydown", handleEscape);
1541
1451
  }, [isOpen, closeOnEscape, onClose]);
1542
- // Robust body scroll lock for iOS and Android
1452
+ // Prevent body scroll when bottom sheet is open
1543
1453
  React__namespace.useEffect(() => {
1544
- if (!isOpen)
1545
- return;
1546
- const iOS = isIOS();
1547
- // Store current scroll position
1548
- scrollPosition.current = window.pageYOffset || document.documentElement.scrollTop;
1549
- // Store original styles
1550
- originalBodyStyles.current = {
1551
- overflow: document.body.style.overflow,
1552
- position: document.body.style.position,
1553
- top: document.body.style.top,
1554
- left: document.body.style.left,
1555
- right: document.body.style.right,
1556
- width: document.body.style.width,
1557
- height: document.body.style.height,
1558
- };
1559
- // Apply scroll lock
1560
- document.body.style.overflow = "hidden";
1561
- // iOS requires position fixed to prevent background scroll
1562
- if (iOS) {
1563
- document.body.style.position = "fixed";
1564
- document.body.style.top = `-${scrollPosition.current}px`;
1565
- document.body.style.left = "0";
1566
- document.body.style.right = "0";
1567
- document.body.style.width = "100%";
1568
- }
1569
- // Track touch position for scroll boundary detection
1570
- let lastTouchY = 0;
1571
- const handleTouchStartForScroll = (e) => {
1572
- lastTouchY = e.touches[0].clientY;
1573
- };
1574
- // Prevent touchmove on overlay to stop iOS rubber-banding
1575
- // But always allow scrolling inside body content
1576
- const preventTouchMove = (e) => {
1577
- const target = e.target;
1578
- const currentTouchY = e.touches[0].clientY;
1579
- const touchDelta = currentTouchY - lastTouchY;
1580
- lastTouchY = currentTouchY;
1581
- // Always allow scrolling inside the body content
1582
- if (bodyRef.current?.contains(target)) {
1583
- const scrollTop = bodyRef.current.scrollTop;
1584
- const scrollHeight = bodyRef.current.scrollHeight;
1585
- const clientHeight = bodyRef.current.clientHeight;
1586
- const canScroll = scrollHeight > clientHeight;
1587
- // If content is scrollable, allow normal scrolling
1588
- if (canScroll) {
1589
- const isAtTop = scrollTop <= 0;
1590
- const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1;
1591
- const isScrollingDown = touchDelta > 0; // finger moving down = scrolling up
1592
- const isScrollingUp = touchDelta < 0; // finger moving up = scrolling down
1593
- // Only prevent at boundaries when trying to scroll beyond
1594
- if ((isAtTop && isScrollingDown) || (isAtBottom && isScrollingUp)) {
1595
- // At boundary, prevent overscroll but allow swipe-to-close from drag handle
1596
- if (isDraggingFromHandle.current) {
1597
- return; // Allow swipe gesture
1598
- }
1599
- e.preventDefault();
1600
- }
1601
- // Otherwise, allow normal scrolling
1602
- return;
1603
- }
1604
- // Content not scrollable, allow touch events to pass through
1605
- return;
1606
- }
1607
- // Also allow scrolling in sticky content area
1608
- if (target.closest('[data-sticky-content]')) {
1609
- return;
1610
- }
1611
- // Prevent scroll on overlay and other areas (to prevent background scroll)
1612
- e.preventDefault();
1613
- };
1614
- document.addEventListener("touchstart", handleTouchStartForScroll, { passive: true });
1615
- document.addEventListener("touchmove", preventTouchMove, { passive: false });
1454
+ if (isOpen) {
1455
+ document.body.style.overflow = "hidden";
1456
+ }
1457
+ else {
1458
+ document.body.style.overflow = "";
1459
+ }
1616
1460
  return () => {
1617
- document.removeEventListener("touchstart", handleTouchStartForScroll);
1618
- document.removeEventListener("touchmove", preventTouchMove);
1619
- // Restore original styles
1620
- if (originalBodyStyles.current) {
1621
- document.body.style.overflow = originalBodyStyles.current.overflow;
1622
- document.body.style.position = originalBodyStyles.current.position;
1623
- document.body.style.top = originalBodyStyles.current.top;
1624
- document.body.style.left = originalBodyStyles.current.left;
1625
- document.body.style.right = originalBodyStyles.current.right;
1626
- document.body.style.width = originalBodyStyles.current.width;
1627
- }
1628
- // Restore scroll position for iOS
1629
- if (iOS) {
1630
- window.scrollTo(0, scrollPosition.current);
1631
- }
1461
+ document.body.style.overflow = "";
1632
1462
  };
1633
1463
  }, [isOpen]);
1634
- // Reset closing state and scroll position when opening
1464
+ // Reset closing state when opening
1635
1465
  React__namespace.useEffect(() => {
1636
1466
  if (isOpen) {
1637
1467
  setIsClosing(false);
1638
- setKeyboardHeight(0);
1639
- // Reset scroll to top when opening - use multiple timeouts for iOS reliability
1640
- const resetScroll = () => {
1641
- if (bodyRef.current) {
1642
- bodyRef.current.scrollTop = 0;
1643
- }
1644
- };
1645
- // Immediate reset
1646
- resetScroll();
1647
- // After animation starts
1648
- requestAnimationFrame(resetScroll);
1649
- // After animation completes
1650
- const timer = setTimeout(resetScroll, 350);
1651
- return () => clearTimeout(timer);
1652
1468
  }
1653
1469
  }, [isOpen]);
1654
- // Scroll focused input into view when keyboard opens
1655
- React__namespace.useEffect(() => {
1656
- if (!isOpen || !scrollToFocusedInput || !adjustForKeyboard)
1657
- return;
1658
- const handleFocusIn = (e) => {
1659
- const target = e.target;
1660
- if (!target || !bodyRef.current)
1661
- return;
1662
- // Check if target is an input/textarea inside the body
1663
- if ((target.tagName === "INPUT" || target.tagName === "TEXTAREA") &&
1664
- bodyRef.current.contains(target)) {
1665
- // Wait for keyboard to open
1666
- setTimeout(() => {
1667
- // Scroll the input into view within the body container
1668
- const targetRect = target.getBoundingClientRect();
1669
- const bodyRect = bodyRef.current?.getBoundingClientRect();
1670
- if (!bodyRect)
1671
- return;
1672
- // Calculate if input is below visible area (considering keyboard)
1673
- const visibleBottom = viewportHeight - keyboardHeight - 20; // 20px buffer
1674
- if (targetRect.bottom > visibleBottom) {
1675
- // Scroll to bring input into view
1676
- const scrollAmount = targetRect.bottom - visibleBottom + 40;
1677
- bodyRef.current?.scrollBy({
1678
- top: scrollAmount,
1679
- behavior: "smooth",
1680
- });
1681
- }
1682
- }, 100);
1683
- }
1684
- };
1685
- document.addEventListener("focusin", handleFocusIn);
1686
- return () => document.removeEventListener("focusin", handleFocusIn);
1687
- }, [isOpen, scrollToFocusedInput, adjustForKeyboard, viewportHeight, keyboardHeight]);
1688
- // Reset scroll to top when content changes (for filtered results)
1689
- // This ensures empty states and filtered results are always visible
1690
- React__namespace.useEffect(() => {
1691
- if (!isOpen || !bodyRef.current)
1692
- return;
1693
- // Use requestAnimationFrame to ensure DOM has updated
1694
- requestAnimationFrame(() => {
1695
- if (bodyRef.current) {
1696
- // Always scroll to top to show content (including empty states)
1697
- bodyRef.current.scrollTop = 0;
1698
- // If keyboard is open, ensure content is in visible area
1699
- if (keyboardHeight > 0) {
1700
- // Force a re-render of the scroll position
1701
- bodyRef.current.style.scrollBehavior = 'auto';
1702
- bodyRef.current.scrollTop = 0;
1703
- bodyRef.current.style.scrollBehavior = '';
1704
- }
1705
- }
1706
- });
1707
- }, [children, isOpen, keyboardHeight]);
1708
1470
  // Handle close with animation
1709
- const handleClose = React__namespace.useCallback(() => {
1471
+ const handleClose = () => {
1710
1472
  if (!onClose)
1711
1473
  return;
1712
- // Blur any focused input to dismiss keyboard
1713
- if (document.activeElement instanceof HTMLElement) {
1714
- document.activeElement.blur();
1715
- }
1716
1474
  setIsClosing(true);
1717
1475
  // Wait for animation to complete before calling onClose
1718
1476
  setTimeout(() => {
1719
1477
  onClose();
1720
1478
  setIsClosing(false);
1721
1479
  }, 300); // Match animation duration
1722
- }, [onClose]);
1480
+ };
1723
1481
  // Handle overlay click
1724
1482
  const handleOverlayClick = (e) => {
1725
1483
  if (closeOnOverlayClick && e.target === e.currentTarget) {
1726
1484
  handleClose();
1727
1485
  }
1728
1486
  };
1729
- // Touch handling for swipe-to-close (only from drag handle or when scrolled to top)
1487
+ // Handle touch events for swipe down
1730
1488
  const handleTouchStart = (e) => {
1731
1489
  if (!closeOnSwipeDown)
1732
1490
  return;
1733
- const touch = e.targetTouches[0];
1734
1491
  setTouchEnd(null);
1735
- setTouchStart(touch.clientY);
1736
- setTouchStartX(touch.clientX);
1737
- setIsSwipeGesture(false);
1738
- // Check if drag started from the drag handle
1739
- isDraggingFromHandle.current = dragHandleRef.current?.contains(e.target) || false;
1492
+ setTouchStart(e.targetTouches[0].clientY);
1740
1493
  };
1741
1494
  const handleTouchMove = (e) => {
1742
- if (!closeOnSwipeDown || touchStart === null)
1495
+ if (!closeOnSwipeDown)
1743
1496
  return;
1744
- const touch = e.targetTouches[0];
1745
- const currentY = touch.clientY;
1746
- const currentX = touch.clientX;
1747
- setTouchEnd(currentY);
1748
- // Determine if this is a vertical swipe gesture (vs horizontal scroll)
1749
- if (!isSwipeGesture && touchStartX !== null) {
1750
- const deltaY = Math.abs(currentY - touchStart);
1751
- const deltaX = Math.abs(currentX - touchStartX);
1752
- // If vertical movement is greater, it's a swipe gesture
1753
- if (deltaY > 10 || deltaX > 10) {
1754
- setIsSwipeGesture(deltaY > deltaX);
1755
- }
1756
- }
1497
+ setTouchEnd(e.targetTouches[0].clientY);
1757
1498
  };
1758
1499
  const handleTouchEnd = () => {
1759
- if (!closeOnSwipeDown || touchStart === null || touchEnd === null) {
1760
- resetTouchState();
1500
+ if (!closeOnSwipeDown || !touchStart || !touchEnd)
1761
1501
  return;
1762
- }
1763
1502
  const distance = touchEnd - touchStart;
1764
- const isSwipeDown = distance > 80; // Minimum swipe distance
1765
- // Only close if:
1766
- // 1. Swiped from drag handle, OR
1767
- // 2. Body is scrolled to top and user swiped down
1768
- const bodyScrolledToTop = (bodyRef.current?.scrollTop || 0) <= 0;
1769
- const shouldClose = isSwipeDown && isSwipeGesture && (isDraggingFromHandle.current || bodyScrolledToTop);
1770
- if (shouldClose) {
1503
+ const isSwipeDown = distance > 100; // Minimum swipe distance
1504
+ if (isSwipeDown) {
1771
1505
  handleClose();
1772
1506
  }
1773
- resetTouchState();
1774
- };
1775
- const resetTouchState = () => {
1776
1507
  setTouchStart(null);
1777
1508
  setTouchEnd(null);
1778
- setTouchStartX(null);
1779
- setIsSwipeGesture(false);
1780
- isDraggingFromHandle.current = false;
1781
1509
  };
1782
- // Handle Android back button
1783
- React__namespace.useEffect(() => {
1784
- if (!isOpen || !isAndroid())
1785
- return;
1786
- const handlePopState = (e) => {
1787
- e.preventDefault();
1788
- handleClose();
1789
- };
1790
- // Push a dummy state so back button can be caught
1791
- window.history.pushState({ bottomSheet: true }, "");
1792
- window.addEventListener("popstate", handlePopState);
1793
- return () => {
1794
- window.removeEventListener("popstate", handlePopState);
1795
- // Clean up the dummy state if still present
1796
- if (window.history.state?.bottomSheet) {
1797
- window.history.back();
1798
- }
1799
- };
1800
- }, [isOpen, handleClose]);
1801
1510
  // Don't render if not open and not closing
1802
1511
  if (!isOpen && !isClosing)
1803
1512
  return null;
1804
1513
  const hasHeader = title || description;
1805
- // Calculate dynamic max height based on keyboard
1806
- // When keyboard is open, limit height to fit in visible viewport above keyboard
1807
- const dynamicMaxHeight = keyboardHeight > 0
1808
- ? `${Math.min(viewportHeight - 20, viewportHeight * 0.9)}px`
1809
- : maxHeight;
1810
- return (jsxRuntime.jsxs("div", { className: cn("fixed z-9999 flex items-end justify-center",
1811
- // Always position at bottom, container height adjusts for keyboard
1812
- className), style: {
1813
- // Position container to fill visible viewport (above keyboard)
1814
- top: 0,
1815
- left: 0,
1816
- right: 0,
1817
- // Use visual viewport height when keyboard is open to stay above keyboard
1818
- height: keyboardHeight > 0 ? `${viewportHeight}px` : "100%",
1819
- // On iOS, also set bottom to ensure proper positioning
1820
- ...(keyboardHeight === 0 && { bottom: 0 }),
1821
- }, role: "dialog", "aria-modal": "true", "aria-label": ariaLabel || title, "aria-describedby": ariaDescribedBy, children: [jsxRuntime.jsx("div", { ref: overlayRef, className: cn("absolute inset-0 z-0 bg-black/50 backdrop-blur-sm transition-opacity duration-300", isClosing ? "opacity-0" : "opacity-100", overlayClassName), onClick: handleOverlayClick, "aria-hidden": "true" }), jsxRuntime.jsxs("div", { ref: contentRef, className: cn("relative z-10 w-full transition-transform duration-300", "flex flex-col overflow-hidden", variant === "default" && "bg-white rounded-t-2xl shadow-xl", isClosing ? "animate-slide-out-bottom" : "animate-slide-in-bottom",
1822
- // Always position at bottom of container
1823
- "mt-auto", contentClassName), style: {
1824
- maxHeight: dynamicMaxHeight,
1825
- // Add safe area padding at the bottom for devices with home indicator
1826
- paddingBottom: footer ? 0 : `max(env(safe-area-inset-bottom, 0px), ${safeAreaBottom}px)`,
1827
- }, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, children: [showDragHandle && variant === "default" && (jsxRuntime.jsx("div", { ref: dragHandleRef, className: "flex justify-center pt-3 pb-2 cursor-grab active:cursor-grabbing touch-none", children: jsxRuntime.jsx("div", { className: "w-10 h-1 bg-neutral-300 rounded-full" }) })), hasHeader && (jsxRuntime.jsxs("div", { className: cn("flex items-start justify-between gap-4 shrink-0", variant === "default" && "px-6", variant === "default" && !showDragHandle && "pt-6", variant === "default" && showDragHandle && "pt-2", variant === "default" && !description && "pb-4", variant === "default" && description && "pb-2", headerClassName), children: [jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [title && (jsxRuntime.jsx(Text, { as: "h2", variant: "body", size: "large", weight: "semibold", color: "default", children: title })), description && (jsxRuntime.jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", color: "subtle", className: "mt-1", children: description }))] }), showCloseButton && onClose && (jsxRuntime.jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet", className: "shrink-0" }))] })), !hasHeader && showCloseButton && onClose && (jsxRuntime.jsx("div", { className: cn("absolute z-10", variant === "default" && "top-4 right-4"), children: jsxRuntime.jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet" }) })), stickyContent && (jsxRuntime.jsx("div", { className: cn("shrink-0", variant === "default" && "px-6 pb-4", variant === "default" && !hasHeader && !showDragHandle && "pt-4", variant === "default" && !hasHeader && showDragHandle && "pt-2", stickyContentClassName), children: stickyContent })), jsxRuntime.jsx("div", { ref: bodyRef, className: cn("flex-1 min-h-0 overflow-y-auto overscroll-contain",
1828
- // Smooth scrolling and momentum scroll for iOS
1829
- "-webkit-overflow-scrolling-touch", variant === "default" && "px-6", variant === "default" && hasHeader && !stickyContent && "py-4", variant === "default" && hasHeader && stickyContent && "pb-4", variant === "default" && !hasHeader && !showDragHandle && !stickyContent && "pt-6 pb-4", variant === "default" && !hasHeader && showDragHandle && !stickyContent && "pt-2 pb-4", variant === "default" && stickyContent && "pt-0 pb-4", variant === "default" && !footer && "pb-6", bodyClassName), children: children }), footer && (jsxRuntime.jsxs("div", { className: "flex flex-col shrink-0", style: {
1830
- // Add safe area padding to footer
1831
- paddingBottom: `max(env(safe-area-inset-bottom, 0px), ${safeAreaBottom}px)`,
1832
- }, children: [variant === "default" && (jsxRuntime.jsx(Divider, { thickness: "thin", variant: "muted" })), jsxRuntime.jsx("div", { className: cn("flex items-center justify-end gap-3", variant === "default" && "px-6 py-4", footerClassName), children: footer })] }))] })] }));
1514
+ return (jsxRuntime.jsxs("div", { className: cn("fixed inset-0 z-9999 flex items-end justify-center", className), role: "dialog", "aria-modal": "true", "aria-label": ariaLabel || title, "aria-describedby": ariaDescribedBy, children: [jsxRuntime.jsx("div", { className: cn("absolute inset-0 z-0 bg-black/50 backdrop-blur-sm transition-opacity duration-300", isClosing ? "opacity-0" : "opacity-100", overlayClassName), onClick: handleOverlayClick, "aria-hidden": "true" }), jsxRuntime.jsxs("div", { ref: contentRef, className: cn("relative z-10 w-full transition-transform duration-300", "flex flex-col overflow-hidden", variant === "default" && "bg-white rounded-t-2xl shadow-xl", isClosing ? "animate-slide-out-bottom" : "animate-slide-in-bottom", contentClassName), style: { maxHeight }, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, children: [showDragHandle && variant === "default" && (jsxRuntime.jsx("div", { className: "flex justify-center pt-3 pb-2", children: jsxRuntime.jsx("div", { className: "w-10 h-1 bg-neutral-300 rounded-full" }) })), hasHeader && (jsxRuntime.jsxs("div", { className: cn("flex items-start justify-between gap-4", variant === "default" && "px-6", variant === "default" && !showDragHandle && "pt-6", variant === "default" && showDragHandle && "pt-2", variant === "default" && !description && "pb-4", variant === "default" && description && "pb-2", headerClassName), children: [jsxRuntime.jsxs("div", { className: "flex-1", children: [title && (jsxRuntime.jsx(Text, { as: "h2", variant: "body", size: "large", weight: "semibold", color: "default", children: title })), description && (jsxRuntime.jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", color: "subtle", className: "mt-1", children: description }))] }), showCloseButton && onClose && (jsxRuntime.jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet", className: "shrink-0" }))] })), !hasHeader && showCloseButton && onClose && (jsxRuntime.jsx("div", { className: cn("absolute z-10", variant === "default" && "top-4 right-4"), children: jsxRuntime.jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet" }) })), jsxRuntime.jsx("div", { className: cn("flex-1 overflow-y-auto", variant === "default" && "px-6", variant === "default" && hasHeader && "py-4", variant === "default" && !hasHeader && !showDragHandle && "pt-6 pb-4", variant === "default" && !hasHeader && showDragHandle && "pt-2 pb-4", variant === "default" && !footer && "pb-6", bodyClassName), children: children }), footer && (jsxRuntime.jsxs("div", { className: "flex flex-col", children: [variant === "default" && (jsxRuntime.jsx(Divider, { thickness: "thin", variant: "muted" })), jsxRuntime.jsx("div", { className: cn("flex items-center justify-end gap-3", variant === "default" && "px-6 py-4", footerClassName), children: footer })] }))] })] }));
1833
1515
  });
1834
1516
  BottomSheet.displayName = "BottomSheet";
1835
1517
 
@@ -4937,16 +4619,11 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
4937
4619
  });
4938
4620
  const showDropdown = isOpen && searchValue.length >= minSearchLength;
4939
4621
  // Render dropdown menu content
4940
- const renderDropdownContent = () => (jsxRuntime.jsx(DropdownMenu, { items: itemsWithHandlers, sectionHeading: isMobile ? undefined : sectionHeading, isLoading: isLoading, isEmpty: itemsWithAddNew.length === 0 && !showAddNew, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, showFooter: (primaryButtonText || secondaryButtonText) && !disableFooter
4622
+ const renderDropdownContent = () => (jsxRuntime.jsx(DropdownMenu, { items: itemsWithHandlers, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: itemsWithAddNew.length === 0 && !showAddNew, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, showFooter: (primaryButtonText || secondaryButtonText) && !disableFooter
4941
4623
  ? true
4942
- : false, footerLayout: footerLayout, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: dropdownClassName, width: isMobile ? "full" : (dropdownWidth === "full" ? "full" : "auto"),
4943
- // On mobile, let BottomSheet body handle scrolling; on desktop use calculated maxHeight
4944
- maxHeight: isMobile ? "none" : `${position.maxHeight}px`, unstyled: isMobile }));
4945
- // Search field component for mobile BottomSheet
4946
- // Note: No autoFocus - keyboard only appears when user taps the field
4947
- const mobileSearchField = (jsxRuntime.jsx(TextField, { value: searchValue, onChange: handleSearchChange, onKeyDown: handleKeyDown, containerClassName: "mb-0", placeholder: textFieldProps.placeholder || "Search...", ...textFieldProps }));
4624
+ : false, footerLayout: footerLayout, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: dropdownClassName, width: isMobile ? "full" : (dropdownWidth === "full" ? "full" : "auto"), maxHeight: `${position.maxHeight}px`, unstyled: isMobile }));
4948
4625
  // Mobile: BottomSheet, Desktop: Regular Dropdown
4949
- const dropdownMenu = showDropdown && (isMobile ? (jsxRuntime.jsx(BottomSheet, { isOpen: isOpen, onClose: () => setIsOpen(false), title: sectionHeading, variant: "default", showDragHandle: true, closeOnOverlayClick: true, closeOnEscape: true, closeOnSwipeDown: true, adjustForKeyboard: true, scrollToFocusedInput: true, maxHeight: "85vh", stickyContent: mobileSearchField, children: renderDropdownContent() })) : (jsxRuntime.jsx("div", { ref: menuRef, style: {
4626
+ const dropdownMenu = showDropdown && (isMobile ? (jsxRuntime.jsxs(BottomSheet, { isOpen: isOpen, onClose: () => setIsOpen(false), title: sectionHeading, variant: "default", showDragHandle: true, closeOnOverlayClick: true, closeOnEscape: true, closeOnSwipeDown: true, children: [jsxRuntime.jsx("div", { className: "mb-4", children: jsxRuntime.jsx(TextField, { value: searchValue, onChange: handleSearchChange, onKeyDown: handleKeyDown, containerClassName: "mb-0", placeholder: textFieldProps.placeholder || "Search...", ...textFieldProps }) }), renderDropdownContent()] })) : (jsxRuntime.jsx("div", { ref: menuRef, style: {
4950
4627
  position: "fixed",
4951
4628
  ...(position.top !== undefined && { top: `${position.top}px` }),
4952
4629
  ...(position.bottom !== undefined && {