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