-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.tsx
More file actions
122 lines (106 loc) · 3.65 KB
/
index.tsx
File metadata and controls
122 lines (106 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import React, { useEffect, useCallback, useState, useRef, Children, RefObject } from 'react';
import { style } from 'typestyle';
interface RerouselProps {
itemWidth?: number;
itemRef: RefObject<HTMLElement>;
interval?: number;
stop?: boolean;
children?: React.ReactNode;
}
const wrapper = style({
display: 'flex',
alignItems: 'center',
overflowX: 'scroll',
height: '100%',
scrollSnapType: 'x mandatory',
'-webkit-overflow-scrolling': 'touch',
flexFlow: 'row nowrap',
'-ms-overflow-style': 'none',
scrollbarWidth: 'none',
$nest: {
'& > *': {
boxSizing: 'border-box',
flexShrink: 0,
},
'&::-webkit-scrollbar': {
display: 'none',
},
},
});
export const Rerousel: React.FC<RerouselProps> = ({ children, itemRef, interval = 3000, stop = false }) => {
const [itemWidth] = useWidth(itemRef);
const [, setScrollInterval] = useState<NodeJS.Timeout>();
const [currentScrollLeft, setCurrentScrollLeft] = useState<number>(0);
const wrapperRef = useRef<HTMLDivElement>(null);
const cc = Children.count(children);
function useWidth(elementRef: RefObject<HTMLElement>) {
const [width, setWidth] = useState<number>(0);
const updateWidth = useCallback(() => {
if (elementRef && elementRef.current) {
const { width } = elementRef.current.getBoundingClientRect();
setWidth(width);
}
}, [elementRef]);
const firstUpdateWidth = useCallback(() => {
if (elementRef && elementRef.current) {
let { width } = elementRef.current.getBoundingClientRect();
width =
width -
parseInt(window.getComputedStyle(elementRef.current).getPropertyValue('border-left-width')) -
parseInt(window.getComputedStyle(elementRef.current).getPropertyValue('border-right-width'));
width =
width -
parseInt(window.getComputedStyle(elementRef.current).getPropertyValue('padding-left')) -
parseInt(window.getComputedStyle(elementRef.current).getPropertyValue('padding-right'));
setWidth(width);
}
}, [elementRef]);
useEffect(() => {
firstUpdateWidth();
window.addEventListener('resize', updateWidth);
return () => {
window.removeEventListener('resize', updateWidth);
};
}, [updateWidth]);
return [width];
}
useEffect(() => {
if (currentScrollLeft === 0) {
wrapperRef.current?.scrollTo({ left: 0 });
setCurrentScrollLeft(1);
return;
}
if (currentScrollLeft > cc) {
setCurrentScrollLeft(0);
return;
}
if (itemWidth != undefined) {
wrapperRef.current?.scrollTo({
left: itemWidth * currentScrollLeft,
behavior: 'smooth',
});
}
}, [currentScrollLeft, itemWidth]);
useEffect(() => {
if (!stop) {
const i = setInterval(() => {
setCurrentScrollLeft((csl) => csl + 1);
}, interval);
setScrollInterval(i);
}
return () => {
setScrollInterval((i) => {
if (i) {
clearInterval(i);
}
return undefined;
});
};
}, [itemWidth, interval, stop]);
return (
<div className={wrapper} ref={wrapperRef}>
{children}
{children}
</div>
);
};