tui_realm_stdlib/components/
sparkline.rs1use tuirealm::command::{Cmd, CmdResult};
4use tuirealm::component::Component;
5use tuirealm::props::{
6 AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, QueryResult, Style, Title,
7};
8use tuirealm::ratatui::Frame;
9use tuirealm::ratatui::layout::Rect;
10use tuirealm::ratatui::widgets::Sparkline as TuiSparkline;
11use tuirealm::state::State;
12
13use crate::prop_ext::CommonProps;
14
15#[derive(Default)]
23#[must_use]
24pub struct Sparkline {
25 common: CommonProps,
26 props: Props,
27}
28
29impl Sparkline {
30 pub fn foreground(mut self, fg: Color) -> Self {
32 self.attr(Attribute::Foreground, AttrValue::Color(fg));
33 self
34 }
35
36 pub fn background(mut self, bg: Color) -> Self {
38 self.attr(Attribute::Background, AttrValue::Color(bg));
39 self
40 }
41
42 pub fn style(mut self, style: Style) -> Self {
46 self.attr(Attribute::Style, AttrValue::Style(style));
47 self
48 }
49
50 pub fn inactive(mut self, s: Style) -> Self {
52 self.attr(Attribute::UnfocusedBorderStyle, AttrValue::Style(s));
53 self
54 }
55
56 pub fn borders(mut self, b: Borders) -> Self {
58 self.attr(Attribute::Borders, AttrValue::Borders(b));
59 self
60 }
61
62 pub fn title<T: Into<Title>>(mut self, title: T) -> Self {
64 self.attr(Attribute::Title, AttrValue::Title(title.into()));
65 self
66 }
67
68 pub fn max_entries(mut self, max: usize) -> Self {
70 self.attr(Attribute::Width, AttrValue::Length(max));
71 self
72 }
73
74 pub fn data(mut self, data: &[u64]) -> Self {
76 self.attr(
77 Attribute::Dataset,
78 AttrValue::Payload(PropPayload::Vec(
79 data.iter().map(|x| PropValue::U64(*x)).collect(),
80 )),
81 );
82 self
83 }
84
85 fn data_len(&self) -> usize {
89 self.props
90 .get(Attribute::Dataset)
91 .and_then(AttrValue::as_payload)
92 .and_then(PropPayload::as_vec)
93 .map_or(0, |v| v.len())
94 }
95
96 fn get_data(&self, max: usize) -> Vec<u64> {
100 self.props
101 .get(Attribute::Dataset)
102 .and_then(AttrValue::as_payload)
103 .and_then(PropPayload::as_vec)
104 .map(|list| {
105 list.iter()
106 .take(max)
107 .filter_map(PropValue::as_u64)
108 .collect()
109 })
110 .unwrap_or_default()
111 }
112}
113
114impl Component for Sparkline {
115 fn view(&mut self, render: &mut Frame, area: Rect) {
116 if !self.common.display {
117 return;
118 }
119
120 let max_entries = self
121 .props
122 .get(Attribute::Width)
123 .and_then(AttrValue::as_length)
124 .unwrap_or(self.data_len());
125 let data: Vec<u64> = self.get_data(max_entries);
127 let mut widget = TuiSparkline::default()
129 .data(data.as_slice())
130 .max(max_entries as u64)
131 .style(self.common.style);
132
133 if let Some(block) = self.common.get_block() {
134 widget = widget.block(block);
135 }
136
137 render.render_widget(widget, area);
139 }
140
141 fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
142 if let Some(value) = self.common.get_for_query(attr) {
143 return Some(value);
144 }
145
146 self.props.get_for_query(attr)
147 }
148
149 fn attr(&mut self, attr: Attribute, value: AttrValue) {
150 if let Some(value) = self.common.set(attr, value) {
151 self.props.set(attr, value);
152 }
153 }
154
155 fn state(&self) -> State {
156 State::None
157 }
158
159 fn perform(&mut self, cmd: Cmd) -> CmdResult {
160 CmdResult::Invalid(cmd)
161 }
162}
163
164#[cfg(test)]
165mod test {
166
167 use pretty_assertions::assert_eq;
168 use tuirealm::props::HorizontalAlignment;
169
170 use super::*;
171
172 #[test]
173 fn test_components_sparkline() {
174 let component = Sparkline::default()
175 .background(Color::White)
176 .foreground(Color::Black)
177 .title(Title::from("bandwidth").alignment(HorizontalAlignment::Center))
178 .borders(Borders::default())
179 .max_entries(8)
180 .data(&[
181 60, 80, 90, 88, 76, 101, 98, 93, 96, 102, 110, 99, 88, 75, 34, 45, 67, 102,
182 ]);
183 assert_eq!(component.state(), State::None);
185 assert_eq!(component.data_len(), 18);
187 assert_eq!(component.get_data(4), vec![60, 80, 90, 88]);
188 }
189}