🔥 Elimina Render para usar siempre el contexto
				
					
				
			This commit is contained in:
		
							parent
							
								
									ddf78c2de8
								
							
						
					
					
						commit
						e3ca6079ff
					
				
					 12 changed files with 205 additions and 146 deletions
				
			
		| 
						 | 
				
			
			@ -2,10 +2,10 @@ pub mod favicon;
 | 
			
		|||
pub mod javascript;
 | 
			
		||||
pub mod stylesheet;
 | 
			
		||||
 | 
			
		||||
use crate::html::{html, Markup, Render};
 | 
			
		||||
use crate::html::{html, Context, Markup};
 | 
			
		||||
use crate::{AutoDefault, Weight};
 | 
			
		||||
 | 
			
		||||
/// Representación genérica de un *script* [`JavaScript`](crate::html::JavaScript) o una hoja de
 | 
			
		||||
/// Representación genérica de un script [`JavaScript`](crate::html::JavaScript) o una hoja de
 | 
			
		||||
/// estilos [`StyleSheet`](crate::html::StyleSheet).
 | 
			
		||||
///
 | 
			
		||||
/// Estos recursos se incluyen en los conjuntos de recursos ([`Assets`]) que suelen renderizarse en
 | 
			
		||||
| 
						 | 
				
			
			@ -13,12 +13,15 @@ use crate::{AutoDefault, Weight};
 | 
			
		|||
///
 | 
			
		||||
/// Cada recurso se identifica por un **nombre único** ([`Asset::name()`]), usado como clave; y un
 | 
			
		||||
/// **peso** ([`Asset::weight()`]), que determina su orden relativo de renderizado.
 | 
			
		||||
pub trait Asset: Render {
 | 
			
		||||
pub trait Asset {
 | 
			
		||||
    /// Devuelve el nombre del recurso, utilizado como clave única.
 | 
			
		||||
    fn name(&self) -> &str;
 | 
			
		||||
 | 
			
		||||
    /// Devuelve el peso del recurso, usado para ordenar el renderizado de menor a mayor peso.
 | 
			
		||||
    fn weight(&self) -> Weight;
 | 
			
		||||
 | 
			
		||||
    /// Renderiza el recurso en el contexto proporcionado.
 | 
			
		||||
    fn render(&self, cx: &mut Context) -> Markup;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Gestión común para conjuntos de recursos como [`JavaScript`](crate::html::JavaScript) y
 | 
			
