tui_realm_stdlib/components/
sparkline.rs1use tuirealm::command::{Cmd, CmdResult};
6use tuirealm::props::{
7 Alignment, AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, Style,
8};
9use tuirealm::ratatui::{layout::Rect, widgets::Sparkline as TuiSparkline};
10use tuirealm::{Frame, MockComponent, State};
11
12#[derive(Default)]
18pub struct Sparkline {
19 props: Props,
20}
21
22impl Sparkline {
23 pub fn foreground(mut self, fg: Color) -> Self {
24 self.attr(Attribute::Foreground, AttrValue::Color(fg));
25 self
26 }
27
28 pub fn background(mut self, bg: Color) -> Self {
29 self.attr(Attribute::Background, AttrValue::Color(bg));
30 self
31 }
32
33 pub fn borders(mut self, b: Borders) -> Self {
34 self.attr(Attribute::Borders, AttrValue::Borders(b));
35 self
36 }
37
38 pub fn title<S: Into<String>>(mut self, t: S, a: Alignment) -> Self {
39 self.attr(Attribute::Title, AttrValue::Title((t.into(), a)));
40 self
41 }
42
43 pub fn max_entries(mut self, max: usize) -> Self {
44 self.attr(Attribute::Width, AttrValue::Length(max));
45 self
46 }
47
48 pub fn data(mut self, data: &[u64]) -> Self {
49 self.attr(
50 Attribute::Dataset,
51 AttrValue::Payload(PropPayload::Vec(
52 data.iter().map(|x| PropValue::U64(*x)).collect(),
53 )),
54 );
55 self
56 }
57
58 fn data_len(&self) -> usize {
62 self.props
63 .get(Attribute::Dataset)
64 .map(|x| x.unwrap_payload().unwrap_vec().len())
65 .unwrap_or(0)
66 }
67
68 fn get_data(&self, max: usize) -> Vec<u64> {
72 match self
73 .props
74 .get(Attribute::Dataset)
75 .map(|x| x.unwrap_payload())
76 {
77 Some(PropPayload::Vec(list)) => {
78 let mut data: Vec<u64> = Vec::with_capacity(max);
79 list.iter()
80 .take(max)
81 .cloned()
82 .map(|x| x.unwrap_u64())
83 .for_each(|x| data.push(x));
84 data
85 }
86 _ => Vec::new(),
87 }
88 }
89}
90
91impl MockComponent for Sparkline {
92 fn view(&mut self, render: &mut Frame, area: Rect) {
93 if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
94 let foreground = self
95 .props
96 .get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
97 .unwrap_color();
98 let background = self
99 .props
100 .get_or(Attribute::Background, AttrValue::Color(Color::Reset))
101 .unwrap_color();
102 let title = self
103 .props
104 .get_or(
105 Attribute::Title,
106 AttrValue::Title((String::default(), Alignment::Center)),
107 )
108 .unwrap_title();
109 let borders = self
110 .props
111 .get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
112 .unwrap_borders();
113 let max_entries = self
114 .props
115 .get_or(Attribute::Width, AttrValue::Length(self.data_len()))
116 .unwrap_length();
117 let data: Vec<u64> = self.get_data(max_entries);
119 let widget: TuiSparkline = TuiSparkline::default()
121 .block(crate::utils::get_block(borders, Some(title), false, None))
122 .data(data.as_slice())
123 .max(max_entries as u64)
124 .style(Style::default().fg(foreground).bg(background));
125 render.render_widget(widget, area);
127 }
128 }
129
130 fn query(&self, attr: Attribute) -> Option<AttrValue> {
131 self.props.get(attr)
132 }
133
134 fn attr(&mut self, attr: Attribute, value: AttrValue) {
135 self.props.set(attr, value)
136 }
137
138 fn state(&self) -> State {
139 State::None
140 }
141
142 fn perform(&mut self, _cmd: Cmd) -> CmdResult {
143 CmdResult::None
144 }
145}
146
147#[cfg(test)]
148mod test {
149
150 use super::*;
151
152 use pretty_assertions::assert_eq;
153
154 #[test]
155 fn test_components_sparkline() {
156 let component = Sparkline::default()
157 .background(Color::White)
158 .foreground(Color::Black)
159 .title("bandwidth", Alignment::Center)
160 .borders(Borders::default())
161 .max_entries(8)
162 .data(&[
163 60, 80, 90, 88, 76, 101, 98, 93, 96, 102, 110, 99, 88, 75, 34, 45, 67, 102,
164 ]);
165 assert_eq!(component.state(), State::None);
167 assert_eq!(component.data_len(), 18);
169 assert_eq!(component.get_data(4), vec![60, 80, 90, 88]);
170 }
171}