{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dock",
  "title": "Dock",
  "description": "A navigation dock component with smooth animations and hover effects, perfect for desktop interfaces.",
  "dependencies": [
    "motion",
    "clsx",
    "tailwind-merge",
    "lucide-react",
    "next-themes"
  ],
  "files": [
    {
      "path": "registry/chamaac/dock/dock.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  easeIn,\n  easeOut,\n  LazyMotion,\n  domAnimation,\n  m,\n  AnimatePresence,\n} from \"motion/react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport React, {\n  useState,\n  useRef,\n  createContext,\n  useContext,\n  useEffect,\n} from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { useTheme } from \"next-themes\";\nimport { X, Menu } from \"lucide-react\";\n\n// Context to manage dock state\ninterface DockContextType {\n  openDropdowns: Record<string, boolean>;\n  hoveredLink: string | null;\n  setHoveredLink: (href: string | null) => void;\n  handleDropdownEnter: (id: string) => void;\n  handleDropdownLeave: (id: string) => void;\n  activePage?: string;\n  isDark: boolean;\n}\n\nconst DockContext = createContext<DockContextType | undefined>(undefined);\n\nconst useDock = () => {\n  const context = useContext(DockContext);\n  if (!context) {\n    throw new Error(\"useDock must be used within a Dock component\");\n  }\n  return context;\n};\n\ninterface DockProps {\n  children: React.ReactNode;\n  closeDelay?: number;\n  bottomOffset?: string;\n  activePage?: string;\n  className?: string;\n}\n\nexport const Dock = ({\n  children,\n  closeDelay = 100,\n  bottomOffset = \"60px\",\n  activePage,\n  className,\n}: DockProps) => {\n  const [openDropdowns, setOpenDropdowns] = useState<Record<string, boolean>>(\n    {}\n  );\n  const closeTimeoutsRef = useRef<Record<string, NodeJS.Timeout | null>>({});\n  const [hoveredLink, setHoveredLink] = useState<string | null>(null);\n  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);\n\n  const { theme } = useTheme();\n  const isDark = theme === \"dark\";\n\n  const handleDropdownEnter = (id: string): void => {\n    if (closeTimeoutsRef.current[id]) {\n      clearTimeout(closeTimeoutsRef.current[id]!);\n    }\n    setOpenDropdowns((prev) => ({ ...prev, [id]: true }));\n  };\n\n  const handleDropdownLeave = (id: string): void => {\n    closeTimeoutsRef.current[id] = setTimeout(() => {\n      setOpenDropdowns((prev) => ({ ...prev, [id]: false }));\n      setHoveredLink(null);\n    }, closeDelay);\n  };\n\n  // Prevent scrolling when mobile menu is open\n  useEffect(() => {\n    if (isMobileMenuOpen) {\n      document.body.style.overflow = \"hidden\";\n    } else {\n      document.body.style.overflow = \"unset\";\n    }\n    return () => {\n      document.body.style.overflow = \"unset\";\n    };\n  }, [isMobileMenuOpen]);\n\n  return (\n    <LazyMotion features={domAnimation}>\n      <DockContext.Provider\n        value={{\n          openDropdowns,\n          hoveredLink,\n          setHoveredLink,\n          handleDropdownEnter,\n          handleDropdownLeave,\n          activePage,\n          isDark,\n        }}\n      >\n        <div className=\"w-full\">\n          {/* Desktop Dock */}\n          <m.nav\n            className=\"fixed bottom-[60px] left-0 w-full z-50 hidden md:block\"\n            style={{ bottom: bottomOffset }}\n          >\n            <div className=\"px-4 flex justify-center\">\n              <m.div\n                className={cn(\n                  \"relative flex flex-col items-center justify-center overflow-hidden backdrop-blur-md bg-white dark:bg-black/50 border border-[#E0E0E0] dark:border-neutral-700 p-[3px] rounded-[25px]\",\n                  className\n                )}\n                transition={{ duration: 0.2 }}\n              >\n                {/* Dropdown Contents */}\n                {React.Children.map(children, (child) => {\n                  if (\n                    React.isValidElement(child) &&\n                    (child.type as { displayName?: string }).displayName ===\n                      \"DockItem\"\n                  ) {\n                    return React.cloneElement(\n                      child as React.ReactElement<DockItemProps>,\n                      { renderType: \"content\" }\n                    );\n                  }\n                  return null;\n                })}\n\n                {/* Navigation Items */}\n                <div className=\"flex items-center gap-[3px] relative z-10\">\n                  {React.Children.map(children, (child) => {\n                    if (React.isValidElement(child)) {\n                      return React.cloneElement(\n                        child as React.ReactElement<\n                          DockItemProps | DockIconProps | DockLinkProps\n                        >,\n                        { renderType: \"trigger\" }\n                      );\n                    }\n                    return null;\n                  })}\n                </div>\n              </m.div>\n            </div>\n          </m.nav>\n\n          {/* Mobile Dock */}\n          <div className=\"md:hidden fixed bottom-8 left-0 w-full z-50 flex justify-center pointer-events-none\">\n            <div className=\"pointer-events-auto\">\n              <AnimatePresence>\n                {isMobileMenuOpen && (\n                  <m.div\n                    initial={{ opacity: 0, y: 20 }}\n                    animate={{ opacity: 1, y: 0 }}\n                    exit={{ opacity: 0, y: 20 }}\n                    transition={{ duration: 0.2 }}\n                    className=\"fixed inset-0 bg-white dark:bg-black z-40 flex flex-col pt-20 px-6 pb-32 overflow-y-auto\"\n                  >\n                    <div className=\"flex flex-col gap-6\">\n                      {React.Children.map(children, (child) => {\n                        if (!React.isValidElement(child)) return null;\n\n                        // Handle DockLink (Top level links)\n                        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n                        if ((child.type as any).displayName === \"DockLink\") {\n                          const props = child.props as DockLinkProps;\n                          return (\n                            <Link\n                              href={props.href}\n                              className=\"text-black dark:text-white text-2xl font-medium\"\n                              onClick={() => setIsMobileMenuOpen(false)}\n                            >\n                              {props.label}\n                            </Link>\n                          );\n                        }\n\n                        // Handle DockItem (Sections with dropdowns)\n                        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n                        if ((child.type as any).displayName === \"DockItem\") {\n                          const props = child.props as DockItemProps;\n                          return (\n                            <div className=\"flex flex-col gap-4\">\n                              <span className=\"text-neutral-500 dark:text-neutral-400 text-lg\">\n                                {props.label}\n                              </span>\n                              <div className=\"flex flex-col gap-4 pl-4 border-l border-neutral-200 dark:border-neutral-800\">\n                                {React.Children.map(\n                                  props.children,\n                                  (subChild) => {\n                                    if (\n                                      React.isValidElement(subChild) &&\n                                      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n                                      (subChild.type as any).displayName ===\n                                        \"DockDropdownItem\"\n                                    ) {\n                                      const subProps =\n                                        subChild.props as DockDropdownItemProps;\n                                      return (\n                                        <Link\n                                          href={subProps.href}\n                                          className=\"text-black dark:text-white text-xl font-medium flex items-center gap-3\"\n                                          onClick={() =>\n                                            setIsMobileMenuOpen(false)\n                                          }\n                                        >\n                                          {subProps.image && (\n                                            <Image\n                                              src={subProps.image}\n                                              alt=\"\"\n                                              width={32}\n                                              height={32}\n                                              className=\"rounded-lg object-cover\"\n                                            />\n                                          )}\n                                          {subProps.label}\n                                        </Link>\n                                      );\n                                    }\n                                    return null;\n                                  }\n                                )}\n                              </div>\n                            </div>\n                          );\n                        }\n                        return null;\n                      })}\n                    </div>\n                  </m.div>\n                )}\n              </AnimatePresence>\n\n              <button\n                onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}\n                className={cn(\n                  \"flex items-center gap-2 px-6 py-3 rounded-full shadow-lg transition-all duration-300 relative z-50\",\n                  isMobileMenuOpen\n                    ? \"bg-transparent border border-black dark:border-white text-black dark:text-white\"\n                    : \"bg-white dark:bg-neutral-900 text-black dark:text-white border border-neutral-200 dark:border-neutral-800\"\n                )}\n              >\n                <span className=\"font-medium text-lg\">Menu</span>\n                {isMobileMenuOpen ? <X size={20} /> : <Menu size={20} />}\n              </button>\n            </div>\n          </div>\n        </div>\n      </DockContext.Provider>\n    </LazyMotion>\n  );\n};\n\ninterface DockItemProps {\n  children: React.ReactNode;\n  label: string;\n  id?: string;\n  renderType?: \"content\" | \"trigger\";\n  className?: string;\n}\n\nexport const DockItem = ({\n  children,\n  label,\n  id,\n  renderType,\n  className,\n}: DockItemProps) => {\n  const {\n    openDropdowns,\n    handleDropdownEnter,\n    handleDropdownLeave,\n    isDark,\n    activePage,\n  } = useDock();\n  const pathname = usePathname();\n\n  const itemId = id || label.toLowerCase().replace(/\\s+/g, \"-\");\n  const isOpen = openDropdowns[itemId] || false;\n\n  const isAnyChildActive = React.Children.toArray(children).some((child) => {\n    if (\n      React.isValidElement<DockDropdownItemProps>(child) &&\n      (child.type as { displayName?: string }).displayName ===\n        \"DockDropdownItem\" &&\n      child.props.href\n    ) {\n      const currentPath = activePage !== undefined ? activePage : pathname;\n      return currentPath === child.props.href;\n    }\n    return false;\n  });\n\n  if (renderType === \"content\") {\n    return (\n      <m.div\n        initial={{ opacity: 0, height: 0 }}\n        animate={{\n          opacity: isOpen ? 1 : 0,\n          height: isOpen ? \"auto\" : 0,\n        }}\n        transition={{\n          duration: 0.3,\n          ease: \"easeInOut\",\n        }}\n        className={cn(\n          \"w-full overflow-hidden\",\n          isOpen ? \"pointer-events-auto min-h-[100px]\" : \"pointer-events-none\"\n        )}\n        onMouseEnter={() => handleDropdownEnter(itemId)}\n        onMouseLeave={() => handleDropdownLeave(itemId)}\n      >\n        <div className=\"px-[15px] pt-[15px] pb-[30px] flex justify-between items-start w-full min-w-[400px] bg-white dark:bg-transparent\">\n          <div className=\"gap-[12.5px] flex flex-col\">{children}</div>\n          <DockItemImagePreview>{children}</DockItemImagePreview>\n        </div>\n      </m.div>\n    );\n  }\n\n  return (\n    <m.div\n      className={cn(\n        \"transition-colors duration-200 text-[14px] leading-[10px] flex items-center gap-1 h-[42px] rounded-full cursor-pointer px-[18px]\",\n        isAnyChildActive\n          ? \"text-black dark:text-white font-medium\"\n          : \"text-black dark:text-white\",\n        className\n      )}\n      onMouseEnter={() => handleDropdownEnter(itemId)}\n      onMouseLeave={() => handleDropdownLeave(itemId)}\n      animate={{\n        backgroundColor:\n          isOpen || isAnyChildActive\n            ? isDark\n              ? \"#262626\"\n              : \"#F0F0F0\"\n            : \"transparent\",\n      }}\n      whileHover={{\n        backgroundColor:\n          isOpen || isAnyChildActive\n            ? isDark\n              ? \"#262626\"\n              : \"#F0F0F0\"\n            : isDark\n              ? \"#262626\"\n              : \"#F0F0F0\",\n      }}\n      transition={{ duration: 0.2 }}\n    >\n      {label}\n      <m.svg\n        width=\"16\"\n        height=\"16\"\n        viewBox=\"0 0 16 16\"\n        className=\"text-black dark:text-white\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        animate={{ rotate: isOpen ? 180 : 0 }}\n        transition={{ duration: 0.2 }}\n      >\n        <path\n          fillRule=\"evenodd\"\n          clipRule=\"evenodd\"\n          d=\"M8 8.93934L4.53033 5.46967L3.46967 6.53033L6.58578 9.64645C7.36683 10.4275 8.63316 10.4275 9.41421 9.64645L12.5303 6.53033L11.4697 5.46967L8 8.93934Z\"\n          fill=\"currentColor\"\n        ></path>\n      </m.svg>\n    </m.div>\n  );\n};\nDockItem.displayName = \"DockItem\";\n\nconst DockItemImagePreview = ({ children }: { children: React.ReactNode }) => {\n  const { hoveredLink, activePage } = useDock();\n  const pathname = usePathname();\n\n  const activeChild = React.Children.toArray(children).find((child) => {\n    if (\n      React.isValidElement<DockDropdownItemProps>(child) &&\n      (child.type as { displayName?: string }).displayName ===\n        \"DockDropdownItem\" &&\n      child.props.href\n    ) {\n      const currentPath = activePage !== undefined ? activePage : pathname;\n      return currentPath === child.props.href;\n    }\n    return false;\n  }) as React.ReactElement<DockDropdownItemProps> | undefined;\n\n  const hoveredChild = React.Children.toArray(children).find((child) => {\n    return (\n      React.isValidElement<DockDropdownItemProps>(child) &&\n      (child.type as { displayName?: string }).displayName ===\n        \"DockDropdownItem\" &&\n      child.props.href === hoveredLink\n    );\n  }) as React.ReactElement<DockDropdownItemProps> | undefined;\n\n  const displayImage = hoveredChild?.props.image || activeChild?.props.image;\n  const shouldShowImage = hoveredLink || activeChild;\n\n  if (!displayImage) return null;\n\n  return (\n    <div className=\"flex flex-col items-end gap-2\">\n      <m.img\n        key={displayImage}\n        initial={{ opacity: 0, scale: 0.8 }}\n        animate={{\n          opacity: shouldShowImage ? 1 : 0.8,\n          scale: shouldShowImage ? 1 : 0.9,\n        }}\n        transition={{\n          ease: shouldShowImage ? easeIn : easeOut,\n          duration: 0.2,\n        }}\n        src={displayImage}\n        className=\"rounded-[15px] w-[80px] h-[80px] object-cover\"\n        alt=\"\"\n      />\n    </div>\n  );\n};\n\ninterface DockDropdownItemProps {\n  href: string;\n  label: string;\n  image?: string;\n  className?: string;\n}\n\nexport const DockDropdownItem = ({\n  href,\n  label,\n  className,\n}: DockDropdownItemProps) => {\n  const { hoveredLink, setHoveredLink, activePage } = useDock();\n  const pathname = usePathname();\n\n  const currentPath = activePage !== undefined ? activePage : pathname;\n  const isMenuItemActive = currentPath === href;\n  const isHovered = hoveredLink === href;\n\n  return (\n    <m.a\n      href={href}\n      whileHover={{ x: 5 }}\n      transition={{ duration: 0.1 }}\n      onMouseEnter={() => setHoveredLink(href)}\n      className={cn(\n        \"block text-[14px] leading-[10px] transition-colors\",\n        isMenuItemActive || isHovered\n          ? \"text-black dark:text-white font-medium\"\n          : \"text-neutral-500 dark:text-[#C1C1C1] hover:text-black dark:hover:text-white\",\n        className\n      )}\n    >\n      {label}\n    </m.a>\n  );\n};\nDockDropdownItem.displayName = \"DockDropdownItem\";\n\ninterface DockIconProps {\n  icon: React.ReactNode;\n  href: string;\n  renderType?: \"content\" | \"trigger\";\n  className?: string;\n}\n\nexport const DockIcon = ({\n  icon,\n  href,\n  renderType,\n  className,\n}: DockIconProps) => {\n  const { isDark, activePage } = useDock();\n  const pathname = usePathname();\n\n  if (renderType === \"content\") return null;\n\n  const currentPath = activePage !== undefined ? activePage : pathname;\n  const isActive = currentPath === href;\n\n  return (\n    <Link href={href}>\n      <m.div\n        className={cn(\n          \"flex items-center justify-center w-[56px] h-[42px] rounded-full cursor-pointer\",\n          className\n        )}\n        animate={{\n          backgroundColor: isActive\n            ? isDark\n              ? \"#262626\"\n              : \"#F0F0F0\"\n            : \"transparent\",\n        }}\n        whileHover={{\n          backgroundColor: isDark ? \"#262626\" : \"#F0F0F0\",\n        }}\n        transition={{ duration: 0.2 }}\n      >\n        {icon}\n      </m.div>\n    </Link>\n  );\n};\nDockIcon.displayName = \"DockIcon\";\n\ninterface DockLinkProps {\n  label: string;\n  href: string;\n  icon?: React.ReactNode;\n  external?: boolean;\n  renderType?: \"content\" | \"trigger\";\n  id?: string;\n  className?: string;\n}\n\nexport const DockLink = ({\n  label,\n  href,\n  icon,\n  external,\n  renderType,\n  className,\n}: DockLinkProps) => {\n  const { isDark, activePage } = useDock();\n  const pathname = usePathname();\n  const [isHovered, setIsHovered] = useState(false);\n\n  if (renderType === \"content\") return null;\n\n  const currentPath = activePage !== undefined ? activePage : pathname;\n  const isActive = currentPath === href;\n\n  const linkContent = (\n    <>\n      {label}\n      {icon && (\n        <m.div\n          initial={{ x: 0, y: 0 }}\n          animate={{\n            x: isHovered ? 2 : 0,\n            y: isHovered ? -2 : 0,\n          }}\n          transition={{ duration: 0.2 }}\n        >\n          {icon}\n        </m.div>\n      )}\n    </>\n  );\n\n  const baseClassName = cn(\n    \"transition-colors duration-200 text-[14px] leading-[10px] flex items-center gap-1 h-[42px] rounded-full px-[18px]\",\n    isActive\n      ? \"text-black dark:text-white font-medium\"\n      : \"text-black dark:text-white\",\n    className\n  );\n\n  if (external) {\n    return (\n      <m.a\n        href={href}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        className={baseClassName}\n        onMouseEnter={() => setIsHovered(true)}\n        onMouseLeave={() => setIsHovered(false)}\n        whileHover={{\n          backgroundColor: isActive\n            ? isDark\n              ? \"#404040\"\n              : \"#E0E0E0\"\n            : isDark\n              ? \"#262626\"\n              : \"#F0F0F0\",\n        }}\n        transition={{ duration: 0.2 }}\n      >\n        {linkContent}\n      </m.a>\n    );\n  }\n\n  return (\n    <m.div\n      className=\"inline-block rounded-full\"\n      animate={{\n        backgroundColor: isActive\n          ? isDark\n            ? \"#262626\"\n            : \"#F0F0F0\"\n          : \"transparent\",\n      }}\n      whileHover={{\n        backgroundColor: isDark ? \"#262626\" : \"#F0F0F0\",\n      }}\n      transition={{ duration: 0.2 }}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n    >\n      <Link href={href} className={baseClassName}>\n        {linkContent}\n      </Link>\n    </m.div>\n  );\n};\nDockLink.displayName = \"DockLink\";\n\nexport default Dock;\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}