Render all months in sidebar

This commit is contained in:
2026-07-01 11:00:41 +02:00
parent ecaf3f4e12
commit 8c9b448bdc
4 changed files with 59 additions and 78 deletions
+8 -13
View File
@@ -1,29 +1,24 @@
<div class="diary-cal panel panel-sidebar" <div class="diary-cal panel panel-sidebar"
data-display-year="{{.DisplayYear}}" data-display-year="{{.DisplayYear}}"
data-display-month="{{.DisplayMonth}}"> data-display-month="{{.DisplayMonth}}">
<div class="panel-header"><a href="{{.DiaryURL}}">Chronological</a></div> <div class="panel-header row">
<div class="diary-cal-nav"> <a href="{{.DiaryURL}}">Chronological</a>
<div class="dropdown diary-cal-drop"> <div class="dropdown diary-cal-drop">
<button type="button" class="btn" data-cal-month-link data-action="cal-month-drop" aria-expanded="false" title="Monat wählen"> {{.DisplayMonthName}} </button> <button type="button" class="btn" data-action="cal-year-drop" aria-expanded="false" title="Jahr wählen">{{.DisplayYear}} </button>
<div class="dropdown-menu scrollable">
{{range .Months}}<a class="btn btn-block" data-cal-month-jump="{{.Num}}" href="{{.AnchorURL}}">{{.Name}}</a>{{end}}
</div>
</div>
<a href="{{.YearURL}}" class="diary-cal-heading">{{.DisplayYear}}</a>
<div class="dropdown diary-cal-drop">
<button type="button" class="btn" data-action="cal-year-drop" aria-expanded="false" title="Jahr wählen"></button>
<div class="dropdown-menu align-right scrollable"> <div class="dropdown-menu align-right scrollable">
{{range .Years}}<a class="btn btn-block{{if .IsCurrent}} cal-current{{end}}" href="{{.URL}}">{{.Num}}</a>{{end}} {{range .Years}}<a class="btn btn-block{{if .IsCurrent}} cal-current{{end}}" href="{{.URL}}">{{.Num}}</a>{{end}}
</div> </div>
</div> </div>
</div> </div>
{{range .Months}} {{range $i, $m := .Months}}
<table class="diary-cal-grid" data-cal-month="{{.Num}}"{{if ne .Num $.DisplayMonth}} hidden{{end}}> {{if $i}}<hr/>{{end}}
<div class="diary-cal-month"><a href="{{$m.AnchorURL}}">{{$m.Name}}</a></div>
<table class="diary-cal-grid" data-cal-month="{{$m.Num}}">
<thead> <thead>
<tr><th>Mo</th><th>Di</th><th>Mi</th><th>Do</th><th>Fr</th><th>Sa</th><th>So</th></tr> <tr><th>Mo</th><th>Di</th><th>Mi</th><th>Do</th><th>Fr</th><th>Sa</th><th>So</th></tr>
</thead> </thead>
<tbody> <tbody>
{{range .Weeks}}<tr>{{range .}}<td class="{{if .IsCurrent}}cal-current{{else if .IsToday}}cal-today{{end}}{{if and .Num (not .HasEntry)}} cal-empty{{end}}">{{if .Num}}<a href="{{.URL}}">{{.Num}}</a>{{end}}</td>{{end}}</tr> {{range $m.Weeks}}<tr>{{range .}}<td class="{{if .IsCurrent}}cal-current{{else if .IsToday}}cal-today{{end}}{{if and .Num (not .HasEntry)}} cal-empty{{end}}">{{if .Num}}<a href="{{.URL}}">{{.Num}}</a>{{end}}</td>{{end}}</tr>
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
+30 -34
View File
@@ -3,47 +3,43 @@
if (!cal) return; if (!cal) return;
cal.querySelectorAll(".dropdown > button").forEach(wireDropdown); cal.querySelectorAll(".dropdown > button").forEach(wireDropdown);
var displayYear = parseInt(cal.dataset.displayYear, 10); var displayMonth = parseInt(cal.dataset.displayMonth, 10);
var current = parseInt(cal.dataset.displayMonth, 10);
var monthLabel = cal.querySelector("[data-cal-month-link]");
var months = {}; var months = {};
cal.querySelectorAll("[data-cal-month]").forEach(function (t) { cal.querySelectorAll("[data-cal-month]").forEach(function (t) {
months[parseInt(t.dataset.calMonth, 10)] = t; months[parseInt(t.dataset.calMonth, 10)] = t;
}); });
var jumpLinks = {};
cal.querySelectorAll("[data-cal-month-jump]").forEach(function (a) {
jumpLinks[parseInt(a.dataset.calMonthJump, 10)] = a;
});
function show(m) { // Centering only applies inside the persistent desktop rail (.sidebar). On
if (m === current) return; // mobile the widget is re-parented into .overlay-body, where we leave the
if (!months[m]) return; // grids stacked from January and do not scroll (design decision).
months[current].hidden = true; function railContainer() {
months[m].hidden = false; var el = cal.parentElement;
current = m; while (el) {
if (monthLabel) { if (el.classList) {
var label = jumpLinks[m]; if (el.classList.contains("overlay-body")) return null;
if (label) monthLabel.textContent = " " + label.textContent + " "; if (el.classList.contains("sidebar")) return el;
}
el = el.parentElement;
} }
return null;
} }
// Dropdown month picks scroll via the href anchor; we also swap the grid // Scroll only the rail so the given month grid sits in its vertical center;
// so the calendar reflects what the user just navigated to. // the main document/window position is untouched (priority: don't disturb
Object.keys(jumpLinks).forEach(function (key) { // the reading position).
var a = jumpLinks[key]; function centerMonth(m) {
a.addEventListener("click", function () { show(parseInt(key, 10)); }); var grid = months[m];
}); if (!grid) return;
var container = railContainer();
// Any in-page anchor click (#YYYY-MM or #YYYY-MM-DD) updates the calendar if (!container) return;
// so it tracks the user's focus through the year page. var offset = grid.getBoundingClientRect().top -
function syncFromHash() { container.getBoundingClientRect().top + container.scrollTop;
var h = window.location.hash; container.scrollTop = offset - (container.clientHeight - grid.clientHeight) / 2;
if (!h) return;
var m = h.match(/^#(\d{4})-(\d{2})(?:-\d{2})?$/);
if (!m) return;
if (parseInt(m[1], 10) !== displayYear) return;
show(parseInt(m[2], 10));
} }
window.addEventListener("hashchange", syncFromHash);
syncFromHash(); // Center the current month once, on initial render. Anchor navigation
// (#YYYY-MM / #YYYY-MM-DD) deliberately does NOT re-center the rail —
// re-scrolling on every hop is disorienting; the today/current cell
// highlighting is enough to keep bearings.
centerMonth(displayMonth);
})(); })();
+9 -15
View File
@@ -757,23 +757,17 @@ aside.tree-sidebar:empty { display: none; }
} }
/* === Diary calendar === */ /* === Diary calendar === */
.diary-cal-nav { /* Header row: "Chronological" on the left, the [year ▾] dropdown pushed to the
display: flex; right. Composes with .row (flex + centering); space-between splits the two. */
align-items: center; .diary-cal .panel-header { justify-content: space-between; }
justify-content: center; .diary-cal-drop .dropdown-menu { min-width: 0; }
gap: 0.2rem; /* Per-month caption above each grid, linking to that month's anchor. */
margin-bottom: 0.4rem; .diary-cal-month {
font-size: var(--font-sm); font-size: var(--font-sm);
margin: var(--space-3) 0 0.2rem;
} }
.diary-cal-nav .diary-cal-drop + .diary-cal-heading { margin-left: var(--space-3); } .diary-cal-month a { color: var(--link); }
/* Anchor the month/year dropdowns to the nav row instead of the ▾ button so .diary-cal-month a:hover { color: var(--link-hover); }
the menu spans the full panel width rather than overflowing the 14rem
sidebar with its default min-width: 9rem. */
.diary-cal-nav { position: relative; }
.diary-cal-nav .diary-cal-drop { position: static; }
.diary-cal-nav .dropdown-menu { left: 0; right: 0; min-width: 0; }
.diary-cal-heading { color: var(--link); }
.diary-cal-heading:hover { color: var(--link-hover); }
.diary-cal-grid { .diary-cal-grid {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
+12 -16
View File
@@ -343,8 +343,8 @@ type calYear struct {
} }
// calMonthGrid carries everything the template needs to render one month's // calMonthGrid carries everything the template needs to render one month's
// grid plus the dropdown / heading entry that targets it. The calendar // grid plus the caption that links to it. The calendar widget renders all 12
// widget ships all 12 in the initial HTML; JS swaps which one is visible. // stacked vertically; JS centers the current month in the rail on load.
type calMonthGrid struct { type calMonthGrid struct {
Num int Num int
Name string Name string
@@ -353,13 +353,11 @@ type calMonthGrid struct {
} }
type calendarData struct { type calendarData struct {
DisplayYear int DisplayYear int
DisplayMonth int DisplayMonth int
DisplayMonthName string // pre-resolved so the template doesn't need arithmetic DiaryURL string
DiaryURL string Months []calMonthGrid
YearURL string Years []calYear
Months []calMonthGrid
Years []calYear
} }
var diaryCalTmpl = template.Must(template.ParseFS(assets, "assets/diary/calendar.html")) var diaryCalTmpl = template.Must(template.ParseFS(assets, "assets/diary/calendar.html"))
@@ -489,13 +487,11 @@ func computeCalendarWidget(diaryRootFS, diaryRootURL, fsPath string, depth int)
sort.Slice(years, func(i, j int) bool { return years[i].Num > years[j].Num }) sort.Slice(years, func(i, j int) bool { return years[i].Num > years[j].Num })
data := calendarData{ data := calendarData{
DisplayYear: displayYear, DisplayYear: displayYear,
DisplayMonth: displayMonth, DisplayMonth: displayMonth,
DisplayMonthName: months[displayMonth-1].Name, DiaryURL: diaryRootURL,
DiaryURL: diaryRootURL, Months: months,
YearURL: yearURL, Years: years,
Months: months,
Years: years,
} }
var buf bytes.Buffer var buf bytes.Buffer