{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-theme-transition",
  "title": "Theme Transition Hook",
  "description": "Animates theme changes with the View Transitions API, expanding from the clicked element.",
  "files": [
    {
      "path": "src/hooks/use-theme-transition.ts",
      "content": "\"use client\";\n\nimport { type RefObject, useCallback } from \"react\";\nimport { flushSync } from \"react-dom\";\n\ninterface UseThemeTransitionOptions {\n  duration?: number;\n}\n\n/**\n * Hook that provides a smooth circular view transition for theme switching.\n * The animation expands from the button position with a circular clip-path.\n *\n * @param buttonRef - Ref to the button element from which the animation originates\n * @param options - Configuration options (duration defaults to 400ms)\n * @returns A function that wraps theme changes with the circular transition\n */\nexport function useThemeTransition(\n  buttonRef: RefObject<HTMLElement | null>,\n  options: UseThemeTransitionOptions = {}\n) {\n  const { duration = 400 } = options;\n\n  return useCallback(\n    async (applyTheme: () => void) => {\n      // Fallback for browsers without View Transitions API support\n      if (\n        !document.startViewTransition ||\n        !buttonRef.current ||\n        window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n      ) {\n        applyTheme();\n        return;\n      }\n\n      // Pause all running animations to prevent jank during transition\n      // This is especially important for pages with continuous animations (marquees, etc.)\n      const animatingElements = document.querySelectorAll(\"*\");\n      const pausedElements: { element: Element; originalState: string }[] = [];\n\n      animatingElements.forEach((el) => {\n        const style = getComputedStyle(el);\n        if (\n          style.animationName !== \"none\" &&\n          style.animationPlayState === \"running\"\n        ) {\n          pausedElements.push({\n            element: el,\n            originalState: (el as HTMLElement).style.animationPlayState,\n          });\n          (el as HTMLElement).style.animationPlayState = \"paused\";\n        }\n      });\n\n      // Start the view transition\n      const transition = document.startViewTransition(() => {\n        flushSync(() => {\n          applyTheme();\n        });\n      });\n\n      // Wait for the transition to be ready\n      await transition.ready;\n\n      // Calculate the circle origin from the button's center\n      const { top, left, width, height } =\n        buttonRef.current.getBoundingClientRect();\n      const x = left + width / 2;\n      const y = top + height / 2;\n\n      // Calculate the maximum radius needed to cover the entire viewport\n      const maxRadius = Math.hypot(\n        Math.max(left, window.innerWidth - left),\n        Math.max(top, window.innerHeight - top)\n      );\n\n      // Animate the new view with a circular reveal\n      document.documentElement.animate(\n        {\n          clipPath: [\n            `circle(0px at ${x}px ${y}px)`,\n            `circle(${maxRadius}px at ${x}px ${y}px)`,\n          ],\n        },\n        {\n          duration,\n          easing: \"ease-in-out\",\n          pseudoElement: \"::view-transition-new(root)\",\n        }\n      );\n\n      // Wait for the transition to finish, then resume animations\n      await transition.finished;\n\n      pausedElements.forEach(({ element, originalState }) => {\n        (element as HTMLElement).style.animationPlayState = originalState || \"\";\n      });\n    },\n    [buttonRef, duration]\n  );\n}\n",
      "type": "registry:hook"
    }
  ],
  "type": "registry:hook"
}
