layerchart 0.59.6 → 0.60.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/dist/components/Dagre.svelte +151 -0
- package/dist/components/Dagre.svelte.d.ts +85 -0
- package/dist/components/Spline.svelte +0 -2
- package/dist/components/TransformControls.svelte +8 -1
- package/dist/components/TransformControls.svelte.d.ts +3 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/docs/CurveMenuField.svelte +8 -1
- package/dist/docs/CurveMenuField.svelte.d.ts +2 -1
- package/dist/utils/graph.d.ts +9 -0
- package/dist/utils/graph.js +29 -0
- package/dist/utils/graph.test.d.ts +1 -0
- package/dist/utils/graph.test.js +84 -0
- package/package.json +2 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<script module>
|
|
2
|
+
export type DagreGraphData = {
|
|
3
|
+
nodes: Array<{ id: string; parent?: string; label?: string | dagre.Label }>;
|
|
4
|
+
edges: Array<{ source: string; target: string; label?: string }>;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const RankDir = {
|
|
8
|
+
'top-bottom': 'TB',
|
|
9
|
+
'bottom-top': 'BT',
|
|
10
|
+
'left-right': 'LR',
|
|
11
|
+
'right-left': 'RL',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const Align = {
|
|
15
|
+
none: undefined,
|
|
16
|
+
'up-left': 'UL',
|
|
17
|
+
'up-right': 'UR',
|
|
18
|
+
'down-left': 'DL',
|
|
19
|
+
'down-right': 'DR',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const EdgeLabelPosition = {
|
|
23
|
+
left: 'l',
|
|
24
|
+
center: 'c',
|
|
25
|
+
right: 'r',
|
|
26
|
+
};
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<script lang="ts">
|
|
30
|
+
import dagre, { type Edge, type EdgeConfig, type GraphEdge } from '@dagrejs/dagre';
|
|
31
|
+
|
|
32
|
+
/** Data of nodes and edges to build graph */
|
|
33
|
+
export let data: DagreGraphData;
|
|
34
|
+
|
|
35
|
+
export let nodes = (d: any) => d.nodes;
|
|
36
|
+
export let nodeId = (d: any) => d.id;
|
|
37
|
+
export let edges = (d: any) => d.edges;
|
|
38
|
+
|
|
39
|
+
/** Set graph as directed (true, default) or undirected (false), which does not treat the order of nodes in an edge as significant. */
|
|
40
|
+
export let directed = true;
|
|
41
|
+
|
|
42
|
+
/** Allow a graph to have multiple edges between the same pair of nodes */
|
|
43
|
+
export let multigraph = false;
|
|
44
|
+
|
|
45
|
+
/** Allow a graph to have compound nodes - nodes which can be the `parent` of other nodes */
|
|
46
|
+
export let compound = false;
|
|
47
|
+
|
|
48
|
+
/** Type of algorithm to assigns a rank to each node in the input graph */
|
|
49
|
+
export let ranker: 'network-simplex' | 'tight-tree' | 'longest-path' = 'network-simplex';
|
|
50
|
+
|
|
51
|
+
/** Direction for rank nodes */
|
|
52
|
+
export let direction: keyof typeof RankDir = 'top-bottom';
|
|
53
|
+
|
|
54
|
+
/** Alignment for rank nodes */
|
|
55
|
+
export let align: keyof typeof Align | undefined = undefined;
|
|
56
|
+
|
|
57
|
+
/** Number of pixels between each rank in the layout */
|
|
58
|
+
export let rankSeparation = 50;
|
|
59
|
+
|
|
60
|
+
/** Number of pixels that separate nodes horizontally in the layout */
|
|
61
|
+
export let nodeSeparation = 50;
|
|
62
|
+
|
|
63
|
+
/** Number of pixels that separate edges horizontally in the layout */
|
|
64
|
+
export let edgeSeparation = 10;
|
|
65
|
+
|
|
66
|
+
/** Default node width if not defined on node */
|
|
67
|
+
export let nodeWidth = 100;
|
|
68
|
+
|
|
69
|
+
/** Default node height if not defined on node */
|
|
70
|
+
export let nodeHeight = 50;
|
|
71
|
+
|
|
72
|
+
/** Default link label width if not defined on edge */
|
|
73
|
+
export let edgeLabelWidth = 100;
|
|
74
|
+
|
|
75
|
+
/** Default edge label height if not defined on edge */
|
|
76
|
+
export let edgeLabelHeight = 20;
|
|
77
|
+
|
|
78
|
+
/** Default edge label height if not defined on edge */
|
|
79
|
+
export let edgeLabelPosition: keyof typeof EdgeLabelPosition = 'center';
|
|
80
|
+
|
|
81
|
+
/** Default pixels to move the label away from the edge if not defined on edge. Applies only when labelpos is l or r.*/
|
|
82
|
+
export let edgeLabelOffset = 10;
|
|
83
|
+
|
|
84
|
+
/** Filter nodes */
|
|
85
|
+
export let filterNodes: (nodeId: string, graph: dagre.graphlib.Graph) => boolean = () => true;
|
|
86
|
+
|
|
87
|
+
let graph: dagre.graphlib.Graph;
|
|
88
|
+
$: {
|
|
89
|
+
let g = new dagre.graphlib.Graph({ directed, multigraph, compound });
|
|
90
|
+
|
|
91
|
+
g.setGraph({
|
|
92
|
+
ranker: ranker,
|
|
93
|
+
rankdir: RankDir[direction],
|
|
94
|
+
align: align ? Align[align] : undefined,
|
|
95
|
+
ranksep: rankSeparation,
|
|
96
|
+
nodesep: nodeSeparation,
|
|
97
|
+
edgesep: edgeSeparation,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
g.setDefaultEdgeLabel(() => {
|
|
101
|
+
return {};
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
nodes(data).forEach((n: any) => {
|
|
105
|
+
const id = nodeId(n);
|
|
106
|
+
|
|
107
|
+
g.setNode(nodeId(n), {
|
|
108
|
+
id,
|
|
109
|
+
label: typeof n.label === 'string' ? n.label : id,
|
|
110
|
+
width: nodeWidth,
|
|
111
|
+
height: nodeHeight,
|
|
112
|
+
...(typeof n.label === 'object' ? n.label : null),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (n.parent) {
|
|
116
|
+
g.setParent(id, n.parent);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
edges(data).forEach((e: any) => {
|
|
121
|
+
const { source, target, label, ...rest } = e;
|
|
122
|
+
g.setEdge(
|
|
123
|
+
e.source,
|
|
124
|
+
e.target,
|
|
125
|
+
label
|
|
126
|
+
? {
|
|
127
|
+
label: label,
|
|
128
|
+
labelpos: EdgeLabelPosition[edgeLabelPosition],
|
|
129
|
+
labeloffset: edgeLabelOffset,
|
|
130
|
+
width: edgeLabelWidth,
|
|
131
|
+
height: edgeLabelHeight,
|
|
132
|
+
...rest,
|
|
133
|
+
}
|
|
134
|
+
: {}
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
g = filterNodes ? g.filterNodes((nodeId) => filterNodes(nodeId, graph)) : graph;
|
|
139
|
+
|
|
140
|
+
dagre.layout(g);
|
|
141
|
+
|
|
142
|
+
graph = g;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
$: graphNodes = graph.nodes().map((id) => graph.node(id));
|
|
146
|
+
$: graphEdges = graph.edges().map((edge) => ({ ...edge, ...graph.edge(edge) })) as Array<
|
|
147
|
+
Edge & EdgeConfig & GraphEdge // `EdgeConfig` is excluded when inferred from usage
|
|
148
|
+
>;
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<slot nodes={graphNodes} edges={graphEdges} />
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
export type DagreGraphData = {
|
|
3
|
+
nodes: Array<{
|
|
4
|
+
id: string;
|
|
5
|
+
parent?: string;
|
|
6
|
+
label?: string | dagre.Label;
|
|
7
|
+
}>;
|
|
8
|
+
edges: Array<{
|
|
9
|
+
source: string;
|
|
10
|
+
target: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
export declare const RankDir: {
|
|
15
|
+
'top-bottom': string;
|
|
16
|
+
'bottom-top': string;
|
|
17
|
+
'left-right': string;
|
|
18
|
+
'right-left': string;
|
|
19
|
+
};
|
|
20
|
+
export declare const Align: {
|
|
21
|
+
none: undefined;
|
|
22
|
+
'up-left': string;
|
|
23
|
+
'up-right': string;
|
|
24
|
+
'down-left': string;
|
|
25
|
+
'down-right': string;
|
|
26
|
+
};
|
|
27
|
+
export declare const EdgeLabelPosition: {
|
|
28
|
+
left: string;
|
|
29
|
+
center: string;
|
|
30
|
+
right: string;
|
|
31
|
+
};
|
|
32
|
+
import dagre from '@dagrejs/dagre';
|
|
33
|
+
declare const __propDef: {
|
|
34
|
+
props: {
|
|
35
|
+
/** Data of nodes and edges to build graph */ data: DagreGraphData;
|
|
36
|
+
nodes?: (d: any) => any;
|
|
37
|
+
nodeId?: (d: any) => any;
|
|
38
|
+
edges?: (d: any) => any;
|
|
39
|
+
/** Set graph as directed (true, default) or undirected (false), which does not treat the order of nodes in an edge as significant. */ directed?: boolean;
|
|
40
|
+
/** Allow a graph to have multiple edges between the same pair of nodes */ multigraph?: boolean;
|
|
41
|
+
/** Allow a graph to have compound nodes - nodes which can be the `parent` of other nodes */ compound?: boolean;
|
|
42
|
+
/** Type of algorithm to assigns a rank to each node in the input graph */ ranker?: "network-simplex" | "tight-tree" | "longest-path";
|
|
43
|
+
/** Direction for rank nodes */ direction?: keyof typeof RankDir;
|
|
44
|
+
/** Alignment for rank nodes */ align?: keyof typeof Align | undefined;
|
|
45
|
+
/** Number of pixels between each rank in the layout */ rankSeparation?: number;
|
|
46
|
+
/** Number of pixels that separate nodes horizontally in the layout */ nodeSeparation?: number;
|
|
47
|
+
/** Number of pixels that separate edges horizontally in the layout */ edgeSeparation?: number;
|
|
48
|
+
/** Default node width if not defined on node */ nodeWidth?: number;
|
|
49
|
+
/** Default node height if not defined on node */ nodeHeight?: number;
|
|
50
|
+
/** Default link label width if not defined on edge */ edgeLabelWidth?: number;
|
|
51
|
+
/** Default edge label height if not defined on edge */ edgeLabelHeight?: number;
|
|
52
|
+
/** Default edge label height if not defined on edge */ edgeLabelPosition?: keyof typeof EdgeLabelPosition;
|
|
53
|
+
/** Default pixels to move the label away from the edge if not defined on edge. Applies only when labelpos is l or r.*/ edgeLabelOffset?: number;
|
|
54
|
+
/** Filter nodes */ filterNodes?: (nodeId: string, graph: dagre.graphlib.Graph) => boolean;
|
|
55
|
+
};
|
|
56
|
+
events: {
|
|
57
|
+
[evt: string]: CustomEvent<any>;
|
|
58
|
+
};
|
|
59
|
+
slots: {
|
|
60
|
+
default: {
|
|
61
|
+
nodes: {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
class?: string | undefined;
|
|
67
|
+
label?: string | undefined;
|
|
68
|
+
padding?: number | undefined;
|
|
69
|
+
paddingX?: number | undefined;
|
|
70
|
+
paddingY?: number | undefined;
|
|
71
|
+
rank?: number | undefined;
|
|
72
|
+
rx?: number | undefined;
|
|
73
|
+
ry?: number | undefined;
|
|
74
|
+
shape?: string | undefined;
|
|
75
|
+
}[];
|
|
76
|
+
edges: (dagre.Edge & dagre.EdgeConfig & dagre.GraphEdge)[];
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
export type DagreProps = typeof __propDef.props;
|
|
81
|
+
export type DagreEvents = typeof __propDef.events;
|
|
82
|
+
export type DagreSlots = typeof __propDef.slots;
|
|
83
|
+
export default class Dagre extends SvelteComponentTyped<DagreProps, DagreEvents, DagreSlots> {
|
|
84
|
+
}
|
|
85
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { type ComponentProps } from 'svelte';
|
|
2
3
|
import { Button, Icon, MenuButton, Tooltip } from 'svelte-ux';
|
|
3
4
|
import { cls } from '@layerstack/tailwind';
|
|
4
5
|
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
|
|
29
30
|
export let placement: Placement = 'top-right';
|
|
30
31
|
export let orientation: 'horizontal' | 'vertical' = 'vertical';
|
|
32
|
+
export let size: ComponentProps<Button>['size'] = 'md';
|
|
31
33
|
|
|
32
34
|
type Actions = 'zoomIn' | 'zoomOut' | 'center' | 'reset' | 'scrollMode';
|
|
33
35
|
export let show: Actions[] = ['zoomIn', 'zoomOut', 'center', 'reset', 'scrollMode'];
|
|
@@ -64,7 +66,7 @@
|
|
|
64
66
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
65
67
|
<div
|
|
66
68
|
class={cls(
|
|
67
|
-
'bg-surface-300/50 rounded-full m-1 backdrop-blur z-10 flex',
|
|
69
|
+
'bg-surface-300/50 border rounded-full m-1 backdrop-blur z-10 flex',
|
|
68
70
|
orientation === 'vertical' && 'flex-col',
|
|
69
71
|
{
|
|
70
72
|
'top-left': 'absolute top-0 left-0',
|
|
@@ -89,6 +91,7 @@
|
|
|
89
91
|
<Button
|
|
90
92
|
icon={mdiMagnifyPlusOutline}
|
|
91
93
|
on:click={() => transform.zoomIn()}
|
|
94
|
+
{size}
|
|
92
95
|
class="text-surface-content p-2"
|
|
93
96
|
/>
|
|
94
97
|
</Tooltip>
|
|
@@ -99,6 +102,7 @@
|
|
|
99
102
|
<Button
|
|
100
103
|
icon={mdiMagnifyMinusOutline}
|
|
101
104
|
on:click={() => transform.zoomOut()}
|
|
105
|
+
{size}
|
|
102
106
|
class="text-surface-content p-2"
|
|
103
107
|
/>
|
|
104
108
|
</Tooltip>
|
|
@@ -109,6 +113,7 @@
|
|
|
109
113
|
<Button
|
|
110
114
|
icon={mdiImageFilterCenterFocus}
|
|
111
115
|
on:click={() => transform.translateCenter()}
|
|
116
|
+
{size}
|
|
112
117
|
class="text-surface-content p-2"
|
|
113
118
|
/>
|
|
114
119
|
</Tooltip>
|
|
@@ -119,6 +124,7 @@
|
|
|
119
124
|
<Button
|
|
120
125
|
icon={mdiArrowULeftTop}
|
|
121
126
|
on:click={() => transform.reset()}
|
|
127
|
+
{size}
|
|
122
128
|
class="text-surface-content p-2"
|
|
123
129
|
/>
|
|
124
130
|
</Tooltip>
|
|
@@ -135,6 +141,7 @@
|
|
|
135
141
|
]}
|
|
136
142
|
menuProps={{ placement: menuPlacementByOrientationAndPlacement[orientation][placement] }}
|
|
137
143
|
menuIcon={null}
|
|
144
|
+
{size}
|
|
138
145
|
value={$scrollMode}
|
|
139
146
|
on:change={(e) => transform.setScrollMode(e.detail.value)}
|
|
140
147
|
class="text-surface-content"
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
import { type ComponentProps } from 'svelte';
|
|
3
|
+
import { Button } from 'svelte-ux';
|
|
2
4
|
declare const __propDef: {
|
|
3
5
|
props: {
|
|
4
6
|
[x: string]: any;
|
|
5
7
|
placement?: ("center" | "left" | "right" | "bottom" | "top" | "top-left" | "top-right" | "bottom-left" | "bottom-right") | undefined;
|
|
6
8
|
orientation?: ("horizontal" | "vertical") | undefined;
|
|
9
|
+
size?: ComponentProps<Button>["size"];
|
|
7
10
|
show?: ("reset" | "scrollMode" | "zoomIn" | "zoomOut" | "center")[] | undefined;
|
|
8
11
|
};
|
|
9
12
|
events: {
|
|
@@ -16,6 +16,7 @@ export { default as Circle } from './Circle.svelte';
|
|
|
16
16
|
export { default as CircleClipPath } from './CircleClipPath.svelte';
|
|
17
17
|
export { default as ClipPath } from './ClipPath.svelte';
|
|
18
18
|
export { default as ColorRamp } from './ColorRamp.svelte';
|
|
19
|
+
export { default as Dagre } from './Dagre.svelte';
|
|
19
20
|
export { default as Frame } from './Frame.svelte';
|
|
20
21
|
export { default as ForceSimulation } from './ForceSimulation.svelte';
|
|
21
22
|
export { default as GeoCircle } from './GeoCircle.svelte';
|
package/dist/components/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { default as Circle } from './Circle.svelte';
|
|
|
17
17
|
export { default as CircleClipPath } from './CircleClipPath.svelte';
|
|
18
18
|
export { default as ClipPath } from './ClipPath.svelte';
|
|
19
19
|
export { default as ColorRamp } from './ColorRamp.svelte';
|
|
20
|
+
export { default as Dagre } from './Dagre.svelte';
|
|
20
21
|
export { default as Frame } from './Frame.svelte';
|
|
21
22
|
export { default as ForceSimulation } from './ForceSimulation.svelte';
|
|
22
23
|
export { default as GeoCircle } from './GeoCircle.svelte';
|
package/dist/utils/graph.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SankeyExtraProperties, SankeyGraph, SankeyLink, SankeyNodeMinimal } from 'd3-sankey';
|
|
2
2
|
import type { hierarchy as d3Hierarchy } from 'd3-hierarchy';
|
|
3
|
+
import dagre from '@dagrejs/dagre';
|
|
3
4
|
/**
|
|
4
5
|
* Convert CSV rows in format: 'source,target,value' to SankeyGraph
|
|
5
6
|
*/
|
|
@@ -26,3 +27,11 @@ export declare function graphFromNode(node: SankeyNodeMinimal<any, any>): {
|
|
|
26
27
|
* Get distinct nodes from link.source and link.target
|
|
27
28
|
*/
|
|
28
29
|
export declare function nodesFromLinks<N extends SankeyExtraProperties, L extends SankeyExtraProperties>(links: Array<SankeyLink<N, L>>): any[];
|
|
30
|
+
/**
|
|
31
|
+
* Get all upstream predecessors for dagre nodeId
|
|
32
|
+
*/
|
|
33
|
+
export declare function ancestors(graph: dagre.graphlib.Graph, nodeId: string, maxDepth?: number, currentDepth?: number): dagre.Node[];
|
|
34
|
+
/**
|
|
35
|
+
* Get all downstream descendants for dagre nodeId
|
|
36
|
+
*/
|
|
37
|
+
export declare function descendants(graph: dagre.graphlib.Graph, nodeId: string, maxDepth?: number, currentDepth?: number): dagre.Node[];
|
package/dist/utils/graph.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { csvParseRows } from 'd3-dsv';
|
|
2
|
+
import dagre from '@dagrejs/dagre';
|
|
2
3
|
/**
|
|
3
4
|
* Convert CSV rows in format: 'source,target,value' to SankeyGraph
|
|
4
5
|
*/
|
|
@@ -64,3 +65,31 @@ export function nodesFromLinks(links) {
|
|
|
64
65
|
}
|
|
65
66
|
return Array.from(nodesByName.values());
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Get all upstream predecessors for dagre nodeId
|
|
70
|
+
*/
|
|
71
|
+
export function ancestors(graph, nodeId, maxDepth = Infinity, currentDepth = 0) {
|
|
72
|
+
if (currentDepth === maxDepth) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const predecessors = graph.predecessors(nodeId) ?? [];
|
|
76
|
+
return [
|
|
77
|
+
...predecessors,
|
|
78
|
+
// @ts-expect-error: Types from dagre appear incorrect
|
|
79
|
+
...predecessors.flatMap((pId) => ancestors(graph, pId, maxDepth, currentDepth + 1)),
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get all downstream descendants for dagre nodeId
|
|
84
|
+
*/
|
|
85
|
+
export function descendants(graph, nodeId, maxDepth = Infinity, currentDepth = 0) {
|
|
86
|
+
if (currentDepth === maxDepth) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
const predecessors = graph.successors(nodeId) ?? [];
|
|
90
|
+
return [
|
|
91
|
+
...predecessors,
|
|
92
|
+
// @ts-expect-error: Types from dagre appear incorrect
|
|
93
|
+
...predecessors.flatMap((pId) => descendants(graph, pId, maxDepth, currentDepth + 1)),
|
|
94
|
+
];
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import dagre from '@dagrejs/dagre';
|
|
3
|
+
import { ancestors, descendants } from './graph.js';
|
|
4
|
+
const exampleGraph = {
|
|
5
|
+
nodes: [
|
|
6
|
+
{ id: 'A' },
|
|
7
|
+
{ id: 'B' },
|
|
8
|
+
{ id: 'C' },
|
|
9
|
+
{ id: 'D' },
|
|
10
|
+
{ id: 'E' },
|
|
11
|
+
{ id: 'F' },
|
|
12
|
+
{ id: 'G' },
|
|
13
|
+
{ id: 'H' },
|
|
14
|
+
{ id: 'I' },
|
|
15
|
+
],
|
|
16
|
+
edges: [
|
|
17
|
+
{ source: 'A', target: 'B' },
|
|
18
|
+
{ source: 'C', target: 'B' },
|
|
19
|
+
{ source: 'B', target: 'E' },
|
|
20
|
+
{ source: 'B', target: 'F' },
|
|
21
|
+
{ source: 'D', target: 'E' },
|
|
22
|
+
{ source: 'D', target: 'F' },
|
|
23
|
+
{ source: 'E', target: 'H' },
|
|
24
|
+
{ source: 'G', target: 'H' },
|
|
25
|
+
{ source: 'H', target: 'I' },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
function buildGraph(data) {
|
|
29
|
+
const g = new dagre.graphlib.Graph();
|
|
30
|
+
g.setGraph({});
|
|
31
|
+
data.nodes.forEach((n) => {
|
|
32
|
+
g.setNode(n.id, {
|
|
33
|
+
label: n.id,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
data.edges.forEach((e) => {
|
|
37
|
+
g.setEdge(e.source, e.target);
|
|
38
|
+
});
|
|
39
|
+
return g;
|
|
40
|
+
}
|
|
41
|
+
describe('accessors', () => {
|
|
42
|
+
it('start of graph ', () => {
|
|
43
|
+
const graph = buildGraph(exampleGraph);
|
|
44
|
+
const actual = ancestors(graph, 'A');
|
|
45
|
+
expect(actual).length(0);
|
|
46
|
+
});
|
|
47
|
+
it('middle of graph ', () => {
|
|
48
|
+
const graph = buildGraph(exampleGraph);
|
|
49
|
+
const actual = ancestors(graph, 'E');
|
|
50
|
+
expect(actual).to.have.members(['A', 'B', 'C', 'D']);
|
|
51
|
+
});
|
|
52
|
+
it('end of graph ', () => {
|
|
53
|
+
const graph = buildGraph(exampleGraph);
|
|
54
|
+
const actual = ancestors(graph, 'I');
|
|
55
|
+
expect(actual).to.have.members(['A', 'B', 'C', 'D', 'E', 'G', 'H']);
|
|
56
|
+
});
|
|
57
|
+
it('max depth', () => {
|
|
58
|
+
const graph = buildGraph(exampleGraph);
|
|
59
|
+
const actual = ancestors(graph, 'H', 2);
|
|
60
|
+
expect(actual).to.have.members(['B', 'D', 'E', 'G']);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('descendants', () => {
|
|
64
|
+
it('start of graph ', () => {
|
|
65
|
+
const graph = buildGraph(exampleGraph);
|
|
66
|
+
const actual = descendants(graph, 'A');
|
|
67
|
+
expect(actual).to.have.members(['B', 'E', 'F', 'H', 'I']);
|
|
68
|
+
});
|
|
69
|
+
it('middle of graph ', () => {
|
|
70
|
+
const graph = buildGraph(exampleGraph);
|
|
71
|
+
const actual = descendants(graph, 'E');
|
|
72
|
+
expect(actual).to.have.members(['H', 'I']);
|
|
73
|
+
});
|
|
74
|
+
it('end of graph ', () => {
|
|
75
|
+
const graph = buildGraph(exampleGraph);
|
|
76
|
+
const actual = descendants(graph, 'I');
|
|
77
|
+
expect(actual).length(0);
|
|
78
|
+
});
|
|
79
|
+
it('max depth', () => {
|
|
80
|
+
const graph = buildGraph(exampleGraph);
|
|
81
|
+
const actual = descendants(graph, 'B', 2);
|
|
82
|
+
expect(actual).to.have.members(['E', 'F', 'H']);
|
|
83
|
+
});
|
|
84
|
+
});
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"author": "Sean Lynch <techniq35@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "techniq/layerchart",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.60.0",
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@changesets/cli": "^2.27.10",
|
|
10
10
|
"@mdi/js": "^7.4.47",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"type": "module",
|
|
69
69
|
"dependencies": {
|
|
70
|
+
"@dagrejs/dagre": "^1.1.4",
|
|
70
71
|
"@layerstack/svelte-actions": "^0.0.9",
|
|
71
72
|
"@layerstack/svelte-stores": "^0.0.9",
|
|
72
73
|
"@layerstack/tailwind": "^0.0.11",
|