🎨 Cambia "Temas" (themes) por "Diseños" (layouts)

This commit is contained in:
Manuel Cillero 2024-12-04 20:24:54 +01:00
parent 0380aade57
commit 8e0a1d5994
32 changed files with 361 additions and 134 deletions

View file

@ -1,9 +1,9 @@
//! Base actions, components, packages, and themes.
//! Base actions, components, layouts, and packages.
pub mod action;
pub mod component;
pub mod package;
pub mod layout;
pub mod theme;
pub mod package;

View file

@ -2,8 +2,8 @@ use crate::prelude::*;
pub type FnActionWithComponent<C> = fn(component: &mut C, cx: &mut Context);
pub mod page;
pub mod theme;
pub mod component;
pub mod layout;
pub mod page;

View file

@ -4,13 +4,13 @@ use crate::base::action::FnActionWithComponent;
pub struct AfterPrepare<C: ComponentTrait> {
f: FnActionWithComponent<C>,
theme_type_id: Option<TypeId>,
layout_type_id: Option<TypeId>,
referer_type_id: Option<TypeId>,
}
impl<C: ComponentTrait> ActionTrait for AfterPrepare<C> {
fn theme_type_id(&self) -> Option<TypeId> {
self.theme_type_id
fn layout_type_id(&self) -> Option<TypeId> {
self.layout_type_id
}
fn referer_type_id(&self) -> Option<TypeId> {
@ -19,10 +19,10 @@ impl<C: ComponentTrait> ActionTrait for AfterPrepare<C> {
}
impl<C: ComponentTrait> AfterPrepare<C> {
pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self {
pub fn new(layout: LayoutRef, f: FnActionWithComponent<C>) -> Self {
AfterPrepare {
f,
theme_type_id: Some(theme.type_id()),
layout_type_id: Some(layout.type_id()),
referer_type_id: Some(TypeId::of::<C>()),
}
}
@ -33,7 +33,7 @@ impl<C: ComponentTrait> AfterPrepare<C> {
dispatch_actions(
&ActionKey::new(
TypeId::of::<Self>(),
Some(cx.theme().type_id()),
Some(cx.layout().type_id()),
Some(TypeId::of::<C>()),
None,
),

View file

@ -4,13 +4,13 @@ use crate::base::action::FnActionWithComponent;
pub struct BeforePrepare<C: ComponentTrait> {
f: FnActionWithComponent<C>,
theme_type_id: Option<TypeId>,
layout_type_id: Option<TypeId>,
referer_type_id: Option<TypeId>,
}
impl<C: ComponentTrait> ActionTrait for BeforePrepare<C> {
fn theme_type_id(&self) -> Option<TypeId> {
self.theme_type_id
fn layout_type_id(&self) -> Option<TypeId> {
self.layout_type_id
}
fn referer_type_id(&self) -> Option<TypeId> {
@ -19,10 +19,10 @@ impl<C: ComponentTrait> ActionTrait for BeforePrepare<C> {
}
impl<C: ComponentTrait> BeforePrepare<C> {
pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self {
pub fn new(layout: LayoutRef, f: FnActionWithComponent<C>) -> Self {
BeforePrepare {
f,
theme_type_id: Some(theme.type_id()),
layout_type_id: Some(layout.type_id()),
referer_type_id: Some(TypeId::of::<C>()),
}
}
@ -33,7 +33,7 @@ impl<C: ComponentTrait> BeforePrepare<C> {
dispatch_actions(
&ActionKey::new(
TypeId::of::<Self>(),
Some(cx.theme().type_id()),
Some(cx.layout().type_id()),
Some(TypeId::of::<C>()),
None,
),

View file

@ -4,13 +4,13 @@ pub type FnRenderComponent<C> = fn(component: &C, cx: &mut Context) -> Option<Ma
pub struct RenderComponent<C: ComponentTrait> {
f: FnRenderComponent<C>,
theme_type_id: Option<TypeId>,
layout_type_id: Option<TypeId>,
referer_type_id: Option<TypeId>,
}
impl<C: ComponentTrait> ActionTrait for RenderComponent<C> {
fn theme_type_id(&self) -> Option<TypeId> {
self.theme_type_id
fn layout_type_id(&self) -> Option<TypeId> {
self.layout_type_id
}
fn referer_type_id(&self) -> Option<TypeId> {
@ -19,10 +19,10 @@ impl<C: ComponentTrait> ActionTrait for RenderComponent<C> {
}
impl<C: ComponentTrait> RenderComponent<C> {
pub fn new(theme: ThemeRef, f: FnRenderComponent<C>) -> Self {
pub fn new(layout: LayoutRef, f: FnRenderComponent<C>) -> Self {
RenderComponent {
f,
theme_type_id: Some(theme.type_id()),
layout_type_id: Some(layout.type_id()),
referer_type_id: Some(TypeId::of::<C>()),
}
}
@ -34,7 +34,7 @@ impl<C: ComponentTrait> RenderComponent<C> {
dispatch_actions(
&ActionKey::new(
TypeId::of::<Self>(),
Some(cx.theme().type_id()),
Some(cx.layout().type_id()),
Some(TypeId::of::<C>()),
None,
),

View file

@ -3,9 +3,9 @@ use crate::prelude::*;
pub struct Basic;
impl PackageTrait for Basic {
fn theme(&self) -> Option<ThemeRef> {
fn layout(&self) -> Option<LayoutRef> {
Some(&Basic)
}
}
impl ThemeTrait for Basic {}
impl LayoutTrait for Basic {}

View file

@ -19,7 +19,7 @@ impl PackageTrait for Welcome {
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_title(L10n::l("welcome_page"))
.with_assets(AssetsOp::Theme("Basic"))
.with_assets(AssetsOp::Layout("Basic"))
.with_assets(AssetsOp::AddStyleSheet(StyleSheet::inline("styles", r##"
body {
background-color: #f3d060;

View file

@ -1,4 +1,4 @@
//! Key types and functions for creating actions, components, packages, and themes.
//! Key types and functions for creating actions, components, layouts, and packages.
use crate::util::TypeInfo;
@ -80,8 +80,8 @@ pub mod action;
// API to build new components.
pub mod component;
// API to add new layouts.
pub mod layout;
// API to add new features with packages.
pub mod package;
// API to add new layouts with themes.
pub mod theme;

View file

@ -6,7 +6,7 @@ pub type ActionBox = Box<dyn ActionTrait>;
#[derive(Eq, PartialEq, Hash)]
pub struct ActionKey {
action_type_id: TypeId,
theme_type_id: Option<TypeId>,
layout_type_id: Option<TypeId>,
referer_type_id: Option<TypeId>,
referer_id: Option<String>,
}
@ -14,13 +14,13 @@ pub struct ActionKey {
impl ActionKey {
pub fn new(
action_type_id: TypeId,
theme_type_id: Option<TypeId>,
layout_type_id: Option<TypeId>,
referer_type_id: Option<TypeId>,
referer_id: Option<String>,
) -> Self {
ActionKey {
action_type_id,
theme_type_id,
layout_type_id,
referer_type_id,
referer_id,
}
@ -32,7 +32,7 @@ pub trait ActionBase {
}
pub trait ActionTrait: ActionBase + AnyBase + Send + Sync {
fn theme_type_id(&self) -> Option<TypeId> {
fn layout_type_id(&self) -> Option<TypeId> {
None
}
@ -53,7 +53,7 @@ impl<A: ActionTrait> ActionBase for A {
fn key(&self) -> ActionKey {
ActionKey {
action_type_id: self.type_id(),
theme_type_id: self.theme_type_id(),
layout_type_id: self.layout_type_id(),
referer_type_id: self.referer_type_id(),
referer_id: self.referer_id(),
}

View file

@ -1,7 +1,7 @@
use crate::concat_string;
use crate::core::component::ChildOp;
use crate::core::theme::all::{theme_by_short_name, DEFAULT_THEME};
use crate::core::theme::{ChildrenInRegions, ThemeRef};
use crate::core::layout::all::{layout_by_short_name, DEFAULT_LAYOUT};
use crate::core::layout::{ChildrenInRegions, LayoutRef};
use crate::html::{html, Markup};
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
use crate::locale::{LanguageIdentifier, DEFAULT_LANGID};
@ -16,7 +16,6 @@ use std::fmt;
pub enum AssetsOp {
LangId(&'static LanguageIdentifier),
Theme(&'static str),
Layout(&'static str),
// Favicon.
SetFavicon(Option<Favicon>),
@ -50,8 +49,7 @@ impl Error for ErrorParam {}
pub struct Context {
request : HttpRequest,
langid : &'static LanguageIdentifier,
theme : ThemeRef,
layout : &'static str,
layout : LayoutRef,
favicon : Option<Favicon>,
stylesheet: Assets<StyleSheet>,
javascript: Assets<JavaScript>,
@ -66,8 +64,7 @@ impl Context {
Context {
request,
langid : &DEFAULT_LANGID,
theme : *DEFAULT_THEME,
layout : "default",
layout : *DEFAULT_LAYOUT,
favicon : None,
stylesheet: Assets::<StyleSheet>::new(),
javascript: Assets::<JavaScript>::new(),
@ -82,11 +79,8 @@ impl Context {
AssetsOp::LangId(langid) => {
self.langid = langid;
}
AssetsOp::Theme(theme_name) => {
self.theme = theme_by_short_name(theme_name).unwrap_or(*DEFAULT_THEME);
}
AssetsOp::Layout(layout) => {
self.layout = layout;
AssetsOp::Layout(layout_name) => {
self.layout = layout_by_short_name(layout_name).unwrap_or(*DEFAULT_LAYOUT);
}
// Favicon.
AssetsOp::SetFavicon(favicon) => {
@ -135,11 +129,7 @@ impl Context {
self.langid
}
pub fn theme(&self) -> ThemeRef {
self.theme
}
pub fn layout(&self) -> &str {
pub fn layout(&self) -> LayoutRef {
self.layout
}
@ -168,7 +158,7 @@ impl Context {
pub fn render_region(&mut self, region: impl Into<String>) -> Markup {
self.regions
.all_in_region(self.theme, &region.into())
.all_in_region(self.layout, &region.into())
.render(self)
}

View file

@ -40,20 +40,20 @@ impl<C: ComponentTrait> ComponentBase for C {
// Comprueba el componente antes de prepararlo.
self.setup_before_prepare(cx);
// Acciones del tema antes de preparar el componente.
action::theme::BeforePrepare::dispatch(self, cx);
// Acciones del diseño antes de preparar el componente.
action::layout::BeforePrepare::dispatch(self, cx);
// Acciones de los módulos antes de preparar el componente.
action::component::BeforePrepare::dispatch(self, cx);
// Renderiza el componente.
let markup = match action::theme::RenderComponent::dispatch(self, cx) {
let markup = match action::layout::RenderComponent::dispatch(self, cx) {
Some(html) => html,
None => self.prepare_component(cx).render(),
};
// Acciones del tema después de preparar el componente.
action::theme::AfterPrepare::dispatch(self, cx);
// Acciones del diseño después de preparar el componente.
action::layout::AfterPrepare::dispatch(self, cx);
// Acciones de los módulos después de preparar el componente.
action::component::AfterPrepare::dispatch(self, cx);

View file

@ -1,5 +1,5 @@
mod definition;
pub use definition::{ThemeRef, ThemeTrait};
pub use definition::{LayoutRef, LayoutTrait};
mod regions;
pub(crate) use regions::ChildrenInRegions;

View file

@ -1,31 +1,33 @@
use crate::core::theme::ThemeRef;
use crate::core::layout::LayoutRef;
use crate::global;
use std::sync::{LazyLock, RwLock};
// THEMES ******************************************************************************************
pub static THEMES: LazyLock<RwLock<Vec<ThemeRef>>> = LazyLock::new(|| RwLock::new(Vec::new()));
pub static LAYOUTS: LazyLock<RwLock<Vec<LayoutRef>>> = LazyLock::new(|| RwLock::new(Vec::new()));
// DEFAULT THEME ***********************************************************************************
pub static DEFAULT_THEME: LazyLock<ThemeRef> =
LazyLock::new(|| match theme_by_short_name(&global::SETTINGS.app.theme) {
Some(theme) => theme,
None => &crate::base::theme::Basic,
});
pub static DEFAULT_LAYOUT: LazyLock<LayoutRef> =
LazyLock::new(
|| match layout_by_short_name(&global::SETTINGS.app.layout) {
Some(layout) => layout,
None => &crate::base::layout::Basic,
},
);
// THEME BY NAME ***********************************************************************************
pub fn theme_by_short_name(short_name: &str) -> Option<ThemeRef> {
pub fn layout_by_short_name(short_name: &str) -> Option<LayoutRef> {
let short_name = short_name.to_lowercase();
match THEMES
match LAYOUTS
.read()
.unwrap()
.iter()
.find(|t| t.short_name().to_lowercase() == short_name)
{
Some(theme) => Some(*theme),
Some(layout) => Some(*layout),
_ => None,
}
}

View file

@ -4,10 +4,10 @@ use crate::html::{html, Markup};
use crate::locale::L10n;
use crate::response::page::Page;
pub type ThemeRef = &'static dyn ThemeTrait;
pub type LayoutRef = &'static dyn LayoutTrait;
/// Los temas deben implementar este "trait".
pub trait ThemeTrait: PackageTrait + Send + Sync {
/// Los diseños deben implementar este "trait".
pub trait LayoutTrait: PackageTrait + Send + Sync {
fn regions(&self) -> Vec<(&'static str, L10n)> {
vec![("content", L10n::l("content"))]
}

View file

@ -1,11 +1,11 @@
use crate::core::component::{ChildComponent, ChildOp, Children};
use crate::core::theme::ThemeRef;
use crate::core::layout::LayoutRef;
use crate::{fn_builder, AutoDefault, TypeId};
use std::collections::HashMap;
use std::sync::{LazyLock, RwLock};
static THEME_REGIONS: LazyLock<RwLock<HashMap<TypeId, ChildrenInRegions>>> =
static LAYOUT_REGIONS: LazyLock<RwLock<HashMap<TypeId, ChildrenInRegions>>> =
LazyLock::new(|| RwLock::new(HashMap::new()));
static COMMON_REGIONS: LazyLock<RwLock<ChildrenInRegions>> =
@ -33,9 +33,9 @@ impl ChildrenInRegions {
self
}
pub fn all_in_region(&self, theme: ThemeRef, region: &str) -> Children {
pub fn all_in_region(&self, layout: LayoutRef, region: &str) -> Children {
let common = COMMON_REGIONS.read().unwrap();
if let Some(r) = THEME_REGIONS.read().unwrap().get(&theme.type_id()) {
if let Some(r) = LAYOUT_REGIONS.read().unwrap().get(&layout.type_id()) {
Children::merge(&[common.0.get(region), self.0.get(region), r.0.get(region)])
} else {
Children::merge(&[common.0.get(region), self.0.get(region)])
@ -46,7 +46,7 @@ impl ChildrenInRegions {
pub enum InRegion {
Content,
Named(&'static str),
OfTheme(&'static str, ThemeRef),
OfLayout(&'static str, LayoutRef),
}
impl InRegion {
@ -64,12 +64,12 @@ impl InRegion {
.unwrap()
.set_in_region(name, ChildOp::Add(child));
}
InRegion::OfTheme(region, theme) => {
let mut regions = THEME_REGIONS.write().unwrap();
if let Some(r) = regions.get_mut(&theme.type_id()) {
InRegion::OfLayout(region, layout) => {
let mut regions = LAYOUT_REGIONS.write().unwrap();
if let Some(r) = regions.get_mut(&layout.type_id()) {
r.set_in_region(region, ChildOp::Add(child));
} else {
regions.insert(theme.type_id(), ChildrenInRegions::with(region, child));
regions.insert(layout.type_id(), ChildrenInRegions::with(region, child));
}
}
}

View file

@ -1,6 +1,6 @@
use crate::core::action::add_action;
use crate::core::layout::all::LAYOUTS;
use crate::core::package::PackageRef;
use crate::core::theme::all::THEMES;
use crate::{global, include_files, include_files_service, service, trace};
use std::sync::{LazyLock, RwLock};
@ -19,8 +19,8 @@ pub fn register_packages(root_package: Option<PackageRef>) {
// Initialize a list for packages to be enabled.
let mut enabled_list: Vec<PackageRef> = Vec::new();
// Add default theme to the enabled list.
add_to_enabled(&mut enabled_list, &crate::base::theme::Basic);
// Add default layout to the enabled list.
add_to_enabled(&mut enabled_list, &crate::base::layout::Basic);
// If a root package is provided, add it to the enabled list.
if let Some(package) = root_package {
@ -54,16 +54,16 @@ fn add_to_enabled(list: &mut Vec<PackageRef>, package: PackageRef) {
// Add the package itself to the enabled list.
list.push(package);
// Check if the package has an associated theme to register.
if let Some(theme) = package.theme() {
let mut registered_themes = THEMES.write().unwrap();
// Ensure the theme is not already registered to avoid duplicates.
if !registered_themes
// Check if the package has an associated layout to register.
if let Some(layout) = package.layout() {
let mut registered_layouts = LAYOUTS.write().unwrap();
// Ensure the layout is not already registered to avoid duplicates.
if !registered_layouts
.iter()
.any(|t| t.type_id() == theme.type_id())
.any(|t| t.type_id() == layout.type_id())
{
registered_themes.push(theme);
trace::debug!("Enabling \"{}\" theme", theme.short_name());
registered_layouts.push(layout);
trace::debug!("Enabling \"{}\" layout", layout.short_name());
}
} else {
trace::debug!("Enabling \"{}\" package", package.short_name());

View file

@ -1,5 +1,5 @@
use crate::core::action::ActionBox;
use crate::core::theme::ThemeRef;
use crate::core::layout::LayoutRef;
use crate::core::AnyBase;
use crate::locale::L10n;
use crate::{actions, service};
@ -16,7 +16,7 @@ pub trait PackageTrait: AnyBase + Send + Sync {
L10n::default()
}
fn theme(&self) -> Option<ThemeRef> {
fn layout(&self) -> Option<LayoutRef> {
None
}

View file

@ -8,7 +8,7 @@ include_config!(SETTINGS: Settings => [
// [app]
"app.name" => "My App",
"app.description" => "Developed with the amazing PageTop framework.",
"app.theme" => "",
"app.layout" => "",
"app.language" => "en-US",
"app.text_direction" => "ltr",
"app.startup_banner" => "Slant",
@ -50,9 +50,9 @@ pub struct App {
/// A brief description of the application.
/// Default: *"Developed with the amazing PageTop framework."*.
pub description: String,
/// Default theme.
/// Default layout.
/// Default: *""*.
pub theme: String,
pub layout: String,
/// Default language (localization).
/// Default: *"en-US"*.
pub language: String,

View file

@ -1,7 +1,7 @@
//! **OptionClasses** implements a *helper* for dynamically adding class names to components.
//!
//! This *helper* differentiates between default classes (generally associated with styles provided
//! by the theme) and user classes (for customizing components based on application styles).
//! by the layout) and user classes (for customizing components based on application styles).
//!
//! Classes can be added using [Add]. Operations to [Remove], [Replace] or [Toggle] a class, as well
//! as [Clear] all classes, are also provided.

View file

@ -15,17 +15,17 @@
//! </div>
//!
//! The `PageTop` core API provides a comprehensive toolkit for extending its functionalities to
//! specific requirements and application scenarios through actions, components, packages, and
//! themes:
//! specific requirements and application scenarios through actions, components, layouts, and
//! packages:
//!
//! * **Actions** serve as a mechanism to customize `PageTop`'s internal behavior by intercepting
//! its execution flow.
//! * **Components** encapsulate HTML, CSS, and JavaScript into functional, configurable, and
//! well-defined units.
//! * **Layouts** enable developers to alter the appearance of pages and components without
//! affecting their functionality.
//! * **Packages** extend or customize existing functionality by interacting with `PageTop` APIs
//! or third-party package APIs.
//! * **Themes** enable developers to alter the appearance of pages and components without
//! affecting their functionality.
//!
//! # ⚡️ Quick start
//!
@ -105,11 +105,11 @@ pub mod locale;
pub mod datetime;
// Essential web framework.
pub mod service;
// Key types and functions for creating actions, components, packages, and themes.
// Key types and functions for creating actions, components, layouts, and packages.
pub mod core;
// Web request response variants.
pub mod response;
// Base actions, components, packages, and themes.
// Base actions, components, layouts, and packages.
pub mod base;
// Prepare and run the application.
pub mod app;

View file

@ -40,13 +40,13 @@ pub use crate::core::{AnyBase, AnyTo};
pub use crate::core::action::*;
pub use crate::core::component::*;
pub use crate::core::layout::*;
pub use crate::core::package::*;
pub use crate::core::theme::*;
pub use crate::response::{json::*, page::*, redirect::*, ResponseError};
pub use crate::base::action;
pub use crate::base::component::*;
pub use crate::base::theme;
pub use crate::base::layout;
pub use crate::app::Application;

View file

@ -156,23 +156,23 @@ impl Page {
// Page RENDER.
pub fn render(&mut self) -> ResultPage<Markup, ErrorPage> {
// Theme-specific operations before rendering the page body.
self.context.theme().before_render_body(self);
// Layout-specific operations before rendering the page body.
self.context.layout().before_render_body(self);
// Execute package actions before rendering the page body.
action::page::BeforeRenderBody::dispatch(self);
// Render the page body.
let body = self.context.theme().render_body(self);
let body = self.context.layout().render_body(self);
// Theme-specific operations after rendering the page body.
self.context.theme().after_render_body(self);
// Layout-specific operations after rendering the page body.
self.context.layout().after_render_body(self);
// Execute package actions after rendering the page body.
action::page::AfterRenderBody::dispatch(self);
// Render the page head.
let head = self.context.theme().render_head(self);
let head = self.context.layout().render_head(self);
// Render the full page with language and direction attributes.
let lang = &self.context.langid().language;