11'use client' ;
22
3+ import useInfiniteCarousel from '@hooks/useInfiniteCarousel' ;
34import Image from 'next/image' ;
45import { useRouter } from 'next/navigation' ;
5- import React , { useState , useEffect , useRef , useCallback } from 'react' ;
6+ import React from 'react' ;
67
7- import BANNER_DATA , { BannerItem } from './bannerData' ;
8+ import BANNER_DATA from './bannerData' ;
89import * as styles from './mainBanner.css' ;
910
10- type SlideItem = BannerItem & { uniqueKey : string } ;
11-
1211const MainBanner = ( ) => {
1312 const router = useRouter ( ) ;
1413
15- const slides = [
16- { ...BANNER_DATA [ BANNER_DATA . length - 1 ] , uniqueKey : 'clone-last' } ,
17- ...BANNER_DATA . map ( ( item ) => ( { ...item , uniqueKey : `real-${ item . id } ` } ) ) ,
18- { ...BANNER_DATA [ 0 ] , uniqueKey : 'clone-first' } ,
19- ] ;
20-
21- const totalOriginalSlides = BANNER_DATA . length ;
22-
23- const [ currentIndex , setCurrentIndex ] = useState ( 1 ) ;
24- const [ isAnimate , setIsAnimate ] = useState ( true ) ;
25- const [ isDragging , setIsDragging ] = useState ( false ) ;
26- const [ startX , setStartX ] = useState ( 0 ) ;
27- const [ startY , setStartY ] = useState ( 0 ) ;
28- const [ currentX , setCurrentX ] = useState ( 0 ) ;
29-
30- const timerRef = useRef < NodeJS . Timeout | null > ( null ) ;
31-
32- const moveNext = useCallback ( ( ) => {
33- setIsAnimate ( true ) ;
34- setCurrentIndex ( ( prev ) => prev + 1 ) ;
35- } , [ ] ) ;
36-
37- const movePrev = useCallback ( ( ) => {
38- setIsAnimate ( true ) ;
39- setCurrentIndex ( ( prev ) => prev - 1 ) ;
40- } , [ ] ) ;
41-
42- const stopAutoSlide = useCallback ( ( ) => {
43- if ( timerRef . current ) {
44- clearInterval ( timerRef . current ) ;
45- }
46- } , [ ] ) ;
47-
48- const startAutoSlide = useCallback ( ( ) => {
49- stopAutoSlide ( ) ;
50- timerRef . current = setInterval ( ( ) => {
51- moveNext ( ) ;
52- } , 4000 ) ;
53- } , [ moveNext , stopAutoSlide ] ) ;
54-
55- useEffect ( ( ) => {
56- startAutoSlide ( ) ;
57- return ( ) => stopAutoSlide ( ) ;
58- } , [ startAutoSlide , stopAutoSlide , currentIndex ] ) ;
59-
60- const handleTransitionEnd = ( ) => {
61- if ( currentIndex === 0 ) {
62- setIsAnimate ( false ) ;
63- setCurrentIndex ( totalOriginalSlides ) ;
64- } else if ( currentIndex === slides . length - 1 ) {
65- setIsAnimate ( false ) ;
66- setCurrentIndex ( 1 ) ;
67- }
68- } ;
69-
70- const handleMouseDown = ( e : React . MouseEvent ) => {
71- stopAutoSlide ( ) ;
72- setIsDragging ( true ) ;
73- setStartX ( e . clientX ) ;
74- setCurrentX ( e . clientX ) ;
75- setIsAnimate ( false ) ;
76- } ;
77-
78- const handleMouseMove = ( e : React . MouseEvent ) => {
79- if ( ! isDragging ) return ;
80- setCurrentX ( e . clientX ) ;
81- } ;
82-
83- const handleMouseUp = ( ) => {
84- if ( ! isDragging ) return ;
85- setIsDragging ( false ) ;
86- startAutoSlide ( ) ;
87-
88- const diff = currentX - startX ;
89- const threshold = 50 ;
90-
91- if ( diff < - threshold ) {
92- moveNext ( ) ;
93- } else if ( diff > threshold ) {
94- movePrev ( ) ;
95- } else {
96- setIsAnimate ( true ) ;
97- }
98- } ;
99-
100- const handleMouseLeave = ( ) => {
101- if ( isDragging ) {
102- setIsDragging ( false ) ;
103- setIsAnimate ( true ) ;
104- startAutoSlide ( ) ;
105- }
106- } ;
107-
108- const handleBannerClick = ( banner : SlideItem ) => {
109- if ( Math . abs ( currentX - startX ) > 5 ) return ;
14+ const {
15+ slides,
16+ currentIndex,
17+ isAnimate,
18+ dragOffset,
19+ displayIndex,
20+ totalOriginalSlides,
21+ isSwiped,
22+ handlers,
23+ } = useInfiniteCarousel ( {
24+ data : BANNER_DATA ,
25+ autoPlayInterval : 4000 ,
26+ } ) ;
27+
28+ const handleBannerClick = ( banner : ( typeof BANNER_DATA ) [ 0 ] ) => {
29+ if ( isSwiped ) return ;
11030
11131 if ( banner . type === 'internal' ) {
11232 router . push ( banner . link ) ;
11333 } else {
11434 window . open ( banner . link , '_blank' ) ;
11535 }
11636 } ;
117-
118- const handleKeyDown = ( e : React . KeyboardEvent < HTMLDivElement > , banner : SlideItem ) => {
119- if ( e . key === 'Enter' || e . key === ' ' ) {
120- handleBannerClick ( banner ) ;
121- }
122- } ;
123-
124- const handleTouchStart = ( e : React . TouchEvent ) => {
125- stopAutoSlide ( ) ;
126- setIsDragging ( true ) ;
127- const touchX = e . touches [ 0 ] . clientX ;
128- const touchY = e . touches [ 0 ] . clientY ;
129- setStartX ( touchX ) ;
130- setStartY ( touchY ) ;
131- setCurrentX ( touchX ) ;
132- setIsAnimate ( false ) ;
133- } ;
134-
135- const handleTouchMove = ( e : React . TouchEvent ) => {
136- if ( ! isDragging ) return ;
137- const touchX = e . touches [ 0 ] . clientX ;
138- const touchY = e . touches [ 0 ] . clientY ;
139-
140- const diffX = Math . abs ( touchX - startX ) ;
141- const diffY = Math . abs ( touchY - startY ) ;
142-
143- // 스크롤 의도 파악
144- if ( diffY > diffX && diffY > 10 ) {
145- setIsDragging ( false ) ;
146- return ;
147- }
148-
149- setCurrentX ( touchX ) ;
150- } ;
151-
152- const handleTouchEnd = ( ) => {
153- if ( ! isDragging ) {
154- startAutoSlide ( ) ;
155- return ;
156- }
157-
158- setIsDragging ( false ) ;
159- startAutoSlide ( ) ;
160-
161- const diff = currentX - startX ;
162- const threshold = 50 ;
163-
164- if ( diff < - threshold ) {
165- moveNext ( ) ;
166- } else if ( diff > threshold ) {
167- movePrev ( ) ;
168- } else {
169- setIsAnimate ( true ) ;
170- }
171- } ;
172-
173- let displayIndex = currentIndex ;
174- if ( currentIndex === 0 ) displayIndex = totalOriginalSlides ;
175- else if ( currentIndex === slides . length - 1 ) displayIndex = 1 ;
176-
177- const dragOffset = isDragging ? currentX - startX : 0 ;
178-
17937 return (
18038 < div className = { styles . container } >
18139 { /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */ }
@@ -186,22 +44,15 @@ const MainBanner = () => {
18644 transition : isAnimate ? 'transform 0.5s ease-in-out' : 'none' ,
18745 touchAction : 'pan-y' ,
18846 } }
189- onTransitionEnd = { handleTransitionEnd }
190- onMouseDown = { handleMouseDown }
191- onMouseMove = { handleMouseMove }
192- onMouseUp = { handleMouseUp }
193- onMouseLeave = { handleMouseLeave }
194- onTouchStart = { handleTouchStart }
195- onTouchMove = { handleTouchMove }
196- onTouchEnd = { handleTouchEnd } >
47+ { ...handlers } >
19748 { slides . map ( ( banner ) => (
19849 < div
19950 key = { banner . uniqueKey }
20051 className = { styles . slideItem }
20152 onClick = { ( ) => handleBannerClick ( banner ) }
20253 role = "button"
20354 tabIndex = { 0 }
204- onKeyDown = { ( e ) => handleKeyDown ( e , banner ) }
55+ onKeyDown = { ( e ) => ( e . key === 'Enter' || e . key === ' ' ) && handleBannerClick ( banner ) }
20556 aria-label = { `${ banner . alt } 배너로 이동` }
20657 onDragStart = { ( e ) => e . preventDefault ( ) } >
20758 < Image
0 commit comments