		||||
| 
						 | 
				
			
			@ -77,16 +80,13 @@ impl<T: Asset> Assets<T> {
 | 
			
		|||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Asset> Render for Assets<T> {
 | 
			
		||||
    fn render(&self) -> Markup {
 | 
			
		||||
    pub fn render(&self, cx: &mut Context) -> Markup {
 | 
			
		||||
        let mut assets = self.0.iter().collect::<Vec<_>>();
 | 
			
		||||
        assets.sort_by_key(|a| a.weight());
 | 
			
		||||
 | 
			
		||||
        html! {
 | 
			
		||||
            @for a in assets {
 | 
			
		||||
                (a)
 | 
			
		||||
                (a.render(cx))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
use crate::html::{html, Markup, Render};
 | 
			
		||||
use crate::html::{html, Context, Markup};
 | 
			
		||||
use crate::AutoDefault;
 | 
			
		||||
 | 
			
		||||
/// Un **Favicon** es un recurso gráfico que usa el navegador como icono asociado al sitio.
 | 
			
		||||
| 
						 | 
				
			
			@ -151,10 +151,12 @@ impl Favicon {
 | 
			
		|||
        });
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Render for Favicon {
 | 
			
		||||
    fn render(&self) -> Markup {
 | 
			
		||||
    /// Renderiza el **Favicon** completo con todas las etiquetas declaradas.
 | 
			
		||||
    ///
 | 
			
		||||
    /// El parámetro `Context` se acepta por coherencia con el resto de *assets*, aunque en este
 | 
			
		||||
    /// caso es ignorado.
 | 
			
		||||
    pub fn render(&self, _cx: &mut Context) -> Markup {
 | 
			
		||||
        html! {
 | 
			
		||||
            @for item in &self.0 {
 | 
			
		||||
                (item)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,35 +1,45 @@
 | 
			
		|||
use crate::html::assets::Asset;
 | 
			
		||||
use crate::html::{html, Markup, Render};
 | 
			
		||||
use crate::html::{html, Context, Markup, PreEscaped};
 | 
			
		||||
use crate::{join, join_pair, AutoDefault, Weight};
 | 
			
		||||
 | 
			
		||||
// Define el origen del recurso JavaScript y cómo debe cargarse en el navegador.
 | 
			
		||||
//
 | 
			
		||||
// Los distintos modos de carga permiten optimizar el rendimiento y controlar el comportamiento del
 | 
			
		||||
// script.
 | 
			
		||||
// script en relación con el análisis del documento HTML y la ejecución del resto de scripts.
 | 
			
		||||
//
 | 
			
		||||
// - [`From`]   – Carga el script de forma estándar con la etiqueta `<script src="...">`.
 | 
			
		||||
// - [`Defer`]  – Igual que [`From`], pero con el atributo `defer`.
 | 
			
		||||
// - [`Async`]  – Igual que [`From`], pero con el atributo `async`.
 | 
			
		||||
// - [`Inline`] – Inserta el código directamente en la etiqueta `<script>`.
 | 
			
		||||
// - [`OnLoad`] – Inserta el código JavaScript y lo ejecuta tras el evento `DOMContentLoaded`.
 | 
			
		||||
// - [`From`]        – Carga estándar con la etiqueta `<script src="...">`.
 | 
			
		||||
// - [`Defer`]       – Igual que [`From`], pero con el atributo `defer`, descarga en paralelo y se
 | 
			
		||||
//                     ejecuta tras el análisis del documento HTML, respetando el orden de
 | 
			
		||||
//                     aparición.
 | 
			
		||||
// - [`Async`]       – Igual que [`From`], pero con el atributo `async`, descarga en paralelo y se
 | 
			
		||||
//                     ejecuta en cuanto esté listo, **sin garantizar** el orden relativo respecto a
 | 
			
		||||
//                     otros scripts.
 | 
			
		||||
// - [`Inline`]      – Inserta el código directamente en la etiqueta `<script>`.
 | 
			
		||||
// - [`OnLoad`]      – Inserta el código JavaScript y lo ejecuta tras el evento `DOMContentLoaded`.
 | 
			
		||||
// - [`OnLoadAsync`] – Igual que [`OnLoad`], pero con manejador asíncrono (`async`), útil si dentro
 | 
			
		||||
//                     del código JavaScript se utiliza `await`.
 | 
			
		||||
#[derive(AutoDefault)]
 | 
			
		||||
enum Source {
 | 
			
		||||
    #[default]
 | 
			
		||||
    From(String),
 | 
			
		||||
    Defer(String),
 | 
			
		||||
    Async(String),
 | 
			
		||||
    Inline(String, String),
 | 
			
		||||
    OnLoad(String, String),
 | 
			
		||||
    // `name`, `closure(Context) -> String`.
 | 
			
		||||
    Inline(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>),
 | 
			
		||||
    // `name`, `closure(Context) -> String` (se ejecuta tras `DOMContentLoaded`).
 | 
			
		||||
    OnLoad(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>),
 | 
			
		||||
    // `name`, `closure(Context) -> String` (manejador `async` tras `DOMContentLoaded`).
 | 
			
		||||
    OnLoadAsync(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Define un recurso **JavaScript** para incluir en un documento HTML.
 | 
			
		||||
///
 | 
			
		||||
/// Este tipo permite añadir *scripts* externos o embebidos con distintas estrategias de carga
 | 
			
		||||
/// Este tipo permite añadir scripts externos o embebidos con distintas estrategias de carga
 | 
			
		||||
/// (`defer`, `async`, *inline*, etc.) y [pesos](crate::Weight) para controlar el orden de inserción
 | 
			
		||||
/// en el documento.
 | 
			
		||||
///
 | 
			
		||||
/// > **Nota**
 | 
			
		||||
/// > Los archivos de los *scripts* deben estar disponibles en el servidor web de la aplicación.
 | 
			
		||||
/// > Los archivos de los scripts deben estar disponibles en el servidor web de la aplicación.
 | 
			
		||||
/// > Pueden servirse usando [`static_files_service!`](crate::static_files_service).
 | 
			
		||||
///
 | 
			
		||||
/// # Ejemplo
 | 
			
		||||
| 
						 | 
				
			
			@ -37,23 +47,37 @@ enum Source {
 | 
			
		|||
/// ```rust
 | 
			
		||||
/// use pagetop::prelude::*;
 | 
			
		||||
///
 | 
			
		||||
/// // Script externo con carga diferida, versión para control de caché y prioriza el renderizado.
 | 
			
		||||
/// // Script externo con carga diferida, versión de caché y prioridad en el renderizado.
 | 
			
		||||
/// let script = JavaScript::defer("/assets/js/app.js")
 | 
			
		||||
///     .with_version("1.2.3")
 | 
			
		||||
///     .with_weight(-10);
 | 
			
		||||
///
 | 
			
		||||
/// // Script embebido que se ejecuta tras la carga del documento.
 | 
			
		||||
/// let script = JavaScript::on_load("init_tooltips", r#"
 | 
			
		||||
/// let script = JavaScript::on_load("init_tooltips", |_| r#"
 | 
			
		||||
///     const tooltips = document.querySelectorAll('[data-tooltip]');
 | 
			
		||||
///     for (const el of tooltips) {
 | 
			
		||||
///         el.addEventListener('mouseenter', showTooltip);
 | 
			
		||||
///     }
 | 
			
		||||
/// "#);
 | 
			
		||||
/// "#.to_string());
 | 
			
		||||
///
 | 
			
		||||
/// // Script embebido con manejador asíncrono (`async`) que puede usar `await`.
 | 
			
		||||
/// let mut cx = Context::new(None).with_param("user_id", 7u32);
 | 
			
		||||
///
 | 
			
		||||
/// let js = JavaScript::on_load_async("hydrate", |cx| {
 | 
			
		||||
///     // Ejemplo: lectura de un parámetro del contexto para inyectarlo en el código.
 | 
			
		||||
///     let uid: u32 = cx.param_or_default("user_id");
 | 
			
		||||
///     format!(r#"
 | 
			
		||||
///         const USER_ID = {};
 | 
			
		||||
///         await Promise.resolve(USER_ID);
 | 
			
		||||
///         // Aquí se podría hidratar la interfaz o cargar módulos dinámicos:
 | 
			
		||||
///         // await import('/assets/js/hydrate.js');
 | 
			
		||||
///     "#, uid)
 | 
			
		||||
/// });
 | 
			
		||||
/// ```
 | 
			
		||||
#[rustfmt::skip]
 | 
			
		||||
#[derive(AutoDefault)]
 | 
			
		||||
pub struct JavaScript {
 | 
			
		||||
    source : Source, // Fuente y modo de carga del script.
 | 
			
		||||
    source : Source, // Fuente y estrategia de carga del script.
 | 
			
		||||
    version: String, // Versión del recurso para la caché del navegador.
 | 
			
		||||
    weight : Weight, // Peso que determina el orden.
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -70,11 +94,11 @@ impl JavaScript {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Crea un **script externo** con el atributo `defer`, que se carga en segundo plano y se
 | 
			
		||||
    /// ejecuta tras analizar completamente el documento HTML.
 | 
			
		||||
    /// Crea un **script externo** con el atributo `defer`, que se descarga en paralelo y se ejecuta
 | 
			
		||||
    /// tras analizar completamente el documento HTML, **respetando el orden** de inserción.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Equivale a `<script src="..." defer>`. Útil para mantener el orden de ejecución y evitar
 | 
			
		||||
    /// bloquear el análisis del documento HTML.
 | 
			
		||||
    /// Equivale a `<script src="..." defer>`. Suele ser la opción recomendada para scripts no
 | 
			
		||||
    /// críticos.
 | 
			
		||||
    pub fn defer(path: impl Into<String>) -> Self {
 | 
			
		||||
        JavaScript {
 | 
			
		||||
            source: Source::Defer(path.into()),
 | 
			
		||||
| 
						 | 
				
			
			@ -82,11 +106,10 @@ impl JavaScript {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Crea un **script externo** con el atributo `async`, que se carga y ejecuta de forma
 | 
			
		||||
    /// asíncrona tan pronto como esté disponible.
 | 
			
		||||
    /// Crea un **script externo** con el atributo `async`, que se descarga en paralelo y se ejecuta
 | 
			
		||||
    /// tan pronto como esté disponible.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Equivale a `<script src="..." async>`. La ejecución puede producirse fuera de orden respecto
 | 
			
		||||
    /// a otros *scripts*.
 | 
			
		||||
    /// Equivale a `<script src="..." async>`. **No garantiza** el orden relativo con otros scripts.
 | 
			
		||||
    pub fn asynchronous(path: impl Into<String>) -> Self {
 | 
			
		||||
        JavaScript {
 | 
			
		||||
            source: Source::Async(path.into()),
 | 
			
		||||
| 
						 | 
				
			
			@ -97,37 +120,68 @@ impl JavaScript {
 | 
			
		|||
    /// Crea un **script embebido** directamente en el documento HTML.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Equivale a `<script>...</script>`. El parámetro `name` se usa como identificador interno del
 | 
			
		||||
    /// *script*.
 | 
			
		||||
    pub fn inline(name: impl Into<String>, script: impl Into<String>) -> Self {
 | 
			
		||||
    /// script.
 | 
			
		||||
    ///
 | 
			
		||||
    /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
 | 
			
		||||
    pub fn inline<F>(name: impl Into<String>, f: F) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn(&mut Context) -> String + Send + Sync + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        JavaScript {
 | 
			
		||||
            source: Source::Inline(name.into(), script.into()),
 | 
			
		||||
            source: Source::Inline(name.into(), Box::new(f)),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Crea un **script embebido** que se ejecuta automáticamente al terminar de cargarse el
 | 
			
		||||
    /// documento HTML.
 | 
			
		||||
    /// Crea un **script embebido** que se ejecuta cuando **el DOM está listo**.
 | 
			
		||||
    ///
 | 
			
		||||
    /// El código se envuelve automáticamente en un `addEventListener('DOMContentLoaded', ...)`. El
 | 
			
		||||
    /// parámetro `name` se usa como identificador interno del *script*.
 | 
			
		||||
    pub fn on_load(name: impl Into<String>, script: impl Into<String>) -> Self {
 | 
			
		||||
    /// El código se envuelve en un `addEventListener('DOMContentLoaded',function(){...})` que lo
 | 
			
		||||
    /// ejecuta tras analizar el documento HTML, **no** espera imágenes ni otros recursos externos.
 | 
			
		||||
    /// Útil para inicializaciones que no dependen de `await`. El parámetro `name` se usa como
 | 
			
		||||
    /// identificador interno del script.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Los scripts con `defer` se ejecutan antes de `DOMContentLoaded`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
 | 
			
		||||
    pub fn on_load<F>(name: impl Into<String>, f: F) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn(&mut Context) -> String + Send + Sync + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        JavaScript {
 | 
			
		||||
            source: Source::OnLoad(name.into(), script.into()),
 | 
			
		||||
            source: Source::OnLoad(name.into(), Box::new(f)),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Crea un **script embebido** con un **manejador asíncrono**.
 | 
			
		||||
    ///
 | 
			
		||||
    /// El código se envuelve en un `addEventListener('DOMContentLoaded',async()=>{...})`, que
 | 
			
		||||
    /// emplea una función `async` para que el cuerpo devuelto por la función *closure* pueda usar
 | 
			
		||||
    /// `await`. Ideal para hidratar la interfaz, cargar módulos dinámicos o realizar lecturas
 | 
			
		||||
    /// iniciales.
 | 
			
		||||
    ///
 | 
			
		||||
    /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
 | 
			
		||||
    pub fn on_load_async<F>(name: impl Into<String>, f: F) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn(&mut Context) -> String + Send + Sync + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        JavaScript {
 | 
			
		||||
            source: Source::OnLoadAsync(name.into(), Box::new(f)),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // JavaScript BUILDER **************************************************************************
 | 
			
		||||
 | 
			
		||||
    /// Asocia una versión al recurso (usada para control de la caché del navegador).
 | 
			
		||||
    /// Asocia una **versión** al recurso (usada para control de la caché del navegador).
 | 
			
		||||
    ///
 | 
			
		||||
    /// Si `version` está vacío, no se añade ningún parámetro a la URL.
 | 
			
		||||
    /// Si `version` está vacío, **no** se añade ningún parámetro a la URL.
 | 
			
		||||
    pub fn with_version(mut self, version: impl Into<String>) -> Self {
 | 
			
		||||
        self.version = version.into();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Modifica el peso del recurso.
 | 
			
		||||
    /// Modifica el **peso** del recurso.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Los recursos se renderizan de menor a mayor peso. Por defecto es `0`, que respeta el orden
 | 
			
		||||
    /// de creación.
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +194,7 @@ impl JavaScript {
 | 
			
		|||
impl Asset for JavaScript {
 | 
			
		||||
    /// Devuelve el nombre del recurso, utilizado como clave única.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Para *scripts* externos es la ruta del recurso; para *scripts* embebidos, un identificador.
 | 
			
		||||
    /// Para scripts externos es la ruta del recurso; para scripts embebidos, un identificador.
 | 
			
		||||
    fn name(&self) -> &str {
 | 
			
		||||
        match &self.source {
 | 
			
		||||
            Source::From(path) => path,
 | 
			
		||||
| 
						 | 
				
			
			@ -148,16 +202,15 @@ impl Asset for JavaScript {
 | 
			
		|||
            Source::Async(path) => path,
 | 
			
		||||
            Source::Inline(name, _) => name,
 | 
			
		||||
            Source::OnLoad(name, _) => name,
 | 
			
		||||
            Source::OnLoadAsync(name, _) => name,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn weight(&self) -> Weight {
 | 
			
		||||
        self.weight
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Render for JavaScript {
 | 
			
		||||
    fn render(&self) -> Markup {
 | 
			
		||||
    fn render(&self, cx: &mut Context) -> Markup {
 | 
			
		||||
        match &self.source {
 | 
			
		||||
            Source::From(path) => html! {
 | 
			
		||||
                script src=(join_pair!(path, "?v=", self.version.as_str())) {};
 | 
			
		||||
| 
						 | 
				
			
			@ -168,12 +221,15 @@ impl Render for JavaScript {
 | 
			
		|||
            Source::Async(path) => html! {
 | 
			
		||||
                script src=(join_pair!(path, "?v=", self.version.as_str())) async {};
 | 
			
		||||
            },
 | 
			
		||||
            Source::Inline(_, code) => html! {
 | 
			
		||||
                script { (code) };
 | 
			
		||||
            Source::Inline(_, f) => html! {
 | 
			
		||||
                script { (PreEscaped((f)(cx))) };
 | 
			
		||||
            },
 | 
			
		||||
            Source::OnLoad(_, code) => html! { (join!(
 | 
			
		||||
                "document.addEventListener('DOMContentLoaded',function(){", code, "});"
 | 
			
		||||
            )) },
 | 
			
		||||
            Source::OnLoad(_, f) => html! { script { (PreEscaped(join!(
 | 
			
		||||
                "document.addEventListener(\"DOMContentLoaded\",function(){", (f)(cx), "});"
 | 
			
		||||
            ))) } },
 | 
			
		||||
            Source::OnLoadAsync(_, f) => html! { script { (PreEscaped(join!(
 | 
			
		||||
                "document.addEventListener(\"DOMContentLoaded\",async()=>{", (f)(cx), "});"
 | 
			
		||||
            ))) } },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
use crate::html::assets::Asset;
 | 
			
		||||
use crate::html::{html, Markup, PreEscaped, Render};
 | 
			
		||||
use crate::html::{html, Context, Markup, PreEscaped};
 | 
			
		||||
use crate::{join_pair, AutoDefault, Weight};
 | 
			
		||||
 | 
			
		||||
// Define el origen del recurso CSS y cómo se incluye en el documento.
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,8 @@ use crate::{join_pair, AutoDefault, Weight};
 | 
			
		|||
enum Source {
 | 
			
		||||
    #[default]
 | 
			
		||||
    From(String),
 | 
			
		||||
    Inline(String, String),
 | 
			
		||||
    // `name`, `closure(Context) -> String`.
 | 
			
		||||
    Inline(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Define el medio objetivo para la hoja de estilos.
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +35,7 @@ pub enum TargetMedia {
 | 
			
		|||
    Speech,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Devuelve el texto asociado al punto de interrupción usado por Bootstrap.
 | 
			
		||||
/// Devuelve el valor para el atributo `media` (`Some(...)`) o `None` para `Default`.
 | 
			
		||||
#[rustfmt::skip]
 | 
			
		||||
impl TargetMedia {
 | 
			
		||||
    fn as_str_opt(&self) -> Option<&str> {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,12 +70,12 @@ impl TargetMedia {
 | 
			
		|||
///     .with_weight(-10);
 | 
			
		||||
///
 | 
			
		||||
/// // Crea una hoja de estilos embebida en el documento HTML.
 | 
			
		||||
/// let embedded = StyleSheet::inline("custom_theme", r#"
 | 
			
		||||
/// let embedded = StyleSheet::inline("custom_theme", |_| r#"
 | 
			
		||||
///     body {
 | 
			
		||||
///         background-color: #f5f5f5;
 | 
			
		||||
///         font-family: 'Segoe UI', sans-serif;
 | 
			
		||||
///     }
 | 
			
		||||
/// "#);
 | 
			
		||||
/// "#.to_string());
 | 
			
		||||
/// ```
 | 
			
		||||
#[rustfmt::skip]
 | 
			
		||||
#[derive(AutoDefault)]
 | 
			
		||||
| 
						 | 
				
			
			@ -100,9 +101,14 @@ impl StyleSheet {
 | 
			
		|||
    ///
 | 
			
		||||
    /// Equivale a `<style>...</style>`. El parámetro `name` se usa como identificador interno del
 | 
			
		||||
    /// recurso.
 | 
			
		||||
    pub fn inline(name: impl Into<String>, styles: impl Into<String>) -> Self {
 | 
			
		||||
    ///
 | 
			
		||||
    /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
 | 
			
		||||
    pub fn inline<F>(name: impl Into<String>, f: F) -> Self
 | 
			
		||||
    where
 | 
			
		||||
        F: Fn(&mut Context) -> String + Send + Sync + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        StyleSheet {
 | 
			
		||||
            source: Source::Inline(name.into(), styles.into()),
 | 
			
		||||
            source: Source::Inline(name.into(), Box::new(f)),
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -133,9 +139,9 @@ impl StyleSheet {
 | 
			
		|||
    /// Según el argumento `media`:
 | 
			
		||||
    ///
 | 
			
		||||
    /// - `TargetMedia::Default` - Se aplica en todos los casos (medio por defecto).
 | 
			
		||||
    /// - `TargetMedia::Print`   - Se aplican cuando el documento se imprime.
 | 
			
		||||
    /// - `TargetMedia::Screen`  - Se aplican en pantallas.
 | 
			
		||||
    /// - `TargetMedia::Speech`  - Se aplican en dispositivos que convierten el texto a voz.
 | 
			
		||||
    /// - `TargetMedia::Print`   - Se aplica cuando el documento se imprime.
 | 
			
		||||
    /// - `TargetMedia::Screen`  - Se aplica en pantallas.
 | 
			
		||||
    /// - `TargetMedia::Speech`  - Se aplica en dispositivos que convierten el texto a voz.
 | 
			
		||||
    pub fn for_media(mut self, media: TargetMedia) -> Self {
 | 
			
		||||
        self.media = media;
 | 
			
		||||
        self
 | 
			
		||||
| 
						 | 
				
			
			@ -156,10 +162,8 @@ impl Asset for StyleSheet {
 | 
			
		|||
    fn weight(&self) -> Weight {
 | 
			
		||||
        self.weight
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Render for StyleSheet {
 | 
			
		||||
    fn render(&self) -> Markup {
 | 
			
		||||
    fn render(&self, cx: &mut Context) -> Markup {
 | 
			
		||||
        match &self.source {
 | 
			
		||||
            Source::From(path) => html! {
 | 
			
		||||
                link
 | 
			
		||||
| 
						 | 
				
			
			@ -167,8 +171,8 @@ impl Render for StyleSheet {
 | 
			
		|||
                    href=(join_pair!(path, "?v=", self.version.as_str()))
 | 
			
		||||
                    media=[self.media.as_str_opt()];
 | 
			
		||||
            },
 | 
			
		||||
            Source::Inline(_, code) => html! {
 | 
			
		||||
                style { (PreEscaped(code)) };
 | 
			
		||||
            Source::Inline(_, f) => html! {
 | 
			
		||||
                style { (PreEscaped((f)(cx))) };
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,9 +25,9 @@ pub enum AssetsOp {
 | 
			
		|||
    RemoveStyleSheet(&'static str),
 | 
			
		||||
 | 
			
		||||
    // JavaScripts.
 | 
			
		||||
    /// Añade un *script* JavaScript al documento.
 | 
			
		||||
    /// Añade un script JavaScript al documento.
 | 
			
		||||
    AddJavaScript(JavaScript),
 | 
			
		||||
    /// Elimina un *script* por su ruta o identificador.
 | 
			
		||||
    /// Elimina un script por su ruta o identificador.
 | 
			
		||||
    RemoveJavaScript(&'static str),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ pub enum ErrorParam {
 | 
			
		|||
/// - Almacenar la **solicitud HTTP** de origen.
 | 
			
		||||
/// - Seleccionar **tema** y **composición** (*layout*) de renderizado.
 | 
			
		||||
/// - Administrar **recursos** del documento como el icono [`Favicon`], las hojas de estilo
 | 
			
		||||
///   [`StyleSheet`] o los *scripts* [`JavaScript`] mediante [`AssetsOp`].
 | 
			
		||||
///   [`StyleSheet`] o los scripts [`JavaScript`] mediante [`AssetsOp`].
 | 
			
		||||
/// - Leer y mantener **parámetros dinámicos tipados** de contexto.
 | 
			
		||||
/// - Generar **identificadores únicos** por tipo de componente.
 | 
			
		||||
///
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +139,7 @@ pub trait Contextual: LangId {
 | 
			
		|||
    /// Devuelve las hojas de estilo de los recursos del contexto.
 | 
			
		||||
    fn stylesheets(&self) -> &Assets<StyleSheet>;
 | 
			
		||||
 | 
			
		||||
    /// Devuelve los *scripts* JavaScript de los recursos del contexto.
 | 
			
		||||
    /// Devuelve los scripts JavaScript de los recursos del contexto.
 | 
			
		||||
    fn javascripts(&self) -> &Assets<JavaScript>;
 | 
			
		||||
 | 
			
		||||
    // Contextual HELPERS **************************************************************************
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +155,7 @@ pub trait Contextual: LangId {
 | 
			
		|||
///
 | 
			
		||||
/// Extiende [`Contextual`] con métodos para **instanciar** y configurar un nuevo contexto,
 | 
			
		||||
/// **renderizar los recursos** del documento (incluyendo el [`Favicon`], las hojas de estilo
 | 
			
		||||
/// [`StyleSheet`] y los *scripts* [`JavaScript`]), o extender el uso de **parámetros dinámicos
 | 
			
		||||
/// [`StyleSheet`] y los scripts [`JavaScript`]), o extender el uso de **parámetros dinámicos
 | 
			
		||||
/// tipados** con nuevos métodos.
 | 
			
		||||
///
 | 
			
		||||
/// # Ejemplos
 | 
			
		||||
| 
						 | 
				
			
			@ -258,14 +258,29 @@ impl Context {
 | 
			
		|||
    // Context RENDER ******************************************************************************
 | 
			
		||||
 | 
			
		||||
    /// Renderiza los recursos del contexto.
 | 
			
		||||
    pub fn render_assets(&self) -> Markup {
 | 
			
		||||
        html! {
 | 
			
		||||
            @if let Some(favicon) = &self.favicon {
 | 
			
		||||
                (favicon)
 | 
			
		||||
    pub fn render_assets(&mut self) -> Markup {
 | 
			
		||||
        use std::mem::take as mem_take;
 | 
			
		||||
 | 
			
		||||
        // Extrae temporalmente los recursos.
 | 
			
		||||
        let favicon = mem_take(&mut self.favicon); // Deja valor por defecto (None) en self.
 | 
			
		||||
        let stylesheets = mem_take(&mut self.stylesheets); // Assets<StyleSheet>::default() en self.
 | 
			
		||||
        let javascripts = mem_take(&mut self.javascripts); // Assets<JavaScript>::default() en self.
 | 
			
		||||
 | 
			
		||||
        // Renderiza con `&mut self` como contexto.
 | 
			
		||||
        let markup = html! {
 | 
			
		||||
            @if let Some(fi) = &favicon {
 | 
			
		||||
                (fi.render(self))
 | 
			
		||||
            }
 | 
			
		||||
            (self.stylesheets)
 | 
			
		||||
            (self.javascripts)
 | 
			
		||||
        }
 | 
			
		||||
            (stylesheets.render(self))
 | 
			
		||||
            (javascripts.render(self))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Restaura los campos tal y como estaban.
 | 
			
		||||
        self.favicon = favicon;
 | 
			
		||||
        self.stylesheets = stylesheets;
 | 
			
		||||
        self.javascripts = javascripts;
 | 
			
		||||
 | 
			
		||||
        markup
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Context PARAMS ******************************************************************************
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,23 +69,6 @@ impl fmt::Write for Escaper<'_> {
 | 
			
		|||
/// `.render()` or `.render_to()`. Since the default definitions of
 | 
			
		||||
/// these methods call each other, not doing this will result in
 | 
			
		||||
/// infinite recursion.
 | 
			
		||||
///
 | 
			
		||||
/// # Example
 | 
			
		||||
///
 | 
			
		||||
/// ```rust
 | 
			
		||||
/// use pagetop::prelude::*;
 | 
			
		||||
///
 | 
			
		||||
/// /// Provides a shorthand for linking to a CSS stylesheet.
 | 
			
		||||
/// pub struct Stylesheet(&'static str);
 | 
			
		||||
///
 | 
			
		||||
/// impl Render for Stylesheet {
 | 
			
		||||
///     fn render(&self) -> Markup {
 | 
			
		||||
///         html! {
 | 
			
		||||
///             link rel="stylesheet" type="text/css" href=(self.0);
 | 
			
		||||
///         }
 | 
			
		||||
///     }
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
pub trait Render {
 | 
			
		||||
    /// Renders `self` as a block of `Markup`.
 | 
			
		||||
    fn render(&self) -> Markup {
 | 
			
		||||
| 
						 | 
				
			
			@ -238,6 +221,10 @@ impl Markup {
 | 
			
		|||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.0.is_empty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_str(&self) -> &str {
 | 
			
		||||
        self.0.as_str()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Into<String>> PreEscaped<T> {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue