Skip to content

Commit

Permalink
SOEOPSFY24-206 | add slider component
Browse files Browse the repository at this point in the history
  • Loading branch information
rebeccahongsf committed Nov 6, 2024
1 parent ca1b925 commit f0d5dee
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 0 deletions.
52 changes: 52 additions & 0 deletions app/components/Slideshow/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { HTMLAttributes } from "react";
import { Slideshow } from "@/components/Slideshow/slideshow";

type Props = HTMLAttributes<HTMLElement> & {
children: React.ReactNode;
};

export const Carousel = ({ children, ...props }: Props) => {
const slides = React.Children.toArray(children);

return (
<article {...props} aria-labelledby="carousel-label">
<div className="relative left-1/2 mb-32 mt-0 w-screen -translate-x-1/2">
<Slideshow className="mx-auto w-[calc(100%-50px)] xl:w-[calc(100%-150px)]">
{slides.map((slide, slideIndex) => {
return (
<CarouselSlide
key={slideIndex} // You can use slideIndex or slide.id if available
slide={slide}
slideNumber={slideIndex + 1}
totalSlides={slides.length}
/>
);
})}
</Slideshow>
</div>
</article>
);
};

const CarouselSlide = ({
slide,
slideNumber,
totalSlides,
}: {
slide: React.ReactNode;
slideNumber: number;
totalSlides: number;
}) => {
const labelId = `slide-${slideNumber}`;

return (
<div
role="group"
aria-roledescription="slide"
aria-labelledby={labelId}
aria-label={`${slideNumber} of ${totalSlides}`}
>
{slide}
</div>
);
};
127 changes: 127 additions & 0 deletions app/components/Slideshow/Slideshow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"use client";

import { HTMLAttributes, JSX, useEffect, useRef } from "react";
import Slider, { CustomArrowProps, Settings } from "react-slick";
import {
ArrowLongRightIcon,
ArrowLongLeftIcon,
} from "@heroicons/react/16/solid";
import { cnb } from "cnbuilder";

const NextArrow = ({ className, onClick }: CustomArrowProps) => {
const slickDisabled = className?.includes("slick-disabled");
return (
<button
className={cnb(
"hocus:outline-3 absolute right-5 top-1/2 z-50 flex h-20 w-20 items-center justify-center rounded-full border-2 border-white bg-digital-red hocus:bg-digital-red hocus:outline hocus:outline-digital-red",
{
"bg-black-40 hocus:bg-black-40 hocus:outline-0": slickDisabled,
},
)}
onClick={onClick}
aria-label="Next"
disabled={slickDisabled}
>
<ArrowLongRightIcon width={40} className="text-white" />
</button>
);
};

const PrevArrow = ({ className, onClick }: CustomArrowProps) => {
const slickDisabled = className?.includes("slick-disabled");
return (
<button
className={cnb(
"hocus:outline-3 absolute left-5 top-1/2 z-50 flex h-20 w-20 items-center justify-center rounded-full border-2 border-white bg-digital-red hocus:bg-digital-red hocus:outline hocus:outline-digital-red",
{
"bg-black-40 hocus:bg-black-40 hocus:outline-0": slickDisabled,
},
)}
onClick={onClick}
aria-label="Previous"
disabled={slickDisabled}
>
<ArrowLongLeftIcon width={40} className="text-white" />
</button>
);
};

type SlideshowProps = HTMLAttributes<HTMLDivElement> & {
children: JSX.Element | JSX.Element[];
slideshowProps?: Omit<Settings, "children">;
};

