Skip to content

Commit

Permalink
merge: useDragIndexCarousel 인터페이스 useDragCarousel과 통합 (#2)
Browse files Browse the repository at this point in the history
merge: useDragIndexCarousel 인터페이스 useDragCarousel과 통합 (#2)
  • Loading branch information
d0422 committed May 8, 2024
2 parents fbf2e80 + 9dbb81d commit 0da4f0f
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 101 deletions.
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,25 @@ you can set `startIndex` and `minMove`.

`minMove` : Minimum movement required for the index to shift.

### useDragCarouselIndex
#### parameters

`dataLength`, `startIndex`, `minMove`

`startIndex` : specifies the start index.

`minMove`: determines how many slides you have to slide over.

#### return values

This hook should be called within the carousel provider provided by the useDragIndexCarousel component.
carousel provider renders children elements. It already has the `display: flex` property included.
useDragCarousel returns `CarouselWrapper`, `next`, `prev`, `index`, `ref`, `isEnd`, `isStart`

you can get current index in carousel children.
`CarouselWrapper`: renders children elements. It already contains `display:flex` property.

`ref`: you need to assign a ref to the Carousel Wrapper.

`next`: increase index

`prev`: decrease index

### useCarousel

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rapiders/react-hooks",
"version": "1.0.1",
"version": "1.1.1",
"description": "react hooks for fast development",
"main": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
Expand Down
2 changes: 0 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import useInput from './useInput/useInput';
import useAnimation from './useAnimation/useAnimation';
import useFocusAnimation from './useFocusAnimation/useFocusAnimation';
import { useDragCarouselIndex } from './useDragIndexCarousel/useDragIndexCarousel';
import useDragIndexCarousel from './useDragIndexCarousel/useDragIndexCarousel';
import useCarousel from './useCarousel/useCarousel';
import useScrollRatio from './useScrollRatio';
Expand All @@ -10,7 +9,6 @@ export {
useInput,
useAnimation,
useFocusAnimation,
useDragCarouselIndex,
useDragIndexCarousel,
useCarousel,
useScrollRatio,
Expand Down
7 changes: 4 additions & 3 deletions src/stories/useDragIndexCarousel/DragCarousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export default function DragCarousel() {
'https://image.xportsnews.com/contents/images/upload/article/2023/0825/mb_1692925582785123.jpg',
'https://photo.newsen.com/news_photo/2022/08/19/202208190935355510_1.jpg',
];
const DragCarousel = useDragIndexCarousel();
const { CarouselWrapper, ref } = useDragIndexCarousel(images.length);
return (
<DragCarousel
<CarouselWrapper
ref={ref}
style={{
width: 500,
height: 500,
Expand Down Expand Up @@ -38,6 +39,6 @@ export default function DragCarousel() {
/>
</div>
))}
</DragCarousel>
</CarouselWrapper>
);
}
18 changes: 16 additions & 2 deletions src/useDragIndexCarousel/useDragIndexCarousel.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { renderHook, act } from '@testing-library/react';
import useDragIndexCarousel from './useDragIndexCarousel';
import { _useDragIndexCarousel } from './useDragIndexCarousel';

describe('_useDragIndexCarousel', () => {
describe('useDragIndexCarousel', () => {
it('초기 상태 테스트 index로 시작할 수 있다.', () => {
const { result } = renderHook(() => _useDragIndexCarousel(3));

expect(result.current.index).toBe(0);
expect(result.current.style.transform).toBe('translateX(0px)');
});

it('index를 next, prev로 조작할 수 있다.', () => {
const { result } = renderHook(() => _useDragIndexCarousel(2));

expect(result.current.index).toBe(0);
act(() => result.current.next());
expect(result.current.index).toBe(1);
act(() => result.current.prev());
expect(result.current.index).toBe(0);
expect(result.current.isStart).toBe(true);
act(() => result.current.next());
act(() => result.current.next());
expect(result.current.isEnd).toBe(true);
});

it('모바일 터치이벤트 테스트: 절대값으로 minMove보다 많이, 오른쪽으로 슬라이드하면 index를 증가시킬 수 있다.', () => {
Expand Down
175 changes: 86 additions & 89 deletions src/useDragIndexCarousel/useDragIndexCarousel.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import React, { CSSProperties, ReactElement, cloneElement } from 'react';
import React, {
CSSProperties,
ForwardedRef,
ReactElement,
cloneElement,
forwardRef,
useEffect,
} from 'react';
import { useRef, useState, Children, ReactNode } from 'react';
import { createContext, useContext } from 'react';

export const CarouselIndex = createContext<number | null>(null);

export const useDragCarouselIndex = () => {
const result = useContext(CarouselIndex);
if (result) return result;
throw new Error(
'Check the provider: You have to use CarouselIndex in IndexCarouselProvider children'
);
};

export function _useDragIndexCarousel(
pageLimit: number,
Expand Down Expand Up @@ -62,104 +58,105 @@ export function _useDragIndexCarousel(
setTouchStartX(0);
};

const style = {
display: 'flex',
transform: `translateX(${-index * getSliderWidth() + transX}px)`,
transitionDuration: '300ms',
transitionTimingFunction: 'ease-out',
const getNext = (index: number) => {
if (index < pageLimit) return index + 1;
return index;
};

const getPrev = (index: number) => {
if (index > 0) return index - 1;
return index;
};

const next = () => setIndex((prev) => getNext(prev));
const prev = () => setIndex((prev) => getPrev(prev));

useEffect(() => {
if (ref.current) {
ref.current.style.display = 'flex';
ref.current.style.transform = `translateX(${-index * getSliderWidth() + transX}px)`;
ref.current.style.transitionDuration = '300ms';
ref.current.style.transitionTimingFunction = 'ease-out';
}
}, [transX]);

return {
style,
ref,
isEnd: index === pageLimit,
isStart: index === 0,
handleTouchStart,
handleTouchMove,
handleMoveEnd,
handleScrollStart,
handleScrollMove,
next,
prev,
index,
};
}

function DragIndexCarousel({
startIndex,
minMove,
style,
className,
children,
}: {
startIndex: number;
minMove?: number;
style?: CSSProperties;
className?: string;
children: ReactNode;
}) {
const pageLimit = Children.count(children) - 1;
const CarouselWrapper = forwardRef(
(
{
children,
style,
className,
}: { children: ReactNode; style?: React.CSSProperties; className?: string },
ref: ForwardedRef<HTMLDivElement>
) => (
<div style={{ overflow: 'hidden', ...style }} className={className}>
<div ref={ref} style={{ height: '100%', width: '100%' }}>
{Children.map(children, (child: ReactElement) =>
cloneElement(child, {
style: { ...child.props.style, flexShrink: 0 },
})
)}
</div>
</div>
)
);

export default function useDragIndexCarousel(
dataLength: number,
startIndex = 0,
minMove = 60
) {
const {
index,
ref,
style: dragStyle,
handleTouchStart,
handleTouchMove,
handleMoveEnd,
handleScrollStart,
handleScrollMove,
} = _useDragIndexCarousel(pageLimit, minMove, startIndex);
next,
prev,
isEnd,
isStart,
} = _useDragIndexCarousel(dataLength - 1, minMove, startIndex);

return (
<div
style={{
overflow: 'hidden',
...style,
}}
className={className}
draggable={false}
>
<div
ref={ref}
style={{ ...dragStyle, height: '100%', width: '100%' }}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleMoveEnd}
onMouseDown={handleScrollStart}
onMouseMove={handleScrollMove}
onMouseUp={handleMoveEnd}
onMouseLeave={handleMoveEnd}
>
<CarouselIndex.Provider value={index}>
{children}
</CarouselIndex.Provider>
</div>
</div>
);
}

export default function useDragIndexCarousel(startIndex = 0, minMove = 60) {
const component = ({
children,
className,
style,
}: {
children: ReactNode;
style?: CSSProperties;
className?: string;
}) => {
return (
<DragIndexCarousel
startIndex={startIndex}
minMove={minMove}
style={style}
className={className}
>
{Children.map(children, (child: ReactElement) =>
cloneElement(child, {
style: { ...child.props.style, flexShrink: 0 },
draggable: false,
})
)}
</DragIndexCarousel>
);
};
useEffect(() => {
if (ref.current) {
ref.current.addEventListener('touchstart', handleTouchStart as any);
ref.current.addEventListener('touchmove', handleTouchMove as any);
ref.current.addEventListener('touchend', handleMoveEnd as any);
ref.current.addEventListener('mousedown', handleScrollStart as any);
ref.current.addEventListener('mouseleave', handleMoveEnd as any);
ref.current.addEventListener('mousemove', handleScrollMove as any);
ref.current.addEventListener('mouseup', handleMoveEnd as any);
}
return () => {
if (ref.current) {
ref.current.removeEventListener('touchstart', handleTouchStart as any);
ref.current.removeEventListener('touchmove', handleTouchMove as any);
ref.current.removeEventListener('touchend', handleMoveEnd as any);
ref.current.removeEventListener('mousedown', handleScrollStart as any);
ref.current.removeEventListener('mouseleave', handleMoveEnd as any);
ref.current.removeEventListener('mousemove', handleScrollMove as any);
ref.current.removeEventListener('mouseup', handleMoveEnd as any);
}
};
});

return component;
return { CarouselWrapper, index, ref, next, prev, isStart, isEnd };
}

0 comments on commit 0da4f0f

Please sign in to comment.