ai-wpa/components/theme-provider.tsx

71 lines
1.7 KiB
TypeScript

'use client'
import { createContext, useContext, useEffect, useState } from 'react'
type Theme = 'dark' | 'light' | 'system'
type ThemeProviderProps = {
children: React.ReactNode
defaultTheme?: Theme
}
type ThemeProviderState = {
theme: Theme
setTheme: (theme: Theme) => void
}
const initialState: ThemeProviderState = {
theme: 'system',
setTheme: () => null,
}
const ThemeProviderContext = createContext<ThemeProviderState>(initialState)
export function ThemeProvider({ children, defaultTheme = 'system' }: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(defaultTheme)
const [mounted, setMounted] = useState(false)
// Only access localStorage after component has mounted on the client
useEffect(() => {
setMounted(true)
const storedTheme = localStorage.getItem('theme') as Theme
if (storedTheme) {
setTheme(storedTheme)
}
}, [])
useEffect(() => {
if (!mounted) return
const root = window.document.documentElement
root.classList.remove('light', 'dark')
if (theme === 'system') {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light'
root.classList.add(systemTheme)
return
}
root.classList.add(theme)
}, [theme, mounted])
const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem('theme', theme)
setTheme(theme)
},
}
return <ThemeProviderContext.Provider 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
}