✨ Completa la API de temas con setup_component!
Elimina `action::theme` fusionando sus responsabilidades en `action::component`. Renombra `AlterMarkup` a `TransformMarkup` y `FnActionAlterMarkup` a `FnActionTransformMarkup`. Simplifica `ActionKey` y mueve los tipos de función al módulo de componente.
This commit is contained in:
parent
0c648fb95a
commit
04e3d5b3c2
18 changed files with 178 additions and 279 deletions
|
|
@ -1,31 +1,5 @@
|
||||||
//! Acciones predefinidas para alterar el funcionamiento interno de PageTop.
|
//! 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 [`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<C> = 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<C> = fn(component: &mut C, cx: &mut Context, markup: Markup) -> Markup;
|
|
||||||
|
|
||||||
// **< Acciones por tipo >**************************************************************************
|
|
||||||
|
|
||||||
pub mod component;
|
pub mod component;
|
||||||
|
|
||||||
pub mod theme;
|
|
||||||
|
|
||||||
pub mod page;
|
pub mod page;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,33 @@
|
||||||
//! Acciones que operan sobre componentes.
|
//! 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<C> = 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<C> = fn(component: &C, cx: &mut Context, markup: Markup) -> Markup;
|
||||||
|
|
||||||
mod before_render_component;
|
mod before_render_component;
|
||||||
pub use before_render_component::*;
|
pub use before_render_component::*;
|
||||||
|
|
||||||
mod after_render_component;
|
mod after_render_component;
|
||||||
pub use after_render_component::*;
|
pub use after_render_component::*;
|
||||||
|
|
||||||
mod alter_markup_component;
|
mod transform_markup_component;
|
||||||
pub use alter_markup_component::*;
|
pub use transform_markup_component::*;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::base::action::FnActionWithComponent;
|
use super::FnActionWithComponent;
|
||||||
|
|
||||||
/// Ejecuta [`FnActionWithComponent`] después de renderizar un componente.
|
/// Ejecuta [`FnActionWithComponent`] después de renderizar un componente.
|
||||||
pub struct AfterRender<C: Component> {
|
pub struct AfterRender<C: Component> {
|
||||||
|
|
@ -57,24 +57,14 @@ impl<C: Component> AfterRender<C> {
|
||||||
pub(crate) fn dispatch(component: &mut C, cx: &mut Context) {
|
pub(crate) fn dispatch(component: &mut C, cx: &mut Context) {
|
||||||
// Primero despacha las acciones para el tipo de componente.
|
// Primero despacha las acciones para el tipo de componente.
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(
|
&ActionKey::new(UniqueId::of::<Self>(), Some(UniqueId::of::<C>()), None),
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|action: &Self| (action.f)(component, cx),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Y luego despacha las acciones para el tipo de componente con un identificador dado.
|
// Y luego despacha las acciones para el tipo de componente con un identificador dado.
|
||||||
if let Some(id) = component.id() {
|
if let Some(id) = component.id() {
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(
|
&ActionKey::new(UniqueId::of::<Self>(), Some(UniqueId::of::<C>()), Some(id)),
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
Some(id),
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|action: &Self| (action.f)(component, cx),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::base::action::FnActionWithComponent;
|
use super::FnActionWithComponent;
|
||||||
|
|
||||||
/// Ejecuta [`FnActionWithComponent`] antes de renderizar el componente.
|
/// Ejecuta [`FnActionWithComponent`] antes de renderizar el componente.
|
||||||
pub struct BeforeRender<C: Component> {
|
pub struct BeforeRender<C: Component> {
|
||||||
|
|
@ -57,24 +57,14 @@ impl<C: Component> BeforeRender<C> {
|
||||||
pub(crate) fn dispatch(component: &mut C, cx: &mut Context) {
|
pub(crate) fn dispatch(component: &mut C, cx: &mut Context) {
|
||||||
// Primero despacha las acciones para el tipo de componente.
|
// Primero despacha las acciones para el tipo de componente.
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(
|
&ActionKey::new(UniqueId::of::<Self>(), Some(UniqueId::of::<C>()), None),
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|action: &Self| (action.f)(component, cx),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Y luego despacha las aciones para el tipo de componente con un identificador dado.
|
// Y luego despacha las aciones para el tipo de componente con un identificador dado.
|
||||||
if let Some(id) = component.id() {
|
if let Some(id) = component.id() {
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(
|
&ActionKey::new(UniqueId::of::<Self>(), Some(UniqueId::of::<C>()), Some(id)),
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
Some(id),
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|action: &Self| (action.f)(component, cx),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::base::action::FnActionAlterMarkup;
|
use super::FnActionTransformMarkup;
|
||||||
use crate::html::html;
|
|
||||||
|
|
||||||
/// Ejecuta [`FnActionAlterMarkup`] para modificar el renderizado de un componente.
|
/// Ejecuta [`FnActionTransformMarkup`] para alterar el renderizado de componentes.
|
||||||
pub struct AlterMarkup<C: Component> {
|
pub struct TransformMarkup<C: Component> {
|
||||||
f: FnActionAlterMarkup<C>,
|
f: FnActionTransformMarkup<C>,
|
||||||
referer_type_id: Option<UniqueId>,
|
referer_type_id: Option<UniqueId>,
|
||||||
referer_id: AttrId,
|
referer_id: AttrId,
|
||||||
weight: Weight,
|
weight: Weight,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filtro para despachar [`FnActionAlterMarkup`] sobre el renderizado de un componente `C`.
|
/// Filtro para despachar [`FnActionTransformMarkup`] sobre el renderizado de un componente `C`.
|
||||||
impl<C: Component> ActionDispatcher for AlterMarkup<C> {
|
impl<C: Component> ActionDispatcher for TransformMarkup<C> {
|
||||||
/// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`.
|
/// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`.
|
||||||
fn referer_type_id(&self) -> Option<UniqueId> {
|
fn referer_type_id(&self) -> Option<UniqueId> {
|
||||||
self.referer_type_id
|
self.referer_type_id
|
||||||
|
|
@ -29,10 +28,10 @@ impl<C: Component> ActionDispatcher for AlterMarkup<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Component> AlterMarkup<C> {
|
impl<C: Component> TransformMarkup<C> {
|
||||||
/// Permite [registrar](Extension::actions) una nueva acción [`FnActionAlterMarkup`].
|
/// Permite [registrar](Extension::actions) una nueva acción [`FnActionTransformMarkup`].
|
||||||
pub fn new(f: FnActionAlterMarkup<C>) -> Self {
|
pub fn new(f: FnActionTransformMarkup<C>) -> Self {
|
||||||
AlterMarkup {
|
TransformMarkup {
|
||||||
f,
|
f,
|
||||||
referer_type_id: Some(UniqueId::of::<C>()),
|
referer_type_id: Some(UniqueId::of::<C>()),
|
||||||
referer_id: AttrId::default(),
|
referer_id: AttrId::default(),
|
||||||
|
|
@ -40,8 +39,8 @@ impl<C: Component> AlterMarkup<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Afina el registro para ejecutar la acción [`FnActionAlterMarkup`] sólo para el componente
|
/// Afina el registro para ejecutar la acción [`FnActionTransformMarkup`] sólo para el
|
||||||
/// `C` con identificador `id`.
|
/// componente `C` con identificador `id`.
|
||||||
pub fn filter_by_referer_id(mut self, id: impl AsRef<str>) -> Self {
|
pub fn filter_by_referer_id(mut self, id: impl AsRef<str>) -> Self {
|
||||||
self.referer_id.alter_id(id);
|
self.referer_id.alter_id(id);
|
||||||
self
|
self
|
||||||
|
|
@ -55,17 +54,12 @@ impl<C: Component> AlterMarkup<C> {
|
||||||
|
|
||||||
/// Despacha las acciones encadenando el [`Markup`] entre cada una.
|
/// Despacha las acciones encadenando el [`Markup`] entre cada una.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn dispatch(component: &mut C, cx: &mut Context, markup: Markup) -> Markup {
|
pub(crate) fn dispatch(component: &C, cx: &mut Context, markup: Markup) -> Markup {
|
||||||
let mut output = markup;
|
let mut output = markup;
|
||||||
|
|
||||||
// Primero despacha las acciones para el tipo de componente.
|
// Primero despacha las acciones para el tipo de componente.
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(
|
&ActionKey::new(UniqueId::of::<Self>(), Some(UniqueId::of::<C>()), None),
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|action: &Self| {
|
|action: &Self| {
|
||||||
let taken = std::mem::replace(&mut output, html! {});
|
let taken = std::mem::replace(&mut output, html! {});
|
||||||
output = (action.f)(component, cx, taken);
|
output = (action.f)(component, cx, taken);
|
||||||
|
|
@ -75,12 +69,7 @@ impl<C: Component> AlterMarkup<C> {
|
||||||
// Y luego despacha las acciones para el tipo de componente con un identificador dado.
|
// Y luego despacha las acciones para el tipo de componente con un identificador dado.
|
||||||
if let Some(id) = component.id() {
|
if let Some(id) = component.id() {
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(
|
&ActionKey::new(UniqueId::of::<Self>(), Some(UniqueId::of::<C>()), Some(id)),
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
Some(id),
|
|
||||||
),
|
|
||||||
|action: &Self| {
|
|action: &Self| {
|
||||||
let taken = std::mem::replace(&mut output, html! {});
|
let taken = std::mem::replace(&mut output, html! {});
|
||||||
output = (action.f)(component, cx, taken);
|
output = (action.f)(component, cx, taken);
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::base::action::page::FnActionWithPage;
|
use super::FnActionWithPage;
|
||||||
|
|
||||||
/// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) después de renderizar
|
/// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) después de renderizar
|
||||||
/// el cuerpo de la página.
|
/// el cuerpo de la página.
|
||||||
|
|
@ -39,7 +39,7 @@ impl AfterRenderBody {
|
||||||
#[allow(clippy::inline_always)]
|
#[allow(clippy::inline_always)]
|
||||||
pub(crate) fn dispatch(page: &mut Page) {
|
pub(crate) fn dispatch(page: &mut Page) {
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(UniqueId::of::<Self>(), None, None, None),
|
&ActionKey::new(UniqueId::of::<Self>(), None, None),
|
||||||
|action: &Self| (action.f)(page),
|
|action: &Self| (action.f)(page),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::base::action::page::FnActionWithPage;
|
use super::FnActionWithPage;
|
||||||
|
|
||||||
/// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) antes de renderizar
|
/// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) antes de renderizar
|
||||||
/// el cuerpo de la página.
|
/// el cuerpo de la página.
|
||||||
|
|
@ -39,7 +39,7 @@ impl BeforeRenderBody {
|
||||||
#[allow(clippy::inline_always)]
|
#[allow(clippy::inline_always)]
|
||||||
pub(crate) fn dispatch(page: &mut Page) {
|
pub(crate) fn dispatch(page: &mut Page) {
|
||||||
dispatch_actions(
|
dispatch_actions(
|
||||||
&ActionKey::new(UniqueId::of::<Self>(), None, None, None),
|
&ActionKey::new(UniqueId::of::<Self>(), None, None),
|
||||||
|action: &Self| (action.f)(page),
|
|action: &Self| (action.f)(page),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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::*;
|
|
||||||
|
|
@ -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<C: Component> {
|
|
||||||
f: FnActionWithComponent<C>,
|
|
||||||
theme_type_id: Option<UniqueId>,
|
|
||||||
referer_type_id: Option<UniqueId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Filtro para despachar [`FnActionWithComponent`] después de que un tema renderice el componente
|
|
||||||
/// `C`.
|
|
||||||
impl<C: Component> ActionDispatcher for AfterRender<C> {
|
|
||||||
/// Devuelve el identificador de tipo ([`UniqueId`]) del tema.
|
|
||||||
fn theme_type_id(&self) -> Option<UniqueId> {
|
|
||||||
self.theme_type_id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`.
|
|
||||||
fn referer_type_id(&self) -> Option<UniqueId> {
|
|
||||||
self.referer_type_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: Component> AfterRender<C> {
|
|
||||||
/// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`] para un
|
|
||||||
/// tema dado.
|
|
||||||
pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self {
|
|
||||||
AfterRender {
|
|
||||||
f,
|
|
||||||
theme_type_id: Some(theme.type_id()),
|
|
||||||
referer_type_id: Some(UniqueId::of::<C>()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Despacha las acciones.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn dispatch(component: &mut C, cx: &mut Context) {
|
|
||||||
dispatch_actions(
|
|
||||||
&ActionKey::new(
|
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
Some(cx.theme().type_id()),
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<C: Component> {
|
|
||||||
f: FnActionWithComponent<C>,
|
|
||||||
theme_type_id: Option<UniqueId>,
|
|
||||||
referer_type_id: Option<UniqueId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Filtro para despachar [`FnActionWithComponent`] antes de que un tema renderice el componente
|
|
||||||
/// `C`.
|
|
||||||
impl<C: Component> ActionDispatcher for BeforeRender<C> {
|
|
||||||
/// Devuelve el identificador de tipo ([`UniqueId`]) del tema.
|
|
||||||
fn theme_type_id(&self) -> Option<UniqueId> {
|
|
||||||
self.theme_type_id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`.
|
|
||||||
fn referer_type_id(&self) -> Option<UniqueId> {
|
|
||||||
self.referer_type_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: Component> BeforeRender<C> {
|
|
||||||
/// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`] para un
|
|
||||||
/// tema dado.
|
|
||||||
pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self {
|
|
||||||
BeforeRender {
|
|
||||||
f,
|
|
||||||
theme_type_id: Some(theme.type_id()),
|
|
||||||
referer_type_id: Some(UniqueId::of::<C>()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Despacha las acciones.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn dispatch(component: &mut C, cx: &mut Context) {
|
|
||||||
dispatch_actions(
|
|
||||||
&ActionKey::new(
|
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
Some(cx.theme().type_id()),
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,50 +14,37 @@ mod all;
|
||||||
pub(crate) use all::add_action;
|
pub(crate) use all::add_action;
|
||||||
pub use all::dispatch_actions;
|
pub use all::dispatch_actions;
|
||||||
|
|
||||||
// **< actions_boxed! >*****************************************************************************
|
// **< actions! >***********************************************************************************
|
||||||
|
|
||||||
/// Facilita la implementación del método [`actions()`](crate::core::extension::Extension::actions).
|
/// Facilita la implementación del método [`actions()`](crate::core::extension::Extension::actions).
|
||||||
///
|
///
|
||||||
/// Evita escribir repetidamente `Box::new(...)` para cada acción de la lista, manteniendo el código
|
/// Evita escribir repetidamente `Box::new(...)` para cada acción de la lista, manteniendo el código
|
||||||
/// más limpio.
|
/// más limpio.
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// Acciones de tema que ajustan un componente antes y después de renderizarlo:
|
/// Extensión que ajusta un botón antes de renderizarlo y transforma su HTML final:
|
||||||
///
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// impl Extension for MyTheme {
|
|
||||||
/// fn actions(&self) -> Vec<ActionBox> {
|
|
||||||
/// actions_boxed![
|
|
||||||
/// action::theme::BeforeRender::<Button>::new(&Self, before_render_button),
|
|
||||||
/// action::theme::AfterRender::<Button>::new(&Self, after_render_button),
|
|
||||||
/// ]
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl Theme for MyTheme {}
|
|
||||||
///
|
|
||||||
/// 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
|
/// ```rust,ignore
|
||||||
/// impl Extension for MyExtension {
|
/// impl Extension for MyExtension {
|
||||||
/// fn actions(&self) -> Vec<ActionBox> {
|
/// fn actions(&self) -> Vec<ActionBox> {
|
||||||
/// actions_boxed![
|
/// actions![
|
||||||
/// action::component::AlterMarkup::<Button>::new(alter_button_markup),
|
/// action::component::BeforeRender::<Button>::new(before_render_button),
|
||||||
|
/// action::component::TransformMarkup::<Button>::new(transform_button_markup),
|
||||||
/// ]
|
/// ]
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn alter_button_markup(c: &mut Button, cx: &mut Context, markup: Markup) -> Markup {
|
/// fn before_render_button(c: &mut Button, cx: &mut Context) {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn transform_button_markup(c: &Button, cx: &mut Context, markup: Markup) -> Markup {
|
||||||
/// todo!()
|
/// todo!()
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! actions_boxed {
|
macro_rules! actions {
|
||||||
() => {
|
() => {
|
||||||
Vec::<ActionBox>::new()
|
Vec::<ActionBox>::new()
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ static ACTIONS: LazyLock<RwLock<HashMap<ActionKey, ActionsList>>> =
|
||||||
pub(crate) fn add_action(action: ActionBox) {
|
pub(crate) fn add_action(action: ActionBox) {
|
||||||
let key = ActionKey::new(
|
let key = ActionKey::new(
|
||||||
action.type_id(),
|
action.type_id(),
|
||||||
action.theme_type_id(),
|
|
||||||
action.referer_type_id(),
|
action.referer_type_id(),
|
||||||
action.referer_id(),
|
action.referer_id(),
|
||||||
);
|
);
|
||||||
|
|
@ -55,7 +54,6 @@ pub(crate) fn add_action(action: ActionBox) {
|
||||||
/// dispatch_actions(
|
/// dispatch_actions(
|
||||||
/// &ActionKey::new(
|
/// &ActionKey::new(
|
||||||
/// UniqueId::of::<Self>(),
|
/// UniqueId::of::<Self>(),
|
||||||
/// Some(cx.theme().type_id()),
|
|
||||||
/// Some(UniqueId::of::<C>()),
|
/// Some(UniqueId::of::<C>()),
|
||||||
/// None,
|
/// None,
|
||||||
/// ),
|
/// ),
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ pub type ActionBox = Box<dyn ActionDispatcher>;
|
||||||
#[derive(Eq, PartialEq, Hash)]
|
#[derive(Eq, PartialEq, Hash)]
|
||||||
pub struct ActionKey {
|
pub struct ActionKey {
|
||||||
action_type_id: UniqueId,
|
action_type_id: UniqueId,
|
||||||
theme_type_id: Option<UniqueId>,
|
|
||||||
referer_type_id: Option<UniqueId>,
|
referer_type_id: Option<UniqueId>,
|
||||||
referer_id: Option<String>,
|
referer_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
@ -22,22 +21,19 @@ impl ActionKey {
|
||||||
/// Se crea con los siguientes campos:
|
/// Se crea con los siguientes campos:
|
||||||
///
|
///
|
||||||
/// - `action_type_id`: Tipo de la acción.
|
/// - `action_type_id`: Tipo de la acción.
|
||||||
/// - `theme_type_id`: Opcional, identificador de tipo ([`UniqueId`]) del tema asociado.
|
|
||||||
/// - `referer_type_id`: Opcional, identificador de tipo ([`UniqueId`]) del componente referido.
|
/// - `referer_type_id`: Opcional, identificador de tipo ([`UniqueId`]) del componente referido.
|
||||||
/// - `referer_id`: Opcional, identificador de la instancia (p. ej. para asociar la acción a un
|
/// - `referer_id`: Opcional, identificador de la instancia (p. ej. para asociar la acción a un
|
||||||
/// componente concreto).
|
/// componente concreto).
|
||||||
///
|
///
|
||||||
/// Esta clave permitirá seleccionar las funciones a ejecutar para ese tipo de acción, con
|
/// Esta clave permitirá seleccionar las funciones a ejecutar para ese tipo de acción, con
|
||||||
/// filtros opcionales por tema, componente, o una instancia concreta según su identificador.
|
/// filtros opcionales por componente o por una instancia concreta según su identificador.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
action_type_id: UniqueId,
|
action_type_id: UniqueId,
|
||||||
theme_type_id: Option<UniqueId>,
|
|
||||||
referer_type_id: Option<UniqueId>,
|
referer_type_id: Option<UniqueId>,
|
||||||
referer_id: Option<String>,
|
referer_id: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ActionKey {
|
ActionKey {
|
||||||
action_type_id,
|
action_type_id,
|
||||||
theme_type_id,
|
|
||||||
referer_type_id,
|
referer_type_id,
|
||||||
referer_id,
|
referer_id,
|
||||||
}
|
}
|
||||||
|
|
@ -49,11 +45,6 @@ impl ActionKey {
|
||||||
/// Las acciones tienen que sobrescribir los métodos para el filtro que apliquen. Por defecto
|
/// Las acciones tienen que sobrescribir los métodos para el filtro que apliquen. Por defecto
|
||||||
/// implementa un filtro nulo.
|
/// implementa un filtro nulo.
|
||||||
pub trait ActionDispatcher: AnyInfo + Send + Sync {
|
pub trait ActionDispatcher: AnyInfo + Send + Sync {
|
||||||
/// Identificador de tipo ([`UniqueId`]) del tema asociado. En este caso devuelve `None`.
|
|
||||||
fn theme_type_id(&self) -> Option<UniqueId> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Identificador de tipo ([`UniqueId`]) del objeto referido. En este caso devuelve `None`.
|
/// Identificador de tipo ([`UniqueId`]) del objeto referido. En este caso devuelve `None`.
|
||||||
fn referer_type_id(&self) -> Option<UniqueId> {
|
fn referer_type_id(&self) -> Option<UniqueId> {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,10 @@ pub trait Component: AnyInfo + ComponentRender + Send + Sync {
|
||||||
///
|
///
|
||||||
/// Este método forma parte del ciclo de vida de los componentes y se invoca automáticamente
|
/// Este método forma parte del ciclo de vida de los componentes y se invoca automáticamente
|
||||||
/// durante el proceso de construcción del documento cuando ningún tema sobrescribe el
|
/// durante el proceso de construcción del documento cuando ningún tema sobrescribe el
|
||||||
/// renderizado mediante [`Theme::prepare_component()`](crate::core::theme::Theme::prepare_component).
|
/// renderizado mediante [`Theme::handle_component()`](crate::core::theme::Theme::handle_component).
|
||||||
///
|
///
|
||||||
/// Se recomienda obtener los datos del componente a través de sus propios métodos para que los
|
/// Se recomienda obtener los datos del componente a través de sus propios métodos para que los
|
||||||
/// temas puedan implementar [`Theme::prepare_component()`](crate::core::theme::Theme::prepare_component)
|
/// temas puedan implementar [`Theme::handle_component()`](crate::core::theme::Theme::handle_component)
|
||||||
/// sin depender de los detalles internos del componente.
|
/// sin depender de los detalles internos del componente.
|
||||||
///
|
///
|
||||||
/// Por defecto, devuelve un [`Markup`] vacío (`Ok(html! {})`).
|
/// Por defecto, devuelve un [`Markup`] vacío (`Ok(html! {})`).
|
||||||
|
|
@ -99,23 +99,17 @@ pub trait Component: AnyInfo + ComponentRender + Send + Sync {
|
||||||
/// contexto actual. Si no es así, devuelve un [`Markup`] vacío.
|
/// contexto actual. Si no es así, devuelve un [`Markup`] vacío.
|
||||||
/// 2. Ejecuta [`setup_before_prepare()`](Component::setup_before_prepare) para que el componente
|
/// 2. Ejecuta [`setup_before_prepare()`](Component::setup_before_prepare) para que el componente
|
||||||
/// pueda ajustar su estructura interna o modificar el contexto.
|
/// pueda ajustar su estructura interna o modificar el contexto.
|
||||||
/// 3. Despacha [`action::theme::BeforeRender<C>`](crate::base::action::theme::BeforeRender) para
|
/// 3. Despacha [`action::component::BeforeRender<C>`](crate::base::action::component::BeforeRender)
|
||||||
/// permitir que el tema realice ajustes previos.
|
/// para que las extensiones puedan hacer ajustes previos.
|
||||||
/// 4. Despacha [`action::component::BeforeRender<C>`](crate::base::action::component::BeforeRender)
|
/// 4. **Prepara el renderizado del componente** recorriendo la cadena de temas (hijo → padre →
|
||||||
/// para que otras extensiones puedan también hacer ajustes previos.
|
/// abuelo…) llamando a [`Theme::handle_component()`](crate::core::theme::Theme::handle_component)
|
||||||
/// 5. **Prepara el renderizado del componente**:
|
/// en cada nivel hasta que uno devuelva `Some`. Si ninguno lo sobrescribe, llama a
|
||||||
/// - Recorre la cadena de temas llamando a
|
/// [`Component::prepare_component()`](Component::prepare_component) del propio componente.
|
||||||
/// [`Theme::prepare_component()`](crate::core::theme::Theme::prepare_component) en cada nivel
|
/// 5. Despacha [`action::component::AfterRender<C>`](crate::base::action::component::AfterRender)
|
||||||
/// (hijo → padre → abuelo…) hasta que uno devuelva `Some`.
|
/// para que las extensiones puedan reaccionar con sus últimos ajustes.
|
||||||
/// - Si ningún tema lo sobrescribe, llama a
|
/// 6. Despacha [`action::component::TransformMarkup<C>`](crate::base::action::component::TransformMarkup)
|
||||||
/// [`Component::prepare_component()`](Component::prepare_component) del propio componente.
|
|
||||||
/// 6. Despacha [`action::theme::AfterRender<C>`](crate::base::action::theme::AfterRender) para que
|
|
||||||
/// el tema pueda reaccionar tras el renderizado.
|
|
||||||
/// 7. Despacha [`action::component::AfterRender<C>`](crate::base::action::component::AfterRender)
|
|
||||||
/// para que otras extensiones puedan reaccionar con sus últimos ajustes.
|
|
||||||
/// 8. Despacha [`action::component::AlterMarkup<C>`](crate::base::action::component::AlterMarkup)
|
|
||||||
/// para que las extensiones puedan modificar el HTML final antes de devolverlo.
|
/// para que las extensiones puedan modificar el HTML final antes de devolverlo.
|
||||||
/// 9. Devuelve el [`Markup`] resultante.
|
/// 7. Devuelve el [`Markup`] resultante.
|
||||||
impl<C: Component> ComponentRender for C {
|
impl<C: Component> ComponentRender for C {
|
||||||
fn render(&mut self, cx: &mut Context) -> Markup {
|
fn render(&mut self, cx: &mut Context) -> Markup {
|
||||||
// Si no es renderizable, devuelve un bloque HTML vacío.
|
// Si no es renderizable, devuelve un bloque HTML vacío.
|
||||||
|
|
@ -126,9 +120,6 @@ impl<C: Component> ComponentRender for C {
|
||||||
// Configura el componente antes de preparar.
|
// Configura el componente antes de preparar.
|
||||||
self.setup_before_prepare(cx);
|
self.setup_before_prepare(cx);
|
||||||
|
|
||||||
// Acciones específicas del tema antes de renderizar el componente.
|
|
||||||
action::theme::BeforeRender::dispatch(self, cx);
|
|
||||||
|
|
||||||
// Acciones de las extensiones antes de renderizar el componente.
|
// Acciones de las extensiones antes de renderizar el componente.
|
||||||
action::component::BeforeRender::dispatch(self, cx);
|
action::component::BeforeRender::dispatch(self, cx);
|
||||||
|
|
||||||
|
|
@ -136,7 +127,7 @@ impl<C: Component> ComponentRender for C {
|
||||||
let prepare = match 'resolve: {
|
let prepare = match 'resolve: {
|
||||||
let mut t: Option<ThemeRef> = Some(cx.theme());
|
let mut t: Option<ThemeRef> = Some(cx.theme());
|
||||||
while let Some(theme) = t {
|
while let Some(theme) = t {
|
||||||
if let Some(r) = theme.prepare_component(self, cx) {
|
if let Some(r) = theme.handle_component(self, cx) {
|
||||||
break 'resolve r;
|
break 'resolve r;
|
||||||
}
|
}
|
||||||
t = theme.parent();
|
t = theme.parent();
|
||||||
|
|
@ -156,13 +147,10 @@ impl<C: Component> ComponentRender for C {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Acciones específicas del tema después de renderizar el componente.
|
|
||||||
action::theme::AfterRender::dispatch(self, cx);
|
|
||||||
|
|
||||||
// Acciones de las extensiones después de renderizar el componente.
|
// Acciones de las extensiones después de renderizar el componente.
|
||||||
action::component::AfterRender::dispatch(self, cx);
|
action::component::AfterRender::dispatch(self, cx);
|
||||||
|
|
||||||
// Acciones de las extensiones que transforman el HTML final antes de devolverlo.
|
// Acciones de las extensiones que transforman el HTML final antes de devolverlo.
|
||||||
action::component::AlterMarkup::dispatch(self, cx, prepare)
|
action::component::TransformMarkup::dispatch(self, cx, prepare)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::core::action::ActionBox;
|
||||||
use crate::core::theme::ThemeRef;
|
use crate::core::theme::ThemeRef;
|
||||||
use crate::core::AnyInfo;
|
use crate::core::AnyInfo;
|
||||||
use crate::locale::L10n;
|
use crate::locale::L10n;
|
||||||
use crate::{actions_boxed, service};
|
use crate::{actions, service};
|
||||||
|
|
||||||
/// Interfaz común que debe implementar cualquier extensión de PageTop.
|
/// Interfaz común que debe implementar cualquier extensión de PageTop.
|
||||||
///
|
///
|
||||||
|
|
@ -74,10 +74,10 @@ pub trait Extension: AnyInfo + Send + Sync {
|
||||||
/// Devuelve la lista de acciones que la extensión registra.
|
/// Devuelve la lista de acciones que la extensión registra.
|
||||||
///
|
///
|
||||||
/// Estas [acciones](crate::core::action) se despachan por orden de registro o por
|
/// Estas [acciones](crate::core::action) se despachan por orden de registro o por
|
||||||
/// [peso](crate::Weight) (ver [`actions_boxed!`](crate::actions_boxed)), permitiendo
|
/// [peso](crate::Weight) (ver [`actions!`](crate::actions)), permitiendo
|
||||||
/// personalizar el comportamiento de la aplicación en puntos específicos.
|
/// personalizar el comportamiento de la aplicación en puntos específicos.
|
||||||
fn actions(&self) -> Vec<ActionBox> {
|
fn actions(&self) -> Vec<ActionBox> {
|
||||||
actions_boxed![]
|
actions![]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inicializa la extensión durante la fase de arranque de la aplicación.
|
/// Inicializa la extensión durante la fase de arranque de la aplicación.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
//!
|
//!
|
||||||
//! PageTop permite crear **temas hijo** que refinan el comportamiento de su tema padre. Un tema
|
//! PageTop permite crear **temas hijo** que refinan el comportamiento de su tema padre. Un tema
|
||||||
//! hijo hereda automáticamente todos los métodos del padre y puede sobrescribirlos selectivamente:
|
//! hijo hereda automáticamente todos los métodos del padre y puede sobrescribirlos selectivamente:
|
||||||
//! por ejemplo, puede redefinir el renderizado de un componente con [`Theme::prepare_component()`]
|
//! por ejemplo, puede redefinir el renderizado de un componente con [`Theme::handle_component()`]
|
||||||
//! sin modificar el resto del comportamiento heredado. Un tema hijo puede ser a su vez padre de
|
//! sin modificar el resto del comportamiento heredado. Un tema hijo puede ser a su vez padre de
|
||||||
//! otro, basta declararlo cada vez con [`Theme::parent()`].
|
//! otro, basta declararlo cada vez con [`Theme::parent()`].
|
||||||
//!
|
//!
|
||||||
|
|
@ -208,27 +208,31 @@ impl Template for DefaultTemplate {}
|
||||||
|
|
||||||
// **< render_component! >**************************************************************************
|
// **< render_component! >**************************************************************************
|
||||||
|
|
||||||
/// Sobrescribe el renderizado de componentes en la implementación de
|
/// Sobrescribe el renderizado de componentes en
|
||||||
/// [`Theme::prepare_component()`](crate::core::theme::Theme::prepare_component).
|
/// [`Theme::handle_component()`](crate::core::theme::Theme::handle_component).
|
||||||
///
|
///
|
||||||
/// Evalúa `$component` contra cada tipo listado en orden. En cuanto encuentra coincidencia,
|
/// Evalúa `$component` contra cada tipo de componente listado en orden. En cuanto encuentra
|
||||||
/// devuelve `Some(Ok(markup))` o `Some(Err(e))` según el resultado de la expresión asociada.
|
/// coincidencia, devuelve `Some(Ok(markup))` o `Some(Err(e))` según el resultado de la expresión
|
||||||
/// Si ningún tipo coincide, devuelve `None` para que el sistema continúe con la cadena de
|
/// asociada. Si ningún tipo coincide, devuelve `None` para que el sistema continúe con la cadena de
|
||||||
/// herencia o con el renderizado por defecto del propio componente.
|
/// herencia o con el renderizado por defecto del propio componente.
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// fn prepare_component(
|
/// fn handle_component(
|
||||||
/// &self,
|
/// &self,
|
||||||
/// component: &dyn Component,
|
/// component: &dyn Component,
|
||||||
/// cx: &mut Context,
|
/// cx: &mut Context,
|
||||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||||
/// render_component!(component, {
|
/// render_component!(component, {
|
||||||
/// Button => |btn| Ok(html! { button.btn.btn-primary { (btn.label()) } }),
|
/// Button => |btn| { Ok(html! { button.btn.btn-primary { (btn.label()) } }) },
|
||||||
/// Heading => |h| Ok(html! { h2.display-4 { (h.text()) } }),
|
/// Heading => |h| self.render_heading(h, cx),
|
||||||
/// })
|
/// })
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// fn render_heading(&self, h: &Heading, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||||
|
/// Ok(html! { h2.display-4 { (h.text()) } })
|
||||||
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! render_component {
|
macro_rules! render_component {
|
||||||
|
|
@ -244,6 +248,66 @@ macro_rules! render_component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **< setup_component! >***************************************************************************
|
||||||
|
|
||||||
|
/// Muta un componente dentro de
|
||||||
|
/// [`Theme::handle_component()`](crate::core::theme::Theme::handle_component).
|
||||||
|
///
|
||||||
|
/// Evalúa `$component` contra cada tipo de componente listado en orden. En cuanto encuentra
|
||||||
|
/// coincidencia, ejecuta el bloque asociado y detiene la evaluación. Si ningún tipo coincide, no
|
||||||
|
/// hace nada.
|
||||||
|
///
|
||||||
|
/// Usa acceso mutable al componente mediante [`downcast_mut`](crate::core::AnyCast::downcast_mut),
|
||||||
|
/// lo que permite modificar su estado. El tema puede devolver `None` tras la mutación para que otro
|
||||||
|
/// nivel de la cadena se encargue del renderizado.
|
||||||
|
///
|
||||||
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// Solo mutación: el tema ajusta el componente y delega el renderizado al siguiente nivel:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// fn handle_component(
|
||||||
|
/// &self,
|
||||||
|
/// component: &mut dyn Component,
|
||||||
|
/// cx: &mut Context,
|
||||||
|
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||||
|
/// setup_component!(component, { Button => |btn| { btn.add_class("btn-primary"); } });
|
||||||
|
/// None
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Mutación y renderizado combinados: el `Button` se muta y se renderiza aquí; el `Heading` se
|
||||||
|
/// muta pero continúa la cadena para que otro nivel lo renderice:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// fn handle_component(
|
||||||
|
/// &self,
|
||||||
|
/// component: &mut dyn Component,
|
||||||
|
/// cx: &mut Context,
|
||||||
|
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||||
|
/// setup_component!(component, {
|
||||||
|
/// Button => |btn| { btn.add_class("btn-primary"); },
|
||||||
|
/// Heading => |h| { h.add_class("display-4"); },
|
||||||
|
/// });
|
||||||
|
/// render_component!(component, {
|
||||||
|
/// Button => |btn| Ok(html! { button.btn { (btn.label()) } }),
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! setup_component {
|
||||||
|
($component:expr, { $($type:ty => |$var:ident| $body:expr),* $(,)? }) => {
|
||||||
|
'setup_component: {
|
||||||
|
$(
|
||||||
|
if let Some($var) = ($component).downcast_mut::<$type>() {
|
||||||
|
$body;
|
||||||
|
break 'setup_component;
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// **< Definitions >********************************************************************************
|
// **< Definitions >********************************************************************************
|
||||||
|
|
||||||
mod definition;
|
mod definition;
|
||||||
|
|
|
||||||
|
|
@ -184,30 +184,42 @@ pub trait Theme: Extension + Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Permite sobrescribir el renderizado de un componente.
|
/// Permite al tema intervenir en el ciclo de renderizado de un componente.
|
||||||
///
|
///
|
||||||
/// Este método tiene especial utilidad en los **temas hijo** porque permite sobrescribir el
|
/// Este método tiene especial utilidad en los **temas hijo** porque permite sobrescribir el
|
||||||
/// renderizado que el propio componente o el tema padre ofrece para un componente concreto, sin
|
/// renderizado que el propio componente o el tema padre ofrece para un componente concreto, sin
|
||||||
/// modificar el resto del comportamiento heredado.
|
/// modificar el resto del comportamiento heredado.
|
||||||
///
|
///
|
||||||
/// Recibe una referencia al componente (como objeto dinámico [`Component`]) y el contexto de
|
/// Recibe una referencia mutable al componente (como objeto dinámico [`Component`]) y el
|
||||||
/// renderizado. Devuelve:
|
/// contexto de renderizado. Devuelve:
|
||||||
///
|
///
|
||||||
/// - `None` si este tema no sobrescribe el componente. Es la implementación por defecto. En
|
/// - `None` si este tema no sobrescribe el renderizado. Es la implementación por defecto. El
|
||||||
/// este caso se recorre la cadena de temas padre y, si ninguno lo sobrescribe, se usa
|
/// sistema continúa con el siguiente tema de la cadena y, si ninguno lo sobrescribe, usa
|
||||||
/// [`Component::prepare_component()`](crate::core::component::Component::prepare_component).
|
/// [`Component::prepare_component()`](crate::core::component::Component::prepare_component).
|
||||||
|
/// El tema puede mutar el componente antes de devolver `None`, dejando que otro nivel de la
|
||||||
|
/// cadena se encargue del renderizado.
|
||||||
/// - `Some(Ok(markup))` con el HTML generado por el tema para el componente.
|
/// - `Some(Ok(markup))` con el HTML generado por el tema para el componente.
|
||||||
|
///
|
||||||
|
/// > **Nota para componentes en región:** los componentes registrados con `InRegion` son
|
||||||
|
/// > instancias únicas compartidas entre peticiones. Cualquier mutación realizada aquí debe
|
||||||
|
/// > ser idempotente — sobrescribir valores, nunca acumular — o el estado se corromperá a
|
||||||
|
/// > partir de la segunda petición.
|
||||||
/// - `Some(Err(e))` si el tema intentó renderizarlo pero falló.
|
/// - `Some(Err(e))` si el tema intentó renderizarlo pero falló.
|
||||||
///
|
///
|
||||||
/// La mejor manera de implementar este método es usando la macro [`render_component!`], que
|
/// Para renderizar usa [`render_component!`], que devuelve `None` si ningún tipo coincide. Para
|
||||||
/// determina el componente a renderizar y devuelve `None` si ninguno coincide:
|
/// mutar sin renderizar usa [`setup_component!`] y devuelve `None` explícitamente:
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// fn prepare_component(
|
/// fn handle_component(
|
||||||
/// &self,
|
/// &self,
|
||||||
/// component: &dyn Component,
|
/// component: &mut dyn Component,
|
||||||
/// cx: &mut Context,
|
/// cx: &mut Context,
|
||||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||||
|
/// // Solo mutación: ajusta el componente y deja que otro nivel lo renderice.
|
||||||
|
/// setup_component!(component, {
|
||||||
|
/// Button => |btn| { btn.add_class("btn-primary"); },
|
||||||
|
/// });
|
||||||
|
/// // O renderizado completo:
|
||||||
/// render_component!(component, {
|
/// render_component!(component, {
|
||||||
/// Button => |btn| Ok(html! { button.btn.btn-primary { (btn.label()) } }),
|
/// Button => |btn| Ok(html! { button.btn.btn-primary { (btn.label()) } }),
|
||||||
/// Heading => |h| Ok(html! { h2.display-4 { (h.text()) } }),
|
/// Heading => |h| Ok(html! { h2.display-4 { (h.text()) } }),
|
||||||
|
|
@ -215,9 +227,9 @@ pub trait Theme: Extension + Send + Sync {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn prepare_component(
|
fn handle_component(
|
||||||
&self,
|
&self,
|
||||||
component: &dyn Component,
|
component: &mut dyn Component,
|
||||||
cx: &mut Context,
|
cx: &mut Context,
|
||||||
) -> Option<Result<Markup, ComponentError>> {
|
) -> Option<Result<Markup, ComponentError>> {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ pub use crate::include_locales;
|
||||||
// crate::service
|
// crate::service
|
||||||
pub use crate::static_files_service;
|
pub use crate::static_files_service;
|
||||||
// crate::core::action
|
// crate::core::action
|
||||||
pub use crate::actions_boxed;
|
pub use crate::actions;
|
||||||
// crate::core::theme
|
// crate::core::theme
|
||||||
pub use crate::render_component;
|
pub use crate::{render_component, setup_component};
|
||||||
|
|
||||||
// API.
|
// API.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue