react-native-timer-picker 2.1.1 → 2.2.1

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.
Files changed (139) hide show
  1. package/README.md +250 -174
  2. package/dist/commonjs/components/DurationScroll/DurationScroll.js +417 -0
  3. package/dist/commonjs/components/DurationScroll/DurationScroll.js.map +1 -0
  4. package/dist/commonjs/components/DurationScroll/index.js +20 -392
  5. package/dist/commonjs/components/DurationScroll/index.js.map +1 -1
  6. package/dist/commonjs/components/DurationScroll/types.js.map +1 -1
  7. package/dist/commonjs/components/Modal/Modal.js +107 -0
  8. package/dist/commonjs/components/Modal/Modal.js.map +1 -0
  9. package/dist/commonjs/components/Modal/index.js +20 -101
  10. package/dist/commonjs/components/Modal/index.js.map +1 -1
  11. package/dist/commonjs/components/TimerPicker/TimerPicker.js +225 -0
  12. package/dist/commonjs/components/TimerPicker/TimerPicker.js.map +1 -0
  13. package/dist/commonjs/components/TimerPicker/index.js +29 -166
  14. package/dist/commonjs/components/TimerPicker/index.js.map +1 -1
  15. package/dist/commonjs/components/TimerPicker/styles.js.map +1 -1
  16. package/dist/commonjs/components/TimerPicker/types.js.map +1 -1
  17. package/dist/commonjs/components/TimerPickerModal/TimerPickerModal.js +143 -0
  18. package/dist/commonjs/components/TimerPickerModal/TimerPickerModal.js.map +1 -0
  19. package/dist/commonjs/components/TimerPickerModal/index.js +30 -131
  20. package/dist/commonjs/components/TimerPickerModal/index.js.map +1 -1
  21. package/dist/commonjs/components/TimerPickerModal/types.js.map +1 -1
  22. package/dist/commonjs/index.js +9 -13
  23. package/dist/commonjs/index.js.map +1 -1
  24. package/dist/commonjs/tests/DurationScroll.test.js +94 -0
  25. package/dist/commonjs/tests/DurationScroll.test.js.map +1 -1
  26. package/dist/commonjs/tests/Modal.test.js +79 -2
  27. package/dist/commonjs/tests/Modal.test.js.map +1 -1
  28. package/dist/commonjs/tests/TimerPicker.test.js +119 -1
  29. package/dist/commonjs/tests/TimerPicker.test.js.map +1 -1
  30. package/dist/commonjs/tests/TimerPickerModal.test.js +120 -0
  31. package/dist/commonjs/tests/TimerPickerModal.test.js.map +1 -1
  32. package/dist/commonjs/tests/colorToRgba.test.js +176 -0
  33. package/dist/commonjs/tests/colorToRgba.test.js.map +1 -0
  34. package/dist/commonjs/tests/generateNumbers.test.js +350 -0
  35. package/dist/commonjs/tests/generateNumbers.test.js.map +1 -0
  36. package/dist/commonjs/tests/getAdjustedLimit.test.js +324 -0
  37. package/dist/commonjs/tests/getAdjustedLimit.test.js.map +1 -0
  38. package/dist/commonjs/tests/getDurationAndIndexFromScrollOffset.test.js +424 -0
  39. package/dist/commonjs/tests/getDurationAndIndexFromScrollOffset.test.js.map +1 -0
  40. package/dist/commonjs/tests/getInitialScrollIndex.test.js +396 -0
  41. package/dist/commonjs/tests/getInitialScrollIndex.test.js.map +1 -0
  42. package/dist/commonjs/tests/getSafeInitialValue.test.js +497 -0
  43. package/dist/commonjs/tests/getSafeInitialValue.test.js.map +1 -0
  44. package/dist/commonjs/tests/padNumber.test.js +301 -0
  45. package/dist/commonjs/tests/padNumber.test.js.map +1 -0
  46. package/dist/commonjs/utils/colorToRgba.js +40 -4
  47. package/dist/commonjs/utils/colorToRgba.js.map +1 -1
  48. package/dist/commonjs/utils/generateNumbers.js +67 -0
  49. package/dist/commonjs/utils/generateNumbers.js.map +1 -1
  50. package/dist/commonjs/utils/getAdjustedLimit.js +28 -3
  51. package/dist/commonjs/utils/getAdjustedLimit.js.map +1 -1
  52. package/dist/commonjs/utils/getDurationAndIndexFromScrollOffset.js +38 -0
  53. package/dist/commonjs/utils/getDurationAndIndexFromScrollOffset.js.map +1 -1
  54. package/dist/commonjs/utils/getInitialScrollIndex.js +38 -0
  55. package/dist/commonjs/utils/getInitialScrollIndex.js.map +1 -1
  56. package/dist/commonjs/utils/getSafeInitialValue.js +28 -0
  57. package/dist/commonjs/utils/getSafeInitialValue.js.map +1 -1
  58. package/dist/commonjs/utils/padNumber.js +25 -0
  59. package/dist/commonjs/utils/padNumber.js.map +1 -1
  60. package/dist/module/components/DurationScroll/DurationScroll.js +410 -0
  61. package/dist/module/components/DurationScroll/DurationScroll.js.map +1 -0
  62. package/dist/module/components/DurationScroll/index.js +2 -390
  63. package/dist/module/components/DurationScroll/index.js.map +1 -1
  64. package/dist/module/components/DurationScroll/types.js.map +1 -1
  65. package/dist/module/components/Modal/Modal.js +99 -0
  66. package/dist/module/components/Modal/Modal.js.map +1 -0
  67. package/dist/module/components/Modal/index.js +2 -98
  68. package/dist/module/components/Modal/index.js.map +1 -1
  69. package/dist/module/components/TimerPicker/TimerPicker.js +217 -0
  70. package/dist/module/components/TimerPicker/TimerPicker.js.map +1 -0
  71. package/dist/module/components/TimerPicker/index.js +3 -166
  72. package/dist/module/components/TimerPicker/index.js.map +1 -1
  73. package/dist/module/components/TimerPicker/styles.js.map +1 -1
  74. package/dist/module/components/TimerPicker/types.js.map +1 -1
  75. package/dist/module/components/TimerPickerModal/TimerPickerModal.js +135 -0
  76. package/dist/module/components/TimerPickerModal/TimerPickerModal.js.map +1 -0
  77. package/dist/module/components/TimerPickerModal/index.js +3 -130
  78. package/dist/module/components/TimerPickerModal/index.js.map +1 -1
  79. package/dist/module/components/TimerPickerModal/types.js.map +1 -1
  80. package/dist/module/index.js +2 -6
  81. package/dist/module/index.js.map +1 -1
  82. package/dist/module/tests/DurationScroll.test.js +94 -0
  83. package/dist/module/tests/DurationScroll.test.js.map +1 -1
  84. package/dist/module/tests/Modal.test.js +80 -3
  85. package/dist/module/tests/Modal.test.js.map +1 -1
  86. package/dist/module/tests/TimerPicker.test.js +119 -1
  87. package/dist/module/tests/TimerPicker.test.js.map +1 -1
  88. package/dist/module/tests/TimerPickerModal.test.js +121 -1
  89. package/dist/module/tests/TimerPickerModal.test.js.map +1 -1
  90. package/dist/module/tests/colorToRgba.test.js +174 -0
  91. package/dist/module/tests/colorToRgba.test.js.map +1 -0
  92. package/dist/module/tests/generateNumbers.test.js +348 -0
  93. package/dist/module/tests/generateNumbers.test.js.map +1 -0
  94. package/dist/module/tests/getAdjustedLimit.test.js +322 -0
  95. package/dist/module/tests/getAdjustedLimit.test.js.map +1 -0
  96. package/dist/module/tests/getDurationAndIndexFromScrollOffset.test.js +422 -0
  97. package/dist/module/tests/getDurationAndIndexFromScrollOffset.test.js.map +1 -0
  98. package/dist/module/tests/getInitialScrollIndex.test.js +394 -0
  99. package/dist/module/tests/getInitialScrollIndex.test.js.map +1 -0
  100. package/dist/module/tests/getSafeInitialValue.test.js +495 -0
  101. package/dist/module/tests/getSafeInitialValue.test.js.map +1 -0
  102. package/dist/module/tests/padNumber.test.js +299 -0
  103. package/dist/module/tests/padNumber.test.js.map +1 -0
  104. package/dist/module/utils/colorToRgba.js +40 -4
  105. package/dist/module/utils/colorToRgba.js.map +1 -1
  106. package/dist/module/utils/generateNumbers.js +68 -0
  107. package/dist/module/utils/generateNumbers.js.map +1 -1
  108. package/dist/module/utils/getAdjustedLimit.js +28 -3
  109. package/dist/module/utils/getAdjustedLimit.js.map +1 -1
  110. package/dist/module/utils/getDurationAndIndexFromScrollOffset.js +38 -0
  111. package/dist/module/utils/getDurationAndIndexFromScrollOffset.js.map +1 -1
  112. package/dist/module/utils/getInitialScrollIndex.js +38 -0
  113. package/dist/module/utils/getInitialScrollIndex.js.map +1 -1
  114. package/dist/module/utils/getSafeInitialValue.js +28 -0
  115. package/dist/module/utils/getSafeInitialValue.js.map +1 -1
  116. package/dist/module/utils/padNumber.js +25 -0
  117. package/dist/module/utils/padNumber.js.map +1 -1
  118. package/dist/typescript/components/DurationScroll/DurationScroll.d.ts +4 -0
  119. package/dist/typescript/components/DurationScroll/index.d.ts +2 -4
  120. package/dist/typescript/components/DurationScroll/types.d.ts +13 -9
  121. package/dist/typescript/components/Modal/Modal.d.ts +5 -0
  122. package/dist/typescript/components/Modal/index.d.ts +2 -5
  123. package/dist/typescript/components/TimerPicker/TimerPicker.d.ts +4 -0
  124. package/dist/typescript/components/TimerPicker/index.d.ts +3 -4
  125. package/dist/typescript/components/TimerPicker/styles.d.ts +922 -771
  126. package/dist/typescript/components/TimerPicker/types.d.ts +26 -10
  127. package/dist/typescript/components/TimerPickerModal/TimerPickerModal.d.ts +4 -0
  128. package/dist/typescript/components/TimerPickerModal/index.d.ts +3 -4
  129. package/dist/typescript/components/TimerPickerModal/styles.d.ts +570 -474
  130. package/dist/typescript/components/TimerPickerModal/types.d.ts +8 -5
  131. package/dist/typescript/index.d.ts +2 -6
  132. package/dist/typescript/utils/colorToRgba.d.ts +34 -0
  133. package/dist/typescript/utils/generateNumbers.d.ts +66 -0
  134. package/dist/typescript/utils/getAdjustedLimit.d.ts +27 -2
  135. package/dist/typescript/utils/getDurationAndIndexFromScrollOffset.d.ts +38 -0
  136. package/dist/typescript/utils/getInitialScrollIndex.d.ts +38 -0
  137. package/dist/typescript/utils/getSafeInitialValue.d.ts +29 -0
  138. package/dist/typescript/utils/padNumber.d.ts +25 -0
  139. package/package.json +12 -30
