|
import { createContext, useContext, useEffect, useState } from "react"; |
|
|
|
type Theme = "dark" | "light"; |
|
|
|
type ThemeProviderProps = { |
|
children: React.ReactNode; |
|
defaultTheme?: Theme; |
|
storageKey?: string; |
|
}; |
|
|
|
type ThemeProviderState = { |
|
theme: Theme; |
|
setTheme: (theme: Theme) => void; |
|
}; |
|
|
|
const initialState: ThemeProviderState = { |
|
theme: "light", |
|
setTheme: () => null, |
|
}; |
|
|
|
const ThemeProviderContext = createContext<ThemeProviderState>(initialState); |
|
|
|
export function ThemeProvider({ |
|
children, |
|
defaultTheme = "light", |
|
storageKey = "ui-theme", |
|
...props |
|
}: ThemeProviderProps) { |
|
const [theme, setTheme] = useState<Theme>(() => { |
|
if (typeof window !== "undefined") { |
|
return (localStorage.getItem(storageKey) as Theme) || defaultTheme; |
|
} |
|
return defaultTheme; |
|
}); |
|
|
|
useEffect(() => { |
|
const root = window.document.documentElement; |
|
root.classList.remove("light", "dark"); |
|
root.classList.add(theme); |
|
}, [theme]); |
|
|
|
const value = { |
|
theme, |
|
setTheme: (theme: Theme) => { |
|
localStorage.setItem(storageKey, theme); |
|
setTheme(theme); |
|
}, |
|
}; |
|
|
|
return ( |
|
<ThemeProviderContext.Provider {...props} value={value}> |
|
{children} |
|
</ThemeProviderContext.Provider> |
|
); |
|
} |
|
|
|
export const useTheme = () => { |
|
const context = useContext(ThemeProviderContext); |
|
|
|
if (context === undefined) |
|
throw new Error("useTheme must be used within a ThemeProvider"); |
|
|
|
return context; |
|
}; |