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);
useEffect(() => {
const fp = flatpickr(inputRef.current, {
dateFormat: "Y-m-d",
defaultDate: new Date(),
onChange: (selectedDates, dateStr) => {
if (onDateChange) {
onDateChange(dateStr); // Pass selected date to parent
}
}
});
const {
field: { onChange, value, ref }
} = useController({
name,
control
});
return () => {
// Cleanup flatpickr instance
fp.destroy();
};
}, [onDateChange]);
useEffect(() => {
if (inputRef.current) {
flatpickr(inputRef.current, {
dateFormat: "d-m-Y",
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 (
<div className="container mt-3">
<div className="mb-3">
{/* <label htmlFor="flatpickr-single" className="form-label">
Select Date
</label> */}
<input
type="text"
id="flatpickr-single"
className="form-control"
placeholder="YYYY-MM-DD"
ref={inputRef}
/>
</div>
<div className={` position-relative ${className}`}>
<input
type="text"
className="form-control form-control-sm "
placeholder={placeholder}
defaultValue={
value ? flatpickr.formatDate(flatpickr.parseDate(value, "Y-m-d"), "d-m-Y") : ""
}
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={() => {
if (inputRef.current && inputRef.current._flatpickr) {
inputRef.current._flatpickr.open();
}
}}
>
<i className="bx bx-calendar bx-sm fs-5 text-muted"></i>
</span>
</div>
);
};

View File

@ -1,8 +1,10 @@
import React, { useEffect, useRef } from "react";
import { useController, useFormContext, useWatch } from "react-hook-form";
const DateRangePicker = ({
md,
sm,
onRangeChange,
DateDifference = 7,
DateDifference = 7,
endDateMode = "yesterday",
}) => {
const inputRef = useRef(null);
@ -10,33 +12,34 @@ const DateRangePicker = ({
useEffect(() => {
const endDate = new Date();
if (endDateMode === "yesterday") {
endDate.setDate(endDate.getDate() - 1);
endDate.setDate(endDate.getDate() - 1);
}
endDate.setHours(0, 0, 0, 0);
const startDate = new Date(endDate);
const startDate = new Date(endDate);
startDate.setDate(endDate.getDate() - (DateDifference - 1));
startDate.setHours(0, 0, 0, 0);
const fp = flatpickr(inputRef.current, {
mode: "range",
dateFormat: "Y-m-d",
altInput: true,
altFormat: "d-m-Y",
defaultDate: [startDate, endDate],
static: true,
dateFormat: "Y-m-d",
altInput: true,
altFormat: "d-m-Y",
defaultDate: [startDate, endDate],
static: false,
// appendTo: document.body,
clickOpens: true,
maxDate: endDate, // Disable future dates
maxDate: endDate,
onChange: (selectedDates, dateStr) => {
const [startDateString, endDateString] = dateStr.split(" to ");
const [startDateString, endDateString] = dateStr.split(" To ");
onRangeChange?.({ startDate: startDateString, endDate: endDateString });
},
});
onRangeChange?.({
startDate: startDate.toLocaleDateString("en-CA"),
endDate: endDate.toLocaleDateString("en-CA"),
startDate: startDate.toLocaleDateString("en-CA"),
endDate: endDate.toLocaleDateString("en-CA"),
});
return () => {
@ -45,14 +48,129 @@ const DateRangePicker = ({
}, [onRangeChange, DateDifference, endDateMode]);
return (
<input
type="text"
className="form-control form-control-sm ms-1"
placeholder="From to End"
id="flatpickr-range"
ref={inputRef}
/>
<div className={`col-${sm} col-sm-${md} px-1 position-relative`}>
<input
type="text"
className="form-control form-control-sm ps-2 pe-5 me-4"
placeholder="From to End"
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 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>
);
};