Skip to content

Commit 9bdd4c4

Browse files
committed
Support tickSpacing for band scales on Axis, thinning tick labels when the domain is larger than the available space. Automatically shows more tick labels when zoomed in on band scale transforms.
1 parent ccbef88 commit 9bdd4c4

5 files changed

Lines changed: 41 additions & 9 deletions

File tree

.changeset/band-tick-spacing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'layerchart': patch
3+
---
4+
5+
Support `tickSpacing` for band scales on Axis, thinning tick labels when the domain is larger than the available space. Automatically shows more tick labels when zoomed in on band scale transforms.

docs/src/content/components/Axis.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ related: [Grid, Rule]
1111

1212
### tickSpacing
1313

14-
If using a continuous scales (ex. linear, time) and ticks become too crowded, you can use `tickSpacing` to control the number of pixels alloted for each tick (higher => fewer ticks).
14+
Controls the number of pixels allotted for each tick (higher => fewer ticks). Works with both continuous scales (linear, time) and band scales. When zoomed in on a band scale, tick labels automatically adjust to show more as bands get wider.
1515

1616
::note
1717
Default: `80` for horizontal axes (top/bottom/angle) and `50` for vertical axes (left/right/radius).

packages/layerchart/src/lib/components/Axis.svelte

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,22 @@
220220
: null
221221
);
222222
223+
// For band scales with domain-mode transform, scale up effective size by the zoom factor
224+
// so that more tick labels appear as bands get wider when zoomed in
225+
const effectiveSize = $derived.by(() => {
226+
if (!ctxSize) return ctxSize;
227+
const ts = ctx.transformState;
228+
if (ts?.mode === 'domain' && isScaleBand(scale) && ts.scale > 1) {
229+
return ctxSize * ts.scale;
230+
}
231+
return ctxSize;
232+
});
233+
223234
const tickCount = $derived(
224235
typeof ticks === 'number'
225236
? ticks
226-
: tickSpacing && ctxSize
227-
? Math.round(ctxSize / tickSpacing)
237+
: tickSpacing && effectiveSize
238+
? Math.round(effectiveSize / tickSpacing)
228239
: undefined
229240
);
230241
const tickVals = $derived.by(() => {

packages/layerchart/src/lib/utils/ticks.test.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,24 @@ describe('autoTickVals', () => {
3434
expect(autoTickVals(scale, ticksConfig)).toEqual([]);
3535
});
3636

37-
it('filters band scale domain with number ticks', () => {
37+
it('filters band scale domain with explicit number ticks', () => {
3838
const scale = { domain: mockDomain, bandwidth: vi.fn() } as any;
39-
expect(autoTickVals(scale, 2)).toEqual(['a', 'c', 'e']);
39+
// ticks=2, domain has 5 items → step = ceil(5/2) = 3 → indices 0, 3
40+
expect(autoTickVals(scale, 2)).toEqual(['a', 'd']);
4041
});
4142

42-
it('returns full domain for band scale without ticks', () => {
43+
it('filters band scale domain using count parameter (from tickSpacing)', () => {
44+
const scale = { domain: mockDomain, bandwidth: vi.fn() } as any;
45+
// count=3, domain has 5 items → step = ceil(5/3) = 2 → indices 0, 2, 4
46+
expect(autoTickVals(scale, undefined, 3)).toEqual(['a', 'c', 'e']);
47+
});
48+
49+
it('returns full domain for band scale when count >= domain length', () => {
50+
const scale = { domain: mockDomain, bandwidth: vi.fn() } as any;
51+
expect(autoTickVals(scale, undefined, 10)).toEqual(['a', 'b', 'c', 'd', 'e']);
52+
});
53+
54+
it('returns full domain for band scale without ticks or count', () => {
4355
const scale = { domain: mockDomain, bandwidth: vi.fn() } as any;
4456
expect(autoTickVals(scale)).toEqual(['a', 'b', 'c', 'd', 'e']);
4557
});

packages/layerchart/src/lib/utils/ticks.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,13 @@ export function autoTickVals(scale: AnyScale, ticks?: TicksConfig, count?: numbe
139139

140140
// Band (use domain)
141141
if (isScaleBand(scale)) {
142-
return ticks && typeof ticks === 'number'
143-
? scale.domain().filter((_, i) => i % ticks === 0)
144-
: scale.domain();
142+
const domain = scale.domain();
143+
const resolvedCount = typeof ticks === 'number' ? ticks : count;
144+
if (resolvedCount != null && resolvedCount < domain.length) {
145+
const step = Math.max(1, Math.ceil(domain.length / resolvedCount));
146+
return domain.filter((_, i) => i % step === 0);
147+
}
148+
return domain;
145149
}
146150

147151
// Ticks from scale

0 commit comments

Comments
 (0)