69 lines
1.8 KiB
JavaScript
69 lines
1.8 KiB
JavaScript
import React, { useEffect, useRef, useState } from "react";
|
|
|
|
const HoverPopup = ({ title, content, children }) => {
|
|
const [visible, setVisible] = useState(false);
|
|
const triggerRef = useRef(null);
|
|
const popupRef = useRef(null);
|
|
|
|
// Toggle popup on hover or click
|
|
const handleMouseEnter = () => setVisible(true);
|
|
const handleClick = () => setVisible((prev) => !prev);
|
|
|
|
// Hide popup on outside click
|
|
useEffect(() => {
|
|
const handleDocumentClick = (e) => {
|
|
if (
|
|
!popupRef.current?.contains(e.target) &&
|
|
!triggerRef.current?.contains(e.target)
|
|
) {
|
|
setVisible(false);
|
|
}
|
|
};
|
|
|
|
if (visible) document.addEventListener("click", handleDocumentClick);
|
|
return () => document.removeEventListener("click", handleDocumentClick);
|
|
}, [visible]);
|
|
|
|
return (
|
|
<div
|
|
className="d-inline-block position-relative"
|
|
ref={triggerRef}
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={() => setVisible(false)}
|
|
onClick={handleClick}
|
|
style={{ cursor: "pointer" }}
|
|
>
|
|
{children}
|
|
|
|
{visible && (
|
|
<div
|
|
ref={popupRef}
|
|
className="bg-white border rounded shadow-sm p-3 text-start"
|
|
style={{
|
|
position: "absolute",
|
|
top: "100%",
|
|
left: "50%",
|
|
transform: "translateX(-50%)",
|
|
zIndex: 1050,
|
|
minWidth: "240px",
|
|
maxWidth: "300px",
|
|
marginTop: "8px",
|
|
whiteSpace: "normal",
|
|
}}
|
|
>
|
|
{title && (
|
|
<h6 className="mb-2 fw-semibold text-dark" style={{ fontSize: "0.9rem" }}>
|
|
{title}
|
|
</h6>
|
|
)}
|
|
<div className="text-muted" style={{ fontSize: "0.85rem", lineHeight: "1.4" }}>
|
|
{content}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default HoverPopup;
|