{"version":3,"file":"sidebar-root.cjs","sources":["../../../components/sidebar/sidebar-root.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'class-variance-authority';\nimport {\n  ComponentProps,\n  createContext,\n  MouseEvent,\n  ReactNode,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useRef,\n  useState\n} from 'react';\nimport { Tooltip } from '../tooltip';\nimport styles from './sidebar.module.css';\n\nexport interface SidebarContextValue {\n  isCollapsed: boolean;\n  isPeeking: boolean;\n  open: boolean;\n  setOpen: (open: boolean) => void;\n  /** Mirrors the `collapsible` prop; `'none'` means it cannot be collapsed. */\n  collapsible: 'icon' | 'hidden' | 'none';\n  position: 'left' | 'right';\n  hideItemTooltips?: boolean;\n  /** id of the sidebar element, for `aria-controls` on toggle controls. */\n  sidebarId?: string;\n}\n\nexport const SidebarContext = createContext<SidebarContextValue | null>(null);\n\nexport function useSidebar(): SidebarContextValue {\n  const context = useContext(SidebarContext);\n  if (context === null) {\n    throw new Error('useSidebar must be used inside of a <Sidebar> provider');\n  }\n  return context;\n}\n\n// Subcomponents rendered outside a <Sidebar> (stories, isolated tests,\n// standalone reuse of an item) worked before the context became nullable,\n// so they fall back to an expanded left sidebar instead of throwing.\n// Internal only — consumers get the throwing hook above.\nconst FALLBACK_CONTEXT: SidebarContextValue = {\n  isCollapsed: false,\n  isPeeking: false,\n  open: true,\n  setOpen: () => undefined,\n  collapsible: 'icon',\n  position: 'left',\n  hideItemTooltips: undefined\n};\n\nexport function useSidebarSafe(): SidebarContextValue {\n  return useContext(SidebarContext) ?? FALLBACK_CONTEXT;\n}\n\n// Menus and tooltips inside the sidebar portal their popups outside the\n// <aside>, so moving the pointer into one fires mouseleave on the sidebar.\n// Popups report their open state here so an active popup holds a hover\n// peek open instead of collapsing it mid-use.\nexport const SidebarPopupContext = createContext<(open: boolean) => void>(\n  () => undefined\n);\n\nexport interface SidebarRootProps extends ComponentProps<'aside'> {\n  position?: 'left' | 'right';\n  variant?: 'plain' | 'floating' | 'inset';\n  /**\n   * Hide the tooltips the Sidebar adds to items: the label tooltip when\n   * collapsed, and the full-text tooltip on clipped labels when expanded.\n   */\n  hideItemTooltips?: boolean;\n  /**\n   * Whether the user can collapse the sidebar, and what the collapsed\n   * state looks like. Expanding always works the same: the sidebar opens\n   * in place and pushes content.\n   * - `'icon'`: collapses to an icon rail (default).\n   * - `'hidden'`: collapses to a thin strip with no visible content.\n   * - `'none'`: the sidebar cannot be collapsed; the resize handle is hidden.\n   *\n   * Unlike `collapsible` on `Sidebar.Group`, which renders a group\n   * as an accordion.\n   */\n  collapsible?: 'icon' | 'hidden' | 'none';\n  /**\n   * Hovering a collapsed sidebar temporarily reveals it as an overlay above\n   * the content, without changing the real open state. Reverts on mouse leave.\n   */\n  peekOnHover?: boolean;\n  /**\n   * Delay in milliseconds before a hover starts a peek.\n   * Only applies when `peekOnHover` is set.\n   * @default 100\n   */\n  peekDelay?: number;\n  /** Tooltip shown when hovering the collapse/expand handle. */\n  collapseTooltip?: ReactNode;\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n}\n\nconst DEFAULT_PEEK_DELAY = 100;\n\nexport function SidebarRoot({\n  className,\n  id: providedId,\n  position = 'left',\n  variant = 'plain',\n  open: providedOpen,\n  onOpenChange,\n  onMouseEnter,\n  onMouseLeave,\n  hideItemTooltips,\n  collapsible = 'icon',\n  peekOnHover = false,\n  peekDelay = DEFAULT_PEEK_DELAY,\n  collapseTooltip,\n  defaultOpen = true,\n  children,\n  ...props\n}: SidebarRootProps) {\n  const generatedId = useId();\n  const sidebarId = providedId ?? generatedId;\n  const [internalOpen, setInternalOpen] = useState(defaultOpen);\n  const open = providedOpen ?? internalOpen;\n  const [isPeeking, setIsPeeking] = useState(false);\n  const peekTimeoutRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n  const openPopupCountRef = useRef(0);\n  const pointerInsideRef = useRef(false);\n\n  const handleOpenChange = useCallback(\n    (value: boolean) => {\n      setInternalOpen(value);\n      onOpenChange?.(value);\n    },\n    [onOpenChange]\n  );\n\n  const canCollapse = collapsible !== 'none';\n  const canPeek = peekOnHover && canCollapse && !open;\n\n  const handleMouseEnter = useCallback(\n    (event: MouseEvent<HTMLElement>) => {\n      onMouseEnter?.(event);\n      pointerInsideRef.current = true;\n      if (!canPeek) return;\n      peekTimeoutRef.current = setTimeout(() => setIsPeeking(true), peekDelay);\n    },\n    [canPeek, onMouseEnter, peekDelay]\n  );\n\n  const handleMouseLeave = useCallback(\n    (event: MouseEvent<HTMLElement>) => {\n      onMouseLeave?.(event);\n      pointerInsideRef.current = false;\n      clearTimeout(peekTimeoutRef.current);\n      // The pointer may have moved into a portaled popup (a More menu, a\n      // truncation tooltip) that visually belongs to the sidebar; hold the\n      // peek until the popup closes.\n      if (openPopupCountRef.current === 0) setIsPeeking(false);\n    },\n    [onMouseLeave]\n  );\n\n  const handlePopupOpenChange = useCallback((popupOpen: boolean) => {\n    openPopupCountRef.current = Math.max(\n      0,\n      openPopupCountRef.current + (popupOpen ? 1 : -1)\n    );\n    if (openPopupCountRef.current === 0 && !pointerInsideRef.current) {\n      setIsPeeking(false);\n    }\n  }, []);\n\n  useEffect(() => () => clearTimeout(peekTimeoutRef.current), []);\n\n  // Opening for real supersedes a peek. Without this, pinning the sidebar\n  // open while peeking (or during the peek delay) leaves it stuck as a\n  // fixed overlay until the mouse happens to leave.\n  useEffect(() => {\n    if (!open) return;\n    clearTimeout(peekTimeoutRef.current);\n    setIsPeeking(false);\n  }, [open]);\n\n  // data-open/data-closed drive the visuals, so a peek counts as open —\n  // every collapse-hiding CSS rule turns off during a peek for free. The\n  // real state stays in `open` (and the toggle controls' aria-expanded).\n  const visualOpen = open || isPeeking;\n\n  return (\n    <SidebarContext\n      value={{\n        isCollapsed: !open && !isPeeking,\n        isPeeking,\n        open,\n        setOpen: handleOpenChange,\n        collapsible,\n        position,\n        hideItemTooltips,\n        sidebarId\n      }}\n    >\n      <SidebarPopupContext value={handlePopupOpenChange}>\n        <aside\n          id={sidebarId}\n          className={cx(styles.root, className)}\n          data-position={position}\n          data-variant={variant}\n          data-open={visualOpen ? '' : undefined}\n          data-closed={!visualOpen ? '' : undefined}\n          data-collapse-mode={collapsible}\n          data-peeking={isPeeking ? '' : undefined}\n          data-slot='sidebar'\n          onMouseEnter={handleMouseEnter}\n          onMouseLeave={handleMouseLeave}\n          aria-label='Navigation Sidebar'\n          role='navigation'\n          {...props}\n        >\n          {canCollapse && (\n            <Tooltip trackCursorAxis='y'>\n              <Tooltip.Trigger\n                render={\n                  <button\n                    type='button'\n                    className={styles['resize-handle']}\n                    data-slot='sidebar-toggle'\n                    onClick={() => handleOpenChange(!open)}\n                    aria-label={open ? 'Collapse sidebar' : 'Expand sidebar'}\n                    aria-expanded={open}\n                    aria-controls={sidebarId}\n                  />\n                }\n              />\n              <Tooltip.Content\n                side={position === 'left' ? 'right' : 'left'}\n                sideOffset={10}\n              >\n                {collapseTooltip ??\n                  (open ? 'Click to collapse' : 'Click to expand')}\n              </Tooltip.Content>\n            </Tooltip>\n          )}\n          {children}\n        </aside>\n      </SidebarPopupContext>\n    </SidebarContext>\n  );\n}\n\nSidebarRoot.displayName = 'Sidebar';\n"],"names":[],"mappings":";;;;;;;;;;;AAkCE;AACA;AACE;;AAEF;AACF;AAEA;AACA;AACA;AACA;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;;;AAIA;AACF;AAEA;AACA;AACA;AACA;AACa;AA0Cb;AAEM;AAkBJ;AACA;;AAEA;;AAEA;AACA;AACA;AAEA;;AAGI;AACF;AAIF;;AAGA;AAEI;AACA;AACA;;AACA;;AAKJ;AAEI;AACA;AACA;;;;AAIA;;AACF;AAIF;;;;;;AAUA;;;;;AAME;;AACA;;AAEF;;;;AAKA;AAEA;AAGM;;;AAGA;;;;;AAKD;AAuCW;AASlB;AAEA;;;;;;"}