import React, { useEffect, useRef, useState } from "react"; import { Combobox } from "@headlessui/react"; import { usePopper } from "react-popper"; import { Placement } from "@popperjs/core"; import { DateRange, DayPicker, Matcher } from "react-day-picker"; import { ArrowRight, CalendarDays } from "lucide-react"; // hooks import useOutsideClickDetector from "hooks/use-outside-click-detector"; import { useDropdownKeyDown } from "hooks/use-dropdown-key-down"; // components import { DropdownButton } from "./buttons"; // ui import { Button } from "@plane/ui"; // helpers import { cn } from "helpers/common.helper"; import { renderFormattedDate } from "helpers/date-time.helper"; // types import { TButtonVariants } from "./types"; type Props = { applyButtonText?: string; bothRequired?: boolean; buttonClassName?: string; buttonContainerClassName?: string; buttonFromDateClassName?: string; buttonToDateClassName?: string; buttonVariant: TButtonVariants; cancelButtonText?: string; className?: string; disabled?: boolean; hideIcon?: { from?: boolean; to?: boolean; }; icon?: React.ReactNode; minDate?: Date; maxDate?: Date; onSelect: (range: DateRange | undefined) => void; placeholder?: { from?: string; to?: string; }; placement?: Placement; required?: boolean; showTooltip?: boolean; tabIndex?: number; value: { from: Date | undefined; to: Date | undefined; }; }; export const DateRangeDropdown: React.FC = (props) => { const { applyButtonText = "Apply changes", bothRequired = true, buttonClassName, buttonContainerClassName, buttonFromDateClassName, buttonToDateClassName, buttonVariant, cancelButtonText = "Cancel", className, disabled = false, hideIcon = { from: true, to: true, }, icon = , minDate, maxDate, onSelect, placeholder = { from: "Add date", to: "Add date", }, placement, required = false, showTooltip = false, tabIndex, value, } = props; // states const [isOpen, setIsOpen] = useState(false); const [dateRange, setDateRange] = useState(value); // refs const dropdownRef = useRef(null); // popper-js refs const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); // popper-js init const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: placement ?? "bottom-start", modifiers: [ { name: "preventOverflow", options: { padding: 12, }, }, ], }); const onOpen = () => { if (referenceElement) referenceElement.focus(); }; const handleClose = () => { if (!isOpen) return; setIsOpen(false); setDateRange({ from: value.from, to: value.to, }); if (referenceElement) referenceElement.blur(); }; const toggleDropdown = () => { if (!isOpen) onOpen(); setIsOpen((prevIsOpen) => !prevIsOpen); }; const handleKeyDown = useDropdownKeyDown(toggleDropdown, handleClose); const handleOnClick = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); toggleDropdown(); }; useOutsideClickDetector(dropdownRef, handleClose); const disabledDays: Matcher[] = []; if (minDate) disabledDays.push({ before: minDate }); if (maxDate) disabledDays.push({ after: maxDate }); useEffect(() => { setDateRange(value); }, [value]); return ( { if (e.key === "Enter") { if (!isOpen) handleKeyDown(e); } else handleKeyDown(e); }} > {isOpen && (
{ // if both the dates are not required, immediately call onSelect if (!bothRequired) onSelect(val); setDateRange({ from: val?.from ?? undefined, to: val?.to ?? undefined, }); }} mode="range" disabled={disabledDays} showOutsideDays initialFocus footer={ bothRequired && (
) } />
)} ); };