infinity-ui-elements 1.14.0 → 1.14.2

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
@@ -1410,12 +1410,102 @@ const Divider = React.forwardRef(({ className, orientation = "horizontal", thick
1410
1410
  });
1411
1411
  Divider.displayName = "Divider";
1412
1412
 
1413
- const BottomSheet = React.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) => {
1413
+ // Detect iOS
1414
+ const isIOS = () => {
1415
+ if (typeof window === "undefined" || typeof navigator === "undefined")
1416
+ return false;
1417
+ return /iPad|iPhone|iPod/.test(navigator.userAgent) ||
1418
+ (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
1419
+ };
1420
+ // Detect Android
1421
+ const isAndroid = () => {
1422
+ if (typeof navigator === "undefined")
1423
+ return false;
1424
+ return /Android/.test(navigator.userAgent);
1425
+ };
1426
+ // Get visual viewport height (accounts for keyboard)
1427
+ const getVisualViewportHeight = () => {
1428
+ if (typeof window === "undefined")
1429
+ return 0;
1430
+ // Use visualViewport API if available (better keyboard detection)
1431
+ if (window.visualViewport) {
1432
+ return window.visualViewport.height;
1433
+ }
1434
+ return window.innerHeight;
1435
+ };
1436
+ // Get safe area inset bottom (for devices with home indicator)
1437
+ const getSafeAreaInsetBottom = () => {
1438
+ if (typeof window === "undefined" || typeof document === "undefined")
1439
+ return 0;
1440
+ const root = document.documentElement;
1441
+ const safeAreaInset = getComputedStyle(root).getPropertyValue("--safe-area-inset-bottom");
1442
+ if (safeAreaInset) {
1443
+ return parseInt(safeAreaInset, 10) || 0;
1444
+ }
1445
+ // Fallback: try to read env() value
1446
+ const testDiv = document.createElement("div");
1447
+ testDiv.style.paddingBottom = "env(safe-area-inset-bottom, 0px)";
1448
+ document.body.appendChild(testDiv);
1449
+ const paddingBottom = parseInt(getComputedStyle(testDiv).paddingBottom, 10) || 0;
1450
+ document.body.removeChild(testDiv);
1451
+ return paddingBottom;
1452
+ };
1453
+ const BottomSheet = React.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, }, ref) => {
1414
1454
  const sheetRef = React.useRef(null);
1415
1455
  const contentRef = ref || sheetRef;
1456
+ const bodyRef = React.useRef(null);
1457
+ const dragHandleRef = React.useRef(null);
1458
+ const overlayRef = React.useRef(null);
1416
1459
  const [isClosing, setIsClosing] = React.useState(false);
1417
1460
  const [touchStart, setTouchStart] = React.useState(null);
1418
1461
  const [touchEnd, setTouchEnd] = React.useState(null);
1462
+ const [touchStartX, setTouchStartX] = React.useState(null);
1463
+ const [isSwipeGesture, setIsSwipeGesture] = React.useState(false);
1464
+ const [keyboardHeight, setKeyboardHeight] = React.useState(0);
1465
+ const [viewportHeight, setViewportHeight] = React.useState(typeof window !== "undefined" ? window.innerHeight : 0);
1466
+ const [safeAreaBottom, setSafeAreaBottom] = React.useState(0);
1467
+ // Track if user started dragging from the drag handle
1468
+ const isDraggingFromHandle = React.useRef(false);
1469
+ // Store original body styles for restoration
1470
+ const originalBodyStyles = React.useRef(null);
1471
+ // Track scroll position before locking
1472
+ const scrollPosition = React.useRef(0);
1473
+ // Initialize safe area inset
1474
+ React.useEffect(() => {
1475
+ setSafeAreaBottom(getSafeAreaInsetBottom());
1476
+ }, []);
1477
+ // Handle Visual Viewport API for keyboard detection
1478
+ React.useEffect(() => {
1479
+ if (!isOpen || !adjustForKeyboard)
1480
+ return;
1481
+ if (typeof window === "undefined")
1482
+ return;
1483
+ const updateViewport = () => {
1484
+ const newHeight = getVisualViewportHeight();
1485
+ const fullHeight = window.innerHeight;
1486
+ const newKeyboardHeight = Math.max(0, fullHeight - newHeight);
1487
+ setViewportHeight(newHeight);
1488
+ setKeyboardHeight(newKeyboardHeight);
1489
+ };
1490
+ // Use visualViewport API if available
1491
+ if (window.visualViewport) {
1492
+ window.visualViewport.addEventListener("resize", updateViewport);
1493
+ window.visualViewport.addEventListener("scroll", updateViewport);
1494
+ updateViewport();
1495
+ return () => {
1496
+ window.visualViewport?.removeEventListener("resize", updateViewport);
1497
+ window.visualViewport?.removeEventListener("scroll", updateViewport);
1498
+ };
1499
+ }
1500
+ else {
1501
+ // Fallback for browsers without visualViewport API
1502
+ window.addEventListener("resize", updateViewport);
1503
+ updateViewport();
1504
+ return () => {
1505
+ window.removeEventListener("resize", updateViewport);
1506
+ };
1507
+ }
1508
+ }, [isOpen, adjustForKeyboard]);
1419
1509
  // Handle escape key
1420
1510
  React.useEffect(() => {
1421
1511
  if (!isOpen || !closeOnEscape || !onClose)
@@ -1428,69 +1518,245 @@ const BottomSheet = React.forwardRef(({ isOpen, onClose, title, description, foo
1428
1518
  document.addEventListener("keydown", handleEscape);
1429
1519
  return () => document.removeEventListener("keydown", handleEscape);
1430
1520
  }, [isOpen, closeOnEscape, onClose]);
1431
- // Prevent body scroll when bottom sheet is open
1521
+ // Robust body scroll lock for iOS and Android
1432
1522
  React.useEffect(() => {
1433
- if (isOpen) {
1434
- document.body.style.overflow = "hidden";
1435
- }
1436
- else {
1437
- document.body.style.overflow = "";
1438
- }
1523
+ if (!isOpen)
1524
+ return;
1525
+ const iOS = isIOS();
1526
+ // Store current scroll position
1527
+ scrollPosition.current = window.pageYOffset || document.documentElement.scrollTop;
1528
+ // Store original styles
1529
+ originalBodyStyles.current = {
1530
+ overflow: document.body.style.overflow,
1531
+ position: document.body.style.position,
1532
+ top: document.body.style.top,
1533
+ left: document.body.style.left,
1534
+ right: document.body.style.right,
1535
+ width: document.body.style.width,
1536
+ height: document.body.style.height,
1537
+ };
1538
+ // Apply scroll lock
1539
+ document.body.style.overflow = "hidden";
1540
+ // iOS requires position fixed to prevent background scroll
1541
+ if (iOS) {
1542
+ document.body.style.position = "fixed";
1543
+ document.body.style.top = `-${scrollPosition.current}px`;
1544
+ document.body.style.left = "0";
1545
+ document.body.style.right = "0";
1546
+ document.body.style.width = "100%";
1547
+ }
1548
+ // Prevent touchmove on overlay to stop iOS rubber-banding
1549
+ const preventTouchMove = (e) => {
1550
+ const target = e.target;
1551
+ // Allow scrolling inside the body content
1552
+ if (bodyRef.current?.contains(target)) {
1553
+ const scrollTop = bodyRef.current.scrollTop;
1554
+ const scrollHeight = bodyRef.current.scrollHeight;
1555
+ const clientHeight = bodyRef.current.clientHeight;
1556
+ // Check if at scroll boundaries
1557
+ const isAtTop = scrollTop <= 0;
1558
+ const isAtBottom = scrollTop + clientHeight >= scrollHeight;
1559
+ // Only prevent default if at boundaries and trying to scroll further
1560
+ if ((isAtTop && e.touches[0].clientY > (touchStart || 0)) ||
1561
+ (isAtBottom && e.touches[0].clientY < (touchStart || 0))) {
1562
+ // Allow the swipe-to-close gesture
1563
+ if (!isDraggingFromHandle.current) {
1564
+ e.preventDefault();
1565
+ }
1566
+ }
1567
+ return;
1568
+ }
1569
+ // Prevent scroll on overlay and other areas
1570
+ e.preventDefault();
1571
+ };
1572
+ document.addEventListener("touchmove", preventTouchMove, { passive: false });
1439
1573
  return () => {
1440
- document.body.style.overflow = "";
1574
+ document.removeEventListener("touchmove", preventTouchMove);
1575
+ // Restore original styles
1576
+ if (originalBodyStyles.current) {
1577
+ document.body.style.overflow = originalBodyStyles.current.overflow;
1578
+ document.body.style.position = originalBodyStyles.current.position;
1579
+ document.body.style.top = originalBodyStyles.current.top;
1580
+ document.body.style.left = originalBodyStyles.current.left;
1581
+ document.body.style.right = originalBodyStyles.current.right;
1582
+ document.body.style.width = originalBodyStyles.current.width;
1583
+ }
1584
+ // Restore scroll position for iOS
1585
+ if (iOS) {
1586
+ window.scrollTo(0, scrollPosition.current);
1587
+ }
1441
1588
  };
1442
1589
  }, [isOpen]);
1443
1590
  // Reset closing state when opening
1444
1591
  React.useEffect(() => {
1445
1592
  if (isOpen) {
1446
1593
  setIsClosing(false);
1594
+ setKeyboardHeight(0);
1447
1595
  }
1448
1596
  }, [isOpen]);
1597
+ // Scroll focused input into view when keyboard opens
1598
+ React.useEffect(() => {
1599
+ if (!isOpen || !scrollToFocusedInput || !adjustForKeyboard)
1600
+ return;
1601
+ const handleFocusIn = (e) => {
1602
+ const target = e.target;
1603
+ if (!target || !bodyRef.current)
1604
+ return;
1605
+ // Check if target is an input/textarea inside the body
1606
+ if ((target.tagName === "INPUT" || target.tagName === "TEXTAREA") &&
1607
+ bodyRef.current.contains(target)) {
1608
+ // Wait for keyboard to open
1609
+ setTimeout(() => {
1610
+ // Scroll the input into view within the body container
1611
+ const targetRect = target.getBoundingClientRect();
1612
+ const bodyRect = bodyRef.current?.getBoundingClientRect();
1613
+ if (!bodyRect)
1614
+ return;
1615
+ // Calculate if input is below visible area (considering keyboard)
1616
+ const visibleBottom = viewportHeight - keyboardHeight - 20; // 20px buffer
1617
+ if (targetRect.bottom > visibleBottom) {
1618
+ // Scroll to bring input into view
1619
+ const scrollAmount = targetRect.bottom - visibleBottom + 40;
1620
+ bodyRef.current?.scrollBy({
1621
+ top: scrollAmount,
1622
+ behavior: "smooth",
1623
+ });
1624
+ }
1625
+ }, 100);
1626
+ }
1627
+ };
1628
+ document.addEventListener("focusin", handleFocusIn);
1629
+ return () => document.removeEventListener("focusin", handleFocusIn);
1630
+ }, [isOpen, scrollToFocusedInput, adjustForKeyboard, viewportHeight, keyboardHeight]);
1631
+ // Reset scroll to top when content changes (for filtered results)
1632
+ React.useEffect(() => {
1633
+ if (!isOpen || !bodyRef.current)
1634
+ return;
1635
+ // Use requestAnimationFrame to ensure DOM has updated
1636
+ requestAnimationFrame(() => {
1637
+ if (bodyRef.current) {
1638
+ bodyRef.current.scrollTop = 0;
1639
+ }
1640
+ });
1641
+ }, [children, isOpen]);
1449
1642
  // Handle close with animation
1450
- const handleClose = () => {
1643
+ const handleClose = React.useCallback(() => {
1451
1644
  if (!onClose)
1452
1645
  return;
1646
+ // Blur any focused input to dismiss keyboard
1647
+ if (document.activeElement instanceof HTMLElement) {
1648
+ document.activeElement.blur();
1649
+ }
1453
1650
  setIsClosing(true);
1454
1651
  // Wait for animation to complete before calling onClose
1455
1652
  setTimeout(() => {
1456
1653
  onClose();
1457
1654
  setIsClosing(false);
1458
1655
  }, 300); // Match animation duration
1459
- };
1656
+ }, [onClose]);
1460
1657
  // Handle overlay click
1461
1658
  const handleOverlayClick = (e) => {
1462
1659
  if (closeOnOverlayClick && e.target === e.currentTarget) {
1463
1660
  handleClose();
1464
1661
  }
1465
1662
  };
1466
- // Handle touch events for swipe down
1663
+ // Touch handling for swipe-to-close (only from drag handle or when scrolled to top)
1467
1664
  const handleTouchStart = (e) => {
1468
1665
  if (!closeOnSwipeDown)
1469
1666
  return;
1667
+ const touch = e.targetTouches[0];
1470
1668
  setTouchEnd(null);
1471
- setTouchStart(e.targetTouches[0].clientY);
1669
+ setTouchStart(touch.clientY);
1670
+ setTouchStartX(touch.clientX);
1671
+ setIsSwipeGesture(false);
1672
+ // Check if drag started from the drag handle
1673
+ isDraggingFromHandle.current = dragHandleRef.current?.contains(e.target) || false;
1472
1674
  };
1473
1675
  const handleTouchMove = (e) => {
1474
- if (!closeOnSwipeDown)
1676
+ if (!closeOnSwipeDown || touchStart === null)
1475
1677
  return;
1476
- setTouchEnd(e.targetTouches[0].clientY);
1678
+ const touch = e.targetTouches[0];
1679
+ const currentY = touch.clientY;
1680
+ const currentX = touch.clientX;
1681
+ setTouchEnd(currentY);
1682
+ // Determine if this is a vertical swipe gesture (vs horizontal scroll)
1683
+ if (!isSwipeGesture && touchStartX !== null) {
1684
+ const deltaY = Math.abs(currentY - touchStart);
1685
+ const deltaX = Math.abs(currentX - touchStartX);
1686
+ // If vertical movement is greater, it's a swipe gesture
1687
+ if (deltaY > 10 || deltaX > 10) {
1688
+ setIsSwipeGesture(deltaY > deltaX);
1689
+ }
1690
+ }
1477
1691
  };
1478
1692
  const handleTouchEnd = () => {
1479
- if (!closeOnSwipeDown || !touchStart || !touchEnd)
1693
+ if (!closeOnSwipeDown || touchStart === null || touchEnd === null) {
1694
+ resetTouchState();
1480
1695
  return;
1696
+ }
1481
1697
  const distance = touchEnd - touchStart;
1482
- const isSwipeDown = distance > 100; // Minimum swipe distance
1483
- if (isSwipeDown) {
1698
+ const isSwipeDown = distance > 80; // Minimum swipe distance
1699
+ // Only close if:
1700
+ // 1. Swiped from drag handle, OR
1701
+ // 2. Body is scrolled to top and user swiped down
1702
+ const bodyScrolledToTop = (bodyRef.current?.scrollTop || 0) <= 0;
1703
+ const shouldClose = isSwipeDown && isSwipeGesture && (isDraggingFromHandle.current || bodyScrolledToTop);
1704
+ if (shouldClose) {
1484
1705
  handleClose();
1485
1706
  }
1707
+ resetTouchState();
1708
+ };
1709
+ const resetTouchState = () => {
1486
1710
  setTouchStart(null);
1487
1711
  setTouchEnd(null);
1712
+ setTouchStartX(null);
1713
+ setIsSwipeGesture(false);
1714
+ isDraggingFromHandle.current = false;
1488
1715
  };
1716
+ // Handle Android back button
1717
+ React.useEffect(() => {
1718
+ if (!isOpen || !isAndroid())
1719
+ return;
1720
+ const handlePopState = (e) => {
1721
+ e.preventDefault();
1722
+ handleClose();
1723
+ };
1724
+ // Push a dummy state so back button can be caught
1725
+ window.history.pushState({ bottomSheet: true }, "");
1726
+ window.addEventListener("popstate", handlePopState);
1727
+ return () => {
1728
+ window.removeEventListener("popstate", handlePopState);
1729
+ // Clean up the dummy state if still present
1730
+ if (window.history.state?.bottomSheet) {
1731
+ window.history.back();
1732
+ }
1733
+ };
1734
+ }, [isOpen, handleClose]);
1489
1735
  // Don't render if not open and not closing
1490
1736
  if (!isOpen && !isClosing)
1491
1737
  return null;
1492
1738
  const hasHeader = title || description;
1493
- return (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: [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" }), 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" && (jsx("div", { className: "flex justify-center pt-3 pb-2", children: jsx("div", { className: "w-10 h-1 bg-neutral-300 rounded-full" }) })), hasHeader && (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: [jsxs("div", { className: "flex-1", children: [title && (jsx(Text, { as: "h2", variant: "body", size: "large", weight: "semibold", color: "default", children: title })), description && (jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", color: "subtle", className: "mt-1", children: description }))] }), showCloseButton && onClose && (jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet", className: "shrink-0" }))] })), !hasHeader && showCloseButton && onClose && (jsx("div", { className: cn("absolute z-10", variant === "default" && "top-4 right-4"), children: jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet" }) })), 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 && (jsxs("div", { className: "flex flex-col", children: [variant === "default" && (jsx(Divider, { thickness: "thin", variant: "muted" })), jsx("div", { className: cn("flex items-center justify-end gap-3", variant === "default" && "px-6 py-4", footerClassName), children: footer })] }))] })] }));
1739
+ // Calculate dynamic max height based on keyboard
1740
+ const dynamicMaxHeight = keyboardHeight > 0
1741
+ ? `calc(${viewportHeight}px - ${safeAreaBottom}px - 20px)`
1742
+ : maxHeight;
1743
+ return (jsxs("div", { className: cn("fixed inset-0 z-9999 flex items-end justify-center",
1744
+ // Use visual viewport height on mobile when keyboard is open
1745
+ keyboardHeight > 0 && "items-start pt-[env(safe-area-inset-top)]", className), style: {
1746
+ // Use visual viewport height when keyboard is open
1747
+ height: keyboardHeight > 0 ? `${viewportHeight}px` : "100%",
1748
+ }, role: "dialog", "aria-modal": "true", "aria-label": ariaLabel || title, "aria-describedby": ariaDescribedBy, children: [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" }), 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",
1749
+ // Ensure bottom sheet is at the bottom when keyboard is open
1750
+ keyboardHeight > 0 && "mt-auto", contentClassName), style: {
1751
+ maxHeight: dynamicMaxHeight,
1752
+ // Add safe area padding at the bottom for devices with home indicator
1753
+ paddingBottom: footer ? 0 : `max(env(safe-area-inset-bottom, 0px), ${safeAreaBottom}px)`,
1754
+ }, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, children: [showDragHandle && variant === "default" && (jsx("div", { ref: dragHandleRef, className: "flex justify-center pt-3 pb-2 cursor-grab active:cursor-grabbing touch-none", children: jsx("div", { className: "w-10 h-1 bg-neutral-300 rounded-full" }) })), hasHeader && (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: [jsxs("div", { className: "flex-1 min-w-0", children: [title && (jsx(Text, { as: "h2", variant: "body", size: "large", weight: "semibold", color: "default", children: title })), description && (jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", color: "subtle", className: "mt-1", children: description }))] }), showCloseButton && onClose && (jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet", className: "shrink-0" }))] })), !hasHeader && showCloseButton && onClose && (jsx("div", { className: cn("absolute z-10", variant === "default" && "top-4 right-4"), children: jsx(IconButton, { icon: "close", onClick: handleClose, color: "neutral", size: "small", "aria-label": "Close bottom sheet" }) })), jsx("div", { ref: bodyRef, className: cn("flex-1 overflow-y-auto overscroll-contain",
1755
+ // Smooth scrolling and momentum scroll for iOS
1756
+ "-webkit-overflow-scrolling-touch", 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 && (jsxs("div", { className: "flex flex-col shrink-0", style: {
1757
+ // Add safe area padding to footer
1758
+ paddingBottom: `max(env(safe-area-inset-bottom, 0px), ${safeAreaBottom}px)`,
1759
+ }, children: [variant === "default" && (jsx(Divider, { thickness: "thin", variant: "muted" })), jsx("div", { className: cn("flex items-center justify-end gap-3", variant === "default" && "px-6 py-4", footerClassName), children: footer })] }))] })] }));
1494
1760
  });
1495
1761
  BottomSheet.displayName = "BottomSheet";
1496
1762
 
@@ -4602,7 +4868,7 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
4602
4868
  ? true
4603
4869
  : false, footerLayout: footerLayout, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: dropdownClassName, width: isMobile ? "full" : (dropdownWidth === "full" ? "full" : "auto"), maxHeight: `${position.maxHeight}px`, unstyled: isMobile }));
4604
4870
  // Mobile: BottomSheet, Desktop: Regular Dropdown
4605
- const dropdownMenu = showDropdown && (isMobile ? (jsxs(BottomSheet, { isOpen: isOpen, onClose: () => setIsOpen(false), title: sectionHeading, variant: "default", showDragHandle: true, closeOnOverlayClick: true, closeOnEscape: true, closeOnSwipeDown: true, children: [jsx("div", { className: "mb-4", children: jsx(TextField, { value: searchValue, onChange: handleSearchChange, onKeyDown: handleKeyDown, containerClassName: "mb-0", placeholder: textFieldProps.placeholder || "Search...", autoFocus: true, ...textFieldProps }) }), renderDropdownContent()] })) : (jsx("div", { ref: menuRef, style: {
4871
+ const dropdownMenu = showDropdown && (isMobile ? (jsxs(BottomSheet, { isOpen: isOpen, onClose: () => setIsOpen(false), title: sectionHeading, variant: "default", showDragHandle: true, closeOnOverlayClick: true, closeOnEscape: true, closeOnSwipeDown: true, adjustForKeyboard: true, scrollToFocusedInput: true, maxHeight: "85vh", children: [jsx("div", { className: "mb-4 sticky top-0 bg-white z-10 -mx-6 px-6 pt-2 pb-4 -mt-4", children: jsx(TextField, { value: searchValue, onChange: handleSearchChange, onKeyDown: handleKeyDown, containerClassName: "mb-0", placeholder: textFieldProps.placeholder || "Search...", autoFocus: true, ...textFieldProps }) }), renderDropdownContent()] })) : (jsx("div", { ref: menuRef, style: {
4606
4872
  position: "fixed",
4607
4873
  ...(position.top !== undefined && { top: `${position.top}px` }),
4608
4874
  ...(position.bottom !== undefined && {