@fusion.dev/helix-graph 0.198.0 → 0.199.0

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.
@@ -69,10 +69,13 @@ export interface GraphDriver {
69
69
  /**
70
70
  * Runs a layout over the drawn graph, moving the notes in `unplaced` and
71
71
  * holding the rest, and calls `placed` once it has settled with where every
72
- * note ended. Returns what stops it early. Without one, notes stay where the
73
- * connector started them.
72
+ * note ended. Returns what stops it early: called with `true`, it ends the
73
+ * layout where the notes stand and calls `placed` with that, as the frame's
74
+ * stop control and the page's pause do; with nothing, it ends it unheard, as
75
+ * a new drawing does. Without one, notes stay where the connector started
76
+ * them.
74
77
  */
75
- settle?(unplaced: ReadonlySet<string>, placed: (positions: ReadonlyMap<string, GraphPoint>) => void): () => void;
78
+ settle?(unplaced: ReadonlySet<string>, placed: (positions: ReadonlyMap<string, GraphPoint>) => void): (keep?: boolean) => void;
76
79
  /**
77
80
  * Which gestures the renderer takes from the page, as the frame's
78
81
  * `gestures` decides. `free` is what the renderer did before it was told
@@ -1,4 +1,4 @@
1
- import { HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, isRtl, reportRuntime, watchTheme, } from '@fusion.dev/helix-core';
1
+ import { HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, isRtl, motionPaused, onMotionChange, reportRuntime, watchTheme, } from '@fusion.dev/helix-core';
2
2
  import { arrangeByGroup, hubsOf } from './arrange.js';
3
3
  import { diffGraph, isEmpty, linkKey, } from './diff.js';
4
4
  import { graphColorsFrom, looksOf, toneColor, } from './looks.js';
@@ -36,6 +36,11 @@ export const connectGraph = (frame, driver, options = {}) => {
36
36
  let colors = null;
37
37
  let current;
38
38
  let stopSettling;
39
+ const stopPaused = onMotionChange((paused) => {
40
+ if (paused) {
41
+ stopSettling?.(true);
42
+ }
43
+ });
39
44
  let pressed;
40
45
  let pending = 0;
41
46
  let connected = true;
@@ -129,6 +134,7 @@ export const connectGraph = (frame, driver, options = {}) => {
129
134
  if (stopSettling) {
130
135
  stopSettling();
131
136
  stopSettling = undefined;
137
+ graph.settling?.();
132
138
  }
133
139
  cancelAnimationFrame(pending);
134
140
  const nodes = graph.nodes ?? [];
@@ -218,8 +224,12 @@ export const connectGraph = (frame, driver, options = {}) => {
218
224
  first = false;
219
225
  if (unplaced.size > 0 && driver.settle) {
220
226
  waiting = unplaced;
227
+ const unseen = still() || motionPaused();
228
+ let finished = false;
221
229
  stopSettling = driver.settle(unplaced, (settled) => {
230
+ finished = true;
222
231
  stopSettling = undefined;
232
+ graph.settling?.();
223
233
  if (!connected) {
224
234
  return;
225
235
  }
@@ -237,6 +247,13 @@ export const connectGraph = (frame, driver, options = {}) => {
237
247
  ready();
238
248
  }
239
249
  });
250
+ const stop = stopSettling;
251
+ if (finished) {
252
+ stopSettling = undefined;
253
+ }
254
+ else if (stop) {
255
+ graph.settling?.(() => stop(true), unseen);
256
+ }
240
257
  return;
241
258
  }
242
259
  waiting = new Set();
@@ -371,6 +388,8 @@ export const connectGraph = (frame, driver, options = {}) => {
371
388
  connected = false;
372
389
  cancelAnimationFrame(pending);
373
390
  stopSettling?.();
391
+ graph.settling?.();
392
+ stopPaused();
374
393
  stopPress?.();
375
394
  stopTheme();
376
395
  frame.removeEventListener(HelixGraphPartChangeEvent.eventName, handlePartChange);
@@ -425,7 +425,11 @@ export const cosmosDriver = (graph, container, options = {}) => {
425
425
  graph.start(1);
426
426
  const limit = setTimeout(finish, options.layoutLimit ?? 15000);
427
427
  const fitting = setInterval(() => fitAll(FOLLOW), FOLLOW);
428
- return () => {
428
+ return (keep = false) => {
429
+ if (keep && !done) {
430
+ finish();
431
+ return;
432
+ }
429
433
  done = true;
430
434
  clearTimeout(limit);
431
435
  clearInterval(fitting);
@@ -455,10 +455,14 @@ export const sigmaDriver = (sigma, options = {}) => {
455
455
  finish();
456
456
  }
457
457
  }, SAMPLE);
458
- return () => {
458
+ return (keep = false) => {
459
459
  if (done) {
460
460
  return;
461
461
  }
462
+ if (keep) {
463
+ finish();
464
+ return;
465
+ }
462
466
  done = true;
463
467
  clearInterval(timer);
464
468
  layout.stop();
@@ -1,4 +1,4 @@
1
- import { pageBudget, reportRuntime } from '@fusion.dev/helix-core';
1
+ import { motionPaused, pageBudget, reportRuntime } from '@fusion.dev/helix-core';
2
2
  import { AdditiveBlending, BoxGeometry, BufferAttribute, BufferGeometry, DoubleSide, Euler, InstancedBufferAttribute, InstancedMesh, Matrix4, Mesh, NormalBlending, OctahedronGeometry, PerspectiveCamera, Points, Quaternion, Raycaster, Scene, ShaderMaterial, TOUCH, Vector2, Vector3, WebGLRenderer, } from 'three';
3
3
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
4
4
  import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
@@ -306,7 +306,7 @@ export const threeDriver = (container, options = {}) => {
306
306
  controls.enableDamping = !still();
307
307
  controls.dampingFactor = 0.08;
308
308
  controls.rotateSpeed = 0.6;
309
- controls.autoRotate = !still() && options.autoRotate !== false;
309
+ controls.autoRotate = !still() && !motionPaused() && options.autoRotate !== false;
310
310
  controls.autoRotateSpeed = 0.5;
311
311
  let ids = [];
312
312
  let index = new Map();
@@ -1047,6 +1047,7 @@ export const threeDriver = (container, options = {}) => {
1047
1047
  }
1048
1048
  controls.autoRotate =
1049
1049
  !still() &&
1050
+ !motionPaused() &&
1050
1051
  options.autoRotate !== false &&
1051
1052
  !touched &&
1052
1053
  hovered === undefined &&
@@ -1359,7 +1360,17 @@ export const threeDriver = (container, options = {}) => {
1359
1360
  placed(new Map(nodes.map((node) => [node.id, { x: node.x, y: node.y, z: node.z }])));
1360
1361
  }
1361
1362
  });
1362
- return () => {
1363
+ return (keep = false) => {
1364
+ if (keep && !done) {
1365
+ if (layout) {
1366
+ layout.done();
1367
+ }
1368
+ else {
1369
+ done = true;
1370
+ placed(new Map(nodes.map((node) => [node.id, { x: node.x, y: node.y, z: node.z }])));
1371
+ }
1372
+ return;
1373
+ }
1363
1374
  done = true;
1364
1375
  layout = undefined;
1365
1376
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-graph",
3
3
  "description": "Connects the Helix Graph frame to a graph renderer: Sigma.js, cosmos.gl, three.js, or a driver of your own.",
4
- "version": "0.198.0",
4
+ "version": "0.199.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -59,12 +59,12 @@
59
59
  },
60
60
  "bugs": "https://github.com/project-fusion/fusion/issues",
61
61
  "dependencies": {
62
- "@fusion.dev/helix-core": "0.198.0"
62
+ "@fusion.dev/helix-core": "0.199.0"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@cosmos.gl/graph": "^3.4.2",
66
- "@fusion.dev/helix-headless": "0.198.0",
67
- "@fusion.dev/helix-testing": "0.198.0",
66
+ "@fusion.dev/helix-headless": "0.199.0",
67
+ "@fusion.dev/helix-testing": "0.199.0",
68
68
  "@types/bun": "^1.4.2",
69
69
  "@types/three": "^0.186.0",
70
70
  "d3-force-3d": "^3.0.6",