use tuirealm::command::{Cmd, CmdResult};
use tuirealm::component::Component;
use tuirealm::props::{
AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, QueryResult, Style, Title,
};
use tuirealm::ratatui::Frame;
use tuirealm::ratatui::layout::Rect;
use tuirealm::ratatui::widgets::Sparkline as TuiSparkline;
use tuirealm::state::State;
use crate::prop_ext::CommonProps;
#[derive(Default)]
#[must_use]
pub struct Sparkline {
common: CommonProps,
props: Props,
}
impl Sparkline {
pub fn foreground(mut self, fg: Color) -> Self {
self.attr(Attribute::Foreground, AttrValue::Color(fg));
self
}
pub fn background(mut self, bg: Color) -> Self {
self.attr(Attribute::Background, AttrValue::Color(bg));
self
}
pub fn style(mut self, style: Style) -> Self {
self.attr(Attribute::Style, AttrValue::Style(style));
self
}
pub fn inactive(mut self, s: Style) -> Self {
self.attr(Attribute::UnfocusedBorderStyle, AttrValue::Style(s));
self
}
pub fn borders(mut self, b: Borders) -> Self {
self.attr(Attribute::Borders, AttrValue::Borders(b));
self
}
pub fn title<T: Into<Title>>(mut self, title: T) -> Self {
self.attr(Attribute::Title, AttrValue::Title(title.into()));
self
}
pub fn max_entries(mut self, max: usize) -> Self {
self.attr(Attribute::Width, AttrValue::Length(max));
self
}
pub fn data(mut self, data: &[u64]) -> Self {
self.attr(
Attribute::Dataset,
AttrValue::Payload(PropPayload::Vec(
data.iter().map(|x| PropValue::U64(*x)).collect(),
)),
);
self
}
fn data_len(&self) -> usize {
self.props
.get(Attribute::Dataset)
.and_then(AttrValue::as_payload)
.and_then(PropPayload::as_vec)
.map_or(0, |v| v.len())
}
fn get_data(&self, max: usize) -> Vec<u64> {
self.props
.get(Attribute::Dataset)
.and_then(AttrValue::as_payload)
.and_then(PropPayload::as_vec)
.map(|list| {
list.iter()
.take(max)
.filter_map(PropValue::as_u64)
.collect()
})
.unwrap_or_default()
}
}
impl Component for Sparkline {
fn view(&mut self, render: &mut Frame, area: Rect) {
if !self.common.display {
return;
}
let max_entries = self
.props
.get(Attribute::Width)
.and_then(AttrValue::as_length)
.unwrap_or(self.data_len());
let data: Vec<u64> = self.get_data(max_entries);
let mut widget = TuiSparkline::default()
.data(data.as_slice())
.max(max_entries as u64)
.style(self.common.style);
if let Some(block) = self.common.get_block() {
widget = widget.block(block);
}
render.render_widget(widget, area);
}
fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
if let Some(value) = self.common.get_for_query(attr) {
return Some(value);
}
self.props.get_for_query(attr)
}
fn attr(&mut self, attr: Attribute, value: AttrValue) {
if let Some(value) = self.common.set(attr, value) {
self.props.set(attr, value);
}
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, cmd: Cmd) -> CmdResult {
CmdResult::Invalid(cmd)
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use tuirealm::props::HorizontalAlignment;
use super::*;
#[test]
fn test_components_sparkline() {
let component = Sparkline::default()
.background(Color::White)
.foreground(Color::Black)
.title(Title::from("bandwidth").alignment(HorizontalAlignment::Center))
.borders(Borders::default())
.max_entries(8)
.data(&[
60, 80, 90, 88, 76, 101, 98, 93, 96, 102, 110, 99, 88, 75, 34, 45, 67, 102,
]);
assert_eq!(component.state(), State::None);
assert_eq!(component.data_len(), 18);
assert_eq!(component.get_data(4), vec![60, 80, 90, 88]);
}
}