From 0ff2ef08fbf6dad434571f5d06043a50f8664dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Sat, 1 Aug 2026 23:07:37 +0300 Subject: [PATCH] compact navigation: prayer view dropdown, menaion calendar with prev/next --- src/ui/menaion-view.ts | 107 ++++++++++++++++++++++++----------------- src/ui/prayer-view.ts | 29 +++-------- 2 files changed, 72 insertions(+), 64 deletions(-) diff --git a/src/ui/menaion-view.ts b/src/ui/menaion-view.ts index 9df0bc2..aedcf81 100644 --- a/src/ui/menaion-view.ts +++ b/src/ui/menaion-view.ts @@ -1,6 +1,6 @@ /** * Menaion view — browse the daily Menaion by month and day. - * Two-step selector: month → day grid → section list. + * Calendar-style navigation with prev/next month arrows. */ import { assembleService, type ServiceContext } from '../core/service-assembler'; @@ -13,6 +13,7 @@ export class MenaionView { private language: LanguageCode; private t: ReturnType; private index: Record = {}; + private currentMonth: number = 1; constructor(container: HTMLElement, language: LanguageCode) { this.container = container; @@ -26,7 +27,20 @@ export class MenaionView { } private getMonthNames(): string[] { - return this.t.calendar.monthsGenitive; + const months = this.t.calendar.monthsGenitive; + // Some i18n month lists are genitive (used in date contexts). + // For labels we need nominative — derive from genitive or use indices. + const NOMINATIVE: Record = { + 'января': 'Январь', 'февраля': 'Февраль', 'марта': 'Март', + 'апреля': 'Апрель', 'мая': 'Май', 'июня': 'Июнь', + 'июля': 'Июль', 'августа': 'Август', 'сентября': 'Сентябрь', + 'октября': 'Октябрь', 'ноября': 'Ноябрь', 'декабря': 'Декабрь', + 'january': 'January', 'february': 'February', 'march': 'March', + 'april': 'April', 'may': 'May', 'june': 'June', + 'july': 'July', 'august': 'August', 'september': 'September', + 'october': 'October', 'november': 'November', 'december': 'December', + }; + return months.map(m => NOMINATIVE[m.toLowerCase()] || m); } async render() { @@ -37,68 +51,75 @@ export class MenaionView { } } catch { /* empty */ } - const months = this.getMonthNames(); - const daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; const today = new Date(); - - // Count sections per month - const monthCounts: number[] = new Array(12).fill(0); - for (const [dk, n] of Object.entries(this.index)) { - const m = parseInt(dk.slice(0, 2), 10) - 1; - if (m >= 0 && m < 12) monthCounts[m] += n; - } - - const monthHtml = months.map((m, i) => ` - - `).join(''); + this.currentMonth = today.getMonth() + 1; this.container.innerHTML = `

${this.t.menaion.title}

-

${months[0]}

-
- ${monthHtml} -
-
+

${this.t.prayer.selectSection}

`; - this.container.querySelectorAll('.menaion-month').forEach(btn => { - btn.addEventListener('click', () => { - const month = parseInt(btn.getAttribute('data-month')!, 10); - this.renderMonth(month, daysInMonth[month - 1]); - }); - }); - - // Default: current month - this.renderMonth(today.getMonth() + 1, daysInMonth[today.getMonth()]); + this.renderCalendar(this.currentMonth); } - private renderMonth(month: number, maxDay: number) { - const daysEl = this.container.querySelector('#menaion-days'); - if (!daysEl) return; + private renderCalendar(month: number) { + const calEl = this.container.querySelector('#menaion-calendar'); + if (!calEl) return; + + const months = this.getMonthNames(); + const daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + const maxDay = daysInMonth[month - 1]; const mm = String(month).padStart(2, '0'); - const days = []; + + // First day of month (Julian) — approximate day-of-week + // Use a simple approach: Jan 1 Julian 2025 = Wednesday (3) + const firstDow = (month === 1 ? 3 : new Date(2025, month - 1, 1).getDay() + 1) % 7; + + let daysHtml = ''; + for (let i = 0; i < firstDow; i++) { + daysHtml += '
'; + } for (let d = 1; d <= maxDay; d++) { const dd = String(d).padStart(2, '0'); const key = `${mm}-${dd}`; const n = this.index[key]; - days.push(` - - `); + `; } - daysEl.innerHTML = days.join(''); - this.container.querySelectorAll('.menaion-day:not([disabled])').forEach(btn => { + + calEl.innerHTML = ` +
+ + ${months[month - 1]} + +
+
+ ${daysHtml} +
+ `; + + document.getElementById('menaion-prev')?.addEventListener('click', () => { + const prev = this.currentMonth <= 1 ? 12 : this.currentMonth - 1; + this.currentMonth = prev; + this.renderCalendar(prev); + }); + document.getElementById('menaion-next')?.addEventListener('click', () => { + const next = this.currentMonth >= 12 ? 1 : this.currentMonth + 1; + this.currentMonth = next; + this.renderCalendar(next); + }); + + calEl.querySelectorAll('[data-date]:not([disabled])').forEach(btn => { btn.addEventListener('click', () => { this.loadDay(btn.getAttribute('data-date')!); }); diff --git a/src/ui/prayer-view.ts b/src/ui/prayer-view.ts index 02a0685..c8edfcf 100644 --- a/src/ui/prayer-view.ts +++ b/src/ui/prayer-view.ts @@ -217,35 +217,22 @@ export class PrayerView { this.container.innerHTML = `

${title}

-

${subtitle}

-
+

${subtitle}

+

${this.t.prayer.selectSection}

`; - this.container.querySelectorAll('.prayer-section').forEach(btn => { - btn.addEventListener('click', () => { - const id = btn.getAttribute('data-section')!; - this.activeSection = id; - this.container.querySelectorAll('.prayer-section').forEach(b => { - const bid = b.getAttribute('data-section'); - b.className = `prayer-section text-left px-4 py-3 rounded-lg border transition-colors ${bid === id ? 'border-gold bg-gold/10 text-navy font-bold' : 'border-gold/20 bg-white/50 text-navy hover:border-gold/50'}`; - }); - this.loadSection(id); - }); + document.getElementById('prayer-select')?.addEventListener('change', (e) => { + const id = (e.target as HTMLSelectElement).value; + this.activeSection = id; + this.loadSection(id); }); }