mirror of https://github.com/fantasticit/think.git
client: improve theme
support set dark, light and follow system
This commit is contained in:
parent
4891c278db
commit
ef8d3924b9
|
@ -1,17 +1,47 @@
|
||||||
import { IconMoon, IconSun } from '@douyinfe/semi-icons';
|
import { IconDesktop, IconMoon, IconSun } from '@douyinfe/semi-icons';
|
||||||
import { Button } from '@douyinfe/semi-ui';
|
import { Button, Dropdown } from '@douyinfe/semi-ui';
|
||||||
import { Tooltip } from 'components/tooltip';
|
import { Theme as ThemeState, ThemeEnum } from 'hooks/use-theme';
|
||||||
import { Theme as ThemeState } from 'hooks/use-theme';
|
import React, { useCallback } from 'react';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export const Theme = () => {
|
export const Theme = () => {
|
||||||
const { theme, toggle } = ThemeState.useHook();
|
const { userPrefer, toggle } = ThemeState.useHook();
|
||||||
const Icon = theme === 'dark' ? IconSun : IconMoon;
|
const Icon = userPrefer === 'dark' ? IconSun : IconMoon;
|
||||||
const text = theme === 'dark' ? '切换到亮色模式' : '切换到深色模式';
|
|
||||||
|
const setLight = useCallback(() => {
|
||||||
|
toggle(ThemeEnum.light);
|
||||||
|
}, [toggle]);
|
||||||
|
|
||||||
|
const setDark = useCallback(() => {
|
||||||
|
toggle(ThemeEnum.dark);
|
||||||
|
}, [toggle]);
|
||||||
|
|
||||||
|
const setSystem = useCallback(() => {
|
||||||
|
toggle(ThemeEnum.system);
|
||||||
|
}, [toggle]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip content={text} position="bottom">
|
<Dropdown
|
||||||
<Button onClick={toggle} icon={<Icon style={{ fontSize: 20 }} />} theme="borderless"></Button>
|
position="bottomRight"
|
||||||
</Tooltip>
|
trigger="click"
|
||||||
|
showTick
|
||||||
|
render={
|
||||||
|
<Dropdown.Menu>
|
||||||
|
<Dropdown.Item active={userPrefer === ThemeEnum.light} onClick={setLight}>
|
||||||
|
<IconSun />
|
||||||
|
亮色
|
||||||
|
</Dropdown.Item>
|
||||||
|
<Dropdown.Item active={userPrefer === ThemeEnum.dark} onClick={setDark}>
|
||||||
|
<IconMoon />
|
||||||
|
夜色
|
||||||
|
</Dropdown.Item>
|
||||||
|
<Dropdown.Item active={userPrefer === ThemeEnum.system} onClick={setSystem}>
|
||||||
|
<IconDesktop />
|
||||||
|
系统
|
||||||
|
</Dropdown.Item>
|
||||||
|
</Dropdown.Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button icon={<Icon style={{ fontSize: 20 }} />} theme="borderless"></Button>
|
||||||
|
</Dropdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,33 +1,31 @@
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { createGlobalHook } from './create-global-hook';
|
import { createGlobalHook } from './create-global-hook';
|
||||||
|
|
||||||
export enum ThemeEnum {
|
export enum ThemeEnum {
|
||||||
'dark' = 'dark',
|
'dark' = 'dark',
|
||||||
'light' = 'light',
|
'light' = 'light',
|
||||||
|
'system' = 'system',
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncSystemTheme() {
|
||||||
|
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
function matchMode(e) {
|
||||||
|
if (e.matches) {
|
||||||
|
document.body.setAttribute('theme-mode', 'dark');
|
||||||
|
} else {
|
||||||
|
document.body.setAttribute('theme-mode', 'light');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matchMode(mql);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useThemeHook = () => {
|
const useThemeHook = () => {
|
||||||
const [theme, setTheme] = useState(ThemeEnum.light);
|
const $remove = useRef<() => void>();
|
||||||
|
const [theme, setTheme] = useState(ThemeEnum.system);
|
||||||
|
const [userPrefer, setUserPrefer] = useState(ThemeEnum.system);
|
||||||
|
|
||||||
const toggle = useCallback(() => {
|
const followSystem = useCallback(() => {
|
||||||
const nextTheme = theme === 'dark' ? ThemeEnum.light : ThemeEnum.dark;
|
|
||||||
setTheme(nextTheme);
|
|
||||||
}, [theme]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const body = document.body;
|
|
||||||
if (theme === 'dark') {
|
|
||||||
body.setAttribute('theme-mode', 'dark');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (theme === 'light') {
|
|
||||||
body.setAttribute('theme-mode', 'light');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}, [theme]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
|
||||||
function matchMode(e) {
|
function matchMode(e) {
|
||||||
|
@ -41,15 +39,58 @@ const useThemeHook = () => {
|
||||||
matchMode(mql);
|
matchMode(mql);
|
||||||
mql.addEventListener('change', matchMode);
|
mql.addEventListener('change', matchMode);
|
||||||
|
|
||||||
return () => {
|
const remove = () => {
|
||||||
mql.removeEventListener('change', matchMode);
|
mql.removeEventListener('change', matchMode);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$remove.current = remove;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggle = useCallback(
|
||||||
|
(nextTheme: ThemeEnum) => {
|
||||||
|
setUserPrefer(nextTheme);
|
||||||
|
setTheme(nextTheme);
|
||||||
|
if (nextTheme !== ThemeEnum.system) {
|
||||||
|
$remove.current && $remove.current();
|
||||||
|
} else {
|
||||||
|
followSystem();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[followSystem]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const body = document.body;
|
||||||
|
|
||||||
|
switch (theme) {
|
||||||
|
case ThemeEnum.light:
|
||||||
|
body.setAttribute('theme-mode', 'light');
|
||||||
|
return;
|
||||||
|
|
||||||
|
case ThemeEnum.dark:
|
||||||
|
body.setAttribute('theme-mode', 'dark');
|
||||||
|
return;
|
||||||
|
|
||||||
|
case ThemeEnum.system:
|
||||||
|
default:
|
||||||
|
syncSystemTheme();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (theme === ThemeEnum.system) {
|
||||||
|
followSystem();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
userPrefer,
|
||||||
theme,
|
theme,
|
||||||
toggle,
|
toggle,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Theme = createGlobalHook<{ theme: ThemeEnum; toggle: () => void }>(useThemeHook);
|
export const Theme =
|
||||||
|
createGlobalHook<{ userPrefer: ThemeEnum; theme: ThemeEnum; toggle: (nextTheme: ThemeEnum) => void }>(useThemeHook);
|
||||||
|
|
Loading…
Reference in New Issue