🚧 Working on regions layout

This commit is contained in:
Manuel Cillero 2024-03-28 23:17:15 +01:00
parent 625d16c0f2
commit ee5742e0b2
11 changed files with 264 additions and 220 deletions

View file

@ -43,43 +43,23 @@ impl ThemeTrait for Bootsier {
} }
fn prepare_body(&self, page: &mut Page) -> Markup { fn prepare_body(&self, page: &mut Page) -> Markup {
let skip_to_id = concat_string!("#", page.skip_to().get().unwrap_or("content".to_owned())); Body::with(match page.context().layout() {
"admin" => flex::Container::new()
flex::Container::body() .add_item(flex::Item::region().with_id("top-menu"))
.with_id(page.body_id().get().unwrap_or_default()) .add_item(flex::Item::region().with_id("side-menu"))
.with_classes(ClassesOp::Add, page.body_classes().get().unwrap_or_default()) .add_item(flex::Item::region().with_id("content")),
.add_item(flex::Item::bundle() _ => flex::Container::new()
.add_component(Html::with(html! { .add_item(flex::Item::region().with_id("header"))
@if let Some(skip) = L10n::l("skip_to_content").using(page.context().langid()) { .add_item(flex::Item::region().with_id("nav_branding"))
div class="skip__to_content" { .add_item(flex::Item::region().with_id("nav_main"))
a href=(skip_to_id) { (skip) } .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_component( .add_item(flex::Item::region().with_id("sidebar_second"))
match page.context().layout() { .add_item(flex::Item::region().with_id("footer")),
"admin" => flex::Container::new().add_item( })
flex::Item::new() .render(page.context())
.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())
} }
fn after_prepare_body(&self, page: &mut Page) { fn after_prepare_body(&self, page: &mut Page) {

View file

@ -4,5 +4,11 @@ pub use html::Html;
mod fluent; mod fluent;
pub use fluent::Fluent; pub use fluent::Fluent;
mod body;
pub use body::Body;
mod components; mod components;
pub use components::Components; pub use components::Components;
mod region;
pub use region::Region;

View file

@ -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
}
}

View file

@ -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<String> {
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<String>) -> &mut Self {
self.0.alter_value(id);
self
}
}

View file

@ -4,9 +4,6 @@ pub use container::Container;
mod item; mod item;
pub use item::Item; pub use item::Item;
mod region;
pub use region::Region;
use crate::prelude::*; use crate::prelude::*;
// ************************************************************************************************* // *************************************************************************************************

View file

@ -4,7 +4,6 @@ use crate::prelude::*;
pub enum ContainerType { pub enum ContainerType {
#[default] #[default]
Default, Default,
Body,
Header, Header,
Main, Main,
Section, Section,
@ -77,11 +76,6 @@ impl ComponentTrait for Container {
(output) (output)
} }
}), }),
ContainerType::Body => PrepareMarkup::With(html! {
body id=[self.id()] class=[self.classes().get()] style=[gap] {
(output)
}
}),
ContainerType::Header => PrepareMarkup::With(html! { ContainerType::Header => PrepareMarkup::With(html! {
header id=[self.id()] class=[self.classes().get()] style=[gap] { header id=[self.id()] class=[self.classes().get()] style=[gap] {
(output) (output)
@ -112,13 +106,6 @@ impl ComponentTrait for Container {
} }
impl Container { impl Container {
pub fn body() -> Self {
Container {
container_type: ContainerType::Body,
..Default::default()
}
}
pub fn header() -> Self { pub fn header() -> Self {
Container { Container {
container_type: ContainerType::Header, container_type: ContainerType::Header,
@ -210,7 +197,6 @@ impl Container {
self self
} }
#[rustfmt::skip]
pub fn add_item(mut self, item: flex::Item) -> Self { pub fn add_item(mut self, item: flex::Item) -> Self {
self.items.alter_value(AnyOp::Add(AnyComponent::with(item))); self.items.alter_value(AnyOp::Add(AnyComponent::with(item)));
self self

View file

@ -4,6 +4,7 @@ use crate::prelude::*;
pub enum ItemType { pub enum ItemType {
#[default] #[default]
Default, Default,
Region,
Wrapper, Wrapper,
Bundle, Bundle,
} }
@ -57,36 +58,60 @@ impl ComponentTrait for Item {
} }
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let output = self.components().render(cx); let (output, region) = match self.item_type() {
if !output.is_empty() { ItemType::Region => (
let order = match self.weight() { self.components().render(cx),
0 => None, if let Some(id) = self.id() {
_ => Some(concat_string!("order: ", self.weight().to_string(), ";")), cx.prepare_region(id)
}; } else {
match self.item_type() { Markup::default()
ItemType::Default => PrepareMarkup::With(html! { },
div id=[self.id()] class=[self.classes().get()] style=[order] { ),
div class="flex__content" { _ => (self.components().render(cx), Markup::default()),
(output) };
} if output.is_empty() && region.is_empty() {
} return PrepareMarkup::None;
}), }
ItemType::Wrapper => PrepareMarkup::With(html! { let order = match self.weight() {
div id=[self.id()] class=[self.classes().get()] style=[order] { 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) (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) (output)
}), }
} }),
} else { ItemType::Bundle => PrepareMarkup::With(html! {
PrepareMarkup::None (output)
}),
} }
} }
} }
impl Item { impl Item {
pub fn region() -> Self {
Item {
item_type: ItemType::Region,
..Default::default()
}
}
pub fn wrapper() -> Self { pub fn wrapper() -> Self {
Item { Item {
item_type: ItemType::Wrapper, item_type: ItemType::Wrapper,

View file

@ -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<String>) -> Self {
Region::new().with_name(name)
}
// Region BUILDER.
#[fn_builder]
pub fn alter_name(&mut self, name: impl Into<String>) -> &mut Self {
self.0.alter_value(name);
self
}
// Region GETTERS.
pub fn name(&self) -> &OptionId {
&self.0
}
}

View file

@ -2,7 +2,9 @@ use crate::base::component::add_base_assets;
use crate::core::component::AnyOp; use crate::core::component::AnyOp;
use crate::core::theme::all::{theme_by_single_name, THEME_DEFAULT}; use crate::core::theme::all::{theme_by_single_name, THEME_DEFAULT};
use crate::core::theme::{ComponentsInRegions, ThemeRef}; 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::locale::{LanguageIdentifier, LANGID_DEFAULT};
use crate::service::HttpRequest; use crate::service::HttpRequest;
use crate::{concat_string, util}; use crate::{concat_string, util};
@ -32,17 +34,20 @@ pub enum AssetsOp {
#[rustfmt::skip] #[rustfmt::skip]
pub struct Context { pub struct Context {
request : HttpRequest, request : HttpRequest,
langid : &'static LanguageIdentifier, langid : &'static LanguageIdentifier,
theme : ThemeRef, theme : ThemeRef,
layout : &'static str, layout : &'static str,
stylesheet: Assets<StyleSheet>, // Stylesheets. stylesheet : Assets<StyleSheet>, // Stylesheets.
headstyles: Assets<HeadStyles>, // Styles in head. headstyles : Assets<HeadStyles>, // Styles in head.
javascript: Assets<JavaScript>, // JavaScripts. javascript : Assets<JavaScript>, // JavaScripts.
headscript: Assets<HeadScript>, // Scripts in head. headscript : Assets<HeadScript>, // Scripts in head.
regions : ComponentsInRegions, body_id : OptionId,
params : HashMap<&'static str, String>, body_classes: OptionClasses,
id_counter: usize, body_skip_to: OptionId,
regions : ComponentsInRegions,
params : HashMap<&'static str, String>,
id_counter : usize,
} }
impl Context { impl Context {
@ -50,16 +55,19 @@ impl Context {
pub(crate) fn new(request: HttpRequest) -> Self { pub(crate) fn new(request: HttpRequest) -> Self {
Context { Context {
request, request,
langid : &LANGID_DEFAULT, langid : &LANGID_DEFAULT,
theme : *THEME_DEFAULT, theme : *THEME_DEFAULT,
layout : "default", layout : "default",
stylesheet: Assets::<StyleSheet>::new(), // Stylesheets. stylesheet : Assets::<StyleSheet>::new(), // Stylesheets.
headstyles: Assets::<HeadStyles>::new(), // Styles in head. headstyles : Assets::<HeadStyles>::new(), // Styles in head.
javascript: Assets::<JavaScript>::new(), // JavaScripts. javascript : Assets::<JavaScript>::new(), // JavaScripts.
headscript: Assets::<HeadScript>::new(), // Scripts in head. headscript : Assets::<HeadScript>::new(), // Scripts in head.
regions : ComponentsInRegions::default(), body_id : OptionId::default(),
params : HashMap::<&str, String>::new(), body_classes: OptionClasses::default(),
id_counter: 0, body_skip_to: OptionId::default(),
regions : ComponentsInRegions::default(),
params : HashMap::<&str, String>::new(),
id_counter : 0,
} }
} }
@ -95,6 +103,21 @@ impl Context {
self self
} }
pub fn alter_body_id(&mut self, id: impl Into<String>) -> &mut Self {
self.body_id.alter_value(id);
self
}
pub fn alter_body_classes(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self {
self.body_classes.alter_value(op, classes);
self
}
pub fn alter_body_skip_to(&mut self, id: impl Into<String>) -> &mut Self {
self.body_skip_to.alter_value(id);
self
}
pub fn alter_regions(&mut self, region: &'static str, op: AnyOp) -> &mut Self { pub fn alter_regions(&mut self, region: &'static str, op: AnyOp) -> &mut Self {
self.regions.alter_components(region, op); self.regions.alter_components(region, op);
self self
@ -128,6 +151,18 @@ impl Context {
self.layout 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 { pub fn regions(&self) -> &ComponentsInRegions {
&self.regions &self.regions
} }
@ -143,7 +178,7 @@ impl Context {
/// Context PREPARE. /// Context PREPARE.
pub fn prepare_assets(&mut self) -> Markup { pub(crate) fn prepare_assets(&mut self) -> Markup {
html! { html! {
(self.stylesheet.prepare()) // Stylesheets. (self.stylesheet.prepare()) // Stylesheets.
(self.headstyles.prepare()) // Styles in head. (self.headstyles.prepare()) // Styles in head.
@ -152,8 +187,10 @@ impl Context {
} }
} }
pub fn prepare_region(&mut self, region: &str) -> Markup { pub(crate) fn prepare_region(&mut self, region: impl Into<String>) -> Markup {
self.regions.all_components(self.theme, region).render(self) self.regions
.all_components(self.theme, region.into().as_str())
.render(self)
} }
// Context EXTRAS. // Context EXTRAS.

View file

@ -1,10 +1,10 @@
use crate::base::component::*; 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::core::package::PackageTrait;
use crate::html::{html, ClassesOp, Favicon, Markup}; use crate::html::{html, Favicon, Markup};
use crate::locale::L10n; use crate::locale::L10n;
use crate::response::page::Page; use crate::response::page::Page;
use crate::{concat_string, config};
pub type ThemeRef = &'static dyn ThemeTrait; 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 before_prepare_body(&self, page: &mut Page) {}
fn prepare_body(&self, page: &mut Page) -> Markup { fn prepare_body(&self, page: &mut Page) -> Markup {
let skip_to_id = concat_string!("#", page.skip_to().get().unwrap_or("content".to_owned())); Body::with(
flex::Container::new()
flex::Container::body() .with_id("body__wrapper")
.with_id(page.body_id().get().unwrap_or_default()) .with_direction(flex::Direction::Column(BreakPoint::None))
.with_classes(ClassesOp::Add, page.body_classes().get().unwrap_or_default()) .with_align(flex::Align::Center)
.add_item(flex::Item::bundle() .add_item(flex::Item::region().with_id("header"))
.add_component(Html::with(html! { .add_item(flex::Item::region().with_id("pagetop"))
@if let Some(skip) = L10n::l("skip_to_content").using(page.context().langid()) { .add_item(
div class="skip__to_content" { flex::Item::with(
a href=(skip_to_id) { (skip) } flex::Container::new()
} .with_direction(flex::Direction::Row(BreakPoint::None))
} .add_item(
})) flex::Item::region()
.add_component(flex::Container::new() .with_id("sidebar_left")
.with_id("body__wrapper") .with_grow(flex::Grow::Is1),
.with_direction(flex::Direction::Column(BreakPoint::None)) )
.with_align(flex::Align::Center) .add_item(
.add_item(flex::Item::with(flex::Region::named("header")).with_id("header")) flex::Item::region()
.add_item(flex::Item::with(flex::Region::named("pagetop")).with_id("pagetop")) .with_id("content")
.add_item( .with_grow(flex::Grow::Is3),
flex::Item::with( )
flex::Container::new() .add_item(
.with_direction(flex::Direction::Row(BreakPoint::None)) flex::Item::region()
.add_item( .with_id("sidebar_right")
flex::Item::with(flex::Region::named("sidebar_left")) .with_grow(flex::Grow::Is1),
.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"),
) )
.add_item(flex::Item::with(flex::Region::named("footer")).with_id("footer")), .with_id("flex__wrapper"),
) )
) .add_item(flex::Item::region().with_id("footer")),
.render(page.context()) )
.render(page.context())
} }
fn after_prepare_body(&self, page: &mut Page) { fn after_prepare_body(&self, page: &mut Page) {

View file

@ -7,8 +7,7 @@ use crate::base::action;
use crate::core::component::{AnyComponent, AnyOp, ComponentTrait}; use crate::core::component::{AnyComponent, AnyOp, ComponentTrait};
use crate::core::component::{AssetsOp, Context}; use crate::core::component::{AssetsOp, Context};
use crate::fn_builder; use crate::fn_builder;
use crate::html::{html, Markup, DOCTYPE}; use crate::html::{html, ClassesOp, Favicon, Markup, OptionTranslated, DOCTYPE};
use crate::html::{ClassesOp, Favicon, OptionClasses, OptionId, OptionTranslated};
use crate::locale::L10n; use crate::locale::L10n;
use crate::service::HttpRequest; use crate::service::HttpRequest;
@ -16,30 +15,24 @@ use unic_langid::CharacterDirection;
#[rustfmt::skip] #[rustfmt::skip]
pub struct Page { pub struct Page {
title : OptionTranslated, title : OptionTranslated,
description : OptionTranslated, description: OptionTranslated,
metadata : Vec<(&'static str, &'static str)>, metadata : Vec<(&'static str, &'static str)>,
properties : Vec<(&'static str, &'static str)>, properties : Vec<(&'static str, &'static str)>,
favicon : Option<Favicon>, favicon : Option<Favicon>,
context : Context, context : Context,
body_id : OptionId,
body_classes: OptionClasses,
skip_to : OptionId,
} }
impl Page { impl Page {
#[rustfmt::skip] #[rustfmt::skip]
pub fn new(request: HttpRequest) -> Self { pub fn new(request: HttpRequest) -> Self {
Page { Page {
title : OptionTranslated::default(), title : OptionTranslated::default(),
description : OptionTranslated::default(), description: OptionTranslated::default(),
metadata : Vec::default(), metadata : Vec::default(),
properties : Vec::default(), properties : Vec::default(),
favicon : None, favicon : None,
context : Context::new(request), context : Context::new(request),
body_id : OptionId::default(),
body_classes: OptionClasses::default(),
skip_to : OptionId::default(),
} }
} }
@ -83,19 +76,19 @@ impl Page {
#[fn_builder] #[fn_builder]
pub fn alter_body_id(&mut self, id: impl Into<String>) -> &mut Self { pub fn alter_body_id(&mut self, id: impl Into<String>) -> &mut Self {
self.body_id.alter_value(id); self.context.alter_body_id(id);
self self
} }
#[fn_builder] #[fn_builder]
pub fn alter_body_classes(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self { pub fn alter_body_classes(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self {
self.body_classes.alter_value(op, classes); self.context.alter_body_classes(op, classes);
self self
} }
#[fn_builder] #[fn_builder]
pub fn alter_skip_to(&mut self, id: impl Into<String>) -> &mut Self { pub fn alter_body_skip_to(&mut self, id: impl Into<String>) -> &mut Self {
self.skip_to.alter_value(id); self.context.alter_body_skip_to(id);
self self
} }
@ -153,18 +146,6 @@ impl Page {
&mut self.context &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. // Page RENDER.
pub fn render(&mut self) -> ResultPage<Markup, ErrorPage> { pub fn render(&mut self) -> ResultPage<Markup, ErrorPage> {