|
import React, { createContext, useContext, useEffect, useState } from 'react'; |
|
|
|
type Theme = 'dark' | 'light' | 'system'; |
|
|
|
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 = 'vite-ui-theme', |
|
...props |
|
}: ThemeProviderProps) { |
|
const [theme, setTheme] = useState<Theme>( |
|
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme, |
|
); |
|
|
|
useEffect(() => { |
|
const root = window.document.documentElement; |
|
root.classList.remove('light', 'dark'); |
|
localStorage.setItem(storageKey, theme); |
|
root.classList.add(theme); |
|
}, [storageKey, theme]); |
|
|
|
return ( |
|
<ThemeProviderContext.Provider |
|
{...props} |
|
value={{ |
|
theme, |
|
setTheme, |
|
}} |
|
> |
|
{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; |
|
}; |
|
|
|
export const useIsDarkTheme = () => { |
|
const { theme } = useTheme(); |
|
|
|
return theme === 'dark'; |
|
}; |
|
|