@faststore/ui 2.0.122-alpha.0 → 2.0.128-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/molecules/Carousel/styles.scss +83 -57
- package/src/components/molecules/NavbarLinks/styles.scss +1 -1
- package/src/components/organisms/ProductShelf/styles.scss +2 -3
- package/src/index.ts +0 -16
- package/dist/components/molecules/Bullets/Bullets.d.ts +0 -35
- package/dist/components/molecules/Bullets/Bullets.js +0 -12
- package/dist/components/molecules/Bullets/Bullets.js.map +0 -1
- package/dist/components/molecules/Bullets/index.d.ts +0 -2
- package/dist/components/molecules/Bullets/index.js +0 -2
- package/dist/components/molecules/Bullets/index.js.map +0 -1
- package/dist/components/molecules/Carousel/Arrows.d.ts +0 -12
- package/dist/components/molecules/Carousel/Arrows.js +0 -6
- package/dist/components/molecules/Carousel/Arrows.js.map +0 -1
- package/dist/components/molecules/Carousel/Carousel.d.ts +0 -54
- package/dist/components/molecules/Carousel/Carousel.js +0 -183
- package/dist/components/molecules/Carousel/Carousel.js.map +0 -1
- package/dist/components/molecules/Carousel/CarouselItem.d.ts +0 -11
- package/dist/components/molecules/Carousel/CarouselItem.js +0 -18
- package/dist/components/molecules/Carousel/CarouselItem.js.map +0 -1
- package/dist/components/molecules/Carousel/hooks/useSlideVisibility.d.ts +0 -9
- package/dist/components/molecules/Carousel/hooks/useSlideVisibility.js +0 -29
- package/dist/components/molecules/Carousel/hooks/useSlideVisibility.js.map +0 -1
- package/dist/components/molecules/Carousel/index.d.ts +0 -2
- package/dist/components/molecules/Carousel/index.js +0 -3
- package/dist/components/molecules/Carousel/index.js.map +0 -1
- package/dist/hooks/useSlider/index.d.ts +0 -2
- package/dist/hooks/useSlider/index.js +0 -3
- package/dist/hooks/useSlider/index.js.map +0 -1
- package/dist/hooks/useSlider/useSlider.d.ts +0 -64
- package/dist/hooks/useSlider/useSlider.js +0 -103
- package/dist/hooks/useSlider/useSlider.js.map +0 -1
- package/src/components/molecules/Bullets/Bullets.tsx +0 -88
- package/src/components/molecules/Bullets/index.ts +0 -2
- package/src/components/molecules/Carousel/Arrows.tsx +0 -58
- package/src/components/molecules/Carousel/Carousel.tsx +0 -387
- package/src/components/molecules/Carousel/CarouselItem.tsx +0 -54
- package/src/components/molecules/Carousel/hooks/useSlideVisibility.ts +0 -59
- package/src/components/molecules/Carousel/index.ts +0 -2
- package/src/hooks/useSlider/index.ts +0 -2
- package/src/hooks/useSlider/useSlider.ts +0 -209
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
UIEvent,
|
|
3
|
-
ReactNode,
|
|
4
|
-
CSSProperties,
|
|
5
|
-
KeyboardEvent,
|
|
6
|
-
PropsWithChildren,
|
|
7
|
-
} from 'react'
|
|
8
|
-
import React, { useMemo, useRef } from 'react'
|
|
9
|
-
import type { SwipeableProps } from 'react-swipeable'
|
|
10
|
-
|
|
11
|
-
import { RightArrowIcon, LeftArrowIcon } from './Arrows'
|
|
12
|
-
import CarouselItem from './CarouselItem'
|
|
13
|
-
import useSlider from '../../../hooks/useSlider/useSlider'
|
|
14
|
-
import Bullets from '../Bullets'
|
|
15
|
-
import { IconButton } from '../../../'
|
|
16
|
-
|
|
17
|
-
const createTransformValues = (infinite: boolean, totalItems: number) => {
|
|
18
|
-
const transformMap: Record<number, number> = {}
|
|
19
|
-
const slideWidth = 100 / totalItems
|
|
20
|
-
|
|
21
|
-
for (let idx = 0; idx < totalItems; ++idx) {
|
|
22
|
-
const currIdx = infinite ? idx - 1 : idx
|
|
23
|
-
const transformValue = -(slideWidth * idx)
|
|
24
|
-
|
|
25
|
-
transformMap[currIdx] = transformValue
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return transformMap
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface CarouselProps extends SwipeableProps {
|
|
32
|
-
/**
|
|
33
|
-
* ID of the current instance of the component.
|
|
34
|
-
*/
|
|
35
|
-
id?: string
|
|
36
|
-
/**
|
|
37
|
-
* ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
|
|
38
|
-
*/
|
|
39
|
-
testId?: string
|
|
40
|
-
/**
|
|
41
|
-
* Returns the value of element's class content attribute.
|
|
42
|
-
*/
|
|
43
|
-
className?: string
|
|
44
|
-
/**
|
|
45
|
-
* Whether or not the Carousel is infinite slide/scroll. Only for the `slide` variant.
|
|
46
|
-
* @default true
|
|
47
|
-
*/
|
|
48
|
-
infiniteMode?: boolean
|
|
49
|
-
/**
|
|
50
|
-
* Specifies which navigation elements should be visible.
|
|
51
|
-
* @default complete
|
|
52
|
-
*/
|
|
53
|
-
controls?: 'complete' | 'navigationArrows' | 'paginationBullets'
|
|
54
|
-
/**
|
|
55
|
-
* Specifies the slide transition. Only for the `slide` variant
|
|
56
|
-
*/
|
|
57
|
-
transition?: {
|
|
58
|
-
duration: number
|
|
59
|
-
property: string
|
|
60
|
-
delay?: number
|
|
61
|
-
timing?: string
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Specifies the number of items per page.
|
|
65
|
-
* @default 1
|
|
66
|
-
*/
|
|
67
|
-
itemsPerPage?: number
|
|
68
|
-
/**
|
|
69
|
-
* Specifies the Carousel track variant.
|
|
70
|
-
* @default slide
|
|
71
|
-
*/
|
|
72
|
-
variant?: 'slide' | 'scroll'
|
|
73
|
-
/**
|
|
74
|
-
* Specifies the navigation icons.
|
|
75
|
-
*/
|
|
76
|
-
navigationIcons?: {
|
|
77
|
-
left?: ReactNode
|
|
78
|
-
right?: ReactNode
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function Carousel({
|
|
83
|
-
infiniteMode = true,
|
|
84
|
-
controls = 'complete',
|
|
85
|
-
testId = 'store-carousel',
|
|
86
|
-
transition = {
|
|
87
|
-
duration: 400,
|
|
88
|
-
property: 'transform',
|
|
89
|
-
},
|
|
90
|
-
children,
|
|
91
|
-
className,
|
|
92
|
-
id = 'store-carousel',
|
|
93
|
-
variant = 'slide',
|
|
94
|
-
itemsPerPage = 1,
|
|
95
|
-
navigationIcons = undefined,
|
|
96
|
-
...swipeableConfigOverrides
|
|
97
|
-
}: PropsWithChildren<CarouselProps>) {
|
|
98
|
-
const carouselTrackRef = useRef<HTMLUListElement>(null)
|
|
99
|
-
const isSlideCarousel = variant === 'slide'
|
|
100
|
-
const isScrollCarousel = variant === 'scroll'
|
|
101
|
-
const childrenArray = React.Children.toArray(children)
|
|
102
|
-
const childrenCount = childrenArray.length
|
|
103
|
-
const numberOfSlides = infiniteMode ? childrenCount + 2 : childrenCount
|
|
104
|
-
const slidingTransition = `${transition.property} ${transition.duration}ms ${
|
|
105
|
-
transition.timing ?? ''
|
|
106
|
-
} ${transition.delay ?? ''}`
|
|
107
|
-
|
|
108
|
-
const showNavigationArrows =
|
|
109
|
-
controls === 'complete' || controls === 'navigationArrows'
|
|
110
|
-
|
|
111
|
-
const showPaginationBullets =
|
|
112
|
-
controls === 'complete' || controls === 'paginationBullets'
|
|
113
|
-
|
|
114
|
-
const transformValues = useMemo(
|
|
115
|
-
() => createTransformValues(infiniteMode, numberOfSlides),
|
|
116
|
-
[numberOfSlides, infiniteMode]
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
const { handlers, slide, sliderState, sliderDispatch } = useSlider({
|
|
120
|
-
itemsPerPage,
|
|
121
|
-
infiniteMode,
|
|
122
|
-
totalItems: childrenCount,
|
|
123
|
-
shouldSlideOnSwipe: isSlideCarousel,
|
|
124
|
-
...swipeableConfigOverrides,
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
const postRenderedSlides =
|
|
128
|
-
infiniteMode && children ? childrenArray.slice(0, 1) : []
|
|
129
|
-
|
|
130
|
-
const preRenderedSlides =
|
|
131
|
-
infiniteMode && children ? childrenArray.slice(childrenCount - 1) : []
|
|
132
|
-
|
|
133
|
-
const slides = preRenderedSlides.concat(
|
|
134
|
-
(children as any) ?? [],
|
|
135
|
-
postRenderedSlides
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
const slideCarouselTrackStyle: CSSProperties = useMemo(
|
|
139
|
-
() => ({
|
|
140
|
-
display: 'flex',
|
|
141
|
-
width: `${numberOfSlides * 100}%`,
|
|
142
|
-
transition: sliderState.sliding ? slidingTransition : undefined,
|
|
143
|
-
transform: `translate3d(${
|
|
144
|
-
transformValues[sliderState.currentPage]
|
|
145
|
-
}%, 0, 0)`,
|
|
146
|
-
}),
|
|
147
|
-
[
|
|
148
|
-
numberOfSlides,
|
|
149
|
-
transformValues,
|
|
150
|
-
slidingTransition,
|
|
151
|
-
sliderState.sliding,
|
|
152
|
-
sliderState.currentPage,
|
|
153
|
-
]
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
const scrollCarouselTrackStyle: CSSProperties = useMemo(
|
|
157
|
-
() => ({
|
|
158
|
-
width: '100%',
|
|
159
|
-
display: 'block',
|
|
160
|
-
overflowX: 'scroll',
|
|
161
|
-
whiteSpace: 'nowrap',
|
|
162
|
-
}),
|
|
163
|
-
[]
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
const carouselTrackStyle =
|
|
167
|
-
((isSlideCarousel && slideCarouselTrackStyle) as CSSProperties) ||
|
|
168
|
-
((isScrollCarousel && scrollCarouselTrackStyle) as CSSProperties)
|
|
169
|
-
|
|
170
|
-
const slidePrevious = () => {
|
|
171
|
-
if (
|
|
172
|
-
sliderState.sliding ||
|
|
173
|
-
(!infiniteMode && sliderState.currentPage === 0)
|
|
174
|
-
) {
|
|
175
|
-
return
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
slide('previous', sliderDispatch)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const slideNext = () => {
|
|
182
|
-
if (
|
|
183
|
-
sliderState.sliding ||
|
|
184
|
-
(!infiniteMode && sliderState.currentPage === childrenCount - 1)
|
|
185
|
-
) {
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
slide('next', sliderDispatch)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const onScrollTrack = (event: UIEvent) => {
|
|
193
|
-
if (isSlideCarousel || itemsPerPage > 1) {
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const itemWidth = Number(event.currentTarget.firstElementChild?.scrollWidth)
|
|
198
|
-
const scrollOffset = event.currentTarget?.scrollLeft
|
|
199
|
-
const formatter = scrollOffset > itemWidth / 2 ? Math.round : Math.floor
|
|
200
|
-
const page = formatter(scrollOffset / itemWidth)
|
|
201
|
-
|
|
202
|
-
slide(page, sliderDispatch)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const onTransitionTrackEnd = () => {
|
|
206
|
-
sliderDispatch({
|
|
207
|
-
type: 'STOP_SLIDE',
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
if (infiniteMode && sliderState.currentItem >= childrenCount) {
|
|
211
|
-
sliderDispatch({
|
|
212
|
-
type: 'GO_TO_PAGE',
|
|
213
|
-
payload: {
|
|
214
|
-
pageIndex: 0,
|
|
215
|
-
shouldSlide: false,
|
|
216
|
-
},
|
|
217
|
-
})
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (infiniteMode && sliderState.currentItem < 0) {
|
|
221
|
-
sliderDispatch({
|
|
222
|
-
type: 'GO_TO_PAGE',
|
|
223
|
-
payload: {
|
|
224
|
-
pageIndex: sliderState.totalPages - 1,
|
|
225
|
-
shouldSlide: false,
|
|
226
|
-
},
|
|
227
|
-
})
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const onScrollPagination = async (
|
|
232
|
-
index: number,
|
|
233
|
-
slideDirection?: 'previous' | 'next'
|
|
234
|
-
) => {
|
|
235
|
-
if (slideDirection === 'previous' && sliderState.currentPage === 0) {
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
slideDirection === 'next' &&
|
|
241
|
-
sliderState.currentPage === sliderState.totalPages - 1
|
|
242
|
-
) {
|
|
243
|
-
return
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
let scrollOffset
|
|
247
|
-
const carouselItemsWidth = Number(
|
|
248
|
-
carouselTrackRef.current?.firstElementChild?.clientWidth
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
if (itemsPerPage > 1) {
|
|
252
|
-
scrollOffset = index * carouselItemsWidth * itemsPerPage
|
|
253
|
-
} else {
|
|
254
|
-
scrollOffset = index * carouselItemsWidth - carouselItemsWidth * 0.125
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
carouselTrackRef.current?.scrollTo({
|
|
258
|
-
left: scrollOffset,
|
|
259
|
-
behavior: 'smooth',
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
slide(index, sliderDispatch)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// accessible behavior for tablist
|
|
266
|
-
const handleBulletsKeyDown = (event: KeyboardEvent) => {
|
|
267
|
-
switch (event.key) {
|
|
268
|
-
case 'ArrowLeft': {
|
|
269
|
-
isSlideCarousel && slidePrevious()
|
|
270
|
-
isScrollCarousel &&
|
|
271
|
-
onScrollPagination(sliderState.currentPage - 1, 'previous')
|
|
272
|
-
break
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
case 'ArrowRight': {
|
|
276
|
-
isSlideCarousel && slideNext()
|
|
277
|
-
isScrollCarousel &&
|
|
278
|
-
onScrollPagination(sliderState.currentPage + 1, 'next')
|
|
279
|
-
break
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
case 'Home': {
|
|
283
|
-
slide(0, sliderDispatch)
|
|
284
|
-
break
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
case 'End': {
|
|
288
|
-
slide(childrenCount - 1, sliderDispatch)
|
|
289
|
-
break
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
default:
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return (
|
|
297
|
-
<section
|
|
298
|
-
id={id}
|
|
299
|
-
data-fs-carousel
|
|
300
|
-
className={className}
|
|
301
|
-
data-testid={testId}
|
|
302
|
-
aria-label="carousel"
|
|
303
|
-
aria-roledescription="carousel"
|
|
304
|
-
>
|
|
305
|
-
<div
|
|
306
|
-
data-fs-carousel-track-container
|
|
307
|
-
style={{
|
|
308
|
-
width: '100%',
|
|
309
|
-
overflow: 'hidden',
|
|
310
|
-
display: isScrollCarousel ? 'block' : undefined,
|
|
311
|
-
}}
|
|
312
|
-
{...handlers}
|
|
313
|
-
>
|
|
314
|
-
<ul
|
|
315
|
-
aria-live="polite"
|
|
316
|
-
ref={carouselTrackRef}
|
|
317
|
-
style={carouselTrackStyle}
|
|
318
|
-
data-fs-carousel-track
|
|
319
|
-
onScroll={onScrollTrack}
|
|
320
|
-
onTransitionEnd={onTransitionTrackEnd}
|
|
321
|
-
>
|
|
322
|
-
{slides.map((currentSlide, idx) => (
|
|
323
|
-
<CarouselItem
|
|
324
|
-
index={idx}
|
|
325
|
-
key={String(idx)}
|
|
326
|
-
state={sliderState}
|
|
327
|
-
totalItems={childrenCount}
|
|
328
|
-
infiniteMode={infiniteMode}
|
|
329
|
-
isScrollCarousel={isScrollCarousel}
|
|
330
|
-
>
|
|
331
|
-
{currentSlide}
|
|
332
|
-
</CarouselItem>
|
|
333
|
-
))}
|
|
334
|
-
</ul>
|
|
335
|
-
</div>
|
|
336
|
-
|
|
337
|
-
{showNavigationArrows && (
|
|
338
|
-
<div data-fs-carousel-controls>
|
|
339
|
-
<IconButton
|
|
340
|
-
data-fs-carousel-control="left"
|
|
341
|
-
aria-controls={id}
|
|
342
|
-
aria-label="previous"
|
|
343
|
-
icon={navigationIcons?.left ?? <LeftArrowIcon />}
|
|
344
|
-
onClick={() => {
|
|
345
|
-
isSlideCarousel && slidePrevious()
|
|
346
|
-
isScrollCarousel &&
|
|
347
|
-
onScrollPagination(sliderState.currentPage - 1, 'previous')
|
|
348
|
-
}}
|
|
349
|
-
/>
|
|
350
|
-
<IconButton
|
|
351
|
-
data-fs-carousel-control="right"
|
|
352
|
-
aria-controls={id}
|
|
353
|
-
aria-label="next"
|
|
354
|
-
icon={navigationIcons?.right ?? <RightArrowIcon />}
|
|
355
|
-
onClick={() => {
|
|
356
|
-
isSlideCarousel && slideNext()
|
|
357
|
-
isScrollCarousel &&
|
|
358
|
-
onScrollPagination(sliderState.currentPage + 1, 'next')
|
|
359
|
-
}}
|
|
360
|
-
/>
|
|
361
|
-
</div>
|
|
362
|
-
)}
|
|
363
|
-
|
|
364
|
-
{showPaginationBullets && (
|
|
365
|
-
<div data-fs-carousel-bullets>
|
|
366
|
-
<Bullets
|
|
367
|
-
tabIndex={0}
|
|
368
|
-
activeBullet={sliderState.currentPage}
|
|
369
|
-
totalQuantity={Math.ceil(childrenCount / sliderState.itemsPerPage)}
|
|
370
|
-
onKeyDown={handleBulletsKeyDown}
|
|
371
|
-
onClick={async (_, idx) => {
|
|
372
|
-
isSlideCarousel &&
|
|
373
|
-
!sliderState.sliding &&
|
|
374
|
-
slide(idx, sliderDispatch)
|
|
375
|
-
|
|
376
|
-
isScrollCarousel && onScrollPagination(idx)
|
|
377
|
-
}}
|
|
378
|
-
onFocus={(event) => event.currentTarget.focus()}
|
|
379
|
-
ariaControlsGenerator={(idx) => `carousel-item-${idx}`}
|
|
380
|
-
/>
|
|
381
|
-
</div>
|
|
382
|
-
)}
|
|
383
|
-
</section>
|
|
384
|
-
)
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
export default Carousel
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { CSSProperties, PropsWithChildren, HTMLAttributes } from 'react'
|
|
3
|
-
import type { SliderState } from '../../../hooks/useSlider/useSlider'
|
|
4
|
-
|
|
5
|
-
import useSlideVisibility from './hooks/useSlideVisibility'
|
|
6
|
-
|
|
7
|
-
interface CarouselItemProps extends HTMLAttributes<HTMLLIElement> {
|
|
8
|
-
index: number
|
|
9
|
-
totalItems: number
|
|
10
|
-
state: SliderState
|
|
11
|
-
infiniteMode: boolean
|
|
12
|
-
isScrollCarousel: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function CarouselItem({
|
|
16
|
-
index,
|
|
17
|
-
state,
|
|
18
|
-
children,
|
|
19
|
-
totalItems,
|
|
20
|
-
infiniteMode,
|
|
21
|
-
isScrollCarousel,
|
|
22
|
-
}: PropsWithChildren<CarouselItemProps>) {
|
|
23
|
-
const { isItemVisible, shouldRenderItem } = useSlideVisibility({
|
|
24
|
-
totalItems,
|
|
25
|
-
currentSlide: state.currentItem,
|
|
26
|
-
itemsPerPage: state.itemsPerPage,
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
const style =
|
|
30
|
-
((!isScrollCarousel && { width: '100%' }) as CSSProperties) ||
|
|
31
|
-
((isScrollCarousel && {
|
|
32
|
-
maxWidth: '80%',
|
|
33
|
-
display: 'inline-block',
|
|
34
|
-
}) as CSSProperties)
|
|
35
|
-
|
|
36
|
-
const shouldDisplayItem =
|
|
37
|
-
isScrollCarousel || shouldRenderItem(index - Number(infiniteMode))
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<li
|
|
41
|
-
style={style}
|
|
42
|
-
data-fs-carousel-item
|
|
43
|
-
aria-roledescription="slide"
|
|
44
|
-
id={`carousel-item-${index}`}
|
|
45
|
-
data-fs-carousel-item-visible={
|
|
46
|
-
isItemVisible(index - Number(infiniteMode)) || undefined
|
|
47
|
-
}
|
|
48
|
-
>
|
|
49
|
-
{shouldDisplayItem ? children : null}
|
|
50
|
-
</li>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export default CarouselItem
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { useRef, useEffect } from 'react'
|
|
2
|
-
|
|
3
|
-
export interface UseSlideVisibilityArgs {
|
|
4
|
-
currentSlide: number
|
|
5
|
-
itemsPerPage: number
|
|
6
|
-
totalItems: number
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface IsSlideVisibleArgs {
|
|
10
|
-
itemsPerPage: number
|
|
11
|
-
currentSlide: number
|
|
12
|
-
slideIdx: number
|
|
13
|
-
totalItems: number
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isSlideVisible({
|
|
17
|
-
itemsPerPage,
|
|
18
|
-
currentSlide,
|
|
19
|
-
slideIdx,
|
|
20
|
-
totalItems,
|
|
21
|
-
}: IsSlideVisibleArgs) {
|
|
22
|
-
const isClonedSlide = currentSlide < 0 || currentSlide >= totalItems
|
|
23
|
-
const isVisible =
|
|
24
|
-
slideIdx >= currentSlide && slideIdx < currentSlide + itemsPerPage
|
|
25
|
-
|
|
26
|
-
return isClonedSlide || isVisible
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default function useSlideVisibility({
|
|
30
|
-
currentSlide,
|
|
31
|
-
itemsPerPage,
|
|
32
|
-
totalItems,
|
|
33
|
-
}: UseSlideVisibilityArgs) {
|
|
34
|
-
/** Keeps track of slides that have been visualized before.
|
|
35
|
-
* We want to keep rendering them because the issue is mostly rendering
|
|
36
|
-
* slides that might never be viewed; On the other hand, hiding slides
|
|
37
|
-
* that were visible causes visual glitches */
|
|
38
|
-
const visitedSlides = useRef<Set<number>>(new Set())
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
for (let i = 0; i < itemsPerPage; i++) {
|
|
42
|
-
visitedSlides.current.add(currentSlide + i)
|
|
43
|
-
}
|
|
44
|
-
}, [currentSlide, itemsPerPage])
|
|
45
|
-
|
|
46
|
-
const isItemVisible = (index: number) =>
|
|
47
|
-
isSlideVisible({
|
|
48
|
-
slideIdx: index,
|
|
49
|
-
currentSlide,
|
|
50
|
-
itemsPerPage,
|
|
51
|
-
totalItems,
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const shouldRenderItem = (index: number) => {
|
|
55
|
-
return visitedSlides.current.has(index) || isItemVisible(index)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return { shouldRenderItem, isItemVisible }
|
|
59
|
-
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import type { Dispatch } from 'react'
|
|
2
|
-
import { useReducer } from 'react'
|
|
3
|
-
import type { SwipeableProps } from 'react-swipeable'
|
|
4
|
-
import { useSwipeable } from 'react-swipeable'
|
|
5
|
-
|
|
6
|
-
export type SlideDirection = 'next' | 'previous'
|
|
7
|
-
|
|
8
|
-
interface NextPageAction {
|
|
9
|
-
type: 'NEXT_PAGE'
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface PreviousPageAction {
|
|
13
|
-
type: 'PREVIOUS_PAGE'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface GoToPageAction {
|
|
17
|
-
type: 'GO_TO_PAGE'
|
|
18
|
-
payload: {
|
|
19
|
-
pageIndex: number
|
|
20
|
-
shouldSlide: boolean
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface StopSlideAction {
|
|
25
|
-
type: 'STOP_SLIDE'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type Action =
|
|
29
|
-
| NextPageAction
|
|
30
|
-
| PreviousPageAction
|
|
31
|
-
| StopSlideAction
|
|
32
|
-
| GoToPageAction
|
|
33
|
-
|
|
34
|
-
export type SliderDispatch = Dispatch<Action>
|
|
35
|
-
|
|
36
|
-
export interface SliderState {
|
|
37
|
-
/**
|
|
38
|
-
* The `currentItem` in a Slider with multiple items in a single page is
|
|
39
|
-
* always **the one with the lowest index** in the current page.
|
|
40
|
-
*/
|
|
41
|
-
currentItem: number
|
|
42
|
-
/** Currently active page */
|
|
43
|
-
currentPage: number
|
|
44
|
-
/**
|
|
45
|
-
* Whether or not the Slider is currently sliding. This is useful to
|
|
46
|
-
* manipulate the `transition` property in a component.
|
|
47
|
-
*/
|
|
48
|
-
sliding: boolean
|
|
49
|
-
/** The direction in which the Slider is sliding. */
|
|
50
|
-
slideDirection: SlideDirection
|
|
51
|
-
/** The total number of unique items in the slider. */
|
|
52
|
-
totalItems: number
|
|
53
|
-
/** The number of items in a single page. */
|
|
54
|
-
itemsPerPage: number
|
|
55
|
-
/** The total number of pages in the slider. */
|
|
56
|
-
totalPages: number
|
|
57
|
-
/** Whether or not the slider is infinite. */
|
|
58
|
-
infinite: boolean
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export const nextPage = (current: number, total: number) =>
|
|
62
|
-
(current + 1) % total
|
|
63
|
-
|
|
64
|
-
export const previousPage = (current: number, total: number) =>
|
|
65
|
-
(total - ((total - current + 1) % total)) % total
|
|
66
|
-
|
|
67
|
-
function reducer(state: SliderState, action: Action): SliderState {
|
|
68
|
-
switch (action.type) {
|
|
69
|
-
case 'NEXT_PAGE': {
|
|
70
|
-
// If `state.infinite` is true, we need to take into account an extra
|
|
71
|
-
// page in the calculation. This extra page is a clone of the first page.
|
|
72
|
-
const adjustedTotalPages = state.infinite
|
|
73
|
-
? state.totalPages + 1
|
|
74
|
-
: state.totalPages
|
|
75
|
-
|
|
76
|
-
const nextPageIndex = nextPage(state.currentPage, adjustedTotalPages)
|
|
77
|
-
|
|
78
|
-
const nextItemIndex =
|
|
79
|
-
(nextPageIndex % adjustedTotalPages) * state.itemsPerPage
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
...state,
|
|
83
|
-
sliding: true,
|
|
84
|
-
slideDirection: 'next',
|
|
85
|
-
currentItem: nextItemIndex,
|
|
86
|
-
currentPage: nextPageIndex,
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
case 'PREVIOUS_PAGE': {
|
|
91
|
-
// If `state.infinite` is true, we need to take into account an extra
|
|
92
|
-
// page in the calculation. This extra page is a clone of the first page.
|
|
93
|
-
const adjustedTotalPages = state.infinite
|
|
94
|
-
? state.totalPages + 1
|
|
95
|
-
: state.totalPages
|
|
96
|
-
|
|
97
|
-
// If `state.infinite` is true and we're currently on page 0, we need to
|
|
98
|
-
// let the slider go to page -1. This -1 page is a clone of the last page.
|
|
99
|
-
const shouldGoToClone = state.infinite && state.currentPage === 0
|
|
100
|
-
const previousPageIndex = shouldGoToClone
|
|
101
|
-
? -1
|
|
102
|
-
: previousPage(state.currentPage, state.totalPages)
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
...state,
|
|
106
|
-
sliding: true,
|
|
107
|
-
slideDirection: 'previous',
|
|
108
|
-
currentItem:
|
|
109
|
-
(previousPageIndex % adjustedTotalPages) * state.itemsPerPage,
|
|
110
|
-
currentPage: previousPageIndex,
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
case 'GO_TO_PAGE': {
|
|
115
|
-
if (action.payload.pageIndex === state.currentPage) {
|
|
116
|
-
return state
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
...state,
|
|
121
|
-
sliding: action.payload.shouldSlide,
|
|
122
|
-
slideDirection:
|
|
123
|
-
action.payload.pageIndex > state.currentPage ? 'next' : 'previous',
|
|
124
|
-
currentItem:
|
|
125
|
-
(action.payload.pageIndex % state.totalPages) * state.itemsPerPage,
|
|
126
|
-
currentPage: action.payload.pageIndex,
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
case 'STOP_SLIDE':
|
|
131
|
-
return { ...state, sliding: false }
|
|
132
|
-
|
|
133
|
-
default:
|
|
134
|
-
return state
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const defaultSliderState = (
|
|
139
|
-
totalItems: number,
|
|
140
|
-
itemsPerPage: number,
|
|
141
|
-
infinite: boolean
|
|
142
|
-
): SliderState => ({
|
|
143
|
-
currentItem: 0,
|
|
144
|
-
currentPage: 0,
|
|
145
|
-
sliding: false,
|
|
146
|
-
slideDirection: 'next',
|
|
147
|
-
totalItems,
|
|
148
|
-
itemsPerPage,
|
|
149
|
-
totalPages: Math.ceil(totalItems / itemsPerPage),
|
|
150
|
-
infinite,
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
const slide = (page: SlideDirection | number, dispatch: Dispatch<Action>) => {
|
|
154
|
-
if (page === 'next') {
|
|
155
|
-
dispatch({ type: 'NEXT_PAGE' })
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (page === 'previous') {
|
|
159
|
-
dispatch({ type: 'PREVIOUS_PAGE' })
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (typeof page === 'number') {
|
|
163
|
-
dispatch({
|
|
164
|
-
type: 'GO_TO_PAGE',
|
|
165
|
-
payload: {
|
|
166
|
-
pageIndex: page,
|
|
167
|
-
shouldSlide: true,
|
|
168
|
-
},
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export interface UseSliderArgs extends SwipeableProps {
|
|
174
|
-
/** The total number of unique items in the slider. */
|
|
175
|
-
totalItems: number
|
|
176
|
-
/** The number of items in a single slider page. */
|
|
177
|
-
itemsPerPage?: number
|
|
178
|
-
/** Whether or not the slider is infinite. */
|
|
179
|
-
infiniteMode?: boolean
|
|
180
|
-
/** Whether or not slide after swiping left/right. */
|
|
181
|
-
shouldSlideOnSwipe?: boolean
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export default function useSlider({
|
|
185
|
-
totalItems,
|
|
186
|
-
itemsPerPage = 1,
|
|
187
|
-
infiniteMode = false,
|
|
188
|
-
shouldSlideOnSwipe = true,
|
|
189
|
-
...swipeableConfigOverrides
|
|
190
|
-
}: UseSliderArgs) {
|
|
191
|
-
const [sliderState, sliderDispatch] = useReducer(reducer, undefined, () =>
|
|
192
|
-
defaultSliderState(totalItems, itemsPerPage, infiniteMode)
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
const handlers = useSwipeable({
|
|
196
|
-
onSwipedRight: () =>
|
|
197
|
-
shouldSlideOnSwipe && slide('previous', sliderDispatch),
|
|
198
|
-
onSwipedLeft: () => shouldSlideOnSwipe && slide('next', sliderDispatch),
|
|
199
|
-
trackMouse: true,
|
|
200
|
-
...swipeableConfigOverrides,
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
handlers,
|
|
205
|
-
slide,
|
|
206
|
-
sliderState,
|
|
207
|
-
sliderDispatch,
|
|
208
|
-
}
|
|
209
|
-
}
|