Skip to content

Commit 10dd956

Browse files
authored
add: expandable images on research and people page (#8)
1 parent 85f231f commit 10dd956

File tree

7 files changed

+273
-49
lines changed

7 files changed

+273
-49
lines changed

app/globals.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
--input: 240 5.9% 90%;
2525
--ring: 240 10% 3.9%;
2626
--radius: 1rem;
27+
scrollbar-gutter: stable;
2728
}
2829

2930
.dark {

app/people/layout.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Metadata } from "next"
2+
3+
export const metadata: Metadata = {
4+
title: "People | Mathis Group",
5+
description: "Meet our team of researchers working at the intersection of neuroscience, machine learning, and computer vision.",
6+
openGraph: {
7+
title: "People | Mathis Group",
8+
description:
9+
"We are a diverse group of researchers working at the intersection of neuroscience, machine learning, and computer vision.",
10+
},
11+
}
12+
13+
export default function PeopleLayout({ children }: { children: React.ReactNode }) {
14+
return <>{children}</>
15+
}

app/people/page.tsx

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import { Mail, ExternalLink, GraduationCap, Calendar, MapPin } from "lucide-react"
1+
"use client"
22

3+
import { Calendar, ExternalLink, GraduationCap, Mail, MapPin } from "lucide-react"
4+
import { useState } from "react"
5+
6+
import { ImageLightbox } from "@/components/ImageLightbox"
37
import { Badge } from "@/components/ui/badge"
48
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
59
import groupPhotos from "@/data/group-photos.json"
610
import peopleData from "@/data/people.json"
711

