1use ratatui::style::Color;
2
3use crate::text_input::TextInputHandle;
4
5use super::component::ComponentElement;
6
7#[derive(Clone, Debug)]
8pub enum Element {
9 Empty,
10 Text(TextNode),
11 Flex(FlexNode),
12 Block(BlockNode),
13 List(ListNode),
14 Gauge(GaugeNode),
15 Button(ButtonNode),
16 Table(TableNode),
17 Tree(TreeNode),
18 Form(FormNode),
19 Input(TextInputNode),
20 Tabs(TabsNode),
21 Layered(LayeredNode),
22 Modal(ModalNode),
23 ToastStack(ToastStackNode),
24 Fragment(Vec<Element>),
25 Component(ComponentElement),
26}
27
28#[derive(Clone, Debug)]
29pub struct TextNode {
30 pub content: String,
31 pub color: Option<Color>,
32}
33
34#[derive(Clone, Debug)]
35pub struct FlexNode {
36 pub direction: FlexDirection,
37 pub children: Vec<Element>,
38}
39
40#[derive(Clone, Debug)]
41pub struct BlockNode {
42 pub title: Option<String>,
43 pub child: Box<Element>,
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum FlexDirection {
48 Row,
49 Column,
50}
51
52impl Element {
53 pub fn text(content: impl Into<String>) -> Self {
54 Element::Text(TextNode {
55 content: content.into(),
56 color: None,
57 })
58 }
59
60 pub fn colored_text(content: impl Into<String>, color: Color) -> Self {
61 Element::Text(TextNode {
62 content: content.into(),
63 color: Some(color),
64 })
65 }
66
67 pub fn vstack(children: Vec<Element>) -> Self {
68 Element::Flex(FlexNode {
69 direction: FlexDirection::Column,
70 children,
71 })
72 }
73
74 pub fn hstack(children: Vec<Element>) -> Self {
75 Element::Flex(FlexNode {
76 direction: FlexDirection::Row,
77 children,
78 })
79 }
80
81 pub fn block(title: impl Into<String>, child: Element) -> Self {
82 Element::Block(BlockNode {
83 title: Some(title.into()),
84 child: Box::new(child),
85 })
86 }
87
88 pub fn fragment(children: Vec<Element>) -> Self {
89 Element::Fragment(children)
90 }
91
92 pub fn list(node: ListNode) -> Self {
93 Element::List(node)
94 }
95
96 pub fn gauge(node: GaugeNode) -> Self {
97 Element::Gauge(node)
98 }
99
100 pub fn button(node: ButtonNode) -> Self {
101 Element::Button(node)
102 }
103
104 pub fn table(node: TableNode) -> Self {
105 Element::Table(node)
106 }
107
108 pub fn tree(node: TreeNode) -> Self {
109 Element::Tree(node)
110 }
111
112 pub fn form(node: FormNode) -> Self {
113 Element::Form(node)
114 }
115
116 pub fn text_input(node: TextInputNode) -> Self {
117 Element::Input(node)
118 }
119
120 pub fn tabs(node: TabsNode) -> Self {
121 Element::Tabs(node)
122 }
123
124 pub fn layers(node: LayeredNode) -> Self {
125 Element::Layered(node)
126 }
127
128 pub fn modal(node: ModalNode) -> Self {
129 Element::Modal(node)
130 }
131
132 pub fn toast_stack(node: ToastStackNode) -> Self {
133 Element::ToastStack(node)
134 }
135}
136
137#[derive(Clone, Debug)]
138pub struct ListNode {
139 pub title: Option<String>,
140 pub items: Vec<ListItemNode>,
141 pub highlight: Option<usize>,
142 pub highlight_color: Option<Color>,
143}
144
145impl ListNode {
146 pub fn new(items: Vec<ListItemNode>) -> Self {
147 Self {
148 title: None,
149 items,
150 highlight: None,
151 highlight_color: None,
152 }
153 }
154
155 pub fn title(mut self, title: impl Into<String>) -> Self {
156 self.title = Some(title.into());
157 self
158 }
159
160 pub fn highlight(mut self, index: usize) -> Self {
161 self.highlight = Some(index);
162 self
163 }
164
165 pub fn highlight_color(mut self, color: Color) -> Self {
166 self.highlight_color = Some(color);
167 self
168 }
169}
170
171#[derive(Clone, Debug)]
172pub struct ListItemNode {
173 pub content: String,
174 pub color: Option<Color>,
175}
176
177impl ListItemNode {
178 pub fn new(content: impl Into<String>) -> Self {
179 Self {
180 content: content.into(),
181 color: None,
182 }
183 }
184
185 pub fn color(mut self, color: Color) -> Self {
186 self.color = Some(color);
187 self
188 }
189}
190
191#[derive(Clone, Debug)]
192pub struct GaugeNode {
193 pub label: Option<String>,
194 pub ratio: f64,
195 pub color: Option<Color>,
196}
197
198impl GaugeNode {
199 pub fn new(ratio: f64) -> Self {
200 Self {
201 label: None,
202 ratio,
203 color: None,
204 }
205 }
206
207 pub fn label(mut self, label: impl Into<String>) -> Self {
208 self.label = Some(label.into());
209 self
210 }
211
212 pub fn color(mut self, color: Color) -> Self {
213 self.color = Some(color);
214 self
215 }
216}
217
218#[derive(Clone, Debug)]
219pub struct ButtonNode {
220 pub id: String,
221 pub label: String,
222 pub accent: Option<Color>,
223 pub filled: bool,
224}
225
226impl ButtonNode {
227 pub fn new(id: impl Into<String>, label: impl Into<String>) -> Self {
228 Self {
229 id: id.into(),
230 label: label.into(),
231 accent: None,
232 filled: false,
233 }
234 }
235
236 pub fn accent(mut self, color: Color) -> Self {
237 self.accent = Some(color);
238 self
239 }
240
241 pub fn filled(mut self, filled: bool) -> Self {
242 self.filled = filled;
243 self
244 }
245}
246
247#[derive(Clone, Debug)]
248pub struct TableNode {
249 pub title: Option<String>,
250 pub header: Option<TableRowNode>,
251 pub rows: Vec<TableRowNode>,
252 pub highlight: Option<usize>,
253 pub column_widths: Option<Vec<u16>>,
254}
255
256impl TableNode {
257 pub fn new(rows: Vec<TableRowNode>) -> Self {
258 Self {
259 title: None,
260 header: None,
261 rows,
262 highlight: None,
263 column_widths: None,
264 }
265 }
266
267 pub fn title(mut self, title: impl Into<String>) -> Self {
268 self.title = Some(title.into());
269 self
270 }
271
272 pub fn header(mut self, header: TableRowNode) -> Self {
273 self.header = Some(header);
274 self
275 }
276
277 pub fn highlight(mut self, index: usize) -> Self {
278 self.highlight = Some(index);
279 self
280 }
281
282 pub fn widths(mut self, widths: Vec<u16>) -> Self {
283 self.column_widths = Some(widths);
284 self
285 }
286}
287
288#[derive(Clone, Debug)]
289pub struct TableRowNode {
290 pub cells: Vec<TableCellNode>,
291}
292
293impl TableRowNode {
294 pub fn new(cells: Vec<TableCellNode>) -> Self {
295 Self { cells }
296 }
297
298 pub fn cell(mut self, cell: TableCellNode) -> Self {
299 self.cells.push(cell);
300 self
301 }
302}
303
304#[derive(Clone, Debug)]
305pub struct TableCellNode {
306 pub content: String,
307 pub color: Option<Color>,
308 pub bold: bool,
309}
310
311impl TableCellNode {
312 pub fn new(content: impl Into<String>) -> Self {
313 Self {
314 content: content.into(),
315 color: None,
316 bold: false,
317 }
318 }
319
320 pub fn color(mut self, color: Color) -> Self {
321 self.color = Some(color);
322 self
323 }
324
325 pub fn bold(mut self) -> Self {
326 self.bold = true;
327 self
328 }
329}
330
331#[derive(Clone, Debug)]
332pub struct TreeNode {
333 pub title: Option<String>,
334 pub items: Vec<TreeItemNode>,
335 pub highlight: Option<usize>,
336}
337
338impl TreeNode {
339 pub fn new(items: Vec<TreeItemNode>) -> Self {
340 Self {
341 title: None,
342 items,
343 highlight: None,
344 }
345 }
346
347 pub fn title(mut self, title: impl Into<String>) -> Self {
348 self.title = Some(title.into());
349 self
350 }
351
352 pub fn highlight(mut self, index: usize) -> Self {
353 self.highlight = Some(index);
354 self
355 }
356}
357
358#[derive(Clone, Debug)]
359pub struct TreeItemNode {
360 pub label: String,
361 pub children: Vec<TreeItemNode>,
362 pub expanded: bool,
363}
364
365impl TreeItemNode {
366 pub fn new(label: impl Into<String>) -> Self {
367 Self {
368 label: label.into(),
369 children: Vec::new(),
370 expanded: true,
371 }
372 }
373
374 pub fn child(mut self, child: TreeItemNode) -> Self {
375 self.children.push(child);
376 self
377 }
378
379 pub fn children(mut self, children: Vec<TreeItemNode>) -> Self {
380 self.children = children;
381 self
382 }
383
384 pub fn expanded(mut self, expanded: bool) -> Self {
385 self.expanded = expanded;
386 self
387 }
388}
389
390#[derive(Clone, Debug)]
391pub struct FormNode {
392 pub title: Option<String>,
393 pub fields: Vec<FormFieldNode>,
394 pub label_width: u16,
395}
396
397impl FormNode {
398 pub fn new(fields: Vec<FormFieldNode>) -> Self {
399 Self {
400 title: None,
401 fields,
402 label_width: 30,
403 }
404 }
405
406 pub fn title(mut self, title: impl Into<String>) -> Self {
407 self.title = Some(title.into());
408 self
409 }
410
411 pub fn label_width(mut self, percent: u16) -> Self {
412 self.label_width = percent.clamp(10, 90);
413 self
414 }
415}
416
417#[derive(Clone, Debug)]
418pub struct FormFieldNode {
419 pub label: String,
420 pub value: String,
421 pub status: FormFieldStatus,
422}
423
424impl FormFieldNode {
425 pub fn new(label: impl Into<String>, value: impl Into<String>) -> Self {
426 Self {
427 label: label.into(),
428 value: value.into(),
429 status: FormFieldStatus::Normal,
430 }
431 }
432
433 pub fn status(mut self, status: FormFieldStatus) -> Self {
434 self.status = status;
435 self
436 }
437}
438
439#[derive(Clone, Copy, Debug, PartialEq, Eq)]
440pub enum FormFieldStatus {
441 Normal,
442 Warning,
443 Error,
444 Success,
445}
446
447#[derive(Clone, Debug)]
448pub struct TextInputNode {
449 pub binding: TextInputHandle,
450 pub label: Option<String>,
451 pub placeholder: Option<String>,
452 pub width: Option<u16>,
453 pub secure: bool,
454 pub accent: Option<Color>,
455 pub border_color: Option<Color>,
456 pub text_color: Option<Color>,
457 pub placeholder_color: Option<Color>,
458 pub background_color: Option<Color>,
459 pub focus_background: Option<Color>,
460 pub status: FormFieldStatus,
461}
462
463impl TextInputNode {
464 pub fn new(binding: TextInputHandle) -> Self {
465 Self {
466 binding,
467 label: None,
468 placeholder: None,
469 width: None,
470 secure: false,
471 accent: None,
472 border_color: None,
473 text_color: None,
474 placeholder_color: None,
475 background_color: None,
476 focus_background: None,
477 status: FormFieldStatus::Normal,
478 }
479 }
480
481 pub fn label(mut self, label: impl Into<String>) -> Self {
482 self.label = Some(label.into());
483 self
484 }
485
486 pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
487 self.placeholder = Some(placeholder.into());
488 self
489 }
490
491 pub fn width(mut self, width: u16) -> Self {
492 self.width = Some(width);
493 self
494 }
495
496 pub fn secure(mut self, secure: bool) -> Self {
497 self.secure = secure;
498 self
499 }
500
501 pub fn accent(mut self, color: Color) -> Self {
502 self.accent = Some(color);
503 self
504 }
505
506 pub fn border_color(mut self, color: Color) -> Self {
507 self.border_color = Some(color);
508 self
509 }
510
511 pub fn text_color(mut self, color: Color) -> Self {
512 self.text_color = Some(color);
513 self
514 }
515
516 pub fn placeholder_color(mut self, color: Color) -> Self {
517 self.placeholder_color = Some(color);
518 self
519 }
520
521 pub fn background_color(mut self, color: Color) -> Self {
522 self.background_color = Some(color);
523 self
524 }
525
526 pub fn focus_background(mut self, color: Color) -> Self {
527 self.focus_background = Some(color);
528 self
529 }
530
531 pub fn status(mut self, status: FormFieldStatus) -> Self {
532 self.status = status;
533 self
534 }
535}
536
537#[derive(Clone, Debug)]
538pub struct TabsNode {
539 pub tabs: Vec<TabPaneNode>,
540 pub active: usize,
541 pub accent: Option<Color>,
542 pub title: Option<String>,
543}
544
545impl TabsNode {
546 pub fn new(tabs: Vec<TabPaneNode>) -> Self {
547 Self {
548 tabs,
549 active: 0,
550 accent: None,
551 title: None,
552 }
553 }
554
555 pub fn active(mut self, index: usize) -> Self {
556 self.active = index;
557 self
558 }
559
560 pub fn accent(mut self, color: Color) -> Self {
561 self.accent = Some(color);
562 self
563 }
564
565 pub fn title(mut self, title: impl Into<String>) -> Self {
566 self.title = Some(title.into());
567 self
568 }
569}
570
571#[derive(Clone, Debug)]
572pub struct TabPaneNode {
573 pub label: String,
574 pub content: Element,
575}
576
577impl TabPaneNode {
578 pub fn new(label: impl Into<String>, content: Element) -> Self {
579 Self {
580 label: label.into(),
581 content,
582 }
583 }
584}
585
586#[derive(Clone, Debug)]
587pub struct LayeredNode {
588 pub layers: Vec<Element>,
589}
590
591impl LayeredNode {
592 pub fn new(layers: Vec<Element>) -> Self {
593 Self { layers }
594 }
595}
596
597#[derive(Clone, Debug)]
598pub struct ModalNode {
599 pub title: Option<String>,
600 pub content: Box<Element>,
601 pub width: Option<u16>,
602 pub height: Option<u16>,
603}
604
605impl ModalNode {
606 pub fn new(content: Element) -> Self {
607 Self {
608 title: None,
609 content: Box::new(content),
610 width: None,
611 height: None,
612 }
613 }
614
615 pub fn title(mut self, title: impl Into<String>) -> Self {
616 self.title = Some(title.into());
617 self
618 }
619
620 pub fn width(mut self, width: u16) -> Self {
621 self.width = Some(width);
622 self
623 }
624
625 pub fn height(mut self, height: u16) -> Self {
626 self.height = Some(height);
627 self
628 }
629}
630
631#[derive(Clone, Debug)]
632pub struct ToastStackNode {
633 pub toasts: Vec<ToastNode>,
634}
635
636impl ToastStackNode {
637 pub fn new(toasts: Vec<ToastNode>) -> Self {
638 Self { toasts }
639 }
640
641 pub fn push(mut self, toast: ToastNode) -> Self {
642 self.toasts.push(toast);
643 self
644 }
645}
646
647#[derive(Clone, Debug)]
648pub struct ToastNode {
649 pub title: String,
650 pub body: Option<String>,
651 pub level: ToastLevel,
652}
653
654impl ToastNode {
655 pub fn new(title: impl Into<String>) -> Self {
656 Self {
657 title: title.into(),
658 body: None,
659 level: ToastLevel::Info,
660 }
661 }
662
663 pub fn body(mut self, body: impl Into<String>) -> Self {
664 self.body = Some(body.into());
665 self
666 }
667
668 pub fn level(mut self, level: ToastLevel) -> Self {
669 self.level = level;
670 self
671 }
672}
673
674#[derive(Clone, Copy, Debug, PartialEq, Eq)]
675pub enum ToastLevel {
676 Info,
677 Success,
678 Warning,
679 Error,
680}