layerchart 2.0.0-next.2 → 2.0.0-next.20
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/dist/components/AnnotationLine.svelte +17 -29
- package/dist/components/AnnotationLine.svelte.d.ts +4 -2
- package/dist/components/AnnotationPoint.svelte +11 -13
- package/dist/components/AnnotationPoint.svelte.d.ts +4 -2
- package/dist/components/AnnotationRange.svelte +3 -3
- package/dist/components/Arc.svelte +2 -2
- package/dist/components/Axis.svelte +52 -24
- package/dist/components/Axis.svelte.d.ts +14 -3
- package/dist/components/Bar.svelte +7 -4
- package/dist/components/Bar.svelte.d.ts +4 -1
- package/dist/components/Bars.svelte +9 -6
- package/dist/components/Bars.svelte.d.ts +3 -3
- package/dist/components/Blur.svelte +20 -12
- package/dist/components/Blur.svelte.d.ts +2 -5
- package/dist/components/Calendar.svelte +10 -6
- package/dist/components/Calendar.svelte.d.ts +2 -1
- package/dist/components/Chart.svelte +2 -2
- package/dist/components/ClipPath.svelte +14 -9
- package/dist/components/Connector.svelte +2 -2
- package/dist/components/Connector.svelte.d.ts +1 -1
- package/dist/components/Ellipse.svelte +187 -0
- package/dist/components/Ellipse.svelte.d.ts +64 -0
- package/dist/components/ForceSimulation.svelte +168 -50
- package/dist/components/ForceSimulation.svelte.d.ts +80 -21
- package/dist/components/GeoEdgeFade.svelte +4 -3
- package/dist/components/GeoEdgeFade.svelte.d.ts +2 -2
- package/dist/components/GeoPath.svelte +12 -5
- package/dist/components/GeoPoint.svelte +1 -2
- package/dist/components/GeoSpline.svelte +4 -4
- package/dist/components/GeoSpline.svelte.d.ts +1 -1
- package/dist/components/Graticule.svelte +3 -2
- package/dist/components/Grid.svelte +8 -7
- package/dist/components/Grid.svelte.d.ts +2 -1
- package/dist/components/Group.svelte +45 -5
- package/dist/components/Group.svelte.d.ts +32 -4
- package/dist/components/Highlight.svelte +1 -1
- package/dist/components/Hull.svelte +4 -4
- package/dist/components/Hull.svelte.d.ts +2 -2
- package/dist/components/Labels.svelte +6 -4
- package/dist/components/Labels.svelte.d.ts +2 -2
- package/dist/components/Legend.svelte +8 -5
- package/dist/components/Legend.svelte.d.ts +3 -3
- package/dist/components/MonthPath.svelte +14 -11
- package/dist/components/MonthPath.svelte.d.ts +4 -3
- package/dist/components/Polygon.svelte +285 -0
- package/dist/components/Polygon.svelte.d.ts +115 -0
- package/dist/components/RadialGradient.svelte +1 -3
- package/dist/components/Rule.svelte +3 -2
- package/dist/components/Spline.svelte +30 -18
- package/dist/components/Spline.svelte.d.ts +12 -4
- package/dist/components/Text.svelte +60 -48
- package/dist/components/Text.svelte.d.ts +6 -0
- package/dist/components/Treemap.svelte +63 -26
- package/dist/components/Treemap.svelte.d.ts +11 -11
- package/dist/components/Voronoi.svelte +55 -36
- package/dist/components/Voronoi.svelte.d.ts +5 -3
- package/dist/components/charts/ArcChart.svelte +2 -2
- package/dist/components/charts/AreaChart.svelte +9 -10
- package/dist/components/charts/BarChart.svelte +63 -53
- package/dist/components/charts/DefaultTooltip.svelte +1 -1
- package/dist/components/charts/LineChart.svelte +8 -5
- package/dist/components/charts/PieChart.svelte +2 -2
- package/dist/components/charts/ScatterChart.svelte +0 -2
- package/dist/components/charts/utils.svelte.d.ts +3 -3
- package/dist/components/charts/utils.svelte.js +7 -3
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +4 -0
- package/dist/components/layout/Canvas.svelte +67 -49
- package/dist/components/layout/Canvas.svelte.d.ts +6 -0
- package/dist/components/layout/Layer.svelte +6 -4
- package/dist/components/layout/Layer.svelte.d.ts +6 -4
- package/dist/components/tooltip/Tooltip.svelte +14 -7
- package/dist/components/tooltip/TooltipContext.svelte +25 -8
- package/dist/components/tooltip/TooltipContext.svelte.d.ts +1 -1
- package/dist/components/tooltip/TooltipHeader.svelte +5 -4
- package/dist/components/tooltip/TooltipHeader.svelte.d.ts +3 -3
- package/dist/components/tooltip/TooltipItem.svelte +5 -4
- package/dist/components/tooltip/TooltipItem.svelte.d.ts +3 -3
- package/dist/components/tooltip/TooltipList.svelte +1 -1
- package/dist/components/tooltip/tooltipMetaContext.d.ts +2 -2
- package/dist/docs/Blockquote.svelte +3 -1
- package/dist/docs/Blockquote.svelte.d.ts +4 -19
- package/dist/docs/Code.svelte +20 -12
- package/dist/docs/Code.svelte.d.ts +9 -23
- package/dist/docs/Header1.svelte +4 -2
- package/dist/docs/Header1.svelte.d.ts +4 -28
- package/dist/docs/Json.svelte +11 -3
- package/dist/docs/Json.svelte.d.ts +9 -21
- package/dist/docs/Layout.svelte +10 -7
- package/dist/docs/Layout.svelte.d.ts +4 -19
- package/dist/docs/Link.svelte +7 -3
- package/dist/docs/Link.svelte.d.ts +4 -38
- package/dist/docs/TilesetField.svelte +20 -19
- package/dist/docs/TilesetField.svelte.d.ts +5 -22
- package/dist/docs/ViewSourceButton.svelte +7 -4
- package/dist/docs/ViewSourceButton.svelte.d.ts +7 -21
- package/dist/utils/arcText.svelte.js +4 -4
- package/dist/utils/canvas.d.ts +77 -0
- package/dist/utils/canvas.js +105 -41
- package/dist/utils/genData.d.ts +14 -0
- package/dist/utils/genData.js +24 -6
- package/dist/utils/path.d.ts +10 -0
- package/dist/utils/path.js +30 -0
- package/dist/utils/scales.svelte.d.ts +3 -2
- package/dist/utils/scales.svelte.js +7 -3
- package/dist/utils/shape.d.ts +43 -0
- package/dist/utils/shape.js +59 -0
- package/dist/utils/string.d.ts +49 -0
- package/dist/utils/string.js +4 -2
- package/dist/utils/ticks.d.ts +4 -4
- package/dist/utils/ticks.js +106 -159
- package/dist/utils/ticks.test.js +6 -16
- package/dist/utils/treemap.d.ts +1 -1
- package/package.json +25 -22
- package/dist/utils/object.js +0 -2
|
@@ -844,12 +844,12 @@
|
|
|
844
844
|
if (verbose === true) {
|
|
845
845
|
if (width <= 0 && isMounted === true) {
|
|
846
846
|
console.warn(
|
|
847
|
-
|
|
847
|
+
`[LayerChart] Target div has zero or negative width (${width}). Did you forget to set an explicit width in CSS on the container?`
|
|
848
848
|
);
|
|
849
849
|
}
|
|
850
850
|
if (height <= 0 && isMounted === true) {
|
|
851
851
|
console.warn(
|
|
852
|
-
|
|
852
|
+
`[LayerChart] Target div has zero or negative height (${height}). Did you forget to set an explicit height in CSS on the container?`
|
|
853
853
|
);
|
|
854
854
|
}
|
|
855
855
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { layerClass } from '../utils/attributes.js';
|
|
5
5
|
import type { Snippet } from 'svelte';
|
|
6
6
|
import type { SVGAttributes } from 'svelte/elements';
|
|
7
|
+
import { getRenderContext } from './Chart.svelte';
|
|
7
8
|
|
|
8
9
|
export type ClipPathPropsWithoutHTML = {
|
|
9
10
|
/**
|
|
@@ -55,20 +56,24 @@
|
|
|
55
56
|
}: ClipPathPropsWithoutHTML = $props();
|
|
56
57
|
|
|
57
58
|
const url = $derived(`url(#${id})`);
|
|
59
|
+
|
|
60
|
+
const renderContext = getRenderContext();
|
|
58
61
|
</script>
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
+
{#if renderContext === 'svg'}
|
|
64
|
+
<defs>
|
|
65
|
+
<clipPath {id} {...restProps}>
|
|
66
|
+
{@render clip?.({ id })}
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
</defs>
|
|
68
|
+
{#if useId}
|
|
69
|
+
<use href="#{useId}" />
|
|
70
|
+
{/if}
|
|
71
|
+
</clipPath>
|
|
72
|
+
</defs>
|
|
73
|
+
{/if}
|
|
69
74
|
|
|
70
75
|
{#if children}
|
|
71
|
-
{#if disabled}
|
|
76
|
+
{#if disabled || renderContext !== 'svg'}
|
|
72
77
|
{@render children({ id, url, useId })}
|
|
73
78
|
{:else}
|
|
74
79
|
<g style:clip-path={url} class={layerClass('clip-path-g')}>
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
type = 'rounded',
|
|
83
83
|
radius = 20,
|
|
84
84
|
curve = curveLinear,
|
|
85
|
-
|
|
85
|
+
pathRef = $bindable(),
|
|
86
86
|
pathData: pathDataProp,
|
|
87
87
|
marker,
|
|
88
88
|
markerStart,
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
|
|
138
138
|
<Spline
|
|
139
139
|
pathData={motionPath.current}
|
|
140
|
-
bind:
|
|
140
|
+
bind:pathRef
|
|
141
141
|
marker-start={markerStartId ? `url(#${markerStartId})` : undefined}
|
|
142
142
|
marker-mid={markerMidId ? `url(#${markerMidId})` : undefined}
|
|
143
143
|
marker-end={markerEndId ? `url(#${markerEndId})` : undefined}
|
|
@@ -46,6 +46,6 @@ import { type CurveFactory } from 'd3-shape';
|
|
|
46
46
|
import { type ConnectorCoords, type ConnectorSweep, type ConnectorType } from '../utils/connectorUtils.js';
|
|
47
47
|
import { type SplineProps, type SplinePropsWithoutHTML } from './Spline.svelte';
|
|
48
48
|
import type { Without } from '../utils/types.js';
|
|
49
|
-
declare const Connector: import("svelte").Component<ConnectorProps, {}, "
|
|
49
|
+
declare const Connector: import("svelte").Component<ConnectorProps, {}, "pathRef">;
|
|
50
50
|
type Connector = ReturnType<typeof Connector>;
|
|
51
51
|
export default Connector;
|
|
@@ -0,0 +1,187 @@
|
|
|
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
|
+
import { layerClass } from '../utils/attributes.js';
|
|
86
|
+
|
|
87
|
+
let {
|
|
88
|
+
cx = 0,
|
|
89
|
+
initialCx: initialCxProp,
|
|
90
|
+
cy = 0,
|
|
91
|
+
initialCy: initialCyProp,
|
|
92
|
+
rx = 1,
|
|
93
|
+
initialRx: initialRxProp,
|
|
94
|
+
ry = 1,
|
|
95
|
+
initialRy: initialRyProp,
|
|
96
|
+
motion,
|
|
97
|
+
fill,
|
|
98
|
+
fillOpacity,
|
|
99
|
+
stroke,
|
|
100
|
+
strokeWidth,
|
|
101
|
+
opacity,
|
|
102
|
+
class: className,
|
|
103
|
+
ref: refProp = $bindable(),
|
|
104
|
+
...restProps
|
|
105
|
+
}: EllipseProps = $props();
|
|
106
|
+
|
|
107
|
+
let ref = $state<SVGEllipseElement>();
|
|
108
|
+
|
|
109
|
+
$effect.pre(() => {
|
|
110
|
+
refProp = ref;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const initialCx = initialCxProp ?? cx;
|
|
114
|
+
const initialCy = initialCyProp ?? cy;
|
|
115
|
+
const initialRx = initialRxProp ?? rx;
|
|
116
|
+
const initialRy = initialRyProp ?? ry;
|
|
117
|
+
|
|
118
|
+
const renderCtx = getRenderContext();
|
|
119
|
+
|
|
120
|
+
const motionCx = createMotion(initialCx, () => cx, motion);
|
|
121
|
+
const motionCy = createMotion(initialCy, () => cy, motion);
|
|
122
|
+
const motionRx = createMotion(initialRx, () => rx, motion);
|
|
123
|
+
const motionRy = createMotion(initialRy, () => ry, motion);
|
|
124
|
+
|
|
125
|
+
function render(
|
|
126
|
+
ctx: CanvasRenderingContext2D,
|
|
127
|
+
styleOverrides: ComputedStylesOptions | undefined
|
|
128
|
+
) {
|
|
129
|
+
renderEllipse(
|
|
130
|
+
ctx,
|
|
131
|
+
{ cx: motionCx.current, cy: motionCy.current, rx: motionRx.current, ry: motionRy.current },
|
|
132
|
+
styleOverrides
|
|
133
|
+
? merge({ styles: { strokeWidth } }, styleOverrides)
|
|
134
|
+
: {
|
|
135
|
+
styles: { fill, fillOpacity, stroke, strokeWidth, opacity },
|
|
136
|
+
classes: className,
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
|
|
142
|
+
const fillKey = createKey(() => fill);
|
|
143
|
+
const strokeKey = createKey(() => stroke);
|
|
144
|
+
|
|
145
|
+
if (renderCtx === 'canvas') {
|
|
146
|
+
registerCanvasComponent({
|
|
147
|
+
name: 'Ellipse',
|
|
148
|
+
render,
|
|
149
|
+
events: {
|
|
150
|
+
click: restProps.onclick,
|
|
151
|
+
pointerdown: restProps.onpointerdown,
|
|
152
|
+
pointerenter: restProps.onpointerenter,
|
|
153
|
+
pointermove: restProps.onpointermove,
|
|
154
|
+
pointerleave: restProps.onpointerleave,
|
|
155
|
+
},
|
|
156
|
+
deps: () => [
|
|
157
|
+
motionCx.current,
|
|
158
|
+
motionCy.current,
|
|
159
|
+
motionRx.current,
|
|
160
|
+
motionRy.current,
|
|
161
|
+
fillKey.current,
|
|
162
|
+
fillOpacity,
|
|
163
|
+
strokeKey.current,
|
|
164
|
+
strokeWidth,
|
|
165
|
+
opacity,
|
|
166
|
+
className,
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
{#if renderCtx === 'svg'}
|
|
173
|
+
<ellipse
|
|
174
|
+
bind:this={ref}
|
|
175
|
+
cx={motionCx.current}
|
|
176
|
+
cy={motionCy.current}
|
|
177
|
+
rx={motionRx.current}
|
|
178
|
+
ry={motionRy.current}
|
|
179
|
+
{fill}
|
|
180
|
+
fill-opacity={fillOpacity}
|
|
181
|
+
{stroke}
|
|
182
|
+
stroke-width={strokeWidth}
|
|
183
|
+
{opacity}
|
|
184
|
+
class={cls(layerClass('ellipse'), fill == null && 'fill-surface-content', className)}
|
|
185
|
+
{...restProps}
|
|
186
|
+
/>
|
|
187
|
+
{/if}
|
|
@@ -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 {
|
|
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
|
|
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,114 @@
|
|
|
11
25
|
y2: number;
|
|
12
26
|
};
|
|
13
27
|
|
|
14
|
-
export type
|
|
28
|
+
export type OnStartEvent<
|
|
29
|
+
NodeDatum extends SimulationNodeDatum,
|
|
30
|
+
LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
|
|
31
|
+
> = {
|
|
32
|
+
alpha: number;
|
|
33
|
+
alphaTarget: number;
|
|
34
|
+
simulation: SimulationFor<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: NodeDatumFor<NodeDatum>[];
|
|
44
|
+
links: LinkDatumFor<NodeDatum, LinkDatum>[];
|
|
45
|
+
simulation: SimulationFor<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: SimulationFor<NodeDatum, LinkDatum>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Default initial alpha value of the simulation.
|
|
59
|
+
*/
|
|
60
|
+
export const DEFAULT_ALPHA: number = 1;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Default target alpha value for the simulation.
|
|
64
|
+
*/
|
|
65
|
+
export const DEFAULT_ALPHA_TARGET: number = 0;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Default alpha decay rate per tick.
|
|
69
|
+
*
|
|
70
|
+
* Formula: `1 - Math.pow(0.001, 1 / 300)`.
|
|
71
|
+
*/
|
|
72
|
+
export const DEFAULT_ALPHA_DECAY: number = 1 - Math.pow(0.001, 1 / 300);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Default minimum alpha value at which simulation stops.
|
|
76
|
+
*/
|
|
77
|
+
export const DEFAULT_ALPHA_MIN: number = 0.01;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Default velocity decay factor applied to nodes each tick.
|
|
81
|
+
*/
|
|
82
|
+
export const DEFAULT_VELOCITY_DECAY: number = 0.4;
|
|
83
|
+
|
|
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
|
+
export type ForceSimulationProps<
|
|
95
|
+
NodeDatum extends SimulationNodeDatum,
|
|
96
|
+
LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,
|
|
97
|
+
> = {
|
|
15
98
|
/**
|
|
16
99
|
* Force simulation parameters
|
|
17
100
|
*/
|
|
18
|
-
forces: Forces
|
|
101
|
+
forces: Forces<NodeDatum, LinkDatum>;
|
|
19
102
|
|
|
20
103
|
/**
|
|
21
|
-
* An
|
|
104
|
+
* An object with arrays of nodes and links,
|
|
105
|
+
* to be used for position calculation.
|
|
22
106
|
*/
|
|
23
|
-
|
|
107
|
+
data: Data<NodeDatum, LinkDatum>;
|
|
24
108
|
|
|
25
109
|
/**
|
|
26
110
|
* Current alpha value of the simulation
|
|
27
|
-
* @default
|
|
111
|
+
* @default DEFAULT_ALPHA
|
|
28
112
|
*/
|
|
29
113
|
alpha?: number;
|
|
30
114
|
|
|
31
115
|
/**
|
|
32
116
|
* Target alpha value for the simulation
|
|
33
|
-
* @default
|
|
117
|
+
* @default DEFAULT_ALPHA_TARGET
|
|
34
118
|
*/
|
|
35
119
|
alphaTarget?: number;
|
|
36
120
|
|
|
37
121
|
/**
|
|
38
122
|
* Alpha decay rate per tick
|
|
39
|
-
* @default
|
|
123
|
+
* @default DEFAULT_ALPHA_DECAY
|
|
40
124
|
*/
|
|
41
125
|
alphaDecay?: number;
|
|
42
126
|
|
|
43
127
|
/**
|
|
44
128
|
* Minimum alpha value at which simulation stops
|
|
45
|
-
* @default
|
|
129
|
+
* @default DEFAULT_ALPHA_MIN
|
|
46
130
|
*/
|
|
47
131
|
alphaMin?: number;
|
|
48
132
|
|
|
49
133
|
/**
|
|
50
134
|
* Velocity decay factor applied to nodes each tick
|
|
51
|
-
* @default
|
|
135
|
+
* @default DEFAULT_VELOCITY_DECAY
|
|
52
136
|
*/
|
|
53
137
|
velocityDecay?: number;
|
|
54
138
|
|
|
@@ -73,65 +157,74 @@
|
|
|
73
157
|
/**
|
|
74
158
|
* Callback function triggered when simulation starts
|
|
75
159
|
*/
|
|
76
|
-
onStart?: () => void;
|
|
160
|
+
onStart?: (e: OnStartEvent<NodeDatum, LinkDatum | undefined>) => void;
|
|
77
161
|
|
|
78
162
|
/**
|
|
79
163
|
* Callback function triggered on each simulation tick
|
|
80
164
|
*/
|
|
81
|
-
onTick?: (e:
|
|
165
|
+
onTick?: (e: OnTickEvent<NodeDatum, LinkDatum | undefined>) => void;
|
|
82
166
|
|
|
83
167
|
/**
|
|
84
168
|
* Callback function triggered when simulation ends
|
|
85
169
|
*/
|
|
86
|
-
onEnd?: () => void;
|
|
170
|
+
onEnd?: (e: OnEndEvent<NodeDatum, LinkDatum | undefined>) => void;
|
|
87
171
|
|
|
88
172
|
children?: Snippet<
|
|
89
173
|
[
|
|
90
174
|
{
|
|
91
|
-
nodes:
|
|
92
|
-
|
|
175
|
+
nodes: NodeDatumFor<NodeDatum>[];
|
|
176
|
+
links: LinkDatumFor<NodeDatum, LinkDatum>[];
|
|
93
177
|
linkPositions: LinkPosition[];
|
|
178
|
+
simulation: SimulationFor<NodeDatum, LinkDatum>;
|
|
94
179
|
},
|
|
95
180
|
]
|
|
96
181
|
>;
|
|
97
182
|
};
|
|
98
183
|
</script>
|
|
99
184
|
|
|
100
|
-
<script
|
|
101
|
-
|
|
185
|
+
<script
|
|
186
|
+
lang="ts"
|
|
187
|
+
generics="NodeDatum extends SimulationNodeDatum,
|
|
188
|
+
LinkDatum extends SimulationLinkDatum<NodeDatum> | undefined,"
|
|
189
|
+
>
|
|
102
190
|
import { watch } from 'runed';
|
|
103
191
|
|
|
104
192
|
let {
|
|
105
193
|
forces,
|
|
106
|
-
|
|
107
|
-
alpha = $bindable(
|
|
108
|
-
alphaTarget =
|
|
109
|
-
alphaDecay =
|
|
110
|
-
alphaMin =
|
|
111
|
-
velocityDecay =
|
|
194
|
+
data,
|
|
195
|
+
alpha = $bindable(DEFAULT_ALPHA),
|
|
196
|
+
alphaTarget = DEFAULT_ALPHA_TARGET,
|
|
197
|
+
alphaDecay = DEFAULT_ALPHA_DECAY,
|
|
198
|
+
alphaMin = DEFAULT_ALPHA_MIN,
|
|
199
|
+
velocityDecay = DEFAULT_VELOCITY_DECAY,
|
|
112
200
|
stopped = false,
|
|
113
201
|
static: staticProp,
|
|
114
|
-
onStart: onStartProp
|
|
115
|
-
onTick: onTickProp
|
|
116
|
-
onEnd: onEndProp
|
|
202
|
+
onStart: onStartProp,
|
|
203
|
+
onTick: onTickProp,
|
|
204
|
+
onEnd: onEndProp,
|
|
117
205
|
children,
|
|
118
206
|
cloneNodes = false,
|
|
119
|
-
}: ForceSimulationProps = $props();
|
|
120
|
-
|
|
121
|
-
const ctx = getChartContext();
|
|
207
|
+
}: ForceSimulationProps<NodeDatum, LinkDatum> = $props();
|
|
122
208
|
|
|
123
209
|
// MARK: Public Props
|
|
124
210
|
|
|
125
211
|
// MARK: Private Props
|
|
126
212
|
|
|
127
|
-
let nodes: SimulationNodeDatum[] = $state([]);
|
|
128
213
|
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
|
+
);
|
|
129
218
|
|
|
130
|
-
|
|
219
|
+
// This casting is unfortunately necessary, due to unfortunate
|
|
220
|
+
// overloading choices made, over at `@typed/d3-force`:
|
|
221
|
+
const simulation: SimulationFor<NodeDatum, LinkDatum> = (
|
|
222
|
+
forceSimulation() as SimulationFor<NodeDatum, LinkDatum>
|
|
223
|
+
).stop();
|
|
131
224
|
|
|
132
225
|
// d3.Simulation does not provide a `.forces()` getter, so we need to
|
|
133
226
|
// keep track of previous forces ourselves, for diffing against `forces`.
|
|
134
|
-
let previousForces: Forces = {};
|
|
227
|
+
let previousForces: Forces<NodeDatum, LinkDatum> = {};
|
|
135
228
|
|
|
136
229
|
let paused: boolean = true;
|
|
137
230
|
|
|
@@ -166,11 +259,11 @@
|
|
|
166
259
|
);
|
|
167
260
|
|
|
168
261
|
watch.pre(
|
|
169
|
-
() =>
|
|
262
|
+
() => data,
|
|
170
263
|
() => {
|
|
171
|
-
// Any time the `data` store gets changed
|
|
172
|
-
// pass them to the internal d3 simulation object:
|
|
173
|
-
pushNodesToSimulation(
|
|
264
|
+
// Any time the `nodes` prop, or the `data` store gets changed
|
|
265
|
+
// we pass them to the internal d3 simulation object:
|
|
266
|
+
pushNodesToSimulation(data.nodes);
|
|
174
267
|
runOrResumeSimulation();
|
|
175
268
|
}
|
|
176
269
|
);
|
|
@@ -192,12 +285,8 @@
|
|
|
192
285
|
// pass it to the internal d3 simulation object:
|
|
193
286
|
pushAlphaToSimulation(alpha);
|
|
194
287
|
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
// otherwise our simulation will never terminate:
|
|
198
|
-
if (simulation.alpha() >= simulation.alphaMin()) {
|
|
199
|
-
runOrResumeSimulation();
|
|
200
|
-
}
|
|
288
|
+
// Then we attempt to resume the simulation:
|
|
289
|
+
runOrResumeSimulation();
|
|
201
290
|
}
|
|
202
291
|
);
|
|
203
292
|
|
|
@@ -235,7 +324,7 @@
|
|
|
235
324
|
simulation.nodes(nodes);
|
|
236
325
|
}
|
|
237
326
|
|
|
238
|
-
function pushForcesToSimulation(forces: Forces) {
|
|
327
|
+
function pushForcesToSimulation(forces: Forces<NodeDatum, LinkDatum>) {
|
|
239
328
|
// Evict obsolete forces:
|
|
240
329
|
const names = Object.keys(previousForces);
|
|
241
330
|
for (const name of names) {
|
|
@@ -259,7 +348,7 @@
|
|
|
259
348
|
// Keeping the link positions in sync with the simulation
|
|
260
349
|
// so we don't need to recalculate _all_ link positions on each tick
|
|
261
350
|
// which bogs down the simulation
|
|
262
|
-
linkPositions =
|
|
351
|
+
linkPositions = simulatedLinks.map((link: any) => ({
|
|
263
352
|
x1: link.source.x ?? 0,
|
|
264
353
|
y1: link.source.y ?? 0,
|
|
265
354
|
x2: link.target.x ?? 0,
|
|
@@ -270,7 +359,8 @@
|
|
|
270
359
|
// MARK: Pull State
|
|
271
360
|
|
|
272
361
|
function pullNodesFromSimulation() {
|
|
273
|
-
|
|
362
|
+
const simulationNodes = simulation.nodes();
|
|
363
|
+
simulatedNodes = cloneNodes ? structuredClone(simulationNodes) : simulationNodes;
|
|
274
364
|
}
|
|
275
365
|
|
|
276
366
|
function pullAlphaFromSimulation() {
|
|
@@ -337,6 +427,13 @@
|
|
|
337
427
|
return;
|
|
338
428
|
}
|
|
339
429
|
|
|
430
|
+
if (simulation.alpha() < simulation.alphaMin()) {
|
|
431
|
+
// Only resume the simulation as long as `alpha`
|
|
432
|
+
// is above the cut-off threshold of `alphaMin`,
|
|
433
|
+
// otherwise our simulation will never terminate:
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
340
437
|
onStart();
|
|
341
438
|
simulation.restart();
|
|
342
439
|
|
|
@@ -364,7 +461,12 @@
|
|
|
364
461
|
}
|
|
365
462
|
|
|
366
463
|
paused = false;
|
|
367
|
-
|
|
464
|
+
|
|
465
|
+
onStartProp?.({
|
|
466
|
+
alpha,
|
|
467
|
+
alphaTarget,
|
|
468
|
+
simulation,
|
|
469
|
+
});
|
|
368
470
|
}
|
|
369
471
|
|
|
370
472
|
function onTick() {
|
|
@@ -372,7 +474,13 @@
|
|
|
372
474
|
pullAlphaFromSimulation();
|
|
373
475
|
updateLinkPositions();
|
|
374
476
|
|
|
375
|
-
onTickProp({
|
|
477
|
+
onTickProp?.({
|
|
478
|
+
alpha,
|
|
479
|
+
alphaTarget,
|
|
480
|
+
nodes: simulatedNodes,
|
|
481
|
+
links: simulatedLinks,
|
|
482
|
+
simulation,
|
|
483
|
+
});
|
|
376
484
|
}
|
|
377
485
|
|
|
378
486
|
function onEnd() {
|
|
@@ -382,7 +490,12 @@
|
|
|
382
490
|
}
|
|
383
491
|
|
|
384
492
|
paused = true;
|
|
385
|
-
|
|
493
|
+
|
|
494
|
+
onEndProp?.({
|
|
495
|
+
alpha,
|
|
496
|
+
alphaTarget,
|
|
497
|
+
simulation,
|
|
498
|
+
});
|
|
386
499
|
}
|
|
387
500
|
|
|
388
501
|
$effect(() => {
|
|
@@ -393,4 +506,9 @@
|
|
|
393
506
|
});
|
|
394
507
|
</script>
|
|
395
508
|
|
|
396
|
-
{@render children?.({
|
|
509
|
+
{@render children?.({
|
|
510
|
+
nodes: simulatedNodes,
|
|
511
|
+
links: simulatedLinks,
|
|
512
|
+
simulation,
|
|
513
|
+
linkPositions,
|
|
514
|
+
})}
|