diff options
Diffstat (limited to 'src/components/forms/DateField')
-rw-r--r-- | src/components/forms/DateField/index.android.tsx | 114 | ||||
-rw-r--r-- | src/components/forms/DateField/index.shared.tsx | 99 | ||||
-rw-r--r-- | src/components/forms/DateField/index.tsx | 72 | ||||
-rw-r--r-- | src/components/forms/DateField/index.web.tsx | 10 | ||||
-rw-r--r-- | src/components/forms/DateField/types.ts | 1 |
5 files changed, 197 insertions, 99 deletions
diff --git a/src/components/forms/DateField/index.android.tsx b/src/components/forms/DateField/index.android.tsx index 83fa285f5..700d15e6d 100644 --- a/src/components/forms/DateField/index.android.tsx +++ b/src/components/forms/DateField/index.android.tsx @@ -1,20 +1,11 @@ import React from 'react' -import {View, Pressable} from 'react-native' -import DateTimePicker, { - BaseProps as DateTimePickerProps, -} from '@react-native-community/datetimepicker' - -import {useTheme, atoms} from '#/alf' -import {Text} from '#/components/Typography' -import {useInteractionState} from '#/components/hooks/useInteractionState' -import * as TextField from '#/components/forms/TextField' -import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' +import DatePicker from 'react-native-date-picker' +import {useTheme} from '#/alf' import {DateFieldProps} from '#/components/forms/DateField/types' -import { - localizeDate, - toSimpleDateString, -} from '#/components/forms/DateField/utils' +import {toSimpleDateString} from '#/components/forms/DateField/utils' +import * as TextField from '#/components/forms/TextField' +import {DateFieldButton} from './index.shared' export * as utils from '#/components/forms/DateField/utils' export const Label = TextField.Label @@ -25,84 +16,55 @@ export function DateField({ label, isInvalid, testID, + accessibilityHint, }: DateFieldProps) { const t = useTheme() const [open, setOpen] = React.useState(false) - const { - state: pressed, - onIn: onPressIn, - onOut: onPressOut, - } = useInteractionState() - const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() - const {chromeFocus, chromeError, chromeErrorHover} = - TextField.useSharedInputStyles() - - const onChangeInternal = React.useCallback< - Required<DateTimePickerProps>['onChange'] - >( - (_event, date) => { + const onChangeInternal = React.useCallback( + (date: Date) => { setOpen(false) - if (date) { - const formatted = toSimpleDateString(date) - onChangeDate(formatted) - } + const formatted = toSimpleDateString(date) + onChangeDate(formatted) }, [onChangeDate, setOpen], ) - return ( - <View style={[atoms.relative, atoms.w_full]}> - <Pressable - aria-label={label} - accessibilityLabel={label} - accessibilityHint={undefined} - onPress={() => setOpen(true)} - onPressIn={onPressIn} - onPressOut={onPressOut} - onFocus={onFocus} - onBlur={onBlur} - style={[ - { - paddingTop: 16, - paddingBottom: 16, - borderColor: 'transparent', - borderWidth: 2, - }, - atoms.flex_row, - atoms.flex_1, - atoms.w_full, - atoms.px_lg, - atoms.rounded_sm, - t.atoms.bg_contrast_50, - focused || pressed ? chromeFocus : {}, - isInvalid ? chromeError : {}, - isInvalid && (focused || pressed) ? chromeErrorHover : {}, - ]}> - <TextField.Icon icon={CalendarDays} /> + const onPress = React.useCallback(() => { + setOpen(true) + }, []) - <Text - style={[atoms.text_md, atoms.pl_xs, t.atoms.text, {paddingTop: 3}]}> - {localizeDate(value)} - </Text> - </Pressable> + const onCancel = React.useCallback(() => { + setOpen(false) + }, []) + + return ( + <> + <DateFieldButton + label={label} + value={value} + onPress={onPress} + isInvalid={isInvalid} + accessibilityHint={accessibilityHint} + /> {open && ( - <DateTimePicker + <DatePicker + modal + open + timeZoneOffsetInMinutes={0} + theme={t.name === 'light' ? 'light' : 'dark'} + date={new Date(value)} + onConfirm={onChangeInternal} + onCancel={onCancel} + mode="date" + testID={`${testID}-datepicker`} aria-label={label} accessibilityLabel={label} - accessibilityHint={undefined} - testID={`${testID}-datepicker`} - mode="date" - timeZoneName={'Etc/UTC'} - display="spinner" - // @ts-ignore applies in iOS only -prf - themeVariant={t.name === 'dark' ? 'dark' : 'light'} - value={new Date(value)} - onChange={onChangeInternal} + accessibilityHint={accessibilityHint} /> )} - </View> + </> ) } diff --git a/src/components/forms/DateField/index.shared.tsx b/src/components/forms/DateField/index.shared.tsx new file mode 100644 index 000000000..1f54bdc8b --- /dev/null +++ b/src/components/forms/DateField/index.shared.tsx @@ -0,0 +1,99 @@ +import React from 'react' +import {Pressable, View} from 'react-native' + +import {android, atoms as a, useTheme, web} from '#/alf' +import * as TextField from '#/components/forms/TextField' +import {useInteractionState} from '#/components/hooks/useInteractionState' +import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' +import {Text} from '#/components/Typography' +import {localizeDate} from './utils' + +// looks like a TextField.Input, but is just a button. It'll do something different on each platform on press +// iOS: open a dialog with an inline date picker +// Android: open the date picker modal + +export function DateFieldButton({ + label, + value, + onPress, + isInvalid, + accessibilityHint, +}: { + label: string + value: string + onPress: () => void + isInvalid?: boolean + accessibilityHint?: string +}) { + const t = useTheme() + + const { + state: pressed, + onIn: onPressIn, + onOut: onPressOut, + } = useInteractionState() + const { + state: hovered, + onIn: onHoverIn, + onOut: onHoverOut, + } = useInteractionState() + const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() + + const {chromeHover, chromeFocus, chromeError, chromeErrorHover} = + TextField.useSharedInputStyles() + + return ( + <View + style={[a.relative, a.w_full]} + {...web({ + onMouseOver: onHoverIn, + onMouseOut: onHoverOut, + })}> + <Pressable + aria-label={label} + accessibilityLabel={label} + accessibilityHint={accessibilityHint} + onPress={onPress} + onPressIn={onPressIn} + onPressOut={onPressOut} + onFocus={onFocus} + onBlur={onBlur} + style={[ + { + paddingTop: 12, + paddingBottom: 12, + paddingLeft: 14, + paddingRight: 14, + borderColor: 'transparent', + borderWidth: 2, + }, + android({ + minHeight: 57.5, + }), + a.flex_row, + a.flex_1, + a.w_full, + a.rounded_sm, + t.atoms.bg_contrast_25, + a.align_center, + hovered ? chromeHover : {}, + focused || pressed ? chromeFocus : {}, + isInvalid || isInvalid ? chromeError : {}, + (isInvalid || isInvalid) && (hovered || focused) + ? chromeErrorHover + : {}, + ]}> + <TextField.Icon icon={CalendarDays} /> + <Text + style={[ + a.text_md, + a.pl_xs, + t.atoms.text, + {lineHeight: a.text_md.fontSize * 1.1875}, + ]}> + {localizeDate(value)} + </Text> + </Pressable> + </View> + ) +} diff --git a/src/components/forms/DateField/index.tsx b/src/components/forms/DateField/index.tsx index c359a9d46..5662bb594 100644 --- a/src/components/forms/DateField/index.tsx +++ b/src/components/forms/DateField/index.tsx @@ -1,13 +1,16 @@ import React from 'react' import {View} from 'react-native' -import DateTimePicker, { - DateTimePickerEvent, -} from '@react-native-community/datetimepicker' +import DatePicker from 'react-native-date-picker' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' -import {useTheme, atoms} from '#/alf' -import * as TextField from '#/components/forms/TextField' -import {toSimpleDateString} from '#/components/forms/DateField/utils' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' import {DateFieldProps} from '#/components/forms/DateField/types' +import {toSimpleDateString} from '#/components/forms/DateField/utils' +import * as TextField from '#/components/forms/TextField' +import {DateFieldButton} from './index.shared' export * as utils from '#/components/forms/DateField/utils' export const Label = TextField.Label @@ -24,11 +27,15 @@ export function DateField({ onChangeDate, testID, label, + isInvalid, + accessibilityHint, }: DateFieldProps) { + const {_} = useLingui() const t = useTheme() + const control = Dialog.useDialogControl() const onChangeInternal = React.useCallback( - (event: DateTimePickerEvent, date: Date | undefined) => { + (date: Date | undefined) => { if (date) { const formatted = toSimpleDateString(date) onChangeDate(formatted) @@ -38,19 +45,44 @@ export function DateField({ ) return ( - <View style={[atoms.relative, atoms.w_full]}> - <DateTimePicker - aria-label={label} - accessibilityLabel={label} - accessibilityHint={undefined} - testID={`${testID}-datepicker`} - mode="date" - timeZoneName={'Etc/UTC'} - display="spinner" - themeVariant={t.name === 'dark' ? 'dark' : 'light'} - value={new Date(value)} - onChange={onChangeInternal} + <> + <DateFieldButton + label={label} + value={value} + onPress={control.open} + isInvalid={isInvalid} + accessibilityHint={accessibilityHint} /> - </View> + <Dialog.Outer control={control} testID={testID}> + <Dialog.Handle /> + <Dialog.Inner label={label}> + <View style={a.gap_lg}> + <View style={[a.relative, a.w_full, a.align_center]}> + <DatePicker + timeZoneOffsetInMinutes={0} + theme={t.name === 'light' ? 'light' : 'dark'} + date={new Date(value)} + onDateChange={onChangeInternal} + mode="date" + testID={`${testID}-datepicker`} + aria-label={label} + accessibilityLabel={label} + accessibilityHint={accessibilityHint} + /> + </View> + <Button + label={_(msg`Done`)} + onPress={() => control.close()} + size="medium" + color="primary" + variant="solid"> + <ButtonText> + <Trans>Done</Trans> + </ButtonText> + </Button> + </View> + </Dialog.Inner> + </Dialog.Outer> + </> ) } diff --git a/src/components/forms/DateField/index.web.tsx b/src/components/forms/DateField/index.web.tsx index 32f38a5d1..982d32711 100644 --- a/src/components/forms/DateField/index.web.tsx +++ b/src/components/forms/DateField/index.web.tsx @@ -1,11 +1,12 @@ import React from 'react' -import {TextInput, TextInputProps, StyleSheet} from 'react-native' +import {StyleSheet, TextInput, TextInputProps} from 'react-native' // @ts-ignore import {unstable_createElement} from 'react-native-web' -import * as TextField from '#/components/forms/TextField' -import {toSimpleDateString} from '#/components/forms/DateField/utils' import {DateFieldProps} from '#/components/forms/DateField/types' +import {toSimpleDateString} from '#/components/forms/DateField/utils' +import * as TextField from '#/components/forms/TextField' +import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays' export * as utils from '#/components/forms/DateField/utils' export const Label = TextField.Label @@ -37,6 +38,7 @@ export function DateField({ label, isInvalid, testID, + accessibilityHint, }: DateFieldProps) { const handleOnChange = React.useCallback( (e: any) => { @@ -52,12 +54,14 @@ export function DateField({ return ( <TextField.Root isInvalid={isInvalid}> + <TextField.Icon icon={CalendarDays} /> <Input value={value} label={label} onChange={handleOnChange} onChangeText={() => {}} testID={testID} + accessibilityHint={accessibilityHint} /> </TextField.Root> ) diff --git a/src/components/forms/DateField/types.ts b/src/components/forms/DateField/types.ts index 129f5672d..5400cf903 100644 --- a/src/components/forms/DateField/types.ts +++ b/src/components/forms/DateField/types.ts @@ -4,4 +4,5 @@ export type DateFieldProps = { label: string isInvalid?: boolean testID?: string + accessibilityHint?: string } |