react-native-timer-picker 1.2.11 → 1.4.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.
Files changed (54) hide show
  1. package/README.md +22 -2
  2. package/dist/commonjs/components/Modal/index.js +13 -14
  3. package/dist/commonjs/components/Modal/index.js.map +1 -1
  4. package/dist/commonjs/components/TimerPicker/DurationScroll.js +72 -34
  5. package/dist/commonjs/components/TimerPicker/DurationScroll.js.map +1 -1
  6. package/dist/commonjs/components/TimerPicker/TimerPicker.styles.js +23 -3
  7. package/dist/commonjs/components/TimerPicker/TimerPicker.styles.js.map +1 -1
  8. package/dist/commonjs/components/TimerPicker/index.js +64 -47
  9. package/dist/commonjs/components/TimerPicker/index.js.map +1 -1
  10. package/dist/commonjs/components/index.js +90 -69
  11. package/dist/commonjs/components/index.js.map +1 -1
  12. package/dist/commonjs/tests/Modal.test.js +3 -1
  13. package/dist/commonjs/tests/Modal.test.js.map +1 -1
  14. package/dist/commonjs/utils/generateNumbers.js +33 -10
  15. package/dist/commonjs/utils/generateNumbers.js.map +1 -1
  16. package/dist/commonjs/utils/padNumber.js +15 -0
  17. package/dist/commonjs/utils/padNumber.js.map +1 -0
  18. package/dist/module/components/Modal/index.js +13 -14
  19. package/dist/module/components/Modal/index.js.map +1 -1
  20. package/dist/module/components/TimerPicker/DurationScroll.js +73 -35
  21. package/dist/module/components/TimerPicker/DurationScroll.js.map +1 -1
  22. package/dist/module/components/TimerPicker/TimerPicker.styles.js +23 -3
  23. package/dist/module/components/TimerPicker/TimerPicker.styles.js.map +1 -1
  24. package/dist/module/components/TimerPicker/index.js +64 -47
  25. package/dist/module/components/TimerPicker/index.js.map +1 -1
  26. package/dist/module/components/index.js +90 -69
  27. package/dist/module/components/index.js.map +1 -1
  28. package/dist/module/tests/Modal.test.js +3 -1
  29. package/dist/module/tests/Modal.test.js.map +1 -1
  30. package/dist/module/utils/generateNumbers.js +31 -9
  31. package/dist/module/utils/generateNumbers.js.map +1 -1
  32. package/dist/module/utils/padNumber.js +8 -0
  33. package/dist/module/utils/padNumber.js.map +1 -0
  34. package/dist/typescript/components/TimerPicker/DurationScroll.d.ts +6 -1
  35. package/dist/typescript/components/TimerPicker/TimerPicker.styles.d.ts +4 -0
  36. package/dist/typescript/components/TimerPicker/index.d.ts +10 -1
  37. package/dist/typescript/components/index.d.ts +6 -1
  38. package/dist/typescript/utils/generateNumbers.d.ts +7 -2
  39. package/dist/typescript/utils/padNumber.d.ts +3 -0
  40. package/package.json +2 -2
  41. package/src/components/Modal/index.tsx +2 -2
  42. package/src/components/TimerPicker/DurationScroll.tsx +95 -12
  43. package/src/components/TimerPicker/TimerPicker.styles.ts +32 -1
  44. package/src/components/TimerPicker/index.tsx +39 -6
  45. package/src/components/index.tsx +45 -15
  46. package/src/tests/Modal.test.tsx +1 -1
  47. package/src/utils/generateNumbers.ts +40 -10
  48. package/src/utils/padNumber.ts +10 -0
  49. package/dist/commonjs/utils/padWithZero.js +0 -15
  50. package/dist/commonjs/utils/padWithZero.js.map +0 -1
  51. package/dist/module/utils/padWithZero.js +0 -8
  52. package/dist/module/utils/padWithZero.js.map +0 -1
  53. package/dist/typescript/utils/padWithZero.d.ts +0 -1
  54. package/src/utils/padWithZero.ts +0 -7
