import { forwardRef, useMemo } from "react"; import DatePicker, { registerLocale } from "react-datepicker"; import { cs } from "date-fns/locale"; import { parse, format } from "date-fns"; import "react-datepicker/dist/react-datepicker.css"; registerLocale("cs", cs); // Ensure portal root exists if ( typeof document !== "undefined" && !document.getElementById("datepicker-portal") ) { const el = document.createElement("div"); el.id = "datepicker-portal"; document.body.appendChild(el); } const isTouchDevice = () => typeof window !== "undefined" && ("ontouchstart" in window || navigator.maxTouchPoints > 0); interface CustomInputProps { value?: string; onClick?: () => void; onChange?: (e: React.ChangeEvent) => void; placeholder?: string; required?: boolean; readOnly?: boolean; disabled?: boolean; } const CustomInput = forwardRef( ( { value, onClick, onChange, placeholder, required, readOnly, disabled }, ref, ) => ( ), ); interface NativeInputProps { mode: string; value: string; onChange: (value: string) => void; required?: boolean; minDate?: string; maxDate?: string; disabled?: boolean; } const modeToInputType: Record = { month: "month", time: "time", }; function NativeInput({ mode, value, onChange, required, minDate, maxDate, disabled, }: NativeInputProps) { const type = modeToInputType[mode] || "date"; // For time inputs, min/max must be in HH:mm format, not date format const formatTimeMinMax = (val: string | undefined): string | undefined => { if (!val) return undefined; // If it looks like a date string (yyyy-MM-dd), extract time portion if present, // otherwise it's not a valid time min/max — return undefined if (val.includes("T")) return val.split("T")[1]?.substring(0, 5); if (val.includes(":")) return val.substring(0, 5); return undefined; }; const minProp = mode === "time" ? formatTimeMinMax(minDate) : minDate || undefined; const maxProp = mode === "time" ? formatTimeMinMax(maxDate) : maxDate || undefined; return ( onChange(e.target.value)} className="admin-form-input" required={required} disabled={disabled} min={minProp} max={maxProp} /> ); } interface AdminDatePickerProps { mode?: "date" | "month" | "time"; value: string; onChange: (value: string) => void; minDate?: string; maxDate?: string; disabled?: boolean; placeholder?: string; required?: boolean; } export default function AdminDatePicker({ mode = "date", value, onChange, required, minDate, maxDate, disabled, placeholder, }: AdminDatePickerProps) { const useNative = useMemo(() => isTouchDevice(), []); if (useNative) { return ( ); } const toDate = (val: string | null | undefined): Date | null => { if (!val) return null; try { if (mode === "date") return parse(val, "yyyy-MM-dd", new Date()); if (mode === "time") { const [h, m] = val.split(":"); const d = new Date(); d.setHours(parseInt(h, 10), parseInt(m, 10), 0, 0); return d; } if (mode === "month") return parse(val, "yyyy-MM", new Date()); } catch { return null; } return null; }; const handleChange = (date: Date | null) => { if (!date) { onChange(""); return; } if (mode === "date") onChange(format(date, "yyyy-MM-dd")); else if (mode === "time") onChange(format(date, "HH:mm")); else if (mode === "month") onChange(format(date, "yyyy-MM")); }; const parseMinMax = (val: string | undefined): Date | undefined => { if (!val) return undefined; try { if (mode === "date") return parse(val, "yyyy-MM-dd", new Date()); if (mode === "month") return parse(val, "yyyy-MM", new Date()); } catch { return undefined; } return undefined; }; const customInput = useMemo( () => ( ), [required, placeholder, disabled], ); const commonProps = { selected: toDate(value), onChange: handleChange, locale: "cs", customInput, minDate: parseMinMax(minDate), maxDate: parseMinMax(maxDate), popperPlacement: "bottom-start" as const, portalId: "datepicker-portal", disabled, }; if (mode === "time") { return ( ); } if (mode === "month") { return ( ); } return ; }