Skip to content

Commit cb4a634

Browse files
authored
Merge pull request #119 from bleu/juliano/fix-date-input-on-calendar
fix date input on calendar
2 parents 261947c + 667d9f3 commit cb4a634

File tree

1 file changed

+46
-13
lines changed

1 file changed

+46
-13
lines changed

src/components/ui/Calendar.tsx

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,43 @@ const TimePicker = ({ value, setValue, className = "" }) => (
2929
</div>
3030
);
3131

32+
const formatDateForInput = (date) => {
33+
if (!date) return "";
34+
const d = new Date(date);
35+
const year = d.getFullYear();
36+
const month = String(d.getMonth() + 1).padStart(2, "0");
37+
const day = String(d.getDate()).padStart(2, "0");
38+
return `${year}-${month}-${day}`;
39+
};
40+
3241
const DateInput = ({ value, onChange, className = "" }) => {
33-
const formatDateForInput = (date) => {
34-
if (!date) return "";
35-
const d = new Date(date);
36-
// Use local date to avoid timezone issues
37-
const year = d.getFullYear();
38-
const month = String(d.getMonth() + 1).padStart(2, "0");
39-
const day = String(d.getDate()).padStart(2, "0");
40-
return `${year}-${month}-${day}`;
41-
};
42+
const [inputValue, setInputValue] = React.useState(() =>
43+
formatDateForInput(value)
44+
);
45+
const isFocused = React.useRef(false);
46+
47+
React.useEffect(() => {
48+
if (!isFocused.current) {
49+
setInputValue(formatDateForInput(value));
50+
}
51+
}, [value]);
4252

4353
const handleDateChange = (e) => {
4454
const dateString = e.target.value;
55+
setInputValue(dateString);
56+
4557
if (!dateString) {
4658
onChange(undefined);
4759
return;
4860
}
4961

50-
// Parse the date string manually to avoid timezone issues
5162
const [year, month, day] = dateString.split("-").map(Number);
52-
const newDate = new Date(year, month - 1, day); // month is 0-indexed
63+
64+
// Browser sends intermediate values like 0002, 0020, 0202 while the user
65+
// is still typing a full year — skip propagating until year is complete.
66+
if (year < 1000) return;
67+
68+
const newDate = new Date(year, month - 1, day);
5369

5470
if (!Number.isNaN(newDate.getTime())) {
5571
onChange(newDate);
@@ -63,8 +79,15 @@ const DateInput = ({ value, onChange, className = "" }) => {
6379
<input
6480
id="date-input"
6581
type="date"
66-
value={formatDateForInput(value)}
82+
value={inputValue}
6783
onChange={handleDateChange}
84+
onFocus={() => {
85+
isFocused.current = true;
86+
}}
87+
onBlur={() => {
88+
isFocused.current = false;
89+
setInputValue(formatDateForInput(value));
90+
}}
6891
className={cn(
6992
buttonVariants({ variant: "outline" }),
7093
"form-input text-sm font-medium transition duration-200 ease-in-out",
@@ -111,6 +134,10 @@ function Calendar({
111134
onSelect: setSelected,
112135
...props
113136
}) {
137+
const [month, setMonth] = React.useState<Date>(
138+
selected ? new Date(selected) : new Date()
139+
);
140+
114141
const [timeValue, setTimeValue] = React.useState<string>(
115142
selected
116143
? `${new Date(selected)
@@ -176,6 +203,8 @@ function Calendar({
176203
return;
177204
}
178205

206+
setMonth(date);
207+
179208
if (!withTime) {
180209
setSelected(date);
181210
return;
@@ -223,12 +252,16 @@ function Calendar({
223252
) : null;
224253
}, [withDateInput, withTime, selected, timeValue]);
225254

255+
const selectedDate = selected ? new Date(selected) : undefined;
256+
226257
return (
227258
<div>
228259
<DayPicker
229260
mode="single"
230261
showOutsideDays={showOutsideDays}
231-
selected={selected}
262+
selected={selectedDate}
263+
month={month}
264+
onMonthChange={setMonth}
232265
onSelect={handleDaySelect}
233266
captionLayout={captionLayout}
234267
fromYear={fromYear}

0 commit comments

Comments
 (0)