layerchart 2.0.0-next.4 → 2.0.0-next.41

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.
Files changed (180) hide show
  1. package/dist/components/AnnotationLine.svelte +15 -2
  2. package/dist/components/AnnotationPoint.svelte +29 -11
  3. package/dist/components/AnnotationRange.svelte +18 -4
  4. package/dist/components/Arc.svelte +5 -5
  5. package/dist/components/Area.svelte +10 -2
  6. package/dist/components/Axis.svelte +175 -58
  7. package/dist/components/Axis.svelte.d.ts +23 -6
  8. package/dist/components/Bar.svelte +20 -15
  9. package/dist/components/Bar.svelte.d.ts +2 -2
  10. package/dist/components/Bars.svelte +4 -4
  11. package/dist/components/Blur.svelte +7 -6
  12. package/dist/components/Blur.svelte.d.ts +2 -5
  13. package/dist/components/BrushContext.svelte +45 -45
  14. package/dist/components/Calendar.svelte +31 -10
  15. package/dist/components/Calendar.svelte.d.ts +2 -1
  16. package/dist/components/Chart.svelte +76 -27
  17. package/dist/components/Chart.svelte.d.ts +26 -8
  18. package/dist/components/ChartClipPath.svelte +1 -1
  19. package/dist/components/Circle.svelte +44 -3
  20. package/dist/components/CircleClipPath.svelte +8 -1
  21. package/dist/components/ClipPath.svelte +1 -2
  22. package/dist/components/ColorRamp.svelte +1 -1
  23. package/dist/components/ComputedStyles.svelte +9 -2
  24. package/dist/components/Connector.svelte +3 -3
  25. package/dist/components/Connector.svelte.d.ts +1 -1
  26. package/dist/components/Ellipse.svelte +228 -0
  27. package/dist/components/Ellipse.svelte.d.ts +64 -0
  28. package/dist/components/ForceSimulation.svelte +184 -50
  29. package/dist/components/ForceSimulation.svelte.d.ts +95 -21
  30. package/dist/components/Frame.svelte +1 -1
  31. package/dist/components/GeoCircle.svelte +1 -1
  32. package/dist/components/GeoEdgeFade.svelte +1 -1
  33. package/dist/components/GeoPath.svelte +30 -8
  34. package/dist/components/GeoPoint.svelte +4 -5
  35. package/dist/components/GeoSpline.svelte +5 -5
  36. package/dist/components/GeoSpline.svelte.d.ts +1 -1
  37. package/dist/components/GeoTile.svelte +1 -1
  38. package/dist/components/Graticule.svelte +5 -5
  39. package/dist/components/Grid.svelte +60 -63
  40. package/dist/components/Group.svelte +13 -8
  41. package/dist/components/Group.svelte.d.ts +10 -3
  42. package/dist/components/Highlight.svelte +55 -28
  43. package/dist/components/Highlight.svelte.d.ts +4 -0
  44. package/dist/components/Hull.svelte +12 -5
  45. package/dist/components/Labels.svelte +24 -13
  46. package/dist/components/Labels.svelte.d.ts +12 -5
  47. package/dist/components/Legend.svelte +143 -70
  48. package/dist/components/Legend.svelte.d.ts +12 -8
  49. package/dist/components/Line.svelte +40 -3
  50. package/dist/components/LinearGradient.svelte +35 -4
  51. package/dist/components/Link.svelte +1 -1
  52. package/dist/components/Marker.svelte +37 -26
  53. package/dist/components/MonthPath.svelte +26 -12
  54. package/dist/components/MonthPath.svelte.d.ts +4 -3
  55. package/dist/components/MotionPath.svelte +1 -1
  56. package/dist/components/Pack.svelte.d.ts +10 -3
  57. package/dist/components/Partition.svelte.d.ts +10 -3
  58. package/dist/components/Pattern.svelte +5 -5
  59. package/dist/components/Pie.svelte +1 -2
  60. package/dist/components/Points.svelte +11 -72
  61. package/dist/components/Points.svelte.d.ts +1 -8
  62. package/dist/components/Polygon.svelte +309 -0
  63. package/dist/components/Polygon.svelte.d.ts +115 -0
  64. package/dist/components/RadialGradient.svelte +4 -6
  65. package/dist/components/Rect.svelte +55 -5
  66. package/dist/components/Rect.svelte.d.ts +2 -2
  67. package/dist/components/RectClipPath.svelte +4 -3
  68. package/dist/components/RectClipPath.svelte.d.ts +2 -2
  69. package/dist/components/Rule.svelte +167 -77
  70. package/dist/components/Rule.svelte.d.ts +7 -2
  71. package/dist/components/Spline.svelte +59 -28
  72. package/dist/components/Spline.svelte.d.ts +12 -4
  73. package/dist/components/Text.svelte +121 -73
  74. package/dist/components/Text.svelte.d.ts +6 -0
  75. package/dist/components/TileImage.svelte +19 -4
  76. package/dist/components/TransformContext.svelte +9 -3
  77. package/dist/components/TransformControls.svelte +89 -38
  78. package/dist/components/Tree.svelte.d.ts +10 -3
  79. package/dist/components/Treemap.svelte +63 -26
  80. package/dist/components/Treemap.svelte.d.ts +21 -14
  81. package/dist/components/Voronoi.svelte +12 -13
  82. package/dist/components/charts/ArcChart.svelte +43 -71
  83. package/dist/components/charts/ArcChart.svelte.d.ts +10 -3
  84. package/dist/components/charts/AreaChart.svelte +29 -59
  85. package/dist/components/charts/AreaChart.svelte.d.ts +10 -3
  86. package/dist/components/charts/BarChart.svelte +79 -71
  87. package/dist/components/charts/BarChart.svelte.d.ts +10 -3
  88. package/dist/components/charts/DefaultTooltip.svelte +3 -3
  89. package/dist/components/charts/DefaultTooltip.svelte.d.ts +1 -1
  90. package/dist/components/charts/LineChart.svelte +69 -75
  91. package/dist/components/charts/LineChart.svelte.d.ts +21 -8
  92. package/dist/components/charts/PieChart.svelte +44 -71
  93. package/dist/components/charts/PieChart.svelte.d.ts +10 -3
  94. package/dist/components/charts/ScatterChart.svelte +10 -39
  95. package/dist/components/charts/ScatterChart.svelte.d.ts +10 -3
  96. package/dist/components/charts/utils.svelte.d.ts +1 -19
  97. package/dist/components/charts/utils.svelte.js +7 -35
  98. package/dist/components/index.d.ts +4 -0
  99. package/dist/components/index.js +5 -1
  100. package/dist/components/layout/Canvas.svelte +96 -69
  101. package/dist/components/layout/Canvas.svelte.d.ts +6 -0
  102. package/dist/components/layout/Html.svelte +15 -9
  103. package/dist/components/layout/Layer.svelte +6 -4
  104. package/dist/components/layout/Layer.svelte.d.ts +6 -4
  105. package/dist/components/layout/Svg.svelte +19 -11
  106. package/dist/components/layout/WebGL.svelte +26 -6
  107. package/dist/components/layout/WebGL.svelte.d.ts +5 -2
  108. package/dist/components/tooltip/Tooltip.svelte +73 -36
  109. package/dist/components/tooltip/Tooltip.svelte.d.ts +10 -3
  110. package/dist/components/tooltip/TooltipContext.svelte +167 -54
  111. package/dist/components/tooltip/TooltipContext.svelte.d.ts +19 -5
  112. package/dist/components/tooltip/TooltipHeader.svelte +32 -18
  113. package/dist/components/tooltip/TooltipHeader.svelte.d.ts +3 -3
  114. package/dist/components/tooltip/TooltipItem.svelte +46 -37
  115. package/dist/components/tooltip/TooltipItem.svelte.d.ts +3 -3
  116. package/dist/components/tooltip/TooltipList.svelte +12 -10
  117. package/dist/components/tooltip/TooltipSeparator.svelte +18 -10
  118. package/dist/components/tooltip/tooltipMetaContext.d.ts +2 -2
  119. package/dist/docs/Blockquote.svelte +6 -4
  120. package/dist/docs/Blockquote.svelte.d.ts +4 -19
  121. package/dist/docs/Code.svelte +70 -28
  122. package/dist/docs/Code.svelte.d.ts +9 -24
  123. package/dist/docs/Header1.svelte +4 -2
  124. package/dist/docs/Header1.svelte.d.ts +4 -28
  125. package/dist/docs/Json.svelte +11 -3
  126. package/dist/docs/Json.svelte.d.ts +9 -21
  127. package/dist/docs/Layout.svelte +10 -7
  128. package/dist/docs/Layout.svelte.d.ts +4 -19
  129. package/dist/docs/Link.svelte +7 -3
  130. package/dist/docs/Link.svelte.d.ts +4 -38
  131. package/dist/docs/Preview.svelte +22 -23
  132. package/dist/docs/Preview.svelte.d.ts +5 -6
  133. package/dist/docs/TilesetField.svelte +20 -19
  134. package/dist/docs/TilesetField.svelte.d.ts +5 -22
  135. package/dist/docs/ViewSourceButton.svelte +10 -7
  136. package/dist/docs/ViewSourceButton.svelte.d.ts +7 -21
  137. package/dist/states/series.svelte.d.ts +30 -0
  138. package/dist/states/series.svelte.js +54 -0
  139. package/dist/styles/daisyui-5.css +6 -0
  140. package/dist/styles/shadcn-svelte.css +11 -0
  141. package/dist/styles/skeleton-3.css +15 -0
  142. package/dist/utils/arcText.svelte.js +4 -4
  143. package/dist/utils/array.d.ts +11 -0
  144. package/dist/utils/array.js +23 -0
  145. package/dist/utils/array.test.d.ts +1 -0
  146. package/dist/utils/array.test.js +200 -0
  147. package/dist/utils/attributes.d.ts +3 -13
  148. package/dist/utils/attributes.js +4 -18
  149. package/dist/utils/canvas.d.ts +77 -0
  150. package/dist/utils/canvas.js +105 -41
  151. package/dist/utils/common.d.ts +9 -0
  152. package/dist/utils/common.js +18 -1
  153. package/dist/utils/common.test.js +26 -1
  154. package/dist/utils/genData.d.ts +22 -8
  155. package/dist/utils/genData.js +34 -14
  156. package/dist/utils/graph/dagre.d.ts +4 -4
  157. package/dist/utils/graph/dagre.js +5 -7
  158. package/dist/utils/index.d.ts +1 -0
  159. package/dist/utils/index.js +1 -0
  160. package/dist/utils/math.d.ts +17 -0
  161. package/dist/utils/math.js +17 -0
  162. package/dist/utils/motion.svelte.js +1 -1
  163. package/dist/utils/path.d.ts +10 -0
  164. package/dist/utils/path.js +30 -0
  165. package/dist/utils/rect.svelte.d.ts +2 -2
  166. package/dist/utils/rect.svelte.js +73 -1
  167. package/dist/utils/scales.svelte.d.ts +9 -3
  168. package/dist/utils/scales.svelte.js +47 -4
  169. package/dist/utils/shape.d.ts +43 -0
  170. package/dist/utils/shape.js +59 -0
  171. package/dist/utils/stack.js +1 -1
  172. package/dist/utils/string.d.ts +49 -0
  173. package/dist/utils/string.js +4 -2
  174. package/dist/utils/ticks.d.ts +15 -4
  175. package/dist/utils/ticks.js +140 -159
  176. package/dist/utils/ticks.test.js +16 -26
  177. package/dist/utils/treemap.d.ts +1 -1
  178. package/dist/utils/types.d.ts +15 -2
  179. package/package.json +36 -35
  180. package/dist/utils/object.js +0 -2
