{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-GHMTJJCC.cjs","../src/components/navigation/sticky-section-nav.tsx","../src/components/navigation/multi-level-navigation.tsx"],"names":["jsx","DEFAULT_FOLDER_INDEX_FILE"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-GHMTJJCC.cjs","sourcesContent":[null,"'use client';\n\nimport type React from 'react';\nimport { useEffect, useState, useCallback, useRef } from 'react';\nimport { cn } from '../../utils';\nimport { scrollElementIntoView } from '../../utils/scroll-into-view';\n\nexport interface StickyNavSection {\n  id: string;\n  label: string;\n}\n\ninterface StickySectionNavProps {\n  sections: StickyNavSection[];\n  activeSection: string;\n  onSectionClick: (sectionId: string) => void;\n  className?: string;\n  ribbonPosition?: 'left' | 'right';\n  ribbonColor?: string;\n}\n\n/**\n * Reusable sticky navigation component for section-based navigation\n * Used in vendor detail pages, knowledge base, documentation, etc.\n */\nexport function StickySectionNav({\n  sections,\n  activeSection,\n  onSectionClick,\n  className,\n  ribbonPosition = 'left',\n  ribbonColor = 'var(--color-accent-primary)',\n}: StickySectionNavProps) {\n  const navHeight = sections.length * 40; // 40px per item (h-10)\n\n  return (\n    <nav className={cn('relative bg-ods-bg', className)}>\n      {/* Background gray vertical line for all nav items */}\n      <div\n        className=\"absolute bg-ods-border\"\n        style={{\n          width: '1px',\n          height: `${navHeight}px`,\n          [ribbonPosition === 'left' ? 'left' : 'right']: '-2px',\n          top: '0px',\n        }}\n      />\n\n      {sections.map(section => (\n        <div key={section.id} className=\"relative flex h-10 w-full items-stretch transition-all duration-200\">\n          {/* Yellow ribbon for active state */}\n          {activeSection === section.id && (\n            <div\n              className=\"absolute z-10 transition-all duration-200\"\n              style={{\n                backgroundColor: ribbonColor,\n                width: '4px',\n                height: '24px',\n                [ribbonPosition === 'left' ? 'left' : 'right']: '-2px',\n                top: '8px',\n                borderRadius: '2px',\n              }}\n            />\n          )}\n\n          {/* Navigation button */}\n          <button\n            onClick={() => onSectionClick(section.id)}\n            className=\"relative flex flex-1 cursor-pointer items-center gap-2 px-3 py-2\"\n          >\n            <span\n              className={cn(\n                'text-left transition-all duration-200 text-h6',\n                activeSection === section.id\n                  ? 'text-ods-text-primary'\n                  : 'text-ods-text-secondary hover:text-ods-text-primary',\n              )}\n            >\n              {section.label}\n            </span>\n          </button>\n        </div>\n      ))}\n    </nav>\n  );\n}\n\n/**\n * SIMPLEST POSSIBLE IMPLEMENTATION - Just make it work\n */\nexport function useSectionNavigation(\n  sections: { id: string; ref: React.RefObject<HTMLElement> }[],\n  options?: {\n    offset?: number;\n  },\n) {\n  const [activeSection, setActiveSection] = useState(sections[0]?.id || '');\n  const isScrollingFromClick = useRef(false);\n  const { offset = 100 } = options || {};\n\n  // Handle click - scroll to the element via the canonical helper.\n  // The `offset` prop maps to `headerOffset` (sticky chrome above the\n  // section nav); same smooth-scroll mechanics every other anchor\n  // surface in the app uses.\n  const handleSectionClick = useCallback(\n    (sectionId: string) => {\n      const targetElement = document.getElementById(sectionId);\n      if (!targetElement) return;\n\n      // Prevent scroll spy while we're scrolling\n      isScrollingFromClick.current = true;\n      setActiveSection(sectionId);\n\n      scrollElementIntoView(targetElement, { headerOffset: offset });\n\n      // Allow scroll spy again after scroll completes\n      setTimeout(() => {\n        isScrollingFromClick.current = false;\n      }, 500);\n    },\n    [offset],\n  );\n\n  // Make sure elements have IDs\n  useEffect(() => {\n    sections.forEach(section => {\n      const el = section.ref.current;\n      if (el && !el.id) {\n        el.id = section.id;\n      }\n    });\n  }, [sections]);\n\n  // Simple scroll spy\n  useEffect(() => {\n    const handleScroll = () => {\n      if (isScrollingFromClick.current) return;\n\n      const scrollPosition = window.scrollY + offset + 50;\n\n      // Find which section we're in\n      let currentSection = sections[0]?.id || '';\n\n      for (let i = sections.length - 1; i >= 0; i--) {\n        const element = document.getElementById(sections[i].id);\n        if (element && scrollPosition >= element.offsetTop) {\n          currentSection = sections[i].id;\n          break;\n        }\n      }\n\n      setActiveSection(currentSection);\n    };\n\n    // Throttle the scroll handler\n    let scrollTimer: NodeJS.Timeout;\n    const throttledScroll = () => {\n      clearTimeout(scrollTimer);\n      scrollTimer = setTimeout(handleScroll, 100);\n    };\n\n    window.addEventListener('scroll', throttledScroll);\n    handleScroll(); // Check initial position\n\n    return () => {\n      window.removeEventListener('scroll', throttledScroll);\n      clearTimeout(scrollTimer);\n    };\n  }, [sections, offset]);\n\n  return {\n    activeSection,\n    handleSectionClick,\n  };\n}\n","'use client';\n\nimport { ChevronDown, ChevronUp, FileText, Folder, FolderOpen } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { DEFAULT_FOLDER_INDEX_FILE as CANONICAL_FOLDER_INDEX_FILE } from '../../utils/doc-tree-nav';\n\nexport interface NavigationNode {\n  id: string;\n  name: string;\n  path: string;\n  type: 'file' | 'folder';\n  hasReadme?: boolean;\n  children?: NavigationNode[];\n  slug?: string;\n  // Mirror DocNode's extra optional fields so a DocNode tree is structurally\n  // assignable to NavigationNode[] without an `as` cast. NavigationNode\n  // intentionally never reads these — they're carried for type-compat only.\n  documentType?: 'markdown' | 'pdf' | 'google_sheet' | 'figma' | 'file';\n  sortOrder?: number;\n}\n\ninterface MultiLevelNavigationProps {\n  nodes: NavigationNode[];\n  selectedPath: string;\n  expandedNodes: Set<string>;\n  onNodeClick: (node: NavigationNode) => void;\n  onToggleExpand?: (nodeId: string) => void;\n  isLoading?: boolean;\n  className?: string;\n  /** Folder-index filename (default `'README.md'`, case-insensitive). When the\n   *  selectedPath is a folder with this file, the visual ribbon moves to the\n   *  child file. */\n  folderIndexFile?: string;\n}\n\n// SSOT lives in `doc-tree-nav` (canonical case `'README.md'`). The visual\n// comparator below lowercases both sides, so re-exporting the canonical\n// constant under the same local name preserves call-site syntax without\n// drifting from the SSOT.\nconst DEFAULT_FOLDER_INDEX_FILE = CANONICAL_FOLDER_INDEX_FILE;\n\n/**\n * Compute the visual \"selected\" state for a navigation node.\n *\n * Folder-with-README convention: when `selectedPath` is a folder that has a\n * README, the FOLDER doesn't get the ribbon — its README child does. The folder\n * is just a container; the README is the actual document being viewed.\n *\n * Mirror case: a README file whose parent folder is the `selectedPath` gets the\n * visual selection ribbon (implicit). This handles both the auto-select-first-\n * folder landing path AND the user-clicks-folder case.\n */\nfunction isNodeVisuallySelected(\n  node: NavigationNode,\n  selectedPath: string,\n  folderIndexFile: string = DEFAULT_FOLDER_INDEX_FILE,\n): boolean {\n  const FOLDER_INDEX_FILE = folderIndexFile.toLowerCase();\n  // Path comparisons keyed on `node.path` (raw, includes `.md`). Do NOT use\n  // `node.name` — the tree-builder runs it through `formatDocName` which strips\n  // the `.md` extension, so `node.name.toLowerCase() === 'readme.md'` is always\n  // false.\n  if (selectedPath === node.path) {\n    // Explicit match — but folder-with-README defers to its README child.\n    return !(node.type === 'folder' && node.hasReadme);\n  }\n  if (node.type !== 'file') return false;\n  const pathLower = node.path.toLowerCase();\n  // Implicit: README child whose parent folder is the selectedPath.\n  if (selectedPath !== '' && pathLower === `${selectedPath.toLowerCase()}/${FOLDER_INDEX_FILE}`) {\n    return true;\n  }\n  // Implicit: ROOT README on the landing page (selectedPath === '' means\n  // \"no explicit doc selected\" — the viewer falls back to the root folder-\n  // index file, so that file IS the active document).\n  if (selectedPath === '' && pathLower === FOLDER_INDEX_FILE) {\n    return true;\n  }\n  return false;\n}\n\nexport function MultiLevelNavigation({\n  nodes,\n  selectedPath,\n  expandedNodes,\n  onNodeClick,\n  onToggleExpand,\n  isLoading,\n  className,\n  folderIndexFile = DEFAULT_FOLDER_INDEX_FILE,\n}: MultiLevelNavigationProps) {\n  if (isLoading) {\n    return (\n      <div className={cn('space-y-2', className)}>\n        {[1, 2, 3, 4].map(i => (\n          <div key={i} className=\"h-12 animate-pulse rounded-lg bg-ods-skeleton p-3\" />\n        ))}\n      </div>\n    );\n  }\n\n  return (\n    <div className={cn('space-y-2', className)} role=\"list\" aria-label=\"Navigation list\">\n      {nodes.map(node => (\n        <NavigationItem\n          key={node.id}\n          node={node}\n          selectedPath={selectedPath}\n          expandedNodes={expandedNodes}\n          onNodeClick={onNodeClick}\n          onToggleExpand={onToggleExpand}\n          level={0}\n          folderIndexFile={folderIndexFile}\n        />\n      ))}\n    </div>\n  );\n}\n\ninterface NavigationItemProps {\n  node: NavigationNode;\n  selectedPath: string;\n  expandedNodes: Set<string>;\n  onNodeClick: (node: NavigationNode) => void;\n  onToggleExpand?: (nodeId: string) => void;\n  level: number;\n  folderIndexFile: string;\n}\n\nfunction NavigationItem({\n  node,\n  selectedPath,\n  expandedNodes,\n  onNodeClick,\n  onToggleExpand,\n  level,\n  folderIndexFile,\n}: NavigationItemProps) {\n  const isExpanded = expandedNodes.has(node.id);\n  const isSelected = isNodeVisuallySelected(node, selectedPath, folderIndexFile);\n  // Bind the (optional) child list once so the render below maps a real array\n  // rather than re-deriving `hasChildren` and asserting it away.\n  const children = node.children ?? [];\n  const hasChildren = children.length > 0;\n\n  return (\n    <div className=\"space-y-1\" role=\"listitem\">\n      <div className={cn('px-2', level > 0 && 'ml-4')}>\n        <div\n          className={cn(\n            'relative w-full rounded-lg border transition-all duration-150',\n            isSelected ? 'border-ods-border bg-ods-border' : 'border-ods-border bg-ods-card',\n          )}\n        >\n          <div className=\"relative flex items-center\">\n            <button\n              className={cn(\n                'flex h-12 w-full items-center rounded-lg px-2 text-ods-text-primary transition-all duration-150 text-h6',\n                !isSelected && 'hover:bg-ods-bg-hover',\n                hasChildren && 'pr-12',\n              )}\n              onClick={() => onNodeClick(node)}\n              aria-label={`${isSelected ? 'Selected' : 'Select'} ${node.name}`}\n            >\n              <span className=\"mr-2 flex-shrink-0\">\n                {node.type === 'folder' ? (\n                  isExpanded ? (\n                    <FolderOpen className=\"h-4 w-4\" />\n                  ) : (\n                    <Folder className=\"h-4 w-4\" />\n                  )\n                ) : (\n                  <FileText className=\"h-4 w-4\" />\n                )}\n              </span>\n              <span className=\"min-w-0 flex-1 truncate text-left\">\n                {node.name.endsWith('.md') ? node.name.replace('.md', '') : node.name}\n              </span>\n              {node.type === 'folder' && node.hasReadme && (\n                <span className=\"mr-2 rounded bg-ods-bg-surface px-1.5 py-0.5 text-ods-text-secondary text-h6\">\n                  README\n                </span>\n              )}\n            </button>\n\n            {hasChildren && (\n              <button\n                className=\"absolute right-0 top-0 flex h-12 w-12 items-center justify-center text-ods-text-secondary transition-colors duration-150 hover:text-ods-text-primary\"\n                onClick={e => {\n                  e.stopPropagation();\n                  if (onToggleExpand) {\n                    onToggleExpand(node.id);\n                  } else {\n                    onNodeClick(node);\n                  }\n                }}\n                aria-label={`${isExpanded ? 'Collapse' : 'Expand'} ${node.name}`}\n              >\n                {isExpanded ? <ChevronUp className=\"h-4 w-4\" /> : <ChevronDown className=\"h-4 w-4\" />}\n              </button>\n            )}\n          </div>\n\n          {isSelected && (\n            <div className=\"absolute right-0 top-1/2 h-[calc(100%-8px)] w-1 -translate-y-1/2 rounded-l bg-ods-accent\" />\n          )}\n        </div>\n      </div>\n\n      {hasChildren && isExpanded && (\n        <div className=\"space-y-1\">\n          {children.map(child => (\n            <NavigationItem\n              key={child.id}\n              node={child}\n              selectedPath={selectedPath}\n              expandedNodes={expandedNodes}\n              onNodeClick={onNodeClick}\n              onToggleExpand={onToggleExpand}\n              level={level + 1}\n              folderIndexFile={folderIndexFile}\n            />\n          ))}\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport function MobileNavigationDropdown({\n  nodes,\n  selectedPath,\n  expandedNodes,\n  onNodeClick,\n  onToggleExpand,\n  isLoading,\n  className,\n  folderIndexFile = DEFAULT_FOLDER_INDEX_FILE,\n}: MultiLevelNavigationProps) {\n  if (isLoading) {\n    return (\n      <div className={cn('space-y-2', className)}>\n        {[1, 2, 3].map(i => (\n          <div key={i} className=\"h-10 animate-pulse rounded-lg bg-ods-skeleton p-3\" />\n        ))}\n      </div>\n    );\n  }\n\n  return (\n    <div className={cn('space-y-2', className)}>\n      {nodes.map(node => (\n        <MobileNavigationItem\n          key={node.id}\n          node={node}\n          selectedPath={selectedPath}\n          expandedNodes={expandedNodes}\n          onNodeClick={onNodeClick}\n          onToggleExpand={onToggleExpand}\n          level={0}\n          folderIndexFile={folderIndexFile}\n        />\n      ))}\n    </div>\n  );\n}\n\nfunction MobileNavigationItem({\n  node,\n  selectedPath,\n  expandedNodes,\n  onNodeClick,\n  onToggleExpand,\n  level,\n  folderIndexFile,\n}: NavigationItemProps) {\n  const isExpanded = expandedNodes.has(node.id);\n  const isSelected = isNodeVisuallySelected(node, selectedPath, folderIndexFile);\n  // Same binding as the desktop item above — a real array to map, no assertion.\n  const children = node.children ?? [];\n  const hasChildren = children.length > 0;\n\n  return (\n    <div className=\"space-y-1\">\n      <div className={cn('px-2', level > 0 && 'ml-3')}>\n        <div\n          className={cn(\n            'relative w-full rounded-lg border transition-all duration-150',\n            isSelected ? 'border-ods-border bg-ods-border' : 'border-ods-border bg-ods-card',\n          )}\n        >\n          <div className=\"relative flex items-center\">\n            <button\n              className={cn(\n                'flex h-11 w-full items-center rounded-lg px-2 text-ods-text-primary transition-all duration-150 text-h6',\n                !isSelected && 'hover:bg-ods-bg-hover',\n                hasChildren && 'pr-11',\n              )}\n              onClick={() => onNodeClick(node)}\n            >\n              <span className=\"mr-2 flex-shrink-0\">\n                {node.type === 'folder' ? (\n                  isExpanded ? (\n                    <FolderOpen className=\"h-4 w-4\" />\n                  ) : (\n                    <Folder className=\"h-4 w-4\" />\n                  )\n                ) : (\n                  <FileText className=\"h-4 w-4\" />\n                )}\n              </span>\n              <span className=\"min-w-0 flex-1 truncate text-left\">\n                {node.name.endsWith('.md') ? node.name.replace('.md', '') : node.name}\n              </span>\n              {node.type === 'folder' && node.hasReadme && (\n                <span className=\"mr-2 rounded bg-ods-bg-surface px-1.5 py-0.5 text-ods-text-secondary text-h6\">\n                  README\n                </span>\n              )}\n            </button>\n\n            {hasChildren && (\n              <button\n                className=\"absolute right-0 top-0 flex h-11 w-11 items-center justify-center text-ods-text-secondary transition-colors duration-150 hover:text-ods-text-primary\"\n                onClick={e => {\n                  e.stopPropagation();\n                  if (onToggleExpand) {\n                    onToggleExpand(node.id);\n                  } else {\n                    onNodeClick(node);\n                  }\n                }}\n                aria-label={`${isExpanded ? 'Collapse' : 'Expand'} ${node.name}`}\n              >\n                {isExpanded ? <ChevronUp className=\"h-3.5 w-3.5\" /> : <ChevronDown className=\"h-3.5 w-3.5\" />}\n              </button>\n            )}\n          </div>\n\n          {isSelected && (\n            <div className=\"absolute right-0 top-1/2 h-[calc(100%-8px)] w-1 -translate-y-1/2 rounded-l bg-ods-accent\" />\n          )}\n        </div>\n      </div>\n\n      {hasChildren && isExpanded && (\n        <div className=\"space-y-1\">\n          {children.map(child => (\n            <MobileNavigationItem\n              key={child.id}\n              node={child}\n              selectedPath={selectedPath}\n              expandedNodes={expandedNodes}\n              onNodeClick={onNodeClick}\n              onToggleExpand={onToggleExpand}\n              level={level + 1}\n              folderIndexFile={folderIndexFile}\n            />\n          ))}\n        </div>\n      )}\n    </div>\n  );\n}\n"]}