@@ -22,7 +22,8 @@ import {
22
22
generateNumbers ,
23
23
} from "../../utils/generateNumbers" ;
24
24
import { getAdjustedLimit } from "../../utils/getAdjustedLimit" ;
25
- import { getScrollIndex } from "../../utils/getScrollIndex" ;
25
+ import { getDurationAndIndexFromScrollOffset } from "../../utils/getDurationAndIndexFromScrollOffset" ;
26
+ import { getInitialScrollIndex } from "../../utils/getInitialScrollIndex" ;
26
27
27
28
import type { DurationScrollProps , DurationScrollRef } from "./types" ;
28
29
@@ -56,19 +57,29 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
56
57
topPickerGradientOverlayProps,
57
58
} = props ;
58
59
59
- const data = useMemo ( ( ) => {
60
+ const safeRepeatNumbersNTimes = useMemo ( ( ) => {
61
+ if ( ! disableInfiniteScroll && repeatNumbersNTimes < 2 ) {
62
+ return 2 ;
63
+ } else if ( repeatNumbersNTimes < 1 ) {
64
+ return 1 ;
65
+ }
66
+
67
+ return Math . round ( repeatNumbersNTimes ) ;
68
+ } , [ disableInfiniteScroll , repeatNumbersNTimes ] ) ;
69
+
70
+ const numbersForFlatList = useMemo ( ( ) => {
60
71
if ( is12HourPicker ) {
61
72
return generate12HourNumbers ( {
62
73
padNumbersWithZero,
63
- repeatNTimes : repeatNumbersNTimes ,
74
+ repeatNTimes : safeRepeatNumbersNTimes ,
64
75
disableInfiniteScroll,
65
76
padWithNItems,
66
77
} ) ;
67
78
}
68
79
69
80
return generateNumbers ( numberOfItems , {
70
81
padNumbersWithZero,
71
- repeatNTimes : repeatNumbersNTimes ,
82
+ repeatNTimes : safeRepeatNumbersNTimes ,
72
83
disableInfiniteScroll,
73
84
padWithNItems,
74
85
} ) ;
@@ -78,22 +89,24 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
78
89
numberOfItems ,
79
90
padNumbersWithZero ,
80
91
padWithNItems ,
81
- repeatNumbersNTimes ,
92
+ safeRepeatNumbersNTimes ,
82
93
] ) ;
83
94
84
95
const initialScrollIndex = useMemo (
85
96
( ) =>
86
- getScrollIndex ( {
97
+ getInitialScrollIndex ( {
98
+ disableInfiniteScroll,
87
99
numberOfItems,
88
100
padWithNItems,
89
- repeatNumbersNTimes,
101
+ repeatNumbersNTimes : safeRepeatNumbersNTimes ,
90
102
value : initialValue ,
91
103
} ) ,
92
104
[
105
+ disableInfiniteScroll ,
93
106
initialValue ,
94
107
numberOfItems ,
95
108
padWithNItems ,
96
- repeatNumbersNTimes ,
109
+ safeRepeatNumbersNTimes ,
97
110
]
98
111
) ;
99
112
@@ -134,6 +147,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
134
147
setClickSound ( sound ) ;
135
148
}
136
149
} ;
150
+
137
151
loadSound ( ) ;
138
152
139
153
// Unload sound when component unmounts
@@ -143,27 +157,6 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
143
157
// eslint-disable-next-line react-hooks/exhaustive-deps
144
158
} , [ Audio ] ) ;
145
159
146
- useImperativeHandle ( ref , ( ) => ( {
147
- reset : ( options ) => {
148
- flatListRef . current ?. scrollToIndex ( {
149
- animated : options ?. animated ?? false ,
150
- index : initialScrollIndex ,
151
- } ) ;
152
- } ,
153
- setValue : ( value , options ) => {
154
- flatListRef . current ?. scrollToIndex ( {
155
- animated : options ?. animated ?? false ,
156
- index : getScrollIndex ( {
157
- numberOfItems,
158
- padWithNItems,
159
- repeatNumbersNTimes,
160
- value : value ,
161
- } ) ,
162
- } ) ;
163
- } ,
164
- latestDuration : latestDuration ,
165
- } ) ) ;
166
-
167
160
const renderItem = useCallback (
168
161
( { item } : { item : string } ) => {
169
162
let stringItem = item ;
@@ -233,25 +226,23 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
233
226
}
234
227
235
228
if ( aggressivelyGetLatestDuration ) {
236
- const newIndex = Math . round (
237
- e . nativeEvent . contentOffset . y /
238
- styles . pickerItemContainer . height
239
- ) ;
240
- let newDuration =
241
- ( disableInfiniteScroll
242
- ? newIndex
243
- : newIndex + padWithNItems ) %
244
- ( numberOfItems + 1 ) ;
229
+ const newValues = getDurationAndIndexFromScrollOffset ( {
230
+ disableInfiniteScroll,
231
+ itemHeight : styles . pickerItemContainer . height ,
232
+ numberOfItems,
233
+ padWithNItems,
234
+ yContentOffset : e . nativeEvent . contentOffset . y ,
235
+ } ) ;
245
236
246
- if ( newDuration !== latestDuration . current ) {
237
+ if ( newValues . duration !== latestDuration . current ) {
247
238
// check limits
248
- if ( newDuration > adjustedLimited . max ) {
249
- newDuration = adjustedLimited . max ;
250
- } else if ( newDuration < adjustedLimited . min ) {
251
- newDuration = adjustedLimited . min ;
239
+ if ( newValues . duration > adjustedLimited . max ) {
240
+ newValues . duration = adjustedLimited . max ;
241
+ } else if ( newValues . duration < adjustedLimited . min ) {
242
+ newValues . duration = adjustedLimited . min ;
252
243
}
253
244
254
- latestDuration . current = newDuration ;
245
+ latestDuration . current = newValues . duration ;
255
246
}
256
247
}
257
248
@@ -299,20 +290,19 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
299
290
300
291
const onMomentumScrollEnd = useCallback (
301
292
( e : NativeSyntheticEvent < NativeScrollEvent > ) => {
302
- const newIndex = Math . round (
303
- e . nativeEvent . contentOffset . y /
304
- styles . pickerItemContainer . height
305
- ) ;
306
- let newDuration =
307
- ( disableInfiniteScroll
308
- ? newIndex
309
- : newIndex + padWithNItems ) %
310
- ( numberOfItems + 1 ) ;
293
+ const newValues = getDurationAndIndexFromScrollOffset ( {
294
+ disableInfiniteScroll,
295
+ itemHeight : styles . pickerItemContainer . height ,
296
+ numberOfItems,
297
+ padWithNItems,
298
+ yContentOffset : e . nativeEvent . contentOffset . y ,
299
+ } ) ;
311
300
312
301
// check limits
313
- if ( newDuration > adjustedLimited . max ) {
302
+ if ( newValues . duration > adjustedLimited . max ) {
314
303
const targetScrollIndex =
315
- newIndex - ( newDuration - adjustedLimited . max ) ;
304
+ newValues . index -
305
+ ( newValues . duration - adjustedLimited . max ) ;
316
306
flatListRef . current ?. scrollToIndex ( {
317
307
animated : true ,
318
308
index :
@@ -321,27 +311,28 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
321
311
? targetScrollIndex
322
312
: adjustedLimited . max - 1 ,
323
313
} ) ; // scroll down to max
324
- newDuration = adjustedLimited . max ;
325
- } else if ( newDuration < adjustedLimited . min ) {
314
+ newValues . duration = adjustedLimited . max ;
315
+ } else if ( newValues . duration < adjustedLimited . min ) {
326
316
const targetScrollIndex =
327
- newIndex + ( adjustedLimited . min - newDuration ) ;
317
+ newValues . index +
318
+ ( adjustedLimited . min - newValues . duration ) ;
328
319
flatListRef . current ?. scrollToIndex ( {
329
320
animated : true ,
330
321
index :
331
322
// guard against scrolling beyond end of list
332
- targetScrollIndex <= data . length - 1
323
+ targetScrollIndex <= numbersForFlatList . length - 1
333
324
? targetScrollIndex
334
325
: adjustedLimited . min ,
335
326
} ) ; // scroll up to min
336
- newDuration = adjustedLimited . min ;
327
+ newValues . duration = adjustedLimited . min ;
337
328
}
338
329
339
- onDurationChange ( newDuration ) ;
330
+ onDurationChange ( newValues . duration ) ;
340
331
} ,
341
332
[
342
333
adjustedLimited . max ,
343
334
adjustedLimited . min ,
344
- data . length ,
335
+ numbersForFlatList . length ,
345
336
disableInfiniteScroll ,
346
337
numberOfItems ,
347
338
onDurationChange ,
@@ -363,15 +354,15 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
363
354
} else if (
364
355
viewableItems [ 0 ] ?. index &&
365
356
viewableItems [ 0 ] . index >=
366
- numberOfItems * ( repeatNumbersNTimes - 0.5 )
357
+ numberOfItems * ( safeRepeatNumbersNTimes - 0.5 )
367
358
) {
368
359
flatListRef . current ?. scrollToIndex ( {
369
360
animated : false ,
370
- index : viewableItems [ 0 ] . index - numberOfItems - 1 ,
361
+ index : viewableItems [ 0 ] . index - numberOfItems ,
371
362
} ) ;
372
363
}
373
364
} ,
374
- [ numberOfItems , repeatNumbersNTimes ]
365
+ [ numberOfItems , safeRepeatNumbersNTimes ]
375
366
) ;
376
367
377
368
const getItemLayout = useCallback (
@@ -391,6 +382,28 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
391
382
} ,
392
383
] ) ;
393
384
385
+ useImperativeHandle ( ref , ( ) => ( {
386
+ reset : ( options ) => {
387
+ flatListRef . current ?. scrollToIndex ( {
388
+ animated : options ?. animated ?? false ,
389
+ index : initialScrollIndex ,
390
+ } ) ;
391
+ } ,
392
+ setValue : ( value , options ) => {
393
+ flatListRef . current ?. scrollToIndex ( {
394
+ animated : options ?. animated ?? false ,
395
+ index : getInitialScrollIndex ( {
396
+ disableInfiniteScroll,
397
+ numberOfItems,
398
+ padWithNItems,
399
+ repeatNumbersNTimes : safeRepeatNumbersNTimes ,
400
+ value : value ,
401
+ } ) ,
402
+ } ) ;
403
+ } ,
404
+ latestDuration : latestDuration ,
405
+ } ) ) ;
406
+
394
407
return (
395
408
< View
396
409
pointerEvents = { isDisabled ? "none" : undefined }
@@ -406,7 +419,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
406
419
testID = { testID } >
407
420
< FlatList
408
421
ref = { flatListRef }
409
- data = { data }
422
+ data = { numbersForFlatList }
410
423
decelerationRate = { 0.88 }
411
424
getItemLayout = { getItemLayout }
412
425
initialScrollIndex = { initialScrollIndex }
@@ -420,7 +433,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
420
433
showsVerticalScrollIndicator = { false }
421
434
snapToAlignment = "start"
422
435
// used in place of snapToOffset due to bug on Android
423
- snapToOffsets = { [ ...Array ( data . length ) ] . map (
436
+ snapToOffsets = { [ ...Array ( numbersForFlatList . length ) ] . map (
424
437
( _ , i ) => i * styles . pickerItemContainer . height
425
438
) }
426
439
testID = "duration-scroll-flatlist"
0 commit comments