rustact/runtime/
element.rs

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}