Symmetric date-range picker: 3 columns (Past / NOW / Future), each sorted by closeness to NOW #110

Open
opened 2026-05-25 14:37:15 +00:00 by mAi · 1 comment
Collaborator

m's report (2026-05-25 16:36)

Reviewing atlas's #79 picker live:

Letzte 30 Tage
Letzte 7 Tage
⌖
Alles
Nächste 7 Tage
Nächste 30 Tage
Nächste 90 Tage

This is a nice try but I would want this to be three columns - Past / NOW / Future - each gets an ALL or -7d / -30d -90d or the same with +. And we sort by closeness to NOW. So inverted order when in a row... And in the middle Column "NOW" (even if not titled that) we should have "Today" and "All"

Layout — three columns, closeness-to-NOW sort

┌────────────────┬────────────┬────────────────┐
│   Vergangen    │     ⌖      │    Zukunft     │
├────────────────┼────────────┼────────────────┤
│ Letzte 7 Tage  │   Heute    │ Nächste 7 Tage │
│ Letzte 30 Tage │   Alles    │ Nächste 30 Tg  │
│ Letzte 90 Tage │            │ Nächste 90 Tg  │
│ Alles          │            │ Alles          │
└────────────────┴────────────┴────────────────┘
  • Past column (left): -7d at top → -30d-90dAlle Vergangenheit at bottom. Sorted by closeness to NOW.
  • NOW column (middle): Heute (today) + Alles (all-time). No title or just the ⌖ glyph.
  • Future column (right): +7d at top → +30d+90dAlle Zukunft at bottom. Same closeness-to-NOW sort as past.

The key insight m wants visualised: the picker is a spatial representation of time around NOW, not a horizontal list. Each column's top is closest to NOW; bottom is furthest.

