1import DateTimePicker, { 2 DateTimePickerEvent, 3 IOSNativeProps, 4} from '@react-native-community/datetimepicker'; 5import SegmentedControl from '@react-native-segmented-control/segmented-control'; 6import moment from 'moment'; 7import React, { useRef, useState } from 'react'; 8import { 9 SafeAreaView, 10 ScrollView, 11 StyleSheet, 12 View, 13 Text, 14 StatusBar, 15 Platform, 16 TextInput, 17 useColorScheme, 18 Switch, 19 TextProps, 20 TextInputProps, 21 Button, 22} from 'react-native'; 23import { Colors } from 'react-native/Libraries/NewAppScreen'; 24 25export const DAY_OF_WEEK = Object.freeze({ 26 Sunday: 0, 27 Monday: 1, 28 Tuesday: 2, 29 Wednesday: 3, 30 Thursday: 4, 31 Friday: 5, 32 Saturday: 6, 33}); 34 35const ThemedText = (props: TextProps) => { 36 const isDarkMode = useColorScheme() === 'dark'; 37 38 const textColorByMode = { color: isDarkMode ? Colors.white : Colors.black }; 39 40 const TextElement = React.createElement(Text, props); 41 return React.cloneElement(TextElement, { 42 style: [props.style, textColorByMode], 43 }); 44}; 45const ThemedTextInput = (props: TextInputProps) => { 46 const isDarkMode = useColorScheme() === 'dark'; 47 48 const textColorByMode = { color: isDarkMode ? Colors.white : Colors.black }; 49 50 const TextElement = React.createElement(TextInput, props); 51 return React.cloneElement(TextElement, { 52 style: [props.style, styles.textInput, textColorByMode], 53 placeholderTextColor: isDarkMode ? Colors.white : Colors.black, 54 }); 55}; 56 57type Mode = NonNullable<IOSNativeProps['mode']>; 58const MODE_VALUES = Platform.select({ 59 ios: ['date', 'time', 'datetime', 'countdown'], 60 android: ['date', 'time'], 61}) as Mode[]; 62const DISPLAY_VALUES = Platform.select({ 63 ios: ['default', 'spinner', 'compact', 'inline'], 64 android: ['default', 'spinner'], 65})! as ['default', 'spinner']; 66 67type MinuteInterval = NonNullable<IOSNativeProps['minuteInterval']>; 68const MINUTE_INTERVALS: MinuteInterval[] = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30]; 69 70// This example is a refactored copy from https://github.com/react-native-community/react-native-datetimepicker/tree/master/example 71// Please try to keep it up to date when updating @react-native-community/datetimepicker package :) 72 73const DateTimePickerScreen = () => { 74 // Sat, 13 Nov 2021 10:00:00 GMT (local: Saturday, November 13, 2021 11:00:00 AM GMT+01:00) 75 const sourceMoment = moment.unix(1636797600); 76 const sourceDate = sourceMoment.local().toDate(); 77 const [show, setShow] = useState(false); 78 const [date, setDate] = useState<Date>(sourceDate); 79 const [mode, setMode] = useState<Mode>(MODE_VALUES[0]); 80 const [textColor, setTextColor] = useState<string | undefined>(); 81 const [accentColor, setAccentColor] = useState<string | undefined>(); 82 const [display, setDisplay] = useState<'default' | 'spinner'>(DISPLAY_VALUES[0]); 83 const [interval, setMinInterval] = useState<MinuteInterval>(1); 84 const [neutralButtonLabel, setNeutralButtonLabel] = useState<string | undefined>(); 85 const [disabled, setDisabled] = useState(false); 86 const [neutralButtonPressed, setNeutralButtonPressed] = useState<boolean>(false); 87 88 const scrollRef = useRef<ScrollView>(null); 89 90 const onChange = (event: DateTimePickerEvent, selectedDate?: Date | undefined) => { 91 if (Platform.OS === 'android') { 92 setShow(false); 93 } 94 const currentDate = selectedDate || date; 95 if (event.type === 'neutralButtonPressed') { 96 setNeutralButtonPressed(true); 97 setDate(new Date()); 98 } else { 99 setNeutralButtonPressed(false); 100 setDate(currentDate); 101 } 102 }; 103 104 const isDarkMode = useColorScheme() === 'dark'; 105 106 const backgroundStyle = { 107 backgroundColor: isDarkMode ? Colors.dark : Colors.lighter, 108 }; 109 110 return ( 111 <SafeAreaView style={[backgroundStyle, { flex: 1 }]}> 112 <StatusBar barStyle="dark-content" /> 113 <ScrollView 114 testID="DateTimePickerScrollView" 115 ref={scrollRef} 116 onContentSizeChange={() => { 117 if (Platform.OS === 'ios') { 118 scrollRef.current?.scrollToEnd({ animated: true }); 119 } 120 }}> 121 {/* @ts-expect-error */} 122 {global.HermesInternal != null && ( 123 <View style={styles.engine}> 124 <Text testID="hermesIndicator" style={styles.footer}> 125 Engine: Hermes 126 </Text> 127 </View> 128 )} 129 <View 130 testID="appRootView" 131 style={{ backgroundColor: isDarkMode ? Colors.black : Colors.white }}> 132 <ThemedText>mode prop:</ThemedText> 133 <SegmentedControl 134 values={MODE_VALUES} 135 selectedIndex={MODE_VALUES.indexOf(mode)} 136 onChange={(event) => { 137 setMode(MODE_VALUES[event.nativeEvent.selectedSegmentIndex]); 138 }} 139 /> 140 <ThemedText>display prop:</ThemedText> 141 <SegmentedControl 142 values={DISPLAY_VALUES} 143 selectedIndex={DISPLAY_VALUES.indexOf(display)} 144 onChange={(event) => { 145 setDisplay(DISPLAY_VALUES[event.nativeEvent.selectedSegmentIndex]); 146 }} 147 /> 148 <ThemedText>minute interval prop:</ThemedText> 149 <SegmentedControl 150 values={MINUTE_INTERVALS.map(String)} 151 selectedIndex={MINUTE_INTERVALS.indexOf(interval)} 152 onChange={(event) => { 153 setMinInterval(MINUTE_INTERVALS[event.nativeEvent.selectedSegmentIndex]); 154 }} 155 /> 156 {Platform.OS === 'ios' && ( 157 <> 158 <View style={styles.header}> 159 <ThemedText style={styles.textLabel}>text color (iOS only)</ThemedText> 160 <ThemedTextInput 161 value={textColor} 162 onChangeText={(text) => { 163 setTextColor(text.toLowerCase()); 164 }} 165 placeholder="textColor" 166 /> 167 </View> 168 <View style={styles.header}> 169 <ThemedText style={styles.textLabel}>accent color (iOS only)</ThemedText> 170 <ThemedTextInput 171 value={accentColor} 172 onChangeText={(text) => { 173 setAccentColor(text.toLowerCase()); 174 }} 175 placeholder="accentColor" 176 /> 177 </View> 178 <View style={styles.header}> 179 <ThemedText style={styles.textLabel}>disabled (iOS only)</ThemedText> 180 <Switch value={disabled} onValueChange={setDisabled} /> 181 </View> 182 </> 183 )} 184 {Platform.OS === 'android' && ( 185 <View style={styles.header}> 186 <ThemedText style={styles.textLabel}>neutralButtonLabel (android only)</ThemedText> 187 <ThemedTextInput 188 value={neutralButtonLabel} 189 onChangeText={setNeutralButtonLabel} 190 placeholder="neutralButtonLabel" 191 testID="neutralButtonLabelTextInput" 192 /> 193 </View> 194 )} 195 <View style={[styles.button, { flexDirection: 'row', justifyContent: 'space-around' }]}> 196 <Button 197 testID="showPickerButton" 198 onPress={() => { 199 setShow(true); 200 }} 201 title="Show picker!" 202 /> 203 <Button testID="hidePicker" onPress={() => setShow(false)} title="Hide picker!" /> 204 </View> 205 <View 206 style={[ 207 styles.header, 208 { 209 flexDirection: 'column', 210 justifyContent: 'space-around', 211 alignContent: 'space-around', 212 }, 213 ]}> 214 <ThemedText testID="dateText" style={styles.dateTimeText}> 215 {moment(date).format('MM/DD/YYYY HH:mm')} 216 </ThemedText> 217 {neutralButtonPressed && ( 218 <ThemedText testID="neutralButtonPressed" style={styles.dateTimeText}> 219 Neutral button was pressed, date changed to now. 220 </ThemedText> 221 )} 222 </View> 223 {show && ( 224 <DateTimePicker 225 testID="dateTimePicker" 226 minuteInterval={interval} 227 value={date} 228 mode={mode} 229 display={display} 230 onChange={onChange} 231 style={styles.iOsPicker} 232 textColor={textColor || undefined} 233 accentColor={accentColor || undefined} 234 disabled={disabled} 235 {...(Platform.OS !== 'android' && { 236 is24Hour: true, 237 neutralButton: neutralButtonLabel 238 ? { label: neutralButtonLabel, textColor: 'grey' } 239 : undefined, 240 })} 241 /> 242 )} 243 </View> 244 </ScrollView> 245 </SafeAreaView> 246 ); 247}; 248 249const styles = StyleSheet.create({ 250 scrollView: { 251 backgroundColor: Colors.lighter, 252 }, 253 engine: { 254 position: 'absolute', 255 right: 0, 256 }, 257 body: { 258 backgroundColor: Colors.white, 259 }, 260 footer: { 261 color: Colors.dark, 262 fontSize: 12, 263 fontWeight: '600', 264 padding: 4, 265 paddingRight: 12, 266 textAlign: 'right', 267 }, 268 container: { 269 marginTop: 32, 270 flex: 1, 271 justifyContent: 'center', 272 backgroundColor: '#F5FCFF', 273 }, 274 containerWindows: { 275 marginTop: 32, 276 flex: 1, 277 justifyContent: 'center', 278 alignItems: 'center', 279 backgroundColor: '#F5FCFF', 280 }, 281 header: { 282 justifyContent: 'center', 283 alignItems: 'center', 284 flexDirection: 'row', 285 }, 286 textLabel: { 287 margin: 10, 288 flex: 1, 289 }, 290 textInput: { 291 height: 60, 292 flex: 1, 293 }, 294 button: { 295 alignItems: 'center', 296 marginBottom: 10, 297 }, 298 resetButton: { 299 width: 150, 300 }, 301 text: { 302 fontSize: 20, 303 fontWeight: 'bold', 304 }, 305 dateTimeText: { 306 fontSize: 16, 307 fontWeight: 'normal', 308 }, 309 iOsPicker: { 310 flex: 1, 311 marginTop: 30, 312 }, 313 windowsPicker: { 314 flex: 1, 315 paddingTop: 10, 316 width: 350, 317 }, 318}); 319 320DateTimePickerScreen.navigationOptions = { 321 title: 'DateTimePicker', 322}; 323 324export default DateTimePickerScreen; 325