59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import { useState, useEffect, useRef } from 'react';
|
|
|
|
const THEMES = [
|
|
{ value: 'salon', label: 'Salon' },
|
|
{ value: 'salon-noir', label: 'Salon Noir' },
|
|
{ value: 'gothic', label: 'Gothic' },
|
|
{ value: 'breakcore', label: 'Breakcore' },
|
|
];
|
|
|
|
interface Props {
|
|
defaultTheme?: string;
|
|
}
|
|
|
|
export default function ThemeSwitcher({ defaultTheme = 'salon' }: Props) {
|
|
const [theme, setTheme] = useState(() => {
|
|
if (typeof window !== 'undefined') {
|
|
return localStorage.getItem('user-theme') || defaultTheme;
|
|
}
|
|
return defaultTheme;
|
|
});
|
|
const [toast, setToast] = useState<string | null>(null);
|
|
const isFirst = useRef(true);
|
|
|
|
useEffect(() => {
|
|
const html = document.documentElement;
|
|
THEMES.forEach(t => html.classList.remove(t.value));
|
|
html.classList.add(theme);
|
|
localStorage.setItem('user-theme', theme);
|
|
|
|
if (isFirst.current) {
|
|
isFirst.current = false;
|
|
return;
|
|
}
|
|
const label = THEMES.find(t => t.value === theme)?.label ?? theme;
|
|
setToast(`Theme: ${label}`);
|
|
const id = setTimeout(() => setToast(null), 1200);
|
|
return () => clearTimeout(id);
|
|
}, [theme]);
|
|
|
|
return (
|
|
<div className="relative inline-block text-left">
|
|
<select
|
|
value={theme}
|
|
onChange={(e) => setTheme(e.target.value)}
|
|
aria-label="Theme"
|
|
className="topbar-control theme-select"
|
|
>
|
|
{THEMES.map(t => (
|
|
<option key={t.value} value={t.value}>{t.label}</option>
|
|
))}
|
|
</select>
|
|
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-[var(--subtext0)]">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
|
</div>
|
|
{toast && <div className="toast" role="status">{toast}</div>}
|
|
</div>
|
|
);
|
|
}
|