Chart
Bar, stacked bar, horizontal bar, line, area, and donut charts of the rows of a list signal, on a scale the browser fits to the values.
Result
use hypersig::html::div;
use hypersig::style::pattern::stack;
use hypersig::style::value::rem;
use hypersig::{Page, Signals};
use hypersig_ui::{ButtonSize, ButtonVariant, Chart, DonutChart, Tabs, button, token};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum Kind {
Bar,
Stacked,
Horizontal,
Donut,
}
#[derive(Signals, Serialize, Deserialize)]
pub struct Month {
name: String,
desktop: i32,
mobile: i32,
}
#[derive(Signals, Serialize, Deserialize)]
pub struct Browser {
name: String,
visitors: i32,
}
#[derive(Signals, Serialize, Deserialize)]
pub struct Traffic {
kind: Kind,
months: Vec<Month>,
browsers: Vec<Browser>,
}
pub fn page() -> Page<Traffic> {
// Each kind of chart draws the same two series of the same months.
let visitors = |chart: Chart<Traffic, Month>| {
chart
.series(
"Desktop",
Month::desktop(),
token::chart_1(),
)
.series(
"Mobile",
Month::mobile(),
token::chart_2(),
)
};
let label = "Visitors";
let names = [
L { en: "Jan", ja: "1 月" },
L { en: "Feb", ja: "2 月" },
L { en: "Mar", ja: "3 月" },
L { en: "Apr", ja: "4 月" },
L { en: "May", ja: "5 月" },
L { en: "Jun", ja: "6 月" },
];
let months = names
.into_iter()
.zip([(186, 80), (305, 200), (237, 120), (73, 190), (209, 130), (214, 140)])
.map(|(name, (desktop, mobile))| Month {
name: site.t(name).to_owned(),
desktop,
mobile,
})
.collect();
let browsers = ["Chrome", "Safari", "Firefox", "Edge"]
.into_iter()
.map(str::to_owned)
.chain(["Other"
.to_owned()])
.zip([275, 200, 187, 173, 90])
.map(|(name, visitors)| Browser { name, visitors })
.collect();
let grow = Traffic::months().update_where(
|month| month.index().equals(3),
Month::desktop(),
|month| month.at(Month::desktop()) + 400,
);
let tabs = Tabs::new(label, Traffic::kind())
.tab(
Kind::Bar,
"Bar",
visitors(Chart::bar(label, Traffic::months(), Month::name())).render(),
)
.tab(
Kind::Stacked,
"Stacked",
visitors(Chart::stacked_bar(label, Traffic::months(), Month::name())).render(),
)
.tab(
Kind::Horizontal,
"Horizontal",
visitors(Chart::horizontal_bar(label, Traffic::months(), Month::name())).render(),
)
.tab(
Kind::Donut,
"Donut",
DonutChart::new(
"Visitors by browser",
Traffic::browsers(),
Browser::name(),
Browser::visitors(),
)
.render(),
);
Page::new(
label,
Traffic {
kind: Kind::Bar,
months,
browsers,
},
)
.child(
div()
.css(stack(rem(1)))
.child(
div().child(
button(ButtonVariant::Outline, ButtonSize::Sm)
.on_click(grow)
.text("Add 400 desktop visitors in April"),
),
)
.child(tabs),
)
}Accessibility
The drawing is an <svg> with viewBox="0 0 100 100" and preserveAspectRatio="none", so it stretches to its box, and its lines keep their width with vector-effect="non-scaling-stroke". The axis labels, the legend, and the box that shows a row's values under the pointer are HTML, which does not stretch. A DonutChart draws each row as a circle with pathLength="100", whose stroke-dasharray is the row's share of the total in hundredths.
The drawing is hidden from assistive technology. A screen reader reads a visually hidden <table> of the same rows, captioned with the chart's name, and the values under the pointer have no keyboard equivalent beyond that table.
A series reads a number field of the row type, so a field of another struct or a text field does not compile. The scale runs from a round number at or below the smallest value, or 0, to one at or above the largest, with grid lines on a step of 1, 2, 2.5, or 5 times a power of ten. The browser computes it with max_of, min_of, and let_in, so a statement or a patch that changes the rows refits it, and a negative value draws its bar down from the line at 0. Each bar and grid line computes the scale from every row, so the work of a change grows with the square of the number of rows.
API
| Item | Summary |
|---|---|
Chart::bar | A bar chart named label of rows, labeled by category. |
Chart::stacked_bar | A stacked bar chart named label of rows, labeled by category. |
Chart::horizontal_bar | A horizontal bar chart named label of rows, labeled by category. |
Chart::line | A line chart named label of rows, labeled by category. |
Chart::area | An area chart named label of rows, labeled by category. |
series | Adds a series named name that draws the number field field in color. |
max_labels | Labels at most count rows along the axis of the categories, 12 until set. |
format | Writes the values and the axis labels in format. |
DonutChart::new | A donut chart named label of rows, with slices of value labeled by category. |
token::chart_1 | The first series of a chart. |