ข้ามไปเนื้อหา
WhereUI

ค้นหาในเอกสาร

ค้นหาชื่อ component หรือหน้าเอกสาร

DateRangePicker

WhereUI

ช่องเลือกช่วงวันที่ (วันเริ่มต้น–วันสิ้นสุด) พร้อมปฏิทิน popover แสดงภาษาไทยและปี พ.ศ. ปิดอัตโนมัติเมื่อเลือกครบทั้งสองวัน

ยังไม่ได้เลือกช่วงวันลา

ติดตั้ง

Terminal
pnpm dlx shadcn@latest add @whereui/date-range-picker

ต้องผูก registry และติดตั้ง @whereui/theme นำหน้าเสมอเมื่อติดตั้งครั้งแรก

การใช้งาน

import { DateRangePicker, type DateRangeValue } from "@/components/ui/date-range-picker"const [range, setRange] = React.useState<DateRangeValue | null>(null)<DateRangePicker value={range} onChange={setRange} />

ตัวอย่างเพิ่มเติม

จำกัดช่วงวันที่และสถานะต่าง ๆ
ลาได้เฉพาะไตรมาสนี้ (min/max)
ปิดใช้งาน

Props

proptypeค่าเริ่มต้นคำอธิบาย
value{ from: Date | null; to: Date | null } | null—ช่วงวันที่ที่เลือกอยู่ (from/to เป็น null = ยังไม่เลือกปลายด้านนั้น)
onChange(range: { from: Date | null; to: Date | null }) => void—เรียกทุกครั้งที่ผู้ใช้คลิกวันในปฏิทิน
placeholderstring"เลือกช่วงวันที่"ข้อความแสดงเมื่อยังไม่เลือกวันเริ่มต้น
minDate—วันต่ำสุดที่เลือกได้
maxDate—วันสูงสุดที่เลือกได้
buddhistErabooleantrueแสดงปีเป็น พ.ศ. (ค.ศ. + 543) เฉพาะการแสดงผล
disabledboolean—ปิดการใช้งาน

ข้อควรรู้

  • ค่าที่รับ/ส่งเป็น Date จริงเสมอ ปีที่แสดงเป็น พ.ศ. เป็นเรื่องการแสดงผลเท่านั้น
  • popover จะปิดเองเมื่อเลือกวันสิ้นสุดครบแล้ว

ซอร์สโค้ด

components/ui/date-range-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 { formatThaiDate } from "@/components/ui/date-picker"import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"/** ค่าช่วงวันที่ — null = ยังไม่เลือกปลายด้านนั้น */export type DateRangeValue = { from: Date | null; to: Date | null }function DateRangePicker({  value,  onChange,  placeholder = "เลือกช่วงวันที่",  disabled,  min,  max,  buddhistEra = true,  className,  ...props}: Omit<React.ComponentProps<"button">, "value" | "onChange"> & {  value?: DateRangeValue | null  onChange?: (range: DateRangeValue) => void  placeholder?: string  /** วันต่ำสุดที่เลือกได้ */  min?: Date  /** วันสูงสุดที่เลือกได้ */  max?: Date  /** แสดงปีเป็น พ.ศ. (ค.ศ. + 543) */  buddhistEra?: boolean}) {  const [open, setOpen] = React.useState(false)  const from = value?.from ?? null  const to = value?.to ?? null  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-range-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",          !from && "text-muted-foreground",          className        )}        {...props}      >        <span className="truncate">          {from            ? `${formatThaiDate(from, buddhistEra, "short")} – ${                to ? formatThaiDate(to, buddhistEra, "short") : "…"              }`            : placeholder}        </span>        <CalendarIcon aria-hidden="true" />      </PopoverTrigger>      <PopoverContent align="start" className="w-auto p-0">        <Calendar          mode="range"          numberOfMonths={1}          locale={th}          defaultMonth={from ?? undefined}          selected={{ from: from ?? undefined, to: to ?? undefined }}          onSelect={(range) => {            onChange?.({ from: range?.from ?? null, to: range?.to ?? null })            if (range?.to) setOpen(false)          }}          disabled={disabledMatchers}          formatters={{            formatCaption: (month) =>              `${new Intl.DateTimeFormat("th-TH", { month: "long" }).format(                month              )} ${month.getFullYear() + (buddhistEra ? 543 : 0)}`,          }}        />      </PopoverContent>    </Popover>  )}export { DateRangePicker }