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