Horizontal bar-chart widget placed between the SearchFilterBar and DocumentList on /documents. Bar height per month reflects document density across the archive. Drag or click to select a date range; the document list filters to that period. Hidden when calendar view is active.
Seven named zones. Every implementation decision maps back to one of these.
Das Widget verwendet ausschließlich diese Tokens — keine Hard-coded-Werte in der Komponente.
Vier Zustände. Gleiche Markup-Struktur — nur CSS-Klassen und ARIA-Attribute ändern sich.
crosshair über den Balken.pointer.Gleiches Verhalten, Farben remappt. Auswahl-Balken wechseln zu Mint (invertiert zu Light mode).
| Szenario | Verhalten |
|---|---|
| Click auf einzelnen Balken | Setzt Start und Ende auf denselben Monat → Einzelmonat-Filter. Direkt gefiltert, kein Drag nötig. |
| Click + Drag | Start = Mousedown-Position, Ende = Mousemove-Position. Richtung egal (rechts → links und links → rechts erlaubt). Filter wird live aktualisiert während des Drags. |
| Handle drag | Start- oder End-Handle verschieben justiert die bestehende Auswahl. Der andere Handle bleibt fixiert. |
| Click außerhalb der Balken | Auswahl wird gelöscht. Liste zeigt alle Dokumente. |
| Löschen-Button (×) | Auswahl löschen. Badge in SearchFilterBar verschwindet. Bars kehren zu Idle zurück. |
| Kombination mit anderen Filtern | AND-Semantik. Zeitachsen-Filter + Personen-Filter + Tag-Filter sind kumulativ. |
| Wechsel zu Kalenderansicht | Timeline-Widget wird ausgeblendet (display:none). Aktive Auswahl bleibt im State erhalten und wird wieder angezeigt beim Rückwechsel. |
| Monate ohne Dokumente | Bar hat Mindesthöhe von 2px (sichtbar, aber minimal). Tooltip zeigt „Monat Jahr · 0 Dokumente". Klick auf Null-Bar setzt Single-Month-Filter (leere Liste erwartet). |
| Granularität (OQ-2) | Entscheidung: ganze Monate. Start = 1. des Startmonats, Ende = letzter Tag des Endmonats. Keine Wochen oder Tage auswählbar. |
| Startmonat beim Öffnen (OQ-1) | Entscheidung: auto-derived range. X-Achse leitet sich aus frühestem und spätestem documentDate ab. Kein festes Datum (1880–heute). |
| Keyboard-Zugang | Tab fokussiert das Widget. Pfeiltasten verschieben einen virtuellen Cursor Monat für Monat. Enter/Space setzt Start; zweites Enter setzt Ende. Escape bricht Drag ab. |
| Mobile (<768 px) | Widget kollabiert auf kompakte Zeile: „Zeitraum: [Jan 1914 – Dez 1920] ×" — kein Balkendiagramm. Tap öffnet ein Modal mit dem vollen Widget. |
documentDate in der Datenbank. Beim Laden der Seite holt der Server GET /api/documents/stats (oder ähnlich) mit minDate und maxDate. Kein hard-coded Bereich.?dateFrom=YYYY-MM&dateTo=YYYY-MM auf dem bestehenden Search-Endpoint, oder dedizierter Density-Endpoint GET /api/documents/density?from=1900-01&to=1950-12 der [{month:"1915-08", count:24}, ...] zurückgibt. Das bestehende Offset-Paging entfällt für diesen Widget-Call.| Element | Tailwind-Klassen | Pixelwert | Anmerkung |
|---|---|---|---|
| Widget-Container | bg-surface border border-line rounded-sm shadow-sm px-4 py-3 | padding 16px 12px | Gleiche Karte wie SearchFilterBar und DocumentList |
| Widget-Header Zeile | flex items-center justify-between mb-2 | margin-bottom 8px | Label links, Gesamtanzahl rechts |
| Header Label | text-[10px] font-bold uppercase tracking-widest text-ink-3 | 10px / 700 | Identisch zu anderen Section-Labels |
| Gesamtanzahl-Text | text-[10px] text-ink-3 — wenn Auswahl aktiv: text-primary font-bold | 10px | Zeigt gefilterte Anzahl sobald Auswahl aktiv |
| Bar-Container | flex items-end gap-px h-10 w-full | height 40px | h-12 (48px) wenn Platz es erlaubt |
| Bar idle | flex-1 rounded-t-sm min-h-[2px] + inline height als %-Wert | var | CSS custom property --bar-h setzen via JS |
| Bar: Farbe idle | bg-[#D4EDE9] (light) / bg-[#0E2535] (dark) | — | Helles Mint, nicht brand-mint — zu dominant |
| Bar: Farbe hover | hover:bg-mint | — | transition-colors duration-100 |
| Bar: Farbe selected | bg-primary (light) / bg-mint (dark) | — | Invertierung in Dark mode beabsichtigt |
| Bar: Farbe outside | bg-muted | — | Gedimmt, aber noch sichtbar |
| Baseline | border-t border-line mt-0.5 mb-1 | 1px | Trennt Balken von X-Achsen-Labels |
| X-Achse Labels | flex justify-between text-[9px] text-ink-3 | 9px | Nur Jahres-Labels alle 10 Jahre; mehr wenn Platz |
| Drag Handle | absolute w-3 h-3 rounded-full bg-primary border-2 border-surface shadow cursor-ew-resize -top-1.5 left-1/2 -translate-x-1/2 z-10 | 12px / 12px | Touch target: mind. 44px durch p-4 auf einem unsichtbaren Wrapper |
| Tooltip | absolute bottom-full left-1/2 -translate-x-1/2 mb-1 bg-ink text-surface text-[9px] font-bold px-1.5 py-0.5 rounded-sm whitespace-nowrap z-20 pointer-events-none | 9px | Erscheint via group-hover:block hidden |
| Badge in SearchFilterBar | flex items-center gap-1 bg-primary text-primary-fg text-[9px] font-bold px-2 py-1 rounded-sm | 9px | Löschen-Button innerhalb: ml-1 text-sm leading-none hover:opacity-70 |
| Mobile Collapse | Widget: sm:block hidden — Mobile-Badge: sm:hidden flex items-center gap-2 | — | Unter 640px nur Badge-Zeile sichtbar, kein Chart |
| Komponenten-Name | TimelineDensityFilter.svelte | — | Props: density: MonthBucket[], bind:from, bind:to, onchange |