{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "vertical-tooltip-navbar",
  "type": "registry:component",
  "title": "Vertical Tooltip Navbar",
  "description": "A vertical tooltip menu with smooth clip-path animations that reveal tooltips on hover",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "files": [
    {
      "path": "components/watermelon/vertical-tooltip-navbar.tsx",
      "type": "registry:component",
      "content": "'use client';\n\nimport { motion, AnimatePresence } from 'motion/react';\nimport { useRef, useState, type ReactNode } from 'react';\nimport { MessageCircle, Inbox, Circle, CommandIcon } from 'lucide-react';\n\nconst DEFAULT_ITEMS: TooltipItem[] = [\n  {\n    icon: <MessageCircle className=\"h-full w-full\" />,\n    label: 'Comment',\n    labelHasKeyword: ['C'],\n    hasBadge: false,\n  },\n  {\n    icon: <Inbox className=\"h-full w-full\" />,\n    label: 'Inbox',\n    labelHasKeyword: ['I'],\n    hasBadge: true,\n  },\n  {\n    icon: <Circle className=\"h-full w-full\" />,\n    label: 'Record',\n    labelHasKeyword: ['R'],\n    hasBadge: false,\n  },\n];\nexport type TooltipItem = {\n  icon: ReactNode;\n  label: string;\n  labelHasKeyword?: (string | ReactNode)[] | false;\n  hasBadge?: boolean;\n};\n\ninterface TooltipVerticalNavbarProps {\n  items: TooltipItem[];\n  tooltipDelay?: number;\n}\n\nexport const TooltipVerticalNavbar = ({\n  items = DEFAULT_ITEMS,\n  tooltipDelay = 300,\n}: TooltipVerticalNavbarProps) => {\n  const [activeIndex, setActiveIndex] = useState<number | null>(null);\n  const [coords, setCoords] = useState({ clipPath: '', translateY: 0 });\n\n  const measureRefs = useRef<(HTMLDivElement | null)[]>([]);\n  const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);\n\n  const [isEntering, setIsEntering] = useState(true);\n  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  const calculatePosition = (index: number) => {\n    const activeLabel = measureRefs.current[index];\n    const activeIcon = buttonRefs.current[index];\n\n    if (!activeLabel || !activeIcon) return null;\n\n    const labelTop = activeLabel.offsetTop;\n    const labelHeight = activeLabel.offsetHeight;\n    const labelCenter = labelTop + labelHeight / 2;\n\n    const iconTop = activeIcon.offsetTop;\n    const iconHeight = activeIcon.offsetHeight;\n    const iconCenter = iconTop + iconHeight / 2;\n\n    const totalHeight = measureRefs.current.reduce(\n      (acc, el) => acc + (el?.offsetHeight || 0),\n      0,\n    );\n\n    const cTop = (labelTop / totalHeight) * 100;\n    const cBottom = 100 - ((labelTop + labelHeight) / totalHeight) * 100;\n\n    return {\n      clipPath: `inset(${cTop}% 0 ${cBottom}% 0 round 8px)`,\n      translateY: iconCenter - labelCenter,\n    };\n  };\n\n  const handleMouseEnter = (index: number) => {\n    const newCoords = calculatePosition(index);\n    if (!newCoords) return;\n\n    if (activeIndex === null) {\n      if (timeoutRef.current) clearTimeout(timeoutRef.current);\n      setIsEntering(true);\n\n      timeoutRef.current = setTimeout(() => {\n        setCoords(newCoords);\n        setActiveIndex(index);\n      }, tooltipDelay);\n    } else {\n      setCoords(newCoords);\n      setActiveIndex(index);\n    }\n  };\n\n  const handleMouseLeave = () => {\n    if (timeoutRef.current) clearTimeout(timeoutRef.current);\n    setActiveIndex(null);\n    setCoords({ clipPath: '', translateY: 0 });\n    setIsEntering(true);\n  };\n\n  return (\n    <div className=\"flex h-screen items-center px-8\">\n      <div className=\"relative text-white\" onMouseLeave={handleMouseLeave}>\n        <AnimatePresence>\n          {activeIndex !== null && coords.clipPath !== '' && (\n            <motion.div\n              className=\"absolute top-0 left-16\"\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              exit={{ opacity: 0 }}\n              transition={{ duration: 0.2 }}\n            >\n              <motion.div\n                className=\"bg-black dark:bg-neutral-800\"\n                animate={{\n                  clipPath: coords.clipPath,\n                  y: coords.translateY,\n                }}\n                transition={{\n                  type: 'spring',\n                  bounce: 0,\n                  duration: isEntering ? 0 : 0.4,\n                }}\n                onUpdate={() => {\n                  if (isEntering) {\n                    setIsEntering(false);\n                  }\n                }}\n              >\n                <div className=\"flex flex-col items-start justify-center\">\n                  {items.map((item, index) => (\n                    <div\n                      key={`real-${index}`}\n                      className=\"flex h-10 items-center justify-center gap-2 px-3 text-sm font-medium whitespace-nowrap\"\n                    >\n                      <span className=\"text-white\">{item.label}</span>\n                      {item.hasBadge && (\n                        <div className=\"flex items-center gap-0.5 text-white/40\">\n                          <span className=\"flex items-center justify-center rounded-sm border border-white/20 p-1\">\n                            <CommandIcon className=\"size-3 text-neutral-500\" />\n                          </span>\n                        </div>\n                      )}\n                      {item.labelHasKeyword && (\n                        <div className=\"flex items-center gap-0.5 text-white/40\">\n                          {item.labelHasKeyword.map((key, i) => (\n                            <span\n                              key={i}\n                              className=\"flex items-center justify-center rounded-sm border border-white/20 px-1 tabular-nums\"\n                            >\n                              {key}\n                            </span>\n                          ))}\n                        </div>\n                      )}\n                    </div>\n                  ))}\n                </div>\n              </motion.div>\n            </motion.div>\n          )}\n        </AnimatePresence>\n\n        <div className=\"z-10 flex flex-col items-center justify-center rounded-full bg-black/95 p-2 backdrop-blur dark:bg-neutral-800\">\n          {items.map((item, index) => (\n            <button\n              key={index}\n              onMouseEnter={() => handleMouseEnter(index)}\n              ref={(el) => {\n                buttonRefs.current[index] = el;\n              }}\n              className=\"flex cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-white/10\"\n            >\n              <div className=\"flex size-10 items-center justify-center p-1.5 dark:text-neutral-200\">\n                {item.icon}\n              </div>\n              <span className=\"sr-only\">{item.label}</span>\n            </button>\n          ))}\n        </div>\n      </div>\n\n      <div className=\"pointer-events-none absolute top-0 left-0 flex flex-col overflow-hidden whitespace-nowrap opacity-0\">\n        {items.map((item, index) => (\n          <div\n            key={`measure-${index}`}\n            ref={(el) => {\n              measureRefs.current[index] = el;\n            }}\n            className=\"flex h-10 items-center justify-center gap-2 px-3 text-sm font-medium whitespace-nowrap\"\n          >\n            <span>{item.label}</span>\n            {item.hasBadge && (\n              <div className=\"flex items-center gap-0.5 text-white/40\">\n                <span className=\"flex items-center justify-center rounded-sm border border-white/20 p-1\">\n                  <CommandIcon className=\"size-3 text-neutral-500\" />\n                </span>\n              </div>\n            )}\n            {item.labelHasKeyword && (\n              <div className=\"flex items-center gap-0.5 text-white/40\">\n                {item.labelHasKeyword.map((key, i) => (\n                  <span\n                    key={i}\n                    className=\"flex items-center justify-center rounded-sm border border-white/20 px-1 tabular-nums\"\n                  >\n                    {typeof key === 'string' ? key : '⌘'}\n                  </span>\n                ))}\n              </div>\n            )}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n};\n"
    }
  ]
}
