@krainovsd/graph 0.14.0-beta.3 → 0.14.0-beta.4

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 CHANGED
@@ -2098,6 +2098,19 @@ function initDraw() {
2098
2098
  nodeRenders.forEach((render) => render());
2099
2099
  textRenders.forEach((render) => render());
2100
2100
  this.context.restore();
2101
+ /** selection rectangle */
2102
+ if (this.isSelecting && this.selectionRect) {
2103
+ const rect = this.selectionRect;
2104
+ const screenX = Math.min(rect.x1, rect.x2) * this.areaTransform.k + this.areaTransform.x;
2105
+ const screenY = Math.min(rect.y1, rect.y2) * this.areaTransform.k + this.areaTransform.y;
2106
+ const screenW = Math.abs(rect.x2 - rect.x1) * this.areaTransform.k;
2107
+ const screenH = Math.abs(rect.y2 - rect.y1) * this.areaTransform.k;
2108
+ this.context.fillStyle = "rgba(66, 133, 244, 0.2)";
2109
+ this.context.fillRect(screenX, screenY, screenW, screenH);
2110
+ this.context.strokeStyle = "rgba(66, 133, 244, 0.8)";
2111
+ this.context.lineWidth = 2;
2112
+ this.context.strokeRect(screenX, screenY, screenW, screenH);
2113
+ }
2101
2114
  this.listeners.onDrawFinished?.call?.(this);
2102
2115
  }
