日历优化

This commit is contained in:
2026-07-18 11:59:20 +08:00 Unverified
parent c6a813f90c
commit 6542757040
2 changed files with 191 additions and 13 deletions
+94 -5
View File
@@ -3,12 +3,39 @@
<div class="memory-calendar" aria-label="记忆日历"> <div class="memory-calendar" aria-label="记忆日历">
<div class="memory-calendar-head"> <div class="memory-calendar-head">
<div class="memory-calendar-nav"> <div class="memory-calendar-nav">
<button type="button" data-calendar-previous aria-label="上一个月">←</button> <button class="memory-calendar-step" type="button" data-calendar-previous aria-label="上一个月">←</button>
<strong data-calendar-label aria-live="polite">本月</strong> <div class="memory-calendar-period" aria-label="快速选择年月" aria-live="polite">
<button type="button" data-calendar-next aria-label="下一个月">→</button> <button type="button" data-calendar-year aria-expanded="false" aria-controls="memory-calendar-jump">2026年</button>
<button type="button" data-calendar-month aria-expanded="false" aria-controls="memory-calendar-jump">本月</button>
</div>
<button class="memory-calendar-step" type="button" data-calendar-next aria-label="下一个月">→</button>
</div> </div>
<button class="memory-calendar-today" type="button" data-calendar-today>回到今天</button> <button class="memory-calendar-today" type="button" data-calendar-today>回到今天</button>
</div> </div>
<form class="memory-calendar-jump" id="memory-calendar-jump" data-calendar-jump hidden>
<label>
<span>年份</span>
<input type="number" min="1900" max="2100" step="1" inputmode="numeric" data-calendar-year-input required />
</label>
<label>
<span>月份</span>
<select data-calendar-month-select required>
<option value="1">1月</option>
<option value="2">2月</option>
<option value="3">3月</option>
<option value="4">4月</option>
<option value="5">5月</option>
<option value="6">6月</option>
<option value="7">7月</option>
<option value="8">8月</option>
<option value="9">9月</option>
<option value="10">10月</option>
<option value="11">11月</option>
<option value="12">12月</option>
</select>
</label>
<button type="submit">跳转</button>
</form>
<div class="memory-calendar-weekdays" aria-hidden="true"> <div class="memory-calendar-weekdays" aria-hidden="true">
<span>日</span><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span> <span>日</span><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span>
</div> </div>
@@ -72,7 +99,11 @@
const summary = root.querySelector<HTMLElement>("[data-memory-summary]"); const summary = root.querySelector<HTMLElement>("[data-memory-summary]");
const grid = root.querySelector<HTMLElement>("[data-memory-grid]"); const grid = root.querySelector<HTMLElement>("[data-memory-grid]");
const empty = root.querySelector<HTMLElement>("[data-memory-empty]"); const empty = root.querySelector<HTMLElement>("[data-memory-empty]");
const calendarLabel = root.querySelector<HTMLElement>("[data-calendar-label]"); const calendarYearButton = root.querySelector<HTMLButtonElement>("[data-calendar-year]");
const calendarMonthButton = root.querySelector<HTMLButtonElement>("[data-calendar-month]");
const jumpForm = root.querySelector<HTMLFormElement>("[data-calendar-jump]");
const yearInput = root.querySelector<HTMLInputElement>("[data-calendar-year-input]");
const monthSelect = root.querySelector<HTMLSelectElement>("[data-calendar-month-select]");
const calendarGrid = root.querySelector<HTMLElement>("[data-calendar-grid]"); const calendarGrid = root.querySelector<HTMLElement>("[data-calendar-grid]");
const previousButton = root.querySelector<HTMLButtonElement>("[data-calendar-previous]"); const previousButton = root.querySelector<HTMLButtonElement>("[data-calendar-previous]");
const nextButton = root.querySelector<HTMLButtonElement>("[data-calendar-next]"); const nextButton = root.querySelector<HTMLButtonElement>("[data-calendar-next]");
@@ -86,6 +117,26 @@
return onThisDayMemories.filter((memory) => memory.monthDay === monthDay); 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) => { const renderMemories = (month: number, day: number) => {
selectedMonth = month; selectedMonth = month;
selectedDay = day; selectedDay = day;
@@ -165,7 +216,8 @@
}; };
const renderCalendar = () => { const renderCalendar = () => {
if (calendarLabel) calendarLabel.textContent = `${calendarYear}年${calendarMonth}月`; if (calendarYearButton) calendarYearButton.textContent = `${calendarYear}年`;
if (calendarMonthButton) calendarMonthButton.textContent = `${calendarMonth}月`;
if (!calendarGrid) return; if (!calendarGrid) return;
const fragment = document.createDocumentFragment(); const fragment = document.createDocumentFragment();
@@ -225,10 +277,30 @@
if (!button) return; if (!button) return;
renderMemories(calendarMonth, Number(button.dataset.calendarDay)); 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(); renderCalendar();
}); });
previousButton?.addEventListener("click", () => { previousButton?.addEventListener("click", () => {
setJumpOpen(false);
calendarMonth -= 1; calendarMonth -= 1;
if (calendarMonth === 0) { if (calendarMonth === 0) {
calendarMonth = 12; calendarMonth = 12;
@@ -238,6 +310,7 @@
}); });
nextButton?.addEventListener("click", () => { nextButton?.addEventListener("click", () => {
setJumpOpen(false);
calendarMonth += 1; calendarMonth += 1;
if (calendarMonth === 13) { if (calendarMonth === 13) {
calendarMonth = 1; calendarMonth = 1;
@@ -247,12 +320,28 @@
}); });
todayButton?.addEventListener("click", () => { todayButton?.addEventListener("click", () => {
setJumpOpen(false);
calendarYear = currentYear; calendarYear = currentYear;
calendarMonth = todayMonth; calendarMonth = todayMonth;
renderMemories(todayMonth, todayDay); renderMemories(todayMonth, todayDay);
renderCalendar(); 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); renderMemories(todayMonth, todayDay);
renderCalendar(); renderCalendar();
} }
+97 -8
View File
@@ -275,12 +275,6 @@ h1 {
gap: 10px; gap: 10px;
} }
.memory-calendar-nav strong {
min-width: 126px;
text-align: center;
font-size: 18px;
}
.memory-calendar-nav button, .memory-calendar-nav button,
.memory-calendar-today { .memory-calendar-today {
min-height: 38px; min-height: 38px;
@@ -293,12 +287,44 @@ h1 {
transition: border-color 160ms ease, background 160ms ease, color 160ms ease; transition: border-color 160ms ease, background 160ms ease, color 160ms ease;
} }
.memory-calendar-nav button { .memory-calendar-step {
width: 38px; width: 38px;
flex: 0 0 38px;
padding: 0; padding: 0;
border-radius: 50%; 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 { .memory-calendar-today {
width: 100%; width: 100%;
padding: 7px 13px; padding: 7px 13px;
@@ -307,13 +333,76 @@ h1 {
font-size: 13px; font-size: 13px;
} }
.memory-calendar-nav button:hover, .memory-calendar-step:hover,
.memory-calendar-today:hover { .memory-calendar-today:hover {
border-color: var(--green); border-color: var(--green);
background: var(--green); background: var(--green);
color: #fff; 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-weekdays,
.memory-calendar-grid { .memory-calendar-grid {
display: grid; display: grid;