From d043d8d31cec5ce741362bfb1dfb08d91d7a0a42 Mon Sep 17 00:00:00 2001
From: graphemecluster
Date: Thu, 3 Oct 2024 18:29:58 +0800
Subject: [PATCH] Use Tooltip for evaluation result copying
Displaying a dialog is too visually obtrusive for such a small action.
---
src/Components/Main.tsx | 40 ++++++++++++++++++++++-----------
src/Components/Tooltip.tsx | 14 +++++++-----
src/Components/TooltipLabel.tsx | 5 ++++-
src/utils.tsx | 32 --------------------------
4 files changed, 40 insertions(+), 51 deletions(-)
diff --git a/src/Components/Main.tsx b/src/Components/Main.tsx
index a21c944..b9b6b99 100644
--- a/src/Components/Main.tsx
+++ b/src/Components/Main.tsx
@@ -13,7 +13,7 @@ import { allOptions, defaultArticle } from "../consts";
import evaluate from "../evaluate";
import { listenArticle } from "../options";
import initialState, { stateStorageLocation } from "../state";
-import { copy, notifyError } from "../utils";
+import TooltipLabel from "./TooltipLabel";
import type { MainState, Option, ReactNode } from "../consts";
@@ -176,11 +176,19 @@ export default function Main({ evaluateHandlerRef }: { evaluateHandlerRef: Mutab
const [loading, setLoading] = useState(false);
- const handleCopy = useCallback(() => {
- const txt = ref.current.textContent?.trim();
- if (txt) copy(txt);
- else notifyError("請先進行操作,再匯出結果");
+ const [copyTooltipText, setCopyTooltipText] = useState("複製到剪貼簿");
+ const copyEvaluationResult = useCallback(async () => {
+ const content = ref.current.textContent?.trim();
+ if (content) {
+ try {
+ await navigator.clipboard.writeText(content);
+ setCopyTooltipText("成功複製到剪貼簿");
+ } catch {
+ setCopyTooltipText("無法複製到剪貼簿");
+ }
+ }
}, []);
+ const onHideTooltip = useCallback(() => setCopyTooltipText("複製到剪貼簿"), []);
// XXX Please Rewrite
useEffect(() => {
@@ -287,14 +295,20 @@ export default function Main({ evaluateHandlerRef }: { evaluateHandlerRef: Mutab
推導結果
-
-
-
-
+ {!loading && (
+ <>
+
+
+
+
+
+
+ >
+ )}
{evaluationResult}
diff --git a/src/Components/Tooltip.tsx b/src/Components/Tooltip.tsx
index 4e56caf..581e828 100644
--- a/src/Components/Tooltip.tsx
+++ b/src/Components/Tooltip.tsx
@@ -40,10 +40,6 @@ const root = createRoot(div);
let tooltipTarget: symbol | null = null;
-function hideTooltip() {
- div.style.visibility = "hidden";
-}
-
function TooltipAnchor({
relativeToNodeBox,
children,
@@ -80,10 +76,12 @@ export default function Tooltip({
element,
children,
fixedWidth = true,
+ onHideTooltip,
}: {
element: ReactElement;
children: ReactElement;
fixedWidth?: boolean;
+ onHideTooltip?: (() => void) | undefined;
}) {
const selfRef = useRef(Symbol("Tooltip"));
const boxRef = useRef(null);
@@ -109,14 +107,20 @@ export default function Tooltip({
renderTooltip();
}
}, [renderTooltip]);
+
+ const hideTooltip = useCallback(() => {
+ div.style.visibility = "hidden";
+ onHideTooltip?.();
+ }, [onHideTooltip]);
useEffect(
() => () => {
if (tooltipTarget === selfRef.current) {
hideTooltip();
}
},
- [],
+ [hideTooltip],
);
+
return cloneElement(children, {
onMouseEnter: showTooltip,
onTouchStart: showTooltip,
diff --git a/src/Components/TooltipLabel.tsx b/src/Components/TooltipLabel.tsx
index 359c6a6..1c10a58 100644
--- a/src/Components/TooltipLabel.tsx
+++ b/src/Components/TooltipLabel.tsx
@@ -22,9 +22,11 @@ const Option = styled.label`
export default function TooltipLabel({
description,
children,
+ onHideTooltip,
}: {
description?: string | undefined;
children: ReactNode;
+ onHideTooltip?: (() => void) | undefined;
}) {
return typeof description === "string" && description ? (
{line}
))}
- }>
+ }
+ onHideTooltip={onHideTooltip}>
) : (
diff --git a/src/utils.tsx b/src/utils.tsx
index 13488fe..81439ad 100644
--- a/src/utils.tsx
+++ b/src/utils.tsx
@@ -53,38 +53,6 @@ export function notifyError(msg: string, err?: unknown) {
return new Error(msg, err instanceof Error ? { cause: err } : {});
}
-export async function copy(txt: string) {
- if (
- await (async () => {
- try {
- await navigator.clipboard.writeText(txt);
- return true;
- } catch {
- const textArea = document.createElement("textarea");
- textArea.value = txt;
- textArea.style.position = "fixed";
- document.body.appendChild(textArea);
- textArea.focus();
- textArea.select();
- try {
- return document.execCommand("copy");
- } catch {
- return false;
- } finally {
- document.body.removeChild(textArea);
- }
- }
- })()
- )
- Swal.fire({
- icon: "success",
- title: "成功",
- text: "已成功匯出至剪貼簿",
- confirmButtonText: "確定",
- });
- else notifyError("瀏覽器不支援匯出至剪貼簿,操作失敗");
-}
-
export async function fetchFile(input: string) {
try {
const text = await (await fetch(input, { cache: "no-cache" })).text();