🧑‍💻 Improve usage of stylesheets and JavaScript

This commit is contained in:
Manuel Cillero 2024-08-04 18:00:34 +02:00
parent c83ae3c451
commit 5fb0a1332e
12 changed files with 126 additions and 172 deletions

View file

@ -14,24 +14,24 @@ pub(crate) fn add_base_assets(cx: &mut Context) {
let weight = cx.get_param::<Weight>(PARAM_BASE_WEIGHT).unwrap_or(-90); let weight = cx.get_param::<Weight>(PARAM_BASE_WEIGHT).unwrap_or(-90);
cx.set_assets(AssetsOp::AddStyleSheet( cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/root.css") StyleSheet::from("/base/css/root.css")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(weight), .with_weight(weight),
)) ))
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/looks.css") StyleSheet::from("/base/css/looks.css")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(weight), .with_weight(weight),
)) ))
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/buttons.css") StyleSheet::from("/base/css/buttons.css")
.with_version("0.0.2") .with_version("0.0.2")
.with_weight(weight), .with_weight(weight),
)); ));
if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_ICONS) { if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_ICONS) {
cx.set_assets(AssetsOp::AddStyleSheet( cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/icons.min.css") StyleSheet::from("/base/css/icons.min.css")
.with_version("1.11.1") .with_version("1.11.1")
.with_weight(weight), .with_weight(weight),
)); ));
@ -39,7 +39,7 @@ pub(crate) fn add_base_assets(cx: &mut Context) {
if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_FLEX_ASSETS) { if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_FLEX_ASSETS) {
cx.set_assets(AssetsOp::AddStyleSheet( cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/flex.css") StyleSheet::from("/base/css/flex.css")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(weight), .with_weight(weight),
)); ));
@ -47,12 +47,12 @@ pub(crate) fn add_base_assets(cx: &mut Context) {
if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_MENU_ASSETS) { if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_MENU_ASSETS) {
cx.set_assets(AssetsOp::AddStyleSheet( cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/menu.css") StyleSheet::from("/base/css/menu.css")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(weight), .with_weight(weight),
)) ))
.set_assets(AssetsOp::AddJavaScript( .set_assets(AssetsOp::AddJavaScript(
JavaScript::at("/base/js/menu.js") JavaScript::defer("/base/js/menu.js")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(weight), .with_weight(weight),
)); ));

View file

@ -35,7 +35,7 @@ fn home(request: HttpRequest, lang: &'static LanguageIdentifier) -> ResultPage<M
Page::new(request) Page::new(request)
.with_title(L10n::l("welcome_title")) .with_title(L10n::l("welcome_title"))
.with_assets(AssetsOp::LangId(lang)) .with_assets(AssetsOp::LangId(lang))
.with_assets(AssetsOp::AddStyleSheet(StyleSheet::at( .with_assets(AssetsOp::AddStyleSheet(StyleSheet::from(
"/base/css/welcome.css", "/base/css/welcome.css",
))) )))
.with_body_id("welcome") .with_body_id("welcome")

View file

@ -16,13 +16,13 @@ impl ThemeTrait for Basic {
fn after_prepare_body(&self, page: &mut Page) { fn after_prepare_body(&self, page: &mut Page) {
page.set_favicon(Some(Favicon::new().with_icon("/base/favicon.ico"))) page.set_favicon(Some(Favicon::new().with_icon("/base/favicon.ico")))
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/normalize.min.css") StyleSheet::from("/base/css/normalize.min.css")
.with_version("8.0.1") .with_version("8.0.1")
.with_weight(-90), .with_weight(-90),
)) ))
.set_assets(AssetsOp::AddBaseAssets) .set_assets(AssetsOp::AddBaseAssets)
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/basic.css") StyleSheet::from("/base/css/basic.css")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(-90), .with_weight(-90),
)); ));

View file

