diff --git a/packages/client/src/components/theme/index.tsx b/packages/client/src/components/theme/index.tsx
index 88f3e9f4..a8b656da 100644
--- a/packages/client/src/components/theme/index.tsx
+++ b/packages/client/src/components/theme/index.tsx
@@ -1,17 +1,47 @@
-import { IconMoon, IconSun } from '@douyinfe/semi-icons';
-import { Button } from '@douyinfe/semi-ui';
-import { Tooltip } from 'components/tooltip';
-import { Theme as ThemeState } from 'hooks/use-theme';
-import React from 'react';
+import { IconDesktop, IconMoon, IconSun } from '@douyinfe/semi-icons';
+import { Button, Dropdown } from '@douyinfe/semi-ui';
+import { Theme as ThemeState, ThemeEnum } from 'hooks/use-theme';
+import React, { useCallback } from 'react';
export const Theme = () => {
- const { theme, toggle } = ThemeState.useHook();
- const Icon = theme === 'dark' ? IconSun : IconMoon;
- const text = theme === 'dark' ? '切换到亮色模式' : '切换到深色模式';
+ const { userPrefer, toggle } = ThemeState.useHook();
+ const Icon = userPrefer === 'dark' ? IconSun : IconMoon;
+
+ const setLight = useCallback(() => {
+ toggle(ThemeEnum.light);
+ }, [toggle]);
+
+ const setDark = useCallback(() => {
+ toggle(ThemeEnum.dark);
+ }, [toggle]);
+
+ const setSystem = useCallback(() => {
+ toggle(ThemeEnum.system);
+ }, [toggle]);
return (
-
- } theme="borderless">
-
+
+
+
+ 亮色
+
+
+
+ 夜色
+
+
+
+ 系统
+
+
+ }
+ >
+ } theme="borderless">
+
);
};
diff --git a/packages/client/src/hooks/use-theme.tsx b/packages/client/src/hooks/use-theme.tsx
index 573d9382..5759e372 100644
--- a/packages/client/src/hooks/use-theme.tsx
+++ b/packages/client/src/hooks/use-theme.tsx
@@ -1,33 +1,31 @@
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
import { createGlobalHook } from './create-global-hook';
export enum ThemeEnum {
'dark' = 'dark',
'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 [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 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 followSystem = useCallback(() => {
const mql = window.matchMedia('(prefers-color-scheme: dark)');
function matchMode(e) {
@@ -41,15 +39,58 @@ const useThemeHook = () => {
matchMode(mql);
mql.addEventListener('change', matchMode);
- return () => {
+ const remove = () => {
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 {
+ userPrefer,
theme,
toggle,
};
};
-export const Theme = createGlobalHook<{ theme: ThemeEnum; toggle: () => void }>(useThemeHook);
+export const Theme =
+ createGlobalHook<{ userPrefer: ThemeEnum; theme: ThemeEnum; toggle: (nextTheme: ThemeEnum) => void }>(useThemeHook);