@fusion.dev/helix-graph 0.147.0 → 0.149.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.
package/README.md CHANGED
@@ -66,7 +66,8 @@ first.
66
66
  and the labels in the theme's face, and draws them again when the theme
67
67
  changes.
68
68
  - Answers the frame's pan and zoom buttons and keys, and tells the renderer which
69
- gestures the frame's `gestures` lets it take.
69
+ gestures the frame's `gestures` lets it take. Once the drawing has moved, it
70
+ calls the frame's `moved()`, and screen readers hear which way it went.
70
71
  - Draws only what changed when the page hands the frame new notes or links, and
71
72
  draws the frame's `signal()`s.
72
73
  - Fades out a snapshot drawn on the server by `graphSnapshot` once the renderer
@@ -397,3 +398,8 @@ Sigma 3 is a peer dependency.
397
398
  bun run --filter @fusion.dev/helix-graph build
398
399
  bun run --filter @fusion.dev/helix-graph test
399
400
  ```
401
+
402
+ The specs in `src/sigma/sigma-scene.dom.spec.ts` draw with Sigma in WebGL, so
403
+ they run in a browser only, with `bun run specs:browser graph`. They check that
404
+ the drawing frames and rings the note chosen in the list, and moves with the
405
+ frame's keys.
@@ -312,11 +312,13 @@ export const connectGraph = (frame, driver, options = {}) => {
312
312
  const inline = (to === 'end' ? 1 : to === 'start' ? -1 : 0) * (isRtl(frame) ? -1 : 1);
313
313
  const block = to === 'down' ? 1 : to === 'up' ? -1 : 0;
314
314
  driver.panBy(inline * width * by, block * height * by, !still());
315
+ graph.moved?.();
315
316
  };
316
317
  const handleZoom = (event) => {
317
318
  const { by } = event.detail;
318
319
  if (by !== 0) {
319
320
  driver.zoomBy(by > 0 ? 1 : -1, !still());
321
+ graph.moved?.();
320
322
  }
321
323
  };
322
324
  frame.addEventListener(HelixGraphPartChangeEvent.eventName, handlePartChange);
@@ -1,4 +1,4 @@
1
- import type { ViewportGestureMode } from '@fusion.dev/helix-core';
1
+ import { type ViewportGestureMode } from '@fusion.dev/helix-core';
2
2
  import type { GraphGroup } from '../connect-graph/connect-graph.js';
3
3
  import { type GraphLooks } from '../connect-graph/looks.js';
4
4
  /** Where a note is on screen and how large, or nothing where it is not drawn. */
@@ -48,14 +48,20 @@ export declare const chromeOver: (container: HTMLElement) => Box[];
48
48
  /** Whether a box overlaps none of `others`. */
49
49
  export declare const clearOf: (box: Box, others: readonly Box[]) => boolean;
50
50
  /**
51
- * Where a note's name starts, along the line: after the note, or before it
52
- * where after it would run under the frame's controls or off the drawing.
53
- * `box` gives the name's box for a start.
51
+ * Where a note's name starts, its left edge on screen: after the note in
52
+ * reading order, or before it where after it would run under the frame's
53
+ * controls or off the drawing. `box` gives the name's box for a start.
54
+ *
55
+ * **After is to the left on a right-to-left page**, `rightToLeft`, as a
56
+ * reader there looks for what follows a mark. The name is drawn from its left
57
+ * edge either way, with `textAlign` set to `left`: a canvas aligns text to its
58
+ * start by default, which on a right-to-left page is the right, so a name
59
+ * given its left edge was drawn ending there, over its own note.
54
60
  */
55
61
  export declare const nameStart: (at: {
56
62
  radius: number;
57
63
  x: number;
58
- }, width: number, box: (start: number) => Box, avoid: readonly Box[], room: number) => number;
64
+ }, width: number, box: (start: number) => Box, avoid: readonly Box[], room: number, rightToLeft?: boolean) => number;
59
65
  /** A note to name over the drawing: its name, and whether it is the one the reader is on. */
60
66
  export interface Named {
61
67
  id: string;
@@ -1,3 +1,4 @@
1
+ import { isRtl } from '@fusion.dev/helix-core';
1
2
  import { mixRgb, rgbOf } from '../connect-graph/looks.js';
2
3
  const SPEED = 340;
3
4
  const QUICKEST = 450;
@@ -288,8 +289,10 @@ export const clearOf = (box, others) => others.every((other) => box.right < othe
288
289
  box.left > other.right ||
289
290
  box.bottom < other.top ||
290
291
  box.top > other.bottom);
291
- export const nameStart = (at, width, box, avoid, room) => {
292
- const after = at.x + at.radius + 4;
292
+ export const nameStart = (at, width, box, avoid, room, rightToLeft = false) => {
293
+ const right = at.x + at.radius + 4;
294
+ const left = at.x - at.radius - 4 - width;
295
+ const [after, before] = rightToLeft ? [left, right] : [right, left];
293
296
  const fits = (start) => {
294
297
  const placed = box(start);
295
298
  return placed.left >= 0 && placed.right <= room && clearOf(placed, avoid);
@@ -297,7 +300,6 @@ export const nameStart = (at, width, box, avoid, room) => {
297
300
  if (fits(after)) {
298
301
  return after;
299
302
  }
300
- const before = at.x - at.radius - 4 - width;
301
303
  return fits(before) ? before : after;
302
304
  };
303
305
  export const namesOver = (container, onScreen) => {
@@ -311,6 +313,8 @@ export const namesOver = (container, onScreen) => {
311
313
  return;
312
314
  }
313
315
  taken = chromeOver(container);
316
+ const rightToLeft = isRtl(container);
317
+ context.textAlign = 'left';
314
318
  for (const { id, name, strong } of named) {
315
319
  const at = onScreen(id);
316
320
  if (!at || looks.node(id).hidden) {
@@ -325,7 +329,7 @@ export const namesOver = (container, onScreen) => {
325
329
  right: start + width + 2,
326
330
  top: y - 13,
327
331
  });
328
- const x = nameStart(at, width, boxAt, taken, container.clientWidth);
332
+ const x = nameStart(at, width, boxAt, taken, container.clientWidth, rightToLeft);
329
333
  const box = boxAt(x);
330
334
  if (!clearOf(box, taken) && !strong) {
331
335
  continue;
@@ -1,3 +1,4 @@
1
+ import { isRtl } from '@fusion.dev/helix-core';
1
2
  import { pixelsOf } from '../connect-graph/sizes.js';
2
3
  import { chromeOver, clearOf, ease, gesturesHeld, groupsOver, nameStart, signalsOver, } from '../overlay/overlay.js';
3
4
  import { EMPHASES, HelixNodeProgram, MATERIALS, SHAPES } from './node-program.js';
@@ -22,14 +23,14 @@ const SAMPLE = 250;
22
23
  const STILL = 0.004;
23
24
  const HELD = 2;
24
25
  const IDLE = 2000;
25
- const hoverOf = (looks) => (context, data, settings) => {
26
+ const hoverOf = (looks, rightToLeft) => (context, data, settings) => {
26
27
  if (!data.label) {
27
28
  return;
28
29
  }
29
30
  const size = settings.labelSize;
30
31
  context.font = `${settings.labelWeight} ${size}px ${settings.labelFont}`;
31
32
  const width = context.measureText(data.label).width;
32
- const x = data.x + data.size + 2;
33
+ const x = rightToLeft() ? data.x - data.size - 2 - width : data.x + data.size + 2;
33
34
  const height = size + 8;
34
35
  context.save();
35
36
  context.shadowBlur = 8;
@@ -40,6 +41,7 @@ const hoverOf = (looks) => (context, data, settings) => {
40
41
  context.fill();
41
42
  context.restore();
42
43
  context.fillStyle = looks.ink;
44
+ context.textAlign = 'left';
43
45
  context.fillText(data.label, x, data.y + size / 3);
44
46
  };
45
47
  const GROW = 300;
@@ -66,6 +68,7 @@ const labelOf = (looks, claim, start) => (context, data, settings) => {
66
68
  context.lineJoin = 'round';
67
69
  context.lineWidth = 3;
68
70
  context.strokeStyle = looks.ground;
71
+ context.textAlign = 'left';
69
72
  context.strokeText(data.label, x, y);
70
73
  context.fillStyle = strong ? looks.ink : looks.quiet;
71
74
  context.fillText(data.label, x, y);
@@ -121,8 +124,10 @@ export const sigmaDriver = (sigma, options = {}) => {
121
124
  }
122
125
  return free || must;
123
126
  };
127
+ let rightToLeft = false;
124
128
  const clear = () => {
125
129
  taken = chromeOver(container);
130
+ rightToLeft = isRtl(container);
126
131
  };
127
132
  sigma.on('beforeRender', clear);
128
133
  const held = gesturesHeld(container);
@@ -240,8 +245,8 @@ export const sigmaDriver = (sigma, options = {}) => {
240
245
  sigma.setSetting('labelFont', looks.font);
241
246
  sigma.setSetting('labelRenderedSizeThreshold', 0);
242
247
  sigma.setSetting('labelGridCellSize', naming === 'all' ? 8 : 130);
243
- sigma.setSetting('defaultDrawNodeLabel', labelOf(looks, claim, (at, width, box) => nameStart(at, width, box, taken, container.clientWidth)));
244
- sigma.setSetting('defaultDrawNodeHover', hoverOf(looks));
248
+ sigma.setSetting('defaultDrawNodeLabel', labelOf(looks, claim, (at, width, box) => nameStart(at, width, box, taken, container.clientWidth, rightToLeft)));
249
+ sigma.setSetting('defaultDrawNodeHover', hoverOf(looks, () => rightToLeft));
245
250
  sigma.setSetting('nodeReducer', (node, data) => {
246
251
  const look = looks.node(node);
247
252
  const grown = look.selected ? 1.5 : look.raised ? 1.25 : 1;
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.147.0",
4
+ "version": "0.149.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -55,12 +55,12 @@
55
55
  },
56
56
  "bugs": "https://github.com/project-fusion/fusion/issues",
57
57
  "dependencies": {
58
- "@fusion.dev/helix-core": "0.147.0"
58
+ "@fusion.dev/helix-core": "0.149.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@cosmos.gl/graph": "^3.4.2",
62
- "@fusion.dev/helix-headless": "0.147.0",
63
- "@fusion.dev/helix-testing": "0.147.0",
62
+ "@fusion.dev/helix-headless": "0.149.0",
63
+ "@fusion.dev/helix-testing": "0.149.0",
64
64
  "@types/bun": "^1.3.0",
65
65
  "@types/three": "^0.186.0",
66
66
  "d3-force-3d": "^3.0.6",