diff --git a/.changeset/eighty-islands-jam.md b/.changeset/eighty-islands-jam.md new file mode 100644 index 000000000..4ba875d87 --- /dev/null +++ b/.changeset/eighty-islands-jam.md @@ -0,0 +1,5 @@ +--- +'layerchart': patch +--- + +fix: Improve memory leak caused by detached DOM increase when using Canvas rendering due to sometimes still rendering Svg components (ex. `` vs ``) (#490) diff --git a/packages/layerchart/src/lib/components/Axis.svelte b/packages/layerchart/src/lib/components/Axis.svelte index 2f50ed5c4..eb869a78c 100644 --- a/packages/layerchart/src/lib/components/Axis.svelte +++ b/packages/layerchart/src/lib/components/Axis.svelte @@ -104,13 +104,11 @@ }; export type AxisProps = AxisPropsWithoutHTML & - Without, AxisPropsWithoutHTML>; + Without>; - {/if} - {#each tickVals as tick, index (tick)} + {#each tickVals as tick, index (tick.toString())} {@const tickCoords = getCoords(tick)} {@const [radialTickCoordsX, radialTickCoordsY] = pointRadial(tickCoords.x, tickCoords.y)} {@const [radialTickMarkCoordsX, radialTickMarkCoordsY] = pointRadial( @@ -386,7 +378,7 @@ ), }} - + {#if grid !== false} {@const ruleProps = extractLayerProps(grid, 'axis-grid')} {#if orientation === 'horizontal'} {/if} {/if} - {#if tickLabel} {@render tickLabel({ props: resolvedTickLabelProps, index })} {:else} {/if} - + {/each} - + diff --git a/packages/layerchart/src/lib/components/Bars.svelte b/packages/layerchart/src/lib/components/Bars.svelte index 1a2524494..329f9516e 100644 --- a/packages/layerchart/src/lib/components/Bars.svelte +++ b/packages/layerchart/src/lib/components/Bars.svelte @@ -27,10 +27,13 @@ - + {#if children} {@render children()} {:else} @@ -65,4 +68,4 @@ /> {/each} {/if} - + diff --git a/packages/layerchart/src/lib/components/Blur.svelte b/packages/layerchart/src/lib/components/Blur.svelte index c5ff138a8..ce8abf54a 100644 --- a/packages/layerchart/src/lib/components/Blur.svelte +++ b/packages/layerchart/src/lib/components/Blur.svelte @@ -21,22 +21,28 @@ - - - - - +{#if renderContext === 'svg'} + + + + + -{#if children} - - {@render children({ id, url: `url(#${id})` })} - + {#if children} + + {@render children({ id, url: `url(#${id})` })} + + {/if} {/if} diff --git a/packages/layerchart/src/lib/components/ClipPath.svelte b/packages/layerchart/src/lib/components/ClipPath.svelte index 38d0db341..64dc9e026 100644 --- a/packages/layerchart/src/lib/components/ClipPath.svelte +++ b/packages/layerchart/src/lib/components/ClipPath.svelte @@ -4,6 +4,7 @@ import { layerClass } from '$lib/utils/attributes.js'; import type { Snippet } from 'svelte'; import type { SVGAttributes } from 'svelte/elements'; + import { getRenderContext } from './Chart.svelte'; export type ClipPathPropsWithoutHTML = { /** @@ -55,20 +56,24 @@ }: ClipPathPropsWithoutHTML = $props(); const url = $derived(`url(#${id})`); + + const renderContext = getRenderContext(); - - - {@render clip?.({ id })} +{#if renderContext === 'svg'} + + + {@render clip?.({ id })} - {#if useId} - - {/if} - - + {#if useId} + + {/if} + + +{/if} {#if children} - {#if disabled} + {#if disabled || renderContext !== 'svg'} {@render children({ id, url, useId })} {:else} diff --git a/packages/layerchart/src/lib/components/GeoEdgeFade.svelte b/packages/layerchart/src/lib/components/GeoEdgeFade.svelte index 17d7c0fb4..8a5e049ff 100644 --- a/packages/layerchart/src/lib/components/GeoEdgeFade.svelte +++ b/packages/layerchart/src/lib/components/GeoEdgeFade.svelte @@ -15,7 +15,7 @@ }; export type GeoEdgeFadeProps = GeoEdgeFadePropsWithoutHTML & - Without, GeoEdgeFadePropsWithoutHTML>; + Without; - + {@render children?.()} - + diff --git a/packages/layerchart/src/lib/components/Graticule.svelte b/packages/layerchart/src/lib/components/Graticule.svelte index 144bed936..67f587925 100644 --- a/packages/layerchart/src/lib/components/Graticule.svelte +++ b/packages/layerchart/src/lib/components/Graticule.svelte @@ -16,6 +16,7 @@ - + {#if !lines && !outline} @@ -44,4 +45,4 @@ {...extractLayerProps(outline, 'graticule-geo-outline')} /> {/if} - + diff --git a/packages/layerchart/src/lib/components/Grid.svelte b/packages/layerchart/src/lib/components/Grid.svelte index 33101f728..64e004c91 100644 --- a/packages/layerchart/src/lib/components/Grid.svelte +++ b/packages/layerchart/src/lib/components/Grid.svelte @@ -77,7 +77,7 @@ }; export type GridProps = Omit< - GridPropsWithoutHTML & Without, GridPropsWithoutHTML>, + GridPropsWithoutHTML & Without>, 'children' >; @@ -93,6 +93,7 @@ import { isScaleBand } from '$lib/utils/scales.svelte.js'; import Circle from './Circle.svelte'; + import Group, { type GroupProps } from './Group.svelte'; import Line from './Line.svelte'; import Rule from './Rule.svelte'; import Spline from './Spline.svelte'; @@ -150,11 +151,11 @@ ); - + {#if x} {@const splineProps = extractLayerProps(x, 'grid-x-line')} - + {#each xTickVals as x (x)} {#if ctx.radial} {@const [x1, y1] = pointRadial(ctx.xScale(x), ctx.yRange[0])} @@ -204,12 +205,12 @@ )} /> {/if} - + {/if} {#if y} {@const splineProps = extractLayerProps(y, 'grid-y-line')} - + {#each yTickVals as y (y)} {#if ctx.radial} {#if radialY === 'circle'} @@ -285,6 +286,6 @@ /> {/if} {/if} - + {/if} - + diff --git a/packages/layerchart/src/lib/components/Group.svelte b/packages/layerchart/src/lib/components/Group.svelte index ad3c94bfc..38d50c197 100644 --- a/packages/layerchart/src/lib/components/Group.svelte +++ b/packages/layerchart/src/lib/components/Group.svelte @@ -1,10 +1,10 @@ - - + {#if geoCtx.projection} {@const polygon = geoVoronoi().hull(points)} {/if} - + diff --git a/packages/layerchart/src/lib/components/Labels.svelte b/packages/layerchart/src/lib/components/Labels.svelte index 4040314e2..92d8583d0 100644 --- a/packages/layerchart/src/lib/components/Labels.svelte +++ b/packages/layerchart/src/lib/components/Labels.svelte @@ -73,6 +73,7 @@ import { isScaleBand } from '$lib/utils/scales.svelte.js'; import { getChartContext } from './Chart.svelte'; + import Group from './Group.svelte'; import { extractLayerProps, layerClass } from '$lib/utils/attributes.js'; const ctx = getChartContext(); @@ -172,7 +173,7 @@ } - + {#snippet children({ points })} {#each points as point, i (key(point.data, i))} @@ -196,4 +197,4 @@ {/each} {/snippet} - + diff --git a/packages/layerchart/src/lib/components/Rule.svelte b/packages/layerchart/src/lib/components/Rule.svelte index b33199f55..44029247b 100644 --- a/packages/layerchart/src/lib/components/Rule.svelte +++ b/packages/layerchart/src/lib/components/Rule.svelte @@ -52,6 +52,7 @@ import { cls } from '@layerstack/tailwind'; import Circle from './Circle.svelte'; + import Group from './Group.svelte'; import Line, { type LinePropsWithoutHTML } from './Line.svelte'; import { getChartContext } from './Chart.svelte'; import { layerClass } from '$lib/utils/attributes.js'; @@ -87,7 +88,7 @@ } - + {#if showRule(x, 'x')} {@const xCoord = x === true || x === 'left' @@ -153,4 +154,4 @@ /> {/if} {/if} - + diff --git a/packages/layerchart/src/lib/components/Voronoi.svelte b/packages/layerchart/src/lib/components/Voronoi.svelte index a06047f0f..433b336d8 100644 --- a/packages/layerchart/src/lib/components/Voronoi.svelte +++ b/packages/layerchart/src/lib/components/Voronoi.svelte @@ -50,7 +50,7 @@ }; export type VoronoiProps = VoronoiPropsWithoutHTML & - Without, 'children'>, VoronoiPropsWithoutHTML>; + Without, VoronoiPropsWithoutHTML>; - + {#if geo.projection} {@const polygons = geoVoronoi().polygons(points)} {#each polygons.features as feature} @@ -158,4 +159,4 @@ {/if} {/each} {/if} - + diff --git a/packages/layerchart/src/routes/_NavMenu.svelte b/packages/layerchart/src/routes/_NavMenu.svelte index db01243a2..a395e5bd7 100644 --- a/packages/layerchart/src/routes/_NavMenu.svelte +++ b/packages/layerchart/src/routes/_NavMenu.svelte @@ -128,6 +128,7 @@ 'series_arrays', 'dimension_arrays', 'dimension_arrays_processed', + 'streaming', ]; diff --git a/packages/layerchart/src/routes/docs/performance/dimension_arrays/+page.svelte b/packages/layerchart/src/routes/docs/performance/dimension_arrays/+page.svelte index fd0dfceed..d66894cc8 100644 --- a/packages/layerchart/src/routes/docs/performance/dimension_arrays/+page.svelte +++ b/packages/layerchart/src/routes/docs/performance/dimension_arrays/+page.svelte @@ -10,8 +10,10 @@ const { data } = $props(); let example = $state<'single'>('single'); + let renderContext = $state<'svg' | 'canvas'>('svg'); let motion = $state(true); + let show = $state(true); let chartProps = $derived['props']>({ xAxis: { format: (v) => format(new Date(v)) }, @@ -25,7 +27,7 @@
-
+
Svg @@ -39,6 +41,13 @@ No + + + + Yes + No + +
@@ -51,15 +60,17 @@ {#if example === 'single'}
- d[0]} - y={(d) => d[1]} - props={chartProps} - brush - {renderContext} - profile - /> + {#if show} + d[0]} + y={(d) => d[1]} + props={chartProps} + brush + {renderContext} + profile + /> + {/if}
{:else if example === 'series'} @@ -72,31 +83,33 @@ }} >
- d[0]} - y={(d) => d[1]} - series={[ - { - key: 'cpu', - data: zip(data.chartData.date, data.chartData.cpu), - color: 'var(--color-danger)', - }, - { - key: 'ram', - data: zip(data.chartData.date, data.chartData.ram), - color: 'var(--color-warning)', - }, - { - key: 'tcp', - data: zip(data.chartData.date, data.chartData.tcp), - color: 'var(--color-success)', - }, - ]} - props={chartProps} - brush - {renderContext} - profile - /> + {#if show} + d[0]} + y={(d) => d[1]} + series={[ + { + key: 'cpu', + data: zip(data.chartData.date, data.chartData.cpu), + color: 'var(--color-danger)', + }, + { + key: 'ram', + data: zip(data.chartData.date, data.chartData.ram), + color: 'var(--color-warning)', + }, + { + key: 'tcp', + data: zip(data.chartData.date, data.chartData.tcp), + color: 'var(--color-success)', + }, + ]} + props={chartProps} + brush + {renderContext} + profile + /> + {/if}
{/if} diff --git a/packages/layerchart/src/routes/docs/performance/dimension_arrays_processed/+page.svelte b/packages/layerchart/src/routes/docs/performance/dimension_arrays_processed/+page.svelte index abaaf60f3..d1c627c57 100644 --- a/packages/layerchart/src/routes/docs/performance/dimension_arrays_processed/+page.svelte +++ b/packages/layerchart/src/routes/docs/performance/dimension_arrays_processed/+page.svelte @@ -10,8 +10,10 @@ const { data } = $props(); let example = $state<'single'>('single'); + let renderContext = $state<'svg' | 'canvas'>('svg'); let motion = $state(true); + let show = $state(true); let chartProps = $derived['props']>({ xAxis: { format: (v) => format(new Date(v)) }, @@ -31,7 +33,7 @@
-
+
Svg @@ -45,6 +47,13 @@ No + + + + Yes + No + +
@@ -57,45 +66,49 @@ {#if example === 'single'}
- d[0]} - y={(d) => d[1]} - props={chartProps} - brush - {renderContext} - profile - /> + {#if show} + d[0]} + y={(d) => d[1]} + props={chartProps} + brush + {renderContext} + profile + /> + {/if}
{:else if example === 'series'}
- d[0]} - y={(d) => d[1]} - series={[ - { - key: 'cpu', - data: chartData.cpu, - color: 'var(--color-danger)', - }, - { - key: 'ram', - data: chartData.ram, - color: 'var(--color-warning)', - }, - { - key: 'tcp', - data: chartData.tcp, - color: 'var(--color-success)', - }, - ]} - props={chartProps} - brush - {renderContext} - profile - /> + {#if show} + d[0]} + y={(d) => d[1]} + series={[ + { + key: 'cpu', + data: chartData.cpu, + color: 'var(--color-danger)', + }, + { + key: 'ram', + data: chartData.ram, + color: 'var(--color-warning)', + }, + { + key: 'tcp', + data: chartData.tcp, + color: 'var(--color-success)', + }, + ]} + props={chartProps} + brush + {renderContext} + profile + /> + {/if}
{/if} diff --git a/packages/layerchart/src/routes/docs/performance/series_arrays/+page.svelte b/packages/layerchart/src/routes/docs/performance/series_arrays/+page.svelte index 53857c34b..7948ddbac 100644 --- a/packages/layerchart/src/routes/docs/performance/series_arrays/+page.svelte +++ b/packages/layerchart/src/routes/docs/performance/series_arrays/+page.svelte @@ -9,8 +9,10 @@ const { data } = $props(); let example = $state<'single'>('single'); + let renderContext = $state<'svg' | 'canvas'>('svg'); let motion = $state(true); + let show = $state(true); let chartProps = $derived['props']>({ xAxis: { format: (v) => format(new Date(v)) }, @@ -24,7 +26,7 @@
-
+
Svg @@ -38,6 +40,13 @@ No + + + + Yes + No + +
@@ -50,33 +59,37 @@ {#if example === 'single'}
- + {#if show} + + {/if}
{:else if example === 'series'}
- + {#if show} + + {/if}
{/if} diff --git a/packages/layerchart/src/routes/docs/performance/streaming/+page.svelte b/packages/layerchart/src/routes/docs/performance/streaming/+page.svelte new file mode 100644 index 000000000..2afac6748 --- /dev/null +++ b/packages/layerchart/src/routes/docs/performance/streaming/+page.svelte @@ -0,0 +1,179 @@ + + +
+
+ + + Svg + Canvas + + + + + + Yes + No + + + + + + Yes + No + + +
+ +
+ + + + + + + + + +
+ +
+ {#if show} + + {/if} +
+ + data: {format(chartData.length)} points +
diff --git a/packages/layerchart/src/routes/docs/performance/wide_data/+page.svelte b/packages/layerchart/src/routes/docs/performance/wide_data/+page.svelte index a3971fd13..458cb5e27 100644 --- a/packages/layerchart/src/routes/docs/performance/wide_data/+page.svelte +++ b/packages/layerchart/src/routes/docs/performance/wide_data/+page.svelte @@ -12,6 +12,8 @@ let renderContext = $state<'svg' | 'canvas'>('svg'); let motion = $state(true); + let show = $state(true); + let chartProps = $derived['props']>({ xAxis: { format: (v) => format(new Date(v * 60 * 1000)) }, yAxis: { format: 'metric' }, @@ -24,7 +26,7 @@
-
+
Svg @@ -38,6 +40,13 @@ No + + + + Yes + No + +
@@ -50,37 +59,41 @@ {#if example === 'single'}
- 100 - d.idl} - props={chartProps} - brush - {renderContext} - profile - /> + {#if show} + 100 - d.idl} + props={chartProps} + brush + {renderContext} + profile + /> + {/if}
{:else if example === 'series'}
- 100 - d.idl, color: 'var(--color-danger)' }, - { - key: 'ram', - value: (d) => (100 * d.writ) / (d.writ + d.used), - color: 'var(--color-warning)', - }, - { key: 'tcp', value: (d) => d.send, color: 'var(--color-success)' }, - ]} - props={chartProps} - brush - {renderContext} - profile - /> + {#if show} + 100 - d.idl, color: 'var(--color-danger)' }, + { + key: 'ram', + value: (d) => (100 * d.writ) / (d.writ + d.used), + color: 'var(--color-warning)', + }, + { key: 'tcp', value: (d) => d.send, color: 'var(--color-success)' }, + ]} + props={chartProps} + brush + {renderContext} + profile + /> + {/if}
{/if} diff --git a/packages/layerchart/src/routes/docs/performance/wide_data_processed/+page.svelte b/packages/layerchart/src/routes/docs/performance/wide_data_processed/+page.svelte index 4e03b51e2..ff0aa5817 100644 --- a/packages/layerchart/src/routes/docs/performance/wide_data_processed/+page.svelte +++ b/packages/layerchart/src/routes/docs/performance/wide_data_processed/+page.svelte @@ -12,6 +12,8 @@ let renderContext = $state<'svg' | 'canvas'>('svg'); let motion = $state(true); + let show = $state(true); + let chartProps = $derived['props']>({ xAxis: { format: PeriodType.Day }, yAxis: { format: 'metric' }, @@ -32,7 +34,7 @@
-
+
Svg @@ -46,6 +48,13 @@ No + + + + Yes + No + +
@@ -58,36 +67,40 @@ {#if example === 'single'}
- + {#if show} + + {/if}
{:else if example === 'series'}
- + {#if show} + + {/if}
{/if}