feat(calendar): polish grid styling + mobile breakpoint + design doc
Phase 5e slice B. Polish pass on the month grid: HTMX-swappable filter
chip strip, mobile breakpoint that collapses the 7-column table into a
vertical list of days, refined CSS for hover/today/adjacent-month, and
the docs/design.md §17 entry that pins the contract.
Templates:
- web/templates/calendar_section.tmpl (new) — extracted #calendar-section
partial. Houses the filter chip strip (form with hx-get=/calendar
hx-target=#calendar-section), counts line, and the grid <table>.
- web/templates/calendar.tmpl trimmed to the page chrome (h1, prev/next
nav, today link) + {{template "calendar-section" .}}. Chrome stays
outside the HTMX swap because chip filtering preserves the month
context.
web/calendar.go:
- handleCalendar now branches on HX-Request: HTMX → calendar_section
fragment, full GET → calendar (chrome + section). Same pattern as
/timeline and /dashboard.
- calendarDay gains LongLabel ("Mi., 14. Mai") — populated by new
formatCalendarLongLabel helper. Hidden on desktop via CSS; revealed at
the ≤480px breakpoint where the column header drops out.
web/server.go:
- Calendar template now bundles the section partial. New calendar_section
template registered as a standalone fragment for HTMX swaps. New
render() entry case "calendar_section" → "calendar-section".
web/static/style.css:
- Refined .calendar-nav (tabular numerals, transition, no surface-alt
fallback fighting the theme).
- New #calendar-filterbar layout (flex, gap, counts pushed right).
- .calendar-cell hover background, adjacent-month opacity bump (0.4→0.45
+ 0.7 on hover so it doesn't disappear when reading lead-in days).
- .today-pill line-height fix so it sits flush in the cell header.
- .cell-row min-width on .time slot, tighter line-height, 0.82em font.
- @media (max-width: 480px) breakpoint: grid + thead + tbody + tr + th +
td all → display:block. Thead hidden; .day-label revealed. Adjacent-
month cells DISPLAY:NONE on mobile (their value on desktop is grid
rectangularity; on a vertical list they're just confusing). Cell rows
bump to 0.95em for readability.
docs/design.md:
- New §17 Calendar view (Phase 5e). Documents sources (VEVENT/VTODO/
dated item_links), what's excluded (creation markers + Gitea + untimed),
the layout calculation, filter integration via TreeFilter, cache key,
the mobile breakpoint, and the German register choice.
Tests (additive, all passing):
- TestFormatCalendarLongLabel — pins the German weekday + day + month
abbreviation (Mo./Di./.../So., 1.–31., Jan/Feb/März/.../Dez).
- TestCalendarFilterChipStripRenders — chip strip present + hx-target +
hx-get + hidden month input + tag/mgmt/kind multi-selects.
- TestCalendarHTMXReturnsSectionOnly — HX-Request returns #calendar-
section only (no <body>, no .calendar-nav chrome).
- TestCalendarCellCarriesLongLabel — May 4 cell ("Mo., 4. Mai") present
in HTML so the mobile breakpoint CSS reveal works.
Net: +315 / -61.
This commit is contained in:
@@ -274,16 +274,23 @@ func New(s *store.Store, logger *slog.Logger) (*Server, error) {
|
||||
}
|
||||
pages["timeline_section"] = timelineSection
|
||||
|
||||
// Calendar page — month grid view, Phase 5e. No HTMX fragment yet;
|
||||
// filter/month changes are full-page nav.
|
||||
// Calendar page — month grid view, Phase 5e. Bundles the section
|
||||
// partial so HTMX swaps (filter chip strip) and the full-page render
|
||||
// share definitions.
|
||||
calTmpl, err := template.New("calendar").Funcs(funcs).ParseFS(templatesFS,
|
||||
"templates/layout.tmpl",
|
||||
"templates/calendar.tmpl",
|
||||
"templates/calendar_section.tmpl",
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse calendar: %w", err)
|
||||
}
|
||||
pages["calendar"] = calTmpl
|
||||
calSection, err := template.New("calendar_section").Funcs(funcs).ParseFS(templatesFS, "templates/calendar_section.tmpl")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse calendar_section: %w", err)
|
||||
}
|
||||
pages["calendar_section"] = calSection
|
||||
|
||||
// Bulk-edit page + its fragment + per-row chip cells. The chip cells share
|
||||
// definitions with bulk_section so we parse them together every time.
|
||||
@@ -903,6 +910,8 @@ func (s *Server) render(w http.ResponseWriter, r *http.Request, name string, dat
|
||||
entry = "dashboard-section"
|
||||
case "timeline_section":
|
||||
entry = "timeline-section"
|
||||
case "calendar_section":
|
||||
entry = "calendar-section"
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := t.ExecuteTemplate(w, entry, data); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user