diff --git a/packages/pagetop-bootsier/src/lib.rs b/packages/pagetop-bootsier/src/lib.rs index f6f3d2a8..60efa329 100644 --- a/packages/pagetop-bootsier/src/lib.rs +++ b/packages/pagetop-bootsier/src/lib.rs @@ -43,43 +43,23 @@ impl ThemeTrait for Bootsier { } fn prepare_body(&self, page: &mut Page) -> Markup { - let skip_to_id = concat_string!("#", page.skip_to().get().unwrap_or("content".to_owned())); - - flex::Container::body() - .with_id(page.body_id().get().unwrap_or_default()) - .with_classes(ClassesOp::Add, page.body_classes().get().unwrap_or_default()) - .add_item(flex::Item::bundle() - .add_component(Html::with(html! { - @if let Some(skip) = L10n::l("skip_to_content").using(page.context().langid()) { - div class="skip__to_content" { - a href=(skip_to_id) { (skip) } - } - } - })) - .add_component( - match page.context().layout() { - "admin" => flex::Container::new().add_item( - flex::Item::new() - .add_component(flex::Region::named("top-menu")) - .add_component(flex::Region::named("side-menu")) - .add_component(flex::Region::named("content")), - ), - _ => flex::Container::new().add_item( - flex::Item::new() - .add_component(flex::Region::named("header")) - .add_component(flex::Region::named("nav_branding")) - .add_component(flex::Region::named("nav_main")) - .add_component(flex::Region::named("nav_additional")) - .add_component(flex::Region::named("breadcrumb")) - .add_component(flex::Region::named("content")) - .add_component(flex::Region::named("sidebar_first")) - .add_component(flex::Region::named("sidebar_second")) - .add_component(flex::Region::named("footer")), - ), - } - ) - ) - .render(page.context()) + Body::with(match page.context().layout() { + "admin" => flex::Container::new() + .add_item(flex::Item::region().with_id("top-menu")) + .add_item(flex::Item::region().with_id("side-menu")) + .add_item(flex::Item::region().with_id("content")), + _ => flex::Container::new() + .add_item(flex::Item::region().with_id("header")) + .add_item(flex::Item::region().with_id("nav_branding")) + .add_item(flex::Item::region().with_id("nav_main")) + .add_item(flex::Item::region().with_id("nav_additional")) + .add_item(flex::Item::region().with_id("breadcrumb")) + .add_item(flex::Item::region().with_id("content")) + .add_item(flex::Item::region().with_id("sidebar_first")) + .add_item(flex::Item::region().with_id("sidebar_second")) + .add_item(flex::Item::region().with_id("footer")), + }) + .render(page.context()) } fn after_prepare_body(&self, page: &mut Page) { diff --git a/src/base/component/basic.rs b/src/base/component/basic.rs index d8ebb8b8..a031c51c 100644 --- a/src/base/component/basic.rs +++ b/src/base/component/basic.rs @@ -4,5 +4,11 @@ pub use html::Html; mod fluent; pub use fluent::Fluent; +mod body; +pub use body::Body; + mod components; pub use components::Components; + +mod region; +pub use region::Region; diff --git a/src/base/component/basic/body.rs b/src/base/component/basic/body.rs new file mode 100644 index 00000000..0e4f14a1 --- /dev/null +++ b/src/base/component/basic/body.rs @@ -0,0 +1,51 @@ +use crate::prelude::*; + +#[derive(AutoDefault)] +pub struct Body(MixedComponents); + +impl ComponentTrait for Body { + fn new() -> Self { + Body::default() + } + + fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { + let skip_to_id = cx.body_skip_to().get().unwrap_or("content".to_owned()); + + PrepareMarkup::With(html! { + body id=[cx.body_id().get()] class=[cx.body_classes().get()] { + @if let Some(skip) = L10n::l("skip_to_content").using(cx.langid()) { + div class="skip__to_content" { + a href=(concat_string!("#", skip_to_id)) { (skip) } + } + } + (self.components().render(cx)) + } + }) + } +} + +impl Body { + pub fn with(component: impl ComponentTrait) -> Self { + Body::default().add_component(component) + } + + // Body BUILDER. + + #[fn_builder] + pub fn alter_components(&mut self, op: AnyOp) -> &mut Self { + self.0.alter_value(op); + self + } + + #[rustfmt::skip] + pub fn add_component(mut self, component: impl ComponentTrait) -> Self { + self.0.alter_value(AnyOp::Add(AnyComponent::with(component))); + self + } + + // Body GETTERS. + + pub fn components(&self) -> &MixedComponents { + &self.0 + } +} diff --git a/src/base/component/basic/region.rs b/src/base/component/basic/region.rs new file mode 100644 index 00000000..09b0abf3 --- /dev/null +++ b/src/base/component/basic/region.rs @@ -0,0 +1,31 @@ +use crate::prelude::*; + +#[derive(AutoDefault)] +pub struct Region(OptionId); + +impl ComponentTrait for Region { + fn new() -> Self { + Region::default() + } + + fn id(&self) -> Option { + self.0.get() + } + + fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { + match self.id() { + Some(id) => PrepareMarkup::With(cx.prepare_region(id)), + _ => PrepareMarkup::None, + } + } +} + +impl Region { + // Region BUILDER. + + #[fn_builder] + pub fn alter_id(&mut self, id: impl Into) -> &mut Self { + self.0.alter_value(id); + self + } +} diff --git a/src/base/component/flex.rs b/src/base/component/flex.rs index 8bcfe297..9753bc14 100644 --- a/src/base/component/flex.rs +++ b/src/base/component/flex.rs @@ -4,9 +4,6 @@ pub use container::Container; mod item; pub use item::Item; -mod region; -pub use region::Region; - use crate::prelude::*; // ************************************************************************************************* diff --git a/src/base/component/flex/container.rs b/src/base/component/flex/container.rs index c391db02..559f1d33 100644 --- a/src/base/component/flex/container.rs +++ b/src/base/component/flex/container.rs @@ -4,7 +4,6 @@ use crate::prelude::*; pub enum ContainerType { #[default] Default, - Body, Header, Main, Section, @@ -77,11 +76,6 @@ impl ComponentTrait for Container { (output) } }), - ContainerType::Body => PrepareMarkup::With(html! { - body id=[self.id()] class=[self.classes().get()] style=[gap] { - (output) - } - }), ContainerType::Header => PrepareMarkup::With(html! { header id=[self.id()] class=[self.classes().get()] style=[gap] { (output) @@ -112,13 +106,6 @@ impl ComponentTrait for Container { } impl Container { - pub fn body() -> Self { - Container { - container_type: ContainerType::Body, - ..Default::default() - } - } - pub fn header() -> Self { Container { container_type: ContainerType::Header, @@ -210,7 +197,6 @@ impl Container { self } - #[rustfmt::skip] pub fn add_item(mut self, item: flex::Item) -> Self { self.items.alter_value(AnyOp::Add(AnyComponent::with(item))); self diff --git a/src/base/component/flex/item.rs b/src/base/component/flex/item.rs index da0b9b16..49b75858 100644 --- a/src/base/component/flex/item.rs +++ b/src/base/component/flex/item.rs @@ -4,6 +4,7 @@ use crate::prelude::*; pub enum ItemType { #[default] Default, + Region, Wrapper, Bundle, } @@ -57,36 +58,60 @@ impl ComponentTrait for Item { } fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let output = self.components().render(cx); - if !output.is_empty() { - let order = match self.weight() { - 0 => None, - _ => Some(concat_string!("order: ", self.weight().to_string(), ";")), - }; - match self.item_type() { - ItemType::Default => PrepareMarkup::With(html! { - div id=[self.id()] class=[self.classes().get()] style=[order] { - div class="flex__content" { - (output) - } - } - }), - ItemType::Wrapper => PrepareMarkup::With(html! { - div id=[self.id()] class=[self.classes().get()] style=[order] { + let (output, region) = match self.item_type() { + ItemType::Region => ( + self.components().render(cx), + if let Some(id) = self.id() { + cx.prepare_region(id) + } else { + Markup::default() + }, + ), + _ => (self.components().render(cx), Markup::default()), + }; + if output.is_empty() && region.is_empty() { + return PrepareMarkup::None; + } + let order = match self.weight() { + 0 => None, + _ => Some(concat_string!("order: ", self.weight().to_string(), ";")), + }; + match self.item_type() { + ItemType::Default => PrepareMarkup::With(html! { + div id=[self.id()] class=[self.classes().get()] style=[order] { + div class="flex__content" { (output) } - }), - ItemType::Bundle => PrepareMarkup::With(html! { + } + }), + ItemType::Region => PrepareMarkup::With(html! { + div id=[self.id()] class=[self.classes().get()] style=[order] { + div class="flex__content flex__region" { + (region) + (output) + } + } + }), + ItemType::Wrapper => PrepareMarkup::With(html! { + div id=[self.id()] class=[self.classes().get()] style=[order] { (output) - }), - } - } else { - PrepareMarkup::None + } + }), + ItemType::Bundle => PrepareMarkup::With(html! { + (output) + }), } } } impl Item { + pub fn region() -> Self { + Item { + item_type: ItemType::Region, + ..Default::default() + } + } + pub fn wrapper() -> Self { Item { item_type: ItemType::Wrapper, diff --git a/src/base/component/flex/region.rs b/src/base/component/flex/region.rs deleted file mode 100644 index 41e3da27..00000000 --- a/src/base/component/flex/region.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::prelude::*; - -#[derive(AutoDefault)] -pub struct Region(OptionId); - -impl ComponentTrait for Region { - fn new() -> Self { - Region::default() - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - if let Some(name) = self.name().get() { - return PrepareMarkup::With(cx.prepare_region(name.as_str())); - } - PrepareMarkup::None - } -} - -impl Region { - pub fn named(name: impl Into) -> Self { - Region::new().with_name(name) - } - - // Region BUILDER. - - #[fn_builder] - pub fn alter_name(&mut self, name: impl Into) -> &mut Self { - self.0.alter_value(name); - self - } - - // Region GETTERS. - - pub fn name(&self) -> &OptionId { - &self.0 - } -} diff --git a/src/core/component/context.rs b/src/core/component/context.rs index 7d10d8d5..ed47ff56 100644 --- a/src/core/component/context.rs +++ b/src/core/component/context.rs @@ -2,7 +2,9 @@ use crate::base::component::add_base_assets; use crate::core::component::AnyOp; use crate::core::theme::all::{theme_by_single_name, THEME_DEFAULT}; use crate::core::theme::{ComponentsInRegions, ThemeRef}; -use crate::html::{html, Assets, HeadScript, HeadStyles, JavaScript, Markup, StyleSheet}; +use crate::html::{html, Markup}; +use crate::html::{Assets, HeadScript, HeadStyles, JavaScript, StyleSheet}; +use crate::html::{ClassesOp, OptionClasses, OptionId}; use crate::locale::{LanguageIdentifier, LANGID_DEFAULT}; use crate::service::HttpRequest; use crate::{concat_string, util}; @@ -32,17 +34,20 @@ pub enum AssetsOp { #[rustfmt::skip] pub struct Context { - request : HttpRequest, - langid : &'static LanguageIdentifier, - theme : ThemeRef, - layout : &'static str, - stylesheet: Assets, // Stylesheets. - headstyles: Assets, // Styles in head. - javascript: Assets, // JavaScripts. - headscript: Assets, // Scripts in head. - regions : ComponentsInRegions, - params : HashMap<&'static str, String>, - id_counter: usize, + request : HttpRequest, + langid : &'static LanguageIdentifier, + theme : ThemeRef, + layout : &'static str, + stylesheet : Assets, // Stylesheets. + headstyles : Assets, // Styles in head. + javascript : Assets, // JavaScripts. + headscript : Assets, // Scripts in head. + body_id : OptionId, + body_classes: OptionClasses, + body_skip_to: OptionId, + regions : ComponentsInRegions, + params : HashMap<&'static str, String>, + id_counter : usize, } impl Context { @@ -50,16 +55,19 @@ impl Context { pub(crate) fn new(request: HttpRequest) -> Self { Context { request, - langid : &LANGID_DEFAULT, - theme : *THEME_DEFAULT, - layout : "default", - stylesheet: Assets::::new(), // Stylesheets. - headstyles: Assets::::new(), // Styles in head. - javascript: Assets::::new(), // JavaScripts. - headscript: Assets::::new(), // Scripts in head. - regions : ComponentsInRegions::default(), - params : HashMap::<&str, String>::new(), - id_counter: 0, + langid : &LANGID_DEFAULT, + theme : *THEME_DEFAULT, + layout : "default", + stylesheet : Assets::::new(), // Stylesheets. + headstyles : Assets::::new(), // Styles in head. + javascript : Assets::::new(), // JavaScripts. + headscript : Assets::::new(), // Scripts in head. + body_id : OptionId::default(), + body_classes: OptionClasses::default(), + body_skip_to: OptionId::default(), + regions : ComponentsInRegions::default(), + params : HashMap::<&str, String>::new(), + id_counter : 0, } } @@ -95,6 +103,21 @@ impl Context { self } + pub fn alter_body_id(&mut self, id: impl Into) -> &mut Self { + self.body_id.alter_value(id); + self + } + + pub fn alter_body_classes(&mut self, op: ClassesOp, classes: impl Into) -> &mut Self { + self.body_classes.alter_value(op, classes); + self + } + + pub fn alter_body_skip_to(&mut self, id: impl Into) -> &mut Self { + self.body_skip_to.alter_value(id); + self + } + pub fn alter_regions(&mut self, region: &'static str, op: AnyOp) -> &mut Self { self.regions.alter_components(region, op); self @@ -128,6 +151,18 @@ impl Context { self.layout } + pub fn body_id(&self) -> &OptionId { + &self.body_id + } + + pub fn body_classes(&self) -> &OptionClasses { + &self.body_classes + } + + pub fn body_skip_to(&self) -> &OptionId { + &self.body_skip_to + } + pub fn regions(&self) -> &ComponentsInRegions { &self.regions } @@ -143,7 +178,7 @@ impl Context { /// Context PREPARE. - pub fn prepare_assets(&mut self) -> Markup { + pub(crate) fn prepare_assets(&mut self) -> Markup { html! { (self.stylesheet.prepare()) // Stylesheets. (self.headstyles.prepare()) // Styles in head. @@ -152,8 +187,10 @@ impl Context { } } - pub fn prepare_region(&mut self, region: &str) -> Markup { - self.regions.all_components(self.theme, region).render(self) + pub(crate) fn prepare_region(&mut self, region: impl Into) -> Markup { + self.regions + .all_components(self.theme, region.into().as_str()) + .render(self) } // Context EXTRAS. diff --git a/src/core/theme/definition.rs b/src/core/theme/definition.rs index 3e42e4f2..187412b7 100644 --- a/src/core/theme/definition.rs +++ b/src/core/theme/definition.rs @@ -1,10 +1,10 @@ use crate::base::component::*; -use crate::core::component::{ComponentBase, ComponentClassesOp, ComponentTrait}; +use crate::config; +use crate::core::component::{ComponentBase, ComponentTrait}; use crate::core::package::PackageTrait; -use crate::html::{html, ClassesOp, Favicon, Markup}; +use crate::html::{html, Favicon, Markup}; use crate::locale::L10n; use crate::response::page::Page; -use crate::{concat_string, config}; pub type ThemeRef = &'static dyn ThemeTrait; @@ -26,51 +26,38 @@ pub trait ThemeTrait: PackageTrait + Send + Sync { fn before_prepare_body(&self, page: &mut Page) {} fn prepare_body(&self, page: &mut Page) -> Markup { - let skip_to_id = concat_string!("#", page.skip_to().get().unwrap_or("content".to_owned())); - - flex::Container::body() - .with_id(page.body_id().get().unwrap_or_default()) - .with_classes(ClassesOp::Add, page.body_classes().get().unwrap_or_default()) - .add_item(flex::Item::bundle() - .add_component(Html::with(html! { - @if let Some(skip) = L10n::l("skip_to_content").using(page.context().langid()) { - div class="skip__to_content" { - a href=(skip_to_id) { (skip) } - } - } - })) - .add_component(flex::Container::new() - .with_id("body__wrapper") - .with_direction(flex::Direction::Column(BreakPoint::None)) - .with_align(flex::Align::Center) - .add_item(flex::Item::with(flex::Region::named("header")).with_id("header")) - .add_item(flex::Item::with(flex::Region::named("pagetop")).with_id("pagetop")) - .add_item( - flex::Item::with( - flex::Container::new() - .with_direction(flex::Direction::Row(BreakPoint::None)) - .add_item( - flex::Item::with(flex::Region::named("sidebar_left")) - .with_id("sidebar_left") - .with_grow(flex::Grow::Is1), - ) - .add_item( - flex::Item::with(flex::Region::named("content")) - .with_id("content") - .with_grow(flex::Grow::Is3), - ) - .add_item( - flex::Item::with(flex::Region::named("sidebar_right")) - .with_id("sidebar_right") - .with_grow(flex::Grow::Is1), - ), - ) - .with_id("flex__wrapper"), + Body::with( + flex::Container::new() + .with_id("body__wrapper") + .with_direction(flex::Direction::Column(BreakPoint::None)) + .with_align(flex::Align::Center) + .add_item(flex::Item::region().with_id("header")) + .add_item(flex::Item::region().with_id("pagetop")) + .add_item( + flex::Item::with( + flex::Container::new() + .with_direction(flex::Direction::Row(BreakPoint::None)) + .add_item( + flex::Item::region() + .with_id("sidebar_left") + .with_grow(flex::Grow::Is1), + ) + .add_item( + flex::Item::region() + .with_id("content") + .with_grow(flex::Grow::Is3), + ) + .add_item( + flex::Item::region() + .with_id("sidebar_right") + .with_grow(flex::Grow::Is1), + ), ) - .add_item(flex::Item::with(flex::Region::named("footer")).with_id("footer")), + .with_id("flex__wrapper"), ) - ) - .render(page.context()) + .add_item(flex::Item::region().with_id("footer")), + ) + .render(page.context()) } fn after_prepare_body(&self, page: &mut Page) { diff --git a/src/response/page.rs b/src/response/page.rs index 99e8fe46..e8f6e1d1 100644 --- a/src/response/page.rs +++ b/src/response/page.rs @@ -7,8 +7,7 @@ use crate::base::action; use crate::core::component::{AnyComponent, AnyOp, ComponentTrait}; use crate::core::component::{AssetsOp, Context}; use crate::fn_builder; -use crate::html::{html, Markup, DOCTYPE}; -use crate::html::{ClassesOp, Favicon, OptionClasses, OptionId, OptionTranslated}; +use crate::html::{html, ClassesOp, Favicon, Markup, OptionTranslated, DOCTYPE}; use crate::locale::L10n; use crate::service::HttpRequest; @@ -16,30 +15,24 @@ use unic_langid::CharacterDirection; #[rustfmt::skip] pub struct Page { - title : OptionTranslated, - description : OptionTranslated, - metadata : Vec<(&'static str, &'static str)>, - properties : Vec<(&'static str, &'static str)>, - favicon : Option, - context : Context, - body_id : OptionId, - body_classes: OptionClasses, - skip_to : OptionId, + title : OptionTranslated, + description: OptionTranslated, + metadata : Vec<(&'static str, &'static str)>, + properties : Vec<(&'static str, &'static str)>, + favicon : Option, + context : Context, } impl Page { #[rustfmt::skip] pub fn new(request: HttpRequest) -> Self { Page { - title : OptionTranslated::default(), - description : OptionTranslated::default(), - metadata : Vec::default(), - properties : Vec::default(), - favicon : None, - context : Context::new(request), - body_id : OptionId::default(), - body_classes: OptionClasses::default(), - skip_to : OptionId::default(), + title : OptionTranslated::default(), + description: OptionTranslated::default(), + metadata : Vec::default(), + properties : Vec::default(), + favicon : None, + context : Context::new(request), } } @@ -83,19 +76,19 @@ impl Page { #[fn_builder] pub fn alter_body_id(&mut self, id: impl Into) -> &mut Self { - self.body_id.alter_value(id); + self.context.alter_body_id(id); self } #[fn_builder] pub fn alter_body_classes(&mut self, op: ClassesOp, classes: impl Into) -> &mut Self { - self.body_classes.alter_value(op, classes); + self.context.alter_body_classes(op, classes); self } #[fn_builder] - pub fn alter_skip_to(&mut self, id: impl Into) -> &mut Self { - self.skip_to.alter_value(id); + pub fn alter_body_skip_to(&mut self, id: impl Into) -> &mut Self { + self.context.alter_body_skip_to(id); self } @@ -153,18 +146,6 @@ impl Page { &mut self.context } - pub fn body_id(&self) -> &OptionId { - &self.body_id - } - - pub fn body_classes(&self) -> &OptionClasses { - &self.body_classes - } - - pub fn skip_to(&self) -> &OptionId { - &self.skip_to - } - // Page RENDER. pub fn render(&mut self) -> ResultPage {