Files
Flaro/web/app/src/icons.ts
T
ScarriffleandClaude Opus 5 1e4a231d1b Elf Abschnitte in einer Spalte waren zehn zu viel
Die Verwaltung war eine Seite mit 1786 Zeilen. Wer das Protokoll sehen
wollte, scrollte an Mediatheken, Prüfliste, Benutzern und Speicher
vorbei — und jede neue Einstellung hätte sie länger gemacht.

Jetzt hat sie eine eigene Leiste mit sieben Bereichen, jeder mit seiner
Adresse: `#/verwaltung/protokolle` lässt sich ablegen, und ein Neuladen
bleibt dort stehen. Die Leiste sieht aus wie die Hauptleiste daneben,
damit sie als Navigation gelesen wird und nicht als Knopfreihe; am
Telefon wird aus der Spalte ein Streifen zum Schieben.

**Gebaut wird nur, was offen ist.** Das ist nicht nur sparsamer: das
Abspielfenster fragt im Fünf-Sekunden-Takt nach, die Mediathekenliste
beobachtet laufende Scans. Gemessen: nach dem Wechsel in einen anderen
Bereich kommt zwölf Sekunden lang keine Anfrage mehr.

`admin.ts` ist in acht Dateien zerlegt — eine je Bereich, dazu `teile.ts`
für die gemeinsame Überschrift und `index.ts` für Leiste und Weiche.

Zwei Dinge mussten dabei nachziehen:

**Die Markierung in der Hauptleiste.** Sie verglich stur zwei Segmente;
bei `#/verwaltung/metadaten` traf das auf keinen Eintrag, und
„Verwaltung" verlor seinen Balken. Jetzt gilt auch ein Segment.

**„5 Titel warten — ansehen".** Der Knopf sprang mit `scrollIntoView` zur
Prüfliste. Die steht nicht mehr im selben Dokument — er wechselt jetzt
den Bereich. Die Hervorhebung danach entfällt damit, und mit ihr ihr CSS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-06 16:39:50 +02:00

