import { createContext, useContext, useEffect, useState } from 'react' import type { PropsWithChildren, RefObject } from 'react' export type UnityTheme = 'legacy' | 'rebrand' interface UnityThemeContextValue { theme: UnityTheme setTheme: (theme: UnityTheme) => void } const UnityThemeContext = createContext({ // Render legacy by default for backwards compatibility theme: 'legacy', // Intentional noop. Will be replaced by a set dispatcher from React // eslint-disable-next-line @typescript-eslint/no-empty-function setTheme: () => {}, }) export interface UnityThemeProviderProps { /** Initial theme. Can be changed at runtime via `useUnityTheme().setTheme`. */ theme?: UnityTheme /** * Element on which `data-uy-theme` is set. * - `undefined` (default): uses `document.documentElement` (``) * - A React ref: uses the referenced element * - A CSS selector string: uses `document.querySelector(selector)` * @default document.documentElement */ target?: RefObject | string } function resolveTarget( target: UnityThemeProviderProps['target'], ): HTMLElement | null { if (target === undefined) return document.documentElement if (typeof target === 'string') return document.querySelector(target) return target.current } export function UnityThemeProvider({ theme: initialTheme = 'legacy', target, children, }: PropsWithChildren) { const [theme, setTheme] = useState(initialTheme) useEffect(() => { const el = resolveTarget(target) if (!el) return el.dataset.uyTheme = theme return () => { delete el.dataset.uyTheme } }, [theme, target]) return ( {children} ) } /** * Returns the current theme and a setter to change it at runtime. * Returns `"legacy"` with a no-op setter when used outside a provider. */ export function useUnityTheme(): UnityThemeContextValue { return useContext(UnityThemeContext) }