"use client"; import { Button, Input, Popover, PopoverContent, PopoverTrigger, } from "@mdxui/primitives"; import { Calendar } from "@mdxui/primitives/calendar"; import { cn } from "@mdxui/primitives/lib/utils"; import { format } from "date-fns"; import { Calendar as CalendarIcon, X } from "lucide-react"; import * as React from "react"; import { parseNaturalLanguageDate } from "./parser"; import type { SmartDateTimePickerProps } from "./types"; export function SmartDateTimePicker({ value, onChange, placeholder = "Enter a date or time...", includeTime = false, minDate, maxDate, className, disabled, }: SmartDateTimePickerProps) { const [inputValue, setInputValue] = React.useState(""); const [isOpen, setIsOpen] = React.useState(false); const [parseError, setParseError] = React.useState(false); const inputRef = React.useRef(null); React.useEffect(() => { if (value) { setInputValue( includeTime ? format(value, "MMM dd, yyyy HH:mm") : format(value, "MMM dd, yyyy"), ); } else { setInputValue(""); } }, [value, includeTime]); const handleInputChange = (e: React.ChangeEvent) => { const newValue = e.target.value; setInputValue(newValue); setParseError(false); if (!newValue.trim()) { onChange?.(undefined); return; } // Try to parse on each change const parsed = parseNaturalLanguageDate(newValue); if (parsed) { // Check bounds if (minDate && parsed.date < minDate) { setParseError(true); return; } if (maxDate && parsed.date > maxDate) { setParseError(true); return; } onChange?.(parsed.date); } }; const handleInputBlur = () => { if (!inputValue.trim()) { onChange?.(undefined); setParseError(false); return; } const parsed = parseNaturalLanguageDate(inputValue); if (parsed) { setInputValue( includeTime ? format(parsed.date, "MMM dd, yyyy HH:mm") : format(parsed.date, "MMM dd, yyyy"), ); setParseError(false); } else { setParseError(true); } }; const handleCalendarSelect = (date: Date | undefined) => { if (date) { // Preserve time if includeTime is true and we have an existing value if (includeTime && value) { date.setHours(value.getHours()); date.setMinutes(value.getMinutes()); } onChange?.(date); setIsOpen(false); } }; const handleClear = () => { setInputValue(""); onChange?.(undefined); setParseError(false); inputRef.current?.focus(); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); handleInputBlur(); inputRef.current?.blur(); } else if (e.key === "Escape") { handleClear(); } }; return (
{inputValue && ( )} { if (minDate && date < minDate) return true; if (maxDate && date > maxDate) return true; return false; }} />
{parseError && (

Unable to parse date. Try formats like "tomorrow", "2024-12-25", or "in 3 days"

)} {!parseError && inputValue && value && (

Parsed as: {format(value, includeTime ? "PPpp" : "PPP")}

)}
); }