Tooltip.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import "./Tooltip.scss";
  2. import React, { useEffect } from "react";
  3. export const getTooltipDiv = () => {
  4. const existingDiv = document.querySelector<HTMLDivElement>(
  5. ".excalidraw-tooltip",
  6. );
  7. if (existingDiv) {
  8. return existingDiv;
  9. }
  10. const div = document.createElement("div");
  11. document.body.appendChild(div);
  12. div.classList.add("excalidraw-tooltip");
  13. return div;
  14. };
  15. export const updateTooltipPosition = (
  16. tooltip: HTMLDivElement,
  17. item: {
  18. left: number;
  19. top: number;
  20. width: number;
  21. height: number;
  22. },
  23. position: "bottom" | "top" = "bottom",
  24. ) => {
  25. const tooltipRect = tooltip.getBoundingClientRect();
  26. const viewportWidth = window.innerWidth;
  27. const viewportHeight = window.innerHeight;
  28. const margin = 5;
  29. let left = item.left + item.width / 2 - tooltipRect.width / 2;
  30. if (left < 0) {
  31. left = margin;
  32. } else if (left + tooltipRect.width >= viewportWidth) {
  33. left = viewportWidth - tooltipRect.width - margin;
  34. }
  35. let top: number;
  36. if (position === "bottom") {
  37. top = item.top + item.height + margin;
  38. if (top + tooltipRect.height >= viewportHeight) {
  39. top = item.top - tooltipRect.height - margin;
  40. }
  41. } else {
  42. top = item.top - tooltipRect.height - margin;
  43. if (top < 0) {
  44. top = item.top + item.height + margin;
  45. }
  46. }
  47. Object.assign(tooltip.style, {
  48. top: `${top}px`,
  49. left: `${left}px`,
  50. });
  51. };
  52. const updateTooltip = (
  53. item: HTMLDivElement,
  54. tooltip: HTMLDivElement,
  55. label: string,
  56. long: boolean,
  57. ) => {
  58. tooltip.classList.add("excalidraw-tooltip--visible");
  59. tooltip.style.minWidth = long ? "50ch" : "10ch";
  60. tooltip.style.maxWidth = long ? "50ch" : "15ch";
  61. tooltip.textContent = label;
  62. const itemRect = item.getBoundingClientRect();
  63. updateTooltipPosition(tooltip, itemRect);
  64. };
  65. type TooltipProps = {
  66. children: React.ReactNode;
  67. label: string;
  68. long?: boolean;
  69. style?: React.CSSProperties;
  70. };
  71. export const Tooltip = ({
  72. children,
  73. label,
  74. long = false,
  75. style,
  76. }: TooltipProps) => {
  77. useEffect(() => {
  78. return () =>
  79. getTooltipDiv().classList.remove("excalidraw-tooltip--visible");
  80. }, []);
  81. return (
  82. <div
  83. className="excalidraw-tooltip-wrapper"
  84. onPointerEnter={(event) =>
  85. updateTooltip(
  86. event.currentTarget as HTMLDivElement,
  87. getTooltipDiv(),
  88. label,
  89. long,
  90. )
  91. }
  92. onPointerLeave={() =>
  93. getTooltipDiv().classList.remove("excalidraw-tooltip--visible")
  94. }
  95. style={style}
  96. >
  97. {children}
  98. </div>
  99. );
  100. };