From 31e28ad74c955fe7ea07ee87fc2b0a0bde486b5a Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 20 Jun 2025 21:23:09 -0400 Subject: [PATCH 1/8] BrushState WIP --- .../src/lib/components/BrushContext.svelte | 427 ++++++++++-------- .../src/lib/components/Chart.svelte | 6 +- .../lib/components/charts/AreaChart.svelte | 4 +- .../src/lib/components/charts/BarChart.svelte | 11 +- .../lib/components/charts/LineChart.svelte | 4 +- .../lib/components/charts/ScatterChart.svelte | 8 +- .../docs/components/BrushContext/+page.svelte | 126 ++++-- 7 files changed, 355 insertions(+), 231 deletions(-) diff --git a/packages/layerchart/src/lib/components/BrushContext.svelte b/packages/layerchart/src/lib/components/BrushContext.svelte index b31e2404c..3d30971a2 100644 --- a/packages/layerchart/src/lib/components/BrushContext.svelte +++ b/packages/layerchart/src/lib/components/BrushContext.svelte @@ -1,7 +1,32 @@ @@ -153,13 +181,16 @@ const ctx = getChartContext(); let { + // xDomain, + // yDomain, + x, + y, brushContext: brushContextProp = $bindable(), + axis = 'x', handleSize = 5, resetOnEnd = false, ignoreResetClick = false, - xDomain: xDomain, - yDomain: yDomain, mode = 'integrated', disabled = false, range = {}, @@ -174,89 +205,106 @@ let rootEl = $state(); - if (xDomain === undefined) { - xDomain = ctx.xScale.domain(); - } - if (yDomain === undefined) { - yDomain = ctx.yScale.domain(); - } - - $effect.pre(() => { - if (xDomain !== undefined) return; - xDomain = ctx.xScale.domain(); - }); - - $effect.pre(() => { - if (yDomain !== undefined) return; - yDomain = ctx.yScale.domain(); - }); - - const ogXDomain = xDomain; - const ogYDomain = yDomain; - const originalXDomain = ctx.config.xDomain; - const originalYDomain = ctx.config.yDomain; - - const xDomainMinMax = $derived(extent(ctx.xScale.domain()) as [number, number]); - const xDomainMin = $derived(xDomainMinMax[0]); - const xDomainMax = $derived(xDomainMinMax[1]); - - const yDomainMinMax = $derived(extent(ctx.yScale.domain()) as [number, number]); - const yDomainMin = $derived(yDomainMinMax[0]); - const yDomainMax = $derived(yDomainMinMax[1]); - - const top = $derived(ctx.yScale(yDomain?.[1])); - const bottom = $derived(ctx.yScale(yDomain?.[0])); - const left = $derived(ctx.xScale(xDomain?.[0])); - const right = $derived(ctx.xScale(xDomain?.[1])); - - const _range = $derived({ - x: axis === 'both' || axis === 'x' ? left : 0, - y: axis === 'both' || axis === 'y' ? top : 0, - width: axis === 'both' || axis === 'x' ? right - left : ctx.width, - height: axis === 'both' || axis === 'y' ? bottom - top : ctx.height, + const brushState = new BrushState({ x, y }); + + // if (xDomain === undefined) { + // xDomain = ctx.xScale.domain(); + // } + // if (yDomain === undefined) { + // yDomain = ctx.yScale.domain(); + // } + + // $effect.pre(() => { + // if (xDomain !== undefined) return; + // xDomain = ctx.xScale.domain(); + // }); + + // $effect.pre(() => { + // if (yDomain !== undefined) return; + // yDomain = ctx.yScale.domain(); + // }); + + // const ogXDomain = xDomain; + // const ogYDomain = yDomain; + // const originalXDomain = ctx.config.xDomain; + // const originalYDomain = ctx.config.yDomain; + + // const xDomainMinMax = $derived(extent(ctx.xScale.domain()) as [number, number]); + // const xDomainMin = $derived(xDomainMinMax[0]); + // const xDomainMax = $derived(xDomainMinMax[1]); + const [xDomainMin, xDomainMax] = $derived(ctx.xScale.domain()); + + // const yDomainMinMax = $derived(extent(ctx.yScale.domain()) as [number, number]); + // const yDomainMin = $derived(yDomainMinMax[0]); + // const yDomainMax = $derived(yDomainMinMax[1]); + const [yDomainMin, yDomainMax] = $derived(ctx.yScale.domain()); + + const top = $derived(ctx.yScale(brushState.y?.[1])); + const bottom = $derived(ctx.yScale(brushState.y?.[0])); + const left = $derived(ctx.xScale(brushState.x?.[0])); + const right = $derived(ctx.xScale(brushState.x?.[1])); + + $effect(() => { + brushState.range = { + x: axis === 'both' || axis === 'x' ? left : 0, + y: axis === 'both' || axis === 'y' ? top : 0, + width: axis === 'both' || axis === 'x' ? right - left : ctx.width, + height: axis === 'both' || axis === 'y' ? bottom - top : ctx.height, + }; + brushState.handleSize = handleSize; }); - let isActive = $state(false); - - const brushContext = { - get xDomain() { - return xDomain!; - }, - set xDomain(v: DomainType) { - xDomain = v; - }, - get yDomain() { - return yDomain!; - }, - set yDomain(v: DomainType) { - yDomain = v; - }, - get isActive() { - return isActive; - }, - set isActive(v: boolean) { - isActive = v; - }, - get range() { - return _range; - }, - get handleSize() { - return handleSize; - }, - }; - - brushContextProp = brushContext; - - setBrushContext(brushContext); + // const _range = $derived({ + // x: axis === 'both' || axis === 'x' ? left : 0, + // y: axis === 'both' || axis === 'y' ? top : 0, + // width: axis === 'both' || axis === 'x' ? right - left : ctx.width, + // height: axis === 'both' || axis === 'y' ? bottom - top : ctx.height, + // }); + + // let isActive = $state(false); + + // const brushContext = { + // get xDomain() { + // return xDomain!; + // }, + // set xDomain(v: DomainType) { + // xDomain = v; + // }, + // get yDomain() { + // return yDomain!; + // }, + // set yDomain(v: DomainType) { + // yDomain = v; + // }, + // get isActive() { + // return isActive; + // }, + // set isActive(v: boolean) { + // isActive = v; + // }, + // get range() { + // return _range; + // }, + // get handleSize() { + // return handleSize; + // }, + // }; + + // brushContextProp = brushContext; + brushContextProp = brushState; + + // setBrushContext(brushContext); + setBrushContext(brushState); const logger = new Logger('BrushContext'); const RESET_THRESHOLD = 1; // size of pointer delta to ignore function handler( + /** Callback on pointer move */ fn: ( start: { - xDomain: [number, number]; - yDomain: [number, number]; + x: DomainType; + y: DomainType; value: { x: number; y: number }; }, value: { x: number; y: number } @@ -284,15 +332,21 @@ } const start = { - xDomain: [xDomain?.[0] ?? xDomainMin, xDomain?.[1] ?? xDomainMax] as [number, number], - yDomain: [yDomain?.[0] ?? yDomainMin, yDomain?.[1] ?? yDomainMax] as [number, number], + x: [ + brushState.x[0] ?? ctx.xScale.domain()[0], + brushState.x[1] ?? ctx.xScale.domain()[1], + ] as DomainType, + y: [ + brushState.y[0] ?? ctx.yScale.domain()[0], + brushState.y[1] ?? ctx.yScale.domain()[1], + ] as DomainType, value: { x: scaleInvert(ctx.xScale, startPoint?.x ?? 0), y: scaleInvert(ctx.yScale, startPoint?.y ?? 0), }, }; - onBrushStart({ xDomain, yDomain }); + onBrushStart({ brush: brushState }); const onPointerMove = (e: PointerEvent) => { const currentPoint = localPoint(e, rootEl); @@ -301,7 +355,7 @@ y: scaleInvert(ctx.yScale, currentPoint?.y ?? 0), }); - onChange({ xDomain, yDomain }); + onChange({ brush: brushState }); }; const onPointerUp = (e: PointerEvent) => { @@ -316,8 +370,8 @@ if ( (isClickOutside && xPointDelta < RESET_THRESHOLD && yPointDelta < RESET_THRESHOLD) || - _range.width < RESET_THRESHOLD || - _range.height < RESET_THRESHOLD + brushState.range.width < RESET_THRESHOLD || + brushState.range.height < RESET_THRESHOLD ) { // Clicked on frame, or pointer delta was less than threshold (default: 1px) if (ignoreResetClick) { @@ -325,24 +379,24 @@ } else { logger.debug('resetting due to frame click'); reset(); - onChange({ xDomain, yDomain }); + onChange({ brush: brushState }); } } else { logger.debug('drag end', { target: e.target, xPointDelta, yPointDelta, - rangeWidth: _range.width, - rangeHeight: _range.height, + rangeWidth: brushState.range.width, + rangeHeight: brushState.range.height, }); } - onBrushEnd({ xDomain, yDomain }); + onBrushEnd({ brush: brushState }); if (resetOnEnd) { if (ignoreResetClick) { // Still hide brush, but do not reset domain - brushContext.isActive = false; + brushState.active = false; } else { reset(); } @@ -359,9 +413,9 @@ const createRange = handler((start, value) => { logger.debug('createRange'); - brushContext.isActive = true; + brushState.active = true; - xDomain = [ + brushState.x = [ // @ts-expect-error clamp(min([start.value.x, value.x]), xDomainMin, xDomainMax), // @ts-expect-error @@ -369,7 +423,7 @@ ]; // xDomain = [start.value.x, value.x]; - yDomain = [ + brushState.y = [ // @ts-expect-error clamp(min([start.value.y, value.y]), yDomainMin, yDomainMax), // @ts-expect-error @@ -379,89 +433,84 @@ const adjustRange = handler((start, value) => { logger.debug('adjustRange'); - const dx = clamp( - value.x - start.value.x, - xDomainMin - start.xDomain[0], - xDomainMax - start.xDomain[1] - ); - xDomain = [add(start.xDomain[0], dx), add(start.xDomain[1], dx)]; - - const dy = clamp( - value.y - start.value.y, - yDomainMin - start.yDomain[0], - yDomainMax - start.yDomain[1] - ); - yDomain = [add(start.yDomain[0], dy), add(start.yDomain[1], dy)]; + const dx = clamp(value.x - start.value.x, xDomainMin - start.x[0], xDomainMax - start.x[1]); + brushState.x = [add(start.x[0], dx), add(start.x[1], dx)]; + + const dy = clamp(value.y - start.value.y, yDomainMin - start.y[0], yDomainMax - start.y[1]); + brushState.y = [add(start.y[0], dy), add(start.y[1], dy)]; }); const adjustTop = handler((start, value) => { logger.debug('adjustTop'); - yDomain = [ - clamp(value.y < start.yDomain[0] ? value.y : start.yDomain[0], yDomainMin, yDomainMax), - clamp(value.y < start.yDomain[0] ? start.yDomain[0] : value.y, yDomainMin, yDomainMax), + brushState.y = [ + clamp(value.y < start.y[0] ? value.y : start.y[0], yDomainMin, yDomainMax), + clamp(value.y < start.y[0] ? start.y[0] : value.y, yDomainMin, yDomainMax), ]; }); const adjustBottom = handler((start, value) => { logger.debug('adjustBottom'); - yDomain = [ - clamp(value.y > start.yDomain[1] ? start.yDomain[1] : value.y, yDomainMin, yDomainMax), - clamp(value.y > start.yDomain[1] ? value.y : start.yDomain[1], yDomainMin, yDomainMax), + brushState.y = [ + clamp(value.y > start.y[1] ? start.y[1] : value.y, yDomainMin, yDomainMax), + clamp(value.y > start.y[1] ? value.y : start.y[1], yDomainMin, yDomainMax), ]; }); const adjustLeft = handler((start, value) => { logger.debug('adjustLeft'); - xDomain = [ - clamp(value.x > start.xDomain[1] ? start.xDomain[1] : value.x, xDomainMin, xDomainMax), - clamp(value.x > start.xDomain[1] ? value.x : start.xDomain[1], xDomainMin, xDomainMax), + brushState.x = [ + clamp(value.x > start.x[1] ? start.x[1] : value.x, xDomainMin, xDomainMax), + clamp(value.x > start.x[1] ? value.x : start.x[1], xDomainMin, xDomainMax), ]; }); const adjustRight = handler((start, value) => { logger.debug('adjustRight'); - xDomain = [ - clamp(value.x < start.xDomain[0] ? value.x : start.xDomain[0], xDomainMin, xDomainMax), - clamp(value.x < start.xDomain[0] ? start.xDomain[0] : value.x, xDomainMin, xDomainMax), + brushState.x = [ + clamp(value.x < start.x[0] ? value.x : start.x[0], xDomainMin, xDomainMax), + clamp(value.x < start.x[0] ? start.x[0] : value.x, xDomainMin, xDomainMax), ]; }); function reset() { logger.debug('reset'); - brushContext.isActive = false; + brushState.active = false; - onReset({ xDomain, yDomain }); + onReset({ brush: brushState }); - xDomain = ogXDomain; - yDomain = ogYDomain; + // xDomain = ogXDomain; + // yDomain = ogYDomain; + // brushState.x = [ctx.xScale.domain()[0], ctx.xScale.domain()[1]]; + // brushState.y = [ctx.yScale.domain()[0], ctx.yScale.domain()[1]]; + brushState.x = [null, null]; + brushState.y = [null, null]; } function selectAll() { logger.debug('selectedAll'); - xDomain = [xDomainMin, xDomainMax]; - yDomain = [yDomainMin, yDomainMax]; + brushState.x = [xDomainMin, xDomainMax]; + brushState.y = [yDomainMin, yDomainMax]; } $effect.pre(() => { if (mode === 'separated') { // Set reactively to handle cases where xDomain/yDomain are set externally (ex. `bind:xDomain`) - const isXAxisActive = - xDomain?.[0]?.valueOf() !== originalXDomain?.[0]?.valueOf() || - xDomain?.[1]?.valueOf() !== originalXDomain?.[1]?.valueOf(); - - const isYAxisActive = - yDomain?.[0]?.valueOf() !== originalYDomain?.[0]?.valueOf() || - yDomain?.[1]?.valueOf() !== originalYDomain?.[1]?.valueOf(); - - const result = - axis === 'x' ? isXAxisActive : axis == 'y' ? isYAxisActive : isXAxisActive || isYAxisActive; - brushContext.isActive = result; + // TODO: Update + // const isXAxisActive = + // brushState.x[0]?.valueOf() !== originalXDomain?.[0]?.valueOf() || + // brushState.x[1]?.valueOf() !== originalXDomain?.[1]?.valueOf(); + // const isYAxisActive = + // brushState.y[0]?.valueOf() !== originalYDomain?.[0]?.valueOf() || + // brushState.y[1]?.valueOf() !== originalYDomain?.[1]?.valueOf(); + // const result = + // axis === 'x' ? isXAxisActive : axis == 'y' ? isYAxisActive : isXAxisActive || isYAxisActive; + // brushState.active = result; } }); {#if disabled} - {@render children?.({ brushContext })} + {@render children?.({ brushContext: brushState })} {:else} {@const handleClass = layerClass('brush-handle')} @@ -482,16 +531,16 @@ style:width="{ctx.containerWidth}px" style:height="{ctx.containerHeight}px" > - {@render children?.({ brushContext })} + {@render children?.({ brushContext: brushState })} - {#if brushContext.isActive} + {#if brushState.active}
{ e.stopPropagation(); - if (yDomain) { - yDomain[0] = yDomainMin; - onChange({ xDomain, yDomain }); + if (brushState.y[0]) { + brushState.y[0] = ctx.yScale.domain()[0]; + onChange({ brush: brushState }); } }} >
{ e.stopPropagation(); - if (yDomain) { - yDomain[1] = yDomainMax; - onChange({ xDomain, yDomain }); + if (brushState.y[1]) { + brushState.y[1] = ctx.yScale.domain()[1]; + onChange({ brush: brushState }); } }} >
@@ -559,10 +608,10 @@ {#if axis === 'both' || axis === 'x'}
{ e.stopPropagation(); - if (xDomain) { - xDomain[0] = xDomainMin; - onChange({ xDomain, yDomain }); + if (brushState.x[0]) { + brushState.x[0] = ctx.xScale.domain()[0]; + onChange({ brush: brushState }); } }} >
@@ -585,9 +634,9 @@
{ e.stopPropagation(); - if (xDomain) { - xDomain[1] = xDomainMax; - onChange({ xDomain: xDomain, yDomain: yDomain }); + if (brushState.x[1]) { + brushState.x[1] = ctx.xScale.domain()[1]; + onChange({ brush: brushState }); } }} >
diff --git a/packages/layerchart/src/lib/components/Chart.svelte b/packages/layerchart/src/lib/components/Chart.svelte index ffc617384..7d824edf4 100644 --- a/packages/layerchart/src/lib/components/Chart.svelte +++ b/packages/layerchart/src/lib/components/Chart.svelte @@ -38,7 +38,7 @@ import { unique } from '@layerstack/utils'; import { geoFitObjectTransform } from '$lib/utils/geo.js'; import TransformContext, { type TransformContextValue } from './TransformContext.svelte'; - import BrushContext, { type BrushContextValue } from './BrushContext.svelte'; + import BrushContext, { type BrushState } from './BrushContext.svelte'; import { layerClass } from '$lib/utils/attributes.js'; const defaultPadding = { top: 0, right: 0, bottom: 0, left: 0 }; @@ -152,7 +152,7 @@ radial: boolean; tooltip: TooltipContextValue; geo: GeoContextValue; - brush: BrushContextValue; + brush: BrushState; transform: TransformContextValue; }; @@ -1049,7 +1049,7 @@ let geoContext = $state(null!); let transformContext = $state(null!); let tooltipContext = $state(null!); - let brushContext = $state(null!); + let brushContext = $state(null!); const context: ChartContextValue = { get activeGetters() { diff --git a/packages/layerchart/src/lib/components/charts/AreaChart.svelte b/packages/layerchart/src/lib/components/charts/AreaChart.svelte index 2e2636f81..a94385e91 100644 --- a/packages/layerchart/src/lib/components/charts/AreaChart.svelte +++ b/packages/layerchart/src/lib/components/charts/AreaChart.svelte @@ -452,10 +452,10 @@ ? { axis: 'x', resetOnEnd: true, - xDomain, + x: xDomain, ...brushProps, onBrushEnd: (e) => { - xDomain = e.xDomain; + xDomain = e.brush.x; brushProps.onBrushEnd?.(e); }, } diff --git a/packages/layerchart/src/lib/components/charts/BarChart.svelte b/packages/layerchart/src/lib/components/charts/BarChart.svelte index a43652b7e..e2ad87867 100644 --- a/packages/layerchart/src/lib/components/charts/BarChart.svelte +++ b/packages/layerchart/src/lib/components/charts/BarChart.svelte @@ -460,11 +460,18 @@ ? { axis: 'x', resetOnEnd: true, - xDomain, + x: xDomain, ...brushProps, onBrushEnd: (e) => { // TOOD: This should set xRange instead of xDomain, and/or xDomain should be all values, not just bounds of brush range - xDomain = e.xDomain; + // const values = context?.xScale.domain() ?? []; + // console.log('domain', values, e.xDomain); + // const i0 = values?.indexOf(e.xDomain[0]); + // const i1 = values?.indexOf(e.xDomain[1]); + // xDomain = values.slice(i0, i1); + + xDomain = e.brush.x; + brushProps.onBrushEnd?.(e); }, } diff --git a/packages/layerchart/src/lib/components/charts/LineChart.svelte b/packages/layerchart/src/lib/components/charts/LineChart.svelte index de1944013..e382a3aa4 100644 --- a/packages/layerchart/src/lib/components/charts/LineChart.svelte +++ b/packages/layerchart/src/lib/components/charts/LineChart.svelte @@ -352,10 +352,10 @@ ? { axis: 'x', resetOnEnd: true, - xDomain, + x: xDomain, ...brushProps, onBrushEnd: (e) => { - xDomain = e.xDomain; + xDomain = e.brush.x; brushProps.onBrushEnd?.(e); }, } diff --git a/packages/layerchart/src/lib/components/charts/ScatterChart.svelte b/packages/layerchart/src/lib/components/charts/ScatterChart.svelte index 2516c491e..deec38267 100644 --- a/packages/layerchart/src/lib/components/charts/ScatterChart.svelte +++ b/packages/layerchart/src/lib/components/charts/ScatterChart.svelte @@ -262,12 +262,12 @@ ? { axis: 'both', resetOnEnd: true, - xDomain, - yDomain, + x: xDomain, + y: yDomain, ...brushProps, onBrushEnd: (e) => { - xDomain = e.xDomain; - yDomain = e.yDomain; + xDomain = e.brush.x; + yDomain = e.brush.y; brushProps.onBrushEnd?.(e); }, } diff --git a/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte b/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte index 4cb84c8e7..14f3068a8 100644 --- a/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte @@ -2,8 +2,8 @@ import { scaleBand, scaleOrdinal, scaleTime } from 'd3-scale'; import { range } from 'd3-array'; import { timeDay } from 'd3-time'; - import { State } from 'svelte-ux'; - import { format } from '@layerstack/utils'; + import { Button, State } from 'svelte-ux'; + import { endOfInterval, format } from '@layerstack/utils'; import { cls } from '@layerstack/tailwind'; import { mdiChevronLeft, mdiChevronRight } from '@mdi/js'; @@ -66,21 +66,90 @@ value: 'integer', keys: ['value', 'baseline'], }); + + let xDomain2 = $state([null, null]);

Examples

- +

Basic

@@ -145,7 +214,7 @@ - {#if context.brush.isActive} + {#if context.brush.active} - {#if context.brush.isActive} + {#if context.brush.active} @@ -234,11 +303,11 @@ - {#if context.brush.isActive} + {#if context.brush.active} @@ -276,7 +345,7 @@ resetOnEnd: true, onBrushEnd: (e) => { // @ts-expect-error - set(e.xDomain); + set(e.brush.x); }, }} > @@ -315,7 +384,7 @@ resetOnEnd: true, onBrushEnd: (e) => { // @ts-expect-error - set(e.yDomain); + set(e.brush.y); }, }} > @@ -356,9 +425,9 @@ onBrushEnd: (e) => { set({ // @ts-expect-error - xDomain: e.xDomain, + xDomain: e.brush.x, // @ts-expect-error - yDomain: e.yDomain, + yDomain: e.brush.y, }); }, }} @@ -419,7 +488,7 @@ brush={{ onChange: (e) => { // @ts-expect-error - set(e.xDomain); + set(e.brush.x); }, }} > @@ -448,7 +517,7 @@ axis: 'y', onChange: (e) => { // @ts-expect-error - set(e.yDomain); + set(e.brush.y); }, }} > @@ -533,7 +602,7 @@ brush={{ onChange: (e) => { // @ts-expect-error - set(e.xDomain); + set(e.brush.x); }, }} > @@ -596,8 +665,8 @@ padding={{ left: 16 }} brush={{ mode: 'separated', - xDomain, - onChange: (e) => (xDomain = e.xDomain), + x: xDomain, + onChange: (e) => (xDomain = e.brush.x), onReset: (e) => (xDomain = null), }} > @@ -606,7 +675,6 @@ line={{ class: 'stroke-2 stroke-(--chart-color)' }} class="fill-(--chart-color) opacity-20" /> - @@ -634,7 +702,7 @@ resetOnEnd: true, onBrushEnd: (e) => { // @ts-expect-error - set(e.xDomain); + set(e.brush.x); }, }} > @@ -700,9 +768,9 @@ onChange: (e) => { set({ // @ts-expect-error - xDomain: e.xDomain, + xDomain: e.brush.x, // @ts-expect-error - yDomain: e.yDomain, + yDomain: e.brush.y, }); }, }} @@ -759,9 +827,9 @@ onBrushEnd: (e) => { set({ // @ts-expect-error - xDomain: e.xDomain, + xDomain: e.brush.x, // @ts-expect-error - yDomain: e.yDomain, + yDomain: e.brush.y, }); }, }} @@ -791,9 +859,9 @@ onChange: (e) => { set({ // @ts-expect-error - xDomain: e.xDomain, + xDomain: e.brush.x, // @ts-expect-error - yDomain: e.yDomain, + yDomain: e.brush.y, }); }, }} From d3947458d1e4eefb3391417cc0480f35dac2c478 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Thu, 11 Sep 2025 07:52:45 -0400 Subject: [PATCH 2/8] Add missing import after merge --- .../src/routes/docs/components/BrushContext/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte b/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte index b182ad309..735be6126 100644 --- a/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte @@ -1,5 +1,5 @@ @@ -48,7 +48,6 @@ import { cls } from '@layerstack/tailwind'; import Axis from '../Axis.svelte'; - import BrushContext from '../BrushContext.svelte'; import Chart from '../Chart.svelte'; import ChartAnnotations from './ChartAnnotations.svelte'; import ChartClipPath from '../ChartClipPath.svelte'; @@ -61,9 +60,10 @@ import Rule from '../Rule.svelte'; import * as Tooltip from '../tooltip/index.js'; - import { accessor, chartDataArray, defaultChartPadding } from '../../utils/common.js'; + import { chartDataArray, defaultChartPadding } from '../../utils/common.js'; import { asAny } from '../../utils/types.js'; import { createLegendProps, SeriesState } from './utils.svelte.js'; + import type { BrushDomainType } from '../BrushContext.svelte'; let { data = [], @@ -246,8 +246,8 @@ ? { axis: 'both', resetOnEnd: true, - x: xDomain, - y: yDomain, + x: xDomain as BrushDomainType, + y: yDomain as BrushDomainType, ...brushProps, onBrushEnd: (e) => { xDomain = e.brush.x; diff --git a/packages/layerchart/src/lib/components/charts/types.ts b/packages/layerchart/src/lib/components/charts/types.ts index e3120fc1f..913f8f881 100644 --- a/packages/layerchart/src/lib/components/charts/types.ts +++ b/packages/layerchart/src/lib/components/charts/types.ts @@ -137,12 +137,6 @@ export type BaseChartProps< */ y?: Accessor; - xScale?: AnyScale; - /** - * The x domain to be used for the chart. - * - */ - xDomain?: ComponentProps['xDomain']; /** * Use radial instead of cartesian coordinates, mapping `x` to `angle` and `y`` to * radial. Radial lines are positioned relative to the origin, use transform @@ -151,18 +145,21 @@ export type BaseChartProps< * @default false */ radial?: boolean; + /** * The series data to be used for the chart. * * @default [{ key: 'default', value: y, color: 'var(--color-primary)' }] */ series?: SeriesData[]; + /** * The layout of the series. * * @default 'overlap' */ seriesLayout?: 'overlap' | 'stack' | 'stackExpand' | 'stackDiverging'; + /** * The axis to be used for the chart. * @@ -174,6 +171,7 @@ export type BaseChartProps< | 'y' | boolean | SimplifiedChartSnippet; + /** * The brush to be used for the chart. * @@ -194,6 +192,7 @@ export type BaseChartProps< * @default false */ labels?: ComponentProps> | boolean | ChartSnippet; + /** * The legend to be used for the chart. * diff --git a/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte b/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte index 98dbed674..99ba2add5 100644 --- a/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte @@ -1289,7 +1289,7 @@ x="date" y="value" {xDomain} - brush={{ onBrushEnd: (e) => (xDomain = e.xDomain) }} + brush={{ onBrushEnd: (e) => (xDomain = e.brush.x) }} props={{ area: { motion: { type: 'tween', duration: 200 } }, xAxis: { motion: { type: 'tween', duration: 200 }, tickMultiline: true }, @@ -1304,8 +1304,7 @@ data={denseDateSeriesData2} x="date" y="value" - {xDomain} - brush={{ onBrushEnd: (e) => (xDomain = e.xDomain) }} + brush={{ onBrushEnd: (e) => (xDomain = e.brush.x) }} props={{ area: { motion: { type: 'tween', duration: 200 } }, xAxis: { motion: { type: 'tween', duration: 200 }, tickMultiline: true }, diff --git a/packages/layerchart/src/routes/docs/components/Axis/+page.svelte b/packages/layerchart/src/routes/docs/components/Axis/+page.svelte index 405bbeebf..3c5ca0d88 100644 --- a/packages/layerchart/src/routes/docs/components/Axis/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Axis/+page.svelte @@ -848,7 +848,7 @@ brush={{ resetOnEnd: true, onBrushEnd: (e) => { - xDomain = asAny(e.xDomain); + xDomain = asAny(e.brush.x); }, }} > @@ -865,9 +865,9 @@ padding={{ top: 20, bottom: 20, left: 20, right: 20 }} brush={{ mode: 'separated', - xDomain, + x: xDomain, onChange: (e) => { - xDomain = asAny(e.xDomain); + xDomain = asAny(e.brush.x); }, }} > @@ -901,7 +901,7 @@ brush={{ resetOnEnd: true, onBrushEnd: (e) => { - xDomain = asAny(e.xDomain); + xDomain = asAny(e.brush.x); }, }} > @@ -918,9 +918,9 @@ padding={{ top: 20, bottom: 20, left: 20, right: 20 }} brush={{ mode: 'separated', - xDomain, + x: xDomain, onChange: (e) => { - xDomain = asAny(e.xDomain); + xDomain = asAny(e.brush.x); }, }} > diff --git a/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte b/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte index 735be6126..9f59032a0 100644 --- a/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte @@ -28,15 +28,13 @@ import Preview from '$lib/docs/Preview.svelte'; import { createDateSeries, randomWalk } from '$lib/utils/genData.js'; import { asAny } from '$lib/utils/types.js'; - import type { DomainType } from '$lib/utils/scales.svelte.js'; + import type { BrushDomainType } from '$lib/components/BrushContext.svelte'; import { shared } from '../../shared.svelte.js'; let { data } = $props(); const now = new Date(); - let xDomain = $state([timeDay.offset(now, -60), timeDay.offset(now, -30)]) as - | DomainType - | undefined; + let xDomain = $state([timeDay.offset(now, -60), timeDay.offset(now, -30)]) as BrushDomainType; const seriesData = [ randomWalk({ count: 100 }).map((value, i) => ({ @@ -69,7 +67,7 @@ keys: ['value', 'baseline'], }); - let xDomain2 = $state([null, null]); + let xDomain2 = $state([null, null]);

Examples

@@ -114,6 +112,7 @@ {@const width = context.xScale(end) - x} {@const height = context.height} +
-

Band scale

+

Band scale (WIP)

@@ -638,7 +637,7 @@ mode: 'separated', x: xDomain, onChange: (e) => (xDomain = e.brush.x), - onReset: (e) => (xDomain = null), + onReset: (e) => (xDomain = [null, null]), }} > @@ -824,8 +823,8 @@ brush={{ axis: 'both', mode: 'separated', - xDomain: value?.xDomain, - yDomain: value?.yDomain, + x: value?.xDomain, + y: value?.yDomain, onChange: (e) => { set({ // @ts-expect-error diff --git a/packages/layerchart/src/routes/docs/examples/Candlestick/+page.svelte b/packages/layerchart/src/routes/docs/examples/Candlestick/+page.svelte index eb1623753..965dbbda9 100644 --- a/packages/layerchart/src/routes/docs/examples/Candlestick/+page.svelte +++ b/packages/layerchart/src/routes/docs/examples/Candlestick/+page.svelte @@ -108,7 +108,7 @@ yNice brush={{ onChange: (e) => { - xDomain = e.xDomain; + xDomain = e.brush.x; }, }} > From d9c45a4c3373bebff5cac60acf8668dc8e59976e Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 12 Sep 2025 08:15:07 -0400 Subject: [PATCH 4/8] Update @layerstack/utils with improved clamp() types. Fix remaining type warnings --- packages/layerchart/package.json | 2 +- .../src/lib/components/BrushContext.svelte | 35 +++++++++---------- packages/layerchart/src/lib/utils/types.ts | 10 ++++++ pnpm-lock.yaml | 16 +++++++-- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/packages/layerchart/package.json b/packages/layerchart/package.json index d9b9b5235..45ee414e1 100644 --- a/packages/layerchart/package.json +++ b/packages/layerchart/package.json @@ -84,7 +84,7 @@ "@layerstack/svelte-actions": "1.0.1-next.14", "@layerstack/svelte-state": "0.1.0-next.19", "@layerstack/tailwind": "2.0.0-next.17", - "@layerstack/utils": "2.0.0-next.14", + "@layerstack/utils": "2.0.0-next.15", "d3-array": "^3.2.4", "d3-color": "^3.1.0", "d3-delaunay": "^6.0.4", diff --git a/packages/layerchart/src/lib/components/BrushContext.svelte b/packages/layerchart/src/lib/components/BrushContext.svelte index 06d8c9d50..e4af0d9ea 100644 --- a/packages/layerchart/src/lib/components/BrushContext.svelte +++ b/packages/layerchart/src/lib/components/BrushContext.svelte @@ -1,4 +1,5 @@