2103
2116
  if (this.graphSettings.showDrawTime) {
@@ -2110,7 +2123,7 @@ function initPointer() {
2110
2123
  if (!this.area || !this.nodes)
2111
2124
  throw new Error("bad init data");
2112
2125
  function onHover(event) {
2113
- if (!this.area)
2126
+ if (!this.area || this.isSelecting)
2114
2127
  return;
2115
2128
  let currentNode;
2116
2129
  let currentLink;
@@ -2322,6 +2335,156 @@ function initResize() {
2322
2335
  observer.observe(this.area);
2323
2336
  }
2324
2337
 
2338
+ function initSelection() {
2339
+ if (!this.area || !this.areaRect)
2340
+ throw new Error("bad init data");
2341
+ this.isSelecting = false;
2342
+ this.selectionRect = null;
2343
+ let selectedNodesSet = new Set();
2344
+ let selectedLinksSet = new Set();
2345
+ let localSelection = false;
2346
+ function onMouseDown(event) {
2347
+ if (event.button !== 0 || !event.shiftKey)
2348
+ return;
2349
+ if (!this.areaRect || !this.area)
2350
+ return;
2351
+ event.stopPropagation();
2352
+ event.preventDefault();
2353
+ const [startX, startY] = pointerGetter(event, this.areaRect, this.areaTransform);
2354
+ this.isSelecting = true;
2355
+ localSelection = true;
2356
+ this.selectionRect = { x1: startX, y1: startY, x2: startX, y2: startY };
2357
+ selectedNodesSet = new Set();
2358
+ selectedLinksSet = new Set();
2359
+ const controller = new AbortController();
2360
+ this.area.addEventListener("mousemove", onMouseMove.bind(this), {
2361
+ signal: controller.signal,
2362
+ });
2363
+ this.area.addEventListener("mouseup", () => {
2364
+ onMouseUp.call(this, controller);
2365
+ }, {
2366
+ signal: controller.signal,
2367
+ });
2368
+ window.addEventListener("keyup", (event) => {
2369
+ if (event.key === "Shift") {
2370
+ onMouseUp.call(this, controller);
2371
+ }
2372
+ }, { signal: controller.signal });
2373
+ }
2374
+ function onMouseMove(event) {
2375
+ if (!this.isSelecting || !this.selectionRect || !this.areaRect)
2376
+ return;
2377
+ event.stopPropagation();
2378
+ const [x, y] = pointerGetter(event, this.areaRect, this.areaTransform);
2379
+ this.selectionRect.x2 = x;
2380
+ this.selectionRect.y2 = y;
2381
+ const rect = normalizeRect(this.selectionRect);
2382
+ const newNodes = new Set();
2383
+ for (let i = 0; i < this.nodes.length; i++) {
2384
+ const node = this.nodes[i];
2385
+ if (node.x == undefined || node.y == undefined)
2386
+ continue;
2387
+ if (node.visible === false)
2388
+ continue;
2389
+ const cache = this.nodeOptionsCache[i];
2390
+ if (isNodeInRect(node, rect, cache)) {
2391
+ newNodes.add(node);
2392
+ }
2393
+ }
2394
+ const newLinks = new Set();
2395
+ for (const link of this.links) {
2396
+ const source = link.source;
2397
+ const target = link.target;
2398
+ if (newNodes.has(source) && newNodes.has(target)) {
2399
+ newLinks.add(link);
2400
+ }
2401
+ }
2402
+ for (const node of selectedNodesSet) {
2403
+ if (!newNodes.has(node)) {
2404
+ node._selected = false;
2405
+ this.listeners.onSelectionOut?.call(this, node, undefined);
2406
+ }
2407
+ }
2408
+ for (const link of selectedLinksSet) {
2409
+ if (!newLinks.has(link)) {
2410
+ link._selected = false;
2411
+ this.listeners.onSelectionOut?.call(this, undefined, link);
2412
+ }
2413
+ }
2414
+ for (const node of newNodes) {
2415
+ if (!selectedNodesSet.has(node)) {
2416
+ node._selected = true;
2417
+ this.listeners.onSelectionIn?.call(this, node, undefined);
2418
+ }
2419
+ }
2420
+ for (const link of newLinks) {
2421
+ if (!selectedLinksSet.has(link)) {
2422
+ link._selected = true;
2423
+ this.listeners.onSelectionIn?.call(this, undefined, link);
2424
+ }
2425
+ }
2426
+ selectedNodesSet = newNodes;
2427
+ selectedLinksSet = newLinks;
2428
+ this.draw();
2429
+ }
2430
+ function onMouseUp(controller) {
2431
+ controller.abort();
2432
+ if (!this.isSelecting)
2433
+ return;
2434
+ const nodes = Array.from(selectedNodesSet);
2435
+ const links = Array.from(selectedLinksSet);
2436
+ for (const node of nodes) {
2437
+ node._selected = false;
2438
+ }
2439
+ for (const link of links) {
2440
+ link._selected = false;
2441
+ }
2442
+ this.isSelecting = false;
2443
+ this.selectionRect = null;
2444
+ selectedNodesSet = new Set();
2445
+ selectedLinksSet = new Set();
2446
+ this.listeners.onSelectionEnd?.call(this, nodes, links);
2447
+ this.tick();
2448
+ }
2449
+ function onSelectionEnd(event) {
2450
+ if (localSelection) {
2451
+ event.stopImmediatePropagation();
2452
+ localSelection = false;
2453
+ }
2454
+ }
2455
+ this.area.addEventListener("mousedown", onMouseDown.bind(this), {
2456
+ signal: this.eventAbortController.signal,
2457
+ });
2458
+ this.area.addEventListener("click", onSelectionEnd, { signal: this.eventAbortController.signal });
2459
+ }
2460
+ function normalizeRect(rect) {
2461
+ const x = Math.min(rect.x1, rect.x2);
2462
+ const y = Math.min(rect.y1, rect.y2);
2463
+ const width = Math.abs(rect.x2 - rect.x1);
2464
+ const height = Math.abs(rect.y2 - rect.y1);
2465
+ return { x, y, width, height };
2466
+ }
2467
+ function isNodeInRect(node, rect, cache) {
2468
+ const nx = node.x;
2469
+ const ny = node.y;
2470
+ if (nx == undefined || ny == undefined)
2471
+ return false;
2472
+ if (cache?.shape === "circle") {
2473
+ const r = cache.radius ?? 0;
2474
+ const closestX = Math.max(rect.x, Math.min(nx, rect.x + rect.width));
2475
+ const closestY = Math.max(rect.y, Math.min(ny, rect.y + rect.height));
2476
+ const dx = nx - closestX;
2477
+ const dy = ny - closestY;
2478
+ return dx * dx + dy * dy <= r * r;
2479
+ }
2480
+ const hw = (cache?.width ?? 0) / 2;
2481
+ const hh = (cache?.height ?? 0) / 2;
2482
+ return (nx - hw >= rect.x &&
2483
+ nx + hw <= rect.x + rect.width &&
2484
+ ny - hh >= rect.y &&
2485
+ ny + hh <= rect.y + rect.height);
2486
+ }
2487
+
2325
2488
  function updateLinkCache() {
2326
2489
  if (!this.context)
2327
2490
  return;
@@ -2574,6 +2737,9 @@ function initZoom(currentZoom) {
2574
2737
  this._translateExtent = translateExtent;
2575
2738
  const zoomInstance = d3Zoom.zoom()
2576
2739
  .scaleExtent(zoomExtent)
2740
+ .filter((event) => {
2741
+ return !(event instanceof MouseEvent && event.shiftKey);
2742
+ })
2577
2743
  .on("zoom", (event) => {
2578
2744
  if (this._zoomAnimating)
2579
2745
  return;
@@ -2816,6 +2982,8 @@ class GraphCanvas {
2816
2982
  _lastNodeZoomK;
2817
2983
  _lastLinkZoomK;
2818
2984
  _zoomAnimating = false;
2985
+ isSelecting = false;
2986
+ selectionRect = null;
2819
2987
  get simulationWorking() {
2820
2988
  const simulationAlpha = this.simulation?.alpha?.() ?? 0;
2821
2989
  const simulationAlphaMin = this.simulation?.alphaMin?.() ?? 0;
@@ -3099,6 +3267,8 @@ class GraphCanvas {
3099
3267
  this.highlightProgress = 0;
3100
3268
  this.highlightStart = null;
3101
3269
  this.highlightPositive = false;
3270
+ this.isSelecting = false;
3271
+ this.selectionRect = null;
3102
3272
  };
3103
3273
  init = () => {
3104
3274
  initArea.call(this);
@@ -3109,6 +3279,7 @@ class GraphCanvas {
3109
3279
  initDnd.call(this);
3110
3280
  initZoom.call(this);
3111
3281
  initResize.call(this);
3282
+ initSelection.call(this);
3112
3283
  initPointer.call(this);
3113
3284
  updateNodeCache.call(this);
3114
3285
  updateLinkCache.call(this);