Skip to content

Commit

Permalink
split the component further
Browse files Browse the repository at this point in the history
  • Loading branch information
Fercas123 committed Feb 5, 2025
1 parent dd86a8d commit 3cfd513
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 204 deletions.
59 changes: 28 additions & 31 deletions packages/lab/src/carousel/Carousel.css
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
.saltCarousel {
position: relative;
overflow: hidden;
display: grid;
grid-template-columns: min-content auto min-content;
grid-template-areas: "prev-button slider next-button" "dots dots dots";
}
/*.saltCarousel {*/
/* position: relative;*/
/* overflow: hidden;*/
/* display: grid;*/
/* grid-template-columns: min-content auto min-content;*/
/* grid-template-areas: "prev-button slider next-button" "dots dots dots";*/
/*}*/

.saltCarousel.saltCarousel-compact {
grid-template-areas: "slider slider slider" "prev-button dots next-button";
}
/*.saltCarousel.saltCarousel-compact {*/
/* grid-template-areas: "slider slider slider" "prev-button dots next-button";*/
/*}*/

.saltCarousel-scroll {
display: flex;
scroll-snap-type: x mandatory;
overflow-x: scroll;
scroll-behavior: smooth;
}
/*.saltCarousel-scroll {*/

.saltCarousel-prev-button {
grid-area: prev-button;
height: 100%;
}
/*}*/

.saltCarousel-next-button {
grid-area: next-button;
height: 100%;
}
/*.saltCarousel-prev-button {*/
/* grid-area: prev-button;*/
/* height: 100%;*/
/*}*/

.saltCarousel-slider {
grid-area: slider;
}
/*.saltCarousel-next-button {*/
/* grid-area: next-button;*/
/* height: 100%;*/
/*}*/

.saltCarousel-dots {
grid-area: dots;
justify-self: center;
}
/*.saltCarousel-slider {*/
/* grid-area: slider;*/
/*}*/

/*.saltCarousel-dots {*/
/* grid-area: dots;*/
/* justify-self: center;*/
/*}*/
149 changes: 9 additions & 140 deletions packages/lab/src/carousel/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,9 @@
import {
Button,
RadioButton,
RadioButtonGroup,
makePrefixer,
useIcon,
} from "@salt-ds/core";
import { makePrefixer } from "@salt-ds/core";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import { clsx } from "clsx";
import {
type ChangeEventHandler,
Children,
type HTMLAttributes,
type ReactElement,
forwardRef,
useEffect,
useRef,
useState,
} from "react";
import { CarouselContext } from "./CarouselContext";
import type { CarouselSlideProps } from "./CarouselSlide";
import { forwardRef, type HTMLAttributes } from "react";
import { CarouselProvider } from "./CarouselContext";

import carouselCss from "./Carousel.css";

Expand All @@ -31,40 +15,17 @@ export interface CarouselProps extends HTMLAttributes<HTMLDivElement> {
* Optional, default 0.
**/
activeSlideIndex?: number;
/**
* The animation when the slides are shown.
* Optional. Defaults to `slide`
**/
animation?: "slide" | "fade";
// TODO: should slide have an active prop, that sets it as active in the context?
/**
* If this props is passed it will set the aria-label with value to the carousel container.
* Optional. Defaults to undefined
*/
carouselDescription?: string;
/**
* Collection of slides to render
* Component must implement CarouselSlideProps. Mandatory.
*/
children: Array<ReactElement<CarouselSlideProps>>;
/**
* This prop will enable compact / reduced width mode.
* The navigation buttons would be part of indicators
* Optional. Defaults to false
**/
compact?: boolean;
}

