useDateRangePicker

Hook để chọn khoảng ngày với lịch hai tháng, xem trước khi di chuột và các khoảng đặt trước.

Import

import { useDateRangePicker } from "react-date-range-picker-headless";

Usage

import { useState } from "react";
import { useDateRangePicker } from "react-date-range-picker-headless";
function MyDateRangePicker() {
const [range, setRange] = useState<{ start: Date | null; end: Date | null }>({
start: null,
end: null,
});
const picker = useDateRangePicker({
value: range,
onChange: setRange,
presets: [
{
label: "Last 7 days",
value: () => {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 6);
return { start, end };
},
},
],
});
return (
<div ref={picker.containerRef}>
<button onClick={picker.handleToggle}>
{picker.displayValue || picker.locale.rangePlaceholder}
</button>
{picker.isOpen && (
<div ref={picker.popupRef} onKeyDown={picker.handleKeyDown}>
{/* Hai lịch cạnh nhau */}
{[picker.leftCalendar, picker.rightCalendar].map((cal, calIdx) => (
<div key={calIdx}>
<span>{picker.locale.formatMonthYear(cal.month)}</span>
{cal.weeks.flat().map((day, i) => {
if (!day) return <span key={i} />;
const dp = picker.getDayProps(day, cal.month);
return (
<button
key={i}
onClick={() => picker.handleDateClick(day)}
onMouseEnter={() => picker.handleDateHover(day)}
onMouseLeave={() => picker.handleDateHover(null)}
>
{dp.day}
</button>
);
})}
</div>
))}
{/* Các khoảng đặt trước */}
{picker.presets.map((preset, i) => (
<button
key={i}
onClick={() => picker.handlePresetClick(preset)}
style={{ fontWeight: i === picker.activePresetIndex ? "bold" : "normal" }}
>
{preset.label}
</button>
))}
<button onClick={picker.handleConfirm}>{picker.locale.confirm}</button>
</div>
)}
</div>
);
}

Tùy chọn

Tùy chọnKiểuMặc địnhMô tả
value{ start: Date | null; end: Date | null }Bắt buộc. Giá trị khoảng hiện tại.
onChange(value: { start: Date | null; end: Date | null }) => voidBắt buộc. Được gọi khi khoảng ngày thay đổi.
maxDaysnumberSố ngày tối đa được phép trong một khoảng (tính cả hai đầu).
minDaysnumberSố ngày tối thiểu yêu cầu trong một khoảng (tính cả hai đầu).
presetsDateRangePreset[]Các khoảng ngày đặt trước (ví dụ: “7 ngày qua”, “Tháng này”).
allowSingleDateInRangebooleantrueKhi false, ngăn không cho chọn một khoảng mà ngày bắt đầu bằng ngày kết thúc.
minDateDateNgày sớm nhất có thể chọn.
maxDateDateNgày muộn nhất có thể chọn.
localePartial<Locale>DEFAULT_LOCALEGhi đè các chuỗi bản địa hóa.
initialMonthDateTháng ban đầu để hiển thị.
sizeDatePickerSizeTruyền qua cho UI.
weekStartsOnWeekDay"sunday"Ngày đầu tiên của tuần.
isDateUnavailable(date: Date) => booleanHàm tùy chỉnh để vô hiệu hóa các ngày cụ thể.
displayFormatstringChuỗi định dạng tùy chỉnh cho giá trị hiển thị.
openbooleanTrạng thái mở được kiểm soát.
initialOpenbooleanfalseTrạng thái mở ban đầu (không kiểm soát).
onOpenChange(open: boolean) => voidCallback khi trạng thái mở thay đổi.
requiredbooleanfalseKhi true, handleClear không làm gì cả.
todayDatenew Date()Ghi đè ngày hôm nay.
onMonthChange(month: Date) => voidCallback khi tháng hiển thị thay đổi.
disablePastbooleanfalseVô hiệu hóa tất cả các ngày trước hôm nay.
disableFuturebooleanfalseVô hiệu hóa tất cả các ngày sau hôm nay.
showOutsideDaysbooleanfalseHiển thị các ngày của tháng liền kề.
highlightDatesDate[]Mảng các ngày cần làm nổi bật.
shouldCloseOnSelectbooleanfalseTự động xác nhận khi ngày kết thúc được chọn.
numberOfMonthsnumber2Số tháng lịch để hiển thị.
captionLayoutCaptionLayout"buttons"Chế độ bố cục cho tiêu đề.
fromYearnumbercurrent year - 100Năm bắt đầu cho danh sách thả xuống.
toYearnumbercurrent year + 10Năm kết thúc cho danh sách thả xuống.

Giá trị trả về

