layerchart 2.0.0-next.57 → 2.0.0-next.59
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 +112 -66
- package/dist/components/AnnotationLine.svelte.d.ts +10 -2
- package/dist/components/AnnotationPoint.svelte +97 -23
- package/dist/components/AnnotationPoint.svelte.d.ts +8 -1
- package/dist/components/GeoPath.svelte +4 -4
- package/dist/components/Legend.svelte +1 -0
- package/dist/components/Link.svelte +261 -75
- package/dist/components/Link.svelte.d.ts +69 -26
- package/dist/components/Text.svelte +1 -1
- package/dist/components/Voronoi.svelte +35 -6
- package/dist/components/Voronoi.svelte.d.ts +9 -0
- package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-separate-data-per-series-should-render-stacked-series-with-separate-data-arrays-1.png +0 -0
- package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-separate-data-per-series-should-render-stacked-series-with-separate-data-arrays-2.png +0 -0
- package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-series-header-for-multi-series-1.png +0 -0
- package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-series-header-for-multi-series-2.png +0 -0
- package/dist/components/index.d.ts +0 -2
- package/dist/components/index.js +0 -2
- package/dist/components/tooltip/TooltipContext.svelte +39 -10
- package/dist/components/tooltip/TooltipContext.svelte.d.ts +14 -0
- package/dist/states/brush.svelte.d.ts +1 -1
- package/dist/states/chart.svelte.js +24 -8
- package/dist/states/chart.svelte.test.js +181 -0
- package/dist/utils/linkUtils.d.ts +42 -0
- package/dist/utils/{connectorUtils.js → linkUtils.js} +56 -6
- package/package.json +1 -1
- package/dist/components/Connector.svelte +0 -167
- package/dist/components/Connector.svelte.d.ts +0 -56
- package/dist/utils/connectorUtils.d.ts +0 -34
|
@@ -3,116 +3,169 @@
|
|
|
3
3
|
import type { Without } from '../utils/types.js';
|
|
4
4
|
import type { MotionNoneOption, MotionTweenOption } from '../utils/motion.svelte.js';
|
|
5
5
|
import { curveBumpX, curveBumpY, type CurveFactory } from 'd3-shape';
|
|
6
|
+
import type { LinkSweep, LinkType } from '../utils/linkUtils.js';
|
|
7
|
+
import type { PathProps, PathPropsWithoutHTML } from './Path.svelte';
|
|
8
|
+
import type { Accessor } from '../utils/common.js';
|
|
6
9
|
|
|
7
10
|
export type LinkPropsWithoutHTML = {
|
|
8
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Source `x` coordinate. Accepts a `number` (pixel value), a string property
|
|
13
|
+
* name, or `(d) => value` accessor. Strings/functions use `ctx.xScale`.
|
|
14
|
+
*/
|
|
15
|
+
x1?: Accessor;
|
|
16
|
+
/**
|
|
17
|
+
* Source `y` coordinate. Accepts a `number`, property name, or
|
|
18
|
+
* `(d) => value` accessor. Strings/functions use `ctx.yScale`.
|
|
19
|
+
*/
|
|
20
|
+
y1?: Accessor;
|
|
21
|
+
/** Target `x` coordinate. See `x1`. */
|
|
22
|
+
x2?: Accessor;
|
|
23
|
+
/** Target `y` coordinate. See `y1`. */
|
|
24
|
+
y2?: Accessor;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Data for the link. In array mode (when `x1`/`y1`/`x2`/`y2` are strings or
|
|
28
|
+
* functions) this should be an array of rows; one path is rendered per row.
|
|
29
|
+
* In hierarchy/sankey mode, a single link object (`{source, target, ...}`).
|
|
30
|
+
* Defaults to `ctx.data` in array mode.
|
|
31
|
+
*/
|
|
9
32
|
data?: any;
|
|
10
33
|
|
|
11
34
|
/**
|
|
12
|
-
* Update source and target accessors to be compatible with d3-sankey.
|
|
35
|
+
* Update source and target accessors to be compatible with d3-sankey. see:
|
|
36
|
+
* https://github.com/d3/d3-sankey#sankeyLinkHorizontal
|
|
13
37
|
*
|
|
14
38
|
* @default false
|
|
15
39
|
*/
|
|
16
40
|
sankey?: boolean;
|
|
41
|
+
/** Accessor returning the source node from `data` (hierarchy/sankey mode). */
|
|
17
42
|
source?: (d: any) => any;
|
|
43
|
+
/** Accessor returning the target node from `data` (hierarchy/sankey mode). */
|
|
18
44
|
target?: (d: any) => any;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Convenient property to swap x/y accessor logic
|
|
22
|
-
*/
|
|
23
|
-
orientation?: 'vertical' | 'horizontal';
|
|
24
|
-
|
|
45
|
+
/** Accessor returning `x` for a node (hierarchy/sankey mode). */
|
|
25
46
|
x?: (d: any) => any;
|
|
47
|
+
/** Accessor returning `y` for a node (hierarchy/sankey mode). */
|
|
26
48
|
y?: (d: any) => any;
|
|
27
|
-
curve?: CurveFactory;
|
|
28
49
|
|
|
29
50
|
/**
|
|
30
|
-
*
|
|
51
|
+
* The link path type.
|
|
52
|
+
*
|
|
53
|
+
* Set to `'d3'` to use a D3 curve function via the `curve` prop.
|
|
54
|
+
*
|
|
55
|
+
* @default 'd3'
|
|
31
56
|
*/
|
|
32
|
-
|
|
57
|
+
type?: LinkType;
|
|
33
58
|
|
|
34
59
|
/**
|
|
35
|
-
*
|
|
60
|
+
* Corner radius (used by `'beveled'` and `'rounded'`).
|
|
61
|
+
*
|
|
62
|
+
* @default 20
|
|
36
63
|
*/
|
|
37
|
-
|
|
64
|
+
radius?: number;
|
|
38
65
|
|
|
39
66
|
/**
|
|
40
|
-
*
|
|
67
|
+
* Bend angle in degrees for the `'swoop'` link type.
|
|
68
|
+
*
|
|
69
|
+
* @default 22.5
|
|
41
70
|
*/
|
|
42
|
-
|
|
71
|
+
bend?: number;
|
|
72
|
+
|
|
73
|
+
/** D3 curve function (used when `type === 'd3'`). */
|
|
74
|
+
curve?: CurveFactory;
|
|
75
|
+
|
|
76
|
+
/** Sweep direction for preset types and d3 paths. */
|
|
77
|
+
sweep?: LinkSweep;
|
|
43
78
|
|
|
44
79
|
/**
|
|
45
|
-
*
|
|
80
|
+
* Natural flow direction (affects default curve and axis-dependent step
|
|
81
|
+
* curves). Also toggles x/y accessor logic in hierarchy mode.
|
|
46
82
|
*/
|
|
47
|
-
|
|
83
|
+
orientation?: 'vertical' | 'horizontal';
|
|
48
84
|
|
|
49
85
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
86
|
+
* Interpret coords as polar (`x` = angle, `y` = radius) and render in
|
|
87
|
+
* radial space. Defaults to `ctx.radial` when unset.
|
|
52
88
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
89
|
+
radial?: boolean;
|
|
90
|
+
|
|
91
|
+
/** Marker at both start and end points. */
|
|
92
|
+
marker?: MarkerOptions;
|
|
93
|
+
/** Marker at the middle point. */
|
|
94
|
+
markerMid?: MarkerOptions;
|
|
95
|
+
/** Marker at the start point. */
|
|
96
|
+
markerStart?: MarkerOptions;
|
|
97
|
+
/** Marker at the end point. */
|
|
98
|
+
markerEnd?: MarkerOptions;
|
|
59
99
|
|
|
60
100
|
motion?: MotionTweenOption | MotionNoneOption;
|
|
61
|
-
};
|
|
62
101
|
|
|
63
|
-
|
|
102
|
+
/**
|
|
103
|
+
* CSS class. In array mode, accepts a `(d) => string` function evaluated
|
|
104
|
+
* per datum.
|
|
105
|
+
*/
|
|
106
|
+
class?: string | ((d: any) => string);
|
|
107
|
+
} & Omit<PathPropsWithoutHTML, 'class'>;
|
|
108
|
+
|
|
109
|
+
export type LinkProps = LinkPropsWithoutHTML & Without<PathProps, LinkPropsWithoutHTML>;
|
|
64
110
|
|
|
65
111
|
const FALLBACK_COORDS = { x: 0, y: 0 };
|
|
112
|
+
|
|
113
|
+
function isAccessorAccessor(value: Accessor | undefined): boolean {
|
|
114
|
+
return typeof value === 'string' || typeof value === 'function';
|
|
115
|
+
}
|
|
66
116
|
</script>
|
|
67
117
|
|
|
68
118
|
<script lang="ts">
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
- [ ] https://observablehq.com/@nitaku/corner-connectors
|
|
76
|
-
- [ ] Straight
|
|
77
|
-
- [ ] Square
|
|
78
|
-
- [ ] Beveled
|
|
79
|
-
- [ ] Rounded
|
|
80
|
-
- [ ] Investigate: https://observablehq.com/@fil/sankey-link-paths
|
|
81
|
-
- [ ] Use for annotations - https://github.com/techniq/layerchart/issues/11
|
|
82
|
-
*/
|
|
83
|
-
import Connector, { type ConnectorProps } from './Connector.svelte';
|
|
84
|
-
import { extractLayerProps } from '../utils/attributes.js';
|
|
119
|
+
import {
|
|
120
|
+
getLinkD3Path,
|
|
121
|
+
getLinkPresetPath,
|
|
122
|
+
getLinkRadialD3Path,
|
|
123
|
+
getLinkRadialPresetPath,
|
|
124
|
+
} from '../utils/linkUtils.js';
|
|
85
125
|
import { getChartContext } from '../contexts/chart.js';
|
|
126
|
+
import Path from './Path.svelte';
|
|
127
|
+
import { extractLayerProps } from '../utils/attributes.js';
|
|
128
|
+
import { accessor } from '../utils/common.js';
|
|
129
|
+
import { cls } from '@layerstack/tailwind';
|
|
130
|
+
import {
|
|
131
|
+
createMotion,
|
|
132
|
+
extractTweenConfig,
|
|
133
|
+
type ResolvedMotion,
|
|
134
|
+
} from '../utils/motion.svelte.js';
|
|
135
|
+
import { interpolatePath } from 'd3-interpolate-path';
|
|
86
136
|
|
|
87
137
|
const ctx = getChartContext();
|
|
88
138
|
|
|
89
139
|
let {
|
|
140
|
+
x1,
|
|
141
|
+
y1,
|
|
142
|
+
x2,
|
|
143
|
+
y2,
|
|
90
144
|
data,
|
|
91
145
|
sankey = false,
|
|
92
146
|
source: sourceProp,
|
|
93
147
|
target: targetProp,
|
|
94
|
-
orientation: orientationProp,
|
|
95
148
|
x: xProp,
|
|
96
149
|
y: yProp,
|
|
150
|
+
orientation: orientationProp,
|
|
97
151
|
curve: curveProp,
|
|
98
|
-
explicitCoords,
|
|
99
152
|
type = 'd3',
|
|
100
|
-
sweep
|
|
153
|
+
sweep: sweepProp,
|
|
101
154
|
radius = 20,
|
|
155
|
+
bend = 22.5,
|
|
156
|
+
radial: radialProp,
|
|
157
|
+
marker,
|
|
158
|
+
markerStart,
|
|
159
|
+
markerMid,
|
|
160
|
+
markerEnd,
|
|
161
|
+
motion,
|
|
162
|
+
pathRef = $bindable(),
|
|
163
|
+
pathData: pathDataProp,
|
|
164
|
+
class: classProp,
|
|
102
165
|
...restProps
|
|
103
166
|
}: LinkProps = $props();
|
|
104
167
|
|
|
105
|
-
const
|
|
106
|
-
if (sourceProp) return sourceProp;
|
|
107
|
-
if (sankey) return (d: any) => ({ node: d.source, y: d.y0, isSource: true });
|
|
108
|
-
return (d: any) => d.source;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const targetAccessor = $derived.by(() => {
|
|
112
|
-
if (targetProp) return targetProp;
|
|
113
|
-
if (sankey) return (d: any) => ({ node: d.target, y: d.y1, isSource: false });
|
|
114
|
-
return (d: any) => d.target;
|
|
115
|
-
});
|
|
168
|
+
const radial = $derived(radialProp ?? ctx.radial ?? false);
|
|
116
169
|
|
|
117
170
|
const orientation = $derived.by(() => {
|
|
118
171
|
if (orientationProp) return orientationProp;
|
|
@@ -126,24 +179,82 @@ TODO:
|
|
|
126
179
|
return curveBumpY;
|
|
127
180
|
});
|
|
128
181
|
|
|
182
|
+
const sweep = $derived.by(() => {
|
|
183
|
+
if (type === 'd3') return sweepProp ?? 'none';
|
|
184
|
+
if (sweepProp && sweepProp !== 'none') return sweepProp;
|
|
185
|
+
return orientation === 'vertical' ? 'horizontal-vertical' : 'vertical-horizontal';
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Array/data mode: any of x1/y1/x2/y2 is a string or function
|
|
189
|
+
const isArrayMode = $derived(
|
|
190
|
+
isAccessorAccessor(x1) || isAccessorAccessor(y1) || isAccessorAccessor(x2) || isAccessorAccessor(y2)
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Pixel mode: any of x1/y1/x2/y2 is a number (and not array mode)
|
|
194
|
+
const isPixelMode = $derived(
|
|
195
|
+
!isArrayMode &&
|
|
196
|
+
(typeof x1 === 'number' ||
|
|
197
|
+
typeof y1 === 'number' ||
|
|
198
|
+
typeof x2 === 'number' ||
|
|
199
|
+
typeof y2 === 'number')
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// --- Hierarchy/sankey accessors (used only when !isArrayMode && !isPixelMode) ---
|
|
203
|
+
const sourceAccessor = $derived.by(() => {
|
|
204
|
+
if (sourceProp) return sourceProp;
|
|
205
|
+
if (sankey) return (d: any) => ({ node: d.source, y: d.y0, isSource: true });
|
|
206
|
+
return (d: any) => d.source;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const targetAccessor = $derived.by(() => {
|
|
210
|
+
if (targetProp) return targetProp;
|
|
211
|
+
if (sankey) return (d: any) => ({ node: d.target, y: d.y1, isSource: false });
|
|
212
|
+
return (d: any) => d.target;
|
|
213
|
+
});
|
|
214
|
+
|
|
129
215
|
const xAccessor = $derived.by(() => {
|
|
130
216
|
if (xProp) return xProp;
|
|
131
217
|
if (sankey) return (d: any) => (d.isSource ? d.node.x1 : d.node.x0);
|
|
132
|
-
if (
|
|
218
|
+
if (radial) return (d: any) => d.x;
|
|
133
219
|
return (d: any) => (orientation === 'horizontal' ? d.y : d.x);
|
|
134
220
|
});
|
|
135
221
|
|
|
136
222
|
const yAccessor = $derived.by(() => {
|
|
137
223
|
if (yProp) return yProp;
|
|
138
224
|
if (sankey) return (d: any) => d.y;
|
|
139
|
-
if (
|
|
225
|
+
if (radial) return (d: any) => d.y;
|
|
140
226
|
return (d: any) => (orientation === 'horizontal' ? d.x : d.y);
|
|
141
227
|
});
|
|
142
228
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
229
|
+
// --- Array mode: resolve endpoint coords for each row ---
|
|
230
|
+
const x1Accessor = $derived(accessor(x1 as Accessor));
|
|
231
|
+
const y1Accessor = $derived(accessor(y1 as Accessor));
|
|
232
|
+
const x2Accessor = $derived(accessor(x2 as Accessor));
|
|
233
|
+
const y2Accessor = $derived(accessor(y2 as Accessor));
|
|
146
234
|
|
|
235
|
+
const resolveArrayCoords = (d: any) => {
|
|
236
|
+
const sxRaw = x1Accessor(d);
|
|
237
|
+
const syRaw = y1Accessor(d);
|
|
238
|
+
const txRaw = x2Accessor(d);
|
|
239
|
+
const tyRaw = y2Accessor(d);
|
|
240
|
+
const scaleX = typeof x1 === 'string' || typeof x1 === 'function' ? ctx.xScale : null;
|
|
241
|
+
const scaleY = typeof y1 === 'string' || typeof y1 === 'function' ? ctx.yScale : null;
|
|
242
|
+
const sx = scaleX && sxRaw != null ? scaleX(sxRaw) : typeof sxRaw === 'number' ? sxRaw : 0;
|
|
243
|
+
const sy = scaleY && syRaw != null ? scaleY(syRaw) : typeof syRaw === 'number' ? syRaw : 0;
|
|
244
|
+
const tx = scaleX && txRaw != null ? scaleX(txRaw) : typeof txRaw === 'number' ? txRaw : 0;
|
|
245
|
+
const ty = scaleY && tyRaw != null ? scaleY(tyRaw) : typeof tyRaw === 'number' ? tyRaw : 0;
|
|
246
|
+
return {
|
|
247
|
+
source: { x: Number.isFinite(sx) ? sx : 0, y: Number.isFinite(sy) ? sy : 0 },
|
|
248
|
+
target: { x: Number.isFinite(tx) ? tx : 0, y: Number.isFinite(ty) ? ty : 0 },
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// --- Single-path coords (pixel or hierarchy/sankey mode) ---
|
|
253
|
+
const singleSourceCoords = $derived.by(() => {
|
|
254
|
+
if (isPixelMode) {
|
|
255
|
+
return { x: typeof x1 === 'number' ? x1 : 0, y: typeof y1 === 'number' ? y1 : 0 };
|
|
256
|
+
}
|
|
257
|
+
if (!data) return FALLBACK_COORDS;
|
|
147
258
|
try {
|
|
148
259
|
const sourceData = sourceAccessor(data);
|
|
149
260
|
if (sourceData == null) return FALLBACK_COORDS;
|
|
@@ -156,10 +267,11 @@ TODO:
|
|
|
156
267
|
}
|
|
157
268
|
});
|
|
158
269
|
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
270
|
+
const singleTargetCoords = $derived.by(() => {
|
|
271
|
+
if (isPixelMode) {
|
|
272
|
+
return { x: typeof x2 === 'number' ? x2 : 100, y: typeof y2 === 'number' ? y2 : 100 };
|
|
273
|
+
}
|
|
161
274
|
if (!data) return FALLBACK_COORDS;
|
|
162
|
-
|
|
163
275
|
try {
|
|
164
276
|
const targetData = targetAccessor(data);
|
|
165
277
|
if (targetData == null) return FALLBACK_COORDS;
|
|
@@ -171,15 +283,89 @@ TODO:
|
|
|
171
283
|
return FALLBACK_COORDS;
|
|
172
284
|
}
|
|
173
285
|
});
|
|
286
|
+
|
|
287
|
+
function buildPath(source: { x: number; y: number }, target: { x: number; y: number }) {
|
|
288
|
+
if (pathDataProp) return pathDataProp;
|
|
289
|
+
if (radial) {
|
|
290
|
+
return type === 'd3'
|
|
291
|
+
? getLinkRadialD3Path({ source, target, curve })
|
|
292
|
+
: getLinkRadialPresetPath({ source, target, type, radius, bend });
|
|
293
|
+
}
|
|
294
|
+
if (type === 'd3') {
|
|
295
|
+
return getLinkD3Path({ source, target, sweep, curve, orientation });
|
|
296
|
+
}
|
|
297
|
+
return getLinkPresetPath({ source, target, sweep, type, radius, bend });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// --- Single-path case ---
|
|
301
|
+
const singlePathData = $derived(
|
|
302
|
+
isArrayMode ? '' : buildPath(singleSourceCoords, singleTargetCoords)
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const extractedTween = extractTweenConfig(motion);
|
|
306
|
+
const tweenOptions: ResolvedMotion | undefined = extractedTween
|
|
307
|
+
? {
|
|
308
|
+
type: extractedTween.type,
|
|
309
|
+
options: {
|
|
310
|
+
interpolate: interpolatePath,
|
|
311
|
+
...extractedTween.options,
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
: undefined;
|
|
315
|
+
|
|
316
|
+
const motionPath = createMotion(
|
|
317
|
+
'',
|
|
318
|
+
() => singlePathData,
|
|
319
|
+
tweenOptions ? tweenOptions : { type: 'none' }
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// --- Array mode paths ---
|
|
323
|
+
const arrayRows = $derived(isArrayMode ? ((data ?? ctx.data) ?? []) : []);
|
|
324
|
+
|
|
325
|
+
function resolvePerDatum<T>(value: T | ((d: any) => T) | undefined, d: any): T | undefined {
|
|
326
|
+
return typeof value === 'function' ? (value as (d: any) => T)(d) : (value as T | undefined);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function resolveClass(d: any): string | undefined {
|
|
330
|
+
return resolvePerDatum<string>(classProp as any, d);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Pull potentially-per-datum style props out of restProps so we can resolve
|
|
334
|
+
// each per row in array mode (e.g. stroke={(d) => colorScale(...)})
|
|
335
|
+
const strokeProp = $derived((restProps as any).stroke);
|
|
336
|
+
const fillProp = $derived((restProps as any).fill);
|
|
337
|
+
const strokeWidthProp = $derived((restProps as any)['stroke-width'] ?? (restProps as any).strokeWidth);
|
|
174
338
|
</script>
|
|
175
339
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
340
|
+
{#if isArrayMode}
|
|
341
|
+
{#each arrayRows as d, i (i)}
|
|
342
|
+
{@const { source, target } = resolveArrayCoords(d)}
|
|
343
|
+
{@const resolvedStroke =
|
|
344
|
+
resolvePerDatum(strokeProp, d) ?? (ctx.config.c ? ctx.cGet(d) : undefined)}
|
|
345
|
+
<Path
|
|
346
|
+
pathData={buildPath(source, target)}
|
|
347
|
+
{marker}
|
|
348
|
+
{markerStart}
|
|
349
|
+
{markerMid}
|
|
350
|
+
{markerEnd}
|
|
351
|
+
{...extractLayerProps(restProps, 'lc-link')}
|
|
352
|
+
{...restProps}
|
|
353
|
+
stroke={resolvedStroke}
|
|
354
|
+
fill={resolvePerDatum(fillProp, d)}
|
|
355
|
+
stroke-width={resolvePerDatum(strokeWidthProp, d)}
|
|
356
|
+
class={cls('lc-link', resolveClass(d))}
|
|
357
|
+
/>
|
|
358
|
+
{/each}
|
|
359
|
+
{:else}
|
|
360
|
+
<Path
|
|
361
|
+
pathData={motionPath.current}
|
|
362
|
+
bind:pathRef
|
|
363
|
+
{marker}
|
|
364
|
+
{markerStart}
|
|
365
|
+
{markerMid}
|
|
366
|
+
{markerEnd}
|
|
367
|
+
{...extractLayerProps(restProps, 'lc-link')}
|
|
368
|
+
{...restProps}
|
|
369
|
+
class={cls('lc-link', typeof classProp === 'string' ? classProp : undefined)}
|
|
370
|
+
/>
|
|
371
|
+
{/if}
|
|
@@ -2,53 +2,96 @@ import type { MarkerOptions } from './MarkerWrapper.svelte';
|
|
|
2
2
|
import type { Without } from '../utils/types.js';
|
|
3
3
|
import type { MotionNoneOption, MotionTweenOption } from '../utils/motion.svelte.js';
|
|
4
4
|
import { type CurveFactory } from 'd3-shape';
|
|
5
|
+
import type { LinkSweep, LinkType } from '../utils/linkUtils.js';
|
|
6
|
+
import type { PathProps, PathPropsWithoutHTML } from './Path.svelte';
|
|
7
|
+
import type { Accessor } from '../utils/common.js';
|
|
5
8
|
export type LinkPropsWithoutHTML = {
|
|
9
|
+
/**
|
|
10
|
+
* Source `x` coordinate. Accepts a `number` (pixel value), a string property
|
|
11
|
+
* name, or `(d) => value` accessor. Strings/functions use `ctx.xScale`.
|
|
12
|
+
*/
|
|
13
|
+
x1?: Accessor;
|
|
14
|
+
/**
|
|
15
|
+
* Source `y` coordinate. Accepts a `number`, property name, or
|
|
16
|
+
* `(d) => value` accessor. Strings/functions use `ctx.yScale`.
|
|
17
|
+
*/
|
|
18
|
+
y1?: Accessor;
|
|
19
|
+
/** Target `x` coordinate. See `x1`. */
|
|
20
|
+
x2?: Accessor;
|
|
21
|
+
/** Target `y` coordinate. See `y1`. */
|
|
22
|
+
y2?: Accessor;
|
|
23
|
+
/**
|
|
24
|
+
* Data for the link. In array mode (when `x1`/`y1`/`x2`/`y2` are strings or
|
|
25
|
+
* functions) this should be an array of rows; one path is rendered per row.
|
|
26
|
+
* In hierarchy/sankey mode, a single link object (`{source, target, ...}`).
|
|
27
|
+
* Defaults to `ctx.data` in array mode.
|
|
28
|
+
*/
|
|
6
29
|
data?: any;
|
|
7
30
|
/**
|
|
8
|
-
* Update source and target accessors to be compatible with d3-sankey.
|
|
31
|
+
* Update source and target accessors to be compatible with d3-sankey. see:
|
|
32
|
+
* https://github.com/d3/d3-sankey#sankeyLinkHorizontal
|
|
9
33
|
*
|
|
10
34
|
* @default false
|
|
11
35
|
*/
|
|
12
36
|
sankey?: boolean;
|
|
37
|
+
/** Accessor returning the source node from `data` (hierarchy/sankey mode). */
|
|
13
38
|
source?: (d: any) => any;
|
|
39
|
+
/** Accessor returning the target node from `data` (hierarchy/sankey mode). */
|
|
14
40
|
target?: (d: any) => any;
|
|
15
|
-
/**
|
|
16
|
-
* Convenient property to swap x/y accessor logic
|
|
17
|
-
*/
|
|
18
|
-
orientation?: 'vertical' | 'horizontal';
|
|
41
|
+
/** Accessor returning `x` for a node (hierarchy/sankey mode). */
|
|
19
42
|
x?: (d: any) => any;
|
|
43
|
+
/** Accessor returning `y` for a node (hierarchy/sankey mode). */
|
|
20
44
|
y?: (d: any) => any;
|
|
21
|
-
curve?: CurveFactory;
|
|
22
45
|
/**
|
|
23
|
-
*
|
|
46
|
+
* The link path type.
|
|
47
|
+
*
|
|
48
|
+
* Set to `'d3'` to use a D3 curve function via the `curve` prop.
|
|
49
|
+
*
|
|
50
|
+
* @default 'd3'
|
|
24
51
|
*/
|
|
25
|
-
|
|
52
|
+
type?: LinkType;
|
|
26
53
|
/**
|
|
27
|
-
*
|
|
54
|
+
* Corner radius (used by `'beveled'` and `'rounded'`).
|
|
55
|
+
*
|
|
56
|
+
* @default 20
|
|
28
57
|
*/
|
|
29
|
-
|
|
58
|
+
radius?: number;
|
|
30
59
|
/**
|
|
31
|
-
*
|
|
60
|
+
* Bend angle in degrees for the `'swoop'` link type.
|
|
61
|
+
*
|
|
62
|
+
* @default 22.5
|
|
32
63
|
*/
|
|
33
|
-
|
|
64
|
+
bend?: number;
|
|
65
|
+
/** D3 curve function (used when `type === 'd3'`). */
|
|
66
|
+
curve?: CurveFactory;
|
|
67
|
+
/** Sweep direction for preset types and d3 paths. */
|
|
68
|
+
sweep?: LinkSweep;
|
|
34
69
|
/**
|
|
35
|
-
*
|
|
70
|
+
* Natural flow direction (affects default curve and axis-dependent step
|
|
71
|
+
* curves). Also toggles x/y accessor logic in hierarchy mode.
|
|
36
72
|
*/
|
|
37
|
-
|
|
73
|
+
orientation?: 'vertical' | 'horizontal';
|
|
38
74
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
75
|
+
* Interpret coords as polar (`x` = angle, `y` = radius) and render in
|
|
76
|
+
* radial space. Defaults to `ctx.radial` when unset.
|
|
41
77
|
*/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
78
|
+
radial?: boolean;
|
|
79
|
+
/** Marker at both start and end points. */
|
|
80
|
+
marker?: MarkerOptions;
|
|
81
|
+
/** Marker at the middle point. */
|
|
82
|
+
markerMid?: MarkerOptions;
|
|
83
|
+
/** Marker at the start point. */
|
|
84
|
+
markerStart?: MarkerOptions;
|
|
85
|
+
/** Marker at the end point. */
|
|
86
|
+
markerEnd?: MarkerOptions;
|
|
48
87
|
motion?: MotionTweenOption | MotionNoneOption;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
88
|
+
/**
|
|
89
|
+
* CSS class. In array mode, accepts a `(d) => string` function evaluated
|
|
90
|
+
* per datum.
|
|
91
|
+
*/
|
|
92
|
+
class?: string | ((d: any) => string);
|
|
93
|
+
} & Omit<PathPropsWithoutHTML, 'class'>;
|
|
94
|
+
export type LinkProps = LinkPropsWithoutHTML & Without<PathProps, LinkPropsWithoutHTML>;
|
|
95
|
+
declare const Link: import("svelte").Component<LinkProps, {}, "pathRef">;
|
|
53
96
|
type Link = ReturnType<typeof Link>;
|
|
54
97
|
export default Link;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import type { Without } from '../utils/types.js';
|
|
3
|
+
import type { Accessor } from '../utils/common.js';
|
|
3
4
|
|
|
4
5
|
export type VoronoiPropsWithoutHTML = {
|
|
5
6
|
/**
|
|
@@ -7,6 +8,16 @@
|
|
|
7
8
|
*/
|
|
8
9
|
data?: any;
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Override the `x` accessor used to place each point. Useful when the
|
|
13
|
+
* chart's `x` accessor returns an array of values (e.g. `['start', 'end']`)
|
|
14
|
+
* and you want to use a specific one.
|
|
15
|
+
*/
|
|
16
|
+
x?: Accessor;
|
|
17
|
+
|
|
18
|
+
/** Override the `y` accessor used to place each point. See `x` above. */
|
|
19
|
+
y?: Accessor;
|
|
20
|
+
|
|
10
21
|
/** Radius to clip voronoi cells. `0` or `undefined` to disables clipping */
|
|
11
22
|
r?: number;
|
|
12
23
|
|
|
@@ -56,7 +67,7 @@
|
|
|
56
67
|
</script>
|
|
57
68
|
|
|
58
69
|
<script lang="ts">
|
|
59
|
-
import {
|
|
70
|
+
import { max } from 'd3-array';
|
|
60
71
|
import { Delaunay } from 'd3-delaunay';
|
|
61
72
|
import { type GeoPermissibleObjects } from 'd3-geo';
|
|
62
73
|
// @ts-expect-error
|
|
@@ -69,10 +80,13 @@
|
|
|
69
80
|
import Path from './Path.svelte';
|
|
70
81
|
import { getChartContext } from '../contexts/chart.js';
|
|
71
82
|
import { getGeoContext } from '../contexts/geo.js';
|
|
83
|
+
import { accessor } from '../utils/common.js';
|
|
72
84
|
import CircleClipPath from './CircleClipPath.svelte';
|
|
73
85
|
|
|
74
86
|
let {
|
|
75
87
|
data,
|
|
88
|
+
x: xProp,
|
|
89
|
+
y: yProp,
|
|
76
90
|
r,
|
|
77
91
|
classes = {},
|
|
78
92
|
onclick,
|
|
@@ -86,14 +100,29 @@
|
|
|
86
100
|
const ctx = getChartContext();
|
|
87
101
|
const geo = getGeoContext();
|
|
88
102
|
|
|
103
|
+
const xAccessorOverride = $derived(xProp != null ? accessor(xProp) : undefined);
|
|
104
|
+
const yAccessorOverride = $derived(yProp != null ? accessor(yProp) : undefined);
|
|
105
|
+
|
|
89
106
|
const points = $derived(
|
|
90
107
|
(data ?? ctx.flatData).map((d: any) => {
|
|
91
108
|
// geo voronoi needs raw latitude/longitude, not mapped to range (chart dimensions)
|
|
92
|
-
const xValue =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
const xValue = xAccessorOverride
|
|
110
|
+
? geo.projection
|
|
111
|
+
? xAccessorOverride(d)
|
|
112
|
+
: ctx.xScale(xAccessorOverride(d))
|
|
113
|
+
: geo.projection
|
|
114
|
+
? ctx.x(d)
|
|
115
|
+
: ctx.xGet(d);
|
|
116
|
+
const yValue = yAccessorOverride
|
|
117
|
+
? geo.projection
|
|
118
|
+
? yAccessorOverride(d)
|
|
119
|
+
: ctx.yScale(yAccessorOverride(d))
|
|
120
|
+
: geo.projection
|
|
121
|
+
? ctx.y(d)
|
|
122
|
+
: ctx.yGet(d);
|
|
123
|
+
|
|
124
|
+
const x = Array.isArray(xValue) ? max(xValue) : xValue;
|
|
125
|
+
const y = Array.isArray(yValue) ? max(yValue) : yValue;
|
|
97
126
|
|
|
98
127
|
let point: [number, number];
|
|
99
128
|
if (ctx.radial) {
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { Without } from '../utils/types.js';
|
|
2
|
+
import type { Accessor } from '../utils/common.js';
|
|
2
3
|
export type VoronoiPropsWithoutHTML = {
|
|
3
4
|
/**
|
|
4
5
|
* Override data instead of using context
|
|
5
6
|
*/
|
|
6
7
|
data?: any;
|
|
8
|
+
/**
|
|
9
|
+
* Override the `x` accessor used to place each point. Useful when the
|
|
10
|
+
* chart's `x` accessor returns an array of values (e.g. `['start', 'end']`)
|
|
11
|
+
* and you want to use a specific one.
|
|
12
|
+
*/
|
|
13
|
+
x?: Accessor;
|
|
14
|
+
/** Override the `y` accessor used to place each point. See `x` above. */
|
|
15
|
+
y?: Accessor;
|
|
7
16
|
/** Radius to clip voronoi cells. `0` or `undefined` to disables clipping */
|
|
8
17
|
r?: number;
|
|
9
18
|
/**
|
|
Binary file
|
|
Binary file
|