diff --git a/README.md b/README.md index 16ace54..dd02692 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,23 @@ const Parent = () => {
+## Carousel + +캐러셀 컴포넌트입니다. + +### Example + +```jsx +const carouselList = [0, 1, 2].map((index) => ({ + id: index, + children:
{index}
, +})); + +; +``` + +
+ ## **Checkbox** 체크박스 컴포넌트입니다. diff --git a/src/Carousel/Carousel.stories.tsx b/src/Carousel/Carousel.stories.tsx new file mode 100644 index 0000000..960c3ba --- /dev/null +++ b/src/Carousel/Carousel.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import Carousel from './Carousel'; + +const meta: Meta = { + title: 'Carousel', + component: Carousel, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + carouselList: [ + { + id: 0, + children:
1
, + }, + { + id: 1, + children:
2
, + }, + { + id: 2, + children:
3
, + }, + ], + }, +}; diff --git a/src/Carousel/Carousel.tsx b/src/Carousel/Carousel.tsx new file mode 100644 index 0000000..7098bef --- /dev/null +++ b/src/Carousel/Carousel.tsx @@ -0,0 +1,65 @@ +import type { ReactNode } from 'react'; +import { useEffect, useState } from 'react'; +import styled from 'styled-components'; + +export interface CarouselProps { + carouselList: { + id: number; + children: ReactNode; + }[]; +} + +const Carousel = ({ carouselList }: CarouselProps) => { + const extendedCarouselList = [...carouselList, carouselList[0]]; + const [currentIndex, setCurrentIndex] = useState(0); + + const CAROUSEL_WIDTH = window.innerWidth; + + const showNextSlide = () => { + setCurrentIndex((prev) => (prev === carouselList.length ? 0 : prev + 1)); + }; + + useEffect(() => { + const timer = setInterval(showNextSlide, 2000); + + return () => clearInterval(timer); + }, [currentIndex]); + + return ( + + + {extendedCarouselList.map(({ id, children }, index) => ( + + {children} + + ))} + + + ); +}; + +export default Carousel; + +const CarouselContainer = styled.div` + display: flex; + width: 100%; + border: 1px solid ${({ theme }) => theme.colors.gray2}; + border-radius: 10px; + overflow: hidden; +`; + +const CarouselWrapper = styled.ul` + display: flex; +`; + +const CarouselItem = styled.li` + height: fit-content; +`; diff --git a/src/Carousel/index.ts b/src/Carousel/index.ts new file mode 100644 index 0000000..5c6de84 --- /dev/null +++ b/src/Carousel/index.ts @@ -0,0 +1,5 @@ +import Carousel from './Carousel'; + +export type { CarouselProps } from './Carousel'; + +export default Carousel; diff --git a/src/index.ts b/src/index.ts index 6c4f2a7..4696b9b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,12 +7,13 @@ export { default as Badge } from './Badge'; export { default as BottomSheet } from './BottomSheet'; export * from './BottomSheet'; export { default as Button } from './Button'; +export { default as Carousel } from './Carousel'; export { default as Checkbox } from './Checkbox'; export { default as Divider } from './Divider'; export { default as Heading } from './Heading'; +export { default as Input } from './Input'; export { default as Link } from './Link'; +export { default as Skeleton } from './Skeleton'; export { default as Spacing } from './Spacing'; export { default as Text } from './Text'; export { default as Textarea } from './Textarea'; -export { default as Input } from './Input'; -export { default as Skeleton } from './Skeleton'; diff --git a/src/types.ts b/src/types.ts index baee39d..2c66fc8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import type { ComponentPropsWithRef, ComponentPropsWithoutRef, ElementType } from 'react'; +import type { ComponentPropsWithRef, ComponentPropsWithoutRef, ElementType, ReactNode } from 'react'; export type Sizes = 'xs' | 'sm' | 'md' | 'lg' | 'xl';