@hpcc-js/graph 3.9.0 → 3.9.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hpcc-js/graph",
3
- "version": "3.9.0",
3
+ "version": "3.9.1",
4
4
  "description": "hpcc-js - Viz Graph",
5
5
  "type": "module",
6
6
  "main": "./dist/index.umd.cjs",
@@ -40,18 +40,18 @@
40
40
  "update-major": "npx --yes npm-check-updates -u"
41
41
  },
42
42
  "dependencies": {
43
- "@hpcc-js/api": "^3.5.5",
44
- "@hpcc-js/common": "^3.9.0",
45
- "@hpcc-js/html": "^3.4.5",
46
- "@hpcc-js/react": "^3.5.5",
47
- "@hpcc-js/util": "^3.6.5"
43
+ "@hpcc-js/api": "^3.5.6",
44
+ "@hpcc-js/common": "^3.9.1",
45
+ "@hpcc-js/html": "^3.4.6",
46
+ "@hpcc-js/react": "^3.5.6",
47
+ "@hpcc-js/util": "^3.6.6"
48
48
  },
49
49
  "optionalPeerDependencies": {
50
50
  "react": ">=17.0.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@hpcc-js/esbuild-plugins": "^1.9.5",
54
- "@hpcc-js/wasm-graphviz": "1.26.0",
53
+ "@hpcc-js/esbuild-plugins": "^1.9.6",
54
+ "@hpcc-js/wasm-graphviz": "1.27.3",
55
55
  "@types/d3-transition": "1.3.6",
56
56
  "@types/dagre": "0.7.54",
57
57
  "d3-force": "^1",
@@ -76,5 +76,5 @@
76
76
  "url": "https://github.com/hpcc-systems/Visualization/issues"
77
77
  },
78
78
  "homepage": "https://github.com/hpcc-systems/Visualization",
79
- "gitHead": "f6408d3f4eb56d5c8360923ec8ecc0391351a3ac"
79
+ "gitHead": "0128487e97c3ca56f55697c40c340de2c5c61ff2"
80
80
  }
@@ -0,0 +1,33 @@
1
+ import { type Engine } from "@hpcc-js/wasm-graphviz";
2
+ import { isLayoutSuccess, type LayoutSVG } from "./workers/graphvizDotOptions.ts";
3
+ // @ts-ignore
4
+ import GraphvizDotWorker from "./workers/graphvizDot.ts?worker&inline";
5
+
6
+ export {
7
+ isLayoutSuccess,
8
+ type LayoutSVG
9
+ };
10
+
11
+ export interface GraphvizDotResponse {
12
+ response: Promise<string>;
13
+ terminate: () => void;
14
+ }
15
+
16
+ export function graphvizDot(dot: string, layout: Engine = "dot"): GraphvizDotResponse {
17
+ const worker = new GraphvizDotWorker();
18
+ const response = new Promise<string>((resolve, reject) => {
19
+ worker.onmessage = event => {
20
+ if (isLayoutSuccess(event.data)) {
21
+ resolve(event.data.svg);
22
+ } else {
23
+ reject(new Error(event.data.error));
24
+ }
25
+ worker.terminate();
26
+ };
27
+ worker.postMessage({ dot, layout });
28
+ });
29
+ return {
30
+ terminate: (): void => worker.terminate(),
31
+ response
32
+ };
33
+ }
@@ -3,5 +3,6 @@ export * from "./dagre.ts";
3
3
  export * from "./forceDirected.ts";
4
4
  export * from "./geoForceDirected.ts";
5
5
  export * from "./graphviz.ts";
6
+ export * from "./graphvizDot.ts";
6
7
  export type { ILayout } from "./layout.ts";
7
8
  export * from "./null.ts";
