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"
37import { Badge } from "@/components/ui/badge"
48import { Button } from "@/components/ui/button"
59import { Card , CardContent , CardDescription , CardHeader , CardTitle } from "@/components/ui/card"
@@ -9,15 +13,6 @@ import { CopyLinkButton } from "./CopyLinkButton"
913import { ReadMoreSection } from "./ReadMoreSection"
1014import { 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
2217interface 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
4338function 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