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") {