{"version":3,"file":"navbar-root.cjs","sources":["../../../components/navbar/navbar-root.tsx"],"sourcesContent":["'use client';\n\nimport { useMergedRefs } from '@base-ui/utils/useMergedRefs';\nimport { cx } from 'class-variance-authority';\nimport { type ComponentProps, type RefObject, useEffect, useRef } from 'react';\nimport styles from './navbar.module.css';\n\n/** Px to accumulate in one scroll direction before hiding (down) or showing (up) the navbar */\nconst SCROLL_THRESHOLD = 5;\n/** When scroll position is at or above this from top, always show the navbar */\nconst SHOW_AT_TOP_THRESHOLD = 20;\n\n/**\n * Finds the nearest scrollable ancestor of el.\n * Returns null if none found (so the window is the scroll container).\n */\nfunction getScrollParent(el: HTMLElement | null): HTMLElement | null {\n  if (!el) return null;\n  let parent = el.parentElement;\n  while (parent) {\n    const { overflowY, overflow } = getComputedStyle(parent);\n    const scrollable =\n      overflowY === 'auto' ||\n      overflowY === 'scroll' ||\n      overflowY === 'overlay' ||\n      overflow === 'auto' ||\n      overflow === 'scroll' ||\n      overflow === 'overlay';\n    if (scrollable && parent.scrollHeight > parent.clientHeight) return parent;\n    parent = parent.parentElement;\n  }\n  return null;\n}\n\nexport interface NavbarRootProps extends ComponentProps<'nav'> {\n  /** Stick navbar to top of viewport (or scroll container) while scrolling */\n  sticky?: boolean;\n  /** Hide navbar when scrolling down, show when scrolling up or near top */\n  hideOnScroll?: boolean;\n  /** Ref to the scroll container. When provided (and current is set), used for hide-on-scroll instead of auto-detection. Otherwise the nearest scrollable ancestor or window is used. */\n  scrollContainerRef?: RefObject<HTMLElement | null>;\n}\n\nexport const NavbarRoot = ({\n  className,\n  sticky = false,\n  hideOnScroll = false,\n  scrollContainerRef,\n  children,\n  ref,\n  ...props\n}: NavbarRootProps) => {\n  // Last scroll position at which we updated data-hidden; only updated when we show/hide or are at top\n  const lastScrollY = useRef(0);\n  // Nav DOM node; used to find scroll container, attach listeners, and set data-hidden\n  const navRef = useRef<HTMLElement>(null);\n\n  useEffect(() => {\n    if (!hideOnScroll) return;\n\n    const el = navRef.current;\n    if (!el) return;\n\n    // Use custom ref if provided and set; else detect scroll parent; else window\n    const scrollContainer: HTMLElement | null =\n      scrollContainerRef?.current ?? getScrollParent(el);\n    const isWindow = scrollContainer === null;\n\n    if (isWindow && typeof window === 'undefined') return;\n\n    const target: Window | HTMLElement = isWindow\n      ? window\n      : (scrollContainer as HTMLElement);\n\n    // Throttle scroll work to one update per frame\n    let ticking = false;\n    const getScrollTop = () =>\n      target === window\n        ? (window.scrollY ?? document.documentElement.scrollTop)\n        : (target as HTMLElement).scrollTop;\n\n    const handleScroll = () => {\n      if (ticking) return;\n      ticking = true;\n      requestAnimationFrame(() => {\n        const scrollY = getScrollTop();\n        const nav = navRef.current;\n        if (!nav) {\n          ticking = false;\n          return;\n        }\n\n        if (scrollY <= SHOW_AT_TOP_THRESHOLD) {\n          nav.setAttribute('data-hidden', 'false');\n          lastScrollY.current = scrollY;\n        } else if (scrollY > lastScrollY.current + SCROLL_THRESHOLD) {\n          nav.setAttribute('data-hidden', 'true');\n          lastScrollY.current = scrollY;\n        } else if (scrollY < lastScrollY.current - SCROLL_THRESHOLD) {\n          nav.setAttribute('data-hidden', 'false');\n          lastScrollY.current = scrollY;\n        }\n        ticking = false;\n      });\n    };\n\n    lastScrollY.current = getScrollTop();\n    el.setAttribute('data-hidden', 'false');\n    target.addEventListener('scroll', handleScroll, { passive: true });\n    return () => {\n      target.removeEventListener('scroll', handleScroll);\n      el.removeAttribute('data-hidden');\n    };\n  }, [hideOnScroll]);\n\n  const mergedRef = useMergedRefs(navRef, ref);\n\n  return (\n    <nav\n      ref={mergedRef}\n      className={cx(styles.root, className)}\n      data-sticky={sticky}\n      role='navigation'\n      data-slot='navbar'\n      {...props}\n    >\n      <div className={styles.container} data-slot='navbar-container'>\n        {children}\n      </div>\n    </nav>\n  );\n};\n\nNavbarRoot.displayName = 'Navbar';\n"],"names":[],"mappings":";;;;;;;;;AAOA;AACA;AACA;AACA;AAEA;;;AAGG;AACH;AACE;AAAS;AACT;;;AAGE;AAEE;AACA;AACA;AACA;;;AAE2D;AAC7D;;AAEF;AACF;AAWa;;AAUX;;AAEA;;AAGE;;AAEA;AACA;;;;AAKA;AAEA;;;AAGE;;;;AAKF;;AAGI;;AAGF;;;;AAGE;AACA;;;;;AAMA;AACE;AACA;;;AAEA;AACA;;;AAEA;AACA;;;AAGJ;AACF;AAEA;AACA;AACA;AACA;AACE;AACA;AACF;AACF;;AAIA;AAcF;AAEA;;"}