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