HOW CUSTOMIZATION IS DONE IN REACT DATE RANGE PICKER
In this blog, React Date Range Picker has been customized so that we can cover as many use cases as possible.
CSS has been customized to change the design of the calendar. You can read the comments given in the source code to understand that why that particular CSS is changed.
I have created two custom fields to show the start date and end date. When any of the fieldĀ is clicked, calendar gets open to select the date range.
I have used MOMENT JS to convert the date object provided by this package into the string to show the date in the start date and end date text fields.
I have created the responsive design so it can be used on any devices.
There are many use cases which are handled in the handleChange function in the below source code. You can read the comments for better understanding of each part of the code.
ClickAwayListener component is used from Material UI to close the calendar if the user clicks anywhere out of the date range text fields or calendar area.
I have attached the desktop view output and mobile view output of the code below.
HOW TO IMPLEMENT REACT DATERANGE IN YOUR PROJECT
First we have to import three files for this package –
- Main css file
- Theme css file
- DateRange component
DateRange component has many properties which can be used to fulfill as per the requirement. I have used some important properties. You can go through its documentation to read about more properties.
Below are the important properties of Date Range Picker react component used in the code –
- direction – this is used to change the direction of calendar as per the screen size for responsive design.
- scroll – to enable scrolling of months when we are viewing calendar on the mobile.
- minDate – to restrict the user to not to select the date earlier than the minDate.
- maxDate – to restrict the user to notĀ to select the date beyond the maxDate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
import "react-date-range/dist/styles.css"; // main css file import "react-date-range/dist/theme/default.css"; // theme css file import { DateRange } from "react-date-range"; import { Box, TextField, ClickAwayListener } from "@mui/material"; import moment from "moment"; import { useState, useEffect, useRef } from "react"; const PickDates = () => { const pickerStyles = { boxShadow: "0px 2px 10px rgba(0, 0, 0, 0.15)", //TO HIDE MONTH AND YEAR DROPDOWN ".rdrMonthAndYearPickers": { display: "none" }, //TO HIDE THE DEFAULT DATE TEXT FIELDS PROVIDED BY THIS PACKAGE ".rdrDateDisplayWrapper": { display: "none" }, //TO ALIGN MONTH ARROW WITH MONTH NAME ".rdrMonthAndYearWrapper": { alignItems: "unset", height: "0px", paddingTop: "20px" }, //TO ALIGN CENTER MONTH AND YEAR NAME ".rdrMonthName": { textAlign: "center", fontSize: "16px" }, ".rdrWeekDays": { fontSize: "14px" }, //TO CHANGE THE RANGE BACKGROUND COLOR ".rdrInRange": { backgroundColor: "#3C4F5D" }, //FOR RESPONSIVE DESIGN(SMALL DEVICES) "@media (max-width: 600px)": { ".rdrMonth,.rdrCalendarWrapper": { width: "100%", padding: 0 }, ".rdrWeekDays": { marginTop: "24px" }, width: "100%" } }; const [range, setRange] = useState({ startDate: null, endDate: new Date(""), // This is required to overcome the bug of auto-range-selection key: "selection" }); const [showCalendar, setCalendarStatus] = useState(false); const [startDate, setStartDate] = useState(""); const [endDate, setEndDate] = useState(""); const startDateRef = useRef(null); const endDateRef = useRef(null); const [dateIdentifier, setDateIdentifier] = useState(null); useEffect(() => { setStartDate( moment(range.startDate).format("YYYY-MM-DD") === "Invalid date" ? "" : moment(range.startDate).format("YYYY-MM-DD") ); setEndDate( moment(range.endDate).format("YYYY-MM-DD") === "Invalid date" ? "" : moment(range.endDate).format("YYYY-MM-DD") ); }, [range]); const handleChange = (item) => { const { startDate, endDate } = item.selection; if (dateIdentifier === "startDate") { //IF USER TRIES TO SELECT START DATE GREATER THAN END DATE if (range.endDate && startDate > range.endDate) { setRange((prev) => ({ ...prev, startDate: startDate, endDate: new Date("") })); const newStartDate = item.selection.startDate !== "Invalid date" ? moment(item.selection.startDate).format("YYYY-MM-DD") : null; setStartDate(newStartDate); setEndDate(""); setDateIdentifier("endDate"); if (endDateRef.current) { endDateRef.current.focus(); } } else { setRange((prev) => ({ ...prev, startDate: startDate })); const newStartDate = item.selection.startDate !== "Invalid date" ? moment(item.selection.startDate).format("YYYY-MM-DD") : null; setStartDate(newStartDate); setDateIdentifier("endDate"); if (endDateRef.current) { endDateRef.current.focus(); } } } else if (dateIdentifier === "endDate") { if (range.startDate === startDate) { //IF USER SELECTS THE END DATE EARLIER THAN START DATE if (range.startDate && endDate < range.startDate) { setRange((prev) => ({ ...prev, startDate: endDate, endDate: new Date("") })); const newStartDate = item.selection.endDate !== "Invalid Date" ? moment(item.selection.endDate).format("YYYY-MM-DD") : null; setStartDate(newStartDate); setEndDate(""); if (endDateRef.current) { endDateRef.current.focus(); } } else { setRange((prev) => ({ ...prev, endDate: endDate })); const newEndDate = item.selection.endDate !== "Invalid Date" ? moment(item.selection.endDate).format("YYYY-MM-DD") : null; setEndDate(newEndDate); setCalendarStatus(false); } } else { // USER CLICKS ON THE END DATE FIELD TO CHANGE THE DATE BUT UPDATED DATE IS COMING IN START DATE FROM PACKAGE if (range.startDate && startDate < range.startDate) { setRange((prev) => ({ ...prev, startDate: startDate, endDate: new Date("") })); const newStartDate = item.selection.startDate !== "Invalid Date" ? moment(item.selection.startDate).format("YYYY-MM-DD") : null; setStartDate(newStartDate); setEndDate(""); if (endDateRef.current) { endDateRef.current.focus(); } } else { setRange((prev) => ({ ...prev, endDate: startDate })); const newEndDate = item.selection.startDate !== "Invalid Date" ? moment(item.selection.startDate).format("YYYY-MM-DD") : null; setEndDate(newEndDate); } } } }; const handleClickAway = () => { setCalendarStatus(false); }; return ( <ClickAwayListener onClickAway={handleClickAway}> <Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }} > <Box sx={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }} > <TextField inputRef={startDateRef} margin="dense" label="Start Date" value={startDate} size="small" onClick={() => { setCalendarStatus(true); setDateIdentifier("startDate"); }} InputProps={{ readOnly: true }} /> <TextField inputRef={endDateRef} margin="dense" label="End Date" value={endDate} size="small" InputProps={{ readOnly: true }} onClick={() => { setCalendarStatus(true); if (startDate !== "") { setDateIdentifier("endDate"); } else { setDateIdentifier("startDate"); if (startDateRef.current) { startDateRef.current.focus(); } } }} /> </Box> {showCalendar && ( <Box sx={pickerStyles}> <DateRange ranges={[range]} onChange={(item) => handleChange(item)} months={2} // CALENDAR WILL SHOW 2 MONTHS VIEW direction={window.innerWidth <= 600 ? "vertical" : "horizontal"} // FOR SMALL DEVICES CALENDAR WILL BE SHOWN VERTICAL scroll={{ enabled: window.innerWidth <= 600 ? true : false }} minDate={new Date()} //USER CANNOT SELECT DATE PRIOR TO TODAY'S DATE maxDate={moment().add(12, "months").toDate()} //USER WILL BE ABLE TO SELECT END DATE MAX WITHIN 1 YEAR. /> </Box> )} </Box> </ClickAwayListener> ); }; export default PickDates; |