@krainovsd/graph 0.14.0-beta.3 → 0.14.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.cjs +258 -66
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/esm/module/GraphCanvas/GraphCanvas.js +31 -2
- package/lib/esm/module/GraphCanvas/GraphCanvas.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/link-by-pointer-getter.js +1 -5
- package/lib/esm/module/GraphCanvas/lib/utils/link-by-pointer-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/node-by-pointer-getter.js +1 -5
- package/lib/esm/module/GraphCanvas/lib/utils/node-by-pointer-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/lib/utils/pointer-getter.js +16 -8
- package/lib/esm/module/GraphCanvas/lib/utils/pointer-getter.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-area.js +0 -1
- package/lib/esm/module/GraphCanvas/slices/init-area.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-dnd.js +4 -14
- package/lib/esm/module/GraphCanvas/slices/init-dnd.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-draw.js +13 -0
- package/lib/esm/module/GraphCanvas/slices/init-draw.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js +39 -38
- package/lib/esm/module/GraphCanvas/slices/init-pointer.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-resize.js +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-resize.js.map +1 -1
- package/lib/esm/module/GraphCanvas/slices/init-selection.js +152 -0
- package/lib/esm/module/GraphCanvas/slices/init-selection.js.map +1 -0
- package/lib/esm/module/GraphCanvas/slices/init-zoom.js +3 -0
- package/lib/esm/module/GraphCanvas/slices/init-zoom.js.map +1 -1
- package/lib/index.d.ts +21 -10
- package/package.json +1 -1
package/lib/cjs/index.cjs
CHANGED
|
@@ -315,14 +315,22 @@ function computeGraphBounds(nodes) {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
function pointerGetter(mouseEvent, areaRect, areaTransform) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
318
|
+
let localX;
|
|
319
|
+
let localY;
|
|
320
|
+
if ("offsetX" in mouseEvent) {
|
|
321
|
+
localX = mouseEvent.offsetX;
|
|
322
|
+
localY = mouseEvent.offsetY;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
if (!areaRect)
|
|
326
|
+
return [0, 0];
|
|
327
|
+
const clientX = mouseEvent.touches[0]?.clientX ?? mouseEvent.changedTouches[0]?.clientX;
|
|
328
|
+
const clientY = mouseEvent.touches[0]?.clientY ?? mouseEvent.changedTouches[0]?.clientY;
|
|
329
|
+
localX = clientX - areaRect.left;
|
|
330
|
+
localY = clientY - areaRect.top;
|
|
331
|
+
}
|
|
332
|
+
const px = (localX - areaTransform.x) / areaTransform.k;
|
|
333
|
+
const py = (localY - areaTransform.y) / areaTransform.k;
|
|
326
334
|
return [px, py];
|
|
327
335
|
}
|
|
328
336
|
|
|
@@ -422,10 +430,7 @@ function linkIterationExtractor(link, i, links, state, option, optionConstantGet
|
|
|
422
430
|
return customOptions;
|
|
423
431
|
}
|
|
424
432
|
|
|
425
|
-
function nodeByPointerGetter({
|
|
426
|
-
if (!areaRect)
|
|
427
|
-
return undefined;
|
|
428
|
-
const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);
|
|
433
|
+
function nodeByPointerGetter({ pointerX, pointerY, nodes, }) {
|
|
429
434
|
return d3Array.greatest(nodes, (node) => {
|
|
430
435
|
if (isOverlapsNode(node, pointerX, pointerY, undefined))
|
|
431
436
|
return node.index;
|
|
@@ -825,10 +830,7 @@ function approximateQuadraticBezierLength(x0, y0, x1, y1, x2, y2) {
|
|
|
825
830
|
return (a + b + c) / 2;
|
|
826
831
|
}
|
|
827
832
|
|
|
828
|
-
function linkByPointerGetter({
|
|
829
|
-
if (!areaRect)
|
|
830
|
-
return undefined;
|
|
831
|
-
const [pointerX, pointerY] = pointerGetter(mouseEvent, areaRect, areaTransform);
|
|
833
|
+
function linkByPointerGetter({ pointerX, pointerY, links, linkHoverExtraZone, curve, }) {
|
|
832
834
|
return d3Array.greatest(links, (link) => {
|
|
833
835
|
if (!jsHelpers.isObject(link.source) ||
|
|
834
836
|
!jsHelpers.isObject(link.target) ||
|
|
@@ -1316,7 +1318,6 @@ function initArea() {
|
|
|
1316
1318
|
if (!this.area)
|
|
1317
1319
|
throw new Error("couldn't create canvas");
|
|
1318
1320
|
this.container.appendChild(this.area);
|
|
1319
|
-
this.areaRect = this.area.getBoundingClientRect();
|
|
1320
1321
|
this.context = this.area.getContext("2d");
|
|
1321
1322
|
if (!this.context)
|
|
1322
1323
|
throw new Error("couldn't create canvas context");
|
|
@@ -1332,10 +1333,8 @@ function initDnd() {
|
|
|
1332
1333
|
if (this.listeners.onDragSubject) {
|
|
1333
1334
|
return this.listeners.onDragSubject.call(this, event);
|
|
1334
1335
|
}
|
|
1335
|
-
if (!this.areaRect)
|
|
1336
|
-
return;
|
|
1337
1336
|
const mouseEvent = event.sourceEvent;
|
|
1338
|
-
const [pointerX, pointerY] =
|
|
1337
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(mouseEvent);
|
|
1339
1338
|
return d3Array.greatest(this.nodes, (node) => {
|
|
1340
1339
|
if (!node.x || !node.y || (jsHelpers.isBoolean(node.drag) && !node.drag))
|
|
1341
1340
|
return undefined;
|
|
@@ -1351,10 +1350,8 @@ function initDnd() {
|
|
|
1351
1350
|
if (this.simulation)
|
|
1352
1351
|
this.simulation.alphaTarget(0.3).restart();
|
|
1353
1352
|
}
|
|
1354
|
-
if (!this.areaRect)
|
|
1355
|
-
return;
|
|
1356
1353
|
const mouseEvent = event.sourceEvent;
|
|
1357
|
-
const [pointerX, pointerY] =
|
|
1354
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(mouseEvent);
|
|
1358
1355
|
if (this._translateExtent) {
|
|
1359
1356
|
event.subject.fx = Math.max(this._translateExtent[0][0], Math.min(this._translateExtent[1][0], pointerX));
|
|
1360
1357
|
event.subject.fy = Math.max(this._translateExtent[0][1], Math.min(this._translateExtent[1][1], pointerY));
|
|
@@ -1370,8 +1367,8 @@ function initDnd() {
|
|
|
1370
1367
|
if (!event.active && this.simulation)
|
|
1371
1368
|
this.simulation.alphaTarget(0);
|
|
1372
1369
|
const sourceEvent = event.sourceEvent;
|
|
1373
|
-
if (sourceEvent.altKey
|
|
1374
|
-
const [pointerX, pointerY] =
|
|
1370
|
+
if (sourceEvent.altKey) {
|
|
1371
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(sourceEvent);
|
|
1375
1372
|
if (this._translateExtent) {
|
|
1376
1373
|
event.subject.fx = Math.max(this._translateExtent[0][0], Math.min(this._translateExtent[1][0], pointerX));
|
|
1377
1374
|
event.subject.fy = Math.max(this._translateExtent[0][1], Math.min(this._translateExtent[1][1], pointerY));
|
|
@@ -2098,6 +2095,19 @@ function initDraw() {
|
|
|
2098
2095
|
nodeRenders.forEach((render) => render());
|
|
2099
2096
|
textRenders.forEach((render) => render());
|
|
2100
2097
|
this.context.restore();
|
|
2098
|
+
/** selection rectangle */
|
|
2099
|
+
if (this.isSelecting && this.selectionRect) {
|
|
2100
|
+
const rect = this.selectionRect;
|
|
2101
|
+
const screenX = Math.min(rect.x1, rect.x2) * this.areaTransform.k + this.areaTransform.x;
|
|
2102
|
+
const screenY = Math.min(rect.y1, rect.y2) * this.areaTransform.k + this.areaTransform.y;
|
|
2103
|
+
const screenW = Math.abs(rect.x2 - rect.x1) * this.areaTransform.k;
|
|
2104
|
+
const screenH = Math.abs(rect.y2 - rect.y1) * this.areaTransform.k;
|
|
2105
|
+
this.context.fillStyle = "rgba(66, 133, 244, 0.2)";
|
|
2106
|
+
this.context.fillRect(screenX, screenY, screenW, screenH);
|
|
2107
|
+
this.context.strokeStyle = "rgba(66, 133, 244, 0.8)";
|
|
2108
|
+
this.context.lineWidth = 2;
|
|
2109
|
+
this.context.strokeRect(screenX, screenY, screenW, screenH);
|
|
2110
|
+
}
|
|
2101
2111
|
this.listeners.onDrawFinished?.call?.(this);
|
|
2102
2112
|
}
|
|
2103
2113
|
if (this.graphSettings.showDrawTime) {
|
|
@@ -2110,7 +2120,7 @@ function initPointer() {
|
|
|
2110
2120
|
if (!this.area || !this.nodes)
|
|
2111
2121
|
throw new Error("bad init data");
|
|
2112
2122
|
function onHover(event) {
|
|
2113
|
-
if (!this.area)
|
|
2123
|
+
if (!this.area || this.isSelecting)
|
|
2114
2124
|
return;
|
|
2115
2125
|
let currentNode;
|
|
2116
2126
|
let currentLink;
|
|
@@ -2119,10 +2129,10 @@ function initPointer() {
|
|
|
2119
2129
|
let highlightNode = true;
|
|
2120
2130
|
let highlightLink = true;
|
|
2121
2131
|
if (checkHighlightNode) {
|
|
2132
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2122
2133
|
currentNode = nodeByPointerGetter({
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
mouseEvent: event,
|
|
2134
|
+
pointerX,
|
|
2135
|
+
pointerY,
|
|
2126
2136
|
nodes: this.nodes,
|
|
2127
2137
|
});
|
|
2128
2138
|
if (currentNode?.highlight != undefined)
|
|
@@ -2132,11 +2142,11 @@ function initPointer() {
|
|
|
2132
2142
|
this.area.style.cursor = "pointer";
|
|
2133
2143
|
}
|
|
2134
2144
|
else if (checkHighlightLink) {
|
|
2145
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2135
2146
|
currentLink = linkByPointerGetter({
|
|
2147
|
+
pointerX,
|
|
2148
|
+
pointerY,
|
|
2136
2149
|
linkHoverExtraZone: this.highlightSettings.linkHoverExtraZone,
|
|
2137
|
-
areaRect: this.areaRect,
|
|
2138
|
-
areaTransform: this.areaTransform,
|
|
2139
|
-
mouseEvent: event,
|
|
2140
2150
|
links: this.links,
|
|
2141
2151
|
curve: this.linkSettings.curve,
|
|
2142
2152
|
});
|
|
@@ -2155,19 +2165,20 @@ function initPointer() {
|
|
|
2155
2165
|
this.animateHighlight(highlightNode ? currentNode : undefined, highlightLink ? currentLink : undefined);
|
|
2156
2166
|
if (!this.listeners.onMove)
|
|
2157
2167
|
return;
|
|
2158
|
-
if (!currentNode && !checkHighlightNode)
|
|
2168
|
+
if (!currentNode && !checkHighlightNode) {
|
|
2169
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2159
2170
|
currentNode = nodeByPointerGetter({
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
mouseEvent: event,
|
|
2171
|
+
pointerX,
|
|
2172
|
+
pointerY,
|
|
2163
2173
|
nodes: this.nodes,
|
|
2164
2174
|
});
|
|
2175
|
+
}
|
|
2165
2176
|
if (!currentNode && (!checkHighlightNode || (!checkHighlightLink && !currentLink))) {
|
|
2177
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2166
2178
|
currentLink = linkByPointerGetter({
|
|
2179
|
+
pointerX,
|
|
2180
|
+
pointerY,
|
|
2167
2181
|
linkHoverExtraZone: this.highlightSettings.linkHoverExtraZone,
|
|
2168
|
-
areaRect: this.areaRect,
|
|
2169
|
-
areaTransform: this.areaTransform,
|
|
2170
|
-
mouseEvent: event,
|
|
2171
2182
|
links: this.links,
|
|
2172
2183
|
curve: this.linkSettings.curve,
|
|
2173
2184
|
});
|
|
@@ -2181,18 +2192,18 @@ function initPointer() {
|
|
|
2181
2192
|
!("button" in event) ||
|
|
2182
2193
|
event.button !== 1)
|
|
2183
2194
|
return;
|
|
2195
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2184
2196
|
const currentNode = nodeByPointerGetter({
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
mouseEvent: event,
|
|
2197
|
+
pointerX,
|
|
2198
|
+
pointerY,
|
|
2188
2199
|
nodes: this.nodes,
|
|
2189
2200
|
});
|
|
2190
2201
|
if (!currentNode) {
|
|
2202
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2191
2203
|
const currentLink = linkByPointerGetter({
|
|
2204
|
+
pointerX,
|
|
2205
|
+
pointerY,
|
|
2192
2206
|
linkHoverExtraZone: this.highlightSettings.linkHoverExtraZone,
|
|
2193
|
-
areaRect: this.areaRect,
|
|
2194
|
-
areaTransform: this.areaTransform,
|
|
2195
|
-
mouseEvent: event,
|
|
2196
2207
|
links: this.links,
|
|
2197
2208
|
curve: this.linkSettings.curve,
|
|
2198
2209
|
});
|
|
@@ -2203,18 +2214,18 @@ function initPointer() {
|
|
|
2203
2214
|
function onRightClick(event) {
|
|
2204
2215
|
if (!this.listeners.onContextMenu)
|
|
2205
2216
|
return;
|
|
2217
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2206
2218
|
const currentNode = nodeByPointerGetter({
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
mouseEvent: event,
|
|
2219
|
+
pointerX,
|
|
2220
|
+
pointerY,
|
|
2210
2221
|
nodes: this.nodes,
|
|
2211
2222
|
});
|
|
2212
2223
|
if (!currentNode) {
|
|
2224
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2213
2225
|
const currentLink = linkByPointerGetter({
|
|
2226
|
+
pointerX,
|
|
2227
|
+
pointerY,
|
|
2214
2228
|
linkHoverExtraZone: this.highlightSettings.linkHoverExtraZone,
|
|
2215
|
-
areaRect: this.areaRect,
|
|
2216
|
-
areaTransform: this.areaTransform,
|
|
2217
|
-
mouseEvent: event,
|
|
2218
2229
|
links: this.links,
|
|
2219
2230
|
curve: this.linkSettings.curve,
|
|
2220
2231
|
});
|
|
@@ -2225,18 +2236,18 @@ function initPointer() {
|
|
|
2225
2236
|
function onDoubleClick(event) {
|
|
2226
2237
|
if (!this.listeners.onDoubleClick)
|
|
2227
2238
|
return;
|
|
2239
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2228
2240
|
const currentNode = nodeByPointerGetter({
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
mouseEvent: event,
|
|
2241
|
+
pointerX,
|
|
2242
|
+
pointerY,
|
|
2232
2243
|
nodes: this.nodes,
|
|
2233
2244
|
});
|
|
2234
2245
|
if (!currentNode) {
|
|
2246
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2235
2247
|
const currentLink = linkByPointerGetter({
|
|
2248
|
+
pointerX,
|
|
2249
|
+
pointerY,
|
|
2236
2250
|
linkHoverExtraZone: this.highlightSettings.linkHoverExtraZone,
|
|
2237
|
-
areaRect: this.areaRect,
|
|
2238
|
-
areaTransform: this.areaTransform,
|
|
2239
|
-
mouseEvent: event,
|
|
2240
2251
|
links: this.links,
|
|
2241
2252
|
curve: this.linkSettings.curve,
|
|
2242
2253
|
});
|
|
@@ -2247,18 +2258,18 @@ function initPointer() {
|
|
|
2247
2258
|
function onClick(event) {
|
|
2248
2259
|
if (this.isDragging || !this.listeners.onClick || ("button" in event && event.button !== 0))
|
|
2249
2260
|
return;
|
|
2261
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2250
2262
|
const currentNode = nodeByPointerGetter({
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
mouseEvent: event,
|
|
2263
|
+
pointerX,
|
|
2264
|
+
pointerY,
|
|
2254
2265
|
nodes: this.nodes,
|
|
2255
2266
|
});
|
|
2256
2267
|
if (!currentNode) {
|
|
2268
|
+
const [pointerX, pointerY] = this.getPointerAreaPosition(event);
|
|
2257
2269
|
const currentLink = linkByPointerGetter({
|
|
2270
|
+
pointerX,
|
|
2271
|
+
pointerY,
|
|
2258
2272
|
linkHoverExtraZone: this.highlightSettings.linkHoverExtraZone,
|
|
2259
|
-
areaRect: this.areaRect,
|
|
2260
|
-
areaTransform: this.areaTransform,
|
|
2261
|
-
mouseEvent: event,
|
|
2262
2273
|
links: this.links,
|
|
2263
2274
|
curve: this.linkSettings.curve,
|
|
2264
2275
|
});
|
|
@@ -2317,11 +2328,161 @@ function initResize() {
|
|
|
2317
2328
|
});
|
|
2318
2329
|
document.addEventListener("visibilitychange", () => {
|
|
2319
2330
|
if (!document.hidden)
|
|
2320
|
-
this.
|
|
2331
|
+
this.updateSize();
|
|
2321
2332
|
}, { signal: abortController.signal });
|
|
2322
2333
|
observer.observe(this.area);
|
|
2323
2334
|
}
|
|
2324
2335
|
|
|
2336
|
+
function initSelection() {
|
|
2337
|
+
if (!this.area)
|
|
2338
|
+
throw new Error("bad init data");
|
|
2339
|
+
this.isSelecting = false;
|
|
2340
|
+
this.selectionRect = null;
|
|
2341
|
+
let selectedNodesSet = new Set();
|
|
2342
|
+
let selectedLinksSet = new Set();
|
|
2343
|
+
let localSelection = false;
|
|
2344
|
+
function onMouseDown(event) {
|
|
2345
|
+
if (event.button !== 0 || !event.shiftKey)
|
|
2346
|
+
return;
|
|
2347
|
+
if (!this.area)
|
|
2348
|
+
return;
|
|
2349
|
+
event.stopPropagation();
|
|
2350
|
+
event.preventDefault();
|
|
2351
|
+
const [startX, startY] = this.getPointerAreaPosition(event);
|
|
2352
|
+
this.isSelecting = true;
|
|
2353
|
+
localSelection = true;
|
|
2354
|
+
this.selectionRect = { x1: startX, y1: startY, x2: startX, y2: startY };
|
|
2355
|
+
selectedNodesSet = new Set();
|
|
2356
|
+
selectedLinksSet = new Set();
|
|
2357
|
+
const controller = new AbortController();
|
|
2358
|
+
this.area.addEventListener("mousemove", onMouseMove.bind(this), {
|
|
2359
|
+
signal: controller.signal,
|
|
2360
|
+
});
|
|
2361
|
+
this.area.addEventListener("mouseup", () => {
|
|
2362
|
+
onMouseUp.call(this, controller);
|
|
2363
|
+
}, {
|
|
2364
|
+
signal: controller.signal,
|
|
2365
|
+
});
|
|
2366
|
+
window.addEventListener("keyup", (event) => {
|
|
2367
|
+
if (event.key === "Shift") {
|
|
2368
|
+
onMouseUp.call(this, controller);
|
|
2369
|
+
}
|
|
2370
|
+
}, { signal: controller.signal });
|
|
2371
|
+
}
|
|
2372
|
+
function onMouseMove(event) {
|
|
2373
|
+
if (!this.isSelecting || !this.selectionRect)
|
|
2374
|
+
return;
|
|
2375
|
+
event.stopPropagation();
|
|
2376
|
+
const [x, y] = this.getPointerAreaPosition(event);
|
|
2377
|
+
this.selectionRect.x2 = x;
|
|
2378
|
+
this.selectionRect.y2 = y;
|
|
2379
|
+
const rect = normalizeRect(this.selectionRect);
|
|
2380
|
+
const newNodes = new Set();
|
|
2381
|
+
for (let i = 0; i < this.nodes.length; i++) {
|
|
2382
|
+
const node = this.nodes[i];
|
|
2383
|
+
if (node.x == undefined || node.y == undefined)
|
|
2384
|
+
continue;
|
|
2385
|
+
if (node.visible === false)
|
|
2386
|
+
continue;
|
|
2387
|
+
const cache = this.nodeOptionsCache[i];
|
|
2388
|
+
if (isNodeInRect(node, rect, cache)) {
|
|
2389
|
+
newNodes.add(node);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
const newLinks = new Set();
|
|
2393
|
+
for (const link of this.links) {
|
|
2394
|
+
const source = link.source;
|
|
2395
|
+
const target = link.target;
|
|
2396
|
+
if (newNodes.has(source) && newNodes.has(target)) {
|
|
2397
|
+
newLinks.add(link);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
for (const node of selectedNodesSet) {
|
|
2401
|
+
if (!newNodes.has(node)) {
|
|
2402
|
+
node._selected = false;
|
|
2403
|
+
this.listeners.onSelectionOut?.call(this, node, undefined);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
for (const link of selectedLinksSet) {
|
|
2407
|
+
if (!newLinks.has(link)) {
|
|
2408
|
+
link._selected = false;
|
|
2409
|
+
this.listeners.onSelectionOut?.call(this, undefined, link);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
for (const node of newNodes) {
|
|
2413
|
+
if (!selectedNodesSet.has(node)) {
|
|
2414
|
+
node._selected = true;
|
|
2415
|
+
this.listeners.onSelectionIn?.call(this, node, undefined);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
for (const link of newLinks) {
|
|
2419
|
+
if (!selectedLinksSet.has(link)) {
|
|
2420
|
+
link._selected = true;
|
|
2421
|
+
this.listeners.onSelectionIn?.call(this, undefined, link);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
selectedNodesSet = newNodes;
|
|
2425
|
+
selectedLinksSet = newLinks;
|
|
2426
|
+
this.draw();
|
|
2427
|
+
}
|
|
2428
|
+
function onMouseUp(controller) {
|
|
2429
|
+
controller.abort();
|
|
2430
|
+
if (!this.isSelecting)
|
|
2431
|
+
return;
|
|
2432
|
+
const nodes = Array.from(selectedNodesSet);
|
|
2433
|
+
const links = Array.from(selectedLinksSet);
|
|
2434
|
+
for (const node of nodes) {
|
|
2435
|
+
node._selected = false;
|
|
2436
|
+
}
|
|
2437
|
+
for (const link of links) {
|
|
2438
|
+
link._selected = false;
|
|
2439
|
+
}
|
|
2440
|
+
this.isSelecting = false;
|
|
2441
|
+
this.selectionRect = null;
|
|
2442
|
+
selectedNodesSet = new Set();
|
|
2443
|
+
selectedLinksSet = new Set();
|
|
2444
|
+
this.listeners.onSelectionEnd?.call(this, nodes, links);
|
|
2445
|
+
this.tick();
|
|
2446
|
+
}
|
|
2447
|
+
function onSelectionEnd(event) {
|
|
2448
|
+
if (localSelection) {
|
|
2449
|
+
event.stopImmediatePropagation();
|
|
2450
|
+
localSelection = false;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
this.area.addEventListener("mousedown", onMouseDown.bind(this), {
|
|
2454
|
+
signal: this.eventAbortController.signal,
|
|
2455
|
+
});
|
|
2456
|
+
this.area.addEventListener("click", onSelectionEnd, { signal: this.eventAbortController.signal });
|
|
2457
|
+
}
|
|
2458
|
+
function normalizeRect(rect) {
|
|
2459
|
+
const x = Math.min(rect.x1, rect.x2);
|
|
2460
|
+
const y = Math.min(rect.y1, rect.y2);
|
|
2461
|
+
const width = Math.abs(rect.x2 - rect.x1);
|
|
2462
|
+
const height = Math.abs(rect.y2 - rect.y1);
|
|
2463
|
+
return { x, y, width, height };
|
|
2464
|
+
}
|
|
2465
|
+
function isNodeInRect(node, rect, cache) {
|
|
2466
|
+
const nx = node.x;
|
|
2467
|
+
const ny = node.y;
|
|
2468
|
+
if (nx == undefined || ny == undefined)
|
|
2469
|
+
return false;
|
|
2470
|
+
if (cache?.shape === "circle") {
|
|
2471
|
+
const r = cache.radius ?? 0;
|
|
2472
|
+
const closestX = Math.max(rect.x, Math.min(nx, rect.x + rect.width));
|
|
2473
|
+
const closestY = Math.max(rect.y, Math.min(ny, rect.y + rect.height));
|
|
2474
|
+
const dx = nx - closestX;
|
|
2475
|
+
const dy = ny - closestY;
|
|
2476
|
+
return dx * dx + dy * dy <= r * r;
|
|
2477
|
+
}
|
|
2478
|
+
const hw = (cache?.width ?? 0) / 2;
|
|
2479
|
+
const hh = (cache?.height ?? 0) / 2;
|
|
2480
|
+
return (nx - hw >= rect.x &&
|
|
2481
|
+
nx + hw <= rect.x + rect.width &&
|
|
2482
|
+
ny - hh >= rect.y &&
|
|
2483
|
+
ny + hh <= rect.y + rect.height);
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2325
2486
|
function updateLinkCache() {
|
|
2326
2487
|
if (!this.context)
|
|
2327
2488
|
return;
|
|
@@ -2574,6 +2735,9 @@ function initZoom(currentZoom) {
|
|
|
2574
2735
|
this._translateExtent = translateExtent;
|
|
2575
2736
|
const zoomInstance = d3Zoom.zoom()
|
|
2576
2737
|
.scaleExtent(zoomExtent)
|
|
2738
|
+
.filter((event) => {
|
|
2739
|
+
return !(event instanceof MouseEvent && event.shiftKey);
|
|
2740
|
+
})
|
|
2577
2741
|
.on("zoom", (event) => {
|
|
2578
2742
|
if (this._zoomAnimating)
|
|
2579
2743
|
return;
|
|
@@ -2798,7 +2962,6 @@ class GraphCanvas {
|
|
|
2798
2962
|
simulation;
|
|
2799
2963
|
areaTransform = d3Zoom.zoomIdentity;
|
|
2800
2964
|
_translateExtent;
|
|
2801
|
-
areaRect;
|
|
2802
2965
|
draw;
|
|
2803
2966
|
eventAbortController;
|
|
2804
2967
|
cachedNodeText = [];
|
|
@@ -2816,6 +2979,12 @@ class GraphCanvas {
|
|
|
2816
2979
|
_lastNodeZoomK;
|
|
2817
2980
|
_lastLinkZoomK;
|
|
2818
2981
|
_zoomAnimating = false;
|
|
2982
|
+
isSelecting = false;
|
|
2983
|
+
selectionRect = null;
|
|
2984
|
+
areaRect;
|
|
2985
|
+
// protected get areaRect() {
|
|
2986
|
+
// return this.area?.getBoundingClientRect();
|
|
2987
|
+
// }
|
|
2819
2988
|
get simulationWorking() {
|
|
2820
2989
|
const simulationAlpha = this.simulation?.alpha?.() ?? 0;
|
|
2821
2990
|
const simulationAlphaMin = this.simulation?.alphaMin?.() ?? 0;
|
|
@@ -2973,7 +3142,7 @@ class GraphCanvas {
|
|
|
2973
3142
|
this.height = height;
|
|
2974
3143
|
this.area.width = this.dpi * this.width;
|
|
2975
3144
|
this.area.height = this.dpi * this.height;
|
|
2976
|
-
this.
|
|
3145
|
+
this.updateRect();
|
|
2977
3146
|
this.context = this.area.getContext("2d");
|
|
2978
3147
|
if (!this.context)
|
|
2979
3148
|
throw new Error("couldn't create canvas context");
|
|
@@ -3077,6 +3246,26 @@ class GraphCanvas {
|
|
|
3077
3246
|
this.clearState();
|
|
3078
3247
|
this.clearCache(true);
|
|
3079
3248
|
};
|
|
3249
|
+
getPointerAreaPosition = (event) => {
|
|
3250
|
+
let localX;
|
|
3251
|
+
let localY;
|
|
3252
|
+
if ("offsetX" in event) {
|
|
3253
|
+
localX = event.offsetX;
|
|
3254
|
+
localY = event.offsetY;
|
|
3255
|
+
}
|
|
3256
|
+
else {
|
|
3257
|
+
const rect = this.areaRect;
|
|
3258
|
+
if (!rect)
|
|
3259
|
+
return [0, 0];
|
|
3260
|
+
const clientX = event.touches[0]?.clientX ?? event.changedTouches[0]?.clientX;
|
|
3261
|
+
const clientY = event.touches[0]?.clientY ?? event.changedTouches[0]?.clientY;
|
|
3262
|
+
localX = clientX - rect.left;
|
|
3263
|
+
localY = clientY - rect.top;
|
|
3264
|
+
}
|
|
3265
|
+
const px = (localX - this.areaTransform.x) / this.areaTransform.k;
|
|
3266
|
+
const py = (localY - this.areaTransform.y) / this.areaTransform.k;
|
|
3267
|
+
return [px, py];
|
|
3268
|
+
};
|
|
3080
3269
|
clearHTMLElements = () => {
|
|
3081
3270
|
this.root.replaceChildren();
|
|
3082
3271
|
this.area = undefined;
|
|
@@ -3099,6 +3288,8 @@ class GraphCanvas {
|
|
|
3099
3288
|
this.highlightProgress = 0;
|
|
3100
3289
|
this.highlightStart = null;
|
|
3101
3290
|
this.highlightPositive = false;
|
|
3291
|
+
this.isSelecting = false;
|
|
3292
|
+
this.selectionRect = null;
|
|
3102
3293
|
};
|
|
3103
3294
|
init = () => {
|
|
3104
3295
|
initArea.call(this);
|
|
@@ -3109,6 +3300,7 @@ class GraphCanvas {
|
|
|
3109
3300
|
initDnd.call(this);
|
|
3110
3301
|
initZoom.call(this);
|
|
3111
3302
|
initResize.call(this);
|
|
3303
|
+
initSelection.call(this);
|
|
3112
3304
|
initPointer.call(this);
|
|
3113
3305
|
updateNodeCache.call(this);
|
|
3114
3306
|
updateLinkCache.call(this);
|