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'],
61})! as ['default', 'spinner'];
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(false);
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'>(DISPLAY_VALUES[0]);
77  const [interval, setMinInterval] = useState(1);
78  const [neutralButtonLabel, setNeutralButtonLabel] = useState<string | undefined>();
79  const [disabled, setDisabled] = useState(false);
80  const [neutralButtonPressed, setNeutralButtonPressed] = useState<boolean>(false);
81
82  const scrollRef = useRef<ScrollView>(null);
83
84  const onChange = (event: DateTimePickerEvent, selectedDate?: Date | undefined) => {
85    if (Platform.OS === 'android') {
86      setShow(false);
87    }
88    const currentDate = selectedDate || date;
89    if (event.type === 'neutralButtonPressed') {
90      setNeutralButtonPressed(true);
91      setDate(new Date());
92    } else {
93      setNeutralButtonPressed(false);
94      setDate(currentDate);
95    }
96  };
97
98  const isDarkMode = useColorScheme() === 'dark';
99
100  const backgroundStyle = {
101    backgroundColor: isDarkMode ? Colors.dark : Colors.lighter,
102  };
103
104  return (
105    <SafeAreaView style={[backgroundStyle, { flex: 1 }]}>
106      <StatusBar barStyle="dark-content" />
107      <ScrollView
108        testID="DateTimePickerScrollView"
109        ref={scrollRef}
110        onContentSizeChange={() => {
111          if (Platform.OS === 'ios') {
112            scrollRef.current?.scrollToEnd({ animated: true });
113          }
114        }}>
115        {/* @ts-expect-error */}
116        {global.HermesInternal != null && (
117          <View style={styles.engine}>
118            <Text testID="hermesIndicator" style={styles.footer}>
119              Engine: Hermes
120            </Text>
121          </View>
122        )}
123        <View
124          testID="appRootView"
125          style={{ backgroundColor: isDarkMode ? Colors.black : Colors.white }}>
126          <ThemedText>mode prop:</ThemedText>
127          <SegmentedControl
128            values={MODE_VALUES}
129            selectedIndex={MODE_VALUES.indexOf(mode)}
130            onChange={(event) => {
131              setMode(MODE_VALUES[event.nativeEvent.selectedSegmentIndex]);
132            }}
133          />
134          <ThemedText>display prop:</ThemedText>
135          <SegmentedControl
136            values={DISPLAY_VALUES}
137            selectedIndex={DISPLAY_VALUES.indexOf(display)}
138            onChange={(event) => {
139              setDisplay(DISPLAY_VALUES[event.nativeEvent.selectedSegmentIndex]);
140            }}
141          />
142          <ThemedText>minute interval prop:</ThemedText>
143          <SegmentedControl
144            values={MINUTE_INTERVALS.map(String)}
145            selectedIndex={MINUTE_INTERVALS.indexOf(interval)}
146            onChange={(event) => {
147              setMinInterval(MINUTE_INTERVALS[event.nativeEvent.selectedSegmentIndex]);
148            }}
149          />
150          {Platform.OS === 'ios' && (
151            <>
152              <View style={styles.header}>
153                <ThemedText style={styles.textLabel}>text color (iOS only)</ThemedText>
154                <ThemedTextInput
155                  value={textColor}
156                  onChangeText={(text) => {
157                    setTextColor(text.toLowerCase());
158                  }}
159                  placeholder="textColor"
160                />
161              </View>
162              <View style={styles.header}>
163                <ThemedText style={styles.textLabel}>accent color (iOS only)</ThemedText>
164                <ThemedTextInput
165                  value={accentColor}
166                  onChangeText={(text) => {
167                    setAccentColor(text.toLowerCase());
168                  }}
169                  placeholder="accentColor"
170                />
171              </View>
172              <View style={styles.header}>
173                <ThemedText style={styles.textLabel}>disabled (iOS only)</ThemedText>
174                <Switch value={disabled} onValueChange={setDisabled} />
175              </View>
176            </>
177          )}
178          {Platform.OS === 'android' && (
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          )}
189          <View style={[styles.button, { flexDirection: 'row', justifyContent: 'space-around' }]}>
190            <Button
191              testID="showPickerButton"
192              onPress={() => {
193                setShow(true);
194              }}
195              title="Show picker!"
196            />
197            <Button testID="hidePicker" onPress={() => setShow(false)} title="Hide picker!" />
198          </View>
199          <View
200            style={[
201              styles.header,
202              {
203                flexDirection: 'column',
204                justifyContent: 'space-around',
205                alignContent: 'space-around',
206              },
207            ]}>
208            <ThemedText testID="dateText" style={styles.dateTimeText}>
209              {moment(date).format('MM/DD/YYYY  HH:mm')}
210            </ThemedText>
211            {neutralButtonPressed && (
212              <ThemedText testID="neutralButtonPressed" style={styles.dateTimeText}>
213                Neutral button was pressed, date changed to now.
214              </ThemedText>
215            )}
216          </View>
217          {show && (
218            <DateTimePicker
219              testID="dateTimePicker"
220              minuteInterval={interval}
221              value={date}
222              mode={mode}
223              is24Hour
224              display={display}
225              onChange={onChange}
226              style={styles.iOsPicker}
227              textColor={textColor || undefined}
228              accentColor={accentColor || undefined}
229              neutralButton={
230                neutralButtonLabel ? { label: neutralButtonLabel, textColor: 'grey' } : undefined
231              }
232              disabled={disabled}
233            />
234          )}
235        </View>
236      </ScrollView>
237    </SafeAreaView>
238  );
239};
240
241const styles = StyleSheet.create({
242  scrollView: {
243    backgroundColor: Colors.lighter,
244  },
245  engine: {
246    position: 'absolute',
247    right: 0,
248  },
249  body: {
250    backgroundColor: Colors.white,
251  },
252  footer: {
253    color: Colors.dark,
254    fontSize: 12,
255    fontWeight: '600',
256    padding: 4,
257    paddingRight: 12,
258    textAlign: 'right',
259  },
260  container: {
261    marginTop: 32,
262    flex: 1,
263    justifyContent: 'center',
264    backgroundColor: '#F5FCFF',
265  },
266  containerWindows: {
267    marginTop: 32,
268    flex: 1,
269    justifyContent: 'center',
270    alignItems: 'center',
271    backgroundColor: '#F5FCFF',
272  },
273  header: {
274    justifyContent: 'center',
275    alignItems: 'center',
276    flexDirection: 'row',
277  },
278  textLabel: {
279    margin: 10,
280    flex: 1,
281  },
282  textInput: {
283    height: 60,
284    flex: 1,
285  },
286  button: {
287    alignItems: 'center',
288    marginBottom: 10,
289  },
290  resetButton: {
291    width: 150,
292  },
293  text: {
294    fontSize: 20,
295    fontWeight: 'bold',
296  },
297  dateTimeText: {
298    fontSize: 16,
299    fontWeight: 'normal',
300  },
301  iOsPicker: {
302    flex: 1,
303    marginTop: 30,
304  },
305  windowsPicker: {
306    flex: 1,
307    paddingTop: 10,
308    width: 350,
309  },
310});
311
312DateTimePickerScreen.navigationOptions = {
313  title: 'DateTimePicker',
314};
315
316export default DateTimePickerScreen;
317