瀏覽代碼

feat: auto-position tooltip and suport overflowing container (#3631)

vanilla_orig
David Luzar 3 年之前
父節點
當前提交
357266e9ab
沒有連結到貢獻者的電子郵件帳戶。
共有 4 個檔案被更改,包括 100 行新增76 行删除
  1. 1
    5
      src/actions/actionExport.tsx
  2. 17
    50
      src/components/Tooltip.scss
  3. 81
    20
      src/components/Tooltip.tsx
  4. 1
    1
      src/excalidraw-app/index.tsx

+ 1
- 5
src/actions/actionExport.tsx 查看文件

@@ -67,11 +67,7 @@ export const actionChangeExportEmbedScene = register({
67 67
         onChange={(event) => updateData(event.target.checked)}
68 68
       />{" "}
69 69
       {t("labels.exportEmbedScene")}
70
-      <Tooltip
71
-        label={t("labels.exportEmbedScene_details")}
72
-        position="above"
73
-        long={true}
74
-      >
70
+      <Tooltip label={t("labels.exportEmbedScene_details")} long={true}>
75 71
         <div className="TooltipIcon">{questionCircle}</div>
76 72
       </Tooltip>
77 73
     </label>

+ 17
- 50
src/components/Tooltip.scss 查看文件

@@ -1,58 +1,25 @@
1 1
 @import "../css/variables.module";
2
-.excalidraw {
3
-  .Tooltip {
4
-    position: relative;
5
-  }
6
-
7
-  .Tooltip__label {
8
-    --arrow-size: 4px;
9
-    visibility: hidden;
10
-    background: $oc-black;
11
-    color: $oc-white;
12
-    text-align: center;
13
-    border-radius: 6px;
14
-    padding: 8px;
15
-    position: absolute;
16
-    z-index: 10;
17
-    font-size: 13px;
18
-    line-height: 1.5;
19
-    font-weight: 500;
20
-    // extra pixel offset for unknown reasons
21
-    left: calc(50% + var(--arrow-size) / 2 - 1px);
22
-    transform: translateX(-50%);
23
-    word-wrap: break-word;
24
-
25
-    &::after {
26
-      content: "";
27
-      border: var(--arrow-size) solid transparent;
28
-      position: absolute;
29
-      left: calc(50% - var(--arrow-size));
30
-    }
2
+.excalidraw-tooltip {
3
+  position: absolute;
4
+  z-index: 1000;
31 5
 
32
-    &--above {
33
-      bottom: calc(100% + var(--arrow-size) + 3px);
6
+  padding: 8px;
7
+  border-radius: 6px;
8
+  box-sizing: border-box;
9
+  pointer-events: none;
10
+  word-wrap: break-word;
34 11
 
35
-      &::after {
36
-        border-top-color: $oc-black;
37
-        top: 100%;
38
-      }
39
-    }
12
+  background: $oc-black;
40 13
 
41
-    &--below {
42
-      top: calc(100% + var(--arrow-size) + 3px);
14
+  line-height: 1.5;
15
+  text-align: center;
16
+  font-size: 13px;
17
+  font-weight: 500;
18
+  color: $oc-white;
43 19
 
44
-      &::after {
45
-        border-bottom-color: $oc-black;
46
-        bottom: 100%;
47
-      }
48
-    }
49
-  }
50
-
51
-  .Tooltip:hover .Tooltip__label {
52
-    visibility: visible;
53
-  }
20
+  display: none;
54 21
 
55
-  .Tooltip__label:hover {
56
-    visibility: visible;
22
+  &.excalidraw-tooltip--visible {
23
+    display: block;
57 24
   }
58 25
 }

+ 81
- 20
src/components/Tooltip.tsx 查看文件

@@ -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
+};

+ 1
- 1
src/excalidraw-app/index.tsx 查看文件

@@ -340,7 +340,7 @@ const ExcalidrawWrapper = () => {
340 340
           rel="noopener noreferrer"
341 341
           aria-label={t("encrypted.link")}
342 342
         >
343
-          <Tooltip label={t("encrypted.tooltip")} position="above" long={true}>
343
+          <Tooltip label={t("encrypted.tooltip")} long={true}>
344 344
             {shield}
345 345
           </Tooltip>
346 346
         </a>

Loading…
取消
儲存