// src/components/DateCalendarPopup.jsx import { useState, useEffect, useRef, useLayoutEffect } from "react"; import { createPortal } from "react-dom"; import { useTranslation } from 'react-i18next'; export default function DateCalendarPopup({ date, onClose, anchorRef }) { const { t } = useTranslation(); const months = t('calendar.months', { returnObjects: true }); const dayNames = t('calendar.days', { returnObjects: true }); const ref = useRef(null); const [coords, setCoords] = useState(null); const selectedDate = new Date(date); const isValidDate = !Number.isNaN(selectedDate.getTime()); const [year, setYear] = useState(isValidDate ? selectedDate.getFullYear() : new Date().getFullYear()); const [month, setMonth] = useState(isValidDate ? selectedDate.getMonth() : new Date().getMonth()); // Двухпроходное позиционирование: // 1) сразу после монтирования попап невидим (opacity: 0), но уже в DOM — // нужно, чтобы измерить его реальные width/height. // 2) как только размеры известны — пересчитываем координаты так, чтобы // попап открывался влево-вверх от якоря, и показываем (opacity: 1). useLayoutEffect(() => { if (!anchorRef?.current || !ref.current) return; const anchorRect = anchorRef.current.getBoundingClientRect(); const popupRect = ref.current.getBoundingClientRect(); setCoords({ top: anchorRect.top + window.scrollY - popupRect.height - 4, left: anchorRect.right + window.scrollX - popupRect.width, }); }, [anchorRef, month, year]); // пересчитываем и при смене месяца — высота грид может немного плавать useEffect(() => { const handler = (e) => { const clickedAnchor = anchorRef?.current?.contains(e.target); const clickedPopup = ref.current?.contains(e.target); if (!clickedAnchor && !clickedPopup) onClose(); }; document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, [onClose, anchorRef]); const goToPrevMonth = () => { if (month === 0) { setMonth(11); setYear(y => y - 1); } else { setMonth(m => m - 1); } }; const goToNextMonth = () => { if (month === 11) { setMonth(0); setYear(y => y + 1); } else { setMonth(m => m + 1); } }; const firstDay = new Date(year, month, 1).getDay(); const offset = firstDay === 0 ? 6 : firstDay - 1; const daysInMonth = new Date(year, month + 1, 0).getDate(); const cells = []; for (let i = 0; i < offset; i++) cells.push(null); for (let i = 1; i <= daysInMonth; i++) cells.push(i); const isSameDay = (day) => isValidDate && selectedDate.getFullYear() === year && selectedDate.getMonth() === month && selectedDate.getDate() === day; return createPortal(
{months[month]} {year}
{dayNames.map(dayName => (
{dayName}
))} {cells.map((day, i) => day ? (
{day}
) :
)}
, document.body ); }