This commit is contained in:
2026-07-18 11:51:56 +08:00 Unverified
parent 4e42797981
commit 85a21e2358
2 changed files with 392 additions and 117 deletions
+141 -15
View File
@@ -1,16 +1,26 @@
<section class="on-this-day-band" aria-labelledby="on-this-day-title">
<div class="section-inner on-this-day-shell" data-on-this-day>
<div class="memory-date-card" aria-hidden="true">
<span data-memory-month>今日</span>
<strong data-memory-day>--</strong>
<small>北京时间</small>
<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>
</div>
<button class="memory-calendar-today" type="button" data-calendar-today>回到今天</button>
</div>
<div class="memory-calendar-weekdays" aria-hidden="true">
<span>日</span><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span>
</div>
<div class="memory-calendar-grid" data-calendar-grid></div>
<p class="memory-calendar-legend"><span aria-hidden="true"></span>圆点表示这一天收录了班级记忆</p>
</div>
<div class="on-this-day-content">
<div class="on-this-day-heading">
<div>
<p class="eyebrow">ON THIS DAY · 那年今日</p>
<h2 id="on-this-day-title">今天,记忆翻到了这一页</h2>
<h2 id="on-this-day-title" data-memory-title>今天,记忆翻到了这一页</h2>
</div>
<p data-memory-summary aria-live="polite">正在寻找同月同日的班级记忆……</p>
</div>
@@ -50,26 +60,49 @@
day: "numeric"
}).formatToParts(new Date());
const dateValues = Object.fromEntries(dateParts.map((part) => [part.type, part.value]));
const month = Number(dateValues.month);
const day = Number(dateValues.day);
const todayMonth = Number(dateValues.month);
const todayDay = Number(dateValues.day);
const currentYear = Number(dateValues.year);
const monthDay = `${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
const matches = onThisDayMemories.filter((memory) => memory.monthDay === monthDay);
const visibleMatches = matches.slice(0, 6);
let calendarYear = currentYear;
let calendarMonth = todayMonth;
let selectedMonth = todayMonth;
let selectedDay = todayDay;
const monthLabel = root.querySelector<HTMLElement>("[data-memory-month]");
const dayLabel = root.querySelector<HTMLElement>("[data-memory-day]");
const titleLabel = root.querySelector<HTMLElement>("[data-memory-title]");
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 calendarGrid = root.querySelector<HTMLElement>("[data-calendar-grid]");
const previousButton = root.querySelector<HTMLButtonElement>("[data-calendar-previous]");
const nextButton = root.querySelector<HTMLButtonElement>("[data-calendar-next]");
const todayButton = root.querySelector<HTMLButtonElement>("[data-calendar-today]");
if (monthLabel) monthLabel.textContent = `${month}月`;
if (dayLabel) dayLabel.textContent = String(day).padStart(2, "0");
const getMonthDay = (month: number, day: number) =>
`${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
const getMemories = (month: number, day: number) => {
const monthDay = getMonthDay(month, day);
return onThisDayMemories.filter((memory) => memory.monthDay === monthDay);
};
const renderMemories = (month: number, day: number) => {
selectedMonth = month;
selectedDay = day;
const matches = getMemories(month, day);
const visibleMatches = matches.slice(0, 6);
const isToday = month === todayMonth && day === todayDay;
if (titleLabel) {
titleLabel.textContent = isToday
? "今天,记忆翻到了这一页"
: `${month}月${day}日,记忆翻到了这一页`;
}
if (summary) {
summary.textContent = matches.length
? `${month}月${day}日,找到了 ${matches.length} 条跨越不同年份的班级记忆。`
: `${month}月${day}日,今天暂时没有同月同日的记录。`;
: `${month}月${day}日,暂时没有同月同日的记录。`;
}
if (grid) {
@@ -129,5 +162,98 @@
}
if (empty) empty.hidden = matches.length > 0;
};
const renderCalendar = () => {
if (calendarLabel) calendarLabel.textContent = `${calendarYear}年${calendarMonth}月`;
if (!calendarGrid) return;
const fragment = document.createDocumentFragment();
const firstWeekday = new Date(calendarYear, calendarMonth - 1, 1).getDay();
const daysInMonth = new Date(calendarYear, calendarMonth, 0).getDate();
for (let index = 0; index < firstWeekday; index += 1) {
const spacer = document.createElement("span");
spacer.className = "memory-calendar-spacer";
spacer.setAttribute("aria-hidden", "true");
fragment.append(spacer);
}
for (let day = 1; day <= daysInMonth; day += 1) {
const matches = getMemories(calendarMonth, day);
const button = document.createElement("button");
button.type = "button";
button.dataset.calendarDay = String(day);
button.className = "memory-calendar-day";
button.setAttribute(
"aria-label",
matches.length ? `${calendarMonth}月${day}日,有${matches.length}条记忆` : `${calendarMonth}月${day}日`
);
if (matches.length) {
button.classList.add("has-memory");
button.dataset.memoryCount = String(matches.length);
}
if (calendarMonth === selectedMonth && day === selectedDay) {
button.classList.add("is-selected");
button.setAttribute("aria-pressed", "true");
}
if (calendarYear === currentYear && calendarMonth === todayMonth && day === todayDay) {
button.classList.add("is-today");
}
const number = document.createElement("span");
number.textContent = String(day);
button.append(number);
if (matches.length) {
const dot = document.createElement("i");
dot.setAttribute("aria-hidden", "true");
button.append(dot);
}
fragment.append(button);
}
calendarGrid.replaceChildren(fragment);
};
calendarGrid?.addEventListener("click", (event) => {
const target = event.target;
if (!(target instanceof Element)) return;
const button = target.closest<HTMLButtonElement>("[data-calendar-day]");
if (!button) return;
renderMemories(calendarMonth, Number(button.dataset.calendarDay));
renderCalendar();
});
previousButton?.addEventListener("click", () => {
calendarMonth -= 1;
if (calendarMonth === 0) {
calendarMonth = 12;
calendarYear -= 1;
}
renderCalendar();
});
nextButton?.addEventListener("click", () => {
calendarMonth += 1;
if (calendarMonth === 13) {
calendarMonth = 1;
calendarYear += 1;
}
renderCalendar();
});
todayButton?.addEventListener("click", () => {
calendarYear = currentYear;
calendarMonth = todayMonth;
renderMemories(todayMonth, todayDay);
renderCalendar();
});
renderMemories(todayMonth, todayDay);
renderCalendar();
}
</script>
+203 -54
View File
@@ -225,56 +225,16 @@ h1 {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 150px minmax(0, 1fr);
grid-template-columns: minmax(280px, 1fr) minmax(0, 2fr);
gap: clamp(28px, 5vw, 64px);
align-items: start;
}
.memory-date-card {
position: sticky;
top: 26px;
overflow: hidden;
border: 1px solid rgba(55, 109, 90, 0.2);
border-radius: 14px;
background: #fffdf7;
box-shadow: 0 18px 42px rgba(39, 55, 52, 0.11);
text-align: center;
}
.memory-date-card span,
.memory-date-card small {
display: block;
}
.memory-date-card span {
padding: 8px 12px;
background: var(--green);
color: #fffdf7;
font-size: 14px;
font-weight: 800;
}
.memory-date-card strong {
display: block;
padding: 12px 12px 2px;
color: var(--ink);
font-family: Georgia, "Times New Roman", serif;
font-size: 62px;
line-height: 1;
}
.memory-date-card small {
padding: 8px 12px 13px;
color: var(--muted);
font-size: 11px;
letter-spacing: 0.08em;
}
.on-this-day-heading {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(240px, 0.8fr);
gap: clamp(20px, 4vw, 52px);
align-items: end;
grid-template-columns: 1fr;
gap: 12px;
align-items: start;
margin-bottom: 30px;
}
@@ -287,6 +247,162 @@ h1 {
color: var(--muted);
}
.memory-calendar {
position: sticky;
top: 26px;
padding: 18px;
border: 1px solid rgba(31, 43, 42, 0.12);
border-radius: 14px;
background: rgba(255, 255, 255, 0.72);
box-shadow: 0 8px 28px rgba(39, 55, 52, 0.06);
backdrop-filter: blur(10px);
}
.memory-calendar-head,
.memory-calendar-nav {
display: flex;
align-items: center;
}
.memory-calendar-head {
display: grid;
gap: 10px;
margin-bottom: 16px;
}
.memory-calendar-nav {
justify-content: space-between;
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;
border: 1px solid var(--line);
background: #fffdf7;
color: var(--ink);
font: inherit;
font-weight: 800;
cursor: pointer;
transition: border-color 160ms ease, background 160ms ease, color 160ms ease;
}
.memory-calendar-nav button {
width: 38px;
padding: 0;
border-radius: 50%;
}
.memory-calendar-today {
width: 100%;
padding: 7px 13px;
border-radius: 999px;
color: var(--green);
font-size: 13px;
}
.memory-calendar-nav button:hover,
.memory-calendar-today:hover {
border-color: var(--green);
background: var(--green);
color: #fff;
}
.memory-calendar-weekdays,
.memory-calendar-grid {
display: grid;
grid-template-columns: repeat(7, minmax(0, 1fr));
gap: 7px;
}
.memory-calendar-weekdays {
margin-bottom: 7px;
color: var(--muted);
text-align: center;
font-size: 12px;
font-weight: 800;
}
.memory-calendar-day,
.memory-calendar-spacer {
min-width: 0;
aspect-ratio: 1.45;
}
.memory-calendar-day {
position: relative;
display: grid;
place-items: center;
align-content: center;
gap: 3px;
padding: 4px;
border: 1px solid transparent;
border-radius: 9px;
background: rgba(244, 247, 238, 0.72);
color: var(--muted);
font: inherit;
font-size: 13px;
cursor: pointer;
transition: transform 150ms ease, border-color 150ms ease, background 150ms ease, color 150ms ease;
}
.memory-calendar-day:hover {
transform: translateY(-1px);
border-color: rgba(55, 109, 90, 0.3);
color: var(--green);
background: #fff;
}
.memory-calendar-day.has-memory {
color: var(--ink);
background: rgba(232, 168, 76, 0.12);
font-weight: 800;
}
.memory-calendar-day i {
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--coral);
}
.memory-calendar-day.is-today {
border-color: rgba(55, 109, 90, 0.55);
}
.memory-calendar-day.is-selected {
border-color: var(--green);
background: var(--green);
color: #fffdf7;
box-shadow: 0 6px 16px rgba(55, 109, 90, 0.2);
}
.memory-calendar-day.is-selected i {
background: #f3cf8b;
}
.memory-calendar-legend {
display: flex;
align-items: center;
gap: 7px;
margin: 12px 0 0;
color: var(--muted);
font-size: 12px;
}
.memory-calendar-legend > span {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--coral);
}
.memory-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -296,7 +412,7 @@ h1 {
.memory-card {
min-height: 190px;
display: grid;
grid-template-columns: 150px minmax(0, 1fr);
grid-template-columns: 120px minmax(0, 1fr);
overflow: hidden;
border: 1px solid rgba(31, 43, 42, 0.12);
border-radius: 12px;
@@ -2366,7 +2482,21 @@ h2 {
transform: translate(-50%, 0);
}
@media (max-width: 1040px) and (min-width: 821px) {
.memory-grid {
grid-template-columns: 1fr;
}
.memory-calendar {
position: static;
}
}
@media (max-width: 820px) {
.memory-calendar {
position: static;
}
body:has(.score-sidebar) {
padding-bottom: 86px;
}
@@ -2659,6 +2789,34 @@ h2 {
min-height: 0;
}
.memory-calendar {
margin-inline: -4px;
padding: 13px 9px;
}
.memory-calendar-head {
align-items: stretch;
flex-direction: column;
}
.memory-calendar-nav {
justify-content: space-between;
}
.memory-calendar-today {
width: 100%;
}
.memory-calendar-weekdays,
.memory-calendar-grid {
gap: 4px;
}
.memory-calendar-day,
.memory-calendar-spacer {
aspect-ratio: 1;
}
.hero-actions {
display: grid;
}
@@ -2673,15 +2831,6 @@ h2 {
min-height: auto;
}
.memory-date-card {
position: static;
width: 104px;
}
.memory-date-card strong {
font-size: 48px;
}
.memory-grid {
grid-template-columns: 1fr;
}