layerchart 2.0.0-next.54 → 2.0.0-next.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/bench/ComposableLineChart.svelte +1 -1
  2. package/dist/bench/GeoBench.svelte +1 -8
  3. package/dist/components/AnnotationRange.svelte +3 -1
  4. package/dist/components/Arc.svelte +1 -3
  5. package/dist/components/ArcLabel.svelte.test.js +7 -7
  6. package/dist/components/Axis.svelte +10 -2
  7. package/dist/components/Axis.svelte.d.ts +8 -2
  8. package/dist/components/Bar.svelte +14 -40
  9. package/dist/components/BoxPlot.svelte +4 -12
  10. package/dist/components/Cell.svelte +13 -8
  11. package/dist/components/Chart.svelte +69 -26
  12. package/dist/components/ChartChildren.svelte +22 -4
  13. package/dist/components/Circle.svelte +51 -9
  14. package/dist/components/Circle.svelte.d.ts +6 -0
  15. package/dist/components/CircleClipPath.svelte +13 -31
  16. package/dist/components/CircleClipPath.svelte.d.ts +7 -1
  17. package/dist/components/ClipPath.svelte +58 -21
  18. package/dist/components/ClipPath.svelte.d.ts +21 -12
  19. package/dist/components/Connector.svelte +18 -0
  20. package/dist/components/Connector.svelte.d.ts +5 -0
  21. package/dist/components/Ellipse.svelte +27 -6
  22. package/dist/components/GeoClipPath.svelte +14 -17
  23. package/dist/components/GeoClipPath.svelte.d.ts +6 -0
  24. package/dist/components/GeoLegend.svelte +1 -3
  25. package/dist/components/GeoPoint.svelte +25 -3
  26. package/dist/components/GeoSpline.svelte +1 -4
  27. package/dist/components/GeoTile.svelte +8 -4
  28. package/dist/components/Grid.svelte +15 -4
  29. package/dist/components/Grid.svelte.d.ts +14 -4
  30. package/dist/components/Group.svelte +11 -5
  31. package/dist/components/Highlight.svelte +4 -3
  32. package/dist/components/Image.svelte +42 -30
  33. package/dist/components/Labels.svelte +2 -4
  34. package/dist/components/Line.svelte +31 -3
  35. package/dist/components/Line.svelte.d.ts +7 -0
  36. package/dist/components/LinearGradient.svelte +8 -4
  37. package/dist/components/Link.svelte +8 -0
  38. package/dist/components/Marker.svelte +9 -1
  39. package/dist/components/Path.svelte +43 -23
  40. package/dist/components/Pattern.svelte +101 -5
  41. package/dist/components/Pattern.svelte.d.ts +3 -1
  42. package/dist/components/Pie.svelte +2 -6
  43. package/dist/components/RadialGradient.svelte +8 -4
  44. package/dist/components/Rect.svelte +117 -9
  45. package/dist/components/Rect.svelte.d.ts +13 -1
  46. package/dist/components/RectClipPath.svelte +11 -15
  47. package/dist/components/RectClipPath.svelte.d.ts +6 -0
  48. package/dist/components/Spline.svelte +22 -4
  49. package/dist/components/Text.svelte +16 -5
  50. package/dist/components/Trail.svelte +19 -7
  51. package/dist/components/Tree.svelte +7 -3
  52. package/dist/components/Vector.svelte +37 -14
  53. package/dist/components/Violin.svelte +1 -2
  54. package/dist/components/charts/ArcChart.svelte +8 -5
  55. package/dist/components/charts/AreaChart.svelte +6 -1
  56. package/dist/components/charts/BarChart.svelte +3 -1
  57. package/dist/components/charts/LineChart.svelte +6 -1
  58. package/dist/components/charts/PieChart.svelte +10 -3
  59. package/dist/components/tooltip/Tooltip.svelte +2 -8
  60. package/dist/contexts/chart.d.ts +1 -1
  61. package/dist/contexts/chart.js +3 -1
  62. package/dist/server/TestBarChart.svelte +28 -28
  63. package/dist/server/TestLineChart.svelte +28 -28
  64. package/dist/server/index.js +1 -1
  65. package/dist/states/brush.svelte.js +16 -13
  66. package/dist/states/chart.svelte.test.js +24 -19
  67. package/dist/states/geo.svelte.js +1 -4
  68. package/dist/states/series.svelte.js +1 -1
  69. package/dist/utils/__screenshots__/canvas.svelte.test.ts/renderPathData-composes-element-opacity-with-inherited-globalAlpha--Group-opacity--1.png +0 -0
  70. package/dist/utils/__screenshots__/canvas.svelte.test.ts/renderPathData-composes-element-opacity-with-inherited-globalAlpha--Group-opacity--2.png +0 -0
  71. package/dist/utils/canvas.d.ts +2 -0
  72. package/dist/utils/canvas.js +20 -11
  73. package/dist/utils/canvas.svelte.test.js +55 -0
  74. package/dist/utils/connectorUtils.d.ts +13 -0
  75. package/dist/utils/connectorUtils.js +120 -1
  76. package/dist/utils/path.d.ts +19 -0
  77. package/dist/utils/path.js +72 -0
  78. package/dist/utils/rect.svelte.d.ts +18 -0
  79. package/dist/utils/rect.svelte.js +33 -0
  80. package/dist/utils/trail.js +3 -4
  81. package/package.json +1 -1
