@krainovsd/graph 0.14.0-beta.2 → 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;
@@ -2900,16 +3068,16 @@ class GraphCanvas {
2900
3068
  }
2901
3069
  this.animateZoom(area, target, this.areaTransform, duration);
2902
3070
  };
2903
- changeData = (options, alpha = 0.5, clearCache = true) => {
3071
+ changeData = (options, alpha = 0.5, clearCache = true, precompute = false) => {
2904
3072
  if (options.links != undefined)
2905
3073
  this.links = options.links;
2906
3074
  if (options.nodes != undefined)
2907
3075
  this.nodes = options.nodes;
2908
3076
  if (options.nodes != undefined || options.links != undefined) {
2909
- this.updateData(alpha, clearCache);
3077
+ this.updateData(alpha, clearCache, precompute);
2910
3078
  }
2911
3079
  };
2912
- changeSettings = (options, clearCache = true) => {
3080
+ changeSettings = (options, clearCache = true, precompute = false) => {
2913
3081
  if (options.graphSettings) {
2914
3082
  this.graphSettings = graphSettingsGetter(options.graphSettings, this.graphSettings);
2915
3083
  this.draw = initDraw.call(this);
@@ -2956,7 +3124,7 @@ class GraphCanvas {
2956
3124
  initCollideForce.call(this, true);
2957
3125
  }
2958
3126
  if (options.forceSettings) {
2959
- return void this.updateSimulation();
3127
+ return void this.updateSimulation(precompute);
2960
3128
  }
2961
3129
  this.tick();
2962
3130
  };
@@ -3085,10 +3253,10 @@ class GraphCanvas {
3085
3253
  this.eventAbortController.abort();
3086
3254
  this.eventAbortController = new AbortController();
3087
3255
  };
3088
- updateSimulation = () => {
3256
+ updateSimulation = (precompute = false) => {
3089
3257
  if (this.simulation) {
3090
3258
  initSimulationForces.call(this);
3091
- this.restart(1);
3259
+ this.restart(1, { precompute });
3092
3260
  }
3093
3261
  };
3094
3262
  clearState = () => {
@@ -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,12 +3279,13 @@ 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);
3115
3286
  this.tick();
3116
3287
  };
3117
- updateData = (alpha = 0.5, clearCache = true) => {
3288
+ updateData = (alpha = 0.5, clearCache = true, precompute = false) => {
3118
3289
  if (clearCache) {
3119
3290
  this.clearCache(clearCache);
3120
3291
  }
@@ -3131,7 +3302,7 @@ class GraphCanvas {
3131
3302
  .iterations(this.forceSettings.forces && this.forceSettings.linkForce
3132
3303
  ? this.forceSettings.linkIterations
3133
3304
  : 0));
3134
- this.restart(alpha);
3305
+ this.restart(alpha, { precompute });
3135
3306
  initZoom.call(this, this.areaTransform);
3136
3307
  }
3137
3308
  };