react-native-timer-picker 1.9.0 → 1.10.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 (34) hide show
  1. package/README.md +3 -0
  2. package/dist/commonjs/components/TimerPicker/index.js +6 -3
  3. package/dist/commonjs/components/TimerPicker/index.js.map +1 -1
  4. package/dist/commonjs/components/TimerPicker/types.js.map +1 -1
  5. package/dist/commonjs/tests/TimerPicker.test.js +14 -0
  6. package/dist/commonjs/tests/TimerPicker.test.js.map +1 -1
  7. package/dist/module/components/TimerPicker/index.js +6 -3
  8. package/dist/module/components/TimerPicker/index.js.map +1 -1
  9. package/dist/module/components/TimerPicker/types.js.map +1 -1
  10. package/dist/module/tests/TimerPicker.test.js +14 -0
  11. package/dist/module/tests/TimerPicker.test.js.map +1 -1
  12. package/dist/typescript/components/TimerPicker/types.d.ts +3 -0
  13. package/package.json +1 -2
  14. package/src/components/DurationScroll/index.tsx +0 -459
  15. package/src/components/DurationScroll/types.ts +0 -75
  16. package/src/components/Modal/index.tsx +0 -124
  17. package/src/components/Modal/styles.ts +0 -26
  18. package/src/components/Modal/types.ts +0 -17
  19. package/src/components/TimerPicker/index.tsx +0 -196
  20. package/src/components/TimerPicker/styles.ts +0 -123
  21. package/src/components/TimerPicker/types.ts +0 -74
  22. package/src/components/TimerPickerModal/index.tsx +0 -190
  23. package/src/components/TimerPickerModal/styles.ts +0 -88
  24. package/src/components/TimerPickerModal/types.ts +0 -52
  25. package/src/index.ts +0 -13
  26. package/src/tests/DurationScroll.test.tsx +0 -66
  27. package/src/tests/Modal.test.tsx +0 -36
  28. package/src/tests/TimerPicker.test.tsx +0 -29
  29. package/src/tests/TimerPickerModal.test.tsx +0 -72
  30. package/src/utils/colorToRgba.ts +0 -49
  31. package/src/utils/generateNumbers.ts +0 -64
  32. package/src/utils/getAdjustedLimit.ts +0 -35
  33. package/src/utils/getScrollIndex.ts +0 -15
  34. package/src/utils/padNumber.ts +0 -10