8-
export const metadata = {
9-
title: "People | Mathis Group",
10-
description: "Meet the team behind our computational neuroscience and machine learning research.",
11-
}
12-
1312
export default function PeoplePage() {
13+
const [lightboxOpen, setLightboxOpen] = useState(false)
14+
const [currentImageIndex, setCurrentImageIndex] = useState(0)
15+
1416
const currentMembers = peopleData.filter(p => p.status === "current")
1517
const alumni = peopleData.filter(p => p.status === "alumni")
1618

@@ -19,6 +21,14 @@ export default function PeoplePage() {
1921
const staff = currentMembers.filter(p => p.role === "Research Staff" || p.role === "Software Engineer")
2022
const admin = currentMembers.filter(p => p.role === "Lab Administration")
2123

24+
// Prepare images for lightbox - just the URLs
25+
const groupPhotoImages = groupPhotos.map((photo) => photo.image)
26+
27+
const openLightbox = (index: number) => {
28+
setCurrentImageIndex(index)
29+
setLightboxOpen(true)
30+
}
31+
2232
return (
2333
<div className="flex flex-col">
2434
{/* Header */}
@@ -380,15 +390,19 @@ export default function PeoplePage() {
380390
<div className="section-container">
381391
<h2 className="mb-8 text-2xl font-bold">Group Photos</h2>
382392
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
383-
{groupPhotos.map((photo) => (
393+
{groupPhotos.map((photo, idx) => (
384394
<Card key={photo.id} className="group overflow-hidden transition-all hover:shadow-soft-lg">
385-
<div className="relative aspect-[4/3] overflow-hidden bg-muted/30">
395+
<button
396+
type="button"
397+
onClick={() => openLightbox(idx)}
398+
className="relative aspect-[4/3] w-full cursor-zoom-in overflow-hidden bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
399+
>
386400
<img
387401
src={photo.image}
388402
alt={photo.title}
389403
className="size-full object-cover transition-transform group-hover:scale-105"
390404
/>
391-
</div>
405+
</button>
392406
<CardHeader>
393407
<CardTitle className="text-lg">{photo.title}</CardTitle>
394408
<CardDescription className="mt-2">{photo.description}</CardDescription>
@@ -408,6 +422,14 @@ export default function PeoplePage() {
408422
</Card>
409423
))}
410424
</div>
425+
<ImageLightbox
426+
images={groupPhotoImages}
427+
currentIndex={currentImageIndex}
428+
isOpen={lightboxOpen}
429+
onClose={() => setLightboxOpen(false)}
430+
onNavigate={setCurrentImageIndex}
431+
title="Group Photos"
432+
/>
411433
</div>
412434
</section>
413435
)}

app/research/layout.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Metadata } from "next"
2+
3+
export const metadata: Metadata = {
4+
title: "Research | Mathis Group",
5+
description: "Our research directions in computational neuroscience and machine learning.",
6+
openGraph: {
7+
title: "Research | Mathis Group",
8+
description:
9+
"We develop machine learning tools for behavioral and neural data analysis, and learn from the brain to solve challenging ML problems.",
10+
},
11+
}
12+
13+
export default function ResearchLayout({ children }: { children: React.ReactNode }) {
14+
return <>{children}</>
15+
}

app/research/page.tsx

Lines changed: 76 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import { ExternalLink, Github, FileText, Globe } from "lucide-react"
1+
"use client"
22

3+
import { ExternalLink, FileText, Github, Globe } from "lucide-react"
4+
import { useState } from "react"
5+
6+
import { ImageLightbox } from "@/components/ImageLightbox"
37
import { Badge } from "@/components/ui/badge"
48
import { Button } from "@/components/ui/button"
59
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
@@ -9,15 +13,6 @@ import { CopyLinkButton } from "./CopyLinkButton"
913
import { ReadMoreSection } from "./ReadMoreSection"
1014
import { StickyLocalNav } from "./StickyLocalNav"
1115

12-
export const metadata = {
13-
title: "Research | Mathis Group",
14-
description: "Our research directions in computational neuroscience and machine learning.",
15-
openGraph: {
16-
title: "Research | Mathis Group",
17-
description: "We develop machine learning tools for behavioral and neural data analysis, and learn from the brain to solve challenging ML problems.",
18-
},
19-
}
20-
2116
// Type-safe interfaces for research data
2217
interface Tool {
2318
name: string
@@ -39,8 +34,16 @@ interface ResearchArea {
3934
image?: string | string[]
4035
}
4136

42-
// Media mosaic with lazy loading
37+
// Media mosaic with lazy loading and lightbox
4338
function MediaMosaic({ images, title }: { images: string[]; title: string }) {
39+
const [lightboxOpen, setLightboxOpen] = useState(false)
40+
const [currentImageIndex, setCurrentImageIndex] = useState(0)
41+
42+
const openLightbox = (index: number) => {
43+
setCurrentImageIndex(index)
44+
setLightboxOpen(true)
45+
}
46+
4447
if (images.length === 0) {
4548
return (
4649
<div className="flex aspect-video items-center justify-center rounded-xl border border-dashed bg-muted/30">
@@ -51,16 +54,33 @@ function MediaMosaic({ images, title }: { images: string[]; title: string }) {
5154

5255
if (images.length === 1) {
5356
return (
54-
<figure className="overflow-hidden rounded-xl bg-white ring-1 ring-border/50 dark:bg-white/95">
55-
<img
56-
src={images[0]}
57-
alt={title}
58-
loading="lazy"
59-
decoding="async"
60-
className="h-auto w-full transition-transform duration-200 hover:scale-[1.01]"
57+
<>
58+
<figure className="overflow-hidden rounded-xl bg-white ring-1 ring-border/50 dark:bg-white/95">
59+
<button
60+
type="button"
61+
onClick={() => openLightbox(0)}
62+
className="w-full cursor-zoom-in transition-transform duration-200 hover:scale-[1.01] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
63+
aria-label={`View full size: ${title}`}
64+
>
65+
<img
66+
src={images[0]}
67+
alt={title}
68+
loading="lazy"
69+
decoding="async"
70+
className="h-auto w-full"
71+
/>
72+
</button>
73+
<figcaption className="sr-only">{title}</figcaption>
74+
</figure>
75+
<ImageLightbox
76+
images={images}
77+
currentIndex={currentImageIndex}
78+
isOpen={lightboxOpen}
79+
onClose={() => setLightboxOpen(false)}
80+
onNavigate={setCurrentImageIndex}
81+
title={title}
6182
/>
62-
<figcaption className="sr-only">{title}</figcaption>
63-
</figure>
83+
</>
6484
)
6585
}
6686

@@ -72,25 +92,42 @@ function MediaMosaic({ images, title }: { images: string[]; title: string }) {
7292
: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
7393

7494
return (
75-
<div className={`grid gap-3 ${gridClass}`}>
76-
{images.map((img, idx) => (
77-
<figure
78-
key={idx}
79-
className="group relative aspect-[4/3] overflow-hidden rounded-xl bg-white ring-1 ring-border/50 transition-all duration-200 hover:scale-[1.01] hover:shadow-lg dark:bg-white/95"
80-
>
81-
<img
82-
src={img}
83-
alt={`${title} - Figure ${idx + 1}`}
84-
loading="lazy"
85-
decoding="async"
86-
className="size-full object-cover"
87-
/>
88-
<figcaption className="sr-only">
89-
{title} - Figure {idx + 1}
90-
</figcaption>
91-
</figure>
92-
))}
93-
</div>
95+
<>
96+
<div className={`grid gap-3 ${gridClass}`}>
97+
{images.map((img, idx) => (
98+
<figure
99+
key={idx}
100+
className="group relative aspect-[4/3] overflow-hidden rounded-xl bg-white ring-1 ring-border/50 dark:bg-white/95"
101+
>
102+
<button
103+
type="button"
104+
onClick={() => openLightbox(idx)}
105+
className="size-full cursor-zoom-in transition-all duration-200 hover:scale-[1.01] hover:shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
106+
aria-label={`View full size: ${title} - Figure ${idx + 1}`}
107+
>
108+
<img
109+
src={img}
110+
alt={`${title} - Figure ${idx + 1}`}
111+
loading="lazy"
112+
decoding="async"
113+
className="size-full object-cover"
114+
/>
115+
</button>
116+
<figcaption className="sr-only">
117+
{title} - Figure {idx + 1}
118+
</figcaption>
119+
</figure>
120+
))}
121+
</div>
122+
<ImageLightbox
123+
images={images}
124+
currentIndex={currentImageIndex}
125+
isOpen={lightboxOpen}
126+
onClose={() => setLightboxOpen(false)}
127+
onNavigate={setCurrentImageIndex}
128+
title={title}
129+
/>
130+
</>
94131
)
95132
}
96133

0 commit comments

Comments
 (0)