79388781

Date: 2025-01-26 15:37:16
Score: 0.5
Natty:
Report link

Without testing your approach, I imagine the issue with it, as you have already guessed, is that you are re-rendering the whole calendar everytime a new day is hovered, which causes the flickering.

To avoid this, we need to use vanilla js, as in the suggestion made by @user25539973. I have done something similar to him, but it should be more reliable, see below.

One caviat though is that you'll need to modify the Calendar component of the primereact library so that the date cells contain the information of their day, month and year. Alternatively, you could implement some logic to find them out based on the header of the month view and on the week of the date cell. I have just created a PR for this information to be available in the next primereact versions: https://github.com/primefaces/primereact/pull/7650. Hopefully, it will be merged soon.

export default function DateRangeSelector() {
  const [selectedDates, setSelectedDates] = useState<(Date | null)[]>([]);
  const [rangeStart, setRangeStart] = useState<Date | null>(null);
  const calendarRef = useRef<Calendar>(null); // Ref to the Calendar DOM

  const handleDateSelect = (selectedDates: (Date | null)[]) => {
    setSelectedDates(selectedDates); // Finalize the range

    if (!rangeStart) {
      setRangeStart(selectedDates[0]); // Set the start date
    } else {
      setRangeStart(null); // Reset start date
    }
  };

  const handleDateHover = (event: React.MouseEvent<HTMLTableCellElement>) => {
    if (!calendarRef.current) return
    if (!rangeStart || selectedDates[1]) return

    if (!(event.target instanceof HTMLTableCellElement)) return

    const hoveredDay = event.target.getAttribute('data-p-day');
    const hoveredMonth = event.target.getAttribute('data-p-month');
    const hoveredYear = event.target.getAttribute('data-p-year');

    if (hoveredDay === null || hoveredMonth === null || hoveredYear === null) return;

    const hoveredDate = new Date(parseInt(hoveredYear), parseInt(hoveredMonth), parseInt(hoveredDay));

    const minDate = rangeStart < hoveredDate ? rangeStart : hoveredDate;
    const maxDate = rangeStart > hoveredDate ? rangeStart : hoveredDate;

    // Remove existing highlights
    const element = calendarRef.current.getOverlay()
    const allCells = element.querySelectorAll('.p-datepicker-calendar td');
    allCells.forEach((cell) => cell.firstElementChild?.classList.remove('p-highlight'));

    // Highlight dates in the range
    allCells.forEach((cell) => {
      const day = cell.getAttribute('data-p-day');
      const month = cell.getAttribute('data-p-month');
      const year = cell.getAttribute('data-p-year');

      if (day === null || month === null || year === null) return;

      const cellDate = new Date(parseInt(year), parseInt(month), parseInt(day));

      if (cellDate >= minDate && cellDate <= maxDate) cell.firstElementChild?.classList.add('p-highlight');
    });
  };

  return (
    <div>
      <h3>Select Date Range</h3>
      <Calendar
        ref={calendarRef} // Attach ref to access DOM
        value={selectedDates}
        onChange={(e) => { if (e.value) handleDateSelect(e.value) }}
        selectionMode="range" numberOfMonths={2}
        readOnlyInput
        pt={{day: { onMouseEnter: (event  ) => handleDateHover(event) }}}
      />
    </div>
  );
}
Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @user25539973
  • Low reputation (1):
Posted by: Acc