Tom Butcher 78dc567a8f
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
Implemented software update installation.
2026-06-21 15:18:04 +01:00

189 lines
4.8 KiB
JavaScript

import {
createContext,
useCallback,
useContext,
useState,
useEffect
} from 'react'
import { theme } from 'antd'
import PropTypes from 'prop-types'
const ThemeContext = createContext()
const COLORS = {
colorPrimary: '#0091FF',
colorSuccess: '#30D158',
colorWarning: '#FF9230',
colorError: '#FF4245',
colorInfo: '#0A84FF',
colorLink: '#5AC8F5',
colorCyan: '#5AC8F5',
colorPink: '#FF69B4',
colorPurple: '#6B5DFF',
colorMagenta: '#FF00FF',
colorVolcano: '#FF4500'
}
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 setThemeMode = useCallback((value) => {
if (value === 'system') {
setIsSystem(true)
setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches)
return
}
setIsSystem(false)
setIsDarkMode(value === 'dark')
}, [])
const setDensityMode = useCallback((value) => {
setIsCompact(value === 'compact')
}, [])
const getThemeAlgorithm = () => {
var baseAlgorithm
if (isDarkMode == true) {
baseAlgorithm = theme.darkAlgorithm
} else {
baseAlgorithm = theme.defaultAlgorithm
}
return isCompact ? [theme.compactAlgorithm, baseAlgorithm] : [baseAlgorithm]
}
const getColors = () => {
return COLORS
}
// Set CSS custom properties for theme colors
useEffect(() => {
const root = document.documentElement
root.style.setProperty('--color-primary', COLORS.colorPrimary)
root.style.setProperty('--color-success', COLORS.colorSuccess)
root.style.setProperty('--color-warning', COLORS.colorWarning)
root.style.setProperty('--color-error', COLORS.colorError)
root.style.setProperty('--color-info', COLORS.colorInfo)
root.style.setProperty('--color-link', COLORS.colorLink)
root.style.setProperty('--color-cyan', COLORS.colorCyan)
root.style.setProperty('--color-pink', COLORS.colorPink)
root.style.setProperty('--color-purple', COLORS.colorPurple)
root.style.setProperty('--color-magenta', COLORS.colorMagenta)
root.style.setProperty('--color-volcano', COLORS.colorVolcano)
root.style.setProperty(
'--layout-header-bg',
isDarkMode ? '#141414' : '#ffffff'
)
}, [isDarkMode])
const themeConfig = {
algorithm: getThemeAlgorithm(),
token: {
...COLORS,
borderRadius: '12px'
},
components: {
Layout: {
headerBg: isDarkMode ? '#141414' : '#ffffff'
}
}
}
return (
<ThemeContext.Provider
value={{
isDarkMode,
toggleTheme,
isCompact,
toggleCompact,
isSystem,
toggleSystem,
setThemeMode,
setDensityMode,
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
}