11// @ts -nocheck
22import React , { useState , useEffect } from 'react' ;
3- import { TextField } from '@mui/material' ;
3+ import { TextField , IconButton } from '@mui/material' ;
44import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' ;
55import { LocalizationProvider , DatePicker } from '@mui/x-date-pickers' ;
66import { CalendarToday } from '@mui/icons-material' ;
@@ -14,13 +14,23 @@ const CustomDateWidget = ({
1414 required,
1515 label,
1616 rawErrors = [ ] ,
17- uiSchema = { } , // <-- include uiSchema
17+ uiSchema = { } ,
1818} : any ) => {
1919 const { minValue, maxValue } = options ;
2020 const { t } = useTranslation ( ) ;
2121
2222 const isDisabled = uiSchema ?. [ 'ui:disabled' ] === true ;
2323
24+ const [ isMobile , setIsMobile ] = useState ( false ) ;
25+ const [ isCalendarOpen , setIsCalendarOpen ] = useState ( false ) ;
26+
27+ useEffect ( ( ) => {
28+ const checkMobile = ( ) => setIsMobile ( window . innerWidth <= 768 ) ;
29+ checkMobile ( ) ;
30+ window . addEventListener ( 'resize' , checkMobile ) ;
31+ return ( ) => window . removeEventListener ( 'resize' , checkMobile ) ;
32+ } , [ ] ) ;
33+
2434 const initialValue =
2535 typeof value === 'string' && value . match ( / ^ \d { 4 } - \d { 2 } - \d { 2 } $ / )
2636 ? value
@@ -49,19 +59,22 @@ const CustomDateWidget = ({
4959 }
5060 } ;
5161
52- // Filter out 'is a required property' messages
53- const displayErrors = rawErrors ?. filter (
62+ const handleCalendarIconClick = ( ) => setIsCalendarOpen ( true ) ;
63+ const handleCalendarClose = ( ) => setIsCalendarOpen ( false ) ;
64+
65+ const displayErrors = rawErrors . filter (
5466 ( error ) => ! error . toLowerCase ( ) . includes ( 'required' )
55- ) || [ ] ;
56-
57- const errorText = displayErrors . length > 0 ? displayErrors [ 0 ] : '' ;
67+ ) ;
68+ const errorText = displayErrors ?. [ 0 ] || '' ;
69+ const hasError =
70+ displayErrors && displayErrors . length > 0 && errorText . trim ( ) !== '' ;
5871
5972 return (
60- < >
61- < input
73+ < LocalizationProvider dateAdapter = { AdapterDayjs } required = { required } >
74+ < input
6275 value = { value ?? '' }
6376 required = { required }
64- onChange = { ( ) => { /* no-op */ } }
77+ onChange = { ( ) => { } }
6578 tabIndex = { - 1 }
6679 style = { {
6780 height : 1 ,
@@ -73,55 +86,80 @@ const CustomDateWidget = ({
7386 marginTop = "50px"
7487
7588 />
76- < LocalizationProvider dateAdapter = { AdapterDayjs } required = { required } >
77- < DatePicker
78- disabled = { isDisabled }
79- label = { `${ t ( `FORM.${ label } ` , {
80- defaultValue : label ,
81- } ) } `}
82- value = { selectedDate || null }
83- onChange = { handleDateChange }
84- minDate = { minValue ? dayjs ( minValue , 'YYYY-MM-DD' ) : undefined }
85- maxDate = { maxValue ? dayjs ( maxValue , 'YYYY-MM-DD' ) : undefined }
86- format = "DD-MM-YYYY"
87- openTo = "day"
88- views = { [ 'year' , 'month' , 'day' ] }
89- slotProps = { {
90- textField : {
91- fullWidth : true ,
92- variant : 'outlined' ,
93- error : displayErrors . length > 0 ,
94- helperText : errorText ,
95- required,
96- inputProps : {
89+ { isMobile ? (
90+ < >
91+ { /* Mobile TextField with calendar icon */ }
92+ < TextField
93+ disabled = { isDisabled }
94+ label = { `${ t ( `FORM.${ label } ` , { defaultValue : label } ) } ` }
95+ value = { selectedDate ? selectedDate . format ( 'DD-MM-YYYY' ) : '' }
96+ fullWidth
97+ variant = "outlined"
98+ required = { required }
99+ InputProps = { {
97100 readOnly : true ,
98- } ,
99- InputProps : {
100- endAdornment : < CalendarToday /> ,
101- } ,
102- } ,
103- popper : {
104- placement : 'bottom-start' ,
105- modifiers : [
106- {
107- name : 'preventOverflow' ,
108- options : {
109- boundary : 'viewport' ,
110- } ,
101+ endAdornment : (
102+ < IconButton
103+ onClick = { handleCalendarIconClick }
104+ disabled = { isDisabled }
105+ edge = "end"
106+ size = "small"
107+ >
108+ < CalendarToday />
109+ </ IconButton >
110+ ) ,
111+ } }
112+ onClick = { ! isDisabled ? handleCalendarIconClick : undefined }
113+ error = { hasError }
114+ helperText = { hasError ? errorText : '' }
115+ />
116+
117+ { /* Mobile DatePicker popup */ }
118+ { isCalendarOpen && (
119+ < DatePicker
120+ open
121+ onClose = { handleCalendarClose }
122+ disabled = { isDisabled }
123+ value = { selectedDate || null }
124+ onChange = { ( date ) => {
125+ handleDateChange ( date ) ;
126+ handleCalendarClose ( ) ;
127+ } }
128+ minDate = { minValue ? dayjs ( minValue , 'YYYY-MM-DD' ) : undefined }
129+ maxDate = { maxValue ? dayjs ( maxValue , 'YYYY-MM-DD' ) : undefined }
130+ format = "DD-MM-YYYY"
131+ slotProps = { { popper : { placement : 'bottom-start' } } }
132+ renderInput = { ( ) => null }
133+ />
134+ ) }
135+ </ >
136+ ) : (
137+ // Desktop DatePicker
138+ < DatePicker
139+ disabled = { isDisabled }
140+ label = { `${ t ( `FORM.${ label } ` , { defaultValue : label } ) } ` }
141+ value = { selectedDate || null }
142+ onChange = { handleDateChange }
143+ minDate = { minValue ? dayjs ( minValue , 'YYYY-MM-DD' ) : undefined }
144+ maxDate = { maxValue ? dayjs ( maxValue , 'YYYY-MM-DD' ) : undefined }
145+ format = "DD-MM-YYYY"
146+ slotProps = { {
147+ textField : {
148+ fullWidth : true ,
149+ variant : 'outlined' ,
150+ error : hasError ,
151+ helperText : hasError ? errorText : '' ,
152+ required,
153+ inputProps : {
154+ readOnly : true , // prevent manual typing
111155 } ,
112- ] ,
113- } ,
114- actionBar : {
115- actions : [ 'clear' , 'today' , 'cancel' , 'accept' ] ,
116- } ,
117- } }
118- required = { required }
119- />
120-
156+ onKeyDown : ( e ) => e . preventDefault ( ) , // block keyboard typing
157+ } ,
158+ } }
159+ required = { required }
160+ />
161+ ) }
121162 </ LocalizationProvider >
122- { /* Hidden text input to force native validation */ }
123-
124- </ >
125163 ) ;
126164} ;
127165
0 commit comments