layerchart 2.0.0-next.50 → 2.0.0-next.52
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/Arc.svelte +12 -4
- package/dist/components/Arc.svelte.d.ts +4 -0
- package/dist/components/ArcLabel.svelte +259 -0
- package/dist/components/ArcLabel.svelte.d.ts +73 -0
- package/dist/components/ArcLabel.svelte.test.d.ts +1 -0
- package/dist/components/ArcLabel.svelte.test.js +235 -0
- package/dist/components/Axis.svelte +25 -0
- package/dist/components/Axis.svelte.d.ts +10 -0
- package/dist/components/Circle.svelte +82 -59
- package/dist/components/CircleLegend.svelte +389 -0
- package/dist/components/CircleLegend.svelte.d.ts +114 -0
- package/dist/components/Ellipse.svelte +83 -64
- package/dist/components/GeoLegend.svelte +404 -0
- package/dist/components/GeoLegend.svelte.d.ts +106 -0
- package/dist/components/GeoRaster.svelte +311 -0
- package/dist/components/GeoRaster.svelte.d.ts +61 -0
- package/dist/components/Grid.svelte +15 -0
- package/dist/components/Grid.svelte.d.ts +5 -0
- package/dist/components/Image.svelte +2 -2
- package/dist/components/Labels.svelte +46 -11
- package/dist/components/Labels.svelte.d.ts +7 -3
- package/dist/components/Legend.svelte +58 -3
- package/dist/components/Legend.svelte.d.ts +7 -0
- package/dist/components/Line.svelte +82 -62
- package/dist/components/Points.svelte +2 -2
- package/dist/components/Polygon.svelte +92 -56
- package/dist/components/Rect.svelte +113 -64
- package/dist/components/Rule.svelte +2 -0
- package/dist/components/Sankey.svelte +0 -2
- package/dist/components/Text.svelte +83 -52
- package/dist/components/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-defaults-placement-to-centroid--x-y-at-the-centroid--middle-anchors--1.png +0 -0
- package/dist/components/__screenshots__/ArcLabel.svelte.test.ts/ArcLabel-defaults-placement-to-centroid--x-y-at-the-centroid--middle-anchors--2.png +0 -0
- package/dist/components/charts/ArcChart.svelte +39 -2
- package/dist/components/charts/ArcChart.svelte.d.ts +12 -1
- package/dist/components/charts/PieChart.svelte +40 -2
- package/dist/components/charts/PieChart.svelte.d.ts +10 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.js +8 -0
- package/dist/components/layers/Canvas.svelte +65 -48
- package/dist/components/layers/Canvas.svelte.d.ts +10 -0
- package/dist/contexts/canvas.d.ts +3 -0
- package/dist/server/ContextCapture.svelte +30 -0
- package/dist/server/ContextCapture.svelte.d.ts +8 -0
- package/dist/server/ServerChart.svelte +26 -0
- package/dist/server/ServerChart.svelte.d.ts +11 -0
- package/dist/server/TestBarChart.svelte +35 -0
- package/dist/server/TestBarChart.svelte.d.ts +14 -0
- package/dist/server/TestLineChart.svelte +35 -0
- package/dist/server/TestLineChart.svelte.d.ts +14 -0
- package/dist/server/captureStore.d.ts +8 -0
- package/dist/server/captureStore.js +18 -0
- package/dist/server/index.d.ts +137 -0
- package/dist/server/index.js +141 -0
- package/dist/server/renderChart.ssr.test.d.ts +1 -0
- package/dist/server/renderChart.ssr.test.js +205 -0
- package/dist/server/renderTree.d.ts +8 -0
- package/dist/server/renderTree.js +29 -0
- package/dist/states/__screenshots__/chart.svelte.test.ts/ChartState-geo-projection-skips-markInfo-should-not-derive-x-y-accessors-from-marks-when-geo-projection-is-active-1.png +0 -0
- package/dist/states/__screenshots__/chart.svelte.test.ts/ChartState-geo-projection-skips-markInfo-should-not-derive-x-y-accessors-from-marks-when-geo-projection-is-active-2.png +0 -0
- package/dist/states/chart.svelte.d.ts +5 -1
- package/dist/states/chart.svelte.js +18 -3
- package/dist/states/chart.svelte.test.js +110 -0
- package/dist/states/geo.svelte.d.ts +5 -1
- package/dist/states/geo.svelte.js +80 -68
- package/dist/utils/arcText.svelte.d.ts +7 -1
- package/dist/utils/arcText.svelte.js +8 -4
- package/dist/utils/canvas.js +29 -10
- package/dist/utils/canvas.svelte.test.js +2 -2
- package/dist/utils/motion.svelte.js +14 -0
- package/package.json +7 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { SeriesData } from './types.js';
|
|
7
7
|
|
|
8
8
|
import Arc from '../Arc.svelte';
|
|
9
|
+
import ArcLabel, { type ArcLabelConfig } from '../ArcLabel.svelte';
|
|
9
10
|
import Group from '../Group.svelte';
|
|
10
11
|
import Pie from '../Pie.svelte';
|
|
11
12
|
|
|
@@ -28,6 +29,13 @@
|
|
|
28
29
|
// Props that don't apply to PieChart
|
|
29
30
|
'data' | 'axis' | 'brush' | 'grid' | 'highlight' | 'labels' | 'points' | 'rule'
|
|
30
31
|
> & {
|
|
32
|
+
/**
|
|
33
|
+
* Render text labels on each arc.
|
|
34
|
+
*
|
|
35
|
+
* Pass `true` to enable with default placement (`centroid`), or an object
|
|
36
|
+
* to customize via `ArcLabel` props (placement, format, value accessor, etc).
|
|
37
|
+
*/
|
|
38
|
+
labels?: boolean | (ArcLabelConfig & { value?: Accessor });
|
|
31
39
|
/**
|
|
32
40
|
* The series data to be used for the chart.
|
|
33
41
|
*/
|
|
@@ -214,10 +222,17 @@
|
|
|
214
222
|
tooltip: tooltipProp,
|
|
215
223
|
pie,
|
|
216
224
|
arc,
|
|
225
|
+
labels = false,
|
|
217
226
|
context = $bindable(),
|
|
218
227
|
...restProps
|
|
219
228
|
}: PieChartProps<TData> = $props();
|
|
220
229
|
|
|
230
|
+
const labelsConfig = $derived.by<(ArcLabelConfig & { value?: Accessor }) | null>(() => {
|
|
231
|
+
if (labels === true) return { placement: 'callout' };
|
|
232
|
+
if (labels) return { placement: 'callout', ...labels };
|
|
233
|
+
return null;
|
|
234
|
+
});
|
|
235
|
+
|
|
221
236
|
const series = $derived(
|
|
222
237
|
seriesProp === undefined ? [{ key: 'default', value: value }] : seriesProp
|
|
223
238
|
);
|
|
@@ -231,7 +246,7 @@
|
|
|
231
246
|
// Reading context.series.allSeriesData here would create a derived_references_self cycle:
|
|
232
247
|
// SeriesState.#series → ChartState.props → data={visibleData} → chartData → context.series.allSeriesData → #series
|
|
233
248
|
const chartData = $derived.by(() => {
|
|
234
|
-
const seriesData = series.flatMap((s) => s.data ?? []);
|
|
249
|
+
const seriesData = series.flatMap((s) => ('data' in s ? s.data : undefined) ?? []);
|
|
235
250
|
return (seriesData.length > 0 ? seriesData : chartDataArray(data)) as Array<TData>;
|
|
236
251
|
});
|
|
237
252
|
|
|
@@ -245,7 +260,7 @@
|
|
|
245
260
|
|
|
246
261
|
// Compute series colors locally to avoid derived_references_self cycle through context.series.allSeriesColors
|
|
247
262
|
const allSeriesColors = $derived(
|
|
248
|
-
series.map((s) => s.color).filter((c) => c != null) as string[]
|
|
263
|
+
series.map((s) => ('color' in s ? s.color : undefined)).filter((c) => c != null) as string[]
|
|
249
264
|
);
|
|
250
265
|
|
|
251
266
|
// Custom tickFormat for PieChart legends - uses data labels instead of series labels
|
|
@@ -402,6 +417,29 @@
|
|
|
402
417
|
index: arcIdx,
|
|
403
418
|
seriesIndex: seriesIdx,
|
|
404
419
|
})}
|
|
420
|
+
{:else if labelsConfig}
|
|
421
|
+
<Arc {...arcProps}>
|
|
422
|
+
{#snippet children({
|
|
423
|
+
centroid,
|
|
424
|
+
startAngle,
|
|
425
|
+
endAngle,
|
|
426
|
+
innerRadius: arcInnerRadius,
|
|
427
|
+
outerRadius: arcOuterRadius,
|
|
428
|
+
getArcTextProps,
|
|
429
|
+
})}
|
|
430
|
+
{@const { value: labelValue, ...labelRest } = labelsConfig}
|
|
431
|
+
<ArcLabel
|
|
432
|
+
{centroid}
|
|
433
|
+
{startAngle}
|
|
434
|
+
{endAngle}
|
|
435
|
+
innerRadius={arcInnerRadius}
|
|
436
|
+
outerRadius={arcOuterRadius}
|
|
437
|
+
{getArcTextProps}
|
|
438
|
+
value={accessor(labelValue ?? value)(arcData.data)}
|
|
439
|
+
{...labelRest}
|
|
440
|
+
/>
|
|
441
|
+
{/snippet}
|
|
442
|
+
</Arc>
|
|
405
443
|
{:else}
|
|
406
444
|
<Arc {...arcProps} />
|
|
407
445
|
{/if}
|
|
@@ -4,6 +4,7 @@ import type { ChartState } from '../../contexts/chart.js';
|
|
|
4
4
|
import type { Accessor } from '../../utils/common.js';
|
|
5
5
|
import type { SeriesData } from './types.js';
|
|
6
6
|
import Arc from '../Arc.svelte';
|
|
7
|
+
import { type ArcLabelConfig } from '../ArcLabel.svelte';
|
|
7
8
|
import Group from '../Group.svelte';
|
|
8
9
|
import Pie from '../Pie.svelte';
|
|
9
10
|
export type PieChartExtraSnippetProps<TData> = {
|
|
@@ -19,6 +20,15 @@ export type PieChartProps<TData> = {
|
|
|
19
20
|
*/
|
|
20
21
|
data?: TData[] | readonly TData[];
|
|
21
22
|
} & Omit<ChartProps<any>, 'data' | 'axis' | 'brush' | 'grid' | 'highlight' | 'labels' | 'points' | 'rule'> & {
|
|
23
|
+
/**
|
|
24
|
+
* Render text labels on each arc.
|
|
25
|
+
*
|
|
26
|
+
* Pass `true` to enable with default placement (`centroid`), or an object
|
|
27
|
+
* to customize via `ArcLabel` props (placement, format, value accessor, etc).
|
|
28
|
+
*/
|
|
29
|
+
labels?: boolean | (ArcLabelConfig & {
|
|
30
|
+
value?: Accessor;
|
|
31
|
+
});
|
|
22
32
|
/**
|
|
23
33
|
* The series data to be used for the chart.
|
|
24
34
|
*/
|
|
@@ -8,6 +8,8 @@ export { default as AnnotationRange } from './AnnotationRange.svelte';
|
|
|
8
8
|
export * from './AnnotationRange.svelte';
|
|
9
9
|
export { default as Arc } from './Arc.svelte';
|
|
10
10
|
export * from './Arc.svelte';
|
|
11
|
+
export { default as ArcLabel } from './ArcLabel.svelte';
|
|
12
|
+
export * from './ArcLabel.svelte';
|
|
11
13
|
export { default as Area } from './Area.svelte';
|
|
12
14
|
export * from './Area.svelte';
|
|
13
15
|
export { default as Axis } from './Axis.svelte';
|
|
@@ -68,6 +70,8 @@ export { default as GeoPath } from './GeoPath.svelte';
|
|
|
68
70
|
export * from './GeoPath.svelte';
|
|
69
71
|
export { default as GeoPoint } from './GeoPoint.svelte';
|
|
70
72
|
export * from './GeoPoint.svelte';
|
|
73
|
+
export { default as GeoRaster } from './GeoRaster.svelte';
|
|
74
|
+
export * from './GeoRaster.svelte';
|
|
71
75
|
export { default as GeoSpline } from './GeoSpline.svelte';
|
|
72
76
|
export * from './GeoSpline.svelte';
|
|
73
77
|
export { default as GeoTile } from './GeoTile.svelte';
|
|
@@ -92,6 +96,10 @@ export { default as Labels } from './Labels.svelte';
|
|
|
92
96
|
export * from './Labels.svelte';
|
|
93
97
|
export { default as Layer } from './layers/Layer.svelte';
|
|
94
98
|
export * from './layers/Layer.svelte';
|
|
99
|
+
export { default as CircleLegend } from './CircleLegend.svelte';
|
|
100
|
+
export * from './CircleLegend.svelte';
|
|
101
|
+
export { default as GeoLegend } from './GeoLegend.svelte';
|
|
102
|
+
export * from './GeoLegend.svelte';
|
|
95
103
|
export { default as Legend } from './Legend.svelte';
|
|
96
104
|
export * from './Legend.svelte';
|
|
97
105
|
export { default as Line } from './Line.svelte';
|
package/dist/components/index.js
CHANGED
|
@@ -8,6 +8,8 @@ export { default as AnnotationRange } from './AnnotationRange.svelte';
|
|
|
8
8
|
export * from './AnnotationRange.svelte';
|
|
9
9
|
export { default as Arc } from './Arc.svelte';
|
|
10
10
|
export * from './Arc.svelte';
|
|
11
|
+
export { default as ArcLabel } from './ArcLabel.svelte';
|
|
12
|
+
export * from './ArcLabel.svelte';
|
|
11
13
|
export { default as Area } from './Area.svelte';
|
|
12
14
|
export * from './Area.svelte';
|
|
13
15
|
export { default as Axis } from './Axis.svelte';
|
|
@@ -68,6 +70,8 @@ export { default as GeoPath } from './GeoPath.svelte';
|
|
|
68
70
|
export * from './GeoPath.svelte';
|
|
69
71
|
export { default as GeoPoint } from './GeoPoint.svelte';
|
|
70
72
|
export * from './GeoPoint.svelte';
|
|
73
|
+
export { default as GeoRaster } from './GeoRaster.svelte';
|
|
74
|
+
export * from './GeoRaster.svelte';
|
|
71
75
|
export { default as GeoSpline } from './GeoSpline.svelte';
|
|
72
76
|
export * from './GeoSpline.svelte';
|
|
73
77
|
export { default as GeoTile } from './GeoTile.svelte';
|
|
@@ -92,6 +96,10 @@ export { default as Labels } from './Labels.svelte';
|
|
|
92
96
|
export * from './Labels.svelte';
|
|
93
97
|
export { default as Layer } from './layers/Layer.svelte';
|
|
94
98
|
export * from './layers/Layer.svelte';
|
|
99
|
+
export { default as CircleLegend } from './CircleLegend.svelte';
|
|
100
|
+
export * from './CircleLegend.svelte';
|
|
101
|
+
export { default as GeoLegend } from './GeoLegend.svelte';
|
|
102
|
+
export * from './GeoLegend.svelte';
|
|
95
103
|
export { default as Legend } from './Legend.svelte';
|
|
96
104
|
export * from './Legend.svelte';
|
|
97
105
|
export { default as Line } from './Line.svelte';
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import type { ComponentNode } from '../../contexts/chart.js';
|
|
3
|
+
import type { ChartState } from '../../states/chart.svelte.js';
|
|
4
|
+
|
|
5
|
+
type SSRCaptureTarget = {
|
|
6
|
+
chartState?: ChartState<any, any, any>;
|
|
7
|
+
rootNode?: ComponentNode;
|
|
8
|
+
};
|
|
3
9
|
|
|
4
10
|
export type CanvasPropsWithoutHTML = {
|
|
5
11
|
/**
|
|
@@ -80,6 +86,12 @@
|
|
|
80
86
|
*/
|
|
81
87
|
debug?: boolean;
|
|
82
88
|
|
|
89
|
+
/** @internal Server-side capture target used by layerchart/server. */
|
|
90
|
+
ssrCapture?: SSRCaptureTarget;
|
|
91
|
+
|
|
92
|
+
/** @internal Server-side capture callback used by layerchart/server. */
|
|
93
|
+
ssrCaptureCallback?: (data: SSRCaptureTarget) => void;
|
|
94
|
+
|
|
83
95
|
children?: Snippet<
|
|
84
96
|
[{ ref: HTMLCanvasElement; canvasContext: CanvasRenderingContext2D | undefined }]
|
|
85
97
|
>;
|
|
@@ -87,7 +99,6 @@
|
|
|
87
99
|
|
|
88
100
|
export type CanvasProps = CanvasPropsWithoutHTML &
|
|
89
101
|
Without<HTMLCanvasAttributes, CanvasPropsWithoutHTML>;
|
|
90
|
-
|
|
91
102
|
</script>
|
|
92
103
|
|
|
93
104
|
<script lang="ts">
|
|
@@ -107,10 +118,13 @@
|
|
|
107
118
|
type CanvasContextValue,
|
|
108
119
|
type ComponentRender,
|
|
109
120
|
} from '../../contexts/canvas.js';
|
|
121
|
+
import { renderTree } from '../../server/renderTree.js';
|
|
110
122
|
|
|
111
123
|
let {
|
|
112
124
|
ref: refProp = $bindable(),
|
|
113
125
|
canvasContext: canvasContextProp = $bindable(),
|
|
126
|
+
ssrCapture,
|
|
127
|
+
ssrCaptureCallback,
|
|
114
128
|
willReadFrequently = false,
|
|
115
129
|
debug = false,
|
|
116
130
|
zIndex = 0,
|
|
@@ -183,9 +197,10 @@
|
|
|
183
197
|
// Bubble up to ancestor groups
|
|
184
198
|
let ancestor = node?.parent;
|
|
185
199
|
while (ancestor) {
|
|
186
|
-
const handler =
|
|
187
|
-
|
|
188
|
-
|
|
200
|
+
const handler =
|
|
201
|
+
ancestor.kind === 'group'
|
|
202
|
+
? (ancestor.canvasRender?.events?.[eventName] as ((e: Event) => void) | null | undefined)
|
|
203
|
+
: undefined;
|
|
189
204
|
handler?.(e);
|
|
190
205
|
ancestor = ancestor.parent;
|
|
191
206
|
}
|
|
@@ -225,21 +240,23 @@
|
|
|
225
240
|
*/
|
|
226
241
|
|
|
227
242
|
// Invalidate/redraw if color scheme changes, either via browser `prefers-color-scheme` (including emulation) or by changing `<html class="dark">` or `<html data-theme="...">`
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
if (typeof window !== 'undefined') {
|
|
244
|
+
const { dark } = new MediaQueryPresets();
|
|
245
|
+
watch(
|
|
246
|
+
() => dark.current,
|
|
247
|
+
() => {
|
|
248
|
+
canvasContext.invalidate();
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
useMutationObserver(
|
|
252
|
+
() => document.documentElement,
|
|
253
|
+
() => canvasContext.invalidate(),
|
|
254
|
+
{
|
|
255
|
+
attributes: true,
|
|
256
|
+
attributeFilter: ['class', 'data-theme'],
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
}
|
|
243
260
|
|
|
244
261
|
onMount(() => {
|
|
245
262
|
context = ref?.getContext('2d', { willReadFrequently }) as CanvasRenderingContext2D;
|
|
@@ -310,33 +327,6 @@
|
|
|
310
327
|
pendingInvalidation = false;
|
|
311
328
|
}
|
|
312
329
|
|
|
313
|
-
/**
|
|
314
|
-
* Recursively render the component tree.
|
|
315
|
-
* Group nodes: save → render (translate/opacity) → recurse children → restore
|
|
316
|
-
* Leaf nodes: save → render → restore
|
|
317
|
-
*/
|
|
318
|
-
function renderTree(canvasCtx: CanvasRenderingContext2D, node: ComponentNode) {
|
|
319
|
-
if (node.kind === 'group' && node.canvasRender) {
|
|
320
|
-
// Group: save state, apply transform, render children, restore
|
|
321
|
-
canvasCtx.save();
|
|
322
|
-
node.canvasRender.render(canvasCtx);
|
|
323
|
-
for (const child of node.children) {
|
|
324
|
-
renderTree(canvasCtx, child);
|
|
325
|
-
}
|
|
326
|
-
canvasCtx.restore();
|
|
327
|
-
} else if (node.canvasRender) {
|
|
328
|
-
// Leaf mark: save, render, restore
|
|
329
|
-
canvasCtx.save();
|
|
330
|
-
node.canvasRender.render(canvasCtx);
|
|
331
|
-
canvasCtx.restore();
|
|
332
|
-
} else {
|
|
333
|
-
// Non-rendering node (e.g. root, composite-mark): just recurse children
|
|
334
|
-
for (const child of node.children) {
|
|
335
|
-
renderTree(canvasCtx, child);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
330
|
function nodeHasEvents(node: ComponentNode) {
|
|
341
331
|
return node.canvasRender?.events && Object.values(node.canvasRender.events).some((d) => d);
|
|
342
332
|
}
|
|
@@ -345,7 +335,11 @@
|
|
|
345
335
|
* Recursively render the hit canvas tree for pointer event detection.
|
|
346
336
|
* Renders components that have event handlers (or whose ancestor group does), using unique colors.
|
|
347
337
|
*/
|
|
348
|
-
function renderHitTree(
|
|
338
|
+
function renderHitTree(
|
|
339
|
+
hitCtx: CanvasRenderingContext2D,
|
|
340
|
+
node: ComponentNode,
|
|
341
|
+
ancestorHasEvents = false
|
|
342
|
+
) {
|
|
349
343
|
if (node.kind === 'group' && node.canvasRender) {
|
|
350
344
|
const groupHasEvents = ancestorHasEvents || nodeHasEvents(node);
|
|
351
345
|
// Group: apply transform, recurse children (scoped by save/restore)
|
|
@@ -383,11 +377,33 @@
|
|
|
383
377
|
|
|
384
378
|
function invalidate() {
|
|
385
379
|
if (pendingInvalidation) return;
|
|
380
|
+
if (typeof requestAnimationFrame === 'undefined') return;
|
|
386
381
|
pendingInvalidation = true;
|
|
387
382
|
frameId = requestAnimationFrame(update);
|
|
388
383
|
}
|
|
389
384
|
|
|
390
|
-
|
|
385
|
+
function getRootNode() {
|
|
386
|
+
return rootNode;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return { register, invalidate, getRootNode };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function captureSSR() {
|
|
393
|
+
if (typeof window !== 'undefined') return '';
|
|
394
|
+
if (!ssrCapture && !ssrCaptureCallback) return '';
|
|
395
|
+
|
|
396
|
+
const captured: SSRCaptureTarget = {
|
|
397
|
+
chartState: ctx,
|
|
398
|
+
rootNode,
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
if (ssrCapture) {
|
|
402
|
+
Object.assign(ssrCapture, captured);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
ssrCaptureCallback?.(captured);
|
|
406
|
+
return '';
|
|
391
407
|
}
|
|
392
408
|
|
|
393
409
|
const canvasContext = createCanvasContext();
|
|
@@ -457,6 +473,7 @@
|
|
|
457
473
|
<canvas bind:this={hitCanvasElement} class="lc-hit-canvas" class:debug></canvas>
|
|
458
474
|
|
|
459
475
|
{@render children?.({ ref, canvasContext: context })}
|
|
476
|
+
{captureSSR()}
|
|
460
477
|
|
|
461
478
|
<style>
|
|
462
479
|
@layer base {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import type { ComponentNode } from '../../contexts/chart.js';
|
|
2
|
+
import type { ChartState } from '../../states/chart.svelte.js';
|
|
3
|
+
type SSRCaptureTarget = {
|
|
4
|
+
chartState?: ChartState<any, any, any>;
|
|
5
|
+
rootNode?: ComponentNode;
|
|
6
|
+
};
|
|
1
7
|
export type CanvasPropsWithoutHTML = {
|
|
2
8
|
/**
|
|
3
9
|
* The `<canvas>` tag. Useful for bindings.
|
|
@@ -67,6 +73,10 @@ export type CanvasPropsWithoutHTML = {
|
|
|
67
73
|
* @default false
|
|
68
74
|
*/
|
|
69
75
|
debug?: boolean;
|
|
76
|
+
/** @internal Server-side capture target used by layerchart/server. */
|
|
77
|
+
ssrCapture?: SSRCaptureTarget;
|
|
78
|
+
/** @internal Server-side capture callback used by layerchart/server. */
|
|
79
|
+
ssrCaptureCallback?: (data: SSRCaptureTarget) => void;
|
|
70
80
|
children?: Snippet<[
|
|
71
81
|
{
|
|
72
82
|
ref: HTMLCanvasElement;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MouseEventHandler, PointerEventHandler, TouchEventHandler } from 'svelte/elements';
|
|
2
2
|
import type { ComputedStylesOptions } from '../utils/canvas.js';
|
|
3
|
+
import type { ComponentNode } from '../states/chart.svelte.js';
|
|
3
4
|
export type ComponentRender<T extends Element = Element> = {
|
|
4
5
|
render: (ctx: CanvasRenderingContext2D, styleOverrides?: ComputedStylesOptions) => any;
|
|
5
6
|
events?: {
|
|
@@ -26,6 +27,8 @@ export type CanvasContextValue = {
|
|
|
26
27
|
*/
|
|
27
28
|
register<T extends Element>(component: ComponentRender<T>): () => void;
|
|
28
29
|
invalidate(): void;
|
|
30
|
+
/** Get the root ComponentNode of the canvas render tree. Used for server-side rendering. */
|
|
31
|
+
getRootNode?: () => ComponentNode;
|
|
29
32
|
};
|
|
30
33
|
export declare function getCanvasContext(): CanvasContextValue;
|
|
31
34
|
export declare function setCanvasContext(context: CanvasContextValue): CanvasContextValue;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getChartContext } from '../contexts/chart.js';
|
|
3
|
+
import { getCanvasContext } from '../contexts/canvas.js';
|
|
4
|
+
import { setSSRCapture, type CaptureTarget } from './captureStore.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
capture,
|
|
8
|
+
onCapture,
|
|
9
|
+
}: {
|
|
10
|
+
capture?: CaptureTarget;
|
|
11
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
const chartState = getChartContext();
|
|
15
|
+
const canvasCtx = getCanvasContext();
|
|
16
|
+
|
|
17
|
+
const captured = {
|
|
18
|
+
chartState,
|
|
19
|
+
rootNode: canvasCtx.getRootNode?.(),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (typeof window === 'undefined') {
|
|
23
|
+
if (capture) {
|
|
24
|
+
Object.assign(capture, captured);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
setSSRCapture(captured);
|
|
28
|
+
onCapture?.(captured);
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CaptureTarget } from './captureStore.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
capture?: CaptureTarget;
|
|
4
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
5
|
+
};
|
|
6
|
+
declare const ContextCapture: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type ContextCapture = ReturnType<typeof ContextCapture>;
|
|
8
|
+
export default ContextCapture;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import Chart from '../components/Chart.svelte';
|
|
4
|
+
import type { ChartProps } from '../components/Chart.svelte';
|
|
5
|
+
import Canvas from '../components/layers/Canvas.svelte';
|
|
6
|
+
import type { CaptureTarget } from './captureStore.js';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
children,
|
|
10
|
+
capture,
|
|
11
|
+
onCapture,
|
|
12
|
+
...chartProps
|
|
13
|
+
}: {
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
capture?: CaptureTarget;
|
|
16
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
17
|
+
} & Omit<ChartProps<any>, 'children'> = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<Chart ssr={true} {...chartProps}>
|
|
21
|
+
<Canvas ssrCapture={capture} ssrCaptureCallback={onCapture}>
|
|
22
|
+
{#if children}
|
|
23
|
+
{@render children()}
|
|
24
|
+
{/if}
|
|
25
|
+
</Canvas>
|
|
26
|
+
</Chart>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ChartProps } from '../components/Chart.svelte';
|
|
3
|
+
import type { CaptureTarget } from './captureStore.js';
|
|
4
|
+
type $$ComponentProps = {
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
capture?: CaptureTarget;
|
|
7
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
8
|
+
} & Omit<ChartProps<any>, 'children'>;
|
|
9
|
+
declare const ServerChart: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type ServerChart = ReturnType<typeof ServerChart>;
|
|
11
|
+
export default ServerChart;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
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
|
+
|
|
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
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<ServerChart
|
|
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
|
+
>
|
|
34
|
+
<Bars fill="rgb(59, 130, 246)" radius={4} />
|
|
35
|
+
</ServerChart>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CaptureTarget } from './captureStore.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
data: {
|
|
4
|
+
category: string;
|
|
5
|
+
value: number;
|
|
6
|
+
}[];
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
capture?: CaptureTarget;
|
|
10
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
11
|
+
};
|
|
12
|
+
declare const TestBarChart: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type TestBarChart = ReturnType<typeof TestBarChart>;
|
|
14
|
+
export default TestBarChart;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
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
|
+
|
|
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
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<ServerChart
|
|
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
|
+
>
|
|
33
|
+
<Area fill="rgba(59, 130, 246, 0.15)" stroke="none" />
|
|
34
|
+
<Spline stroke="rgb(59, 130, 246)" strokeWidth={2} />
|
|
35
|
+
</ServerChart>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CaptureTarget } from './captureStore.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
data: {
|
|
4
|
+
date: number;
|
|
5
|
+
value: number;
|
|
6
|
+
}[];
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
capture?: CaptureTarget;
|
|
10
|
+
onCapture?: (data: CaptureTarget) => void;
|
|
11
|
+
};
|
|
12
|
+
declare const TestLineChart: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type TestLineChart = ReturnType<typeof TestLineChart>;
|
|
14
|
+
export default TestLineChart;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChartState, ComponentNode } from '../states/chart.svelte.js';
|
|
2
|
+
export type CaptureTarget = {
|
|
3
|
+
chartState?: ChartState;
|
|
4
|
+
rootNode?: ComponentNode;
|
|
5
|
+
};
|
|
6
|
+
export type SSRCapture = CaptureTarget | null;
|
|
7
|
+
export declare function setSSRCapture(target: SSRCapture): void;
|
|
8
|
+
export declare function getSSRCapture(): SSRCapture;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const SSR_CAPTURE_KEY = Symbol.for('layerchart.ssr-capture');
|
|
2
|
+
let _capture = null;
|
|
3
|
+
function getGlobalCaptureStore() {
|
|
4
|
+
return globalThis;
|
|
5
|
+
}
|
|
6
|
+
export function setSSRCapture(target) {
|
|
7
|
+
_capture = target;
|
|
8
|
+
const globalStore = getGlobalCaptureStore();
|
|
9
|
+
if (target == null) {
|
|
10
|
+
delete globalStore[SSR_CAPTURE_KEY];
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
globalStore[SSR_CAPTURE_KEY] = target;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function getSSRCapture() {
|
|
17
|
+
return getGlobalCaptureStore()[SSR_CAPTURE_KEY] ?? _capture;
|
|
18
|
+
}
|