svelte-bar-chart-race v0.1.2
svelte-bar-chart-race
Bar chart race visualization for Svelte
A bar chart race is a horizontal bar chart that animates bars over an interval (usually time). The x-axis represents the bar chart values. The bars are sorted based on their value from highest to lowest as the interval changes.
This library exports the following components:
- BarChartRace: Parent component that accepts data, options and manages internal state
- Chart: Child component that displays the chart with the ability to customize the chart display and animation
- Slider: Child component that shows the range input to control the interval (current value)
Demo · Svelte REPL
Installation
# Yarn
yarn add -D svelte-bar-chart-race
# NPM
npm i -D svelte-bar-chart-race
# pnpm
pnpm i -D svelte-bar-chart-raceUsage
The examples use data sampled from StackOverflow's annual developer survey for "Most Loved Languages."
// demo/data.ts
export const data = [
{
title: "Rust",
color: "#dea584",
values: [
{ year: 2021, value: 86.98 },
{ year: 2020, value: 86.1 },
{ year: 2019, value: 83.5 },
{ year: 2018, value: 78.9 },
{ year: 2017, value: 73.1 },
],
},
{
title: "TypeScript",
color: "#2b7489",
values: [
{ year: 2021, value: 72.73 },
{ year: 2020, value: 67.1 },
{ year: 2019, value: 73.1 },
{ year: 2018, value: 67.0 },
{ year: 2017, value: 64.1 },
],
},
{
title: "Python",
color: "#3572A5",
values: [
{ year: 2021, value: 67.83 },
{ year: 2020, value: 66.7 },
{ year: 2019, value: 73.1 },
{ year: 2018, value: 68.0 },
{ year: 2017, value: 62.7 },
],
},
{
title: "Go",
color: "#00ADD8",
values: [
{ year: 2021, value: 62.74 },
{ year: 2020, value: 62.3 },
{ year: 2019, value: 67.9 },
{ year: 2018, value: 65.6 },
{ year: 2017, value: 63.3 },
],
},
{
title: "Swift",
color: "#F05138",
values: [
{ year: 2021, value: 63.56 },
{ year: 2020, value: 59.5 },
{ year: 2019, value: 69.2 },
{ year: 2018, value: 65.1 },
{ year: 2017, value: 63.7 },
],
},
];Simple
In the simplest example, all you need is to pass data to BarChartRace.
Then, pass Chart and Slider through its default slot.
<script>
import { BarChartRace, Slider, Chart } from "svelte-bar-chart-race";
import { data } from "./data";
</script>
<BarChartRace {data}>
<Chart />
<Slider />
</BarChartRace>Source: demo/Simple.svelte
Two-way Binding
The currentValue prop supports two-way binding.
In this example, the initial value is 2018. Any change to the currentValue via Slider will be reflected to the bound value.
currentValue can also be programmatically updated.
Finally, setting currentValue to null resets the value to the lowest value (in this case, 2017).
<script>
import { BarChartRace, Slider, Chart } from "svelte-bar-chart-race";
import { data } from "./data";
let currentValue = 2018;
</script>
<BarChartRace {data} bind:currentValue>
<Slider />
<Chart />
</BarChartRace>
<button on:click={() => (currentValue = 2020)}> Set value to 2020 </button>
<button on:click={() => (currentValue = null)}> Set value to null </button>
Current value:
<strong>{currentValue}</strong>Source: demo/TwoWayBinding.svelte
Kitchen Sink
The kitchen sink showcases all available features.
An alternative to accessing the currentValue is to destructure it from the BarChartRace slot using the let: directive.
Other slot props include:
- setMax (
() => void): sets the current value to the maximum value defined indata - setMin (
() => void): sets the current value to the minimum value defined indata - setValue (
(value: number) => void): sets the current value to the provided value - play (
() => void): function to animate the bar chart - pause (
() => void): function to pause the animation - isPlaying (
boolean):trueif the chart is currently animating
<script>
import { BarChartRace, Slider, Chart } from "svelte-bar-chart-race";
import { quadOut } from "svelte/easing";
import { data } from "./data";
</script>
<BarChartRace
{data}
interval={1_500}
options={{ key: "year" }}
let:currentValue={value}
let:setMax
let:setMin
let:setValue
let:play
let:pause
let:isPlaying
>
<Slider labelText="Year: {value}" />
<Chart
--bar-height="10px"
--bar-margin-bottom="12px"
animate={{
easing: quadOut,
duration: 400,
delay: 50,
}}
let:datum
>
<strong>{datum.title}</strong>
{datum.value.toFixed(1)}%
</Chart>
<button on:click={isPlaying ? pause : play}>
{isPlaying ? "Pause" : "Play"}
</button>
<button on:click={setMax}>Set max</button>
<button on:click={setMin}>Set min</button>
<button on:click={() => setValue(2019)}> Set value to 2019 </button>
</BarChartRace>Source: demo/KitchenSink.svelte
API
BarChartRace
Props
| Prop | Type | Default value |
|---|---|---|
| data | BarChartRaceDatum[] | [] |
| options | BarChartRaceOptions | { key: "", unit: "%", max: 100 } |
| interval | number | 2_000 |
If no key is explicitly provided to options, BarChartRace will infer the key by finding the first key that is not "value" in data[0].values.
interface BarChartRaceDatum {
/**
* Title must be unique because it's
* used as a key in `Chart.svelte`.
*/
title: string;
/**
* Specify the chart bar color.
* @default "#333"
* @example "red"
* @example "#f00"
*/
color?: string;
/**
* Tabular data used as values for each bar.
* @example
* [
{ key: 2021, value: 63.56 },
{ key: 2020, value: 59.5 },
// ...
]
*/
values: BarChartRaceValue[];
}
interface BarChartRaceOptions {
/**
* Specify the key used to normalize values in the `data.values` array.
* If no key is specified, it will be inferred.
* @example
* For a value object { year: 2021, value: 62.74 },
* the inferred key would be "year".
*/
key?: any;
/**
* Specify the unit.
* @default "%"
*/
unit?: string;
/**
* Specify the maximum value the
* chart value should be divided by.
* @default 100
*/
max?: number;
}Slots
Use the default slot to pass through the Chart and Slider components or any other element.
<BarChartRace
{data}
let:currentValue
let:isPlaying
let:setMax
let:setMin
let:setValue
let:play
let:pause
>
<!-- Default slot -->
</BarChartRace>Chart
Props
| Prop | Type | Default value |
|---|---|---|
| animate | FlipParams | { delay: 0, duration: d => Math.sqrt(d) * 120, easing: cubicOut } |
The Chart component uses the flip function to animate the bar chart.
Svelte provides numerous easing functions you can use to customize the animation.
<script>
import { quadOut } from "svelte/easing";
</script>
<Chart
animate={{
easing: quadOut,
duration: 400,
delay: 50,
}}
/>Style Props
Customize the chart styles using --style-props.
| Style prop | Description | Default value |
|---|---|---|
| --bar-height | Chart bar height | 6px |
| --bar-margin-top | Chart bar margin top | 4px |
| --bar-margin-bottom | Chart bar margin bottom | 10px |
| --chart-padding | ol element padding | 0 |
<Chart
--bar-height="10px"
--margin-top="20px"
--margin-bottom="20px"
--chart-padding="40px"
/>Slots
Use the default slot to customize the bar chart display value.
Access arbitrary values from data values.
<Chart let:datum>
<strong>{datum.title}</strong>
{datum.value}%
{datum.additionalValue ?? ""}
</Chart>Slider
Props
| Name | Type | Default value |
|---|---|---|
| id | string | "slider-" + Math.random().toString(36) |
| labelText | any | "" |
The current value from BarChartRace will be used as the labelText.
Customize labelText to override this behavior.
<Slider labelText="Year: {value}" />