1- import { useState } from "react" ;
1+ import { useState , useRef , useEffect } from "react" ;
22import ProjectCard from "./ProjectCard" ;
33
44const Projects = ( ) => {
@@ -64,6 +64,57 @@ const Projects = () => {
6464 ? projectsData
6565 : projectsData . filter ( ( project ) => project . category === selectedCategory ) ;
6666
67+ const sliderRef = useRef ( null ) ;
68+
69+ useEffect ( ( ) => {
70+ const slider = sliderRef . current ;
71+ if ( ! slider ) return ;
72+
73+ let isDown = false ;
74+ let startX ;
75+ let scrollLeft ;
76+
77+ const mouseDownHandler = ( e ) => {
78+ isDown = true ;
79+ slider . classList . add ( 'cursor-grabbing' ) ;
80+ slider . classList . remove ( 'cursor-grab' ) ;
81+ slider . classList . add ( 'select-none' ) ;
82+ startX = e . pageX - slider . offsetLeft ;
83+ scrollLeft = slider . scrollLeft ;
84+ } ;
85+
86+ const mouseMoveHandler = ( e ) => {
87+ if ( ! isDown ) return ;
88+ e . preventDefault ( ) ;
89+ const x = e . pageX - slider . offsetLeft ;
90+ const walk = ( x - startX ) * 2 ;
91+ slider . scrollLeft = scrollLeft - walk ;
92+ } ;
93+
94+ const mouseUpHandler = ( ) => {
95+ if ( ! isDown ) return ;
96+ isDown = false ;
97+ slider . classList . remove ( 'cursor-grabbing' ) ;
98+ slider . classList . add ( 'cursor-grab' ) ;
99+ slider . classList . remove ( 'select-none' ) ;
100+ } ;
101+
102+ slider . addEventListener ( 'mousedown' , mouseDownHandler ) ;
103+ window . addEventListener ( 'mousemove' , mouseMoveHandler ) ;
104+ window . addEventListener ( 'mouseup' , mouseUpHandler ) ;
105+
106+ return ( ) => {
107+ slider . removeEventListener ( 'mousedown' , mouseDownHandler ) ;
108+ window . removeEventListener ( 'mousemove' , mouseMoveHandler ) ;
109+ window . removeEventListener ( 'mouseup' , mouseUpHandler ) ;
110+
111+ if ( slider ) {
112+ slider . classList . remove ( 'cursor-grabbing' ) ;
113+ slider . classList . add ( 'cursor-grab' ) ;
114+ slider . classList . remove ( 'select-none' ) ;
115+ }
116+ } ;
117+ } , [ ] ) ;
67118
68119 return (
69120 < section id = "projects" className = "py-20" >
@@ -87,7 +138,7 @@ const Projects = () => {
87138 </ div >
88139
89140 { filteredProjects . length > 0 ? (
90- < div className = "overflow-x-auto pb-4" >
141+ < div ref = { sliderRef } className = "overflow-x-auto pb-4 cursor-grab " >
91142 < div className = "flex gap-6" >
92143 { filteredProjects . map ( ( project ) => (
93144 < ProjectCard key = { project . id } project = { project } />
@@ -99,8 +150,16 @@ const Projects = () => {
99150 ) }
100151
101152 { filteredProjects . length > 0 && (
102- < div className = "text-center mt-4 text-sm text-gray-500" >
153+ < div className = "text-center mt-4 text-sm text-gray-500 flex items-center justify-center gap-2 " >
103154 < p > Swipe to view more projects →</ p >
155+ < div className = "relative group" >
156+ < span className = "cursor-help text-gray-400 hover:text-gray-600" >
157+ (?)
158+ </ span >
159+ < div className = "absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 w-max max-w-xs p-2 text-xs text-white bg-gray-700 rounded-md opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none" >
160+ Using an external mouse? Hold left-click and drag to swipe.
161+ </ div >
162+ </ div >
104163 </ div >
105164 ) }
106165 </ div >
0 commit comments