DEV Community

Cover image for Custom Date range picker with React JS and React-Datepicker
Md. Azad Ul Kabir
Md. Azad Ul Kabir

Posted on

Custom Date range picker with React JS and React-Datepicker

This component completely reusable component you can just copy and past it will be work and look like below images.

Image description

Here we need two files one is jsx file and one is css file both of the file given below.

DateRangePicker.jsx

import React, { useState, useEffect, useRef } from "react";
import DatePicker from "react-datepicker";
import { addDays, subDays, format, subMonths, isAfter, isBefore } from "date-fns";

// Import Css
import "react-datepicker/dist/react-datepicker.css";
import "./daterange.css";

const DateRangePicker = ({ onChange }) => {
  const [startDate, setStartDate] = useState(null);
  const [endDate, setEndDate] = useState(null);
  const [dateType, setDateType] = useState("today");
  const [showDatePicker, setShowDatePicker] = useState(false);
  const [selectedRange, setSelectedRange] = useState(format(new Date(), "yyy-MM-dd"));
  const [activeRange, setActiveRange] = useState("Today");
  const [manualDate, setManualDate] = useState([
    "Today",
    "Yesterday",
    "Last 7 Days",
    "Last Month",
    "Last 3 Month",
    "Last Year",
    "LifeTime"
  ]);

  // Temporary state for handling date selection before confirmation
  const [tempStartDate, setTempStartDate] = useState(new Date());
  const [tempEndDate, setTempEndDate] = useState(null);

  // Ref to the date picker container for detecting outside clicks
  const datePickerRef = useRef(null);

  const handleDateChange = (dates) => {
    const [start, end] = dates;
    setStartDate(format(start, "yyy-MM-dd"));
    setEndDate(format(end, "yyy-MM-dd"));
    setTempStartDate(start);
    setTempEndDate(end);
    setDateType("custom");
  };

  const handleConfirm = () => {
    if (tempStartDate.getTime() === tempEndDate?.getTime()) {
      setSelectedRange(format(tempStartDate, "yyy-MM-dd"));
    } else {
      setSelectedRange(`${format(tempStartDate, "yyy-MM-dd")} - ${format(tempEndDate, "yyy-MM-dd")}`);
    }
    setShowDatePicker(false);
    onChange({ dateType: dateType, startDate: dateType === "custom"?"":startDate?.toString(), endDate: dateType === "custom"?"": endDate?.toString() });
  };
  console.log(startDate, endDate, dateType, "date");

  const setCustomRange = (range) => {
    const today = new Date();
    let start, end;

    switch (range) {
      case "Today":
        start = today;
        end = today;
        setDateType("today");
        break;
      case "Yesterday":
        start = subDays(today, 1);
        end = today;
        setDateType("yesterday");
        break;
      case "Last 7 Days":
        start = subDays(today, 6);
        end = today;
        setDateType("lastWeek");
        break;
      case "Last Month":
        start = subMonths(today, 1);
        end = today;
        setDateType("lastMonth");
        break;
      case "Last 3 Month":
        start = subMonths(today, 3);
        end = today;
        setDateType("lastTriMonth");
        break;
      case "Last Year":
        start = subDays(today, 365);
        end = today;
        setDateType("lastYear");
        break;
      case "LifeTime":
        start = new Date(2015, 0, 1);
        end = today;
        setDateType("overview");
        break;
      default:
        break;
    }

    // Update the active range
    setActiveRange(range);

    // Set the range in temporary state without confirming
    setStartDate(format(start, "yyy-MM-dd"));
    setEndDate(format(end, "yyy-MM-dd"));
    setTempStartDate(start);
    setTempEndDate(end);
  };

  useEffect(() => {
    const handleClickOutside = (event) => {
      if (datePickerRef.current && !datePickerRef.current.contains(event.target)) {
        setShowDatePicker(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, []);

  return (
    <div className="relative daterange">
      <div className="relative w-full max-w-xs">
        <input
          type="text"
          value={selectedRange}
          placeholder="Select a date range"
          onFocus={() => setShowDatePicker(true)}
          readOnly
          className="w-full max-w-xs p-2 border bg-white border-gray-50 rounded shadow-sm text-gray-600 text-sm font-semibold placeholder-gray-400 focus:outline-none focus:ring-1 focus:ring-offset-slate-100 cursor-pointer"
        />
        <span className="absolute inset-y-0 right-2 flex items-center pointer-events-none">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
            <path d="M19 3h-2V1h-2v2H9V1H7v2H5c-1.1 0-1.99.9-1.99 2L3 19c0 1.1.89 2 1.99 2H19c1.1 0 1.99-.9 1.99-2L21 5c0-1.1-.89-2-1.99-2zM5 5h14v14H5V5z" />
          </svg>
        </span>
      </div>

      {showDatePicker && (
        <div ref={datePickerRef} className="absolute right-0 flex bg-white rounded-lg shadow-md">
          <div className="py-6 px-4 flex flex-col text-nowrap items-start gap-y-2 text-lg font-normal text-gray-800 shadow-md">
            {manualDate.map((range) => (
              <button key={range} className="flex items-center gap-x-1" onClick={() => setCustomRange(range)}>
                <span className="relative inline-block w-5 h-5 rounded-full border border-gray-300 bg-white">
                  <span
                    className={`absolute inset-1 rounded-full ${activeRange === range ? "bg-[#F7E5BE]" : ""}`}
                  ></span>
                </span>
                {range}
              </button>
            ))}
          </div>

          <div className="pt-6 px-4">
            <DatePicker
              selected={tempStartDate}
              onChange={handleDateChange}
              startDate={tempStartDate}
              endDate={tempEndDate}
              selectsRange
              inline
              monthsShown={2}
              maxDate={new Date()}
            />
            <div className="flex justify-end items-center py-4">
              {/* <div>
                <h2 className="text-sm font-semibold text-gray-600">Selected Date: {selectedRange}</h2>
                <p className="text-xs font-normal text-gray-400 capitalize">Dates are shown in Asia/Dhaka</p>
              </div> */}
              <button
                onClick={handleConfirm}
                class="px-3 py-1.5 bg-blue-500 rounded hover:bg-blue-600 text-xs font-semibold text-white uppercase"
              >
                Confirm
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

export default DateRangePicker;

Enter fullscreen mode Exit fullscreen mode

daterange.css

@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap");

.daterange .react-datepicker {
  font-family: "Open Sans", serif;
  display: flex;
  border: none;
}
.daterange .react-datepicker__header {
  background-color: transparent;
  border: none;
}

.daterange .react-datepicker__navigation-icon:before {
  color: rgb(31, 41, 55);
  border-width: 2px 2px 0 0;
  top: 12px;
}
.daterange .react-datepicker__current-month {
  font-size: 16px;
  line-height: 24px;
  font-weight: 500;
  color: #1f2937;
}
.daterange .react-datepicker__day-name {
  font-size: 12px;
  font-weight: 600;
  color: rgb(156 163 175);
  padding-bottom: 6px;
}

.daterange .react-datepicker__day {
  font-size: 12px;
  font-weight: 500;
  color: #1f2937;
}
.daterange .react-datepicker__day--disabled {
  color: rgb(156 163 175);
  cursor: not-allowed;
}
.react-datepicker__day--in-range,
.react-datepicker__day--in-selecting-range:not(
    .react-datepicker__day--in-range,
    .react-datepicker__month-text--in-range,
    .react-datepicker__quarter-text--in-range,
    .react-datepicker__year-text--in-range
  ),
.react-datepicker__month-text--in-selecting-range:not(
    .react-datepicker__day--in-range,
    .react-datepicker__month-text--in-range,
    .react-datepicker__quarter-text--in-range,
    .react-datepicker__year-text--in-range
  ),
.react-datepicker__quarter-text--in-selecting-range:not(
    .react-datepicker__day--in-range,
    .react-datepicker__month-text--in-range,
    .react-datepicker__quarter-text--in-range,
    .react-datepicker__year-text--in-range
  ),
.react-datepicker__year-text--in-selecting-range:not(
    .react-datepicker__day--in-range,
    .react-datepicker__month-text--in-range,
    .react-datepicker__quarter-text--in-range,
    .react-datepicker__year-text--in-range
  ),
.react-datepicker__day-selected,
.react-datepicker__day--keyboard-selected,
.react-datepicker__day--keyboard-selected:not([aria-disabled="true"]):hover,
.react-datepicker__day--in-range:not([aria-disabled="true"]):hover,
.react-datepicker__day--selected:not([aria-disabled="true"]):hover,
.react-datepicker__day--in-selecting-range:not([aria-disabled="true"]):hover {
  background-color: #f7e5be;
  border-radius: 4px;
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)