Skip to content

Commit 15a3240

Browse files
authored
Export ForceSimulation's Forces<NodeDatum, LinkDatum> type. (#531)
* Export `ForceSimulation`'s `Forces<NodeDatum, LinkDatum>` type * Fix breakages
1 parent a932273 commit 15a3240

10 files changed

Lines changed: 143 additions & 58 deletions

File tree

.changeset/tricky-pears-help.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'layerchart': patch
3+
---
4+
5+
fix(ForceSimulation): Export Forces<NodeDatum, LinkDatum> type.

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
} from 'd3-force';
99
import type { Snippet } from 'svelte';
1010
11-
type Forces = Record<string, Force<any, any>>;
11+
export type Forces<
12+
NodeDatum extends SimulationNodeDatum,
13+
LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
14+
> = Record<string, Force<NodeDatum, LinkDatum>>;
1215
1316
export type Data<TNode = any, TLink = any> = {
1417
nodes: TNode[];
@@ -60,11 +63,14 @@
6063
LinkDatumFor<NodeDatum, LinkDatum>
6164
>;
6265
63-
export type ForceSimulationProps<NodeDatum, LinkDatum> = {
66+
export type ForceSimulationProps<
67+
NodeDatum extends SimulationNodeDatum,
68+
LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
69+
> = {
6470
/**
6571
* Force simulation parameters
6672
*/
67-
forces: Forces;
73+
forces: Forces<NodeDatum, LinkDatum>;
6874
6975
/**
7076
* An object with arrays of nodes and links,
@@ -148,7 +154,11 @@
148154
};
149155
</script>
150156

151-
<script lang="ts" generics="NodeDatum, LinkDatum = undefined">
157+
<script
158+
lang="ts"
159+
generics="NodeDatum extends SimulationNodeDatum,
160+
LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,"
161+
>
152162
import { watch } from 'runed';
153163
import type { Prettify } from '@layerstack/utils';
154164
@@ -187,7 +197,7 @@
187197
188198
// d3.Simulation does not provide a `.forces()` getter, so we need to
189199
// keep track of previous forces ourselves, for diffing against `forces`.
190-
let previousForces: Forces = {};
200+
let previousForces: Forces<NodeDatum, LinkDatum> = {};
191201
192202
let paused: boolean = true;
193203
@@ -291,7 +301,7 @@
291301
simulation.nodes(nodes);
292302
}
293303
294-
function pushForcesToSimulation(forces: Forces) {
304+
function pushForcesToSimulation(forces: Forces<NodeDatum, LinkDatum>) {
295305
// Evict obsolete forces:
296306
const names = Object.keys(previousForces);
297307
for (const name of names) {

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
<script lang="ts">
22
import { scaleOrdinal } from 'd3-scale';
3-
import { forceX, forceY, forceCollide } from 'd3-force';
3+
import { forceX, forceY, forceCollide, type SimulationNodeDatum } from 'd3-force';
44
55
import { asAny, Axis, Chart, Circle, ForceSimulation, Svg, Tooltip } from 'layerchart';
66
77
import Preview from '$lib/docs/Preview.svelte';
8+
import type { USSenatorsDatum } from '$static/data/examples/us-senators.js';
9+
import type { Prettify } from '@layerstack/utils';
10+
11+
type NodeDatum = USSenatorsDatum;
12+
13+
type MySimulationNodeDatum = Prettify<NodeDatum & SimulationNodeDatum>;
814
915
let { data } = $props();
1016
11-
const nodes = data.usSenators;
17+
const nodes: MySimulationNodeDatum[] = data.usSenators;
1218
1319
const genderColor = scaleOrdinal(['var(--color-info)', 'var(--color-warning)']);
1420
15-
const xForce = forceX().strength(0.95);
16-
const yForce = forceY().strength(0.075);
17-
const collideForce = forceCollide();
21+
const xForce = forceX<MySimulationNodeDatum>().strength(0.95);
22+
const yForce = forceY<MySimulationNodeDatum>().strength(0.075);
23+
const collideForce = forceCollide<MySimulationNodeDatum>();
1824
</script>
1925

2026
<h1>Examples</h1>

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,31 @@
66
import { Chart, Circle, Group, ForceSimulation, Svg } from 'layerchart';
77
88
import Preview from '$lib/docs/Preview.svelte';
9+
import type { Prettify } from '@layerstack/utils';
10+
11+
type NodeDatum = { r: number; group: number };
12+
type MySimulationNodeDatum = Prettify<NodeDatum & SimulationNodeDatum>;
913
1014
const k = 600 / 200;
1115
const r = randomUniform(k, k * 4);
1216
const n = 4;
13-
const randomData = Array.from({ length: 200 }, (_, i) => ({ r: r(), group: i && (i % n) + 1 }));
17+
const randomData: MySimulationNodeDatum[] = Array.from({ length: 200 }, (_, i) => ({
18+
r: r(),
19+
group: i && (i % n) + 1,
20+
}));
1421
1522
const groupColor = scaleOrdinal([
1623
'var(--color-info)',
1724
'var(--color-warning)',
1825
'var(--color-danger)',
1926
]);
2027
21-
const xForce = forceX().strength(0.01);
22-
const yForce = forceY().strength(0.01);
23-
const collideForce = forceCollide<SimulationNodeDatum & { r: number }>()
28+
const xForce = forceX<MySimulationNodeDatum>().strength(0.01);
29+
const yForce = forceY<MySimulationNodeDatum>().strength(0.01);
30+
const collideForce = forceCollide<MySimulationNodeDatum>()
2431
.radius((d) => d.r + 1)
2532
.iterations(3);
26-
const manyBodyForce = forceManyBody();
33+
const manyBodyForce = forceManyBody<MySimulationNodeDatum>();
2734
</script>
2835

2936
<h1>Examples</h1>

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,39 @@
1414
import { Chart, Circle, ForceSimulation, Link, Svg } from 'layerchart';
1515
1616
import Preview from '$lib/docs/Preview.svelte';
17+
import type { Prettify } from '@layerstack/utils';
1718
1819
let { data } = $props();
1920
20-
type Node = {
21+
type NodeDatum = {
2122
id: string;
2223
group: number;
2324
};
2425
25-
type Link = {
26+
type LinkDatum = {
2627
source: string;
2728
target: string;
2829
value: number;
2930
};
3031
31-
const nodes: (Node & SimulationNodeDatum)[] = data.miserables.nodes;
32-
const links: (Link & SimulationLinkDatum<Node & SimulationNodeDatum>)[] = data.miserables.links;
32+
type MySimulationNodeDatum = Prettify<NodeDatum & SimulationNodeDatum>;
33+
type MySimulationLinkDatum = Prettify<
34+
LinkDatum & SimulationLinkDatum<NodeDatum & SimulationNodeDatum>
35+
>;
36+
37+
const nodes: MySimulationNodeDatum[] = data.miserables.nodes;
38+
const links: MySimulationLinkDatum[] = data.miserables.links;
3339
3440
const colorScale = scaleOrdinal(schemeCategory10);
3541
36-
// @ts-expect-error - TODO: can we fix these types
37-
const linkForce = $derived(forceLink(links).id((d) => d.id));
38-
const chargeForce = forceManyBody().strength(-30).theta(0.9);
39-
const xForce = forceX();
40-
const yForce = forceY();
42+
const linkForce = $derived(
43+
forceLink<MySimulationNodeDatum, MySimulationLinkDatum>(links).id((d) => d.id)
44+
);
45+
const chargeForce = forceManyBody<MySimulationNodeDatum>().strength(-30).theta(0.9);
46+
const xForce = forceX<MySimulationNodeDatum>();
47+
const yForce = forceY<MySimulationNodeDatum>();
4148
42-
function keyForLink(link: Link & SimulationLinkDatum<Node & SimulationNodeDatum>): any {
49+
function keyForLink(link: MySimulationLinkDatum): any {
4350
return link.value + link.index!;
4451
}
4552
</script>

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,37 @@
11
<script lang="ts">
2-
import { forceManyBody, forceLink, forceCenter } from 'd3-force';
2+
import {
3+
forceManyBody,
4+
forceLink,
5+
forceCenter,
6+
type SimulationNodeDatum,
7+
type SimulationLinkDatum,
8+
} from 'd3-force';
39
import { curveLinear } from 'd3-shape';
410
511
import { Field, Switch } from 'svelte-ux';
612
import { Chart, ForceSimulation, Link, Svg, Tooltip } from 'layerchart';
713
import { cls } from '@layerstack/tailwind';
8-
import { clamp } from '@layerstack/utils';
14+
import { clamp, type Prettify } from '@layerstack/utils';
915
1016
import Preview from '$lib/docs/Preview.svelte';
1117
import { movable } from '$lib/actions/movable.js';
1218
13-
const nodes = Array.from({ length: 13 }, (_, i) => ({ id: i }));
14-
const links = [
19+
type NodeDatum = {
20+
id: number;
21+
};
22+
23+
type LinkDatum = {
24+
source: number;
25+
target: number;
26+
};
27+
28+
type MySimulationNodeDatum = Prettify<NodeDatum & SimulationNodeDatum>;
29+
type MySimulationLinkDatum = Prettify<
30+
LinkDatum & SimulationLinkDatum<NodeDatum & SimulationNodeDatum>
31+
>;
32+
33+
const nodes: MySimulationNodeDatum[] = Array.from({ length: 13 }, (_, i) => ({ id: i }));
34+
const links: MySimulationLinkDatum[] = [
1535
{ source: 0, target: 1 },
1636
{ source: 1, target: 2 },
1737
{ source: 2, target: 0 },

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,28 @@
1515
import { Checkbox, Field, ProgressCircle, RangeField } from 'svelte-ux';
1616
1717
import Preview from '$lib/docs/Preview.svelte';
18+
import type { Prettify } from '@layerstack/utils';
1819
1920
let { data } = $props();
2021
21-
type Node = {
22+
type NodeDatum = {
2223
id: string;
2324
group: number;
2425
};
2526
26-
type Link = {
27+
type LinkDatum = {
2728
source: string;
2829
target: string;
2930
value: number;
3031
};
3132
32-
const nodes: (Node & SimulationNodeDatum)[] = data.miserables.nodes;
33-
const links: (Link & SimulationLinkDatum<Node & SimulationNodeDatum>)[] = data.miserables.links;
33+
type MySimulationNodeDatum = Prettify<NodeDatum & SimulationNodeDatum>;
34+
type MySimulationLinkDatum = Prettify<
35+
LinkDatum & SimulationLinkDatum<NodeDatum & SimulationNodeDatum>
36+
>;
37+
38+
const nodes: MySimulationNodeDatum[] = data.miserables.nodes;
39+
const links: MySimulationLinkDatum[] = data.miserables.links;
3440
3541
const colorScale = scaleOrdinal(schemeCategory10);
3642
@@ -61,11 +67,12 @@
6167
});
6268
});
6369
64-
// @ts-expect-error
65-
const linkForce = $derived(forceLink(links).id((d) => d.id));
66-
const chargeForce = forceManyBody();
67-
const collideForce = forceCollide();
68-
const centerForce = forceCenter(0, 0);
70+
const linkForce = $derived(
71+
forceLink<MySimulationNodeDatum, MySimulationLinkDatum>(links).id((d) => d.id)
72+
);
73+
const chargeForce = forceManyBody<MySimulationNodeDatum>();
74+
const collideForce = forceCollide<MySimulationNodeDatum>();
75+
const centerForce = forceCenter<MySimulationNodeDatum>(0, 0);
6976
7077
let linkDistance = $state(30);
7178

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
<script lang="ts">
22
import { scaleBand, scaleOrdinal } from 'd3-scale';
3-
import { forceX, forceManyBody, forceCollide, forceCenter } from 'd3-force';
3+
import {
4+
forceX,
5+
forceManyBody,
6+
forceCollide,
7+
forceCenter,
8+
type SimulationNodeDatum,
9+
} from 'd3-force';
410
511
import { asAny, Chart, Circle, ForceSimulation, Svg } from 'layerchart';
612
import { Field, ToggleGroup, ToggleOption } from 'svelte-ux';
713
814
import Preview from '$lib/docs/Preview.svelte';
915
1016
import dots from './dots.json' with { type: 'json' };
17+
import type { Prettify } from '@layerstack/utils';
18+
19+
type NodeDatum = { category: string; value: number };
20+
type MySimulationNodeDatum = Prettify<NodeDatum & SimulationNodeDatum>;
21+
22+
const nodes: MySimulationNodeDatum[] = dots as MySimulationNodeDatum[];
1123
1224
const categoryColor = scaleOrdinal([
1325
'var(--color-info)',
@@ -22,10 +34,10 @@
2234
reheatSimulation({ groupBy });
2335
});
2436
25-
const xForce = forceX().strength(0.1);
26-
const chargeForce = forceManyBody().strength(3);
27-
const collideForce = forceCollide();
28-
const centerForce = forceCenter();
37+
const xForce = forceX<MySimulationNodeDatum>().strength(0.1);
38+
const chargeForce = forceManyBody<MySimulationNodeDatum>().strength(3);
39+
const collideForce = forceCollide<MySimulationNodeDatum>();
40+
const centerForce = forceCenter<MySimulationNodeDatum>();
2941
3042
function reheatSimulation(args: Record<string, any> = {}) {
3143
const _ = args;
@@ -45,9 +57,9 @@
4557
</Field>
4658
</div>
4759

48-
<Preview data={dots}>
60+
<Preview data={nodes}>
4961
<div class="h-[300px] p-4 border rounded-sm">
50-
<Chart data={dots} x="category" xScale={scaleBand()} r="value" rRange={[3, 12]}>
62+
<Chart data={nodes} x="category" xScale={scaleBand()} r="value" rRange={[3, 12]}>
5163
{#snippet children({ context })}
5264
{@const nodeStrokeWidth = 1}
5365
<Svg>
@@ -64,7 +76,7 @@
6476
),
6577
center: centerForce.x(context.width / 2).y(context.height / 2),
6678
}}
67-
data={{ nodes: dots }}
79+
data={{ nodes }}
6880
bind:alpha
6981
>
7082
{#snippet children({ nodes })}

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
<script lang="ts">
2-
import { hierarchy } from 'd3-hierarchy';
3-
import { forceX, forceY, forceManyBody, forceLink } from 'd3-force';
2+
import { hierarchy, type HierarchyLink, type HierarchyNode } from 'd3-hierarchy';
3+
import { forceX, forceY, forceManyBody, forceLink, type SimulationNodeDatum } from 'd3-force';
44
55
import { Chart, Circle, ForceSimulation, Link, Svg, Tooltip } from 'layerchart';
66
import { cls } from '@layerstack/tailwind';
77
88
import Preview from '$lib/docs/Preview.svelte';
9+
import type { Prettify } from '@layerstack/utils';
10+
11+
type NodeDatum = { name: string; value: number };
12+
type MySimulationNodeDatum = Prettify<NodeDatum & SimulationNodeDatum>;
913
1014
let { data } = $props();
1115
12-
const root = hierarchy(data.flare);
13-
const nodes = root.descendants();
14-
const links = root.links();
16+
const root: HierarchyNode<MySimulationNodeDatum> = hierarchy<MySimulationNodeDatum>(data.flare);
17+
const nodes: HierarchyNode<MySimulationNodeDatum>[] = root.descendants();
18+
const links: HierarchyLink<MySimulationNodeDatum>[] = root.links();
1519
16-
const linkForce = forceLink(links).distance(0).strength(1);
17-
const chargeForce = forceManyBody().strength(-50);
18-
const xForce = forceX();
19-
const yForce = forceY();
20+
const linkForce = forceLink<
21+
HierarchyNode<MySimulationNodeDatum>,
22+
HierarchyLink<MySimulationNodeDatum>
23+
>(links)
24+
.distance(0)
25+
.strength(1);
26+
const chargeForce = forceManyBody<HierarchyNode<MySimulationNodeDatum>>().strength(-50);
27+
const xForce = forceX<HierarchyNode<MySimulationNodeDatum>>();
28+
const yForce = forceY<HierarchyNode<MySimulationNodeDatum>>();
2029
</script>
2130

2231
<h1>Examples</h1>
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
export type USSenatorsData = {
1+
export type USSenatorsDatum = {
22
state_name: string;
33
party: string;
44
name: string;
55
gender: string;
66
date_of_birth: Date;
7-
}[];
7+
};
8+
9+
export type USSenatorsData = USSenatorsDatum[];

0 commit comments

Comments
 (0)