|
31 | 31 | > = { |
32 | 32 | alpha: number; |
33 | 33 | alphaTarget: number; |
34 | | - simulation: SimulationFor<NodeDatum, LinkDatum>; |
| 34 | + simulation: Simulation<NodeDatum, LinkDatum>; |
35 | 35 | }; |
36 | 36 |
|
37 | 37 | export type OnTickEvent< |
|
40 | 40 | > = { |
41 | 41 | alpha: number; |
42 | 42 | alphaTarget: number; |
43 | | - nodes: NodeDatumFor<NodeDatum>[]; |
44 | | - links: LinkDatumFor<NodeDatum, LinkDatum>[]; |
45 | | - simulation: SimulationFor<NodeDatum, LinkDatum>; |
| 43 | + nodes: NodeDatum[]; |
| 44 | + links: LinkDatum[]; |
| 45 | + simulation: Simulation<NodeDatum, LinkDatum>; |
46 | 46 | }; |
47 | 47 |
|
48 | 48 | export type OnEndEvent< |
|
51 | 51 | > = { |
52 | 52 | alpha: number; |
53 | 53 | alphaTarget: number; |
54 | | - simulation: SimulationFor<NodeDatum, LinkDatum>; |
| 54 | + simulation: Simulation<NodeDatum, LinkDatum>; |
| 55 | + }; |
| 56 | +
|
| 57 | + export type OnNodesChangeEvent< |
| 58 | + NodeDatum extends SimulationNodeDatum, |
| 59 | + LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined, |
| 60 | + > = { |
| 61 | + alpha: number; |
| 62 | + alphaTarget: number; |
| 63 | + nodes: NodeDatum[]; |
| 64 | + links: LinkDatum[]; |
| 65 | + simulation: Simulation<NodeDatum, LinkDatum>; |
55 | 66 | }; |
56 | 67 |
|
57 | 68 | /** |
|
81 | 92 | */ |
82 | 93 | export const DEFAULT_VELOCITY_DECAY: number = 0.4; |
83 | 94 |
|
84 | | - type NodeDatumFor<NodeDatum> = NodeDatum & SimulationNodeDatum; |
85 | | -
|
86 | | - type LinkDatumFor<NodeDatum, LinkDatum> = LinkDatum & |
87 | | - SimulationLinkDatum<NodeDatumFor<NodeDatum>>; |
88 | | -
|
89 | | - type SimulationFor<NodeDatum, LinkDatum> = Simulation< |
90 | | - NodeDatumFor<NodeDatum>, |
91 | | - LinkDatumFor<NodeDatum, LinkDatum> |
92 | | - >; |
93 | | -
|
94 | 95 | export type ForceSimulationProps< |
95 | 96 | NodeDatum extends SimulationNodeDatum, |
96 | 97 | LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined, |
|
159 | 160 | */ |
160 | 161 | onStart?: (e: OnStartEvent<NodeDatum, LinkDatum | undefined>) => void; |
161 | 162 |
|
| 163 | + /** |
| 164 | + * Callback function triggered right before nodes get passed to the simulation |
| 165 | + */ |
| 166 | + onNodesChange?: (e: OnNodesChangeEvent<NodeDatum, LinkDatum | undefined>) => void; |
| 167 | +
|
162 | 168 | /** |
163 | 169 | * Callback function triggered on each simulation tick |
164 | 170 | */ |
|
172 | 178 | children?: Snippet< |
173 | 179 | [ |
174 | 180 | { |
175 | | - nodes: NodeDatumFor<NodeDatum>[]; |
176 | | - links: LinkDatumFor<NodeDatum, LinkDatum>[]; |
| 181 | + nodes: NodeDatum[]; |
| 182 | + links: LinkDatum[]; |
177 | 183 | linkPositions: LinkPosition[]; |
178 | | - simulation: SimulationFor<NodeDatum, LinkDatum>; |
| 184 | + simulation: Simulation<NodeDatum, LinkDatum>; |
179 | 185 | }, |
180 | 186 | ] |
181 | 187 | >; |
|
200 | 206 | stopped = false, |
201 | 207 | static: staticProp, |
202 | 208 | onStart: onStartProp, |
| 209 | + onNodesChange: onNodesChangeProp, |
203 | 210 | onTick: onTickProp, |
204 | 211 | onEnd: onEndProp, |
205 | 212 | children, |
|
211 | 218 | // MARK: Private Props |
212 | 219 |
|
213 | 220 | let linkPositions: LinkPosition[] = $state([]); |
214 | | - let simulatedNodes: NodeDatumFor<NodeDatum>[] = $state([]); |
215 | | - let simulatedLinks: LinkDatumFor<NodeDatum, LinkDatum>[] = $derived( |
216 | | - (data.links ?? []) as LinkDatumFor<NodeDatum, LinkDatum>[] |
217 | | - ); |
| 221 | + let simulatedNodes: NodeDatum[] = $state([]); |
| 222 | + let simulatedLinks: LinkDatum[] = $derived(data.links ?? []); |
218 | 223 |
|
219 | 224 | // This casting is unfortunately necessary, due to unfortunate |
220 | 225 | // overloading choices made, over at `@typed/d3-force`: |
221 | | - const simulation: SimulationFor<NodeDatum, LinkDatum> = ( |
222 | | - forceSimulation() as SimulationFor<NodeDatum, LinkDatum> |
| 226 | + const simulation: Simulation<NodeDatum, LinkDatum> = ( |
| 227 | + forceSimulation<NodeDatum>() as Simulation<NodeDatum, LinkDatum> |
223 | 228 | ).stop(); |
224 | 229 |
|
225 | 230 | // d3.Simulation does not provide a `.forces()` getter, so we need to |
|
263 | 268 | () => { |
264 | 269 | // Any time the `nodes` prop, or the `data` store gets changed |
265 | 270 | // we pass them to the internal d3 simulation object: |
| 271 | + onNodesChange(); |
266 | 272 | pushNodesToSimulation(data.nodes); |
267 | 273 | runOrResumeSimulation(); |
268 | 274 | } |
|
498 | 504 | }); |
499 | 505 | } |
500 | 506 |
|
| 507 | + function onNodesChange() { |
| 508 | + onNodesChangeProp?.({ |
| 509 | + alpha, |
| 510 | + alphaTarget, |
| 511 | + nodes: data.nodes, |
| 512 | + links: data.links ?? [], |
| 513 | + simulation, |
| 514 | + }); |
| 515 | + } |
| 516 | +
|
501 | 517 | $effect(() => { |
502 | 518 | return () => { |
503 | 519 | simulation.stop(); |
|
0 commit comments