use maud::{Markup, Render, html}; use crate::ui::UIWidget; use super::{div::Div, space::ScreenValue}; #[allow(non_snake_case)] pub fn Table(inner: Vec>) -> TableWidget { let inner = Div().vanish().push_for_each(&inner, |row| { TableRow( Div() .vanish() .push_for_each(&row, |col| TableData(col.clone())), ) }); TableWidget(Box::new(inner), Vec::new(), None, None) } #[allow(non_snake_case)] pub fn TableRaw(inner: T) -> TableWidget { TableWidget(Box::new(inner), Vec::new(), None, None) } pub struct TableWidget( Box, Vec, Option>, Option, ); impl TableWidget { pub fn header(mut self, header: T) -> Self { self.2 = Some(Box::new(header)); self } pub fn caption(mut self, caption: Caption) -> Self { self.3 = Some(caption); self } pub fn border_collapse(mut self) -> Self { self.1.push("border-collapse".to_string()); self } pub fn border_seperate(mut self) -> Self { self.1.push("border-separate".to_string()); self } pub fn border_spacing(mut self, spacing: ScreenValue) -> Self { self.1 .push(format!("border-spacing-{}", spacing.to_value())); self } pub fn border_spacing_x(mut self, spacing: ScreenValue) -> Self { self.1 .push(format!("border-spacing-x-{}", spacing.to_value())); self } pub fn border_spacing_y(mut self, spacing: ScreenValue) -> Self { self.1 .push(format!("border-spacing-y-{}", spacing.to_value())); self } pub fn layout_fixed(mut self) -> Self { self.1.push("table-fixed".to_string()); self } pub fn layout_auto(mut self) -> Self { self.1.push("table-auto".to_string()); self } } impl Render for TableWidget { fn render(&self) -> maud::Markup { self.render_with_class("") } } impl UIWidget for TableWidget { fn can_inherit(&self) -> bool { true } fn base_class(&self) -> Vec { self.1.clone() } fn extended_class(&self) -> Vec { self.base_class() } fn render_with_class(&self, class: &str) -> maud::Markup { html! { table class=(format!("{} {class}", self.base_class().join(" "))) { @if let Some(caption) = &self.3 { (caption) } @if let Some(header) = &self.2 { thead { (header) }; }; (self.0.as_ref()) }; } } } pub struct Caption(Box, bool); impl Caption { #[allow(non_snake_case)] pub fn Top(inner: T) -> Self { Self(Box::new(inner), true) } #[allow(non_snake_case)] pub fn Bottom(inner: T) -> Self { Self(Box::new(inner), false) } } impl Render for Caption { fn render(&self) -> maud::Markup { self.render_with_class("") } } impl UIWidget for Caption { fn can_inherit(&self) -> bool { false } fn base_class(&self) -> Vec { if self.1 { vec!["caption-top".to_string()] } else { vec!["caption-bottom".to_string()] } } fn extended_class(&self) -> Vec { self.base_class() } fn render_with_class(&self, _: &str) -> maud::Markup { html! { caption class=(self.base_class().join(" ")) { (self.0.as_ref()) }; } } } macro_rules! element_widget { ($name:ident, $widget:ident, $element:ident) => { #[allow(non_snake_case)] pub fn $name(inner: T) -> $widget { $widget(Box::new(inner)) } pub struct $widget(Box); impl Render for $widget { fn render(&self) -> Markup { self.render_with_class("") } } impl UIWidget for $widget { fn can_inherit(&self) -> bool { true } fn base_class(&self) -> Vec { Vec::new() } fn extended_class(&self) -> Vec { let mut c = self.base_class(); c.extend_from_slice(&self.0.extended_class()); c } fn render_with_class(&self, class: &str) -> Markup { html! { $element class=(class) { (self.0.as_ref()) } } } } }; } element_widget!(TableRow, TableRowWidget, tr); element_widget!(TableHead, TableHeadWidget, th); element_widget!(TableData, TableDataWidget, td); element_widget!(Header, HeaderWidget, header); // https://flowbite.com/docs/components/tables/ // TODO : tables // table options // wrap data to table (crud) // TODO: TABLE https://flowbite.com/docs/components/pagination/#table-data-pagination