From 6427b830ea9c9092ba281574c4dd8cf6a37e6e95 Mon Sep 17 00:00:00 2001 From: sagitchu Date: Tue, 10 Mar 2026 17:19:21 +0800 Subject: [PATCH] refactor(node): improve card metadata popup with hover trigger Entire-Checkpoint: 7a033056acc0 --- vite-frontend/.dockerignore | 18 +++++ vite-frontend/src/pages/node.tsx | 133 +++++++++++++++++++------------ 2 files changed, 99 insertions(+), 52 deletions(-) create mode 100644 vite-frontend/.dockerignore diff --git a/vite-frontend/.dockerignore b/vite-frontend/.dockerignore new file mode 100644 index 0000000..bcfac75 --- /dev/null +++ b/vite-frontend/.dockerignore @@ -0,0 +1,18 @@ +node_modules +dist + +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +.DS_Store +.vscode +.idea + +.env.local +.env.*.local + +coverage +*.tsbuildinfo diff --git a/vite-frontend/src/pages/node.tsx b/vite-frontend/src/pages/node.tsx index 187373e..e0e56cc 100644 --- a/vite-frontend/src/pages/node.tsx +++ b/vite-frontend/src/pages/node.tsx @@ -278,7 +278,7 @@ const SortableItem = ({ ref={setNodeRef} style={style} {...attributes} - className="overflow-hidden h-full" + className="overflow-visible h-full" {...listeners} > {children(listeners)} @@ -377,6 +377,30 @@ export default function NodePage() { const [upgradeProgress, setUpgradeProgress] = useState< Record >({}); + const [infoPopoverPlacement, setInfoPopoverPlacement] = useState< + Record + >({}); + + const updateInfoPopoverPlacement = useCallback( + (nodeId: number, triggerElement: HTMLElement | null) => { + if (!triggerElement) { + return; + } + + const rect = triggerElement.getBoundingClientRect(); + const estimatedPanelWidth = 288; + const viewportPadding = 24; + const nextPlacement: "left" | "bottom" = + rect.left >= estimatedPanelWidth + viewportPadding ? "left" : "bottom"; + + setInfoPopoverPlacement((prev) => + prev[nodeId] === nextPlacement + ? prev + : { ...prev, [nodeId]: nextPlacement }, + ); + }, + [], + ); const handleNodeOffline = useCallback((nodeId: number) => { setNodeList((prev) => @@ -1654,20 +1678,19 @@ export default function NodePage() { node.connectionStatus, ); const hasRemark = Boolean(node.remark?.trim()); - const metaCount = - 1 + - (isRemoteNode ? 1 : 0) + - (node.expiryTime && node.expiryTime > 0 && node.renewalCycle - ? 1 - : 0) + - (hasRemark ? 1 : 0); + const hasExpiryInfo = Boolean( + node.expiryTime && node.expiryTime > 0 && node.renewalCycle, + ); + const hasInfoTrigger = hasRemark || hasExpiryInfo; + const infoCount = Number(hasExpiryInfo) + Number(hasRemark); + const infoPlacement = infoPopoverPlacement[node.id] ?? "left"; return ( {(listeners) => (
@@ -1707,67 +1730,72 @@ export default function NodePage() {
-
- - 信息 - - {metaCount} - + {hasInfoTrigger && ( +
+
-
+ {hasRemark && ( + + )} + +
-
-
- 标签 -
-
- {isRemoteNode && ( + {hasExpiryInfo && ( +
+
+ 到期提醒 +
+
- 远程 + {expiryMeta.label} - )} - - {connectionStatusMeta.text} - - {node.expiryTime && - node.expiryTime > 0 && - node.renewalCycle && ( - - {expiryMeta.label} - - )} +
+
+ {formatNodeRenewalTime(expiryMeta.nextDueTime)} +
-
+ )} {hasRemark && (
@@ -1784,7 +1812,8 @@ export default function NodePage() { )}
-
+
+ )}