@@ -0,0 +1,228 @@
1
+ <script lang="ts" module>
2
+ import type { CommonStyleProps, Without } from '../utils/types.js';
3
+
4
+ export type EllipsePropsWithoutHTML = {
5
+ /**
6
+ * The center x position of the ellipse.
7
+ *
8
+ * @default 0
9
+ */
10
+ cx?: number;
11
+
12
+ /**
13
+ * The initial center x position of the ellipse.
14
+ *
15
+ * @default cx
16
+ */
17
+ initialCx?: number;
18
+
19
+ /**
20
+ * The center y position of the ellipse.
21
+ *
22
+ * @default 0
23
+ */
24
+ cy?: number;
25
+
26
+ /**
27
+ * The initial center y position of the ellipse.
28
+ *
29
+ * @default cy
30
+ */
31
+ initialCy?: number;
32
+
33
+ /**
34
+ * The radius of the ellipse on the x-axis.
35
+ *
36
+ * @default 1
37
+ */
38
+ rx?: number;
39
+
40
+ /**
41
+ * The initial radius of the ellipse on the x-axis.
42
+ *
43
+ * @default rx
44
+ */
45
+ initialRx?: number;
46
+
47
+ /**
48
+ * The radius of the ellipse on the y-axis.
49
+ *
50
+ * @default 1
51
+ */
52
+ ry?: number;
53
+
54
+ /**
55
+ * The initial radius of the ellipse on the y-axis.
56
+ *
57
+ * @default ry
58
+ */
59
+ initialRy?: number;
60
+
61
+ /**
62
+ * A bindable reference to the `<ellipse>` element
63
+ *
64
+ * @bindable
65
+ */
66
+ ref?: SVGEllipseElement;
67
+
68
+ motion?: MotionProp;
69
+ } & CommonStyleProps;
70
+
71
+ export type EllipseProps = EllipsePropsWithoutHTML &
72
+ Without<SVGAttributes<Element>, EllipsePropsWithoutHTML>;
73
+ </script>
74
+
75
+ <script lang="ts">
76
+ import { cls } from '@layerstack/tailwind';
77
+ import { merge } from 'lodash-es';
78
+
79
+ import { getRenderContext } from './Chart.svelte';
80
+ import { createMotion, type MotionProp } from '../utils/motion.svelte.js';
81
+ import { registerCanvasComponent } from './layout/Canvas.svelte';
82
+ import { renderEllipse, type ComputedStylesOptions } from '../utils/canvas.js';
83
+ import type { SVGAttributes } from 'svelte/elements';
84
+ import { createKey } from '../utils/key.svelte.js';
85
+
86
+ let {
87
+ cx = 0,
88
+ initialCx: initialCxProp,
89
+ cy = 0,
90
+ initialCy: initialCyProp,
91
+ rx = 1,
92
+ initialRx: initialRxProp,
93
+ ry = 1,
94
+ initialRy: initialRyProp,
95
+ motion,
96
+ fill,
97
+ fillOpacity,
98
+ stroke,
99
+ strokeWidth,
100
+ opacity,
101
+ class: className,
102
+ ref: refProp = $bindable(),
103
+ ...restProps
104
+ }: EllipseProps = $props();
105
+
106
+ let ref = $state<SVGEllipseElement>();
107
+
108
+ $effect.pre(() => {
109
+ refProp = ref;
110
+ });
111
+
112
+ const initialCx = initialCxProp ?? cx;
113
+ const initialCy = initialCyProp ?? cy;
114
+ const initialRx = initialRxProp ?? rx;
115
+ const initialRy = initialRyProp ?? ry;
116
+
117
+ const renderCtx = getRenderContext();
118
+
119
+ const motionCx = createMotion(initialCx, () => cx, motion);
120
+ const motionCy = createMotion(initialCy, () => cy, motion);
121
+ const motionRx = createMotion(initialRx, () => rx, motion);
122
+ const motionRy = createMotion(initialRy, () => ry, motion);
123
+
124
+ function render(
125
+ ctx: CanvasRenderingContext2D,
126
+ styleOverrides: ComputedStylesOptions | undefined
127
+ ) {
128
+ renderEllipse(
129
+ ctx,
130
+ { cx: motionCx.current, cy: motionCy.current, rx: motionRx.current, ry: motionRy.current },
131
+ styleOverrides
132
+ ? merge({ styles: { strokeWidth } }, styleOverrides)
133
+ : {
134
+ styles: { fill, fillOpacity, stroke, strokeWidth, opacity },
135
+ classes: cls('lc-ellipse', className),
136
+ }
137
+ );
138
+ }
139
+
140
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
141
+ const fillKey = createKey(() => fill);
142
+ const strokeKey = createKey(() => stroke);
143
+
144
+ if (renderCtx === 'canvas') {
145
+ registerCanvasComponent({
146
+ name: 'Ellipse',
147
+ render,
148
+ events: {
149
+ click: restProps.onclick,
150
+ pointerdown: restProps.onpointerdown,
151
+ pointerenter: restProps.onpointerenter,
152
+ pointermove: restProps.onpointermove,
153
+ pointerleave: restProps.onpointerleave,
154
+ },
155
+ deps: () => [
156
+ motionCx.current,
157
+ motionCy.current,
158
+ motionRx.current,
159
+ motionRy.current,
160
+ fillKey.current,
161
+ fillOpacity,
162
+ strokeKey.current,
163
+ strokeWidth,
164
+ opacity,
165
+ className,
166
+ ],
167
+ });
168
+ }
169
+ </script>
170
+
171
+ {#if renderCtx === 'svg'}
172
+ <ellipse
173
+ bind:this={ref}
174
+ cx={motionCx.current}
175
+ cy={motionCy.current}
176
+ rx={motionRx.current}
177
+ ry={motionRy.current}
178
+ {fill}
179
+ fill-opacity={fillOpacity}
180
+ {stroke}
181
+ stroke-width={strokeWidth}
182
+ {opacity}
183
+ class={cls('lc-ellipse', className)}
184
+ {...restProps}
185
+ />
186
+ {:else if renderCtx === 'html'}
187
+ <div
188
+ style:position="absolute"
189
+ style:left="{motionCx.current}px"
190
+ style:top="{motionCy.current}px"
191
+ style:width="{motionRx.current * 2}px"
192
+ style:height="{motionRy.current * 2}px"
193
+ style:border-radius="50%"
194
+ style:background-color={fill}
195
+ style:opacity
196
+ style:border-width={strokeWidth}
197
+ style:border-color={stroke}
198
+ style:border-style="solid"
199
+ style:transform="translate(-50%, -50%)"
200
+ class={cls('lc-ellipse', className)}
201
+ {...restProps}
202
+ ></div>
203
+ {/if}
204
+
205
+ <style>
206
+ @layer base {
207
+ :global(:where(.lc-ellipse)) {
208
+ --fill-color: var(--color-surface-content, currentColor);
209
+ --stroke-color: initial;
210
+ }
211
+
212
+ /* Svg | Canvas layers */
213
+ :global(:where(.lc-layout-svg .lc-ellipse, svg.lc-ellipse):not([fill])) {
214
+ fill: var(--fill-color);
215
+ }
216
+ :global(:where(.lc-layout-svg .lc-ellipse, svg.lc-ellipse):not([stroke])) {
217
+ stroke: var(--stroke-color);
218
+ }
219
+
220
+ /* Html layers */
221
+ :global(:where(.lc-layout-html .lc-ellipse):not([background-color])) {
222
+ background-color: var(--fill-color);
223
+ }
224
+ :global(:where(.lc-layout-html .lc-ellipse):not([border-color])) {
225
+ border-color: var(--stroke-color);
226
+ }
227
+ }
228
+ </style>
@@ -0,0 +1,64 @@
1
+ import type { CommonStyleProps, Without } from '../utils/types.js';
2
+ export type EllipsePropsWithoutHTML = {
3
+ /**
4
+ * The center x position of the ellipse.
5
+ *
6
+ * @default 0
7
+ */
8
+ cx?: number;
9
+ /**
10
+ * The initial center x position of the ellipse.
11
+ *
12
+ * @default cx
13
+ */
14
+ initialCx?: number;
15
+ /**
16
+ * The center y position of the ellipse.
17
+ *
18
+ * @default 0
19
+ */
20
+ cy?: number;
21
+ /**
22
+ * The initial center y position of the ellipse.
23
+ *
24
+ * @default cy
25
+ */
26
+ initialCy?: number;
27
+ /**
28
+ * The radius of the ellipse on the x-axis.
29
+ *
30
+ * @default 1
31
+ */
32
+ rx?: number;
33
+ /**
34
+ * The initial radius of the ellipse on the x-axis.
35
+ *
36
+ * @default rx
37
+ */
38
+ initialRx?: number;
39
+ /**
40
+ * The radius of the ellipse on the y-axis.
41
+ *
42
+ * @default 1
43
+ */
44
+ ry?: number;
45
+ /**
46
+ * The initial radius of the ellipse on the y-axis.
47
+ *
48
+ * @default ry
49
+ */
50
+ initialRy?: number;
51
+ /**
52
+ * A bindable reference to the `<ellipse>` element
53
+ *
54
+ * @bindable
55
+ */
56
+ ref?: SVGEllipseElement;
57
+ motion?: MotionProp;
58
+ } & CommonStyleProps;
59
+ export type EllipseProps = EllipsePropsWithoutHTML & Without<SVGAttributes<Element>, EllipsePropsWithoutHTML>;
60
+ import { type MotionProp } from '../utils/motion.svelte.js';
61
+ import type { SVGAttributes } from 'svelte/elements';
62
+ declare const Ellipse: import("svelte").Component<EllipseProps, {}, "ref">;
63
+ type Ellipse = ReturnType<typeof Ellipse>;
64
+ export default Ellipse;
@@ -1,8 +1,22 @@
1
1
  <script lang="ts" module>
2
- import { forceSimulation, type Force, type Simulation, type SimulationNodeDatum } from 'd3-force';
2
+ import {
3
+ forceSimulation,
4
+ type Force,
5
+ type Simulation,
6
+ type SimulationLinkDatum,
7
+ type SimulationNodeDatum,
8
+ } from 'd3-force';
3
9
  import type { Snippet } from 'svelte';
4
10
 
5
- 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>>;
15
+
16
+ export type Data<TNode = any, TLink = any> = {
17
+ nodes: TNode[];
18
+ links?: TLink[];
19
+ };
6
20
 
7
21
  export type LinkPosition = {
8
22
  x1: number;
@@ -11,44 +25,115 @@
11
25
  y2: number;
12
26
  };
13
27
 
14
- export type ForceSimulationProps = {
28
+ export type OnStartEvent<
29
+ NodeDatum extends SimulationNodeDatum,
30
+ LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
31
+ > = {
32
+ alpha: number;
33
+ alphaTarget: number;
34
+ simulation: Simulation<NodeDatum, LinkDatum>;
35
+ };
36
+
37
+ export type OnTickEvent<
38
+ NodeDatum extends SimulationNodeDatum,
39
+ LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
40
+ > = {
41
+ alpha: number;
42
+ alphaTarget: number;
43
+ nodes: NodeDatum[];
44
+ links: LinkDatum[];
45
+ simulation: Simulation<NodeDatum, LinkDatum>;
46
+ };
47
+
48
+ export type OnEndEvent<
49
+ NodeDatum extends SimulationNodeDatum,
50
+ LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
51
+ > = {
52
+ alpha: number;
53
+ alphaTarget: number;
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>;
66
+ };
67
+
68
+ /**
69
+ * Default initial alpha value of the simulation.
70
+ */
71
+ export const DEFAULT_ALPHA: number = 1;
72
+
73
+ /**
74
+ * Default target alpha value for the simulation.
75
+ */
76
+ export const DEFAULT_ALPHA_TARGET: number = 0;
77
+
78
+ /**
79
+ * Default alpha decay rate per tick.
80
+ *
81
+ * Formula: `1 - Math.pow(0.001, 1 / 300)`.
82
+ */
83
+ export const DEFAULT_ALPHA_DECAY: number = 1 - Math.pow(0.001, 1 / 300);
84
+
85
+ /**
86
+ * Default minimum alpha value at which simulation stops.
87
+ */
88
+ export const DEFAULT_ALPHA_MIN: number = 0.01;
89
+
90
+ /**
91
+ * Default velocity decay factor applied to nodes each tick.
92
+ */
93
+ export const DEFAULT_VELOCITY_DECAY: number = 0.4;
94
+
95
+ export type ForceSimulationProps<
96
+ NodeDatum extends SimulationNodeDatum,
97
+ LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
98
+ > = {
15
99
  /**
16
100
  * Force simulation parameters
17
101
  */
18
- forces: Forces;
102
+ forces: Forces<NodeDatum, LinkDatum>;
19
103
 
20
104
  /**
21
- * An array of links to be used for position calculation.
105
+ * An object with arrays of nodes and links,
106
+ * to be used for position calculation.
22
107
  */
23
- links?: any[];
108
+ data: Data<NodeDatum, LinkDatum>;
24
109
 
25
110
  /**
26
111
  * Current alpha value of the simulation
27
- * @default 1
112
+ * @default DEFAULT_ALPHA
28
113
  */
29
114
  alpha?: number;
30
115
 
31
116
  /**
32
117
  * Target alpha value for the simulation
33
- * @default 0
118
+ * @default DEFAULT_ALPHA_TARGET
34
119
  */
35
120
  alphaTarget?: number;
36
121
 
37
122
  /**
38
123
  * Alpha decay rate per tick
39
- * @default 1 - Math.pow(0.001, 1 / 300)
124
+ * @default DEFAULT_ALPHA_DECAY
40
125
  */
41
126
  alphaDecay?: number;
42
127
 
43
128
  /**
44
129
  * Minimum alpha value at which simulation stops
45
- * @default 0.01
130
+ * @default DEFAULT_ALPHA_MIN
46
131
  */
47
132
  alphaMin?: number;
48
133
 
49
134
  /**
50
135
  * Velocity decay factor applied to nodes each tick
51
- * @default 0.4
136
+ * @default DEFAULT_VELOCITY_DECAY
52
137
  */
53
138
  velocityDecay?: number;
54
139
 
@@ -73,65 +158,78 @@
73
158
  /**
74
159
  * Callback function triggered when simulation starts
75
160
  */
76
- onStart?: () => void;
161
+ onStart?: (e: OnStartEvent<NodeDatum, LinkDatum | undefined>) => void;
162
+
163
+ /**
164
+ * Callback function triggered right before nodes get passed to the simulation
165
+ */
166
+ onNodesChange?: (e: OnNodesChangeEvent<NodeDatum, LinkDatum | undefined>) => void;
77
167
 
78
168
  /**
79
169
  * Callback function triggered on each simulation tick
80
170
  */
81
- onTick?: (e: { alpha: number; alphaTarget: number }) => void;
171
+ onTick?: (e: OnTickEvent<NodeDatum, LinkDatum | undefined>) => void;
82
172
 
83
173
  /**
84
174
  * Callback function triggered when simulation ends
85
175
  */
86
- onEnd?: () => void;
176
+ onEnd?: (e: OnEndEvent<NodeDatum, LinkDatum | undefined>) => void;
87
177
 
88
178
  children?: Snippet<
89
179
  [
90
180
  {
91
- nodes: any[];
92
- simulation: Simulation<SimulationNodeDatum, undefined>;
181
+ nodes: NodeDatum[];
182
+ links: LinkDatum[];
93
183
  linkPositions: LinkPosition[];
184
+ simulation: Simulation<NodeDatum, LinkDatum>;
94
185
  },
95
186
  ]
96
187
  >;
97
188
  };
98
189
  </script>
99
190
 
100
- <script lang="ts">
101
- import { getChartContext } from './Chart.svelte';
191
+ <script
192
+ lang="ts"
193
+ generics="NodeDatum extends SimulationNodeDatum,
194
+ LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,"
195
+ >
102
196
  import { watch } from 'runed';
103
197
 
104
198
  let {
105
199
  forces,
106
- links = [],
107
- alpha = $bindable(1),
108
- alphaTarget = 0,
109
- alphaDecay = 1 - Math.pow(0.001, 1 / 300),
110
- alphaMin = 0.001,
111
- velocityDecay = 0.4,
200
+ data,
201
+ alpha = $bindable(DEFAULT_ALPHA),
202
+ alphaTarget = DEFAULT_ALPHA_TARGET,
203
+ alphaDecay = DEFAULT_ALPHA_DECAY,
204
+ alphaMin = DEFAULT_ALPHA_MIN,
205
+ velocityDecay = DEFAULT_VELOCITY_DECAY,
112
206
  stopped = false,
113
207
  static: staticProp,
114
- onStart: onStartProp = () => {},
115
- onTick: onTickProp = () => {},
116
- onEnd: onEndProp = () => {},
208
+ onStart: onStartProp,
209
+ onNodesChange: onNodesChangeProp,
210
+ onTick: onTickProp,
211
+ onEnd: onEndProp,
117
212
  children,
118
213
  cloneNodes = false,
119
- }: ForceSimulationProps = $props();
120
-
121
- const ctx = getChartContext();
214
+ }: ForceSimulationProps<NodeDatum, LinkDatum> = $props();
122
215
 
123
216
  // MARK: Public Props
124
217
 
125
218
  // MARK: Private Props
126
219
 
127
- let nodes: SimulationNodeDatum[] = $state([]);
128
220
  let linkPositions: LinkPosition[] = $state([]);
221
+ let simulatedNodes: NodeDatum[] = $state([]);
222
+ let simulatedLinks: LinkDatum[] = $derived(data.links ?? []);
129
223
 
130
- const simulation = forceSimulation().stop();
224
+ // This casting is unfortunately necessary, due to unfortunate
225
+ // overloading choices made, over at `@typed/d3-force`:
226
+ const simulation: Simulation<NodeDatum, LinkDatum> = (
227
+ forceSimulation<NodeDatum>() as Simulation<NodeDatum, LinkDatum>
228
+ ).stop();
131
229
 
132
230
  // d3.Simulation does not provide a `.forces()` getter, so we need to
133
231
  // keep track of previous forces ourselves, for diffing against `forces`.
134
- let previousForces: Forces = {};
232
+ let previousForces: Forces<NodeDatum, LinkDatum> = {};
135
233
 
136
234
  let paused: boolean = true;
137
235
 
@@ -166,11 +264,12 @@
166
264
  );
