hac_client/pages/
error_popup.rs1use ratatui::buffer::Buffer;
2use ratatui::layout::{Constraint, Direction, Flex, Layout, Rect};
3use ratatui::style::{Style, Stylize};
4use ratatui::widgets::{Block, Borders, Clear, Padding, Paragraph, Widget, Wrap};
5
6#[derive(Debug, PartialEq)]
7pub struct ErrorPopupLayout {
8 message_pane: Rect,
9 confirmation_pane: Rect,
10}
11
12pub struct ErrorPopup<'a> {
13 message: String,
14 colors: &'a hac_colors::Colors,
15}
16
17impl<'a> ErrorPopup<'a> {
18 pub fn new(message: String, colors: &'a hac_colors::Colors) -> Self {
19 ErrorPopup { message, colors }
20 }
21
22 fn build_popup(&self) -> (Paragraph<'_>, Paragraph<'_>) {
23 let message = Paragraph::new(self.message.clone().fg(self.colors.normal.red))
24 .wrap(Wrap { trim: true });
25
26 let confirmation = Paragraph::new("(O)k".fg(self.colors.normal.green).into_centered_line())
27 .wrap(Wrap { trim: true });
28
29 (message, confirmation)
30 }
31
32 fn layout(&self, size: &Rect) -> ErrorPopupLayout {
33 let size = Rect::new(
34 size.x + 2,
35 size.y + 2,
36 size.width.saturating_sub(4),
37 size.height.saturating_sub(4),
38 );
39
40 let [message_pane, confirmation_pane] = Layout::default()
41 .direction(Direction::Vertical)
42 .flex(Flex::SpaceBetween)
43 .constraints([Constraint::Fill(1), Constraint::Length(1)])
44 .areas(size);
45
46 ErrorPopupLayout {
47 message_pane,
48 confirmation_pane,
49 }
50 }
51
52 fn build_container(&self) -> Block<'_> {
53 Block::default()
54 .borders(Borders::ALL)
55 .border_style(Style::default().fg(self.colors.bright.black))
56 .padding(Padding::new(2, 2, 1, 1))
57 .bg(self.colors.normal.black)
58 }
59}
60
61impl Widget for ErrorPopup<'_> {
62 fn render(self, size: Rect, buf: &mut Buffer)
63 where
64 Self: Sized,
65 {
66 Clear.render(size, buf);
67 let layout = self.layout(&size);
68 let (message, confirmation) = self.build_popup();
69 let full_block = self.build_container();
70
71 full_block.render(size, buf);
72 message.render(layout.message_pane, buf);
73 confirmation.render(layout.confirmation_pane, buf);
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_build_with_correct_message() {
83 let colors = hac_colors::Colors::default();
84 let popup = ErrorPopup::new("my error message".into(), &colors);
85
86 let (message, confirmation) = popup.build_popup();
87
88 assert_eq!(
89 message,
90 Paragraph::new("my error message".fg(colors.normal.red)).wrap(Wrap { trim: true })
91 );
92
93 assert_eq!(
94 confirmation,
95 Paragraph::new("(O)k".fg(colors.normal.green).into_centered_line())
96 .wrap(Wrap { trim: true })
97 );
98 }
99
100 #[test]
101 fn test_build_layout_correctly() {
102 let colors = hac_colors::Colors::default();
103 let popup = ErrorPopup::new("my error message".into(), &colors);
104 let rect = Rect::new(0, 0, 10, 10);
105 let expected = ErrorPopupLayout {
106 message_pane: Rect::new(2, 2, 6, 5),
107 confirmation_pane: Rect::new(2, 7, 6, 1),
108 };
109
110 let layout = popup.layout(&rect);
111
112 assert_eq!(layout, expected);
113 }
114
115 #[test]
116 fn test_build_container_correctly() {
117 let colors = hac_colors::Colors::default();
118 let popup = ErrorPopup::new("my error message".into(), &colors);
119
120 let expected = Block::default()
121 .borders(Borders::ALL)
122 .border_style(Style::default().fg(colors.bright.black))
123 .padding(Padding::new(2, 2, 1, 1))
124 .bg(colors.normal.black);
125
126 let block = popup.build_container();
127
128 assert_eq!(expected, block);
129 }
130}