limn
    ↑↓ ChooseEnter OpenEsc Close
    GitHub

    Components/Data

    Charts

    Read-only charts for data the host already has: BarChart and ColumnChart compare categories, LineChart and AreaChart follow a measure along an axis, PieChart shares out a whole, RadarChart compares series over several axes, HeatmapChart shows intensity over a grid such as a calendar, and ChartLegend explains a color scale. They take no clicks and add no tab stops; the pointer over a chart marks what it is nearest and shows its values.

    Native componentGuide source

    Preview

    CodeSource
    fn view(state: &State, palette: ComponentPalette) -> Element {
        let data = state
            .languages
            .iter()
            .map(|language| ChartDatum::new(&language.id, &language.name, language.files))
            .collect();
        BarChart::new("languages", data, palette).width(state.content_width).render()
    }

    Examples

    Each one is the library drawing it. Press, type and hover.

    Column chart2 / 4
    CodeSource
    ColumnChart::series("charts-grouped", MONTHS_HALF, traffic(), palette).width(width).gradient(true).radius(6.0).render()
    Column chart3 / 4
    CodeSource
    ColumnChart::series("charts-stacked", MONTHS_HALF, traffic(), palette)
        .width(width)
        .stacked()
        .total("Total")
        .value_labels(true)
        .color(accent)
        .render()
    Column chart4 / 4
    CodeSource
    ColumnChart::new(
        "charts-negative",
        [("Mon", 4.0), ("Tue", -2.0), ("Wed", 6.5), ("Thu", -4.5), ("Fri", 3.0)]
            .into_iter()
            .map(|(day, change)| {
                let datum = ChartDatum::new(day, day, change);
                if change < 0.0 { datum.color(AlertColors::for_tone(AlertTone::Error, palette).accent) } else { datum }
            })
            .collect(),
        palette,
    )
    .width(width)
    .height(200.0)
    .render()
    Bar chart1 / 3
    CodeSource
    BarChart::new("charts-bars", languages(), palette).width(width).gradient(true).render()
    Bar chart2 / 3
    CodeSource
    BarChart::new(
        "charts-bars-ink",
        vec![
            ChartDatum::new("passed", "Passed", 412.0),
            ChartDatum::new("skipped", "Skipped", 37.0).value_label("37 (8 %)").tooltip("Behind a feature flag"),
            ChartDatum::new("failed", "Failed", 9.0).color(AlertColors::for_tone(AlertTone::Error, palette).accent),
        ],
        palette,
    )
    .width(width)
    .color(accent)
    .value_axis(false)
    .grid(false)
    .render()
    Bar chart3 / 3
    CodeSource
    BarChart::series("charts-bars-stacked", ["Engine", "Components", "Gallery"], vec![
        ChartSeries::new("Code", [16.6, 21.4, 6.2]),
        ChartSeries::new("Tests", [8.1, 9.7, 0.4]),
        ChartSeries::new("Docs", [1.9, 3.3, 0.2]),
    ], palette)
    .width(width)
    .stacked()
    .total("Thousand lines")
    .render()
    Line chart1 / 3
    CodeSource
    LineChart::new("charts-line", MONTHS_HALF, traffic(), palette).width(width).render()
    Line chart2 / 3
    CodeSource
    LineChart::new("charts-line-straight", MONTHS_HALF, vec![ChartSeries::new("Desktop", [186.0, 305.0, 237.0, 73.0, 209.0, 214.0])], palette)
        .width(width)
        .height(180.0)
        .smooth(false)
        .dots(true)
        .color(accent)
        .render()
    Line chart3 / 3
    CodeSource
    LineChart::new("charts-line-range", BANDS, vec![ChartSeries::new("Gain", [4.0, 5.5, 3.0, -1.5, -3.0, -2.0, 1.0, 3.5, 6.0, 2.5])], palette)
        .width(width)
        .height(180.0)
        .range(-12.0, 12.0)
        .dashed_zero(true)
        .point_axis(false)
        .mark(6)
        .color(accent)
        .render()
    Area chart
    CodeSource
    AreaChart::new("charts-area", MONTHS_HALF, traffic(), palette).width(width).color(accent).render()
    Pie chart
    CodeSource
    row(vec![
        PieChart::new("charts-pie", browsers(), palette).render(),
        PieChart::new("charts-donut", browsers(), palette).donut(0.62).center("1,125", "Visitors").color(accent).render(),
    ])
    Radar chart
    CodeSource
    RadarChart::new(
        "charts-radar",
        ["Speed", "Memory", "Startup", "Size", "Battery", "Input"],
        vec![
            ChartSeries::new("Native", [92.0, 78.0, 95.0, 84.0, 88.0, 96.0]).color(accent),
            ChartSeries::new("Web view", [61.0, 42.0, 55.0, 38.0, 51.0, 70.0]),
        ],
        palette,
    )
    .size(width.min(420.0), 300.0)
    .maximum(100.0)
    .render()
    Heatmap chart1 / 2
    CodeSource
    HeatmapChart::new("charts-heat", weeks(), palette)
        .width(width)
        .row_labels(["", "Tue", "", "Thu", "", "Sat", ""])
        .column_labels(MONTHS.iter().map(|(column, month)| ChartAxisLabel::new(*column, *month)).collect())
        .render()
    Heatmap chart2 / 2
    CodeSource
    HeatmapChart::new("charts-heat-tinted", weeks(), palette)
        .width(width.min(260.0))
        .cell_size(10.0)
        .gap(2.0)
        .scale(tinted)
        .render()
    Chart legend1 / 2
    CodeSource
    row(vec![ChartLegend::new(HeatmapScale::neutral(palette).colors, "Less", "More", palette).render()])
    Chart legend2 / 2
    CodeSource
    row(vec![ChartLegend::new(tinted.colors, "0", "8 commits", palette).render()])

    Charts

    Read-only charts for data the host already has: BarChart and ColumnChart compare categories, LineChart and AreaChart follow a measure along an axis, PieChart shares out a whole, RadarChart compares series over several axes, HeatmapChart shows intensity over a grid such as a calendar, and ChartLegend explains a color scale. They take no clicks and add no tab stops; the pointer over a chart marks what it is nearest and shows its values.

    Import

    use limn::{
        AreaChart, BarChart, ChartAxisLabel, ChartDatum, ChartLegend, ChartSeries, ColumnChart,
        Component, HeatmapCell, HeatmapChart, HeatmapScale, LineChart, PieChart, RadarChart,
    };

    Usage

    A chart takes an ID, its data and the palette. A ChartDatum is an ID within the chart, a label and a value.

    fn view(state: &State, palette: ComponentPalette) -> Element {
        let data = state
            .languages
            .iter()
            .map(|language| ChartDatum::new(&language.id, &language.name, language.files))
            .collect();
        BarChart::new("languages", data, palette).width(state.content_width).render()
    }

    A bar chart takes the width it is offered unless width is set; a heatmap is 720 px wide. Charts report nothing.

    Data

    ChartDatum::new("skipped", "Skipped", 37.0)
        .value_label("37 (8 %)")
        .tooltip("Behind a feature flag")
        .color(warning)

    The value is an f64. A negative one grows the other way from the zero line; one that is not finite counts as zero. value_label replaces the number written at the bar’s tip, which is otherwise the value with no decimals when it is whole and one when it is not. tooltip is what the tooltip calls this bar’s row, and color paints this one bar instead of the chart’s color.

    Several series

    ColumnChart::series(
        "traffic",
        ["Jan", "Feb", "Mar"],
        vec![
            ChartSeries::new("Desktop", [186.0, 305.0, 237.0]),
            ChartSeries::new("Mobile", [80.0, 200.0, 120.0]),
        ],
        palette,
    )
    .render()

    series takes the categories and a ChartSeries for each measure: a name and a value for each category. A category’s bars stand side by side. stacked() piles them on one another, and total("Total") adds a last row to the tooltip with their sum. Each series is a lighter step of the chart’s color unless it is given one with ChartSeries::color.

    BarChart and ColumnChart

    BarChart::new("tests", data, palette).width(480.0).color(accent).render()
    ColumnChart::new("timings", data, palette).height(180.0).gradient(true).render()

    A ColumnChart stands its bars on the category axis; a BarChart lays them down, with the categories’ names at the side. BarChart::vertical() turns one into the other. Both have the same builders:

    BuilderDefaultWhat it does
    width(f32)the width offeredFixes the width, never below 160
    height(f32)240, or 34 for each bar of a BarChartFixes the height, from 80 to 1200
    color(Color)the palette’s textThe color of the first series; the others are lighter steps of it
    stacked()side by sidePiles each category’s series
    gradient(bool)offFades each bar from a third of its color at its foot to all of it at its tip
    radius(f32)4Rounds the tip of each bar; the foot stays square
    max_bar_width(f32)32The widest a bar is drawn
    grid(bool)onThe dashed line at each tick
    value_axis(bool)onThe ticks of the value axis
    value_labels(bool)on for a chart built from dataEach bar’s value at its tip, or a stack’s sum
    total(name)noneA row in the tooltip that sums a category

    The value axis always includes zero and ends on a tick; ticks are steps of 1, 2 or 5 times a power of ten. A number that is not finite is ignored by every builder.

    LineChart and AreaChart

    LineChart::new("visits", ["Jan", "Feb", "Mar"], series, palette).render()
    AreaChart::new("visits", ["Jan", "Feb", "Mar"], series, palette).color(accent).render()

    A line for each ChartSeries over the points of an axis. AreaChart::new gives the same chart with the area under each line filled, from its color down to nothing at the zero line; it is LineChart::area(true).

    BuilderDefaultWhat it does
    width(f32), height(f32)the width offered, 240As for a bar chart
    color(Color)the palette’s textThe color of the first series; the others are lighter steps of it
    smooth(bool)onRounds each line through its points without passing over or under any; off joins them with straight strokes
    area(bool)offFills under each line
    dots(bool)offA dot at every point
    grid(bool), value_axis(bool)onThe dashed lines and the ticks
    point_axis(bool)onThe names of the points under the plot, and the room they take; the tooltip says them either way
    range(low, high)fitted to the valuesA fixed value axis with ticks at both ends and at zero between them; a positive tick of an axis that runs below zero is written with its sign, as a gain is. Values past it are drawn past it
    dashed_zero(bool)offDashes the zero line
    mark(usize)noneA dot on every line at this point, counted from 0, as the pointer marks the one it reads
    LineChart::new("response", ["31", "62", "125", "250", "500", "1k", "2k", "4k", "8k", "16k"], gain, palette)
        .range(-12.0, 12.0)
        .dashed_zero(true)
        .point_axis(false)
        .mark(state.band)
        .render()

    PieChart

    PieChart::new("browsers", data, palette).render()
    PieChart::new("browsers", data, palette).donut(0.62).center("1,125", "Visitors").render()

    One slice for each ChartDatum, clockwise from twelve o’clock in their order. size is the side of the square it takes, 220 by default. donut makes it a ring, with the hole as a share of the radius up to 0.9, and center writes a figure and a caption in the hole. A datum’s color paints its slice; the others are steps of the chart’s color. A datum of zero, or one that is negative or not finite, has no slice.

    RadarChart

    RadarChart::new("profile", ["Speed", "Memory", "Startup", "Size"], series, palette)
        .maximum(100.0)
        .render()

    Each ChartSeries is a shape whose corners stand on the axes at its values, over four rings and a spoke for each axis. maximum is the value at the rim, otherwise a round number over the greatest value; size(width, height) is 320 by 260 by default. It needs three axes or more.

    Motion

    When a chart first appears it is drawn in: bars grow from the zero line, lines are drawn from left to right, a pie sweeps round from twelve o’clock a radar opens from its center and the columns of a heatmap come in one after another. Under the pointer, a bar chart lights the category with a band that glides to the next while the others recede, a line chart marks the nearest point with a line and a dot on each series, a pie stands the slice out, a radar marks the nearest axis, and a heatmap grows the cell over its neighbours while the others recede. A chart keeps this by its ID, so a chart that is rebuilt every frame does not start again. UiTree::set_reduced_motion(true) draws a chart at once and moves its marks without gliding.

    Tooltip

    The tooltip is the charts’ own, not the one of the other components: it follows the pointer, on the side of it where the chart has more room and never out of the window, and shows what the pointer reads over a row for each series with its color, its name and its value. A pie’s says the slice’s value and its share. It appears as soon as the pointer is over the chart, and anywhere over a category, not only over its bars.

    HeatmapChart

    let weeks: Vec<Vec<Option<HeatmapCell>>> = state
        .weeks
        .iter()
        .map(|week| {
            week.iter()
                .map(|day| day.as_ref().map(|day| HeatmapCell::new(&day.date, day.level, &day.summary)))
                .collect()
        })
        .collect();
    
    HeatmapChart::new("activity", weeks, palette)
        .width(state.content_width)
        .row_labels(["", "Tue", "", "Thu", "", "Sat", ""])
        .column_labels(vec![ChartAxisLabel::new(0, "Jan"), ChartAxisLabel::new(5, "Feb")])
        .render()

    The data is a list of columns, each a list of cells from the top. A HeatmapCell is an ID within the chart, a level from 0 to 4 and the text of its tooltip; a cell with empty text has no tooltip. None leaves a hole, for a day outside the period, while level zero paints a cell.

    row_labels names the rows at the left and column_labels names columns above the grid, each ChartAxisLabel at the column it is given. A label that would run into the one before it, or past the end, is left out.

    Cells are 14 px squares 3 px apart unless cell_size (1 to 40) and gap (0 to 16) say otherwise. When the columns do not fit in the width, the cells and gaps shrink so that the whole period stays visible, and the row labels are dropped when the columns become too narrow for them.

    Scale and legend

    let scale = HeatmapScale::new([none, low, medium, high, highest]);
    
    HeatmapChart::new("activity", weeks, palette).scale(scale).render()
    ChartLegend::new(scale.colors, "Less", "More", palette).render()

    A HeatmapScale is the five colors of levels 0 to 4. Without scale a heatmap uses HeatmapScale::neutral(palette), built from the palette.

    ChartLegend::new(colors, low, high, palette) draws a square for each color between a label for each end. It takes any colors, so it shows the scale the chart was given.

    API reference

    The guide covers composition, options and actions. Generate the complete Rust type and builder reference from the checkout:

    cargo doc -p limn --open

    Component API overview · State and actions

    The example is drawn by crates/limn-gallery/src/pages/charts.rs.