Files
WebDrop/web/src/ui/ThemeMenu.tsx
T
2026-10-02 00:59:37 +08:00

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>
);
}