Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { themeText } from '@/lib/i18n/shell'
|
|
|
|
type Thema = 'dark' | 'light' | 'system'
|
|
|
|
export const THEME_KEY = 'afis:thema'
|
|
|
|
/**
|
|
* Umschalter zwischen dunklem und hellem Thema.
|
|
*
|
|
* Drei Zustände, nicht zwei: „System" ist der Auslieferungszustand und folgt
|
|
* `prefers-color-scheme`. Wer ausdrücklich wählt, bekommt seine Wahl auf
|
|
* diesem Gerät gemerkt - nicht am Konto, denn es ist eine Eigenschaft des
|
|
* Bildschirms, an dem jemand sitzt, nicht der Person.
|
|
*
|
|
* Gesetzt wird das Attribut schon vor dem ersten Malen, siehe das Skript im
|
|
* Wurzel-Layout. Sonst blitzt beim Laden das falsche Thema auf.
|
|
*/
|
|
export function ThemeToggle() {
|
|
const [thema, setThema] = useState<Thema>('system')
|
|
|
|
useEffect(() => {
|
|
try {
|
|
const gespeichert = localStorage.getItem(THEME_KEY)
|
|
if (gespeichert === 'dark' || gespeichert === 'light') setThema(gespeichert)
|
|
} catch {
|
|
// Privates Fenster: dann eben ohne Merken.
|
|
}
|
|
}, [])
|
|
|
|
function waehlen(neu: Thema) {
|
|
setThema(neu)
|
|
try {
|
|
if (neu === 'system') localStorage.removeItem(THEME_KEY)
|
|
else localStorage.setItem(THEME_KEY, neu)
|
|
} catch {
|
|
// s. o.
|
|
}
|
|
|
|
const wurzel = document.documentElement
|
|
if (neu === 'system') wurzel.removeAttribute('data-theme')
|
|
else wurzel.setAttribute('data-theme', neu)
|
|
}
|
|
|
|
return (
|
|
<select
|
|
value={thema}
|
|
onChange={(event) => waehlen(event.target.value as Thema)}
|
|
aria-label={themeText.label}
|
|
className="field text-2xs"
|
|
>
|
|
<option value="system">{themeText.system}</option>
|
|
<option value="dark">{themeText.dark}</option>
|
|
<option value="light">{themeText.light}</option>
|
|
</select>
|
|
)
|
|
}
|