layerchart 2.0.0-next.56 → 2.0.0-next.57

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.
@@ -73,20 +73,31 @@
73
73
  const ctx = getChartContext();
74
74
 
75
75
  const rect = $derived.by(() => {
76
- const x0 = x ? ctx.xScale(x[0] ?? ctx.xDomain[0]) : ctx.xRange[0];
77
- const x1 = x ? ctx.xScale(x[1] ?? ctx.xDomain[1]) : ctx.xRange[1];
78
- const y0 = y ? ctx.yScale(y[0] ?? ctx.yDomain[0]) : ctx.yRange[0];
79
- const y1 = y ? ctx.yScale(y[1] ?? ctx.yDomain[1]) : ctx.yRange[1];
76
+ // `null`/`undefined` on either side means "extend to chart edge" (xRange/yRange).
77
+ // Tracked so band adjustments only apply to endpoints derived from the band scale,
78
+ // not to the xRange fallback (which already contains the full pixel extent).
79
+ const x0FromScale = x?.[0] != null;
80
+ const x1FromScale = x?.[1] != null;
81
+ const x0 = x0FromScale ? ctx.xScale(x![0]) : ctx.xRange[0];
82
+ const x1 = x1FromScale ? ctx.xScale(x![1]) : ctx.xRange[1];
83
+ const y0 = y?.[0] != null ? ctx.yScale(y[0]) : ctx.yRange[0];
84
+ const y1 = y?.[1] != null ? ctx.yScale(y[1]) : ctx.yRange[1];
80
85
 
81
86
  const bandPadding = isScaleBand(ctx.xScale)
82
87
  ? (ctx.xScale.padding() * ctx.xScale.step()) / 2
83
88
  : 0;
84
89
  const bandStep = isScaleBand(ctx.xScale) ? ctx.xScale.step() : 0;
85
90
 
91
+ const leftFromScale = x0 <= x1 ? x0FromScale : x1FromScale;
92
+ const rightFromScale = x0 <= x1 ? x1FromScale : x0FromScale;
93
+
94
+ const left = Math.min(x0, x1) - (leftFromScale ? bandPadding : 0);
95
+ const right = Math.max(x0, x1) + (rightFromScale ? bandStep - bandPadding : 0);
96
+
86
97
  return {
87
- x: Math.min(x0, x1) - bandPadding,
98
+ x: left,
88
99
  y: Math.min(y0, y1),
89
- width: Math.abs(x1 - x0) + bandStep,
100
+ width: right - left,
90
101
  height: Math.abs(y1 - y0),
91
102
  } satisfies ComponentProps<typeof Rect>;
92
103
  });
@@ -301,9 +301,9 @@
301
301
  pathData={seg.d}
302
302
  stroke={seg.stroke}
303
303
  fill={seg.fill}
304
+ opacity={seg.opacity ?? seriesOpacity}
304
305
  {...series?.props}
305
306
  {...restProps}
306
- opacity={seg.opacity ?? seriesOpacity}
307
307
  />
308
308
  {/each}
309
309
  {:else}
@@ -311,8 +311,8 @@
311
311
  pathData={isTweened ? tweenState.current : d}
312
312
  stroke={(typeof stroke === 'string' ? stroke : undefined) ?? series?.color}
313
313
  fill={typeof fill === 'string' ? fill : undefined}
314
+ opacity={(typeof opacity === 'number' ? opacity : undefined) ?? seriesOpacity}
314
315
  {...series?.props}
315
316
  {...restProps}
316
- opacity={(typeof opacity === 'number' ? opacity : undefined) ?? seriesOpacity}
317
317
  />
318
318
  {/if}
