From 65427570400c591049b53ddda245112ffa8d1a76 Mon Sep 17 00:00:00 2001 From: biss Date: Sat, 18 Jul 2026 11:59:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E5=8E=86=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/OnThisDay.astro | 99 +++++++++++++++++++++++++++++-- src/styles/global.css | 105 ++++++++++++++++++++++++++++++--- 2 files changed, 191 insertions(+), 13 deletions(-) diff --git a/src/components/OnThisDay.astro b/src/components/OnThisDay.astro index dabf21a..d85575e 100644 --- a/src/components/OnThisDay.astro +++ b/src/components/OnThisDay.astro @@ -3,12 +3,39 @@
- - 本月 - + +
+ + +
+
+ @@ -72,7 +99,11 @@ const summary = root.querySelector("[data-memory-summary]"); const grid = root.querySelector("[data-memory-grid]"); const empty = root.querySelector("[data-memory-empty]"); - const calendarLabel = root.querySelector("[data-calendar-label]"); + const calendarYearButton = root.querySelector("[data-calendar-year]"); + const calendarMonthButton = root.querySelector("[data-calendar-month]"); + const jumpForm = root.querySelector("[data-calendar-jump]"); + const yearInput = root.querySelector("[data-calendar-year-input]"); + const monthSelect = root.querySelector("[data-calendar-month-select]"); const calendarGrid = root.querySelector("[data-calendar-grid]"); const previousButton = root.querySelector("[data-calendar-previous]"); const nextButton = root.querySelector("[data-calendar-next]"); @@ -86,6 +117,26 @@ return onThisDayMemories.filter((memory) => memory.monthDay === monthDay); }; + const setJumpOpen = (open: boolean, focusTarget?: "year" | "month") => { + if (!jumpForm) return; + jumpForm.hidden = !open; + calendarYearButton?.setAttribute("aria-expanded", String(open)); + calendarMonthButton?.setAttribute("aria-expanded", String(open)); + + if (open) { + if (yearInput) yearInput.value = String(calendarYear); + if (monthSelect) monthSelect.value = String(calendarMonth); + window.requestAnimationFrame(() => { + if (focusTarget === "year") { + yearInput?.focus(); + yearInput?.select(); + } else { + monthSelect?.focus(); + } + }); + } + }; + const renderMemories = (month: number, day: number) => { selectedMonth = month; selectedDay = day; @@ -165,7 +216,8 @@ }; const renderCalendar = () => { - if (calendarLabel) calendarLabel.textContent = `${calendarYear}年${calendarMonth}月`; + if (calendarYearButton) calendarYearButton.textContent = `${calendarYear}年`; + if (calendarMonthButton) calendarMonthButton.textContent = `${calendarMonth}月`; if (!calendarGrid) return; const fragment = document.createDocumentFragment(); @@ -225,10 +277,30 @@ if (!button) return; renderMemories(calendarMonth, Number(button.dataset.calendarDay)); + setJumpOpen(false); + renderCalendar(); + }); + + calendarYearButton?.addEventListener("click", () => { + setJumpOpen(jumpForm?.hidden ?? true, "year"); + }); + + calendarMonthButton?.addEventListener("click", () => { + setJumpOpen(jumpForm?.hidden ?? true, "month"); + }); + + jumpForm?.addEventListener("submit", (event) => { + event.preventDefault(); + if (!jumpForm.reportValidity() || !yearInput || !monthSelect) return; + + calendarYear = Number(yearInput.value); + calendarMonth = Number(monthSelect.value); + setJumpOpen(false); renderCalendar(); }); previousButton?.addEventListener("click", () => { + setJumpOpen(false); calendarMonth -= 1; if (calendarMonth === 0) { calendarMonth = 12; @@ -238,6 +310,7 @@ }); nextButton?.addEventListener("click", () => { + setJumpOpen(false); calendarMonth += 1; if (calendarMonth === 13) { calendarMonth = 1; @@ -247,12 +320,28 @@ }); todayButton?.addEventListener("click", () => { + setJumpOpen(false); calendarYear = currentYear; calendarMonth = todayMonth; renderMemories(todayMonth, todayDay); renderCalendar(); }); + document.addEventListener("click", (event) => { + if (jumpForm?.hidden) return; + const target = event.target; + if (!(target instanceof Node)) return; + if (jumpForm?.contains(target) || calendarYearButton?.contains(target) || calendarMonthButton?.contains(target)) return; + setJumpOpen(false); + }); + + root.addEventListener("keydown", (event) => { + if (event.key === "Escape" && jumpForm && !jumpForm.hidden) { + setJumpOpen(false); + calendarYearButton?.focus(); + } + }); + renderMemories(todayMonth, todayDay); renderCalendar(); } diff --git a/src/styles/global.css b/src/styles/global.css index e6b2103..bb83c84 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -275,12 +275,6 @@ h1 { gap: 10px; } -.memory-calendar-nav strong { - min-width: 126px; - text-align: center; - font-size: 18px; -} - .memory-calendar-nav button, .memory-calendar-today { min-height: 38px; @@ -293,12 +287,44 @@ h1 { transition: border-color 160ms ease, background 160ms ease, color 160ms ease; } -.memory-calendar-nav button { +.memory-calendar-step { width: 38px; + flex: 0 0 38px; padding: 0; border-radius: 50%; } +.memory-calendar-period { + min-width: 0; + display: flex; + justify-content: center; + gap: 2px; +} + +.memory-calendar-period button { + min-width: 0; + min-height: 38px; + padding: 4px 7px; + border-color: transparent; + border-radius: 7px; + background: transparent; + font-size: 18px; +} + +.memory-calendar-period button::after { + content: "⌄"; + margin-left: 3px; + color: var(--muted); + font-size: 12px; +} + +.memory-calendar-period button:hover, +.memory-calendar-period button[aria-expanded="true"] { + border-color: rgba(55, 109, 90, 0.2); + background: rgba(55, 109, 90, 0.08); + color: var(--green); +} + .memory-calendar-today { width: 100%; padding: 7px 13px; @@ -307,13 +333,76 @@ h1 { font-size: 13px; } -.memory-calendar-nav button:hover, +.memory-calendar-step:hover, .memory-calendar-today:hover { border-color: var(--green); background: var(--green); color: #fff; } +.memory-calendar-period button:hover::after, +.memory-calendar-period button[aria-expanded="true"]::after { + color: currentColor; +} + +.memory-calendar-jump { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: -4px 0 16px; + padding: 12px; + border: 1px solid rgba(55, 109, 90, 0.2); + border-radius: 10px; + background: #fffdf7; + box-shadow: 0 12px 28px rgba(39, 55, 52, 0.1); +} + +.memory-calendar-jump[hidden] { + display: none; +} + +.memory-calendar-jump label { + display: grid; + gap: 5px; + color: var(--muted); + font-size: 11px; + font-weight: 800; +} + +.memory-calendar-jump input, +.memory-calendar-jump select { + width: 100%; + min-width: 0; + min-height: 38px; + padding: 6px 9px; + border: 1px solid var(--line); + border-radius: 7px; + outline: none; + background: #fff; + color: var(--ink); + font: inherit; + font-size: 14px; +} + +.memory-calendar-jump input:focus, +.memory-calendar-jump select:focus { + border-color: var(--green); + box-shadow: 0 0 0 3px rgba(55, 109, 90, 0.1); +} + +.memory-calendar-jump > button { + grid-column: 1 / -1; + min-height: 38px; + border: 0; + border-radius: 7px; + background: var(--green); + color: #fff; + font: inherit; + font-size: 13px; + font-weight: 800; + cursor: pointer; +} + .memory-calendar-weekdays, .memory-calendar-grid { display: grid;