package/README.md CHANGED
@@ -13,33 +13,37 @@ Works with Expo and bare React Native apps ✅
13
13
 
14
14
  Includes iOS-style haptic and audio feedback 🍏
15
15
 
16
- - [Demos 📱](#demos-)
17
- - [Peer Dependencies 👶](#peer-dependencies-)
18
- - [Linear Gradient](#linear-gradient)
19
- - [Haptic Feedback](#haptic-feedback)
20
- - [Audio Feedback (Click Sound)](#audio-feedback-click-sound)
21
- - [Installation 🚀](#installation-)
22
- - [Examples 😎](#examples-)
23
- - [Timer Picker Modal (Dark Mode) 🌚](#timer-picker-modal-dark-mode-)
24
- - [Timer Picker Modal (Light Mode) 🌞](#timer-picker-modal-light-mode-)
25
- - [Timer Picker with Transparent Fade-Out (Dark Mode) 🌒](#timer-picker-with-transparent-fade-out-dark-mode-)
26
- - [Timer Picker with Customisation (Light Mode) 🌔](#timer-picker-with-customisation-light-mode-)
27
- - [Props 💅](#props-)
28
- - [TimerPicker ⏲️](#timerpicker-️)
29
- - [Custom Styles 👗](#custom-styles-)
30
- - [Performance](#performance)
31
- - [Custom FlatList](#custom-flatlist)
32
- - [Generic feedback](#generic-feedback)
33
- - [TimerPickerModal ](#timerpickermodal-)
34
- - [Custom Styles 👕](#custom-styles--1)
35
- - [Methods 🔄](#methods-)
36
- - [TimerPicker](#timerpicker)
37
- - [TimerPickerModal](#timerpickermodal)
38
- - [Contributing 🧑‍🤝‍🧑](#contributing-)
39
- - [Dev Setup](#dev-setup)
40
- - [GitHub Guidelines](#github-guidelines)
41
- - [Limitations ](#limitations-)
42
- - [License 📝](#license-)
16
+ - [React Native Timer Picker ⏰🕰️⏳](#react-native-timer-picker-️)
17
+ - [Demos 📱](#demos-)
18
+ - [Installation 🚀](#installation-)
19
+ - [Peer Dependencies 👶](#peer-dependencies-)
20
+ - [Linear Gradient](#linear-gradient)
21
+ - [Masked View](#masked-view)
22
+ - [Examples 😎](#examples-)
23
+ - [Timer Picker Modal (Dark Mode) 🌚](#timer-picker-modal-dark-mode-)
24
+ - [Timer Picker Modal (Light Mode) 🌞](#timer-picker-modal-light-mode-)
25
+ - [Timer Picker with Transparent Fade-Out (Dark Mode) 🌒](#timer-picker-with-transparent-fade-out-dark-mode-)
26
+ - [Timer Picker with Customisation (Light Mode) 🌔](#timer-picker-with-customisation-light-mode-)
27
+ - [Props 💅](#props-)
28
+ - [TimerPicker ⏲️](#timerpicker-️)
29
+ - [Custom Styles 👗](#custom-styles-)
30
+ - [Performance](#performance)
31
+ - [Custom FlatList](#custom-flatlist)
32
+ - [TimerPickerModal ](#timerpickermodal-)
33
+ - [Custom Styles 👕](#custom-styles--1)
34
+ - [Methods 🔄](#methods-)
35
+ - [TimerPicker](#timerpicker)
36
+ - [TimerPickerModal](#timerpickermodal)
37
+ - [Picker Feedback 📳🔉](#picker-feedback-)
38
+ - [Audio Feedack](#audio-feedack)
39
+ - [Haptic Feedback](#haptic-feedback)
40
+ - [Feedback Example](#feedback-example)
41
+ - [Expo-Specific Audio/Haptic Feedback (DEPRECATED)](#expo-specific-audiohaptic-feedback-deprecated)
42
+ - [Contributing 🧑‍🤝‍🧑](#contributing-)
43
+ - [Dev Setup](#dev-setup)
44
+ - [GitHub Guidelines](#github-guidelines)
45
+ - [Limitations ⚠](#limitations-)
46
+ - [License 📝](#license-)
43
47
 
44
48
  <br>
45
49
 
@@ -58,11 +62,27 @@ Includes iOS-style haptic and audio feedback 🍏
58
62
 
59
63
  <br>
60
64
 
61
- ## Peer Dependencies 👶
65
+ ## Installation 🚀
66
+
67
+ Supports React Native >= 0.72.0 and React >= 18.2.0.
68
+
69
+ Just run:
70
+
71
+ ```bash
72
+ npm install react-native-timer-picker
73
+ ```
74
+
75
+ or
76
+
77
+ ```bash
78
+ yarn add react-native-timer-picker
79
+ ```
62
80
 
63
- This component will work in your React Native Project **_without any peer dependencies_**. However, to enable certain additional features (e.g. fade-out, feedback) you will need to supply various libraries as props. These are detailed below.
81
+ ### Peer Dependencies 👶
64
82
 
65
- ### Linear Gradient
83
+ This component will work in your React Native Project **_without any peer dependencies_**. However, to enable certain additional features (e.g. fade-out) you will need to supply various libraries as props. These are detailed below.
84
+
85
+ #### Linear Gradient
66
86
 
67
87
  If you want the numbers to fade in/out at the top and bottom of the picker, you will need to install either:
68
88
 
@@ -71,7 +91,7 @@ If you want the numbers to fade in/out at the top and bottom of the picker, you
71
91
 
72
92
  **To enable the linear gradient, you need to supply the component as a prop to either TimerPickerModal or TimerPicker.**
73
93
 
74
- ### Masked View
94
+ #### Masked View
75
95
 
76
96
  To make the numbers fade in/out on a transparent background (e.g. if the picker is rendered on top of a gradient or image), you will need to install the [@react-native-masked-view/masked-view
77
97
  ](https://www.npmjs.com/package/@react-native-masked-view/masked-view) component. This is as the standard LinearGradient implementation relies on there being a solid background colour. You then just need to set `backgroundColor: "transparent` on the `TimerPicker` styles prop.
@@ -80,47 +100,6 @@ To make the numbers fade in/out on a transparent background (e.g. if the picker
80
100
 
81
101
  **To enable the fade-out on a transparent background, you need to supply the imported `MaskedView` component AND one of the LinearGradient components as props to either TimerPickerModal or TimerPicker. (see [this example](#timer-picker-with-transparent-fade-out-dark-mode-))**
82
102
 
83
- ### Haptic Feedback
84
-
85
- Enable haptic feedback with the [expo-haptics](https://www.npmjs.com/package/expo-haptics) module:
86
-
87
- `import * as Haptics from "expo-haptics";`
88
-
89
- **To enable haptic feedback, you need to supply the imported `Haptics` namespace as a prop to either TimerPickerModal or TimerPicker.**
90
-
91
- [Generic feedback](#generic-feedback) support is possible with the `pickerFeeback` prop.
92
-
93
- ### Audio Feedback (Click Sound)
94
-
95
- Enable audio feedback with the [expo-av](https://www.npmjs.com/package/expo-av) module:
96
-
97
- `import { Audio } from "expo-av";`
98
-
99
- **To enable audio feedback, you need to supply the imported `Audio` class as a prop to either TimerPickerModal or TimerPicker.**
100
-
101
- Please note that the default click sound uses a hosted mp3 file. To make the click sound work offline, you need to supply your own
102
- sound asset through the `clickSoundAsset` prop. You can download the default click sound [here](https://drive.google.com/uc?export=download&id=10e1YkbNsRh-vGx1jmS1Nntz8xzkBp4_I).
103
-
104
- [Generic feedback](#generic-feedback) support is possible with the `pickerFeeback` prop.
105
-
106
- <br>
107
-
108
- ## Installation 🚀
109
-
110
- Supports React Native >= 0.59.0 and React >= 16.8.0.
111
-
112
- Just run:
113
-
114
- ```bash
115
- npm install react-native-timer-picker
116
- ```
117
-
118
- or
119
-
120
- ```bash
121
- yarn add react-native-timer-picker
122
- ```
123
-
124
103
  <br>
125
104
 
126
105
  ## Examples 😎
@@ -130,8 +109,6 @@ yarn add react-native-timer-picker
130
109
  ```jsx
131
110
  import { TimerPickerModal } from "react-native-timer-picker";
132
111
  import { LinearGradient } from "expo-linear-gradient"; // or `import LinearGradient from "react-native-linear-gradient"`
133
- import { Audio } from "expo-av"; // for audio feedback (click sound as you scroll)
134
- import * as Haptics from "expo-haptics"; // for haptic feedback
135
112
 
136
113
  ....
137
114
  const [showPicker, setShowPicker] = useState(false);
@@ -210,9 +187,7 @@ return (
210
187
  modalTitle="Set Alarm"
211
188
  onCancel={() => setShowPicker(false)}
212
189
  closeOnOverlayPress
213
- Audio={Audio}
214
190
  LinearGradient={LinearGradient}
215
- Haptics={Haptics}
216
191
  styles={{
217
192
  theme: "dark",
218
193
  }}
@@ -232,8 +207,6 @@ return (
232
207
  ```jsx
233
208
  import { TimerPickerModal } from "react-native-timer-picker";
234
209
  import { LinearGradient } from "expo-linear-gradient"; // or `import LinearGradient from "react-native-linear-gradient"`
235
- import { Audio } from "expo-av"; // for audio feedback (click sound as you scroll)
236
- import * as Haptics from "expo-haptics"; // for haptic feedback
237
210
 
238
211
  ....
239
212
  const [showPicker, setShowPicker] = useState(false);
@@ -312,11 +285,7 @@ return (
312
285
  onCancel={() => setShowPicker(false)}
313
286
  closeOnOverlayPress
314
287
  use12HourPicker
315
- Audio={Audio}
316
- // supply your own custom click sound asset
317
- clickSoundAsset={require("./assets/custom_click.mp3")}
318
288
  LinearGradient={LinearGradient}
319
- Haptics={Haptics}
320
289
  styles={{
321
290
  theme: "light",
322
291
  }}
@@ -334,8 +303,6 @@ return (
334
303
  import { TimerPicker } from "react-native-timer-picker";
335
304
  import MaskedView from "@react-native-masked-view/masked-view"; // for transparent fade-out
336
305
  import { LinearGradient } from "expo-linear-gradient"; // or `import LinearGradient from "react-native-linear-gradient"`
337
- import { Audio } from "expo-av"; // for audio feedback (click sound as you scroll)
338
- import * as Haptics from "expo-haptics"; // for haptic feedback
339
306
 
340
307
  ....
341
308
  const [showPicker, setShowPicker] = useState(false);
@@ -354,9 +321,7 @@ return (
354
321
  hourLabel=":"
355
322
  minuteLabel=":"
356
323
  secondLabel=""
357
- Audio={Audio}
358
324
  LinearGradient={LinearGradient}
359
- Haptics={Haptics}
360
325
  MaskedView={MaskedView}
361
326
  styles={{
362
327
  theme: "dark",
@@ -395,8 +360,6 @@ return (
395
360
  ```jsx
396
361
  import { TimerPicker } from "react-native-timer-picker";
397
362
  import { LinearGradient } from "expo-linear-gradient"; // or `import LinearGradient from "react-native-linear-gradient"`
398
- import { Audio } from "expo-av"; // for audio feedback (click sound as you scroll)
399
- import * as Haptics from "expo-haptics"; // for haptic feedback
400
363
 
401
364
  ....
402
365
  const [showPicker, setShowPicker] = useState(false);
@@ -411,9 +374,7 @@ return (
411
374
  hideHours
412
375
  minuteLabel="min"
413
376
  secondLabel="sec"
414
- Audio={Audio}
415
377
  LinearGradient={LinearGradient}
416
- Haptics={Haptics}
417
378
  styles={{
418
379
  theme: "light",
419
380
  pickerItem: {
@@ -444,74 +405,83 @@ return (
444
405
 
445
406
  ### TimerPicker ⏲️
446
407
 
447
- | Prop | Description | Type | Default | Required |
448
- | :------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------: | :------: |
449
- | onDurationChange | Callback when the duration changes | `(duration: { hours: number, minutes: number, seconds: number }) => void` | - | false |
450
- | initialValue | Initial value for the picker | `{ hours?: number, minutes?: number, seconds?: number }` | - | false |
451
- | hideHours | Hide the hours picker | Boolean | false | false |
452
- | hideMinutes | Hide the minutes picker | Boolean | false | false |
453
- | hideSeconds | Hide the seconds picker | Boolean | false | false |
454
- | hoursPickerIsDisabled | Disable the hours picker picker | Boolean | false | false |
455
- | minutesPickerIsDisabled | Disable the minutes picker picker | Boolean | false | false |
456
- | secondsPickerIsDisabled | Disable the seconds picker picker | Boolean | false | false |
457
- | hourLimit | Limit on the hours it is possible to select | `{ max?: Number, min?: Number }` | - | false |
458
- | minuteLimit | Limit on the minutes it is possible to select | `{ max?: Number, min?: Number }` | - | false |
459
- | secondLimit | Limit on the seconds it is possible to select | `{ max?: Number, min?: Number }` | - | false |
460
- | maximumHours | The highest value on the hours picker | Number | 23 | false |
461
- | maximumMinutes | The highest value on the minutes picker | Number | 59 | false |
462
- | maximumSeconds | The highest value on the seconds picker | Number | 59 | false |
463
- | hourInterval | The interval between values on the hours picker | Number | 1 | false |
464
- | minuteInterval | The interval between values on the minutes picker | Number | 1 | false |
465
- | secondInterval | The interval between values on the seconds picker | Number | 1 | false |
466
- | hourLabel | Label for the hours picker | String \| React.ReactElement | h | false |
467
- | minuteLabel | Label for the minutes picker | String \| React.ReactElement | m | false |
468
- | secondLabel | Label for the seconds picker | String \| React.ReactElement | s | false |
469
- | padHoursWithZero | Pad single-digit hours in the picker with a zero | Boolean | false | false |
470
- | padMinutesWithZero | Pad single-digit minutes in the picker with a zero | Boolean | true | false |
471
- | padSecondsWithZero | Pad single-digit seconds in the picker with a zero | Boolean | true | false |
472
- | padWithNItems | Number of items to pad the picker with on either side | Number | 1 | false |
473
- | aggressivelyGetLatestDuration | Set to True to ask DurationScroll to aggressively update the latestDuration ref | Boolean | false | false |
474
- | allowFontScaling | Allow font in the picker to scale with accessibility settings | Boolean | false | false |
475
- | use12HourPicker | Switch the hour picker to 12-hour format with an AM / PM label | Boolean | false | false |
476
- | amLabel | Set the AM label if using the 12-hour picker | String | am | false |
477
- | pmLabel | Set the PM label if using the 12-hour picker | String | pm | false |
478
- | repeatHourNumbersNTimes | Set the number of times the list of hours is repeated in the picker | Number | 7 | false |
479
- | repeatMinuteNumbersNTimes | Set the number of times the list of minutes is repeated in the picker | Number | 3 | false |
480
- | repeatSecondNumbersNTimes | Set the number of times the list of seconds is repeated in the picker | Number | 3 | false |
481
- | disableInfiniteScroll | Disable the infinite scroll feature | Boolean | false | false |
482
- | LinearGradient | [Linear Gradient Component (required for picker fade-out)](#linear-gradient) | [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 |
483
- | MaskedView | [Masked View Component (required for picker fade-out on transparent background)](#masked-view) | [@react-native-masked-view/masked-view](https://www.npmjs.com/package/@react-native-masked-view/masked-view).default | - | false |
484
- | Haptics | [Haptics Namespace (required for Haptic feedback)](#haptic-feedback) | [expo-haptics](https://www.npmjs.com/package/expo-haptics) | - | false |
485
- | Audio | [Audio Class (required for audio feedback i.e. click sound)](#audio-feedback-click-sound) | [expo-av](https://www.npmjs.com/package/expo-av).Audio | - | false |
486
- | pickerFeedback | [Generic picker feedback as alternative to the above Expo feedback support](#generic-feedback) | `() => void \| Promise<void> ` | - | false |
487
- | FlatList | FlatList component used internally to implement each picker (hour, minutes and seconds). More info [below](#custom-flatlist) | [react-native](https://reactnative.dev/docs/flatlist).FlatList | `FlatList` from `react-native` | false |
488
- | clickSoundAsset | Custom sound asset for click sound (required for offline click sound - download default [here](https://drive.google.com/uc?export=download&id=10e1YkbNsRh-vGx1jmS1Nntz8xzkBp4_I)) | require(.../somefolderpath) or {uri: www.someurl} | - | false |
489
- | pickerContainerProps | Props for the picker container | `React.ComponentProps<typeof View>` | - | false |
490
- | pickerGradientOverlayProps | Props for the gradient overlay (supply a different `locations` array to adjust its position) overlays | `Partial<LinearGradientProps>` | - | false |
491
- | styles | Custom styles for the timer picker | [CustomTimerPickerStyles](#custom-styles-) | - | false |
408
+ | Prop | Description | Type | Default | Required |
409
+ | :---------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------: | :------: |
410
+ | onDurationChange | Callback when the duration changes | `(duration: { days: number, hours: number, minutes: number, seconds: number }) => void` | - | false |
411
+ | initialValue | Initial value for the picker | `{ days?: number, hours?: number, minutes?: number, seconds?: number }` | - | false |
412
+ | hideDays | Hide the days picker | Boolean | true | false |
413
+ | hideHours | Hide the hours picker | Boolean | false | false |
414
+ | hideMinutes | Hide the minutes picker | Boolean | false | false |
415
+ | hideSeconds | Hide the seconds picker | Boolean | false | false |
416
+ | daysPickerIsDisabled | Disable the days picker | Boolean | false | false |
417
+ | hoursPickerIsDisabled | Disable the hours picker | Boolean | false | false |
418
+ | minutesPickerIsDisabled | Disable the minutes picker | Boolean | false | false |
419
+ | secondsPickerIsDisabled | Disable the seconds picker | Boolean | false | false |
420
+ | dayLimit | Limit on the days it is possible to select | `{ max?: Number, min?: Number }` | - | false |
421
+ | hourLimit | Limit on the hours it is possible to select | `{ max?: Number, min?: Number }` | - | false |
422
+ | minuteLimit | Limit on the minutes it is possible to select | `{ max?: Number, min?: Number }` | - | false |
423
+ | secondLimit | Limit on the seconds it is possible to select | `{ max?: Number, min?: Number }` | - | false |
424
+ | maximumDays | The highest value on the days picker | Number | 23 | false |
425
+ | maximumHours | The highest value on the hours picker | Number | 23 | false |
426
+ | maximumMinutes | The highest value on the minutes picker | Number | 59 | false |
427
+ | maximumSeconds | The highest value on the seconds picker | Number | 59 | false |
428
+ | dayInterval | The interval between values on the days picker | Number | 1 | false |
429
+ | hourInterval | The interval between values on the hours picker | Number | 1 | false |
430
+ | minuteInterval | The interval between values on the minutes picker | Number | 1 | false |
431
+ | secondInterval | The interval between values on the seconds picker | Number | 1 | false |
432
+ | dayLabel | Label for the days picker | String \| React.ReactElement | d | false |
433
+ | hourLabel | Label for the hours picker | String \| React.ReactElement | h | false |
434
+ | minuteLabel | Label for the minutes picker | String \| React.ReactElement | m | false |
435
+ | secondLabel | Label for the seconds picker | String \| React.ReactElement | s | false |
436
+ | padDaysWithZero | Pad single-digit days in the picker with a zero | Boolean | false | false |
437
+ | padHoursWithZero | Pad single-digit hours in the picker with a zero | Boolean | false | false |
438
+ | padMinutesWithZero | Pad single-digit minutes in the picker with a zero | Boolean | true | false |
439
+ | padSecondsWithZero | Pad single-digit seconds in the picker with a zero | Boolean | true | false |
440
+ | padWithNItems | Number of items to pad the picker with on either side | Number | 1 | false |
441
+ | aggressivelyGetLatestDuration | Set to True to ask DurationScroll to aggressively update the latestDuration ref | Boolean | false | false |
442
+ | allowFontScaling | Allow font in the picker to scale with accessibility settings | Boolean | false | false |
443
+ | use12HourPicker | Switch the hour picker to 12-hour format with an AM / PM label | Boolean | false | false |
444
+ | amLabel | Set the AM label if using the 12-hour picker | String | am | false |
445
+ | pmLabel | Set the PM label if using the 12-hour picker | String | pm | false |
446
+ | repeatDayNumbersNTimes | Set the number of times the list of days is repeated in the picker | Number | 3 | false |
447
+ | repeatHourNumbersNTimes | Set the number of times the list of hours is repeated in the picker | Number | 7 | false |
448
+ | repeatMinuteNumbersNTimes | Set the number of times the list of minutes is repeated in the picker | Number | 3 | false |
449
+ | repeatSecondNumbersNTimes | Set the number of times the list of seconds is repeated in the picker | Number | 3 | false |
450
+ | disableInfiniteScroll | Disable the infinite scroll feature | Boolean | false | false |
451
+ | LinearGradient | [Linear Gradient Component (required for picker fade-out)](#linear-gradient) | [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 |
452
+ | MaskedView | [Masked View Component (required for picker fade-out on transparent background)](#masked-view) | [@react-native-masked-view/masked-view](https://www.npmjs.com/package/@react-native-masked-view/masked-view).default | - | false |
453
+ | FlatList | FlatList component used internally to implement each picker (day, hour, minutes and seconds). More info [below](#custom-flatlist) | [react-native](https://reactnative.dev/docs/flatlist).FlatList | `FlatList` from `react-native` | false |
454
+ | pickerFeedback | [Callback for providing audio/haptic feedback](#picker-feedback-) (fired whenever the picker ticks over a value) | `() => void \| Promise<void> ` | - | false |
455
+ | Haptics (DEPRECATED) | [Expo Haptics Namespace](#expo-specific-audiohaptic-feedback-deprecated) (please use pickerFeedback instead) | [expo-haptics](https://www.npmjs.com/package/expo-haptics) | - | false |
456
+ | Audio (DEPRECATED) | [Expo AV Audio Class](#expo-specific-audiohaptic-feedback-deprecated) | [expo-av](https://www.npmjs.com/package/expo-av).Audio (please use pickerFeedback instead) | - | false |
457
+ | clickSoundAsset (DEPRECATED) | Custom sound asset for click sound (please use pickerFeedback instead), was required for offline click sound - download default [here](https://drive.google.com/uc?export=download&id=10e1YkbNsRh-vGx1jmS1Nntz8xzkBp4_I) | require(.../somefolderpath) or {uri: www.someurl} | - | false |
458
+ | pickerContainerProps | Props for the picker container | `React.ComponentProps<typeof View>` | - | false |
459
+ | pickerGradientOverlayProps | Props for the gradient overlay (supply a different `locations` array to adjust its position) overlays | `Partial<LinearGradientProps>` | - | false |
460
+ | styles | Custom styles for the timer picker | [CustomTimerPickerStyles](#custom-styles-) | - | false |
461
+ | decelerationRate | Set how quickly the picker decelerates after the user lifts their finger | 'fast', 'normal', or Number | 0.88 | false |
492
462
 
493
463
  #### Custom Styles 👗
494
464
 
495
465
  The following custom styles can be supplied to re-style the component in any way. Various styles are applied by default - you can take a look at these [here](src/components/TimerPicker/styles.ts).
496
466
 
497
- | Style Prop | Description | Type |
498
- | :---------------------: | :------------------------------------------- | :--------------------------------------: |
499
- | theme | Theme of the component | "light" \| "dark" |
500
- | backgroundColor | Main background color | string |
501
- | text | Base text style | TextStyle |
502
- | pickerContainer | Main container for the picker | ViewStyle & { backgroundColor?: string } |
503
- | pickerLabelContainer | Container for the picker's labels | ViewStyle |
504
- | pickerLabel | Style for the picker's labels | TextStyle |
505
- | pickerAmPmContainer | Style for the picker's labels | ViewStyle |
506
- | pickerAmPmLabel | Style for the picker's labels | TextStyle |
507
- | pickerItemContainer | Container for each number in the picker | ViewStyle & { height?: number } |
508
- | pickerItem | Style for each individual picker number | TextStyle |
509
- | disabledPickerItem | Style for any numbers outside any set limits | TextStyle |
510
- | disabledPickerContainer | Style for disabled pickers | ViewStyle |
511
- | pickerGradientOverlay | Style for the gradient overlay (fade out) | ViewStyle |
512
- | durationScrollFlatList | Style for the Flatlist in each picker | ViewStyle |
513
- | durationScrollFlatListContainer | Style for the View that contains the Flatlist in each picker | ViewStyle |
514
- | durationScrollFlatListContentContainer | Style for the Flatlist's `contentContainerStyle` prop in each picker | ViewStyle |
467
+ | Style Prop | Description | Type |
468
+ | :------------------------------------: | :------------------------------------------------------------------- | :--------------------------------------: |
469
+ | theme | Theme of the component | "light" \| "dark" |
470
+ | backgroundColor | Main background color | string |
471
+ | text | Base text style | TextStyle |
472
+ | pickerContainer | Main container for the picker | ViewStyle & { backgroundColor?: string } |
473
+ | pickerLabelContainer | Container for the picker's labels | ViewStyle |
474
+ | pickerLabel | Style for the picker's labels | TextStyle |
475
+ | pickerAmPmContainer | Style for the picker's labels | ViewStyle |
476
+ | pickerAmPmLabel | Style for the picker's labels | TextStyle |
477
+ | pickerItemContainer | Container for each number in the picker | ViewStyle & { height?: number } |
478
+ | pickerItem | Style for each individual picker number | TextStyle |
479
+ | disabledPickerItem | Style for any numbers outside any set limits | TextStyle |
480
+ | disabledPickerContainer | Style for disabled pickers | ViewStyle |
481
+ | pickerGradientOverlay | Style for the gradient overlay (fade out) | ViewStyle |
482
+ | durationScrollFlatList | Style for the Flatlist in each picker | ViewStyle |
483
+ | durationScrollFlatListContainer | Style for the View that contains the Flatlist in each picker | ViewStyle |
484
+ | durationScrollFlatListContentContainer | Style for the Flatlist's `contentContainerStyle` prop in each picker | ViewStyle |
515
485
 
516
486
  Note the minor limitations to the allowed styles for `pickerContainer` and `pickerItemContainer`. These are made because these styles are used for internal calculations and all possible `backgroundColor`/`height` types are not supported.
517
487
 
@@ -519,7 +489,7 @@ Note the minor limitations to the allowed styles for `pickerContainer` and `pick
519
489
 
520
490
  When the `disableInfiniteScroll` prop is not set, the picker gives the appearance of an infinitely scrolling picker by auto-scrolling forward/back when you near the start/end of the list. When the picker auto-scrolls, a momentary flicker is visible if you are scrolling very slowly.
521
491
 
522
- To mitigate for this, the list of numbers in each picker is repeated a given number of times based on the length of the list (8 times for the hours picker, and 3 times for the minutes and seconds picker). These have a performance trade-off: higher values mean the picker has to auto-scroll less to maintain the infinite scroll, but has to render a longer list of numbers. The number of repetitions automatically adjusts if the number of items in the picker changes (e.g. if an interval is included, or the maximum value is modified), balancing the trade-off. You can also manually adjust the number of repetitions in each picker with the `repeatHourNumbersNTimes`, `repeatMinuteNumbersNTimes` and `repeatSecondNumbersNTimes` props.
492
+ To mitigate for this, the list of numbers in each picker is repeated a given number of times based on the length of the list (7 times for the hours picker, and 3 times for the days/minutes/seconds picker). These have a performance trade-off: higher values mean the picker has to auto-scroll less to maintain the infinite scroll, but has to render a longer list of numbers. The number of repetitions automatically adjusts if the number of items in the picker changes (e.g. if an interval is included, or the maximum value is modified), balancing the trade-off. You can also manually adjust the number of repetitions in each picker with the `repeatHourNumbersNTimes`, `repeatMinuteNumbersNTimes` and `repeatSecondNumbersNTimes` props.
523
493
 
524
494
  Note that you can avoid the auto-scroll flickering entirely by disabling infinite scroll. You could then set the above props to high values, so that a user has to scroll far down/up the list to reach the end of the list.
525
495
 
@@ -547,23 +517,6 @@ Please note that this solution does not work for all bottom-sheet components (e.
547
517
  **Important**:
548
518
  The custom component needs to have the same interface as React Native's `<FlatList />` in order for it to work as expected. A complete reference of the current usage can be found [here](/src/components/DurationScroll/index.tsx).
549
519
 
550
- #### Generic feedback
551
-
552
- To enable haptic feedback from the non-Expo module [react-native-haptic-feedback](https://github.com/mkuczera/react-native-haptic-feedback) (or provide feedback in any other form) you can use the generic feedback callback prop `pickerFeedback`. This function is called whenever any of the pickers tick onto a new number.
553
-
554
- ```Jsx
555
- import { trigger } from 'react-native-haptic-feedback';
556
- import { TimerPicker } from "react-native-timer-picker";
557
-
558
- // ...
559
-
560
- <TimerPicker
561
- {...props}
562
- pickerFeedback={() => trigger('impactLight')}
563
- />
564
-
565
- ```
566
-
567
520
  ### TimerPickerModal ⏰
568
521
 
569
522
  The TimerPickerModal component accepts all [TimerPicker props](#timerpicker-️), and the below additional props.
@@ -646,10 +599,132 @@ An identical ref is also exposed for the TimerPickerModal component.
646
599
 
647
600
  <br>
648
601
 
602
+ ## Picker Feedback 📳🔉
603
+
604
+ You can use the picker feedback callback prop `pickerFeedback` to provide any form of audio/haptic feedback for the picker. This function is called whenever any of the pickers tick onto a new number.
605
+
606
+ Note that this prop should be used in lieu of the now deprecated expo-specific audio/haptic feedback props.
607
+
608
+ ### Audio Feedack
609
+
610
+ There is a challenge here with audio latency as we need to be able to play the click-sound repeatedly and rapidly when a user scrolls fast. Most React Native sound libraries are designed for playing audio tracks and the latency is too high for this application.
611
+
612
+ Recommended libraries:
613
+
614
+ - [react-native-audio-api](https://www.npmjs.com/package/react-native-audio-api): this is a new library but it's built by Software Mansion and has strong potential for our application with low-latency audio. It is not currently straightforward to use a local sound asset with this library; please check out the [Expo example](./examples/example-expo/App.tsx) and [Bare React Native example](./examples/example-bare//App.tsx) for a guide on how to do that. Note that this library will not work in Expo Go (you must create a development build).
615
+
616
+ Libraries to avoid:
617
+
618
+ - [react-native-sound](https://www.npmjs.com/package/react-native-sound): this likely has low enough latency to work but has not been maintained for 3+ years.
619
+ - [expo-av](https://docs.expo.dev/versions/latest/sdk/av/): the latency is too high to work well. Expo are developing a new audio module [expo-audio](https://docs.expo.dev/versions/latest/sdk/audio/) that may be better, but at the time of writing (May 2025) it does not have the capability to repeatedly play the same sound.
620
+
621
+ ### Haptic Feedback
622
+
623
+ Recommended libraries:
624
+
625
+ - [expo-haptics](https://www.npmjs.com/package/expo-haptics): for Expo apps.
626
+ - [react-native-haptic-feedback](https://github.com/mkuczera/react-native-haptic-feedback): for bare React Native apps.
627
+
628
+ ### Feedback Example
629
+
630
+ ```Jsx
631
+ import { useCallback, useRef } from "react";
632
+ import { TimerPicker } from "react-native-timer-picker";
633
+
634
+ import { AudioContext, type AudioBuffer } from "react-native-audio-api";
635
+ import * as Haptics from 'expo-haptics'; // Expo apps
636
+ import { trigger } from 'react-native-haptic-feedback'; // Bare RN apps
637
+
638
+ // see examples/example-expo and examples/example-bare for how to load a local sound
639
+ import { getClickSound } from "./utils/getClickSound";
640
+
641
+ // ...
642
+
643
+ const audioContextRef = useRef<AudioContext | null>(null);
644
+ const audioBufferRef = useRef<AudioBuffer | null>(null);
645
+
646
+ useEffect(() => {
647
+ const setupAudio = async () => {
648
+ try {
649
+ const context = new AudioContext();
650
+ const arrayBuffer = await getClickSound();
651
+ const buffer = await context.decodeAudioData(arrayBuffer);
652
+
653
+ audioContextRef.current = context;
654
+ audioBufferRef.current = buffer;
655
+ } catch (error) {
656
+ console.warn("Audio setup failed:", error);
657
+ }
658
+ };
659
+
660
+ setupAudio();
661
+
662
+ return () => {
663
+ audioContextRef.current?.close();
664
+ };
665
+ }, []);
666
+
667
+ const pickerFeedback = useCallback(() => {
668
+ try {
669
+ // Audio
670
+ const context = audioContextRef.current;
671
+ const buffer = audioBufferRef.current;
672
+
673
+ if (!context || !buffer) {
674
+ console.warn("Audio not initialized");
675
+ return;
676
+ }
677
+
678
+ const playerNode = context.createBufferSource();
679
+ playerNode.buffer = buffer;
680
+ playerNode.connect(context.destination);
681
+ playerNode.start(context.currentTime);
682
+
683
+ // Haptics (Expo apps)
684
+ Haptics.selectionAsync();
685
+ // Hatpics (Bare RN apps)
686
+ trigger('selection');
687
+ } catch {
688
+ console.warn("Picker feedback failed");
689
+ }
690
+ }, [])
691
+
692
+ <TimerPicker
693
+ {...props}
694
+ pickerFeedback={pickerFeedback}
695
+ />
696
+
697
+ ```
698
+
699
+ ### Expo-Specific Audio/Haptic Feedback (DEPRECATED)
700
+
701
+ **⚠️ This was deprecated in v2.2.0 - please use the [picker feedback](#generic-feedback) prop instead.**
702
+
703
+ Enable haptic feedback with the [expo-haptics](https://www.npmjs.com/package/expo-haptics) module:
704
+
705
+ `import * as Haptics from "expo-haptics";`
706
+
707
+ To enable haptic feedback, you need to supply the imported `Haptics` namespace as a prop to either TimerPickerModal or TimerPicker.
708
+
709
+ <br>
710
+
711
+ Enable audio feedback with the [expo-av](https://www.npmjs.com/package/expo-av) module:
712
+
713
+ `import { Audio } from "expo-av";`
714
+
715
+ To enable audio feedback, you need to supply the imported `Audio` class as a prop to either TimerPickerModal or TimerPicker.
716
+
717
+ Please note that the default click sound uses a hosted mp3 file. To make the click sound work offline, you need to supply your own
718
+ sound asset through the `clickSoundAsset` prop. You can download the default click sound [here](https://drive.google.com/uc?export=download&id=10e1YkbNsRh-vGx1jmS1Nntz8xzkBp4_I).
719
+
720
+ <br>
721
+
649
722
  ## Contributing 🧑‍🤝‍🧑
650
723
 
651
724
  Contributions to this project are more than welcome.
652
725
 
726
+ **N.B. Please submit PRs into `develop`, not `main`.**
727
+
653
728
  ### Dev Setup
654
729
 
655
730
  To get this project running locally:
@@ -659,8 +734,8 @@ To get this project running locally:
659
734
 
660
735
  You can then start either the Expo example or the bare React Native example:
661
736
 
662
- - For Expo, run `yarn start` to start the Expo example in Expo Go.
663
- - For bare React Native, run `yarn start-bare:android` or `start-bare:ios` to start the project on an emulator/device.
737
+ - For Expo, run `yarn start` to start the Expo example in Expo Go. For audio feedback, uncomment the relevant lines in `examples/example-expo/App.tsx` and create a development build with `yarn build:android` or `yarn build:ios`.
738
+ - For bare React Native, run `yarn start-bare:android` or `start-bare:ios` to start the project on an emulator/device (you have to refresh the app once on startup for it to work).
664
739
 
665
740
  ### GitHub Guidelines
666
741
 
@@ -674,7 +749,8 @@ There are two permenant branches: `main` and `develop`. You should never work di
674
749
 
675
750
  ## Limitations ⚠
676
751
 
677
- The project is not compatibile with React Native versions prior to `v0.72.0` due to this [React Native issue](https://github.com/facebook/react-native/issues/36329).
752
+ - The project is not compatibile with React Native versions prior to `v0.72.0` due to this [React Native issue](https://github.com/facebook/react-native/issues/36329).
753
+ - The audio feedback provided by the `Audio` prop with `expo-av` suffers from high latency and doesn't work well when a user scrolls quickly. This has now been deprecated in place of the `pickerFeedback` prop. Please try `react-native-audio-api` instead.
678
754
 
679
755
  <br>
680
756