From 728ec815a3015b1e012ad015e4f746b289b39133 Mon Sep 17 00:00:00 2001 From: oliver Date: Sat, 1 Aug 2026 13:49:58 +0800 Subject: [PATCH] Polish topology toolbar search and restore sidebar icons. Group tools with display, drop the redundant locate button, clear find query after Enter, and restore corrupted edit/delete/collapse glyphs. Co-authored-by: Cursor --- web/src/index.css | 29 ++- web/src/pages/TopologyPage.tsx | 414 +++++++++++++++++---------------- 2 files changed, 233 insertions(+), 210 deletions(-) diff --git a/web/src/index.css b/web/src/index.css index 407facd..61c4609 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -2257,26 +2257,47 @@ pre { } .topo-toolbar__group + .topo-toolbar__group, -.topo-tools + .topo-toolbar__group, -.topo-toolbar__group + .topo-toolbar__display, -.topo-tools + .topo-toolbar__display { +.topo-toolbar__panel + .topo-toolbar__group, +.topo-toolbar__group + .topo-toolbar__group--find, +.topo-toolbar__panel + .topo-toolbar__group--find { margin-left: 2px; padding-left: 10px; border-left: 1px solid #e2e8f0; } +.topo-toolbar__panel { + display: inline-flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + padding: 4px 8px; + background: #f1f5f9; + border: 1px solid #e2e8f0; + border-radius: 10px; +} + +.topo-toolbar__panel .topo-tools { + background: #e2e8f0; +} + +.topo-toolbar__panel .topo-toolbar__display > summary { + background: #fff; +} + .topo-toolbar__group--find { position: relative; display: inline-flex; align-items: center; gap: 6px; min-width: 0; + margin-left: auto; } .topo-find-suggest { position: absolute; top: calc(100% + 4px); - left: 0; + right: 0; + left: auto; z-index: 30; width: min(320px, 70vw); max-height: 280px; diff --git a/web/src/pages/TopologyPage.tsx b/web/src/pages/TopologyPage.tsx index 574536f..7d674b0 100644 --- a/web/src/pages/TopologyPage.tsx +++ b/web/src/pages/TopologyPage.tsx @@ -461,6 +461,7 @@ export function TopologyPage() { const discoverAbortRef = useRef(null); const searchHitTimerRef = useRef(null); const findBoxRef = useRef(null); + const findJustLocatedRef = useRef(false); const [nodes, setNodes, onNodesChange] = useNodesState>([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const rfRef = useRef, Edge> | null>(null); @@ -1430,9 +1431,13 @@ export function TopologyPage() { useEffect(() => { const q = canvasQuery.trim(); if (!q) { - setSearchHitIds([]); setFindActiveIdx(0); setFindOpen(false); + if (findJustLocatedRef.current) { + findJustLocatedRef.current = false; + return; + } + setSearchHitIds([]); return; } setFindOpen(true); @@ -1452,7 +1457,10 @@ export function TopologyPage() { nodeId || canvasHits[Math.max(0, Math.min(findActiveIdx, canvasHits.length - 1))]?.id || canvasHits[0].id; + findJustLocatedRef.current = true; setFindOpen(false); + setCanvasQuery(""); + setFindActiveIdx(0); locateNode(id); }, [canvasQuery, canvasHits, findActiveIdx, locateNode, showError, t], @@ -1533,7 +1541,7 @@ export function TopologyPage() { name, ip: ne.ip_address || "", vendor: "ZTE", - meta: `${ne.ip_address || "-"} · ${ne.ne_type || "UME"}`, + meta: `${ne.ip_address || "-"} ? ${ne.ne_type || "UME"}`, connect_status: ne.connection_status || "", }; }); @@ -1839,109 +1847,136 @@ export function TopologyPage() {
-
- {( - [ - ["select", t("topology.toolSelect"), "V"], - ["pan", t("topology.toolPan"), "H"], - ["drag", t("topology.toolDrag"), "A"], - ["connect", t("topology.toolConnect"), "C"], - ] as const - ).map(([mode, label, key]) => ( - - ))} -
-
- setCanvasQuery(e.target.value)} - onFocus={() => { - if (canvasQuery.trim()) setFindOpen(true); - }} - onKeyDown={(e) => { - if (e.key === "ArrowDown") { - e.preventDefault(); - if (!canvasHits.length) return; - setFindOpen(true); - setFindActiveIdx((i) => (i + 1) % canvasHits.length); - return; - } - if (e.key === "ArrowUp") { - e.preventDefault(); - if (!canvasHits.length) return; - setFindOpen(true); - setFindActiveIdx((i) => (i - 1 + canvasHits.length) % canvasHits.length); - return; - } - if (e.key === "Escape") { - setFindOpen(false); - return; - } - if (e.key === "Enter") { - e.preventDefault(); - findOnCanvas(); - } - }} - placeholder={t("topology.findNodePh")} - disabled={!mapId || nodes.length === 0} - aria-label={t("topology.findNode")} - aria-autocomplete="list" - aria-expanded={findOpen} - /> - - {findOpen && canvasQuery.trim() ? ( -
- {canvasHits.length === 0 ? ( -
{t("topology.findNoMatch")}
- ) : ( - canvasHits.slice(0, 12).map((n, idx) => ( - + ))} +
+
+ {t("topology.display")} +
+ + + + + + +
+
+ {t("topology.edgeDefaults")} + - )) - )} - {canvasHits.length > 12 ? ( -
- {t("topology.findMore").replace("{{count}}", String(canvasHits.length - 12))}
- ) : null} + {( + [ + ["manual", t("topology.edgeManual")], + ["discovered", t("topology.edgeDiscovered")], + ["stale", t("topology.edgeStale")], + ] as const + ).map(([kind, label]) => { + const d = edgeDefaults[kind]; + return ( +
+ {label} + updateEdgeDefault(kind, { stroke_color: e.target.value })} + /> + + +
+ ); + })} +
- ) : null} +
-
- {t("topology.display")} -
- - - - - - -
-
- {t("topology.edgeDefaults")} - -
- {( - [ - ["manual", t("topology.edgeManual")], - ["discovered", t("topology.edgeDiscovered")], - ["stale", t("topology.edgeStale")], - ] as const - ).map(([kind, label]) => { - const d = edgeDefaults[kind]; - return ( -
- {label} - updateEdgeDefault(kind, { stroke_color: e.target.value })} - /> - - -
- ); - })} +
+ setCanvasQuery(e.target.value)} + onFocus={() => { + if (canvasQuery.trim()) setFindOpen(true); + }} + onKeyDown={(e) => { + if (e.key === "ArrowDown") { + e.preventDefault(); + if (!canvasHits.length) return; + setFindOpen(true); + setFindActiveIdx((i) => (i + 1) % canvasHits.length); + return; + } + if (e.key === "ArrowUp") { + e.preventDefault(); + if (!canvasHits.length) return; + setFindOpen(true); + setFindActiveIdx((i) => (i - 1 + canvasHits.length) % canvasHits.length); + return; + } + if (e.key === "Escape") { + setFindOpen(false); + return; + } + if (e.key === "Enter") { + e.preventDefault(); + findOnCanvas(); + } + }} + placeholder={t("topology.findNodePh")} + disabled={!mapId || nodes.length === 0} + aria-label={t("topology.findNode")} + aria-autocomplete="list" + aria-expanded={findOpen} + /> + {findOpen && canvasQuery.trim() ? ( +
+ {canvasHits.length === 0 ? ( +
{t("topology.findNoMatch")}
+ ) : ( + canvasHits.slice(0, 12).map((n, idx) => ( + + )) + )} + {canvasHits.length > 12 ? ( +
+ {t("topology.findMore").replace("{{count}}", String(canvasHits.length - 12))} +
+ ) : null}
-
-
+ ) : null} +