@ -16,13 +16,13 @@ impl ThemeTrait for Chassis {
fn after_prepare_body(&self, page: &mut Page) { fn after_prepare_body(&self, page: &mut Page) {
page.set_favicon(Some(Favicon::new().with_icon("/base/favicon.ico"))) page.set_favicon(Some(Favicon::new().with_icon("/base/favicon.ico")))
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/normalize.min.css") StyleSheet::from("/base/css/normalize.min.css")
.with_version("8.0.1") .with_version("8.0.1")
.with_weight(-90), .with_weight(-90),
)) ))
.set_assets(AssetsOp::AddBaseAssets) .set_assets(AssetsOp::AddBaseAssets)
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/chassis.css") StyleSheet::from("/base/css/chassis.css")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(-90), .with_weight(-90),
)); ));

View file

@ -16,13 +16,13 @@ impl ThemeTrait for Inception {
fn after_prepare_body(&self, page: &mut Page) { fn after_prepare_body(&self, page: &mut Page) {
page.set_favicon(Some(Favicon::new().with_icon("/base/favicon.ico"))) page.set_favicon(Some(Favicon::new().with_icon("/base/favicon.ico")))
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/normalize.min.css") StyleSheet::from("/base/css/normalize.min.css")
.with_version("8.0.1") .with_version("8.0.1")
.with_weight(-90), .with_weight(-90),
)) ))
.set_assets(AssetsOp::AddBaseAssets) .set_assets(AssetsOp::AddBaseAssets)
.set_assets(AssetsOp::AddStyleSheet( .set_assets(AssetsOp::AddStyleSheet(
StyleSheet::at("/base/css/inception.css") StyleSheet::from("/base/css/inception.css")
.with_version("0.0.1") .with_version("0.0.1")
.with_weight(-90), .with_weight(-90),
)); ));

View file

