Files
vu/src/ui/day-nav.ts
T
will 17112c06a8
Deploy / deploy (push) Successful in 1m26s
Make the Today link context-sensitive to time of day
When already viewing today, clicking Today now snaps the selected hour
to whatever hour would be default for the current wall-clock time
(same as a fresh visit to /vu); when viewing another date it still just
jumps to today while preserving the current hour, as before. The link's
href is built from the same todayTargetHour() decision as the click
handler, so right-click -> open in new tab lands on the same target
instead of following a stale href.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbHcmjnqQPJZ1i2yoebmp5
2026-08-28 16:52:09 -04:00

58 lines
3.2 KiB
TypeScript

import { getState, shiftDate, goToToday, addDays, todayIso, todayTargetHour } from '../app-state';
import { resolveDay } from '../calendar';
import { resolveEveningDay } from '../calendar/vespers';
import { getDayLabel, getVespersStatusLabel } from '../calendar/day-label';
import { urlFor } from '../router';
import { formatDateLong, renderBilingual } from './format';
/** Renders navigation plus the merged "when" header: the civil date/
* weekday and the liturgical day-being-celebrated, together in one place
* — previously the date lived here while the day-being-celebrated text
* was repeated separately inside every hour's own view. The day-being-
* celebrated text is hour-independent for the daytime hours (the same
* `LiturgicalDay` for every hour on a given date) but NOT for Vespers/
* Compline: those two can belong to *tomorrow's* identity instead (First
* Vespers anticipation — see `calendar/vespers.ts`'s `resolveEveningDay`),
* so this header must resolve through that function for those two hours
* specifically, or it shows the wrong saint (previously always used plain
* `resolveDay`, silently mismatched against what the hour itself was
* actually rendering). */
export function renderDayNav(container: HTMLElement): void {
const { date, selectedHour, languages } = getState();
const isEveningHour = selectedHour === 'vespers' || selectedHour === 'compline';
const day = isEveningHour ? resolveEveningDay(date) : resolveDay(date);
const dateLabel = formatDateLong(date);
const dayLabel = getDayLabel(day);
const vespersStatus = selectedHour === 'vespers' ? getVespersStatusLabel(day) : undefined;
const prevUrl = urlFor(addDays(date, -1), selectedHour, languages);
const nextUrl = urlFor(addDays(date, 1), selectedHour, languages);
const todayUrl = urlFor(todayIso(), todayTargetHour(), languages);
container.innerHTML = `
<nav class="day-nav" aria-label="Day navigation">
<a href="${prevUrl}" class="day-nav-btn" data-action="prev" aria-label="Previous day">&larr;</a>
<a href="${todayUrl}" class="day-nav-btn day-nav-today" data-action="today">Today</a>
<div class="day-nav-header">
${renderBilingual(dateLabel, languages, 'day-nav-date')}
${renderBilingual(dayLabel, languages, 'day-nav-label')}
${vespersStatus ? renderBilingual(vespersStatus, languages, 'day-nav-vespers-status') : ''}
</div>
<a href="${nextUrl}" class="day-nav-btn" data-action="next" aria-label="Next day">&rarr;</a>
</nav>
`;
const bindNav = (action: string, mutate: () => void): void => {
container.querySelector<HTMLAnchorElement>(`[data-action="${action}"]`)?.addEventListener('click', (event) => {
// Let modifier/middle clicks fall through to normal browser navigation
// (open in new tab, etc.) — only plain left-clicks are SPA-routed.
if (event.defaultPrevented || (event as MouseEvent).button !== 0 || (event as MouseEvent).metaKey || (event as MouseEvent).ctrlKey || (event as MouseEvent).shiftKey || (event as MouseEvent).altKey) {
return;
}
event.preventDefault();
mutate();
});
};
bindNav('prev', () => shiftDate(-1));
bindNav('next', () => shiftDate(1));
bindNav('today', () => goToToday());
}