@@ -678,8 +678,13 @@ export class ChartState {
678
678
  rDomain = $derived(calcDomain('r', this.extents, this.props.rDomain));
679
679
  x1Domain = $derived.by(() => {
680
680
  if (this.props.x1Domain) {
681
- const visibleKeys = new Set(this.seriesState.visibleSeries.map((s) => s.key));
682
- return this.props.x1Domain.filter((key) => visibleKeys.has(key));
681
+ // Only filter by visible series when series are configured — otherwise the
682
+ // full x1Domain is used as-is (composable charts without series).
683
+ if (this.seriesState.series.length > 0) {
684
+ const visibleKeys = new Set(this.seriesState.visibleSeries.map((s) => s.key));
685
+ return this.props.x1Domain.filter((key) => visibleKeys.has(key));
686
+ }
687
+ return this.props.x1Domain;
683
688
  }
684
689
  // Auto-derive for grouped series when x is the category axis
685
690
  if (this.props.seriesLayout === 'group' && this.valueAxis === 'y') {
@@ -692,8 +697,13 @@ export class ChartState {
692
697
  });
693
698
  y1Domain = $derived.by(() => {
694
699
  if (this.props.y1Domain) {
695
- const visibleKeys = new Set(this.seriesState.visibleSeries.map((s) => s.key));
696
- return this.props.y1Domain.filter((key) => visibleKeys.has(key));
700
+ // Only filter by visible series when series are configured — otherwise the
701
+ // full y1Domain is used as-is (composable charts without series).
702
+ if (this.seriesState.series.length > 0) {
703
+ const visibleKeys = new Set(this.seriesState.visibleSeries.map((s) => s.key));
704
+ return this.props.y1Domain.filter((key) => visibleKeys.has(key));
705
+ }
706
+ return this.props.y1Domain;
697
707
  }
698
708
  // Auto-derive for grouped series when y is the category axis
699
709
  if (this.props.seriesLayout === 'group' && this.valueAxis === 'x') {
@@ -1406,3 +1406,49 @@ describe('ChartState group layout auto-derives x1/y1', () => {
1406
1406
  }
1407
1407
  });
1408
1408
  });
1409
+ describe('ChartState x1Domain/y1Domain without series', () => {
1410
+ const longData = [
1411
+ { year: 2019, fruit: 'apples', value: 3840 },
1412
+ { year: 2019, fruit: 'bananas', value: 1920 },
1413
+ { year: 2018, fruit: 'apples', value: 1600 },
1414
+ { year: 2018, fruit: 'bananas', value: 1440 },
1415
+ ];
1416
+ it('should pass through explicit x1Domain when no series are configured', () => {
1417
+ const { state, cleanup } = createChartState({
1418
+ data: longData,
1419
+ x: 'year',
1420
+ xScale: scaleBand(),
1421
+ y: 'value',
1422
+ x1: 'fruit',
1423
+ x1Domain: ['apples', 'bananas'],
1424
+ x1Range: ({ xScale }) => [0, xScale.bandwidth()],
1425
+ });
1426
+ try {
1427
+ expect(state.seriesState.series).toHaveLength(0);
1428
+ expect(state.x1Domain).toEqual(['apples', 'bananas']);
1429
+ expect(state.x1Scale.domain()).toEqual(['apples', 'bananas']);
1430
+ }
1431
+ finally {
1432
+ cleanup();
1433
+ }
1434
+ });
1435
+ it('should pass through explicit y1Domain when no series are configured', () => {
1436
+ const { state, cleanup } = createChartState({
1437
+ data: longData,
1438
+ y: 'year',
1439
+ yScale: scaleBand(),
1440
+ x: 'value',
1441
+ y1: 'fruit',
1442
+ y1Domain: ['apples', 'bananas'],
1443
+ y1Range: ({ yScale }) => [0, yScale.bandwidth()],
1444
+ });
1445
+ try {
1446
+ expect(state.seriesState.series).toHaveLength(0);
1447
+ expect(state.y1Domain).toEqual(['apples', 'bananas']);
1448
+ expect(state.y1Scale.domain()).toEqual(['apples', 'bananas']);
1449
+ }
1450
+ finally {
1451
+ cleanup();
1452
+ }
1453
+ });
1454
+ });
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
7
  "homepage": "https://layerchart.com",
8
- "version": "2.0.0-next.56",
8
+ "version": "2.0.0-next.57",
9
9
  "devDependencies": {
10
10
  "@changesets/cli": "^2.30.0",
11
11
  "@napi-rs/canvas": "^0.1.97",