1use crate::component::Component;
4use crate::layout::{AnyWidget, LayoutWrapper};
5use ratatui::layout::{Constraint, Direction, Layout};
6use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
7use std::{
8 any::{Any, TypeId},
9 rc::Rc,
10};
11
12impl Default for Element {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl<T: Component + 'static> From<T> for Element {
19 fn from(component: T) -> Self {
20 Element::component(component)
21 }
22}
23
24type RenderFn = Rc<dyn Fn(&dyn Any, Rect, &mut Buffer)>;
26
27#[derive(Clone)]
29pub enum Element {
30 Component {
32 type_id: TypeId,
34 props: Rc<dyn Any>,
36 children: Vec<Element>,
38 key: Option<String>,
40 component: Rc<dyn Component>,
42 },
43 Widget {
45 widget: Rc<dyn Any>,
47 render_fn: RenderFn,
49 key: Option<String>,
51 },
52 Text(String),
54}
55
56impl Element {
57 pub fn new() -> Self {
59 Element::Text(String::new())
60 }
61
62 pub fn component<C: Component + 'static>(component: C) -> Self {
64 Element::Component {
65 type_id: TypeId::of::<C>(),
66 props: Rc::new(()),
67 children: Vec::new(),
68 key: None,
69 component: Rc::new(component),
70 }
71 }
72
73 pub fn widget<W: Widget + Clone + 'static>(widget: W) -> Self {
75 let widget_box = Rc::new(widget.clone());
76 let render_fn = Rc::new(move |any: &dyn Any, area: Rect, buffer: &mut Buffer| {
77 if let Some(w) = any.downcast_ref::<W>() {
78 w.clone().render(area, buffer);
79 }
80 });
81
82 Element::Widget {
83 widget: widget_box,
84 render_fn,
85 key: None,
86 }
87 }
88
89 pub fn text<S: Into<String>>(text: S) -> Self {
91 Element::Text(text.into())
92 }
93
94 pub fn fragment(elements: Vec<Element>) -> Self {
97 if elements.is_empty() {
98 Element::text("")
99 } else if elements.len() == 1 {
100 elements.into_iter().next().unwrap()
101 } else {
102 let children: Vec<AnyWidget> = elements.into_iter().map(AnyWidget::VNode).collect();
106
107 let constraints: Vec<Constraint> =
109 (0..children.len()).map(|_| Constraint::Min(0)).collect();
110
111 let layout = Layout::default()
112 .direction(Direction::Vertical)
113 .constraints(constraints);
114
115 let layout_wrapper = LayoutWrapper::new(layout, children);
116
117 Element::Widget {
118 widget: Rc::new(layout_wrapper),
119 render_fn: Rc::new(|widget, area, buffer| {
120 if let Some(layout_wrapper) = widget.downcast_ref::<LayoutWrapper>() {
121 layout_wrapper.clone().render(area, buffer);
122 }
123 }),
124 key: None,
125 }
126 }
127 }
128
129 pub fn with_key<S: Into<String>>(mut self, key: S) -> Self {
131 match &mut self {
132 Element::Component { key: k, .. } => *k = Some(key.into()),
133 Element::Widget { key: k, .. } => *k = Some(key.into()),
134 Element::Text(_) => {} }
136 self
137 }
138
139 pub fn render(&self, area: Rect, buffer: &mut Buffer) {
141 match self {
142 Element::Component { component, .. } => {
143 crate::component::render_component_with_lifecycle(component, area, buffer);
145 }
146 Element::Widget {
147 widget, render_fn, ..
148 } => {
149 render_fn(widget.as_ref(), area, buffer);
150 }
151 Element::Text(_) => {
152 }
154 }
155 }
156}
157
158#[derive(Clone)]
160pub enum PropValue {
161 String(String),
163 Int(i64),
165 Number(f64),
167 Bool(bool),
169 Object(Rc<dyn Any>),
171}