1
1
import React , { KeyboardEventHandler , forwardRef } from 'react' ;
2
2
import classNames from 'classnames' ;
3
- import { DatePickerProps , Emphasis , FlexBox , IconButton , Text , TextField , Toolbar } from '@lumx/react' ;
3
+ import { DatePickerProps , Emphasis , FlexBox , IconButton , Text , TextField , TextFieldProps , Toolbar } from '@lumx/react' ;
4
4
import { mdiChevronLeft , mdiChevronRight } from '@lumx/icons' ;
5
5
import { Comp } from '@lumx/react/utils/type' ;
6
6
import { getMonthCalendar } from '@lumx/react/utils/date/getMonthCalendar' ;
@@ -62,35 +62,56 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
62
62
} , [ locale , minDate , maxDate , selectedMonth ] ) ;
63
63
64
64
const selectedYear = selectedMonth . toLocaleDateString ( locale , { year : 'numeric' } ) . slice ( 0 , 4 ) ;
65
- const [ textFieldYearValue , setTextFieldYearValue ] = React . useState ( selectedYear ) ;
66
- const isYearValid = Number ( textFieldYearValue ) > 0 && Number ( textFieldYearValue ) <= 9999 ;
65
+ const [ currentYear , setCurrentYear ] = React . useState ( selectedYear ) ;
67
66
68
67
// Updates month offset when validating year. Adds or removes 12 months per year when updating year value.
69
- const updateMonthOffset = React . useCallback ( ( ) => {
70
- if ( isYearValid ) {
71
- const yearNumber = selectedMonth . getFullYear ( ) ;
72
- const offset = ( Number ( textFieldYearValue ) - yearNumber ) * 12 ;
73
- if ( onMonthChange ) {
74
- onMonthChange ( addMonthResetDay ( selectedMonth , offset ) ) ;
68
+ const updateMonthOffset = React . useCallback (
69
+ ( newYearValue : string ) => {
70
+ const yearNumber = Number ( newYearValue ) ;
71
+ if ( yearNumber < 0 && yearNumber >= 9999 ) {
72
+ return ;
75
73
}
76
- }
77
- } , [ isYearValid , selectedMonth , textFieldYearValue , onMonthChange ] ) ;
78
74
79
- const monthYear = selectedMonth . toLocaleDateString ( locale , { year : 'numeric' , month : 'long' } ) ;
75
+ const previousYearNumber = selectedMonth . getFullYear ( ) ;
76
+ const offset = ( yearNumber - previousYearNumber ) * 12 ;
77
+ onMonthChange ?.( addMonthResetDay ( selectedMonth , offset ) ) ;
78
+ } ,
79
+ [ selectedMonth , onMonthChange ] ,
80
+ ) ;
81
+
82
+ const onYearChange = React . useCallback < TextFieldProps [ 'onChange' ] > (
83
+ ( newYearValue , _ , event ) => {
84
+ setCurrentYear ( newYearValue ) ;
80
85
81
- // Year can only be validated by pressing Enter key or on Blur. The below handles the press Enter key case
82
- const handleKeyPress : KeyboardEventHandler = React . useMemo (
83
- ( ) => onEnterPressed ( updateMonthOffset ) ,
86
+ // Detect if change is coming from the spin up/down arrows
87
+ const inputType = ( event ?. nativeEvent as any ) ?. inputType ;
88
+ if (
89
+ // Chrome/Safari
90
+ ! inputType ||
91
+ // Firefox
92
+ inputType === 'insertReplacementText'
93
+ ) {
94
+ updateMonthOffset ( newYearValue ) ;
95
+ }
96
+ } ,
84
97
[ updateMonthOffset ] ,
85
98
) ;
86
99
87
- // Required to update year in the TextField when the user changes year by using prev next month arrows
100
+ const updateYear = React . useCallback ( ( ) => {
101
+ updateMonthOffset ( currentYear ) ;
102
+ } , [ updateMonthOffset , currentYear ] ) ;
103
+
104
+ const updateYearOnEnterPressed : KeyboardEventHandler = React . useMemo (
105
+ ( ) => onEnterPressed ( updateYear ) ,
106
+ [ updateYear ] ,
107
+ ) ;
108
+
109
+ const monthYear = selectedMonth . toLocaleDateString ( locale , { year : 'numeric' , month : 'long' } ) ;
110
+
111
+ // Update current year when selected year changes
88
112
React . useEffect ( ( ) => {
89
- if ( Number ( textFieldYearValue ) !== selectedMonth . getFullYear ( ) ) {
90
- setTextFieldYearValue ( selectedMonth . getFullYear ( ) . toString ( ) ) ;
91
- }
92
- // eslint-disable-next-line react-hooks/exhaustive-deps
93
- } , [ selectedMonth ] ) ;
113
+ setCurrentYear ( selectedYear ) ;
114
+ } , [ selectedYear ] ) ;
94
115
95
116
const prevSelectedMonth = usePreviousValue ( selectedMonth ) ;
96
117
const monthHasChanged = prevSelectedMonth && ! isSameDay ( selectedMonth , prevSelectedMonth ) ;
@@ -101,7 +122,7 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
101
122
if ( monthHasChanged ) setLabelAriaLive ( 'polite' ) ;
102
123
} , [ monthHasChanged ] ) ;
103
124
104
- const label = getYearDisplayName ( locale ) ;
125
+ const yearLabel = getYearDisplayName ( locale ) ;
105
126
106
127
return (
107
128
< div ref = { ref } className = { `${ CLASSNAME } ` } >
@@ -144,14 +165,14 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
144
165
. map ( ( part ) =>
145
166
part === selectedYear ? (
146
167
< TextField
147
- value = { textFieldYearValue }
148
- aria-label = { label }
149
- onChange = { setTextFieldYearValue }
168
+ value = { currentYear }
169
+ aria-label = { yearLabel }
170
+ onChange = { onYearChange }
150
171
type = "number"
151
172
max = { 9999 }
152
173
min = { 0 }
153
- onBlur = { updateMonthOffset }
154
- onKeyPress = { handleKeyPress }
174
+ onBlur = { updateYear }
175
+ onKeyPress = { updateYearOnEnterPressed }
155
176
key = "year"
156
177
className = { `${ CLASSNAME } __year` }
157
178
/>
0 commit comments