35 lines
1.4 KiB
TypeScript
35 lines
1.4 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Palette } from 'lucide-react';
|
|
import { applyTheme, isTheme, resolveTheme, THEMES, THEME_STORAGE_KEY } from './theme.ts';
|
|
|
|
export function ThemePicker() {
|
|
const [theme, setTheme] = useState(() => resolveTheme(document.documentElement.dataset.theme));
|
|
|
|
useEffect(() => {
|
|
const sync = (event: StorageEvent) => {
|
|
if (event.key !== THEME_STORAGE_KEY && event.key !== null) return;
|
|
const next = resolveTheme(event.newValue);
|
|
applyTheme(next);
|
|
setTheme(next);
|
|
};
|
|
window.addEventListener('storage', sync);
|
|
return () => window.removeEventListener('storage', sync);
|
|
}, []);
|
|
|
|
const choose = (value: string) => {
|
|
// Saved preferences are untrusted: only known palette identifiers may reach the DOM.
|
|
if (!isTheme(value)) return;
|
|
applyTheme(value);
|
|
setTheme(value);
|
|
try { localStorage.setItem(THEME_STORAGE_KEY, value); }
|
|
catch { /* Theme switching still works for this page when storage is blocked. */ }
|
|
};
|
|
|
|
return <label className="theme-picker" title="Choose your Catppuccin flavor">
|
|
<Palette size={15} aria-hidden="true" />
|
|
<select aria-label="Catppuccin theme" value={theme} onChange={event => choose(event.target.value)}>
|
|
{THEMES.map(option => <option key={option.id} value={option.id}>{option.label}</option>)}
|
|
</select>
|
|
</label>;
|
|
}
|