import { SelectInputProps } from "@/components/ui/SelectInput/SelectInput"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; const isValidDate = (year: number, month: number, day: number) => { if (!year || !month || !day) return false; const date = new Date(year, month - 1, day); return ( date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ); }; const parseDateValue = ( value?: string | null ): { year: string; month: string; day: string } | null => { if (!value) return null; // Поддерживаем форматы: "2003-04-09" и "2003-04-09 12:00" const dateMatch = value.match(/^(\d{4})-(\d{2})-(\d{2})(?:\s+\d{2}:\d{2})?$/); if (!dateMatch) return null; const [, year, month, day] = dateMatch; return { year, month, day }; }; // Упрощенное определение порядка полей даты на основе локали. // В реальном приложении здесь лучше использовать данные из next-intl. const getDateInputLocaleFormat = (locale: string): ("d" | "m" | "y")[] => { const format = new Intl.DateTimeFormat(locale).format(new Date(2001, 1, 3)); // Используем 3/Feb/2001 if (/^3.*2/.test(format)) return ["d", "m", "y"]; // 3/2/2001 -> d/m/y if (/^2.*3/.test(format)) return ["m", "d", "y"]; // 2/3/2001 -> m/d/y return ["y", "m", "d"]; // 2001/2/3 -> y/m/d }; interface UseDateInputProps { value?: string | null; onChange?: (value: string | null) => void; maxYear?: number; yearsRange?: number; locale?: string; } export const useDateInput = ({ value, onChange, maxYear = new Date().getFullYear() - 11, yearsRange = 100, locale = "en", }: UseDateInputProps) => { const [year, setYear] = useState(""); const [month, setMonth] = useState(""); const [day, setDay] = useState(""); const lastEmittedValue = useRef(null); useEffect(() => { const parsedDate = parseDateValue(value); if (parsedDate) { setYear(parsedDate.year); setMonth(parsedDate.month); setDay(parsedDate.day); } else { setYear(""); setMonth(""); setDay(""); } }, [value]); const updateValue = useCallback( (newValue: string | null) => { if (newValue !== lastEmittedValue.current) { lastEmittedValue.current = newValue; onChange?.(newValue); } }, [onChange] ); useEffect(() => { const numericYear = Number(year); const numericMonth = Number(month); const numericDay = Number(day); if (isValidDate(numericYear, numericMonth, numericDay)) { const formattedDate = `${year}-${month}-${day}`; updateValue(formattedDate); } else { if (year || month || day) { updateValue(null); } } }, [year, month, day, updateValue]); const yearOptions = useMemo( () => Array.from({ length: yearsRange }, (_, i) => ({ value: maxYear - i, label: String(maxYear - i), })), [maxYear, yearsRange] ); const monthOptions = useMemo( () => Array.from({ length: 12 }, (_, i) => ({ value: String(i + 1).padStart(2, "0"), label: String(i + 1), })), [] ); const dayOptions = useMemo(() => { const daysInMonth = year && month ? new Date(Number(year), Number(month), 0).getDate() : 31; return Array.from({ length: daysInMonth }, (_, i) => ({ value: String(i + 1).padStart(2, "0"), label: String(i + 1), })); }, [year, month]); const localeFormat = useMemo( () => getDateInputLocaleFormat(locale), [locale] ); const handleYearChange: SelectInputProps["onValueChange"] = useCallback( (value: string) => { setYear(value); }, [] ); const handleMonthChange: SelectInputProps["onValueChange"] = useCallback( (value: string) => { setMonth(value); }, [] ); const handleDayChange: SelectInputProps["onValueChange"] = useCallback( (value: string) => { setDay(value); }, [] ); return { year, month, day, yearOptions, monthOptions, dayOptions, localeFormat, handleYearChange, handleMonthChange, handleDayChange, }; };