@@ -1 +1 @@
1
- {"version":3,"names":["padWithZero","generateNumbers","numberOfItems","options","numbers","i","push","String","repeatNTimes","Array","fill","flat","disableInfiniteScroll","padWithNItems","unshift"],"sources":["generateNumbers.ts"],"sourcesContent":["import { padWithZero } from \"./padWithZero\";\n\nexport const generateNumbers = (\n numberOfItems: number,\n options: {\n repeatNTimes?: number;\n padWithZero?: boolean;\n disableInfiniteScroll?: boolean;\n padWithNItems: number;\n }\n) => {\n if (numberOfItems <= 0) {\n return [];\n }\n\n let numbers: string[] = [];\n if (options.padWithZero) {\n for (let i = 0; i <= numberOfItems; i++) {\n numbers.push(padWithZero(i));\n }\n } else {\n for (let i = 0; i <= numberOfItems; i++) {\n numbers.push(String(i));\n }\n }\n if ((options.repeatNTimes ?? 1) > 1) {\n numbers = Array(options.repeatNTimes).fill(numbers).flat();\n }\n if (options.disableInfiniteScroll) {\n numbers.push(...Array(options.padWithNItems).fill(\"\"));\n numbers.unshift(...Array(options.padWithNItems).fill(\"\"));\n }\n return numbers;\n};\n"],"mappings":"AAAA,SAASA,WAAW,QAAQ,eAAe;AAE3C,OAAO,MAAMC,eAAe,GAAGA,CAC3BC,aAAqB,EACrBC,OAKC,KACA;EACD,IAAID,aAAa,IAAI,CAAC,EAAE;IACpB,OAAO,EAAE;EACb;EAEA,IAAIE,OAAiB,GAAG,EAAE;EAC1B,IAAID,OAAO,CAACH,WAAW,EAAE;IACrB,KAAK,IAAIK,CAAC,GAAG,CAAC,EAAEA,CAAC,IAAIH,aAAa,EAAEG,CAAC,EAAE,EAAE;MACrCD,OAAO,CAACE,IAAI,CAACN,WAAW,CAACK,CAAC,CAAC,CAAC;IAChC;EACJ,CAAC,MAAM;IACH,KAAK,IAAIA,CAAC,GAAG,CAAC,EAAEA,CAAC,IAAIH,aAAa,EAAEG,CAAC,EAAE,EAAE;MACrCD,OAAO,CAACE,IAAI,CAACC,MAAM,CAACF,CAAC,CAAC,CAAC;IAC3B;EACJ;EACA,IAAI,CAACF,OAAO,CAACK,YAAY,IAAI,CAAC,IAAI,CAAC,EAAE;IACjCJ,OAAO,GAAGK,KAAK,CAACN,OAAO,CAACK,YAAY,CAAC,CAACE,IAAI,CAACN,OAAO,CAAC,CAACO,IAAI,CAAC,CAAC;EAC9D;EACA,IAAIR,OAAO,CAACS,qBAAqB,EAAE;IAC/BR,OAAO,CAACE,IAAI,CAAC,GAAGG,KAAK,CAACN,OAAO,CAACU,aAAa,CAAC,CAACH,IAAI,CAAC,EAAE,CAAC,CAAC;IACtDN,OAAO,CAACU,OAAO,CAAC,GAAGL,KAAK,CAACN,OAAO,CAACU,aAAa,CAAC,CAACH,IAAI,CAAC,EAAE,CAAC,CAAC;EAC7D;EACA,OAAON,OAAO;AAClB,CAAC"}
1
+ {"version":3,"names":["padNumber","generateNumbers","numberOfItems","options","numbers","i","push","padWithZero","padNumbersWithZero","repeatNTimes","Array","fill","flat","disableInfiniteScroll","padWithNItems","unshift","generate12HourNumbers","hour"],"sources":["generateNumbers.ts"],"sourcesContent":["import { padNumber } from \"./padNumber\";\n\nexport const generateNumbers = (\n numberOfItems: number,\n options: {\n repeatNTimes?: number;\n padNumbersWithZero?: boolean;\n disableInfiniteScroll?: boolean;\n padWithNItems: number;\n }\n) => {\n if (numberOfItems <= 0) {\n return [];\n }\n\n let numbers: string[] = [];\n for (let i = 0; i <= numberOfItems; i++) {\n numbers.push(padNumber(i, { padWithZero: options.padNumbersWithZero }));\n }\n\n if ((options.repeatNTimes ?? 1) > 1) {\n numbers = Array(options.repeatNTimes).fill(numbers).flat();\n }\n if (options.disableInfiniteScroll) {\n numbers.push(...Array(options.padWithNItems).fill(\"\"));\n numbers.unshift(...Array(options.padWithNItems).fill(\"\"));\n }\n return numbers;\n};\n\nexport const generate12HourNumbers = (options: {\n repeatNTimes?: number;\n padNumbersWithZero?: boolean;\n disableInfiniteScroll?: boolean;\n padWithNItems: number;\n}) => {\n let numbers: string[] = [];\n\n // Generate numbers from 0 to 11 for AM\n for (let i = 0; i <= 11; i++) {\n numbers.push(\n `${padNumber(i, { padWithZero: options.padNumbersWithZero })} AM`\n );\n }\n\n // Generate numbers from 12 to 11 for PM\n for (let i = 12; i <= 23; i++) {\n const hour = i > 12 ? i - 12 : i;\n numbers.push(\n `${padNumber(hour, { padWithZero: options.padNumbersWithZero })} PM`\n );\n }\n\n if ((options.repeatNTimes ?? 1) > 1) {\n numbers = Array(options.repeatNTimes).fill(numbers).flat();\n }\n\n if (options.disableInfiniteScroll) {\n numbers.push(...Array(options.padWithNItems).fill(\"\"));\n numbers.unshift(...Array(options.padWithNItems).fill(\"\"));\n }\n\n return numbers;\n};\n"],"mappings":"AAAA,SAASA,SAAS,QAAQ,aAAa;AAEvC,OAAO,MAAMC,eAAe,GAAGA,CAC3BC,aAAqB,EACrBC,OAKC,KACA;EACD,IAAID,aAAa,IAAI,CAAC,EAAE;IACpB,OAAO,EAAE;EACb;EAEA,IAAIE,OAAiB,GAAG,EAAE;EAC1B,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,IAAIH,aAAa,EAAEG,CAAC,EAAE,EAAE;IACrCD,OAAO,CAACE,IAAI,CAACN,SAAS,CAACK,CAAC,EAAE;MAAEE,WAAW,EAAEJ,OAAO,CAACK;IAAmB,CAAC,CAAC,CAAC;EAC3E;EAEA,IAAI,CAACL,OAAO,CAACM,YAAY,IAAI,CAAC,IAAI,CAAC,EAAE;IACjCL,OAAO,GAAGM,KAAK,CAACP,OAAO,CAACM,YAAY,CAAC,CAACE,IAAI,CAACP,OAAO,CAAC,CAACQ,IAAI,CAAC,CAAC;EAC9D;EACA,IAAIT,OAAO,CAACU,qBAAqB,EAAE;IAC/BT,OAAO,CAACE,IAAI,CAAC,GAAGI,KAAK,CAACP,OAAO,CAACW,aAAa,CAAC,CAACH,IAAI,CAAC,EAAE,CAAC,CAAC;IACtDP,OAAO,CAACW,OAAO,CAAC,GAAGL,KAAK,CAACP,OAAO,CAACW,aAAa,CAAC,CAACH,IAAI,CAAC,EAAE,CAAC,CAAC;EAC7D;EACA,OAAOP,OAAO;AAClB,CAAC;AAED,OAAO,MAAMY,qBAAqB,GAAIb,OAKrC,IAAK;EACF,IAAIC,OAAiB,GAAG,EAAE;;EAE1B;EACA,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,IAAI,EAAE,EAAEA,CAAC,EAAE,EAAE;IAC1BD,OAAO,CAACE,IAAI,CACP,GAAEN,SAAS,CAACK,CAAC,EAAE;MAAEE,WAAW,EAAEJ,OAAO,CAACK;IAAmB,CAAC,CAAE,KACjE,CAAC;EACL;;EAEA;EACA,KAAK,IAAIH,CAAC,GAAG,EAAE,EAAEA,CAAC,IAAI,EAAE,EAAEA,CAAC,EAAE,EAAE;IAC3B,MAAMY,IAAI,GAAGZ,CAAC,GAAG,EAAE,GAAGA,CAAC,GAAG,EAAE,GAAGA,CAAC;IAChCD,OAAO,CAACE,IAAI,CACP,GAAEN,SAAS,CAACiB,IAAI,EAAE;MAAEV,WAAW,EAAEJ,OAAO,CAACK;IAAmB,CAAC,CAAE,KACpE,CAAC;EACL;EAEA,IAAI,CAACL,OAAO,CAACM,YAAY,IAAI,CAAC,IAAI,CAAC,EAAE;IACjCL,OAAO,GAAGM,KAAK,CAACP,OAAO,CAACM,YAAY,CAAC,CAACE,IAAI,CAACP,OAAO,CAAC,CAACQ,IAAI,CAAC,CAAC;EAC9D;EAEA,IAAIT,OAAO,CAACU,qBAAqB,EAAE;IAC/BT,OAAO,CAACE,IAAI,CAAC,GAAGI,KAAK,CAACP,OAAO,CAACW,aAAa,CAAC,CAACH,IAAI,CAAC,EAAE,CAAC,CAAC;IACtDP,OAAO,CAACW,OAAO,CAAC,GAAGL,KAAK,CAACP,OAAO,CAACW,aAAa,CAAC,CAACH,IAAI,CAAC,EAAE,CAAC,CAAC;EAC7D;EAEA,OAAOP,OAAO;AAClB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export const padNumber = (value, options) => {
2
+ if (value < 10) {
3
+ return (options !== null && options !== void 0 && options.padWithZero ? "0" : " ") + value;
4
+ } else {
5
+ return String(value);
6
+ }
7
+ };
8
+ //# sourceMappingURL=padNumber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["padNumber","value","options","padWithZero","String"],"sources":["padNumber.ts"],"sourcesContent":["export const padNumber = (\n value: number,\n options?: { padWithZero?: boolean }\n): string => {\n if (value < 10) {\n return (options?.padWithZero ? \"0\" : \" \") + value;\n } else {\n return String(value);\n }\n};\n"],"mappings":"AAAA,OAAO,MAAMA,SAAS,GAAGA,CACrBC,KAAa,EACbC,OAAmC,KAC1B;EACT,IAAID,KAAK,GAAG,EAAE,EAAE;IACZ,OAAO,CAACC,OAAO,aAAPA,OAAO,eAAPA,OAAO,CAAEC,WAAW,GAAG,GAAG,GAAG,GAAG,IAAIF,KAAK;EACrD,CAAC,MAAM;IACH,OAAOG,MAAM,CAACH,KAAK,CAAC;EACxB;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { MutableRefObject } from "react";
2
2
  import { View } from "react-native";
