- {@render children?.({ brushContext })}
+ {@render children?.({ brushContext: brushState })}
- {#if brushContext.isActive}
+ {#if brushState.active}
reset()}
@@ -498,36 +446,36 @@
{#if axis === 'both' || axis === 'y'}
{
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 });
}
}}
>
@@ -536,36 +484,36 @@
{#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 });
}
}}
>
{
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 479e555fb..f24ef2ebe 100644
--- a/packages/layerchart/src/lib/components/Chart.svelte
+++ b/packages/layerchart/src/lib/components/Chart.svelte
@@ -1,53 +1,28 @@
-{@render children({
- geoContext,
+{@render props.children({
+ geoState,
})}
diff --git a/packages/layerchart/src/lib/components/charts/AreaChart.svelte b/packages/layerchart/src/lib/components/charts/AreaChart.svelte
index da1b5122f..e201c832e 100644
--- a/packages/layerchart/src/lib/components/charts/AreaChart.svelte
+++ b/packages/layerchart/src/lib/components/charts/AreaChart.svelte
@@ -97,6 +97,7 @@
import { setTooltipMetaContext } from '../tooltip/tooltipMetaContext.js';
import DefaultTooltip from './DefaultTooltip.svelte';
import ChartAnnotations from './ChartAnnotations.svelte';
+ import type { BrushDomainType } from '../../states/brush.svelte.js';
import { getSettings } from '$lib/contexts/settings.js';
const settings = getSettings();
@@ -429,10 +430,10 @@
? {
axis: 'x',
resetOnEnd: true,
- xDomain,
+ x: xDomain as BrushDomainType,
...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 7395175d9..bfab6ef69 100644
--- a/packages/layerchart/src/lib/components/charts/BarChart.svelte
+++ b/packages/layerchart/src/lib/components/charts/BarChart.svelte
@@ -115,6 +115,7 @@
import DefaultTooltip from './DefaultTooltip.svelte';
import ChartAnnotations from './ChartAnnotations.svelte';
import { getSettings } from '$lib/contexts/settings.js';
+ import type { BrushDomainType } from '../../states/brush.svelte.js';
const settings = getSettings();
@@ -464,11 +465,18 @@
? {
axis: 'x',
resetOnEnd: true,
- xDomain,
+ x: xDomain as BrushDomainType,
...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 c4308318d..49229d956 100644
--- a/packages/layerchart/src/lib/components/charts/LineChart.svelte
+++ b/packages/layerchart/src/lib/components/charts/LineChart.svelte
@@ -104,6 +104,7 @@
import DefaultTooltip from './DefaultTooltip.svelte';
import ChartAnnotations from './ChartAnnotations.svelte';
import { isScaleTime } from '../../utils/scales.svelte.js';
+ import type { BrushDomainType } from '../../states/brush.svelte.js';
import { getSettings } from '$lib/contexts/settings.js';
const settings = getSettings();
@@ -348,10 +349,10 @@
? {
axis: 'x',
resetOnEnd: true,
- xDomain,
+ x: xDomain as BrushDomainType,
...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 be93cafe7..42df30ab2 100644
--- a/packages/layerchart/src/lib/components/charts/ScatterChart.svelte
+++ b/packages/layerchart/src/lib/components/charts/ScatterChart.svelte
@@ -38,8 +38,8 @@
'radial'
> & {
props?: ScatterChartPropsObjProp;
- yDomain?: ComponentProps
['yDomain'];
- yScale?: AnyScale;
+ // yDomain?: ComponentProps['yDomain'];
+ // yScale?: AnyScale;
};
@@ -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';
@@ -66,6 +65,7 @@
import { SeriesState } from '$lib/states/series.svelte.js';
import { createLegendProps } from './utils.svelte.js';
import { getSettings } from '$lib/contexts/settings.js';
+ import type { BrushDomainType } from '../../states/brush.svelte.js';
const settings = getSettings();
@@ -165,7 +165,6 @@
const activeSeries = $derived.by(() => {
if (!context?.tooltip?.data) return null;
- // @ts-expect-error - shh
return series.find((s) => s.key === context?.tooltip.data?.seriesKey) ?? series[0];
});
@@ -241,12 +240,12 @@
? {
axis: 'both',
resetOnEnd: true,
- xDomain,
- yDomain,
+ x: xDomain as BrushDomainType,
+ y: yDomain as BrushDomainType,
...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/lib/components/charts/types.ts b/packages/layerchart/src/lib/components/charts/types.ts
index 006211504..b7a52f921 100644
--- a/packages/layerchart/src/lib/components/charts/types.ts
+++ b/packages/layerchart/src/lib/components/charts/types.ts
@@ -26,7 +26,7 @@ import type TooltipList from '../tooltip/TooltipList.svelte';
import type TooltipItem from '../tooltip/TooltipItem.svelte';
import type TooltipSeparator from '../tooltip/TooltipSeparator.svelte';
import type { ChartPropsWithoutHTML } from '../Chart.svelte';
-import type { ChartContextValue } from '$lib/contexts/chart.js';
+import type { ChartState } from '$lib/states/chart.svelte.js';
import type Grid from '../Grid.svelte';
import type Bars from '../Bars.svelte';
import type Pie from '../Pie.svelte';
@@ -51,7 +51,7 @@ export type SimplifiedChartSnippetProps;
+ context: ChartState;
/**
* The series of data for the chart.
@@ -138,12 +138,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
@@ -152,18 +146,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.
*
@@ -175,6 +172,7 @@ export type BaseChartProps<
| 'y'
| boolean
| SimplifiedChartSnippet;
+
/**
* The brush to be used for the chart.
*
@@ -195,6 +193,7 @@ export type BaseChartProps<
* @default false
*/
labels?: ComponentProps> | boolean | ChartSnippet;
+
/**
* The legend to be used for the chart.
*
@@ -261,7 +260,7 @@ export type BaseChartProps<
/**
* A bindable reference to the chart context.
*/
- context?: ChartContextValue;
+ context?: ChartState;
children?: ChartSnippet;
aboveContext?: ChartSnippet;
diff --git a/packages/layerchart/src/lib/components/tooltip/Tooltip.svelte b/packages/layerchart/src/lib/components/tooltip/Tooltip.svelte
index b22c6909d..6e92a8d53 100644
--- a/packages/layerchart/src/lib/components/tooltip/Tooltip.svelte
+++ b/packages/layerchart/src/lib/components/tooltip/Tooltip.svelte
@@ -146,7 +146,7 @@
* Optionally pass the chart's context to the tooltip to get
* type inference for the data.
*/
- context?: ChartContextValue;
+ context?: ChartState;
};
export type TooltipProps = TooltipPropsWithoutHTML &
@@ -158,7 +158,8 @@
import { cls } from '@layerstack/tailwind';
import { isScaleBand } from '../../utils/scales.svelte.js';
- import { getChartContext, type ChartContextValue } from '$lib/contexts/chart.js';
+ import { getChartContext } from '$lib/contexts/chart.js';
+ import type { ChartState } from '$lib/states/chart.svelte.js';
import { getTooltipContext } from '$lib/contexts/tooltip.js';
import { createMotion, type MotionProp } from '$lib/utils/motion.svelte.js';
import { type Snippet } from 'svelte';
diff --git a/packages/layerchart/src/lib/components/tooltip/tooltipMetaContext.ts b/packages/layerchart/src/lib/components/tooltip/tooltipMetaContext.ts
index 6ad4111c2..1395bd030 100644
--- a/packages/layerchart/src/lib/components/tooltip/tooltipMetaContext.ts
+++ b/packages/layerchart/src/lib/components/tooltip/tooltipMetaContext.ts
@@ -6,7 +6,7 @@ import { format, type FormatType, type FormatConfig } from '@layerstack/utils';
import { accessor, findRelatedData, type Accessor } from '$lib/utils/common.js';
import type { SeriesData } from '../charts/index.js';
-import type { ChartContextValue } from '$lib/contexts/chart.js';
+import type { ChartState } from '$lib/states/chart.svelte.js';
import { asAny } from '$lib/utils/types.js';
export type SimplifiedChartType = 'bar' | 'area' | 'line' | 'pie' | 'scatter';
@@ -77,7 +77,7 @@ export type TooltipPayload = {
};
type BasePayloadHandlerProps = {
- ctx: ChartContextValue;
+ ctx: ChartState;
data: any;
};
@@ -243,7 +243,7 @@ export function getTooltipPayload({
tooltipData,
metaCtx,
}: {
- ctx: ChartContextValue;
+ ctx: ChartState;
tooltipData: any;
metaCtx: TooltipMetaContextValue | null;
}): TooltipPayload[] {
diff --git a/packages/layerchart/src/lib/contexts/chart.ts b/packages/layerchart/src/lib/contexts/chart.ts
index 5a92c744b..2800a20f7 100644
--- a/packages/layerchart/src/lib/contexts/chart.ts
+++ b/packages/layerchart/src/lib/contexts/chart.ts
@@ -1,118 +1,25 @@
import { Context } from 'runed';
-import type { TimeInterval } from 'd3-time';
+import type { ChartState } from '$lib/states/chart.svelte.js';
+import type { AnyScale } from '$lib/utils/scales.svelte.js';
-import type {
- AxisKey,
- DataType,
- Extents,
- Nice,
- Padding,
- PaddingArray,
- XRangeWithScale,
- YRangeWithScale,
-} from '$lib/utils/types.js';
-import { type AnyScale, type DomainType } from '$lib/utils/scales.svelte.js';
+export type { ChartState };
-import type { GeoContextValue } from '$lib/contexts/geo.js';
-import type { TooltipContextValue } from '$lib/contexts/tooltip.js';
-import type { TransformContextValue } from '$lib/contexts/transform.js';
-import type { BrushContextValue } from '../components/BrushContext.svelte';
-import type { PreservedChartConfig } from '../components/Chart.svelte';
-
-export type ChartContextValue<
- T = any,
- XScale extends AnyScale = AnyScale,
- YScale extends AnyScale = AnyScale,
-> = {
- activeGetters: Record any>;
- width: number;
- height: number;
- percentRange: boolean;
- aspectRatio: number;
- containerRef: HTMLElement | undefined;
- containerWidth: number;
- containerHeight: number;
- config: PreservedChartConfig;
- x: (d: T) => any;
- y: (d: T) => any;
- z: (d: T) => any;
- r: (d: T) => any;
- x1: (d: T) => any;
- y1: (d: T) => any;
- c: (d: T) => any;
- data: DataType;
- xNice: Nice;
- yNice: Nice;
- zNice: Nice;
- rNice: Nice;
- xDomainSort: boolean;
- yDomainSort: boolean;
- zDomainSort: boolean;
- rDomainSort: boolean;
- xReverse: boolean;
- yReverse: boolean;
- zReverse: boolean;
- rReverse: boolean;
- xPadding: PaddingArray;
- yPadding: PaddingArray;
- zPadding: PaddingArray;
- rPadding: PaddingArray;
- padding: Padding;
- flatData: T[];
- extents: Extents;
- xDomain: number[];
- yDomain: number[];
- zDomain: DomainType;
- rDomain: DomainType;
- cDomain: DomainType;
- x1Domain: DomainType;
- y1Domain: DomainType;
- xRange: any[];
- yRange: any[];
- zRange: any[];
- rRange: any[];
- cRange: readonly string[] | string[] | undefined;
- x1Range: XRangeWithScale | undefined;
- y1Range: YRangeWithScale | undefined;
- meta: Record;
- xScale: AnyScale;
- yScale: AnyScale;
- zScale: AnyScale;
- rScale: AnyScale;
- cScale: AnyScale | null;
- x1Scale: AnyScale | null;
- y1Scale: AnyScale | null;
- yGet: (d: T) => any;
- xGet: (d: T) => any;
- zGet: (d: T) => any;
- rGet: (d: T) => any;
- cGet: (d: T) => any;
- x1Get: (d: T) => any;
- y1Get: (d: T) => any;
- xInterval: TimeInterval | null;
- yInterval: TimeInterval | null;
- radial: boolean;
- tooltip: TooltipContextValue;
- geo: GeoContextValue;
- brush: BrushContextValue;
- transform: TransformContextValue;
-};
-
-const _ChartContext = new Context>('ChartContext');
+const _ChartContext = new Context>('ChartContext');
export function getChartContext<
T,
XScale extends AnyScale = AnyScale,
YScale extends AnyScale = AnyScale,
->(): ChartContextValue {
- return _ChartContext.getOr({} as ChartContextValue);
+>(): ChartState {
+ // @ts-expect-error - Type variance is acceptable here
+ return _ChartContext.getOr({} as ChartState);
}
export function setChartContext<
T,
XScale extends AnyScale = AnyScale,
YScale extends AnyScale = AnyScale,
->(context: ChartContextValue): ChartContextValue {
+>(context: ChartState): ChartState {
// @ts-expect-error - shh
return _ChartContext.set(context);
}
diff --git a/packages/layerchart/src/lib/contexts/geo.ts b/packages/layerchart/src/lib/contexts/geo.ts
index c1f9947cc..72cb165d7 100644
--- a/packages/layerchart/src/lib/contexts/geo.ts
+++ b/packages/layerchart/src/lib/contexts/geo.ts
@@ -1,19 +1,17 @@
import { Context } from 'runed';
-import { type GeoProjection } from 'd3-geo';
+import type { GeoState } from '$lib/states/geo.svelte.js';
-export type GeoContextValue = {
- projection: GeoProjection | undefined;
-};
+export type { GeoState };
/**
* Access or set the current GeoContext.
*/
-const _GeoContext = new Context('GeoContext');
+const _GeoContext = new Context('GeoContext');
export function getGeoContext() {
- return _GeoContext.getOr({ projection: undefined } as GeoContextValue);
+ return _GeoContext.getOr({ projection: undefined } as GeoState);
}
-export function setGeoContext(geo: GeoContextValue) {
+export function setGeoContext(geo: GeoState) {
return _GeoContext.set(geo);
}
diff --git a/packages/layerchart/src/lib/states/brush.svelte.ts b/packages/layerchart/src/lib/states/brush.svelte.ts
new file mode 100644
index 000000000..12f5396c9
--- /dev/null
+++ b/packages/layerchart/src/lib/states/brush.svelte.ts
@@ -0,0 +1,58 @@
+import { getChartContext } from '$lib/contexts/chart.js';
+
+// TODO: Should we support the full `DomainType` (`string`, etc)
+// type BrushDomainType = NonNullable;
+export type BrushDomainType = Array;
+
+export type BrushRange = {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+};
+
+export class BrushState {
+ ctx: ReturnType | null;
+
+ x = $state([null, null]);
+ y = $state([null, null]);
+ active = $state();
+ axis = $state<'x' | 'y' | 'both'>('x');
+ handleSize = $state(0);
+
+ constructor(
+ ctx: typeof this.ctx,
+ options?: {
+ x?: BrushDomainType;
+ y?: BrushDomainType;
+ active?: boolean;
+ axis?: 'x' | 'y' | 'both';
+ }
+ ) {
+ this.ctx = ctx;
+
+ this.x = options?.x ?? [null, null];
+ this.y = options?.y ?? [null, null];
+ // this.active = options?.active ?? (this.x !== null || this.y !== null);
+ this.active = options?.active;
+ this.axis = options?.axis ?? 'x';
+ }
+
+ get range() {
+ if (!this.ctx) {
+ return { x: 0, y: 0, width: 0, height: 0 };
+ }
+
+ const top = this.ctx.yScale(this.y?.[1]);
+ const bottom = this.ctx.yScale(this.y?.[0]);
+ const left = this.ctx.xScale(this.x?.[0]);
+ const right = this.ctx.xScale(this.x?.[1]);
+
+ return {
+ x: this.axis === 'both' || this.axis === 'x' ? left : 0,
+ y: this.axis === 'both' || this.axis === 'y' ? top : 0,
+ width: this.axis === 'both' || this.axis === 'x' ? right - left : this.ctx.width,
+ height: this.axis === 'both' || this.axis === 'y' ? bottom - top : this.ctx.height,
+ };
+ }
+}
diff --git a/packages/layerchart/src/lib/states/chart.svelte.ts b/packages/layerchart/src/lib/states/chart.svelte.ts
new file mode 100644
index 000000000..a4011adc8
--- /dev/null
+++ b/packages/layerchart/src/lib/states/chart.svelte.ts
@@ -0,0 +1,519 @@
+import { scaleOrdinal, scaleSqrt } from 'd3-scale';
+import { extent, max, min } from 'd3-array';
+import { unique } from '@layerstack/utils';
+import { useDebounce } from 'runed';
+
+import type { AnyScale, DomainType } from '$lib/utils/scales.svelte.js';
+import {
+ autoScale,
+ createScale,
+ getRange,
+ isScaleBand,
+ isScaleTime,
+ makeAccessor,
+} from '$lib/utils/scales.svelte.js';
+import type { ChartPropsWithoutHTML } from '$lib/components/Chart.svelte';
+import type { Extents } from '$lib/utils/types.js';
+import { accessor, chartDataArray } from '$lib/utils/common.js';
+import { filterObject } from '$lib/utils/filterObject.js';
+import { calcDomain, calcScaleExtents, createGetter, createChartScale } from '$lib/utils/chart.js';
+import { printDebug } from '$lib/utils/debug.js';
+
+import type { GeoState } from '$lib/contexts/geo.js';
+import type { TransformContextValue } from '$lib/contexts/transform.js';
+import type { TooltipContextValue } from '$lib/contexts/tooltip.js';
+import type { BrushState } from './brush.svelte.js';
+
+const defaultPadding = { top: 0, right: 0, bottom: 0, left: 0 };
+
+interface ScaleEntry {
+ scale: AnyScale;
+ sort?: boolean;
+}
+
+export class ChartState<
+ TData = any,
+ XScale extends AnyScale = AnyScale,
+ YScale extends AnyScale = AnyScale,
+> {
+ // Props getter function - set in constructor
+ private _propsGetter!: () => ChartPropsWithoutHTML;
+
+ // Props - accessed via getter function for fine-grained reactivity
+ props = $derived(this._propsGetter());
+
+ // Context references
+ geoContext = $state(null!);
+ transformContext = $state(null!);
+ tooltipContext = $state(null!);
+ brushContext = $state(null!);
+
+ // Container dimensions
+ _containerWidth = $state(100);
+ _containerHeight = $state(100);
+
+ // Mount state
+ isMounted = $state(false);
+
+ // Container ref (set from Chart.svelte)
+ containerRef = $state();
+
+ // Meta data - reactive to props.meta changes
+ meta = $derived(this.props.meta ?? {});
+
+ constructor(propsGetter: () => ChartPropsWithoutHTML) {
+ this._propsGetter = propsGetter;
+
+ const logDebug = useDebounce(printDebug, 200);
+
+ // Set mounted state once component initializes
+ $effect(() => {
+ this.isMounted = true;
+ });
+
+ // Call onResize callback when dimensions change
+ $effect(() => {
+ if (!this.isMounted) return;
+ this.props.onResize?.({
+ width: this.width,
+ height: this.height,
+ containerWidth: this.containerWidth,
+ containerHeight: this.containerHeight,
+ });
+ });
+
+ // Debug logging when mounted
+ $effect(() => {
+ if (
+ !this.isMounted ||
+ !this.props.debug ||
+ (!this.props.ssr && typeof window === 'undefined')
+ ) {
+ return;
+ }
+
+ if (this.box) {
+ logDebug({
+ data: this.data,
+ flatData: this.flatData,
+ boundingBox: this.box,
+ activeGetters: this.activeGetters,
+ x: this.props.x,
+ y: this.props.y,
+ z: this.props.z,
+ r: this.props.r,
+ xScale: this.xScale,
+ yScale: this.yScale,
+ zScale: this.zScale,
+ rScale: this.rScale,
+ });
+ }
+ });
+ }
+
+ // Use $derived fields instead of getters for caching
+ containerWidth = $derived(this.props.width ?? this._containerWidth);
+ containerHeight = $derived(this.props.height ?? this._containerHeight);
+ data = $derived(this.props.data ?? []);
+ flatData = $derived((this.props.flatData ?? this.data) as TData[]);
+
+ // Cached scale props - use props directly to avoid accessing this.flatData
+ _xScaleProp = $derived.by(() => {
+ const flatData = (this.props.flatData ?? this.props.data ?? []) as TData[];
+ return this.props.xScale ?? autoScale(this.props.xDomain, flatData, this.props.x);
+ });
+
+ _yScaleProp = $derived.by(() => {
+ const flatData = (this.props.flatData ?? this.props.data ?? []) as TData[];
+ return this.props.yScale ?? autoScale(this.props.yDomain, flatData, this.props.y);
+ });
+
+ _zScaleProp = $derived.by(() => {
+ const flatData = (this.props.flatData ?? this.props.data ?? []) as TData[];
+ return this.props.zScale ?? autoScale(this.props.zDomain, flatData, this.props.z);
+ });
+
+ _rScaleProp = $derived(this.props.rScale ?? scaleSqrt());
+
+ xRangeProp = $derived(
+ this.props.xRange ? this.props.xRange : this.props.radial ? [0, 2 * Math.PI] : undefined
+ );
+
+ yRangeProp = $derived(
+ this.props.yRange ??
+ (this.props.radial ? ({ height }: { height: number }) => [0, height / 2] : undefined)
+ );
+
+ yReverse = $derived(
+ this.props.yScale ? !isScaleBand(this.props.yScale) && !isScaleTime(this.props.yScale) : true
+ );
+
+ _xDomain = $derived.by((): DomainType | undefined => {
+ if (this.props.xDomain !== undefined) return this.props.xDomain;
+
+ if (this.props.xInterval != null && Array.isArray(this.data) && this.data.length > 0) {
+ const lastXValue = accessor(this.props.x)(this.data[this.data.length - 1]);
+ return [null, this.props.xInterval.offset(lastXValue)];
+ }
+
+ if (this.props.xBaseline != null && Array.isArray(this.data)) {
+ const xValues = this.data.flatMap(accessor(this.props.x));
+ return [min([this.props.xBaseline, ...xValues]), max([this.props.xBaseline, ...xValues])];
+ }
+ });
+
+ _yDomain = $derived.by((): DomainType | undefined => {
+ if (this.props.yDomain !== undefined) return this.props.yDomain;
+
+ if (this.props.yInterval != null && Array.isArray(this.data) && this.data.length > 0) {
+ const lastYValue = accessor(this.props.y)(this.data[this.data.length - 1]);
+ return [null, this.props.yInterval.offset(lastYValue)];
+ }
+
+ if (this.props.yBaseline != null && Array.isArray(this.data)) {
+ const yValues = this.data.flatMap(accessor(this.props.y));
+ return [min([this.props.yBaseline, ...yValues]), max([this.props.yBaseline, ...yValues])];
+ }
+ });
+
+ x = $derived(makeAccessor(this.props.x));
+ y = $derived(makeAccessor(this.props.y));
+ z = $derived(makeAccessor(this.props.z));
+ r = $derived(makeAccessor(this.props.r));
+ c = $derived(accessor(this.props.c));
+ x1 = $derived(accessor(this.props.x1));
+ y1 = $derived(accessor(this.props.y1));
+
+ filteredExtents = $derived(filterObject($state.snapshot(this.props.extents ?? {})));
+
+ activeGetters = $derived({
+ x: this.x,
+ y: this.y,
+ z: this.z,
+ r: this.r,
+ });
+
+ padding = $derived.by(() => {
+ const paddingProp = this.props.padding ?? {};
+ if (typeof paddingProp === 'number') {
+ return {
+ ...defaultPadding,
+ top: paddingProp,
+ right: paddingProp,
+ bottom: paddingProp,
+ left: paddingProp,
+ };
+ }
+ return { ...defaultPadding, ...paddingProp };
+ });
+
+ box = $derived.by(() => {
+ const top = this.padding.top;
+ const right = this.containerWidth - this.padding.right;
+ const bottom = this.containerHeight - this.padding.bottom;
+ const left = this.padding.left;
+ const width = right - left;
+ const height = bottom - top;
+
+ if (this.props.verbose === true) {
+ if (width <= 0 && this.isMounted === true) {
+ console.warn(
+ `[LayerChart] Target div has zero or negative width (${width}). Did you forget to set an explicit width in CSS on the container?`
+ );
+ }
+ if (height <= 0 && this.isMounted === true) {
+ console.warn(
+ `[LayerChart] Target div has zero or negative height (${height}). Did you forget to set an explicit width in CSS on the container?`
+ );
+ }
+ }
+
+ return {
+ top,
+ left,
+ bottom,
+ right,
+ width,
+ height,
+ };
+ });
+
+ width = $derived(this.box.width);
+ height = $derived(this.box.height);
+
+ extents = $derived.by((): Extents => {
+ const scaleLookup: Record = {
+ x: {
+ scale: this._xScaleProp,
+ sort: this.props.xDomainSort,
+ },
+ y: {
+ scale: this._yScaleProp,
+ sort: this.props.yDomainSort,
+ },
+ z: {
+ scale: this._zScaleProp,
+ sort: this.props.zDomainSort,
+ },
+ r: {
+ scale: this._rScaleProp,
+ sort: this.props.rDomainSort,
+ },
+ };
+
+ const getters = filterObject(this.activeGetters, this.filteredExtents);
+ const activeScales: Record = Object.fromEntries(
+ Object.keys(getters).map((k) => [k, scaleLookup[k]])
+ );
+
+ if (Object.keys(getters).length > 0) {
+ const calculatedExtents = calcScaleExtents(this.flatData, getters, activeScales);
+ return { ...calculatedExtents, ...this.filteredExtents };
+ } else {
+ return {};
+ }
+ });
+
+ xDomain = $derived(calcDomain('x', this.extents, this._xDomain));
+ yDomain = $derived(calcDomain('y', this.extents, this._yDomain));
+ zDomain = $derived(calcDomain('z', this.extents, this.props.zDomain));
+ rDomain = $derived(calcDomain('r', this.extents, this.props.rDomain));
+
+ x1Domain = $derived(this.props.x1Domain ?? extent(chartDataArray(this.data), this.x1));
+ y1Domain = $derived(this.props.y1Domain ?? extent(chartDataArray(this.data), this.y1));
+ cDomain = $derived(this.props.cDomain ?? unique(chartDataArray(this.data).map(this.c)));
+
+ snappedPadding = $derived($state.snapshot(this.props.xPadding));
+ snappedExtents = $derived($state.snapshot(this.extents));
+
+ xScale = $derived(
+ createChartScale('x', {
+ scale: this._xScaleProp,
+ domain: this.xDomain,
+ padding: this.snappedPadding,
+ nice: this.props.xNice ?? false,
+ reverse: this.props.xReverse ?? false,
+ percentRange: this.props.percentRange ?? false,
+ range: this.xRangeProp,
+ height: this.height,
+ width: this.width,
+ extents: this.snappedExtents,
+ })
+ );
+
+ xGet = $derived(createGetter(this.x, this.xScale));
+
+ yScale = $derived(
+ createChartScale('y', {
+ scale: this._yScaleProp,
+ domain: this.yDomain,
+ padding: this.props.yPadding,
+ nice: this.props.yNice ?? false,
+ reverse: this.yReverse,
+ percentRange: this.props.percentRange ?? false,
+ range: this.yRangeProp,
+ height: this.height,
+ width: this.width,
+ extents: this.filteredExtents,
+ })
+ );
+
+ yGet = $derived(createGetter(this.y, this.yScale));
+
+ zScale = $derived(
+ createChartScale('z', {
+ scale: this._zScaleProp,
+ domain: this.zDomain,
+ padding: this.props.zPadding,
+ nice: this.props.zNice ?? false,
+ reverse: this.props.zReverse ?? false,
+ percentRange: this.props.percentRange ?? false,
+ range: this.props.zRange,
+ height: this.height,
+ width: this.width,
+ extents: this.filteredExtents,
+ })
+ );
+
+ zGet = $derived(createGetter(this.z, this.zScale));
+
+ rScale = $derived(
+ createChartScale('r', {
+ scale: this._rScaleProp,
+ domain: this.rDomain,
+ padding: this.props.rPadding,
+ nice: this.props.rNice ?? false,
+ reverse: this.props.rReverse ?? false,
+ percentRange: this.props.percentRange ?? false,
+ range: this.props.rRange,
+ height: this.height,
+ width: this.width,
+ extents: this.filteredExtents,
+ })
+ );
+
+ rGet = $derived(createGetter(this.r, this.rScale));
+
+ x1Scale = $derived(
+ this.props.x1Range
+ ? createScale(
+ this.props.x1Scale ?? autoScale(this.props.x1Domain, this.flatData, this.props.x1),
+ this.x1Domain,
+ this.props.x1Range,
+ {
+ xScale: this.xScale,
+ width: this.width,
+ height: this.height,
+ }
+ )
+ : null
+ );
+
+ x1Get = $derived(createGetter(this.x1, this.x1Scale));
+
+ y1Scale = $derived(
+ this.props.y1Range
+ ? createScale(
+ this.props.y1Scale ?? autoScale(this.props.y1Domain, this.flatData, this.props.y1),
+ this.y1Domain,
+ this.props.y1Range,
+ {
+ yScale: this.yScale,
+ width: this.width,
+ height: this.height,
+ }
+ )
+ : null
+ );
+
+ y1Get = $derived(createGetter(this.y1, this.y1Scale));
+
+ cScale = $derived(
+ this.props.cRange
+ ? createScale(this.props.cScale ?? scaleOrdinal(), this.cDomain, this.props.cRange, {
+ width: this.width,
+ height: this.height,
+ })
+ : null
+ );
+
+ cGet = $derived((d: any) => this.cScale?.(this.c(d)));
+
+ xDomainPossiblyNice = $derived(this.xScale.domain());
+ yDomainPossiblyNice = $derived(this.yScale.domain());
+ zDomainPossiblyNice = $derived(this.zScale.domain());
+ rDomainPossiblyNice = $derived(this.rScale.domain());
+
+ xRange = $derived(getRange(this.xScale));
+ yRange = $derived(getRange(this.yScale));
+ zRange = $derived(getRange(this.zScale));
+ rRange = $derived(getRange(this.rScale));
+
+ aspectRatio = $derived(this.width / this.height);
+
+ // Properties that come directly from props (not derived)
+ get percentRange() {
+ return this.props.percentRange ?? false;
+ }
+ get xNice() {
+ return this.props.xNice ?? false;
+ }
+ get yNice() {
+ return this.props.yNice ?? false;
+ }
+ get zNice() {
+ return this.props.zNice ?? false;
+ }
+ get rNice() {
+ return this.props.rNice ?? false;
+ }
+ get xDomainSort() {
+ return this.props.xDomainSort ?? false;
+ }
+ get yDomainSort() {
+ return this.props.yDomainSort ?? false;
+ }
+ get zDomainSort() {
+ return this.props.zDomainSort ?? false;
+ }
+ get rDomainSort() {
+ return this.props.rDomainSort ?? false;
+ }
+ get xReverse() {
+ return this.props.xReverse ?? false;
+ }
+ get zReverse() {
+ return this.props.zReverse ?? false;
+ }
+ get rReverse() {
+ return this.props.rReverse ?? false;
+ }
+ get xPadding() {
+ return this.props.xPadding;
+ }
+ get yPadding() {
+ return this.props.yPadding;
+ }
+ get zPadding() {
+ return this.props.zPadding;
+ }
+ get rPadding() {
+ return this.props.rPadding;
+ }
+ get cRange() {
+ return this.props.cRange;
+ }
+ get x1Range() {
+ return this.props.x1Range;
+ }
+ get y1Range() {
+ return this.props.y1Range;
+ }
+ get xInterval() {
+ return this.props.xInterval ?? null;
+ }
+ get yInterval() {
+ return this.props.yInterval ?? null;
+ }
+ get radial() {
+ return this.props.radial ?? false;
+ }
+ get tooltip() {
+ return this.tooltipContext;
+ }
+ get geo() {
+ return this.geoContext;
+ }
+ get brush() {
+ return this.brushContext;
+ }
+ get transform() {
+ return this.transformContext;
+ }
+
+ get config() {
+ return {
+ x: this.props.x,
+ y: this.props.y,
+ z: this.props.z,
+ r: this.props.r,
+ c: this.props.c,
+ x1: this.props.x1,
+ y1: this.props.y1,
+ xDomain: this._xDomain,
+ yDomain: this._yDomain,
+ zDomain: this.props.zDomain,
+ rDomain: this.props.rDomain,
+ x1Domain: this.props.x1Domain,
+ y1Domain: this.props.y1Domain,
+ cDomain: this.props.cDomain,
+ xRange: this.props.xRange,
+ yRange: this.props.yRange,
+ zRange: this.props.zRange,
+ rRange: this.props.rRange,
+ cRange: this.props.cRange,
+ x1Range: this.props.x1Range,
+ y1Range: this.props.y1Range,
+ };
+ }
+}
diff --git a/packages/layerchart/src/lib/states/geo.svelte.ts b/packages/layerchart/src/lib/states/geo.svelte.ts
new file mode 100644
index 000000000..19266e275
--- /dev/null
+++ b/packages/layerchart/src/lib/states/geo.svelte.ts
@@ -0,0 +1,111 @@
+import type { GeoProjection } from 'd3-geo';
+import type { GeoContextProps } from '$lib/components/GeoContext.svelte';
+
+export class GeoState {
+ // Props getter function - set in constructor
+ private _propsGetter!: () => GeoContextProps;
+
+ // Props - accessed via getter function for fine-grained reactivity
+ props = $derived(this._propsGetter());
+
+ // Context references
+ chartWidth = $state(100);
+ chartHeight = $state(100);
+ transformScale = $state(1);
+ transformTranslateX = $state(0);
+ transformTranslateY = $state(0);
+
+ // The actual projection instance
+ projection = $state(undefined);
+
+ constructor(propsGetter: () => GeoContextProps) {
+ this._propsGetter = propsGetter;
+
+ // Main effect to build and configure the projection
+ $effect.pre(() => {
+ if (!this.props.projection) return;
+
+ const _projection = this.props.projection();
+
+ // Apply fitSize if fitGeojson is provided
+ if (this.props.fitGeojson && 'fitSize' in _projection) {
+ _projection.fitSize(this.fitSizeRange, this.props.fitGeojson);
+ }
+
+ // Apply scale
+ if ('scale' in _projection) {
+ if (this.props.scale) {
+ _projection.scale(this.props.scale);
+ }
+
+ if (this.props.applyTransform?.includes('scale')) {
+ _projection.scale(this.transformScale);
+ }
+ }
+
+ // Apply rotate
+ if ('rotate' in _projection) {
+ if (this.props.rotate) {
+ _projection.rotate([
+ this.props.rotate.yaw,
+ this.props.rotate.pitch,
+ this.props.rotate.roll,
+ ]);
+ }
+
+ if (this.props.applyTransform?.includes('rotate')) {
+ _projection.rotate([
+ this.transformTranslateX, // yaw
+ this.transformTranslateY, // pitch
+ // TODO: `roll` from `transformContext`?
+ ]);
+ }
+ }
+
+ // Apply translate
+ if ('translate' in _projection) {
+ if (this.props.translate) {
+ _projection.translate(this.props.translate);
+ }
+
+ if (this.props.applyTransform?.includes('translate')) {
+ _projection.translate([this.transformTranslateX, this.transformTranslateY]);
+ }
+ }
+
+ // Apply center
+ if (this.props.center && 'center' in _projection) {
+ _projection.center(this.props.center);
+ }
+
+ // Apply reflectX
+ if (this.props.reflectX) {
+ _projection.reflectX(this.props.reflectX);
+ }
+
+ // Apply reflectY
+ if (this.props.reflectY) {
+ _projection.reflectY(this.props.reflectY);
+ }
+
+ // Apply clipAngle
+ if (this.props.clipAngle && 'clipAngle' in _projection) {
+ _projection.clipAngle(this.props.clipAngle);
+ }
+
+ // Apply clipExtent
+ if (this.props.clipExtent && 'clipExtent' in _projection) {
+ _projection.clipExtent(this.props.clipExtent);
+ }
+
+ this.projection = _projection;
+ });
+ }
+
+ // Derived properties
+ fitSizeRange = $derived(
+ this.props.fixedAspectRatio
+ ? [100, 100 / this.props.fixedAspectRatio]
+ : [this.chartWidth, this.chartHeight]
+ ) as [number, number];
+}
diff --git a/packages/layerchart/src/lib/states/series.svelte.ts b/packages/layerchart/src/lib/states/series.svelte.ts
index 8cbe491be..fd1346e88 100644
--- a/packages/layerchart/src/lib/states/series.svelte.ts
+++ b/packages/layerchart/src/lib/states/series.svelte.ts
@@ -3,14 +3,6 @@ import type { SeriesData } from '../components/charts/types.js';
import { SelectionState } from '@layerstack/svelte-state';
-class HighlightKey {
- current = $state['key'] | null>(null);
-
- set = (seriesKey: typeof this.current) => {
- this.current = seriesKey;
- };
-}
-
export class SeriesState {
#series = $state.raw[]>([]);
selectedKeys = new SelectionState();
@@ -68,3 +60,11 @@ export class SeriesState {
>;
}
}
+
+class HighlightKey {
+ current = $state['key'] | null>(null);
+
+ set = (seriesKey: typeof this.current) => {
+ this.current = seriesKey;
+ };
+}
diff --git a/packages/layerchart/src/lib/utils/rect.svelte.ts b/packages/layerchart/src/lib/utils/rect.svelte.ts
index 75dfb20b5..bcece7e4f 100644
--- a/packages/layerchart/src/lib/utils/rect.svelte.ts
+++ b/packages/layerchart/src/lib/utils/rect.svelte.ts
@@ -1,5 +1,5 @@
import { max, min } from 'd3-array';
-import type { ChartContextValue } from '$lib/contexts/chart.js';
+import type { ChartState } from '$lib/states/chart.svelte.js';
import { accessor, type Accessor } from './common.js';
import { isScaleBand } from './scales.svelte.js';
@@ -58,7 +58,7 @@ function resolveInsets(insets?: Insets): ResolvedInsets {
}
export function createDimensionGetter(
- ctx: ChartContextValue,
+ ctx: ChartState,
getOptions?: () => DimensionGetterOptions
) {
const options = $derived(getOptions?.());
diff --git a/packages/layerchart/src/lib/utils/types.ts b/packages/layerchart/src/lib/utils/types.ts
index dfb41385d..cf1f3a714 100644
--- a/packages/layerchart/src/lib/utils/types.ts
+++ b/packages/layerchart/src/lib/utils/types.ts
@@ -19,12 +19,22 @@ export function asAny(x: any): any {
*
* @template T - The base object type from which properties will be omitted.
* @template U - The object type whose properties will be omitted from 'T'.
+ *
* @example
* type Result = Without<{ a: number; b: string; }, { b: string; }>;
* // Result type will be { a: number; }
*/
export type Without = Omit;
+/**
+ * Extracts the non-nullable array type from a type that may include nulls.
+ *
+ * @example
+ * type Result = NonNullArray<(number | null)[]>;
+ * // Result type will be number[].
+ */
+export type NonNullArray = T extends (infer U | null)[] ? U[] : never;
+
export type AxisKey = 'x' | 'y' | 'z' | 'r';
export type Extents = {
diff --git a/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte b/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte
index d7531a00a..ebf9dec6a 100644
--- a/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte
+++ b/packages/layerchart/src/routes/docs/components/AreaChart/+page.svelte
@@ -14,7 +14,7 @@
Threshold,
pivotLonger,
accessor,
- type ChartContextValue,
+ type ChartState,
defaultChartPadding,
Layer,
getSettings,
@@ -129,7 +129,7 @@
let xDomain: DomainType | undefined = $state();
let markerPoints: { date: Date; value: number }[] = $state([]);
- let context = $state>(null!);
+ let context = $state>(null!);
let selectedCurve = $state(curveStepAfter);
@@ -1228,7 +1228,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 },
@@ -1241,8 +1241,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 c21ddbce7..f72f0c92b 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 2e3b1e3f9..516f204f9 100644
--- a/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte
+++ b/packages/layerchart/src/routes/docs/components/BrushContext/+page.svelte
@@ -1,9 +1,9 @@
Examples
+Html click
+
+
+
+
+
{
+ console.log('onBrushEnd', e);
+ xDomain2 = e.brush.x;
+ },
+ }}
+ >
+ {#snippet children({ context })}
+
+ {#each dataSeriesData as d}
+ {@const start = d.date}
+ {@const end = endOfInterval('day', d.date)}
+
+ {@const x = context.xScale(start)}
+ {@const width = context.xScale(end) - x}
+ {@const height = context.height}
+
+
+ {
+ console.log('clicked', start);
+ }}
+ onpointerenter={(e) => {
+ // console.log(start);
+ }}
+ >
+ {/each}
+
+ {/snippet}
+
+
+
+
+Band scale (WIP)
+
+
+
+ {
+ console.log(e);
+ },
+ }}
+ >
+
+
+
+
+
+
+
Basic
@@ -131,7 +211,7 @@
- {#if context.brush.isActive}
+ {#if context.brush.active}
- {#if context.brush.isActive}
+ {#if context.brush.active}
@@ -203,11 +283,11 @@
- {#if context.brush.isActive}
+ {#if context.brush.active}
@@ -244,7 +324,7 @@
resetOnEnd: true,
onBrushEnd: (e) => {
// @ts-expect-error
- set(e.xDomain);
+ set(e.brush.x);
},
}}
>
@@ -282,7 +362,7 @@
resetOnEnd: true,
onBrushEnd: (e) => {
// @ts-expect-error
- set(e.yDomain);
+ set(e.brush.y);
},
}}
>
@@ -322,9 +402,9 @@
onBrushEnd: (e) => {
set({
// @ts-expect-error
- xDomain: e.xDomain,
+ xDomain: e.brush.x,
// @ts-expect-error
- yDomain: e.yDomain,
+ yDomain: e.brush.y,
});
},
}}
@@ -383,7 +463,7 @@
brush={{
onChange: (e) => {
// @ts-expect-error
- set(e.xDomain);
+ set(e.brush.x);
},
}}
>
@@ -411,7 +491,7 @@
axis: 'y',
onChange: (e) => {
// @ts-expect-error
- set(e.yDomain);
+ set(e.brush.y);
},
}}
>
@@ -493,7 +573,7 @@
brush={{
onChange: (e) => {
// @ts-expect-error
- set(e.xDomain);
+ set(e.brush.x);
},
}}
>
@@ -554,9 +634,9 @@
padding={{ left: 16 }}
brush={{
mode: 'separated',
- xDomain,
- onChange: (e) => (xDomain = e.xDomain),
- onReset: (e) => (xDomain = null),
+ x: xDomain,
+ onChange: (e) => (xDomain = e.brush.x),
+ onReset: (e) => (xDomain = [null, null]),
}}
>
@@ -564,7 +644,6 @@
line={{ class: 'stroke-2 stroke-(--chart-color)' }}
class="fill-(--chart-color) opacity-20"
/>
-
@@ -591,7 +670,7 @@
resetOnEnd: true,
onBrushEnd: (e) => {
// @ts-expect-error
- set(e.xDomain);
+ set(e.brush.x);
},
}}
>
@@ -657,9 +736,9 @@
onChange: (e) => {
set({
// @ts-expect-error
- xDomain: e.xDomain,
+ xDomain: e.brush.x,
// @ts-expect-error
- yDomain: e.yDomain,
+ yDomain: e.brush.y,
});
},
}}
@@ -716,9 +795,9 @@
onBrushEnd: (e) => {
set({
// @ts-expect-error
- xDomain: e.xDomain,
+ xDomain: e.brush.x,
// @ts-expect-error
- yDomain: e.yDomain,
+ yDomain: e.brush.y,
});
},
}}
@@ -743,14 +822,14 @@
brush={{
axis: 'both',
mode: 'separated',
- xDomain: value?.xDomain,
- yDomain: value?.yDomain,
+ x: value?.xDomain,
+ y: value?.yDomain,
onChange: (e) => {
set({
// @ts-expect-error
- xDomain: e.xDomain,
+ xDomain: e.brush.x,
// @ts-expect-error
- yDomain: e.yDomain,
+ yDomain: e.brush.y,
});
},
}}
diff --git a/packages/layerchart/src/routes/docs/components/Tooltip/+page.svelte b/packages/layerchart/src/routes/docs/components/Tooltip/+page.svelte
index b33028208..604f6270b 100644
--- a/packages/layerchart/src/routes/docs/components/Tooltip/+page.svelte
+++ b/packages/layerchart/src/routes/docs/components/Tooltip/+page.svelte
@@ -14,7 +14,7 @@
Points,
Rule,
Tooltip,
- type ChartContextValue,
+ type ChartState,
} from 'layerchart';
import { Button, Duration, Field, Menu, MenuField, Toggle } from 'svelte-ux';
import { flatten, format } from '@layerstack/utils';
@@ -137,7 +137,7 @@
let snap: 'pointer' | 'data' = $state('pointer');
let contained: ComponentProps