Skip to main content

guise/chart/
sparkline.rs

1//! `Sparkline` — a tiny inline trend line with an optional area fill.
2//!
3//! ```ignore
4//! use guise::chart::Sparkline;
5//!
6//! Sparkline::new([3.0, 5.0, 2.0, 8.0, 6.0]).fill()
7//! ```
8
9use gpui::prelude::*;
10use gpui::{canvas, px, App, Hsla, IntoElement, Window};
11
12use crate::style::ColorValue;
13use crate::theme::theme;
14
15use super::{paint_polyline, resolve_color};
16
17/// A minimal, axis-free polyline over a value series. Values are min/max
18/// normalized to the chart height; fewer than two values paint nothing.
19#[derive(IntoElement)]
20pub struct Sparkline {
21    values: Vec<f32>,
22    color: Option<ColorValue>,
23    stroke: f32,
24    fill: bool,
25    width: Option<f32>,
26    full_width: bool,
27    height: f32,
28}
29
30impl Sparkline {
31    pub fn new(values: impl IntoIterator<Item = f32>) -> Self {
32        Sparkline {
33            values: values.into_iter().collect(),
34            color: None,
35            stroke: 2.0,
36            fill: false,
37            width: None,
38            full_width: false,
39            height: 32.0,
40        }
41    }
42
43    /// Line color. Defaults to the theme primary.
44    pub fn color(mut self, color: impl Into<ColorValue>) -> Self {
45        self.color = Some(color.into());
46        self
47    }
48
49    /// Stroke width in px (default 2).
50    pub fn stroke(mut self, width: f32) -> Self {
51        self.stroke = width.max(0.5);
52        self
53    }
54
55    /// Fill the area between the line and the baseline (line color at 0.15 alpha).
56    pub fn fill(mut self) -> Self {
57        self.fill = true;
58        self
59    }
60
61    /// Fixed width in px (default 120).
62    pub fn width(mut self, width: f32) -> Self {
63        self.width = Some(width);
64        self
65    }
66
67    /// Stretch to the parent's width instead of a fixed one.
68    pub fn full_width(mut self) -> Self {
69        self.full_width = true;
70        self
71    }
72
73    /// Height in px (default 32).
74    pub fn height(mut self, height: f32) -> Self {
75        self.height = height;
76        self
77    }
78}
79
80impl RenderOnce for Sparkline {
81    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
82        let t = theme(cx);
83        let line = self
84            .color
85            .map(|c| resolve_color(t, c))
86            .unwrap_or_else(|| t.primary().hsla());
87        let area = self.fill.then_some(Hsla { a: 0.15, ..line });
88        let stroke = self.stroke;
89        let values = self.values;
90
91        let plot = canvas(
92            |_, _, _| (),
93            move |bounds, _, window, _cx| {
94                paint_polyline(window, bounds, &values, stroke, line, area);
95            },
96        )
97        .h(px(self.height));
98
99        if self.full_width {
100            plot.w_full()
101        } else {
102            plot.w(px(self.width.unwrap_or(120.0)))
103        }
104    }
105}