3
3
  import { generateStyles } from "./TimerPicker.styles";
4
4
  export interface DurationScrollRef {
@@ -8,6 +8,7 @@ export interface DurationScrollRef {
8
8
  setValue: (value: number, options?: {
9
9
  animated?: boolean;
10
10
  }) => void;
11
+ latestDuration: MutableRefObject<number>;
11
12
  }
12
13
  type LinearGradientPoint = {
13
14
  x: number;
@@ -31,6 +32,10 @@ interface DurationScrollProps {
31
32
  padNumbersWithZero?: boolean;
32
33
  disableInfiniteScroll?: boolean;
33
34
  limit?: LimitType;
35
+ aggressivelyGetLatestDuration: boolean;
36
+ is12HourPicker?: boolean;
37
+ amLabel?: string;
38
+ pmLabel?: string;
34
39
  padWithNItems: number;
35
40
  pickerGradientOverlayProps?: Partial<LinearGradientProps>;
36
41
  topPickerGradientOverlayProps?: Partial<LinearGradientProps>;
@@ -5,6 +5,8 @@ export interface CustomTimerPickerStyles {
5
5
  pickerContainer?: any;
6
6
  pickerLabelContainer?: any;
7
7
  pickerLabel?: any;
8
+ pickerAmPmContainer?: any;
9
+ pickerAmPmLabel?: any;
8
10
  pickerItemContainer?: any;
9
11
  pickerItem?: any;
10
12
  disabledPickerItem?: any;
@@ -18,6 +20,8 @@ export declare const generateStyles: (customStyles: CustomTimerPickerStyles | un
18
20
  pickerLabel: any;
19
21
  pickerItemContainer: any;
20
22
  pickerItem: any;
23
+ pickerAmPmContainer: any;
24
+ pickerAmPmLabel: any;
21
25
  disabledPickerItem: any;
22
26
  pickerGradientOverlay: any;
23
27
  };
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { MutableRefObject } from "react";
2
2
  import { View } from "react-native";
3
3
  import { LimitType } from "./DurationScroll";
4
4
  import { CustomTimerPickerStyles } from "./TimerPicker.styles";
@@ -14,6 +14,11 @@ export interface TimerPickerRef {
14
14
  }, options?: {
15
15
  animated?: boolean;
16
16
  }) => void;
17
+ latestDuration: {
18
+ hours: MutableRefObject<number> | undefined;
19
+ minutes: MutableRefObject<number> | undefined;
20
+ seconds: MutableRefObject<number> | undefined;
21
+ };
17
22
  }
18
23
  export interface TimerPickerProps {
19
24
  onDurationChange?: (duration: {
@@ -24,6 +29,10 @@ export interface TimerPickerProps {
24
29
  initialHours?: number;
25
30
  initialMinutes?: number;
26
31
  initialSeconds?: number;
32
+ aggressivelyGetLatestDuration?: boolean;
33
+ use12HourPicker?: boolean;
34
+ amLabel?: string;
35
+ pmLabel?: string;
27
36
  hideHours?: boolean;
28
37
  hideMinutes?: boolean;
29
38
  hideSeconds?: boolean;
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { MutableRefObject } from "react";
2
2
  import { View, Text, TouchableOpacity } from "react-native";
3
3
  import { TimerPickerProps } from "./TimerPicker";
4
4
  import Modal from "./Modal";
@@ -14,6 +14,11 @@ export interface TimerPickerModalRef {
14
14
  }, options?: {
15
15
  animated?: boolean;
16
16
  }) => void;
17
+ latestDuration: {
18
+ hours: MutableRefObject<number> | undefined;
19
+ minutes: MutableRefObject<number> | undefined;
20
+ seconds: MutableRefObject<number> | undefined;
21
+ };
17
22
  }
18
23
  export interface TimerPickerModalProps extends TimerPickerProps {
19
24
  visible: boolean;
@@ -1,7 +1,12 @@
1
- import { padWithZero } from "./padWithZero";
2
1
  export declare const generateNumbers: (numberOfItems: number, options: {
3
2
  repeatNTimes?: number;
4
- padWithZero?: boolean;
3
+ padNumbersWithZero?: boolean;
4
+ disableInfiniteScroll?: boolean;
5
+ padWithNItems: number;
6
+ }) => string[];
7
+ export declare const generate12HourNumbers: (options: {
8
+ repeatNTimes?: number;
9
+ padNumbersWithZero?: boolean;
5
10
  disableInfiniteScroll?: boolean;
6
11
  padWithNItems: number;
7
12
  }) => string[];
@@ -0,0 +1,3 @@
1
+ export declare const padNumber: (value: number, options?: {
2
+ padWithZero?: boolean;
3
+ }) => string;
package/package.json CHANGED
@@ -6,14 +6,14 @@
6
6
  "url": "https://github.com/troberts-28"
7
7
  },
8
8
  "license": "MIT",
9
- "version": "1.2.11",
9
+ "version": "1.4.0",
10
10
  "main": "dist/commonjs/index.js",
11
11
  "types": "dist/typescript/src/index.d.ts",
12
12
  "scripts": {
13
13
  "test": "jest --forceExit --silent",
14
14
  "build": "bob build",
15
15
  "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",
17
17
  "lint": "eslint --ext .ts,.tsx .",
18
18
  "lint:fix": "eslint --ext .ts,.tsx . --fix",
19
19
  "prepare": "yarn build"
@@ -33,7 +33,7 @@ export const Modal = ({
33
33
  modalProps,
34
34
  contentStyle,
35
35
  overlayStyle,
36
- testID,
36
+ testID = "modal",
37
37
  }: ModalProps): React.ReactElement => {
38
38
  const { width: screenWidth, height: screenHeight } = useWindowDimensions();
39
39
 
@@ -109,7 +109,7 @@ export const Modal = ({
109
109
  animationType="fade"
110
110
  visible={isVisible}
111
111
  {...modalProps}
112
- testID={testID ?? "modal"}>
112
+ testID={testID}>
113
113
  <TouchableWithoutFeedback
114
114
  onPress={onOverlayPress}
115
115
  testID="modal-backdrop">
@@ -3,6 +3,7 @@ import React, {
3
3
  useCallback,
4
4
  forwardRef,
5
5
  useImperativeHandle,
6
+ MutableRefObject,
6
7
  } from "react";
7
8
  import {
8
9
  View,
@@ -14,7 +15,10 @@ import {
14
15
  NativeScrollEvent,
15
16
  } from "react-native";
16
17
 
17
- import { generateNumbers } from "../../utils/generateNumbers";
18
+ import {
19
+ generate12HourNumbers,
20
+ generateNumbers,
21
+ } from "../../utils/generateNumbers";
18
22
  import { colorToRgba } from "../../utils/colorToRgba";
19
23
  import { generateStyles } from "./TimerPicker.styles";
20
24
  import { getAdjustedLimit } from "../../utils/getAdjustedLimit";
@@ -23,6 +27,7 @@ import { getScrollIndex } from "../../utils/getScrollIndex";
23
27
  export interface DurationScrollRef {
24
28
  reset: (options?: { animated?: boolean }) => void;
25
29
  setValue: (value: number, options?: { animated?: boolean }) => void;
30
+ latestDuration: MutableRefObject<number>;
26
31
  }
27
32
 
28
33
  type LinearGradientPoint = {
@@ -50,6 +55,10 @@ interface DurationScrollProps {
50
55
  padNumbersWithZero?: boolean;
51
56
  disableInfiniteScroll?: boolean;
52
57
  limit?: LimitType;
58
+ aggressivelyGetLatestDuration: boolean;
59
+ is12HourPicker?: boolean;
60
+ amLabel?: string;
61
+ pmLabel?: string;
53
62
  padWithNItems: number;
54
63
  pickerGradientOverlayProps?: Partial<LinearGradientProps>;
55
64
  topPickerGradientOverlayProps?: Partial<LinearGradientProps>;
@@ -73,6 +82,10 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
73
82
  padNumbersWithZero = false,
74
83
  disableInfiniteScroll = false,
75
84
  limit,
85
+ aggressivelyGetLatestDuration,
86
+ is12HourPicker,
87
+ amLabel,
88
+ pmLabel,
76
89
  padWithNItems,
77
90
  pickerGradientOverlayProps,
78
91
  topPickerGradientOverlayProps,
@@ -83,14 +96,19 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
83
96
  },
84
97
  ref
85
98
  ): React.ReactElement => {
86
- const flatListRef = useRef<FlatList | null>(null);
87
-
88
- const data = generateNumbers(numberOfItems, {
89
- padWithZero: padNumbersWithZero,
90
- repeatNTimes: 3,
91
- disableInfiniteScroll,
92
- padWithNItems: padWithNItems,
93
- });
99
+ const data = !is12HourPicker
100
+ ? generateNumbers(numberOfItems, {
101
+ padNumbersWithZero,
102
+ repeatNTimes: 3,
103
+ disableInfiniteScroll,
104
+ padWithNItems,
105
+ })
106
+ : generate12HourNumbers({
107
+ padNumbersWithZero,
108
+ repeatNTimes: 3,
109
+ disableInfiniteScroll,
110
+ padWithNItems,
111
+ });
94
112
 
95
113
  const numberOfItemsToShow = 1 + padWithNItems * 2;
96
114
 
@@ -103,6 +121,10 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
103
121
  disableInfiniteScroll,
104
122
  });
105
123
 
124
+ const latestDuration = useRef(0);
125
+
126
+ const flatListRef = useRef<FlatList | null>(null);
127
+
106
128
  useImperativeHandle(ref, () => ({
107
129
  reset: (options) => {
108
130
  flatListRef.current?.scrollToIndex({
@@ -121,11 +143,22 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
121
143
  }),
122
144
  });
123
145
  },
146
+ latestDuration: latestDuration,
124
147
  }));
125
148
 
126
149
  const renderItem = useCallback(
127
150
  ({ item }: { item: string }) => {
128
- const intItem = parseInt(item);
151
+ let stringItem = item;
152
+ let intItem: number;
153
+ let isAm: boolean | undefined;
154
+
155
+ if (!is12HourPicker) {
156
+ intItem = parseInt(item);
157
+ } else {
158
+ isAm = item.includes("AM");
159
+ stringItem = item.replace(/\s[AP]M/g, "");
160
+ intItem = parseInt(stringItem);
161
+ }
129
162
 
130
163
  return (
131
164
  <View
@@ -140,20 +173,67 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
140
173
  ? styles.disabledPickerItem
141
174
  : {},
142
175
  ]}>
143
- {item}
176
+ {stringItem}
144
177
  </Text>
178
+ {is12HourPicker ? (
179
+ <View
180
+ style={styles.pickerAmPmContainer}
181
+ pointerEvents="none">
182
+ <Text style={[styles.pickerAmPmLabel]}>
183
+ {isAm ? amLabel : pmLabel}
184
+ </Text>
185
+ </View>
186
+ ) : null}
145
187
  </View>
146
188
  );
147
189
  },
148
190
  [
149
191
  adjustedLimited.max,
150
192
  adjustedLimited.min,
193
+ amLabel,
194
+ is12HourPicker,
195
+ pmLabel,
151
196
  styles.disabledPickerItem,
197
+ styles.pickerAmPmContainer,
198
+ styles.pickerAmPmLabel,
152
199
  styles.pickerItem,
153
200
  styles.pickerItemContainer,
154
201
  ]
155
202
  );
156
203
 
204
+ const onScroll = useCallback(
205
+ (e: NativeSyntheticEvent<NativeScrollEvent>) => {
206
+ // this function is only used when the picker is in a modal
207
+ // it is used to ensure that the modal gets the latest duration on clicking
208
+ // the confirm button, even if the scrollview is still scrolling
209
+ const newIndex = Math.round(
210
+ e.nativeEvent.contentOffset.y /
211
+ styles.pickerItemContainer.height
212
+ );
213
+ let newDuration =
214
+ (disableInfiniteScroll
215
+ ? newIndex
216
+ : newIndex + padWithNItems) %
217
+ (numberOfItems + 1);
218
+
219
+ // check limits
220
+ if (newDuration > adjustedLimited.max) {
221
+ newDuration = adjustedLimited.max;
222
+ } else if (newDuration < adjustedLimited.min) {
223
+ newDuration = adjustedLimited.min;
224
+ }
225
+ latestDuration.current = newDuration;
226
+ },
227
+ [
228
+ adjustedLimited.max,
229
+ adjustedLimited.min,
230
+ disableInfiniteScroll,
231
+ numberOfItems,
232
+ padWithNItems,
233
+ styles.pickerItemContainer.height,
234
+ ]
235
+ );
236
+
157
237
  const onMomentumScrollEnd = useCallback(
158
238
  (e: NativeSyntheticEvent<NativeScrollEvent>) => {
159
239
  const newIndex = Math.round(
@@ -264,7 +344,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
264
344
  renderItem={renderItem}
265
345
  keyExtractor={KEY_EXTRACTOR}
266
346
  showsVerticalScrollIndicator={false}
267
- decelerationRate={0.9}
347
+ decelerationRate={0.88}
268
348
  scrollEventThrottle={16}
269
349
  snapToAlignment="start"
270
350
  // used in place of snapToOffset due to bug on Android
@@ -277,6 +357,9 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
277
357
  : undefined
278
358
  }
279
359
  onMomentumScrollEnd={onMomentumScrollEnd}
360
+ onScroll={
361
+ aggressivelyGetLatestDuration ? onScroll : undefined
362
+ }
280
363
  testID="duration-scroll-flatlist"
281
364
  />
282
365
  <View style={styles.pickerLabelContainer} pointerEvents="none">
@@ -8,6 +8,8 @@ export interface CustomTimerPickerStyles {
8
8
  pickerContainer?: any;
9
9
  pickerLabelContainer?: any;
10
10
  pickerLabel?: any;
11
+ pickerAmPmContainer?: any;
12
+ pickerAmPmLabel?: any;
11
13
  pickerItemContainer?: any;
12
14
  pickerItem?: any;
13
15
  disabledPickerItem?: any;
@@ -40,12 +42,19 @@ export const generateStyles = (
40
42
  top: 0,
41
43
  bottom: 0,
42
44
  justifyContent: "center",
45
+ minWidth:
46
+ (customStyles?.pickerLabel?.fontSize ??
47
+ customStyles?.text?.fontSize ??
48
+ 25) * 0.65,
43
49
  ...customStyles?.pickerLabelContainer,
44
50
  },
45
51
  pickerLabel: {
46
52
  fontSize: 18,
47
53
  fontWeight: "bold",
48
- marginTop: (customStyles?.pickerItem?.fontSize ?? 25) / 6,
54
+ marginTop:
55
+ (customStyles?.pickerItem?.fontSize ??
56
+ customStyles?.text?.fontSize ??
57
+ 25) / 6,
49
58
  color:
50
59
  customStyles?.theme === "dark"
51
60
  ? DARK_MODE_TEXT_COLOR
@@ -54,6 +63,7 @@ export const generateStyles = (
54
63
  ...customStyles?.pickerLabel,
55
64
  },
56
65
  pickerItemContainer: {
66
+ flexDirection: "row",
57
67
  height: 50,
58
68
  justifyContent: "center",
59
69
  alignItems: "center",
@@ -70,6 +80,27 @@ export const generateStyles = (
70
80
  ...customStyles?.text,
71
81
  ...customStyles?.pickerItem,
72
82
  },
83
+ pickerAmPmContainer: {
84
+ position: "absolute",
85
+ right: 0,
86
+ top: 0,
87
+ bottom: 0,
88
+ justifyContent: "center",
89
+ ...customStyles?.pickerLabelContainer,
90
+ ...customStyles?.pickerAmPmContainer,
91
+ },
92
+ pickerAmPmLabel: {
93
+ fontSize: 18,
94
+ fontWeight: "bold",
95
+ marginTop: (customStyles?.pickerItem?.fontSize ?? 25) / 6,
96
+ color:
97
+ customStyles?.theme === "dark"
98
+ ? DARK_MODE_TEXT_COLOR
99
+ : LIGHT_MODE_TEXT_COLOR,
100
+ ...customStyles?.text,
101
+ ...customStyles?.pickerLabel,
102
+ ...customStyles?.pickerAmPmLabel,
103
+ },
73
104
  disabledPickerItem: {
74
105
  opacity: 0.2,
75
106
  ...customStyles?.disabledPickerItem,
@@ -1,4 +1,5 @@
1
1
  import React, {
2
+ MutableRefObject,
2
3
  forwardRef,
3
4
  useEffect,
4
5
  useImperativeHandle,
@@ -23,6 +24,11 @@ export interface TimerPickerRef {
23
24
  },
24
25
  options?: { animated?: boolean }
25
26
  ) => void;
27
+ latestDuration: {
28
+ hours: MutableRefObject<number> | undefined;
29
+ minutes: MutableRefObject<number> | undefined;
30
+ seconds: MutableRefObject<number> | undefined;
31
+ };
26
32
  }
27
33
 
28
34
  export interface TimerPickerProps {
@@ -34,6 +40,10 @@ export interface TimerPickerProps {
34
40
  initialHours?: number;
35
41
  initialMinutes?: number;
36
42
  initialSeconds?: number;
43
+ aggressivelyGetLatestDuration?: boolean;
44
+ use12HourPicker?: boolean;
45
+ amLabel?: string;
46
+ pmLabel?: string;
37
47
  hideHours?: boolean;
38
48
  hideMinutes?: boolean;
39
49
  hideSeconds?: boolean;
@@ -67,11 +77,15 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
67
77
  hourLimit,
68
78
  minuteLimit,
69
79
  secondLimit,
70
- hourLabel = "h",
71
- minuteLabel = "m",
72
- secondLabel = "s",
80
+ hourLabel,
81
+ minuteLabel,
82
+ secondLabel,
73
83
  padWithNItems = 1,
74
84
  disableInfiniteScroll = false,
85
+ aggressivelyGetLatestDuration = false,
86
+ use12HourPicker = false,
87
+ amLabel = "am",
88
+ pmLabel = "pm",
75
89
  LinearGradient,
76
90
  pickerContainerProps,
77
91
  pickerGradientOverlayProps,
@@ -133,6 +147,11 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
133
147
  options
134
148
  );
135
149
  },
150
+ latestDuration: {
151
+ hours: hoursDurationScrollRef.current?.latestDuration,
152
+ minutes: minutesDurationScrollRef.current?.latestDuration,
153
+ seconds: secondsDurationScrollRef.current?.latestDuration,
154
+ },
136
155
  }));
137
156
 
138
157
  return (
@@ -144,8 +163,13 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
144
163
  <DurationScroll
145
164
  ref={hoursDurationScrollRef}
146
165
  numberOfItems={23}
147
- label={hourLabel}
166
+ label={
167
+ hourLabel ?? (!use12HourPicker ? "h" : undefined)
168
+ }
148
169
  initialValue={initialHours}
170
+ aggressivelyGetLatestDuration={
171
+ aggressivelyGetLatestDuration
172
+ }
149
173
  onDurationChange={setSelectedHours}
150
174
  pickerGradientOverlayProps={pickerGradientOverlayProps}
151
175
  topPickerGradientOverlayProps={
@@ -158,6 +182,9 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
158
182
  padWithNItems={checkedPadWithNItems}
159
183
  limit={hourLimit}
160
184
  LinearGradient={LinearGradient}
185
+ is12HourPicker={use12HourPicker}
186
+ amLabel={amLabel}
187
+ pmLabel={pmLabel}
161
188
  styles={styles}
162
189
  testID="duration-scroll-hour"
163
190
  />
@@ -166,8 +193,11 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
166
193
  <DurationScroll
167
194
  ref={minutesDurationScrollRef}
168
195
  numberOfItems={59}
169
- label={minuteLabel}
196
+ label={minuteLabel ?? "m"}
170
197
  initialValue={initialMinutes}
198
+ aggressivelyGetLatestDuration={
199
+ aggressivelyGetLatestDuration
200
+ }
171
201
  onDurationChange={setSelectedMinutes}
172
202
  padNumbersWithZero
173
203
  pickerGradientOverlayProps={pickerGradientOverlayProps}
@@ -189,8 +219,11 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
189
219
  <DurationScroll
190
220
  ref={secondsDurationScrollRef}
191
221
  numberOfItems={59}
192
- label={secondLabel}
222
+ label={secondLabel ?? "s"}
193
223
  initialValue={initialSeconds}
224
+ aggressivelyGetLatestDuration={
225
+ aggressivelyGetLatestDuration
226
+ }
194
227
  onDurationChange={setSelectedSeconds}
195
228
  padNumbersWithZero
196
229
  pickerGradientOverlayProps={pickerGradientOverlayProps}