102 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Icons als SVG, nicht als Zeichen aus einer Schriftart.
*
* **Keine Emojis.** Ein ▶ oder ✓ aus Unicode sieht auf jedem System anders
* aus, lässt sich nicht einfärben, nicht in der Strichstärke anpassen und
* springt in der Grundlinie. Diese hier sind auf 24×24 gezeichnet, erben die
* Textfarbe und skalieren mit der Schriftgrösse.
*
* Der Strichstil ist durchgehend derselbe: 1.8 Einheiten, runde Enden. */
const rahmen = (inhalt: string, gefuellt = false): string =>
`<svg viewBox="0 0 24 24" aria-hidden="true" fill="${gefuellt ? 'currentColor' : 'none'}"`
+ ` stroke="${gefuellt ? 'none' : 'currentColor'}" stroke-width="1.8"`
+ ` stroke-linecap="round" stroke-linejoin="round">${inhalt}</svg>`;
export const icons = {
start: rahmen('<path d="M3 10.5 12 3l9 7.5"/><path d="M5 9.5V21h14V9.5"/>'),
/** Ordner — fürs Durchklicken durch das Dateisystem. */
ordner: rahmen('<path d="M3 7.5A1.5 1.5 0 0 1 4.5 6h4.3a1.5 1.5 0 0 1 1.07.45L11.4 7.6"'
+ ' /><path d="M3 7.5h16.5A1.5 1.5 0 0 1 21 9v8.5a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 17.5Z"/>'),
/** Drei Punkte — der sichtbare Griff zum Kontextmenü. */
punkte: rahmen('<circle cx="12" cy="5" r="1.4"/><circle cx="12" cy="12" r="1.4"/>'
+ '<circle cx="12" cy="19" r="1.4"/>', true),
/** Winkel nach oben — eine Ebene zurück im Baum. */
hoch: rahmen('<path d="m6 14 6-6 6 6"/>'),
runter: rahmen('<path d="m6 10 6 6 6-6"/>'),
// Sechs Punkte — das übliche Zeichen für „hier anfassen". Gefüllt,
// weil Kreise mit Radius 1 als Umriss nur ein Fleck wären.
griff: rahmen('<circle cx="9" cy="6" r="1"/><circle cx="15" cy="6" r="1"/>'
+ '<circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/>'
+ '<circle cx="9" cy="18" r="1"/><circle cx="15" cy="18" r="1"/>', true),
/** Filmstreifen — für eine Filmmediathek. */
filme: rahmen('<rect x="3" y="4" width="18" height="16" rx="2"/>'
+ '<path d="M7 4v16M17 4v16M3 9h4M3 15h4M17 9h4M17 15h4"/>'),
/** Bildschirm — für eine Serienmediathek. */
serien: rahmen('<rect x="2.5" y="4.5" width="19" height="12" rx="2"/><path d="M8 20h8"/>'),
verwaltung: rahmen('<circle cx="12" cy="12" r="3.2"/>'
+ '<path d="M19.4 14.5a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-1.8-.3 1.6 1.6 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-1-1.5 1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0 .3-1.8 1.6 1.6 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.5-1 1.6 1.6 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3H9a1.6 1.6 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 1 1.5 1.6 1.6 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-.3 1.8V9a1.6 1.6 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1z"/>'),
suche: rahmen('<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>'),
abspielen: rahmen('<path d="M8 5.5v13l11-6.5z"/>', true),
haken: rahmen('<path d="m5 12.5 4.5 4.5L19 7"/>'),
hakenKreis: rahmen('<circle cx="12" cy="12" r="9"/><path d="m8 12.3 2.8 2.7L16 9.6"/>'),
stift: rahmen('<path d="M4 20h4L19.5 8.5a2.1 2.1 0 0 0-3-3L5 17v3z"/>'),
download: rahmen('<path d="M12 3v12"/><path d="m7.5 11 4.5 4.5L16.5 11"/><path d="M4 20h16"/>'),
links: rahmen('<path d="M15 5 8 12l7 7"/>'),
rechts: rahmen('<path d="m9 5 7 7-7 7"/>'),
mehr: rahmen('<circle cx="12" cy="5.5" r="1.3" fill="currentColor" stroke="none"/>'
+ '<circle cx="12" cy="12" r="1.3" fill="currentColor" stroke="none"/>'
+ '<circle cx="12" cy="18.5" r="1.3" fill="currentColor" stroke="none"/>'),
abmelden: rahmen('<path d="M15 4h3a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-3"/>'
+ '<path d="M10 16.5 14.5 12 10 7.5"/><path d="M14.5 12H4"/>'),
schliessen: rahmen('<path d="M6 6l12 12M18 6 6 18"/>'),
stern: rahmen('<path d="m12 3.6 2.6 5.3 5.9.9-4.3 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8L3.5 9.8l5.9-.9z"/>'),
/* --- Spieler -------------------------------------------------------- */
pause: rahmen('<rect x="7" y="5" width="3.5" height="14" rx="1"/>'
+ '<rect x="13.5" y="5" width="3.5" height="14" rx="1"/>', true),
ton: rahmen('<path d="M4 9.5h3.5L12 5.5v13L7.5 14.5H4z"/>'
+ '<path d="M16 9a4.5 4.5 0 0 1 0 6"/><path d="M18.5 6.5a8 8 0 0 1 0 11"/>'),
stummGeschaltet: rahmen('<path d="M4 9.5h3.5L12 5.5v13L7.5 14.5H4z"/>'
+ '<path d="m16.5 9.5 5 5M21.5 9.5l-5 5"/>'),
vollbild: rahmen('<path d="M4 9V5h4"/><path d="M20 9V5h-4"/>'
+ '<path d="M4 15v4h4"/><path d="M20 15v4h-4"/>'),
untertitel: rahmen('<rect x="3" y="5" width="18" height="14" rx="2"/>'
+ '<path d="M6.5 14.5h4"/><path d="M13.5 14.5h4"/><path d="M6.5 10h11"/>'),
/** Eine Wellenform — Tonspur, und nicht zu verwechseln mit der Lautstärke. */
tonspur: rahmen('<path d="M4 10.5v3M8 7v10M12 4.5v15M16 8.5v7M20 11v2"/>'),
/** Schieberegler. **Vorher stand hier ein Zahnrad mit acht Strahlen** — das
* sah aus wie eine Sonne, und niemand vermutete die Bildqualität dahinter. */
regler: rahmen('<path d="M4 7h7M15 7h5M4 17h3M11 17h9"/>'
+ '<circle cx="13" cy="7" r="2.1"/><circle cx="9" cy="17" r="2.1"/>'),
/** Eine Uhr — für alles, was erst später gilt. */
uhr: rahmen('<circle cx="12" cy="12" r="8.5"/><path d="M12 7.5V12l3 1.8"/>'),
/** Den Spieler auf die Leiste unten schrumpfen. */
verkleinern: rahmen('<path d="M5 9.5 12 16l7-6.5"/>'),
/** Und wieder aufs ganze Bild. */
vergroessern: rahmen('<path d="m5 14.5 7-6.5 7 6.5"/>'),
/** Ein Mensch — Konten, Profil. */
person: rahmen('<circle cx="12" cy="8" r="3.6"/>'
+ '<path d="M4.8 20c.6-3.6 3.6-5.6 7.2-5.6s6.6 2 7.2 5.6"/>'),
/** Ein beschriebenes Blatt — die Tagesprotokolle. */
protokoll: rahmen('<path d="M6 3.5h8L19 8v12.5H6Z"/><path d="M14 3.5V8h5"/>'
+ '<path d="M9 12.5h7M9 16h7"/>'),
/** Schieberegler — Einstellungen, die eine Stufe haben. */
stufen: rahmen('<path d="M5 7h14M5 17h14"/><circle cx="9.5" cy="7" r="2.3"/>'
+ '<circle cx="15" cy="17" r="2.3"/>'),
} as const;
export type IconName = keyof typeof icons;
/** Ein Icon als Element — für alles, was nicht aus einer Zeichenkette gebaut wird. */
export function icon(name: IconName, klasse = 'icon'): HTMLElement {
const h = document.createElement('span');
h.className = klasse;
h.innerHTML = icons[name];
return h;
}