@cnvx/nodal 0.0.4 → 0.1.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 +6 -0
- package/dist/Diagram.svelte +18 -20
- package/dist/Diagram.svelte.d.ts +6 -6
- package/dist/DiagramController.svelte +94 -31
- package/dist/DiagramController.svelte.d.ts +4 -2
- package/dist/DiagramNode.svelte +46 -86
- package/dist/DiagramNode.svelte.d.ts +5 -8
- package/dist/PrerenderDiagram.svelte +15 -10
- package/dist/PrerenderDiagram.svelte.d.ts +1 -1
- package/dist/diagram-lib.d.ts +2 -0
- package/dist/diagram-lib.js +3 -0
- package/dist/index.d.ts +1 -1
- package/package.json +81 -81
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A Svelte 5 library for creating interactive node diagrams with customizable conn
|
|
|
7
7
|
- Static rendering by default
|
|
8
8
|
- SVG Rendering
|
|
9
9
|
- Fully typed
|
|
10
|
+
- Lightweight and performant
|
|
10
11
|
|
|
11
12
|
## Quick Start
|
|
12
13
|
|
|
@@ -220,3 +221,8 @@ const nodes: DiagramNodeType[] = [
|
|
|
220
221
|
## License
|
|
221
222
|
|
|
222
223
|
MIT License - see LICENSE file for details.
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# TODOs:
|
|
227
|
+
- Refactor away from Diagram.svelte:247 into a more efficient rendering system
|
|
228
|
+
- Add a testing suite that uses the browser DOM and takes screenshots to verify correct rendering against expected outputs
|
package/dist/Diagram.svelte
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
const browser = !!globalThis?.window
|
|
3
|
-
const dev = globalThis.process?.env?.NODE_ENV && !globalThis.process.env.NODE_ENV.toLowerCase().startsWith('prod');
|
|
4
|
-
|
|
5
2
|
import { onMount, setContext, type Snippet } from "svelte";
|
|
6
3
|
import { SvelteMap } from "svelte/reactivity";
|
|
7
4
|
import {
|
|
8
|
-
debugSide,
|
|
9
5
|
eq,
|
|
10
6
|
getBezierPath,
|
|
11
7
|
getSmoothStepPath,
|
|
12
8
|
normaliseAngle,
|
|
13
9
|
Side,
|
|
14
10
|
sideForAngle,
|
|
15
|
-
unitVectorFromAngle,
|
|
16
11
|
vector2,
|
|
17
12
|
type Vector2,
|
|
18
13
|
Anchor,
|
|
14
|
+
browser,
|
|
15
|
+
dev,
|
|
19
16
|
} from "./diagram-lib.js";
|
|
20
|
-
import { draw } from "svelte/transition";
|
|
21
17
|
|
|
22
|
-
export interface
|
|
18
|
+
export interface DiagramNodeDef {
|
|
23
19
|
id: string;
|
|
24
20
|
x: number;
|
|
25
21
|
y: number;
|
|
@@ -44,7 +40,7 @@
|
|
|
44
40
|
|
|
45
41
|
export type DiagramEdgeParams = {
|
|
46
42
|
// target: string;
|
|
47
|
-
snippet?: Snippet<[edge:
|
|
43
|
+
snippet?: Snippet<[edge: DiagramEdgeDef, path: string, extra: any]>;
|
|
48
44
|
snippetExtraArg?: any;
|
|
49
45
|
|
|
50
46
|
sourceAnchor?: Vector2;
|
|
@@ -55,14 +51,14 @@
|
|
|
55
51
|
zIndex?: number;
|
|
56
52
|
} & PathGenParams;
|
|
57
53
|
|
|
58
|
-
export type
|
|
54
|
+
export type DiagramEdgeDef = {
|
|
59
55
|
source: string;
|
|
60
56
|
target: string;
|
|
61
57
|
} & DiagramEdgeParams;
|
|
62
58
|
|
|
63
59
|
export type DiagramProps = {
|
|
64
|
-
nodes: SvelteMap<string,
|
|
65
|
-
edges: SvelteMap<string,
|
|
60
|
+
nodes: SvelteMap<string, DiagramNodeDef>;
|
|
61
|
+
edges: SvelteMap<string, DiagramEdgeDef>;
|
|
66
62
|
children: Snippet;
|
|
67
63
|
};
|
|
68
64
|
|
|
@@ -70,7 +66,7 @@
|
|
|
70
66
|
// export const getNodeOrigin = (node: DiagramNode) => node.origin ?? vector2(0.5, 0.5);
|
|
71
67
|
// export const getNodeOrigin = (node: DiagramNode) => vector2(0.0, 0.0);
|
|
72
68
|
|
|
73
|
-
const getNodeSize = (node:
|
|
69
|
+
const getNodeSize = (node: DiagramNodeDef) => ({
|
|
74
70
|
x: node.width ?? 0,
|
|
75
71
|
y: node.height ?? 0,
|
|
76
72
|
});
|
|
@@ -83,7 +79,7 @@
|
|
|
83
79
|
y1: number,
|
|
84
80
|
x2: number,
|
|
85
81
|
y2: number,
|
|
86
|
-
edge:
|
|
82
|
+
edge: DiagramEdgeDef,
|
|
87
83
|
): string {
|
|
88
84
|
const {
|
|
89
85
|
sourceAnchor: source = Anchor.CENTER_CENTER,
|
|
@@ -154,7 +150,7 @@
|
|
|
154
150
|
}
|
|
155
151
|
|
|
156
152
|
function calculateDimensions(_nodes: typeof nodes) {
|
|
157
|
-
console.time("dim");
|
|
153
|
+
// console.time("dim");
|
|
158
154
|
let newMin = vector2(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
159
155
|
let newMax = vector2(Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
|
|
160
156
|
|
|
@@ -177,7 +173,7 @@
|
|
|
177
173
|
);
|
|
178
174
|
}
|
|
179
175
|
|
|
180
|
-
console.timeEnd("dim");
|
|
176
|
+
// console.timeEnd("dim");
|
|
181
177
|
return { min: newMin, max: newMax };
|
|
182
178
|
}
|
|
183
179
|
|
|
@@ -195,11 +191,12 @@
|
|
|
195
191
|
setContext("nodeMap", () => nodes);
|
|
196
192
|
setContext("edgeMap", () => edges);
|
|
197
193
|
setContext("dimensions", () => dimensions);
|
|
194
|
+
setContext("prerendering", false);
|
|
198
195
|
|
|
199
196
|
let width = $derived(Math.max(dimensions.max.x - dimensions.min.x, 1));
|
|
200
197
|
let height = $derived(Math.max(dimensions.max.y - dimensions.min.y, 1));
|
|
201
198
|
|
|
202
|
-
function generateEdgePath(edge:
|
|
199
|
+
function generateEdgePath(edge: DiagramEdgeDef) {
|
|
203
200
|
const sourceNode = nodes.get(edge.source)!;
|
|
204
201
|
const targetNode = nodes.get(edge.target)!;
|
|
205
202
|
|
|
@@ -221,7 +218,7 @@
|
|
|
221
218
|
);
|
|
222
219
|
}
|
|
223
220
|
|
|
224
|
-
function getNodeAnchor(node:
|
|
221
|
+
function getNodeAnchor(node: DiagramNodeDef, anchor: Vector2) {
|
|
225
222
|
const size = getNodeSize(node);
|
|
226
223
|
|
|
227
224
|
// if (!browser && !eq(anchor, Anchor.CENTER_CENTER) && eq(size, vector2(0, 0))) {
|
|
@@ -236,15 +233,16 @@
|
|
|
236
233
|
return { left, top };
|
|
237
234
|
}
|
|
238
235
|
|
|
236
|
+
// TODO: desperate need for refactoring
|
|
239
237
|
let edgesByZIndexPlane = $derived(
|
|
240
|
-
edges.values().reduce((acc, edge) => {
|
|
238
|
+
Array.from(edges.values()).reduce((acc, edge) => {
|
|
241
239
|
const zIndex = edge.zIndex ?? 0;
|
|
242
240
|
if (!acc.has(zIndex)) {
|
|
243
241
|
acc.set(zIndex, []);
|
|
244
242
|
}
|
|
245
243
|
acc.get(zIndex)!.push(edge);
|
|
246
244
|
return acc;
|
|
247
|
-
}, new Map<number,
|
|
245
|
+
}, new Map<number, DiagramEdgeDef[]>()),
|
|
248
246
|
);
|
|
249
247
|
|
|
250
248
|
// let depthMap = new SvelteMap<number, [DiagramEdge[], DiagramNode[]]>();
|
|
@@ -262,7 +260,7 @@
|
|
|
262
260
|
});
|
|
263
261
|
</script>
|
|
264
262
|
|
|
265
|
-
{#snippet defaultEdge(edge:
|
|
263
|
+
{#snippet defaultEdge(edge: DiagramEdgeDef, edgePath: string)}
|
|
266
264
|
<path
|
|
267
265
|
d={edgePath}
|
|
268
266
|
fill="none"
|
package/dist/Diagram.svelte.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Snippet } from "svelte";
|
|
2
2
|
import { SvelteMap } from "svelte/reactivity";
|
|
3
3
|
import { type Vector2 } from "./diagram-lib.js";
|
|
4
|
-
export interface
|
|
4
|
+
export interface DiagramNodeDef {
|
|
5
5
|
id: string;
|
|
6
6
|
x: number;
|
|
7
7
|
y: number;
|
|
@@ -19,7 +19,7 @@ type PathGenParams = {
|
|
|
19
19
|
center?: Vector2;
|
|
20
20
|
};
|
|
21
21
|
export type DiagramEdgeParams = {
|
|
22
|
-
snippet?: Snippet<[edge:
|
|
22
|
+
snippet?: Snippet<[edge: DiagramEdgeDef, path: string, extra: any]>;
|
|
23
23
|
snippetExtraArg?: any;
|
|
24
24
|
sourceAnchor?: Vector2;
|
|
25
25
|
targetAnchor?: Vector2;
|
|
@@ -27,17 +27,17 @@ export type DiagramEdgeParams = {
|
|
|
27
27
|
style?: string;
|
|
28
28
|
zIndex?: number;
|
|
29
29
|
} & PathGenParams;
|
|
30
|
-
export type
|
|
30
|
+
export type DiagramEdgeDef = {
|
|
31
31
|
source: string;
|
|
32
32
|
target: string;
|
|
33
33
|
} & DiagramEdgeParams;
|
|
34
34
|
export type DiagramProps = {
|
|
35
|
-
nodes: SvelteMap<string,
|
|
36
|
-
edges: SvelteMap<string,
|
|
35
|
+
nodes: SvelteMap<string, DiagramNodeDef>;
|
|
36
|
+
edges: SvelteMap<string, DiagramEdgeDef>;
|
|
37
37
|
children: Snippet;
|
|
38
38
|
};
|
|
39
39
|
declare const Diagram: import("svelte").Component<DiagramProps, {
|
|
40
|
-
generateCurvePath: (x1: number, y1: number, x2: number, y2: number, edge:
|
|
40
|
+
generateCurvePath: (x1: number, y1: number, x2: number, y2: number, edge: DiagramEdgeDef) => string;
|
|
41
41
|
}, "">;
|
|
42
42
|
type Diagram = ReturnType<typeof Diagram>;
|
|
43
43
|
export default Diagram;
|
|
@@ -1,42 +1,105 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { SvelteMap } from "svelte/reactivity";
|
|
3
|
+
import Diagram, {
|
|
4
|
+
type DiagramNodeDef,
|
|
5
|
+
type DiagramEdgeDef,
|
|
6
|
+
} from "./Diagram.svelte";
|
|
7
|
+
import {
|
|
8
|
+
createRawSnippet,
|
|
9
|
+
flushSync,
|
|
10
|
+
onMount,
|
|
11
|
+
setContext,
|
|
12
|
+
type Snippet,
|
|
13
|
+
} from "svelte";
|
|
14
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
15
|
+
import PrerenderDiagram from "./PrerenderDiagram.svelte";
|
|
16
|
+
import { browser } from "./diagram-lib.js";
|
|
7
17
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
let {
|
|
19
|
+
children,
|
|
20
|
+
eagerLoad = false,
|
|
21
|
+
rootMargin = "100px", // start a bit before it enters the viewport
|
|
22
|
+
...rest
|
|
23
|
+
}: {
|
|
24
|
+
children: Snippet;
|
|
25
|
+
eagerLoad: boolean;
|
|
26
|
+
rootMargin?: string;
|
|
27
|
+
} & HTMLAttributes<HTMLDivElement> = $props();
|
|
14
28
|
|
|
15
|
-
|
|
29
|
+
const nodes = new SvelteMap<string, DiagramNodeDef>();
|
|
30
|
+
const layers = new SvelteMap<number, Record<string, DiagramNodeDef>>();
|
|
31
|
+
setContext("layerNodeMap", () => layers);
|
|
32
|
+
const edges = new SvelteMap<string, DiagramEdgeDef>();
|
|
16
33
|
|
|
17
|
-
|
|
34
|
+
let containerEl: HTMLDivElement | undefined;
|
|
18
35
|
|
|
19
|
-
|
|
36
|
+
// If we're SSR (not browser), or eagerLoad is false, render immediately.
|
|
37
|
+
// Otherwise wait until after load + idle + intersection.
|
|
38
|
+
let shouldRender = $derived(!eagerLoad);
|
|
20
39
|
|
|
21
|
-
|
|
22
|
-
// initialDiagramContainer.remove();
|
|
23
|
-
// });
|
|
40
|
+
const initialTime = performance.now();
|
|
24
41
|
|
|
25
|
-
|
|
42
|
+
onMount(() => {
|
|
43
|
+
if (!eagerLoad || !containerEl) return;
|
|
44
|
+
|
|
45
|
+
let io: IntersectionObserver | null = null;
|
|
46
|
+
|
|
47
|
+
const idle = (fn: () => void) => {
|
|
48
|
+
const ric = (window as any).requestIdleCallback as
|
|
49
|
+
| ((cb: () => void) => number)
|
|
50
|
+
| undefined;
|
|
51
|
+
if (ric) ric(fn);
|
|
52
|
+
else setTimeout(fn, 0);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const startObserving = () => {
|
|
56
|
+
// In case the element is already visible at this moment
|
|
57
|
+
io = new IntersectionObserver(
|
|
58
|
+
(entries) => {
|
|
59
|
+
if (entries.some((e) => e.isIntersecting)) {
|
|
60
|
+
shouldRender = true;
|
|
61
|
+
io?.disconnect();
|
|
62
|
+
io = null;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{ root: null, rootMargin, threshold: 0 },
|
|
66
|
+
);
|
|
67
|
+
io.observe(containerEl!);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (document.readyState === "complete") {
|
|
71
|
+
idle(startObserving);
|
|
72
|
+
} else {
|
|
73
|
+
// Wait until the whole document (including images) has loaded
|
|
74
|
+
window.addEventListener("load", () => idle(startObserving), {
|
|
75
|
+
once: true,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return () => {
|
|
80
|
+
io?.disconnect();
|
|
81
|
+
io = null;
|
|
82
|
+
};
|
|
83
|
+
});
|
|
26
84
|
</script>
|
|
27
85
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<!--
|
|
33
|
-
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<!--
|
|
86
|
+
{#if shouldRender}
|
|
87
|
+
<!-- first pass: register all the nodes and edges -->
|
|
88
|
+
<PrerenderDiagram {nodes} {edges} {children} />
|
|
89
|
+
|
|
90
|
+
<!-- second pass: render with computed positions -->
|
|
91
|
+
<div {...rest}>
|
|
92
|
+
<Diagram {nodes} {edges}>
|
|
93
|
+
{@render children()}
|
|
94
|
+
</Diagram>
|
|
95
|
+
</div>
|
|
96
|
+
{:else}
|
|
97
|
+
<!-- Lightweight placeholder / container used for intersection observation. -->
|
|
98
|
+
<div bind:this={containerEl} {...rest}></div>
|
|
99
|
+
{/if}
|
|
100
|
+
|
|
101
|
+
<!--
|
|
40
102
|
<svelte:boundary>
|
|
41
103
|
{@const _ = console.log('Finished rendering diagram in', performance.now() - initialTime, 'ms')}
|
|
42
|
-
</svelte:boundary>
|
|
104
|
+
</svelte:boundary>
|
|
105
|
+
-->
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { type Snippet } from
|
|
2
|
-
import type { HTMLAttributes } from
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
3
|
type $$ComponentProps = {
|
|
4
4
|
children: Snippet;
|
|
5
|
+
eagerLoad: boolean;
|
|
6
|
+
rootMargin?: string;
|
|
5
7
|
} & HTMLAttributes<HTMLDivElement>;
|
|
6
8
|
declare const DiagramController: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
9
|
type DiagramController = ReturnType<typeof DiagramController>;
|
package/dist/DiagramNode.svelte
CHANGED
|
@@ -1,47 +1,35 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
getContext,
|
|
4
|
-
onMount,
|
|
5
|
-
tick,
|
|
6
|
-
type ComponentProps,
|
|
7
|
-
type Snippet,
|
|
8
|
-
} from "svelte";
|
|
2
|
+
import { getContext, onMount, tick, type Snippet } from "svelte";
|
|
9
3
|
import type {
|
|
10
|
-
|
|
4
|
+
DiagramNodeDef,
|
|
11
5
|
DiagramEdgeParams,
|
|
12
|
-
|
|
6
|
+
DiagramEdgeDef,
|
|
13
7
|
} from "./Diagram.svelte";
|
|
14
8
|
import type { SvelteMap } from "svelte/reactivity";
|
|
15
9
|
import type { HTMLAttributes } from "svelte/elements";
|
|
16
|
-
import { vector2, type Vector2 } from "./diagram-lib";
|
|
10
|
+
import { vector2, type Vector2 } from "./diagram-lib.js";
|
|
17
11
|
|
|
18
|
-
// type IndividualConnectActionParam = string | Omit<DiagramEdge, 'source'>;
|
|
19
12
|
type IndividualConnectActionParam =
|
|
20
13
|
| string
|
|
21
|
-
| (DiagramEdgeParams & { target: string })
|
|
22
|
-
type IndividualConnectSourceActionParam =
|
|
23
|
-
| string
|
|
14
|
+
| (DiagramEdgeParams & { target: string })
|
|
24
15
|
| (DiagramEdgeParams & { source: string });
|
|
16
|
+
|
|
25
17
|
type DiagramNodeConnectParam =
|
|
26
18
|
| IndividualConnectActionParam
|
|
27
19
|
| IndividualConnectActionParam[];
|
|
28
|
-
type DiagramNodeConnectSourceParam =
|
|
29
|
-
| IndividualConnectSourceActionParam
|
|
30
|
-
| IndividualConnectSourceActionParam[];
|
|
31
20
|
|
|
32
21
|
export type DiagramNodeProps = {
|
|
33
22
|
children?: Snippet;
|
|
34
23
|
connect?: DiagramNodeConnectParam;
|
|
35
|
-
connectSource?: DiagramNodeConnectSourceParam;
|
|
36
24
|
autosize?: boolean;
|
|
37
25
|
origin?: Vector2;
|
|
38
|
-
} & Omit<
|
|
26
|
+
} & Omit<DiagramNodeDef, "snippet"> &
|
|
39
27
|
HTMLAttributes<HTMLDivElement>;
|
|
40
28
|
|
|
41
29
|
let {
|
|
42
30
|
children,
|
|
43
31
|
connect,
|
|
44
|
-
connectSource: connectFrom,
|
|
32
|
+
// connectSource: connectFrom,
|
|
45
33
|
id,
|
|
46
34
|
x,
|
|
47
35
|
y,
|
|
@@ -56,11 +44,12 @@
|
|
|
56
44
|
}: DiagramNodeProps = $props();
|
|
57
45
|
|
|
58
46
|
const nodeMap = (
|
|
59
|
-
getContext("nodeMap") as () => SvelteMap<string,
|
|
47
|
+
getContext("nodeMap") as () => SvelteMap<string, DiagramNodeDef>
|
|
60
48
|
)();
|
|
61
49
|
const edgeMap = (
|
|
62
|
-
getContext("edgeMap") as () => SvelteMap<string,
|
|
50
|
+
getContext("edgeMap") as () => SvelteMap<string, DiagramEdgeDef>
|
|
63
51
|
)();
|
|
52
|
+
|
|
64
53
|
let dimensions = (
|
|
65
54
|
getContext("dimensions") as () =>
|
|
66
55
|
| { min: Vector2; max: Vector2 }
|
|
@@ -76,8 +65,7 @@
|
|
|
76
65
|
y: y - (origin?.y ?? 0.5) * (height ?? 0),
|
|
77
66
|
});
|
|
78
67
|
|
|
79
|
-
|
|
80
|
-
const nodeDef: DiagramNode = $derived({
|
|
68
|
+
const nodeDef: DiagramNodeDef = $derived({
|
|
81
69
|
id,
|
|
82
70
|
x: absolutePosition.x,
|
|
83
71
|
y: absolutePosition.y,
|
|
@@ -86,17 +74,8 @@
|
|
|
86
74
|
clientOnly: clientOnly || autosize,
|
|
87
75
|
snippet: children,
|
|
88
76
|
});
|
|
89
|
-
const prerender = $state.snapshot(getContext("prerendering"));
|
|
90
|
-
|
|
91
|
-
// console.log('set', nodeDef.id, nodeDef);
|
|
92
|
-
|
|
93
|
-
// const source = elementNodeMap.get(element);
|
|
94
|
-
// if (!source) {
|
|
95
|
-
// return console.warn(`Could not find source element for`, element);
|
|
96
|
-
// }
|
|
97
77
|
|
|
98
78
|
let mounted = $state(false);
|
|
99
|
-
|
|
100
79
|
const previousEdgeIds = new Set();
|
|
101
80
|
|
|
102
81
|
// TODO: this should be done only if clientWidth is needed
|
|
@@ -108,63 +87,59 @@
|
|
|
108
87
|
// nodeDef.y -= (origin?.y ?? 0) * $state.snapshot(nodeDef.height ?? 0);
|
|
109
88
|
// }
|
|
110
89
|
|
|
111
|
-
|
|
112
|
-
|
|
90
|
+
onMount(async () => {
|
|
91
|
+
if (autosize) {
|
|
113
92
|
width = clientWidth;
|
|
114
93
|
height = clientHeight;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
94
|
+
}
|
|
117
95
|
|
|
118
|
-
onMount(async () => {
|
|
119
96
|
if (nodeDef.clientOnly) {
|
|
120
97
|
await tick();
|
|
121
98
|
mounted = true;
|
|
122
99
|
}
|
|
123
100
|
});
|
|
124
101
|
|
|
125
|
-
function updateEdgeFrom(
|
|
126
|
-
param: IndividualConnectSourceActionParam,
|
|
127
|
-
index: number = 0,
|
|
128
|
-
) {
|
|
129
|
-
const getEdgeId = (source: string, i: number) =>
|
|
130
|
-
`${source}:${nodeDef.id}:(${i})`;
|
|
131
|
-
// const edgeId = getEdgeId();
|
|
132
|
-
if (typeof param == "string") {
|
|
133
|
-
const source = param;
|
|
134
|
-
|
|
135
|
-
const edgeId = getEdgeId(source, index);
|
|
136
|
-
previousEdgeIds.add(edgeId);
|
|
137
|
-
|
|
138
|
-
edgeMap.set(edgeId, { target: nodeDef.id, source });
|
|
139
|
-
} else {
|
|
140
|
-
const edgeId = getEdgeId(param.source, index);
|
|
141
|
-
previousEdgeIds.add(edgeId);
|
|
142
|
-
|
|
143
|
-
(param as DiagramEdge).target = nodeDef.id;
|
|
144
|
-
edgeMap.set(edgeId, param as DiagramEdge);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
102
|
function updateEdge(
|
|
149
103
|
param: IndividualConnectActionParam,
|
|
150
104
|
index: number = 0,
|
|
151
105
|
) {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
106
|
+
const selfId = nodeDef.id;
|
|
107
|
+
const getEdgeId = ({
|
|
108
|
+
source,
|
|
109
|
+
target,
|
|
110
|
+
index,
|
|
111
|
+
}: {
|
|
112
|
+
source: string;
|
|
113
|
+
target: string;
|
|
114
|
+
index: number;
|
|
115
|
+
}) => `${source}:${target}:(${index})`;
|
|
116
|
+
|
|
155
117
|
if (typeof param == "string") {
|
|
156
118
|
const target = param;
|
|
157
119
|
|
|
158
|
-
const edgeId = getEdgeId(target, index);
|
|
120
|
+
const edgeId = getEdgeId({ source: selfId, target, index });
|
|
159
121
|
previousEdgeIds.add(edgeId);
|
|
160
122
|
|
|
161
123
|
edgeMap.set(edgeId, { source: nodeDef.id, target });
|
|
162
|
-
} else {
|
|
163
|
-
const edgeId = getEdgeId(param.target, index);
|
|
124
|
+
} else if ("target" in param) {
|
|
125
|
+
// const edgeId = getEdgeId(param.target, index);
|
|
126
|
+
const edgeId = getEdgeId({
|
|
127
|
+
source: selfId,
|
|
128
|
+
target: param.target,
|
|
129
|
+
index,
|
|
130
|
+
});
|
|
164
131
|
previousEdgeIds.add(edgeId);
|
|
165
|
-
|
|
166
|
-
(param as
|
|
167
|
-
|
|
132
|
+
(param as DiagramEdgeDef).source = nodeDef.id;
|
|
133
|
+
edgeMap.set(edgeId, param as DiagramEdgeDef);
|
|
134
|
+
} else if ("source" in param) {
|
|
135
|
+
const edgeId = getEdgeId({
|
|
136
|
+
source: param.source,
|
|
137
|
+
target: selfId,
|
|
138
|
+
index,
|
|
139
|
+
});
|
|
140
|
+
previousEdgeIds.add(edgeId);
|
|
141
|
+
(param as DiagramEdgeDef).target = selfId;
|
|
142
|
+
edgeMap.set(edgeId, param as DiagramEdgeDef);
|
|
168
143
|
}
|
|
169
144
|
}
|
|
170
145
|
|
|
@@ -176,28 +151,14 @@
|
|
|
176
151
|
}
|
|
177
152
|
}
|
|
178
153
|
|
|
179
|
-
if (connectFrom) {
|
|
180
|
-
if (!Array.isArray(connectFrom)) {
|
|
181
|
-
updateEdgeFrom(connectFrom);
|
|
182
|
-
} else {
|
|
183
|
-
connectFrom.forEach(updateEdgeFrom);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
154
|
let left = $derived(nodeDef.x - (dimensions?.min.x ?? 0));
|
|
188
155
|
let top = $derived(nodeDef.y - (dimensions?.min.y ?? 0));
|
|
189
156
|
|
|
157
|
+
nodeMap.set(nodeDef.id, nodeDef);
|
|
190
158
|
$effect(() => {
|
|
191
159
|
nodeMap.set(nodeDef.id, nodeDef);
|
|
192
160
|
});
|
|
193
161
|
|
|
194
|
-
// nodeMap.set(nodeDef.id, nodeDef);
|
|
195
|
-
$effect(() => {
|
|
196
|
-
// nodeDef.x -= (origin?.x ?? 0.5) * (nodeDef?.width ?? clientWidth ?? 0);
|
|
197
|
-
// nodeDef.y -= (origin?.y ?? 0.5) * (nodeDef?.height ?? clientHeight ?? 0);
|
|
198
|
-
nodeMap.set(nodeDef.id, nodeDef);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
162
|
// $inspect(
|
|
202
163
|
// 'mounted render diagramNode',
|
|
203
164
|
// nodeDef.id,
|
|
@@ -226,6 +187,5 @@ top:${top}px;left:${left}px;${nodeDef.clientOnly && !mounted ? "opacity:0" : ""}
|
|
|
226
187
|
bind:clientHeight
|
|
227
188
|
></div>
|
|
228
189
|
{/if}
|
|
229
|
-
<!-- <div class="absolute top-0 left-0 w-full h-full bg-red-500"> {nodeDef.clientOnly} {mounted}</div> -->
|
|
230
190
|
{@render children?.()}
|
|
231
191
|
</div>
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import { type Snippet } from "svelte";
|
|
2
|
-
import type {
|
|
2
|
+
import type { DiagramNodeDef, DiagramEdgeParams } from "./Diagram.svelte";
|
|
3
3
|
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
-
import { type Vector2 } from "./diagram-lib";
|
|
4
|
+
import { type Vector2 } from "./diagram-lib.js";
|
|
5
5
|
type IndividualConnectActionParam = string | (DiagramEdgeParams & {
|
|
6
6
|
target: string;
|
|
7
|
-
})
|
|
8
|
-
type IndividualConnectSourceActionParam = string | (DiagramEdgeParams & {
|
|
7
|
+
}) | (DiagramEdgeParams & {
|
|
9
8
|
source: string;
|
|
10
9
|
});
|
|
11
10
|
type DiagramNodeConnectParam = IndividualConnectActionParam | IndividualConnectActionParam[];
|
|
12
|
-
type DiagramNodeConnectSourceParam = IndividualConnectSourceActionParam | IndividualConnectSourceActionParam[];
|
|
13
11
|
export type DiagramNodeProps = {
|
|
14
12
|
children?: Snippet;
|
|
15
13
|
connect?: DiagramNodeConnectParam;
|
|
16
|
-
connectSource?: DiagramNodeConnectSourceParam;
|
|
17
14
|
autosize?: boolean;
|
|
18
15
|
origin?: Vector2;
|
|
19
|
-
} & Omit<
|
|
20
|
-
declare const DiagramNode:
|
|
16
|
+
} & Omit<DiagramNodeDef, "snippet"> & HTMLAttributes<HTMLDivElement>;
|
|
17
|
+
declare const DiagramNode: import("svelte").Component<DiagramNodeProps, {}, "">;
|
|
21
18
|
type DiagramNode = ReturnType<typeof DiagramNode>;
|
|
22
19
|
export default DiagramNode;
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { setContext } from "svelte";
|
|
3
|
+
import type { DiagramProps } from "./Diagram.svelte";
|
|
4
|
+
import { vector2 } from "./diagram-lib.js";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
let { nodes, edges, children }: DiagramProps = $props();
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
setContext("nodeMap", () => nodes);
|
|
9
|
+
setContext("edgeMap", () => edges);
|
|
10
|
+
setContext("dimensions", () => ({
|
|
11
|
+
min: vector2(0, 0),
|
|
12
|
+
max: vector2(0, 0),
|
|
13
|
+
}));
|
|
14
|
+
setContext("prerendering", true);
|
|
12
15
|
</script>
|
|
13
16
|
|
|
14
17
|
<template>
|
|
15
|
-
|
|
18
|
+
{@render children()}
|
|
16
19
|
</template>
|
|
17
20
|
|
|
18
|
-
<svelte:boundary>
|
|
21
|
+
<svelte:boundary>
|
|
22
|
+
{@const _ = setContext("prerendering", false)}
|
|
23
|
+
</svelte:boundary>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DiagramProps } from
|
|
1
|
+
import type { DiagramProps } from "./Diagram.svelte";
|
|
2
2
|
declare const PrerenderDiagram: import("svelte").Component<DiagramProps, {}, "">;
|
|
3
3
|
type PrerenderDiagram = ReturnType<typeof PrerenderDiagram>;
|
|
4
4
|
export default PrerenderDiagram;
|
package/dist/diagram-lib.d.ts
CHANGED
package/dist/diagram-lib.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export const browser = !!globalThis?.window;
|
|
2
|
+
export const dev = globalThis?.process?.env?.NODE_ENV &&
|
|
3
|
+
!globalThis?.process.env.NODE_ENV.toLowerCase().startsWith("prod");
|
|
1
4
|
export const vector2 = (x, y) => ({ x, y });
|
|
2
5
|
export const eq = (a, b) => a.x === b.x && a.y === b.y;
|
|
3
6
|
export const Anchor = {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export { default as DiagramController } from './DiagramController.svelte';
|
|
|
3
3
|
export { default as DiagramNode } from './DiagramNode.svelte';
|
|
4
4
|
export { default as PrerenderDiagram } from './PrerenderDiagram.svelte';
|
|
5
5
|
export * from './diagram-lib.js';
|
|
6
|
-
export type {
|
|
6
|
+
export type { DiagramEdgeDef as DiagramEdge, DiagramEdgeParams, DiagramProps } from './Diagram.svelte';
|
|
7
7
|
export type { DiagramNodeProps } from './DiagramNode.svelte';
|
package/package.json
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
2
|
+
"name": "@cnvx/nodal",
|
|
3
|
+
"private": false,
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite dev",
|
|
8
|
+
"dev-lib": "svelte-package -w",
|
|
9
|
+
"build": "vite build && npm run prepack",
|
|
10
|
+
"preview": "vite preview",
|
|
11
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
12
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
13
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
14
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
15
|
+
"test:capture": "bun run test/capture.ts",
|
|
16
|
+
"format": "prettier --write .",
|
|
17
|
+
"lint": "prettier --check . && eslint ."
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"!dist/**/*.test.*",
|
|
22
|
+
"!dist/**/*.spec.*"
|
|
23
|
+
],
|
|
24
|
+
"sideEffects": [
|
|
25
|
+
"**/*.css"
|
|
26
|
+
],
|
|
27
|
+
"svelte": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"type": "module",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"svelte": "./dist/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"svelte": "^5.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@eslint/compat": "^1.2.5",
|
|
41
|
+
"@eslint/js": "^9.18.0",
|
|
42
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
43
|
+
"@sveltejs/adapter-static": "^3.0.8",
|
|
44
|
+
"@sveltejs/kit": "^2.22.0",
|
|
45
|
+
"@sveltejs/package": "^2.0.0",
|
|
46
|
+
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
|
47
|
+
"@tailwindcss/vite": "^4.1.11",
|
|
48
|
+
"@types/bun": "^1.2.19",
|
|
49
|
+
"@types/pixelmatch": "^5.2.6",
|
|
50
|
+
"@types/pngjs": "^6.0.5",
|
|
51
|
+
"eslint": "^9.18.0",
|
|
52
|
+
"eslint-config-prettier": "^10.0.1",
|
|
53
|
+
"eslint-plugin-svelte": "^3.0.0",
|
|
54
|
+
"globals": "^16.0.0",
|
|
55
|
+
"mdsvex": "^0.12.6",
|
|
56
|
+
"pixelmatch": "^7.1.0",
|
|
57
|
+
"playwright": "^1.53.0",
|
|
58
|
+
"prettier": "^3.4.2",
|
|
59
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
60
|
+
"publint": "^0.3.2",
|
|
61
|
+
"svelte": "^5.0.0",
|
|
62
|
+
"svelte-check": "^4.0.0",
|
|
63
|
+
"typescript": "^5.0.0",
|
|
64
|
+
"typescript-eslint": "^8.20.0",
|
|
65
|
+
"vite": "^7.0.4"
|
|
66
|
+
},
|
|
67
|
+
"keywords": [
|
|
68
|
+
"svelte",
|
|
69
|
+
"diagram",
|
|
70
|
+
"nodes",
|
|
71
|
+
"graph",
|
|
72
|
+
"visualization",
|
|
73
|
+
"flow-chart",
|
|
74
|
+
"connections",
|
|
75
|
+
"bezier",
|
|
76
|
+
"smoothstep"
|
|
77
|
+
],
|
|
78
|
+
"repository": {
|
|
79
|
+
"type": "git",
|
|
80
|
+
"url": "git+https://github.com/Convex-Works/nodal.git"
|
|
81
|
+
},
|
|
82
|
+
"dependencies": {}
|
|
83
83
|
}
|