-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 배너 기본 컴포넌트 구현 * refactor: 이미지 컨테이너 컴포넌트 분리 * feat: 스토리북 추가 * feat: 슬라이더 버튼 추가 * style: 홈페이지 마진
- Loading branch information
Showing
7 changed files
with
168 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const BANNER_IMAGES = ["/당근.svg", "/번개장터.svg", "/중고나라.svg"]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
import Banner from "@ui/Banner"; | ||
import Search from "@ui/Search"; | ||
|
||
export default function Home() { | ||
return ( | ||
<main className="flex min-h-screen flex-col items-center justify-between p-12 md:p-24 "> | ||
<Search className="w-full" /> | ||
<main className="flex min-h-screen flex-col items-center justify-between pb-28 pt-14"> | ||
<Banner className="mb-8" /> | ||
<Search className="mt-6 w-full" /> | ||
</main> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { BANNER_IMAGES } from "@lib/constants/ banner"; | ||
import Image from "next/image"; | ||
import { HTMLAttributes } from "react"; | ||
|
||
function ImageContainer({ | ||
className = "", | ||
...props | ||
}: HTMLAttributes<HTMLDivElement>) { | ||
return ( | ||
<div className={`flex shrink-0 ${className}`} {...props}> | ||
{BANNER_IMAGES.map((image) => ( | ||
<Image | ||
key={image} | ||
src={image} | ||
alt="banner" | ||
width={360} | ||
height={256} | ||
className="h-full w-screen bg-stone-300 object-contain" | ||
/> | ||
))} | ||
</div> | ||
); | ||
} | ||
|
||
export default ImageContainer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
"use client"; | ||
|
||
import { BANNER_IMAGES } from "@lib/constants/ banner"; | ||
import { Dispatch, HTMLAttributes, MouseEvent, SetStateAction } from "react"; | ||
|
||
interface SliderButtonProps extends HTMLAttributes<HTMLDivElement> { | ||
curImage: number; | ||
setCurImage: Dispatch<SetStateAction<number>>; | ||
intervalRef: React.MutableRefObject<NodeJS.Timeout | null>; | ||
} | ||
|
||
function SliderButton({ | ||
curImage, | ||
setCurImage, | ||
intervalRef, | ||
className, | ||
...props | ||
}: SliderButtonProps) { | ||
const handleClick = (idx: number) => (e: MouseEvent<HTMLButtonElement>) => { | ||
setCurImage(idx); | ||
|
||
clearInterval(intervalRef.current!); | ||
intervalRef.current = setInterval(() => { | ||
setCurImage((prev) => prev + 1); | ||
}, 3000); | ||
}; | ||
|
||
const buttonDefaultStyle = | ||
"w-3 h-3 rounded-full bg-stone-400 hover:bg-stone-300 transition-all duration-300 origin-center"; | ||
const buttonActiveStyle = (idx: number) => | ||
idx === curImage % BANNER_IMAGES.length ? "cs:bg-stone-200 scale-150" : ""; | ||
|
||
return ( | ||
<div className={`flex justify-center gap-3 p-4 ${className}`} {...props}> | ||
{BANNER_IMAGES.map((image, idx) => ( | ||
<button | ||
key={image} | ||
className={`${buttonDefaultStyle} ${buttonActiveStyle(idx)}`} | ||
onClick={handleClick(idx)} | ||
/> | ||
))} | ||
</div> | ||
); | ||
} | ||
|
||
export default SliderButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
"use client"; | ||
|
||
import { BANNER_IMAGES } from "@lib/constants/ banner"; | ||
import ImageContainer from "@ui/Banner/ImageContainer"; | ||
import SliderButton from "@ui/Banner/SliderButton"; | ||
import { HTMLAttributes, useEffect, useRef, useState } from "react"; | ||
|
||
function Banner({ className = "", ...props }: HTMLAttributes<HTMLDivElement>) { | ||
const [curImage, setCurImage] = useState(0); | ||
const [isReturning, setIsReturning] = useState(false); | ||
const intervalRef = useRef<NodeJS.Timeout | null>(null); | ||
|
||
useEffect(() => { | ||
intervalRef.current = setInterval(() => { | ||
setCurImage((prev) => prev + 1); | ||
}, 3000); | ||
|
||
return () => clearInterval(intervalRef.current!); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (curImage >= BANNER_IMAGES.length) { | ||
setTimeout(() => { | ||
setIsReturning(true); | ||
setCurImage(0); | ||
|
||
setTimeout(() => { | ||
setIsReturning(false); | ||
}, 100); | ||
}, 500); | ||
} | ||
}, [curImage]); | ||
|
||
const imageTransitionStyle = isReturning | ||
? "" | ||
: "transition-transform duration-500"; | ||
|
||
const imageTranslateStyle = [ | ||
"", | ||
"-translate-x-[100vw]", | ||
"-translate-x-[200vw]", | ||
"-translate-x-[300vw]", | ||
]; | ||
|
||
return ( | ||
<div | ||
className={`relative flex h-64 w-full overflow-hidden ${className}`} | ||
{...props}> | ||
<ImageContainer | ||
className={`${imageTransitionStyle} ${imageTranslateStyle[curImage]}`} | ||
/> | ||
<ImageContainer | ||
className={`${imageTransitionStyle} ${imageTranslateStyle[curImage]}`} | ||
/> | ||
<SliderButton | ||
curImage={curImage} | ||
setCurImage={setCurImage} | ||
intervalRef={intervalRef} | ||
className="absolute bottom-0 left-0 w-full" | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
export default Banner; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import Banner from "@ui/Banner"; | ||
|
||
const meta = { | ||
title: "ui/Banner", | ||
component: Banner, | ||
parameters: { | ||
layout: "centered", | ||
}, | ||
tags: ["autodocs"], | ||
} satisfies Meta<typeof Banner>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const HomeBanner: Story = {}; |