Eigene Auswahllisten ueberall: dunkel und ueber der Seite statt im Fluss

Chrome zeichnet die Liste eines <select> hell auf grau und laesst sich davon
nicht abbringen. js/solar/auswahl.js ergaenzt jedes <select> um einen eigenen
Knopf; die Liste haengt an <body> und liegt ueber der Seite, damit ein Modal
nicht hoeher wird und der Inhalt nicht springt. Ab neun Eintraegen mit
Suchfeld. Der eigene Picker im Raumfenster faellt dafuer weg.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 07:55:17 +02:00
co-authored by Claude Opus 5
parent fe8630eb4f
commit d36989ae0d
6 changed files with 391 additions and 69 deletions
+20 -41
View File
@@ -620,60 +620,39 @@ function rfLampeStarten(karte) {
eigen.addEventListener("change", () => setzen(eigen.value));
}
// Preset, Effekt und andere Listen: Chips, lange Listen im aufklappbaren
// Picker mit Suche.
// Preset, Effekt und andere Listen: wenige Eintraege als Chips, lange
// Listen als <select>, aus dem js/solar/auswahl.js die eigene Liste macht.
karte.querySelectorAll(".rf-liste").forEach(liste => {
const knopf = liste.querySelector(".rf-picker-knopf");
const picker = liste.querySelector(".rf-picker");
const suche = liste.querySelector(".rf-suche");
const auf = offen => {
if (!picker) return;
picker.hidden = !offen;
knopf.setAttribute("aria-expanded", String(offen));
if (offen) {
suche.value = "";
liste.querySelectorAll("[data-wert]").forEach(k => { k.hidden = false; });
const gewaehlt = liste.querySelector("[data-wert][aria-pressed='true']");
if (gewaehlt) gewaehlt.scrollIntoView({ block: "nearest" });
suche.focus({ preventScroll: true });
}
};
if (knopf) knopf.addEventListener("click", () => auf(picker.hidden));
if (suche) {
suche.addEventListener("input", () => {
const text = suche.value.trim().toLowerCase();
liste.querySelectorAll("[data-wert]").forEach(k => { k.hidden = text !== "" && !k.textContent.toLowerCase().includes(text); });
});
suche.addEventListener("keydown", e => {
if (e.key === "Escape") { e.stopPropagation(); auf(false); knopf.focus(); }
if (e.key === "Enter") {
const erster = [...liste.querySelectorAll("[data-wert]")].find(k => !k.hidden);
if (erster) erster.click();
}
});
}
liste.querySelectorAll("[data-wert]").forEach(k => k.addEventListener("click", () => {
rfListeSetzen(liste, k.dataset.wert);
auf(false);
const params = {};
params[liste.dataset.param] = k.dataset.wert;
rfKommando(Number(liste.dataset.cmd), params, liste);
rfListeSenden(liste, k.dataset.wert);
}));
const auswahl = liste.querySelector("select");
if (auswahl) auswahl.addEventListener("change", () => rfListeSenden(liste, auswahl.value));
});
if (karte.dataset.wled) rfWledLesen(karte);
}
/** Den gewaehlten Eintrag an das Geraet schicken. */
function rfListeSenden(liste, wert) {
const params = {};
params[liste.dataset.param] = wert;
rfKommando(Number(liste.dataset.cmd), params, liste);
}
/** Einen Eintrag einer Liste als gewaehlt zeigen; null = keiner. */
function rfListeSetzen(liste, wert) {
let name = null;
liste.querySelectorAll("[data-wert]").forEach(k => {
const ja = wert !== null && k.dataset.wert === String(wert);
k.setAttribute("aria-pressed", String(ja));
if (ja) name = k.textContent;
k.setAttribute("aria-pressed", String(wert !== null && k.dataset.wert === String(wert)));
});
const anzeige = liste.querySelector("[data-rf-picker-wert]");
if (anzeige) anzeige.textContent = name === null ? "" : name;
const auswahl = liste.querySelector("select");
if (auswahl && wert !== null) {
auswahl.value = String(wert);
// Die Beschriftung des eigenen Knopfes haengt am <select>, nicht am DOM
// darunter - sie muss also mitgezogen werden.
if (typeof auswahlAktualisieren === "function") auswahlAktualisieren(auswahl);
}
}
/**