Skip to content

Commit 772c3c6

Browse files
authored
Enhance papers layout with year grouping and controls
Refactor papers display to group by year and add expand/collapse functionality.
1 parent 481175a commit 772c3c6

1 file changed

Lines changed: 188 additions & 40 deletions

File tree

src/pages/papers.astro

Lines changed: 188 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,200 @@
22
import Layout from "@/layouts/Layout.astro";
33
import { publications } from "@/data/cv";
44
import { highlightAuthor } from "@/lib/utils";
5-
import {template} from "@/settings";
5+
import { template } from "@/settings";
66
7-
const ALL_PAPERS = publications; // This will be replaced with dynamic data later
7+
// Group publications by year (descending)
8+
const grouped = publications.reduce((acc, paper) => {
9+
const year = paper.time;
10+
if (!acc[year]) acc[year] = [];
11+
acc[year].push(paper);
12+
return acc;
13+
}, {} as Record<string, typeof publications>);
14+
15+
const years = Object.keys(grouped).sort((a, b) => Number(b) - Number(a));
16+
const totalPapers = publications.length;
817
---
918

1019
<Layout title="Papers">
11-
<div class="prose max-w-none">
12-
<h1 class="text-3xl font-bold mb-8">Publications</h1>
13-
14-
<div class="space-y-8">
15-
{
16-
ALL_PAPERS.map((paper) => (
17-
<div class="card bg-base-200">
18-
<div class="card-body">
19-
<h2 class="card-title text-xl">
20-
<a href={template.base + paper.link} class="hover:text-accent">
21-
{paper.title}
22-
</a>
23-
</h2>
24-
<p class="text-base mb-2">
25-
<span class="font-medium">Authors:</span>{" "}
26-
<span set:html={highlightAuthor(paper.authors)} />
27-
</p>
28-
<p class="text-sm mb-3">
29-
<span class="italic">{paper.journal}</span> • {paper.time}
30-
</p>
31-
<p class="text-sm text-base-content/80">{paper.abstract}</p>
32-
<div class="card-actions justify-end mt-4">
33-
<a href={paper.link} class="btn btn-sm btn-secondary text-secondary-content">
34-
Read Paper
35-
</a>
36-
</div>
37-
</div>
38-
</div>
39-
))
40-
}
20+
<div class="max-w-3xl mx-auto">
21+
<!-- Header -->
22+
<div class="mb-6">
23+
<h1 class="text-3xl font-bold">Publications</h1>
24+
<p class="text-base-content/60 mt-1">
25+
{totalPapers} papers &middot; {years[years.length - 1]}&ndash;{years[0]}
26+
</p>
4127
</div>
4228

