waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app src shared ansicht.ts

3,6 KB Rohdatei
app/src/shared/ansicht.ts — 91 Zeilen
1 /**
2 * Die Anzeigegröße der Anwendung – reine Logik, ohne Electron.
3 *
4 * **Warum Zoom und nicht nur Schriftgröße.** Eine reine Schriftvergrößerung
5 * lässt Bilder unberührt. Der Katalog führt siebzehn Prüfzeichen, bei denen das
6 * Bild die Antwort ist – ein Beschusszeichen, ein PTB-Kreis. Wer
7 * größere Schrift braucht, braucht diese Zeichen erst recht größer. Der Zoom
8 * skaliert alles gemeinsam, und das Layout dieser Anwendung ist darauf
9 * ausgelegt: Es fließt um, statt zu brechen.
10 *
11 * **Warum feste Stufen.** Stufenlos wäre schwerer zu treffen und schwerer
12 * anzusagen. Die Leiter deckt den Bereich ab, den WCAG 1.4.4 verlangt
13 * (200 Prozent), reicht bis zu dem, an dem WCAG 1.4.10 misst (400 Prozent),
14 * und lässt darunter Luft für kleinere Anzeigen.
15 *
16 * **Warum sie bis 400 Prozent geht.** 1.4.10 (Reflow) ist an einer Messung
17 * festgemacht: 400 Prozent Vergrößerung auf einem 1280 Pixel breiten Fenster,
18 * also 320 CSS-Pixel Inhaltsbreite. Endete die Leiter bei 200 Prozent, ließe
19 * sich dieser Fall mit Bordmitteln gar nicht herstellen – das Kriterium wäre
20 * nicht erfüllt, sondern nur unprüfbar. Die drei oberen Stufen (250, 300,
21 * 400) sind dieselben, die auch Chromium anbietet; wer sie aus dem Browser
22 * kennt, findet keine fremde Skala vor.
23 */
24
25 /** Die wählbaren Anzeigegrößen in Prozent. */
26 export const ANZEIGEGROESSEN = Object.freeze([
27 80, 90, 100, 110, 125, 150, 175, 200, 250, 300, 400,
28 ] as const);
29
30 export type Anzeigegroesse = (typeof ANZEIGEGROESSEN)[number];
31
32 /** Vorgabe: unverändert. */
33 export const ANZEIGEGROESSE_STANDARD = 100;
34
35 /**
36 * Bringt einen beliebigen Wert auf eine gültige Stufe.
37 *
38 * Nicht die nächstgelegene, sondern die nächstkleinere gültige Stufe: Wer
39 * einen Wert zwischen zwei Stufen gespeichert hat – etwa aus einer früheren
40 * Fassung –, soll nicht plötzlich größer sehen als vorher.
41 */
42 export function anzeigegroesseBereinigen(wert: unknown): number {
43 if (typeof wert !== 'number' || !Number.isFinite(wert)) {
44 return ANZEIGEGROESSE_STANDARD;
45 }
46 const kleinste = ANZEIGEGROESSEN[0];
47 const groesste = ANZEIGEGROESSEN[ANZEIGEGROESSEN.length - 1] ?? ANZEIGEGROESSE_STANDARD;
48 if (wert <= kleinste) {
49 return kleinste;
50 }
51 if (wert >= groesste) {
52 return groesste;
53 }
54 return [...ANZEIGEGROESSEN].reverse().find((stufe) => stufe <= wert) ?? ANZEIGEGROESSE_STANDARD;
55 }
56
57 /** Eine Stufe größer; an der obersten Stufe bleibt es dabei. */
58 export function groesser(aktuell: number): number {
59 const jetzt = anzeigegroesseBereinigen(aktuell);
60 return ANZEIGEGROESSEN.find((stufe) => stufe > jetzt) ?? jetzt;
61 }
62
63 /** Eine Stufe kleiner; an der untersten Stufe bleibt es dabei. */
64 export function kleiner(aktuell: number): number {
65 const jetzt = anzeigegroesseBereinigen(aktuell);
66 return [...ANZEIGEGROESSEN].reverse().find((stufe) => stufe < jetzt) ?? jetzt;
67 }
68
69 export function istGroesstmoeglich(wert: number): boolean {
70 return anzeigegroesseBereinigen(wert) === ANZEIGEGROESSEN[ANZEIGEGROESSEN.length - 1];
71 }
72
73 export function istKleinstmoeglich(wert: number): boolean {
74 return anzeigegroesseBereinigen(wert) === ANZEIGEGROESSEN[0];
75 }
76
77 /**
78 * Chromiums Zoomfaktor zur Prozentangabe.
79 *
80 * `setZoomFactor(1.5)` entspricht 150 Prozent – die Umrechnung ist trivial
81 * und steht trotzdem hier, damit die Prozentzahl die eine Wahrheit bleibt
82 * und niemand an zwei Stellen mit 1,5 gegen 150 hantiert.
83 */
84 export function zoomfaktor(prozent: number): number {
85 return anzeigegroesseBereinigen(prozent) / 100;
86 }
87
88 /** „125 Prozent" – für Ansagen und Beschriftungen. */
89 export function anzeigegroesseText(prozent: number): string {
90 return `${String(anzeigegroesseBereinigen(prozent))} Prozent`;
91 }