11"use client" ;
22
3- import { motion } from "framer-motion" ;
3+ import { useState } from "react" ;
4+ import { motion , type PanInfo } from "framer-motion" ;
45import { BlurImage } from "@/components/ui/blur-image" ;
56import { MapPin , Coffee , Utensils , Wifi , Bus , Bike , Shield } from "lucide-react" ;
67import { GradientBlob } from "@/components/ui/GradientBlob" ;
78import { PageSegue } from "@/components/ui/PageSegue" ;
8- import Carousel from "@/components/ui/Carousel" ;
99
1010import dormImg1 from "@/public/images/uni/IMG_20251103_130246 (1).webp" ;
1111import dormImg2 from "@/public/images/uni/IMG_20251109_094544.webp" ;
@@ -67,6 +67,22 @@ export default function CampusLifePage() {
6767 }
6868 ] ;
6969
70+ const [ currentSlide , setCurrentSlide ] = useState ( 0 ) ;
71+
72+ const getSlideOffset = ( index : number ) => {
73+ const vw = typeof window !== "undefined" ? window . innerWidth : 375 ;
74+ return - index * ( vw * 0.8 + 16 ) ;
75+ } ;
76+
77+ const handleDragEnd = ( _ : MouseEvent | TouchEvent | PointerEvent , info : PanInfo ) => {
78+ const { offset, velocity } = info ;
79+ let direction = 0 ;
80+ if ( offset . x < - 50 || velocity . x < - 500 ) direction = 1 ;
81+ else if ( offset . x > 50 || velocity . x > 500 ) direction = - 1 ;
82+ const next = Math . max ( 0 , Math . min ( currentSlide + direction , oldDorms . length - 1 ) ) ;
83+ setCurrentSlide ( next ) ;
84+ } ;
85+
7086 const galleryImages = [
7187 { src : galleryImg1 , caption : "Campus at Night" } ,
7288 { src : galleryImg2 , caption : "Campus in Autumn" } ,
@@ -146,7 +162,6 @@ export default function CampusLifePage() {
146162 </ div >
147163 </ motion . div >
148164 < div className = "space-y-16" >
149- { /* New Building */ }
150165 < div >
151166 < h3 className = "text-xl md:text-2xl font-display font-bold mb-4 md:mb-8 text-primary" > New Building (Bohou)</ h3 >
152167
@@ -182,7 +197,7 @@ export default function CampusLifePage() {
182197 ) ) }
183198 </ div >
184199
185- { /* Mobile - centered card matching carousel width */ }
200+ { /* Mobile*/ }
186201 < div className = "md:hidden flex justify-center" >
187202 { newDorms . map ( ( dorm , idx ) => (
188203 < motion . div
@@ -251,22 +266,36 @@ export default function CampusLifePage() {
251266 ) ) }
252267 </ div >
253268
254- { /* Mobile Carousel */ }
255- < div className = "md:hidden flex justify-center" >
256- < Carousel baseWidth = { 320 } loop >
269+ { /* Mobile Drag Carousel */ }
270+ < div className = "md:hidden -mx-6 overflow-hidden" >
271+ < motion . div
272+ className = "flex gap-4 px-[10vw] cursor-grab active:cursor-grabbing touch-pan-y"
273+ drag = "x"
274+ dragConstraints = { {
275+ left : getSlideOffset ( oldDorms . length - 1 ) ,
276+ right : 0 ,
277+ } }
278+ dragElastic = { 0.15 }
279+ onDragEnd = { handleDragEnd }
280+ animate = { { x : getSlideOffset ( currentSlide ) } }
281+ transition = { { type : "spring" , stiffness : 300 , damping : 30 } }
282+ >
257283 { oldDorms . map ( ( dorm , idx ) => (
258- < div key = { idx } className = "bg-white rounded-2xl overflow-hidden shadow-sm border border-border h-full" >
259- < div className = "h-44 overflow-hidden relative" >
260- < BlurImage src = { dorm . image } alt = { dorm . title } placeholder = "blur" className = "w-full h-full object-cover" />
261- < div className = "absolute bottom-3 right-3 bg-black/70 backdrop-blur-md text-white px-2.5 py-0.5 rounded-full text-xs font-medium z-10" >
284+ < div
285+ key = { idx }
286+ className = "flex-shrink-0 w-[80vw] select-none"
287+ >
288+ < div className = "aspect-[4/3] overflow-hidden relative" >
289+ < BlurImage src = { dorm . image } alt = { dorm . title } placeholder = "blur" className = "w-full h-full object-cover pointer-events-none" />
290+ < div className = "absolute bottom-3 left-3 bg-black/60 backdrop-blur-md text-white px-3 py-1 rounded-full text-xs font-medium z-10" >
262291 { dorm . price }
263292 </ div >
264293 </ div >
265- < div className = "p-4 " >
266- < h3 className = "text-base font-bold mb-3 " > { dorm . title } </ h3 >
294+ < div className = "pt-4 pb-2 flex flex-col items-center " >
295+ < h3 className = "text-lg font-bold mb-2 text-center " > { dorm . title } </ h3 >
267296 < ul className = "space-y-1.5" >
268297 { dorm . features . map ( ( feature , i ) => (
269- < li key = { i } className = "flex items-center gap-2 text-xs text-muted-foreground" >
298+ < li key = { i } className = "flex items-center gap-2 text-sm text-muted-foreground" >
270299 < div className = "w-1.5 h-1.5 rounded-full bg-primary flex-shrink-0" />
271300 { feature }
272301 </ li >
@@ -275,7 +304,24 @@ export default function CampusLifePage() {
275304 </ div >
276305 </ div >
277306 ) ) }
278- </ Carousel >
307+ </ motion . div >
308+ { /* Dot indicators */ }
309+ < div className = "flex justify-center mt-4" >
310+ < div className = "flex gap-2.5 items-center" >
311+ { oldDorms . map ( ( _ , index ) => (
312+ < button
313+ key = { index }
314+ className = { `rounded-full transition-all duration-200 ${
315+ currentSlide === index
316+ ? "bg-primary w-3 h-3"
317+ : "bg-primary/30 w-2.5 h-2.5"
318+ } `}
319+ onClick = { ( ) => setCurrentSlide ( index ) }
320+ aria-label = { `Go to slide ${ index + 1 } ` }
321+ />
322+ ) ) }
323+ </ div >
324+ </ div >
279325 </ div >
280326 </ div >
281327 </ div >
0 commit comments