Files
openpylon/src/components/card-detail/DueDatePicker.tsx
Your Name bc12b5569a feat: custom calendar date picker replacing native input
Build fully custom CalendarPopover with date-fns + Radix Popover.
Month/year dropdown selectors, today button, clear button, past
dates dimmed. Rewrite DueDatePicker to use it instead of <input type=date>.
2026-02-15 22:04:31 +02:00

81 lines
2.6 KiB
TypeScript

import { format, formatDistanceToNow, isPast, isToday } from "date-fns";
import { X } from "lucide-react";
import { useBoardStore } from "@/stores/board-store";
import { CalendarPopover } from "@/components/card-detail/CalendarPopover";
interface DueDatePickerProps {
cardId: string;
dueDate: string | null;
}
export function DueDatePicker({ cardId, dueDate }: DueDatePickerProps) {
const updateCard = useBoardStore((s) => s.updateCard);
const dateObj = dueDate ? new Date(dueDate) : null;
const overdue = dateObj != null && isPast(dateObj) && !isToday(dateObj);
function handleSelect(date: Date) {
updateCard(cardId, { dueDate: format(date, "yyyy-MM-dd") });
}
function handleClear() {
updateCard(cardId, { dueDate: null });
}
return (
<div className="flex flex-col gap-2">
{/* Header with clear button */}
<div className="flex items-center justify-between">
<h4 className="font-mono text-xs uppercase tracking-widest text-pylon-text-secondary">
Due Date
</h4>
{dueDate && (
<button
onClick={handleClear}
className="rounded p-0.5 text-pylon-text-secondary transition-colors hover:bg-pylon-danger/10 hover:text-pylon-danger"
aria-label="Clear due date"
>
<X className="size-3.5" />
</button>
)}
</div>
{/* Clickable date display -> opens calendar */}
<CalendarPopover
selectedDate={dateObj}
onSelect={handleSelect}
onClear={handleClear}
>
<button className="flex w-full items-center gap-2 rounded-md px-1 py-1 text-left transition-colors hover:bg-pylon-column/60">
{dateObj ? (
<>
<span
className={`text-sm font-medium ${
overdue ? "text-pylon-danger" : "text-pylon-text"
}`}
>
{format(dateObj, "MMM d, yyyy")}
</span>
<span
className={`text-xs ${
overdue ? "text-pylon-danger" : "text-pylon-text-secondary"
}`}
>
{overdue
? `overdue by ${formatDistanceToNow(dateObj)}`
: isToday(dateObj)
? "today"
: `in ${formatDistanceToNow(dateObj)}`}
</span>
</>
) : (
<span className="text-sm italic text-pylon-text-secondary/60">
Click to set date...
</span>
)}
</button>
</CalendarPopover>
</div>
);
}