customize date picker, flexible can set max and min date and also handle react-hook-form

This commit is contained in:
pramod mahajan 2025-08-05 23:54:22 +05:30
parent c85d21ad0c
commit cb009c77a4
2 changed files with 200 additions and 50 deletions

View File

@ -1,39 +1,71 @@
import React, { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { useController } from "react-hook-form";
const DatePicker = ({ onDateChange }) => {
const DatePicker = ({
name,
control,
placeholder = "DD-MM-YYYY",
className = "",
allowText = false,
maxDate=new Date(),
minDate,
...rest
}) => {
const inputRef = useRef(null); const inputRef = useRef(null);
useEffect(() => { const {
const fp = flatpickr(inputRef.current, { field: { onChange, value, ref }
dateFormat: "Y-m-d", } = useController({
defaultDate: new Date(), name,
onChange: (selectedDates, dateStr) => { control
if (onDateChange) { });
onDateChange(dateStr); // Pass selected date to parent
}
}
});
return () => { useEffect(() => {
// Cleanup flatpickr instance if (inputRef.current) {
fp.destroy(); flatpickr(inputRef.current, {
}; dateFormat: "d-m-Y",
}, [onDateChange]); allowInput: allowText,
defaultDate: value
? flatpickr.parseDate(value, "Y-m-d")
: null,
maxDate:maxDate,
minDate:new Date(minDate?.split("T")[0]) ?? undefined,
onChange: function (selectedDates, dateStr) {
onChange(dateStr);
},
...rest
});
}
}, [inputRef]);
return ( return (
<div className="container mt-3"> <div className={` position-relative ${className}`}>
<div className="mb-3"> <input
{/* <label htmlFor="flatpickr-single" className="form-label"> type="text"
Select Date className="form-control form-control-sm "
</label> */} placeholder={placeholder}
<input defaultValue={
type="text" value ? flatpickr.formatDate(flatpickr.parseDate(value, "Y-m-d"), "d-m-Y") : ""
id="flatpickr-single" }
className="form-control" ref={(el) => {
placeholder="YYYY-MM-DD" inputRef.current = el;
ref={inputRef} ref(el);
/> }}
</div> readOnly={!allowText}
autoComplete="off"
/>
<span
className="position-absolute top-50 end-0 pe-1 translate-middle-y cursor-pointer"
onClick={() => {
if (inputRef.current && inputRef.current._flatpickr) {
inputRef.current._flatpickr.open();
}
}}
>
<i className="bx bx-calendar bx-sm fs-5 text-muted"></i>
</span>
</div> </div>
); );
}; };

View File

@ -1,6 +1,8 @@
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { useController, useFormContext, useWatch } from "react-hook-form";
const DateRangePicker = ({ const DateRangePicker = ({
md,
sm,
onRangeChange, onRangeChange,
DateDifference = 7, DateDifference = 7,
endDateMode = "yesterday", endDateMode = "yesterday",
@ -25,11 +27,12 @@ const DateRangePicker = ({
altInput: true, altInput: true,
altFormat: "d-m-Y", altFormat: "d-m-Y",
defaultDate: [startDate, endDate], defaultDate: [startDate, endDate],
static: true, static: false,
// appendTo: document.body,
clickOpens: true, clickOpens: true,
maxDate: endDate, // Disable future dates maxDate: endDate,
onChange: (selectedDates, dateStr) => { onChange: (selectedDates, dateStr) => {
const [startDateString, endDateString] = dateStr.split(" to "); const [startDateString, endDateString] = dateStr.split(" To ");
onRangeChange?.({ startDate: startDateString, endDate: endDateString }); onRangeChange?.({ startDate: startDateString, endDate: endDateString });
}, },
}); });
@ -45,14 +48,129 @@ const DateRangePicker = ({
}, [onRangeChange, DateDifference, endDateMode]); }, [onRangeChange, DateDifference, endDateMode]);
return ( return (
<input <div className={`col-${sm} col-sm-${md} px-1 position-relative`}>
type="text" <input
className="form-control form-control-sm ms-1" type="text"
placeholder="From to End" className="form-control form-control-sm ps-2 pe-5 me-4"
id="flatpickr-range" placeholder="From to End"
ref={inputRef} id="flatpickr-range"
/> ref={inputRef}
/>
<i
className="bx bx-calendar calendar-icon cursor-pointer position-absolute top-50 translate-middle-y "
style={{ right: "12px" }}
></i>
</div>
); );
}; };
export default DateRangePicker; export default DateRangePicker;
export const DateRangePicker1 = ({
startField = "startDate",
endField = "endDate",
placeholder = "Select date range",
className = "",
allowText = false,
resetSignal, // <- NEW prop
...rest
}) => {
const inputRef = useRef(null);
const { control, setValue, getValues } = useFormContext();
const {
field: { ref },
} = useController({ name: startField, control });
const applyDefaultDates = () => {
const today = new Date();
const past = new Date();
past.setDate(today.getDate() - 6);
const format = (d) => flatpickr.formatDate(d, "d-m-Y");
const formattedStart = format(past);
const formattedEnd = format(today);
setValue(startField, formattedStart, { shouldValidate: true });
setValue(endField, formattedEnd, { shouldValidate: true });
if (inputRef.current?._flatpickr) {
inputRef.current._flatpickr.setDate([past, today]);
}
};
useEffect(() => {
if (!inputRef.current || inputRef.current._flatpickr) return;
const instance = flatpickr(inputRef.current, {
mode: "range",
dateFormat: "d-m-Y",
allowInput: allowText,
onChange: (selectedDates) => {
if (selectedDates.length === 2) {
const [start, end] = selectedDates;
const format = (d) => flatpickr.formatDate(d, "d-m-Y");
setValue(startField, format(start), { shouldValidate: true });
setValue(endField, format(end), { shouldValidate: true });
} else {
setValue(startField, "", { shouldValidate: true });
setValue(endField, "", { shouldValidate: true });
}
},
...rest,
});
// Apply default if empty
const currentStart = getValues(startField);
const currentEnd = getValues(endField);
if (!currentStart && !currentEnd) {
applyDefaultDates();
} else if (currentStart && currentEnd) {
instance.setDate([
flatpickr.parseDate(currentStart, "d-m-Y"),
flatpickr.parseDate(currentEnd, "d-m-Y"),
]);
}
return () => instance.destroy();
}, []);
// Reapply default range on resetSignal change
useEffect(() => {
if (resetSignal !== undefined) {
applyDefaultDates();
}
}, [resetSignal]);
const start = getValues(startField);
const end = getValues(endField);
const formattedValue = start && end ? `${start} To ${end}` : "";
return (
<div className={`position-relative ${className}`}>
<input
type="text"
className="form-control form-control-sm"
placeholder={placeholder}
defaultValue={formattedValue}
ref={(el) => {
inputRef.current = el;
ref(el);
}}
readOnly={!allowText}
autoComplete="off"
/>
<span
className="position-absolute top-50 end-0 pe-1 translate-middle-y cursor-pointer"
onClick={() => inputRef.current?._flatpickr?.open()}
>
<i className="bx bx-calendar bx-sm fs-5 text-muted"></i>
</span>
</div>
);
};