Skip to content

Commit 22fe631

Browse files
regexidenttechniq
andauthored
Decouple ForceSimulation from ChartContext, by taking nodes and links via data prop (#526)
* Decouple `ForceSimulation` from `ChartContext`, by taking nodes and links via `data` prop * Update "Force" examples * Add changeset * Remove unneeded Chart data * Further simplify --------- Co-authored-by: Sean Lynch <techniq35@gmail.com>
1 parent aec28f8 commit 22fe631

11 files changed

Lines changed: 45 additions & 29 deletions

File tree

.changeset/dark-pandas-start.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'layerchart': minor
3+
---
4+
5+
Decoupled `ForceSimulation` from `ChartContext`, by taking nodes and links via `data` prop.

packages/layerchart/src/lib/components/ForceSimulation.svelte

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
55
type Forces = Record<string, Force<any, any>>;
66
7+
export type Data<TNode = any, TLink = any> = {
8+
nodes: TNode[];
9+
links?: TLink[];
10+
};
11+
712
export type LinkPosition = {
813
x1: number;
914
y1: number;
@@ -18,9 +23,10 @@
1823
forces: Forces;
1924
2025
/**
21-
* An array of links to be used for position calculation.
26+
* An object with arrays of nodes and links,
27+
* to be used for position calculation.
2228
*/
23-
links?: any[];
29+
data: Data;
2430
2531
/**
2632
* Current alpha value of the simulation
@@ -98,12 +104,11 @@
98104
</script>
99105

100106
<script lang="ts">
101-
import { getChartContext } from './Chart.svelte';
102107
import { watch } from 'runed';
103108
104109
let {
105110
forces,
106-
links = [],
111+
data,
107112
alpha = $bindable(1),
108113
alphaTarget = 0,
109114
alphaDecay = 1 - Math.pow(0.001, 1 / 300),
@@ -118,13 +123,11 @@
118123
cloneNodes = false,
119124
}: ForceSimulationProps = $props();
120125
121-
const ctx = getChartContext();
122-
123126
// MARK: Public Props
124127
125128
// MARK: Private Props
126129
127-
let nodes: SimulationNodeDatum[] = $state([]);
130+
let simulatedNodes: SimulationNodeDatum[] = $state([]);
128131
let linkPositions: LinkPosition[] = $state([]);
129132
130133
const simulation = forceSimulation().stop();
@@ -166,11 +169,11 @@
166169
);
167170
168171
watch.pre(
169-
() => ctx.data,
172+
() => data,
170173
() => {
171-
// Any time the `data` store gets changed we
172-
// pass them to the internal d3 simulation object:
173-
pushNodesToSimulation(ctx.data as any[]);
174+
// Any time the `nodes` prop, or the `data` store gets changed
175+
// we pass them to the internal d3 simulation object:
176+
pushNodesToSimulation(data.nodes);
174177
runOrResumeSimulation();
175178
}
176179
);
@@ -256,6 +259,8 @@
256259
}
257260
258261
function updateLinkPositions() {
262+
const links = data.links ?? [];
263+
259264
// Keeping the link positions in sync with the simulation
260265
// so we don't need to recalculate _all_ link positions on each tick
261266
// which bogs down the simulation
@@ -270,7 +275,7 @@
270275
// MARK: Pull State
271276
272277
function pullNodesFromSimulation() {
273-
nodes = cloneNodes ? structuredClone(simulation.nodes()) : simulation.nodes();
278+
simulatedNodes = cloneNodes ? structuredClone(simulation.nodes()) : simulation.nodes();
274279
}
275280
276281
function pullAlphaFromSimulation() {
@@ -393,4 +398,4 @@
393398
});
394399
</script>
395400

396-
{@render children?.({ nodes: nodes, simulation, linkPositions })}
401+
{@render children?.({ nodes: simulatedNodes, simulation, linkPositions })}

packages/layerchart/src/routes/docs/examples/Beeswarm/+page.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
99
let { data } = $props();
1010
11+
const nodes = data.usSenators;
12+
1113
const genderColor = scaleOrdinal(['var(--color-info)', 'var(--color-warning)']);
1214
1315
const xForce = forceX().strength(0.95);
@@ -35,6 +37,7 @@
3537
y: yForce.y(context.height / 2),
3638
collide: collideForce.radius(r),
3739
}}
40+
data={{ nodes }}
3841
static
3942
>
4043
{#snippet children({ nodes })}

packages/layerchart/src/routes/docs/examples/CollisionDetection/+page.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
<Preview data={randomData}>
3232
<div class="h-[600px] p-4 border rounded-sm overflow-hidden">
33-
<Chart data={randomData}>
33+
<Chart>
3434
{#snippet children({ context })}
3535
<Svg>
3636
<ForceSimulation
@@ -40,6 +40,7 @@
4040
collide: collideForce,
4141
charge: manyBodyForce.strength((d, i) => (i ? 0 : (-context.width * 2) / 3)),
4242
}}
43+
data={{ nodes: randomData }}
4344
alphaTarget={0.3}
4445
velocityDecay={0.1}
4546
>

packages/layerchart/src/routes/docs/examples/ForceDisjointGraph/+page.svelte

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
1111
let { data } = $props();
1212
13-
const nodes = $derived(data.miserables.nodes);
14-
const links = $derived(data.miserables.links);
13+
const nodes = data.miserables.nodes;
14+
const links = data.miserables.links;
1515
1616
const colorScale = scaleOrdinal(schemeCategory10);
1717
@@ -30,7 +30,7 @@
3030

3131
<Preview data={data.miserables}>
3232
<div class="h-[680px] p-4 border rounded-sm">
33-
<Chart data={nodes}>
33+
<Chart>
3434
<Svg center>
3535
<ForceSimulation
3636
forces={{
@@ -39,7 +39,7 @@
3939
x: xForce,
4040
y: yForce,
4141
}}
42-
{links}
42+
data={{ nodes, links }}
4343
>
4444
{#snippet children({ nodes, linkPositions })}
4545
{#each links as link, i (link.value + link.index)}

packages/layerchart/src/routes/docs/examples/ForceDrag/+page.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151

5252
<Preview data={nodes}>
5353
<div class="h-[600px] p-4 border rounded-sm overflow-hidden">
54-
<Chart data={nodes}>
54+
<Chart>
5555
{#snippet children({ context })}
5656
<Svg>
5757
<ForceSimulation
@@ -60,7 +60,7 @@
6060
charge: chargeForce,
6161
center: centerForce.x(context.width / 2).y(context.height / 2),
6262
}}
63-
{links}
63+
data={{ nodes, links }}
6464
>
6565
{#snippet children({ nodes, simulation, linkPositions })}
6666
{#each links as link, i}

packages/layerchart/src/routes/docs/examples/ForceGraph/+page.svelte

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
1212
let { data } = $props();
1313
14-
const nodes = $derived(data.miserables.nodes);
15-
const links = $derived(data.miserables.links);
14+
const nodes = data.miserables.nodes;
15+
const links = data.miserables.links;
1616
1717
const colorScale = scaleOrdinal(schemeCategory10);
1818
@@ -250,7 +250,7 @@
250250
</div>
251251
<Preview data={data.miserables}>
252252
<div class="h-[600px] p-4 border rounded-sm overflow-hidden">
253-
<Chart data={nodes}>
253+
<Chart>
254254
{#snippet children({ context })}
255255
<Svg>
256256
<ForceSimulation
@@ -269,7 +269,7 @@
269269
onStart={handleStart}
270270
onTick={handleTick}
271271
onEnd={handleEnd}
272-
{links}
272+
data={{ nodes, links }}
273273
>
274274
{#snippet children({ nodes, linkPositions })}
275275
{#each links as link, i}

packages/layerchart/src/routes/docs/examples/ForceGroup/+page.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
),
6565
center: centerForce.x(context.width / 2).y(context.height / 2),
6666
}}
67+
data={{ nodes: dots }}
6768
bind:alpha
6869
>
6970
{#snippet children({ nodes })}

packages/layerchart/src/routes/docs/examples/ForceLattice/+page.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424

2525
<Preview data={nodes}>
2626
<div class="h-[800px] p-4 border rounded-sm overflow-hidden">
27-
<Chart data={nodes}>
27+
<Chart>
2828
<Svg center>
2929
<ForceSimulation
30-
{links}
30+
data={{ nodes, links }}
3131
forces={{
3232
charge: chargeForce,
3333
link: linkForce,

packages/layerchart/src/routes/docs/examples/ForceText/+page.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@
9090
<Preview {data}>
9191
<div class="h-[500px] p-4 border rounded-sm overflow-hidden">
9292
<Chart
93+
{data}
9394
x="x"
9495
xDomain={[0, 1]}
9596
xRange={[0, 1]}
9697
y="y"
9798
yDomain={[0, 1]}
9899
yRange={[0, 1]}
99-
{data}
100100
{onResize}
101101
>
102102
{#snippet children({ context })}
@@ -111,6 +111,7 @@
111111
charge: manyBodyForce.strength((d, i) => (i ? 0 : (-context.width * 2) / 10)),
112112
}),
113113
}}
114+
data={{ nodes: data }}
114115
alphaTarget={1}
115116
velocityDecay={0.2}
116117
>

0 commit comments

Comments
 (0)