diff --git a/src/base/action.rs b/src/base/action.rs index 977ae9e2..1e4debca 100644 --- a/src/base/action.rs +++ b/src/base/action.rs @@ -1,17 +1,5 @@ //! Acciones predefinidas para alterar el funcionamiento interno de PageTop. -use crate::prelude::*; - -/// Tipo de función para manipular componentes y su contexto de renderizado. -/// -/// Se usa en acciones definidas en [`component`] y [`theme`] para alterar el comportamiento de los -/// componentes. -/// -/// Recibe referencias mutables (`&mut`) del componente `component` y del contexto `cx`. -pub type FnActionWithComponent = fn(component: &mut C, cx: &mut Context); - pub mod component; -pub mod theme; - pub mod page; diff --git a/src/base/action/component.rs b/src/base/action/component.rs index 30c7ba4a..b25dd3b1 100644 --- a/src/base/action/component.rs +++ b/src/base/action/component.rs @@ -1,7 +1,33 @@ //! Acciones que operan sobre componentes. +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. +/// +/// Recibe referencias mutables (`&mut`) del componente `component` y del contexto `cx`. +pub type FnActionWithComponent = fn(component: &mut C, cx: &mut Context); + +/// Tipo de función para alterar el [`Markup`] generado por un componente. +/// +/// Se usa en [`action::component::TransformMarkup`] para permitir a las extensiones alterar el HTML +/// final producido por el renderizado de un componente. La edición trabaja a nivel de texto: el +/// [`Markup`] recibido expone su contenido como [`String`], lo que permite aplicar búsquedas, +/// 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 = fn(component: &C, cx: &mut Context, markup: Markup) -> Markup; + mod before_render_component; pub use before_render_component::*; mod after_render_component; pub use after_render_component::*; + +mod transform_markup_component; +pub use transform_markup_component::*; diff --git a/src/base/action/component/after_render_component.rs b/src/base/action/component/after_render_component.rs index c4fddffe..0778e9c8 100644 --- a/src/base/action/component/after_render_component.rs +++ b/src/base/action/component/after_render_component.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use crate::base::action::FnActionWithComponent; +use super::FnActionWithComponent; /// Ejecuta [`FnActionWithComponent`] después de renderizar un componente. pub struct AfterRender { @@ -57,23 +57,14 @@ impl AfterRender { pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { // Primero despacha las acciones para el tipo de componente. dispatch_actions( - &ActionKey::new( - UniqueId::of::(), - None, - Some(UniqueId::of::()), - None, - ), + &ActionKey::new(UniqueId::of::(), Some(UniqueId::of::()), None), |action: &Self| (action.f)(component, cx), ); + // Y luego despacha las acciones para el tipo de componente con un identificador dado. if let Some(id) = component.id() { dispatch_actions( - &ActionKey::new( - UniqueId::of::(), - None, - Some(UniqueId::of::()), - Some(id), - ), + &ActionKey::new(UniqueId::of::(), Some(UniqueId::of::()), Some(id)), |action: &Self| (action.f)(component, cx), ); } diff --git a/src/base/action/component/before_render_component.rs b/src/base/action/component/before_render_component.rs index 7f0b5b57..051a3dd6 100644 --- a/src/base/action/component/before_render_component.rs +++ b/src/base/action/component/before_render_component.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use crate::base::action::FnActionWithComponent; +use super::FnActionWithComponent; /// Ejecuta [`FnActionWithComponent`] antes de renderizar el componente. pub struct BeforeRender { @@ -57,23 +57,14 @@ impl BeforeRender { pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { // Primero despacha las acciones para el tipo de componente. dispatch_actions( - &ActionKey::new( - UniqueId::of::(), - None, - Some(UniqueId::of::()), - None, - ), + &ActionKey::new(UniqueId::of::(), Some(UniqueId::of::()), None), |action: &Self| (action.f)(component, cx), ); + // Y luego despacha las aciones para el tipo de componente con un identificador dado. if let Some(id) = component.id() { dispatch_actions( - &ActionKey::new( - UniqueId::of::(), - None, - Some(UniqueId::of::()), - Some(id), - ), + &ActionKey::new(UniqueId::of::(), Some(UniqueId::of::()), Some(id)), |action: &Self| (action.f)(component, cx), ); } diff --git a/src/base/action/component/transform_markup_component.rs b/src/base/action/component/transform_markup_component.rs new file mode 100644 index 00000000..0dfd1f51 --- /dev/null +++ b/src/base/action/component/transform_markup_component.rs @@ -0,0 +1,82 @@ +use crate::prelude::*; + +use super::FnActionTransformMarkup; + +/// Ejecuta [`FnActionTransformMarkup`] para alterar el renderizado de componentes. +pub struct TransformMarkup { + f: FnActionTransformMarkup, + referer_type_id: Option, + referer_id: AttrId, + weight: Weight, +} + +/// Filtro para despachar [`FnActionTransformMarkup`] sobre el renderizado de un componente `C`. +impl ActionDispatcher for TransformMarkup { + /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. + fn referer_type_id(&self) -> Option { + self.referer_type_id + } + + /// Devuelve el identificador del componente. + fn referer_id(&self) -> Option { + self.referer_id.get() + } + + /// Devuelve el peso para definir el orden de ejecución. + fn weight(&self) -> Weight { + self.weight + } +} + +impl TransformMarkup { + /// Permite [registrar](Extension::actions) una nueva acción [`FnActionTransformMarkup`]. + pub fn new(f: FnActionTransformMarkup) -> Self { + TransformMarkup { + f, + referer_type_id: Some(UniqueId::of::()), + referer_id: AttrId::default(), + weight: 0, + } + } + + /// Afina el registro para ejecutar la acción [`FnActionTransformMarkup`] sólo para el + /// componente `C` con identificador `id`. + pub fn filter_by_referer_id(mut self, id: impl AsRef) -> Self { + self.referer_id.alter_id(id); + self + } + + /// Opcional. Acciones con pesos más bajos se aplican antes. Se pueden usar valores negativos. + pub fn with_weight(mut self, value: Weight) -> Self { + self.weight = value; + self + } + + /// Despacha las acciones encadenando el [`Markup`] entre cada una. + #[inline] + pub(crate) fn dispatch(component: &C, cx: &mut Context, markup: Markup) -> Markup { + let mut output = markup; + + // Primero despacha las acciones para el tipo de componente. + dispatch_actions( + &ActionKey::new(UniqueId::of::(), Some(UniqueId::of::()), None), + |action: &Self| { + let taken = std::mem::replace(&mut output, html! {}); + output = (action.f)(component, cx, taken); + }, + ); + + // Y luego despacha las acciones para el tipo de componente con un identificador dado. + if let Some(id) = component.id() { + dispatch_actions( + &ActionKey::new(UniqueId::of::(), Some(UniqueId::of::()), Some(id)), + |action: &Self| { + let taken = std::mem::replace(&mut output, html! {}); + output = (action.f)(component, cx, taken); + }, + ); + } + + output + } +} diff --git a/src/base/action/page/after_render_body.rs b/src/base/action/page/after_render_body.rs index 7ecc353a..74ee0df1 100644 --- a/src/base/action/page/after_render_body.rs +++ b/src/base/action/page/after_render_body.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use crate::base::action::page::FnActionWithPage; +use super::FnActionWithPage; /// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) después de renderizar /// el cuerpo de la página. @@ -39,7 +39,7 @@ impl AfterRenderBody { #[allow(clippy::inline_always)] pub(crate) fn dispatch(page: &mut Page) { dispatch_actions( - &ActionKey::new(UniqueId::of::(), None, None, None), + &ActionKey::new(UniqueId::of::(), None, None), |action: &Self| (action.f)(page), ); } diff --git a/src/base/action/page/before_render_body.rs b/src/base/action/page/before_render_body.rs index d4ae64d0..7a72854f 100644 --- a/src/base/action/page/before_render_body.rs +++ b/src/base/action/page/before_render_body.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use crate::base::action::page::FnActionWithPage; +use super::FnActionWithPage; /// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) antes de renderizar /// el cuerpo de la página. @@ -39,7 +39,7 @@ impl BeforeRenderBody { #[allow(clippy::inline_always)] pub(crate) fn dispatch(page: &mut Page) { dispatch_actions( - &ActionKey::new(UniqueId::of::(), None, None, None), + &ActionKey::new(UniqueId::of::(), None, None), |action: &Self| (action.f)(page), ); } diff --git a/src/base/action/theme.rs b/src/base/action/theme.rs deleted file mode 100644 index a3db61a0..00000000 --- a/src/base/action/theme.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Acciones lanzadas desde los temas. - -mod before_render_component; -pub use before_render_component::*; - -mod after_render_component; -pub use after_render_component::*; diff --git a/src/base/action/theme/after_render_component.rs b/src/base/action/theme/after_render_component.rs deleted file mode 100644 index a31ad56a..00000000 --- a/src/base/action/theme/after_render_component.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::prelude::*; - -use crate::base::action::FnActionWithComponent; - -/// Ejecuta [`FnActionWithComponent`] después de que un tema renderice el componente. -pub struct AfterRender { - f: FnActionWithComponent, - theme_type_id: Option, - referer_type_id: Option, -} - -/// Filtro para despachar [`FnActionWithComponent`] después de que un tema renderice el componente -/// `C`. -impl ActionDispatcher for AfterRender { - /// Devuelve el identificador de tipo ([`UniqueId`]) del tema. - fn theme_type_id(&self) -> Option { - self.theme_type_id - } - - /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. - fn referer_type_id(&self) -> Option { - self.referer_type_id - } -} - -impl AfterRender { - /// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`] para un - /// tema dado. - pub fn new(theme: ThemeRef, f: FnActionWithComponent) -> Self { - AfterRender { - f, - theme_type_id: Some(theme.type_id()), - referer_type_id: Some(UniqueId::of::()), - } - } - - /// Despacha las acciones. - #[inline] - pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { - dispatch_actions( - &ActionKey::new( - UniqueId::of::(), - Some(cx.theme().type_id()), - Some(UniqueId::of::()), - None, - ), - |action: &Self| (action.f)(component, cx), - ); - } -} diff --git a/src/base/action/theme/before_render_component.rs b/src/base/action/theme/before_render_component.rs deleted file mode 100644 index c00de8e9..00000000 --- a/src/base/action/theme/before_render_component.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::prelude::*; - -use crate::base::action::FnActionWithComponent; - -/// Ejecuta [`FnActionWithComponent`] antes de que un tema renderice el componente. -pub struct BeforeRender { - f: FnActionWithComponent, - theme_type_id: Option, - referer_type_id: Option, -} - -/// Filtro para despachar [`FnActionWithComponent`] antes de que un tema renderice el componente -/// `C`. -impl ActionDispatcher for BeforeRender { - /// Devuelve el identificador de tipo ([`UniqueId`]) del tema. - fn theme_type_id(&self) -> Option { - self.theme_type_id - } - - /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. - fn referer_type_id(&self) -> Option { - self.referer_type_id - } -} - -impl BeforeRender { - /// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`] para un - /// tema dado. - pub fn new(theme: ThemeRef, f: FnActionWithComponent) -> Self { - BeforeRender { - f, - theme_type_id: Some(theme.type_id()), - referer_type_id: Some(UniqueId::of::()), - } - } - - /// Despacha las acciones. - #[inline] - pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { - dispatch_actions( - &ActionKey::new( - UniqueId::of::(), - Some(cx.theme().type_id()), - Some(UniqueId::of::()), - None, - ), - |action: &Self| (action.f)(component, cx), - ); - } -} diff --git a/src/core/action.rs b/src/core/action.rs index 92b3f1aa..ceb5eef4 100644 --- a/src/core/action.rs +++ b/src/core/action.rs @@ -14,7 +14,7 @@ mod all; pub(crate) use all::add_action; pub use all::dispatch_actions; -// **< actions_boxed! >***************************************************************************** +// **< actions! >*********************************************************************************** /// Facilita la implementación del método [`actions()`](crate::core::extension::Extension::actions). /// @@ -23,23 +23,28 @@ pub use all::dispatch_actions; /// /// # Ejemplo /// +/// Extensión que ajusta un botón antes de renderizarlo y transforma su HTML final: +/// /// ```rust,ignore -/// impl Extension for MyTheme { +/// impl Extension for MyExtension { /// fn actions(&self) -> Vec { -/// actions_boxed![ -/// action::theme::BeforeRender::