69 lines
2.5 KiB
JavaScript
69 lines
2.5 KiB
JavaScript
import { useEffect, useRef, useLayoutEffect, useState } from "react";
|
||
import { createPortal } from "react-dom";
|
||
import { useTranslation } from "react-i18next";
|
||
|
||
/**
|
||
* Всплывающее окно с текстом вознаграждения (rewardText).
|
||
* Открывается под/рядом с кнопкой-якорем, закрывается по клику мимо или Esc.
|
||
*/
|
||
export default function RewardPopup({ text, anchorRef, onClose }) {
|
||
const { t } = useTranslation();
|
||
const ref = useRef(null);
|
||
const [coords, setCoords] = useState(null);
|
||
|
||
useLayoutEffect(() => {
|
||
if (!anchorRef?.current || !ref.current) return;
|
||
const anchorRect = anchorRef.current.getBoundingClientRect();
|
||
const popupRect = ref.current.getBoundingClientRect();
|
||
const gap = 8;
|
||
const margin = 10;
|
||
|
||
// Всплываем слева сверху от кнопки (как календарь): низ попапа — у верха якоря,
|
||
// правая грань попапа выравнивается по правой грани кнопки.
|
||
let top = anchorRect.top + window.scrollY - popupRect.height - gap;
|
||
const minTop = window.scrollY + margin;
|
||
if (top < minTop) top = minTop;
|
||
|
||
let left = anchorRect.right + window.scrollX - popupRect.width;
|
||
const minLeft = window.scrollX + margin;
|
||
if (left < minLeft) left = minLeft;
|
||
|
||
setCoords({ top, left });
|
||
}, [anchorRef]);
|
||
|
||
useEffect(() => {
|
||
const onDown = (e) => {
|
||
const inAnchor = anchorRef?.current?.contains(e.target);
|
||
const inPopup = ref.current?.contains(e.target);
|
||
if (!inAnchor && !inPopup) onClose();
|
||
};
|
||
const onKey = (e) => {
|
||
if (e.key === "Escape") onClose();
|
||
};
|
||
document.addEventListener("mousedown", onDown);
|
||
document.addEventListener("keydown", onKey);
|
||
return () => {
|
||
document.removeEventListener("mousedown", onDown);
|
||
document.removeEventListener("keydown", onKey);
|
||
};
|
||
}, [onClose, anchorRef]);
|
||
|
||
return createPortal(
|
||
<div
|
||
className="f-reward-popup"
|
||
ref={ref}
|
||
role="tooltip"
|
||
style={{
|
||
position: "absolute",
|
||
top: coords?.top ?? 0,
|
||
left: coords?.left ?? 0,
|
||
zIndex: 1000,
|
||
visibility: coords ? "visible" : "hidden",
|
||
}}
|
||
>
|
||
<div className="f-reward-popup-title">💛 {t("postCard.reward")}</div>
|
||
<div className="f-reward-popup-text">{text}</div>
|
||
</div>,
|
||
document.body
|
||
);
|
||
} |