-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathNativePicker.tsx
More file actions
128 lines (116 loc) · 3.59 KB
/
NativePicker.tsx
File metadata and controls
128 lines (116 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import * as React from "react";
import { StyleSheet, Platform, Keyboard } from "react-native";
import { Picker as NativePickerComponent } from "@react-native-picker/picker";
import { useDeepCompareMemo } from "../../utilities";
import {
CommonPickerProps,
SinglePickerProps,
normalizeToPickerOptions,
} from "./PickerCommon";
import PickerInputContainer from "./PickerInputContainer";
import { withTheme } from "@draftbit/theme";
const isAndroid = Platform.OS === "android";
const isWeb = Platform.OS === "web";
const NativePicker: React.FC<CommonPickerProps & SinglePickerProps> = ({
options: optionsProp = [],
onValueChange,
Icon,
placeholder,
value,
autoDismissKeyboard = true,
theme,
disabled,
...rest
}) => {
const pickerRef = React.useRef<NativePickerComponent<string | number>>(null);
const [pickerVisible, setPickerVisible] = React.useState(false);
const options = useDeepCompareMemo(() => {
const normalizedOptions = normalizeToPickerOptions(optionsProp);
// Underlying Picker component defaults selection to first element when value is not provided (or undefined)
// Placholder must be the 1st option in order to allow selection of the 'actual' 1st option
if (placeholder) {
return [{ label: placeholder, value: placeholder }, ...normalizedOptions];
} else {
return normalizedOptions;
}
}, [placeholder, optionsProp]);
// When no placeholder is provided then first item should be marked selected to reflect underlying Picker internal state
if (!placeholder && options.length && !value && value !== options[0].value) {
onValueChange?.(options[0].value);
}
const renderNativePicker = () => (
<NativePickerComponent
ref={pickerRef}
testID="native-picker-component"
selectedValue={value}
onValueChange={(newValue) => {
if (newValue !== placeholder) {
onValueChange?.(newValue);
} else if (newValue === placeholder) {
onValueChange?.("");
}
}}
style={styles.nativePicker}
onBlur={() => setPickerVisible(false)}
>
{options.map((option) => (
<NativePickerComponent.Item
testID="native-picker-item"
label={option.label.toString()}
value={option.value}
key={option.value}
/>
))}
</NativePickerComponent>
);
React.useEffect(() => {
if (pickerVisible && pickerRef.current) {
pickerRef?.current?.focus();
}
}, [pickerVisible, pickerRef]);
React.useEffect(() => {
if (pickerVisible && autoDismissKeyboard) {
Keyboard.dismiss();
}
}, [pickerVisible, autoDismissKeyboard]);
return (
<PickerInputContainer
testID="native-picker"
Icon={Icon}
placeholder={placeholder}
selectedValue={value}
options={options}
onPress={() => setPickerVisible(!pickerVisible)}
disabled={disabled}
{...rest}
>
{/* Web version is collapsed by default, always show to allow direct expand */}
{/* Android version needs to always be visible to allow .focus() call to launch the dialog */}
{(pickerVisible || isAndroid || isWeb) &&
!disabled &&
renderNativePicker()}
</PickerInputContainer>
);
};
const styles = StyleSheet.create({
nativePicker: {
position: "absolute",
bottom: 0,
left: 0,
right: 0,
flexDirection: "row",
justifyContent: "center",
width: "100%",
backgroundColor: "white",
opacity: 0,
...Platform.select({
web: {
height: "100%",
},
android: {
opacity: 0,
},
}),
},
});
export default withTheme(NativePicker);