parent
b73820cd26
commit
f43229d96a
4 changed files with 151 additions and 1 deletions
@ -0,0 +1,121 @@ |
|||||||
|
import React, { |
||||||
|
FunctionComponent, |
||||||
|
MouseEvent, |
||||||
|
useState, |
||||||
|
useRef, |
||||||
|
useCallback, |
||||||
|
} from 'react'; |
||||||
|
import { Item, SlideDirection } from '../../types/carousel'; |
||||||
|
import styles from '../../styles/slider/styles.module.css'; |
||||||
|
|
||||||
|
export const Slider: FunctionComponent<SliderProps> = ({ |
||||||
|
children, |
||||||
|
className, |
||||||
|
}: SliderProps) => { |
||||||
|
const slider = useRef<HTMLDivElement>(null); |
||||||
|
const [isDown, setIsDown] = useState(false); |
||||||
|
const [position, setPosition] = useState({ |
||||||
|
startX: 0, |
||||||
|
scrollLeft: 0, |
||||||
|
}); |
||||||
|
|
||||||
|
const showArrows = (): Arrows => { |
||||||
|
const sliderElement = slider.current; |
||||||
|
return { |
||||||
|
left: !!sliderElement && sliderElement.scrollLeft > 0, |
||||||
|
right: |
||||||
|
!!sliderElement && |
||||||
|
sliderElement.scrollWidth > |
||||||
|
sliderElement.scrollLeft + sliderElement.offsetWidth, |
||||||
|
}; |
||||||
|
}; |
||||||
|
const [showArrow, setShowArrow] = useState<Arrows>(showArrows()); |
||||||
|
|
||||||
|
const ref = useCallback( |
||||||
|
(node) => { |
||||||
|
if (node !== null) { |
||||||
|
Object.defineProperty(slider, 'current', { value: node }); |
||||||
|
setShowArrow(showArrows()); |
||||||
|
} |
||||||
|
}, |
||||||
|
[slider], |
||||||
|
); |
||||||
|
|
||||||
|
const mouseDown = (e: MouseEvent) => { |
||||||
|
setIsDown(true); |
||||||
|
setPosition({ |
||||||
|
startX: e.pageX - slider.current!.offsetLeft, |
||||||
|
scrollLeft: slider.current!.scrollLeft, |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const mouseUp = (_: MouseEvent) => { |
||||||
|
setIsDown(false); |
||||||
|
setShowArrow(showArrows()); |
||||||
|
}; |
||||||
|
|
||||||
|
const mouseMove = (e: MouseEvent) => { |
||||||
|
if (!isDown) return; |
||||||
|
e.preventDefault(); |
||||||
|
|
||||||
|
const eventPosition = e.pageX - slider.current!.offsetLeft; |
||||||
|
const slide = eventPosition - position.startX; |
||||||
|
|
||||||
|
slider.current!.scrollLeft = position.scrollLeft - slide; |
||||||
|
}; |
||||||
|
|
||||||
|
const slide = (direction: SlideDirection) => { |
||||||
|
const slideAmount = -1 * direction * slider.current!.offsetWidth * 0.9; |
||||||
|
const start = slider.current!.scrollLeft; |
||||||
|
smoothHorizontalScroll(500, slideAmount, start); |
||||||
|
setShowArrow(showArrows()); |
||||||
|
}; |
||||||
|
|
||||||
|
const smoothHorizontalScroll = (time: number, amount: number, start: number) => { |
||||||
|
let curTime = 0; |
||||||
|
for (let scrollCounter = 0; curTime <= time; scrollCounter++) { |
||||||
|
window.setTimeout( |
||||||
|
smoothHorizontalScrollBehavior, |
||||||
|
curTime, |
||||||
|
(scrollCounter * amount) / 100 + start, |
||||||
|
); |
||||||
|
curTime += time / 100; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const smoothHorizontalScrollBehavior = (amount: number) => { |
||||||
|
slider.current!.scrollLeft = amount; |
||||||
|
setShowArrow(showArrows()); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className={`${styles.sliderBase} ${className}`}> |
||||||
|
{showArrow.left && ( |
||||||
|
<button onClick={() => slide(SlideDirection.Left)}>left</button> |
||||||
|
)} |
||||||
|
{showArrow.right && ( |
||||||
|
<button onClick={() => slide(SlideDirection.Right)}>right</button> |
||||||
|
)} |
||||||
|
<div |
||||||
|
ref={ref} |
||||||
|
onMouseDown={mouseDown} |
||||||
|
onMouseLeave={mouseUp} |
||||||
|
onMouseUp={mouseUp} |
||||||
|
onMouseMove={mouseMove} |
||||||
|
className={styles.slider} |
||||||
|
> |
||||||
|
{children} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export interface SliderProps { |
||||||
|
children: Item[]; |
||||||
|
className?: string; |
||||||
|
} |
||||||
|
|
||||||
|
export type Arrows = { |
||||||
|
left: boolean; |
||||||
|
right: boolean; |
||||||
|
}; |
@ -1,3 +1,4 @@ |
|||||||
import { Carousel } from './components/carousel'; |
import { Carousel } from './components/carousel'; |
||||||
|
import { Slider } from './components/slider'; |
||||||
|
|
||||||
export { Carousel }; |
export { Carousel, Slider }; |
||||||
|
@ -0,0 +1,20 @@ |
|||||||
|
.sliderBase { |
||||||
|
width: 100%; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.slider { |
||||||
|
display: flex; |
||||||
|
overflow-x: auto; |
||||||
|
scrollbar-width: none; /* Firefox 64 */ |
||||||
|
-ms-overflow-style: none; /* Internet Explorer 11 */ |
||||||
|
} |
||||||
|
|
||||||
|
.slider::-webkit-scrollbar { |
||||||
|
/** WebKit */ |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
.slider > * { |
||||||
|
flex: 0 0 auto; |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
export const sliderBase: string; |
||||||
|
export const slider: string; |
||||||
|
interface Namespace { |
||||||
|
sliderBase: string; |
||||||
|
slider: string; |
||||||
|
} |
||||||
|
declare const stylesModule: Namespace; |
||||||
|
export default stylesModule; |
Loading…
Reference in new issue