hac_client/pages/
input.rs1use ratatui::buffer::Buffer;
2use ratatui::layout::Rect;
3use ratatui::style::{Style, Styled};
4use ratatui::widgets::{Block, Borders, Paragraph, StatefulWidget, Widget};
5
6pub struct Input<'a> {
9 colors: &'a hac_colors::Colors,
10 focused: bool,
11 name: String,
12 placeholder: Option<String>,
13}
14
15impl<'a> Input<'a> {
16 pub fn new(colors: &'a hac_colors::Colors, name: String) -> Self {
17 Input {
18 colors,
19 focused: false,
20 name,
21 placeholder: None,
22 }
23 }
24
25 pub fn placeholder(self, placeholder: String) -> Self {
26 Input {
27 colors: self.colors,
28 focused: self.focused,
29 name: self.name,
30 placeholder: Some(placeholder),
31 }
32 }
33
34 pub fn focus(&mut self) {
35 self.focused = true;
36 }
37
38 fn build_input(&self, value: String, size: Rect) -> Paragraph<'_> {
39 let border_color = if self.focused {
40 Style::default().fg(self.colors.normal.red)
41 } else {
42 Style::default().fg(self.colors.primary.hover)
43 };
44
45 let (value, style) = if value.is_empty() {
46 let color = Style::default().fg(self.colors.normal.magenta);
47 (self.placeholder.clone().unwrap_or_default(), color)
48 } else {
49 let color = Style::default().fg(self.colors.normal.white);
50 (value, color)
51 };
52
53 let without_space = format!(
54 "{value}{}",
55 " ".repeat(size.width.saturating_sub(value.len() as u16) as usize)
56 );
57 Paragraph::new(without_space)
58 .block(
59 Block::default()
60 .title(self.name.clone())
61 .title_style(Style::default().fg(self.colors.normal.white))
62 .borders(Borders::ALL)
63 .border_style(border_color),
64 )
65 .set_style(style)
66 }
67}
68
69impl StatefulWidget for Input<'_> {
70 type State = String;
71
72 fn render(self, size: Rect, buf: &mut Buffer, state: &mut Self::State) {
73 let input = self.build_input(state.to_string(), size);
74 input.render(size, buf);
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_build_input_with_placeholder_unfocused() {
84 let colors = hac_colors::Colors::default();
85 let input = Input::new(&colors, "my input".into()).placeholder("my placeholder".into());
86 let expected = Paragraph::new(vec!["my placeholder".into()])
87 .block(
88 Block::default()
89 .title("my input".to_string())
90 .title_style(Style::default().fg(colors.normal.white))
91 .borders(Borders::ALL)
92 .border_style(Style::default().fg(colors.primary.hover)),
93 )
94 .style(Style::default().fg(colors.normal.magenta));
95
96 let size = Rect::new(0, 0, 10, 10);
97 let result = input.build_input("".into(), size);
98
99 assert_eq!(expected, result);
100 }
101
102 #[test]
103 fn test_build_input_with_placeholder_focused() {
104 let colors = hac_colors::Colors::default();
105 let mut input = Input::new(&colors, "my input".into()).placeholder("my placeholder".into());
106 let expected = Paragraph::new(vec!["my placeholder".into()])
107 .block(
108 Block::default()
109 .title("my input".to_string())
110 .title_style(Style::default().fg(colors.normal.white))
111 .borders(Borders::ALL)
112 .border_style(Style::default().fg(colors.normal.red)),
113 )
114 .style(Style::default().fg(colors.normal.magenta));
115
116 input.focus();
117 let size = Rect::new(0, 0, 10, 10);
118 let result = input.build_input("".into(), size);
119
120 assert_eq!(expected, result);
121 }
122
123 #[test]
124 fn test_build_input_with_value_unfocused() {
125 let colors = hac_colors::Colors::default();
126 let input = Input::new(&colors, "my input".into()).placeholder("my placeholder".into());
127 let expected = Paragraph::new(vec!["my value".into()])
128 .block(
129 Block::default()
130 .title("my input".to_string())
131 .title_style(Style::default().fg(colors.normal.white))
132 .borders(Borders::ALL)
133 .border_style(Style::default().fg(colors.primary.hover)),
134 )
135 .style(Style::default().fg(colors.normal.white));
136
137 let size = Rect::new(0, 0, 8, 3);
138 let result = input.build_input("my value".into(), size);
139
140 assert_eq!(expected, result);
141 }
142
143 #[test]
144 fn test_build_input_with_value_focused() {
145 let colors = hac_colors::Colors::default();
146 let mut input = Input::new(&colors, "my input".into()).placeholder("my placeholder".into());
147 let expected = Paragraph::new(vec!["my value".into()])
148 .block(
149 Block::default()
150 .title("my input".to_string())
151 .title_style(Style::default().fg(colors.normal.white))
152 .borders(Borders::ALL)
153 .border_style(Style::default().fg(colors.normal.red)),
154 )
155 .style(Style::default().fg(colors.normal.white));
156
157 input.focus();
158 let size = Rect::new(0, 0, 8, 3);
159 let result = input.build_input("my value".into(), size);
160
161 assert_eq!(expected, result);
162 }
163}