diff --git a/workspace/assets/css/kopf.js b/workspace/assets/css/kopf.js new file mode 100644 index 00000000..d4f1ae4f --- /dev/null +++ b/workspace/assets/css/kopf.js @@ -0,0 +1,68 @@ +/* =================================================================== + Gemeinsames Verhalten der Kopfleiste: Zurück-Knopf und Abmelden. + + Liegt bewusst in einer eigenen Datei. Vorher stand das Abmelden in + jeder Seitendatei noch einmal -- fünfmal derselbe Block, fünfmal eine + Stelle zum Vergessen, wenn sich etwas ändert. + =================================================================== */ +(() => { + 'use strict'; + + const START = '/workspace/start.html'; + + /* Gibt es eine sinnvolle vorherige Seite INNERHALB des Workspace? + + Blindes history.back() reicht nicht: Wer die Adresse direkt eingibt + oder gerade von der Anmeldung kommt, landet damit außerhalb des + Workspace oder wieder im Anmeldeformular. Deshalb wird zuerst + geprüft, woher der Aufruf kam. */ + function hatVorherigeSeite() { + if (!document.referrer) return false; + let u; + try { u = new URL(document.referrer); } catch { return false; } + if (u.origin !== location.origin) return false; + if (!u.pathname.startsWith('/workspace/')) return false; + /* Die Anmeldeseite ist kein sinnvolles Ziel -- wer angemeldet ist, + würde dort sofort wieder weitergeleitet. */ + if (u.pathname === '/workspace/' || u.pathname.endsWith('/index.html')) return false; + /* Gleiche Seite heißt: neu geladen, nicht hergekommen. */ + if (u.pathname === location.pathname) return false; + return true; + } + + /* Die Startseite hat den Knopf gar nicht erst im Dokument -- dort gibt + es kein "davor". Deshalb genügt hier die Prüfung, ob er existiert. */ + const knopf = document.getElementById('zurueck'); + if (knopf) { + const beschriftung = knopf.querySelector('.zurueck-knopf__text'); + if (hatVorherigeSeite()) { + knopf.hidden = false; + knopf.addEventListener('click', () => { + /* history.back() statt eines festen Ziels: Es führt wirklich + dorthin zurück, wo man war -- samt Bildlaufposition. Nur wenn + es keine Historie gibt, wird ersatzweise umgeleitet. */ + if (history.length > 1) history.back(); + else location.assign(START); + }); + } else { + /* Kein Verlauf, aber wir sind in einem Unterbereich: dann wenigstens + ein Weg zur Übersicht. */ + knopf.hidden = false; + if (beschriftung) beschriftung.textContent = 'Übersicht'; + knopf.addEventListener('click', () => location.assign(START)); + } + } + + const abmelden = document.getElementById('abmelden'); + if (abmelden) { + abmelden.addEventListener('click', async () => { + abmelden.disabled = true; + try { + await fetch('/workspace/api/abmelden', { + method: 'POST', credentials: 'same-origin', + }); + } catch { /* Auch ohne Antwort weiterleiten -- das Cookie läuft ohnehin ab. */ } + location.assign('/workspace/'); + }); + } +})(); diff --git a/workspace/assets/css/start.css b/workspace/assets/css/start.css index 6f020545..7f982623 100644 --- a/workspace/assets/css/start.css +++ b/workspace/assets/css/start.css @@ -39,26 +39,57 @@ body.start { .kopfleiste .marke { margin: 0; font-size: .72rem; } /* Zurück-Knopf. Steht ganz links, weil dort jeder ihn erwartet -- gleiche - Stelle wie im Browser. Bleibt verborgen (hidden), wenn es nichts gibt, - wohin er führen könnte; das entscheidet kopf.js. */ + Stelle wie im Browser. Auf der Startseite gibt es ihn gar nicht: Dort + ist kein "davor", und ein Knopf, der nichts tut, ist schlimmer als + keiner. Auf den übrigen Seiten entscheidet kopf.js, ob er erscheint. */ .zurueck-knopf { flex: 0 0 auto; - padding: 7px 13px; - background: rgba(255, 255, 255, .045); - border: 1px solid var(--rand); - border-radius: 9px; + display: inline-flex; align-items: center; gap: 7px; + padding: 8px 15px 8px 12px; + /* Verlaufsrahmen: Die Füllung ist DECKEND, dadurch bleibt vom unteren + Verlauf nur der 1px schmale Rand sichtbar. Wäre sie durchsichtig, + schiene der Rahmenverlauf durch die ganze Fläche und der Knopf sähe + aus wie eine Hauptaktion -- das soll er gerade nicht. */ + background: + linear-gradient(180deg, #151c28, #10161f) padding-box, + linear-gradient(180deg, rgba(158, 196, 230, .40), rgba(158, 196, 230, .12)) border-box; + border: 1px solid transparent; + border-radius: 999px; color: var(--text-leise); - font: inherit; font-size: .82rem; + font: inherit; font-size: .82rem; font-weight: 500; + letter-spacing: .01em; cursor: pointer; white-space: nowrap; - transition: background var(--tempo), border-color var(--tempo), color var(--tempo); + box-shadow: 0 1px 2px rgba(0, 0, 0, .35), inset 0 1px 0 rgba(255, 255, 255, .06); + transition: color var(--tempo), box-shadow var(--tempo), background var(--tempo); } + +.zurueck-knopf__pfeil { + width: 15px; height: 15px; flex: 0 0 auto; + fill: none; stroke: currentColor; + stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; + transition: transform var(--tempo); +} + .zurueck-knopf:hover { - background: rgba(255, 255, 255, .08); - border-color: var(--rand-hell); color: var(--text); + background: + linear-gradient(180deg, #1a2431, #131b25) padding-box, + linear-gradient(180deg, rgba(63, 189, 245, .62), rgba(138, 118, 255, .30)) border-box; + box-shadow: 0 2px 12px -4px rgba(63, 189, 245, .35), inset 0 1px 0 rgba(255, 255, 255, .08); +} +/* Der Pfeil rückt beim Überfahren ein Stück nach links -- die Bewegung + sagt in welche Richtung es geht, ohne zusätzlichen Text. */ +.zurueck-knopf:hover .zurueck-knopf__pfeil { transform: translateX(-2px); } + +.zurueck-knopf:active { box-shadow: inset 0 1px 3px rgba(0, 0, 0, .4); } +.zurueck-knopf:active .zurueck-knopf__pfeil { transform: translateX(-3px); } + +@media (prefers-reduced-motion: reduce) { + .zurueck-knopf__pfeil { transition: none; } + .zurueck-knopf:hover .zurueck-knopf__pfeil, + .zurueck-knopf:active .zurueck-knopf__pfeil { transform: none; } } -.zurueck-knopf:active { transform: translateX(-1px); } .kopfleiste__rechts { display: flex; align-items: center; gap: 12px; } diff --git a/workspace/assets/js/kopf.js b/workspace/assets/js/kopf.js index bd3d93d8..d4f1ae4f 100644 --- a/workspace/assets/js/kopf.js +++ b/workspace/assets/js/kopf.js @@ -30,9 +30,11 @@ return true; } + /* Die Startseite hat den Knopf gar nicht erst im Dokument -- dort gibt + es kein "davor". Deshalb genügt hier die Prüfung, ob er existiert. */ const knopf = document.getElementById('zurueck'); if (knopf) { - const aufStartseite = location.pathname.endsWith('/start.html'); + const beschriftung = knopf.querySelector('.zurueck-knopf__text'); if (hatVorherigeSeite()) { knopf.hidden = false; knopf.addEventListener('click', () => { @@ -42,15 +44,13 @@ if (history.length > 1) history.back(); else location.assign(START); }); - } else if (!aufStartseite) { + } else { /* Kein Verlauf, aber wir sind in einem Unterbereich: dann wenigstens ein Weg zur Übersicht. */ knopf.hidden = false; - knopf.textContent = '← Übersicht'; + if (beschriftung) beschriftung.textContent = 'Übersicht'; knopf.addEventListener('click', () => location.assign(START)); } - /* Auf der Startseite ohne Verlauf bleibt der Knopf aus -- ein Knopf, - der nichts tut, ist schlimmer als keiner. */ } const abmelden = document.getElementById('abmelden'); diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 57f24088..c31618dc 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -15,7 +15,12 @@
Creator Workspace · Aufgaben
Creator Workspace · Kalender
Creator Workspace · Personen
Creator Workspace · Profil
Dogfather Universe · Creator Workspace