@@ -34,7 +34,7 @@
34
34
  }: Props = $props();
35
35
  </script>
36
36
 
37
- <Chart {data} {x} {y} {width} {height} {series} xDomain={xDomain} yDomain={yDomain}>
37
+ <Chart {data} {x} {y} {width} {height} {series} {xDomain} {yDomain}>
38
38
  <Layer type={layer}>
39
39
  {#if axis}
40
40
  <Axis placement="left" />
@@ -12,14 +12,7 @@
12
12
  width?: number;
13
13
  };
14
14
 
15
- let {
16
- features,
17
- fitGeojson,
18
- projection,
19
- layer = 'svg',
20
- height = 400,
21
- width,
22
- }: Props = $props();
15
+ let { features, fitGeojson, projection, layer = 'svg', height = 400, width }: Props = $props();
23
16
  </script>
24
17
 
25
18
  <Chart geo={{ projection, fitGeojson }} {width} {height}>
@@ -78,7 +78,9 @@
78
78
  const y0 = y ? ctx.yScale(y[0] ?? ctx.yDomain[0]) : ctx.yRange[0];
79
79
  const y1 = y ? ctx.yScale(y[1] ?? ctx.yDomain[1]) : ctx.yRange[1];
80
80
 
81
- const bandPadding = isScaleBand(ctx.xScale) ? (ctx.xScale.padding() * ctx.xScale.step()) / 2 : 0;
81
+ const bandPadding = isScaleBand(ctx.xScale)
82
+ ? (ctx.xScale.padding() * ctx.xScale.step()) / 2
83
+ : 0;
82
84
  const bandStep = isScaleBand(ctx.xScale) ? ctx.xScale.step() : 0;
83
85
 
