Widget-Rahmen: Widgets sind Beschreibungen, keine Kaesten in der Seite
widgetAnmelden({ id, titel, wo, sichtbar, laden, leer, inhalt, takt }) —
der Rahmen macht daraus ein Fenster in der Spalte (Terminal) oder ein
Schild (Desktop), kennt lädt/fertig/nichts da/Fehler und haelt den
Nachladetakt. Benutzer, Wetter und Systemzustand sind umgestellt; aus
index.html sind vier feste Kaesten verschwunden und aus dem Raster die
Einzelplaetze.
Zwei Dinge, die erst dabei sichtbar wurden:
- Der Server meldete 'kein Ort eingestellt' und 'Dienst nicht
erreichbar' beide als 204. Ein Ausfall sah damit aus wie ein
abgeschaltetes Widget. Ausfall ist jetzt 502, mit Test.
- Die Reihenfolge haengt nicht mehr an der Modulauswertung: Seit
glyphs.js seine Raster mit await auf oberster Ebene laedt, laufen
Module ohne Symbole vorher durch — der Systemzustand stand deshalb
ueber dem Benutzer. widgetsOrdnen() macht die Reihenfolge zur Angabe,
und genau dort haengt spaeter die im Server gespeicherte ein.
This commit is contained in:
1 parent
981a2331df
commit
b7cc06b7c1
12 files changed
+461
-180
No files matched your search
@@ -0,0 +1,151 @@
|
||||
/* ── Der Widget-Rahmen ─────────────────────────────────────────────────────
|
||||
*
|
||||
* Ein Widget ist hier eine **Beschreibung**, kein Stück HTML im Dokument.
|
||||
* Vorher standen Benutzer und Wetter fest in index.html, jedes mit eigener
|
||||
* Zeigen-und-Verstecken-Logik in seiner Datei — beim dritten Widget hätte man
|
||||
* dieselben zwanzig Zeilen ein drittes Mal geschrieben.
|
||||
*
|
||||
* Der Rahmen kümmert sich um das, was jedes Widget braucht und keines für sich
|
||||
* lösen sollte:
|
||||
*
|
||||
* · beide Betriebsarten — Fenster im Terminal, Schild auf dem Schreibtisch
|
||||
* · die vier Zustände: lädt · fertig · nichts da · Fehler
|
||||
* · Reihenfolge und Nachladen im Takt
|
||||
*
|
||||
* Ein Widget beschreibt sich so:
|
||||
*
|
||||
* {
|
||||
* id, titel,
|
||||
* wo: ["terminal", "desktop"] // wo es erscheinen darf
|
||||
* sichtbar: () => true // Schalter aus → gar nicht erst da
|
||||
* laden: async () => false|void // false = es gibt nichts zu zeigen
|
||||
* leer: "verbergen" | "Text" // was dann passiert
|
||||
* inhalt: () => "<html>"
|
||||
* takt: 600000 // ms, optional
|
||||
* }
|
||||
*
|
||||
* Absichtlich offen gelassen: eine dritte Form. Die Widgets sollen später auch
|
||||
* als Aufklappfeld in der HoneyBar leben können — deshalb erzeugt `inhalt()`
|
||||
* nur den Kern, und die Verpackung entsteht hier.
|
||||
*/
|
||||
import { $, state } from "../core.js";
|
||||
|
||||
const REGISTER = [];
|
||||
/** id → "laedt" | "fertig" | "leer" | "fehler" */
|
||||
const zustand = {};
|
||||
const uhren = {};
|
||||
|
||||
export function widgetAnmelden(def) {
|
||||
REGISTER.push(def);
|
||||
zustand[def.id] = def.laden ? "laedt" : "fertig";
|
||||
}
|
||||
|
||||
/** Nur zum Nachsehen von außen — die Liste selbst gehört dem Rahmen. */
|
||||
export function widgetListe() {
|
||||
return REGISTER.map((w) => w.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt die Reihenfolge fest. Was nicht genannt ist, hängt sich hinten an.
|
||||
*
|
||||
* Warum überhaupt? Weil die Reihenfolge sonst davon abhinge, in welcher
|
||||
* Reihenfolge die Module ausgewertet werden — und das ist **nicht** die
|
||||
* Reihenfolge der Importzeilen: `glyphs.js` lädt seine Raster mit `await` auf
|
||||
* oberster Ebene, also warten alle Module, die es brauchen, während die
|
||||
* anderen schon durchlaufen. Der Systemzustand braucht keine Symbole und stand
|
||||
* deshalb vor dem Benutzer, obwohl er darunter importiert wird.
|
||||
*
|
||||
* Hier ist die Reihenfolge damit eine Angabe statt einer Nebenwirkung — und
|
||||
* genau die Stelle, an der später die im Server gespeicherte Reihenfolge
|
||||
* einhaken kann.
|
||||
*/
|
||||
export function widgetsOrdnen(ids) {
|
||||
const platz = (w) => {
|
||||
const i = ids.indexOf(w.id);
|
||||
return i < 0 ? ids.length : i;
|
||||
};
|
||||
REGISTER.sort((a, b) => platz(a) - platz(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt ein Widget (oder alle) und zeichnet neu.
|
||||
*
|
||||
* Fehler bleiben hier: Ein Widget, das seinen Server nicht erreicht, sagt das
|
||||
* in seinem eigenen Rahmen — es darf weder den Schreibtisch aufhalten noch die
|
||||
* anderen Widgets mitreißen.
|
||||
*/
|
||||
export async function widgetsLaden(nurId) {
|
||||
const dran = REGISTER.filter((w) => w.laden && (!nurId || w.id === nurId));
|
||||
await Promise.all(
|
||||
dran.map(async (w) => {
|
||||
zustand[w.id] = "laedt";
|
||||
widgetsZeichnen();
|
||||
try {
|
||||
zustand[w.id] = (await w.laden()) === false ? "leer" : "fertig";
|
||||
} catch {
|
||||
zustand[w.id] = "fehler";
|
||||
}
|
||||
})
|
||||
);
|
||||
widgetsZeichnen();
|
||||
}
|
||||
|
||||
/** Startet die Nachlade-Takte. Einmal beim Aufbau des Schreibtischs. */
|
||||
export function widgetsStarten() {
|
||||
REGISTER.forEach((w) => {
|
||||
if (!w.takt || uhren[w.id]) return;
|
||||
uhren[w.id] = setInterval(() => widgetsLaden(w.id), w.takt);
|
||||
});
|
||||
}
|
||||
|
||||
function zustandHtml(w) {
|
||||
switch (zustand[w.id]) {
|
||||
case "laedt":
|
||||
return `<p class="w-zustand">lädt …</p>`;
|
||||
case "fehler":
|
||||
return `<p class="w-zustand w-fehler">nicht erreichbar</p>`;
|
||||
case "leer":
|
||||
return `<p class="w-zustand">${w.leer}</p>`;
|
||||
default:
|
||||
return w.inhalt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeichnet alle Widgets in die Ansicht, die gerade gilt.
|
||||
*
|
||||
* Beide Container werden bei jedem Durchgang neu gefüllt statt einzeln
|
||||
* nachgeführt: Bei einer Handvoll Widgets ist das billiger als Buchführung
|
||||
* darüber, was sich geändert hat — und es kann nicht auseinanderlaufen.
|
||||
*/
|
||||
export function widgetsZeichnen() {
|
||||
const desktop = state.settings.mode === "desktop";
|
||||
const modus = desktop ? "desktop" : "terminal";
|
||||
const spalte = $("widgetSpalte");
|
||||
const schilder = $("deskwidgets");
|
||||
if (!spalte || !schilder) return;
|
||||
|
||||
const dran = REGISTER.filter((w) => {
|
||||
if (w.wo && !w.wo.includes(modus)) return false;
|
||||
if (w.sichtbar && !w.sichtbar()) return false;
|
||||
// „nichts da" heißt bei manchen Widgets: gar nicht erst hinstellen. Eine
|
||||
// leere Wetterkachel wäre schlechter als keine.
|
||||
return !(zustand[w.id] === "leer" && w.leer === "verbergen");
|
||||
});
|
||||
|
||||
spalte.innerHTML = desktop
|
||||
? ""
|
||||
: dran
|
||||
.map(
|
||||
(w) => `
|
||||
<section class="win win-widget" data-widget="${w.id}">
|
||||
<div class="win-bar"><span class="win-title">${w.titel}</span><span class="win-dots">●●●</span></div>
|
||||
<div class="win-body">${zustandHtml(w)}</div>
|
||||
</section>`
|
||||
)
|
||||
.join("");
|
||||
|
||||
schilder.innerHTML = desktop
|
||||
? dran.map((w) => `<div class="deskwidget" data-widget="${w.id}">${zustandHtml(w)}</div>`).join("")
|
||||
: "";
|
||||
}
|
||||
Reference in new issue
Block a user