diff --git a/pagetop-admin/src/summary.rs b/pagetop-admin/src/summary.rs index 79bf426e..b91b97fa 100644 --- a/pagetop-admin/src/summary.rs +++ b/pagetop-admin/src/summary.rs @@ -2,37 +2,37 @@ use pagetop::prelude::*; use super::l; pub async fn summary() -> app::Result { - let top_menu = Menu::prepare() + let top_menu = Menu::new() .add(MenuItem::label(l("module_fullname").as_str())) .add(MenuItem::link("Opción 2", "https://www.google.es")) .add(MenuItem::link_blank("Opción 3", "https://www.google.es")) - .add(MenuItem::submenu("Submenú 1", Menu::prepare() + .add(MenuItem::submenu("Submenú 1", Menu::new() .add(MenuItem::label("Opción 1")) .add(MenuItem::label("Opción 2")) )) .add(MenuItem::separator()) - .add(MenuItem::submenu("Submenú 2", Menu::prepare() + .add(MenuItem::submenu("Submenú 2", Menu::new() .add(MenuItem::label("Opción 1")) .add(MenuItem::label("Opción 2")) )) .add(MenuItem::label("Opción 4")); - let side_menu = Menu::prepare() + let side_menu = Menu::new() .add(MenuItem::label("Opción 1")) .add(MenuItem::link("Opción 2", "https://www.google.es")) .add(MenuItem::link_blank("Opción 3", "https://www.google.es")) - .add(MenuItem::submenu("Submenú 1", Menu::prepare() + .add(MenuItem::submenu("Submenú 1", Menu::new() .add(MenuItem::label("Opción 1")) .add(MenuItem::label("Opción 2")) )) .add(MenuItem::separator()) - .add(MenuItem::submenu("Submenú 2", Menu::prepare() + .add(MenuItem::submenu("Submenú 2", Menu::new() .add(MenuItem::label("Opción 1")) .add(MenuItem::label("Opción 2")) )) .add(MenuItem::label("Opción 4")); - Page::prepare() + Page::new() .using_theme("bootsier") @@ -40,12 +40,12 @@ pub async fn summary() -> app::Result { .add_to("top-menu", top_menu) - .add_to("content", Container::row() - .add(Container::column() + .add_to("content", grid::Row::new() + .add_column(grid::Column::new() .add(side_menu) ) - .add(Container::column() - .add(Chunck::markup(html! { + .add_column(grid::Column::new() + .add(Chunck::with(html! { p { "Columna 2"} })) ) diff --git a/pagetop-node/src/lib.rs b/pagetop-node/src/lib.rs index afcd729f..0f5c4662 100644 --- a/pagetop-node/src/lib.rs +++ b/pagetop-node/src/lib.rs @@ -35,7 +35,7 @@ impl ModuleTrait for NodeModule { } async fn node() -> app::Result { - Page::prepare() + Page::new() .with_title( "Nodo" ) diff --git a/pagetop-user/src/lib.rs b/pagetop-user/src/lib.rs index 343daa7a..7ef2ecf3 100644 --- a/pagetop-user/src/lib.rs +++ b/pagetop-user/src/lib.rs @@ -32,7 +32,7 @@ impl ModuleTrait for UserModule { } fn form_login() -> impl PageComponent { - Form::prepare() + Form::new() .with_id("user-login") .add(form::Input::textfield() .with_name("name") @@ -51,11 +51,11 @@ fn form_login() -> impl PageComponent { } async fn login() -> app::Result { - Page::prepare() + Page::new() .with_title( "Identificación del usuario" ) - .add_to("content", Container::prepare() + .add_to("content", Container::new() .with_id("welcome") .add(form_login()) ) diff --git a/pagetop/src/base/component/block.rs b/pagetop/src/base/component/block.rs index 96fce677..b857712f 100644 --- a/pagetop/src/base/component/block.rs +++ b/pagetop/src/base/component/block.rs @@ -3,21 +3,21 @@ use crate::prelude::*; pub struct Block { renderable: fn() -> bool, weight : i8, - id : Option, - title : Option, - markup : Vec, + id : OptionId, + title : OptionAttr, + html : Vec, template : String, } impl PageComponent for Block { - fn prepare() -> Self { + fn new() -> Self { Block { renderable: always, weight : 0, - id : None, - title : None, - markup : Vec::new(), + id : OptionId::none(), + title : OptionAttr::none(), + html : Vec::new(), template : "default".to_owned(), } } @@ -31,15 +31,16 @@ impl PageComponent for Block { } fn default_render(&self, assets: &mut PageAssets) -> Markup { - let id = assets.serial_id(self.name(), self.id()); + let id = assets.serial_id(self.name(), self.id.value()); + let title = self.title.value(); html! { div id=(id) class="block" { - @if self.title != None { - h2 class="block-title" { (self.title()) } + @if !title.is_empty() { + h2 class="block-title" { (title) } } div class="block-body" { - @for markup in self.markup.iter() { - (*markup) + @for html in self.html.iter() { + (*html) } } } @@ -49,8 +50,8 @@ impl PageComponent for Block { impl Block { - pub fn markup(markup: Markup) -> Self { - Block::prepare().add_markup(markup) + pub fn with(html: Markup) -> Self { + Block::new().add(html) } // Block BUILDER. @@ -66,17 +67,17 @@ impl Block { } pub fn with_id(mut self, id: &str) -> Self { - self.id = util::valid_id(id); + self.id.with_value(id); self } pub fn with_title(mut self, title: &str) -> Self { - self.title = util::valid_str(title); + self.title.with_value(title); self } - pub fn add_markup(mut self, markup: Markup) -> Self { - self.markup.push(markup); + pub fn add(mut self, html: Markup) -> Self { + self.html.push(html); self } @@ -88,11 +89,11 @@ impl Block { // Block GETTERS. pub fn id(&self) -> &str { - util::assigned_str(&self.id) + self.id.value() } pub fn title(&self) -> &str { - util::assigned_str(&self.title) + self.title.value() } pub fn template(&self) -> &str { diff --git a/pagetop/src/base/component/chunck.rs b/pagetop/src/base/component/chunck.rs index 63cc20f7..b03ac0c8 100644 --- a/pagetop/src/base/component/chunck.rs +++ b/pagetop/src/base/component/chunck.rs @@ -3,17 +3,17 @@ use crate::prelude::*; pub struct Chunck { renderable: fn() -> bool, weight : i8, - markup : Vec, + html : Vec, template : String, } impl PageComponent for Chunck { - fn prepare() -> Self { + fn new() -> Self { Chunck { renderable: always, weight : 0, - markup : Vec::new(), + html : Vec::new(), template : "default".to_owned(), } } @@ -28,8 +28,8 @@ impl PageComponent for Chunck { fn default_render(&self, _: &mut PageAssets) -> Markup { html! { - @for markup in self.markup.iter() { - (*markup) + @for html in self.html.iter() { + (*html) } } } @@ -37,8 +37,8 @@ impl PageComponent for Chunck { impl Chunck { - pub fn markup(markup: Markup) -> Self { - Chunck::prepare().add_markup(markup) + pub fn with(html: Markup) -> Self { + Chunck::new().add(html) } // Chunck BUILDER. @@ -53,8 +53,8 @@ impl Chunck { self } - pub fn add_markup(mut self, markup: Markup) -> Self { - self.markup.push(markup); + pub fn add(mut self, html: Markup) -> Self { + self.html.push(html); self } diff --git a/pagetop/src/base/component/container.rs b/pagetop/src/base/component/container.rs index 161bea78..18d6337a 100644 --- a/pagetop/src/base/component/container.rs +++ b/pagetop/src/base/component/container.rs @@ -1,11 +1,11 @@ use crate::prelude::*; -enum ContainerType { Column, Row, Wrapper } +enum ContainerType { Header, Footer, Main, Section, Wrapper } pub struct Container { renderable: fn() -> bool, weight : i8, - id : Option, + id : OptionId, container : ContainerType, components: PageContainer, template : String, @@ -13,11 +13,11 @@ pub struct Container { impl PageComponent for Container { - fn prepare() -> Self { + fn new() -> Self { Container { renderable: always, weight : 0, - id : None, + id : OptionId::none(), container : ContainerType::Wrapper, components: PageContainer::new(), template : "default".to_owned(), @@ -33,14 +33,39 @@ impl PageComponent for Container { } fn default_render(&self, assets: &mut PageAssets) -> Markup { - let classes = match self.container { - ContainerType::Wrapper => "container", - ContainerType::Row => "row", - ContainerType::Column => "col", - }; - html! { - div id=[&self.id] class=(classes) { - (self.components.render(assets)) + match self.container { + ContainerType::Header => html! { + header id=[&self.id.option()] class="header" { + div class="container" { + (self.components.render(assets)) + } + } + }, + ContainerType::Footer => html! { + footer id=[&self.id.option()] class="footer" { + div class="container" { + (self.components.render(assets)) + } + } + }, + ContainerType::Main => html! { + main id=[&self.id.option()] class="main" { + div class="container" { + (self.components.render(assets)) + } + } + }, + ContainerType::Section => html! { + section id=[&self.id.option()] class="section" { + div class="container" { + (self.components.render(assets)) + } + } + }, + _ => html! { + div id=[&self.id.option()] class="container" { + (self.components.render(assets)) + } } } } @@ -48,16 +73,28 @@ impl PageComponent for Container { impl Container { - pub fn row() -> Self { - let mut grid = Container::prepare(); - grid.container = ContainerType::Row; - grid + pub fn header() -> Self { + let mut c = Container::new(); + c.container = ContainerType::Header; + c } - pub fn column() -> Self { - let mut grid = Container::prepare(); - grid.container = ContainerType::Column; - grid + pub fn footer() -> Self { + let mut c = Container::new(); + c.container = ContainerType::Footer; + c + } + + pub fn main() -> Self { + let mut c = Container::new(); + c.container = ContainerType::Main; + c + } + + pub fn section() -> Self { + let mut c = Container::new(); + c.container = ContainerType::Section; + c } // Container BUILDER. @@ -73,7 +110,7 @@ impl Container { } pub fn with_id(mut self, id: &str) -> Self { - self.id = util::valid_id(id); + self.id.with_value(id); self } @@ -90,7 +127,7 @@ impl Container { // Container GETTERS. pub fn id(&self) -> &str { - util::assigned_str(&self.id) + self.id.value() } pub fn template(&self) -> &str { diff --git a/pagetop/src/base/component/form/button.rs b/pagetop/src/base/component/form/button.rs index 57810fea..a1975ea4 100644 --- a/pagetop/src/base/component/form/button.rs +++ b/pagetop/src/base/component/form/button.rs @@ -6,24 +6,24 @@ pub struct Button { renderable : fn() -> bool, weight : i8, button_type: ButtonType, - name : Option, - value : Option, - autofocus : Option, - disabled : Option, + name : OptionAttr, + value : OptionAttr, + autofocus : OptionAttr, + disabled : OptionAttr, template : String, } impl PageComponent for Button { - fn prepare() -> Self { + fn new() -> Self { Button { renderable : always, weight : 0, button_type: ButtonType::Button, - name : None, - value : None, - autofocus : None, - disabled : None, + name : OptionAttr::none(), + value : OptionAttr::none(), + autofocus : OptionAttr::none(), + disabled : OptionAttr::none(), template : "default".to_owned(), } } @@ -42,7 +42,7 @@ impl PageComponent for Button { ButtonType::Reset => ("reset", "btn btn-primary form-reset" ), ButtonType::Submit => ("submit", "btn btn-primary form-submit") }; - let id_item = match &self.name { + let id_item = match &self.name.option() { Some(name) => Some(format!("edit-{}", name)), _ => None }; @@ -51,15 +51,12 @@ impl PageComponent for Button { type=(button_type) id=[&id_item] class=(button_class) - name=[&self.name] - value=[&self.value] - autofocus=[&self.autofocus] - disabled=[&self.disabled] + name=[&self.name.option()] + value=[&self.value.option()] + autofocus=[&self.autofocus.option()] + disabled=[&self.disabled.option()] { - @match &self.value { - Some(value) => (value), - _ => "" - }; + (self.value.value()) } } } @@ -68,17 +65,17 @@ impl PageComponent for Button { impl Button { pub fn button(value: &str) -> Self { - Button::prepare().with_value(value) + Button::new().with_value(value) } pub fn reset(value: &str) -> Self { - let mut button = Button::prepare().with_value(value); + let mut button = Button::new().with_value(value); button.button_type = ButtonType::Reset; button } pub fn submit(value: &str) -> Self { - let mut button = Button::prepare().with_value(value); + let mut button = Button::new().with_value(value); button.button_type = ButtonType::Submit; button } @@ -96,28 +93,28 @@ impl Button { } pub fn with_name(mut self, name: &str) -> Self { - self.name = util::valid_id(name); + self.name.with_value(name); self } pub fn with_value(mut self, value: &str) -> Self { - self.value = util::valid_str(value); + self.value.with_value(value); self } pub fn autofocus(mut self, toggle: bool) -> Self { - self.autofocus = match toggle { - true => Some("autofocus".to_owned()), - false => None - }; + self.autofocus.with_value(match toggle { + true => "autofocus", + false => "", + }); self } pub fn disabled(mut self, toggle: bool) -> Self { - self.disabled = match toggle { - true => Some("disabled".to_owned()), - false => None - }; + self.disabled.with_value(match toggle { + true => "disabled", + false => "", + }); self } @@ -129,25 +126,19 @@ impl Button { // Button GETTERS. pub fn name(&self) -> &str { - util::assigned_str(&self.name) + self.name.value() } pub fn value(&self) -> &str { - util::assigned_str(&self.value) + self.value.value() } pub fn has_autofocus(&self) -> bool { - match &self.autofocus { - Some(_) => true, - _ => false - } + self.autofocus.has_value() } pub fn is_disabled(&self) -> bool { - match &self.disabled { - Some(_) => true, - _ => false - } + self.disabled.has_value() } pub fn template(&self) -> &str { diff --git a/pagetop/src/base/component/form/date.rs b/pagetop/src/base/component/form/date.rs index 6614670a..7f04691b 100644 --- a/pagetop/src/base/component/form/date.rs +++ b/pagetop/src/base/component/form/date.rs @@ -3,35 +3,35 @@ use crate::prelude::*; pub struct Date { renderable : fn() -> bool, weight : i8, - name : Option, - value : Option, - label : Option, - placeholder : Option, - autofocus : Option, - autocomplete: Option, - disabled : Option, - readonly : Option, - required : Option, - help_text : Option, + name : OptionAttr, + value : OptionAttr, + label : OptionAttr, + placeholder : OptionAttr, + autofocus : OptionAttr, + autocomplete: OptionAttr, + disabled : OptionAttr, + readonly : OptionAttr, + required : OptionAttr, + help_text : OptionAttr, template : String, } impl PageComponent for Date { - fn prepare() -> Self { + fn new() -> Self { Date { renderable : always, weight : 0, - name : None, - value : None, - label : None, - placeholder : None, - autofocus : None, - autocomplete: None, - disabled : None, - readonly : None, - required : None, - help_text : None, + name : OptionAttr::none(), + value : OptionAttr::none(), + label : OptionAttr::none(), + placeholder : OptionAttr::none(), + autofocus : OptionAttr::none(), + autocomplete: OptionAttr::none(), + disabled : OptionAttr::none(), + readonly : OptionAttr::none(), + required : OptionAttr::none(), + help_text : OptionAttr::none(), template : "default".to_owned(), } } @@ -45,7 +45,7 @@ impl PageComponent for Date { } fn default_render(&self, _: &mut PageAssets) -> Markup { - let (class_item, id_item) = match &self.name { + let (class_item, id_item) = match self.name.option() { Some(name) => ( format!("form-item form-item-{} form-type-date", name), Some(format!("edit-{}", name)) @@ -57,10 +57,10 @@ impl PageComponent for Date { }; html! { div class=(class_item) { - @if self.label != None { + @if self.label.has_value() { label class="form-label" for=[&id_item] { - (self.label()) " " - @if self.required != None { + (self.label.value()) " " + @if self.required.has_value() { span class="form-required" title="Este campo es obligatorio." @@ -74,17 +74,17 @@ impl PageComponent for Date { type="date" id=[&id_item] class="form-control" - name=[&self.name] - value=[&self.value] - placeholder=[&self.placeholder] - autofocus=[&self.autofocus] - autocomplete=[&self.autocomplete] - readonly=[&self.readonly] - required=[&self.required] - disabled=[&self.disabled]; - @if self.help_text != None { + name=[&self.name.option()] + value=[&self.value.option()] + placeholder=[&self.placeholder.option()] + autofocus=[&self.autofocus.option()] + autocomplete=[&self.autocomplete.option()] + readonly=[&self.readonly.option()] + required=[&self.required.option()] + disabled=[&self.disabled.option()]; + @if self.help_text.has_value() { div class="form-text" { - (self.help_text()) + (self.help_text.value()) } } } @@ -107,67 +107,67 @@ impl Date { } pub fn with_name(mut self, name: &str) -> Self { - self.name = util::valid_id(name); + self.name.with_value(name); self } pub fn with_value(mut self, value: &str) -> Self { - self.value = util::valid_str(value); + self.value.with_value(value); self } pub fn with_label(mut self, label: &str) -> Self { - self.label = util::valid_str(label); + self.label.with_value(label); self } pub fn with_placeholder(mut self, placeholder: &str) -> Self { - self.placeholder = util::valid_str(placeholder); + self.placeholder.with_value(placeholder); self } pub fn autofocus(mut self, toggle: bool) -> Self { - self.autofocus = match toggle { - true => Some("autofocus".to_owned()), - false => None - }; + self.autofocus.with_value(match toggle { + true => "autofocus", + false => "", + }); self } pub fn autocomplete(mut self, toggle: bool) -> Self { - self.autocomplete = match toggle { - true => None, - false => Some("off".to_owned()) - }; + self.autocomplete.with_value(match toggle { + true => "", + false => "off", + }); self } pub fn disabled(mut self, toggle: bool) -> Self { - self.disabled = match toggle { - true => Some("disabled".to_owned()), - false => None - }; + self.disabled.with_value(match toggle { + true => "disabled", + false => "", + }); self } pub fn readonly(mut self, toggle: bool) -> Self { - self.readonly = match toggle { - true => Some("readonly".to_owned()), - false => None - }; + self.readonly.with_value(match toggle { + true => "readonly", + false => "", + }); self } pub fn required(mut self, toggle: bool) -> Self { - self.required = match toggle { - true => Some("required".to_owned()), - false => None - }; + self.required.with_value(match toggle { + true => "required", + false => "", + }); self } pub fn with_help_text(mut self, help_text: &str) -> Self { - self.help_text = util::valid_str(help_text); + self.help_text.with_value(help_text); self } @@ -179,58 +179,43 @@ impl Date { // Date GETTERS. pub fn name(&self) -> &str { - util::assigned_str(&self.name) + self.name.value() } pub fn value(&self) -> &str { - util::assigned_str(&self.value) + self.value.value() } pub fn label(&self) -> &str { - util::assigned_str(&self.label) + self.label.value() } pub fn placeholder(&self) -> &str { - util::assigned_str(&self.placeholder) + self.placeholder.value() } pub fn has_autofocus(&self) -> bool { - match &self.autofocus { - Some(_) => true, - _ => false - } + self.autofocus.has_value() } pub fn has_autocomplete(&self) -> bool { - match &self.autocomplete { - Some(_) => false, - _ => true - } + !self.autocomplete.has_value() } pub fn is_disabled(&self) -> bool { - match &self.disabled { - Some(_) => true, - _ => false - } + self.disabled.has_value() } pub fn is_readonly(&self) -> bool { - match &self.readonly { - Some(_) => true, - _ => false - } + self.readonly.has_value() } pub fn is_required(&self) -> bool { - match &self.required { - Some(_) => true, - _ => false - } + self.required.has_value() } pub fn help_text(&self) -> &str { - util::assigned_str(&self.help_text) + self.help_text.value() } pub fn template(&self) -> &str { diff --git a/pagetop/src/base/component/form/form.rs b/pagetop/src/base/component/form/form.rs index 2fac4403..e93926a7 100644 --- a/pagetop/src/base/component/form/form.rs +++ b/pagetop/src/base/component/form/form.rs @@ -5,24 +5,24 @@ pub enum FormMethod {Get, Post} pub struct Form { renderable: fn() -> bool, weight : i8, - id : Option, - action : Option, + id : OptionId, + action : OptionAttr, method : FormMethod, - charset : Option, + charset : OptionAttr, elements : PageContainer, template : String, } impl PageComponent for Form { - fn prepare() -> Self { + fn new() -> Self { Form { renderable: always, weight : 0, - id : None, - action : None, + id : OptionId::none(), + action : OptionAttr::none(), method : FormMethod::Post, - charset : Some("UTF-8".to_owned()), + charset : OptionAttr::some("UTF-8"), elements : PageContainer::new(), template : "default".to_owned(), } @@ -43,10 +43,10 @@ impl PageComponent for Form { }; html! { form - id=[&self.id] - action=[&self.action] + id=[&self.id.option()] + action=[&self.action.option()] method=[method] - accept-charset=[&self.charset] + accept-charset=[&self.charset.option()] { div { (self.elements.render(assets)) @@ -71,12 +71,12 @@ impl Form { } pub fn with_id(mut self, id: &str) -> Self { - self.id = util::valid_id(id); + self.id.with_value(id); self } pub fn with_action(mut self, action: &str) -> Self { - self.action = util::valid_str(action); + self.action.with_value(action); self } @@ -86,7 +86,7 @@ impl Form { } pub fn with_charset(mut self, charset: &str) -> Self { - self.charset = util::valid_str(charset); + self.charset.with_value(charset); self } @@ -103,11 +103,11 @@ impl Form { // Form GETTERS. pub fn id(&self) -> &str { - util::assigned_str(&self.id) + self.id.value() } pub fn action(&self) -> &str { - util::assigned_str(&self.action) + self.action.value() } pub fn method(&self) -> &str { @@ -118,7 +118,7 @@ impl Form { } pub fn charset(&self) -> &str { - util::assigned_str(&self.charset) + self.charset.value() } pub fn template(&self) -> &str { diff --git a/pagetop/src/base/component/form/hidden.rs b/pagetop/src/base/component/form/hidden.rs index bd158b85..b27caf37 100644 --- a/pagetop/src/base/component/form/hidden.rs +++ b/pagetop/src/base/component/form/hidden.rs @@ -2,17 +2,17 @@ use crate::prelude::*; pub struct Hidden { weight : i8, - name : Option, - value : Option, + name : OptionId, + value : OptionAttr, } impl PageComponent for Hidden { - fn prepare() -> Self { + fn new() -> Self { Hidden { weight : 0, - name : None, - value : None, + name : OptionId::none(), + value : OptionAttr::none(), } } @@ -21,7 +21,7 @@ impl PageComponent for Hidden { } fn default_render(&self, _: &mut PageAssets) -> Markup { - let id_item = match &self.name { + let id_item = match self.name.option() { Some(name) => Some(format!("value-{}", name)), _ => None }; @@ -29,8 +29,8 @@ impl PageComponent for Hidden { input type="hidden" id=[&id_item] - name=[&self.name] - value=[&self.value]; + name=[&self.name.option()] + value=[&self.value.option()]; } } } @@ -38,7 +38,7 @@ impl PageComponent for Hidden { impl Hidden { pub fn set(name: &str, value: &str) -> Self { - Hidden::prepare().with_name(name).with_value(value) + Hidden::new().with_name(name).with_value(value) } // Hidden BUILDER. @@ -49,22 +49,22 @@ impl Hidden { } pub fn with_name(mut self, name: &str) -> Self { - self.name = util::valid_id(name); + self.name.with_value(name); self } pub fn with_value(mut self, value: &str) -> Self { - self.value = util::valid_str(value); + self.value.with_value(value); self } // Hidden GETTERS. pub fn name(&self) -> &str { - util::assigned_str(&self.name) + self.name.value() } pub fn value(&self) -> &str { - util::assigned_str(&self.value) + self.value.value() } } diff --git a/pagetop/src/base/component/form/input.rs b/pagetop/src/base/component/form/input.rs index f0e73a63..c483c9a9 100644 --- a/pagetop/src/base/component/form/input.rs +++ b/pagetop/src/base/component/form/input.rs @@ -6,42 +6,42 @@ pub struct Input { renderable : fn() -> bool, weight : i8, input_type : InputType, - name : Option, - value : Option, - label : Option, + name : OptionId, + value : OptionAttr, + label : OptionAttr, size : Option, minlength : Option, maxlength : Option, - placeholder : Option, - autofocus : Option, - autocomplete: Option, - disabled : Option, - readonly : Option, - required : Option, - help_text : Option, + placeholder : OptionAttr, + autofocus : OptionAttr, + autocomplete: OptionAttr, + disabled : OptionAttr, + readonly : OptionAttr, + required : OptionAttr, + help_text : OptionAttr, template : String, } impl PageComponent for Input { - fn prepare() -> Self { + fn new() -> Self { Input { renderable : always, weight : 0, input_type : InputType::Textfield, - name : None, - value : None, - label : None, + name : OptionId::none(), + value : OptionAttr::none(), + label : OptionAttr::none(), size : Some(60), minlength : None, maxlength : Some(128), - placeholder : None, - autofocus : None, - autocomplete: None, - disabled : None, - readonly : None, - required : None, - help_text : None, + placeholder : OptionAttr::none(), + autofocus : OptionAttr::none(), + autocomplete: OptionAttr::none(), + disabled : OptionAttr::none(), + readonly : OptionAttr::none(), + required : OptionAttr::none(), + help_text : OptionAttr::none(), template : "default".to_owned(), } } @@ -63,7 +63,7 @@ impl PageComponent for Input { InputType::Textfield => ("text", "form-type-textfield"), InputType::Url => ("url", "form-type-url") }; - let (class_item, id_item) = match &self.name { + let (class_item, id_item) = match &self.name.option() { Some(name) => ( format!("form-item form-item-{} {}", name, class_type), Some(format!("edit-{}", name)) @@ -75,10 +75,10 @@ impl PageComponent for Input { }; html! { div class=(class_item) { - @if self.label != None { + @if self.label.has_value() { label class="form-label" for=[&id_item] { - (self.label()) " " - @if self.required != None { + (self.label.value()) " " + @if self.required.has_value() { span class="form-required" title="Este campo es obligatorio." @@ -92,20 +92,20 @@ impl PageComponent for Input { type=(input_type) id=[&id_item] class="form-control" - name=[&self.name] - value=[&self.value] + name=[&self.name.option()] + value=[&self.value.option()] size=[self.size] minlength=[self.minlength] maxlength=[self.maxlength] - placeholder=[&self.placeholder] - autofocus=[&self.autofocus] - autocomplete=[&self.autocomplete] - readonly=[&self.readonly] - required=[&self.required] - disabled=[&self.disabled]; - @if self.help_text != None { + placeholder=[&self.placeholder.option()] + autofocus=[&self.autofocus.option()] + autocomplete=[&self.autocomplete.option()] + readonly=[&self.readonly.option()] + required=[&self.required.option()] + disabled=[&self.disabled.option()]; + @if self.help_text.has_value() { div class="form-text" { - (self.help_text()) + (self.help_text.value()) } } } @@ -116,35 +116,35 @@ impl PageComponent for Input { impl Input { pub fn textfield() -> Self { - Input::prepare() + Input::new() } pub fn password() -> Self { - let mut input = Input::prepare(); + let mut input = Input::new(); input.input_type = InputType::Password; input } pub fn search() -> Self { - let mut input = Input::prepare(); + let mut input = Input::new(); input.input_type = InputType::Search; input } pub fn email() -> Self { - let mut input = Input::prepare(); + let mut input = Input::new(); input.input_type = InputType::Email; input } pub fn telephone() -> Self { - let mut input = Input::prepare(); + let mut input = Input::new(); input.input_type = InputType::Telephone; input } pub fn url() -> Self { - let mut input = Input::prepare(); + let mut input = Input::new(); input.input_type = InputType::Url; input } @@ -162,17 +162,17 @@ impl Input { } pub fn with_name(mut self, name: &str) -> Self { - self.name = util::valid_id(name); + self.name.with_value(name); self } pub fn with_value(mut self, value: &str) -> Self { - self.value = util::valid_str(value); + self.value.with_value(value); self } pub fn with_label(mut self, label: &str) -> Self { - self.label = util::valid_str(label); + self.label.with_value(label); self } @@ -192,52 +192,52 @@ impl Input { } pub fn with_placeholder(mut self, placeholder: &str) -> Self { - self.placeholder = util::valid_str(placeholder); + self.placeholder.with_value(placeholder); self } pub fn autofocus(mut self, toggle: bool) -> Self { - self.autofocus = match toggle { - true => Some("autofocus".to_owned()), - false => None - }; + self.autofocus.with_value(match toggle { + true => "autofocus", + false => "", + }); self } pub fn autocomplete(mut self, toggle: bool) -> Self { - self.autocomplete = match toggle { - true => None, - false => Some("off".to_owned()) - }; + self.autocomplete.with_value(match toggle { + true => "", + false => "off", + }); self } pub fn disabled(mut self, toggle: bool) -> Self { - self.disabled = match toggle { - true => Some("disabled".to_owned()), - false => None - }; + self.disabled.with_value(match toggle { + true => "disabled", + false => "", + }); self } pub fn readonly(mut self, toggle: bool) -> Self { - self.readonly = match toggle { - true => Some("readonly".to_owned()), - false => None - }; + self.readonly.with_value(match toggle { + true => "readonly", + false => "", + }); self } pub fn required(mut self, toggle: bool) -> Self { - self.required = match toggle { - true => Some("required".to_owned()), - false => None - }; + self.required.with_value(match toggle { + true => "required", + false => "", + }); self } pub fn with_help_text(mut self, help_text: &str) -> Self { - self.help_text = util::valid_str(help_text); + self.help_text.with_value(help_text); self } @@ -249,15 +249,15 @@ impl Input { // Input GETTERS. pub fn name(&self) -> &str { - util::assigned_str(&self.name) + self.name.value() } pub fn value(&self) -> &str { - util::assigned_str(&self.value) + self.value.value() } pub fn label(&self) -> &str { - util::assigned_str(&self.label) + self.label.value() } pub fn size(&self) -> Option { @@ -273,46 +273,31 @@ impl Input { } pub fn placeholder(&self) -> &str { - util::assigned_str(&self.placeholder) + self.placeholder.value() } pub fn has_autofocus(&self) -> bool { - match &self.autofocus { - Some(_) => true, - _ => false - } + self.autofocus.has_value() } pub fn has_autocomplete(&self) -> bool { - match &self.autocomplete { - Some(_) => false, - _ => true - } + !self.autocomplete.has_value() } pub fn is_disabled(&self) -> bool { - match &self.disabled { - Some(_) => true, - _ => false - } + self.disabled.has_value() } pub fn is_readonly(&self) -> bool { - match &self.readonly { - Some(_) => true, - _ => false - } + self.readonly.has_value() } pub fn is_required(&self) -> bool { - match &self.required { - Some(_) => true, - _ => false - } + self.required.has_value() } pub fn help_text(&self) -> &str { - util::assigned_str(&self.help_text) + self.help_text.value() } pub fn template(&self) -> &str { diff --git a/pagetop/src/base/component/grid/column.rs b/pagetop/src/base/component/grid/column.rs new file mode 100644 index 00000000..96463490 --- /dev/null +++ b/pagetop/src/base/component/grid/column.rs @@ -0,0 +1,82 @@ +use crate::prelude::*; + +pub struct Column { + renderable: fn() -> bool, + weight : i8, + id : OptionId, + components: PageContainer, + template : String, +} + +impl PageComponent for Column { + + fn new() -> Self { + Column { + renderable: always, + weight : 0, + id : OptionId::none(), + components: PageContainer::new(), + template : "default".to_owned(), + } + } + + fn is_renderable(&self) -> bool { + (self.renderable)() + } + + fn weight(&self) -> i8 { + self.weight + } + + fn default_render(&self, assets: &mut PageAssets) -> Markup { + html! { + div id=[&self.id.option()] class="col" { + (self.components.render(assets)) + } + } + } +} + +impl Column { + + // Column BUILDER. + + pub fn with_renderable(mut self, renderable: fn() -> bool) -> Self { + self.renderable = renderable; + self + } + + pub fn with_weight(mut self, weight: i8) -> Self { + self.weight = weight; + self + } + + pub fn with_id(mut self, id: &str) -> Self { + self.id.with_value(id); + self + } + + pub fn add(mut self, component: impl PageComponent) -> Self { + self.components.add(component); + self + } + + pub fn using_template(mut self, template: &str) -> Self { + self.template = template.to_owned(); + self + } + + // Column GETTERS. + + pub fn id(&self) -> &str { + self.id.value() + } + + pub fn template(&self) -> &str { + self.template.as_str() + } +} + +fn always() -> bool { + true +} diff --git a/pagetop/src/base/component/grid/mod.rs b/pagetop/src/base/component/grid/mod.rs new file mode 100644 index 00000000..ca7796ef --- /dev/null +++ b/pagetop/src/base/component/grid/mod.rs @@ -0,0 +1,4 @@ +mod row; +pub use row::Row; +mod column; +pub use column::Column; diff --git a/pagetop/src/base/component/grid/row.rs b/pagetop/src/base/component/grid/row.rs new file mode 100644 index 00000000..6aed7d0f --- /dev/null +++ b/pagetop/src/base/component/grid/row.rs @@ -0,0 +1,82 @@ +use crate::prelude::*; + +pub struct Row { + renderable: fn() -> bool, + weight : i8, + id : OptionId, + columns : PageContainer, + template : String, +} + +impl PageComponent for Row { + + fn new() -> Self { + Row { + renderable: always, + weight : 0, + id : OptionId::none(), + columns : PageContainer::new(), + template : "default".to_owned(), + } + } + + fn is_renderable(&self) -> bool { + (self.renderable)() + } + + fn weight(&self) -> i8 { + self.weight + } + + fn default_render(&self, assets: &mut PageAssets) -> Markup { + html! { + div id=[&self.id.option()] class="row" { + (self.columns.render(assets)) + } + } + } +} + +impl Row { + + // Row BUILDER. + + pub fn with_renderable(mut self, renderable: fn() -> bool) -> Self { + self.renderable = renderable; + self + } + + pub fn with_weight(mut self, weight: i8) -> Self { + self.weight = weight; + self + } + + pub fn with_id(mut self, id: &str) -> Self { + self.id.with_value(id); + self + } + + pub fn add_column(mut self, column: grid::Column) -> Self { + self.columns.add(column); + self + } + + pub fn using_template(mut self, template: &str) -> Self { + self.template = template.to_owned(); + self + } + + // Row GETTERS. + + pub fn id(&self) -> &str { + self.id.value() + } + + pub fn template(&self) -> &str { + self.template.as_str() + } +} + +fn always() -> bool { + true +} diff --git a/pagetop/src/base/component/image.rs b/pagetop/src/base/component/image.rs new file mode 100644 index 00000000..b330b374 --- /dev/null +++ b/pagetop/src/base/component/image.rs @@ -0,0 +1,70 @@ +use crate::prelude::*; + +pub struct Image { + renderable: fn() -> bool, + weight : i8, + source : Option, + template : String, +} + +impl PageComponent for Image { + + fn new() -> Self { + Image { + renderable: always, + weight : 0, + source : None, + template : "default".to_owned(), + } + } + + fn is_renderable(&self) -> bool { + (self.renderable)() + } + + fn weight(&self) -> i8 { + self.weight + } + + fn default_render(&self, _: &mut PageAssets) -> Markup { + html! { + img src=[&self.source] class="img-fluid" {} + } + } +} + +impl Image { + + pub fn image(source: &str) -> Self { + let mut i = Image::new(); + i.source = Some(source.to_owned()); + i + } + + // Image BUILDER. + + pub fn with_renderable(mut self, renderable: fn() -> bool) -> Self { + self.renderable = renderable; + self + } + + pub fn with_weight(mut self, weight: i8) -> Self { + self.weight = weight; + self + } + + pub fn using_template(mut self, template: &str) -> Self { + self.template = template.to_owned(); + self + } + + // Image GETTERS. + + pub fn template(&self) -> &str { + self.template.as_str() + } +} + +fn always() -> bool { + true +} diff --git a/pagetop/src/base/component/menu.rs b/pagetop/src/base/component/menu.rs index d3cdb6b8..10ac8ca5 100644 --- a/pagetop/src/base/component/menu.rs +++ b/pagetop/src/base/component/menu.rs @@ -4,7 +4,7 @@ enum MenuItemType { Label(String), Link(String, String), LinkBlank(String, String), - Markup(Markup), + Html(Markup), Separator, Submenu(String, Menu), } @@ -21,7 +21,7 @@ pub struct MenuItem { impl PageComponent for MenuItem { - fn prepare() -> Self { + fn new() -> Self { MenuItem { renderable: always, weight : 0, @@ -50,8 +50,8 @@ impl PageComponent for MenuItem { a href=(path) target="_blank" { (label) } } }, - Some(MenuItemType::Markup(markup)) => html! { - li class="markup" { (*markup) } + Some(MenuItemType::Html(html)) => html! { + li class="html" { (*html) } }, Some(MenuItemType::Submenu(label, menu)) => html! { li class="submenu" { @@ -101,11 +101,11 @@ impl MenuItem { } } - pub fn markup(markup: Markup) -> Self { + pub fn html(html: Markup) -> Self { MenuItem { renderable: always, weight : 0, - item_type : Some(MenuItemType::Markup(markup)), + item_type : Some(MenuItemType::Html(html)), } } @@ -148,18 +148,18 @@ impl MenuItem { pub struct Menu { renderable: fn() -> bool, weight : i8, - id : Option, + id : OptionId, items : PageContainer, template : String, } impl PageComponent for Menu { - fn prepare() -> Self { + fn new() -> Self { Menu { renderable: always, weight : 0, - id : None, + id : OptionId::none(), items : PageContainer::new(), template : "default".to_owned(), } @@ -186,7 +186,7 @@ impl PageComponent for Menu { )) .add_jquery(); - let id = assets.serial_id(self.name(), self.id()); + let id = assets.serial_id(self.name(), self.id.value()); html! { ul id=(id) class="sm sm-clean" { (self.render_items(assets)) @@ -216,7 +216,7 @@ impl Menu { } pub fn with_id(mut self, id: &str) -> Self { - self.id = util::valid_id(id); + self.id.with_value(id); self } @@ -233,7 +233,7 @@ impl Menu { // Menu GETTERS. pub fn id(&self) -> &str { - util::assigned_str(&self.id) + self.id.value() } pub fn template(&self) -> &str { diff --git a/pagetop/src/base/component/mod.rs b/pagetop/src/base/component/mod.rs index f9973108..4619c937 100644 --- a/pagetop/src/base/component/mod.rs +++ b/pagetop/src/base/component/mod.rs @@ -1,9 +1,14 @@ mod container; pub use container::Container; + +pub mod grid; + mod chunck; pub use chunck::Chunck; mod block; pub use block::Block; +mod image; +pub use image::Image; mod menu; pub use menu::{Menu, MenuItem}; diff --git a/pagetop/src/base/module/demopage/locales/en-US/demopage.ftl b/pagetop/src/base/module/demopage/locales/en-US/demopage.ftl index 14c93f2f..d23f6ce6 100644 --- a/pagetop/src/base/module/demopage/locales/en-US/demopage.ftl +++ b/pagetop/src/base/module/demopage/locales/en-US/demopage.ftl @@ -3,19 +3,23 @@ module_description = Displays a demo homepage when none is configured. page_title = Hello world! -text_welcome = This page is used to test the proper operation of { $app } after installation. This web solution is powered by { $pagetop }. If you can read this page, it means that the PageTop server is working properly, but has not yet been configured. +welcome_to = Welcome to { $app } +welcome_intro = This page is used to test the proper operation of { $app } after installation. +welcome_pagetop = This web solution is powered by { $pagetop }. -title_normal_user = Just visiting? -text1_normal_user = If you are a normal user of this web site and don't know what this page is about, this probably means that the site is either experiencing problems or is undergoing routine maintenance. -text2_normal_user = If the problem persists, please contact your system administrator. +visiting_title = Just visiting? +visiting_subtitle = Are you user of this website? +visiting_text1 = If you don't know what this page is about, this probably means that the site is either experiencing problems or is undergoing routine maintenance. +visiting_text2 = If the problem persists, please contact your system administrator. -title_about_pagetop = About PageTop -text1_about_pagetop = PageTop defines an interface for the most stable and popular Rust packages to build modular, extensible and configurable web solutions. -text2_about_pagetop = For information on PageTop please visit the "PageTop website". +pagetop_title = About PageTop +pagetop_text1 = If you can read this page, it means that the PageTop server is working properly, but has not yet been configured. +pagetop_text2 = PageTop defines an interface for the most stable and popular Rust packages to build modular, extensible and configurable web solutions. +pagetop_text3 = For information on PageTop please visit the "PageTop website". -title_promo_pagetop = Promoting PageTop -text1_promo_pagetop = You are free to use the image below on applications powered by { $pagetop }. Thanks for using PageTop! +pagetop_promo_title = Promoting PageTop +pagetop_promo_text1 = You are free to use the image below on applications powered by { $pagetop }. Thanks for using PageTop! -title_report_problems = Reporting Problems -text1_report_problems = Please use the GitLab tool to report bugs in PageTop. However, check "existing bug reports" before reporting a new bug. -text2_report_problems = Please report bugs specific to modules (such as admin, and others) to respective packages, not to PageTop itself. +report_problems_title = Reporting Problems +report_problems_text1 = Please use the GitLab tool to report bugs in PageTop. However, check "existing bug reports" before reporting a new bug. +report_problems_text2 = Please report bugs specific to modules (such as admin, and others) to respective packages, not to PageTop itself. diff --git a/pagetop/src/base/module/demopage/locales/es-ES/demopage.ftl b/pagetop/src/base/module/demopage/locales/es-ES/demopage.ftl index 39720fcd..70706ca8 100644 --- a/pagetop/src/base/module/demopage/locales/es-ES/demopage.ftl +++ b/pagetop/src/base/module/demopage/locales/es-ES/demopage.ftl @@ -3,19 +3,23 @@ module_description = Muestra una página de demostración predeterminada cuando page_title = ¡Hola mundo! -text_welcome = Esta página se utiliza para probar el correcto funcionamiento de { $app } después de la instalación. Esta solución web funciona con { $pagetop }. Si puede leer esta página, significa que el servidor PageTop funciona correctamente, pero aún no se ha configurado. +welcome_to = Bienvenido a { $app } +welcome_intro = Esta página se utiliza para probar el correcto funcionamiento de { $app } después de la instalación. +welcome_pagetop = Esta solución web funciona con { $pagetop }. -title_normal_user = ¿Sólo de visita? -text1_normal_user = Si usted es un usuario normal de este sitio web y no sabe de qué trata esta página, probablemente significa que el sitio está experimentando problemas o está pasando por un mantenimiento de rutina. -text2_normal_user = Si el problema persiste, póngase en contacto con el administrador del sistema. +visiting_title = ¿Sólo de visita? +visiting_subtitle = ¿Eres usuario de este sitio web? +visiting_text1 = Si no sabes de qué trata esta página, probablemente significa que el sitio está experimentando problemas o está pasando por un mantenimiento de rutina. +visiting_text2 = Si el problema persiste, póngase en contacto con el administrador del sistema. -title_about_pagetop = Sobre PageTop -text1_about_pagetop = PageTop define una interfaz para los paquetes Rust más estables y populares para crear soluciones web modulares, extensibles y configurables. -text2_about_pagetop = Para obtener información sobre PageTop, visite el "sitio web de PageTop". +pagetop_title = Sobre PageTop +pagetop_text1 = Si puedes leer esta página, significa que el servidor PageTop funciona correctamente, pero aún no se ha configurado. +pagetop_text2 = PageTop define una interfaz para los paquetes Rust más estables y populares para crear soluciones web modulares, extensibles y configurables. +pagetop_text3 = Para obtener información sobre PageTop, visita el "sitio web de PageTop". -title_promo_pagetop = Promociona PageTop -text1_promo_pagetop = Puede usar la siguiente imagen en aplicaciones desarrolladas sobre { $pagetop }. ¡Gracias por usar PageTop! +pagetop_promo_title = Promociona PageTop +pagetop_promo_text1 = Puedes usar la siguiente imagen en aplicaciones desarrolladas sobre { $pagetop }. ¡Gracias por usar PageTop! -title_report_problems = Informando Problemas -text1_report_problems = Utilice la herramienta GitLab para informar errores en PageTop. Sin embargo, verifique los "informes de errores existentes" antes de informar de un nuevo error. -text2_report_problems = Informe los errores específicos de los módulos (como admin y otros) a los paquetes respectivos, no a PageTop en sí. +report_problems_title = Informando Problemas +report_problems_text1 = Utilice la herramienta GitLab para informar errores en PageTop. Sin embargo, verifique los "informes de errores existentes" antes de informar de un nuevo error. +report_problems_text2 = Informe los errores específicos de los módulos (como admin y otros) a los paquetes respectivos, no a PageTop en sí. diff --git a/pagetop/src/base/module/demopage/mod.rs b/pagetop/src/base/module/demopage/mod.rs index bb6e3b94..f3d3a903 100644 --- a/pagetop/src/base/module/demopage/mod.rs +++ b/pagetop/src/base/module/demopage/mod.rs @@ -18,61 +18,210 @@ impl ModuleTrait for DemopageModule { } fn configure_module(&self, cfg: &mut app::web::ServiceConfig) { - cfg.route("/", app::web::get().to(home)); + cfg.route("/", app::web::get().to(demo)); } } -async fn home() -> app::Result { - Page::prepare() +async fn demo() -> app::Result { + Page::new() .using_theme("Bootsier") - .with_title( - l("page_title").as_str() - ) - - - - .add_to("content", Container::prepare() - .with_id("welcome") - .add(Chunck::markup(html! { - h1 { (l("page_title")) } - p { (e("text_welcome", &args![ - "app" => format!("{}", &SETTINGS.app.name), - "pagetop" => "PageTop" - ])) } - })) - ) - .add_to("content", Container::prepare() - .add(Container::row() - .add(Container::column() - .with_id("visitors") - .add(Chunck::markup(html! { - h2 { (l("title_normal_user")) } - p { (l("text1_normal_user")) } - p { (l("text2_normal_user")) } - }))) - .add(Container::column() - .with_id("pagetop") - .add(Chunck::markup(html! { - h2 { (l("title_about_pagetop")) } - p { (l("text1_about_pagetop")) } - p { (l("text2_about_pagetop")) } - - h2 { (l("title_promo_pagetop")) } - p { (e("text1_promo_pagetop", &args![ - "pagetop" => - "PageTop" - ])) } - })) - ) - ) - ) - .add_to("content", Container::prepare() - .with_id("reporting") - .add(Chunck::markup(html! { - h2 { (l("title_report_problems")) } - p { (l("text1_report_problems")) } - p { (l("text2_report_problems")) } - })) - ) + .with_title(l("page_title").as_str()) + .add_to("content", hello_world()) + .add_to("content", hello_world2()) + .add_to("content", just_visiting()) + .add_to("content", about_pagetop()) + .add_to("content", promo_pagetop()) + .add_to("content", reporting_problems()) .render() } + +fn hello_world2() -> Container { + Container::header() + .add(grid::Row::new() + .add_column(grid::Column::new() + .add(Chunck::with(html! { + div class="section-title" { + (t("welcome_to", &args![ + "app" => SETTINGS.app.name.as_str() + ])) + } + h1 class="h1-large" { + (l("page_title")) + } + p class="p-large" { + (e("welcome_intro", &args![ + "app" => format!( + "{}", + &SETTINGS.app.name + ) + ])) + } + p { + (e("welcome_pagetop", &args![ + "pagetop" => "PageTop" + ])) + } + a class="btn-solid-lg" href="#services" { + "Offered services" + } + a class="quote" href="#contact" { + i class="fas fa-paper-plane" {} + "Get quote" + } + })) + ) + .add_column(grid::Column::new() + .add(Image::image("/bootsier/images/demo-header.svg")) + ) + ) +} + +fn hello_world() -> Chunck { + Chunck::with(html! { + header id="header" class="header" { + div class="container" { + div class="row" { + div class="col-lg-6 col-xl-5" { + div class="text-container" { + div class="section-title" { + (t("welcome_to", &args![ + "app" => SETTINGS.app.name.as_str() + ])) + } + h1 class="h1-large" { + (l("page_title")) + } + p class="p-large" { + (e("welcome_intro", &args![ + "app" => format!( + "{}", + &SETTINGS.app.name + ) + ])) + } + p { + (e("welcome_pagetop", &args![ + "pagetop" => "PageTop" + ])) + } + a class="btn-solid-lg" href="#services" { + "Offered services" + } + a class="quote" href="#contact" { + i class="fas fa-paper-plane" {} + "Get quote" + } + } + } + div class="col-lg-6 col-xl-7" { + div class="image-container" { + img class="img-fluid" src="/bootsier/images/demo-header.svg" alt="alternative" {} + } + } + } + } + } + }) +} + +fn just_visiting() -> Chunck { + Chunck::with(html! { + div id="details" class="basic-1" { + div class="container" { + div class="row" { + div class="col-lg-6 col-xl-7" { + div class="image-container" { + img class="img-fluid" src="/bootsier/images/demo-visiting.svg" alt="alternative" {} + } + } + div class="col-lg-6 col-xl-5" { + div class="text-container" { + h2 { + span { + (l("visiting_title")) + } + br {} + (l("visiting_subtitle")) + } + p { (l("visiting_text1")) } + p { (l("visiting_text2")) } + a class="btn-solid-reg" data-bs-toggle="modal" data-bs-target="#staticBackdrop" { "Modal" } + } + } + } + } + } + }) +} + +fn about_pagetop() -> Chunck { + Chunck::with(html! { + div id="pagetop" class="basic-2" { + div class="container" { + div class="row" { + div class="col-lg-6 col-xl-5" { + div class="text-container" { + h2 { (l("pagetop_title")) } + p { (l("pagetop_text1")) } + p { (l("pagetop_text2")) } + p { (l("pagetop_text3")) } + } + } + div class="col-lg-6 col-xl-7" { + div class="image-container" { + img class="img-fluid" src="/bootsier/images/demo-pagetop.svg" alt="alternative" {} + } + } + } + } + } + }) +} + +fn promo_pagetop() -> Chunck { + Chunck::with(html! { + div id="promo" class="basic-3" { + div class="container" { + div class="row" { + div class="col-lg-6 col-xl-5" { + div class="text-container" { + h2 { (l("pagetop_promo_title")) } + p { (e("pagetop_promo_text1", &args![ + "pagetop" => + "PageTop" + ])) } + } + } + div class="col-lg-6 col-xl-7" { + div class="image-container" { + img class="img-fluid" src="/bootsier/images/demo-pagetop.svg" alt="alternative" {} + } + } + } + } + } + }) +} + +fn reporting_problems() -> Chunck { + Chunck::with(html! { + div id="reporting" class="basic-4" { + div class="container" { + div class="row" { + div class="col-lg-6 col-xl-5" { + div class="text-container" { + h2 { (l("report_problems_title")) } + p { (l("report_problems_text1")) } + p { (l("report_problems_text2")) } + } + } + div class="col-lg-6 col-xl-7" { + div class="image-container" { + img class="img-fluid" src="/bootsier/images/demo-pagetop.svg" alt="alternative" {} + } + } + } + } + } + }) +} diff --git a/pagetop/src/base/theme/bootsier/mod.rs b/pagetop/src/base/theme/bootsier/mod.rs index ada65b0e..90111723 100644 --- a/pagetop/src/base/theme/bootsier/mod.rs +++ b/pagetop/src/base/theme/bootsier/mod.rs @@ -52,9 +52,9 @@ impl ThemeTrait for BootsierTheme { s = app::http::StatusCode::INTERNAL_SERVER_ERROR; } } - Page::prepare() + Page::new() .with_title(format!("Error {}", s.as_str()).as_str()) - .add_to("content", Chunck::markup(html! { + .add_to("content", Chunck::with(html! { div class="jumbotron" { div class="media" { img diff --git a/pagetop/src/html.rs b/pagetop/src/html.rs deleted file mode 100644 index ba885bb3..00000000 --- a/pagetop/src/html.rs +++ /dev/null @@ -1 +0,0 @@ -pub use maud::{DOCTYPE, Markup, PreEscaped, html}; diff --git a/pagetop/src/html/classes.rs b/pagetop/src/html/classes.rs new file mode 100644 index 00000000..16c5ac73 --- /dev/null +++ b/pagetop/src/html/classes.rs @@ -0,0 +1,63 @@ +pub struct Classes { + classes: Vec, + option : Option, + updated: bool, +} + +impl Classes { + pub fn none() -> Self { + Classes { + classes: Vec::new(), + option : None, + updated: true, + } + } + + pub fn some_class(class: &str) -> Self { + let mut c = Classes::none(); + c.add_class(class); + c + } + + pub fn some_classes(classes: Vec) -> Self { + let mut c = Classes::none(); + c.add_classes(classes); + c + } + + pub fn add_class(&mut self, class: &str) { + let class = class.trim().replace(" ", "_"); + if !class.is_empty() && !self.classes.iter().any(|c| *c == class) { + self.classes.push(class.to_owned()); + self.updated = false; + } + } + + pub fn add_classes(&mut self, classes: Vec) { + for class in classes.iter() { + self.add_class(class); + } + } + + pub fn classes(&mut self) -> &str { + match self.option() { + Some(classes) => classes.as_str(), + None => "", + } + } + + pub fn has_classes(&self) -> bool { + self.classes.len() > 0 + } + + pub fn option(&mut self) -> &Option { + if !self.updated { + self.option = match self.classes.len() { + 0 => None, + _ => Some(self.classes.join(" ")), + }; + self.updated = true; + } + &self.option + } +} diff --git a/pagetop/src/html/mod.rs b/pagetop/src/html/mod.rs new file mode 100644 index 00000000..60a9bd82 --- /dev/null +++ b/pagetop/src/html/mod.rs @@ -0,0 +1,8 @@ +pub use maud::{DOCTYPE, Markup, PreEscaped, html}; + +mod optional_id; +pub use optional_id::OptionId; +mod optional_attr; +pub use optional_attr::OptionAttr; +mod classes; +pub use classes::Classes; diff --git a/pagetop/src/html/optional_attr.rs b/pagetop/src/html/optional_attr.rs new file mode 100644 index 00000000..f6b48189 --- /dev/null +++ b/pagetop/src/html/optional_attr.rs @@ -0,0 +1,41 @@ +pub struct OptionAttr(Option); + +impl OptionAttr { + pub fn none() -> Self { + OptionAttr(None) + } + + pub fn some(value: &str) -> Self { + let value = value.trim(); + match value.is_empty() { + true => OptionAttr(None), + false => OptionAttr(Some(value.to_owned())), + } + } + + pub fn with_value(&mut self, value: &str) { + let value = value.trim(); + self.0 = match value.is_empty() { + true => None, + false => Some(value.to_owned()), + }; + } + + pub fn value(&self) -> &str { + match &self.0 { + Some(value) => value.as_str(), + None => "", + } + } + + pub fn has_value(&self) -> bool { + match &self.0 { + Some(_) => true, + None => false, + } + } + + pub fn option(&self) -> &Option { + &self.0 + } +} diff --git a/pagetop/src/html/optional_id.rs b/pagetop/src/html/optional_id.rs new file mode 100644 index 00000000..678ff5c8 --- /dev/null +++ b/pagetop/src/html/optional_id.rs @@ -0,0 +1,33 @@ +pub struct OptionId(Option); + +impl OptionId { + pub fn none() -> Self { + OptionId(None) + } + + pub fn with_value(&mut self, id: &str) { + let id = id.trim(); + self.0 = match id.is_empty() { + true => None, + false => Some(id.replace(" ", "_")), + }; + } + + pub fn value(&self) -> &str { + match &self.0 { + Some(id) => id.as_str(), + None => "", + } + } + + pub fn has_value(&self) -> bool { + match &self.0 { + Some(_) => true, + None => false, + } + } + + pub fn option(&self) -> &Option { + &self.0 + } +} diff --git a/pagetop/src/response/page/component.rs b/pagetop/src/response/page/component.rs index 26151cab..630b8a9b 100644 --- a/pagetop/src/response/page/component.rs +++ b/pagetop/src/response/page/component.rs @@ -7,7 +7,7 @@ use std::any::type_name; pub trait PageComponent: Downcast + Send + Sync { - fn prepare() -> Self where Self: Sized; + fn new() -> Self where Self: Sized; fn name(&self) -> &'static str { let name = type_name::(); diff --git a/pagetop/src/response/page/page.rs b/pagetop/src/response/page/page.rs index eff38daa..1ccb9603 100644 --- a/pagetop/src/response/page/page.rs +++ b/pagetop/src/response/page/page.rs @@ -1,9 +1,8 @@ -use crate::{Lazy, app, trace, util}; +use crate::{Lazy, app, trace}; use crate::config::SETTINGS; -use crate::html::{DOCTYPE, Markup, html}; +use crate::html::{Classes, DOCTYPE, Markup, OptionAttr, html}; use crate::response::page::{PageAssets, PageComponent, PageContainer}; -use std::borrow::Cow; use std::sync::RwLock; use std::collections::HashMap; @@ -41,31 +40,31 @@ static DEFAULT_DIRECTION: Lazy> = Lazy::new(|| { pub enum TextDirection { Auto, LeftToRight, RightToLeft } pub struct Page<'a> { - language : Option, - direction : Option, - title : Option, - description : Option, + language : OptionAttr, + direction : OptionAttr, + title : OptionAttr, + description : OptionAttr, assets : PageAssets, - body_classes: Cow<'a, str>, + body_classes: Classes, regions : HashMap<&'a str, PageContainer>, template : String, } impl<'a> Page<'a> { - pub fn prepare() -> Self { + pub fn new() -> Self { Page { language : match &*DEFAULT_LANGUAGE { - Some(language) => Some(language.to_owned()), - _ => None, + Some(language) => OptionAttr::some(language), + _ => OptionAttr::none(), }, direction : match &*DEFAULT_DIRECTION { - Some(direction) => Some(direction.to_owned()), - _ => None, + Some(direction) => OptionAttr::some(direction), + _ => OptionAttr::none(), }, - title : None, - description : None, - body_classes: "body".into(), + title : OptionAttr::none(), + description : OptionAttr::none(), + body_classes: Classes::some_class("body"), assets : PageAssets::new(), regions : COMPONENTS.read().unwrap().clone(), template : "default".to_owned(), @@ -75,38 +74,36 @@ impl<'a> Page<'a> { // Page BUILDER. pub fn with_language(&mut self, language: &str) -> &mut Self { - self.language = util::valid_str(language); + self.language.with_value(language); self } pub fn with_direction(&mut self, dir: TextDirection) -> &mut Self { - self.direction = match dir { - TextDirection::Auto => Some("auto".to_owned()), - TextDirection::LeftToRight => Some("ltr".to_owned()), - TextDirection::RightToLeft => Some("rtl".to_owned()), - }; + self.direction.with_value(match dir { + TextDirection::Auto => "auto", + TextDirection::LeftToRight => "ltr", + TextDirection::RightToLeft => "rtl", + }); self } pub fn with_title(&mut self, title: &str) -> &mut Self { - self.title = util::valid_str(title); + self.title.with_value(title); self } pub fn with_description(&mut self, description: &str) -> &mut Self { - self.description = util::valid_str(description); + self.description.with_value(description); self } - pub fn with_body_classes(&mut self, body_classes: &'a str) -> &mut Self { - self.body_classes = body_classes.into(); + pub fn add_body_class(&mut self, class: &str) -> &mut Self { + self.body_classes.add_class(class); self } - pub fn add_body_classes(&mut self, body_classes: &'a str) -> &mut Self { - self.body_classes = String::from( - format!("{} {}", self.body_classes, body_classes).trim() - ).into(); + pub fn add_body_classes(&mut self, classes: Vec) -> &mut Self { + self.body_classes.add_classes(classes); self } @@ -131,26 +128,23 @@ impl<'a> Page<'a> { // Page GETTERS. pub fn language(&self) -> &str { - util::assigned_str(&self.language) + self.language.value() } pub fn direction(&self) -> &str { - util::assigned_str(&self.direction) + self.direction.value() } pub fn title(&self) -> &str { - util::assigned_str(&self.title) + self.title.value() } pub fn description(&self) -> &str { - util::assigned_str(&self.description) + self.description.value() } - pub fn body_classes(&self) -> &str { - if self.body_classes.is_empty() { - return "body"; - } - &self.body_classes + pub fn body_classes(&mut self) -> &str { + self.body_classes.classes() } pub fn assets(&mut self) -> &mut PageAssets { @@ -176,7 +170,7 @@ impl<'a> Page<'a> { // Finalmente, renderizar la página. return Ok(html! { (DOCTYPE) - html lang=[&self.language] dir=[&self.direction] { + html lang=[&self.language.option()] dir=[&self.direction.option()] { (head) (body) } @@ -204,7 +198,7 @@ pub fn render_component( ) -> Markup { match component.is_renderable() { true => match assets.theme().render_component(component, assets) { - Some(markup) => markup, + Some(html) => html, None => component.default_render(assets) }, false => html! {} diff --git a/pagetop/src/theme/definition.rs b/pagetop/src/theme/definition.rs index 733736e2..15f6566e 100644 --- a/pagetop/src/theme/definition.rs +++ b/pagetop/src/theme/definition.rs @@ -99,9 +99,9 @@ pub trait ThemeTrait: Send + Sync { } fn render_error_page(&self, s: app::http::StatusCode) -> app::Result { - Page::prepare() + Page::new() .with_title(format!("Error {}", s.as_str()).as_str()) - .add_to("content", Chunck::markup(html! { + .add_to("content", Chunck::with(html! { div { h1 { (s) } } diff --git a/pagetop/src/util.rs b/pagetop/src/util.rs index 4ba4f76e..c2fd58d3 100644 --- a/pagetop/src/util.rs +++ b/pagetop/src/util.rs @@ -35,26 +35,3 @@ macro_rules! theme_static_files { } }}; } - -pub fn valid_id(id: &str) -> Option { - let id = id.trim(); - match id.is_empty() { - true => None, - false => Some(id.replace(" ", "_").to_lowercase()), - } -} - -pub fn valid_str(s: &str) -> Option { - let s = s.trim(); - match s.is_empty() { - true => None, - false => Some(s.to_owned()), - } -} - -pub fn assigned_str(optional: &Option) -> &str { - match optional { - Some(o) => o.as_str(), - None => "", - } -} diff --git a/pagetop/static/bootsier/images/demo-header.svg b/pagetop/static/bootsier/images/demo-header.svg new file mode 100644 index 00000000..060757ee --- /dev/null +++ b/pagetop/static/bootsier/images/demo-header.svg @@ -0,0 +1 @@ +header-illustration \ No newline at end of file diff --git a/pagetop/static/bootsier/images/demo-pagetop.svg b/pagetop/static/bootsier/images/demo-pagetop.svg new file mode 100644 index 00000000..08fbbb09 --- /dev/null +++ b/pagetop/static/bootsier/images/demo-pagetop.svg @@ -0,0 +1 @@ +details-2 \ No newline at end of file diff --git a/pagetop/static/bootsier/images/demo-visiting.svg b/pagetop/static/bootsier/images/demo-visiting.svg new file mode 100644 index 00000000..d06680b3 --- /dev/null +++ b/pagetop/static/bootsier/images/demo-visiting.svg @@ -0,0 +1 @@ +details-1 \ No newline at end of file