84
86
  return {
@@ -255,9 +255,7 @@
255
255
 
256
256
  const endAngle = $derived(
257
257
  endAngleProp ??
258
- degreesToRadians(
259
- (ctx.config.xRange ? max(ctx.config.xRange as number[]) : max(range))!
260
- )
258
+ degreesToRadians((ctx.config.xRange ? max(ctx.config.xRange as number[]) : max(range))!)
261
259
  );
262
260
 
263
261
  const motionEndAngle = createMotion(initialValue, () => value, motion);
@@ -19,7 +19,7 @@ describe('ArcLabel', () => {
19
19
  childComponents: [
20
20
  {
21
21
  component: ArcLabel,
22
- props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
22
+ props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
23
23
  centroid,
24
24
  startAngle,
25
25
  endAngle,
@@ -46,7 +46,7 @@ describe('ArcLabel', () => {
46
46
  childComponents: [
47
47
  {
48
48
  component: ArcLabel,
49
- props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
49
+ props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
50
50
  centroid,
51
51
  startAngle,
52
52
  endAngle,
@@ -77,7 +77,7 @@ describe('ArcLabel', () => {
77
77
  childComponents: [
78
78
  {
79
79
  component: ArcLabel,
80
- props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
80
+ props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
81
81
  centroid,
82
82
  startAngle,
83
83
  endAngle,
@@ -109,7 +109,7 @@ describe('ArcLabel', () => {
109
109
  childComponents: [
110
110
  {
111
111
  component: ArcLabel,
112
- props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
112
+ props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
113
113
  centroid,
114
114
  startAngle,
115
115
  endAngle,
@@ -141,7 +141,7 @@ describe('ArcLabel', () => {
141
141
  childComponents: [
142
142
  {
143
143
  component: ArcLabel,
144
- props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
144
+ props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
145
145
  centroid,
146
146
  startAngle,
147
147
  endAngle,
@@ -180,7 +180,7 @@ describe('ArcLabel', () => {
180
180
  childComponents: [
181
181
  {
182
182
  component: ArcLabel,
183
- props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
183
+ props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
184
184
  centroid,
185
185
  startAngle,
186
186
  endAngle,
@@ -210,7 +210,7 @@ describe('ArcLabel', () => {
210
210
  childComponents: [
211
211
  {
212
212
  component: ArcLabel,
213
- props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps }) => ({
213
+ props: ({ centroid, startAngle, endAngle, innerRadius, outerRadius, getArcTextProps, }) => ({
214
214
  centroid,
215
215
  startAngle,
216
216
  endAngle,
@@ -33,10 +33,18 @@
33
33
  rule?: boolean | Partial<ComponentProps<typeof Rule>>;
34
34
 
35
35
  /**
36
- * Draw grid lines
36
+ * Draw grid lines. Pass props (class, style, stroke, strokeWidth, opacity,
37
+ * dashArray) to forward onto the underlying grid line.
37
38
  * @default false
38
39
  */
39
- grid?: boolean | Pick<SVGAttributes<SVGElement>, 'class' | 'style'>;
40
+ grid?:
41
+ | boolean
42
+ | (Pick<SVGAttributes<SVGElement>, 'class' | 'style'> & {
43
+ stroke?: string;
44
+ strokeWidth?: number;
45
+ opacity?: number;
46
+ dashArray?: number | number[] | string;
47
+ });
40
48
 
41
49
  /**
42
50
  * Control the number of ticks
@@ -28,10 +28,16 @@ export type AxisPropsWithoutHTML<In extends Transition = Transition> = {
28
28
  */
29
29
  rule?: boolean | Partial<ComponentProps<typeof Rule>>;
30
30
  /**
31
- * Draw grid lines
31
+ * Draw grid lines. Pass props (class, style, stroke, strokeWidth, opacity,
32
+ * dashArray) to forward onto the underlying grid line.
32
33
  * @default false
33
34
  */
34
- grid?: boolean | Pick<SVGAttributes<SVGElement>, 'class' | 'style'>;
35
+ grid?: boolean | (Pick<SVGAttributes<SVGElement>, 'class' | 'style'> & {
36
+ stroke?: string;
37
+ strokeWidth?: number;
38
+ opacity?: number;
39
+ dashArray?: number | number[] | string;
40
+ });
35
41
  /**
36
42
  * Control the number of ticks
37
43
  */
@@ -105,14 +105,13 @@
105
105
  import { greatestAbs } from '@layerstack/utils';
106
106
 
107
107
  import Rect from './Rect.svelte';
108
- import Path from './Path.svelte';
109
108
 
110
109
  import { isScaleBand, isScaleTime } from '../utils/scales.svelte.js';
111
110
  import { accessor, type Accessor } from '../utils/common.js';
112
111
  import { getChartContext } from '../contexts/chart.js';
113
112
  import type { CommonEvents, CommonStyleProps, Without } from '../utils/types.js';
114
113
  import { extractLayerProps } from '../utils/attributes.js';
115
- import { extractTweenConfig, type MotionProp } from '../utils/motion.svelte.js';
114
+ import { type MotionProp } from '../utils/motion.svelte.js';
116
115
  import Arc from './Arc.svelte';
117
116
 
118
117
  const ctx = getChartContext();
@@ -254,41 +253,31 @@
254
253
  const topRight = $derived(['all', 'top', 'right', 'top-right'].includes(rounded));
255
254
  const bottomLeft = $derived(['all', 'bottom', 'left', 'bottom-left'].includes(rounded));
256
255
  const bottomRight = $derived(['all', 'bottom', 'right', 'bottom-right'].includes(rounded));
257
- const width = $derived(dimensions.width);
258
- const height = $derived(dimensions.height);
259
256
 
260
- // Clamp radius to prevent extending beyond bounding box
261
- const r = $derived(Math.min(radius, width / 2, height / 2));
262
- const diameter = $derived(2 * r);
257
+ // Per-corner radii: [tl, tr, br, bl], matching CSS `border-radius` shorthand.
258
+ const corners = $derived<[number, number, number, number]>([
259
+ topLeft ? radius : 0,
260
+ topRight ? radius : 0,
261
+ bottomRight ? radius : 0,
262
+ bottomLeft ? radius : 0,
263
+ ]);
263
264
 
264
265
  // Auto-compute initial values for mount animation when motion is configured
265
266
  const resolvedInitialY = $derived(
266
- initialY ?? (motion && ctx.valueAxis === 'y' ? Math.max(ctx.yRange[0], ctx.yRange[1]) : undefined)
267
+ initialY ??
268
+ (motion && ctx.valueAxis === 'y' ? Math.max(ctx.yRange[0], ctx.yRange[1]) : undefined)
267
269
  );
268
270
  const resolvedInitialHeight = $derived(
269
271
  initialHeight ?? (motion && ctx.valueAxis === 'y' ? 0 : undefined)
270
272
  );
271
273
  const resolvedInitialX = $derived(
272
- initialX ?? (motion && ctx.valueAxis === 'x' ? Math.min(ctx.xRange[0], ctx.xRange[1]) : undefined)
274
+ initialX ??
275
+ (motion && ctx.valueAxis === 'x' ? Math.min(ctx.xRange[0], ctx.xRange[1]) : undefined)
273
276
  );
274
277
  const resolvedInitialWidth = $derived(
275
278
  initialWidth ?? (motion && ctx.valueAxis === 'x' ? 0 : undefined)
276
279
  );
277
280
 
278
- const pathData = $derived(
279
- `M${dimensions.x + r},${dimensions.y} h${width - diameter}
280
- ${topRight ? `a${r},${r} 0 0 1 ${r},${r}` : `h${r}v${r}`}
281
- v${height - diameter}
282
- ${bottomRight ? `a${r},${r} 0 0 1 ${-r},${r}` : `v${r}h${-r}`}
283
- h${diameter - width}
284
- ${bottomLeft ? `a${r},${r} 0 0 1 ${-r},${-r}` : `h${-r}v${-r}`}
285
- v${diameter - height}
286
- ${topLeft ? `a${r},${r} 0 0 1 ${r},${-r}` : `v${-r}h${r}`}
287
- z`
288
- .split('\n')
289
- .join('')
290
- );
291
-
292
281
  const onPointerEnter: PointerEventHandler<Element> = (e) => {
293
282
  onpointerenter?.(e);
294
283
  if (tooltip) ctx.tooltip.show(e, data);
@@ -322,14 +311,14 @@
322
311
  onpointerleave={onPointerLeave}
323
312
  {...extractLayerProps(restProps, 'lc-bar')}
324
313
  />
325
- {:else if rounded === 'all' || rounded === 'none' || radius === 0}
314
+ {:else}
326
315
  <Rect
327
316
  {fill}
328
317
  {fillOpacity}
329
318
  {stroke}
330
319
  {strokeWidth}
331
320
  {opacity}
332
- rx={rounded === 'none' ? 0 : radius}
321
+ {corners}
333
322
  {motion}
334
323
  initialX={resolvedInitialX}
335
324
  initialY={resolvedInitialY}
@@ -341,19 +330,4 @@
341
330
  onpointerleave={onPointerLeave}
342
331
  {...extractLayerProps(restProps, 'lc-bar')}
343
332
  />
344
- {:else}
345
- {@const tweenMotion = extractTweenConfig(motion)}
346
- <Path
347
- {pathData}
348
- {fill}
349
- {fillOpacity}
350
- {stroke}
351
- {strokeWidth}
352
- {opacity}
353
- motion={tweenMotion}
354
- onpointerenter={onPointerEnter}
355
- onpointermove={onPointerMove}
356
- onpointerleave={onPointerLeave}
357
- {...extractLayerProps(restProps, 'lc-bar')}
358
- />
359
333
  {/if}
@@ -134,21 +134,13 @@
134
134
  });
135
135
 
136
136
  // Resolve final statistics: explicit props take priority over computed values
137
- const minVal = $derived(
138
- minProp != null ? accessor(minProp)(data) : computedStats?.min
139
- );
140
- const q1Val = $derived(
141
- q1Prop != null ? accessor(q1Prop)(data) : computedStats?.q1
142
- );
137
+ const minVal = $derived(minProp != null ? accessor(minProp)(data) : computedStats?.min);
138
+ const q1Val = $derived(q1Prop != null ? accessor(q1Prop)(data) : computedStats?.q1);
143
139
  const medianVal = $derived(
144
140
  medianProp != null ? accessor(medianProp)(data) : computedStats?.median
145
141
  );
146
- const q3Val = $derived(
147
- q3Prop != null ? accessor(q3Prop)(data) : computedStats?.q3
148
- );
149
- const maxVal = $derived(
150
- maxProp != null ? accessor(maxProp)(data) : computedStats?.max
151
- );
142
+ const q3Val = $derived(q3Prop != null ? accessor(q3Prop)(data) : computedStats?.q3);
143
+ const maxVal = $derived(maxProp != null ? accessor(maxProp)(data) : computedStats?.max);
152
144
  const outliersVal = $derived<number[]>(
153
145
  outliersProp != null ? (accessor(outliersProp)(data) ?? []) : (computedStats?.outliers ?? [])
154
146
  );
@@ -4,7 +4,18 @@
4
4
 
5
5
  type BaseRectCellProps = Omit<
6
6
  RectProps,
7
- 'width' | 'height' | 'x0' | 'x1' | 'y0' | 'y1' | 'initialX' | 'initialY' | 'initialWidth' | 'initialHeight' | 'motion' | 'ref'
7
+ | 'width'
8
+ | 'height'
9
+ | 'x0'
10
+ | 'x1'
11
+ | 'y0'
12
+ | 'y1'
13
+ | 'initialX'
14
+ | 'initialY'
15
+ | 'initialWidth'
16
+ | 'initialHeight'
17
+ | 'motion'
18
+ | 'ref'
8
19
  >;
9
20
 
10
21
  export type CellProps = BaseRectCellProps & {
@@ -36,13 +47,7 @@
36
47
  import Circle from './Circle.svelte';
37
48
  import Group from './Group.svelte';
38
49
 
39
- let {
40
- shape = 'rect',
41
- r,
42
- x,
43
- y,
44
- ...restProps
45
- }: CellProps = $props();
50
+ let { shape = 'rect', r, x, y, ...restProps }: CellProps = $props();
46
51
 
47
52
  const chartCtx = getChartContext();
48
53
  const cellWidth = $derived(isScaleBand(chartCtx.xScale) ? chartCtx.xScale.bandwidth() : 0);
@@ -22,7 +22,11 @@
22
22
  import { geoFitObjectTransform } from '../utils/geo.js';
23
23
  import TransformContext from './TransformContext.svelte';
24
24
  import BrushContext from './BrushContext.svelte';
25
- import { type BrushDomainType, type BrushState, expandBandBrushDomain } from '../states/brush.svelte.js';
25
+ import {
26
+ type BrushDomainType,
27
+ type BrushState,
28
+ expandBandBrushDomain,
29
+ } from '../states/brush.svelte.js';
26
30
 
27
31
  import { setChartContext } from '../contexts/chart.js';
28
32
  import { ChartState } from '../states/chart.svelte.js';
@@ -741,7 +745,8 @@
741
745
 
742
746
  // Resolve which projection properties the transform state applies to
743
747
  const resolvedApply = $derived.by(() => {
744
- if (transform?.mode !== 'projection') return { rotation: false, scale: false, translate: false };
748
+ if (transform?.mode !== 'projection')
749
+ return { rotation: false, scale: false, translate: false };
745
750
 
746
751
  // Auto-detect globe projections from clipAngle (flat projections return 0, globes return > 0)
747
752
  let isGlobe = false;
@@ -773,7 +778,10 @@
773
778
  });
774
779
 
775
780
  const initialTransform = $derived(
776
- transform?.mode === 'projection' && (resolvedApply.translate || resolvedApply.scale) && geo?.fitGeojson && geo?.projection
781
+ transform?.mode === 'projection' &&
782
+ (resolvedApply.translate || resolvedApply.scale) &&
783
+ geo?.fitGeojson &&
784
+ geo?.projection
777
785
  ? geoFitObjectTransform(
778
786
  geo.projection(),
779
787
  [chartState.width, chartState.height],
@@ -827,7 +835,9 @@
827
835
  axisScale: number,
828
836
  dimension: number,
829
837
  baseDomain: number[],
830
- extent: { min?: number | Date | 'data'; max?: number | Date | 'data'; minRange?: number } | undefined
838
+ extent:
839
+ | { min?: number | Date | 'data'; max?: number | Date | 'data'; minRange?: number }
840
+ | undefined
831
841
  ): number => {
832
842
  if (!extent || baseDomain.length < 2 || dimension <= 0) return axisTranslate;
833
843
 
@@ -844,13 +854,17 @@
844
854
 
845
855
  // Normalize reversed domains by flipping the translate
846
856
  const reversed = rawD0 > rawD1;
847
- const normTranslate = reversed ? dimension * axisScale - axisTranslate - dimension : axisTranslate;
857
+ const normTranslate = reversed
858
+ ? dimension * axisScale - axisTranslate - dimension
859
+ : axisTranslate;
848
860
  const numMin = Math.min(rawD0, rawD1);
849
861
 
850
862
  const rawMinVal = resolveValue(extent.min, baseDomain[0]);
851
863
  const rawMaxVal = resolveValue(extent.max, baseDomain[1]);
852
- const minVal = rawMinVal != null && rawMaxVal != null ? Math.min(rawMinVal, rawMaxVal) : rawMinVal;
853
- const maxVal = rawMinVal != null && rawMaxVal != null ? Math.max(rawMinVal, rawMaxVal) : rawMaxVal;
864
+ const minVal =
865
+ rawMinVal != null && rawMaxVal != null ? Math.min(rawMinVal, rawMaxVal) : rawMinVal;
866
+ const maxVal =
867
+ rawMinVal != null && rawMaxVal != null ? Math.max(rawMinVal, rawMaxVal) : rawMaxVal;
854
868
 
855
869
  // Current visible domain from translate/scale
856
870
  const f0 = -normTranslate / axisScale / dimension;
@@ -869,11 +883,15 @@
869
883
  // Enforce domain min/max (pan boundaries)
870
884
  if (minVal != null && visMin < minVal) {
871
885
  visMin = minVal;
872
- visMax = visMin + (extent.minRange != null && visRange < extent.minRange ? extent.minRange : visRange);
886
+ visMax =
887
+ visMin +
888
+ (extent.minRange != null && visRange < extent.minRange ? extent.minRange : visRange);
873
889
  }
874
890
  if (maxVal != null && visMax > maxVal) {
875
891
  visMax = maxVal;
876
- visMin = visMax - (extent.minRange != null && visRange < extent.minRange ? extent.minRange : visRange);
892
+ visMin =
893
+ visMax -
894
+ (extent.minRange != null && visRange < extent.minRange ? extent.minRange : visRange);
877
895
  if (minVal != null && visMin < minVal) visMin = minVal;
878
896
  }
879
897
 
@@ -930,10 +948,9 @@
930
948
 
931
949
  // Whether this is a band scale domain transform (affects scaleExtent and constrain defaults)
932
950
  const isBandDomainTransform = $derived(
933
- transform?.mode === 'domain' && (
934
- ((transform.axis ?? 'both') !== 'y' && isScaleBand(chartState._xScaleProp)) ||
935
- ((transform.axis ?? 'both') !== 'x' && isScaleBand(chartState._yScaleProp))
936
- )
951
+ transform?.mode === 'domain' &&
952
+ (((transform.axis ?? 'both') !== 'y' && isScaleBand(chartState._xScaleProp)) ||
953
+ ((transform.axis ?? 'both') !== 'x' && isScaleBand(chartState._yScaleProp)))
937
954
  );
938
955
 
939
956
  // For projection mode, scaleExtent is relative to the initial fitted scale (like d3-zoom).
@@ -942,10 +959,10 @@
942
959
  const resolvedScaleExtent = $derived.by(() => {
943
960
  if (transform?.mode === 'projection' && transform?.scaleExtent && initialTransform) {
944
961
  const baseScale = initialTransform.scale;
945
- return [
946
- transform.scaleExtent[0] * baseScale,
947
- transform.scaleExtent[1] * baseScale,
948
- ] as [number, number];
962
+ return [transform.scaleExtent[0] * baseScale, transform.scaleExtent[1] * baseScale] as [
963
+ number,
964
+ number,
965
+ ];
949
966
  }
950
967
  if (!isBandDomainTransform) return transform?.scaleExtent;
951
968
  const userExtent = transform?.scaleExtent;
@@ -972,7 +989,12 @@
972
989
  // The viewport (at current zoom) must overlap with the translateExtent world bounds.
973
990
  // As zoom increases, the allowed translate range grows proportionally.
974
991
  const projectionTranslateConstrain = $derived.by(() => {
975
- if (transform?.mode !== 'projection' || !transform?.translateExtent || !initialTransform || resolvedApply.rotation) {
992
+ if (
993
+ transform?.mode !== 'projection' ||
994
+ !transform?.translateExtent ||
995
+ !initialTransform ||
996
+ resolvedApply.rotation
997
+ ) {
976
998
  return undefined;
977
999
  }
978
1000
 
@@ -998,10 +1020,8 @@
998
1020
  // Default constrain for band scale domain transforms: prevent panning past data boundaries
999
1021
  const bandScaleConstrain = $derived.by(() => {
1000
1022
  if (!isBandDomainTransform) return undefined;
1001
- const xIsBand =
1002
- (transform!.axis ?? 'both') !== 'y' && isScaleBand(chartState._xScaleProp);
1003
- const yIsBand =
1004
- (transform!.axis ?? 'both') !== 'x' && isScaleBand(chartState._yScaleProp);
1023
+ const xIsBand = (transform!.axis ?? 'both') !== 'y' && isScaleBand(chartState._xScaleProp);
1024
+ const yIsBand = (transform!.axis ?? 'both') !== 'x' && isScaleBand(chartState._yScaleProp);
1005
1025
 
1006
1026
  return (t: { scale: number; translate: { x: number; y: number } }) => {
1007
1027
  let { scale, translate } = t;
@@ -1021,7 +1041,17 @@
1021
1041
  // Compose user-provided constrain with domainExtent constrain and band scale constrain
1022
1042
  const composedConstrain = $derived.by(() => {
1023
1043
  const userConstrain = transform?.constrain;
1024
- const constrains = [bandScaleConstrain, domainExtentConstrain, projectionTranslateConstrain, userConstrain].filter(Boolean) as Array<(t: { scale: number; translate: { x: number; y: number } }) => { scale: number; translate: { x: number; y: number } }>;
1044
+ const constrains = [
1045
+ bandScaleConstrain,
1046
+ domainExtentConstrain,
1047
+ projectionTranslateConstrain,
1048
+ userConstrain,
1049
+ ].filter(Boolean) as Array<
1050
+ (t: { scale: number; translate: { x: number; y: number } }) => {
1051
+ scale: number;
1052
+ translate: { x: number; y: number };
1053
+ }
1054
+ >;
1025
1055
  if (constrains.length === 0) return undefined;
1026
1056
  if (constrains.length === 1) return constrains[0];
1027
1057
  return (t: { scale: number; translate: { x: number; y: number } }) => {
@@ -1089,7 +1119,14 @@
1089
1119
  >
1090
1120
  {#key chartState.isMounted}
1091
1121
  <!-- svelte-ignore ownership_invalid_binding -->
1092
- {@const { domainExtent: _de, constrain: _uc, apply: _apply, scaleExtent: _se, translateExtent: _te, ...transformProps } = transform ?? {}}
1122
+ {@const {
1123
+ domainExtent: _de,
1124
+ constrain: _uc,
1125
+ apply: _apply,
1126
+ scaleExtent: _se,
1127
+ translateExtent: _te,
1128
+ ...transformProps
1129
+ } = transform ?? {}}
1093
1130
  <TransformContext
1094
1131
  bind:state={chartState.transformState}
1095
1132
  mode={transform?.mode ?? 'none'}
@@ -1100,7 +1137,9 @@
1100
1137
  scaleExtent={resolvedScaleExtent}
1101
1138
  translateExtent={resolvedTranslateExtent}
1102
1139
  constrain={composedConstrain}
1103
- disablePointer={(brush === true || (typeof brush === 'object' && !brush.disabled)) || transform?.disablePointer}
1140
+ disablePointer={brush === true ||
1141
+ (typeof brush === 'object' && !brush.disabled) ||
1142
+ transform?.disablePointer}
1104
1143
  {ondragstart}
1105
1144
  {onTransform}
1106
1145
  {ondragend}
@@ -1108,7 +1147,11 @@
1108
1147
  <!-- svelte-ignore ownership_invalid_binding -->
1109
1148
  <BrushContext {...enhancedBrushProps} bind:state={chartState.brushState}>
1110
1149
  <!-- svelte-ignore ownership_invalid_binding -->
1111
- <TooltipContext onclick={onTooltipClick} {...getObjectOrNull(tooltipContext)} bind:state={chartState.tooltipState}>
1150
+ <TooltipContext
1151
+ onclick={onTooltipClick}
1152
+ {...getObjectOrNull(tooltipContext)}
1153
+ bind:state={chartState.tooltipState}
1154
+ >
1112
1155
  <ChartChildren {children} {tooltipContext} {...restProps} />
1113
1156
  </TooltipContext>
1114
1157
  </BrushContext>
@@ -195,7 +195,12 @@
195
195
  {#if typeof grid === 'function'}
196
196
  {@render grid(snippetProps)}
197
197
  {:else if grid}
198
- <Grid x={context.valueAxis === 'x' || context.radial} y={context.valueAxis === 'y' || context.radial} {...getObjectOrNull(grid)} {...props.grid} />
198
+ <Grid
199
+ x={context.valueAxis === 'x' || context.radial}
200
+ y={context.valueAxis === 'y' || context.radial}
201
+ {...getObjectOrNull(grid)}
202
+ {...props.grid}
203
+ />
199
204
  {/if}
200
205
 
201
206
  <ChartClipPath disabled={!context.props.brush && context.transformState?.mode !== 'domain'}>
@@ -212,7 +217,12 @@
212
217
  {#if typeof rule === 'function'}
213
218
  {@render rule(snippetProps)}
214
219
  {:else if rule}
215
- <Rule x={context.valueAxis === 'x' ? 0 : false} y={context.valueAxis === 'y' ? 0 : false} {...getObjectOrNull(rule)} {...props.rule} />
220
+ <Rule
221
+ x={context.valueAxis === 'x' ? 0 : false}
222
+ y={context.valueAxis === 'y' ? 0 : false}
223
+ {...getObjectOrNull(rule)}
224
+ {...props.rule}
225
+ />
216
226
  {/if}
217
227
  {:else if axis}
218
228
  {#if axis !== 'x'}
@@ -236,12 +246,20 @@
236
246
  {#if typeof rule === 'function'}
237
247
  {@render rule(snippetProps)}
238
248
  {:else if rule}
239
- <Rule x={context.valueAxis === 'x' ? 0 : false} y={context.valueAxis === 'y' ? 0 : false} {...getObjectOrNull(rule)} {...props.rule} />
249
+ <Rule
250
+ x={context.valueAxis === 'x' ? 0 : false}
251
+ y={context.valueAxis === 'y' ? 0 : false}
252
+ {...getObjectOrNull(rule)}
253
+ {...props.rule}
254
+ />
240
255
  {/if}
241
256
  {/if}
242
257
 
243
258
  <!-- Use `full` to allow labels on edge to not be cropped (bleed into padding) -->
244
- <ChartClipPath disabled={!context.props.brush && context.transformState?.mode !== 'domain'} full>
259
+ <ChartClipPath
260
+ disabled={!context.props.brush && context.transformState?.mode !== 'domain'}
261
+ full
262
+ >
245
263
  {#if typeof points === 'function'}
246
264
  {@render points(snippetProps)}
247
265
  {:else if points}