48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
import { useCallback, useState } from 'react';
|
|
import { setTheme, useTheme, type ThemePref } from '@/lib/theme';
|
|
import { Button } from './Button';
|
|
import { Icon, type IconName } from './Icon';
|
|
import { Popover } from './Popover';
|
|
import styles from './ThemeMenu.module.css';
|
|
|
|
const OPTIONS: { value: ThemePref; label: string; icon: IconName }[] = [
|
|
{ value: 'system', label: '跟随系统', icon: 'auto' },
|
|
{ value: 'light', label: '亮色', icon: 'sun' },
|
|
{ value: 'dark', label: '暗色', icon: 'moon' },
|
|
];
|
|
|
|
export function ThemeMenu() {
|
|
const theme = useTheme();
|
|
const [open, setOpen] = useState(false);
|
|
const close = useCallback(() => setOpen(false), []);
|
|
const current = OPTIONS.find((o) => o.value === theme) ?? OPTIONS[0];
|
|
|
|
return (
|
|
<Popover
|
|
open={open}
|
|
onClose={close}
|
|
trigger={<Button variant="ghost" icon={current?.icon ?? 'auto'} label="外观" onClick={() => setOpen((v) => !v)} />}
|
|
>
|
|
<div role="radiogroup" aria-label="外观">
|
|
{OPTIONS.map((o) => (
|
|
<button
|
|
key={o.value}
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={o.value === theme}
|
|
className={styles.item}
|
|
onClick={() => {
|
|
setTheme(o.value);
|
|
close();
|
|
}}
|
|
>
|
|
<Icon name={o.icon} size={18} />
|
|
<span className={styles.label}>{o.label}</span>
|
|
{o.value === theme ? <Icon name="check" size={16} /> : null}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</Popover>
|
|
);
|
|
}
|