Skip to content

Commit d8b2190

Browse files
committed
[Brush] Begin to support scaleBand() (Bar charts)
1 parent b380b36 commit d8b2190

File tree

3 files changed

+75
-7
lines changed

3 files changed

+75
-7
lines changed

.changeset/metal-windows-wait.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'layerchart': minor
3+
---
4+
5+
[Brush] Support scaleBand() (Bar charts)

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { chartContext } from './ChartContext.svelte';
88
import Frame from './Frame.svelte';
99
import { localPoint } from '$lib/utils/event.js';
10+
import { scaleInvert } from '$lib/utils/scales.js';
1011
import Group from './Group.svelte';
1112
1213
const { xScale, yScale, width, height, padding } = chartContext();
@@ -68,21 +69,22 @@
6869
) => void
6970
) {
7071
return (e: PointerEvent) => {
72+
// TODO: Handle scaleBand domains
7173
const start = {
7274
xDomain: [xDomain[0] ?? xDomainMin, xDomain[1] ?? xDomainMax] as [number, number],
7375
yDomain: [yDomain[0] ?? yDomainMin, yDomain[1] ?? yDomainMax] as [number, number],
7476
value: {
75-
x: $xScale.invert?.(localPoint(frameEl, e)?.x ?? 0 - $padding.left),
76-
y: $yScale.invert?.(localPoint(frameEl, e)?.y ?? 0 - $padding.top),
77+
x: scaleInvert($xScale, localPoint(frameEl, e)?.x ?? 0 - $padding.left),
78+
y: scaleInvert($yScale, localPoint(frameEl, e)?.y ?? 0 - $padding.top),
7779
},
7880
};
7981
8082
dispatch('brushStart', { xDomain, yDomain });
8183
8284
const onPointerMove = (e: PointerEvent) => {
8385
fn(start, {
84-
x: $xScale.invert?.(localPoint(frameEl, e)?.x ?? 0 - $padding.left),
85-
y: $yScale.invert?.(localPoint(frameEl, e)?.y ?? 0 - $padding.top),
86+
x: scaleInvert($xScale, localPoint(frameEl, e)?.x ?? 0 - $padding.left),
87+
y: scaleInvert($yScale, localPoint(frameEl, e)?.y ?? 0 - $padding.top),
8688
});
8789
8890
// if (xDomain[0] === xDomain[1] || yDomain[0] === yDomain[1]) {

packages/layerchart/src/routes/docs/components/Brush/+page.svelte

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts">
2-
import { scaleOrdinal, scaleTime } from 'd3-scale';
3-
import { range } from 'd3-array';
4-
import { PeriodType, State, cls, format } from 'svelte-ux';
2+
import { scaleBand, scaleOrdinal, scaleTime } from 'd3-scale';
3+
import { bin, range, sum } from 'd3-array';
4+
import { timeMonths } from 'd3-time';
5+
import { PeriodType, State, cls, format, formatDate } from 'svelte-ux';
56
import { subDays } from 'date-fns';
67
import { mdiChevronRight } from '@mdi/js';
78
@@ -19,6 +20,7 @@
1920
Rule,
2021
Tooltip,
2122
Svg,
23+
Bars,
2224
} from 'layerchart';
2325
2426
import Preview from '$lib/docs/Preview.svelte';
@@ -39,10 +41,69 @@
3941
const randomData = range(200).map((d) => {
4042
return { x: d, y: Math.random() };
4143
});
44+
45+
$: binByTime = bin()
46+
.thresholds((_data, min, max) => timeMonths(min, max))
47+
.value((d) => d.date);
48+
$: appleStockBinByMonth = binByTime(data.appleStock);
49+
// Simplify data for simplier brush example
50+
$: appleStockByMonth = appleStockBinByMonth.map((monthBin) => {
51+
return {
52+
date: monthBin.x0,
53+
value: sum(monthBin, (d) => d.value),
54+
};
55+
});
4256
</script>
4357

4458
<h1>Examples</h1>
4559

60+
<h2>Bar (scaleBand) support</h2>
61+
62+
<Preview data={appleStockByMonth}>
63+
<div class="border rounded p-4 grid gap-1">
64+
<State initial={[null, null]} let:value={xDomain} let:set>
65+
<div class="h-[300px]">
66+
<Chart
67+
_data={appleStockByMonth}
68+
data={appleStockByMonth.filter(
69+
(d) =>
70+
(xDomain[0] == null || d.date >= xDomain[0]) &&
71+
(xDomain[1] == null || d.date <= xDomain[1])
72+
)}
73+
x="date"
74+
xScale={scaleBand().padding(0.2)}
75+
y="value"
76+
yDomain={[0, null]}
77+
yNice
78+
padding={{ left: 24, bottom: 24 }}
79+
>
80+
<Svg>
81+
<Axis placement="left" grid rule />
82+
<Axis
83+
placement="bottom"
84+
rule
85+
ticks={(scale) => scaleTime(scale.domain(), scale.range()).ticks(4)}
86+
format={(value) => formatDate(value, PeriodType.Custom, { custom: 'MMM yyyy' })}
87+
/>
88+
<ChartClipPath>
89+
<Bars radius={4} strokeWidth={1} class="fill-primary" />
90+
</ChartClipPath>
91+
92+
<Brush
93+
axis="x"
94+
resetOnEnd
95+
on:brushEnd={(e) => {
96+
console.log(e.detail);
97+
set(e.detail.xDomain);
98+
}}
99+
/>
100+
</Svg>
101+
</Chart>
102+
</div>
103+
</State>
104+
</div>
105+
</Preview>
106+
46107
<h2>Styling via classes</h2>
47108

48109
<Preview data={data.appleStock}>

0 commit comments

Comments
 (0)