export const Carousel = forwardRef<HTMLDivElement, CarouselProps>(
function Carousel(
{
activeSlideIndex = 0,
animation = "slide",
carouselDescription,
children,
className,
compact,
...rest
},
{ activeSlideIndex = 0, carouselDescription, children, className, ...rest },
ref,
) {
const targetWindow = useWindow();
Expand All @@ -73,112 +34,20 @@ export const Carousel = forwardRef<HTMLDivElement, CarouselProps>(
css: carouselCss,
window: targetWindow,
});
const { NextIcon, PreviousIcon } = useIcon();

const containerRef = useRef<HTMLDivElement>(null);
const slidesCount = Children.count(children);
const [activeSlide, setActiveSlide] = useState(activeSlideIndex);
const [slides, setSlides] = useState<string[]>([]);

const registerSlide = (slideId: string) => {
setSlides((prev) => [...prev, slideId]);
};

const scrollToSlide = (index: number) => {
if (containerRef.current) {
const slideW = containerRef.current.offsetWidth;
containerRef.current.scrollTo({
left: index * slideW,
behavior: "smooth",
});
setActiveSlide(index);
}
};
const nextSlide = () => scrollToSlide(activeSlide + 1);
const prevSlide = () => scrollToSlide(activeSlide - 1);
const goToSlide = (index: number) => scrollToSlide(index);

// TODO: implement on scroll
const handleRadioChange: ChangeEventHandler<HTMLInputElement> = ({
target: { value },
}) => {
goToSlide(Number(value));
};

useEffect(() => {
if (process.env.NODE_ENV !== "production") {
if (slidesCount < 1) {
console.warn(
"Carousel component requires more than one children to render. At least two elements should be provided.",
);
}
}
}, [slidesCount]);

return (
<CarouselContext.Provider
value={{
activeSlide,
nextSlide,
prevSlide,
goToSlide,
slides,
registerSlide,
}}
>
<CarouselProvider>
<div
aria-label={carouselDescription}
aria-roledescription="carousel"
role="region"
ref={ref}
className={clsx(
withBaseName(),
compact && withBaseName("compact"),
className,
)}
className={clsx(withBaseName(), className)}
{...rest}
>
<Button
appearance="transparent"
sentiment="neutral"
className={withBaseName("prev-button")}
onClick={prevSlide}
disabled={activeSlide === 0}
>
<PreviousIcon size={2} />
</Button>
<div
ref={containerRef}
className={withBaseName("scroll")}
aria-live="polite"
>
{children}
</div>
<Button
appearance="transparent"
sentiment="neutral"
className={withBaseName("next-button")}
onClick={nextSlide}
disabled={activeSlide === slidesCount - 1}
>
<NextIcon size={2} />
</Button>
<div className={withBaseName("dots")}>
<RadioButtonGroup
aria-label="Carousel buttons"
onChange={handleRadioChange}
value={`${activeSlide}`}
direction={"horizontal"}
>
{Array.from({ length: slidesCount }, (_, index) => ({
value: `${index}`,
})).map((radio) => (
<RadioButton {...radio} key={radio.value} />
))}
</RadioButtonGroup>
</div>
{children}
</div>
</CarouselContext.Provider>
</CarouselProvider>
);
},
);
56 changes: 44 additions & 12 deletions packages/lab/src/carousel/CarouselContext.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,60 @@
import { createContext } from "@salt-ds/core";
import { type SyntheticEvent, useContext } from "react";
import {
type ReactNode,
type SyntheticEvent,
useContext,
useState,
} from "react";

export interface CarouselContextValue {
activeSlide: number;

nextSlide: (event: SyntheticEvent) => void;
prevSlide: (event: SyntheticEvent) => void;
goToSlide: (index: number) => void;
slides: string[];
registerSlide: (slideId: string) => void;
}

export const CarouselContext = createContext<CarouselContextValue>(
export const CarouselContext = createContext<CarouselContextValue | null>(
"CarouselContext",
{
activeSlide: 0,
nextSlide: () => undefined,
prevSlide: () => undefined,
goToSlide: () => undefined,
slides: [],
registerSlide: () => undefined,
},
null,
);

export function useCarousel() {
return useContext(CarouselContext);
const context = useContext(CarouselContext);
if (!context) {
throw new Error("useCarousel must be used within carousel provider");
}
return context;
}

export function CarouselProvider({ children }: { children: ReactNode }) {
// TODO: check active slide to initialy set the carousel prop
const [activeSlide, setActiveSlide] = useState(0);
const [slides, setSlides] = useState<string[]>([]);

const registerSlide = (slideId: string) => {
setSlides((prev) => [...prev, slideId]);
};
const scrollToSlide = (index: number) => {
setActiveSlide(index);
};
const nextSlide = () => scrollToSlide(activeSlide + 1);
const prevSlide = () => scrollToSlide(activeSlide - 1);
const goToSlide = (index: number) => scrollToSlide(index);

return (
<CarouselContext.Provider
value={{
activeSlide,
nextSlide,
prevSlide,
goToSlide,
slides,
registerSlide,
}}
>
{children}
</CarouselContext.Provider>
);
}
Empty file.
Loading

0 comments on commit 3cfd513

Please sign in to comment.