@@ -22,7 +22,8 @@ import {
2222 generateNumbers ,
2323} from "../../utils/generateNumbers" ;
2424import { getAdjustedLimit } from "../../utils/getAdjustedLimit" ;
25- import { getScrollIndex } from "../../utils/getScrollIndex" ;
25+ import { getDurationAndIndexFromScrollOffset } from "../../utils/getDurationAndIndexFromScrollOffset" ;
26+ import { getInitialScrollIndex } from "../../utils/getInitialScrollIndex" ;
2627
2728import type { DurationScrollProps , DurationScrollRef } from "./types" ;
2829
@@ -56,19 +57,29 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
5657 topPickerGradientOverlayProps,
5758 } = props ;
5859
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 ( ( ) => {
6071 if ( is12HourPicker ) {
6172 return generate12HourNumbers ( {
6273 padNumbersWithZero,
63- repeatNTimes : repeatNumbersNTimes ,
74+ repeatNTimes : safeRepeatNumbersNTimes ,
6475 disableInfiniteScroll,
6576 padWithNItems,
6677 } ) ;
6778 }
6879
6980 return generateNumbers ( numberOfItems , {
7081 padNumbersWithZero,
71- repeatNTimes : repeatNumbersNTimes ,
82+ repeatNTimes : safeRepeatNumbersNTimes ,
7283 disableInfiniteScroll,
7384 padWithNItems,
7485 } ) ;
@@ -78,22 +89,24 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
7889 numberOfItems ,
7990 padNumbersWithZero ,
8091 padWithNItems ,
81- repeatNumbersNTimes ,
92+ safeRepeatNumbersNTimes ,
8293 ] ) ;
8394
8495 const initialScrollIndex = useMemo (
8596 ( ) =>
86- getScrollIndex ( {
97+ getInitialScrollIndex ( {
98+ disableInfiniteScroll,
8799 numberOfItems,
88100 padWithNItems,
89- repeatNumbersNTimes,
101+ repeatNumbersNTimes : safeRepeatNumbersNTimes ,
90102 value : initialValue ,
91103 } ) ,
92104 [
105+ disableInfiniteScroll ,
93106 initialValue ,
94107 numberOfItems ,
95108 padWithNItems ,
96- repeatNumbersNTimes ,
109+ safeRepeatNumbersNTimes ,
97110 ]
98111 ) ;
99112
@@ -134,6 +147,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
134147 setClickSound ( sound ) ;
135148 }
136149 } ;
150+
137151 loadSound ( ) ;
138152
139153 // Unload sound when component unmounts
@@ -143,27 +157,6 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
143157 // eslint-disable-next-line react-hooks/exhaustive-deps
144158 } , [ Audio ] ) ;
145159
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-
167160 const renderItem = useCallback (
168161 ( { item } : { item : string } ) => {
169162 let stringItem = item ;
@@ -233,25 +226,23 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
233226 }
234227
235228 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+ } ) ;
245236
246- if ( newDuration !== latestDuration . current ) {
237+ if ( newValues . duration !== latestDuration . current ) {
247238 // 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 ;
252243 }
253244
254- latestDuration . current = newDuration ;
245+ latestDuration . current = newValues . duration ;
255246 }
256247 }
257248
@@ -299,20 +290,19 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
299290
300291 const onMomentumScrollEnd = useCallback (
301292 ( 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+ } ) ;
311300
312301 // check limits
313- if ( newDuration > adjustedLimited . max ) {
302+ if ( newValues . duration > adjustedLimited . max ) {
314303 const targetScrollIndex =
315- newIndex - ( newDuration - adjustedLimited . max ) ;
304+ newValues . index -
305+ ( newValues . duration - adjustedLimited . max ) ;
316306 flatListRef . current ?. scrollToIndex ( {
317307 animated : true ,
318308 index :
@@ -321,27 +311,28 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
321311 ? targetScrollIndex
322312 : adjustedLimited . max - 1 ,
323313 } ) ; // 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 ) {
326316 const targetScrollIndex =
327- newIndex + ( adjustedLimited . min - newDuration ) ;
317+ newValues . index +
318+ ( adjustedLimited . min - newValues . duration ) ;
328319 flatListRef . current ?. scrollToIndex ( {
329320 animated : true ,
330321 index :
331322 // guard against scrolling beyond end of list
332- targetScrollIndex <= data . length - 1
323+ targetScrollIndex <= numbersForFlatList . length - 1
333324 ? targetScrollIndex
334325 : adjustedLimited . min ,
335326 } ) ; // scroll up to min
336- newDuration = adjustedLimited . min ;
327+ newValues . duration = adjustedLimited . min ;
337328 }
338329
339- onDurationChange ( newDuration ) ;
330+ onDurationChange ( newValues . duration ) ;
340331 } ,
341332 [
342333 adjustedLimited . max ,
343334 adjustedLimited . min ,
344- data . length ,
335+ numbersForFlatList . length ,
345336 disableInfiniteScroll ,
346337 numberOfItems ,
347338 onDurationChange ,
@@ -363,15 +354,15 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
363354 } else if (
364355 viewableItems [ 0 ] ?. index &&
365356 viewableItems [ 0 ] . index >=
366- numberOfItems * ( repeatNumbersNTimes - 0.5 )
357+ numberOfItems * ( safeRepeatNumbersNTimes - 0.5 )
367358 ) {
368359 flatListRef . current ?. scrollToIndex ( {
369360 animated : false ,
370- index : viewableItems [ 0 ] . index - numberOfItems - 1 ,
361+ index : viewableItems [ 0 ] . index - numberOfItems ,
371362 } ) ;
372363 }
373364 } ,
374- [ numberOfItems , repeatNumbersNTimes ]
365+ [ numberOfItems , safeRepeatNumbersNTimes ]
375366 ) ;
376367
377368 const getItemLayout = useCallback (
@@ -391,6 +382,28 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
391382 } ,
392383 ] ) ;
393384
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+
394407 return (
395408 < View
396409 pointerEvents = { isDisabled ? "none" : undefined }
@@ -406,7 +419,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
406419 testID = { testID } >
407420 < FlatList
408421 ref = { flatListRef }
409- data = { data }
422+ data = { numbersForFlatList }
410423 decelerationRate = { 0.88 }
411424 getItemLayout = { getItemLayout }
412425 initialScrollIndex = { initialScrollIndex }
@@ -420,7 +433,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
420433 showsVerticalScrollIndicator = { false }
421434 snapToAlignment = "start"
422435 // used in place of snapToOffset due to bug on Android
423- snapToOffsets = { [ ...Array ( data . length ) ] . map (
436+ snapToOffsets = { [ ...Array ( numbersForFlatList . length ) ] . map (
424437 ( _ , i ) => i * styles . pickerItemContainer . height
425438 ) }
426439 testID = "duration-scroll-flatlist"
0 commit comments