52 lines
1.4 KiB
TypeScript
52 lines
1.4 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
|
|
export type Theme = 'dark' | 'light' | 'system'
|
|
|
|
export function useTheme() {
|
|
const [theme, setTheme] = useState<Theme>(() => {
|
|
const saved = localStorage.getItem('theme') as Theme
|
|
return saved || 'dark' // Mặc định dark theme cho ứng dụng
|
|
})
|
|
|
|
useEffect(() => {
|
|
const root = window.document.documentElement
|
|
|
|
const applyTheme = (targetTheme: Theme) => {
|
|
root.classList.remove('light', 'dark')
|
|
|
|
if (targetTheme === 'system') {
|
|
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
? 'dark'
|
|
: 'light'
|
|
root.classList.add(systemTheme)
|
|
} else {
|
|
root.classList.add(targetTheme)
|
|
}
|
|
}
|
|
|
|
applyTheme(theme)
|
|
localStorage.setItem('theme', theme)
|
|
}, [theme])
|
|
|
|
// System listener
|
|
useEffect(() => {
|
|
if (theme !== 'system') return
|
|
|
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
const handleChange = () => {
|
|
const root = window.document.documentElement
|
|
root.classList.remove('light', 'dark')
|
|
root.classList.add(mediaQuery.matches ? 'dark' : 'light')
|
|
}
|
|
|
|
mediaQuery.addEventListener('change', handleChange)
|
|
return () => mediaQuery.removeEventListener('change', handleChange)
|
|
}, [theme])
|
|
|
|
const toggleTheme = () => {
|
|
setTheme((prev) => (prev === 'dark' ? 'light' : 'dark'))
|
|
}
|
|
|
|
return { theme, setTheme, toggleTheme }
|
|
}
|