based/src/ui/primitives/table.rs
2025-02-05 16:22:07 +01:00

217 lines
5.2 KiB
Rust

use maud::{Markup, Render, html};
use crate::ui::UIWidget;
use super::{div::Div, space::ScreenValue};
#[allow(non_snake_case)]
pub fn Table<T: UIWidget + 'static + Clone>(inner: Vec<Vec<T>>) -> 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<T: UIWidget + 'static + Clone>(inner: T) -> TableWidget {
TableWidget(Box::new(inner), Vec::new(), None, None)
}
pub struct TableWidget(
Box<dyn UIWidget>,
Vec<String>,
Option<Box<dyn UIWidget>>,
Option<Caption>,
);
impl TableWidget {
pub fn header<T: UIWidget + 'static>(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<String> {
self.1.clone()
}
fn extended_class(&self) -> Vec<String> {
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<dyn UIWidget>, bool);
impl Caption {
#[allow(non_snake_case)]
pub fn Top<T: UIWidget + 'static>(inner: T) -> Self {
Self(Box::new(inner), true)
}
#[allow(non_snake_case)]
pub fn Bottom<T: UIWidget + 'static>(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<String> {
if self.1 {
vec!["caption-top".to_string()]
} else {
vec!["caption-bottom".to_string()]
}
}
fn extended_class(&self) -> Vec<String> {
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<T: UIWidget + 'static>(inner: T) -> $widget {
$widget(Box::new(inner))
}
pub struct $widget(Box<dyn UIWidget>);
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<String> {
Vec::new()
}
fn extended_class(&self) -> Vec<String> {
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