@ -4,7 +4,7 @@ use crate::core::component::AnyOp;
use crate::core::theme::all::{theme_by_short_name, THEME_DEFAULT}; use crate::core::theme::all::{theme_by_short_name, THEME_DEFAULT};
use crate::core::theme::{ComponentsInRegions, ThemeRef}; use crate::core::theme::{ComponentsInRegions, ThemeRef};
use crate::html::{html, Markup}; use crate::html::{html, Markup};
use crate::html::{Assets, HeadScript, HeadStyles, JavaScript, StyleSheet}; use crate::html::{Assets, JavaScript, StyleSheet};
use crate::locale::{LanguageIdentifier, LANGID_DEFAULT}; use crate::locale::{LanguageIdentifier, LANGID_DEFAULT};
use crate::service::HttpRequest; use crate::service::HttpRequest;
use crate::util::TypeInfo; use crate::util::TypeInfo;
@ -22,15 +22,9 @@ pub enum AssetsOp {
// Stylesheets. // Stylesheets.
AddStyleSheet(StyleSheet), AddStyleSheet(StyleSheet),
RemoveStyleSheet(&'static str), RemoveStyleSheet(&'static str),
// Styles in head.
AddHeadStyles(HeadStyles),
RemoveHeadStyles(&'static str),
// JavaScripts. // JavaScripts.
AddJavaScript(JavaScript), AddJavaScript(JavaScript),
RemoveJavaScript(&'static str), RemoveJavaScript(&'static str),
// Scripts in head.
AddHeadScript(HeadScript),
RemoveHeadScript(&'static str),
// Add assets to properly use base components. // Add assets to properly use base components.
AddBaseAssets, AddBaseAssets,
} }
@ -58,10 +52,8 @@ pub struct Context {
langid : &'static LanguageIdentifier, langid : &'static LanguageIdentifier,
theme : ThemeRef, theme : ThemeRef,
layout : &'static str, layout : &'static str,
stylesheet: Assets<StyleSheet>, // Stylesheets. stylesheet: Assets<StyleSheet>,
headstyles: Assets<HeadStyles>, // Styles in head. javascript: Assets<JavaScript>,
javascript: Assets<JavaScript>, // JavaScripts.
headscript: Assets<HeadScript>, // Scripts in head.
regions : ComponentsInRegions, regions : ComponentsInRegions,
params : HashMap<&'static str, String>, params : HashMap<&'static str, String>,
id_counter: usize, id_counter: usize,
@ -75,10 +67,8 @@ impl Context {
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(),
headstyles: Assets::<HeadStyles>::new(), // Styles in head. javascript: Assets::<JavaScript>::new(),
javascript: Assets::<JavaScript>::new(), // JavaScripts.
headscript: Assets::<HeadScript>::new(), // Scripts in head.
regions : ComponentsInRegions::default(), regions : ComponentsInRegions::default(),
params : HashMap::<&str, String>::new(), params : HashMap::<&str, String>::new(),
id_counter: 0, id_counter: 0,
@ -101,15 +91,9 @@ impl Context {
// Stylesheets. // Stylesheets.
AssetsOp::AddStyleSheet(css) => { self.stylesheet.add(css); } AssetsOp::AddStyleSheet(css) => { self.stylesheet.add(css); }
AssetsOp::RemoveStyleSheet(path) => { self.stylesheet.remove(path); } AssetsOp::RemoveStyleSheet(path) => { self.stylesheet.remove(path); }
// Styles in head.
AssetsOp::AddHeadStyles(styles) => { self.headstyles.add(styles); }
AssetsOp::RemoveHeadStyles(path) => { self.headstyles.remove(path); }
// JavaScripts. // JavaScripts.
AssetsOp::AddJavaScript(js) => { self.javascript.add(js); } AssetsOp::AddJavaScript(js) => { self.javascript.add(js); }
AssetsOp::RemoveJavaScript(path) => { self.javascript.remove(path); } AssetsOp::RemoveJavaScript(path) => { self.javascript.remove(path); }
// Scripts in head.
AssetsOp::AddHeadScript(script) => { self.headscript.add(script); }
AssetsOp::RemoveHeadScript(path) => { self.headscript.remove(path); }
// Add assets to properly use base components. // Add assets to properly use base components.
AssetsOp::AddBaseAssets => { add_base_assets(self); } AssetsOp::AddBaseAssets => { add_base_assets(self); }
@ -160,10 +144,8 @@ impl Context {
pub(crate) fn prepare_assets(&mut self) -> Markup { pub(crate) fn prepare_assets(&mut self) -> Markup {
html! { html! {
(self.stylesheet.prepare()) // Stylesheets. (self.stylesheet.prepare())
(self.headstyles.prepare()) // Styles in head. (self.javascript.prepare())
(self.javascript.prepare()) // JavaScripts.
(self.headscript.prepare()) // Scripts in head.
} }
} }

View file

@ -4,11 +4,9 @@ mod maud;
pub use maud::{html, html_private, Markup, PreEscaped, DOCTYPE}; pub use maud::{html, html_private, Markup, PreEscaped, DOCTYPE};
mod assets; mod assets;
pub use assets::headscript::HeadScript; pub use assets::javascript::JavaScript;
pub use assets::headstyles::HeadStyles;
pub use assets::javascript::{JavaScript, ModeJS};
pub use assets::stylesheet::{StyleSheet, TargetMedia}; pub use assets::stylesheet::{StyleSheet, TargetMedia};
pub use assets::Assets; pub(crate) use assets::Assets;
mod favicon; mod favicon;
pub use favicon::Favicon; pub use favicon::Favicon;

View file

@ -1,5 +1,3 @@
pub mod headscript;
pub mod headstyles;
pub mod javascript; pub mod javascript;
pub mod stylesheet; pub mod stylesheet;
@ -7,7 +5,7 @@ use crate::html::{html, Markup};
use crate::{AutoDefault, Weight}; use crate::{AutoDefault, Weight};
pub trait AssetsTrait { pub trait AssetsTrait {
fn path(&self) -> &str; fn name(&self) -> &String;
fn weight(&self) -> Weight; fn weight(&self) -> Weight;
@ -15,7 +13,7 @@ pub trait AssetsTrait {
} }
#[derive(AutoDefault)] #[derive(AutoDefault)]
pub struct Assets<T>(Vec<T>); pub(crate) struct Assets<T>(Vec<T>);
impl<T: AssetsTrait> Assets<T> { impl<T: AssetsTrait> Assets<T> {
pub fn new() -> Self { pub fn new() -> Self {
@ -23,7 +21,7 @@ impl<T: AssetsTrait> Assets<T> {
} }
pub fn add(&mut self, asset: T) -> &mut Self { pub fn add(&mut self, asset: T) -> &mut Self {
match self.0.iter().position(|x| x.path() == asset.path()) { match self.0.iter().position(|x| x.name() == asset.name()) {
Some(index) => { Some(index) => {
if self.0[index].weight() > asset.weight() { if self.0[index].weight() > asset.weight() {
self.0.remove(index); self.0.remove(index);
@ -35,8 +33,8 @@ impl<T: AssetsTrait> Assets<T> {
self self
} }
pub fn remove(&mut self, path: &'static str) -> &mut Self { pub fn remove(&mut self, name: &'static str) -> &mut Self {
if let Some(index) = self.0.iter().position(|x| x.path() == path) { if let Some(index) = self.0.iter().position(|x| x.name() == name) {
self.0.remove(index); self.0.remove(index);
}; };
self self

View file

@ -1,44 +0,0 @@
use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup};
use crate::{AutoDefault, Weight};
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct HeadScript {
path : String,
code : String,
weight: Weight,
}
impl AssetsTrait for HeadScript {
fn path(&self) -> &str {
self.path.as_str()
}
fn weight(&self) -> Weight {
self.weight
}
fn prepare(&self) -> Markup {
html! { script { (self.code) }; }
}
}
impl HeadScript {
pub fn named(path: impl Into<String>) -> Self {
HeadScript {
path: path.into(),
..Default::default()
}
}
pub fn with_code(mut self, code: impl Into<String>) -> Self {
self.code = code.into().trim().to_owned();
self
}
pub fn with_weight(mut self, value: Weight) -> Self {
self.weight = value;
self
}
}

View file

@ -1,44 +0,0 @@
use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup};
use crate::{AutoDefault, Weight};
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct HeadStyles {
path : String,
styles: String,
weight: Weight,
}
impl AssetsTrait for HeadStyles {
fn path(&self) -> &str {
self.path.as_str()
}
fn weight(&self) -> Weight {
self.weight
}
fn prepare(&self) -> Markup {
html! { styles { (self.styles) }; }
}
}
impl HeadStyles {
pub fn named(path: impl Into<String>) -> Self {
HeadStyles {
path: path.into(),
..Default::default()
}
}
pub fn with_styles(mut self, styles: impl Into<String>) -> Self {
self.styles = styles.into().trim().to_owned();
self
}
pub fn with_weight(mut self, value: Weight) -> Self {
self.weight = value;
self
}
}

View file

@ -1,28 +1,35 @@
use crate::html::assets::AssetsTrait; use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup}; use crate::html::{html, Markup};
use crate::{AutoDefault, Weight}; use crate::{concat_string, AutoDefault, Weight};
#[derive(Default, Eq, PartialEq)] #[derive(AutoDefault)]
pub enum ModeJS { enum Source {
Async,
#[default] #[default]
Defer, From(String),
Normal, Defer(String),
Async(String),
Inline(String, String),
OnLoad(String, String),
} }
#[rustfmt::skip] #[rustfmt::skip]
#[derive(AutoDefault)] #[derive(AutoDefault)]
pub struct JavaScript { pub struct JavaScript {
path : String, source : Source,
prefix : &'static str, prefix : &'static str,
version: &'static str, version: &'static str,
weight : Weight, weight : Weight,
mode : ModeJS,
} }
impl AssetsTrait for JavaScript { impl AssetsTrait for JavaScript {
fn path(&self) -> &str { fn name(&self) -> &String {
self.path.as_str() match &self.source {
Source::From(path) => path,
Source::Defer(path) => path,
Source::Async(path) => path,
Source::Inline(name, _) => name,
Source::OnLoad(name, _) => name,
}
} }
fn weight(&self) -> Weight { fn weight(&self) -> Weight {
@ -30,20 +37,60 @@ impl AssetsTrait for JavaScript {
} }
fn prepare(&self) -> Markup { fn prepare(&self) -> Markup {
html! { match &self.source {
script type="text/javascript" Source::From(path) => html! {
src=(crate::concat_string!(self.path, self.prefix, self.version)) script src=(concat_string!(path, self.prefix, self.version)) {};
async[self.mode == ModeJS::Async] },
defer[self.mode == ModeJS::Defer] Source::Defer(path) => html! {
{}; script src=(concat_string!(path, self.prefix, self.version)) defer {};
},
Source::Async(path) => html! {
script src=(concat_string!(path, self.prefix, self.version)) async {};
},
Source::Inline(_, code) => html! {
script { (code) };
},
Source::OnLoad(_, code) => html! { (concat_string!(
"document.addEventListener('DOMContentLoaded',function(){",
code,
"});"
)) },
} }
} }
} }
impl JavaScript { impl JavaScript {
pub fn at(path: impl Into<String>) -> Self { pub fn from(path: impl Into<String>) -> Self {
JavaScript { JavaScript {
path: path.into(), source: Source::From(path.into()),
..Default::default()
}
}
pub fn defer(path: impl Into<String>) -> Self {
JavaScript {
source: Source::Defer(path.into()),
..Default::default()
}
}
pub fn asynchronous(path: impl Into<String>) -> Self {
JavaScript {
source: Source::Async(path.into()),
..Default::default()
}
}
pub fn inline(name: impl Into<String>, script: impl Into<String>) -> Self {
JavaScript {
source: Source::Inline(name.into(), script.into()),
..Default::default()
}
}
pub fn on_load(name: impl Into<String>, script: impl Into<String>) -> Self {
JavaScript {
source: Source::OnLoad(name.into(), script.into()),
..Default::default() ..Default::default()
} }
} }
@ -61,9 +108,4 @@ impl JavaScript {
self.weight = value; self.weight = value;
self self
} }
pub fn with_mode(mut self, mode: ModeJS) -> Self {
self.mode = mode;
self
}
} }

View file

@ -1,6 +1,13 @@
use crate::html::assets::AssetsTrait; use crate::html::assets::AssetsTrait;
use crate::html::{html, Markup}; use crate::html::{html, Markup};
use crate::{AutoDefault, Weight}; use crate::{concat_string, AutoDefault, Weight};
#[derive(AutoDefault)]
enum Source {
#[default]
From(String),
Inline(String, String),
}
pub enum TargetMedia { pub enum TargetMedia {
Default, Default,
@ -12,7 +19,7 @@ pub enum TargetMedia {
#[rustfmt::skip] #[rustfmt::skip]
#[derive(AutoDefault)] #[derive(AutoDefault)]
pub struct StyleSheet { pub struct StyleSheet {
path : String, source : Source,
prefix : &'static str, prefix : &'static str,
version: &'static str, version: &'static str,
media : Option<&'static str>, media : Option<&'static str>,
@ -20,8 +27,11 @@ pub struct StyleSheet {
} }
impl AssetsTrait for StyleSheet { impl AssetsTrait for StyleSheet {
fn path(&self) -> &str { fn name(&self) -> &String {
self.path.as_str() match &self.source {
Source::From(path) => path,
Source::Inline(name, _) => name,
}
} }
fn weight(&self) -> Weight { fn weight(&self) -> Weight {
@ -29,19 +39,31 @@ impl AssetsTrait for StyleSheet {
} }
fn prepare(&self) -> Markup { fn prepare(&self) -> Markup {
html! { match &self.source {
link Source::From(path) => html! {
rel="stylesheet" link
href=(crate::concat_string!(self.path, self.prefix, self.version)) rel="stylesheet"
media=[self.media]; href=(concat_string!(path, self.prefix, self.version))
media=[self.media];
},
Source::Inline(_, code) => html! {
styles { (code) };
},
} }
} }
} }
impl StyleSheet { impl StyleSheet {
pub fn at(path: impl Into<String>) -> Self { pub fn from(path: impl Into<String>) -> Self {
StyleSheet { StyleSheet {
path: path.into(), source: Source::From(path.into()),
..Default::default()
}
}
pub fn inline(name: impl Into<String>, styles: impl Into<String>) -> Self {
StyleSheet {
source: Source::Inline(name.into(), styles.into()),
..Default::default() ..Default::default()
} }
} }