Skip to content
Snippets Groups Projects
Commit 933eb70b authored by Dennis Collaris's avatar Dennis Collaris
Browse files

feat: system light/dark theme

parent 54e7ac1c
No related branches found
Tags v1.115.2
1 merge request!188feat: system light/dark theme
Pipeline #138950 passed
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
useQuerybuilderSettings, useQuerybuilderSettings,
useSessionCache, useSessionCache,
} from '@graphpolaris/shared/lib/data-access'; } from '@graphpolaris/shared/lib/data-access';
import { setCurrentTheme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { resetGraphQueryResults, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice'; import { resetGraphQueryResults, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
import { Query2BackendQuery, QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder'; import { Query2BackendQuery, QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder';
import { Navbar } from '../components/navbar/navbar'; import { Navbar } from '../components/navbar/navbar';
...@@ -52,6 +53,10 @@ export function App(props: App) { ...@@ -52,6 +53,10 @@ export function App(props: App) {
} }
}, [props]); }, [props]);
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
dispatch(setCurrentTheme(event.matches));
});
const [authCheck, setAuthCheck] = useState(false); const [authCheck, setAuthCheck] = useState(false);
const [tab, setTab] = useState<SideNavTab>('Schema'); const [tab, setTab] = useState<SideNavTab>('Schema');
// const [visFullSize, setVisFullSize] = useState<boolean>(false); // const [visFullSize, setVisFullSize] = useState<boolean>(false);
......
...@@ -22,12 +22,16 @@ const ColorMode = () => { ...@@ -22,12 +22,16 @@ const ColorMode = () => {
// Function to toggle the theme // Function to toggle the theme
const toggleTheme = () => { const toggleTheme = () => {
const themes = [Theme.light, Theme.dark]; const themes = [
Theme.system,
Theme.light,
Theme.dark,
];
const newTheme = themes[(themes.indexOf(config.theme) + 1) % themes.length]; const newTheme = themes[(themes.indexOf(config.theme) + 1) % themes.length];
dispatch(setTheme(newTheme)); dispatch(setTheme(newTheme));
}; };
const iconComponent = config.theme === Theme.dark ? 'icon-[ic--baseline-dark-mode]' : 'icon-[ic--baseline-light-mode]'; const iconComponent = config.theme === Theme.dark ? 'icon-[ic--baseline-dark-mode]' : config.theme === Theme.light ? 'icon-[ic--baseline-light-mode]' : 'icon-[ic--baseline-auto-mode]';
return ( return (
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
...@@ -36,7 +40,7 @@ const ColorMode = () => { ...@@ -36,7 +40,7 @@ const ColorMode = () => {
<Button variant="ghost" size="sm" iconComponent={iconComponent} onClick={toggleTheme} /> <Button variant="ghost" size="sm" iconComponent={iconComponent} onClick={toggleTheme} />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>{`Switch to ${config.theme === Theme.dark ? 'light' : 'dark'}-mode`}</p> <p>{`Currently on ${config.theme.split('-')[0]} theme`}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
......
...@@ -2,6 +2,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; ...@@ -2,6 +2,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from './store'; import type { RootState } from './store';
export enum Theme { export enum Theme {
system = 'system-mode',
light = 'light-mode', light = 'light-mode',
dark = 'dark-mode', dark = 'dark-mode',
} }
...@@ -9,6 +10,7 @@ export enum Theme { ...@@ -9,6 +10,7 @@ export enum Theme {
// Define the initial state using that type // Define the initial state using that type
export type ConfigStateI = { export type ConfigStateI = {
theme: Theme; theme: Theme;
currentTheme: Theme;
autoSendQueries: boolean; autoSendQueries: boolean;
errors: string[]; errors: string[];
warnings: string[]; warnings: string[];
...@@ -16,7 +18,8 @@ export type ConfigStateI = { ...@@ -16,7 +18,8 @@ export type ConfigStateI = {
successes: string[]; successes: string[];
}; };
export const initialState: ConfigStateI = { export const initialState: ConfigStateI = {
theme: localStorage.getItem('theme') as Theme ?? Theme.light, theme: localStorage.getItem('theme') as Theme ?? Theme.system,
currentTheme: resolveTheme(localStorage.getItem('theme') as Theme ?? Theme.system),
autoSendQueries: true, autoSendQueries: true,
errors: [], errors: [],
warnings: [], warnings: [],
...@@ -24,6 +27,14 @@ export const initialState: ConfigStateI = { ...@@ -24,6 +27,14 @@ export const initialState: ConfigStateI = {
successes: [], successes: [],
}; };
function resolveTheme(theme: Theme) {
if (theme == Theme.system) {
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
return prefersDarkMode ? Theme.dark : Theme.light;
}
return theme
}
export const configSlice = createSlice({ export const configSlice = createSlice({
name: 'config', name: 'config',
// `createSlice` will infer the state type from the `initialState` argument // `createSlice` will infer the state type from the `initialState` argument
...@@ -58,11 +69,17 @@ export const configSlice = createSlice({ ...@@ -58,11 +69,17 @@ export const configSlice = createSlice({
setTheme: (state, action: PayloadAction<Theme>) => { setTheme: (state, action: PayloadAction<Theme>) => {
localStorage.setItem('theme', action.payload); localStorage.setItem('theme', action.payload);
state.theme = action.payload; state.theme = action.payload;
state.currentTheme = resolveTheme(action.payload);
},
setCurrentTheme: (state, action: PayloadAction<boolean>) => {
if (state.theme === Theme.system) {
state.currentTheme = action.payload ? Theme.dark : Theme.light;
}
}, },
}, },
}); });
export const { addError, removeLastError, addWarning, removeLastWarning, addSuccess, removeLastSuccess, addInfo, removeLastInfo, setTheme } = export const { addError, removeLastError, addWarning, removeLastWarning, addSuccess, removeLastSuccess, addInfo, removeLastInfo, setTheme, setCurrentTheme } =
configSlice.actions; configSlice.actions;
// Other code such as selectors can use the imported `RootState` type // Other code such as selectors can use the imported `RootState` type
......
...@@ -43,7 +43,7 @@ export const NLPixi = forwardRef((props: Props, refExternal) => { ...@@ -43,7 +43,7 @@ export const NLPixi = forwardRef((props: Props, refExternal) => {
useEffect(() => { useEffect(() => {
update(); update();
}, [globalConfig.theme]); }, [globalConfig.currentTheme]);
const app = useMemo( const app = useMemo(
() => () =>
...@@ -318,7 +318,7 @@ export const NLPixi = forwardRef((props: Props, refExternal) => { ...@@ -318,7 +318,7 @@ export const NLPixi = forwardRef((props: Props, refExternal) => {
getBackgroundColor() { getBackgroundColor() {
// Colors corresponding to .bg-light class // Colors corresponding to .bg-light class
return globalConfig.theme === Theme.dark ? 0x121621 : 0xffffff; return globalConfig.currentTheme === Theme.dark ? 0x121621 : 0xffffff;
}, },
})); }));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment