{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-effects-theme-toggler",
  "title": "Theme Toggler",
  "description": "An effect that allows you to toggle the theme gradually.",
  "files": [
    {
      "path": "registry/primitives/effects/theme-toggler/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { flushSync } from 'react-dom';\n\ntype ThemeSelection = 'light' | 'dark' | 'system';\ntype Resolved = 'light' | 'dark';\ntype Direction = 'btt' | 'ttb' | 'ltr' | 'rtl';\n\ntype ChildrenRender =\n  | React.ReactNode\n  | ((state: {\n      resolved: Resolved;\n      effective: ThemeSelection;\n      toggleTheme: (theme: ThemeSelection) => void;\n    }) => React.ReactNode);\n\nfunction getSystemEffective(): Resolved {\n  if (typeof window === 'undefined') return 'light';\n  return window.matchMedia('(prefers-color-scheme: dark)').matches\n    ? 'dark'\n    : 'light';\n}\n\nfunction getClipKeyframes(direction: Direction): [string, string] {\n  switch (direction) {\n    case 'ltr':\n      return ['inset(0 100% 0 0)', 'inset(0 0 0 0)'];\n    case 'rtl':\n      return ['inset(0 0 0 100%)', 'inset(0 0 0 0)'];\n    case 'ttb':\n      return ['inset(0 0 100% 0)', 'inset(0 0 0 0)'];\n    case 'btt':\n      return ['inset(100% 0 0 0)', 'inset(0 0 0 0)'];\n    default:\n      return ['inset(0 100% 0 0)', 'inset(0 0 0 0)'];\n  }\n}\n\ntype ThemeTogglerProps = {\n  theme: ThemeSelection;\n  resolvedTheme: Resolved;\n  setTheme: (theme: ThemeSelection) => void;\n  direction?: Direction;\n  onImmediateChange?: (theme: ThemeSelection) => void;\n  children?: ChildrenRender;\n};\n\nfunction ThemeToggler({\n  theme,\n  resolvedTheme,\n  setTheme,\n  onImmediateChange,\n  direction = 'ltr',\n  children,\n  ...props\n}: ThemeTogglerProps) {\n  const [preview, setPreview] = React.useState<null | {\n    effective: ThemeSelection;\n    resolved: Resolved;\n  }>(null);\n  const [current, setCurrent] = React.useState<{\n    effective: ThemeSelection;\n    resolved: Resolved;\n  }>({\n    effective: theme,\n    resolved: resolvedTheme,\n  });\n\n  React.useEffect(() => {\n    if (\n      preview &&\n      theme === preview.effective &&\n      resolvedTheme === preview.resolved\n    ) {\n      setPreview(null);\n    }\n  }, [theme, resolvedTheme, preview]);\n\n  const [fromClip, toClip] = getClipKeyframes(direction);\n\n  const toggleTheme = React.useCallback(\n    async (theme: ThemeSelection) => {\n      const resolved = theme === 'system' ? getSystemEffective() : theme;\n\n      setCurrent({ effective: theme, resolved });\n      onImmediateChange?.(theme);\n\n      if (theme === 'system' && resolved === resolvedTheme) {\n        setTheme(theme);\n        return;\n      }\n\n      if (!document.startViewTransition) {\n        flushSync(() => {\n          setPreview({ effective: theme, resolved });\n        });\n        setTheme(theme);\n        return;\n      }\n\n      await document.startViewTransition(() => {\n        flushSync(() => {\n          setPreview({ effective: theme, resolved });\n          document.documentElement.classList.toggle(\n            'dark',\n            resolved === 'dark',\n          );\n        });\n      }).ready;\n\n      document.documentElement\n        .animate(\n          { clipPath: [fromClip, toClip] },\n          {\n            duration: 700,\n            easing: 'ease-in-out',\n            pseudoElement: '::view-transition-new(root)',\n          },\n        )\n        .finished.finally(() => {\n          setTheme(theme);\n        });\n    },\n    [onImmediateChange, resolvedTheme, fromClip, toClip, setTheme],\n  );\n\n  return (\n    <React.Fragment {...props}>\n      {typeof children === 'function'\n        ? children({\n            effective: current.effective,\n            resolved: current.resolved,\n            toggleTheme,\n          })\n        : children}\n      <style>{`::view-transition-old(root), ::view-transition-new(root){animation:none;mix-blend-mode:normal;}`}</style>\n    </React.Fragment>\n  );\n}\n\nexport {\n  ThemeToggler,\n  type ThemeTogglerProps,\n  type ThemeSelection,\n  type Resolved,\n  type Direction,\n};\n",
      "type": "registry:ui",
      "target": "components/animate-ui/primitives/effects/theme-toggler.tsx"
    }
  ],
  "type": "registry:ui"
}