update
This commit is contained in:
parent
a9a8b8b951
commit
e02def6bc1
7 changed files with 897 additions and 12 deletions
|
@ -1,6 +1,5 @@
|
||||||
use components::Shell;
|
use components::Shell;
|
||||||
use maud::{Markup, PreEscaped, Render};
|
use maud::{Markup, PreEscaped, Render, html};
|
||||||
use prelude::Text;
|
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
|
|
||||||
|
@ -154,37 +153,37 @@ impl UIWidget for PreEscaped<String> {
|
||||||
|
|
||||||
impl UIWidget for String {
|
impl UIWidget for String {
|
||||||
fn can_inherit(&self) -> bool {
|
fn can_inherit(&self) -> bool {
|
||||||
Text(&self).can_inherit()
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_class(&self) -> Vec<String> {
|
fn base_class(&self) -> Vec<String> {
|
||||||
Text(&self).base_class()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extended_class(&self) -> Vec<String> {
|
fn extended_class(&self) -> Vec<String> {
|
||||||
Text(&self).extended_class()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_with_class(&self, class: &str) -> Markup {
|
fn render_with_class(&self, _: &str) -> Markup {
|
||||||
Text(&self).render_with_class(class)
|
html!((self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UIWidget for &str {
|
impl UIWidget for &str {
|
||||||
fn can_inherit(&self) -> bool {
|
fn can_inherit(&self) -> bool {
|
||||||
Text(&self).can_inherit()
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_class(&self) -> Vec<String> {
|
fn base_class(&self) -> Vec<String> {
|
||||||
Text(&self).base_class()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extended_class(&self) -> Vec<String> {
|
fn extended_class(&self) -> Vec<String> {
|
||||||
Text(&self).extended_class()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_with_class(&self, class: &str) -> Markup {
|
fn render_with_class(&self, _: &str) -> Markup {
|
||||||
Text(&self).render_with_class(class)
|
html!((self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,3 +281,34 @@ impl Overflow {
|
||||||
constructor!(XScroll, "overflow-x-scroll");
|
constructor!(XScroll, "overflow-x-scroll");
|
||||||
constructor!(YScroll, "overflow-y-scroll");
|
constructor!(YScroll, "overflow-y-scroll");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string_class_widget!(JustifySelf);
|
||||||
|
|
||||||
|
impl JustifySelf {
|
||||||
|
constructor!(Auto, "justify-self-auto");
|
||||||
|
constructor!(Start, "justify-self-start");
|
||||||
|
constructor!(End, "justify-self-end");
|
||||||
|
constructor!(Center, "justify-self-center");
|
||||||
|
constructor!(Stretch, "justify-self-stretch");
|
||||||
|
}
|
||||||
|
|
||||||
|
string_class_widget!(PlaceSelf);
|
||||||
|
|
||||||
|
impl PlaceSelf {
|
||||||
|
constructor!(Auto, "place-self-auto");
|
||||||
|
constructor!(Start, "place-self-start");
|
||||||
|
constructor!(End, "place-self-end");
|
||||||
|
constructor!(Center, "place-self-center");
|
||||||
|
constructor!(Stretch, "place-self-stretch");
|
||||||
|
}
|
||||||
|
|
||||||
|
string_class_widget!(AlignSelf);
|
||||||
|
|
||||||
|
impl AlignSelf {
|
||||||
|
constructor!(Auto, "self-auto");
|
||||||
|
constructor!(Start, "self-start");
|
||||||
|
constructor!(End, "self-end");
|
||||||
|
constructor!(Center, "self-center");
|
||||||
|
constructor!(Stretch, "self-stretch");
|
||||||
|
constructor!(Baseline, "self-baseline");
|
||||||
|
}
|
||||||
|
|
|
@ -109,6 +109,24 @@ impl FlexWidget {
|
||||||
self
|
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]
|
#[must_use]
|
||||||
pub fn justify(mut self, value: Justify) -> Self {
|
pub fn justify(mut self, value: Justify) -> Self {
|
||||||
let class = match value {
|
let class = match value {
|
||||||
|
@ -500,3 +518,69 @@ impl DivideStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum JustifyItems {
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
Center,
|
||||||
|
Stretch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JustifyItems {
|
||||||
|
pub const fn to_value(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
JustifyItems::Start => "justify-items-start",
|
||||||
|
JustifyItems::End => "justify-items-end",
|
||||||
|
JustifyItems::Center => "justify-items-center",
|
||||||
|
JustifyItems::Stretch => "justify-items-stretch",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AlignContent {
|
||||||
|
Normal,
|
||||||
|
Center,
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
Between,
|
||||||
|
Around,
|
||||||
|
Evenly,
|
||||||
|
Baseline,
|
||||||
|
Stretch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlignContent {
|
||||||
|
pub const fn to_value(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
AlignContent::Normal => "content-normal",
|
||||||
|
AlignContent::Center => "content-center",
|
||||||
|
AlignContent::Start => "content-start",
|
||||||
|
AlignContent::End => "content-end",
|
||||||
|
AlignContent::Between => "content-between",
|
||||||
|
AlignContent::Around => "content-around",
|
||||||
|
AlignContent::Evenly => "content-evenly",
|
||||||
|
AlignContent::Baseline => "content-baseline",
|
||||||
|
AlignContent::Stretch => "content-stretch",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AlignItems {
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
Center,
|
||||||
|
Baseline,
|
||||||
|
Stretch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlignItems {
|
||||||
|
pub const fn to_value(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
AlignItems::Start => "items-start",
|
||||||
|
AlignItems::End => "items-end",
|
||||||
|
AlignItems::Center => "items-center",
|
||||||
|
AlignItems::Baseline => "items-baseline",
|
||||||
|
AlignItems::Stretch => "items-stretch",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
461
src/ui/primitives/grid.rs
Normal file
461
src/ui/primitives/grid.rs
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
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");
|
||||||
|
}
|
103
src/ui/primitives/list.rs
Normal file
103
src/ui/primitives/list.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
use crate::ui::UIWidget;
|
||||||
|
use maud::{Markup, Render, html};
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn OrderedList() -> ListWidget {
|
||||||
|
ListWidget(Vec::new(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn UnorderedList() -> ListWidget {
|
||||||
|
ListWidget(Vec::new(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ListWidget(Vec<Box<dyn UIWidget>>, bool);
|
||||||
|
|
||||||
|
impl ListWidget {
|
||||||
|
#[must_use]
|
||||||
|
pub fn push<T: UIWidget + 'static>(mut self, element: T) -> Self {
|
||||||
|
self.0.push(Box::new(element));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn push_some<T: UIWidget + 'static, X, U: Fn(X) -> T>(
|
||||||
|
mut self,
|
||||||
|
option: Option<X>,
|
||||||
|
then: U,
|
||||||
|
) -> Self {
|
||||||
|
if let Some(val) = option {
|
||||||
|
self.0.push(Box::new(then(val)));
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn push_if<T: UIWidget + 'static, U: Fn() -> T>(
|
||||||
|
mut self,
|
||||||
|
condition: bool,
|
||||||
|
then: U,
|
||||||
|
) -> Self {
|
||||||
|
if condition {
|
||||||
|
self.0.push(Box::new(then()));
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn push_for_each<T, X, F>(mut self, items: &[X], mut action: F) -> Self
|
||||||
|
where
|
||||||
|
T: UIWidget + 'static,
|
||||||
|
F: FnMut(&X) -> T,
|
||||||
|
{
|
||||||
|
for item in items {
|
||||||
|
self.0.push(Box::new(action(item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for ListWidget {
|
||||||
|
fn render(&self) -> Markup {
|
||||||
|
self.render_with_class("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UIWidget for ListWidget {
|
||||||
|
fn can_inherit(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base_class(&self) -> Vec<String> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extended_class(&self) -> Vec<String> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_with_class(&self, class: &str) -> Markup {
|
||||||
|
let inner = html! {
|
||||||
|
@for e in &self.0 {
|
||||||
|
li { (e.as_ref()) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.1 {
|
||||||
|
html! {
|
||||||
|
ol class=(class) {
|
||||||
|
(inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {
|
||||||
|
ul class=(class) {
|
||||||
|
(inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,11 +11,13 @@ pub mod display;
|
||||||
pub mod div;
|
pub mod div;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod flex;
|
pub mod flex;
|
||||||
|
pub mod grid;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod height;
|
pub mod height;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod link;
|
pub mod link;
|
||||||
|
pub mod list;
|
||||||
pub mod margin;
|
pub mod margin;
|
||||||
pub mod padding;
|
pub mod padding;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
|
@ -25,6 +27,7 @@ pub mod shadow;
|
||||||
pub mod sized;
|
pub mod sized;
|
||||||
pub mod space;
|
pub mod space;
|
||||||
pub mod svg;
|
pub mod svg;
|
||||||
|
pub mod table;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
pub mod visibility;
|
pub mod visibility;
|
||||||
|
|
204
src/ui/primitives/table.rs
Normal file
204
src/ui/primitives/table.rs
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
Loading…
Add table
Reference in a new issue