@cnvx/nodal 0.0.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/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Svelte library
2
+
3
+ Everything you need to build a Svelte library, powered by [`sv`](https://npmjs.com/package/sv).
4
+
5
+ Read more about creating a library [in the docs](https://svelte.dev/docs/kit/packaging).
6
+
7
+ ## Creating a project
8
+
9
+ If you're seeing this, you've probably already done this step. Congrats!
10
+
11
+ ```bash
12
+ # create a new project in the current directory
13
+ npx sv create
14
+
15
+ # create a new project in my-app
16
+ npx sv create my-app
17
+ ```
18
+
19
+ ## Developing
20
+
21
+ Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
22
+
23
+ ```bash
24
+ npm run dev
25
+
26
+ # or start the server and open the app in a new browser tab
27
+ npm run dev -- --open
28
+ ```
29
+
30
+ Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
31
+
32
+ ## Building
33
+
34
+ To build your library:
35
+
36
+ ```bash
37
+ npm run package
38
+ ```
39
+
40
+ To create a production version of your showcase app:
41
+
42
+ ```bash
43
+ npm run build
44
+ ```
45
+
46
+ You can preview the production build with `npm run preview`.
47
+
48
+ > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
49
+
50
+ ## Publishing
51
+
52
+ Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
53
+
54
+ To publish your library to [npm](https://www.npmjs.com):
55
+
56
+ ```bash
57
+ npm publish
58
+ ```
@@ -0,0 +1,324 @@
1
+ <script lang="ts" module>
2
+ import { browser, dev } from "$app/environment";
3
+ import { onMount, setContext, type Snippet } from "svelte";
4
+ import { SvelteMap } from "svelte/reactivity";
5
+ import {
6
+ debugSide,
7
+ eq,
8
+ getBezierPath,
9
+ getSmoothStepPath,
10
+ normaliseAngle,
11
+ Side,
12
+ sideForAngle,
13
+ unitVectorFromAngle,
14
+ vector2,
15
+ type Vector2,
16
+ Anchor,
17
+ } from "./diagram-lib";
18
+ import { draw } from "svelte/transition";
19
+
20
+ export interface DiagramNode {
21
+ id: string;
22
+ x: number;
23
+ y: number;
24
+ width?: number;
25
+ height?: number;
26
+
27
+ // this will make the node and all of its connections only client side
28
+ clientOnly?: boolean;
29
+ }
30
+
31
+ type PathGenParams =
32
+ | {
33
+ pathGen?: "bezier";
34
+ curvature?: number;
35
+ offset?: Vector2;
36
+ }
37
+ | {
38
+ pathGen: "smoothstep";
39
+ borderRadius?: number;
40
+ center?: Vector2;
41
+ };
42
+
43
+ export type DiagramEdgeParams = {
44
+ // target: string;
45
+ snippet?: Snippet<[edge: DiagramEdge, path: string, extra: any]>;
46
+ snippetExtraArg?: any;
47
+
48
+ sourceAnchor?: Vector2;
49
+ targetAnchor?: Vector2;
50
+
51
+ class?: string;
52
+ style?: string;
53
+ zIndex?: number;
54
+ } & PathGenParams;
55
+
56
+ export type DiagramEdge = {
57
+ source: string;
58
+ target: string;
59
+ } & DiagramEdgeParams;
60
+
61
+ export type DiagramProps = {
62
+ nodes: SvelteMap<string, DiagramNode>;
63
+ edges: SvelteMap<string, DiagramEdge>;
64
+ children: Snippet;
65
+ };
66
+
67
+ // const getNodeOrigin = (node: DiagramNode) => node.origin ?? vector2(0.0, 0.5);
68
+ // export const getNodeOrigin = (node: DiagramNode) => node.origin ?? vector2(0.5, 0.5);
69
+ // export const getNodeOrigin = (node: DiagramNode) => vector2(0.0, 0.0);
70
+
71
+ const getNodeSize = (node: DiagramNode) => ({
72
+ x: node.width ?? 0,
73
+ y: node.height ?? 0,
74
+ });
75
+ </script>
76
+
77
+ <script lang="ts">
78
+ let { nodes, edges, children }: DiagramProps = $props();
79
+ export function generateCurvePath(
80
+ x1: number,
81
+ y1: number,
82
+ x2: number,
83
+ y2: number,
84
+ edge: DiagramEdge,
85
+ ): string {
86
+ const {
87
+ sourceAnchor: source = Anchor.CENTER_CENTER,
88
+ targetAnchor: dest = Anchor.CENTER_CENTER,
89
+ } = edge;
90
+
91
+ function anchorToRads({ x, y }: Vector2) {
92
+ // vector from the node’s centre (0.5, 0.5)
93
+ const dx = x - 0.5; // + right
94
+ const dy = 0.5 - y; // + up (flip screen-y for math coords)
95
+ const raw = Math.atan2(dy, dx); // signed angle
96
+ return normaliseAngle(raw);
97
+ }
98
+
99
+ // CRUCIAL TO INVEST THE Y DIRECTION SINCE IT GOES FROM
100
+ // NEGATIVE TO POSITIVE!!!!!!!!!!!!!
101
+ const leaveAngle = Math.atan2(y1 - y2, x2 - x1);
102
+ const arriveAngle = leaveAngle + Math.PI;
103
+
104
+ const sourcePosition = eq(source, Anchor.CENTER_CENTER)
105
+ ? sideForAngle(leaveAngle)
106
+ : sideForAngle(anchorToRads(source));
107
+
108
+ const targetPosition = eq(dest, Anchor.CENTER_CENTER)
109
+ ? sideForAngle(arriveAngle)
110
+ : sideForAngle(anchorToRads(dest));
111
+
112
+ const props = {
113
+ sourceX: x1,
114
+ sourceY: y1,
115
+ sourcePosition: sourcePosition,
116
+
117
+ targetX: x2,
118
+ targetY: y2,
119
+ targetPosition: targetPosition,
120
+ };
121
+
122
+ edge.pathGen ??= "bezier";
123
+ if (edge.pathGen == "bezier") {
124
+ return getBezierPath({
125
+ ...props,
126
+ curvature: edge.curvature ?? 0.25,
127
+ })[0];
128
+ } else if (edge.pathGen == "smoothstep") {
129
+ const pathgenParams = {
130
+ borderRadius: edge?.borderRadius ?? 15,
131
+ center: edge?.center,
132
+ };
133
+
134
+ // calculate the absolute center from the relative center
135
+ let centerX = undefined,
136
+ centerY = undefined;
137
+
138
+ if (pathgenParams.center) {
139
+ centerX = x1 + pathgenParams.center.x * (x2 - x1);
140
+ centerY = y1 + pathgenParams.center.y * (y2 - y1);
141
+ }
142
+
143
+ return getSmoothStepPath({
144
+ ...props,
145
+ borderRadius: pathgenParams.borderRadius,
146
+ centerX,
147
+ centerY,
148
+ })[0];
149
+ }
150
+
151
+ throw new Error("unreachable");
152
+ }
153
+
154
+ function calculateDimensions(_nodes: typeof nodes) {
155
+ console.time("dim");
156
+ let newMin = vector2(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
157
+ let newMax = vector2(Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
158
+
159
+ for (let node of _nodes.values()) {
160
+ // if (!browser && node.clientOnly) continue;
161
+
162
+ const size = getNodeSize(node);
163
+ // const origin = getNodeOrigin(node);
164
+
165
+ const left = node.x;
166
+ const top = node.y;
167
+
168
+ const right = left + size.x;
169
+ const bottom = top + size.y;
170
+
171
+ newMin = vector2(Math.min(newMin.x, left), Math.min(newMin.y, top));
172
+ newMax = vector2(
173
+ Math.max(newMax.x, right),
174
+ Math.max(newMax.y, bottom),
175
+ );
176
+ }
177
+
178
+ console.timeEnd("dim");
179
+ return { min: newMin, max: newMax };
180
+ }
181
+
182
+ // const dimensions = $derived(calculateDimensions(nodes));
183
+ // let dimensions = $derived(calculateDimensions(nodes));
184
+ let dimensions = $state(calculateDimensions(nodes));
185
+ $effect(() => {
186
+ const newDimensions = calculateDimensions(nodes);
187
+ dimensions.min = newDimensions.min;
188
+ dimensions.max = newDimensions.max;
189
+ });
190
+ // let dimensions = calculateDimensions(nodes);
191
+ // onMount(() => (dimensions = calculateDimensions(nodes)));
192
+
193
+ setContext("nodeMap", () => nodes);
194
+ setContext("edgeMap", () => edges);
195
+ setContext("dimensions", () => dimensions);
196
+
197
+ let width = $derived(Math.max(dimensions.max.x - dimensions.min.x, 1));
198
+ let height = $derived(Math.max(dimensions.max.y - dimensions.min.y, 1));
199
+
200
+ function generateEdgePath(edge: DiagramEdge) {
201
+ const sourceNode = nodes.get(edge.source)!;
202
+ const targetNode = nodes.get(edge.target)!;
203
+
204
+ const sourceAnchor = getNodeAnchor(
205
+ sourceNode,
206
+ edge.sourceAnchor ?? Anchor.CENTER_CENTER,
207
+ );
208
+ const targetAnchor = getNodeAnchor(
209
+ targetNode,
210
+ edge.targetAnchor ?? Anchor.CENTER_CENTER,
211
+ );
212
+
213
+ return generateCurvePath(
214
+ sourceAnchor.left,
215
+ sourceAnchor.top,
216
+ targetAnchor.left,
217
+ targetAnchor.top,
218
+ edge,
219
+ );
220
+ }
221
+
222
+ function getNodeAnchor(node: DiagramNode, anchor: Vector2) {
223
+ const size = getNodeSize(node);
224
+
225
+ // if (!browser && !eq(anchor, Anchor.CENTER_CENTER) && eq(size, vector2(0, 0))) {
226
+ // throw new Error(
227
+ // `To use anchor other than CENTER,CENTER please set the width and height of the node explicitly or set autosize to true for the node\n\nNode '${node.id}' does not have explicity width or height and thus cannot be connected with a relative anchor`
228
+ // );
229
+ // }
230
+
231
+ const left = node.x - dimensions.min.x + anchor.x * size.x;
232
+ const top = node.y - dimensions.min.y + anchor.y * size.y;
233
+
234
+ return { left, top };
235
+ }
236
+
237
+ let edgesByZIndexPlane = $derived(
238
+ edges.values().reduce((acc, edge) => {
239
+ const zIndex = edge.zIndex ?? 0;
240
+ if (!acc.has(zIndex)) {
241
+ acc.set(zIndex, []);
242
+ }
243
+ acc.get(zIndex)!.push(edge);
244
+ return acc;
245
+ }, new Map<number, DiagramEdge[]>()),
246
+ );
247
+
248
+ // let depthMap = new SvelteMap<number, [DiagramEdge[], DiagramNode[]]>();
249
+ // $effect(() => {
250
+ // if (nodes || edges) {
251
+ // depthMap.clear();
252
+ // for (const node of nodes.values()) {
253
+ // // depthMap.has(node.zIndex)
254
+ // }
255
+ // }
256
+ // });
257
+
258
+ onMount(() => {
259
+ dimensions = calculateDimensions(nodes);
260
+ });
261
+ </script>
262
+
263
+ {#snippet defaultEdge(edge: DiagramEdge, edgePath: string)}
264
+ <path
265
+ d={edgePath}
266
+ fill="none"
267
+ stroke="currentColor"
268
+ class={edge.class}
269
+ vector-effect="non-scaling-stroke"
270
+ stroke-linecap="round"
271
+ stroke-linejoin="round"
272
+ shape-rendering="smooth"
273
+ style={edge.style}
274
+ />
275
+ {/snippet}
276
+
277
+ <figure
278
+ aria-label="Diagram"
279
+ aria-hidden={!dev}
280
+ inert={!dev}
281
+ role="img"
282
+ style="position:relative;width:{width}px;height:{height}px;overflow:show;user-select:none;"
283
+ >
284
+ <!-- <svg class="absolute top-0 right-0 bottom-0 left-0 z-0 h-full w-full overflow-visible"> -->
285
+ <!-- {#each edges.values() as edge, i}
286
+ {@const sourceNode = nodes.get(edge.source)}
287
+ {@const targetNode = nodes.get(edge.target)}
288
+
289
+ {#if sourceNode && targetNode && !(!browser && (sourceNode.clientOnly || targetNode.clientOnly))}
290
+ <svg
291
+ class="absolute top-0 right-0 bottom-0 left-0 h-full w-full overflow-visible"
292
+ style="z-index:{edge.zIndex ?? 0};"
293
+ >
294
+ {@render (edge.snippet ? edge.snippet : defaultEdge)(
295
+ edge,
296
+ generateEdgePath(edge),
297
+ edge.snippetExtraArg
298
+ )}
299
+ </svg>
300
+ {/if}
301
+ {/each} -->
302
+ <!-- </svg> -->
303
+
304
+ {#each edgesByZIndexPlane as [zIndex, edges]}
305
+ <svg
306
+ shape-rendering="crispEdges"
307
+ style="z-index:{zIndex};position:absolute;top:0;right:0;bottom:0;left:0;height:100%;width:100%;overflow:visible;"
308
+ >
309
+ {#each edges as edge}
310
+ {@const sourceNode = nodes.get(edge.source)}
311
+ {@const targetNode = nodes.get(edge.target)}
312
+ {#if sourceNode && targetNode && !(!browser && (sourceNode.clientOnly || targetNode.clientOnly))}
313
+ {@render (edge.snippet ? edge.snippet : defaultEdge)(
314
+ edge,
315
+ generateEdgePath(edge),
316
+ edge.snippetExtraArg,
317
+ )}
318
+ {/if}
319
+ {/each}
320
+ </svg>
321
+ {/each}
322
+
323
+ {@render children()}
324
+ </figure>
@@ -0,0 +1,43 @@
1
+ import { type Snippet } from "svelte";
2
+ import { SvelteMap } from "svelte/reactivity";
3
+ import { type Vector2 } from "./diagram-lib";
4
+ export interface DiagramNode {
5
+ id: string;
6
+ x: number;
7
+ y: number;
8
+ width?: number;
9
+ height?: number;
10
+ clientOnly?: boolean;
11
+ }
12
+ type PathGenParams = {
13
+ pathGen?: "bezier";
14
+ curvature?: number;
15
+ offset?: Vector2;
16
+ } | {
17
+ pathGen: "smoothstep";
18
+ borderRadius?: number;
19
+ center?: Vector2;
20
+ };
21
+ export type DiagramEdgeParams = {
22
+ snippet?: Snippet<[edge: DiagramEdge, path: string, extra: any]>;
23
+ snippetExtraArg?: any;
24
+ sourceAnchor?: Vector2;
25
+ targetAnchor?: Vector2;
26
+ class?: string;
27
+ style?: string;
28
+ zIndex?: number;
29
+ } & PathGenParams;
30
+ export type DiagramEdge = {
31
+ source: string;
32
+ target: string;
33
+ } & DiagramEdgeParams;
34
+ export type DiagramProps = {
35
+ nodes: SvelteMap<string, DiagramNode>;
36
+ edges: SvelteMap<string, DiagramEdge>;
37
+ children: Snippet;
38
+ };
39
+ declare const Diagram: import("svelte").Component<DiagramProps, {
40
+ generateCurvePath: (x1: number, y1: number, x2: number, y2: number, edge: DiagramEdge) => string;
41
+ }, "">;
42
+ type Diagram = ReturnType<typeof Diagram>;
43
+ export default Diagram;
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import { SvelteMap } from 'svelte/reactivity';
3
+ import Diagram, { type DiagramNode, type DiagramEdge } from './Diagram.svelte';
4
+ import { createRawSnippet, flushSync, onMount, setContext, type Snippet } from 'svelte';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import PrerenderDiagram from './PrerenderDiagram.svelte';
7
+
8
+ let {
9
+ children,
10
+ ...rest
11
+ }: {
12
+ children: Snippet;
13
+ } & HTMLAttributes<HTMLDivElement> = $props();
14
+
15
+ const nodes = new SvelteMap<string, DiagramNode>();
16
+
17
+ const edges = new SvelteMap<string, DiagramEdge>();
18
+
19
+ // let initialDiagramContainer: HTMLElement;
20
+
21
+ // onMount(() => {
22
+ // initialDiagramContainer.remove();
23
+ // });
24
+
25
+ const initialTime = performance.now();
26
+ </script>
27
+
28
+ <!-- first time to register all the nodes and edges -->
29
+ <PrerenderDiagram {nodes} {edges} {children} />
30
+
31
+ <!-- second time to actually render it after we calculate all the relative positions and total widths and heights -->
32
+ <!-- this is necessary because we need to calculate the relative positions of the nodes and edges -->
33
+ <!-- TODO: Investigate how to do this properly with hydrate and mount and render https://svelte.dev/docs/svelte/imperative-component-api#hydrate -->
34
+ <div {...rest}>
35
+ <Diagram {nodes} {edges}>
36
+ {@render children()}
37
+ </Diagram>
38
+ </div>
39
+ <!--
40
+ <svelte:boundary>
41
+ {@const _ = console.log('Finished rendering diagram in', performance.now() - initialTime, 'ms')}
42
+ </svelte:boundary> -->
@@ -0,0 +1,8 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type $$ComponentProps = {
4
+ children: Snippet;
5
+ } & HTMLAttributes<HTMLDivElement>;
6
+ declare const DiagramController: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type DiagramController = ReturnType<typeof DiagramController>;
8
+ export default DiagramController;
@@ -0,0 +1,231 @@
1
+ <script lang="ts">
2
+ import {
3
+ getContext,
4
+ onMount,
5
+ tick,
6
+ type ComponentProps,
7
+ type Snippet,
8
+ } from "svelte";
9
+ import type {
10
+ DiagramNode,
11
+ DiagramEdgeParams,
12
+ DiagramEdge,
13
+ } from "./Diagram.svelte";
14
+ import type { SvelteMap } from "svelte/reactivity";
15
+ import type { HTMLAttributes } from "svelte/elements";
16
+ import { vector2, type Vector2 } from "./diagram-lib";
17
+
18
+ // type IndividualConnectActionParam = string | Omit<DiagramEdge, 'source'>;
19
+ type IndividualConnectActionParam =
20
+ | string
21
+ | (DiagramEdgeParams & { target: string });
22
+ type IndividualConnectSourceActionParam =
23
+ | string
24
+ | (DiagramEdgeParams & { source: string });
25
+ type DiagramNodeConnectParam =
26
+ | IndividualConnectActionParam
27
+ | IndividualConnectActionParam[];
28
+ type DiagramNodeConnectSourceParam =
29
+ | IndividualConnectSourceActionParam
30
+ | IndividualConnectSourceActionParam[];
31
+
32
+ export type DiagramNodeProps = {
33
+ children?: Snippet;
34
+ connect?: DiagramNodeConnectParam;
35
+ connectSource?: DiagramNodeConnectSourceParam;
36
+ autosize?: boolean;
37
+ origin?: Vector2;
38
+ } & Omit<DiagramNode, "snippet"> &
39
+ HTMLAttributes<HTMLDivElement>;
40
+
41
+ let {
42
+ children,
43
+ connect,
44
+ connectSource: connectFrom,
45
+ id,
46
+ x,
47
+ y,
48
+ width,
49
+ height,
50
+ autosize,
51
+ clientOnly,
52
+ origin,
53
+ class: className,
54
+ style: inlineStyles,
55
+ ...rest
56
+ }: DiagramNodeProps = $props();
57
+
58
+ const nodeMap = (
59
+ getContext("nodeMap") as () => SvelteMap<string, DiagramNode>
60
+ )();
61
+ const edgeMap = (
62
+ getContext("edgeMap") as () => SvelteMap<string, DiagramEdge>
63
+ )();
64
+ let dimensions = (
65
+ getContext("dimensions") as () =>
66
+ | { min: Vector2; max: Vector2 }
67
+ | undefined
68
+ )();
69
+
70
+ if (!origin && (width || height) && !autosize) {
71
+ origin = vector2(0.5, 0.5);
72
+ }
73
+
74
+ let absolutePosition = $derived({
75
+ x: x - (origin?.x ?? 0.5) * (width ?? 0),
76
+ y: y - (origin?.y ?? 0.5) * (height ?? 0),
77
+ });
78
+
79
+ // const nodeDef: DiagramNode = $derived({ id, x, y, width, height, clientOnly, snippet: children });
80
+ const nodeDef: DiagramNode = $derived({
81
+ id,
82
+ x: absolutePosition.x,
83
+ y: absolutePosition.y,
84
+ width,
85
+ height,
86
+ clientOnly: clientOnly || autosize,
87
+ snippet: children,
88
+ });
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
+
98
+ let mounted = $state(false);
99
+
100
+ const previousEdgeIds = new Set();
101
+
102
+ // TODO: this should be done only if clientWidth is needed
103
+ let clientWidth: number = $state(0);
104
+ let clientHeight: number = $state(0);
105
+
106
+ // if (origin) {
107
+ // nodeDef.x -= (origin?.x ?? 0) * $state.snapshot(nodeDef.width ?? 0);
108
+ // nodeDef.y -= (origin?.y ?? 0) * $state.snapshot(nodeDef.height ?? 0);
109
+ // }
110
+
111
+ if (autosize) {
112
+ onMount(() => {
113
+ width = clientWidth;
114
+ height = clientHeight;
115
+ });
116
+ }
117
+
118
+ onMount(async () => {
119
+ if (nodeDef.clientOnly) {
120
+ await tick();
121
+ mounted = true;
122
+ }
123
+ });
124
+
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
+ function updateEdge(
149
+ param: IndividualConnectActionParam,
150
+ index: number = 0,
151
+ ) {
152
+ const getEdgeId = (target: string, i: number) =>
153
+ `${nodeDef.id}:${target}:(${i})`;
154
+ // const edgeId = getEdgeId();
155
+ if (typeof param == "string") {
156
+ const target = param;
157
+
158
+ const edgeId = getEdgeId(target, index);
159
+ previousEdgeIds.add(edgeId);
160
+
161
+ edgeMap.set(edgeId, { source: nodeDef.id, target });
162
+ } else {
163
+ const edgeId = getEdgeId(param.target, index);
164
+ previousEdgeIds.add(edgeId);
165
+
166
+ (param as DiagramEdge).source = nodeDef.id;
167
+ edgeMap.set(edgeId, param as DiagramEdge);
168
+ }
169
+ }
170
+
171
+ if (connect) {
172
+ if (!Array.isArray(connect)) {
173
+ updateEdge(connect);
174
+ } else {
175
+ connect.forEach(updateEdge);
176
+ }
177
+ }
178
+
179
+ if (connectFrom) {
180
+ if (!Array.isArray(connectFrom)) {
181
+ updateEdgeFrom(connectFrom);
182
+ } else {
183
+ connectFrom.forEach(updateEdgeFrom);
184
+ }
185
+ }
186
+
187
+ let left = $derived(nodeDef.x - (dimensions?.min.x ?? 0));
188
+ let top = $derived(nodeDef.y - (dimensions?.min.y ?? 0));
189
+
190
+ $effect(() => {
191
+ nodeMap.set(nodeDef.id, nodeDef);
192
+ });
193
+
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
+ // $inspect(
202
+ // 'mounted render diagramNode',
203
+ // nodeDef.id,
204
+ // left,
205
+ // top,
206
+ // nodeDef.width,
207
+ // nodeDef.height,
208
+ // dimensions
209
+ // );
210
+ </script>
211
+
212
+ <div
213
+ class={className?.toString() || ""}
214
+ style={`position:absolute;
215
+ ${!origin ? "transform:translate(-50%, -50%)" : ""};
216
+ top:${top}px;left:${left}px;${nodeDef.clientOnly && !mounted ? "opacity:0" : ""} ${inlineStyles || ""}`}
217
+ style:width={nodeDef.width ? nodeDef.width + "px" : "auto"}
218
+ style:height={nodeDef.height ? nodeDef.height + "px" : "auto"}
219
+ {...rest}
220
+ >
221
+ {#if autosize}
222
+ <div
223
+ class="nodal-autosize"
224
+ style="position:absolute;top:0;right:0;left:0;bottom:0;z-index:-50;"
225
+ bind:clientWidth
226
+ bind:clientHeight
227
+ ></div>
228
+ {/if}
229
+ <!-- <div class="absolute top-0 left-0 w-full h-full bg-red-500"> {nodeDef.clientOnly} {mounted}</div> -->
230
+ {@render children?.()}
231
+ </div>