在特定 Ref 上呈现工具提示元素

Render tooltop element on specific Ref

我只想在用户单击此按钮且特定条件成立时在按钮上呈现我的 TooltipComponent

我熟悉 react-tooltip 库,但我不想使用它,我对这个库的看法不是积极的。在这个库中有一个 ReactTooltop.show 函数。

我希望能够做类似的事情(同样,不使用这个库:

const onBtnClick = () => {
    if(...condition...){
       MyTooltip.show(btnRef, ...); // maybe pass some arguments like tooltip text...
    }
}

知道我该如何实现吗?

谢谢!

您需要的是 popoverhere is an example。您可以在互联网上找到一些,但也很容易重新创建:

.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 */
}

Here is the repro on stackblitz。然后,您可以通过获取子项的宽度并使用它来调整弹出窗口(例如通过更改算法重新创建选项 "top"、"bottom"、"left"、"right")。