22import BaseLayout from ' ../layouts/BaseLayout.astro' ;
33import TeamMember from ' ../components/TeamMember.astro' ;
44import { getCollection } from ' astro:content' ;
5+ import { promises as fs } from ' node:fs' ;
6+ import * as path from ' node:path' ;
7+ import yaml from ' js-yaml' ;
58
69const allMembers = await getCollection (' team' );
710
@@ -33,15 +36,9 @@ const sortedAlumni = alumni.sort((a, b) => {
3336 return a .data .name .localeCompare (b .data .name );
3437});
3538
36-
37- const photos = [
38- { src: ' /img/GroupBowlingPhoto.JPG' , alt: ' squaresLab after a friendly but competitive bowling excursion' },
39- { src: ' /img/GroupEscapeRoomPhoto.jpg' , alt: " squaresLab after successfully solving Carnegie'$ Million$ at Escape Room Pittsburgh" },
40- { src: ' /img/GroupAxePhoto.jpg' , alt: ' squaresLab in our squaresLab t-shirts after an axe throwing social event' },
41- { src: ' /img/GroupApples.jpg' , alt: ' squaresLab goes apple picking' },
42- { src: ' /img/GroupTopgolfPhoto.jpg' , alt: ' squaresLab goes to Topgolf' },
43- { src: ' /img/GroupBowling2.jpg' , alt: ' squaresLab the next generation goes bowling, too' },
44- ];
39+ // Load photos from data file
40+ const photosRaw = await fs .readFile (path .resolve (' src/data/photos.yaml' ), ' utf-8' );
41+ const photos = (yaml .load (photosRaw ) as { src: string ; alt: string }[]) || [];
4542---
4643
4744<BaseLayout title =" Team" >
@@ -52,15 +49,36 @@ const photos = [
5249 </div >
5350 </section >
5451
55- { /* Photo Gallery */ }
52+ { /* Photo Carousel */ }
5653 <section class =" max-w-content mx-auto px-8 py-8" >
57- <div class =" grid grid-cols-1 md:grid-cols-2 gap-4" >
58- { photos .map ((photo ) => (
59- <figure class = " m-0" >
60- <img src = { photo .src } alt = { photo .alt } class = " w-full rounded-lg" loading = " lazy" />
61- <figcaption class = " text-xs text-gray-secondary mt-1 italic" >{ photo .alt } </figcaption >
62- </figure >
63- ))}
54+ <div id =" photo-carousel" class =" relative" >
55+ <div class =" grid grid-cols-1 md:grid-cols-2 gap-4" id =" carousel-grid" >
56+ { photos .map ((photo , i ) => (
57+ <figure
58+ class = " m-0 carousel-slide"
59+ data-index = { i }
60+ style = { i >= 2 ? ' display:none' : ' ' }
61+ >
62+ <img src = { photo .src } alt = { photo .alt } class = " w-full rounded-lg" loading = " lazy" />
63+ <figcaption class = " text-xs text-gray-secondary mt-1 italic" >{ photo .alt } </figcaption >
64+ </figure >
65+ ))}
66+ </div >
67+ { photos .length > 2 && (
68+ <div class = " flex items-center justify-center gap-4 mt-4" >
69+ <button id = " carousel-prev" class = " text-gray-secondary hover:text-red-cmu transition-colors p-1" aria-label = " Previous photos" >
70+ <svg class = " w-5 h-5" fill = " none" stroke = " currentColor" viewBox = " 0 0 24 24" >
71+ <path stroke-linecap = " round" stroke-linejoin = " round" stroke-width = " 2" d = " M15 19l-7-7 7-7" />
72+ </svg >
73+ </button >
74+ <span id = " carousel-indicator" class = " text-xs text-gray-secondary" ></span >
75+ <button id = " carousel-next" class = " text-gray-secondary hover:text-red-cmu transition-colors p-1" aria-label = " Next photos" >
76+ <svg class = " w-5 h-5" fill = " none" stroke = " currentColor" viewBox = " 0 0 24 24" >
77+ <path stroke-linecap = " round" stroke-linejoin = " round" stroke-width = " 2" d = " M9 5l7 7-7 7" />
78+ </svg >
79+ </button >
80+ </div >
81+ )}
6482 </div >
6583 </section >
6684
@@ -179,4 +197,45 @@ const photos = [
179197 details[open] .details-arrow {
180198 transform: rotate(90deg);
181199 }
200+ .carousel-slide {
201+ transition: opacity 0.4s ease;
202+ }
182203</style >
204+
205+ <script >
206+ const slides = document.querySelectorAll<HTMLElement>('.carousel-slide');
207+ const total = slides.length;
208+ if (total > 2) {
209+ const perPage = window.matchMedia('(min-width: 768px)').matches ? 2 : 1;
210+ let offset = 0;
211+ const indicator = document.getElementById('carousel-indicator');
212+
213+ function show() {
214+ slides.forEach((s, i) => {
215+ s.style.display = (i >= offset && i < offset + perPage) ? '' : 'none';
216+ });
217+ if (indicator) {
218+ const page = Math.floor(offset / perPage) + 1;
219+ const pages = Math.ceil(total / perPage);
220+ indicator.textContent = `${page} / ${pages}`;
221+ }
222+ }
223+
224+ function advance(dir: number) {
225+ const perPage = window.matchMedia('(min-width: 768px)').matches ? 2 : 1;
226+ offset += dir * perPage;
227+ if (offset >= total) offset = 0;
228+ if (offset < 0) offset = Math.max(0, total - perPage);
229+ show();
230+ }
231+
232+ document.getElementById('carousel-prev')?.addEventListener('click', () => {
233+ advance(-1);
234+ });
235+ document.getElementById('carousel-next')?.addEventListener('click', () => {
236+ advance(1);
237+ });
238+
239+ show();
240+ }
241+ </script >
0 commit comments