Skip to content

Commit 1a1e50d

Browse files
committed
[ FIX ] Mouse Swipe for projects
- fix swiping using external mouse - add helper tooltip
1 parent 730cb38 commit 1a1e50d

File tree

1 file changed

+62
-3
lines changed

1 file changed

+62
-3
lines changed

src/components/Projects.jsx

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react";
1+
import { useState, useRef, useEffect } from "react";
22
import ProjectCard from "./ProjectCard";
33

44
const 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 &rarr;</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

Comments
 (0)