Skip to content

Commit 424f81c

Browse files
committed
Complete bar chart example
1 parent 185e5bb commit 424f81c

File tree

2 files changed

+133
-15
lines changed

2 files changed

+133
-15
lines changed

docs/examples/barchart/BarChart.svelte

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,71 @@
22
// let's borrow svelte's fly transitions for the circles that need to be
33
// created or destroyed
44
// https://svelte.dev/tutorial/transition
5-
// import { fly } from "svelte/transition";
5+
import { fly } from "svelte/transition";
66
77
// here we declare `data` as a prop that this component can expect
88
// https://svelte.dev/tutorial/declaring-props
9-
// export let data;
9+
// in this case we're expecting an array of up to 5 numbers as `data` and
10+
// the max chart height as `height`
11+
export let data = [50, 45, 15, 25, 30];
12+
export let height = 100;
13+
export let width = 400;
14+
export let barWidth = 25;
15+
export let barColor = "black"
1016
1117
// prefix a statement with $: to make it reactive (so it runs every time
1218
// data changes)
1319
// https://svelte.dev/tutorial/reactive-statements
14-
// $: console.log("Dataset prop:", data);
20+
// here we're going to normalise our data to 100
21+
$: normalisedData = data.map(d => ({
22+
y: d,
23+
h: d / Math.max(...data) * height
24+
}));
25+
$: console.log(normalisedData);
1526
1627
</script>
1728

1829
<style>
19-
svg text {
20-
fill: red;
30+
@import url('https://fonts.googleapis.com/css2?family=Public+Sans:wght@300&display=swap');
31+
32+
text {
33+
font-family: "Public Sans";
34+
font-size: smaller;
2135
}
2236
</style>
2337

24-
<!-- we use svelte's in/out transitions for entering and leaving dom elements,
25-
and vanilla css transitions for retained elements that change. the
26-
#each block means we create an svg <circle> for each element of data -->
27-
<svg>
28-
<text x="50" y="50">Hello world!</text>
38+
<!-- this bar chart's pretty simple: just bars and labels,
39+
with a single, unlabelled baseline.
40+
it does, however, use the `height`, `width` and `barWidth`
41+
props to scale the element sizes, so this can be dynamically
42+
resized easily -->
43+
44+
<svg width={width} height={height}>
45+
<!-- for each data element, draw a rectangle and a label -->
46+
{#each normalisedData as d, i (i)}
47+
<rect
48+
in:fly="{{x: 10}}" out:fly="{{x: 10}}"
49+
style={"transition: all 1s ease-out"}
50+
x="{width / 6 * (i + 0.5) - (barWidth / 2)}px"
51+
y="{height - d.h}px"
52+
width="{barWidth}px"
53+
height="{d.h}px"
54+
fill="{barColor}"
55+
>
56+
</rect>
57+
<!-- place label either above or below bar,
58+
depending on its height -->
59+
<text
60+
in:fly="{{x: 10}}" out:fly="{{x: 10}}"
61+
style={"transition: all 1s ease-out"}
62+
text-anchor="middle"
63+
x="{width / 6 * (i + 0.5)}px"
64+
y="{d.h > 35 ?
65+
height - d.h + 16 :
66+
height - d.h - 6}px"
67+
fill="{d.h > 35 ? "white" : barColor}"
68+
>{d.y}</text>
69+
{/each}
70+
<!-- and a single x axis baseline -->
71+
<line x1="0" x2="{width * 5 / 6}" y1="{height}" y2="{height}" stroke="black"></line>
2972
</svg>

docs/examples/barchart/index.qmd

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Examples: barchart"
2+
title: "Examples: basic barchart"
33
author: James Goldie, 360info
44
date: last-modified
55
---
@@ -8,16 +8,91 @@ date: last-modified
88
{{< include /.sverto/examples/barchart/index.qmd >}}
99
:::
1010

11-
Let's start with a barchart.
11+
Let's start with a simple barchart.
12+
13+
This isn't going to be one of those barcharts you've seen on TV, what with tick marks and hover effects and such. This one's just some bars, some labels and a baseline. It will, however, come with some useful features to help us reuse it.
14+
15+
::::: {.columns .column-page}
16+
17+
# Try it out
18+
19+
::::{.column width="40%"}
20+
21+
The following code initialises our bar chart.
1222

1323
```{ojs}
14-
BarChart = import_svelte("BarChart.svelte")
24+
//| code-fold: true
25+
26+
BarChart =
27+
import_svelte("BarChart.svelte")
28+
1529
myBarChart = new BarChart.default({
16-
target: document.querySelector("#mybarchart")
30+
target: document.querySelector("#mybarchart"),
31+
props: {
32+
data: [5, 5, 5, 5, 5],
33+
height: 200,
34+
width: 200,
35+
barWidth: 25,
36+
barColor: "#36a7e9"
37+
}
1738
});
1839
```
1940

2041
:::{#mybarchart}
2142
:::
2243

23-
There it is!
44+
[See the bar chart source at [BarChart.svelte](./BarChart.svelte)]{style="font-size: smaller; font-color: #999999"}
45+
46+
::::
47+
48+
<!-- TODO - .svelte file content here? -->
49+
50+
::::{.column width="5%"}
51+
::::
52+
53+
54+
::::{.column width="45%"}
55+
This Svelte component accepts a few props:
56+
57+
* `data`: a simple array of (up to) 5 numbers
58+
* `height`: the height of the chart in pixels
59+
* `width`: the width of the chart in pixels
60+
* `barWidth`: the width of each bar
61+
* `barColor`: the colour of the bars and labels (note the US spelling here)
62+
63+
We can hook any of those values up to OJS code using `myBarChart.propName`.
64+
65+
For example, let's make the data user-configurable:
66+
67+
```{ojs}
68+
//| code-fold: true
69+
70+
viewof userData = Inputs.form([
71+
Inputs.text({type: "number", value: 25, width: 20}),
72+
Inputs.text({type: "number", value: 35, width: 20}),
73+
Inputs.text({type: "number", value: 65, width: 20}),
74+
Inputs.text({type: "number", value: 5, width: 20}),
75+
Inputs.text({type: "number", value: 50, width: 20})
76+
]);
77+
myBarChart.data = [...userData];
78+
```
79+
80+
Or the colour:
81+
82+
```{ojs}
83+
//| code-fold: true
84+
viewof userColor = Inputs.color({value: "#36a7e9"});
85+
myBarChart.barColor = userColor;
86+
```
87+
::::
88+
89+
:::::
90+
91+
## Challenges
92+
93+
This Svelte component's pretty basic, though. What else is it missing?
94+
95+
* The height and width are configurable, and the bars resize in response to them, but their CSS transitions are slow to catch up. Ideally we'd have the bars apply the transition when they resize because of a change in data but _not_ in response to a change in chart height or width.
96+
* We have no axes other than the baseline. That's fine for a lot of uses, but we might want to add those elements for other uses.
97+
- We could add those elements manually, but the [`d3-axis`](https://github.com/d3/d3-axis) package has some helpers for creating axes quickly!
98+
* The bars are all the same colour. We could write a function that converts each bar's data value to a colour, and use it for the `<rect>` `fill` attribute, but the [`d3-scale-chromatic`](https://github.com/d3/d3-scale-chromatic) also has some helpers to do this quickly!

0 commit comments

Comments
 (0)