62 lines
1.5 KiB
TypeScript
62 lines
1.5 KiB
TypeScript
'use client'
|
|
|
|
import { createContext, useContext, useEffect, useState, useCallback } from 'react'
|
|
import type { ThemeMode } from '@/constants/theme'
|
|
|
|
interface IThemeContext {
|
|
mode: ThemeMode
|
|
toggle: () => void
|
|
setMode: (mode: ThemeMode) => void
|
|
}
|
|
|
|
const ThemeContext = createContext<IThemeContext>({
|
|
mode: 'dark',
|
|
toggle: () => {},
|
|
setMode: () => {},
|
|
})
|
|
|
|
export function useTheme() {
|
|
return useContext(ThemeContext)
|
|
}
|
|
|
|
export default function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
const [mode, setModeState] = useState<ThemeMode>('dark')
|
|
const [mounted, setMounted] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const stored = localStorage.getItem('inchstep-theme') as ThemeMode | null
|
|
if (stored === 'light' || stored === 'dark') {
|
|
setModeState(stored)
|
|
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
setModeState('dark')
|
|
} else {
|
|
setModeState('dark')
|
|
}
|
|
setMounted(true)
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (!mounted) return
|
|
document.documentElement.setAttribute('data-theme', mode)
|
|
localStorage.setItem('inchstep-theme', mode)
|
|
}, [mode, mounted])
|
|
|
|
const toggle = useCallback(() => {
|
|
setModeState(prev => (prev === 'light' ? 'dark' : 'light'))
|
|
}, [])
|
|
|
|
const setMode = useCallback((m: ThemeMode) => {
|
|
setModeState(m)
|
|
}, [])
|
|
|
|
if (!mounted) {
|
|
return <div style={{ visibility: 'hidden' }}>{children}</div>
|
|
}
|
|
|
|
return (
|
|
<ThemeContext.Provider value={{ mode, toggle, setMode }}>
|
|
{children}
|
|
</ThemeContext.Provider>
|
|
)
|
|
}
|