@@ -1,459 +0,0 @@
1
- import React, {
2
- useRef,
3
- useCallback,
4
- forwardRef,
5
- useImperativeHandle,
6
- useState,
7
- useEffect,
8
- } from "react";
9
-
10
- import { View, Text, FlatList as RNFlatList } from "react-native";
11
- import type {
12
- ViewabilityConfigCallbackPairs,
13
- ViewToken,
14
- NativeSyntheticEvent,
15
- NativeScrollEvent,
16
- } from "react-native";
17
-
18
- import { colorToRgba } from "../../utils/colorToRgba";
19
- import {
20
- generate12HourNumbers,
21
- generateNumbers,
22
- } from "../../utils/generateNumbers";
23
- import { getAdjustedLimit } from "../../utils/getAdjustedLimit";
24
- import { getScrollIndex } from "../../utils/getScrollIndex";
25
-
26
- import type { DurationScrollProps, DurationScrollRef } from "./types";
27
-
28
- const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
29
- (props, ref) => {
30
- const {
31
- aggressivelyGetLatestDuration,
32
- allowFontScaling = false,
33
- amLabel,
34
- Audio,
35
- bottomPickerGradientOverlayProps,
36
- clickSoundAsset,
37
- disableInfiniteScroll = false,
38
- FlatList = RNFlatList,
39
- Haptics,
40
- initialValue = 0,
41
- is12HourPicker,
42
- isDisabled,
43
- label,
44
- limit,
45
- LinearGradient,
46
- numberOfItems,
47
- onDurationChange,
48
- padNumbersWithZero = false,
49
- padWithNItems,
50
- pickerGradientOverlayProps,
51
- pmLabel,
52
- styles,
53
- testID,
54
- topPickerGradientOverlayProps,
55
- } = props;
56
-
57
- const data = !is12HourPicker
58
- ? generateNumbers(numberOfItems, {
59
- padNumbersWithZero,
60
- repeatNTimes: 3,
61
- disableInfiniteScroll,
62
- padWithNItems,
63
- })
64
- : generate12HourNumbers({
65
- padNumbersWithZero,
66
- repeatNTimes: 3,
67
- disableInfiniteScroll,
68
- padWithNItems,
69
- });
70
-
71
- const numberOfItemsToShow = 1 + padWithNItems * 2;
72
-
73
- const adjustedLimited = getAdjustedLimit(limit, numberOfItems);
74
-
75
- const initialScrollIndex = getScrollIndex({
76
- value: initialValue,
77
- numberOfItems,
78
- padWithNItems,
79
- disableInfiniteScroll,
80
- });
81
-
82
- // keep track of the latest duration as it scrolls
83
- const latestDuration = useRef(0);
84
- // keep track of the last index scrolled past for haptic/audio feedback
85
- const lastFeedbackIndex = useRef(0);
86
-
87
- const flatListRef = useRef<RNFlatList | null>(null);
88
-
89
- const [clickSound, setClickSound] = useState<
90
- | {
91
- replayAsync: () => Promise<void>;
92
- unloadAsync: () => Promise<void>;
93
- }
94
- | undefined
95
- >();
96
-
97
- // Preload the sound when the component mounts
98
- useEffect(() => {
99
- const loadSound = async () => {
100
- if (Audio) {
101
- const { sound } = await Audio.Sound.createAsync(
102
- clickSoundAsset ?? {
103
- // use a hosted sound as a fallback (do not use local asset due to loader issues
104
- // in some environments when including mp3 in library)
105
- uri: "https://drive.google.com/uc?export=download&id=10e1YkbNsRh-vGx1jmS1Nntz8xzkBp4_I",
106
- },
107
- { shouldPlay: false }
108
- );
109
- setClickSound(sound);
110
- }
111
- };
112
- loadSound();
113
-
114
- // Unload sound when component unmounts
115
- return () => {
116
- clickSound?.unloadAsync();
117
- };
118
- // eslint-disable-next-line react-hooks/exhaustive-deps
119
- }, [Audio]);
120
-
121
- useImperativeHandle(ref, () => ({
122
- reset: (options) => {
123
- flatListRef.current?.scrollToIndex({
124
- animated: options?.animated ?? false,
125
- index: initialScrollIndex,
126
- });
127
- },
128
- setValue: (value, options) => {
129
- flatListRef.current?.scrollToIndex({
130
- animated: options?.animated ?? false,
131
- index: getScrollIndex({
132
- value: value,
133
- numberOfItems,
134
- padWithNItems,
135
- disableInfiniteScroll,
136
- }),
137
- });
138
- },
139
- latestDuration: latestDuration,
140
- }));
141
-
142
- const renderItem = useCallback(
143
- ({ item }: { item: string }) => {
144
- let stringItem = item;
145
- let intItem: number;
146
- let isAm: boolean | undefined;
147
-
148
- if (!is12HourPicker) {
149
- intItem = parseInt(item);
150
- } else {
151
- isAm = item.includes("AM");
152
- stringItem = item.replace(/\s[AP]M/g, "");
153
- intItem = parseInt(stringItem);
154
- }
155
-
156
- return (
157
- <View
158
- key={item}
159
- style={styles.pickerItemContainer}
160
- testID="picker-item">
161
- <Text
162
- allowFontScaling={allowFontScaling}
163
- style={[
164
- styles.pickerItem,
165
- intItem > adjustedLimited.max ||
166
- intItem < adjustedLimited.min
167
- ? styles.disabledPickerItem
168
- : {},
169
- ]}>
170
- {stringItem}
171
- </Text>
172
- {is12HourPicker ? (
173
- <View
174
- pointerEvents="none"
175
- style={styles.pickerAmPmContainer}>
176
- <Text
177
- allowFontScaling={allowFontScaling}
178
- style={[styles.pickerAmPmLabel]}>
179
- {isAm ? amLabel : pmLabel}
180
- </Text>
181
- </View>
182
- ) : null}
183
- </View>
184
- );
185
- },
186
- [
187
- adjustedLimited.max,
188
- adjustedLimited.min,
189
- allowFontScaling,
190
- amLabel,
191
- is12HourPicker,
192
- pmLabel,
193
- styles.disabledPickerItem,
194
- styles.pickerAmPmContainer,
195
- styles.pickerAmPmLabel,
196
- styles.pickerItem,
197
- styles.pickerItemContainer,
198
- ]
199
- );
200
-
201
- const onScroll = useCallback(
202
- (e: NativeSyntheticEvent<NativeScrollEvent>) => {
203
- // this function is only used when the picker is in a modal and/or has Haptic/Audio feedback
204
- // it is used to ensure that the modal gets the latest duration on clicking
205
- // the confirm button, even if the scrollview is still scrolling
206
- if (!aggressivelyGetLatestDuration && !Haptics && !Audio) {
207
- return;
208
- }
209
-
210
- if (aggressivelyGetLatestDuration) {
211
- const newIndex = Math.round(
212
- e.nativeEvent.contentOffset.y /
213
- styles.pickerItemContainer.height
214
- );
215
- let newDuration =
216
- (disableInfiniteScroll
217
- ? newIndex
218
- : newIndex + padWithNItems) %
219
- (numberOfItems + 1);
220
-
221
- if (newDuration !== latestDuration.current) {
222
- // check limits
223
- if (newDuration > adjustedLimited.max) {
224
- newDuration = adjustedLimited.max;
225
- } else if (newDuration < adjustedLimited.min) {
226
- newDuration = adjustedLimited.min;
227
- }
228
-
229
- latestDuration.current = newDuration;
230
- }
231
- }
232
-
233
- if (Haptics || Audio) {
234
- const feedbackIndex = Math.round(
235
- (e.nativeEvent.contentOffset.y +
236
- styles.pickerItemContainer.height / 2) /
237
- styles.pickerItemContainer.height
238
- );
239
-
240
- if (feedbackIndex !== lastFeedbackIndex.current) {
241
- // this check stops the feedback firing when the component mounts
242
- if (lastFeedbackIndex.current) {
243
- // fire haptic feedback if available
244
- Haptics?.selectionAsync();
245
-
246
- // play click sound if available
247
- clickSound?.replayAsync();
248
- }
249
-
250
- lastFeedbackIndex.current = feedbackIndex;
251
- }
252
- }
253
- },
254
- // eslint-disable-next-line react-hooks/exhaustive-deps
255
- [
256
- adjustedLimited.max,
257
- adjustedLimited.min,
258
- aggressivelyGetLatestDuration,
259
- clickSound,
260
- disableInfiniteScroll,
261
- numberOfItems,
262
- padWithNItems,
263
- styles.pickerItemContainer.height,
264
- ]
265
- );
266
-
267
- const onMomentumScrollEnd = useCallback(
268
- (e: NativeSyntheticEvent<NativeScrollEvent>) => {
269
- const newIndex = Math.round(
270
- e.nativeEvent.contentOffset.y /
271
- styles.pickerItemContainer.height
272
- );
273
- let newDuration =
274
- (disableInfiniteScroll
275
- ? newIndex
276
- : newIndex + padWithNItems) %
277
- (numberOfItems + 1);
278
-
279
- // check limits
280
- if (newDuration > adjustedLimited.max) {
281
- const targetScrollIndex =
282
- newIndex - (newDuration - adjustedLimited.max);
283
- flatListRef.current?.scrollToIndex({
284
- animated: true,
285
- index:
286
- // guard against scrolling beyond end of list
287
- targetScrollIndex >= 0
288
- ? targetScrollIndex
289
- : adjustedLimited.max - 1,
290
- }); // scroll down to max
291
- newDuration = adjustedLimited.max;
292
- } else if (newDuration < adjustedLimited.min) {
293
- const targetScrollIndex =
294
- newIndex + (adjustedLimited.min - newDuration);
295
- flatListRef.current?.scrollToIndex({
296
- animated: true,
297
- index:
298
- // guard against scrolling beyond end of list
299
- targetScrollIndex <= data.length - 1
300
- ? targetScrollIndex
301
- : adjustedLimited.min,
302
- }); // scroll up to min
303
- newDuration = adjustedLimited.min;
304
- }
305
-
306
- onDurationChange(newDuration);
307
- },
308
- [
309
- adjustedLimited.max,
310
- adjustedLimited.min,
311
- data.length,
312
- disableInfiniteScroll,
313
- numberOfItems,
314
- onDurationChange,
315
- padWithNItems,
316
- styles.pickerItemContainer.height,
317
- ]
318
- );
319
-
320
- const onViewableItemsChanged = useCallback(
321
- ({ viewableItems }: { viewableItems: ViewToken[] }) => {
322
- if (
323
- viewableItems[0]?.index &&
324
- viewableItems[0].index < numberOfItems * 0.5
325
- ) {
326
- flatListRef.current?.scrollToIndex({
327
- animated: false,
328
- index: viewableItems[0].index + numberOfItems,
329
- });
330
- } else if (
331
- viewableItems[0]?.index &&
332
- viewableItems[0].index >= numberOfItems * 2.5
333
- ) {
334
- flatListRef.current?.scrollToIndex({
335
- animated: false,
336
- index: viewableItems[0].index - numberOfItems,
337
- });
338
- }
339
- },
340
- [numberOfItems]
341
- );
342
-
343
- const getItemLayout = useCallback(
344
- (_: ArrayLike<string> | null | undefined, index: number) => ({
345
- length: styles.pickerItemContainer.height,
346
- offset: styles.pickerItemContainer.height * index,
347
- index,
348
- }),
349
- [styles.pickerItemContainer.height]
350
- );
351
-
352
- const viewabilityConfigCallbackPairs =
353
- useRef<ViewabilityConfigCallbackPairs>([
354
- {
355
- viewabilityConfig: { viewAreaCoveragePercentThreshold: 25 },
356
- onViewableItemsChanged: onViewableItemsChanged,
357
- },
358
- ]);
359
-
360
- return (
361
- <View
362
- pointerEvents={isDisabled ? "none" : undefined}
363
- style={[
364
- {
365
- height:
366
- styles.pickerItemContainer.height *
367
- numberOfItemsToShow,
368
- overflow: "visible",
369
- },
370
- isDisabled && styles.disabledPickerContainer,
371
- ]}
372
- testID={testID}>
373
- <FlatList
374
- ref={flatListRef}
375
- data={data}
376
- decelerationRate={0.88}
377
- getItemLayout={getItemLayout}
378
- initialScrollIndex={initialScrollIndex}
379
- keyExtractor={(_, index) => index.toString()}
380
- nestedScrollEnabled
381
- onMomentumScrollEnd={onMomentumScrollEnd}
382
- onScroll={onScroll}
383
- renderItem={renderItem}
384
- scrollEnabled={!isDisabled}
385
- scrollEventThrottle={16}
386
- showsVerticalScrollIndicator={false}
387
- snapToAlignment="start"
388
- // used in place of snapToOffset due to bug on Android
389
- snapToOffsets={[...Array(data.length)].map(
390
- (_, i) => i * styles.pickerItemContainer.height
391
- )}
392
- testID="duration-scroll-flatlist"
393
- viewabilityConfigCallbackPairs={
394
- !disableInfiniteScroll
395
- ? viewabilityConfigCallbackPairs?.current
396
- : undefined
397
- }
398
- windowSize={numberOfItemsToShow}
399
- />
400
- <View pointerEvents="none" style={styles.pickerLabelContainer}>
401
- {typeof label === "string" ? (
402
- <Text
403
- allowFontScaling={allowFontScaling}
404
- style={styles.pickerLabel}>
405
- {label}
406
- </Text>
407
- ) : (
408
- label ?? null
409
- )}
410
- </View>
411
- {LinearGradient ? (
412
- <>
413
- <LinearGradient
414
- colors={[
415
- styles.pickerContainer.backgroundColor ??
416
- "white",
417
- colorToRgba({
418
- color:
419
- styles.pickerContainer
420
- .backgroundColor ?? "white",
421
- opacity: 0,
422
- }),
423
- ]}
424
- end={{ x: 1, y: 1 }}
425
- pointerEvents="none"
426
- start={{ x: 1, y: 0.3 }}
427
- {...pickerGradientOverlayProps}
428
- {...topPickerGradientOverlayProps}
429
- style={[styles.pickerGradientOverlay, { top: 0 }]}
430
- />
431
- <LinearGradient
432
- colors={[
433
- colorToRgba({
434
- color:
435
- styles.pickerContainer
436
- .backgroundColor ?? "white",
437
- opacity: 0,
438
- }),
439
- styles.pickerContainer.backgroundColor ??
440
- "white",
441
- ]}
442
- end={{ x: 1, y: 0.7 }}
443
- pointerEvents="none"
444
- start={{ x: 1, y: 0 }}
445
- {...pickerGradientOverlayProps}
446
- {...bottomPickerGradientOverlayProps}
447
- style={[
448
- styles.pickerGradientOverlay,
449
- { bottom: -1 },
450
- ]}
451
- />
452
- </>
453
- ) : null}
454
- </View>
455
- );
456
- }
457
- );
458
-
459
- export default React.memo(DurationScroll);
@@ -1,75 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type { MutableRefObject } from "react";
3
-
4
- import type {
5
- View,
6
- FlatList as RNFlatList,
7
- FlatListProps as RNFlatListProps,
8
- } from "react-native";
9
-
10
- import type { generateStyles } from "../TimerPicker/styles";
11
-
12
- export type CustomFlatList = <ItemT = any>(
13
- props: React.PropsWithChildren<
14
- RNFlatListProps<ItemT> & React.RefAttributes<RNFlatList<ItemT>>
15
- >,
16
- ref: React.ForwardedRef<RNFlatList<ItemT>>
17
- ) => React.ReactElement | null;
18
-
19
- export interface DurationScrollProps {
20
- Audio?: any;
21
- FlatList?: CustomFlatList;
22
- Haptics?: any;
23
- LinearGradient?: any;
24
- aggressivelyGetLatestDuration: boolean;
25
- allowFontScaling?: boolean;
26
- amLabel?: string;
27
- bottomPickerGradientOverlayProps?: Partial<LinearGradientProps>;
28
- clickSoundAsset?: SoundAssetType;
29
- disableInfiniteScroll?: boolean;
30
- initialValue?: number;
31
- is12HourPicker?: boolean;
32
- isDisabled?: boolean;
33
- label?: string | React.ReactElement;
34
- limit?: LimitType;
35
- numberOfItems: number;
36
- onDurationChange: (duration: number) => void;
37
- padNumbersWithZero?: boolean;
38
- padWithNItems: number;
39
- pickerGradientOverlayProps?: Partial<LinearGradientProps>;
40
- pmLabel?: string;
41
- styles: ReturnType<typeof generateStyles>;
42
- testID?: string;
43
- topPickerGradientOverlayProps?: Partial<LinearGradientProps>;
44
- }
45
-
46
- export interface DurationScrollRef {
47
- latestDuration: MutableRefObject<number>;
48
- reset: (options?: { animated?: boolean }) => void;
49
- setValue: (value: number, options?: { animated?: boolean }) => void;
50
- }
51
-
52
- type LinearGradientPoint = {
53
- x: number;
54
- y: number;
55
- };
56
-
57
- export type LinearGradientProps = React.ComponentProps<typeof View> & {
58
- colors: string[];
59
- end?: LinearGradientPoint | null;
60
- locations?: number[] | null;
61
- start?: LinearGradientPoint | null;
62
- };
63
-
64
- export type LimitType = {
65
- max?: number;
66
- min?: number;
67
- };
68
-
69
- export type SoundAssetType =
70
- | number
71
- | {
72
- headers?: Record<string, string>;
73
- overrideFileExtensionAndroid?: string;
74
- uri: string;
75
- };
@@ -1,124 +0,0 @@
1
- import React, { useCallback, useEffect, useRef } from "react";
2
-
3
- import {
4
- Animated,
5
- Easing,
6
- Modal as ReactNativeModal,
7
- TouchableWithoutFeedback,
8
- useWindowDimensions,
9
- } from "react-native";
10
-
11
- import { styles } from "./styles";
12
- import type { ModalProps } from "./types";
13
-
14
- export const Modal = (props: ModalProps) => {
15
- const {
16
- animationDuration = 300,
17
- children,
18
- contentStyle,
19
- isVisible = false,
20
- modalProps,
21
- onHide,
22
- onOverlayPress,
23
- overlayOpacity = 0.4,
24
- overlayStyle,
25
- testID = "modal",
26
- } = props;
27
-
28
- const { height: screenHeight, width: screenWidth } = useWindowDimensions();
29
-
30
- const isMounted = useRef(false);
31
- const animatedOpacity = useRef(new Animated.Value(0));
32
-
33
- useEffect(() => {
34
- isMounted.current = true;
35
- if (isVisible) {
36
- show();
37
- }
38
-
39
- return () => {
40
- isMounted.current = false;
41
- };
42
- // eslint-disable-next-line react-hooks/exhaustive-deps
43
- }, []);
44
-
45
- const backdropAnimatedStyle = {
46
- opacity: animatedOpacity.current.interpolate({
47
- inputRange: [0, 1],
48
- outputRange: [0, overlayOpacity],
49
- }),
50
- };
51
- const contentAnimatedStyle = {
52
- transform: [
53
- {
54
- translateY: animatedOpacity.current.interpolate({
55
- inputRange: [0, 1],
56
- outputRange: [screenHeight, 0],
57
- extrapolate: "clamp",
58
- }),
59
- },
60
- ],
61
- };
62
-
63
- const show = useCallback(() => {
64
- Animated.timing(animatedOpacity.current, {
65
- easing: Easing.inOut(Easing.quad),
66
- // Using native driver in the modal makes the content flash
67
- useNativeDriver: true,
68
- duration: animationDuration,
69
- toValue: 1,
70
- }).start();
71
- }, [animationDuration]);
72
-
73
- const hide = useCallback(() => {
74
- Animated.timing(animatedOpacity.current, {
75
- easing: Easing.inOut(Easing.quad),
76
- // Using native driver in the modal makes the content flash
77
- useNativeDriver: true,
78
- duration: animationDuration,
79
- toValue: 0,
80
- }).start(() => {
81
- if (isMounted.current) {
82
- onHide?.();
83
- }
84
- });
85
- }, [animationDuration, onHide]);
86
-
87
- useEffect(() => {
88
- if (isVisible) {
89
- show();
90
- } else {
91
- hide();
92
- }
93
- // eslint-disable-next-line react-hooks/exhaustive-deps
94
- }, [isVisible]);
95
-
96
- return (
97
- <ReactNativeModal
98
- animationType="fade"
99
- transparent
100
- visible={isVisible}
101
- {...modalProps}
102
- testID={testID}>
103
- <TouchableWithoutFeedback
104
- onPress={onOverlayPress}
105
- testID="modal-backdrop">
106
- <Animated.View
107
- style={[
108
- styles.backdrop,
109
- backdropAnimatedStyle,
110
- { width: screenWidth, height: screenHeight },
111
- overlayStyle,
112
- ]}
113
- />
114
- </TouchableWithoutFeedback>
115
- <Animated.View
116
- pointerEvents="box-none"
117
- style={[styles.content, contentAnimatedStyle, contentStyle]}>
118
- {children}
119
- </Animated.View>
120
- </ReactNativeModal>
121
- );
122
- };
123
-
124
- export default React.memo(Modal);
@@ -1,26 +0,0 @@
1
- import { StyleSheet } from "react-native";
2
-
3
- export const styles = StyleSheet.create({
4
- container: {
5
- position: "absolute",
6
- top: 0,
7
- left: 0,
8
- right: 0,
9
- bottom: 0,
10
- },
11
- backdrop: {
12
- position: "absolute",
13
- top: 0,
14
- bottom: 0,
15
- left: 0,
16
- right: 0,
17
- backgroundColor: "black",
18
- opacity: 0,
19
- },
20
- content: {
21
- flex: 1,
22
- justifyContent: "center",
23
- alignItems: "center",
24
- zIndex: 1,
25
- },
26
- });
@@ -1,17 +0,0 @@
1
- import type { ComponentProps } from "react";
2
-
3
- import type { ViewStyle } from "react-native";
4
- import type { Modal as ReactNativeModal } from "react-native";
5
-
6
- export interface ModalProps {
7
- animationDuration?: number;
8
- children?: React.ReactElement;
9
- contentStyle?: ViewStyle;
10
- isVisible?: boolean;
11
- modalProps?: ComponentProps<typeof ReactNativeModal>;
12
- onHide?: () => void;
13
- onOverlayPress?: () => void;
14
- overlayOpacity?: number;
15
- overlayStyle?: ViewStyle;
16
- testID?: string;
17
- }