based/src/ui/primitives/grid.rs
2025-01-21 13:40:56 +01:00

461 lines
12 KiB
Rust

use crate::ui::{UIWidget, color::UIColor};
use maud::{Markup, Render, html};
use super::{
flex::{AlignContent, AlignItems, DivideStyle, DivideWidth, Justify, JustifyItems},
space::ScreenValue,
};
#[allow(non_snake_case)]
pub fn Grid<T: UIWidget + 'static>(inner: T) -> GridWidget {
GridWidget(Box::new(inner), vec![], false)
}
pub struct GridWidget(Box<dyn UIWidget>, Vec<String>, bool);
impl Render for GridWidget {
fn render(&self) -> Markup {
self.render_with_class("")
}
}
pub enum GridAmount {
_1,
_2,
_3,
_4,
_5,
_6,
_7,
_8,
_9,
_10,
_11,
_12,
None,
Subgrid,
}
impl GridAmount {
pub const fn to_value(&self) -> &str {
match self {
GridAmount::_1 => "1",
GridAmount::_2 => "2",
GridAmount::_3 => "3",
GridAmount::_4 => "4",
GridAmount::_5 => "5",
GridAmount::_6 => "6",
GridAmount::_7 => "7",
GridAmount::_8 => "8",
GridAmount::_9 => "9",
GridAmount::_10 => "10",
GridAmount::_11 => "11",
GridAmount::_12 => "12",
GridAmount::None => "none",
GridAmount::Subgrid => "subgrid",
}
}
}
impl GridWidget {
#[must_use]
pub fn columns(mut self, amount: GridAmount) -> Self {
self.1.push(format!("grid-cols-{}", amount.to_value()));
self
}
#[must_use]
pub fn rows(mut self, amount: GridAmount) -> Self {
self.1.push(format!("grid-rows-{}", amount.to_value()));
self
}
#[must_use]
pub fn auto_flow(mut self, flow: GridAutoFlow) -> Self {
self.1.push(flow.to_value().to_string());
self
}
#[must_use]
pub fn auto_columns(mut self, size: GridAutoSize) -> Self {
self.1.push(format!("auto-cols-{}", size.to_value()));
self
}
#[must_use]
pub fn auto_rows(mut self, size: GridAutoSize) -> Self {
self.1.push(format!("auto-rows-{}", size.to_value()));
self
}
#[must_use]
pub fn full_center(mut self) -> Self {
self.1.push("items-center".to_owned());
self.1.push("justify-center".to_owned());
self
}
#[must_use]
pub const fn group(mut self) -> Self {
self.2 = true;
self
}
#[must_use]
pub fn divide_style(mut self, style: DivideStyle) -> Self {
self.1.push(style.to_value().to_string());
self
}
#[must_use]
pub fn divide_color<C: UIColor + 'static>(mut self, color: C) -> Self {
self.1.push(format!("divide-{}", color.color_class()));
self
}
#[must_use]
pub fn divide_x(mut self, width: DivideWidth) -> Self {
self.1.push(format!("divide-x-{}", width.to_value()));
self
}
#[must_use]
pub fn divide_y(mut self, width: DivideWidth) -> Self {
self.1.push(format!("divide-y-{}", width.to_value()));
self
}
#[must_use]
pub fn justify_items(mut self, justify: JustifyItems) -> Self {
self.1.push(justify.to_value().to_string());
self
}
#[must_use]
pub fn align_content(mut self, align: AlignContent) -> Self {
self.1.push(align.to_value().to_string());
self
}
#[must_use]
pub fn align_items(mut self, align: AlignItems) -> Self {
self.1.push(align.to_value().to_string());
self
}
#[must_use]
pub fn justify(mut self, value: Justify) -> Self {
let class = match value {
Justify::Center => "justify-center".to_string(),
Justify::Between => "justify-between".to_string(),
Justify::Normal => "justify-normal".to_string(),
Justify::Start => "justify-start".to_string(),
Justify::End => "justify-end".to_string(),
Justify::Around => "justify-around".to_string(),
Justify::Evenly => "justify-evenly".to_string(),
Justify::Stretch => "justify-stretch".to_string(),
};
self.1.push(class);
self
}
#[must_use]
pub fn items_center(mut self) -> Self {
self.1.push("items-center".to_owned());
self
}
#[must_use]
pub fn gap(mut self, amount: ScreenValue) -> Self {
self.1.push(format!("gap-{}", amount.to_value()));
self
}
#[must_use]
pub fn gap_x(mut self, amount: ScreenValue) -> Self {
self.1.push(format!("gap-x-{}", amount.to_value()));
self
}
#[must_use]
pub fn gap_y(mut self, amount: ScreenValue) -> Self {
self.1.push(format!("gap-y-{}", amount.to_value()));
self
}
}
impl UIWidget for GridWidget {
fn can_inherit(&self) -> bool {
true
}
fn base_class(&self) -> Vec<String> {
let mut res = vec!["grid".to_string()];
res.extend_from_slice(&self.1);
res
}
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 {
if self.0.as_ref().can_inherit() && !self.2 {
self.0
.as_ref()
.render_with_class(&format!("{} {class}", self.base_class().join(" ")))
} else {
html! {
div class=(format!("{} {class}", self.base_class().join(" "))) {
(self.0.as_ref())
}
}
}
}
}
pub enum GridAutoFlow {
Row,
Column,
Dense,
RowDense,
ColumnDense,
}
impl GridAutoFlow {
pub const fn to_value(&self) -> &str {
match self {
GridAutoFlow::Row => "grid-flow-row",
GridAutoFlow::Column => "grid-flow-col",
GridAutoFlow::Dense => "grid-flow-dense",
GridAutoFlow::RowDense => "grid-flow-row-dense",
GridAutoFlow::ColumnDense => "grid-flow-col-dense",
}
}
}
pub enum GridAutoSize {
Auto,
Min,
Max,
Fr,
}
impl GridAutoSize {
pub const fn to_value(&self) -> &str {
match self {
GridAutoSize::Auto => "auto",
GridAutoSize::Min => "min",
GridAutoSize::Max => "max",
GridAutoSize::Fr => "fr",
}
}
}
#[allow(non_snake_case)]
pub fn GridElementColumn<T: UIWidget + 'static>(inner: T) -> GridElement {
GridElement(Box::new(inner), Vec::new(), "col".to_string())
}
#[allow(non_snake_case)]
pub fn GridElementRow<T: UIWidget + 'static>(inner: T) -> GridElement {
GridElement(Box::new(inner), Vec::new(), "row".to_string())
}
pub struct GridElement(Box<dyn UIWidget>, Vec<String>, String);
impl GridElement {
pub fn auto(mut self) -> Self {
self.1.push(format!("{}-auto", self.2));
self
}
pub fn span(mut self, value: GridElementValue) -> Self {
self.1.push(format!("{}-span-{}", self.2, match value {
GridElementValue::_1 => "1",
GridElementValue::_2 => "2",
GridElementValue::_3 => "3",
GridElementValue::_4 => "4",
GridElementValue::_5 => "5",
GridElementValue::_6 => "6",
GridElementValue::_7 => "7",
GridElementValue::_8 => "8",
GridElementValue::_9 => "9",
GridElementValue::_10 => "10",
GridElementValue::_11 => "11",
GridElementValue::_12 => "12",
GridElementValue::Auto => "full",
}));
self
}
pub fn start(mut self, value: GridElementValue) -> Self {
self.1
.push(format!("{}-start-{}", self.2, value.to_value()));
self
}
pub fn end(mut self, value: GridElementValue) -> Self {
self.1.push(format!("{}-end-{}", self.2, value.to_value()));
self
}
}
impl Render for GridElement {
fn render(&self) -> Markup {
self.render_with_class("")
}
}
impl UIWidget for GridElement {
fn can_inherit(&self) -> bool {
true
}
fn base_class(&self) -> Vec<String> {
let mut res = vec!["grid".to_string()];
res.extend_from_slice(&self.1);
res
}
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 {
if self.0.as_ref().can_inherit() {
self.0
.as_ref()
.render_with_class(&format!("{} {class}", self.base_class().join(" ")))
} else {
html! {
div class=(format!("{} {class}", self.base_class().join(" "))) {
(self.0.as_ref())
}
}
}
}
}
pub enum GridElementValue {
_1,
_2,
_3,
_4,
_5,
_6,
_7,
_8,
_9,
_10,
_11,
_12,
Auto,
}
impl GridElementValue {
pub const fn to_value(&self) -> &str {
match self {
GridElementValue::_1 => "1",
GridElementValue::_2 => "2",
GridElementValue::_3 => "3",
GridElementValue::_4 => "4",
GridElementValue::_5 => "5",
GridElementValue::_6 => "6",
GridElementValue::_7 => "7",
GridElementValue::_8 => "8",
GridElementValue::_9 => "9",
GridElementValue::_10 => "10",
GridElementValue::_11 => "11",
GridElementValue::_12 => "12",
GridElementValue::Auto => "auto",
}
}
}
macro_rules! string_class_widget {
($name:ident) => {
pub struct $name(Box<dyn UIWidget>, String);
impl Render for $name {
fn render(&self) -> Markup {
self.render_with_class("")
}
}
impl UIWidget for $name {
fn can_inherit(&self) -> bool {
true
}
fn base_class(&self) -> Vec<String> {
vec![self.1.clone()]
}
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 {
if self.0.as_ref().can_inherit() {
self.0
.as_ref()
.render_with_class(&format!("{} {class}", self.base_class().join(" ")))
} else {
html! {
div class=(format!("{} {class}", self.base_class().join(" "))) {
(self.0.as_ref())
}
}
}
}
}
};
}
macro_rules! constructor {
($name:ident, $class:literal) => {
#[allow(non_snake_case)]
pub fn $name<T: UIWidget + 'static>(inner: T) -> Self {
Self(Box::new(inner), $class.to_string())
}
};
}
string_class_widget!(Columns);
impl Columns {
constructor!(_1, "columns-1");
constructor!(_2, "columns-2");
constructor!(_3, "columns-3");
constructor!(_4, "columns-4");
constructor!(_5, "columns-5");
constructor!(_6, "columns-6");
constructor!(_7, "columns-7");
constructor!(_8, "columns-8");
constructor!(_9, "columns-9");
constructor!(_10, "columns-10");
constructor!(_11, "columns-11");
constructor!(_12, "columns-12");
constructor!(Auto, "columns-auto");
constructor!(_3XS, "columns-3xs");
constructor!(_2XS, "columns-2xs");
constructor!(XS, "columns-xs");
constructor!(Small, "columns-sm");
constructor!(Medium, "columns-md");
constructor!(Large, "columns-lg");
constructor!(XL, "columns-xl");
constructor!(_2XL, "columns-2xl");
constructor!(_3XL, "columns-3xl");
constructor!(_4XL, "columns-4xl");
constructor!(_5XL, "columns-5xl");
constructor!(_6XL, "columns-6xl");
constructor!(_7XL, "columns-7xl");
}