Modifica el renderizado de componentes

La función default_render() se implementa usando llamadas a funciones,
nunca usando campos internos de la estructura. Esto es así para dar las
mismas opciones a los temas que alteren el render de un componente dado.
This commit is contained in:
Manuel Cillero 2022-03-29 22:53:17 +02:00
parent 5aee113f54
commit 68d79f6090
23 changed files with 335 additions and 295 deletions

View file

@ -40,7 +40,7 @@ fn form_login() -> impl PageComponent {
.with_help_text(t("username_help", &args![ .with_help_text(t("username_help", &args![
"app" => SETTINGS.app.name.to_owned() "app" => SETTINGS.app.name.to_owned()
]).as_str()) ]).as_str())
.autofocus(true) .with_autofocus(true)
) )
.add(form::Input::password() .add(form::Input::password()
.with_name("pass") .with_name("pass")

View file

@ -31,15 +31,15 @@ impl PageComponent for Block {
} }
fn default_render(&self, assets: &mut PageAssets) -> Markup { fn default_render(&self, assets: &mut PageAssets) -> Markup {
let id = assets.serial_id(self.name(), self.id.value()); let id = assets.serial_id(self.name(), self.id());
let title = self.title.value();
html! { html! {
div id=(id) class="block" { div id=(id) class="block" {
@if !title.is_empty() { @match self.title() {
h2 class="block-title" { (title) } Some(title) => h2 class="block-title" { (title) },
None => {}
} }
div class="block-body" { div class="block-body" {
@for html in self.html.iter() { @for html in self.html().iter() {
(*html) (*html)
} }
} }
@ -88,12 +88,16 @@ impl Block {
// Block GETTERS. // Block GETTERS.
pub fn id(&self) -> &str { pub fn id(&self) -> &Option<String> {
self.id.value() self.id.option()
} }
pub fn title(&self) -> &str { pub fn title(&self) -> &Option<String> {
self.title.value() self.title.option()
}
pub fn html(&self) -> &Vec<Markup> {
&self.html
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {

View file

@ -28,7 +28,7 @@ impl PageComponent for Chunck {
fn default_render(&self, _: &mut PageAssets) -> Markup { fn default_render(&self, _: &mut PageAssets) -> Markup {
html! { html! {
@for html in self.html.iter() { @for html in self.html().iter() {
(*html) (*html)
} }
} }
@ -65,6 +65,10 @@ impl Chunck {
// Chunck GETTERS. // Chunck GETTERS.
pub fn html(&self) -> &Vec<Markup> {
&self.html
}
pub fn template(&self) -> &str { pub fn template(&self) -> &str {
self.template.as_str() self.template.as_str()
} }

View file

@ -1,12 +1,12 @@
use crate::prelude::*; use crate::prelude::*;
enum ContainerType { Header, Footer, Main, Section, Wrapper } pub enum ContainerType { Header, Footer, Main, Section, Wrapper }
pub struct Container { pub struct Container {
renderable: fn() -> bool, renderable: fn() -> bool,
weight : i8, weight : i8,
id : OptIden,
container : ContainerType, container : ContainerType,
id : OptIden,
components: PageContainer, components: PageContainer,
template : String, template : String,
} }
@ -17,8 +17,8 @@ impl PageComponent for Container {
Container { Container {
renderable: always, renderable: always,
weight : 0, weight : 0,
id : OptIden::none(),
container : ContainerType::Wrapper, container : ContainerType::Wrapper,
id : OptIden::none(),
components: PageContainer::new(), components: PageContainer::new(),
template : "default".to_owned(), template : "default".to_owned(),
} }
@ -33,38 +33,38 @@ impl PageComponent for Container {
} }
fn default_render(&self, assets: &mut PageAssets) -> Markup { fn default_render(&self, assets: &mut PageAssets) -> Markup {
match self.container { match self.container_type() {
ContainerType::Header => html! { ContainerType::Header => html! {
header id=[&self.id.option()] class="header" { header id=[self.id()] class="header" {
div class="container" { div class="container" {
(self.components.render(assets)) (self.render_components(assets))
} }
} }
}, },
ContainerType::Footer => html! { ContainerType::Footer => html! {
footer id=[&self.id.option()] class="footer" { footer id=[self.id()] class="footer" {
div class="container" { div class="container" {
(self.components.render(assets)) (self.render_components(assets))
} }
} }
}, },
ContainerType::Main => html! { ContainerType::Main => html! {
main id=[&self.id.option()] class="main" { main id=[self.id()] class="main" {
div class="container" { div class="container" {
(self.components.render(assets)) (self.render_components(assets))
} }
} }
}, },
ContainerType::Section => html! { ContainerType::Section => html! {
section id=[&self.id.option()] class="section" { section id=[self.id()] class="section" {
div class="container" { div class="container" {
(self.components.render(assets)) (self.render_components(assets))
} }
} }
}, },
_ => html! { _ => html! {
div id=[&self.id.option()] class="container" { div id=[self.id()] class="container" {
(self.components.render(assets)) (self.render_components(assets))
} }
} }
} }
@ -126,13 +126,23 @@ impl Container {
// Container GETTERS. // Container GETTERS.
pub fn id(&self) -> &str { pub fn container_type(&self) -> &ContainerType {
self.id.value() &self.container
}
pub fn id(&self) -> &Option<String> {
self.id.option()
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {
self.template.as_str() self.template.as_str()
} }
// Container EXTRAS.
pub fn render_components(&self, assets: &mut PageAssets) -> Markup {
html! { (self.components.render(assets)) }
}
} }
fn always() -> bool { fn always() -> bool {

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
enum ButtonType {Button, Reset, Submit} pub enum ButtonType {Button, Reset, Submit}
pub struct Button { pub struct Button {
renderable : fn() -> bool, renderable : fn() -> bool,
@ -37,26 +37,29 @@ impl PageComponent for Button {
} }
fn default_render(&self, _: &mut PageAssets) -> Markup { fn default_render(&self, _: &mut PageAssets) -> Markup {
let (button_type, button_class) = match &self.button_type { let (button_type, button_class) = match self.button_type() {
ButtonType::Button => ("button", "btn btn-primary form-button"), ButtonType::Button => ("button", "btn btn-primary form-button"),
ButtonType::Reset => ("reset", "btn btn-primary form-reset" ), ButtonType::Reset => ("reset", "btn btn-primary form-reset" ),
ButtonType::Submit => ("submit", "btn btn-primary form-submit") ButtonType::Submit => ("submit", "btn btn-primary form-submit"),
}; };
let id = match &self.name.option() { let id = match self.name() {
Some(name) => Some(format!("edit-{}", name)), Some(name) => Some(concat_string!("edit-", name)),
_ => None _ => None
}; };
html! { html! {
button button
type=(button_type) type=(button_type)
id=[&id] id=[id]
class=(button_class) class=(button_class)
name=[&self.name.option()] name=[self.name()]
value=[&self.value.option()] value=[self.value()]
autofocus=[&self.autofocus.option()] autofocus=[self.autofocus()]
disabled=[&self.disabled.option()] disabled=[self.disabled()]
{ {
(self.value.value()) @match self.value() {
Some(value) => { (value) },
None => {},
}
} }
} }
} }
@ -102,7 +105,7 @@ impl Button {
self self
} }
pub fn autofocus(mut self, toggle: bool) -> Self { pub fn with_autofocus(mut self, toggle: bool) -> Self {
self.autofocus.with_value(match toggle { self.autofocus.with_value(match toggle {
true => "autofocus", true => "autofocus",
false => "", false => "",
@ -110,7 +113,7 @@ impl Button {
self self
} }
pub fn disabled(mut self, toggle: bool) -> Self { pub fn with_disabled(mut self, toggle: bool) -> Self {
self.disabled.with_value(match toggle { self.disabled.with_value(match toggle {
true => "disabled", true => "disabled",
false => "", false => "",
@ -125,20 +128,24 @@ impl Button {
// Button GETTERS. // Button GETTERS.
pub fn name(&self) -> &str { pub fn button_type(&self) -> &ButtonType {
self.name.value() &self.button_type
} }
pub fn value(&self) -> &str { pub fn name(&self) -> &Option<String> {
self.value.value() self.name.option()
} }
pub fn has_autofocus(&self) -> bool { pub fn value(&self) -> &Option<String> {
self.autofocus.has_value() self.value.option()
} }
pub fn is_disabled(&self) -> bool { pub fn autofocus(&self) -> &Option<String> {
self.disabled.has_value() self.autofocus.option()
}
pub fn disabled(&self) -> &Option<String> {
self.disabled.option()
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {

View file

@ -45,10 +45,10 @@ impl PageComponent for Date {
} }
fn default_render(&self, _: &mut PageAssets) -> Markup { fn default_render(&self, _: &mut PageAssets) -> Markup {
let (class, id) = match self.name.option() { let (class, id) = match self.name() {
Some(name) => ( Some(name) => (
format!("form-item form-item-{} form-type-date", name), concat_string!("form-item form-item-", name, " form-type-date"),
Some(format!("edit-{}", name)) Some(concat_string!("edit-", name))
), ),
None => ( None => (
"form-item form-type-date".to_owned(), "form-item form-type-date".to_owned(),
@ -57,35 +57,33 @@ impl PageComponent for Date {
}; };
html! { html! {
div class=(class) { div class=(class) {
@if self.label.has_value() { @match self.label() {
label class="form-label" for=[&id] { Some(label) => label class="form-label" for=[&id] {
(self.label.value()) " " (label) " "
@if self.required.has_value() { @match self.required() {
span Some(_) => span
class="form-required" class="form-required"
title="Este campo es obligatorio." title="Este campo es obligatorio." { "*" } " ",
{ None => {}
"*"
} " "
}
} }
},
None => {}
} }
input input
type="date" type="date"
id=[&id] id=[id]
class="form-control" class="form-control"
name=[&self.name.option()] name=[self.name()]
value=[&self.value.option()] value=[self.value()]
placeholder=[&self.placeholder.option()] placeholder=[self.placeholder()]
autofocus=[&self.autofocus.option()] autofocus=[self.autofocus()]
autocomplete=[&self.autocomplete.option()] autocomplete=[self.autocomplete()]
readonly=[&self.readonly.option()] readonly=[self.readonly()]
required=[&self.required.option()] required=[self.required()]
disabled=[&self.disabled.option()]; disabled=[self.disabled()];
@if self.help_text.has_value() { @match self.help_text() {
div class="form-text" { Some(help_text) => div class="form-text" { (help_text) },
(self.help_text.value()) None => {}
}
} }
} }
} }
@ -126,7 +124,7 @@ impl Date {
self self
} }
pub fn autofocus(mut self, toggle: bool) -> Self { pub fn with_autofocus(mut self, toggle: bool) -> Self {
self.autofocus.with_value(match toggle { self.autofocus.with_value(match toggle {
true => "autofocus", true => "autofocus",
false => "", false => "",
@ -134,7 +132,7 @@ impl Date {
self self
} }
pub fn autocomplete(mut self, toggle: bool) -> Self { pub fn with_autocomplete(mut self, toggle: bool) -> Self {
self.autocomplete.with_value(match toggle { self.autocomplete.with_value(match toggle {
true => "", true => "",
false => "off", false => "off",
@ -142,7 +140,7 @@ impl Date {
self self
} }
pub fn disabled(mut self, toggle: bool) -> Self { pub fn with_disabled(mut self, toggle: bool) -> Self {
self.disabled.with_value(match toggle { self.disabled.with_value(match toggle {
true => "disabled", true => "disabled",
false => "", false => "",
@ -150,7 +148,7 @@ impl Date {
self self
} }
pub fn readonly(mut self, toggle: bool) -> Self { pub fn with_readonly(mut self, toggle: bool) -> Self {
self.readonly.with_value(match toggle { self.readonly.with_value(match toggle {
true => "readonly", true => "readonly",
false => "", false => "",
@ -158,7 +156,7 @@ impl Date {
self self
} }
pub fn required(mut self, toggle: bool) -> Self { pub fn with_required(mut self, toggle: bool) -> Self {
self.required.with_value(match toggle { self.required.with_value(match toggle {
true => "required", true => "required",
false => "", false => "",
@ -178,44 +176,44 @@ impl Date {
// Date GETTERS. // Date GETTERS.
pub fn name(&self) -> &str { pub fn name(&self) -> &Option<String> {
self.name.value() self.name.option()
} }
pub fn value(&self) -> &str { pub fn value(&self) -> &Option<String> {
self.value.value() self.value.option()
} }
pub fn label(&self) -> &str { pub fn label(&self) -> &Option<String> {
self.label.value() self.label.option()
} }
pub fn placeholder(&self) -> &str { pub fn placeholder(&self) -> &Option<String> {
self.placeholder.value() self.placeholder.option()
} }
pub fn has_autofocus(&self) -> bool { pub fn autofocus(&self) -> &Option<String> {
self.autofocus.has_value() self.autofocus.option()
} }
pub fn has_autocomplete(&self) -> bool { pub fn autocomplete(&self) -> &Option<String> {
!self.autocomplete.has_value() self.autocomplete.option()
} }
pub fn is_disabled(&self) -> bool { pub fn disabled(&self) -> &Option<String> {
self.disabled.has_value() self.disabled.option()
} }
pub fn is_readonly(&self) -> bool { pub fn readonly(&self) -> &Option<String> {
self.readonly.has_value() self.readonly.option()
} }
pub fn is_required(&self) -> bool { pub fn required(&self) -> &Option<String> {
self.required.has_value() self.required.option()
} }
pub fn help_text(&self) -> &str { pub fn help_text(&self) -> &Option<String> {
self.help_text.value() self.help_text.option()
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {

View file

@ -7,8 +7,8 @@ pub struct Form {
weight : i8, weight : i8,
id : OptIden, id : OptIden,
action : OptAttr, action : OptAttr,
method : FormMethod,
charset : OptAttr, charset : OptAttr,
method : FormMethod,
elements : PageContainer, elements : PageContainer,
template : String, template : String,
} }
@ -21,8 +21,8 @@ impl PageComponent for Form {
weight : 0, weight : 0,
id : OptIden::none(), id : OptIden::none(),
action : OptAttr::none(), action : OptAttr::none(),
method : FormMethod::Post,
charset : OptAttr::some("UTF-8"), charset : OptAttr::some("UTF-8"),
method : FormMethod::Post,
elements : PageContainer::new(), elements : PageContainer::new(),
template : "default".to_owned(), template : "default".to_owned(),
} }
@ -37,19 +37,19 @@ impl PageComponent for Form {
} }
fn default_render(&self, assets: &mut PageAssets) -> Markup { fn default_render(&self, assets: &mut PageAssets) -> Markup {
let method = match self.method { let method = match self.method() {
FormMethod::Get => None, FormMethod::Get => None,
FormMethod::Post => Some("post".to_owned()) FormMethod::Post => Some("post".to_owned())
}; };
html! { html! {
form form
id=[&self.id.option()] id=[self.id()]
action=[&self.action.option()] action=[self.action()]
method=[method] method=[method]
accept-charset=[&self.charset.option()] accept-charset=[self.charset()]
{ {
div { div {
(self.elements.render(assets)) (self.render_elements(assets))
} }
} }
} }
@ -80,13 +80,13 @@ impl Form {
self self
} }
pub fn with_method(mut self, method: FormMethod) -> Self { pub fn with_charset(mut self, charset: &str) -> Self {
self.method = method; self.charset.with_value(charset);
self self
} }
pub fn with_charset(mut self, charset: &str) -> Self { pub fn with_method(mut self, method: FormMethod) -> Self {
self.charset.with_value(charset); self.method = method;
self self
} }
@ -102,28 +102,31 @@ impl Form {
// Form GETTERS. // Form GETTERS.
pub fn id(&self) -> &str { pub fn id(&self) -> &Option<String> {
self.id.value() self.id.option()
} }
pub fn action(&self) -> &str { pub fn action(&self) -> &Option<String> {
self.action.value() self.action.option()
} }
pub fn method(&self) -> &str { pub fn charset(&self) -> &Option<String> {
match &self.method { self.charset.option()
FormMethod::Get => "get",
FormMethod::Post => "post"
}
} }
pub fn charset(&self) -> &str { pub fn method(&self) -> &FormMethod {
self.charset.value() &self.method
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {
self.template.as_str() self.template.as_str()
} }
// Form EXTRAS.
pub fn render_elements(&self, assets: &mut PageAssets) -> Markup {
html! { (self.elements.render(assets)) }
}
} }
fn always() -> bool { fn always() -> bool {

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Hidden { pub struct Hidden {
weight : i8, weight: i8,
name : OptIden, name : OptIden,
value : OptAttr, value : OptAttr,
} }
@ -10,7 +10,7 @@ impl PageComponent for Hidden {
fn new() -> Self { fn new() -> Self {
Hidden { Hidden {
weight : 0, weight: 0,
name : OptIden::none(), name : OptIden::none(),
value : OptAttr::none(), value : OptAttr::none(),
} }
@ -21,16 +21,12 @@ impl PageComponent for Hidden {
} }
fn default_render(&self, _: &mut PageAssets) -> Markup { fn default_render(&self, _: &mut PageAssets) -> Markup {
let id = match self.name.option() { let id = match self.name() {
Some(name) => Some(format!("value-{}", name)), Some(name) => Some(concat_string!("value-", name)),
_ => None _ => None
}; };
html! { html! {
input input type="hidden" id=[id] name=[self.name()] value=[self.value()];
type="hidden"
id=[&id]
name=[&self.name.option()]
value=[&self.value.option()];
} }
} }
} }
@ -60,11 +56,11 @@ impl Hidden {
// Hidden GETTERS. // Hidden GETTERS.
pub fn name(&self) -> &str { pub fn name(&self) -> &Option<String> {
self.name.value() self.name.option()
} }
pub fn value(&self) -> &str { pub fn value(&self) -> &Option<String> {
self.value.value() self.value.option()
} }
} }

View file

@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
enum InputType {Email, Password, Search, Telephone, Textfield, Url} pub enum InputType {Email, Password, Search, Telephone, Textfield, Url}
pub struct Input { pub struct Input {
renderable : fn() -> bool, renderable : fn() -> bool,
@ -55,7 +55,7 @@ impl PageComponent for Input {
} }
fn default_render(&self, _: &mut PageAssets) -> Markup { fn default_render(&self, _: &mut PageAssets) -> Markup {
let (type_input, type_class) = match &self.input_type { let (type_input, type_class) = match self.input_type() {
InputType::Email => ("email", "form-type-email"), InputType::Email => ("email", "form-type-email"),
InputType::Password => ("password", "form-type-password"), InputType::Password => ("password", "form-type-password"),
InputType::Search => ("search", "form-type-search"), InputType::Search => ("search", "form-type-search"),
@ -63,50 +63,48 @@ impl PageComponent for Input {
InputType::Textfield => ("text", "form-type-textfield"), InputType::Textfield => ("text", "form-type-textfield"),
InputType::Url => ("url", "form-type-url") InputType::Url => ("url", "form-type-url")
}; };
let (class, id) = match &self.name.option() { let (class, id) = match self.name() {
Some(name) => ( Some(name) => (
format!("form-item form-item-{} {}", name, type_class), concat_string!("form-item form-item-", name, " ", type_class),
Some(format!("edit-{}", name)) Some(concat_string!("edit-", name))
), ),
None => ( None => (
format!("form-item {}", type_class), concat_string!("form-item ", type_class),
None None
) )
}; };
html! { html! {
div class=(class) { div class=(class) {
@if self.label.has_value() { @match self.label() {
label class="form-label" for=[&id] { Some(label) => label class="form-label" for=[&id] {
(self.label.value()) " " (label) " "
@if self.required.has_value() { @match self.required() {
span Some(_) => span
class="form-required" class="form-required"
title="Este campo es obligatorio." title="Este campo es obligatorio." { "*" } " ",
{ None => {}
"*"
} " "
}
} }
},
None => {}
} }
input input
type=(type_input) type=(type_input)
id=[&id] id=[id]
class="form-control" class="form-control"
name=[&self.name.option()] name=[self.name()]
value=[&self.value.option()] value=[self.value()]
size=[self.size] size=[self.size()]
minlength=[self.minlength] minlength=[self.minlength()]
maxlength=[self.maxlength] maxlength=[self.maxlength()]
placeholder=[&self.placeholder.option()] placeholder=[self.placeholder()]
autofocus=[&self.autofocus.option()] autofocus=[self.autofocus()]
autocomplete=[&self.autocomplete.option()] autocomplete=[self.autocomplete()]
readonly=[&self.readonly.option()] readonly=[self.readonly()]
required=[&self.required.option()] required=[self.required()]
disabled=[&self.disabled.option()]; disabled=[self.disabled()];
@if self.help_text.has_value() { @match self.help_text() {
div class="form-text" { Some(help_text) => div class="form-text" { (help_text) },
(self.help_text.value()) None => {}
}
} }
} }
} }
@ -196,7 +194,7 @@ impl Input {
self self
} }
pub fn autofocus(mut self, toggle: bool) -> Self { pub fn with_autofocus(mut self, toggle: bool) -> Self {
self.autofocus.with_value(match toggle { self.autofocus.with_value(match toggle {
true => "autofocus", true => "autofocus",
false => "", false => "",
@ -204,7 +202,7 @@ impl Input {
self self
} }
pub fn autocomplete(mut self, toggle: bool) -> Self { pub fn with_autocomplete(mut self, toggle: bool) -> Self {
self.autocomplete.with_value(match toggle { self.autocomplete.with_value(match toggle {
true => "", true => "",
false => "off", false => "off",
@ -212,7 +210,7 @@ impl Input {
self self
} }
pub fn disabled(mut self, toggle: bool) -> Self { pub fn with_disabled(mut self, toggle: bool) -> Self {
self.disabled.with_value(match toggle { self.disabled.with_value(match toggle {
true => "disabled", true => "disabled",
false => "", false => "",
@ -220,7 +218,7 @@ impl Input {
self self
} }
pub fn readonly(mut self, toggle: bool) -> Self { pub fn with_readonly(mut self, toggle: bool) -> Self {
self.readonly.with_value(match toggle { self.readonly.with_value(match toggle {
true => "readonly", true => "readonly",
false => "", false => "",
@ -228,7 +226,7 @@ impl Input {
self self
} }
pub fn required(mut self, toggle: bool) -> Self { pub fn with_required(mut self, toggle: bool) -> Self {
self.required.with_value(match toggle { self.required.with_value(match toggle {
true => "required", true => "required",
false => "", false => "",
@ -248,16 +246,20 @@ impl Input {
// Input GETTERS. // Input GETTERS.
pub fn name(&self) -> &str { pub fn input_type(&self) -> &InputType {
self.name.value() &self.input_type
} }
pub fn value(&self) -> &str { pub fn name(&self) -> &Option<String> {
self.value.value() self.name.option()
} }
pub fn label(&self) -> &str { pub fn value(&self) -> &Option<String> {
self.label.value() self.value.option()
}
pub fn label(&self) -> &Option<String> {
self.label.option()
} }
pub fn size(&self) -> Option<u16> { pub fn size(&self) -> Option<u16> {
@ -272,32 +274,32 @@ impl Input {
self.maxlength self.maxlength
} }
pub fn placeholder(&self) -> &str { pub fn placeholder(&self) -> &Option<String> {
self.placeholder.value() self.placeholder.option()
} }
pub fn has_autofocus(&self) -> bool { pub fn autofocus(&self) -> &Option<String> {
self.autofocus.has_value() self.autofocus.option()
} }
pub fn has_autocomplete(&self) -> bool { pub fn autocomplete(&self) -> &Option<String> {
!self.autocomplete.has_value() self.autocomplete.option()
} }
pub fn is_disabled(&self) -> bool { pub fn disabled(&self) -> &Option<String> {
self.disabled.has_value() self.disabled.option()
} }
pub fn is_readonly(&self) -> bool { pub fn readonly(&self) -> &Option<String> {
self.readonly.has_value() self.readonly.option()
} }
pub fn is_required(&self) -> bool { pub fn required(&self) -> &Option<String> {
self.required.has_value() self.required.option()
} }
pub fn help_text(&self) -> &str { pub fn help_text(&self) -> &Option<String> {
self.help_text.value() self.help_text.option()
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {

View file

@ -2,10 +2,10 @@ mod form;
pub use form::{Form, FormMethod}; pub use form::{Form, FormMethod};
mod input; mod input;
pub use input::Input; pub use input::{Input, InputType};
mod hidden; mod hidden;
pub use hidden::Hidden; pub use hidden::Hidden;
mod date; mod date;
pub use date::Date; pub use date::Date;
mod button; mod button;
pub use button::Button; pub use button::{Button, ButtonType};

View file

@ -32,8 +32,8 @@ impl PageComponent for Column {
fn default_render(&self, assets: &mut PageAssets) -> Markup { fn default_render(&self, assets: &mut PageAssets) -> Markup {
html! { html! {
div id=[&self.id.option()] class=[&self.classes.option()] { div id=[self.id()] class=[self.classes()] {
(self.components.render(assets)) (self.render_components(assets))
} }
} }
} }
@ -75,13 +75,23 @@ impl Column {
// Column GETTERS. // Column GETTERS.
pub fn id(&self) -> &str { pub fn id(&self) -> &Option<String> {
self.id.value() self.id.option()
}
pub fn classes(&self) -> &Option<String> {
self.classes.option()
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {
self.template.as_str() self.template.as_str()
} }
// Column EXTRAS.
pub fn render_components(&self, assets: &mut PageAssets) -> Markup {
html! { (self.components.render(assets)) }
}
} }
fn always() -> bool { fn always() -> bool {

View file

@ -32,8 +32,8 @@ impl PageComponent for Row {
fn default_render(&self, assets: &mut PageAssets) -> Markup { fn default_render(&self, assets: &mut PageAssets) -> Markup {
html! { html! {
div id=[&self.id.option()] class=[&self.classes.option()] { div id=[self.id()] class=[self.classes()] {
(self.columns.render(assets)) (self.render_columns(assets))
} }
} }
} }
@ -75,13 +75,23 @@ impl Row {
// Row GETTERS. // Row GETTERS.
pub fn id(&self) -> &str { pub fn id(&self) -> &Option<String> {
self.id.value() self.id.option()
}
pub fn classes(&self) -> &Option<String> {
self.classes.option()
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {
self.template.as_str() self.template.as_str()
} }
// Row EXTRAS.
pub fn render_columns(&self, assets: &mut PageAssets) -> Markup {
html! { (self.columns.render(assets)) }
}
} }
fn always() -> bool { fn always() -> bool {

View file

@ -3,7 +3,7 @@ use crate::prelude::*;
pub struct Image { pub struct Image {
renderable: fn() -> bool, renderable: fn() -> bool,
weight : i8, weight : i8,
source : Option<String>, source : OptAttr,
template : String, template : String,
} }
@ -13,7 +13,7 @@ impl PageComponent for Image {
Image { Image {
renderable: always, renderable: always,
weight : 0, weight : 0,
source : None, source : OptAttr::none(),
template : "default".to_owned(), template : "default".to_owned(),
} }
} }
@ -28,7 +28,7 @@ impl PageComponent for Image {
fn default_render(&self, _: &mut PageAssets) -> Markup { fn default_render(&self, _: &mut PageAssets) -> Markup {
html! { html! {
img src=[&self.source] class="img-fluid" {} img src=[self.source()] class="img-fluid";
} }
} }
} }
@ -36,9 +36,7 @@ impl PageComponent for Image {
impl Image { impl Image {
pub fn image(source: &str) -> Self { pub fn image(source: &str) -> Self {
let mut i = Image::new(); Image::new().with_source(source)
i.source = Some(source.to_owned());
i
} }
// Image BUILDER. // Image BUILDER.
@ -53,6 +51,11 @@ impl Image {
self self
} }
pub fn with_source(mut self, source: &str) -> Self {
self.source.with_value(source);
self
}
pub fn using_template(mut self, template: &str) -> Self { pub fn using_template(mut self, template: &str) -> Self {
self.template = template.to_owned(); self.template = template.to_owned();
self self
@ -60,6 +63,10 @@ impl Image {
// Image GETTERS. // Image GETTERS.
pub fn source(&self) -> &Option<String> {
self.source.option()
}
pub fn template(&self) -> &str { pub fn template(&self) -> &str {
self.template.as_str() self.template.as_str()
} }

View file

@ -1,12 +1,13 @@
use crate::prelude::*; use crate::prelude::*;
enum MenuItemType { pub enum MenuItemType {
Label(String), Label(String),
Link(String, String), Link(String, String),
LinkBlank(String, String), LinkBlank(String, String),
Html(Markup), Html(Markup),
Separator, Separator,
Submenu(String, Menu), Submenu(String, Menu),
Void,
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -16,7 +17,7 @@ enum MenuItemType {
pub struct MenuItem { pub struct MenuItem {
renderable: fn() -> bool, renderable: fn() -> bool,
weight : i8, weight : i8,
item_type : Option<MenuItemType>, item_type : MenuItemType,
} }
impl PageComponent for MenuItem { impl PageComponent for MenuItem {
@ -25,7 +26,7 @@ impl PageComponent for MenuItem {
MenuItem { MenuItem {
renderable: always, renderable: always,
weight : 0, weight : 0,
item_type : None, item_type : MenuItemType::Void,
} }
} }
@ -38,22 +39,22 @@ impl PageComponent for MenuItem {
} }
fn default_render(&self, assets: &mut PageAssets) -> Markup { fn default_render(&self, assets: &mut PageAssets) -> Markup {
match &self.item_type { match self.item_type() {
Some(MenuItemType::Label(label)) => html! { MenuItemType::Label(label) => html! {
li class="label" { a href="#" { (label) } } li class="label" { a href="#" { (label) } }
}, },
Some(MenuItemType::Link(label, path)) => html! { MenuItemType::Link(label, path) => html! {
li class="link" { a href=(path) { (label) } } li class="link" { a href=(path) { (label) } }
}, },
Some(MenuItemType::LinkBlank(label, path)) => html! { MenuItemType::LinkBlank(label, path) => html! {
li class="link_blank" { li class="link_blank" {
a href=(path) target="_blank" { (label) } a href=(path) target="_blank" { (label) }
} }
}, },
Some(MenuItemType::Html(html)) => html! { MenuItemType::Html(html) => html! {
li class="html" { (*html) } li class="html" { (*html) }
}, },
Some(MenuItemType::Submenu(label, menu)) => html! { MenuItemType::Submenu(label, menu) => html! {
li class="submenu" { li class="submenu" {
a href="#" { (label) } a href="#" { (label) }
ul { ul {
@ -61,10 +62,10 @@ impl PageComponent for MenuItem {
} }
} }
}, },
Some(MenuItemType::Separator) => html! { MenuItemType::Separator => html! {
li class="separator" { } li class="separator" { }
}, },
None => html! {} MenuItemType::Void => html! {},
} }
} }
} }
@ -75,7 +76,7 @@ impl MenuItem {
MenuItem { MenuItem {
renderable: always, renderable: always,
weight : 0, weight : 0,
item_type : Some(MenuItemType::Label(label.to_owned())), item_type : MenuItemType::Label(label.to_owned()),
} }
} }
@ -83,10 +84,10 @@ impl MenuItem {
MenuItem { MenuItem {
renderable: always, renderable: always,
weight : 0, weight : 0,
item_type : Some(MenuItemType::Link( item_type : MenuItemType::Link(
label.to_owned(), label.to_owned(),
path.to_owned(), path.to_owned(),
)), ),
} }
} }
@ -94,10 +95,10 @@ impl MenuItem {
MenuItem { MenuItem {
renderable: always, renderable: always,
weight : 0, weight : 0,
item_type : Some(MenuItemType::LinkBlank( item_type : MenuItemType::LinkBlank(
label.to_owned(), label.to_owned(),
path.to_owned(), path.to_owned(),
)), ),
} }
} }
@ -105,7 +106,7 @@ impl MenuItem {
MenuItem { MenuItem {
renderable: always, renderable: always,
weight : 0, weight : 0,
item_type : Some(MenuItemType::Html(html)), item_type : MenuItemType::Html(html),
} }
} }
@ -113,7 +114,7 @@ impl MenuItem {
MenuItem { MenuItem {
renderable: always, renderable: always,
weight : 0, weight : 0,
item_type : Some(MenuItemType::Separator), item_type : MenuItemType::Separator,
} }
} }
@ -121,10 +122,10 @@ impl MenuItem {
MenuItem { MenuItem {
renderable: always, renderable: always,
weight : 0, weight : 0,
item_type : Some(MenuItemType::Submenu( item_type : MenuItemType::Submenu(
label.to_owned(), label.to_owned(),
menu menu
)), ),
} }
} }
@ -139,6 +140,12 @@ impl MenuItem {
self.weight = weight; self.weight = weight;
self self
} }
// MenuItem GETTERS.
pub fn item_type(&self) -> &MenuItemType {
&self.item_type
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -186,7 +193,7 @@ impl PageComponent for Menu {
)) ))
.add_jquery(); .add_jquery();
let id = assets.serial_id(self.name(), self.id.value()); let id = assets.serial_id(self.name(), self.id());
html! { html! {
ul id=(id) class="sm sm-clean" { ul id=(id) class="sm sm-clean" {
(self.render_items(assets)) (self.render_items(assets))
@ -232,8 +239,8 @@ impl Menu {
// Menu GETTERS. // Menu GETTERS.
pub fn id(&self) -> &str { pub fn id(&self) -> &Option<String> {
self.id.value() self.id.option()
} }
pub fn template(&self) -> &str { pub fn template(&self) -> &str {

View file

@ -1,5 +1,5 @@
mod container; mod container;
pub use container::Container; pub use container::{Container, ContainerType};
pub mod grid; pub mod grid;
@ -10,7 +10,7 @@ pub use block::Block;
mod image; mod image;
pub use image::Image; pub use image::Image;
mod menu; mod menu;
pub use menu::{Menu, MenuItem}; pub use menu::{Menu, MenuItem, MenuItemType};
pub mod form; pub mod form;
pub use form::{Form, FormMethod}; pub use form::{Form, FormMethod};

View file

@ -140,7 +140,7 @@ fn just_visiting() -> Chunck {
span { span {
(l("visiting_title")) (l("visiting_title"))
} }
br {} br;
(l("visiting_subtitle")) (l("visiting_subtitle"))
} }
p { (l("visiting_text1")) } p { (l("visiting_text1")) }

View file

@ -19,17 +19,6 @@ impl OptAttr {
}; };
} }
pub fn value(&self) -> &str {
match &self.0 {
Some(value) => value.as_str(),
None => "",
}
}
pub fn has_value(&self) -> bool {
self.0 != None
}
pub fn option(&self) -> &Option<String> { pub fn option(&self) -> &Option<String> {
&self.0 &self.0
} }

View file

@ -19,17 +19,6 @@ impl OptIden {
}; };
} }
pub fn value(&self) -> &str {
match &self.0 {
Some(id) => id.as_str(),
None => "",
}
}
pub fn has_value(&self) -> bool {
self.0 != None
}
pub fn option(&self) -> &Option<String> { pub fn option(&self) -> &Option<String> {
&self.0 &self.0
} }

View file

@ -1,8 +1,11 @@
// Exports.
pub use concat_string::concat_string;
pub use doc_comment::doc_comment;
pub use once_cell::sync::Lazy;
// Local. // Local.
pub(crate) use concat_string::concat_string;
pub(crate) use doc_comment::doc_comment;
pub(crate) use once_cell::sync::Lazy;
pub(crate) use futures::executor::block_on as run_now; pub(crate) use futures::executor::block_on as run_now;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View file

@ -3,6 +3,7 @@
// Macros. // Macros.
pub use crate::{ pub use crate::{
args, args,
concat_string,
theme_static_files, theme_static_files,
}; };

View file

@ -1,4 +1,4 @@
use crate::{Lazy, base}; use crate::{Lazy, base, concat_string};
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::html::{Markup, PreEscaped, html}; use crate::html::{Markup, PreEscaped, html};
use crate::theme::*; use crate::theme::*;
@ -289,8 +289,10 @@ impl PageAssets {
// Assets EXTRAS. // Assets EXTRAS.
pub fn serial_id(&mut self, prefix: &str, id: &str) -> String { pub fn serial_id(&mut self, prefix: &str, id: &Option<String>) -> String {
if id.is_empty() { match id {
Some(id) => id.to_string(),
None => {
let prefix = prefix.trim().replace(" ", "_").to_lowercase(); let prefix = prefix.trim().replace(" ", "_").to_lowercase();
let prefix = if prefix.is_empty() { let prefix = if prefix.is_empty() {
"prefix".to_owned() "prefix".to_owned()
@ -298,9 +300,8 @@ impl PageAssets {
prefix prefix
}; };
self.id_counter += 1; self.id_counter += 1;
[prefix, self.id_counter.to_string()].join("-") concat_string!(prefix, "-", self.id_counter.to_string())
} else { }
id.to_owned()
} }
} }
} }

View file

@ -122,20 +122,20 @@ impl<'a> Page<'a> {
// Page GETTERS. // Page GETTERS.
pub fn language(&self) -> &str { pub fn language(&self) -> &Option<String> {
self.language.value() self.language.option()
} }
pub fn direction(&self) -> &str { pub fn direction(&self) -> &Option<String> {
self.direction.value() self.direction.option()
} }
pub fn title(&self) -> &str { pub fn title(&self) -> &Option<String> {
self.title.value() self.title.option()
} }
pub fn description(&self) -> &str { pub fn description(&self) -> &Option<String> {
self.description.value() self.description.option()
} }
pub fn body_classes(&mut self) -> &str { pub fn body_classes(&mut self) -> &str {
@ -165,7 +165,7 @@ impl<'a> Page<'a> {
// Finalmente, renderizar la página. // Finalmente, renderizar la página.
return Ok(html! { return Ok(html! {
(DOCTYPE) (DOCTYPE)
html lang=[&self.language.option()] dir=[&self.direction.option()] { html lang=[self.language()] dir=[self.direction()] {
(head) (head)
(body) (body)
} }

View file

@ -1,4 +1,4 @@
use crate::app; use crate::{app, concat_string};
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::html::{Markup, html}; use crate::html::{Markup, html};
use crate::response::page::{Favicon, Page, PageAssets, PageComponent}; use crate::response::page::{Favicon, Page, PageAssets, PageComponent};
@ -28,22 +28,21 @@ pub trait ThemeTrait: Send + Sync {
} }
fn render_page_head(&self, page: &mut Page) -> Markup { fn render_page_head(&self, page: &mut Page) -> Markup {
let title = page.title();
let title = if title.is_empty() {
SETTINGS.app.name.to_owned()
} else {
[SETTINGS.app.name.to_string(), title.to_string()].join(" | ")
};
let description = page.description();
let viewport = "width=device-width, initial-scale=1, shrink-to-fit=no"; let viewport = "width=device-width, initial-scale=1, shrink-to-fit=no";
html! { html! {
head { head {
meta charset="utf-8"; meta charset="utf-8";
title { (title) } @match page.title() {
Some(t) => title {
(concat_string!(SETTINGS.app.name, " | ", t))
},
None => title { (SETTINGS.app.name) }
}
@if !description.is_empty() { @match page.description() {
meta name="description" content=(description); Some(d) => meta name="description" content=(d);,
None => {}
} }
meta http-equiv="X-UA-Compatible" content="IE=edge"; meta http-equiv="X-UA-Compatible" content="IE=edge";