43-
<div class="flex justify-center mt-8">
44-
<div class="join">
45-
<button class="join-item btn btn-sm">«</button>
46-
<button class="join-item btn btn-sm">1</button>
47-
<button class="join-item btn btn-sm btn-active">2</button>
48-
<button class="join-item btn btn-sm">3</button>
49-
<button class="join-item btn btn-sm">»</button>
50-
</div>
29+
<!-- Controls -->
30+
<div class="flex justify-end gap-2 mb-4">
31+
<button id="expand-all" class="btn btn-xs btn-outline btn-secondary">Expand All</button>
32+
<button id="collapse-all" class="btn btn-xs btn-outline btn-secondary">Collapse All</button>
33+
</div>
34+
35+
<!-- Year Sections -->
36+
<div class="space-y-2">
37+
{years.map((year) => (
38+
<div class="year-group" data-year={year}>
39+
<!-- Year Header (clickable) -->
40+
<button
41+
class="year-toggle w-full flex items-center gap-3 py-3 cursor-pointer select-none group"
42+
aria-expanded="true"
43+
aria-controls={`papers-${year}`}
44+
>
45+
<span class="text-2xl font-bold">{year}</span>
46+
<span class="badge badge-sm badge-secondary">{grouped[year].length}</span>
47+
<span class="flex-1 h-px bg-base-300"></span>
48+
<svg
49+
class="chevron-icon w-5 h-5 text-base-content/50 transition-transform duration-300 group-hover:text-base-content"
50+
viewBox="0 0 24 24"
51+
fill="none"
52+
stroke="currentColor"
53+
stroke-width="2.2"
54+
stroke-linecap="round"
55+
stroke-linejoin="round"
56+
>
57+
<polyline points="6 9 12 15 18 9" />
58+
</svg>
59+
</button>
60+
61+
<!-- Papers List -->
62+
<div id={`papers-${year}`} class="papers-container overflow-hidden transition-all duration-300">
63+
<div class="space-y-3 pb-4">
64+
{grouped[year].map((paper) => (
65+
<div class="card bg-base-200 shadow-sm hover:shadow-md transition-shadow duration-200">
66+
<div class="card-body p-5">
67+
<!-- Title -->
68+
<h2 class="card-title text-base leading-snug">
69+
<a
70+
href={paper.link}
71+
class="hover:text-accent transition-colors duration-200"
72+
target="_blank"
73+
rel="noopener noreferrer"
74+
>
75+
{paper.title}
76+
</a>
77+
</h2>
78+
79+
<!-- Authors -->
80+
<p class="text-sm text-base-content/70 mt-1">
81+
<span set:html={highlightAuthor(paper.authors)} />
82+
</p>
83+
84+
<!-- Journal + Link row -->
85+
<div class="flex items-center justify-between flex-wrap gap-2 mt-2">
86+
<span class="text-sm italic text-base-content/60">{paper.journal}</span>
87+
<a
88+
href={paper.link}
89+
target="_blank"
90+
rel="noopener noreferrer"
91+
class="btn btn-xs btn-outline btn-accent gap-1"
92+
>
93+
DOI
94+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
95+
<path d="M7 17L17 7" /><path d="M7 7h10v10" />
96+
</svg>
97+
</a>
98+
</div>
99+
100+
<!-- Abstract Toggle -->
101+
{paper.abstract && (
102+
<div class="mt-2">
103+
<button class="abstract-toggle text-xs font-medium text-base-content/50 hover:text-accent transition-colors cursor-pointer">
104+
▸ Abstract
105+
</button>
106+
<div class="abstract-text hidden mt-2 text-sm text-base-content/70 leading-relaxed p-3 bg-base-300/40 rounded-lg border-l-3 border-accent/30">
107+
{paper.abstract}
108+
</div>
109+
</div>
110+
)}
111+
</div>
112+
</div>
113+
))}
114+
</div>
115+
</div>
116+
</div>
117+
))}
51118
</div>
52119
</div>
53120
</Layout>
121+
122+
<script>
123+
document.addEventListener("astro:page-load", () => {
124+
// Year section collapse/expand
125+
const yearToggles = document.querySelectorAll<HTMLButtonElement>(".year-toggle");
126+
127+
yearToggles.forEach((toggle) => {
128+
const container = toggle.nextElementSibling as HTMLElement;
129+
if (!container) return;
130+
131+
// Set initial max-height for smooth animation
132+
container.style.maxHeight = container.scrollHeight + "px";
133+
134+
toggle.addEventListener("click", () => {
135+
const expanded = toggle.getAttribute("aria-expanded") === "true";
136+
toggle.setAttribute("aria-expanded", String(!expanded));
137+
138+
const chevron = toggle.querySelector(".chevron-icon") as HTMLElement;
139+
140+
if (expanded) {
141+
// Collapse
142+
container.style.maxHeight = "0px";
143+
container.style.opacity = "0";
144+
if (chevron) chevron.style.transform = "rotate(-90deg)";
145+
} else {
146+
// Expand
147+
container.style.maxHeight = container.scrollHeight + "px";
148+
container.style.opacity = "1";
149+
if (chevron) chevron.style.transform = "rotate(0deg)";
150+
}
151+
});
152+
});
153+
154+
// Expand All
155+
document.getElementById("expand-all")?.addEventListener("click", () => {
156+
yearToggles.forEach((toggle) => {
157+
toggle.setAttribute("aria-expanded", "true");
158+
const container = toggle.nextElementSibling as HTMLElement;
159+
const chevron = toggle.querySelector(".chevron-icon") as HTMLElement;
160+
if (container) {
161+
container.style.maxHeight = container.scrollHeight + "px";
162+
container.style.opacity = "1";
163+
}
164+
if (chevron) chevron.style.transform = "rotate(0deg)";
165+
});
166+
});
167+
168+
// Collapse All
169+
document.getElementById("collapse-all")?.addEventListener("click", () => {
170+
yearToggles.forEach((toggle) => {
171+
toggle.setAttribute("aria-expanded", "false");
172+
const container = toggle.nextElementSibling as HTMLElement;
173+
const chevron = toggle.querySelector(".chevron-icon") as HTMLElement;
174+
if (container) {
175+
container.style.maxHeight = "0px";
176+
container.style.opacity = "0";
177+
}
178+
if (chevron) chevron.style.transform = "rotate(-90deg)";
179+
});
180+
});
181+
182+
// Abstract toggles
183+
document.querySelectorAll<HTMLButtonElement>(".abstract-toggle").forEach((btn) => {
184+
btn.addEventListener("click", () => {
185+
const abstractText = btn.nextElementSibling as HTMLElement;
186+
if (!abstractText) return;
187+
188+
const isHidden = abstractText.classList.contains("hidden");
189+
abstractText.classList.toggle("hidden");
190+
btn.textContent = isHidden ? "▾ Abstract" : "▸ Abstract";
191+
});
192+
});
193+
});
194+
</script>
195+
196+
<style>
197+
.papers-container {
198+
transition: max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1),
199+
opacity 0.25s ease;
200+
}
201+
</style>

0 commit comments

Comments
 (0)