layerchart 2.0.0-next.55 → 2.0.0-next.56
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/bench/ComposableLineChart.svelte +1 -1
- package/dist/bench/GeoBench.svelte +1 -8
- package/dist/components/AnnotationRange.svelte +3 -1
- package/dist/components/Arc.svelte +1 -3
- package/dist/components/ArcLabel.svelte.test.js +7 -7
- package/dist/components/Bar.svelte +4 -2
- package/dist/components/BoxPlot.svelte +4 -12
- package/dist/components/Cell.svelte +13 -8
- package/dist/components/Chart.svelte +69 -26
- package/dist/components/ChartChildren.svelte +22 -4
- package/dist/components/Circle.svelte +34 -12
- package/dist/components/ClipPath.svelte +3 -9
- package/dist/components/Ellipse.svelte +27 -6
- package/dist/components/GeoLegend.svelte +1 -3
- package/dist/components/GeoPoint.svelte +25 -3
- package/dist/components/GeoSpline.svelte +1 -4
- package/dist/components/GeoTile.svelte +8 -4
- package/dist/components/Group.svelte +11 -5
- package/dist/components/Highlight.svelte +3 -3
- package/dist/components/Image.svelte +42 -30
- package/dist/components/Labels.svelte +2 -4
- package/dist/components/Line.svelte +7 -6
- package/dist/components/LinearGradient.svelte +8 -4
- package/dist/components/Link.svelte +0 -1
- package/dist/components/Marker.svelte +9 -1
- package/dist/components/Path.svelte +43 -23
- package/dist/components/Pattern.svelte +101 -5
- package/dist/components/Pattern.svelte.d.ts +3 -1
- package/dist/components/Pie.svelte +2 -6
- package/dist/components/RadialGradient.svelte +8 -4
- package/dist/components/Rect.svelte +29 -12
- package/dist/components/Spline.svelte +22 -4
- package/dist/components/Text.svelte +9 -5
- package/dist/components/Trail.svelte +19 -7
- package/dist/components/Vector.svelte +37 -14
- package/dist/components/Violin.svelte +1 -2
- package/dist/components/charts/ArcChart.svelte +8 -5
- package/dist/components/charts/AreaChart.svelte +6 -1
- package/dist/components/charts/BarChart.svelte +3 -1
- package/dist/components/charts/LineChart.svelte +6 -1
- package/dist/components/charts/PieChart.svelte +10 -3
- package/dist/components/tooltip/Tooltip.svelte +2 -8
- package/dist/contexts/chart.d.ts +1 -1
- package/dist/contexts/chart.js +3 -1
- package/dist/server/TestBarChart.svelte +28 -28
- package/dist/server/TestLineChart.svelte +28 -28
- package/dist/server/index.js +1 -1
- package/dist/states/brush.svelte.js +16 -13
- package/dist/states/chart.svelte.test.js +24 -19
- package/dist/states/geo.svelte.js +1 -4
- package/dist/states/series.svelte.js +1 -1
- package/dist/utils/canvas.js +7 -4
- package/dist/utils/trail.js +3 -4
- package/package.json +1 -1
|
@@ -95,12 +95,30 @@
|
|
|
95
95
|
const ctx = getChartContext();
|
|
96
96
|
const geo = getGeoContext();
|
|
97
97
|
|
|
98
|
-
let {
|
|
98
|
+
let {
|
|
99
|
+
data,
|
|
100
|
+
x,
|
|
101
|
+
y,
|
|
102
|
+
seriesKey,
|
|
103
|
+
defined,
|
|
104
|
+
curve,
|
|
105
|
+
stroke,
|
|
106
|
+
fill,
|
|
107
|
+
opacity,
|
|
108
|
+
motion,
|
|
109
|
+
...restProps
|
|
110
|
+
}: SplineProps = $props();
|
|
99
111
|
|
|
100
112
|
ctx.registerComponent({
|
|
101
113
|
name: 'Spline',
|
|
102
114
|
kind: 'mark',
|
|
103
|
-
markInfo: () => ({
|
|
115
|
+
markInfo: () => ({
|
|
116
|
+
data,
|
|
117
|
+
x,
|
|
118
|
+
y,
|
|
119
|
+
seriesKey,
|
|
120
|
+
color: typeof stroke === 'string' ? stroke : undefined,
|
|
121
|
+
}),
|
|
104
122
|
});
|
|
105
123
|
|
|
106
124
|
function getScaleValue(
|
|
@@ -267,8 +285,8 @@
|
|
|
267
285
|
|
|
268
286
|
const seriesOpacity = $derived(
|
|
269
287
|
series?.key == null ||
|
|
270
|
-
|
|
271
|
-
|
|
288
|
+
ctx.series.visibleSeries.length <= 1 ||
|
|
289
|
+
ctx.series.isHighlighted(series.key, true)
|
|
272
290
|
? 1
|
|
273
291
|
: 0.1
|
|
274
292
|
);
|
|
@@ -686,7 +686,14 @@
|
|
|
686
686
|
if (segments) {
|
|
687
687
|
let xOffset = baseX;
|
|
688
688
|
for (const segment of segments) {
|
|
689
|
-
const segStyles = getTextStyles(
|
|
689
|
+
const segStyles = getTextStyles(
|
|
690
|
+
undefined,
|
|
691
|
+
undefined,
|
|
692
|
+
undefined,
|
|
693
|
+
undefined,
|
|
694
|
+
undefined,
|
|
695
|
+
segment.class
|
|
696
|
+
);
|
|
690
697
|
const text = String(segment.value);
|
|
691
698
|
// Set font before rendering and measuring so width is accurate
|
|
692
699
|
const segComputedStyles = getComputedStyles(ctx.canvas, segStyles);
|
|
@@ -838,10 +845,7 @@
|
|
|
838
845
|
>
|
|
839
846
|
{#if segments}
|
|
840
847
|
{#each segments as segment, index (index)}
|
|
841
|
-
<tspan
|
|
842
|
-
dy={index === 0 ? startDy : 0}
|
|
843
|
-
class={['lc-text-tspan', segment.class]}
|
|
844
|
-
>
|
|
848
|
+
<tspan dy={index === 0 ? startDy : 0} class={['lc-text-tspan', segment.class]}>
|
|
845
849
|
{segment.value}
|
|
846
850
|
</tspan>
|
|
847
851
|
{/each}
|
|
@@ -180,9 +180,15 @@
|
|
|
180
180
|
.map((d: any) => ({
|
|
181
181
|
x: getScaleValue(d, ctx.xScale, xAccessor) + xOffset,
|
|
182
182
|
y: getScaleValue(d, ctx.yScale, yAccessor) + yOffset,
|
|
183
|
-
r:
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
r:
|
|
184
|
+
resolvedR != null
|
|
185
|
+
? resolveDataProp(
|
|
186
|
+
resolvedR,
|
|
187
|
+
d,
|
|
188
|
+
ctx.rScale,
|
|
189
|
+
typeof resolvedR === 'number' ? resolvedR : 4
|
|
190
|
+
)
|
|
191
|
+
: 4,
|
|
186
192
|
}));
|
|
187
193
|
|
|
188
194
|
return computeTrailPath(points, { curve, cap, tension, resolution });
|
|
@@ -206,9 +212,15 @@
|
|
|
206
212
|
.map((d: any) => ({
|
|
207
213
|
x: getScaleValue(d, ctx.xScale, xAccessor) + xOffset,
|
|
208
214
|
y: baseline,
|
|
209
|
-
r:
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
r:
|
|
216
|
+
resolvedR != null
|
|
217
|
+
? resolveDataProp(
|
|
218
|
+
resolvedR,
|
|
219
|
+
d,
|
|
220
|
+
ctx.rScale,
|
|
221
|
+
typeof resolvedR === 'number' ? resolvedR : 4
|
|
222
|
+
)
|
|
223
|
+
: 4,
|
|
212
224
|
}));
|
|
213
225
|
|
|
214
226
|
return computeTrailPath(points, { curve, cap, tension, resolution });
|
|
@@ -236,7 +248,7 @@
|
|
|
236
248
|
<Path
|
|
237
249
|
pathData={isTweened ? tweenState.current : trailPath}
|
|
238
250
|
{fill}
|
|
239
|
-
|
|
251
|
+
{fillOpacity}
|
|
240
252
|
{opacity}
|
|
241
253
|
stroke="none"
|
|
242
254
|
class={cls('lc-trail', className)}
|
|
@@ -127,10 +127,22 @@
|
|
|
127
127
|
import { getGeoContext } from '../contexts/geo.js';
|
|
128
128
|
import { getLayerContext } from '../contexts/layer.js';
|
|
129
129
|
import { createMotion, createDataMotionMap, type MotionProp } from '../utils/motion.svelte.js';
|
|
130
|
-
import {
|
|
130
|
+
import {
|
|
131
|
+
hasAnyDataProp,
|
|
132
|
+
resolveDataProp,
|
|
133
|
+
extractRawDataValue,
|
|
134
|
+
resolveGeoDataPair,
|
|
135
|
+
resolveStyleProp,
|
|
136
|
+
resolveColorProp,
|
|
137
|
+
} from '../utils/dataProp.js';
|
|
131
138
|
import { chartDataArray } from '../utils/common.js';
|
|
132
139
|
import { cls } from '@layerstack/tailwind';
|
|
133
|
-
import {
|
|
140
|
+
import {
|
|
141
|
+
vectorArrowPath,
|
|
142
|
+
vectorArrowFilledPath,
|
|
143
|
+
vectorSpikePath,
|
|
144
|
+
transformVectorPath,
|
|
145
|
+
} from '../utils/path.js';
|
|
134
146
|
import Path from './Path.svelte';
|
|
135
147
|
|
|
136
148
|
let {
|
|
@@ -169,11 +181,11 @@
|
|
|
169
181
|
// Per-item style mode: when any style prop is a function, we must render individual paths
|
|
170
182
|
const hasPerItemStyles = $derived(
|
|
171
183
|
typeof fill === 'function' ||
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
184
|
+
typeof stroke === 'function' ||
|
|
185
|
+
typeof fillOpacity === 'function' ||
|
|
186
|
+
typeof strokeWidth === 'function' ||
|
|
187
|
+
typeof opacity === 'function' ||
|
|
188
|
+
typeof className === 'function'
|
|
177
189
|
);
|
|
178
190
|
|
|
179
191
|
// Contexts
|
|
@@ -182,9 +194,7 @@
|
|
|
182
194
|
const layerCtx = getLayerContext();
|
|
183
195
|
|
|
184
196
|
// Data to iterate over in data mode
|
|
185
|
-
const resolvedData: any[] = $derived(
|
|
186
|
-
dataMode ? (dataProp ?? chartDataArray(chartCtx.data)) : []
|
|
187
|
-
);
|
|
197
|
+
const resolvedData: any[] = $derived(dataMode ? (dataProp ?? chartDataArray(chartCtx.data)) : []);
|
|
188
198
|
|
|
189
199
|
// Resolve a single data item to pixel coordinates and values
|
|
190
200
|
function resolveVector(d: any) {
|
|
@@ -200,8 +210,14 @@
|
|
|
200
210
|
return {
|
|
201
211
|
x: resolvedX,
|
|
202
212
|
y: resolvedY,
|
|
203
|
-
length: resolveDataProp(
|
|
204
|
-
|
|
213
|
+
length: resolveDataProp(
|
|
214
|
+
lengthProp,
|
|
215
|
+
d,
|
|
216
|
+
chartCtx.rScale,
|
|
217
|
+
typeof lengthProp === 'number' ? lengthProp : 12
|
|
218
|
+
),
|
|
219
|
+
rotate:
|
|
220
|
+
typeof rotateProp === 'number' ? rotateProp : (extractRawDataValue(rotateProp, d) ?? 0),
|
|
205
221
|
};
|
|
206
222
|
}
|
|
207
223
|
|
|
@@ -289,7 +305,11 @@
|
|
|
289
305
|
|
|
290
306
|
const motionX = createMotion(initialX, () => (typeof x === 'number' ? x : 0), motion);
|
|
291
307
|
const motionY = createMotion(initialY, () => (typeof y === 'number' ? y : 0), motion);
|
|
292
|
-
const motionLength = createMotion(
|
|
308
|
+
const motionLength = createMotion(
|
|
309
|
+
initialLength,
|
|
310
|
+
() => (typeof lengthProp === 'number' ? lengthProp : 12),
|
|
311
|
+
motion
|
|
312
|
+
);
|
|
293
313
|
|
|
294
314
|
const pixelRotate = $derived(typeof rotateProp === 'number' ? rotateProp : 0);
|
|
295
315
|
|
|
@@ -346,7 +366,10 @@
|
|
|
346
366
|
stroke={stroke as string}
|
|
347
367
|
strokeWidth={strokeWidth as number}
|
|
348
368
|
opacity={opacity as number}
|
|
349
|
-
class="lc-vector {isFilled ? 'lc-vector-filled' : 'lc-vector-stroked'} {typeof className ===
|
|
369
|
+
class="lc-vector {isFilled ? 'lc-vector-filled' : 'lc-vector-stroked'} {typeof className ===
|
|
370
|
+
'string'
|
|
371
|
+
? className
|
|
372
|
+
: ''}"
|
|
350
373
|
/>
|
|
351
374
|
{/if}
|
|
352
375
|
|
|
@@ -148,8 +148,7 @@
|
|
|
148
148
|
const ext: [number, number] = [sorted[0], sorted[sorted.length - 1]];
|
|
149
149
|
|
|
150
150
|
// Silverman's rule of thumb for bandwidth
|
|
151
|
-
const bw =
|
|
152
|
-
bandwidthProp ?? 1.06 * (deviation(sorted) ?? 1) * Math.pow(sorted.length, -1 / 5);
|
|
151
|
+
const bw = bandwidthProp ?? 1.06 * (deviation(sorted) ?? 1) * Math.pow(sorted.length, -1 / 5);
|
|
153
152
|
|
|
154
153
|
const kernelFn = epanechnikov(bw);
|
|
155
154
|
|
|
@@ -240,9 +240,7 @@
|
|
|
240
240
|
);
|
|
241
241
|
|
|
242
242
|
// Compute series colors locally to avoid derived_references_self cycle through context.series.allSeriesColors
|
|
243
|
-
const allSeriesColors = $derived(
|
|
244
|
-
series.map((s) => s.color).filter((c) => c != null) as string[]
|
|
245
|
-
);
|
|
243
|
+
const allSeriesColors = $derived(series.map((s) => s.color).filter((c) => c != null) as string[]);
|
|
246
244
|
|
|
247
245
|
// Custom tickFormat for ArcChart legends - uses data labels instead of series labels
|
|
248
246
|
const legendTickFormat = (tick: any) => {
|
|
@@ -416,8 +414,13 @@
|
|
|
416
414
|
value={valueAccessor(data)}
|
|
417
415
|
color={snippetProps.context.cScale?.(snippetProps.context.c(data))}
|
|
418
416
|
{format}
|
|
419
|
-
onpointerenter={() => {
|
|
420
|
-
|
|
417
|
+
onpointerenter={() => {
|
|
418
|
+
if (snippetProps.context)
|
|
419
|
+
snippetProps.context.series.highlightKey = keyAccessor(data);
|
|
420
|
+
}}
|
|
421
|
+
onpointerleave={() => {
|
|
422
|
+
if (snippetProps.context) snippetProps.context.series.highlightKey = null;
|
|
423
|
+
}}
|
|
421
424
|
{...props.tooltip?.item}
|
|
422
425
|
/>
|
|
423
426
|
</Tooltip.List>
|
|
@@ -108,7 +108,12 @@
|
|
|
108
108
|
...(typeof tooltipContext === 'object' ? tooltipContext : null),
|
|
109
109
|
}}
|
|
110
110
|
brush={brush
|
|
111
|
-
? {
|
|
111
|
+
? {
|
|
112
|
+
axis: 'x',
|
|
113
|
+
zoomOnBrush: true,
|
|
114
|
+
...(typeof brush === 'object' ? brush : null),
|
|
115
|
+
...props.brush,
|
|
116
|
+
}
|
|
112
117
|
: false}
|
|
113
118
|
{series}
|
|
114
119
|
{seriesLayout}
|
|
@@ -184,7 +184,9 @@
|
|
|
184
184
|
x1={valueAxis === 'y' && isGroupSeries ? (d) => s.value ?? s.key : undefined}
|
|
185
185
|
y1={valueAxis === 'x' && isGroupSeries ? (d) => s.value ?? s.key : undefined}
|
|
186
186
|
rounded={context.series.divergingEdgeKeys
|
|
187
|
-
? context.series.divergingEdgeKeys.has(s.key)
|
|
187
|
+
? context.series.divergingEdgeKeys.has(s.key)
|
|
188
|
+
? 'edge'
|
|
189
|
+
: 'none'
|
|
188
190
|
: context.series.isStacked && i !== context.series.visibleSeries.length - 1
|
|
189
191
|
? 'none'
|
|
190
192
|
: Array.isArray(xProp) || Array.isArray(yProp)
|
|
@@ -133,7 +133,12 @@
|
|
|
133
133
|
...(typeof tooltipContext === 'object' ? tooltipContext : null),
|
|
134
134
|
}}
|
|
135
135
|
brush={brush
|
|
136
|
-
? {
|
|
136
|
+
? {
|
|
137
|
+
axis: 'x',
|
|
138
|
+
zoomOnBrush: true,
|
|
139
|
+
...(typeof brush === 'object' ? brush : null),
|
|
140
|
+
...props.brush,
|
|
141
|
+
}
|
|
137
142
|
: false}
|
|
138
143
|
{series}
|
|
139
144
|
highlight={highlightWithPointClick as any}
|
|
@@ -308,7 +308,9 @@
|
|
|
308
308
|
startAngle: arc.startAngle,
|
|
309
309
|
endAngle: arc.endAngle,
|
|
310
310
|
outerRadius:
|
|
311
|
-
(context?.series.visibleSeries.length ?? 0) > 1
|
|
311
|
+
(context?.series.visibleSeries.length ?? 0) > 1
|
|
312
|
+
? seriesIndex * (outerRadius ?? 0)
|
|
313
|
+
: outerRadius,
|
|
312
314
|
innerRadius,
|
|
313
315
|
cornerRadius,
|
|
314
316
|
padAngle,
|
|
@@ -464,8 +466,13 @@
|
|
|
464
466
|
value={valueAccessor(data)}
|
|
465
467
|
color={snippetProps.context.cScale?.(snippetProps.context.c(data))}
|
|
466
468
|
{format}
|
|
467
|
-
onpointerenter={() => {
|
|
468
|
-
|
|
469
|
+
onpointerenter={() => {
|
|
470
|
+
if (snippetProps.context)
|
|
471
|
+
snippetProps.context.series.highlightKey = keyAccessor(data);
|
|
472
|
+
}}
|
|
473
|
+
onpointerleave={() => {
|
|
474
|
+
if (snippetProps.context) snippetProps.context.series.highlightKey = null;
|
|
475
|
+
}}
|
|
469
476
|
{...props.tooltip?.item}
|
|
470
477
|
/>
|
|
471
478
|
</Tooltip.List>
|
|
@@ -377,10 +377,7 @@
|
|
|
377
377
|
) {
|
|
378
378
|
rect.left = alignValue(xValue, 'end', xOffset, tooltipWidth);
|
|
379
379
|
}
|
|
380
|
-
if (
|
|
381
|
-
(xAlign === 'end' || xAlign === 'center') &&
|
|
382
|
-
containerRect.left + rect.left < 0
|
|
383
|
-
) {
|
|
380
|
+
if ((xAlign === 'end' || xAlign === 'center') && containerRect.left + rect.left < 0) {
|
|
384
381
|
rect.left = alignValue(xValue, 'start', xOffset, tooltipWidth);
|
|
385
382
|
}
|
|
386
383
|
}
|
|
@@ -393,10 +390,7 @@
|
|
|
393
390
|
) {
|
|
394
391
|
rect.top = alignValue(yValue, 'end', yOffset, tooltipHeight);
|
|
395
392
|
}
|
|
396
|
-
if (
|
|
397
|
-
(yAlign === 'end' || yAlign === 'center') &&
|
|
398
|
-
containerRect.top + rect.top < 0
|
|
399
|
-
) {
|
|
393
|
+
if ((yAlign === 'end' || yAlign === 'center') && containerRect.top + rect.top < 0) {
|
|
400
394
|
rect.top = alignValue(yValue, 'start', yOffset, tooltipHeight);
|
|
401
395
|
}
|
|
402
396
|
}
|
package/dist/contexts/chart.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ChartState } from '../states/chart.svelte.js';
|
|
2
2
|
import type { AnyScale } from '../utils/scales.svelte.js';
|
|
3
3
|
export type { ChartState };
|
|
4
|
-
export type { NodeKind, ComponentNode, RegisterComponentOptions } from '../states/chart.svelte.js';
|
|
4
|
+
export type { NodeKind, ComponentNode, RegisterComponentOptions, } from '../states/chart.svelte.js';
|
|
5
5
|
export declare function getChartContext<T, XScale extends AnyScale = AnyScale, YScale extends AnyScale = AnyScale>(): ChartState<T, XScale, YScale>;
|
|
6
6
|
export declare function setChartContext<T, XScale extends AnyScale = AnyScale, YScale extends AnyScale = AnyScale>(context: ChartState<T, XScale, YScale>): ChartState<T, XScale, YScale>;
|
package/dist/contexts/chart.js
CHANGED
|
@@ -5,7 +5,9 @@ const _ChartContext = new Context('ChartContext');
|
|
|
5
5
|
* Provides safe defaults to prevent runtime errors.
|
|
6
6
|
*/
|
|
7
7
|
const fallbackContext = {
|
|
8
|
-
registerMark: () => () => {
|
|
8
|
+
registerMark: () => () => {
|
|
9
|
+
/* no-op */
|
|
10
|
+
},
|
|
9
11
|
registerComponent: (_options) => ({
|
|
10
12
|
id: Symbol('noop'),
|
|
11
13
|
kind: 'mark',
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { scaleBand } from 'd3-scale';
|
|
3
|
+
import ServerChart from './ServerChart.svelte';
|
|
4
|
+
import type { CaptureTarget } from './captureStore.js';
|
|
5
|
+
import Bars from '../components/Bars.svelte';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
let {
|
|
8
|
+
data,
|
|
9
|
+
width,
|
|
10
|
+
height,
|
|
11
|
+
capture,
|
|
12
|
+
onCapture,
|
|
13
|
+
}: {
|
|
14
|
+
data: { category: string; value: number }[];
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
capture?: CaptureTarget;
|
|
18
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
19
|
+
} = $props();
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
22
|
<ServerChart
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
{capture}
|
|
24
|
+
{onCapture}
|
|
25
|
+
{width}
|
|
26
|
+
{height}
|
|
27
|
+
{data}
|
|
28
|
+
x="category"
|
|
29
|
+
xScale={scaleBand().paddingInner(0.2).paddingOuter(0.1)}
|
|
30
|
+
y="value"
|
|
31
|
+
yDomain={[0, null]}
|
|
32
|
+
padding={{ top: 20, right: 20, bottom: 30, left: 40 }}
|
|
33
33
|
>
|
|
34
|
-
|
|
34
|
+
<Bars fill="rgb(59, 130, 246)" radius={4} />
|
|
35
35
|
</ServerChart>
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import ServerChart from './ServerChart.svelte';
|
|
3
|
+
import type { CaptureTarget } from './captureStore.js';
|
|
4
|
+
import Area from '../components/Area.svelte';
|
|
5
|
+
import Spline from '../components/Spline.svelte';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
let {
|
|
8
|
+
data,
|
|
9
|
+
width,
|
|
10
|
+
height,
|
|
11
|
+
capture,
|
|
12
|
+
onCapture,
|
|
13
|
+
}: {
|
|
14
|
+
data: { date: number; value: number }[];
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
capture?: CaptureTarget;
|
|
18
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
19
|
+
} = $props();
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
22
|
<ServerChart
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
{capture}
|
|
24
|
+
{onCapture}
|
|
25
|
+
{width}
|
|
26
|
+
{height}
|
|
27
|
+
{data}
|
|
28
|
+
x="date"
|
|
29
|
+
y="value"
|
|
30
|
+
yDomain={[0, null]}
|
|
31
|
+
padding={{ top: 20, right: 20, bottom: 20, left: 20 }}
|
|
32
32
|
>
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
<Area fill="rgba(59, 130, 246, 0.15)" stroke="none" />
|
|
34
|
+
<Spline stroke="rgb(59, 130, 246)" strokeWidth={2} />
|
|
35
35
|
</ServerChart>
|
package/dist/server/index.js
CHANGED
|
@@ -64,7 +64,7 @@ export function renderChart(component, options) {
|
|
|
64
64
|
const captureTarget = {};
|
|
65
65
|
// SSR render to build the component tree and capture chart state
|
|
66
66
|
const rendered = render(component, {
|
|
67
|
-
props: { ...props, width, height, capture: captureTarget }
|
|
67
|
+
props: { ...props, width, height, capture: captureTarget },
|
|
68
68
|
});
|
|
69
69
|
// Force the SSR render to fully flush
|
|
70
70
|
void rendered.body;
|
|
@@ -6,9 +6,7 @@ import { add } from '../utils/math.js';
|
|
|
6
6
|
* For continuous scales, returns the domain unchanged.
|
|
7
7
|
*/
|
|
8
8
|
export function expandBandBrushDomain(brushDomain, baseDomain) {
|
|
9
|
-
if (brushDomain[0] == null ||
|
|
10
|
-
brushDomain[1] == null ||
|
|
11
|
-
typeof brushDomain[0] !== 'string') {
|
|
9
|
+
if (brushDomain[0] == null || brushDomain[1] == null || typeof brushDomain[0] !== 'string') {
|
|
12
10
|
return brushDomain;
|
|
13
11
|
}
|
|
14
12
|
const startIdx = baseDomain.indexOf(brushDomain[0]);
|
|
@@ -109,8 +107,7 @@ export class BrushState {
|
|
|
109
107
|
// Determine active state from current values
|
|
110
108
|
const hasX = this.x[0] != null && this.x[1] != null;
|
|
111
109
|
const hasY = this.y[0] != null && this.y[1] != null;
|
|
112
|
-
this.active =
|
|
113
|
-
this.axis === 'x' ? hasX : this.axis === 'y' ? hasY : hasX || hasY;
|
|
110
|
+
this.active = this.axis === 'x' ? hasX : this.axis === 'y' ? hasY : hasX || hasY;
|
|
114
111
|
}
|
|
115
112
|
/** Set brush to a new range, clamped to domain bounds */
|
|
116
113
|
setRange(startValue, currentValue) {
|
|
@@ -177,12 +174,16 @@ export class BrushState {
|
|
|
177
174
|
const yDomain = this.ctx?.baseYScale.domain() ?? [];
|
|
178
175
|
const xCat = isCategoricalDomain(xDomain);
|
|
179
176
|
const yCat = isCategoricalDomain(yDomain);
|
|
180
|
-
const clampX = (v) => xCat
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
177
|
+
const clampX = (v) => xCat
|
|
178
|
+
? clampByIndex(v, this.xDomainMin, this.xDomainMax, xDomain)
|
|
179
|
+
: clamp(v, this.xDomainMin, this.xDomainMax);
|
|
180
|
+
const clampY = (v) => yCat
|
|
181
|
+
? clampByIndex(v, this.yDomainMin, this.yDomainMax, yDomain)
|
|
182
|
+
: clamp(v, this.yDomainMin, this.yDomainMax);
|
|
183
|
+
const ltX = (a, b) => (xCat ? xDomain.indexOf(a) < xDomain.indexOf(b) : a < +b);
|
|
184
|
+
const gtX = (a, b) => (xCat ? xDomain.indexOf(a) > xDomain.indexOf(b) : a > +b);
|
|
185
|
+
const ltY = (a, b) => (yCat ? yDomain.indexOf(a) < yDomain.indexOf(b) : a < +b);
|
|
186
|
+
const gtY = (a, b) => (yCat ? yDomain.indexOf(a) > yDomain.indexOf(b) : a > +b);
|
|
186
187
|
switch (edge) {
|
|
187
188
|
case 'top':
|
|
188
189
|
this.y = [
|
|
@@ -218,10 +219,12 @@ export class BrushState {
|
|
|
218
219
|
const newX = externalX ?? [null, null];
|
|
219
220
|
const newY = externalY ?? [null, null];
|
|
220
221
|
// Only write when values actually differ to avoid reactive loops
|
|
221
|
-
if (this.x[0]?.valueOf() !== newX[0]?.valueOf() ||
|
|
222
|
+
if (this.x[0]?.valueOf() !== newX[0]?.valueOf() ||
|
|
223
|
+
this.x[1]?.valueOf() !== newX[1]?.valueOf()) {
|
|
222
224
|
this.x = newX;
|
|
223
225
|
}
|
|
224
|
-
if (this.y[0]?.valueOf() !== newY[0]?.valueOf() ||
|
|
226
|
+
if (this.y[0]?.valueOf() !== newY[0]?.valueOf() ||
|
|
227
|
+
this.y[1]?.valueOf() !== newY[1]?.valueOf()) {
|
|
225
228
|
this.y = newY;
|
|
226
229
|
}
|
|
227
230
|
const isXAxisActive = externalX != null &&
|
|
@@ -287,17 +287,23 @@ describe('ChartState mark registration', () => {
|
|
|
287
287
|
flushSync();
|
|
288
288
|
expect(state.seriesState.isDefaultSeries).toBe(false);
|
|
289
289
|
expect(state.seriesState.series).toHaveLength(2);
|
|
290
|
-
expect(state.seriesState.series[0]).toMatchObject({
|
|
291
|
-
|
|
290
|
+
expect(state.seriesState.series[0]).toMatchObject({
|
|
291
|
+
key: 'apples',
|
|
292
|
+
color: 'red',
|
|
293
|
+
value: 'apples',
|
|
294
|
+
});
|
|
295
|
+
expect(state.seriesState.series[1]).toMatchObject({
|
|
296
|
+
key: 'bananas',
|
|
297
|
+
color: 'yellow',
|
|
298
|
+
value: 'bananas',
|
|
299
|
+
});
|
|
292
300
|
}
|
|
293
301
|
finally {
|
|
294
302
|
cleanup();
|
|
295
303
|
}
|
|
296
304
|
});
|
|
297
305
|
it('should not generate implicit series when explicit series are provided', () => {
|
|
298
|
-
const data = [
|
|
299
|
-
{ date: '2024-01', apples: 10, bananas: 15 },
|
|
300
|
-
];
|
|
306
|
+
const data = [{ date: '2024-01', apples: 10, bananas: 15 }];
|
|
301
307
|
const { state, cleanup } = createChartState({
|
|
302
308
|
data,
|
|
303
309
|
x: 'date',
|
|
@@ -611,8 +617,16 @@ describe('ChartState geo projection skips markInfo', () => {
|
|
|
611
617
|
// seriesKey/color/label should still create implicit series for legends
|
|
612
618
|
expect(state.seriesState.isDefaultSeries).toBe(false);
|
|
613
619
|
expect(state.seriesState.series).toHaveLength(2);
|
|
614
|
-
expect(state.seriesState.series[0]).toMatchObject({
|
|
615
|
-
|
|
620
|
+
expect(state.seriesState.series[0]).toMatchObject({
|
|
621
|
+
key: 'earthquakes',
|
|
622
|
+
color: 'red',
|
|
623
|
+
label: 'Earthquakes',
|
|
624
|
+
});
|
|
625
|
+
expect(state.seriesState.series[1]).toMatchObject({
|
|
626
|
+
key: 'volcanos',
|
|
627
|
+
color: 'orange',
|
|
628
|
+
label: 'Volcanos',
|
|
629
|
+
});
|
|
616
630
|
// But flatData should not include extra mark data
|
|
617
631
|
expect(state.flatData).toHaveLength(3);
|
|
618
632
|
}
|
|
@@ -861,9 +875,7 @@ describe('ChartState implicit x/y from marks (no x/y on Chart)', () => {
|
|
|
861
875
|
}
|
|
862
876
|
});
|
|
863
877
|
it('should deduplicate repeated mark x keys into a single accessor', () => {
|
|
864
|
-
const data = [
|
|
865
|
-
{ date: new Date(2024, 0, 1), value: 10 },
|
|
866
|
-
];
|
|
878
|
+
const data = [{ date: new Date(2024, 0, 1), value: 10 }];
|
|
867
879
|
const { state, cleanup } = createChartState({});
|
|
868
880
|
try {
|
|
869
881
|
// Two marks, same x='date' — should not create duplicate keys
|
|
@@ -878,9 +890,7 @@ describe('ChartState implicit x/y from marks (no x/y on Chart)', () => {
|
|
|
878
890
|
}
|
|
879
891
|
});
|
|
880
892
|
it('should use explicit x/y from Chart props over mark-derived values', () => {
|
|
881
|
-
const data = [
|
|
882
|
-
{ date: new Date(2024, 0, 1), value: 10 },
|
|
883
|
-
];
|
|
893
|
+
const data = [{ date: new Date(2024, 0, 1), value: 10 }];
|
|
884
894
|
const { state, cleanup } = createChartState({
|
|
885
895
|
x: 'value', // explicit — should override 'date' from marks
|
|
886
896
|
y: 'value',
|
|
@@ -1250,12 +1260,7 @@ describe('ChartState group layout auto-derives x1/y1', () => {
|
|
|
1250
1260
|
{ year: '2016', apples: 480, bananas: 240, cherries: 120, grapes: 50 },
|
|
1251
1261
|
{ year: '2017', apples: 960, bananas: 480, cherries: 240, grapes: 100 },
|
|
1252
1262
|
];
|
|
1253
|
-
const series = [
|
|
1254
|
-
{ key: 'apples' },
|
|
1255
|
-
{ key: 'bananas' },
|
|
1256
|
-
{ key: 'cherries' },
|
|
1257
|
-
{ key: 'grapes' },
|
|
1258
|
-
];
|
|
1263
|
+
const series = [{ key: 'apples' }, { key: 'bananas' }, { key: 'cherries' }, { key: 'grapes' }];
|
|
1259
1264
|
it('should auto-derive x1Domain from series keys when seriesLayout=group and valueAxis=y', () => {
|
|
1260
1265
|
const { state, cleanup } = createChartState({
|
|
1261
1266
|
data: wideData,
|