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;