From 3653fe2fa2031ef3bb1e5023796553288c7b422e Mon Sep 17 00:00:00 2001 From: ShirayukiRin Date: Mon, 10 Feb 2025 00:23:48 +0800 Subject: [PATCH] add a drag-to-select feature on the graph page --- dist/workers/graph.js | 56 +++++++++++++++++++++++++++++ src/ts/component/graph/provider.tsx | 54 ++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/dist/workers/graph.js b/dist/workers/graph.js index c4d6d1aef2..bb54021409 100644 --- a/dist/workers/graph.js +++ b/dist/workers/graph.js @@ -85,6 +85,8 @@ let paused = false; let isOver = ''; let maxDegree = 0; let clusters = {}; +let dragToSelectStartCoord = {}; +let dragToSelectCursorCoord = {}; addEventListener('message', ({ data }) => { if (this[data.id]) { @@ -355,6 +357,10 @@ draw = (t) => { }; }); + if(dragToSelectStartCoord.x && dragToSelectStartCoord.y && dragToSelectCursorCoord.x && dragToSelectStartCoord.y) { + drawDragToSelectBox(); + } + ctx.restore(); }; @@ -577,11 +583,61 @@ drawNode = (d) => { }; }; +drawDragToSelectBox = () => { + const width = dragToSelectCursorCoord.x - dragToSelectStartCoord.x; + const height = dragToSelectCursorCoord.y - dragToSelectStartCoord.y; + util.roundedRect(dragToSelectStartCoord.x, dragToSelectStartCoord.y, width, height, 1); + + ctx.strokeStyle = data.colors.selected; + ctx.lineWidth = getLineWidth() * 3; + ctx.stroke(); + + ctx.restore(); +} + onZoom = (data) => { transform = Object.assign(transform, data.transform); redraw(); }; +onDragToSelectStart = (data) => { + let {x, y} = data; + + x = transform.invertX(x); + y = transform.invertY(y); + dragToSelectStartCoord = {x: x, y: y}; + + redraw(); +} + +onDragToSelectMove = (data) => { + let {x, y} = data; + + x = transform.invertX(x); + y = transform.invertY(y); + dragToSelectCursorCoord = {x: x, y: y}; + + const left = Math.min(dragToSelectStartCoord.x, x); + const top = Math.min(dragToSelectStartCoord.y, y); + const right = Math.max(dragToSelectStartCoord.x, x); + const bottom = Math.max(dragToSelectStartCoord.y, y); + + nodes.forEach((d) => { + if (d.x >= left && d.x <= right && d.y >= top && d.y <= bottom) { + send('onSelect', { node: d.id }) + }; + }); + + redraw(); +} + +onDragToSelectEnd = (data) => { + dragToSelectStartCoord = {}; + dragToSelectCursorCoord = {}; + send('onTransform', { ...transform }); + redraw(); +} + onDragStart = ({ active }) => { if (!active) { restart(0.3); diff --git a/src/ts/component/graph/provider.tsx b/src/ts/component/graph/provider.tsx index 84c34c476d..8d55acbf3c 100644 --- a/src/ts/component/graph/provider.tsx +++ b/src/ts/component/graph/provider.tsx @@ -42,6 +42,7 @@ const Graph = observer(forwardRef(({ const isPreviewDisabled = useRef(false); const ids = useRef([]); const zoom = useRef(null); + const isDraggingToSelect = useRef(false); const send = (id: string, param: any, transfer?: any[]) => { if (worker.current) { @@ -73,7 +74,10 @@ const Graph = observer(forwardRef(({ const settings = S.Common.getGraph(storageKey); images.current = {}; - zoom.current = d3.zoom().scaleExtent([ 0.05, 10 ]).on('zoom', e => onZoom(e)); + zoom.current = d3.zoom().scaleExtent([ 0.05, 10 ]) + .on('start', e => onZoomStart(e)) + .on('zoom', e => onZoom(e)) + .on('end', e => onZoomEnd(e)); edges.current = (data.edges || []).map(edgeMapper); nodes.current = (data.nodes || []).map(nodeMapper); @@ -227,8 +231,33 @@ const Graph = observer(forwardRef(({ send('onDragEnd', { active: e.active }); }; - const onZoom = ({ transform }) => { - send('onZoom', { transform }); + const onZoomStart = ({ sourceEvent }) => { + if (sourceEvent && sourceEvent.type === 'mousedown' && sourceEvent.shiftKey) { + const p = d3.pointer(sourceEvent, d3.select(canvas.current)); + const node = $(nodeRef.current); + const { left, top } = node.offset(); + isDraggingToSelect.current = true; + send('onDragToSelectStart', { x: p[0] - left, y: p[1] - top }); + }; + }; + + const onZoom = ({ transform, sourceEvent }) => { + if(isDraggingToSelect.current && sourceEvent) { + const p = d3.pointer(sourceEvent, d3.select(canvas.current)); + const node = $(nodeRef.current); + const { left, top } = node.offset(); + + send('onDragToSelectMove', { x: p[0] - left, y: p[1] - top }); + } else { + send('onZoom', { transform }); + }; + }; + + const onZoomEnd = (e: any) => { + if(isDraggingToSelect.current){ + send('onDragToSelectEnd', {}); + }; + isDraggingToSelect.current = false; }; const onPreviewShow = (data: any) => { @@ -469,14 +498,19 @@ const Graph = observer(forwardRef(({ ret = ret.concat(related); }; - ret.forEach(id => { - if (isSelected) { - ids.current = ids.current.filter(it => it != id); - return; - }; + if(isDraggingToSelect.current){ + if(isSelected) return; + ids.current = ids.current.concat([id]); + } else { + ret.forEach(id => { + if (isSelected) { + ids.current = ids.current.filter(it => it != id); + return; + }; - ids.current = ids.current.includes(id) ? ids.current.filter(it => it != id) : ids.current.concat([ id ]); - }); + ids.current = ids.current.includes(id) ? ids.current.filter(it => it != id) : ids.current.concat([ id ]); + }); + }; setSelected(ids.current); };