// Slide padding styles are added in the tailwind index.css file.
export const Slideshow = ({
children,
slideshowProps,
...props
}: SlideshowProps) => {
const slideShowRef = useRef<HTMLDivElement>(null);

const adjustSlideLinks = () => {
// Set tabindex attributes based on if the slides are visible or not.
const hiddenLinks = slideShowRef.current?.querySelectorAll(
".slick-slide:not(.slick-active) a",
);
if (hiddenLinks) {
[...hiddenLinks].map((link) => link.setAttribute("tabindex", "-1"));
}

const visibleLinks = slideShowRef.current?.querySelectorAll(
".slick-slide.slick-active a",
);
if (visibleLinks) {
[...visibleLinks].map((link) => link.removeAttribute("tabindex"));
}
};

useEffect(() => {
adjustSlideLinks();
}, []);

const settings: Settings = {
afterChange: () => adjustSlideLinks(),
autoplay: false,
centerMode: false,
className:
"[&_.slick-track]:flex [&_.slick-track] [&_.slick-slider]:relative [&_.slick-slide>div]:h-full [&_.slick-slide>div>div]:h-full",
dots: false,
infinite: false,
initialSlide: 0,
nextArrow: <NextArrow />,
prevArrow: <PrevArrow />,
slidesToScroll: 1,
slidesToShow: 3,
speed: 500,
responsive: [
{
breakpoint: 1200,
settings: {
slidesToShow: 2,
slidesToScroll: 1,
centerMode: false,
},
},
{
breakpoint: 768,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
centerMode: false,
},
},
],
...slideshowProps,
};
return (
<section
ref={slideShowRef}
{...props}
aria-roledescription="carousel"
className={cnb("relative", props.className)}
>
<Slider {...settings}>{children}</Slider>
</section>
);
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@
"next": "15.0.2",
"react": "19.0.0-rc-fb9a90fa48-20240614",
"react-dom": "19.0.0-rc-fb9a90fa48-20240614",
"react-slick": "^0.30.2",
"typescript-eslint": "^8.12.2"
},
"devDependencies": {
"@types/flubber": "^0.4.0",
"@types/node": "^22.8.4",
"@types/react": "^19.0.0-rc.1",
"@types/react-dom": "19.0.0-rc.1",
"@types/react-slick": "^0",
"@xpd/tailwind-3dtransforms": "^1.0.3",
"decanter": "^7.3.0",
"eslint": "^9.13.0",
Expand Down
71 changes: 71 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,15 @@ __metadata:
languageName: node
linkType: hard

"@types/react-slick@npm:^0":
version: 0.23.13
resolution: "@types/react-slick@npm:0.23.13"
dependencies:
"@types/react": "npm:*"
checksum: 10c0/35dd72dfbac234b8db019fb8af250ac3891e564d13001642353aa0b7c37f6ed1ff1f5e8fa8b034706993c4d27ac1977c031942636cfd05414ee4667d45c8aea6
languageName: node
linkType: hard

"@types/react@npm:[email protected]":
version: 19.0.0-rc.1
resolution: "types-react@npm:19.0.0-rc.1"
Expand Down Expand Up @@ -1147,6 +1156,13 @@ __metadata:
languageName: node
linkType: hard

"classnames@npm:^2.2.5":
version: 2.5.1
resolution: "classnames@npm:2.5.1"
checksum: 10c0/afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69
languageName: node
linkType: hard

"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
Expand Down Expand Up @@ -1444,6 +1460,13 @@ __metadata:
languageName: node
linkType: hard

"enquire.js@npm:^2.1.6":
version: 2.1.6
resolution: "enquire.js@npm:2.1.6"
checksum: 10c0/3ab739ed0885b5dda973de3fe610d2e3ce261a3ee77321ee4c0c876294c1c37f87f6cccead3ee1de8ead24b31f730586847f89118f083e776677f7c3cd71abaa
languageName: node
linkType: hard

"env-paths@npm:^2.2.0":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
Expand Down Expand Up @@ -2745,6 +2768,15 @@ __metadata:
languageName: node
linkType: hard

"json2mq@npm:^0.2.0":
version: 0.2.0
resolution: "json2mq@npm:0.2.0"
dependencies:
string-convert: "npm:^0.2.0"
checksum: 10c0/fc9e2f2306572522d3e61d246afdf70b56ca9ea32f4ad5924c30949867851ab59c926bd0ffc821ebb54d32f3e82e95225f3906eacdb3e54c1ad49acdadf7e0c7
languageName: node
linkType: hard

"json5@npm:^1.0.2":
version: 1.0.2
resolution: "json5@npm:1.0.2"
Expand Down Expand Up @@ -2833,6 +2865,13 @@ __metadata:
languageName: node
linkType: hard

"lodash.debounce@npm:^4.0.8":
version: 4.0.8
resolution: "lodash.debounce@npm:4.0.8"
checksum: 10c0/762998a63e095412b6099b8290903e0a8ddcb353ac6e2e0f2d7e7d03abd4275fe3c689d88960eb90b0dde4f177554d51a690f22a343932ecbc50a5d111849987
languageName: node
linkType: hard

"lodash.merge@npm:^4.6.2":
version: 4.6.2
resolution: "lodash.merge@npm:4.6.2"
Expand Down Expand Up @@ -3547,6 +3586,22 @@ __metadata:
languageName: node
linkType: hard

"react-slick@npm:^0.30.2":
version: 0.30.2
resolution: "react-slick@npm:0.30.2"
dependencies:
classnames: "npm:^2.2.5"
enquire.js: "npm:^2.1.6"
json2mq: "npm:^0.2.0"
lodash.debounce: "npm:^4.0.8"
resize-observer-polyfill: "npm:^1.5.0"
peerDependencies:
react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0
react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0
checksum: 10c0/8994ae2934aa39e889674124b68669e4e012b2a5286bbbc2a3455c2e11ebd5364ab9c56e1d268f3b7b9fe1fbc3a165b82dd84d6f234f3542385df09998c9fbb5
languageName: node
linkType: hard

"react@npm:19.0.0-rc-fb9a90fa48-20240614":
version: 19.0.0-rc-fb9a90fa48-20240614
resolution: "react@npm:19.0.0-rc-fb9a90fa48-20240614"
Expand Down Expand Up @@ -3599,6 +3654,13 @@ __metadata:
languageName: node
linkType: hard

"resize-observer-polyfill@npm:^1.5.0":
version: 1.5.1
resolution: "resize-observer-polyfill@npm:1.5.1"
checksum: 10c0/5e882475067f0b97dc07e0f37c3e335ac5bc3520d463f777cec7e894bb273eddbfecb857ae668e6fb6881fd6f6bb7148246967172139302da50fa12ea3a15d95
languageName: node
linkType: hard

"resolve-from@npm:^4.0.0":
version: 4.0.0
resolution: "resolve-from@npm:4.0.0"
Expand Down Expand Up @@ -3922,6 +3984,7 @@ __metadata:
"@types/node": "npm:^22.8.4"
"@types/react": "npm:^19.0.0-rc.1"
"@types/react-dom": "npm:19.0.0-rc.1"
"@types/react-slick": "npm:^0"
"@xpd/tailwind-3dtransforms": "npm:^1.0.3"
cnbuilder: "npm:^3.1.0"
decanter: "npm:^7.3.0"
Expand All @@ -3936,6 +3999,7 @@ __metadata:
prettier: "npm:^3.3.3"
react: "npm:19.0.0-rc-fb9a90fa48-20240614"
react-dom: "npm:19.0.0-rc-fb9a90fa48-20240614"
react-slick: "npm:^0.30.2"
tailwindcss: "npm:^3.4.1"
typescript: "npm:^5"
typescript-eslint: "npm:^8.12.2"
Expand Down Expand Up @@ -3972,6 +4036,13 @@ __metadata:
languageName: node
linkType: hard

"string-convert@npm:^0.2.0":
version: 0.2.1
resolution: "string-convert@npm:0.2.1"
checksum: 10c0/00673ed8a3106137395436537ace7d3672c91a3290da73466055daa0134331dc84bc58c54ba2d2ea40711adc5744426d3c8239dbfc30290438fa3e9ff65db528
languageName: node
linkType: hard

"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0":
version: 4.2.3
resolution: "string-width@npm:4.2.3"
Expand Down

0 comments on commit f0d5dee

Please sign in to comment.