Skip to content

Commit a328788

Browse files
committed
Fix d3-dag layout TypeScript types (#307)
1 parent 8a74f5d commit a328788

File tree

3 files changed

+51
-52
lines changed

3 files changed

+51
-52
lines changed

modules/graph-layers/src/core/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export type NodeType =
2323
// edge shape
2424
export type EdgeType = 'spline' | 'line' | 'path';
2525

26+
export const EDGE_TYPE = {
27+
SPLINE: 'spline',
28+
LINE: 'line',
29+
PATH: 'path'
30+
} as const satisfies Record<string, EdgeType>;
31+
2632
// decorators on edges
2733
export type EdgeDecoratorType = 'label' | 'flow' | 'arrow';
2834

modules/graph-layers/src/layouts/d3-dag/d3-dag-layout.ts

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import {GraphLayout, GraphLayoutOptions} from '../../core/graph-layout';
66
import type {Graph} from '../../graph/graph';
77
import {Node} from '../../graph/node';
88
import {Edge} from '../../graph/edge';
9-
import {EDGE_TYPE} from '../../core/constants';
10-
119
import {
1210
coordCenter,
1311
coordGreedy,
@@ -31,10 +29,10 @@ import {
3129
type DefaultGrid,
3230
type DefaultSugiyama,
3331
type DefaultZherebko,
34-
type Graph as DagGraph,
3532
type LayoutResult,
3633
type MutGraph,
3734
type MutGraphNode,
35+
type MutGraphLink,
3836
type NodeSize
3937
} from 'd3-dag';
4038

@@ -77,9 +75,20 @@ export type D3DagLayoutOptions = GraphLayoutOptions & {
7775

7876
type DagBuilder = (graph: Graph) => MutGraph<Node, Edge>;
7977

80-
type LayeringOperator = ReturnType<typeof layeringSimplex>;
81-
type DecrossOperator = ReturnType<typeof decrossTwoLayer>;
82-
type CoordOperator = ReturnType<typeof coordGreedy>;
78+
type LayeringOperator =
79+
| ReturnType<typeof layeringSimplex>
80+
| ReturnType<typeof layeringLongestPath>
81+
| ReturnType<typeof layeringTopological>;
82+
type DecrossOperator =
83+
| ReturnType<typeof decrossTwoLayer>
84+
| ReturnType<typeof decrossOpt>
85+
| ReturnType<typeof decrossDfs>;
86+
type CoordOperator =
87+
| ReturnType<typeof coordCenter>
88+
| ReturnType<typeof coordGreedy>
89+
| ReturnType<typeof coordQuad>
90+
| ReturnType<typeof coordSimplex>
91+
| ReturnType<typeof coordTopological>;
8392

8493
type LayoutCallable = (dag: MutGraph<Node, Edge>) => LayoutResult;
8594

@@ -291,7 +300,7 @@ export class D3DagLayout extends GraphLayout<D3DagLayoutOptions> {
291300
try {
292301
this._dag = this._buildDag();
293302
const layout = this._getLayoutOperator();
294-
layout(this._dag as DagGraph<Node, Edge>);
303+
layout(this._dag);
295304
this._cacheGeometry();
296305
this._onLayoutChange();
297306
this._onLayoutDone();
@@ -306,8 +315,7 @@ export class D3DagLayout extends GraphLayout<D3DagLayoutOptions> {
306315

307316
if (typeof builder === 'function') {
308317
const dag = builder(this._graph);
309-
this._ensureEdgeData(dag);
310-
return dag;
318+
return this._ensureEdgeData(dag);
311319
}
312320

313321
switch (builder) {
@@ -346,13 +354,15 @@ export class D3DagLayout extends GraphLayout<D3DagLayoutOptions> {
346354
}
347355

348356
private _buildDagWithConnect(): MutGraph<Node, Edge> {
357+
type ConnectDatum = {source: string; target: string; edge: Edge};
358+
349359
const connect = graphConnect()
350-
.sourceId((link) => link.source)
351-
.targetId((link) => link.target)
352-
.nodeDatum((id) => this._nodeLookup.get(this._fromDagId(id)) ?? new Node({id}))
360+
.sourceId(({source}: ConnectDatum): string => source)
361+
.targetId(({target}: ConnectDatum): string => target)
362+
.nodeDatum((id: string): Node => this._nodeLookup.get(this._fromDagId(id)) ?? new Node({id}))
353363
.single(true);
354364

355-
const data = this._graph
365+
const data: ConnectDatum[] = this._graph
356366
.getEdges()
357367
.filter((edge) => edge.isDirected())
358368
.map((edge) => ({
@@ -366,8 +376,8 @@ export class D3DagLayout extends GraphLayout<D3DagLayoutOptions> {
366376
const seenIds = new Set<string | number>();
367377
for (const dagNode of dag.nodes()) {
368378
const datum = dagNode.data;
369-
if (datum) {
370-
seenIds.add((datum).getId());
379+
if (datum instanceof Node) {
380+
seenIds.add(datum.getId());
371381
}
372382
}
373383

@@ -377,57 +387,41 @@ export class D3DagLayout extends GraphLayout<D3DagLayoutOptions> {
377387
}
378388
}
379389

380-
for (const link of dag.links()) {
381-
const datum = link.data as {edge?: Edge};
382-
if (datum?.edge instanceof Edge) {
383-
link.data = datum.edge;
384-
}
385-
}
386-
387-
this._ensureEdgeData(dag);
388-
return dag;
390+
return this._ensureEdgeData(dag);
389391
}
390392

391393
private _buildDagWithStratify(): MutGraph<Node, Edge> {
392-
const stratify = graphStratify();
393-
394-
const nodeData = this._graph.getNodes().map((node) => {
395-
const id = this._toDagId(node.getId());
396-
const parentIds = (this._incomingParentMap.get(node.getId()) ?? [])
397-
.filter((parentId) => this._nodeLookup.has(parentId))
398-
.map((parentId) => this._toDagId(parentId));
399-
400-
return {
401-
id,
402-
node,
403-
parentIds
404-
};
405-
});
406-
407-
const dag = stratify(nodeData);
408-
409-
for (const dagNode of dag.nodes()) {
410-
const datum = dagNode.data as {node: Node};
411-
dagNode.data = datum.node;
412-
}
413-
414-
this._ensureEdgeData(dag);
415-
return dag as MutGraph<Node, Edge>;
394+
const stratify = graphStratify()
395+
.id((node: Node): string => this._toDagId(node.getId()))
396+
.parentIds((node: Node): Iterable<string> => {
397+
const parentIds = this._incomingParentMap.get(node.getId()) ?? [];
398+
return parentIds
399+
.filter((parentId) => this._nodeLookup.has(parentId))
400+
.map((parentId) => this._toDagId(parentId));
401+
});
402+
403+
const dag = stratify(this._graph.getNodes());
404+
return this._ensureEdgeData(dag);
416405
}
417406

418-
private _ensureEdgeData(dag: MutGraph<Node, Edge>): void {
407+
private _ensureEdgeData<T>(dag: MutGraph<Node, T>): MutGraph<Node, Edge> {
419408
for (const link of dag.links()) {
420409
if (link.data instanceof Edge) {
421410
continue;
422411
}
423412
const sourceNode = link.source.data;
424413
const targetNode = link.target.data;
414+
if (!(sourceNode instanceof Node) || !(targetNode instanceof Node)) {
415+
continue;
416+
}
425417
const key = this._edgeKey(sourceNode.getId(), targetNode.getId());
426418
const edge = this._edgeLookup.get(key);
427419
if (edge) {
428-
link.data = edge;
420+
(link as unknown as MutGraphLink<Node, Edge>).data = edge;
429421
}
430422
}
423+
424+
return dag as unknown as MutGraph<Node, Edge>;
431425
}
432426

433427
private _getLayoutOperator(): LayoutWithConfiguration {

modules/graph-layers/test/layouts/d3-dag-layout.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {describe, it, expect} from 'vitest';
77
import {Graph} from '../../src/graph/graph';
88
import {Node} from '../../src/graph/node';
99
import {Edge} from '../../src/graph/edge';
10-
import {EDGE_TYPE} from '../../src/core/constants';
1110
import {D3DagLayout} from '../../src/layouts/d3-dag/d3-dag-layout';
1211

1312
type SampleGraph = {
@@ -54,7 +53,7 @@ describe('D3DagLayout', () => {
5453
expect(layout.getNodePosition(nodes.d)).toEqual([0, -1.5]);
5554

5655
const edgeAB = layout.getEdgePosition(edges.ab);
57-
expect(edgeAB?.type).toBe(EDGE_TYPE.LINE);
56+
expect(edgeAB?.type).toBe('line');
5857
expect(edgeAB?.sourcePosition).toEqual(layout.getNodePosition(nodes.a));
5958
expect(edgeAB?.targetPosition).toEqual(layout.getNodePosition(nodes.b));
6059
expect(edgeAB?.controlPoints).toEqual([]);

0 commit comments

Comments
 (0)