{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "BranchedMenu-JS-TW",
	"title": "BranchedMenu",
	"description": "Collapsible menu that branches each section from a trunk and traces a line to your pick.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "BranchedMenu/BranchedMenu.jsx",
			"content": "'use client';\n\nimport { isValidElement, useLayoutEffect, useRef, useState } from 'react';\nimport { HugeiconsIcon } from '@hugeicons/react';\nimport {\n  CursorPointer01Icon,\n  Download04Icon,\n  Layers01Icon,\n  Notification03Icon,\n  PaintBoardIcon,\n  Rocket01Icon,\n  Settings02Icon,\n  TextFontIcon\n} from '@hugeicons/core-free-icons';\nconst DEFAULT_ITEMS = [\n  {\n    label: 'Getting started',\n    children: [\n      { value: 'install', label: 'Installation', icon: Download04Icon },\n      { value: 'quick', label: 'Quick start', icon: Rocket01Icon },\n      { value: 'config', label: 'Configuration', icon: Settings02Icon },\n      { value: 'theming', label: 'Theming', icon: PaintBoardIcon }\n    ]\n  },\n  {\n    label: 'Components',\n    children: [\n      { value: 'buttons', label: 'Buttons', icon: CursorPointer01Icon },\n      { value: 'typography', label: 'Typography', icon: TextFontIcon },\n      { value: 'overlays', label: 'Overlays', icon: Layers01Icon },\n      { value: 'toasts', label: 'Toasts', icon: Notification03Icon }\n    ]\n  }\n];\nconst PAD = 6;\nconst MARK = 16;\n\nconst renderIcon = icon => (isValidElement(icon) ? icon : <HugeiconsIcon icon={icon} size={16} strokeWidth={1.8} />);\nconst toSet = open => new Set(Array.isArray(open) ? open : open >= 0 ? [open] : []);\n\nexport default function BranchedMenu({\n  items = DEFAULT_ITEMS,\n  defaultOpen = 0,\n  defaultActive = '',\n  onSelect,\n  onToggle,\n  color = '#f5f5f5',\n  accentColor = '#f5f5f5',\n  lineColor = '#3f3f46',\n  width = 240,\n  rowHeight = 36,\n  indent = 40,\n  trunk = 14,\n  radius = 10,\n  lineWidth = 1.5,\n  fontSize = 14,\n  drawDuration = 400,\n  foldDuration = 300,\n  className = ''\n}) {\n  const [open, setOpen] = useState(() => toSet(defaultOpen));\n  const [active, setActive] = useState(() => {\n    if (defaultActive) return defaultActive;\n    const first = items.find((it, i) => it.children && toSet(defaultOpen).has(i));\n    return first?.children?.[0]?.value ?? '';\n  });\n  const navRef = useRef(null);\n  const heads = useRef([]);\n  const markerRef = useRef(null);\n  const latest = useRef({});\n  latest.current = { onSelect, onToggle };\n\n  const activeSection = items.findIndex(it => it.children?.some(kid => kid.value === active));\n  const markerShown = activeSection >= 0 && open.has(activeSection);\n  useLayoutEffect(() => {\n    const place = glide => {\n      const m = markerRef.current;\n      const el = heads.current[activeSection];\n      if (!m) return;\n      const on = markerShown && el;\n      if (!glide) m.style.transition = 'none';\n      if (on) m.style.top = `${el.offsetTop + (el.offsetHeight - MARK) / 2}px`;\n      m.toggleAttribute('data-on', Boolean(on));\n      if (!glide) {\n        void m.offsetHeight;\n        m.style.transition = '';\n      }\n    };\n    place(true);\n    let first = true;\n    const ro = new ResizeObserver(() => {\n      if (first) {\n        first = false;\n        return;\n      }\n      place(false);\n    });\n    if (navRef.current) ro.observe(navRef.current);\n    return () => ro.disconnect();\n  }, [activeSection, markerShown, items, fontSize, rowHeight]);\n\n  const select = (value, item) => {\n    setActive(value);\n    latest.current.onSelect?.(value, item);\n  };\n  const toggle = i => {\n    setOpen(prev => {\n      const next = new Set(prev);\n      const isOpen = !next.has(i);\n      if (isOpen) next.add(i);\n      else next.delete(i);\n      latest.current.onToggle?.(i, isOpen);\n      return next;\n    });\n  };\n\n  const r = Math.min(radius, rowHeight / 2 - 2);\n  const endX = indent - 8;\n  const rowY = k => PAD + k * rowHeight + rowHeight / 2;\n  const branch = k => `M ${trunk} ${rowY(k) - r} A ${r} ${r} 0 0 0 ${trunk + r} ${rowY(k)} H ${endX}`;\n  const reach = k => `M ${trunk} 0 V ${rowY(k) - r} A ${r} ${r} 0 0 0 ${trunk + r} ${rowY(k)} H ${endX}`;\n  const length = k => rowY(k) - r + (Math.PI * r) / 2 + (endX - trunk - r);\n\n  return (\n    <nav\n      ref={navRef}\n      className={`relative flex w-fit max-w-[min(var(--bm-w),100%)] flex-col pl-3.5 leading-[1.2] [color:var(--bm-ink)] [font-family:inherit] [font-size:var(--bm-font)] before:absolute before:top-2 before:bottom-0 before:left-0 before:w-0.5 before:rounded-[1px] before:[background:linear-gradient(to_bottom,var(--bm-line)_0%,var(--bm-line)_55%,transparent_100%)] before:content-['']${className ? ` ${className}` : ''}`}\n      style={{\n        '--bm-w': `${width}px`,\n        '--bm-ink': color,\n        '--bm-accent': accentColor,\n        '--bm-line': lineColor,\n        '--bm-font': `${fontSize}px`,\n        '--bm-row': `${rowHeight}px`,\n        '--bm-indent': `${indent}px`,\n        '--bm-line-w': lineWidth,\n        '--bm-draw': `${drawDuration}ms`,\n        '--bm-muted': `color-mix(in srgb, ${color} 55%, transparent)`,\n        '--bm-fold': `${foldDuration}ms`\n      }}\n    >\n      <span\n        ref={markerRef}\n        className=\"absolute -top-px left-0 z-[1] h-4 w-0.5 rounded-[1px] opacity-0 [background:var(--bm-accent)] [transition:top_220ms_cubic-bezier(0.23,1,0.32,1),opacity_150ms_ease] data-[on]:opacity-100 motion-reduce:[transition:opacity_150ms_ease]\"\n        aria-hidden=\"true\"\n      />\n      {items.map((item, i) => {\n        const kids = item.children;\n        const isOpen = kids ? open.has(i) : false;\n        const leafValue = item.value ?? item.label;\n        const leafActive = !kids && leafValue === active;\n        const bodyH = kids ? PAD * 2 + kids.length * rowHeight : 0;\n        return (\n          <div\n            key={item.value ?? item.label}\n            className=\"group/section flex flex-col\"\n            data-open={isOpen ? '' : undefined}\n          >\n            <button\n              ref={el => {\n                heads.current[i] = el;\n              }}\n              type=\"button\"\n              className=\"m-0 block cursor-pointer border-0 bg-transparent py-[9px] text-left font-medium outline-none [color:var(--bm-muted)] [font-family:inherit] [font-size:calc(var(--bm-font)+1px)] [-webkit-tap-highlight-color:transparent] [transition:color_200ms_ease] group-data-[open]/section:[color:var(--bm-ink)] data-[active]:[color:var(--bm-ink)] hover:[color:var(--bm-ink)]\"\n              aria-expanded={kids ? isOpen : undefined}\n              aria-current={leafActive ? 'true' : undefined}\n              data-active={leafActive ? '' : undefined}\n              onClick={() => (kids ? toggle(i) : select(leafValue, item))}\n            >\n              {item.label}\n            </button>\n            {kids ? (\n              <div className=\"grid [grid-template-rows:0fr] [transition:grid-template-rows_var(--bm-fold)_cubic-bezier(0.23,1,0.32,1)] group-data-[open]/section:[grid-template-rows:1fr] motion-reduce:transition-none\">\n                <div className=\"min-h-0 overflow-hidden\">\n                  <div className=\"relative box-border py-1.5\" style={{ height: bodyH }}>\n                    <svg\n                      className=\"pointer-events-none absolute top-0 left-0 overflow-visible opacity-0 [transition:opacity_200ms_ease] group-data-[open]/section:opacity-100 group-data-[open]/section:[transition:opacity_250ms_ease_100ms]\"\n                      width={indent}\n                      height={bodyH}\n                      aria-hidden=\"true\"\n                    >\n                      <path\n                        className=\"fill-none [stroke:var(--bm-line)] [stroke-width:var(--bm-line-w)] [stroke-linecap:round] [stroke-linejoin:round]\"\n                        d={`M ${trunk} 0 V ${rowY(kids.length - 1) - r}`}\n                      />\n                      {kids.map((kid, k) => (\n                        <path\n                          key={kid.value}\n                          className=\"fill-none [stroke:var(--bm-line)] [stroke-width:var(--bm-line-w)] [stroke-linecap:round] [stroke-linejoin:round]\"\n                          d={branch(k)}\n                        />\n                      ))}\n                      {kids.map((kid, k) => (\n                        <path\n                          key={kid.value}\n                          className=\"fill-none [stroke:var(--bm-accent)] [stroke-width:var(--bm-line-w)] [stroke-linecap:round] [stroke-linejoin:round] [transition:stroke-dashoffset_var(--bm-draw)_cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none\"\n                          d={reach(k)}\n                          style={{\n                            strokeDasharray: length(k),\n                            strokeDashoffset: kid.value === active ? 0 : length(k)\n                          }}\n                        />\n                      ))}\n                    </svg>\n                    {kids.map(kid => (\n                      <button\n                        key={kid.value}\n                        type=\"button\"\n                        className=\"m-0 box-border flex w-full cursor-pointer items-center gap-2 border-0 bg-transparent py-0 pr-0 text-left outline-none [height:var(--bm-row)] [padding-left:var(--bm-indent)] [color:var(--bm-muted)] [font-family:inherit] [-webkit-tap-highlight-color:transparent] [transition:color_200ms_ease] hover:[color:var(--bm-ink)] data-[active]:font-medium data-[active]:[color:var(--bm-accent)]\"\n                        aria-current={kid.value === active ? 'true' : undefined}\n                        data-active={kid.value === active ? '' : undefined}\n                        tabIndex={isOpen ? 0 : -1}\n                        onClick={() => select(kid.value, kid)}\n                      >\n                        {kid.icon ? (\n                          <span className=\"inline-flex flex-none\" aria-hidden=\"true\">\n                            {renderIcon(kid.icon)}\n                          </span>\n                        ) : null}\n                        <span className=\"whitespace-nowrap\">{kid.label}</span>\n                      </button>\n                    ))}\n                  </div>\n                </div>\n              </div>\n            ) : null}\n          </div>\n        );\n      })}\n    </nav>\n  );\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"@hugeicons/react@^1.1.10",
		"@hugeicons/core-free-icons@^4.3.3"
	]
}