Componentpickers2.15 KB gzipZero dependencies
Date Picker
Date picker input with popover calendar and quick date selection.
Preview
Live Preview
Click to open admission calendar popover
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add date-pickerOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { DatePicker } from "@/components/ui/date-picker";
<DatePicker defaultValue={new Date()} onValueChange={(d) => console.log(d)} />Usage guidance
Use Date Picker when a ticket stub surface needs Date picker input with popover calendar and quick date selection. Keep props minimal and prefer composition with sibling stubs.
Avoid Date Picker for long form flows or dense data grids where a dedicated layout block fits better.
Source code
date-picker.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface DatePickerProps {
value?: Date;
defaultValue?: Date;
onValueChange?: (date: Date) => void;
placeholder?: string;
className?: string;
}
const MONTHS = [
"JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
"JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
];
const DAYS = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"];
export function DatePicker({
value: controlledValue,
defaultValue,
onValueChange,
placeholder = "SELECT ADMISSION DATE",
className,
}: DatePickerProps) {
const [internalDate, setInternalDate] = React.useState<Date | undefined>(defaultValue);
const [isOpen, setIsOpen] = React.useState(false);
const containerRef = React.useRef<HTMLDivElement>(null);
const isControlled = controlledValue !== undefined;
const selectedDate = isControlled ? controlledValue : internalDate;
const [viewDate, setViewDate] = React.useState(selectedDate || new Date());
const year = viewDate.getFullYear();
const month = viewDate.getMonth();
React.useEffect(() => {
if (!isOpen) return;
const handleClickOutside = (e: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
setIsOpen(false);
}
};
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") setIsOpen(false);
};
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("keydown", handleKeyDown);
};
}, [isOpen]);
const handleSelectDay = (day: number) => {
const newDate = new Date(year, month, day);
if (!isControlled) {
setInternalDate(newDate);
}
onValueChange?.(newDate);
setIsOpen(false);
};
const formatDate = (date: Date) => {
const d = date.getDate().toString().padStart(2, "0");
const m = MONTHS[date.getMonth()].slice(0, 3);
const y = date.getFullYear();
return `${d} ${m} ${y}`;
};
const firstDayOfWeek = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
return (
<div ref={containerRef} className={cn("relative inline-block font-mono select-none", className)}>
<button
type="button"
onClick={() => setIsOpen((prev) => !prev)}
className={cn(
"flex min-w-[220px] cursor-pointer items-center justify-between rounded-lg border-2 border-foreground bg-card px-3 py-2 text-xs transition-all shadow-xs",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
isOpen ? "border-accent ring-1 ring-accent" : "hover:border-foreground/70"
)}
>
<div className="flex items-center gap-2">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4"><rect x="3.5" y="5" width="17" height="15.5" rx="1.5" /><path d="M3.5 9.8h17M8 3v3.6M16 3v3.6M7.5 13.5h2M11 13.5h2M14.5 13.5h2M7.5 16.8h2M11 16.8h2" /></svg>
<span className={cn("font-bold uppercase", !selectedDate && "text-muted-foreground font-normal")}>
{selectedDate ? formatDate(selectedDate) : placeholder}
</span>
</div>
<span className="text-[10px] text-muted-foreground transition-transform duration-150">
{isOpen ? "▲" : "▼"}
</span>
</button>
{isOpen && (
<div className="absolute left-0 top-full z-50 mt-2 inline-flex flex-col rounded-xl border-2 border-foreground bg-card p-4 shadow-2xl outline-1 outline-dashed outline-offset-[-6px] animate-[scale-in_0.15s_ease-out_both] w-72">
{/* Header */}
<div className="flex items-center justify-between border-b-2 border-dashed border-border pb-3">
<button
type="button"
onClick={() => setViewDate(new Date(year, month - 1, 1))}
className="size-7 rounded-sm border border-border bg-secondary flex items-center justify-center font-mono text-xs hover:bg-foreground hover:text-background cursor-pointer"
>
←
</button>
<span className="font-mono text-xs font-bold uppercase tracking-wider text-foreground">
{MONTHS[month]} {year}
</span>
<button
type="button"
onClick={() => setViewDate(new Date(year, month + 1, 1))}
className="size-7 rounded-sm border border-border bg-secondary flex items-center justify-center font-mono text-xs hover:bg-foreground hover:text-background cursor-pointer"
>
→
</button>
</div>
{/* Days labels */}
<div className="mt-3 grid grid-cols-7 gap-1 text-center font-mono text-[10px] uppercase text-muted-foreground font-semibold">
{DAYS.map((d) => (
<div key={d} className="py-1">
{d}
</div>
))}
</div>
{/* Days Grid */}
<div className="mt-1 grid grid-cols-7 gap-1 text-center font-mono text-xs">
{Array.from({ length: firstDayOfWeek }).map((_, i) => (
<div key={`blank-${i}`} className="size-8" />
))}
{Array.from({ length: daysInMonth }).map((_, i) => {
const day = i + 1;
const isSelected =
selectedDate &&
selectedDate.getDate() === day &&
selectedDate.getMonth() === month &&
selectedDate.getFullYear() === year;
return (
<button
key={`day-${day}`}
type="button"
onClick={() => handleSelectDay(day)}
className={cn(
"size-8 rounded-full flex items-center justify-center font-mono text-xs transition-all cursor-pointer",
isSelected
? "bg-accent font-bold text-accent-foreground shadow-xs scale-105"
: "hover:bg-secondary text-foreground"
)}
>
{day}
</button>
);
})}
</div>
</div>
)}
</div>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| value? | Date | |
| defaultValue? | Date | |
| onValueChange? | (date: Date) => void | |
| placeholder? | string | |
| className? | string |