Added theme toggle

+Added dark-mode and light-mode component icons
+Added ThemeToggle in the toolbar
+Added ThemeManager and ThemeContext
This commit is contained in:
izawartka 2025-08-15 21:23:46 +02:00
parent 0d2580f23e
commit 285c5b04e8
9 changed files with 88 additions and 11 deletions

View File

@ -10,23 +10,26 @@ import DistanceMeterContext from '../contexts/DistanceMeterContext';
import {GradientsManager} from "./GradientsManager"; import {GradientsManager} from "./GradientsManager";
import SideMenuContext from '../contexts/SideMenuContext'; import SideMenuContext from '../contexts/SideMenuContext';
import SceneryManager from './SceneryManager'; import SceneryManager from './SceneryManager';
import ThemeManager from './ThemeManager';
function App() { function App() {
const [sideMenuOpen, setSideMenuOpen] = useState(false); const [sideMenuOpen, setSideMenuOpen] = useState(false);
const [distancePoints, setDistancePoints] = useState(null); const [distancePoints, setDistancePoints] = useState(null);
return ( return (
<SettingsManager> <ThemeManager>
<SceneryManager> <SettingsManager>
<GradientsManager> <SceneryManager>
<DistanceMeterContext.Provider value={{distancePoints, setDistancePoints}}> <GradientsManager>
<SideMenuContext.Provider value={{sideMenuOpen, setSideMenuOpen}}> <DistanceMeterContext.Provider value={{distancePoints, setDistancePoints}}>
<AppContent /> <SideMenuContext.Provider value={{sideMenuOpen, setSideMenuOpen}}>
</SideMenuContext.Provider> <AppContent />
</DistanceMeterContext.Provider> </SideMenuContext.Provider>
</GradientsManager> </DistanceMeterContext.Provider>
</SceneryManager> </GradientsManager>
</SettingsManager> </SceneryManager>
</SettingsManager>
</ThemeManager>
); );
} }

View File

@ -0,0 +1,20 @@
import { useState } from "react";
import ThemeContext from "../contexts/ThemeContext";
export default function ThemeManager(props) {
const [ lightMode, setLightMode ] = useState(false);
if(lightMode === true) {
document.body.classList.add('light-mode');
} else {
document.body.classList.remove('light-mode');
}
return (
<ThemeContext.Provider value={{
lightMode, setLightMode
}}>
{ props.children }
</ThemeContext.Provider>
);
}

View File

@ -0,0 +1,19 @@
import { useContext } from 'react';
import { LightModeIcon, DarkModeIcon } from '../../icons';
import ThemeContext from '../../contexts/ThemeContext';
export default function ThemeToggle() {
const { setLightMode, lightMode } = useContext(ThemeContext);
const toggleLightMode = () => {
setLightMode((prev) => !prev);
}
return (
<div className='theme-toggle'>
<button onClick={toggleLightMode} className='invis-button'>
{lightMode ? <LightModeIcon /> : <DarkModeIcon />}
</button>
</div>
);
}

View File

@ -54,10 +54,12 @@
width: 2rem; width: 2rem;
} }
.theme-toggle g,
.side-menu-toggle g { .side-menu-toggle g {
stroke: #aaa; stroke: #aaa;
} }
.theme-toggle:hover g,
.side-menu-toggle:hover g { .side-menu-toggle:hover g {
stroke: #fff; stroke: #fff;
} }

View File

@ -4,6 +4,7 @@ import CustomFileSelect from './CustomFileSelect';
import SideMenuToggle from './SideMenuToggle'; import SideMenuToggle from './SideMenuToggle';
import SceneryList from './SceneryList'; import SceneryList from './SceneryList';
import ElectrificationStepMode from './ElectrificationStepMode'; import ElectrificationStepMode from './ElectrificationStepMode';
import ThemeToggle from './ThemeToggle';
export default function Toolbar() { export default function Toolbar() {
return ( return (
@ -14,6 +15,7 @@ export default function Toolbar() {
<CustomFileSelect /> <CustomFileSelect />
<ElectrificationStepMode /> <ElectrificationStepMode />
<div className='toolbar-spacer'></div> <div className='toolbar-spacer'></div>
<ThemeToggle />
<SideMenuToggle /> <SideMenuToggle />
</div> </div>
<div className='loading-indicator-cont'> <div className='loading-indicator-cont'>

View File

@ -0,0 +1,4 @@
import { createContext } from 'react';
const ThemeContext = createContext();
export default ThemeContext;

View File

@ -1,4 +1,6 @@
/* Toolbar Icons */ /* Toolbar Icons */
export { DarkModeIcon } from "./toolbar/dark-mode";
export { LightModeIcon } from "./toolbar/light-mode";
export { SideMenuIcon } from "./toolbar/side-menu"; export { SideMenuIcon } from "./toolbar/side-menu";
/* TrackHoverInfo Icons */ /* TrackHoverInfo Icons */

View File

@ -0,0 +1,7 @@
export const DarkModeIcon = ({color = '#aaa', strokeWidth = 9}) => (
<svg version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke={color} strokeLinecap="round" strokeLinejoin="round" strokeWidth={strokeWidth}>
<path d="m52.52 19c-17.021 0.12272-30.775 13.978-30.775 31 1.6e-4 17.109 13.891 31 31 31 10.113 3.5e-5 19.594-4.936 25.394-13.221 0.46456-0.66594-0.01618-1.5787-0.82812-1.5723-0.06911 5.69e-4 -0.13791-7.4e-5 -0.20703 0h-2e-3c-16.027-0.0012-28.998-12.973-28.998-29-5.3e-5 -5.951 1.831-11.758 5.2441-16.633 0.41109-0.63002-0.01926-1.5353-0.82807-1.5742z" />
</g>
</svg>
);

View File

@ -0,0 +1,18 @@
export const LightModeIcon = ({color = '#aaa', strokeWidth = 10}) => (
<svg version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g stroke={color} fill="none">
<circle cx="50" cy="50" r="14" strokeWidth={strokeWidth * 0.8} />
<g strokeLinecap="round" strokeWidth={strokeWidth} >
<path d="m14 50h7"/>
<path d="m79 50h7"/>
<path d="m24.544 75.456 4.9497-4.9497"/>
<path d="m70.506 29.494 4.9497-4.9497"/>
<path d="m50 86v-7"/>
<path d="m50 21v-7"/>
<path d="m75.456 75.456-4.9497-4.9497"/>
<path d="m29.494 29.494-4.9497-4.9497"/>
</g>
</g>
</svg>
);