react-native-timer-picker 1.2.7 → 1.2.9
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.
- package/dist/commonjs/components/Modal/Modal.styles.js +32 -0
- package/dist/commonjs/components/Modal/Modal.styles.js.map +1 -0
- package/dist/commonjs/components/Modal/index.js +108 -0
- package/dist/commonjs/components/Modal/index.js.map +1 -0
- package/dist/commonjs/components/TimerPicker/DurationScroll.js +210 -0
- package/dist/commonjs/components/TimerPicker/DurationScroll.js.map +1 -0
- package/dist/commonjs/components/TimerPicker/TimerPicker.styles.js +67 -0
- package/dist/commonjs/components/TimerPicker/TimerPicker.styles.js.map +1 -0
- package/dist/commonjs/components/TimerPicker/index.js +130 -0
- package/dist/commonjs/components/TimerPicker/index.js.map +1 -0
- package/dist/commonjs/components/TimerPickerModal.styles.js +69 -0
- package/dist/commonjs/components/TimerPickerModal.styles.js.map +1 -0
- package/dist/commonjs/components/index.js +156 -0
- package/dist/commonjs/components/index.js.map +1 -0
- package/dist/commonjs/index.js +21 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/tests/DurationScroll.test.js +56 -0
- package/dist/commonjs/tests/DurationScroll.test.js.map +1 -0
- package/dist/commonjs/tests/Modal.test.js +40 -0
- package/dist/commonjs/tests/Modal.test.js.map +1 -0
- package/dist/commonjs/tests/TimerPicker.test.js +37 -0
- package/dist/commonjs/tests/TimerPicker.test.js.map +1 -0
- package/dist/commonjs/tests/TimerPickerModal.test.js +73 -0
- package/dist/commonjs/tests/TimerPickerModal.test.js.map +1 -0
- package/dist/commonjs/utils/colorToRgba.js +51 -0
- package/dist/commonjs/utils/colorToRgba.js.map +1 -0
- package/dist/commonjs/utils/generateNumbers.js +32 -0
- package/dist/commonjs/utils/generateNumbers.js.map +1 -0
- package/dist/commonjs/utils/getAdjustedLimit.js +32 -0
- package/dist/commonjs/utils/getAdjustedLimit.js.map +1 -0
- package/dist/commonjs/utils/getScrollIndex.js +17 -0
- package/dist/commonjs/utils/getScrollIndex.js.map +1 -0
- package/dist/commonjs/utils/padWithZero.js +15 -0
- package/dist/commonjs/utils/padWithZero.js.map +1 -0
- package/dist/module/components/Modal/Modal.styles.js +26 -0
- package/dist/module/components/Modal/Modal.styles.js.map +1 -0
- package/dist/module/components/Modal/index.js +100 -0
- package/dist/module/components/Modal/index.js.map +1 -0
- package/dist/module/components/TimerPicker/DurationScroll.js +202 -0
- package/dist/module/components/TimerPicker/DurationScroll.js.map +1 -0
- package/dist/module/components/TimerPicker/TimerPicker.styles.js +59 -0
- package/dist/module/components/TimerPicker/TimerPicker.styles.js.map +1 -0
- package/dist/module/components/TimerPicker/index.js +121 -0
- package/dist/module/components/TimerPicker/index.js.map +1 -0
- package/dist/module/components/TimerPickerModal.styles.js +61 -0
- package/dist/module/components/TimerPickerModal.styles.js.map +1 -0
- package/dist/module/components/index.js +147 -0
- package/dist/module/components/index.js.map +1 -0
- package/dist/module/index.js +3 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/tests/DurationScroll.test.js +53 -0
- package/dist/module/tests/DurationScroll.test.js.map +1 -0
- package/dist/module/tests/Modal.test.js +37 -0
- package/dist/module/tests/Modal.test.js.map +1 -0
- package/dist/module/tests/TimerPicker.test.js +34 -0
- package/dist/module/tests/TimerPicker.test.js.map +1 -0
- package/dist/module/tests/TimerPickerModal.test.js +70 -0
- package/dist/module/tests/TimerPickerModal.test.js.map +1 -0
- package/dist/module/utils/colorToRgba.js +44 -0
- package/dist/module/utils/colorToRgba.js.map +1 -0
- package/dist/module/utils/generateNumbers.js +25 -0
- package/dist/module/utils/generateNumbers.js.map +1 -0
- package/dist/module/utils/getAdjustedLimit.js +25 -0
- package/dist/module/utils/getAdjustedLimit.js.map +1 -0
- package/dist/module/utils/getScrollIndex.js +10 -0
- package/dist/module/utils/getScrollIndex.js.map +1 -0
- package/dist/module/utils/padWithZero.js +8 -0
- package/dist/module/utils/padWithZero.js.map +1 -0
- package/dist/typescript/index.d.ts +4 -0
- package/dist/{utils → typescript/utils}/colorToRgba.d.ts +1 -1
- package/dist/{utils → typescript/utils}/getScrollIndex.d.ts +1 -1
- package/package.json +29 -12
- package/{dist/components/Modal/Modal.styles.js → src/components/Modal/Modal.styles.ts} +4 -6
- package/src/components/Modal/index.tsx +134 -0
- package/src/components/TimerPicker/DurationScroll.tsx +337 -0
- package/src/components/TimerPicker/TimerPicker.styles.ts +87 -0
- package/src/components/TimerPicker/index.tsx +216 -0
- package/src/components/TimerPickerModal.styles.ts +87 -0
- package/src/components/index.tsx +243 -0
- package/src/index.ts +14 -0
- package/src/tests/DurationScroll.test.tsx +57 -0
- package/src/tests/Modal.test.tsx +34 -0
- package/src/tests/TimerPicker.test.tsx +27 -0
- package/src/tests/TimerPickerModal.test.tsx +70 -0
- package/{dist/utils/colorToRgba.js → src/utils/colorToRgba.ts} +18 -17
- package/src/utils/generateNumbers.ts +34 -0
- package/{dist/utils/getAdjustedLimit.js → src/utils/getAdjustedLimit.ts} +14 -7
- package/src/utils/getScrollIndex.ts +15 -0
- package/src/utils/padWithZero.ts +7 -0
- package/dist/components/Modal/index.js +0 -109
- package/dist/components/TimerPicker/DurationScroll.js +0 -211
- package/dist/components/TimerPicker/TimerPicker.styles.js +0 -41
- package/dist/components/TimerPicker/index.js +0 -81
- package/dist/components/TimerPickerModal.styles.js +0 -37
- package/dist/components/index.js +0 -118
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -10
- package/dist/utils/generateNumbers.js +0 -30
- package/dist/utils/getScrollIndex.js +0 -10
- package/dist/utils/padWithZero.js +0 -12
- /package/dist/{components → typescript/components}/Modal/Modal.styles.d.ts +0 -0
- /package/dist/{components → typescript/components}/Modal/index.d.ts +0 -0
- /package/dist/{components → typescript/components}/TimerPicker/DurationScroll.d.ts +0 -0
- /package/dist/{components → typescript/components}/TimerPicker/TimerPicker.styles.d.ts +0 -0
- /package/dist/{components → typescript/components}/TimerPicker/index.d.ts +0 -0
- /package/dist/{components → typescript/components}/TimerPickerModal.styles.d.ts +0 -0
- /package/dist/{components → typescript/components}/index.d.ts +0 -0
- /package/dist/{utils → typescript/utils}/generateNumbers.d.ts +0 -0
- /package/dist/{utils → typescript/utils}/getAdjustedLimit.d.ts +0 -0
- /package/dist/{utils → typescript/utils}/padWithZero.d.ts +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
|
|
4
|
+
import type { CustomTimerPickerStyles } from "./TimerPicker/TimerPicker.styles";
|
|
5
|
+
|
|
6
|
+
export interface CustomTimerPickerModalStyles extends CustomTimerPickerStyles {
|
|
7
|
+
container?: any;
|
|
8
|
+
contentContainer?: any;
|
|
9
|
+
buttonContainer?: any;
|
|
10
|
+
button?: any;
|
|
11
|
+
cancelButton?: any;
|
|
12
|
+
confirmButton?: any;
|
|
13
|
+
modalTitle?: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const DARK_MODE_BACKGROUND_COLOR = "#232323";
|
|
17
|
+
const DARK_MODE_TEXT_COLOR = "#E9E9E9";
|
|
18
|
+
const LIGHT_MODE_BACKGROUND_COLOR = "#F1F1F1";
|
|
19
|
+
const LIGHT_MODE_TEXT_COLOR = "#1B1B1B";
|
|
20
|
+
|
|
21
|
+
export const generateStyles = (
|
|
22
|
+
customStyles: CustomTimerPickerModalStyles | undefined
|
|
23
|
+
) =>
|
|
24
|
+
StyleSheet.create({
|
|
25
|
+
container: {
|
|
26
|
+
justifyContent: "center",
|
|
27
|
+
alignItems: "center",
|
|
28
|
+
overflow: "hidden",
|
|
29
|
+
...customStyles?.container,
|
|
30
|
+
},
|
|
31
|
+
contentContainer: {
|
|
32
|
+
backgroundColor:
|
|
33
|
+
customStyles?.backgroundColor ?? customStyles?.theme === "dark"
|
|
34
|
+
? DARK_MODE_BACKGROUND_COLOR
|
|
35
|
+
: LIGHT_MODE_BACKGROUND_COLOR,
|
|
36
|
+
justifyContent: "center",
|
|
37
|
+
alignItems: "center",
|
|
38
|
+
borderRadius: 20,
|
|
39
|
+
padding: 20,
|
|
40
|
+
...customStyles?.contentContainer,
|
|
41
|
+
},
|
|
42
|
+
buttonContainer: {
|
|
43
|
+
flexDirection: "row",
|
|
44
|
+
marginTop: 25,
|
|
45
|
+
...customStyles?.buttonContainer,
|
|
46
|
+
},
|
|
47
|
+
button: {
|
|
48
|
+
marginHorizontal: 12,
|
|
49
|
+
paddingVertical: 10,
|
|
50
|
+
paddingHorizontal: 20,
|
|
51
|
+
borderWidth: 1,
|
|
52
|
+
borderRadius: 10,
|
|
53
|
+
fontSize: 16,
|
|
54
|
+
overflow: "hidden",
|
|
55
|
+
...customStyles?.text,
|
|
56
|
+
...customStyles?.button,
|
|
57
|
+
},
|
|
58
|
+
cancelButton: {
|
|
59
|
+
borderColor: "gray",
|
|
60
|
+
color:
|
|
61
|
+
customStyles?.theme === "dark" ? DARK_MODE_TEXT_COLOR : "gray",
|
|
62
|
+
backgroundColor:
|
|
63
|
+
customStyles?.theme === "dark" ? "gray" : undefined,
|
|
64
|
+
...customStyles?.text,
|
|
65
|
+
...customStyles?.cancelButton,
|
|
66
|
+
},
|
|
67
|
+
confirmButton: {
|
|
68
|
+
borderColor: "green",
|
|
69
|
+
color:
|
|
70
|
+
customStyles?.theme === "dark" ? DARK_MODE_TEXT_COLOR : "green",
|
|
71
|
+
backgroundColor:
|
|
72
|
+
customStyles?.theme === "dark" ? "green" : undefined,
|
|
73
|
+
...customStyles?.text,
|
|
74
|
+
...customStyles?.confirmButton,
|
|
75
|
+
},
|
|
76
|
+
modalTitle: {
|
|
77
|
+
fontSize: 24,
|
|
78
|
+
fontWeight: "bold",
|
|
79
|
+
marginBottom: 15,
|
|
80
|
+
color:
|
|
81
|
+
customStyles?.theme === "dark"
|
|
82
|
+
? DARK_MODE_TEXT_COLOR
|
|
83
|
+
: LIGHT_MODE_TEXT_COLOR,
|
|
84
|
+
...customStyles?.text,
|
|
85
|
+
...customStyles?.modalTitle,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { View, Text, TouchableOpacity } from "react-native";
|
|
9
|
+
|
|
10
|
+
import TimerPicker, { TimerPickerProps, TimerPickerRef } from "./TimerPicker";
|
|
11
|
+
import Modal from "./Modal";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
generateStyles,
|
|
15
|
+
CustomTimerPickerModalStyles,
|
|
16
|
+
} from "./TimerPickerModal.styles";
|
|
17
|
+
|
|
18
|
+
export interface TimerPickerModalRef {
|
|
19
|
+
reset: (options?: { animated?: boolean }) => void;
|
|
20
|
+
setValue: (
|
|
21
|
+
value: {
|
|
22
|
+
hours: number;
|
|
23
|
+
minutes: number;
|
|
24
|
+
seconds: number;
|
|
25
|
+
},
|
|
26
|
+
options?: { animated?: boolean }
|
|
27
|
+
) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TimerPickerModalProps extends TimerPickerProps {
|
|
31
|
+
visible: boolean;
|
|
32
|
+
setIsVisible: (isVisible: boolean) => void;
|
|
33
|
+
onConfirm: ({
|
|
34
|
+
hours,
|
|
35
|
+
minutes,
|
|
36
|
+
seconds,
|
|
37
|
+
}: {
|
|
38
|
+
hours: number;
|
|
39
|
+
minutes: number;
|
|
40
|
+
seconds: number;
|
|
41
|
+
}) => void;
|
|
42
|
+
onCancel?: () => void;
|
|
43
|
+
closeOnOverlayPress?: boolean;
|
|
44
|
+
hideCancelButton?: boolean;
|
|
45
|
+
confirmButtonText?: string;
|
|
46
|
+
cancelButtonText?: string;
|
|
47
|
+
modalTitle?: string;
|
|
48
|
+
modalProps?: React.ComponentProps<typeof Modal>;
|
|
49
|
+
containerProps?: React.ComponentProps<typeof View>;
|
|
50
|
+
contentContainerProps?: React.ComponentProps<typeof View>;
|
|
51
|
+
buttonContainerProps?: React.ComponentProps<typeof View>;
|
|
52
|
+
buttonTouchableOpacityProps?: React.ComponentProps<typeof TouchableOpacity>;
|
|
53
|
+
modalTitleProps?: React.ComponentProps<typeof Text>;
|
|
54
|
+
styles?: CustomTimerPickerModalStyles;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const TimerPickerModal = forwardRef<TimerPickerModalRef, TimerPickerModalProps>(
|
|
58
|
+
(
|
|
59
|
+
{
|
|
60
|
+
visible,
|
|
61
|
+
setIsVisible,
|
|
62
|
+
onConfirm,
|
|
63
|
+
onCancel,
|
|
64
|
+
onDurationChange,
|
|
65
|
+
closeOnOverlayPress,
|
|
66
|
+
initialHours = 0,
|
|
67
|
+
initialMinutes = 0,
|
|
68
|
+
initialSeconds = 0,
|
|
69
|
+
hideHours = false,
|
|
70
|
+
hideMinutes = false,
|
|
71
|
+
hideSeconds = false,
|
|
72
|
+
hourLimit,
|
|
73
|
+
minuteLimit,
|
|
74
|
+
secondLimit,
|
|
75
|
+
hourLabel = "h",
|
|
76
|
+
minuteLabel = "m",
|
|
77
|
+
secondLabel = "s",
|
|
78
|
+
padWithNItems = 1,
|
|
79
|
+
disableInfiniteScroll = false,
|
|
80
|
+
hideCancelButton = false,
|
|
81
|
+
confirmButtonText = "Confirm",
|
|
82
|
+
cancelButtonText = "Cancel",
|
|
83
|
+
modalTitle,
|
|
84
|
+
LinearGradient,
|
|
85
|
+
modalProps,
|
|
86
|
+
containerProps,
|
|
87
|
+
contentContainerProps,
|
|
88
|
+
pickerContainerProps,
|
|
89
|
+
buttonContainerProps,
|
|
90
|
+
buttonTouchableOpacityProps,
|
|
91
|
+
modalTitleProps,
|
|
92
|
+
pickerGradientOverlayProps,
|
|
93
|
+
topPickerGradientOverlayProps,
|
|
94
|
+
bottomPickerGradientOverlayProps,
|
|
95
|
+
styles: customStyles,
|
|
96
|
+
},
|
|
97
|
+
ref
|
|
98
|
+
): React.ReactElement => {
|
|
99
|
+
const styles = generateStyles(customStyles);
|
|
100
|
+
|
|
101
|
+
const [selectedDuration, setSelectedDuration] = useState({
|
|
102
|
+
hours: initialHours,
|
|
103
|
+
minutes: initialMinutes,
|
|
104
|
+
seconds: initialSeconds,
|
|
105
|
+
});
|
|
106
|
+
const [confirmedDuration, setConfirmedDuration] = useState({
|
|
107
|
+
hours: initialHours,
|
|
108
|
+
minutes: initialMinutes,
|
|
109
|
+
seconds: initialSeconds,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const hideModal = () => {
|
|
113
|
+
setSelectedDuration({
|
|
114
|
+
hours: confirmedDuration.hours,
|
|
115
|
+
minutes: confirmedDuration.minutes,
|
|
116
|
+
seconds: confirmedDuration.seconds,
|
|
117
|
+
});
|
|
118
|
+
setIsVisible(false);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const confirm = () => {
|
|
122
|
+
setConfirmedDuration(selectedDuration);
|
|
123
|
+
onConfirm(selectedDuration);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const cancel = () => {
|
|
127
|
+
setIsVisible(false);
|
|
128
|
+
setSelectedDuration(confirmedDuration);
|
|
129
|
+
onCancel?.();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// wrapped in useCallback to avoid unnecessary re-renders of TimerPicker
|
|
133
|
+
const durationChange = useCallback(
|
|
134
|
+
(duration: { hours: number; minutes: number; seconds: number }) => {
|
|
135
|
+
setSelectedDuration(duration);
|
|
136
|
+
onDurationChange?.(duration);
|
|
137
|
+
},
|
|
138
|
+
[onDurationChange]
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const timerPickerRef = useRef<TimerPickerRef>(null);
|
|
142
|
+
|
|
143
|
+
useImperativeHandle(ref, () => ({
|
|
144
|
+
reset: (options) => {
|
|
145
|
+
const initialDuration = {
|
|
146
|
+
hours: initialHours,
|
|
147
|
+
minutes: initialMinutes,
|
|
148
|
+
seconds: initialSeconds,
|
|
149
|
+
};
|
|
150
|
+
setSelectedDuration(initialDuration);
|
|
151
|
+
setConfirmedDuration(initialDuration);
|
|
152
|
+
timerPickerRef.current?.reset(options);
|
|
153
|
+
},
|
|
154
|
+
setValue: (value, options) => {
|
|
155
|
+
setSelectedDuration(value);
|
|
156
|
+
setConfirmedDuration(value);
|
|
157
|
+
timerPickerRef.current?.setValue(value, options);
|
|
158
|
+
},
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<Modal
|
|
163
|
+
isVisible={visible}
|
|
164
|
+
onOverlayPress={closeOnOverlayPress ? hideModal : undefined}
|
|
165
|
+
{...modalProps}
|
|
166
|
+
testID="timer-picker-modal">
|
|
167
|
+
<View {...containerProps} style={styles.container}>
|
|
168
|
+
<View
|
|
169
|
+
{...contentContainerProps}
|
|
170
|
+
style={styles.contentContainer}>
|
|
171
|
+
{modalTitle ? (
|
|
172
|
+
<Text
|
|
173
|
+
{...modalTitleProps}
|
|
174
|
+
style={styles.modalTitle}>
|
|
175
|
+
{modalTitle}
|
|
176
|
+
</Text>
|
|
177
|
+
) : null}
|
|
178
|
+
<TimerPicker
|
|
179
|
+
ref={timerPickerRef}
|
|
180
|
+
onDurationChange={durationChange}
|
|
181
|
+
initialHours={confirmedDuration.hours}
|
|
182
|
+
initialMinutes={confirmedDuration.minutes}
|
|
183
|
+
initialSeconds={confirmedDuration.seconds}
|
|
184
|
+
hideHours={hideHours}
|
|
185
|
+
hideMinutes={hideMinutes}
|
|
186
|
+
hideSeconds={hideSeconds}
|
|
187
|
+
hourLimit={hourLimit}
|
|
188
|
+
minuteLimit={minuteLimit}
|
|
189
|
+
secondLimit={secondLimit}
|
|
190
|
+
hourLabel={hourLabel}
|
|
191
|
+
minuteLabel={minuteLabel}
|
|
192
|
+
secondLabel={secondLabel}
|
|
193
|
+
padWithNItems={padWithNItems}
|
|
194
|
+
disableInfiniteScroll={disableInfiniteScroll}
|
|
195
|
+
LinearGradient={LinearGradient}
|
|
196
|
+
pickerContainerProps={pickerContainerProps}
|
|
197
|
+
pickerGradientOverlayProps={
|
|
198
|
+
pickerGradientOverlayProps
|
|
199
|
+
}
|
|
200
|
+
topPickerGradientOverlayProps={
|
|
201
|
+
topPickerGradientOverlayProps
|
|
202
|
+
}
|
|
203
|
+
bottomPickerGradientOverlayProps={
|
|
204
|
+
bottomPickerGradientOverlayProps
|
|
205
|
+
}
|
|
206
|
+
styles={customStyles}
|
|
207
|
+
/>
|
|
208
|
+
<View
|
|
209
|
+
{...buttonContainerProps}
|
|
210
|
+
style={styles.buttonContainer}>
|
|
211
|
+
{!hideCancelButton ? (
|
|
212
|
+
<TouchableOpacity
|
|
213
|
+
onPress={cancel}
|
|
214
|
+
{...buttonTouchableOpacityProps}>
|
|
215
|
+
<Text
|
|
216
|
+
style={[
|
|
217
|
+
styles.button,
|
|
218
|
+
styles.cancelButton,
|
|
219
|
+
]}>
|
|
220
|
+
{cancelButtonText}
|
|
221
|
+
</Text>
|
|
222
|
+
</TouchableOpacity>
|
|
223
|
+
) : null}
|
|
224
|
+
<TouchableOpacity
|
|
225
|
+
onPress={confirm}
|
|
226
|
+
{...buttonTouchableOpacityProps}>
|
|
227
|
+
<Text
|
|
228
|
+
style={[
|
|
229
|
+
styles.button,
|
|
230
|
+
styles.confirmButton,
|
|
231
|
+
]}>
|
|
232
|
+
{confirmButtonText}
|
|
233
|
+
</Text>
|
|
234
|
+
</TouchableOpacity>
|
|
235
|
+
</View>
|
|
236
|
+
</View>
|
|
237
|
+
</View>
|
|
238
|
+
</Modal>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
export default React.memo(TimerPickerModal);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
default as TimerPickerModal,
|
|
3
|
+
type TimerPickerModalProps,
|
|
4
|
+
type TimerPickerModalRef,
|
|
5
|
+
} from "./components";
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
default as TimerPicker,
|
|
9
|
+
type TimerPickerProps,
|
|
10
|
+
type TimerPickerRef,
|
|
11
|
+
} from "./components/TimerPicker";
|
|
12
|
+
|
|
13
|
+
export { type CustomTimerPickerModalStyles } from "./components/TimerPickerModal.styles";
|
|
14
|
+
export { type CustomTimerPickerStyles } from "./components/TimerPicker/TimerPicker.styles";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import DurationScroll from "../components/TimerPicker/DurationScroll";
|
|
4
|
+
|
|
5
|
+
describe("DurationScroll", () => {
|
|
6
|
+
const onDurationChangeMock = jest.fn();
|
|
7
|
+
const emptyStyles = {
|
|
8
|
+
pickerContainer: {},
|
|
9
|
+
pickerLabelContainer: {},
|
|
10
|
+
pickerLabel: {},
|
|
11
|
+
pickerItemContainer: {},
|
|
12
|
+
pickerItem: {},
|
|
13
|
+
pickerGradientOverlay: {},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
it("renders without crashing", () => {
|
|
17
|
+
const { getByTestId } = render(
|
|
18
|
+
<DurationScroll
|
|
19
|
+
numberOfItems={1}
|
|
20
|
+
onDurationChange={onDurationChangeMock}
|
|
21
|
+
padWithNItems={0}
|
|
22
|
+
styles={emptyStyles}
|
|
23
|
+
testID="duration-scroll"
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
const component = getByTestId("duration-scroll");
|
|
27
|
+
expect(component).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("renders the correct number of items", () => {
|
|
31
|
+
const { getAllByTestId } = render(
|
|
32
|
+
<DurationScroll
|
|
33
|
+
numberOfItems={2}
|
|
34
|
+
onDurationChange={onDurationChangeMock}
|
|
35
|
+
padWithNItems={1}
|
|
36
|
+
styles={emptyStyles}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
const items = getAllByTestId("picker-item");
|
|
40
|
+
expect(items).toHaveLength(7);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("renders the label if provided", () => {
|
|
44
|
+
const { getByText } = render(
|
|
45
|
+
<DurationScroll
|
|
46
|
+
numberOfItems={59}
|
|
47
|
+
label="Duration"
|
|
48
|
+
onDurationChange={onDurationChangeMock}
|
|
49
|
+
padWithNItems={1}
|
|
50
|
+
styles={emptyStyles}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
const label = getByText("Duration");
|
|
54
|
+
expect(label).toBeDefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import { render, fireEvent } from "@testing-library/react-native";
|
|
4
|
+
import Modal from "../components/Modal";
|
|
5
|
+
|
|
6
|
+
describe("Modal", () => {
|
|
7
|
+
it("renders without crashing", () => {
|
|
8
|
+
const { getByTestId } = render(<Modal />);
|
|
9
|
+
const component = getByTestId("modal");
|
|
10
|
+
expect(component).toBeDefined();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("renders children when visible", () => {
|
|
14
|
+
const { getByText } = render(
|
|
15
|
+
<Modal isVisible>
|
|
16
|
+
<Text>{"Modal Content"}</Text>
|
|
17
|
+
</Modal>
|
|
18
|
+
);
|
|
19
|
+
const content = getByText("Modal Content");
|
|
20
|
+
expect(content).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("calls onOverlayPress when overlay is pressed", () => {
|
|
24
|
+
const onOverlayPressMock = jest.fn();
|
|
25
|
+
const { getByTestId } = render(
|
|
26
|
+
<Modal isVisible onOverlayPress={onOverlayPressMock} />
|
|
27
|
+
);
|
|
28
|
+
const overlay = getByTestId("modal-backdrop");
|
|
29
|
+
fireEvent.press(overlay);
|
|
30
|
+
expect(onOverlayPressMock).toHaveBeenCalled();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Add more test cases to cover different interactions, scenarios, and edge cases
|
|
34
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import TimerPicker from "../components/TimerPicker";
|
|
4
|
+
|
|
5
|
+
describe("TimerPicker", () => {
|
|
6
|
+
it("renders without crashing", () => {
|
|
7
|
+
const { getByTestId } = render(<TimerPicker />);
|
|
8
|
+
const component = getByTestId("timer-picker");
|
|
9
|
+
expect(component).toBeDefined();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("renders without crashing with negative padWithNItems", () => {
|
|
13
|
+
const { getByTestId } = render(<TimerPicker padWithNItems={-1} />);
|
|
14
|
+
const component = getByTestId("timer-picker");
|
|
15
|
+
expect(component).toBeDefined();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("hides minutes and seconds when respective hide props are provided", () => {
|
|
19
|
+
const { queryByTestId } = render(
|
|
20
|
+
<TimerPicker hideMinutes hideSeconds />
|
|
21
|
+
);
|
|
22
|
+
const minutePicker = queryByTestId("duration-scroll-minute");
|
|
23
|
+
const secondPicker = queryByTestId("duration-scroll-second");
|
|
24
|
+
expect(minutePicker).toBeNull();
|
|
25
|
+
expect(secondPicker).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, fireEvent } from "@testing-library/react-native";
|
|
3
|
+
import TimerPickerModal from "../components";
|
|
4
|
+
|
|
5
|
+
describe("TimerPickerModal", () => {
|
|
6
|
+
const mockOnConfirm = jest.fn();
|
|
7
|
+
const mockOnCancel = jest.fn();
|
|
8
|
+
|
|
9
|
+
const defaultProps = {
|
|
10
|
+
visible: true,
|
|
11
|
+
setIsVisible: jest.fn(),
|
|
12
|
+
onConfirm: mockOnConfirm,
|
|
13
|
+
onCancel: mockOnCancel,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
it("renders without crashing", () => {
|
|
17
|
+
const { getByTestId } = render(<TimerPickerModal {...defaultProps} />);
|
|
18
|
+
const component = getByTestId("timer-picker-modal");
|
|
19
|
+
expect(component).toBeDefined();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("calls onConfirm when Confirm button is pressed", () => {
|
|
23
|
+
const { getByText } = render(<TimerPickerModal {...defaultProps} />);
|
|
24
|
+
const confirmButton = getByText("Confirm");
|
|
25
|
+
fireEvent.press(confirmButton);
|
|
26
|
+
expect(mockOnConfirm).toHaveBeenCalled();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("calls onCancel when Cancel button is pressed", () => {
|
|
30
|
+
const { getByText } = render(<TimerPickerModal {...defaultProps} />);
|
|
31
|
+
const cancelButton = getByText("Cancel");
|
|
32
|
+
fireEvent.press(cancelButton);
|
|
33
|
+
expect(mockOnCancel).toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("hides the modal when Cancel button is pressed", () => {
|
|
37
|
+
const setIsVisibleMock = jest.fn();
|
|
38
|
+
const { getByText } = render(
|
|
39
|
+
<TimerPickerModal
|
|
40
|
+
{...defaultProps}
|
|
41
|
+
setIsVisible={setIsVisibleMock}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
const cancelButton = getByText("Cancel");
|
|
45
|
+
fireEvent.press(cancelButton);
|
|
46
|
+
expect(setIsVisibleMock).toHaveBeenCalledWith(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("hides the modal when overlay is pressed", () => {
|
|
50
|
+
const setIsVisibleMock = jest.fn();
|
|
51
|
+
const { getByTestId } = render(
|
|
52
|
+
<TimerPickerModal
|
|
53
|
+
{...defaultProps}
|
|
54
|
+
setIsVisible={setIsVisibleMock}
|
|
55
|
+
closeOnOverlayPress
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
const overlay = getByTestId("modal-backdrop");
|
|
59
|
+
fireEvent.press(overlay);
|
|
60
|
+
expect(setIsVisibleMock).toHaveBeenCalledWith(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("calls onConfirm with selected duration when Confirm button is pressed", () => {
|
|
64
|
+
const { getByText } = render(<TimerPickerModal {...defaultProps} />);
|
|
65
|
+
// Select duration in TimerPicker, assuming its interaction is tested separately
|
|
66
|
+
const confirmButton = getByText("Confirm");
|
|
67
|
+
fireEvent.press(confirmButton);
|
|
68
|
+
expect(mockOnConfirm).toHaveBeenCalledWith(expect.objectContaining({}));
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.colorToRgba = void 0;
|
|
4
|
-
var colorToRgba = function (variables) {
|
|
1
|
+
export const colorToRgba = (variables: { color: string; opacity?: number }) => {
|
|
5
2
|
// this function is required for expo-linear-gradient on iOS. To fade to transparent, we need
|
|
6
3
|
// to be able to add opacity to the background color. Supplying 'transparent' does not work
|
|
7
4
|
// because that is actually a transparent black (rgba(0, 0, 0, 1)), which results in dodgy rendering
|
|
8
|
-
|
|
5
|
+
|
|
6
|
+
const { color, opacity = 1 } = variables;
|
|
9
7
|
// Handle named colors
|
|
10
|
-
|
|
8
|
+
const namedColors: { [key: string]: string } = {
|
|
11
9
|
transparent: "rgba(0, 0, 0, 0)",
|
|
12
10
|
black: "rgba(0, 0, 0, 1)",
|
|
13
11
|
white: "rgba(255, 255, 255, 1)",
|
|
@@ -16,33 +14,36 @@ var colorToRgba = function (variables) {
|
|
|
16
14
|
gray: "rgba(128, 128, 128, 1)",
|
|
17
15
|
red: "rgba(255, 0, 0, 1)",
|
|
18
16
|
};
|
|
17
|
+
|
|
19
18
|
if (color in namedColors) {
|
|
20
19
|
return namedColors[color];
|
|
21
20
|
}
|
|
21
|
+
|
|
22
22
|
// Handle RGB format
|
|
23
23
|
if (color.startsWith("rgb(")) {
|
|
24
|
-
|
|
24
|
+
const rgbValues = color
|
|
25
25
|
.replace("rgb(", "")
|
|
26
26
|
.replace(")", "")
|
|
27
27
|
.split(",")
|
|
28
|
-
.map(
|
|
29
|
-
|
|
30
|
-
return
|
|
28
|
+
.map((value) => parseInt(value.trim(), 10));
|
|
29
|
+
const [r, g, b] = rgbValues;
|
|
30
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
31
31
|
}
|
|
32
|
+
|
|
32
33
|
// Handle hex format
|
|
33
34
|
if (color.startsWith("#")) {
|
|
34
|
-
|
|
35
|
+
let hexColor = color.slice(1);
|
|
35
36
|
if (hexColor.length === 3) {
|
|
36
37
|
hexColor = hexColor
|
|
37
38
|
.split("")
|
|
38
|
-
.map(
|
|
39
|
+
.map((value) => value + value)
|
|
39
40
|
.join("");
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return
|
|
42
|
+
const r = parseInt(hexColor.slice(0, 2), 16);
|
|
43
|
+
const g = parseInt(hexColor.slice(2, 4), 16);
|
|
44
|
+
const b = parseInt(hexColor.slice(4, 6), 16);
|
|
45
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
45
46
|
}
|
|
47
|
+
|
|
46
48
|
return color; // Return unchanged if unable to parse
|
|
47
49
|
};
|
|
48
|
-
exports.colorToRgba = colorToRgba;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { padWithZero } from "./padWithZero";
|
|
2
|
+
|
|
3
|
+
export const generateNumbers = (
|
|
4
|
+
numberOfItems: number,
|
|
5
|
+
options: {
|
|
6
|
+
repeatNTimes?: number;
|
|
7
|
+
padWithZero?: boolean;
|
|
8
|
+
disableInfiniteScroll?: boolean;
|
|
9
|
+
padWithNItems: number;
|
|
10
|
+
}
|
|
11
|
+
) => {
|
|
12
|
+
if (numberOfItems <= 0) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let numbers: string[] = [];
|
|
17
|
+
if (options.padWithZero) {
|
|
18
|
+
for (let i = 0; i <= numberOfItems; i++) {
|
|
19
|
+
numbers.push(padWithZero(i));
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
for (let i = 0; i <= numberOfItems; i++) {
|
|
23
|
+
numbers.push(String(i));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if ((options.repeatNTimes ?? 1) > 1) {
|
|
27
|
+
numbers = Array(options.repeatNTimes).fill(numbers).flat();
|
|
28
|
+
}
|
|
29
|
+
if (options.disableInfiniteScroll) {
|
|
30
|
+
numbers.push(...Array(options.padWithNItems).fill(""));
|
|
31
|
+
numbers.unshift(...Array(options.padWithNItems).fill(""));
|
|
32
|
+
}
|
|
33
|
+
return numbers;
|
|
34
|
+
};
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { LimitType } from "../components/TimerPicker/DurationScroll";
|
|
2
|
+
|
|
3
|
+
export const getAdjustedLimit = (
|
|
4
|
+
limit: LimitType | undefined,
|
|
5
|
+
numberOfItems: number
|
|
6
|
+
): {
|
|
7
|
+
max: number;
|
|
8
|
+
min: number;
|
|
9
|
+
} => {
|
|
5
10
|
if (!limit || (!limit.max && !limit.min)) {
|
|
6
11
|
return {
|
|
7
12
|
max: numberOfItems,
|
|
8
13
|
min: 0,
|
|
9
14
|
};
|
|
10
15
|
}
|
|
16
|
+
|
|
11
17
|
// guard against limits that are out of bounds
|
|
12
|
-
|
|
18
|
+
const adjustedMaxLimit = limit.max
|
|
13
19
|
? Math.min(limit.max, numberOfItems)
|
|
14
20
|
: numberOfItems;
|
|
15
|
-
|
|
21
|
+
const adjustedMinLimit = limit.min ? Math.max(limit.min, 0) : 0;
|
|
22
|
+
|
|
16
23
|
// guard against invalid limits
|
|
17
24
|
if (adjustedMaxLimit < adjustedMinLimit) {
|
|
18
25
|
return {
|
|
@@ -20,9 +27,9 @@ var getAdjustedLimit = function (limit, numberOfItems) {
|
|
|
20
27
|
min: 0,
|
|
21
28
|
};
|
|
22
29
|
}
|
|
30
|
+
|
|
23
31
|
return {
|
|
24
32
|
max: adjustedMaxLimit,
|
|
25
33
|
min: adjustedMinLimit,
|
|
26
34
|
};
|
|
27
35
|
};
|
|
28
|
-
exports.getAdjustedLimit = getAdjustedLimit;
|