Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
c306f00
feat: allow typing a date into the date field, one segment at a time
Krishna2323 Sep 17, 2026
79a94e8
fix: reach the overlay-free popover from a narrow pane, and open the …
Krishna2323 Sep 17, 2026
de3a085
fix: keep the date mask a fixed width and track the caret per digit
Krishna2323 Sep 17, 2026
59e4711
fix: match the prototype's segment rules for typed dates
Krishna2323 Sep 17, 2026
0d46d4f
fix: do not drag the calendar to the date limit while a year is typed
Krishna2323 Sep 17, 2026
b13855f
fix: fill the month and day from the right so a single digit shows as 02
Krishna2323 Sep 17, 2026
b9896d6
fix: send a click on the field's empty space to the first unfilled se…
Krishna2323 Sep 17, 2026
905605a
fix: hide the calendar icon once a digit is typed
Krishna2323 Sep 17, 2026
7d4f9c6
fix: keep the typing calendar attached to its field while the page sc…
Krishna2323 Sep 17, 2026
fc6df11
fix: keep the calendar open while the year or month picker is used
Krishna2323 Sep 17, 2026
6010595
refactor: remove the workarounds that accumulated around the typed da…
Krishna2323 Sep 17, 2026
cf9bbd1
feat: update the date field when a month or year is picked
Krishna2323 Sep 17, 2026
6848a74
fix: stop a stray caret report moving the date field's active segment
Krishna2323 Sep 17, 2026
c534524
refactor: rebuild the date field as one input per segment
Krishna2323 Sep 18, 2026
8f9b152
fix: measure each date segment instead of estimating its width
Krishna2323 Sep 18, 2026
1f44f08
fix: show the committed date when the date field is not being edited
Krishna2323 Sep 18, 2026
d3068c3
fix: let a press land on the date segment it was aimed at
Krishna2323 Sep 18, 2026
1d90b46
fix: stop the date field's props being dropped by the segmented input
Krishna2323 Sep 18, 2026
996c988
fix: land the caret at the end of a date segment reached by keystroke
Krishna2323 Sep 18, 2026
62993b7
fix: reposition the calendar when the date field changes height
Krishna2323 Sep 18, 2026
aa094c3
fix: keep a committed date from restarting the segment being typed
Krishna2323 Sep 18, 2026
6970cff
fix: drop a date segment keystroke that cannot complete a leading zero
Krishna2323 Sep 18, 2026
c763339
feat: keep the unreached mask letters on screen while a date is typed
Krishna2323 Sep 18, 2026
b1ba2f0
Merge branch 'main' into krishna2323/issue-101117
Krishna2323 Sep 21, 2026
ef0e09a
fix: keep the date field's empty space from taking focus off its segm…
Krishna2323 Sep 21, 2026
32108c3
fix: block submission when the date field is left holding an unusable…
Krishna2323 Sep 21, 2026
44b6350
fix: return the date field to its first segment when it is cleared
Krishna2323 Sep 21, 2026
2588a05
fix: report a date field's value as it is typed rather than when it i…
Krishna2323 Sep 21, 2026
30187a5
Merge branch 'main' into krishna2323/issue-101117
Krishna2323 Sep 23, 2026
acadecd
fix: address the design review on the typed date field
Krishna2323 Sep 23, 2026
cb275ea
Merge branch 'main' into krishna2323/issue-101117
Krishna2323 Sep 24, 2026
6b32216
fix: limit month and year commits to typing, and hide decorative date…
Krishna2323 Sep 24, 2026
c09056e
refactor: follow existing patterns in the typed date field
Krishna2323 Sep 24, 2026
1deb756
chore: remove local debug files committed by mistake
Krishna2323 Sep 24, 2026
f394b45
refactor: tidy the typed date field to match codebase conventions
Krishna2323 Sep 24, 2026
ef67e16
chore: stop exporting the date segment hook's params type
Krishna2323 Sep 24, 2026
95ae9b0
fix: keep the date mask on one line
Krishna2323 Sep 24, 2026
80e330f
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Sep 26, 2026
c070ec0
fix: paste into the segment the user is in, rather than rebuilding th…
Krishna2323 Sep 26, 2026
e947fe0
fix: keep the typed date while the value prop catches up
Krishna2323 Sep 26, 2026
116d61c
fix: show the clear button while a date is still being typed
Krishna2323 Sep 26, 2026
1bd6f79
fix: follow a month typed before the year is filled in
Krishna2323 Sep 26, 2026
b83e90d
fix: follow the typed year into the calendar, even outside the pickab…
Krishna2323 Sep 26, 2026
b24cd31
fix: keep the typed date when the calendar is reopened
Krishna2323 Sep 26, 2026
8031665
fix: refuse a date that was typed but does not exist
Krishna2323 Sep 26, 2026
c5302c2
fix: hold the day to the month that was typed
Krishna2323 Sep 26, 2026
2efce43
fix: move the day back when the month typed after it is too short
Krishna2323 Sep 26, 2026
cf5bb1a
fix: block submitting a date the calendar would never have offered
Krishna2323 Sep 27, 2026
c9f21ab
fix: open the calendar in range, keep pasted digits, and delete to th…
Krishna2323 Sep 27, 2026
48c8e94
fix: let the calendar tell a date it holds from one standing in for none
Krishna2323 Sep 27, 2026
5726876
fix: use the shared color helper for the date mask
Krishna2323 Sep 27, 2026
04d939f
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Sep 30, 2026
f423ceb
fix: collapse a selected date on press and copy it on cmd+c
Krishna2323 Sep 30, 2026
a68efd2
fix: hold back a date being typed, and block saving one that cannot b…
Krishna2323 Sep 30, 2026
d3b92dd
fix: show a date field's required error once it is left, not as it em…
Krishna2323 Oct 1, 2026
61d823a
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Oct 5, 2026
22a74af
fix: let a date be cut, keep the separator in place, and block confir…
Krishna2323 Oct 5, 2026
2ee096b
fix: copy a date that is only part typed instead of refusing to
Krishna2323 Oct 5, 2026
e236b1a
fix: report a pasted date that does not exist instead of nudging it t…
Krishna2323 Oct 5, 2026
02e667e
fix: match a text field's double and triple click beside the date
Krishna2323 Oct 5, 2026
9b227b1
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Oct 7, 2026
3542415
fix: keep a day its month does not have instead of moving the user of…
Krishna2323 Oct 7, 2026
0d7e1e7
fix: let a typed date be undone, and dismiss the calendar on tabbing …
Krishna2323 Oct 7, 2026
e63c369
Merge remote-tracking branch 'upstream/main' into krishna2323/issue-1…
Krishna2323 Oct 8, 2026
793d83f
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Oct 9, 2026
c90b3e6
Merge remote-tracking branch 'upstream/main' into krishna2323/issue-1…
Krishna2323 Oct 13, 2026
7c9d115
Merge remote-tracking branch 'upstream/main' into krishna2323/issue-1…
Krishna2323 Oct 13, 2026
56b6ea5
fix: keep the day on a year pick, read a paste by what it inserted, a…
Krishna2323 Oct 13, 2026
e62aade
fix: read a paste too short to fill a date segment into the digits al…
Krishna2323 Oct 13, 2026
07218af
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Oct 13, 2026
9bbc7ee
Merge remote-tracking branch 'upstream/main' into krishna2323/issue-1…
Krishna2323 Oct 14, 2026
de7a4d0
fix: leave Escape to a modal opened above an overlay-free popover
Krishna2323 Oct 14, 2026
13e26f1
fix: answer autofocus the way a text field does
Krishna2323 Oct 14, 2026
eff5a47
fix: count the calendar as part of the field rather than somewhere ou…
Krishna2323 Oct 14, 2026
66a152a
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Oct 14, 2026
75eb56b
fix: put focus back in the date field after a month or year is picked
Krishna2323 Oct 14, 2026
1661f17
fix: take focus back once the month or year picker has closed, not wh…
Krishna2323 Oct 14, 2026
2689ce6
fix: show the field as focused on any segment, and let typing open th…
Krishna2323 Oct 14, 2026
a802aa7
fix: open the calendar when a date is deleted, not only when one is t…
Krishna2323 Oct 14, 2026
9c712ae
fix: report a missing date on submit rather than on leaving the field
Krishna2323 Oct 14, 2026
12daf75
Merge remote-tracking branch 'upstream/main' into krishna2323/issue-1…
Krishna2323 Oct 14, 2026
fa29d47
fix: settle a date segment left reading as zero on the first month or…
Krishna2323 Oct 15, 2026
873b666
Merge branch 'Expensify:main' into krishna2323/issue-101117
Krishna2323 Oct 15, 2026
b271050
fix: read a date segment of zeroes as the first month or day
Krishna2323 Oct 15, 2026
bf7ecaa
fix: settle a zero that starts a filled date segment over on the firs…
Krishna2323 Oct 15, 2026
7f362d3
Merge branch 'krishna2323/issue-101117' of https://github.com/Krishna…
Krishna2323 Oct 15, 2026
5aeb011
Merge branch 'main' into krishna2323/issue-101117
Krishna2323 Oct 18, 2026
538d9b9
fix: paste over a selected date, cutting one segment, and re-picking …
Krishna2323 Oct 18, 2026
f5fea3c
Merge branch 'main' into krishna2323/issue-101117
Krishna2323 Oct 25, 2026
017bea5
fix: clamp a month or year pick, skip a dead separator, and clear a s…
Krishna2323 Oct 25, 2026
21b6201
refactor: name the date field's handlers for what they do and share t…
Krishna2323 Oct 25, 2026
e476d8a
test: cover the date range validator and the zero segment settling
Krishna2323 Oct 25, 2026
e4bb2cd
test: cover the error paths a date field reports through the form
Krishna2323 Oct 25, 2026
9db0c63
test: cover the date segment hook's state machine
Krishna2323 Oct 25, 2026
575ad90
refactor: share the start date validation between the pages asking fo…
Krishna2323 Oct 25, 2026
8f43bdb
test: cover the popover guards the typed calendar depends on
Krishna2323 Oct 25, 2026
90e7788
test: cover the date field's blur, its click selections and typing a …
Krishna2323 Oct 25, 2026
9728b6d
fix: drop a lint suppression that had nothing left to suppress
Krishna2323 Oct 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,11 @@ const CONST = {
shortcutKey: 'Backspace',
modifiers: [],
},
DELETE: {
descriptionKey: null,
shortcutKey: 'Delete',
modifiers: [],
},
SPACE: {
descriptionKey: null,
shortcutKey: 'Space',
Expand Down
18 changes: 17 additions & 1 deletion src/components/DatePicker/CalendarPicker/MonthPickerModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Modal from '@components/Modal';
import {usePopoverActions} from '@components/PopoverProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelectListItem';
Expand All @@ -11,8 +12,10 @@ import DateUtils from '@libs/DateUtils';

import CONST from '@src/CONST';

import type {View} from 'react-native';

import {Str} from 'expensify-common';
import React, {useEffect, useState} from 'react';
import React, {useEffect, useRef, useState} from 'react';
import {Keyboard} from 'react-native';

import type CalendarPickerListItem from './types';
Expand Down Expand Up @@ -41,6 +44,8 @@ type MonthPickerModalProps = {
function MonthPickerModal({isVisible, currentMonth, minMonth, maxMonth, onMonthChange, onClose, shouldEnableBackdropInNarrowPane = false}: MonthPickerModalProps) {
const styles = useThemeStyles();
const {translate, dateFnsLocale} = useLocalize();
const {setActivePopoverExtraAnchorRef} = usePopoverActions();
const contentRef = useRef<View>(null);
const [searchText, setSearchText] = useState('');
// The rows are built here from numbers, so the compiler can cache them across the calendar's re-renders.
const months: CalendarPickerListItem[] = DateUtils.getMonthNames(dateFnsLocale).map((month, index) => ({
Expand All @@ -51,6 +56,16 @@ function MonthPickerModal({isVisible, currentMonth, minMonth, maxMonth, onMonthC
isDisabled: index < minMonth || index > maxMonth,
}));

useEffect(() => {
if (!isVisible) {
return;
}

// This modal is rendered above the calendar rather than inside it, so a press on a month counts as a press
// outside the calendar popover and would dismiss it. Registering the content keeps the calendar open.
setActivePopoverExtraAnchorRef(contentRef);
}, [isVisible, setActivePopoverExtraAnchorRef]);

const filteredMonths = searchText === '' ? months : months.filter((month) => month.text?.toLowerCase().includes(searchText.toLowerCase()));
const headerMessage = !filteredMonths.length ? translate('common.noResultsFound') : '';
const data = filteredMonths;
Expand Down Expand Up @@ -82,6 +97,7 @@ function MonthPickerModal({isVisible, currentMonth, minMonth, maxMonth, onMonthC
enableEdgeToEdgeBottomSafeAreaPadding
>
<ScreenWrapper
ref={contentRef}
style={[styles.pb0]}
includePaddingTop={false}
enableEdgeToEdgeBottomSafeAreaPadding
Expand Down
19 changes: 18 additions & 1 deletion src/components/DatePicker/CalendarPicker/YearPickerModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Modal from '@components/Modal';
import {usePopoverActions} from '@components/PopoverProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import SingleSelectListItem from '@components/SelectionList/ListItem/SingleSelectListItem';
Expand All @@ -12,7 +13,9 @@ import moveInitialSelectionToTop, {shouldMoveInitialSelectionToTop} from '@libs/

import CONST from '@src/CONST';

import React, {useEffect, useState} from 'react';
import type {View} from 'react-native';

import React, {useEffect, useRef, useState} from 'react';
import {Keyboard} from 'react-native';

import type CalendarPickerListItem from './types';
Expand Down Expand Up @@ -41,7 +44,20 @@ type YearPickerModalProps = {
function YearPickerModal({isVisible, minYear, maxYear, currentYear, onYearChange, onClose, shouldEnableBackdropInNarrowPane = false}: YearPickerModalProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {setActivePopoverExtraAnchorRef} = usePopoverActions();
const contentRef = useRef<View>(null);
const [searchText, setSearchText] = useState('');

useEffect(() => {
if (!isVisible) {
return;
}

// This modal is rendered above the calendar rather than inside it, so a press on a year counts as a press
// outside the calendar popover and would dismiss it. Registering the content keeps the calendar open.
setActivePopoverExtraAnchorRef(contentRef);
}, [isVisible, setActivePopoverExtraAnchorRef]);

// Freeze the year selected when the picker opened so it stays pinned to the top for the whole open cycle, even as the live selection changes.
const initialYear = useInitialSelection(currentYear, {isVisible});
// The rows are built here from numbers, so the compiler can cache them and the sort below across the calendar's re-renders.
Expand Down Expand Up @@ -97,6 +113,7 @@ function YearPickerModal({isVisible, minYear, maxYear, currentYear, onYearChange
enableEdgeToEdgeBottomSafeAreaPadding
>
<ScreenWrapper
ref={contentRef}
style={[styles.pb0]}
includePaddingTop={false}
enableEdgeToEdgeBottomSafeAreaPadding
Expand Down
64 changes: 58 additions & 6 deletions src/components/DatePicker/CalendarPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import CONST from '@src/CONST';
import type {ComponentRef} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';

import {addMonths, addYears, clamp, isSameDay, isValid, setMonth, setYear, startOfDay, startOfMonth, subMonths, subYears} from 'date-fns';
import {addMonths, addYears, clamp, format, isSameDay, isValid, setMonth, setYear, startOfDay, startOfMonth, subMonths, subYears} from 'date-fns';
import {Str} from 'expensify-common';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {View} from 'react-native';
Expand Down Expand Up @@ -47,6 +47,12 @@ type CalendarPickerProps = {
/** Called with the picked day at local midnight */
onSelected?: (selectedDate: Date) => void;

/**
* Called when a month or year is picked from its own picker, with the date that pick lands on. The selection is
* not finished, so the calendar stays open for the day press. Leave it out to have those picks only move the view.
*/
onMonthOrYearSelected?: (selectedDate: string) => void;

/** Optional style override for the header container */
headerContainerStyle?: StyleProp<ViewStyle>;

Expand All @@ -55,6 +61,21 @@ type CalendarPickerProps = {

/** Whether Month/Year right-docked picker modals should keep backdrop in narrow pane context */
shouldEnableMonthYearBackdropInNarrowPane?: boolean;

/**
* Moves the calendar to this month without selecting a day, so it can follow a date being typed into the input.
* The calendar still owns its own view, so its arrows and month picker keep working between updates.
*/
viewDate?: Date;

/** Changes every time `viewDate` is asserted, including when it repeats the month the calendar already shows */
viewDateVersion?: number;

/**
* Called once the month or year picker has closed, whether a pick was made or not. Those pickers are modals, so
* they take focus with them, which a caller that owns the focused element has to take back.
*/
onMonthOrYearPickerClose?: () => void;
};

/** A bound can be built from data, such as a time zone or a stored date, so an invalid one falls back to the default like a missing one, instead of making every date invalid. */
Expand All @@ -68,11 +89,15 @@ function CalendarPicker({
minDate: minDateProp,
maxDate: maxDateProp,
onSelected,
onMonthOrYearSelected,
DayComponent = Day,
isDateSelectable,
headerContainerStyle,
containerStyle,
shouldEnableMonthYearBackdropInNarrowPane = false,
viewDate,
viewDateVersion = 0,
onMonthOrYearPickerClose,
}: CalendarPickerProps) {
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
const {isSmallScreenWidth} = useResponsiveLayout();
Expand All @@ -83,7 +108,10 @@ function CalendarPicker({
const monthPressableRef = useRef<ComponentRef<typeof View>>(null);
const minDate = getBound(minDateProp, CONST.CALENDAR_PICKER.MIN_YEAR);
const maxDate = getBound(maxDateProp, CONST.CALENDAR_PICKER.MAX_YEAR);
const [dateView, setDateView] = useState(value ?? defaultMonth ?? new Date());
// The popover unmounts its content, so the calendar starts over every time it is reopened. A view the field has
// already asserted is what it starts from, since a date still being typed has no committed value to fall back to.
const [dateView, setDateView] = useState(() => viewDate ?? value ?? defaultMonth ?? new Date());
const [appliedViewDateVersion, setAppliedViewDateVersion] = useState(viewDateVersion);
// defaultMonth can be a new Date on every render, such as today, so the view follows it only when it moves to another month and nothing is selected.
const validDefaultMonth = defaultMonth && isValid(defaultMonth) ? defaultMonth : undefined;
const defaultMonthTime = validDefaultMonth && startOfMonth(validDefaultMonth).getTime();
Expand All @@ -94,6 +122,14 @@ function CalendarPicker({
setDateView(validDefaultMonth ?? new Date());
}
}

// Catching up with the caller here rather than in an effect keeps the view and the month matrix in step within a
// single render, so the calendar never paints the old month first.
if (viewDate && viewDateVersion !== appliedViewDateVersion) {
Comment thread
Krishna2323 marked this conversation as resolved.
setAppliedViewDateVersion(viewDateVersion);
setDateView(viewDate);
}

const currentDateView = clamp(dateView, {start: minDate, end: maxDate});
const [isYearPickerVisible, setIsYearPickerVisible] = useState(false);
const [isMonthPickerVisible, setIsMonthPickerVisible] = useState(false);
Expand All @@ -113,13 +149,23 @@ function CalendarPicker({
const maxMonthStart = startOfMonth(maxDate);
const currentMonthStart = startOfMonth(currentDateView);

/**
* The year and month on offer are each held to the bounds, but the date they land on need not be, since the day and
* whichever of the two was not picked come along unchanged. The view is pulled into range on its way to the screen,
* so the date handed over is pulled in the same way rather than disagreeing with the month being shown.
*/
const selectMonthOrYear = (newDateView: Date) => {
setDateView(newDateView);
onMonthOrYearSelected?.(format(clamp(newDateView, {start: minDate, end: maxDate}), CONST.DATE.FNS_FORMAT_STRING));
};

const onYearSelected = (year: number) => {
setDateView(addYears(currentDateView, year - currentYearView));
selectMonthOrYear(addYears(currentDateView, year - currentYearView));
requestAnimationFrame(() => setIsYearPickerVisible(false));
};

const onMonthSelected = (month: number) => {
setDateView(setMonth(currentDateView, month));
selectMonthOrYear(setMonth(currentDateView, month));
requestAnimationFrame(() => setIsMonthPickerVisible(false));
};

Expand Down Expand Up @@ -389,7 +435,10 @@ function CalendarPicker({
maxYear={maxYear}
currentYear={currentYearView}
onYearChange={onYearSelected}
onClose={() => setIsYearPickerVisible(false)}
onClose={() => {
setIsYearPickerVisible(false);
onMonthOrYearPickerClose?.();
}}
shouldEnableBackdropInNarrowPane={shouldEnableMonthYearBackdropInNarrowPane}
/>
<MonthPickerModal
Expand All @@ -398,7 +447,10 @@ function CalendarPicker({
minMonth={minMonth}
maxMonth={maxMonth}
onMonthChange={onMonthSelected}
onClose={() => setIsMonthPickerVisible(false)}
onClose={() => {
setIsMonthPickerVisible(false);
onMonthOrYearPickerClose?.();
}}
shouldEnableBackdropInNarrowPane={shouldEnableMonthYearBackdropInNarrowPane}
/>
</View>
Expand Down
41 changes: 37 additions & 4 deletions src/components/DatePicker/DatePickerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,27 @@ function DatePickerModal({
anchorPosition,
anchorAlignment = DEFAULT_ANCHOR_ORIGIN,
onSelected,
onMonthOrYearSelected,
shouldCloseWhenBrowserNavigationChanged = false,
shouldPositionFromTop = false,
forwardedFSClass,
shouldEnableMonthYearBackdropInNarrowPane = false,
anchorRef: anchorRefProp,
withoutOverlay = false,
shouldAllowWithoutOverlayInNarrowPane = false,
shouldCloseOnWheel = true,
viewDate,
viewDateVersion,
onMonthOrYearPickerClose,
shouldMeasureContentHeight = false,
windowMargin,
shouldSwitchPositionIfOverflow = true,
}: DatePickerProps) {
const [selectedDate, setSelectedDate] = useState(value);
const anchorRef = useRef<ComponentRef<typeof View>>(null);
const fallbackAnchorRef = useRef<ComponentRef<typeof View>>(null);
// PopoverProvider treats a click inside the anchor as "not outside", so the caller's own anchor has to be used
// or clicking the date input while the calendar is open would dismiss it.
const anchorRef = anchorRefProp ?? fallbackAnchorRef;
const styles = useThemeStyles();

// We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to distinguish RHL and narrow layout
Expand All @@ -77,14 +88,29 @@ function DatePickerModal({
}
}, [formID, inputID, selectedDate, shouldSaveDraft, value]);

const handleDateSelection = (date: Date) => {
const newValue = format(date, CONST.DATE.FNS_FORMAT_STRING);
onSelected?.(newValue);
const applySelection = (newValue: string) => {
onTouched?.();
onInputChange?.(newValue);
setSelectedDate(newValue);
};

const handleDateSelection = (date: Date) => {
const newValue = format(date, CONST.DATE.FNS_FORMAT_STRING);
onSelected?.(newValue);
applySelection(newValue);
};

const applyMonthOrYearSelection = (newValue: string) => {
onMonthOrYearSelected?.(newValue);

// An empty field has no day to mark as selected, so the pick only moves the calendar
if (!value) {
return;
}

applySelection(newValue);
};

// Pass the CalendarPicker's existing bottom padding (pb4) as the base style so the safe-area padding is
// added on top of it instead of overriding it (containerStyle is applied after pb4 in CalendarPicker).
// The modal doesn't render an offline indicator inside it, so disable the offline-indicator padding —
Expand All @@ -110,6 +136,9 @@ function DatePickerModal({
forwardedFSClass={forwardedFSClass}
shouldDisplayBelowModals
enableEdgeToEdgeBottomSafeAreaPadding
withoutOverlay={withoutOverlay}
shouldAllowWithoutOverlayInNarrowPane={shouldAllowWithoutOverlayInNarrowPane}
shouldCloseOnWheel={shouldCloseOnWheel}
>
<CalendarPicker
minDate={minDate}
Expand All @@ -118,8 +147,12 @@ function DatePickerModal({
value={DateUtils.parseCalendarDate(selectedDate)}
defaultMonth={DateUtils.parseCalendarDate(defaultValue)}
onSelected={handleDateSelection}
onMonthOrYearSelected={onMonthOrYearSelected ? applyMonthOrYearSelection : undefined}
containerStyle={bottomSafeAreaPaddingStyle}
shouldEnableMonthYearBackdropInNarrowPane={shouldEnableMonthYearBackdropInNarrowPane}
viewDate={viewDate}
viewDateVersion={viewDateVersion}
onMonthOrYearPickerClose={onMonthOrYearPickerClose}
/>
</PopoverWithMeasuredContent>
);
Expand Down
Loading
Loading