TênKiểuMô tả
isOpenbooleanPopup có đang mở hay không.
tempStartDateDate | nullNgày bắt đầu tạm thời (trước khi xác nhận).
tempEndDateDate | nullNgày kết thúc tạm thời (trước khi xác nhận).
hoveredDateDate | nullNgày đang được di chuột qua (để xem trước khoảng).
leftMonthDateTháng hiển thị của lịch bên trái.
rightMonthDateTháng hiển thị của lịch bên phải.
localeLocaleĐối tượng locale đã được giải quyết.
leftCalendarCalendarMonthDữ liệu lịch cho khung bên trái.
rightCalendarCalendarMonthDữ liệu lịch cho khung bên phải.
calendarsCalendarMonth[]Mảng của tất cả các tháng lịch.
getDayProps(date: Date, referenceMonth?: Date) => DayPropsTính toán các cờ cho một ô ngày trong lịch. Truyền referenceMonth để phát hiện ngày ngoài tháng hoạt động chính xác trong các bố cục nhiều tháng.
displayValuestringChuỗi đã định dạng của khoảng đã xác nhận.
hasValuebooleanMột khoảng có đang được xác nhận hay không.
canConfirmbooleanLựa chọn hiện tại có hợp lệ để xác nhận hay không.
handleDateClick(date: Date) => voidXử lý một cú nhấp vào ô ngày. Cú nhấp đầu tiên đặt ngày bắt đầu, cú nhấp thứ hai đặt ngày kết thúc.
handleDateHover(date: Date | null) => voidXử lý sự kiện di chuột qua để xem trước khoảng.
handlePrevMonth() => voidChuyển cả hai lịch về một tháng trước.
handleNextMonth() => voidChuyển cả hai lịch tới một tháng sau.
handleOpen() => voidMở popup.
handleClose() => voidĐóng popup.
handleToggle() => voidBật/tắt popup.
handleConfirm() => voidXác nhận lựa chọn và đóng.
handleCancel() => voidHủy và hoàn nguyên về giá trị trước đó.
handleClear() => voidXóa khoảng (không làm gì nếu required).
handleGoToToday() => voidChuyển đến tháng hiện tại.
containerRefRefObject<HTMLDivElement | null>Gắn vào trình bao bọc để phát hiện nhấp chuột bên ngoài.
popupRefRefObject<HTMLDivElement | null>Gắn vào phần tử popup.
focusedDateDate | nullNgày được tập trung bằng bàn phím.
handleKeyDown(e: KeyboardEvent<HTMLElement>) => voidTrình xử lý điều hướng bằng bàn phím.
presetsDateRangePreset[]Mảng các khoảng đặt trước (truyền qua để render).
handlePresetClick(preset: DateRangePreset) => voidÁp dụng một khoảng đặt trước.
activePresetIndexnumberChỉ số của khoảng đặt trước hiện đang khớp (-1 nếu không có).
yearsnumber[]Mảng các năm cho chế độ thả xuống.
monthsnumber[]Chỉ số các tháng cho chế độ thả xuống.
handleYearSelect(year: number, calendarIndex?: number) => voidĐặt năm hiển thị.
handleMonthSelect(month: number, calendarIndex?: number) => voidĐặt tháng hiển thị.

Các hành vi chính

Luồng chọn khoảng

  1. Lần nhấp đầu tiên đặt ngày bắt đầu (ngày kết thúc bị xóa)
  2. Lần nhấp thứ hai đặt ngày kết thúc (tự động hoán đổi nếu trước ngày bắt đầu)
  3. Lần nhấp thứ ba đặt lại và bắt đầu một khoảng mới

Xem trước khi di chuột

Khi người dùng đã chọn ngày bắt đầu nhưng chưa chọn ngày kết thúc, handleDateHover sẽ cập nhật hoveredDate. Hàm getDayProps sử dụng điều này để tính toán isInHoverRangeisHoverTarget, cho phép bạn hiển thị bản xem trước của khoảng tiềm năng.

Các khoảng đặt trước

Các khoảng đặt trước có thể là các đối tượng tĩnh hoặc các hàm factory:

const presets = [
{ label: "Today", value: { start: new Date(), end: new Date() } },
{
label: "Last 30 days",
value: () => {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 29);
return { start, end };
},
},
];

activePresetIndex trả về chỉ số của khoảng đặt trước khớp với lựa chọn hiện tại, hoặc -1 nếu không có cái nào khớp.

maxDays / minDays

Khi maxDays được đặt, các ngày vượt quá khoảng cho phép từ ngày bắt đầu sẽ tự động bị vô hiệu hóa. Khi minDays được đặt, các ngày quá gần ngày bắt đầu sẽ bị vô hiệu hóa.

shouldCloseOnSelect

Đối với bộ chọn khoảng, shouldCloseOnSelect sẽ kích hoạt tự động xác nhận khi ngày kết thúc được chọn (chứ không phải ngày bắt đầu).