1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use bytesize::ByteSize;
use tui::widgets::Widget;
use tui::{
    layout::{Constraint, Layout},
    symbols::bar::Set,
};
use tui::{
    style::{Color, Style},
    text::Span,
};

use crate::{
    components::{Footer, FooterItem, FooterWidget, Sparkline},
    define_widget,
    utils::get_block,
};

pub const DOTS: Set = Set {
    empty: " ",
    one_eighth: "⡀",
    one_quarter: "⣀",
    three_eighths: "⣄",
    half: "⣤",
    five_eighths: "⣦",
    three_quarters: "⣶",
    seven_eighths: "⣷",
    full: "⣿",
};

pub const REV_DOTS: Set = Set {
    empty: " ",
    one_eighth: "⠁",
    one_quarter: "⠉",
    three_eighths: "⠋",
    half: "⠛",
    five_eighths: "⠟",
    three_quarters: "⠿",
    seven_eighths: "⡿",
    full: "⣿",
};

pub const HALF: Constraint = Constraint::Percentage(50);

define_widget!(Traffics);

impl<'a> Widget for Traffics<'a> {
    fn render(self, area: tui::layout::Rect, buf: &mut tui::buffer::Buffer) {
        let traffic_size = area.width - 2;

        let traffics = self.state.traffics.iter().rev().take(traffic_size.into());

        let (up, down): (Vec<_>, Vec<_>) = traffics.map(|x| (x.up, x.down)).unzip();

        let (up_max, down_max) = (
            *up.iter().max().unwrap_or(&100),
            *down.iter().max().unwrap_or(&100),
        );

        let title = format!("▲ Max = {}/s", ByteSize(up_max).to_string_as(true));

        let up_line = Sparkline::default()
            .data(&up)
            .max(up_max)
            .bar_set(DOTS)
            .style(Style::default().fg(Color::Green));

        let down_line = Sparkline::default()
            .data(&down)
            .max(down_max)
            .bar_set(REV_DOTS)
            .style(Style::default().fg(Color::White))
            .reversed(true);

        let block = get_block(&title);

        let inner = block.inner(area);

        let layout = Layout::default()
            .direction(tui::layout::Direction::Vertical)
            .constraints([HALF, HALF])
            .split(inner);

        block.render(area, buf);
        up_line.render(layout[0], buf);
        down_line.render(layout[1], buf);

        let mut footer = Footer::default();
        footer
            .push_left(FooterItem::span(Span::raw(format!(
                " ▼ Max = {}/s ",
                ByteSize(down_max).to_string_as(true)
            ))))
            .left_offset(1);
        let footer_widget = FooterWidget::new(&footer);
        footer_widget.render(area, buf);
    }
}