From 2aadb667fbff3f4975cf1227698e875bfd2297b1 Mon Sep 17 00:00:00 2001 From: "m0@nas" Date: Tue, 15 Sep 2026 13:43:11 +0200 Subject: [PATCH] Zeitleiste: Karte je Eintrag mit Bearbeiten, Pausieren/Fortsetzen, Loeschen Ein Klick auf einen Eintrag oeffnete bisher gleich den Editor - pausieren und loeschen ging nur in der Liste. Jetzt oeffnet sich eine kleine Karte: was die Automatik tut, wann, wie sie heute steht, die Bedingung, und die Knoepfe Bearbeiten, Pausieren bzw. Fortsetzen und Loeschen. Auch die Eintraege in den Baendern "Jederzeit" und "Pausiert" oeffnen sie. Pausieren und Loeschen gehen ueber toggleAutomation und deleteAutomation aus autoActionFuncs.js - dieselben Wege wie in der Liste, samt Rueckfrage bei abhaengigen Automatiken. Die Karte schliesst per Kreuz, Esc oder Klick daneben; solange sie offen ist, leuchtet die Kette des Eintrags. Co-Authored-By: Claude Opus 5 --- css/solar.css | 58 +++++++++++++++++++++++ js/solar/zeitleiste.js | 104 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 161 insertions(+), 1 deletion(-) diff --git a/css/solar.css b/css/solar.css index 85178c8..0efaffc 100644 --- a/css/solar.css +++ b/css/solar.css @@ -3100,6 +3100,64 @@ input[type='range']::-webkit-slider-runnable-track { .zl-l-balken { width: 22px; height: 7px; border-radius: 4px; background: rgba(95, 176, 220, .22); border: 1px solid rgba(95, 176, 220, .4); } .zl-l-kette { width: 22px; border-top: 1.2px dashed rgba(154, 166, 173, .7); } +/* Karte zu einem Eintrag: schwebt ueber der Zeitleiste, Akzent der Bahn. */ +.zl { position: relative; } +.zl-karte { + position: absolute; + z-index: 20; + width: min(22rem, 100%); + padding: .7rem .85rem .75rem; + border-radius: var(--ton-radius-klein, .5rem); + border: 1px solid rgba(var(--zl-farbe-rgb), .45); + border-top: 3px solid var(--zl-farbe); + background: var(--ton-flaeche-2, #2b3237); + box-shadow: 0 10px 28px rgba(0, 0, 0, .45); + font-size: .8rem; +} +.zl-karte-kopf { + display: flex; + align-items: center; + gap: .45rem; + margin-bottom: .45rem; +} +.zl-karte-kopf .bi { color: var(--zl-farbe); } +.zl-karte-name { + flex: 1 1 auto; + font: 500 .92rem var(--font-anzeige, inherit); + color: var(--ton-schrift, #e8ede9); +} +.zl-karte .btn-close.zl-karte-zu { font-size: .6rem; opacity: .6; } +.zl-karte-zeile { + display: flex; + gap: .5rem; + align-items: baseline; + padding: .12rem 0; + color: var(--ton-schrift, #e8ede9); +} +.zl-karte-zeile .bi { color: var(--ton-schrift-3, #6f7a81); flex: none; } +.zl-karte-stand.gut, .zl-karte-stand.gut .bi { color: var(--ton-gut, #4caf7d); } +.zl-karte-stand.leise { color: var(--ton-schrift-2, #9aa6ad); } +.zl-karte-wenn { + margin-top: .35rem; + padding-top: .35rem; + border-top: 1px solid var(--zl-linie); + color: var(--ton-schrift-3, #6f7a81); + font-size: .72rem; + line-height: 1.4; + max-height: 5.6em; + overflow: auto; +} +.zl-karte-knoepfe { + display: flex; + gap: .35rem; + margin-top: .65rem; +} +.zl-karte-knoepfe .btn.zl-karte-weg { + margin-left: auto; + color: var(--ton-schrift-3, #6f7a81); +} +.zl-karte-knoepfe .btn.zl-karte-weg:hover { color: var(--ton-schlecht, #e06060); } + /* Ansicht umschalten: eine Klasse mehr als Bootstrap (solar.css laedt vorher). */ .zl-ansicht .btn.active { background: rgba(0, 120, 143, .25); diff --git a/js/solar/zeitleiste.js b/js/solar/zeitleiste.js index fbeee91..5cd7947 100644 --- a/js/solar/zeitleiste.js +++ b/js/solar/zeitleiste.js @@ -438,6 +438,94 @@ function zlKetteHervorheben(id, an) { .forEach(el => el.classList.toggle("hell", an)); } +/* --- Karte zu einem Eintrag ---------------------------------------------- */ + +/** + * Ein Klick auf einen Eintrag öffnet eine kleine Karte statt gleich den + * Editor: was die Automatik tut, wann, wie sie heute steht - und die drei + * Dinge, die man damit tun will. Pausieren und Löschen gab es vorher nur in + * der Liste. + * + * Pausieren und Löschen gehen über dieselben Funktionen wie dort + * (toggleAutomation, deleteAutomation in autoActionFuncs.js) - samt der + * Rückfrage, was mit abhängigen Automatiken geschieht. Beide rufen danach + * refreshAutomations(), und das lädt die Zeitleiste mit. + */ +function zlKarteOeffnen(anker, id) { + const ziel = document.getElementById("zeitleiste"); + const d = zeitleiste.daten; + const a = d && d.automatiken.find(x => x.id === Number(id)); + if (!ziel || !a) return; + zlKarteSchliessen(); + + const zustand = a.enabled ? zlZustand(a, d) : "pausiert"; + const bahn = d.bahnen[a.bahn] || d.bahnen.sonstiges; + const wann = a.art === "fenster" + ? zlUhr(a.von) + "–" + zlUhr(a.bis) + (a.punkt !== null && a.punkt > a.von ? ", frühestens " + zlUhr(a.punkt) : "") + : a.art === "jederzeit" ? "jederzeit, wenn die Bedingung eintritt" + : (a.art === "kette" || a.wartet ? "frühestens " : "um ") + zlUhr(a.punkt) + (a.sonne ? " (Sonnenstand)" : ""); + const stand = { + gelaufen: ["bi-check2-circle", "gut", "gelaufen um " + a.laeufe.join(", ")], + nichtdran: ["bi-calendar-x", "leise", "nicht dran: " + a.grund], + vorbei: ["bi-dash-circle", "leise", "nicht gelaufen" + (a.wartet ? " – Bedingung war nicht erfüllt" : "")], + aktiv: ["bi-hourglass-split", "", a.wartet ? "wartet auf die Bedingung" : "steht an"], + kommt: ["bi-clock", "", "kommt noch"], + pausiert: ["bi-pause-circle", "leise", "pausiert"], + }[zustand]; + + const karte = document.createElement("div"); + karte.className = "zl-karte zl-bahn-" + a.bahn; + karte.setAttribute("role", "dialog"); + karte.innerHTML = + '
' + + '' + zlText(a.name) + "" + + '' + zlText(a.floor) + "" + + '
' + + '
' + zlText(a.satz) + "
" + + '
' + zlText(wann) + "
" + + '
' + zlText(stand[2]) + "
" + + (a.wenn ? '
Wenn: ' + zlText(a.wenn) + "
" : "") + + '
' + + '' + + (a.enabled + ? '' + : '') + + '' + + "
"; + karte.dataset.id = a.id; + ziel.appendChild(karte); + + // Unter den Eintrag, sonst darüber; waagerecht in der Karte der Zeitleiste halten. + const hoch = ziel.getBoundingClientRect(); + const r = anker.getBoundingClientRect(); + const breit = karte.offsetWidth, hoehe = karte.offsetHeight; + let links = r.left - hoch.left; + links = Math.max(0, Math.min(links, ziel.clientWidth - breit)); + let oben = r.bottom - hoch.top + 6; + if (r.bottom + hoehe + 12 > window.innerHeight && r.top - hoehe - 6 > 0) oben = r.top - hoch.top - hoehe - 6; + karte.style.left = links + "px"; + karte.style.top = oben + "px"; + zlKetteHervorheben(a.id, true); + + karte.addEventListener("click", function (ereignis) { + const knopf = ereignis.target.closest("[data-tun]"); + if (ereignis.target.closest(".zl-karte-zu")) { zlKarteSchliessen(); return; } + if (!knopf) return; + const tun = knopf.dataset.tun; + zlKarteSchliessen(); + if (tun === "bearbeiten") openAutoActionModal("?action=editor&id=" + a.id); + if (tun === "umschalten") toggleAutomation(a.id); + if (tun === "loeschen") deleteAutomation(a.id, a.name); + }); +} + +function zlKarteSchliessen() { + document.querySelectorAll("#zeitleiste .zl-karte").forEach(k => { + zlKetteHervorheben(k.dataset.id, false); + k.remove(); + }); +} + /* --- Laden und Bedienen -------------------------------------------------- */ async function zeitleisteLaden() { @@ -510,11 +598,15 @@ document.addEventListener("DOMContentLoaded", function () { ziel.addEventListener("click", function (ereignis) { const t = ereignis.target; + if (t.closest(".zl-karte")) return; // die Karte bedient sich selbst const ding = t.closest("[data-id]"); if (ding) { - openAutoActionModal("?action=editor&id=" + ding.dataset.id); + const offen = ziel.querySelector(".zl-karte"); + if (offen && offen.dataset.id === ding.dataset.id) { zlKarteSchliessen(); return; } + zlKarteOeffnen(ding, ding.dataset.id); return; } + zlKarteSchliessen(); const tag = t.closest("[data-tag]"); if (tag) { const schritt = Number(tag.dataset.tag); @@ -550,9 +642,19 @@ document.addEventListener("DOMContentLoaded", function () { ["mouseover", "mouseout"].forEach(typ => ziel.addEventListener(typ, function (ereignis) { const ding = ereignis.target.closest(".zl-ding"); if (!ding || (ereignis.relatedTarget && ding.contains(ereignis.relatedTarget))) return; + const offen = ziel.querySelector(".zl-karte"); + if (offen && offen.dataset.id === ding.dataset.id) return; // die offene Karte haelt ihre Kette hell zlKetteHervorheben(ding.dataset.id, typ === "mouseover"); })); + // Ausserhalb der Zeitleiste geklickt oder Esc: Karte zu. + document.addEventListener("click", function (ereignis) { + if (!ziel.contains(ereignis.target)) zlKarteSchliessen(); + }); + document.addEventListener("keydown", function (ereignis) { + if (ereignis.key === "Escape") zlKarteSchliessen(); + }); + // Neu zeichnen, wenn sich die Breite ändert - Stapeln und Ketten hängen daran. let warte = null; if (typeof ResizeObserver !== "undefined") {