use crate::component::Component;
use crate::layout::{AnyWidget, LayoutWrapper};
use ratatui::layout::{Constraint, Direction, Layout};
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use std::{
any::{Any, TypeId},
rc::Rc,
};
impl Default for Element {
fn default() -> Self {
Self::new()
}
}
impl<T: Component + 'static> From<T> for Element {
fn from(component: T) -> Self {
Element::component(component)
}
}
type RenderFn = Rc<dyn Fn(&dyn Any, Rect, &mut Buffer)>;
#[derive(Clone)]
pub enum Element {
Component {
type_id: TypeId,
props: Rc<dyn Any>,
children: Vec<Element>,
key: Option<String>,
component: Rc<dyn Component>,
},
Widget {
widget: Rc<dyn Any>,
render_fn: RenderFn,
key: Option<String>,
},
Text(String),
}
impl Element {
pub fn new() -> Self {
Element::Text(String::new())
}
pub fn component<C: Component + 'static>(component: C) -> Self {
Element::Component {
type_id: TypeId::of::<C>(),
props: Rc::new(()),
children: Vec::new(),
key: None,
component: Rc::new(component),
}
}
pub fn widget<W: Widget + Clone + 'static>(widget: W) -> Self {
let widget_box = Rc::new(widget.clone());
let render_fn = Rc::new(move |any: &dyn Any, area: Rect, buffer: &mut Buffer| {
if let Some(w) = any.downcast_ref::<W>() {
w.clone().render(area, buffer);
}
});
Element::Widget {
widget: widget_box,
render_fn,
key: None,
}
}
pub fn text<S: Into<String>>(text: S) -> Self {
Element::Text(text.into())
}
pub fn fragment(elements: Vec<Element>) -> Self {
if elements.is_empty() {
Element::text("")
} else if elements.len() == 1 {
elements.into_iter().next().unwrap()
} else {
let children: Vec<AnyWidget> = elements.into_iter().map(AnyWidget::VNode).collect();
let constraints: Vec<Constraint> =
(0..children.len()).map(|_| Constraint::Min(0)).collect();
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints);
let layout_wrapper = LayoutWrapper::new(layout, children);
Element::Widget {
widget: Rc::new(layout_wrapper),
render_fn: Rc::new(|widget, area, buffer| {
if let Some(layout_wrapper) = widget.downcast_ref::<LayoutWrapper>() {
layout_wrapper.clone().render(area, buffer);
}
}),
key: None,
}
}
}
pub fn with_key<S: Into<String>>(mut self, key: S) -> Self {
match &mut self {
Element::Component { key: k, .. } => *k = Some(key.into()),
Element::Widget { key: k, .. } => *k = Some(key.into()),
Element::Text(_) => {} }
self
}
pub fn render(&self, area: Rect, buffer: &mut Buffer) {
match self {
Element::Component { component, .. } => {
crate::component::render_component_with_lifecycle(component, area, buffer);
}
Element::Widget {
widget, render_fn, ..
} => {
render_fn(widget.as_ref(), area, buffer);
}
Element::Text(_) => {
}
}
}
}
#[derive(Clone)]
pub enum PropValue {
String(String),
Int(i64),
Number(f64),
Bool(bool),
Object(Rc<dyn Any>),
}