♻️ Refactoriza la API de Children e InRegion
- Patrón prototipo en `InRegion`: cada petición recibe clones profundos. - `ComponentClone` habilita clonar `dyn Component` de forma segura. - `ChildTyped<C>` renombrado a `Slot<C>`, elimina `ChildTypedOp`. - `Mutex` en lugar de `Arc<RwLock>` en `Child` y `Slot`. - `is_renderable` y `setup_before_prepare` reciben `&Context`. - Nuevos tests para `Children`, `ChildOp` y `Slot`.
This commit is contained in:
parent
04e3d5b3c2
commit
54f990b11c
33 changed files with 740 additions and 314 deletions
|
|
@ -5,9 +5,9 @@ use pagetop::prelude::*;
|
|||
/// Tipo de función para manipular componentes y su contexto de renderizado.
|
||||
///
|
||||
/// Se usa en [`action::component::BeforeRender`] y [`action::component::AfterRender`] para alterar
|
||||
/// el comportamiento predefinido de los componentes.
|
||||
/// el comportamiento predefinido de los componentes antes y después de renderizarlos.
|
||||
///
|
||||
/// Recibe referencias mutables (`&mut`) del componente `component` y del contexto `cx`.
|
||||
/// Recibe referencias mutables del componente `component` y del contexto `cx`.
|
||||
pub type FnActionWithComponent<C> = fn(component: &mut C, cx: &mut Context);
|
||||
|
||||
/// Tipo de función para alterar el [`Markup`] generado por un componente.
|
||||
|
|
@ -18,10 +18,12 @@ pub type FnActionWithComponent<C> = fn(component: &mut C, cx: &mut Context);
|
|||
/// sustituciones, concatenaciones y cualquier otra primitiva de trabajo con cadenas.
|
||||
///
|
||||
/// La función recibe una referencia inmutable al componente `component` (el renderizado ya ha
|
||||
/// concluido, solo se necesita leer su estado), una referencia mutable al contexto `cx`, y toma
|
||||
/// posesión del `markup` producido hasta ese momento. Devuelve el nuevo [`Markup`] transformado,
|
||||
/// que se encadena como entrada para la siguiente acción registrada, si la hay.
|
||||
pub type FnActionTransformMarkup<C> = fn(component: &C, cx: &mut Context, markup: Markup) -> Markup;
|
||||
/// concluido, solo se necesita leer su estado), y al contexto `cx` (solo para consulta), y toma
|
||||
/// posesión del `markup` producido hasta ese momento.
|
||||
///
|
||||
/// Devuelve el nuevo [`Markup`] transformado, que se encadena como entrada para la siguiente acción
|
||||
/// registrada, si la hay.
|
||||
pub type FnActionTransformMarkup<C> = fn(component: &C, cx: &Context, markup: Markup) -> Markup;
|
||||
|
||||
mod before_render_component;
|
||||
pub use before_render_component::*;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ impl<C: Component> TransformMarkup<C> {
|
|||
|
||||
/// Despacha las acciones encadenando el [`Markup`] entre cada una.
|
||||
#[inline]
|
||||
pub(crate) fn dispatch(component: &C, cx: &mut Context, markup: Markup) -> Markup {
|
||||
pub(crate) fn dispatch(component: &C, cx: &Context, markup: Markup) -> Markup {
|
||||
let mut output = markup;
|
||||
|
||||
// Primero despacha las acciones para el tipo de componente.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::prelude::*;
|
|||
///
|
||||
/// Los bloques se utilizan como contenedores de otros componentes o contenidos, con un título
|
||||
/// opcional y un cuerpo que sólo se renderiza si existen componentes hijos (*children*).
|
||||
#[derive(AutoDefault, Debug, Getters)]
|
||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||
pub struct Block {
|
||||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
|
|
@ -25,11 +25,11 @@ impl Component for Block {
|
|||
self.id.get()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
self.alter_classes(ClassesOp::Prepend, "block");
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
let block_body = self.children().render(cx);
|
||||
|
||||
if block_body.is_empty() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Componente básico que renderiza dinámicamente código HTML según el contexto.
|
||||
///
|
||||
|
|
@ -31,7 +32,8 @@ use std::fmt;
|
|||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
pub struct Html(Box<dyn Fn(&mut Context) -> Markup + Send + Sync>);
|
||||
#[derive(Clone)]
|
||||
pub struct Html(Arc<dyn Fn(&mut Context) -> Markup + Send + Sync>);
|
||||
|
||||
impl fmt::Debug for Html {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
@ -52,7 +54,7 @@ impl Component for Html {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
Ok(self.html(cx))
|
||||
}
|
||||
}
|
||||
|
|
@ -62,26 +64,26 @@ impl Html {
|
|||
|
||||
/// Crea una instancia que generará el `Markup`, con acceso opcional al contexto.
|
||||
///
|
||||
/// El método [`Self::prepare_component()`] delega el renderizado a la función que aquí se
|
||||
/// proporciona, que recibe una referencia mutable al [`Context`].
|
||||
/// El método [`Self::prepare()`] delega el renderizado a la función que aquí se proporciona,
|
||||
/// con una llamada que requiere una referencia mutable al [`Context`].
|
||||
pub fn with<F>(f: F) -> Self
|
||||
where
|
||||
F: Fn(&mut Context) -> Markup + Send + Sync + 'static,
|
||||
{
|
||||
Html(Box::new(f))
|
||||
Html(Arc::new(f))
|
||||
}
|
||||
|
||||
/// Sustituye la función que genera el `Markup`.
|
||||
///
|
||||
/// Permite a otras extensiones modificar la función de renderizado que se ejecutará cuando
|
||||
/// [`Self::prepare_component()`] invoque esta instancia. La nueva función también recibe una
|
||||
/// referencia al [`Context`].
|
||||
/// [`Self::prepare()`] invoque esta instancia. La nueva función también recibe una referencia
|
||||
/// mutable al [`Context`].
|
||||
#[builder_fn]
|
||||
pub fn with_fn<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(&mut Context) -> Markup + Send + Sync + 'static,
|
||||
{
|
||||
self.0 = Box::new(f);
|
||||
self.0 = Arc::new(f);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -91,8 +93,8 @@ impl Html {
|
|||
///
|
||||
/// Normalmente no se invoca manualmente, ya que el proceso de renderizado de los componentes lo
|
||||
/// invoca automáticamente durante la construcción de la página. Puede usarse, no obstante, para
|
||||
/// sobrescribir [`prepare_component()`](crate::core::component::Component::prepare_component)
|
||||
/// y alterar el comportamiento del componente.
|
||||
/// sobrescribir [`prepare()`](crate::core::component::Component::prepare) y alterar el
|
||||
/// comportamiento del componente.
|
||||
pub fn html(&self, cx: &mut Context) -> Markup {
|
||||
(self.0)(cx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ pub enum IntroOpening {
|
|||
/// })),
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Getters)]
|
||||
#[derive(Clone, Debug, Getters)]
|
||||
pub struct Intro {
|
||||
/// Devuelve el título de entrada.
|
||||
title: L10n,
|
||||
|
|
@ -109,13 +109,10 @@ impl Component for Intro {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, cx: &mut Context) {
|
||||
fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
cx.alter_assets(AssetsOp::AddStyleSheet(
|
||||
StyleSheet::from("/css/intro.css").with_version(PAGETOP_VERSION),
|
||||
));
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
if *self.opening() == IntroOpening::PageTop {
|
||||
cx.alter_assets(AssetsOp::AddJavaScript(JavaScript::on_load_async("intro-js", |cx|
|
||||
util::indoc!(r#"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const LINK: &str = "<a href=\"https://pagetop.cillero.es\" rel=\"noopener norefe
|
|||
/// Por defecto, usando [`default()`](Self::default) sólo se muestra un reconocimiento a PageTop.
|
||||
/// Sin embargo, se puede usar [`new()`](Self::new) para crear una instancia con un texto de
|
||||
/// copyright predeterminado.
|
||||
#[derive(AutoDefault, Debug, Getters)]
|
||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||
pub struct PoweredBy {
|
||||
/// Devuelve el texto de copyright actual, si existe.
|
||||
copyright: Option<String>,
|
||||
|
|
@ -25,7 +25,7 @@ impl Component for PoweredBy {
|
|||
PoweredBy { copyright: Some(c) }
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
Ok(html! {
|
||||
div id=[self.id()] class="poweredby" {
|
||||
@if let Some(c) = self.copyright() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue