about summary refs log tree commit diff
path: root/src/components/forms
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/forms')
-rw-r--r--src/components/forms/DateField/index.android.tsx73
-rw-r--r--src/components/forms/DateField/index.shared.tsx99
-rw-r--r--src/components/forms/DateField/index.tsx59
-rw-r--r--src/components/forms/DateField/index.web.tsx4
-rw-r--r--src/components/forms/DateField/types.ts1
-rw-r--r--src/components/forms/TextField.tsx6
6 files changed, 172 insertions, 70 deletions
diff --git a/src/components/forms/DateField/index.android.tsx b/src/components/forms/DateField/index.android.tsx
index 451810a5e..35c2459f0 100644
--- a/src/components/forms/DateField/index.android.tsx
+++ b/src/components/forms/DateField/index.android.tsx
@@ -1,19 +1,12 @@
 import React from 'react'
-import {View, Pressable} from 'react-native'
 
-import {useTheme, atoms} from '#/alf'
-import {Text} from '#/components/Typography'
-import {useInteractionState} from '#/components/hooks/useInteractionState'
+import {useTheme} from '#/alf'
 import * as TextField from '#/components/forms/TextField'
-import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays'
-
 import {DateFieldProps} from '#/components/forms/DateField/types'
-import {
-  localizeDate,
-  toSimpleDateString,
-} from '#/components/forms/DateField/utils'
+import {toSimpleDateString} from '#/components/forms/DateField/utils'
 import DatePicker from 'react-native-date-picker'
 import {isAndroid} from 'platform/detection'
+import {DateFieldButton} from './index.shared'
 
 export * as utils from '#/components/forms/DateField/utils'
 export const Label = TextField.Label
@@ -24,18 +17,10 @@ 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(
     (date: Date) => {
@@ -47,45 +32,23 @@ export function DateField({
     [onChangeDate, setOpen],
   )
 
+  const onPress = React.useCallback(() => {
+    setOpen(true)
+  }, [])
+
   const onCancel = React.useCallback(() => {
     setOpen(false)
   }, [])
 
   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} />
-
-        <Text
-          style={[atoms.text_md, atoms.pl_xs, t.atoms.text, {paddingTop: 3}]}>
-          {localizeDate(value)}
-        </Text>
-      </Pressable>
+    <>
+      <DateFieldButton
+        label={label}
+        value={value}
+        onPress={onPress}
+        isInvalid={isInvalid}
+        accessibilityHint={accessibilityHint}
+      />
 
       {open && (
         <DatePicker
@@ -99,9 +62,9 @@ export function DateField({
           testID={`${testID}-datepicker`}
           aria-label={label}
           accessibilityLabel={label}
-          accessibilityHint={undefined}
+          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..29b3e8cb6
--- /dev/null
+++ b/src/components/forms/DateField/index.shared.tsx
@@ -0,0 +1,99 @@
+import React from 'react'
+import {View, Pressable} from 'react-native'
+
+import {atoms as a, android, useTheme, web} 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 {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 49e47a01e..22fa3a9f5 100644
--- a/src/components/forms/DateField/index.tsx
+++ b/src/components/forms/DateField/index.tsx
@@ -1,11 +1,16 @@
 import React from 'react'
 import {View} from 'react-native'
 
-import {useTheme, atoms} from '#/alf'
+import {useTheme, atoms as a} from '#/alf'
 import * as TextField from '#/components/forms/TextField'
 import {toSimpleDateString} from '#/components/forms/DateField/utils'
 import {DateFieldProps} from '#/components/forms/DateField/types'
 import DatePicker from 'react-native-date-picker'
+import * as Dialog from '#/components/Dialog'
+import {DateFieldButton} from './index.shared'
+import {Button, ButtonText} from '#/components/Button'
+import {Trans, msg} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
 
 export * as utils from '#/components/forms/DateField/utils'
 export const Label = TextField.Label
@@ -22,8 +27,12 @@ export function DateField({
   onChangeDate,
   testID,
   label,
+  isInvalid,
+  accessibilityHint,
 }: DateFieldProps) {
+  const {_} = useLingui()
   const t = useTheme()
+  const control = Dialog.useDialogControl()
 
   const onChangeInternal = React.useCallback(
     (date: Date | undefined) => {
@@ -36,17 +45,43 @@ export function DateField({
   )
 
   return (
-    <View style={[atoms.relative, atoms.w_full]}>
-      <DatePicker
-        theme={t.name === 'light' ? 'light' : 'dark'}
-        date={new Date(value)}
-        onDateChange={onChangeInternal}
-        mode="date"
-        testID={`${testID}-datepicker`}
-        aria-label={label}
-        accessibilityLabel={label}
-        accessibilityHint={undefined}
+    <>
+      <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
+                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..a3aa302f9 100644
--- a/src/components/forms/DateField/index.web.tsx
+++ b/src/components/forms/DateField/index.web.tsx
@@ -2,6 +2,7 @@ import React from 'react'
 import {TextInput, TextInputProps, StyleSheet} from 'react-native'
 // @ts-ignore
 import {unstable_createElement} from 'react-native-web'
+import {CalendarDays_Stroke2_Corner0_Rounded as CalendarDays} from '#/components/icons/CalendarDays'
 
 import * as TextField from '#/components/forms/TextField'
 import {toSimpleDateString} from '#/components/forms/DateField/utils'
@@ -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
 }
diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx
index 3cffe5b2b..376883c9d 100644
--- a/src/components/forms/TextField.tsx
+++ b/src/components/forms/TextField.tsx
@@ -126,8 +126,8 @@ export function useSharedInputStyles() {
 
 export type InputProps = Omit<TextInputProps, 'value' | 'onChangeText'> & {
   label: string
-  value: string
-  onChangeText: (value: string) => void
+  value?: string
+  onChangeText?: (value: string) => void
   isInvalid?: boolean
   inputRef?: React.RefObject<TextInput>
 }
@@ -277,7 +277,7 @@ export function Icon({icon: Comp}: {icon: React.ComponentType<SVGIconProps>}) {
       <Comp
         size="md"
         style={[
-          {color: t.palette.contrast_500, pointerEvents: 'none'},
+          {color: t.palette.contrast_500, pointerEvents: 'none', flexShrink: 0},
           ctx.hovered ? hover : {},
           ctx.focused ? focus : {},
           ctx.isInvalid && ctx.hovered ? errorHover : {},