@@ -0,0 +1,25 @@
1
+ import { Graphviz, type Engine } from "@hpcc-js/wasm-graphviz";
2
+ import { LayoutSVG } from "./graphvizDotOptions.js";
3
+
4
+ async function graphvizDot(dot: string, layout: Engine): Promise<LayoutSVG> {
5
+ const graphviz = await Graphviz.load();
6
+ try {
7
+ return {
8
+ svg: graphviz.layout(dot, "svg", layout)
9
+ };
10
+ } catch (e: any) {
11
+ if (e instanceof Error) {
12
+ return {
13
+ error: e.message,
14
+ errorDot: dot
15
+ };
16
+ }
17
+ throw e;
18
+ }
19
+ }
20
+
21
+ self.onmessage = event => {
22
+ graphvizDot(event.data.dot, event.data.layout).then(result => {
23
+ self.postMessage(result);
24
+ });
25
+ };
@@ -0,0 +1,14 @@
1
+ export interface LayoutError {
2
+ error?: string;
3
+ errorDot?: string;
4
+ }
5
+
6
+ export interface LayoutSVG extends LayoutError {
7
+ svg?: string;
8
+ }
9
+
10
+ export type LayoutSuccess = { svg: string };
11
+
12
+ export function isLayoutSuccess(item: any): item is LayoutSuccess {
13
+ return (item as LayoutSuccess)?.svg !== undefined;
14
+ }
@@ -6,13 +6,13 @@
6
6
  --gv-select-fill: light-dark(#deecf9, #1b3a5c);
7
7
 
8
8
  --gv-unknown-stroke: light-dark(#616161, #9e9e9e);
9
- --gv-unknown-fill: light-dark(#e0e0e0, #2a2a3e);
9
+ --gv-unknown-fill: light-dark(#e0e0e020, #2a2a3e20);
10
10
  --gv-complete-stroke: light-dark(#2e7d32, #66bb6a);
11
- --gv-complete-fill: light-dark(#e8f5e9, #1b3a1e);
11
+ --gv-complete-fill: light-dark(#e8f5e920, #1b3a1e20);
12
12
  --gv-failed-stroke: light-dark(#c62828, #ef5350);
13
- --gv-failed-fill: light-dark(#ffebee, #3a1b1b);
13
+ --gv-failed-fill: light-dark(#ffebee20, #3a1b1b20);
14
14
  --gv-running-stroke: light-dark(#e65100, #ffa726);
15
- --gv-running-fill: light-dark(#fff3e0, #3a2a1b);
15
+ --gv-running-fill: light-dark(#fff3e020, #3a2a1b20);
16
16
 
17
17
  background-color: var(--gv-shadow);
18
18
  stroke: var(--gv-fg);
@@ -124,6 +124,23 @@
124
124
  --_fill: var(--gv-running-fill);
125
125
  }
126
126
 
127
+ /* When selected and stateful, keep state color on border but selected color on fill. */
128
+ .graph_GraphvizWidget :is(.unknown, .complete, .failed, .running).selected>:is(path, polygon, ellipse, polyline, rect) {
129
+ stroke: var(--_stroke);
130
+ }
131
+
132
+ .graph_GraphvizWidget :is(.unknown, .complete, .failed, .running).cluster.selected>:is(path, polygon, ellipse, polyline, rect) {
133
+ stroke: var(--_stroke);
134
+ }
135
+
136
+ .graph_GraphvizWidget :is(.unknown, .complete, .failed, .running):is(.node, .cluster).selected>:is(path, polygon, ellipse, polyline, rect):first-of-type {
137
+ fill: var(--gv-select-fill);
138
+ }
139
+
140
+ .graph_GraphvizWidget :is(.unknown, .complete, .failed, .running).edge.selected>:is(polygon, ellipse, polyline) {
141
+ fill: var(--gv-select-stroke);
142
+ }
143
+
127
144
  /* Keep node and cluster outlines visible when zoomed out */
128
145
  .graph_GraphvizWidget .node>path,
129
146
  .graph_GraphvizWidget .node>polygon,
@@ -1,5 +1,6 @@
1
1
  import type { Graph } from "@hpcc-js/wasm-graphviz";
2
2
  import { d3Event, SVGZoomWidget, type Widget as WidgetT } from "@hpcc-js/common";
3
+ import { graphvizDot, GraphvizDotResponse } from "../common/layouts/index.ts";
3
4
 
4
5
  import "./Widget.css";
5
6
 
@@ -24,8 +25,6 @@ export class Widget extends SVGZoomWidget {
24
25
  protected _selection: { [id: string]: boolean } = {};
25
26
  protected _selectionPreferredNode: { [id: string]: SVGGElement } = {};
26
27
 
27
- protected _customVertices: CustomVertex[] = [];
28
-
29
28
  constructor() {
30
29
  super();
31
30
  this._drawStartPos = "origin";
@@ -341,8 +340,6 @@ export class Widget extends SVGZoomWidget {
341
340
  }
342
341
  }
343
342
 
344
- protected _prevDot: string | undefined;
345
-
346
343
  itemBBox(scopeID: string): ReturnType<Widget["getRenderElementBBox"]>;
347
344
  itemBBox(node: SVGGraphicsElement): ReturnType<Widget["getRenderElementBBox"]>;
348
345
  itemBBox(scopeIDOrNode: string | SVGGraphicsElement) {
@@ -386,18 +383,6 @@ export class Widget extends SVGZoomWidget {
386
383
 
387
384
  update(domNode: HTMLElement | SVGElement, element: SVGGElement): this {
388
385
  super.update(domNode, element);
389
-
390
- this._customVertices = this.collectCustomVertices();
391
- const dot = this._data.toDot();
392
- if (this._prevDot !== dot) {
393
- this._prevDot = dot;
394
- const svg = this._data.layout("svg", "dot").replace(Widget._svgColorRe, m => Widget._svgColorMap[m]);
395
- const svgDoc = new DOMParser().parseFromString(svg, "image/svg+xml");
396
- const renderNode = this._renderElement.node();
397
- renderNode.replaceChildren(...svgDoc.documentElement.childNodes);
398
- this.postrenderCustomVertices(this._customVertices);
399
- this._selectionChanged();
400
- }
401
386
  return this;
402
387
  }
403
388
 
@@ -406,11 +391,48 @@ export class Widget extends SVGZoomWidget {
406
391
  return this;
407
392
  }
408
393
 
394
+ protected _prevDot: string | undefined;
395
+ protected _prevLayout: GraphvizDotResponse | undefined;
409
396
  render(callback?: (w: WidgetT) => void): this {
410
397
  super.render((w: WidgetT) => {
411
-
412
- if (callback) {
413
- callback(w);
398
+ const customVertices = this.collectCustomVertices();
399
+ const dot = this._data.toDot();
400
+ if (this._prevDot !== dot) {
401
+ this._prevDot = dot;
402
+ if (this._prevLayout) {
403
+ this._prevLayout.terminate();
404
+ }
405
+ const renderNode = this._renderElement.node();
406
+ renderNode.innerHTML = "";
407
+
408
+ this._prevLayout = graphvizDot(dot, "dot");
409
+ this._prevLayout.response
410
+ .then(svg => {
411
+ if (this._prevLayout) {
412
+ this._prevLayout = undefined;
413
+ }
414
+ svg = svg.replace(Widget._svgColorRe, m => Widget._svgColorMap[m]);
415
+ const svgDoc = new DOMParser().parseFromString(svg, "image/svg+xml");
416
+ renderNode.replaceChildren(...svgDoc.documentElement.childNodes);
417
+ this.postrenderCustomVertices(customVertices);
418
+ this._selectionChanged();
419
+ }).catch(e => {
420
+ if (this._prevLayout) {
421
+ this._prevLayout = undefined;
422
+ }
423
+ renderNode.innerHTML = "";
424
+ this.postrenderCustomVertices([]);
425
+ this._selectionChanged();
426
+ console.error(e);
427
+ }).finally(() => {
428
+ if (callback) {
429
+ callback(w);
430
+ }
431
+ });
432
+ } else {
433
+ if (callback) {
434
+ callback(w);
435
+ }
414
436
  }
415
437
  });
416
438
  return this;
@@ -0,0 +1,8 @@
1
+ import { type Engine } from "@hpcc-js/wasm-graphviz";
2
+ import { isLayoutSuccess, type LayoutSVG } from "./workers/graphvizDotOptions.ts";
3
+ export { isLayoutSuccess, type LayoutSVG };
4
+ export interface GraphvizDotResponse {
5
+ response: Promise<string>;
6
+ terminate: () => void;
7
+ }
8
+ export declare function graphvizDot(dot: string, layout?: Engine): GraphvizDotResponse;
@@ -3,5 +3,6 @@ export * from "./dagre.ts";
3
3
  export * from "./forceDirected.ts";
4
4
  export * from "./geoForceDirected.ts";
5
5
  export * from "./graphviz.ts";
6
+ export * from "./graphvizDot.ts";
6
7
  export type { ILayout } from "./layout.ts";
7
8
  export * from "./null.ts";
@@ -0,0 +1,11 @@
1
+ export interface LayoutError {
2
+ error?: string;
3
+ errorDot?: string;
4
+ }
5
+ export interface LayoutSVG extends LayoutError {
6
+ svg?: string;
7
+ }
8
+ export type LayoutSuccess = {
9
+ svg: string;
10
+ };
11
+ export declare function isLayoutSuccess(item: any): item is LayoutSuccess;
@@ -1,5 +1,6 @@
1
1
  import type { Graph } from "@hpcc-js/wasm-graphviz";
2
2
  import { SVGZoomWidget, type Widget as WidgetT } from "@hpcc-js/common";
3
+ import { GraphvizDotResponse } from "../common/layouts/index.ts";
3
4
  import "./Widget.css";
4
5
  export declare const CUSTOM_HTML = "htmlContent";
5
6
  export declare const CUSTOM_SVG = "svgContent";
@@ -18,7 +19,6 @@ export declare class Widget extends SVGZoomWidget {
18
19
  protected _selectionPreferredNode: {
19
20
  [id: string]: SVGGElement;
20
21
  };
21
- protected _customVertices: CustomVertex[];
22
22
  constructor();
23
23
  protected _data: Graph;
24
24
  data(_: Graph): this;
@@ -36,13 +36,14 @@ export declare class Widget extends SVGZoomWidget {
36
36
  enter(domNode: HTMLElement | SVGElement, element?: SVGGElement): this;
37
37
  protected collectCustomVertices(): CustomVertex[];
38
38
  protected postrenderCustomVertices(customVertices: CustomVertex[]): void;
39
- protected _prevDot: string | undefined;
40
39
  itemBBox(scopeID: string): ReturnType<Widget["getRenderElementBBox"]>;
41
40
  itemBBox(node: SVGGraphicsElement): ReturnType<Widget["getRenderElementBBox"]>;
42
41
  zoomToItem(scopeID: string): this;
43
42
  zoomToItem(node: SVGGraphicsElement): this;
44
43
  update(domNode: HTMLElement | SVGElement, element: SVGGElement): this;
45
44
  exit(domNode: HTMLElement | SVGElement, element?: SVGGElement): this;
45
+ protected _prevDot: string | undefined;
46
+ protected _prevLayout: GraphvizDotResponse | undefined;
46
47
  render(callback?: (w: WidgetT) => void): this;
47
48
  selectionChanged(): void;
48
49
  vertexButtonClicked(id: string, action: string): void;