Compare commits
No commits in common. "75eec8bebc996d1e9fb8b256bee1a3ddd58f7977" and "bf2c298d189e774edf263d9369764c28a6908217" have entirely different histories.
75eec8bebc
...
bf2c298d18
15 changed files with 74 additions and 218 deletions
11
src/app.rs
11
src/app.rs
|
@ -3,9 +3,6 @@
|
||||||
mod figfont;
|
mod figfont;
|
||||||
|
|
||||||
use crate::core::{extension, extension::ExtensionRef};
|
use crate::core::{extension, extension::ExtensionRef};
|
||||||
use crate::html::Markup;
|
|
||||||
use crate::response::page::{ErrorPage, ResultPage};
|
|
||||||
use crate::service::HttpRequest;
|
|
||||||
use crate::{global, locale, service, trace};
|
use crate::{global, locale, service, trace};
|
||||||
|
|
||||||
use actix_session::config::{BrowserSession, PersistentSession, SessionLifecycle};
|
use actix_session::config::{BrowserSession, PersistentSession, SessionLifecycle};
|
||||||
|
@ -173,12 +170,6 @@ impl Application {
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
> {
|
> {
|
||||||
service::App::new()
|
service::App::new().configure(extension::all::configure_services)
|
||||||
.configure(extension::all::configure_services)
|
|
||||||
.default_service(service::web::route().to(service_not_found))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn service_not_found(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
|
||||||
Err(ErrorPage::NotFound(request))
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub type ExtensionRef = &'static dyn Extension;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub trait Extension: AnyInfo + Send + Sync {
|
pub trait Extension: AnyInfo + Send + Sync {
|
||||||
/// Nombre localizado de la extensión legible para el usuario.
|
/// Nombre legible para el usuario.
|
||||||
///
|
///
|
||||||
/// Predeterminado por el [`short_name()`](AnyInfo::short_name) del tipo asociado a la
|
/// Predeterminado por el [`short_name()`](AnyInfo::short_name) del tipo asociado a la
|
||||||
/// extensión.
|
/// extensión.
|
||||||
|
@ -34,15 +34,18 @@ pub trait Extension: AnyInfo + Send + Sync {
|
||||||
L10n::n(self.short_name())
|
L10n::n(self.short_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Descripción corta localizada de la extensión para paneles, listados, etc.
|
/// Descripción corta para paneles, listados, etc.
|
||||||
fn description(&self) -> L10n {
|
fn description(&self) -> L10n {
|
||||||
L10n::default()
|
L10n::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve una referencia a esta misma extensión cuando se trata de un tema.
|
/// Los temas son extensiones que implementan [`Extension`] y también
|
||||||
|
/// [`Theme`](crate::core::theme::Theme).
|
||||||
///
|
///
|
||||||
/// Para ello, debe implementar [`Extension`] y también [`Theme`](crate::core::theme::Theme). Si
|
/// Si la extensión no es un tema, este método devuelve `None` por defecto.
|
||||||
/// la extensión no es un tema, este método devuelve `None` por defecto.
|
///
|
||||||
|
/// En caso contrario, este método debe implementarse para devolver una referencia de sí mismo
|
||||||
|
/// como tema. Por ejemplo:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
|
@ -78,7 +81,7 @@ pub trait Extension: AnyInfo + Send + Sync {
|
||||||
actions_boxed![]
|
actions_boxed![]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inicializa la extensión durante la fase de arranque de la aplicación.
|
/// Inicializa la extensión durante la lógica de arranque de la aplicación.
|
||||||
///
|
///
|
||||||
/// Se llama una sola vez, después de que todas las dependencias se han inicializado y antes de
|
/// Se llama una sola vez, después de que todas las dependencias se han inicializado y antes de
|
||||||
/// aceptar cualquier petición HTTP.
|
/// aceptar cualquier petición HTTP.
|
||||||
|
@ -101,8 +104,8 @@ pub trait Extension: AnyInfo + Send + Sync {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {}
|
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {}
|
||||||
|
|
||||||
/// Permite crear extensiones para deshabilitar y desinstalar recursos de otras de versiones
|
/// Permite crear extensiones para deshabilitar y desinstalar los recursos de otras extensiones
|
||||||
/// anteriores de la aplicación.
|
/// utilizadas en versiones anteriores de la aplicación.
|
||||||
///
|
///
|
||||||
/// Actualmente no se usa, pero se deja como *placeholder* para futuras implementaciones.
|
/// Actualmente no se usa, pero se deja como *placeholder* para futuras implementaciones.
|
||||||
fn drop_extensions(&self) -> Vec<ExtensionRef> {
|
fn drop_extensions(&self) -> Vec<ExtensionRef> {
|
||||||
|
|
|
@ -9,16 +9,18 @@
|
||||||
//! tipografías, espaciados y cualquier otro detalle visual o de comportamiento (como animaciones,
|
//! tipografías, espaciados y cualquier otro detalle visual o de comportamiento (como animaciones,
|
||||||
//! *scripts* de interfaz, etc.).
|
//! *scripts* de interfaz, etc.).
|
||||||
//!
|
//!
|
||||||
//! Los temas son extensiones que implementan [`Extension`](crate::core::extension::Extension); por
|
//! Es una extensión más (implementando [`Extension`](crate::core::extension::Extension)). Se
|
||||||
//! lo que se instancian, declaran sus dependencias y se inician igual que el resto de extensiones;
|
//! instala, activa y declara dependencias igual que el resto de extensiones; y se señala a sí misma
|
||||||
//! pero serán temas si además implementan [`theme()`](crate::core::extension::Extension::theme) y
|
//! como tema (implementando [`theme()`](crate::core::extension::Extension::theme) y [`Theme`]).
|
||||||
//! [`Theme`].
|
|
||||||
|
|
||||||
mod definition;
|
mod definition;
|
||||||
pub use definition::{Theme, ThemeRef};
|
pub use definition::{Theme, ThemeRef};
|
||||||
|
|
||||||
mod regions;
|
mod regions;
|
||||||
pub(crate) use regions::ChildrenInRegions;
|
pub(crate) use regions::ChildrenInRegions;
|
||||||
pub use regions::{InRegion, Region, REGION_CONTENT};
|
pub use regions::InRegion;
|
||||||
|
|
||||||
pub(crate) mod all;
|
pub(crate) mod all;
|
||||||
|
|
||||||
|
/// Nombre de la región por defecto: `content`.
|
||||||
|
pub const CONTENT_REGION_NAME: &str = "content";
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use crate::core::extension::Extension;
|
use crate::core::extension::Extension;
|
||||||
use crate::core::theme::Region;
|
use crate::core::theme::CONTENT_REGION_NAME;
|
||||||
use crate::global;
|
use crate::global;
|
||||||
use crate::html::{html, Markup};
|
use crate::html::{html, Markup};
|
||||||
use crate::locale::L10n;
|
use crate::locale::L10n;
|
||||||
use crate::response::page::Page;
|
use crate::response::page::Page;
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
/// Representa una referencia a un tema.
|
/// Representa una referencia a un tema.
|
||||||
///
|
///
|
||||||
/// Los temas son también extensiones. Por tanto se deben definir igual, es decir, como instancias
|
/// Los temas son también extensiones. Por tanto se deben definir igual, es decir, como instancias
|
||||||
|
@ -16,7 +14,7 @@ pub type ThemeRef = &'static dyn Theme;
|
||||||
/// Interfaz común que debe implementar cualquier tema de `PageTop`.
|
/// Interfaz común que debe implementar cualquier tema de `PageTop`.
|
||||||
///
|
///
|
||||||
/// Un tema implementará [`Theme`] y los métodos que sean necesarios de [`Extension`], aunque el
|
/// Un tema implementará [`Theme`] y los métodos que sean necesarios de [`Extension`], aunque el
|
||||||
/// único obligatorio será [`theme()`](Extension::theme).
|
/// único obligatorio es [`theme()`](Extension::theme).
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
|
@ -24,13 +22,8 @@ pub type ThemeRef = &'static dyn Theme;
|
||||||
/// pub struct MyTheme;
|
/// pub struct MyTheme;
|
||||||
///
|
///
|
||||||
/// impl Extension for MyTheme {
|
/// impl Extension for MyTheme {
|
||||||
/// fn name(&self) -> L10n {
|
/// fn name(&self) -> L10n { L10n::n("My theme") }
|
||||||
/// L10n::n("My theme")
|
/// fn description(&self) -> L10n { L10n::n("Un tema personal") }
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn description(&self) -> L10n {
|
|
||||||
/// L10n::n("A personal theme")
|
|
||||||
/// }
|
|
||||||
///
|
///
|
||||||
/// fn theme(&self) -> Option<ThemeRef> {
|
/// fn theme(&self) -> Option<ThemeRef> {
|
||||||
/// Some(&Self)
|
/// Some(&Self)
|
||||||
|
@ -40,67 +33,21 @@ pub type ThemeRef = &'static dyn Theme;
|
||||||
/// impl Theme for MyTheme {}
|
/// impl Theme for MyTheme {}
|
||||||
/// ```
|
/// ```
|
||||||
pub trait Theme: Extension + Send + Sync {
|
pub trait Theme: Extension + Send + Sync {
|
||||||
/// **Obsoleto desde la versión 0.4.0**: usar [`declared_regions()`](Self::declared_regions) en
|
|
||||||
/// su lugar.
|
|
||||||
#[deprecated(since = "0.4.0", note = "Use `declared_regions()` instead")]
|
|
||||||
fn regions(&self) -> Vec<(&'static str, L10n)> {
|
fn regions(&self) -> Vec<(&'static str, L10n)> {
|
||||||
vec![("content", L10n::l("content"))]
|
vec![(CONTENT_REGION_NAME, L10n::l("content"))]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Declaración ordenada de las regiones disponibles en la página.
|
|
||||||
///
|
|
||||||
/// Devuelve una lista estática de pares `(Region, L10n)` que se usará para renderizar en el
|
|
||||||
/// orden indicado todas las regiones que componen una página. Los identificadores deben ser
|
|
||||||
/// **estables** como `"sidebar-left"` o `"content"`. La etiqueta `L10n` devuelve el nombre de la
|
|
||||||
/// región en el idioma activo de la página.
|
|
||||||
///
|
|
||||||
/// Si el tema requiere un conjunto distinto de regiones, se puede sobrescribir este método para
|
|
||||||
/// devolver una lista diferente. Si no, se usará la lista predeterminada:
|
|
||||||
///
|
|
||||||
/// - `"header"`: cabecera.
|
|
||||||
/// - `"content"`: contenido principal (**obligatoria**).
|
|
||||||
/// - `"footer"`: pie.
|
|
||||||
///
|
|
||||||
/// Sólo la región `"content"` es obligatoria, usa [`Region::default()`] para declararla.
|
|
||||||
#[inline]
|
|
||||||
fn declared_regions(&self) -> &'static [(Region, L10n)] {
|
|
||||||
static REGIONS: LazyLock<[(Region, L10n); 3]> = LazyLock::new(|| {
|
|
||||||
[
|
|
||||||
(Region::declare("header"), L10n::l("region_header")),
|
|
||||||
(Region::default(), L10n::l("region_content")),
|
|
||||||
(Region::declare("footer"), L10n::l("region_footer")),
|
|
||||||
]
|
|
||||||
});
|
|
||||||
®IONS[..]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Acciones específicas del tema antes de renderizar el `<body>` de la página.
|
|
||||||
///
|
|
||||||
/// Útil para preparar clases, inyectar recursos o ajustar metadatos.
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn before_render_page_body(&self, page: &mut Page) {}
|
fn before_render_page_body(&self, page: &mut Page) {}
|
||||||
|
|
||||||
/// Renderiza el contenido del `<body>` de la página.
|
|
||||||
///
|
|
||||||
/// Por defecto, recorre [`declared_regions()`](Self::declared_regions) **en el orden que se han
|
|
||||||
/// declarado** y, para cada región con contenido, genera un contenedor con `role="region"` y
|
|
||||||
/// `aria-label` localizado.
|
|
||||||
fn render_page_body(&self, page: &mut Page) -> Markup {
|
fn render_page_body(&self, page: &mut Page) -> Markup {
|
||||||
html! {
|
html! {
|
||||||
body id=[page.body_id().get()] class=[page.body_classes().get()] {
|
body id=[page.body_id().get()] class=[page.body_classes().get()] {
|
||||||
@for (region, region_label) in self.declared_regions() {
|
@for (region_name, _) in self.regions() {
|
||||||
@let output = page.render_region(region.key());
|
@let output = page.render_region(region_name);
|
||||||
@if !output.is_empty() {
|
@if !output.is_empty() {
|
||||||
@let region_name = region.name();
|
div id=(region_name) class={ "region-container region-" (region_name) } {
|
||||||
div
|
(output)
|
||||||
id=(region_name)
|
|
||||||
class="region"
|
|
||||||
role="region"
|
|
||||||
aria-label=[region_label.using(page)]
|
|
||||||
{
|
|
||||||
div class={ "region__" (region_name) } {
|
|
||||||
(output)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,16 +55,9 @@ pub trait Theme: Extension + Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Acciones específicas del tema después de renderizar el `<body>` de la página.
|
|
||||||
///
|
|
||||||
/// Útil para *tracing*, métricas o ajustes finales del estado de la página.
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn after_render_page_body(&self, page: &mut Page) {}
|
fn after_render_page_body(&self, page: &mut Page) {}
|
||||||
|
|
||||||
/// Renderiza el contenido del `<head>` de la página.
|
|
||||||
///
|
|
||||||
/// Por defecto, genera las etiquetas básicas (`charset`, `title`, `description`, `viewport`,
|
|
||||||
/// `X-UA-Compatible`), los metadatos y propiedades de la página y los recursos (CSS/JS).
|
|
||||||
fn render_page_head(&self, page: &mut Page) -> Markup {
|
fn render_page_head(&self, page: &mut Page) -> Markup {
|
||||||
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! {
|
||||||
|
@ -149,17 +89,11 @@ pub trait Theme: Extension + Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Página de error "*403 – Forbidden*" predeterminada.
|
fn error403(&self, _page: &mut Page) -> Markup {
|
||||||
///
|
html! { div { h1 { ("FORBIDDEN ACCESS") } } }
|
||||||
/// Se puede sobrescribir este método para personalizar y adaptar este contenido al tema.
|
|
||||||
fn error403(&self, page: &mut Page) -> Markup {
|
|
||||||
html! { div { h1 { (L10n::l("error403_notice").to_markup(page)) } } }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Página de error "*404 – Not Found*" predeterminada.
|
fn error404(&self, _page: &mut Page) -> Markup {
|
||||||
///
|
html! { div { h1 { ("RESOURCE NOT FOUND") } } }
|
||||||
/// Se puede sobrescribir este método para personalizar y adaptar este contenido al tema.
|
|
||||||
fn error404(&self, page: &mut Page) -> Markup {
|
|
||||||
html! { div { h1 { (L10n::l("error404_notice").to_markup(page)) } } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::core::component::{Child, ChildOp, Children};
|
use crate::core::component::{Child, ChildOp, Children};
|
||||||
use crate::core::theme::ThemeRef;
|
use crate::core::theme::{ThemeRef, CONTENT_REGION_NAME};
|
||||||
use crate::{builder_fn, AutoDefault, UniqueId};
|
use crate::{builder_fn, AutoDefault, UniqueId};
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
@ -7,71 +7,15 @@ use parking_lot::RwLock;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
// Conjunto de regiones globales asociadas a un tema específico.
|
// Regiones globales con componentes para un tema dado.
|
||||||
static THEME_REGIONS: LazyLock<RwLock<HashMap<UniqueId, ChildrenInRegions>>> =
|
static THEME_REGIONS: LazyLock<RwLock<HashMap<UniqueId, ChildrenInRegions>>> =
|
||||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
// Conjunto de regiones globales comunes a todos los temas.
|
// Regiones globales con componentes para cualquier tema.
|
||||||
static COMMON_REGIONS: LazyLock<RwLock<ChildrenInRegions>> =
|
static COMMON_REGIONS: LazyLock<RwLock<ChildrenInRegions>> =
|
||||||
LazyLock::new(|| RwLock::new(ChildrenInRegions::default()));
|
LazyLock::new(|| RwLock::new(ChildrenInRegions::default()));
|
||||||
|
|
||||||
/// Nombre de la región de contenido por defecto (`"content"`).
|
// Estructura interna para mantener los componentes de una región.
|
||||||
pub const REGION_CONTENT: &str = "content";
|
|
||||||
|
|
||||||
/// Identificador de una región de página.
|
|
||||||
///
|
|
||||||
/// Incluye una **clave estática** ([`key()`](Self::key)) que identifica la región en el tema, y un
|
|
||||||
/// **nombre normalizado** ([`name()`](Self::name)) en minúsculas para su uso en atributos HTML
|
|
||||||
/// (p.ej., clases `region__{name}`).
|
|
||||||
///
|
|
||||||
/// Se utiliza para declarar las regiones que componen una página en un tema (ver
|
|
||||||
/// [`declared_regions()`](crate::core::theme::Theme::declared_regions)).
|
|
||||||
pub struct Region {
|
|
||||||
key: &'static str,
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Region {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
key: REGION_CONTENT,
|
|
||||||
name: String::from(REGION_CONTENT),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Region {
|
|
||||||
/// Declara una región a partir de su clave estática.
|
|
||||||
///
|
|
||||||
/// Genera además un nombre normalizado de la clave, eliminando espacios iniciales y finales,
|
|
||||||
/// convirtiendo a minúsculas y sustituyendo los espacios intermedios por guiones (`-`).
|
|
||||||
///
|
|
||||||
/// Esta clave se usará para añadir componentes a la región; por ello se recomiendan nombres
|
|
||||||
/// sencillos, limitando los caracteres a `[a-z0-9-]` (p.ej., `"sidebar"` o `"main-menu"`), cuyo
|
|
||||||
/// nombre normalizado coincidirá con la clave.
|
|
||||||
#[inline]
|
|
||||||
pub fn declare(key: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
key,
|
|
||||||
name: key.trim().to_ascii_lowercase().replace(' ', "-"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve la clave estática asignada a la región.
|
|
||||||
#[inline]
|
|
||||||
pub fn key(&self) -> &'static str {
|
|
||||||
self.key
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve el nombre normalizado de la región (para atributos y búsquedas).
|
|
||||||
#[inline]
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contenedor interno de componentes agrupados por región.
|
|
||||||
#[derive(AutoDefault)]
|
#[derive(AutoDefault)]
|
||||||
pub struct ChildrenInRegions(HashMap<&'static str, Children>);
|
pub struct ChildrenInRegions(HashMap<&'static str, Children>);
|
||||||
|
|
||||||
|
@ -104,24 +48,25 @@ impl ChildrenInRegions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Punto de acceso para añadir componentes a regiones globales o específicas de un tema.
|
/// Permite añadir componentes a regiones globales o regiones de temas concretos.
|
||||||
///
|
///
|
||||||
/// Según la variante, se pueden añadir componentes ([`add()`](Self::add)) que permanecerán
|
/// Dada una región, según la variante seleccionada, se le podrán añadir ([`add()`](Self::add))
|
||||||
/// disponibles durante toda la ejecución.
|
/// componentes que se mantendrán durante la ejecución de la aplicación.
|
||||||
///
|
///
|
||||||
/// Estos componentes se renderizarán automáticamente al procesar los documentos HTML que incluyen
|
/// Estas estructuras de componentes se renderizarán automáticamente al procesar los documentos HTML
|
||||||
/// estas regiones, como las páginas de contenido ([`Page`](crate::response::page::Page)).
|
/// que las usan, como las páginas de contenido ([`Page`](crate::response::page::Page)), por
|
||||||
|
/// ejemplo.
|
||||||
pub enum InRegion {
|
pub enum InRegion {
|
||||||
/// Región de contenido por defecto.
|
/// Representa la región por defecto en la que se pueden añadir componentes.
|
||||||
Content,
|
Content,
|
||||||
/// Región identificada por el nombre proporcionado.
|
/// Representa la región con el nombre del argumento.
|
||||||
Named(&'static str),
|
Named(&'static str),
|
||||||
/// Región identificada por un nombre y asociada a un tema concreto.
|
/// Representa la región con el nombre y del tema especificado en los argumentos.
|
||||||
OfTheme(&'static str, ThemeRef),
|
OfTheme(&'static str, ThemeRef),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InRegion {
|
impl InRegion {
|
||||||
/// Añade un componente a la región indicada por la variante.
|
/// Permite añadir un componente en la región de la variante seleccionada.
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
|
@ -143,7 +88,7 @@ impl InRegion {
|
||||||
InRegion::Content => {
|
InRegion::Content => {
|
||||||
COMMON_REGIONS
|
COMMON_REGIONS
|
||||||
.write()
|
.write()
|
||||||
.alter_child_in_region(REGION_CONTENT, ChildOp::Add(child));
|
.alter_child_in_region(CONTENT_REGION_NAME, ChildOp::Add(child));
|
||||||
}
|
}
|
||||||
InRegion::Named(name) => {
|
InRegion::Named(name) => {
|
||||||
COMMON_REGIONS
|
COMMON_REGIONS
|
||||||
|
|
|
@ -9,9 +9,10 @@ use crate::{builder_fn, join};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{self, Display};
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
/// Operaciones para modificar el contexto ([`Context`]) del documento.
|
/// Operaciones para modificar el contexto ([`Context`]) del documento.
|
||||||
pub enum AssetsOp {
|
pub enum AssetsOp {
|
||||||
// Favicon.
|
// Favicon.
|
||||||
|
@ -42,7 +43,7 @@ pub enum ErrorParam {
|
||||||
ParseError(String),
|
ParseError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ErrorParam {
|
impl fmt::Display for ErrorParam {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ErrorParam::NotFound => write!(f, "Parameter not found"),
|
ErrorParam::NotFound => write!(f, "Parameter not found"),
|
||||||
|
|
|
@ -25,7 +25,6 @@ pub enum ClassesOp {
|
||||||
///
|
///
|
||||||
/// - El [orden de las clases no es relevante](https://stackoverflow.com/a/1321712) en CSS.
|
/// - El [orden de las clases no es relevante](https://stackoverflow.com/a/1321712) en CSS.
|
||||||
/// - No se permiten clases duplicadas.
|
/// - No se permiten clases duplicadas.
|
||||||
/// - Las clases se convierten a minúsculas.
|
|
||||||
/// - Las clases vacías se ignoran.
|
/// - Las clases vacías se ignoran.
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
|
@ -33,8 +32,8 @@ pub enum ClassesOp {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
///
|
///
|
||||||
/// let classes = OptionClasses::new("Btn btn-primary")
|
/// let classes = OptionClasses::new("btn btn-primary")
|
||||||
/// .with_value(ClassesOp::Add, "Active")
|
/// .with_value(ClassesOp::Add, "active")
|
||||||
/// .with_value(ClassesOp::Remove, "btn-primary");
|
/// .with_value(ClassesOp::Remove, "btn-primary");
|
||||||
///
|
///
|
||||||
/// assert_eq!(classes.get(), Some(String::from("btn active")));
|
/// assert_eq!(classes.get(), Some(String::from("btn active")));
|
||||||
|
@ -52,7 +51,7 @@ impl OptionClasses {
|
||||||
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_value(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
|
pub fn with_value(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
|
||||||
let classes = classes.as_ref().to_ascii_lowercase();
|
let classes: &str = classes.as_ref();
|
||||||
let classes: Vec<&str> = classes.split_ascii_whitespace().collect();
|
let classes: Vec<&str> = classes.split_ascii_whitespace().collect();
|
||||||
|
|
||||||
if classes.is_empty() {
|
if classes.is_empty() {
|
||||||
|
|
|
@ -7,7 +7,6 @@ use crate::{builder_fn, AutoDefault};
|
||||||
/// # Normalización
|
/// # Normalización
|
||||||
///
|
///
|
||||||
/// - Se eliminan los espacios al principio y al final.
|
/// - Se eliminan los espacios al principio y al final.
|
||||||
/// - Se convierte a minúsculas.
|
|
||||||
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
|
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
|
||||||
/// - Si el resultado es una cadena vacía, se guarda `None`.
|
/// - Si el resultado es una cadena vacía, se guarda `None`.
|
||||||
///
|
///
|
||||||
|
@ -16,7 +15,7 @@ use crate::{builder_fn, AutoDefault};
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
///
|
///
|
||||||
/// let id = OptionId::new(" main Section ");
|
/// let id = OptionId::new("main section");
|
||||||
/// assert_eq!(id.get(), Some(String::from("main_section")));
|
/// assert_eq!(id.get(), Some(String::from("main_section")));
|
||||||
///
|
///
|
||||||
/// let empty = OptionId::default();
|
/// let empty = OptionId::default();
|
||||||
|
@ -40,7 +39,7 @@ impl OptionId {
|
||||||
/// El valor se normaliza automáticamente.
|
/// El valor se normaliza automáticamente.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
|
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
|
||||||
let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_");
|
let value = value.as_ref().trim().replace(' ', "_");
|
||||||
self.0 = (!value.is_empty()).then_some(value);
|
self.0 = (!value.is_empty()).then_some(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use crate::{builder_fn, AutoDefault};
|
||||||
/// # Normalización
|
/// # Normalización
|
||||||
///
|
///
|
||||||
/// - Se eliminan los espacios al principio y al final.
|
/// - Se eliminan los espacios al principio y al final.
|
||||||
/// - Se convierte a minúsculas.
|
|
||||||
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
|
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
|
||||||
/// - Si el resultado es una cadena vacía, se guarda `None`.
|
/// - Si el resultado es una cadena vacía, se guarda `None`.
|
||||||
///
|
///
|
||||||
|
@ -16,7 +15,7 @@ use crate::{builder_fn, AutoDefault};
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
///
|
///
|
||||||
/// let name = OptionName::new(" DISplay name ");
|
/// let name = OptionName::new(" display name ");
|
||||||
/// assert_eq!(name.get(), Some(String::from("display_name")));
|
/// assert_eq!(name.get(), Some(String::from("display_name")));
|
||||||
///
|
///
|
||||||
/// let empty = OptionName::default();
|
/// let empty = OptionName::default();
|
||||||
|
@ -40,7 +39,7 @@ impl OptionName {
|
||||||
/// El valor se normaliza automáticamente.
|
/// El valor se normaliza automáticamente.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
|
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
|
||||||
let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_");
|
let value = value.as_ref().trim().replace(' ', "_");
|
||||||
self.0 = (!value.is_empty()).then_some(value);
|
self.0 = (!value.is_empty()).then_some(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,2 @@
|
||||||
# Regions.
|
content = Content
|
||||||
region_header = Header
|
|
||||||
region_content = Content
|
|
||||||
region_footer = Footer
|
|
||||||
|
|
||||||
error403_notice = FORBIDDEN ACCESS
|
|
||||||
error404_notice = RESOURCE NOT FOUND
|
|
||||||
|
|
||||||
pagetop_logo = PageTop Logo
|
pagetop_logo = PageTop Logo
|
||||||
|
|
|
@ -1,9 +1,2 @@
|
||||||
# Regions.
|
content = Contenido
|
||||||
region_header = Cabecera
|
|
||||||
region_content = Contenido
|
|
||||||
region_footer = Pie de página
|
|
||||||
|
|
||||||
error403_notice = ACCESO NO PERMITIDO
|
|
||||||
error404_notice = RECURSO NO ENCONTRADO
|
|
||||||
|
|
||||||
pagetop_logo = Logotipo de PageTop
|
pagetop_logo = Logotipo de PageTop
|
||||||
|
|
|
@ -6,7 +6,7 @@ pub use actix_web::Result as ResultPage;
|
||||||
use crate::base::action;
|
use crate::base::action;
|
||||||
use crate::builder_fn;
|
use crate::builder_fn;
|
||||||
use crate::core::component::{Child, ChildOp, Component};
|
use crate::core::component::{Child, ChildOp, Component};
|
||||||
use crate::core::theme::{ChildrenInRegions, ThemeRef, REGION_CONTENT};
|
use crate::core::theme::{ChildrenInRegions, ThemeRef, CONTENT_REGION_NAME};
|
||||||
use crate::html::{html, AssetsOp, Context, Markup, DOCTYPE};
|
use crate::html::{html, AssetsOp, Context, Markup, DOCTYPE};
|
||||||
use crate::html::{ClassesOp, OptionClasses, OptionId, OptionTranslated};
|
use crate::html::{ClassesOp, OptionClasses, OptionId, OptionTranslated};
|
||||||
use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier};
|
use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier};
|
||||||
|
@ -123,7 +123,7 @@ impl Page {
|
||||||
/// Añade un componente a la región de contenido por defecto.
|
/// Añade un componente a la región de contenido por defecto.
|
||||||
pub fn with_component(mut self, component: impl Component) -> Self {
|
pub fn with_component(mut self, component: impl Component) -> Self {
|
||||||
self.regions
|
self.regions
|
||||||
.alter_child_in_region(REGION_CONTENT, ChildOp::Add(Child::with(component)));
|
.alter_child_in_region(CONTENT_REGION_NAME, ChildOp::Add(Child::with(component)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +172,11 @@ impl Page {
|
||||||
self.context.request()
|
self.context.request()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Devuelve el identificador de idioma asociado.
|
||||||
|
pub fn langid(&self) -> &LanguageIdentifier {
|
||||||
|
self.context.langid()
|
||||||
|
}
|
||||||
|
|
||||||
/// Devuelve el tema que se usará para renderizar la página.
|
/// Devuelve el tema que se usará para renderizar la página.
|
||||||
pub fn theme(&self) -> ThemeRef {
|
pub fn theme(&self) -> ThemeRef {
|
||||||
self.context.theme()
|
self.context.theme()
|
||||||
|
@ -245,9 +250,3 @@ impl Page {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LangId for Page {
|
|
||||||
fn langid(&self) -> &'static LanguageIdentifier {
|
|
||||||
self.context.langid()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::service::{HttpRequest, HttpResponse};
|
||||||
|
|
||||||
use super::Page;
|
use super::Page;
|
||||||
|
|
||||||
use std::fmt::{self, Display};
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ErrorPage {
|
pub enum ErrorPage {
|
||||||
|
@ -19,7 +19,7 @@ pub enum ErrorPage {
|
||||||
Timeout(HttpRequest),
|
Timeout(HttpRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ErrorPage {
|
impl fmt::Display for ErrorPage {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
// Error 304.
|
// Error 304.
|
||||||
|
|
|
@ -56,10 +56,8 @@ pub fn resolve_absolute_dir<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Obsoleto desde la versión 0.3.0**: usar [`resolve_absolute_dir()`] en su lugar.
|
|
||||||
///
|
|
||||||
/// Devuelve la ruta absoluta a un directorio existente.
|
/// Devuelve la ruta absoluta a un directorio existente.
|
||||||
#[deprecated(since = "0.3.0", note = "Use `resolve_absolute_dir()` instead")]
|
#[deprecated(since = "0.3.0", note = "Use [`resolve_absolute_dir`] instead")]
|
||||||
pub fn absolute_dir<P, Q>(root_path: P, relative_path: Q) -> io::Result<PathBuf>
|
pub fn absolute_dir<P, Q>(root_path: P, relative_path: Q) -> io::Result<PathBuf>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
|
|
|
@ -295,6 +295,11 @@ a:hover:visited {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#poweredby-link:hover {
|
||||||
|
transition: all .5s;
|
||||||
|
transform: rotate(-3deg) scale(1.1);
|
||||||
|
box-shadow: 0px 3px 5px rgba(0,0,0,.4);
|
||||||
|
}
|
||||||
#poweredby-link:hover span {
|
#poweredby-link:hover span {
|
||||||
animation-play-state: paused;
|
animation-play-state: paused;
|
||||||
}
|
}
|
||||||
|
@ -318,11 +323,6 @@ a:hover:visited {
|
||||||
max-width: 29.375rem;
|
max-width: 29.375rem;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
#poweredby-link:hover {
|
|
||||||
transition: all .5s;
|
|
||||||
transform: rotate(-3deg) scale(1.1);
|
|
||||||
box-shadow: 0px 3px 5px rgba(0,0,0,.4);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-text {
|
.content-text {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue