Что вам нужно, это popover
, вот пример . Вы можете найти некоторые из них на inte rnet, но их также легко воссоздать:
.ts:
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
const App = () => {
const [isPopoverVisible, setPopoverVisibility] = React.useState(false);
const togglePopover = () => {
if (!isPopoverVisible) {
setPopoverVisibility(!isPopoverVisible);
} else {
setTimeout(() => setPopoverVisibility(!isPopoverVisible), 20000);
}
};
return (
<>
<div>
Content Content Content Content Content Content Content Content Content
Content Content Content Content Content Content Content Content Content
Content Content Content Content Content Content Content Content Content
Content Content Content Content Content Content Content Content Content
Content Content Content Content
</div>
<span>Some content</span>
<Popover
isVisible={isPopoverVisible}
resetVisibility={togglePopover}
>
<button onClick={() => togglePopover()}>coucou</button>
</Popover>
<div>other content</div>
</>
);
};
const Popover = ({ children, isVisible, resetVisibility, position }) => {
const [classes, setClasses] = React.useState("popover");
const [style, setStyle] = React.useState({});
const childrenRef = React.createRef();
const popoverMessageRef = React.createRef();
React.useEffect(() => {
const messagePos = popoverMessageRef.current.getBoundingClientRect();
const childrenPos = childrenRef.current.getBoundingClientRect();
// x and y from getBoundingClientRect() dos not take into account the padding, we need to add it to our calculs
const messagePaddingFull = window
.getComputedStyle(popoverMessageRef.current)
.getPropertyValue("padding");
const messagePadding =
messagePaddingFull.substring(0, messagePaddingFull.length - 2);
if(position){
if (position.toLowerCase() === "top") {
setStyle({
top: `${childrenPos.y - messagePos.height - 10}px`,
left: `${(childrenPos.x - messagePadding) - childrenPos.width / 2}px`
});
} else if(position.toLowerCase() === "left"){
setStyle({
top: `${(+childrenPos.y + +messagePadding/2) - childrenPos.height / 2}px`,
left: `${(childrenPos.x) - messagePos.width - 10}px`
});
} else if(position.toLowerCase() === "right"){
setStyle({
top: `${(+childrenPos.y + +messagePadding/2) - childrenPos.height / 2}px`,
left: `${+childrenPos.x + +childrenPos.width + 10}px`
});
} else {
// default bottom
setStyle({
top: `${(childrenPos.y) + childrenPos.height + 10}px`,
left: `${(childrenPos.x - messagePadding/2) - childrenPos.width / 2}px`
});
}
} else {
// default bottom position
setStyle({
top: `${(childrenPos.y) + childrenPos.height + 10}px`,
left: `${(childrenPos.x - messagePadding/2) - childrenPos.width / 2}px`
});
}
console.log("messagePos = ", messagePos);
console.log("messagePos = ", messagePadding);
console.log("childrenPos = ", childrenPos);
}, [classes]);
React.useEffect(() => {
setClasses(`popover ${isVisible ? "pop" : ""}`);
if (isVisible) {
resetVisibility();
}
}, [isVisible]);
return (
<div className="popover-component">
<span ref={childrenRef}>{children}</span>
<div className={classes} ref={popoverMessageRef} style={style}>
This is the popover
</div>
</div>
);
};
render(<App />, document.getElementById("root"));
. css:
.popover-component{
display: inline-block;
position: relative;
}
.popover{
display: none;
position: fixed; /* You can play with absolute position as well */
background-color: #797979;
border-radius: 4px;
padding: 5px;
}
.pop {
display: block; /* You can play with inline-block as well */
}
Вот репродукция на stackblitz . Затем вы можете настроить всплывающее окно, получив ширину детей и поиграв с ней (например, воссоздав параметры «верх», «низ», «левый», «правый», изменив свой al go).