Range area chart
A band from each point's low to its high, such as daily low and high temperatures, with an optional line through the middle. The spread is the story, so the band leads.
Daily temperature
Overnight low to daytime high for each day of July, with the daily mean.
| Low | High | Mean | Range | |
|---|---|---|---|---|
| 1 Jul | 13°C | 21°C | 17°C | 8°C |
| 2 Jul | 13°C | 23°C | 18°C | 10°C |
| 3 Jul | 14°C | 25°C | 19°C | 11°C |
| 4 Jul | 15°C | 25°C | 20°C | 10°C |
| 5 Jul | 15°C | 24°C | 19°C | 9°C |
| 6 Jul | 14°C | 21°C | 18°C | 7°C |
| 7 Jul | 12°C | 20°C | 16°C | 7°C |
| 8 Jul | 12°C | 20°C | 16°C | 9°C |
| 9 Jul | 13°C | 23°C | 18°C | 10°C |
| 10 Jul | 14°C | 25°C | 20°C | 11°C |
| 11 Jul | 16°C | 28°C | 22°C | 12°C |
| 12 Jul | 16°C | 29°C | 23°C | 13°C |
| 13 Jul | 17°C | 30°C | 24°C | 13°C |
| 14 Jul | 16°C | 28°C | 22°C | 12°C |
| 15 Jul | 15°C | 25°C | 20°C | 10°C |
| 16 Jul | 14°C | 22°C | 18°C | 8°C |
| 17 Jul | 13°C | 22°C | 17°C | 9°C |
| 18 Jul | 14°C | 24°C | 19°C | 10°C |
| 19 Jul | 15°C | 26°C | 21°C | 11°C |
| 20 Jul | 17°C | 29°C | 23°C | 12°C |
| 21 Jul | 18°C | 32°C | 25°C | 13°C |
| 22 Jul | 19°C | 32°C | 26°C | 13°C |
| 23 Jul | 18°C | 30°C | 24°C | 12°C |
| 24 Jul | 15°C | 25°C | 20°C | 10°C |
| 25 Jul | 14°C | 23°C | 19°C | 9°C |
| 26 Jul | 14°C | 22°C | 18°C | 9°C |
| 27 Jul | 14°C | 24°C | 19°C | 10°C |
| 28 Jul | 15°C | 26°C | 20°C | 11°C |
| 29 Jul | 14°C | 23°C | 19°C | 9°C |
| 30 Jul | 13°C | 22°C | 18°C | 9°C |
| 31 Jul | 13°C | 21°C | 17°C | 8°C |
Wholesale power price
Lowest and highest half-hourly price in each hour, last 30 days.
| Low | High | Price range | |
|---|---|---|---|
| 00:00 | £42 | £61 | £19 |
| 01:00 | £39 | £58 | £19 |
| 02:00 | £37 | £55 | £18 |
| 03:00 | £36 | £54 | £18 |
| 04:00 | £38 | £57 | £19 |
| 05:00 | £45 | £72 | £27 |
| 06:00 | £58 | £96 | £38 |
| 07:00 | £71 | £118 | £47 |
| 08:00 | £66 | £104 | £38 |
| 09:00 | £55 | £88 | £33 |
| 10:00 | £48 | £79 | £31 |
| 11:00 | £44 | £74 | £30 |
| 12:00 | £41 | £70 | £29 |
| 13:00 | £40 | £69 | £29 |
| 14:00 | £43 | £75 | £32 |
| 15:00 | £52 | £89 | £37 |
| 16:00 | £68 | £121 | £53 |
| 17:00 | £84 | £142 | £58 |
| 18:00 | £79 | £131 | £52 |
| 19:00 | £66 | £109 | £43 |
| 20:00 | £57 | £92 | £35 |
| 21:00 | £51 | £80 | £29 |
| 22:00 | £47 | £71 | £24 |
| 23:00 | £44 | £65 | £21 |
Usage
use hydroxy_leptos::chart_frame::ChartFrame;
use hydroxy_leptos::range_area_chart::*;
use leptos::prelude::*;
#[component]
pub fn Demo() -> impl IntoView {
let lows = [
13.2, 12.8, 14.1, 15.0, 14.6, 13.9, 12.4, 11.8, 12.9, 14.3, 15.8, 16.4, 17.1, 16.2, 14.8, 13.5, 13.1, 14.0,
15.2, 16.9, 18.3, 19.0, 17.6, 15.4, 14.2, 13.8, 14.5, 15.1, 14.0, 13.3, 12.9,
];
let highs = [
21.4, 22.9, 24.6, 25.1, 23.8, 21.2, 19.6, 20.4, 22.8, 25.3, 27.9, 29.4, 30.2, 28.1, 24.7, 22.0, 21.8, 23.5,
26.1, 28.8, 31.6, 32.4, 29.7, 25.2, 23.0, 22.4, 24.1, 25.6, 23.3, 21.9, 21.2,
];
let july: Vec<Interval> = (0..31)
.map(|d| Interval::new(format!("{} Jul", d + 1), f64::midpoint(lows[d], highs[d]), lows[d], highs[d]))
.collect();
// No middle value: a NaN mid draws just the band.
let price = [
(42.0, 61.0),
(39.0, 58.0),
(37.0, 55.0),
(36.0, 54.0),
(38.0, 57.0),
(45.0, 72.0),
(58.0, 96.0),
(71.0, 118.0),
(66.0, 104.0),
(55.0, 88.0),
(48.0, 79.0),
(44.0, 74.0),
(41.0, 70.0),
(40.0, 69.0),
(43.0, 75.0),
(52.0, 89.0),
(68.0, 121.0),
(84.0, 142.0),
(79.0, 131.0),
(66.0, 109.0),
(57.0, 92.0),
(51.0, 80.0),
(47.0, 71.0),
(44.0, 65.0),
];
let hours: Vec<Interval> =
price.iter().enumerate().map(|(h, &(lo, hi))| Interval::new(format!("{h:02}:00"), f64::NAN, lo, hi)).collect();
view! {
<div style="display: grid; gap: var(--space-10);">
<ChartFrame title="Daily temperature" description="Overnight low to daytime high for each day of July, with the daily mean.">
<RangeAreaChart data=july label="Daily low and high temperature in July" curve=Curve::Monotone format=|v: f64| format!("{v:.0}°C") />
</ChartFrame>
<ChartFrame title="Wholesale power price" description="Lowest and highest half-hourly price in each hour, last 30 days.">
<RangeAreaChart data=hours label="Wholesale electricity price range by hour" range_label="Price range" height=200 format=|v: f64| format!("£{v:.0}") />
</ChartFrame>
</div>
}
}use dioxus::prelude::*;
use hydroxy_dioxus::chart_frame::ChartFrame;
use hydroxy_dioxus::range_area_chart::{Curve, Interval, RangeAreaChart};
#[component]
pub fn RangeAreaChartDemo() -> Element {
let lows = [
13.2, 12.8, 14.1, 15.0, 14.6, 13.9, 12.4, 11.8, 12.9, 14.3, 15.8, 16.4, 17.1, 16.2, 14.8, 13.5, 13.1, 14.0,
15.2, 16.9, 18.3, 19.0, 17.6, 15.4, 14.2, 13.8, 14.5, 15.1, 14.0, 13.3, 12.9,
];
let highs = [
21.4, 22.9, 24.6, 25.1, 23.8, 21.2, 19.6, 20.4, 22.8, 25.3, 27.9, 29.4, 30.2, 28.1, 24.7, 22.0, 21.8, 23.5,
26.1, 28.8, 31.6, 32.4, 29.7, 25.2, 23.0, 22.4, 24.1, 25.6, 23.3, 21.9, 21.2,
];
let july: Vec<Interval> = (0..31)
.map(|d| Interval::new(format!("{} Jul", d + 1), f64::midpoint(lows[d], highs[d]), lows[d], highs[d]))
.collect();
// No middle value: a NaN mid draws just the band.
let price = [
(42.0, 61.0),
(39.0, 58.0),
(37.0, 55.0),
(36.0, 54.0),
(38.0, 57.0),
(45.0, 72.0),
(58.0, 96.0),
(71.0, 118.0),
(66.0, 104.0),
(55.0, 88.0),
(48.0, 79.0),
(44.0, 74.0),
(41.0, 70.0),
(40.0, 69.0),
(43.0, 75.0),
(52.0, 89.0),
(68.0, 121.0),
(84.0, 142.0),
(79.0, 131.0),
(66.0, 109.0),
(57.0, 92.0),
(51.0, 80.0),
(47.0, 71.0),
(44.0, 65.0),
];
let hours: Vec<Interval> =
price.iter().enumerate().map(|(h, &(lo, hi))| Interval::new(format!("{h:02}:00"), f64::NAN, lo, hi)).collect();
rsx! {
div { style: "display: grid; gap: var(--space-10);",
ChartFrame { title: "Daily temperature", description: "Overnight low to daytime high for each day of July, with the daily mean.",
RangeAreaChart {
data: july,
label: "Daily low and high temperature in July",
curve: Curve::Monotone,
format: move |v: f64| format!("{v:.0}°C"),
}
}
ChartFrame { title: "Wholesale power price", description: "Lowest and highest half-hourly price in each hour, last 30 days.",
RangeAreaChart {
data: hours,
label: "Wholesale electricity price range by hour",
range_label: "Price range",
height: 200,
format: move |v: f64| format!("£{v:.0}"),
}
}
}
}
}
Anatomy
| Component | data-part | Renders |
|---|---|---|
RangeAreaChart | root | figure with a screen-reader table of lows, highs and spreads |
| band | The low-to-high band, revealed left to right |
| line | The middle value, drawn along its length |
| span | The hovered point's range |
| dot | The hovered point's high, low and middle (data-kind) |
| tooltip | High, low, middle and spread |
Keyboard
| Key | Does |
|---|---|
ArrowLeft / ArrowRight | Previous or next point |
Home / End | First or last point |
Escape | Hide the tooltip |
Props
| Prop | Type | Notes |
|---|---|---|
data | Vec<Interval> | Required. Interval::new(label, mid, low, high); a NaN mid draws no line |
label | String | Required. What the chart shows; the accessible name |
range_label | String | Names the band. Default "Range" |
mid_label | String | Names the middle line. Default "Mean" |
height | u16 | Plot height in px. Default 240 |
curve | Curve | Linear (default) or Monotone |
scale | ScaleKind | Linear (default), Log or Symlog |
format | Fn(f64) -> String | Formats ticks, tooltip and table |
annotations | Vec<Annotation> | Reference lines, bands and points; x is a point index |
Events
| Prop | Type | Notes |
|---|---|---|
on_hover | Callback<Option<Datum>> | The mark under the pointer or keyboard focus changes, or is left. Fires only on a change |
on_select | Callback<Datum> | Click, tap or Enter on a mark |