Kachelsymbole: jedes Bootstrap-Icon statt einer Handvoll

Fuer die Symbole vor den Zahlen standen drei Codepunkte von Hand in
rooms.php, und die Maske bot genau diese drei an. Jedes weitere Symbol waere
eine Codeaenderung gewesen.

Die Codepunkte stehen aber schon im Haus: css/bootstrap-icons.min.css fuehrt
sie unter .bi-<name>::before - dieselbe Quelle, aus der auch der Browser
liest. bootstrapIcons() liest sie von dort (2078 Stueck, einmal je Anfrage),
kachelIcon() schlaegt darin nach. Damit ist jedes Symbol moeglich, und ein
Versionswechsel der Icons zieht von selbst mit.

In der Maske steht deshalb kein Auswahlfeld mehr, sondern der Name mit einer
Vorschau davor - "moisture" sagt nichts, das Symbol daneben schon. Die
Vorschlagsliste beginnt mit gut dreissig ueblichen Symbolen unter deutscher
Beschriftung (Temperatur, Feuchte, Leistung, Wallbox, Regen ...), danach folgt
der ganze Rest alphabetisch. Ein Name, den es nicht gibt, zeigt sofort ein
rotes Fragezeichen und wird beim Speichern mit Begruendung abgelehnt.

In der eingeklappten Kurzfassung einer Kachel steht das Symbol jetzt mit -
es ist das schnellste Erkennungszeichen dafuer, welcher der drei Werte
gemeint ist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 23:24:14 +02:00
co-authored by Claude Opus 5
parent eb1aa8c760
commit cd398bc800
5 changed files with 234 additions and 31 deletions
+75 -10
View File
@@ -376,9 +376,14 @@ function zeichneKacheln() {
/** Eine Raumzeile: Name, Kurzfassung der Werte, darunter der Editor. */
function kachelZeile(k) {
const werte = k._entwurf || k.werte;
// Das Symbol steht auch in der Kurzfassung: es ist das, was auf der Kachel
// vor der Zahl steht, und damit der schnellste Weg zu erkennen, welcher der
// drei Werte gemeint ist.
const kurz = werte.length
? werte.map(w => '<span class="kachel-chip">' + textSchuetzen(kachelWertKurz(w, k))
+ "</span>").join("")
? werte.map(w => '<span class="kachel-chip">'
+ (w.icon && kachelSymbolBekannt(w.icon)
? '<i class="bi bi-' + textSchuetzen(w.icon) + '"></i>' : "")
+ textSchuetzen(kachelWertKurz(w, k)) + "</span>").join("")
: '<span class="kachel-chip leer">ohne Messwert</span>';
let html = '<div class="kachel-zeile' + (k._offen ? " offen" : "") + '"'
@@ -407,6 +412,59 @@ function kachelZeile(k) {
return html + "</div>";
}
/* --- Symbole ------------------------------------------------------------- */
/**
* Das Feld für das Symbol: eine Vorschau und ein Name.
*
* Statt einer Auswahlliste mit einer Handvoll Symbole steht hier der Name des
* Bootstrap-Icons - damit sind alle gut zweitausend möglich, ohne dass für
* jedes neue eines im Code nachgetragen werden müsste. Die üblichen stehen in
* der Vorschlagsliste obenan, mit deutscher Beschriftung; wer ein anderes
* will, tippt und bekommt den Rest angeboten (icons.getbootstrap.com).
*
* Die Vorschau ist die eigentliche Hilfe: ein Name wie "moisture" sagt nichts,
* das Symbol daneben schon.
*/
function kachelSymbolFeld(icon) {
return '<span class="kw-symbol-schau">' + kachelSymbolVorschau(icon) + "</span>"
+ '<input type="text" class="form-control form-control-sm" data-feld="icon"'
+ ' list="kachelIcons" placeholder="Symbol" autocomplete="off"'
+ ' value="' + textSchuetzen(icon) + '">';
}
/** Der Inhalt der Vorschau: das Symbol, ein Strich, oder ein Fragezeichen. */
function kachelSymbolVorschau(icon) {
if (!icon) return '<i class="bi bi-dash" title="ohne Symbol"></i>';
if (!kachelSymbolBekannt(icon)) {
return '<i class="bi bi-question-circle unbekannt" title="Dieses Symbol gibt es nicht"></i>';
}
return '<i class="bi bi-' + textSchuetzen(icon) + '" title="' + textSchuetzen(icon) + '"></i>';
}
function kachelSymbolBekannt(icon) {
if (!kachelSymbolBekannt._satz) {
kachelSymbolBekannt._satz = new Set((kachelModell.symbole || []).map(s => s.name));
}
return kachelSymbolBekannt._satz.has(icon);
}
/**
* Die Vorschlagsliste steht einmal im Dokument, nicht in jeder Zeile.
*
* Zweitausend Einträge sind rund siebzig Kilobyte Markup; bei jedem
* Neuzeichnen einer Kachel neu zu bauen waere Verschwendung. Sie haengt
* deshalb am Ende des Dokuments und ueberlebt das Neuzeichnen der Liste.
*/
function kachelIconListeSicherstellen() {
if (document.getElementById("kachelIcons")) return;
const liste = document.createElement("datalist");
liste.id = "kachelIcons";
liste.innerHTML = (kachelModell.symbole || []).map(s => '<option value="' + s.name + '"'
+ (s.titel ? ' label="' + textSchuetzen(s.titel) + '"' : "") + ">").join("");
document.body.appendChild(liste);
}
/**
* Eine Wertdefinition als Formularzeile.
*
@@ -418,11 +476,6 @@ function kachelZeile(k) {
function kachelWertZeile(wert, i, kachel) {
const quelle = kachelQuelle(wert);
const katalog = (kachelModell && kachelModell.katalog) || [];
const symbole = kachelModell.symbole || {};
const symbolOptionen = Object.keys(symbole).map(function (key) {
return '<option value="' + key + '"' + ((wert.icon || "") === key ? " selected" : "")
+ ">" + textSchuetzen(symbole[key]) + "</option>";
}).join("");
// Eingabeart. "Messwert" steht nur zur Wahl, wenn es einen Katalog gibt -
// ohne Mesh-Datenbank waere es eine leere Liste.
@@ -516,8 +569,7 @@ function kachelWertZeile(wert, i, kachel) {
+ (wert.format === "leistung" ? " disabled" : "")
+ ' value="' + (wert.stellen === undefined ? "" : wert.stellen) + '"></div>'
+ '<div class="kw-symbol"><select class="form-select form-select-sm" data-feld="icon">'
+ symbolOptionen + "</select></div>"
+ '<div class="kw-symbol">' + kachelSymbolFeld(wert.icon || "") + "</div>"
+ '<div class="kw-schalter">'
+ '<label title="Leistung: skaliert selbst zwischen W und kW">'
@@ -584,7 +636,7 @@ function kachelWertLesen(feldZeile) {
const stellen = hol("stellen");
if (stellen !== "" && stellen !== undefined) wert.stellen = parseInt(stellen, 10);
}
const icon = hol("icon");
const icon = (hol("icon") || "").trim();
if (icon) wert.icon = icon;
if (hol("gross")) wert.gross = true;
return wert;
@@ -627,6 +679,8 @@ async function kachelnHolen() {
const d = await antwort.json();
if (!antwort.ok || d.error) throw new Error(d.error || "HTTP " + antwort.status);
kachelModell = d;
delete kachelSymbolBekannt._satz; // neue Antwort, neue Namen
kachelIconListeSicherstellen();
zeichneKacheln();
} catch (e) {
ziel.innerHTML = '<div class="alert alert-danger mb-0">Kacheln nicht abrufbar: '
@@ -715,6 +769,17 @@ function kachelnBinden() {
}
});
/*
* Die Vorschau folgt dem Tippen. Neu gezeichnet wird dafuer nicht - das
* naehme dem Feld nach jedem Buchstaben den Cursor.
*/
liste.addEventListener("input", function (ereignis) {
const feld = ereignis.target.closest('[data-feld="icon"]');
if (!feld) return;
const schau = feld.parentElement.querySelector(".kw-symbol-schau");
if (schau) schau.innerHTML = kachelSymbolVorschau(feld.value.trim());
});
const neuLaden = document.getElementById("kachelNeuLaden");
if (neuLaden) neuLaden.addEventListener("click", kachelnHolen);
}