Added system theming and fixed particles background to match theme.

This commit is contained in:
Tom Butcher 2025-06-01 23:45:50 +01:00
parent 60f62df55a
commit da07c882a5
3 changed files with 93 additions and 14 deletions

View File

@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React, { useState, useEffect, useMemo, useCallback } from 'react'
import Particles, { initParticlesEngine } from '@tsparticles/react'
import { loadSlim } from '@tsparticles/slim'
import { useThemeContext } from '../Dashboard/context/ThemeContext'
const ParticlesComponent = React.memo(({ options, particlesLoaded }) => {
return (
@ -17,6 +18,7 @@ ParticlesComponent.displayName = 'ParticlesComponent'
const AppParticles = () => {
const [init, setInit] = useState(false)
const { isDarkMode } = useThemeContext()
// this should be run only once per application lifetime
useEffect(() => {
@ -35,7 +37,7 @@ const AppParticles = () => {
() => ({
background: {
color: {
value: '#141414'
value: isDarkMode ? '#000000' : '#ffffff'
}
},
fpsLimit: 120,
@ -62,10 +64,10 @@ const AppParticles = () => {
},
particles: {
color: {
value: '#ffffff'
value: isDarkMode ? 'rgb(66, 66, 66)' : 'rgb(217, 217, 217)'
},
links: {
color: '#ffffff',
color: isDarkMode ? 'rgb(66, 66, 66)' : 'rgb(217, 217, 217)',
distance: 150,
enable: true,
opacity: 0.5,
@ -99,7 +101,7 @@ const AppParticles = () => {
},
detectRetina: true
}),
[]
[isDarkMode]
)
return (
<>

View File

@ -8,19 +8,32 @@ const { Title } = Typography
const { Option } = Select
const Settings = () => {
const { isDarkMode, toggleTheme, isCompact, toggleCompact } =
useThemeContext()
const {
isDarkMode,
toggleTheme,
isCompact,
toggleCompact,
isSystem,
toggleSystem
} = useThemeContext()
const [collapseState, updateCollapseState] = useCollapseState('Settings', {
appearance: true
})
const handleThemeChange = (value) => {
if (value === 'system') {
toggleSystem()
} else {
if (isSystem) {
toggleSystem()
}
if (value === 'dark' && !isDarkMode) {
toggleTheme()
} else if (value === 'light' && isDarkMode) {
toggleTheme()
}
}
}
const handleCompactChange = (value) => {
if (value === 'compact' && !isCompact) {
@ -30,6 +43,11 @@ const Settings = () => {
}
}
const getCurrentThemeValue = () => {
if (isSystem) return 'system'
return isDarkMode ? 'dark' : 'light'
}
return (
<div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}>
<Collapse
@ -71,12 +89,13 @@ const Settings = () => {
>
<Descriptions.Item label='Theme'>
<Select
value={isDarkMode ? 'dark' : 'light'}
value={getCurrentThemeValue()}
onChange={handleThemeChange}
style={{ width: '200px' }}
>
<Option value='light'>Light</Option>
<Option value='dark'>Dark</Option>
<Option value='system'>System</Option>
</Select>
</Descriptions.Item>
<Descriptions.Item label='UI Density'>

View File

@ -1,17 +1,73 @@
import React, { createContext, useContext, useState } from 'react'
import React, { createContext, useContext, useState, useEffect } from 'react'
import { theme } from 'antd'
import PropTypes from 'prop-types'
const ThemeContext = createContext()
export const ThemeProvider = ({ children }) => {
const [isDarkMode, setIsDarkMode] = useState(true)
const [isCompact, setIsCompact] = useState(false)
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)
}
@ -50,6 +106,8 @@ export const ThemeProvider = ({ children }) => {
toggleTheme,
isCompact,
toggleCompact,
isSystem,
toggleSystem,
themeConfig
}}
>