146 lines
3.4 KiB
JavaScript
146 lines
3.4 KiB
JavaScript
import { createContext, useContext, useState, useEffect } from 'react'
|
|
import { theme } from 'antd'
|
|
import PropTypes from 'prop-types'
|
|
|
|
const ThemeContext = createContext()
|
|
|
|
export const ThemeProvider = ({ children }) => {
|
|
const [isSystem, setIsSystem] = useState(() => {
|
|
const savedSystem = sessionStorage.getItem('isSystem')
|
|
return savedSystem ? JSON.parse(savedSystem) : true
|
|
})
|
|
const [isDarkMode, setIsDarkMode] = useState(() => {
|
|
const savedTheme = sessionStorage.getItem('isDarkMode')
|
|
return savedTheme ? JSON.parse(savedTheme) : true
|
|
})
|
|
const [isCompact, setIsCompact] = useState(() => {
|
|
const savedCompact = sessionStorage.getItem('isCompact')
|
|
return savedCompact ? JSON.parse(savedCompact) : false
|
|
})
|
|
|
|
// System theme detection
|
|
useEffect(() => {
|
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
|
|
const handleSystemThemeChange = (e) => {
|
|
if (isSystem) {
|
|
setIsDarkMode(e.matches)
|
|
}
|
|
}
|
|
|
|
// Set initial value
|
|
if (isSystem) {
|
|
setIsDarkMode(mediaQuery.matches)
|
|
}
|
|
|
|
// Add listener
|
|
mediaQuery.addEventListener('change', handleSystemThemeChange)
|
|
|
|
// Cleanup
|
|
return () =>
|
|
mediaQuery.removeEventListener('change', handleSystemThemeChange)
|
|
}, [isSystem])
|
|
|
|
useEffect(() => {
|
|
sessionStorage.setItem('isSystem', JSON.stringify(isSystem))
|
|
}, [isSystem])
|
|
|
|
useEffect(() => {
|
|
sessionStorage.setItem('isDarkMode', JSON.stringify(isDarkMode))
|
|
}, [isDarkMode])
|
|
|
|
useEffect(() => {
|
|
sessionStorage.setItem('isCompact', JSON.stringify(isCompact))
|
|
}, [isCompact])
|
|
|
|
const toggleTheme = () => {
|
|
if (isSystem) {
|
|
setIsSystem(false)
|
|
}
|
|
setIsDarkMode(!isDarkMode)
|
|
}
|
|
|
|
const toggleSystem = () => {
|
|
setIsSystem(!isSystem)
|
|
if (!isSystem) {
|
|
// When enabling system theme, update to match system preference
|
|
setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
}
|
|
}
|
|
|
|
const toggleCompact = () => {
|
|
setIsCompact(!isCompact)
|
|
}
|
|
|
|
const getThemeAlgorithm = () => {
|
|
var baseAlgorithm
|
|
if (isDarkMode == true) {
|
|
baseAlgorithm = theme.darkAlgorithm
|
|
} else {
|
|
baseAlgorithm = theme.defaultAlgorithm
|
|
}
|
|
return isCompact ? [theme.compactAlgorithm, baseAlgorithm] : [baseAlgorithm]
|
|
}
|
|
|
|
const colors = {
|
|
colorPrimary: '#0091FF',
|
|
colorSuccess: '#30D158',
|
|
colorWarning: '#FF9230',
|
|
colorError: '#FF4245',
|
|
colorInfo: '#0A84FF',
|
|
colorLink: '#5AC8F5',
|
|
colorCyan: '#5AC8F5',
|
|
colorPink: '#FF69B4',
|
|
colorPurple: '#800080',
|
|
colorMagenta: '#FF00FF',
|
|
colorVolcano: '#FF4500'
|
|
}
|
|
|
|
const getColors = () => {
|
|
return colors
|
|
}
|
|
|
|
const themeConfig = {
|
|
algorithm: getThemeAlgorithm(),
|
|
token: {
|
|
...colors,
|
|
borderRadius: '12px'
|
|
},
|
|
components: {
|
|
Layout: {
|
|
headerBg: isDarkMode ? '#141414' : '#ffffff'
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<ThemeContext.Provider
|
|
value={{
|
|
isDarkMode,
|
|
toggleTheme,
|
|
isCompact,
|
|
toggleCompact,
|
|
isSystem,
|
|
toggleSystem,
|
|
getColors,
|
|
themeConfig
|
|
}}
|
|
>
|
|
{children}
|
|
</ThemeContext.Provider>
|
|
)
|
|
}
|
|
|
|
ThemeProvider.propTypes = {
|
|
children: PropTypes.node.isRequired
|
|
}
|
|
|
|
// eslint-disable-next-line react-refresh/only-export-components
|
|
export const useThemeContext = () => {
|
|
const context = useContext(ThemeContext)
|
|
if (!context) {
|
|
throw new Error('useThemeContext must be used within a ThemeProvider')
|
|
}
|
|
return context
|
|
}
|