DatePicker
WhereUIช่องเลือกวันที่พร้อมปฏิทิน popover แสดงวันที่ภาษาไทยและปี พ.ศ. ได้ตั้งแต่เริ่มต้น ค่าที่รับ/ส่งเป็น Date จริงเสมอ
ยังไม่ได้เลือกวันเริ่มงาน
ติดตั้ง
Terminal
pnpm dlx shadcn@latest add @whereui/date-pickerต้องผูก registry และติดตั้ง @whereui/theme นำหน้าเสมอเมื่อติดตั้งครั้งแรก
การใช้งาน
import { DatePicker } from "@/components/ui/date-picker"const [date, setDate] = React.useState<Date | null>(null)<DatePicker value={date} onChange={setDate} />ตัวอย่างเพิ่มเติม
จำกัดช่วงวันที่และสถานะต่าง ๆ
ลาได้เฉพาะวันทำการในปีนี้ (min/max)
แสดงปีเป็น ค.ศ.
ปิดใช้งาน
Props
| prop | type | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
| value | Date | null | — | วันที่ที่เลือกอยู่ (null = ยังไม่เลือก) |
| onChange | (date: Date | null) => void | — | เรียกเมื่อผู้ใช้เลือกวันที่จากปฏิทิน |
| placeholder | string | "เลือกวันที่" | ข้อความแสดงเมื่อยังไม่เลือกวันที่ |
| min | Date | — | วันต่ำสุดที่เลือกได้ |
| max | Date | — | วันสูงสุดที่เลือกได้ |
| buddhistEra | boolean | true | แสดงปีเป็น พ.ศ. (ค.ศ. + 543) เป็นเพียงการแสดงผล ค่าที่ส่งกลับยังเป็นปี ค.ศ. |
| disabled | boolean | — | ปิดการใช้งานช่องเลือกวันที่ |
ข้อควรรู้
- ค่าที่รับ/ส่งเป็น Date หรือ ISO yyyy-mm-dd เสมอ การแสดง พ.ศ. เป็นเรื่องการแสดงผลเท่านั้น
- เปิดปฏิทินด้วยคีย์บอร์ดได้ (Tab เข้าปุ่มแล้วกด Enter) และเลื่อนวันในปฏิทินด้วยลูกศรได้
ซอร์สโค้ด
components/ui/date-picker.tsx
"use client"import * as React from "react"import { th } from "date-fns/locale"import { CalendarIcon } from "lucide-react"import { cn } from "cn"import type { Matcher } from "react-day-picker"import { Calendar } from "@/components/ui/calendar"import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"/** ชื่อเดือนภาษาไทย ("มกราคม", "ม.ค.") */function thaiMonth(date: Date, style: "long" | "short") { return new Intl.DateTimeFormat("th-TH", { month: style }).format(date)}/** จัดรูปแบบวันที่ภาษาไทย เช่น "13 พฤศจิกายน 2569" — ปี พ.ศ. = ค.ศ. + 543 (เฉพาะการแสดงผล) */export function formatThaiDate( date: Date, buddhistEra: boolean, style: "long" | "short" = "long") { const year = date.getFullYear() + (buddhistEra ? 543 : 0) return `${date.getDate()} ${thaiMonth(date, style)} ${year}`}function DatePicker({ value, onChange, placeholder = "เลือกวันที่", disabled, min, max, buddhistEra = true, className, ...props}: Omit<React.ComponentProps<"button">, "value" | "onChange"> & { /** วันที่ที่เลือก (null = ยังไม่เลือก) */ value?: Date | null onChange?: (date: Date | null) => void placeholder?: string /** วันต่ำสุดที่เลือกได้ */ min?: Date /** วันสูงสุดที่เลือกได้ */ max?: Date /** แสดงปีเป็น พ.ศ. (ค.ศ. + 543) */ buddhistEra?: boolean}) { const [open, setOpen] = React.useState(false) const disabledMatchers = React.useMemo(() => { const matchers: Matcher[] = [] if (min) matchers.push({ before: min }) if (max) matchers.push({ after: max }) return matchers.length ? matchers : undefined }, [min, max]) return ( <Popover open={open} onOpenChange={setOpen}> <PopoverTrigger type="button" data-slot="date-picker" disabled={disabled} className={cn( "flex h-8 w-full min-w-0 items-center justify-between gap-2 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground", !value && "text-muted-foreground", className )} {...props} > <span className="truncate"> {value ? formatThaiDate(value, buddhistEra) : placeholder} </span> <CalendarIcon aria-hidden="true" /> </PopoverTrigger> <PopoverContent align="start" className="w-auto p-0"> <Calendar mode="single" locale={th} defaultMonth={value ?? undefined} selected={value ?? undefined} onSelect={(date) => { onChange?.(date ?? null) setOpen(false) }} disabled={disabledMatchers} formatters={{ formatCaption: (month) => `${thaiMonth(month, "long")} ${ month.getFullYear() + (buddhistEra ? 543 : 0) }`, }} /> </PopoverContent> </Popover> )}export { DatePicker }