{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "theme-toggle",
  "type": "registry:component",
  "title": "Theme Toggle",
  "description": "Button to toggle between light and dark themes.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/theme-toggle/theme-toggle.tsx",
      "content": "\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\n\nimport { useTheme } from \"next-themes\";\n\nimport { useMounted } from \"@vllnt/ui\";\nimport { Button } from \"@vllnt/ui\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@vllnt/ui\";\n\ntype ThemeToggleProps = {\n  dict: {\n    theme: {\n      dark: string;\n      light: string;\n      system: string;\n      toggle_theme: string;\n    };\n  };\n};\n\ntype ThemeButtonProps = {\n  ariaLabel: string;\n  children?: React.ReactNode;\n  icon: string;\n} & React.ButtonHTMLAttributes<HTMLButtonElement>;\n\nfunction ThemeButton({\n  ariaLabel,\n  children,\n  icon,\n  ...props\n}: ThemeButtonProps) {\n  return (\n    <Button\n      aria-label={ariaLabel}\n      className=\"bg-background text-foreground border border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 transition-colors\"\n      size=\"icon\"\n      variant=\"outline\"\n      {...props}\n    >\n      <span className=\"text-sm font-mono\">{icon}</span>\n      <span className=\"sr-only\">{ariaLabel}</span>\n      {children}\n    </Button>\n  );\n}\n\nfunction ThemeMenuItem({\n  icon,\n  label,\n  onClick,\n}: {\n  icon: string;\n  label: string;\n  onClick: () => void;\n}) {\n  return (\n    <DropdownMenuItem\n      className=\"hover:bg-accent cursor-pointer\"\n      onClick={onClick}\n    >\n      {icon} {label}\n    </DropdownMenuItem>\n  );\n}\n\nexport function ThemeToggle({ dict }: ThemeToggleProps) {\n  const { setTheme, theme } = useTheme();\n  const mounted = useMounted();\n  const [open, setOpen] = useState(false);\n  const closeTimerReference = useRef<null | number>(null);\n  const isHoveringOverMenuAreaReference = useRef(false);\n\n  const clearCloseTimer = useCallback(() => {\n    if (closeTimerReference.current !== null) {\n      window.clearTimeout(closeTimerReference.current);\n      closeTimerReference.current = null;\n    }\n  }, []);\n\n  const scheduleClose = useCallback(() => {\n    clearCloseTimer();\n    closeTimerReference.current = window.setTimeout(() => {\n      setOpen(false);\n    }, 250);\n  }, [clearCloseTimer]);\n\n  const handleOpenChange = useCallback((nextOpen: boolean) => {\n    if (!nextOpen && isHoveringOverMenuAreaReference.current) {\n      return;\n    }\n    setOpen(nextOpen);\n  }, []);\n\n  const getThemeIcon = useCallback(() => {\n    if (!mounted) return \"☀\";\n\n    switch (theme) {\n      case \"light\":\n        return \"☀\";\n      case \"dark\":\n        return \"☾\";\n      case \"system\":\n        return \"⚙\";\n      case undefined:\n        return \"⚙\";\n      default:\n        return \"⚙\";\n    }\n  }, [theme, mounted]);\n\n  const themeIcon = getThemeIcon();\n\n  const handleThemeChange = useCallback(\n    (newTheme: string) => {\n      setTheme(newTheme);\n    },\n    [setTheme],\n  );\n\n  // Prevent hydration mismatch by not rendering until mounted\n  if (!mounted) {\n    return <ThemeButton ariaLabel={dict.theme.toggle_theme} icon=\"☀\" />;\n  }\n\n  return (\n    <DropdownMenu modal={false} onOpenChange={handleOpenChange} open={open}>\n      <DropdownMenuTrigger asChild>\n        <ThemeButton\n          ariaLabel={dict.theme.toggle_theme}\n          icon={themeIcon}\n          onMouseEnter={() => {\n            isHoveringOverMenuAreaReference.current = true;\n            clearCloseTimer();\n            setOpen(true);\n          }}\n          onMouseLeave={() => {\n            isHoveringOverMenuAreaReference.current = false;\n            scheduleClose();\n          }}\n        />\n      </DropdownMenuTrigger>\n      <DropdownMenuContent\n        align=\"end\"\n        className=\"bg-background border border-gray-300 dark:border-gray-600\"\n        onMouseEnter={() => {\n          isHoveringOverMenuAreaReference.current = true;\n          clearCloseTimer();\n        }}\n        onMouseLeave={() => {\n          isHoveringOverMenuAreaReference.current = false;\n          scheduleClose();\n        }}\n      >\n        <ThemeMenuItem\n          icon=\"☀\"\n          label={dict.theme.light}\n          onClick={() => {\n            handleThemeChange(\"light\");\n          }}\n        />\n        <ThemeMenuItem\n          icon=\"☾\"\n          label={dict.theme.dark}\n          onClick={() => {\n            handleThemeChange(\"dark\");\n          }}\n        />\n        <ThemeMenuItem\n          icon=\"⚙\"\n          label={dict.theme.system}\n          onClick={() => {\n            handleThemeChange(\"system\");\n          }}\n        />\n      </DropdownMenuContent>\n    </DropdownMenu>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