167
265
 
168
266
  watch.pre(
169
- () => ctx.data,
267
+ () => data,
170
268
  () => {
171
- // Any time the `data` store gets changed we
172
- // pass them to the internal d3 simulation object:
173
- pushNodesToSimulation(ctx.data as any[]);
269
+ // Any time the `nodes` prop, or the `data` store gets changed
270
+ // we pass them to the internal d3 simulation object:
271
+ onNodesChange();
272
+ pushNodesToSimulation(data.nodes);
174
273
  runOrResumeSimulation();
175
274
  }
176
275
  );
@@ -192,12 +291,8 @@
192
291
  // pass it to the internal d3 simulation object:
193
292
  pushAlphaToSimulation(alpha);
194
293
 
195
- // Only resume the simulation as long as `alpha`
196
- // is above the cut-off threshold of `alphaMin`,
197
- // otherwise our simulation will never terminate:
198
- if (simulation.alpha() >= simulation.alphaMin()) {
199
- runOrResumeSimulation();
200
- }
294
+ // Then we attempt to resume the simulation:
295
+ runOrResumeSimulation();
201
296
  }
202
297
  );
203
298
 
@@ -235,7 +330,7 @@
235
330
  simulation.nodes(nodes);
236
331
  }
237
332
 
238
- function pushForcesToSimulation(forces: Forces) {
333
+ function pushForcesToSimulation(forces: Forces<NodeDatum, LinkDatum>) {
239
334
  // Evict obsolete forces:
240
335
  const names = Object.keys(previousForces);
241
336
  for (const name of names) {
@@ -259,7 +354,7 @@
259
354
  // Keeping the link positions in sync with the simulation
260
355
  // so we don't need to recalculate _all_ link positions on each tick
261
356
  // which bogs down the simulation
262
- linkPositions = links.map((link: any) => ({
357
+ linkPositions = simulatedLinks.map((link: any) => ({
263
358
  x1: link.source.x ?? 0,
264
359
  y1: link.source.y ?? 0,
265
360
  x2: link.target.x ?? 0,
@@ -270,7 +365,8 @@
270
365
  // MARK: Pull State
271
366
 
272
367
  function pullNodesFromSimulation() {
273
- nodes = cloneNodes ? structuredClone(simulation.nodes()) : simulation.nodes();
368
+ const simulationNodes = simulation.nodes();
369
+ simulatedNodes = cloneNodes ? structuredClone(simulationNodes) : simulationNodes;
274
370
  }
275
371
 
276
372
  function pullAlphaFromSimulation() {
@@ -337,6 +433,13 @@
337
433
  return;
338
434
  }
339
435
 
436
+ if (simulation.alpha() < simulation.alphaMin()) {
437
+ // Only resume the simulation as long as `alpha`
438
+ // is above the cut-off threshold of `alphaMin`,
439
+ // otherwise our simulation will never terminate:
440
+ return;
441
+ }
442
+
340
443
  onStart();
341
444
  simulation.restart();
342
445
 
@@ -364,7 +467,12 @@
364
467
  }
365
468
 
366
469
  paused = false;
367
- onStartProp();
470
+
471
+ onStartProp?.({
472
+ alpha,
473
+ alphaTarget,
474
+ simulation,
475
+ });
368
476
  }
369
477
 
370
478
  function onTick() {
@@ -372,7 +480,13 @@
372
480
  pullAlphaFromSimulation();
373
481
  updateLinkPositions();
374
482
 
375
- onTickProp({ alpha, alphaTarget });
483
+ onTickProp?.({
484
+ alpha,
485
+ alphaTarget,
486
+ nodes: simulatedNodes,
487
+ links: simulatedLinks,
488
+ simulation,
489
+ });
376
490
  }
377
491
 
378
492
  function onEnd() {
@@ -382,7 +496,22 @@
382
496
  }
383
497
 
384
498
  paused = true;
385
- onEndProp();
499
+
500
+ onEndProp?.({
501
+ alpha,
502
+ alphaTarget,
503
+ simulation,
504
+ });
505
+ }
506
+
507
+ function onNodesChange() {
508
+ onNodesChangeProp?.({
509
+ alpha,
510
+ alphaTarget,
511
+ nodes: data.nodes,
512
+ links: data.links ?? [],
513
+ simulation,
514
+ });
386
515
  }
387
516
 
388
517
  $effect(() => {
@@ -393,4 +522,9 @@
393
522
  });
394
523
  </script>
395
524
 
396
- {@render children?.({ nodes: nodes, simulation, linkPositions })}
525
+ {@render children?.({
526
+ nodes: simulatedNodes,
527
+ links: simulatedLinks,
528
+ simulation,
529
+ linkPositions,
530
+ })}