Skip to content

Commit 8c8c827

Browse files
authored
Improve compound/dual axis examples (combine and add) (#539)
* Consolidate Dual Axis examples into Compound * docs(Compound): Add example with second stacked chart with inverted range (top-down * Load hydro data from /static
1 parent 34fdfa8 commit 8c8c827

7 files changed

Lines changed: 1749 additions & 158 deletions

File tree

packages/layerchart/src/routes/_NavMenu.svelte

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
'Candlestick',
1616
'Compound',
1717
'Duration',
18-
'DualAxis',
1918
'Histogram',
2019
'Line',
2120
'Oscilloscope',

packages/layerchart/src/routes/docs/examples/Compound/+page.svelte

Lines changed: 224 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<script lang="ts">
2-
import { Area, BarChart, Spline, Tooltip } from 'layerchart';
2+
import { Area, Axis, BarChart, Chart, Highlight, Spline, Svg, Tooltip } from 'layerchart';
3+
import { scaleLinear, scaleTime } from 'd3-scale';
4+
import { extent } from 'd3-array';
35
46
import Preview from '$lib/docs/Preview.svelte';
57
import { createDateSeries } from '$lib/utils/genData.js';
@@ -17,9 +19,9 @@
1719

1820
<h1>Examples</h1>
1921

20-
<h2>Common scale</h2>
22+
<h2>Common scale with extra marks</h2>
2123

22-
<Preview {data}>
24+
<Preview data={dateSeries}>
2325
<div class="h-[300px] p-4 border rounded-sm">
2426
<BarChart
2527
data={dateSeries}
@@ -48,9 +50,9 @@
4850
</div>
4951
</Preview>
5052

51-
<h2>Stacked Charts</h2>
53+
<h2>Separate scales with stacked charts and overridden marks</h2>
5254

53-
<Preview {data}>
55+
<Preview data={data.appleTicker}>
5456
<div class="h-[300px] grid grid-stack p-4 border rounded-sm">
5557
<!-- First chart (bar), with different domain scale for volume -->
5658
<BarChart
@@ -101,3 +103,220 @@
101103
</BarChart>
102104
</div>
103105
</Preview>
106+
107+
<h2>Dual axis with single chart using remapped scale</h2>
108+
109+
<Preview data={data.newPassengerCars}>
110+
<div class="h-[300px] p-4 border rounded-sm">
111+
<!-- Remap efficiency to its equivalent value in sales - https://observablehq.com/@observablehq/plot-dual-axis -->
112+
<Chart
113+
data={data.newPassengerCars}
114+
x="year"
115+
y="sales"
116+
yDomain={[0, null]}
117+
yNice
118+
y1="efficiency"
119+
y1Scale={scaleLinear()}
120+
y1Range={({ yScale }) => yScale.domain()}
121+
padding={{ top: 24, bottom: 24, left: 24, right: 24 }}
122+
tooltip={{ mode: 'bisect-x' }}
123+
>
124+
{#snippet children({ context })}
125+
<Svg>
126+
<Axis
127+
placement="left"
128+
rule
129+
format="metric"
130+
label="↑ sales (M)"
131+
labelPlacement="start"
132+
labelProps={{ class: 'fill-primary' }}
133+
/>
134+
<Axis
135+
placement="right"
136+
scale={scaleLinear(context.y1Scale?.domain() ?? [], [context.height, 0])}
137+
ticks={context.y1Scale?.ticks?.()}
138+
rule
139+
label="efficiency (mpg) ↑"
140+
labelPlacement="start"
141+
labelProps={{ class: 'fill-secondary' }}
142+
/>
143+
<Axis placement="bottom" format="none" rule />
144+
<Spline class="stroke-2 stroke-primary" />
145+
<Spline y={(d) => context.y1Scale?.(d.efficiency)} class="stroke-2 stroke-secondary" />
146+
<Highlight lines points />
147+
<Highlight
148+
points={{ class: 'fill-secondary' }}
149+
y={(d) => context.y1Scale?.(d.efficiency)}
150+
/>
151+
</Svg>
152+
153+
<Tooltip.Root {context}>
154+
{#snippet children({ data })}
155+
<Tooltip.Header>{data.year}</Tooltip.Header>
156+
<Tooltip.List>
157+
<Tooltip.Item label="sales" value={data.sales} format="currencyRound" />
158+
<Tooltip.Item label="efficiency" value={data.efficiency} />
159+
</Tooltip.List>
160+
{/snippet}
161+
</Tooltip.Root>
162+
{/snippet}
163+
</Chart>
164+
</div>
165+
</Preview>
166+
167+
<h2>Dual axis with stacked charts</h2>
168+
169+
<Preview data={data.newPassengerCars}>
170+
<div class="h-[300px] grid grid-stack p-4 border rounded-sm">
171+
<!-- Sales chart-->
172+
<Chart
173+
data={data.newPassengerCars}
174+
x="year"
175+
y="sales"
176+
yDomain={[0, null]}
177+
yNice
178+
padding={{ top: 24, bottom: 24, left: 24, right: 24 }}
179+
>
180+
<Svg>
181+
<Axis
182+
placement="left"
183+
rule
184+
format="metric"
185+
label="↑ sales (M)"
186+
labelPlacement="start"
187+
labelProps={{ class: 'fill-primary' }}
188+
/>
189+
<Axis placement="bottom" format="none" rule />
190+
<Spline class="stroke-2 stroke-primary" />
191+
<Highlight lines points />
192+
</Svg>
193+
</Chart>
194+
195+
<!-- Efficiency chart, provides tooltip for both values -->
196+
<Chart
197+
data={data.newPassengerCars}
198+
x="year"
199+
y="efficiency"
200+
padding={{ top: 24, bottom: 24, left: 24, right: 24 }}
201+
tooltip={{ mode: 'bisect-x' }}
202+
>
203+
<Svg>
204+
<Axis
205+
placement="right"
206+
rule
207+
label="efficiency (mpg) ↑"
208+
labelPlacement="start"
209+
labelProps={{ class: 'fill-secondary' }}
210+
/>
211+
<Spline class="stroke-2 stroke-secondary" />
212+
<!-- Difficult to add points for both charts without using a remaped scale for one value -->
213+
<Highlight lines />
214+
</Svg>
215+
216+
<Tooltip.Root>
217+
{#snippet children({ data })}
218+
<Tooltip.Header>{data.year}</Tooltip.Header>
219+
<Tooltip.List>
220+
<Tooltip.Item label="sales" value={data.sales} format="currencyRound" />
221+
<Tooltip.Item label="efficiency" value={data.efficiency} />
222+
</Tooltip.List>
223+
{/snippet}
224+
</Tooltip.Root>
225+
</Chart>
226+
</div>
227+
</Preview>
228+
229+
<h2>Separate scales with stacked charts with inverted range (top down)</h2>
230+
231+
<Preview data={data.hydro}>
232+
<div class="h-[300px] grid grid-stack p-4 border rounded-sm">
233+
<!-- First chart with inverted yRange (top down) -->
234+
<BarChart
235+
data={data.hydro}
236+
x="date"
237+
y="rain"
238+
axis={{ placement: 'right', tickMarks: false }}
239+
yDomain={[0, 500]}
240+
yRange={({ height }) => [0, height]}
241+
padding={{ left: 32, right: 32, bottom: 20 }}
242+
props={{
243+
bars: {
244+
// TODO: Determine why non-rounded Rect within Bar is not working for inverted range
245+
// rounded: 'none',
246+
class: 'stroke-none fill-blue-500',
247+
},
248+
}}
249+
/>
250+
251+
<BarChart
252+
data={data.hydro}
253+
x="date"
254+
yDomain={[0, 1000]}
255+
padding={{ left: 32, right: 32, bottom: 20 }}
256+
series={[
257+
{
258+
key: 'infiltration',
259+
// TODO: Not sure what to be done with negative values
260+
// value: (d) => Math.abs(d.infiltration),
261+
value: (d) => (d.infiltration > 0 ? d.infiltration : 0),
262+
color: 'hsl(25, 95%, 53%)',
263+
props: {
264+
rounded: 'none',
265+
},
266+
},
267+
{
268+
key: 'dirtyh2o',
269+
color: 'hsl(0, 84%, 60%)',
270+
props: {
271+
rounded: 'none',
272+
},
273+
},
274+
{
275+
key: 'rain_induced',
276+
color: 'hsl(142, 71%, 45%)',
277+
props: {
278+
rounded: 'none',
279+
},
280+
},
281+
]}
282+
seriesLayout="stack"
283+
>
284+
{#snippet axis({ context })}
285+
<Axis placement="left" />
286+
<!-- Provide better axis than band scale currently does with time data-->
287+
<Axis
288+
placement="bottom"
289+
scale={scaleTime(
290+
// @ts-expect-error
291+
extent(data.hydro, (d) => d.date),
292+
[0, context.width]
293+
)}
294+
tickMultiline
295+
rule
296+
/>
297+
{/snippet}
298+
299+
{#snippet tooltip({ context })}
300+
<Tooltip.Root {context}>
301+
{#snippet children({ data })}
302+
<Tooltip.Header value={data.date} format="day" />
303+
<Tooltip.List>
304+
<Tooltip.Item label="rain" color="hsl(200 100% 50%)" value={data.rain} />
305+
<Tooltip.Item
306+
label="infiltration"
307+
color="hsl(25, 95%, 53%)"
308+
value={data.infiltration}
309+
/>
310+
<Tooltip.Item label="dirtyh2o" color="hsl(0, 84%, 60%)" value={data.dirtyh2o} />
311+
<Tooltip.Item
312+
label="rain_induced"
313+
color="hsl(142, 71%, 45%)"
314+
value={data.rain_induced}
315+
/>
316+
</Tooltip.List>
317+
{/snippet}
318+
</Tooltip.Root>
319+
{/snippet}
320+
</BarChart>
321+
</div>
322+
</Preview>

packages/layerchart/src/routes/docs/examples/Compound/+page.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import { autoType, csvParse } from 'd3-dsv';
12
import { parse } from '@layerstack/utils';
3+
24
import type { AppleTickerData } from '$static/data/examples/date/apple-ticker.js';
5+
import type { NewPassengerCars } from '$static/data/examples/new-passenger-cars.js';
6+
import type { HydroData } from '$static/data/examples/date/hydro.js';
37

48
import pageSource from './+page.svelte?raw';
59

@@ -8,6 +12,13 @@ export async function load({ fetch }) {
812
appleTicker: (await fetch('/data/examples/date/apple-ticker.json').then(async (r) =>
913
parse(await r.text())
1014
)) as AppleTickerData,
15+
newPassengerCars: await fetch('/data/examples/new-passenger-cars.csv').then(async (r) =>
16+
// @ts-expect-error
17+
csvParse<NewPassengerCars>(await r.text(), autoType)
18+
),
19+
hydro: (await fetch('/data/examples/date/hydro.json').then(async (r) =>
20+
parse(await r.text())
21+
)) as HydroData,
1122
meta: {
1223
pageSource,
1324
related: [

0 commit comments

Comments
 (0)