mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-24 22:09:08 +08:00
feat: add auto mode in theme selection
This commit is contained in:
@@ -21,6 +21,13 @@ const Home: NextPage = () => {
|
|||||||
>
|
>
|
||||||
current theme mode :{mode}(click to change)
|
current theme mode :{mode}(click to change)
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
changeMode('auto');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
click to set "auto" mode
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ import {
|
|||||||
Global,
|
Global,
|
||||||
css,
|
css,
|
||||||
} from '@emotion/react';
|
} from '@emotion/react';
|
||||||
import { createContext, useEffect, useRef, useState } from 'react';
|
import { createContext, useEffect, useState } from 'react';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { ThemeMode, ThemeProviderProps, ThemeProviderValue } from './types';
|
import {
|
||||||
|
Theme,
|
||||||
|
ThemeMode,
|
||||||
|
ThemeProviderProps,
|
||||||
|
ThemeProviderValue,
|
||||||
|
} from './types';
|
||||||
import { lightTheme, darkTheme, globalThemeConstant } from './theme';
|
import { lightTheme, darkTheme, globalThemeConstant } from './theme';
|
||||||
import { SystemTheme, LocalStorageThemeMode } from '@/styles/utils';
|
import { SystemTheme, localStorageThemeMode } from '@/styles/utils';
|
||||||
|
|
||||||
export const ThemeContext = createContext<ThemeProviderValue>({
|
export const ThemeContext = createContext<ThemeProviderValue>({
|
||||||
mode: 'light',
|
mode: 'light',
|
||||||
@@ -16,57 +21,53 @@ export const ThemeContext = createContext<ThemeProviderValue>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ThemeProvider = ({
|
export const ThemeProvider = ({
|
||||||
defaultThemeMode = 'light',
|
defaultTheme = 'light',
|
||||||
children,
|
children,
|
||||||
}: PropsWithChildren<ThemeProviderProps>) => {
|
}: PropsWithChildren<ThemeProviderProps>) => {
|
||||||
const { current: localStorageThemeMode } = useRef(
|
const [theme, setTheme] = useState<Theme>(defaultTheme);
|
||||||
new LocalStorageThemeMode()
|
const [mode, setMode] = useState<ThemeMode>('auto');
|
||||||
);
|
|
||||||
|
|
||||||
const [mode, setMode] = useState<ThemeMode>(defaultThemeMode);
|
|
||||||
const theme = mode === 'dark' ? darkTheme : lightTheme;
|
|
||||||
|
|
||||||
|
const themeStyle = theme === 'light' ? lightTheme : darkTheme;
|
||||||
const changeMode = (themeMode: ThemeMode) => {
|
const changeMode = (themeMode: ThemeMode) => {
|
||||||
setMode(themeMode);
|
themeMode !== mode && setMode(themeMode);
|
||||||
// Remember the theme mode which user selected for next time
|
// Remember the theme mode which user selected for next time
|
||||||
localStorageThemeMode.set(themeMode);
|
localStorageThemeMode.set(themeMode);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const systemTheme = new SystemTheme();
|
const systemTheme = new SystemTheme();
|
||||||
|
|
||||||
// TODO: System theme mode and user theme mode which selected need to be prioritized
|
|
||||||
// If user has selected a theme mode, use it
|
|
||||||
const selectedThemeMode = localStorageThemeMode.get();
|
const selectedThemeMode = localStorageThemeMode.get();
|
||||||
if (selectedThemeMode) {
|
|
||||||
defaultThemeMode !== selectedThemeMode && setMode(selectedThemeMode);
|
const themeMode = selectedThemeMode || mode;
|
||||||
|
if (themeMode === 'auto') {
|
||||||
|
setTheme(systemTheme.get());
|
||||||
} else {
|
} else {
|
||||||
// If user has not selected a theme mode, use system theme
|
setTheme(themeMode);
|
||||||
const systemThemeMode = systemTheme.get();
|
|
||||||
defaultThemeMode !== systemThemeMode && setMode(systemThemeMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When system theme changed, change the theme mode
|
// When system theme changed, change the theme mode
|
||||||
systemTheme.onChange(() => {
|
systemTheme.onChange(() => {
|
||||||
// TODO: There may be should be provided a way to let user choose whether to
|
// TODO: There may be should be provided a way to let user choose whether to
|
||||||
setMode(systemTheme.get());
|
if (mode === 'auto') {
|
||||||
|
setTheme(systemTheme.get());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
systemTheme.dispose();
|
systemTheme.dispose();
|
||||||
};
|
};
|
||||||
}, []);
|
}, [mode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeContext.Provider value={{ mode, changeMode, theme }}>
|
<ThemeContext.Provider value={{ mode, changeMode, theme: themeStyle }}>
|
||||||
<Global
|
<Global
|
||||||
styles={css`
|
styles={css`
|
||||||
:root {
|
:root {
|
||||||
${globalThemeConstant(theme)}
|
${globalThemeConstant(themeStyle)}
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
<EmotionThemeProvider theme={theme}>{children}</EmotionThemeProvider>
|
<EmotionThemeProvider theme={themeStyle}>{children}</EmotionThemeProvider>
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,8 @@
|
|||||||
export type ThemeMode = 'light' | 'dark';
|
export type Theme = 'light' | 'dark';
|
||||||
|
export type ThemeMode = Theme | 'auto';
|
||||||
|
|
||||||
export type ThemeProviderProps = {
|
export type ThemeProviderProps = {
|
||||||
defaultThemeMode?: ThemeMode;
|
defaultTheme?: Theme;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ThemeProviderValue = {
|
export type ThemeProviderValue = {
|
||||||
|
|||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
import { ThemeMode } from '@/styles/types';
|
import { Theme, ThemeMode } from '@/styles/types';
|
||||||
|
|
||||||
export class SystemTheme {
|
export class SystemTheme {
|
||||||
media: MediaQueryList = window.matchMedia('(prefers-color-scheme: light)');
|
media: MediaQueryList = window.matchMedia('(prefers-color-scheme: light)');
|
||||||
@@ -11,7 +11,7 @@ export class SystemTheme {
|
|||||||
this.media.addEventListener('change', this.eventHandler);
|
this.media.addEventListener('change', this.eventHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
get = (): ThemeMode => {
|
get = (): Theme => {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return 'light';
|
return 'light';
|
||||||
}
|
}
|
||||||
@@ -37,3 +37,5 @@ export class LocalStorageThemeMode {
|
|||||||
localStorage.setItem(this.name, mode);
|
localStorage.setItem(this.name, mode);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const localStorageThemeMode = new LocalStorageThemeMode();
|
||||||
|
|||||||
Reference in New Issue
Block a user