Beeswarm chart
Every value as a dot, packed outwards from the row's centre line where values crowd so none overlap. The swarm's outline shows the distribution while each observation stays readable.
| Group | Value |
|---|---|
| Free | 3 min |
| Free | 5 min |
| Free | 4 min |
| Free | 7 min |
| Free | 2 min |
| Free | 6 min |
| Free | 8 min |
| Free | 4 min |
| Free | 5 min |
| Free | 3 min |
| Free | 9 min |
| Free | 6 min |
| Free | 4 min |
| Free | 12 min |
| Free | 5 min |
| Free | 7 min |
| Free | 3 min |
| Free | 6 min |
| Free | 4 min |
| Free | 5 min |
| Free | 8 min |
| Free | 2 min |
| Free | 6 min |
| Free | 15 min |
| Free | 4 min |
| Pro | 12 min |
| Pro | 18 min |
| Pro | 15 min |
| Pro | 22 min |
| Pro | 9 min |
| Pro | 25 min |
| Pro | 17 min |
| Pro | 14 min |
| Pro | 20 min |
| Pro | 31 min |
| Pro | 16 min |
| Pro | 19 min |
| Pro | 13 min |
| Pro | 24 min |
| Pro | 21 min |
| Pro | 18 min |
| Pro | 27 min |
| Pro | 15 min |
| Pro | 11 min |
| Pro | 23 min |
| Pro | 19 min |
| Pro | 16 min |
| Pro | 40 min |
| Pro | 20 min |
| Pro | 14 min |
| Team | 28 min |
| Team | 35 min |
| Team | 31 min |
| Team | 42 min |
| Team | 25 min |
| Team | 38 min |
| Team | 33 min |
| Team | 47 min |
| Team | 30 min |
| Team | 36 min |
| Team | 29 min |
| Team | 52 min |
| Team | 34 min |
| Team | 39 min |
| Team | 27 min |
| Team | 44 min |
| Team | 32 min |
| Team | 37 min |
| Team | 41 min |
| Team | 26 min |
| Team | 60 min |
| Team | 35 min |
| Team | 33 min |
| Team | 38 min |
| Team | 30 min |
Usage
use hydroxy_leptos::beeswarm_chart::{BeeswarmChart, Group};
use leptos::prelude::*;
#[component]
pub fn Demo() -> impl IntoView {
let groups = vec![
Group::new(
"Free",
[
3.0, 5.0, 4.0, 7.0, 2.0, 6.0, 8.0, 4.0, 5.0, 3.0, 9.0, 6.0, 4.0, 12.0, 5.0, 7.0, 3.0, 6.0, 4.0, 5.0,
8.0, 2.0, 6.0, 15.0, 4.0,
],
),
Group::new(
"Pro",
[
12.0, 18.0, 15.0, 22.0, 9.0, 25.0, 17.0, 14.0, 20.0, 31.0, 16.0, 19.0, 13.0, 24.0, 21.0, 18.0, 27.0,
15.0, 11.0, 23.0, 19.0, 16.0, 40.0, 20.0, 14.0,
],
),
Group::new(
"Team",
[
28.0, 35.0, 31.0, 42.0, 25.0, 38.0, 33.0, 47.0, 30.0, 36.0, 29.0, 52.0, 34.0, 39.0, 27.0, 44.0, 32.0,
37.0, 41.0, 26.0, 60.0, 35.0, 33.0, 38.0, 30.0,
],
),
];
view! { <BeeswarmChart groups=groups label="Session length by plan" format=|v: f64| format!("{v:.0} min") /> }
}use dioxus::prelude::*;
use hydroxy_dioxus::beeswarm_chart::{BeeswarmChart, Group};
#[component]
pub fn BeeswarmChartDemo() -> Element {
let groups = vec![
Group::new(
"Free",
[
3.0, 5.0, 4.0, 7.0, 2.0, 6.0, 8.0, 4.0, 5.0, 3.0, 9.0, 6.0, 4.0, 12.0, 5.0, 7.0, 3.0, 6.0, 4.0, 5.0,
8.0, 2.0, 6.0, 15.0, 4.0,
],
),
Group::new(
"Pro",
[
12.0, 18.0, 15.0, 22.0, 9.0, 25.0, 17.0, 14.0, 20.0, 31.0, 16.0, 19.0, 13.0, 24.0, 21.0, 18.0, 27.0,
15.0, 11.0, 23.0, 19.0, 16.0, 40.0, 20.0, 14.0,
],
),
Group::new(
"Team",
[
28.0, 35.0, 31.0, 42.0, 25.0, 38.0, 33.0, 47.0, 30.0, 36.0, 29.0, 52.0, 34.0, 39.0, 27.0, 44.0, 32.0,
37.0, 41.0, 26.0, 60.0, 35.0, 33.0, 38.0, 30.0,
],
),
];
rsx! {
BeeswarmChart { groups, label: "Session length by plan", format: move |v: f64| format!("{v:.0} min") }
}
}
Anatomy
| Component | data-part | Renders |
|---|---|---|
BeeswarmChart | root | figure with a screen-reader table of every value |
| overlay | Focusable plot; arrow keys step through dots |
| dot | One per value |
| tooltip | Group and value of the active dot |
| annotation | Reference line, band or point |
Keyboard
| Key | Does |
|---|---|
ArrowRight / ArrowLeft | Next or previous dot, row by row |
Home / End | First or last dot |
Escape | Clear |
Props
| Prop | Type | Notes |
|---|---|---|
groups | Vec<Group> | Required. One row each; pass one group for a single swarm |
label | String | Required. What the chart shows; the accessible name |
scale | ScaleKind | Linear, Log or Symlog value axis. Default Linear |
annotations | Vec<Annotation> | X in data values, y in row indices |
height | u16 | Plot height in px. Default 260 |
format | Fn(f64) -> String | Formats ticks, tooltip and table |
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 |