日历优化
This commit is contained in:
@@ -3,12 +3,39 @@
|
||||
<div class="memory-calendar" aria-label="记忆日历">
|
||||
<div class="memory-calendar-head">
|
||||
<div class="memory-calendar-nav">
|
||||
<button type="button" data-calendar-previous aria-label="上一个月">←</button>
|
||||
<strong data-calendar-label aria-live="polite">本月</strong>
|
||||
<button type="button" data-calendar-next aria-label="下一个月">→</button>
|
||||
<button class="memory-calendar-step" type="button" data-calendar-previous aria-label="上一个月">←</button>
|
||||
<div class="memory-calendar-period" aria-label="快速选择年月" aria-live="polite">
|
||||
<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>
|
||||
<button class="memory-calendar-today" type="button" data-calendar-today>回到今天</button>
|
||||
</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">
|
||||
<span>日</span><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span>
|
||||
</div>
|
||||
@@ -72,7 +99,11 @@
|
||||
const summary = root.querySelector<HTMLElement>("[data-memory-summary]");
|
||||
const grid = root.querySelector<HTMLElement>("[data-memory-grid]");
|
||||
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 previousButton = root.querySelector<HTMLButtonElement>("[data-calendar-previous]");
|
||||
const nextButton = root.querySelector<HTMLButtonElement>("[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();
|
||||
}
|
||||
|
||||
+97
-8
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user