1- import { addMonths , addYears , format , getYear , isSameDay , parseISO , setDate , setMonth , setYear , startOfDay , subMonths , subYears } from 'date-fns' ;
1+ import { addMonths , addYears , format , isSameDay , parseISO , setDate , setMonth , setYear , startOfDay , subMonths , subYears } from 'date-fns' ;
22import { Str } from 'expensify-common' ;
33import React , { useCallback , useEffect , useRef , useState } from 'react' ;
44import type { StyleProp , ViewStyle } from 'react-native' ;
@@ -81,8 +81,8 @@ function CalendarPicker({
8181 const initialHeight = ( calendarDaysMatrix ?. length || CONST . MAX_CALENDAR_PICKER_ROWS ) * CONST . CALENDAR_PICKER_DAY_HEIGHT ;
8282 const heightValue = useSharedValue ( initialHeight ) ;
8383
84- const minYear = Math . min ( getYear ( new Date ( minDate ) ) , CONST . CALENDAR_PICKER . MIN_YEAR ) ;
85- const maxYear = Math . max ( getYear ( new Date ( maxDate ) ) , CONST . CALENDAR_PICKER . MAX_YEAR ) ;
84+ const minYear = CONST . CALENDAR_PICKER . MIN_YEAR ;
85+ const maxYear = CONST . CALENDAR_PICKER . MAX_YEAR ;
8686
8787 const [ years , setYears ] = useState < CalendarPickerListItem [ ] > ( ( ) =>
8888 Array . from ( { length : maxYear - minYear + 1 } , ( v , i ) => i + minYear ) . map ( ( year ) => ( {
@@ -122,12 +122,20 @@ function CalendarPicker({
122122 onSelected ?.( format ( newCurrentDateView , CONST . DATE . FNS_FORMAT_STRING ) ) ;
123123 } ;
124124
125+ const isAtMinBoundary = currentYearView <= CONST . CALENDAR_PICKER . MIN_YEAR && currentMonthView === 0 ;
126+ const isAtMaxBoundary = currentYearView >= CONST . CALENDAR_PICKER . MAX_YEAR && currentMonthView === 11 ;
127+ const isAtMinYear = currentYearView <= CONST . CALENDAR_PICKER . MIN_YEAR ;
128+ const isAtMaxYear = currentYearView >= CONST . CALENDAR_PICKER . MAX_YEAR ;
129+
125130 /**
126131 * Handles the user pressing the previous month arrow of the calendar picker.
127132 */
128133 const moveToPrevMonth = ( ) => {
129134 setCurrentDateView ( ( prev ) => {
130135 const prevMonth = subMonths ( new Date ( prev ) , 1 ) ;
136+ if ( prevMonth . getFullYear ( ) < CONST . CALENDAR_PICKER . MIN_YEAR ) {
137+ return prev ;
138+ }
131139 // if year is subtracted, we need to update the years list
132140 if ( prevMonth . getFullYear ( ) < prev . getFullYear ( ) ) {
133141 setYears ( ( prevYears ) =>
@@ -147,6 +155,9 @@ function CalendarPicker({
147155 const moveToNextMonth = ( ) => {
148156 setCurrentDateView ( ( prev ) => {
149157 const nextMonth = addMonths ( new Date ( prev ) , 1 ) ;
158+ if ( nextMonth . getFullYear ( ) > CONST . CALENDAR_PICKER . MAX_YEAR ) {
159+ return prev ;
160+ }
150161 // if year is added, we need to update the years list
151162 if ( nextMonth . getFullYear ( ) > prev . getFullYear ( ) ) {
152163 setYears ( ( prevYears ) =>
@@ -164,6 +175,9 @@ function CalendarPicker({
164175 const moveToPrevYear = ( ) => {
165176 setCurrentDateView ( ( prev ) => {
166177 const prevYear = subYears ( new Date ( prev ) , 1 ) ;
178+ if ( prevYear . getFullYear ( ) < CONST . CALENDAR_PICKER . MIN_YEAR ) {
179+ return prev ;
180+ }
167181 setYears ( ( prevYears ) => prevYears . map ( ( item ) => ( { ...item , isSelected : item . value === prevYear . getFullYear ( ) } ) ) ) ;
168182 return prevYear ;
169183 } ) ;
@@ -172,6 +186,9 @@ function CalendarPicker({
172186 const moveToNextYear = ( ) => {
173187 setCurrentDateView ( ( prev ) => {
174188 const nextYear = addYears ( new Date ( prev ) , 1 ) ;
189+ if ( nextYear . getFullYear ( ) > CONST . CALENDAR_PICKER . MAX_YEAR ) {
190+ return prev ;
191+ }
175192 setYears ( ( prevYears ) => prevYears . map ( ( item ) => ( { ...item , isSelected : item . value === nextYear . getFullYear ( ) } ) ) ) ;
176193 return nextYear ;
177194 } ) ;
@@ -218,13 +235,17 @@ function CalendarPicker({
218235 < PressableWithFeedback
219236 shouldUseAutoHitSlop = { false }
220237 testID = "prev-month-arrow"
238+ disabled = { isAtMinBoundary }
221239 onPress = { moveToPrevMonth }
222240 hoverDimmingValue = { 1 }
223241 accessibilityLabel = { translate ( 'common.previousMonth' ) }
224242 role = { CONST . ROLE . BUTTON }
225243 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . PREV_MONTH }
226244 >
227- < ArrowIcon direction = { CONST . DIRECTION . LEFT } />
245+ < ArrowIcon
246+ disabled = { isAtMinBoundary }
247+ direction = { CONST . DIRECTION . LEFT }
248+ />
228249 </ PressableWithFeedback >
229250 < View style = { [ themeStyles . flex1 , themeStyles . alignItemsCenter ] } >
230251 < PressableWithFeedback
@@ -253,26 +274,31 @@ function CalendarPicker({
253274 < PressableWithFeedback
254275 shouldUseAutoHitSlop = { false }
255276 testID = "next-month-arrow"
277+ disabled = { isAtMaxBoundary }
256278 onPress = { moveToNextMonth }
257279 hoverDimmingValue = { 1 }
258280 accessibilityLabel = { translate ( 'common.nextMonth' ) }
259281 role = { CONST . ROLE . BUTTON }
260282 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . NEXT_MONTH }
261283 >
262- < ArrowIcon />
284+ < ArrowIcon disabled = { isAtMaxBoundary } />
263285 </ PressableWithFeedback >
264286 </ View >
265287 < View style = { [ themeStyles . alignItemsCenter , themeStyles . flexRow , { flex : 2 } ] } >
266288 < PressableWithFeedback
267289 shouldUseAutoHitSlop = { false }
268290 testID = "prev-year-arrow"
291+ disabled = { isAtMinYear }
269292 onPress = { moveToPrevYear }
270293 hoverDimmingValue = { 1 }
271294 accessibilityLabel = { translate ( 'common.previousYear' ) }
272295 role = { CONST . ROLE . BUTTON }
273296 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . PREV_YEAR }
274297 >
275- < ArrowIcon direction = { CONST . DIRECTION . LEFT } />
298+ < ArrowIcon
299+ disabled = { isAtMinYear }
300+ direction = { CONST . DIRECTION . LEFT }
301+ />
276302 </ PressableWithFeedback >
277303 < View style = { [ themeStyles . flex1 , themeStyles . alignItemsCenter ] } >
278304 < PressableWithFeedback
@@ -300,13 +326,14 @@ function CalendarPicker({
300326 < PressableWithFeedback
301327 shouldUseAutoHitSlop = { false }
302328 testID = "next-year-arrow"
329+ disabled = { isAtMaxYear }
303330 onPress = { moveToNextYear }
304331 hoverDimmingValue = { 1 }
305332 accessibilityLabel = { translate ( 'common.nextYear' ) }
306333 role = { CONST . ROLE . BUTTON }
307334 sentryLabel = { CONST . SENTRY_LABEL . CALENDAR_PICKER . NEXT_YEAR }
308335 >
309- < ArrowIcon />
336+ < ArrowIcon disabled = { isAtMaxYear } />
310337 </ PressableWithFeedback >
311338 </ View >
312339 </ View >
0 commit comments