From 0c648fb95a03e3a8ae134c31e6ef5ecbfb71d76a Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sun, 22 Mar 2026 12:15:31 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20A=C3=B1ade=20acci=C3=B3n=20`AlterMa?= =?UTF-8?q?rkup`=20para=20filtrar=20render?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite a las extensiones transformar el `Markup` final de un componente mediante edición de texto. Se despacha como último paso del ciclo de renderizado. --- src/base/action.rs | 18 +++- src/base/action/component.rs | 3 + .../component/after_render_component.rs | 1 + .../component/alter_markup_component.rs | 93 +++++++++++++++++++ .../component/before_render_component.rs | 1 + src/core/action.rs | 20 +++- src/core/component/definition.rs | 14 +-- 7 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 src/base/action/component/alter_markup_component.rs diff --git a/src/base/action.rs b/src/base/action.rs index 977ae9e2..c4f5d27a 100644 --- a/src/base/action.rs +++ b/src/base/action.rs @@ -4,12 +4,26 @@ 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. +/// Se usa en acciones definidas en [`action::component`] y [`action::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); +/// Tipo de función para modificar el [`Markup`] generado por un componente. +/// +/// Se usa en [`action::component::AlterMarkup`] para permitir a las extensiones modificar 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 referencias mutables del componente `component` y del contexto `cx`, y toma +/// posesión del `markup` producido hasta ese momento. Devuelve el nuevo [`Markup`] modificado, que +/// se encadena como entrada para la siguiente acción registrada, si la hay. +pub type FnActionAlterMarkup = fn(component: &mut C, cx: &mut Context, markup: Markup) -> Markup; + +// **< Acciones por tipo >************************************************************************** + pub mod component; pub mod theme; diff --git a/src/base/action/component.rs b/src/base/action/component.rs index 30c7ba4a..924f4075 100644 --- a/src/base/action/component.rs +++ b/src/base/action/component.rs @@ -5,3 +5,6 @@ pub use before_render_component::*; mod after_render_component; pub use after_render_component::*; + +mod alter_markup_component; +pub use alter_markup_component::*; diff --git a/src/base/action/component/after_render_component.rs b/src/base/action/component/after_render_component.rs index c4fddffe..0bed68c5 100644 --- a/src/base/action/component/after_render_component.rs +++ b/src/base/action/component/after_render_component.rs @@ -65,6 +65,7 @@ impl AfterRender { ), |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( diff --git a/src/base/action/component/alter_markup_component.rs b/src/base/action/component/alter_markup_component.rs new file mode 100644 index 00000000..8bddd97a --- /dev/null +++ b/src/base/action/component/alter_markup_component.rs @@ -0,0 +1,93 @@ +use crate::prelude::*; + +use crate::base::action::FnActionAlterMarkup; +use crate::html::html; + +/// Ejecuta [`FnActionAlterMarkup`] para modificar el renderizado de un componente. +pub struct AlterMarkup { + f: FnActionAlterMarkup, + referer_type_id: Option, + referer_id: AttrId, + weight: Weight, +} + +/// Filtro para despachar [`FnActionAlterMarkup`] sobre el renderizado de un componente `C`. +impl ActionDispatcher for AlterMarkup { + /// 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 AlterMarkup { + /// Permite [registrar](Extension::actions) una nueva acción [`FnActionAlterMarkup`]. + pub fn new(f: FnActionAlterMarkup) -> Self { + AlterMarkup { + f, + referer_type_id: Some(UniqueId::of::()), + referer_id: AttrId::default(), + weight: 0, + } + } + + /// Afina el registro para ejecutar la acción [`FnActionAlterMarkup`] 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: &mut 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::(), + None, + 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::(), + None, + 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/component/before_render_component.rs b/src/base/action/component/before_render_component.rs index 7f0b5b57..14f756d4 100644 --- a/src/base/action/component/before_render_component.rs +++ b/src/base/action/component/before_render_component.rs @@ -65,6 +65,7 @@ impl BeforeRender { ), |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( diff --git a/src/core/action.rs b/src/core/action.rs index 92b3f1aa..de66b296 100644 --- a/src/core/action.rs +++ b/src/core/action.rs @@ -21,7 +21,9 @@ pub use all::dispatch_actions; /// Evita escribir repetidamente `Box::new(...)` para cada acción de la lista, manteniendo el código /// más limpio. /// -/// # Ejemplo +/// # Ejemplos +/// +/// Acciones de tema que ajustan un componente antes y después de renderizarlo: /// /// ```rust,ignore /// impl Extension for MyTheme { @@ -38,6 +40,22 @@ pub use all::dispatch_actions; /// fn before_render_button(c: &mut Button, cx: &mut Context) { todo!() } /// fn after_render_button(c: &mut Button, cx: &mut Context) { todo!() } /// ``` +/// +/// Acción de extensión que transforma el HTML final de un componente mediante edición de texto: +/// +/// ```rust,ignore +/// impl Extension for MyExtension { +/// fn actions(&self) -> Vec { +/// actions_boxed![ +/// action::component::AlterMarkup::