Skip to content

Commit 4af4fc3

Browse files
committed
Merge branch 'release-v1.3.0'
2 parents d1c954b + 4e4ae1d commit 4af4fc3

File tree

7 files changed

+119
-24
lines changed

7 files changed

+119
-24
lines changed

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# React Native Timer Picker ⏰🕰️⏳
22

33
[![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=for-the-badge)]()
4-
![platforms](https://img.shields.io/badge/platforms-Android%20%7C%20iOS%20%7C%20Web-brightgreen.svg?style=for-the-badge&colorB=191A17)
4+
![platforms](https://img.shields.io/badge/platforms-Android%20%7C%20iOS-brightgreen.svg?style=for-the-badge&colorB=191A17)
55
[![Version](https://img.shields.io/npm/v/react-native-timer-picker.svg?style=for-the-badge)](https://www.npmjs.com/package/react-native-timer-picker)
66
[![npm](https://img.shields.io/npm/dt/react-native-timer-picker.svg?style=for-the-badge)](https://www.npmjs.com/package/react-native-timer-picker)
77

@@ -33,7 +33,7 @@ Works with Expo and bare React Native apps.
3333

3434
## Demos 📱
3535

36-
**Try it out for yourself on [Expo Snack](https://snack.expo.dev/@nuumi/react-native-timer-picker-demo)!** Make sure to run it on iOS/Android to try it out properly.
36+
**Try it out for yourself on [Expo Snack](https://snack.expo.dev/@nuumi/react-native-timer-picker-demo)!** Make sure to run it on iOS/Android to see it working properly.
3737

3838
<p>
3939
<img src="demos/example1.gif" width="250" height="550" style="margin-right:50px"/>
@@ -331,6 +331,7 @@ return (
331331
| minuteLabel | Label for the minutes picker | String \| React.ReactElement | m | false |
332332
| secondLabel | Label for the seconds picker | String \| React.ReactElement | s | false |
333333
| padWithNItems | Number of items to pad the picker with on either side | Number | 1 | false |
334+
| aggressivelyGetLatestDuration | Set to True to ask DurationScroll to aggressively update the latestDuration ref | Boolean | false | false |
334335
| disableInfiniteScroll | Disable the infinite scroll feature | Boolean | false | false |
335336
| LinearGradient | Linear Gradient Component | [expo-linear-gradient](https://www.npmjs.com/package/expo-linear-gradient).LinearGradient or [react-native-linear-gradient](https://www.npmjs.com/package/react-native-linear-gradient).default | - | false |
336337
| pickerContainerProps | Props for the picker container | `React.ComponentProps<typeof View>` | - | false |
@@ -419,6 +420,19 @@ timerPickerRef.current.reset(options?: { animated: boolean });
419420
timerPickerRef.current.setValue({ hours: number, minutes: number, seconds: number }, options?: { animated: boolean });
420421
```
421422

423+
It also exposes the following ref object:
424+
425+
`latestDuration` - provides access to the latest duration (even during scrolls). **This only works if `aggressivelyGetLatestDuration` is set to True (as in TimerPickerModal).** It is used internally to ensure that the latest duration is returned in `TimerPickerModal` on pressing the confirm button, even if the inputs are still scrolling.
426+
427+
```javascript
428+
const latestDuration = timerPickerRef.current?.latestDuration;
429+
const newDuration = {
430+
hours: latestDuration?.hours?.current,
431+
minutes: latestDuration?.minutes?.current,
432+
seconds: latestDuration?.seconds?.current,
433+
};
434+
```
435+
422436
### TimerPickerModal
423437
424438
An identical ref is also exposed for the TimerPickerModal component.

example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"expo": "^49.0.16",
1313
"expo-linear-gradient": "~12.3.0",
1414
"react": "18.2.0",
15-
"react-native": "0.72.5"
15+
"react-native": "0.72.6"
1616
},
1717
"devDependencies": {
1818
"@babel/core": ">=7.20.0",

example/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6377,10 +6377,10 @@ react-is@^17.0.1:
63776377
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
63786378
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
63796379

6380-
6381-
version "0.72.5"
6382-
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.72.5.tgz#2c343fa6f3ead362cf07376634a33a4078864357"
6383-
integrity sha512-oIewslu5DBwOmo7x5rdzZlZXCqDIna0R4dUwVpfmVteORYLr4yaZo5wQnMeR+H7x54GaMhmgeqp0ZpULtulJFg==
6380+
6381+
version "0.72.6"
6382+
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.72.6.tgz#9f8d090694907e2f83af22e115cc0e4a3d5fa626"
6383+
integrity sha512-RafPY2gM7mcrFySS8TL8x+TIO3q7oAlHpzEmC7Im6pmXni6n1AuufGaVh0Narbr1daxstw7yW7T9BKW5dpVc2A==
63846384
dependencies:
63856385
"@jest/create-cache-key-function" "^29.2.1"
63866386
"@react-native-community/cli" "11.3.7"

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
"url": "https://github.com/troberts-28"
77
},
88
"license": "MIT",
9-
"version": "1.2.11",
9+
"version": "1.3.0",
1010
"main": "dist/commonjs/index.js",
1111
"types": "dist/typescript/src/index.d.ts",
1212
"scripts": {
1313
"test": "jest --forceExit --silent",
1414
"build": "bob build",
1515
"clean": "rm yarn.lock && rm -rf ./node_modules && yarn install",
16-
"start": "cp -Rf src example && cd example && yarn add expo && npx expo install && npx expo start",
16+
"start": "cp -Rf src example && cd example && npx expo install && npx expo start",
1717
"lint": "eslint --ext .ts,.tsx .",
1818
"lint:fix": "eslint --ext .ts,.tsx . --fix",
1919
"prepare": "yarn build"

src/components/TimerPicker/DurationScroll.tsx

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, {
33
useCallback,
44
forwardRef,
55
useImperativeHandle,
6+
MutableRefObject,
67
} from "react";
78
import {
89
View,
@@ -23,6 +24,7 @@ import { getScrollIndex } from "../../utils/getScrollIndex";
2324
export interface DurationScrollRef {
2425
reset: (options?: { animated?: boolean }) => void;
2526
setValue: (value: number, options?: { animated?: boolean }) => void;
27+
latestDuration: MutableRefObject<number>;
2628
}
2729

2830
type LinearGradientPoint = {
@@ -50,6 +52,7 @@ interface DurationScrollProps {
5052
padNumbersWithZero?: boolean;
5153
disableInfiniteScroll?: boolean;
5254
limit?: LimitType;
55+
aggressivelyGetLatestDuration: boolean;
5356
padWithNItems: number;
5457
pickerGradientOverlayProps?: Partial<LinearGradientProps>;
5558
topPickerGradientOverlayProps?: Partial<LinearGradientProps>;
@@ -73,6 +76,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
7376
padNumbersWithZero = false,
7477
disableInfiniteScroll = false,
7578
limit,
79+
aggressivelyGetLatestDuration,
7680
padWithNItems,
7781
pickerGradientOverlayProps,
7882
topPickerGradientOverlayProps,
@@ -83,8 +87,6 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
8387
},
8488
ref
8589
): React.ReactElement => {
86-
const flatListRef = useRef<FlatList | null>(null);
87-
8890
const data = generateNumbers(numberOfItems, {
8991
padWithZero: padNumbersWithZero,
9092
repeatNTimes: 3,
@@ -103,6 +105,10 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
103105
disableInfiniteScroll,
104106
});
105107

108+
const latestDuration = useRef(0);
109+
110+
const flatListRef = useRef<FlatList | null>(null);
111+
106112
useImperativeHandle(ref, () => ({
107113
reset: (options) => {
108114
flatListRef.current?.scrollToIndex({
@@ -121,6 +127,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
121127
}),
122128
});
123129
},
130+
latestDuration: latestDuration,
124131
}));
125132

126133
const renderItem = useCallback(
@@ -154,6 +161,39 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
154161
]
155162
);
156163

164+
const onScroll = useCallback(
165+
(e: NativeSyntheticEvent<NativeScrollEvent>) => {
166+
// this function is only used when the picker is in a modal
167+
// it is used to ensure that the modal gets the latest duration on clicking
168+
// the confirm button, even if the scrollview is still scrolling
169+
const newIndex = Math.round(
170+
e.nativeEvent.contentOffset.y /
171+
styles.pickerItemContainer.height
172+
);
173+
let newDuration =
174+
(disableInfiniteScroll
175+
? newIndex
176+
: newIndex + padWithNItems) %
177+
(numberOfItems + 1);
178+
179+
// check limits
180+
if (newDuration > adjustedLimited.max) {
181+
newDuration = adjustedLimited.max;
182+
} else if (newDuration < adjustedLimited.min) {
183+
newDuration = adjustedLimited.min;
184+
}
185+
latestDuration.current = newDuration;
186+
},
187+
[
188+
adjustedLimited.max,
189+
adjustedLimited.min,
190+
disableInfiniteScroll,
191+
numberOfItems,
192+
padWithNItems,
193+
styles.pickerItemContainer.height,
194+
]
195+
);
196+
157197
const onMomentumScrollEnd = useCallback(
158198
(e: NativeSyntheticEvent<NativeScrollEvent>) => {
159199
const newIndex = Math.round(
@@ -264,7 +304,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
264304
renderItem={renderItem}
265305
keyExtractor={KEY_EXTRACTOR}
266306
showsVerticalScrollIndicator={false}
267-
decelerationRate={0.9}
307+
decelerationRate={0.88}
268308
scrollEventThrottle={16}
269309
snapToAlignment="start"
270310
// used in place of snapToOffset due to bug on Android
@@ -277,6 +317,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
277317
: undefined
278318
}
279319
onMomentumScrollEnd={onMomentumScrollEnd}
320+
onScroll={aggressivelyGetLatestDuration ? onScroll : undefined}
280321
testID="duration-scroll-flatlist"
281322
/>
282323
<View style={styles.pickerLabelContainer} pointerEvents="none">

src/components/TimerPicker/index.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, {
2+
MutableRefObject,
23
forwardRef,
34
useEffect,
45
useImperativeHandle,
@@ -23,6 +24,11 @@ export interface TimerPickerRef {
2324
},
2425
options?: { animated?: boolean }
2526
) => void;
27+
latestDuration: {
28+
hours: MutableRefObject<number> | undefined;
29+
minutes: MutableRefObject<number> | undefined;
30+
seconds: MutableRefObject<number> | undefined;
31+
};
2632
}
2733

2834
export interface TimerPickerProps {
@@ -34,6 +40,7 @@ export interface TimerPickerProps {
3440
initialHours?: number;
3541
initialMinutes?: number;
3642
initialSeconds?: number;
43+
aggressivelyGetLatestDuration?: boolean;
3744
hideHours?: boolean;
3845
hideMinutes?: boolean;
3946
hideSeconds?: boolean;
@@ -72,6 +79,7 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
7279
secondLabel = "s",
7380
padWithNItems = 1,
7481
disableInfiniteScroll = false,
82+
aggressivelyGetLatestDuration = false,
7583
LinearGradient,
7684
pickerContainerProps,
7785
pickerGradientOverlayProps,
@@ -133,6 +141,11 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
133141
options
134142
);
135143
},
144+
latestDuration: {
145+
hours: hoursDurationScrollRef.current?.latestDuration,
146+
minutes: minutesDurationScrollRef.current?.latestDuration,
147+
seconds: secondsDurationScrollRef.current?.latestDuration,
148+
},
136149
}));
137150

138151
return (
@@ -146,6 +159,7 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
146159
numberOfItems={23}
147160
label={hourLabel}
148161
initialValue={initialHours}
162+
aggressivelyGetLatestDuration={aggressivelyGetLatestDuration}
149163
onDurationChange={setSelectedHours}
150164
pickerGradientOverlayProps={pickerGradientOverlayProps}
151165
topPickerGradientOverlayProps={
@@ -168,6 +182,7 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
168182
numberOfItems={59}
169183
label={minuteLabel}
170184
initialValue={initialMinutes}
185+
aggressivelyGetLatestDuration={aggressivelyGetLatestDuration}
171186
onDurationChange={setSelectedMinutes}
172187
padNumbersWithZero
173188
pickerGradientOverlayProps={pickerGradientOverlayProps}
@@ -191,6 +206,7 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
191206
numberOfItems={59}
192207
label={secondLabel}
193208
initialValue={initialSeconds}
209+
aggressivelyGetLatestDuration={aggressivelyGetLatestDuration}
194210
onDurationChange={setSelectedSeconds}
195211
padNumbersWithZero
196212
pickerGradientOverlayProps={pickerGradientOverlayProps}

0 commit comments

Comments
 (0)