Migrate to expo/ui DatePicker
This commit is contained in:
+1
-1
@@ -121,6 +121,7 @@
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@expo/html-elements": "^0.12.5",
|
||||
"@expo/ui": "~57.0.11",
|
||||
"@expo/webpack-config": "^19.0.1",
|
||||
"@floating-ui/dom": "^1.6.3",
|
||||
"@floating-ui/react-dom": "^2.0.8",
|
||||
@@ -230,7 +231,6 @@
|
||||
"react-is": "19",
|
||||
"react-keyed-flatten-children": "^5.0.0",
|
||||
"react-native": "0.86.0",
|
||||
"react-native-date-picker": "^5.0.13",
|
||||
"react-native-device-attest": "^0.1.6",
|
||||
"react-native-drawer-layout": "^4.2.3",
|
||||
"react-native-edge-to-edge": "^1.8.1",
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
diff --git a/ios/RNDatePicker.h b/ios/RNDatePicker.h
|
||||
index 480746eb7acfbe86f67547d9e1de7a5be4d5faf2..13d30cb547195993dfcb4005cc0d248de9ac391a 100644
|
||||
--- a/ios/RNDatePicker.h
|
||||
+++ b/ios/RNDatePicker.h
|
||||
@@ -15,6 +15,7 @@ NS_ASSUME_NONNULL_END
|
||||
#else
|
||||
#import "DatePicker.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
+#include <string>
|
||||
|
||||
@interface RNDatePicker : DatePicker
|
||||
|
||||
@@ -22,4 +23,3 @@ NS_ASSUME_NONNULL_END
|
||||
@end
|
||||
|
||||
#endif
|
||||
-
|
||||
diff --git a/package.json b/package.json
|
||||
index 469386dc2e81ded8994818dd4340409da1396488..36460e4e303ae56534c4d389471fc827433c7028 100644
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -70,9 +70,6 @@
|
||||
"ios": {
|
||||
"componentProvider": {
|
||||
"RNDatePicker": "RNDatePicker"
|
||||
- },
|
||||
- "modulesProvider": {
|
||||
- "RNDatePicker": "RNDatePickerManager"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,6 @@ patchedDependencies:
|
||||
'expo-notifications@57.0.7': patches/expo-notifications@57.0.7.patch
|
||||
'expo-updates@57.0.10': patches/expo-updates@57.0.10.patch
|
||||
expo@57.0.8: patches/expo@57.0.8.patch
|
||||
'react-native-date-picker@5.0.13': patches/react-native-date-picker@5.0.13.patch
|
||||
'react-native-dotenv@3.4.11': patches/react-native-dotenv@3.4.11.patch
|
||||
'react-native-drawer-layout@4.2.3': patches/react-native-drawer-layout@4.2.3.patch
|
||||
'react-native-keyboard-controller@1.21.9': patches/react-native-keyboard-controller@1.21.9.patch
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import {useCallback, useImperativeHandle, useState} from 'react'
|
||||
import {Keyboard} from 'react-native'
|
||||
import DatePicker from 'react-native-date-picker'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {DatePickerDialog, Host} from '@expo/ui/jetpack-compose'
|
||||
|
||||
import {useTheme} from '#/alf'
|
||||
import {type DateFieldProps} from '#/components/forms/DateField/types'
|
||||
import {toSimpleDateString} from '#/components/forms/DateField/utils'
|
||||
import {
|
||||
toLocalMidnight,
|
||||
toSimpleDateString,
|
||||
} from '#/components/forms/DateField/utils'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {DateFieldButton} from './index.shared'
|
||||
|
||||
@@ -20,20 +22,18 @@ export function DateField({
|
||||
placeholder,
|
||||
label,
|
||||
isInvalid,
|
||||
testID,
|
||||
accessibilityHint,
|
||||
maximumDate,
|
||||
minimumDate,
|
||||
}: DateFieldProps) {
|
||||
const {i18n} = useLingui()
|
||||
const t = useTheme()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
/*
|
||||
* The picker requires a valid date, so when value is empty we open at
|
||||
* maximumDate (if set) or today. Normalize through toSimpleDateString so a
|
||||
* date-only value is parsed as UTC midnight, consistent with the picker's
|
||||
* timeZoneOffsetInMinutes={0} and the maximumDate below.
|
||||
* date-only value is parsed as UTC midnight, which is what the Material 3
|
||||
* picker expects for its selected date (it works in UTC day millis).
|
||||
*/
|
||||
const initialDate =
|
||||
value === ''
|
||||
@@ -42,6 +42,19 @@ export function DateField({
|
||||
: new Date()
|
||||
: new Date(toSimpleDateString(value))
|
||||
|
||||
/*
|
||||
* Unlike the selection, the selectable range is resolved by the native side
|
||||
* in the device time zone, so pass local midnight to keep the intended
|
||||
* calendar day for devices west of UTC.
|
||||
*/
|
||||
const selectableDates =
|
||||
minimumDate || maximumDate
|
||||
? {
|
||||
start: minimumDate ? toLocalMidnight(minimumDate) : undefined,
|
||||
end: maximumDate ? toLocalMidnight(maximumDate) : undefined,
|
||||
}
|
||||
: undefined
|
||||
|
||||
const onChangeInternal = useCallback(
|
||||
(date: Date) => {
|
||||
setOpen(false)
|
||||
@@ -86,31 +99,15 @@ export function DateField({
|
||||
accessibilityHint={accessibilityHint}
|
||||
/>
|
||||
{open && (
|
||||
// Android implementation of DatePicker currently does not change default button colors according to theme and only takes hex values for buttonColor
|
||||
// Can remove the buttonColor setting if/when this PR is merged: https://github.com/henninghall/react-native-date-picker/pull/871
|
||||
<DatePicker
|
||||
modal
|
||||
open
|
||||
timeZoneOffsetInMinutes={0}
|
||||
theme={t.scheme}
|
||||
buttonColor={t.name === 'light' ? '#000000' : '#ffffff'}
|
||||
date={initialDate}
|
||||
onConfirm={onChangeInternal}
|
||||
onCancel={onCancel}
|
||||
mode="date"
|
||||
locale={i18n.locale}
|
||||
is24hourSource="locale"
|
||||
testID={`${testID}-datepicker`}
|
||||
aria-label={label}
|
||||
accessibilityLabel={label}
|
||||
accessibilityHint={accessibilityHint}
|
||||
maximumDate={
|
||||
maximumDate ? new Date(toSimpleDateString(maximumDate)) : undefined
|
||||
}
|
||||
minimumDate={
|
||||
minimumDate ? new Date(toSimpleDateString(minimumDate)) : undefined
|
||||
}
|
||||
/>
|
||||
<Host colorScheme={t.scheme}>
|
||||
<DatePickerDialog
|
||||
initialDate={initialDate.toISOString()}
|
||||
selectableDates={selectableDates}
|
||||
showVariantToggle={false}
|
||||
onDateSelected={onChangeInternal}
|
||||
onDismissRequest={onCancel}
|
||||
/>
|
||||
</Host>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useCallback, useImperativeHandle, useState} from 'react'
|
||||
import {Keyboard, View} from 'react-native'
|
||||
import DatePicker from 'react-native-date-picker'
|
||||
import {DateTimePicker} from '@expo/ui/community/datetime-picker'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -16,6 +16,15 @@ import {DateFieldButton} from './index.shared'
|
||||
export * as utils from '#/components/forms/DateField/utils'
|
||||
export const LabelText = TextField.LabelText
|
||||
|
||||
/**
|
||||
* SwiftUI only accepts identifiers from `Locale.availableIdentifiers`, which
|
||||
* use underscores (`pt_BR`) rather than BCP 47 hyphens (`pt-BR`). Unknown
|
||||
* identifiers make the picker fall back to the system locale.
|
||||
*/
|
||||
function toAppleLocale(locale: string): string {
|
||||
return locale.replace(/-/g, '_')
|
||||
}
|
||||
|
||||
/**
|
||||
* Date-only input. Accepts a string in the format YYYY-MM-DD, or a Date object.
|
||||
* Date objects are converted to strings in the format YYYY-MM-DD.
|
||||
@@ -78,6 +87,7 @@ export function DateField({
|
||||
}),
|
||||
[control, value, fallbackDate],
|
||||
)
|
||||
console.log('locale===', i18n.locale, toAppleLocale(i18n.locale))
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -101,17 +111,16 @@ export function DateField({
|
||||
<Dialog.ScrollableInner label={label}>
|
||||
<View style={a.gap_lg}>
|
||||
<View style={[a.relative, a.w_full, a.align_center]}>
|
||||
<DatePicker
|
||||
timeZoneOffsetInMinutes={0}
|
||||
theme={t.scheme}
|
||||
date={new Date(draft)}
|
||||
onDateChange={onChangeInternal}
|
||||
<DateTimePicker
|
||||
style={a.w_full}
|
||||
value={new Date(draft)}
|
||||
onValueChange={(_event, date) => onChangeInternal(date)}
|
||||
mode="date"
|
||||
locale={i18n.locale}
|
||||
display="spinner"
|
||||
timeZoneName="UTC"
|
||||
themeVariant={t.scheme}
|
||||
locale={toAppleLocale(i18n.locale)}
|
||||
testID={`${testID}-datepicker`}
|
||||
aria-label={label}
|
||||
accessibilityLabel={label}
|
||||
accessibilityHint={accessibilityHint}
|
||||
maximumDate={
|
||||
maximumDate
|
||||
? new Date(toSimpleDateString(maximumDate))
|
||||
|
||||
@@ -3,3 +3,13 @@ export function toSimpleDateString(date: Date | string): string {
|
||||
const _date = typeof date === 'string' ? new Date(date) : date
|
||||
return _date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Date at local midnight of the given calendar day. Use when a
|
||||
* native API resolves timestamps in the device time zone rather than UTC, so
|
||||
* the intended YYYY-MM-DD is preserved regardless of the device offset.
|
||||
*/
|
||||
export function toLocalMidnight(date: Date | string): Date {
|
||||
const [year, month, day] = toSimpleDateString(date).split('-').map(Number)
|
||||
return new Date(year, month - 1, day)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user