From 42a8c78d6321742a2169b6c10270aa15b186c2b6 Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 31 Oct 2026 22:52:58 +0100 Subject: dashboard: double-click pins and unpins --- src/api/dashboard/dashboard.html | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'src/api/dashboard/dashboard.html') diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html index ccf4892..5854f61 100644 --- a/src/api/dashboard/dashboard.html +++ b/src/api/dashboard/dashboard.html @@ -169,7 +169,7 @@
drag to pan, wheel or pinch to zoom, turn with two fingers; - drag a node to pin it, double-click it to let go + drag a node to move it, double-click it to pin it there or let it go
@@ -496,7 +496,7 @@ function relax(nodes, pairs, width, height, gain, most, only) { } let moved = 0; for (const id of nodes) { - if (pinned.has(id) || (only && !only.has(id))) continue; + if (pinned.has(id) || id === grabbed || (only && !only.has(id))) continue; const p = positions.get(id), f = force.get(id); const fx = f.x * gain, fy = f.y * gain; const x = Math.min(width - 40, Math.max(40, p.x + Math.max(-most, Math.min(most, fx)))); @@ -807,7 +807,7 @@ function renderGraph() { (dormant ? " (dormant)" : "") + (clock && clock.off ? " (clock " + seconds(clock.offsetMs) + " off the swarm's, ±" + clock.withinMs + " ms)" : "")); - // Pinned (dragged there): a dot in the middle. + // Pinned (double-clicked): a dot in the middle. const pin = pinned.has(id) ? [drawing("pin " + id, "circle", { cx: p.x, cy: p.y, r: 2, fill: member.self || reported ? "var(--panel)" : "var(--fg)", "pointer-events": "none" })] : []; @@ -1308,12 +1308,14 @@ graph.addEventListener("pointerup", event => { const click = press && !press.moved ? press.click : null; press = null; graph.classList.remove("dragging"); + dropped(); if (click) click(); }); graph.addEventListener("pointercancel", event => { untouched(event); press = null; graph.classList.remove("dragging"); + dropped(); }); // The map dragged: it pans; a click, `click`. function pan(event, click) { @@ -1331,34 +1333,47 @@ graph.addEventListener("pointerdown", event => { }); // Not the browser's drag and drop of the picture. graph.addEventListener("dragstart", event => event.preventDefault()); -// A node: dragged, it stays where it is put (`pinned`), and the layout places the others -// around it; a click, it is in focus; a second one soon after, it's let go (`unpin`). -// Told apart here: the map holds the pointer while pressed (`pressing`), so the -// browser's clicks and double-clicks go to the map, not the node. +// A node: dragged, it moves, held where the pointer is (`grabbed`) until let go; a click, +// it is in focus; a second one soon after pins it where it is (`pinned`: the layout +// leaves it there), or lets it go if pinned (`unpin`). Told apart here: the map holds +// the pointer while pressed (`pressing`), so the browser's clicks and double-clicks +// go to the map, not the node. let lastNodeClick = { id: null, at: 0 }; const DOUBLE_CLICK_MS = 400; +let grabbed = null; function drag(event, id) { const p = positions.get(id), from = { x: p.x, y: p.y }; + grabbed = id; pressing(event, (dx, dy) => { // The pointer's move, on the map (unturned and unscaled). const [x, y] = turned(dx, dy, -view.turn); p.x = from.x + x / view.k; p.y = from.y + y / view.k; - pinned.add(id); renderGraph(); - // Resized by round trip: the others follow it at once. + // Dynamic: the others follow it at once. if (byRtt) settle(); }, () => { const now = performance.now(); if (lastNodeClick.id === id && now - lastNodeClick.at < DOUBLE_CLICK_MS) { lastNodeClick = { id: null, at: now }; - unpin(id); + if (pinned.has(id)) unpin(id); else pin(id); } else { lastNodeClick = { id, at: now }; focus(id); } }); } +// A dragged node let go of: dynamic, it moves to its place among the others again. +function dropped() { + if (grabbed === null) return; + grabbed = null; + if (byRtt) settle(); +} +// A node pinned where it is: the layout leaves it there. +function pin(id) { + pinned.add(id); + renderGraph(); +} // A node let go: it moves to its place among the others, smoothly (`settle`); on a // static map, only it moves. function unpin(id) { -- cgit v1.3.1