diff --git a/src/components/DateCalendarPopup.jsx b/src/components/DateCalendarPopup.jsx index 182ee2a..cdf613f 100644 --- a/src/components/DateCalendarPopup.jsx +++ b/src/components/DateCalendarPopup.jsx @@ -1,11 +1,14 @@ -import { useState, useEffect, useRef } from "react"; +// 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 }) { +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()); @@ -13,11 +16,32 @@ export default function DateCalendarPopup({ date, onClose }) { 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) => { if (ref.current && !ref.current.contains(e.target)) onClose(); }; + 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]); + }, [onClose, anchorRef]); const goToPrevMonth = () => { if (month === 0) { @@ -51,8 +75,20 @@ export default function DateCalendarPopup({ date, onClose }) { selectedDate.getMonth() === month && selectedDate.getDate() === day; - return ( -