sendou.ink/app/components/DateInput.tsx
Kalle dd1adad94b
Some checks are pending
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run
BIome v2 upgrade
2025-06-22 16:49:27 +03:00

72 lines
2.3 KiB
TypeScript

import * as React from "react";
import { useIsMounted } from "~/hooks/useIsMounted";
import { dateToYearMonthDayHourMinuteString, isValidDate } from "~/utils/dates";
import { logger } from "~/utils/logger";
export interface DateInputProps
extends Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"defaultValue" | "min" | "max" | "onChange" | "value"
> {
defaultValue?: Date;
min?: Date;
max?: Date;
onChange?: (newDate: Date | null) => void;
}
export function DateInput({
name,
defaultValue,
min,
max,
onChange,
...inputProps
}: DateInputProps) {
// Keeping track of the value as a string is a nice fallback for browsers that
// don't show a date picker but actually expect the user to type in the date
// as a text. This was Safari Desktop until recently, but nowadays all current
// versions of the main browsers set the input to either a valid date string
// or "". (The browser will handle transitional invalid states internally).
const [[parsedDate, valueString], setDate] = React.useState<
[Date | null, string]
>(() => {
if (defaultValue) {
if (isValidDate(defaultValue)) {
return [defaultValue, dateToYearMonthDayHourMinuteString(defaultValue)];
}
logger.warn("DateInput got invalid date as defaultValue");
}
return [null, ""];
});
const isMounted = useIsMounted();
return (
<>
{parsedDate && isMounted && (
<input name={name} type="hidden" value={parsedDate.getTime() ?? ""} />
)}
<input
{...inputProps}
type="datetime-local"
disabled={!isMounted || inputProps.disabled}
// This is important, because SSR will likely have a date in the wrong
// timezone. We can only fill in a value once hydration is over.
value={isMounted ? valueString : ""}
min={min ? dateToYearMonthDayHourMinuteString(min) : undefined}
max={max ? dateToYearMonthDayHourMinuteString(max) : undefined}
onChange={(e) => {
const newValueString = e.target.value;
const parsedValue = new Date(newValueString);
const newDate = isValidDate(parsedValue) ? parsedValue : null;
setDate([newDate, newValueString]);
onChange?.(newDate);
}}
// Firefox fix for hydration error "prop `disabled` did not match" */
// https://github.com/facebook/react/issues/21459
autoComplete="off"
/>
</>
);
}