Skip to content

Commit

Permalink
basic carousel
Browse files Browse the repository at this point in the history
  • Loading branch information
nickbristow committed Oct 25, 2024
1 parent 8e939d5 commit aa5c2d0
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# misc
.DS_Store
*.pem

*.vscode
# debug
npm-debug.log*
yarn-debug.log*
Expand Down
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"@trussworks/react-uswds": "^9.1.0",
"@uswds/uswds": "^3.9.0",
"classnames": "^2.5.1",
"fs": "^0.0.1-security",
"gray-matter": "^4.0.3",
"next": "14.2.15",
"path": "^0.12.7",
Expand All @@ -35,6 +34,7 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.2",
"fs": "^0.0.1-security",
"husky": "^9.1.6",
"lint-staged": "^15.2.10",
"postcss": "^8",
Expand Down
8 changes: 8 additions & 0 deletions public/images/placeholder-carousel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 122 additions & 0 deletions src/app/components/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use client';
import { useState, useRef } from 'react';
import Image from 'next/image';
import { basePath } from '../utils/constants';

export default function MultiImageCarousel() {
const images = [
`${basePath}/images/placeholder-carousel.svg`,
`${basePath}/images/placeholder-carousel.svg`,
`${basePath}/images/placeholder-carousel.svg`,
`${basePath}/images/placeholder-carousel.svg`,
`${basePath}/images/placeholder-carousel.svg`,
`${basePath}/images/placeholder-carousel.svg`,
`${basePath}/images/placeholder-carousel.svg`,
`${basePath}/images/placeholder-carousel.svg`,
];

const [currentIndex, setCurrentIndex] = useState(0);
const itemsPerView = 3; // Number of images visible at once
const [isDragging, setIsDragging] = useState(false);
const [dragStartX, setDragStartX] = useState(0);
const [dragOffset, setDragOffset] = useState(0);

const carouselRef = useRef(null);

// Handle drag start
const handleDragStart = (
e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,
) => {
let startX: number;

// Check if the event is a TouchEvent (touches exist on TouchEvent)
if ('touches' in e) {
startX = e.touches[0].clientX; // TouchEvent uses touches array
} else {
startX = e.pageX; // MouseEvent uses pageX
}

setDragStartX(startX);
setIsDragging(true);
};

// Handle dragging
const handleDragMove = (
e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,
) => {
if (!isDragging) return;
let currentX: number;
if ('touches' in e) {
currentX = e.touches[0].clientX;
} else {
currentX = e.pageX;
}
setDragOffset(currentX - dragStartX);
};

// Handle drag end
const handleDragEnd = () => {
if (Math.abs(dragOffset) > 50) {
if (dragOffset < 0) {
nextSlide();
} else {
prevSlide();
}
}
setIsDragging(false);
setDragOffset(0);
};

const prevSlide = () => {
setCurrentIndex(
currentIndex === 0 ? images.length - itemsPerView : currentIndex - 1,
);
};

const nextSlide = () => {
setCurrentIndex((currentIndex + 1) % (images.length - itemsPerView + 1));
};

return (
<div
className="carousel-container"
onMouseDown={handleDragStart}
onMouseMove={handleDragMove}
onMouseUp={handleDragEnd}
onMouseLeave={handleDragEnd}
onTouchStart={handleDragStart}
onTouchMove={handleDragMove}
onTouchEnd={handleDragEnd}
ref={carouselRef}
>
<button onClick={prevSlide} className="carousel-btn prev">
</button>
<div className="carousel-wrapper">
<div
className="carousel"
style={{
transform: `translateX(calc(-${(currentIndex / images.length) * 100}% + ${dragOffset}px))`,
width: `${(images.length / itemsPerView) * 100}%`,
transition: isDragging ? 'none' : 'transform 0.5s ease-in-out',
}}
>
{images.map((src, index) => (
<div key={index} className="carousel-item">
<Image
src={src}
alt={`Slide ${index + 1}`}
width={160}
height={100}
draggable={false}
/>
</div>
))}
</div>
</div>
<button onClick={nextSlide} className="carousel-btn next">
</button>
</div>
);
}
70 changes: 70 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,73 @@ p {
margin-top: 1rem;
margin-bottom: 1rem;
}

.carousel-container {
position: relative;
width: 100%; /* Full width of the container */
}

.carousel-wrapper {
display: flex;
width: 100%;
overflow: hidden;
cursor: grab; /* Shows a hand cursor when hovering for drag */
}

.carousel-wrapper:active {
cursor: grabbing; /* Changes cursor when dragging */
}

.carousel {
display: flex;
transition: transform 0.5s ease-in-out;
will-change: transform;
}

.carousel-item {
min-width: calc(100% / 5); /* Adjust based on number of visible items */
flex: 1 0 auto;
padding: 0 10px;
}

.carousel-btn {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}

.carousel-btn:focus {
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
padding: 10px 20px;
font-size: 2rem;
cursor: pointer;
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 10;
transition: background 0.3s ease;
width: auto;
height: auto;
margin: inherit;
overflow: visible;
clip: inherit;
}

.carousel-btn:hover {
background: rgba(0, 0, 0, 0.8);
}

.prev {
left: 10px;
}

.next {
right: 10px;
}
4 changes: 4 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import MarkdownContent from './components/MarkdownComponent';
import { Grid } from '@trussworks/react-uswds';
import Image from 'next/image';
import { basePath } from './utils/constants';
import SimpleCarousel from './components/Carousel';

export default async function Home() {
return (
Expand Down Expand Up @@ -48,6 +49,9 @@ export default async function Home() {
{MarkdownContent('homepage/section_4.md')}
</Grid>
</Grid>
<Grid row>
<SimpleCarousel />
</Grid>
</div>
</section>
</>
Expand Down

0 comments on commit aa5c2d0

Please sign in to comment.