forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {useCallback, useImperativeHandle} from 'react'
2import {Keyboard, View} from 'react-native'
3import DatePicker from 'react-native-date-picker'
4import {msg, Trans} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
6
7import {atoms as a, useTheme} from '#/alf'
8import {Button, ButtonText} from '#/components/Button'
9import * as Dialog from '#/components/Dialog'
10import {type DateFieldProps} from '#/components/forms/DateField/types'
11import {toSimpleDateString} from '#/components/forms/DateField/utils'
12import * as TextField from '#/components/forms/TextField'
13import {DateFieldButton} from './index.shared'
14
15export * as utils from '#/components/forms/DateField/utils'
16export const LabelText = TextField.LabelText
17
18/**
19 * Date-only input. Accepts a string in the format YYYY-MM-DD, or a Date object.
20 * Date objects are converted to strings in the format YYYY-MM-DD.
21 * Returns a string in the format YYYY-MM-DD.
22 *
23 * To generate a string in the format YYYY-MM-DD from a Date object, use the
24 * `utils.toSimpleDateString(Date)` export of this file.
25 */
26export function DateField({
27 value,
28 inputRef,
29 onChangeDate,
30 testID,
31 label,
32 isInvalid,
33 accessibilityHint,
34 maximumDate,
35}: DateFieldProps) {
36 const {_, i18n} = useLingui()
37 const t = useTheme()
38 const control = Dialog.useDialogControl()
39
40 const onChangeInternal = useCallback(
41 (date: Date | undefined) => {
42 if (date) {
43 const formatted = toSimpleDateString(date)
44 onChangeDate(formatted)
45 }
46 },
47 [onChangeDate],
48 )
49
50 useImperativeHandle(
51 inputRef,
52 () => ({
53 focus: () => {
54 Keyboard.dismiss()
55 control.open()
56 },
57 blur: () => {
58 control.close()
59 },
60 }),
61 [control],
62 )
63
64 return (
65 <>
66 <DateFieldButton
67 label={label}
68 value={value}
69 onPress={() => {
70 Keyboard.dismiss()
71 control.open()
72 }}
73 isInvalid={isInvalid}
74 accessibilityHint={accessibilityHint}
75 />
76 <Dialog.Outer
77 control={control}
78 testID={testID}
79 nativeOptions={{preventExpansion: true}}>
80 <Dialog.Handle />
81 <Dialog.ScrollableInner label={label}>
82 <View style={a.gap_lg}>
83 <View style={[a.relative, a.w_full, a.align_center]}>
84 <DatePicker
85 timeZoneOffsetInMinutes={0}
86 theme={t.scheme}
87 date={new Date(toSimpleDateString(value))}
88 onDateChange={onChangeInternal}
89 mode="date"
90 locale={i18n.locale}
91 testID={`${testID}-datepicker`}
92 aria-label={label}
93 accessibilityLabel={label}
94 accessibilityHint={accessibilityHint}
95 maximumDate={
96 maximumDate
97 ? new Date(toSimpleDateString(maximumDate))
98 : undefined
99 }
100 />
101 </View>
102 <Button
103 label={_(msg`Done`)}
104 onPress={() => control.close()}
105 size="large"
106 color="primary"
107 variant="solid">
108 <ButtonText>
109 <Trans>Done</Trans>
110 </ButtonText>
111 </Button>
112 </View>
113 </Dialog.ScrollableInner>
114 </Dialog.Outer>
115 </>
116 )
117}