What to do

  1. frontend/src/client/date-range-picker.ts (atlas's main render) — restructure the popover body into a 3-column grid.
  2. CSS: .drp-popover becomes a 3-column flex/grid container. Each column is a vertical chip stack.
  3. Chip render: same as today (button + label + active-state lime accent).
  4. Active-chip indicator: lime fill + checkmark — visually clear which one is currently selected.
  5. Custom range: keep the existing inline date-pair below the 3-column block (full width, separate visual section).
  6. .test.ts tests for date-range-picker-pure.ts may need an update if column ordering matters in the chip list export.

Hard rules

  • Mobile: at narrow widths, the 3 columns stack vertically (one column per row) but maintain the closeness-to-NOW sort within each.
  • Keep all 12 horizon values (Slice A scope) — just regroup the layout.
  • Keep upckommentar-slicer deferral (Slice D).
  • go build ./... && go test ./internal/... && cd frontend && bun run build clean.
  • Branch: mai/<worker>/date-picker-3col-layout.

Out of scope

  • Changing the horizon set (still 12 values + custom).
  • Adding new chip variants beyond what Slice A has.
  • Renaming the i18n keys (atlas's keys stay).

Reporting

mai report completed with branch + SHAs + before/after layout description + verification: open any filter using the date picker → confirm 3-column layout → past chips closest-to-NOW at top → middle 'Heute' + 'Alles' → future chips closest-to-NOW at top.

## m's report (2026-05-25 16:36) Reviewing atlas's #79 picker live: ``` Letzte 30 Tage Letzte 7 Tage ⌖ Alles Nächste 7 Tage Nächste 30 Tage Nächste 90 Tage ``` > This is a nice try but I would want this to be three columns - Past / NOW / Future - each gets an ALL or -7d / -30d -90d or the same with +. And we sort by closeness to NOW. So inverted order when in a row... And in the middle Column "NOW" (even if not titled that) we should have "Today" and "All" ## Layout — three columns, closeness-to-NOW sort ``` ┌────────────────┬────────────┬────────────────┐ │ Vergangen │ ⌖ │ Zukunft │ ├────────────────┼────────────┼────────────────┤ │ Letzte 7 Tage │ Heute │ Nächste 7 Tage │ │ Letzte 30 Tage │ Alles │ Nächste 30 Tg │ │ Letzte 90 Tage │ │ Nächste 90 Tg │ │ Alles │ │ Alles │ └────────────────┴────────────┴────────────────┘ ``` - **Past column** (left): `-7d` at top → `-30d` → `-90d` → `Alle Vergangenheit` at bottom. Sorted by closeness to NOW. - **NOW column** (middle): `Heute` (today) + `Alles` (all-time). No title or just the ⌖ glyph. - **Future column** (right): `+7d` at top → `+30d` → `+90d` → `Alle Zukunft` at bottom. Same closeness-to-NOW sort as past. The key insight m wants visualised: the picker is a **spatial** representation of time around NOW, not a horizontal list. Each column's top is closest to NOW; bottom is furthest. ## What to do 1. `frontend/src/client/date-range-picker.ts` (atlas's main render) — restructure the popover body into a 3-column grid. 2. CSS: `.drp-popover` becomes a 3-column flex/grid container. Each column is a vertical chip stack. 3. Chip render: same as today (button + label + active-state lime accent). 4. Active-chip indicator: lime fill + checkmark — visually clear which one is currently selected. 5. Custom range: keep the existing inline date-pair below the 3-column block (full width, separate visual section). 6. `.test.ts` tests for `date-range-picker-pure.ts` may need an update if column ordering matters in the chip list export. ## Hard rules - Mobile: at narrow widths, the 3 columns stack vertically (one column per row) but maintain the **closeness-to-NOW** sort within each. - Keep all 12 horizon values (Slice A scope) — just regroup the layout. - Keep upckommentar-slicer deferral (Slice D). - `go build ./... && go test ./internal/... && cd frontend && bun run build` clean. - Branch: `mai/<worker>/date-picker-3col-layout`. ## Out of scope - Changing the horizon set (still 12 values + custom). - Adding new chip variants beyond what Slice A has. - Renaming the i18n keys (atlas's keys stay). ## Reporting `mai report completed` with branch + SHAs + before/after layout description + verification: open any filter using the date picker → confirm 3-column layout → past chips closest-to-NOW at top → middle 'Heute' + 'Alles' → future chips closest-to-NOW at top.
mAi self-assigned this 2026-05-25 14:37:15 +00:00
Author
Collaborator

3-column layout shipped — Past / NOW / Future (closeness-to-NOW sort)

Branch: mai/hermes/gitster-date-range
Commit: 4f910e31ea

Before → After

BEFORE (horizontal row, atlas #79):
  Letzte 30 Tage · Letzte 7 Tage · ⌖ALLES · Morgen · Nächste 7 Tage · Nächste 30 Tage · Nächste 90 Tage

AFTER (3 columns, closeness-to-NOW sort):
  ┌─────────────────────────┬──────────────┬─────────────────────────┐
  │      Vergangenheit      │      ⌖       │        Zukunft          │
  ├─────────────────────────┼──────────────┼─────────────────────────┤
  │ Letzte 7 Tage           │   Heute      │ Nächste 7 Tage          │
  │ Letzte 30 Tage          │   Alles      │ Nächste 30 Tage         │
  │ Letzte 90 Tage          │              │ Nächste 90 Tage         │
  │ Ganze Vergangenheit     │              │ Ganze Zukunft           │
  └─────────────────────────┴──────────────┴─────────────────────────┘
  [ Anpassen ▸ ]  (custom-range section unchanged, full width below the grid)

What I changed

  • frontend/src/client/date-range-picker.tsrenderPanel now builds a .date-range-grid with three vertical .date-range-col children. Past iterates PAST_HORIZONS reversed (past_1d at top → past_all at bottom). NOW hosts next_1d ("Heute") + any ("Alles") plus a glyph header. Future iterates NEXT_HORIZONS minus next_1d (which moved to NOW). Legacy all horizon keeps lighting up Alles (saved-Custom-View back-compat). The old renderFan / renderCenter helpers are gone.
  • frontend/src/styles/global.css.date-range-row / .date-range-fan / .date-range-center{,-btn,-glyph,-label} replaced with .date-range-grid (3-col CSS grid) + .date-range-col (vertical chip stack) + .date-range-col-heading (small caps label + ⌖ variant). Chips stretch to 100% column width for clean vertical alignment. Panel widened from 32rem → 34rem so "Ganze Vergangenheit" never wraps. Mobile (max-width: 540px) collapses the grid to a single column, preserving in-column sort.
  • frontend/src/client/i18n.tsdate_range.horizon.next_1d fixed from "Morgen"/"Tomorrow" → "Heute"/"Today". next_1d bounds are [today, tomorrow) = single-day today, so the prior label was semantically wrong; this aligns label with bounds and matches m's NOW-column spec.
  • frontend/src/client/filter-bar/axes.tsDEFAULT_TIME_PRESETS updated to m's spec (past_7d/30d/90d/all · next_1d · any · next_7d/30d/90d/all · custom). projects-detail.ts continues to override via timePresets for its past-only Verlauf surface.

Horizon values preserved

All 12 fan horizons in the union stay (past_1d / past_14d / next_14d included) — registries unchanged, parseURL still accepts them for saved URLs. The default UI just no longer surfaces chips for them; surfaces wanting finer granularity opt back in via timePresets.

Verification

  • bun test src/client/date-range-picker-pure.test.ts — 38 pass, 0 fail
  • cd frontend && bun run build — clean (i18n codegen: 2873 keys (unchanged), branding clean)
  • go build ./... — clean
  • go test ./internal/... — pass

Manual-verification steps (next eyes)

  1. Open any filter-bar surface with the time axis (e.g. dashboard / inbox).
  2. Click the time-range trigger.
  3. Confirm: 3 columns (Vergangenheit · ⌖ · Zukunft), each chip stack top→bottom is closest-to-NOW → farthest.
  4. Middle column shows Heute + Alles (no other chips).
  5. Click any chip → it commits + closes the popover; the trigger label updates.
  6. Narrow the window below 540px → grid collapses to a single column, sort preserved.
  7. Anpassen still expands inline below the grid.

If the visual width feels off (e.g. "Ganze Vergangenheit" wraps on a specific theme), the 34rem panel width is the knob — bump to 36rem in .date-range-panel.

## 3-column layout shipped — Past / NOW / Future (closeness-to-NOW sort) Branch: `mai/hermes/gitster-date-range` Commit: https://mgit.msbls.de/m/paliad/commit/4f910e31ea849d02050da0b5944c4502f09575ef ### Before → After ``` BEFORE (horizontal row, atlas #79): Letzte 30 Tage · Letzte 7 Tage · ⌖ALLES · Morgen · Nächste 7 Tage · Nächste 30 Tage · Nächste 90 Tage AFTER (3 columns, closeness-to-NOW sort): ┌─────────────────────────┬──────────────┬─────────────────────────┐ │ Vergangenheit │ ⌖ │ Zukunft │ ├─────────────────────────┼──────────────┼─────────────────────────┤ │ Letzte 7 Tage │ Heute │ Nächste 7 Tage │ │ Letzte 30 Tage │ Alles │ Nächste 30 Tage │ │ Letzte 90 Tage │ │ Nächste 90 Tage │ │ Ganze Vergangenheit │ │ Ganze Zukunft │ └─────────────────────────┴──────────────┴─────────────────────────┘ [ Anpassen ▸ ] (custom-range section unchanged, full width below the grid) ``` ### What I changed - **`frontend/src/client/date-range-picker.ts`** — `renderPanel` now builds a `.date-range-grid` with three vertical `.date-range-col` children. Past iterates `PAST_HORIZONS` **reversed** (`past_1d` at top → `past_all` at bottom). NOW hosts `next_1d` ("Heute") + `any` ("Alles") plus a `⌖` glyph header. Future iterates `NEXT_HORIZONS` minus `next_1d` (which moved to NOW). Legacy `all` horizon keeps lighting up Alles (saved-Custom-View back-compat). The old `renderFan` / `renderCenter` helpers are gone. - **`frontend/src/styles/global.css`** — `.date-range-row / .date-range-fan / .date-range-center{,-btn,-glyph,-label}` replaced with `.date-range-grid` (3-col CSS grid) + `.date-range-col` (vertical chip stack) + `.date-range-col-heading` (small caps label + ⌖ variant). Chips stretch to 100% column width for clean vertical alignment. Panel widened from 32rem → 34rem so "Ganze Vergangenheit" never wraps. Mobile (`max-width: 540px`) collapses the grid to a single column, preserving in-column sort. - **`frontend/src/client/i18n.ts`** — `date_range.horizon.next_1d` fixed from "Morgen"/"Tomorrow" → "Heute"/"Today". `next_1d` bounds are `[today, tomorrow)` = single-day today, so the prior label was semantically wrong; this aligns label with bounds and matches m's NOW-column spec. - **`frontend/src/client/filter-bar/axes.ts`** — `DEFAULT_TIME_PRESETS` updated to m's spec (`past_7d/30d/90d/all` · `next_1d` · `any` · `next_7d/30d/90d/all` · `custom`). `projects-detail.ts` continues to override via `timePresets` for its past-only Verlauf surface. ### Horizon values preserved All 12 fan horizons in the union stay (`past_1d` / `past_14d` / `next_14d` included) — registries unchanged, `parseURL` still accepts them for saved URLs. The default UI just no longer surfaces chips for them; surfaces wanting finer granularity opt back in via `timePresets`. ### Verification - `bun test src/client/date-range-picker-pure.test.ts` — 38 pass, 0 fail - `cd frontend && bun run build` — clean (`i18n codegen: 2873 keys (unchanged)`, branding clean) - `go build ./...` — clean - `go test ./internal/...` — pass ### Manual-verification steps (next eyes) 1. Open any filter-bar surface with the time axis (e.g. dashboard / inbox). 2. Click the time-range trigger. 3. Confirm: 3 columns (Vergangenheit · ⌖ · Zukunft), each chip stack top→bottom is closest-to-NOW → farthest. 4. Middle column shows **Heute** + **Alles** (no other chips). 5. Click any chip → it commits + closes the popover; the trigger label updates. 6. Narrow the window below 540px → grid collapses to a single column, sort preserved. 7. Anpassen still expands inline below the grid. If the visual width feels off (e.g. "Ganze Vergangenheit" wraps on a specific theme), the 34rem panel width is the knob — bump to 36rem in `.date-range-panel`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#110
No description provided.