Añade acciones base y renderizado de componentes

- Añade acciones BeforeRender y AfterRender para ejecutar código
  personalizado antes y después de renderizar un componente.
- Introduce la acción PrepareRender para personalizar totalmente el
  renderizado de un componente.
- Se actualizan las definiciones de acciones para utilizar el nuevo
  "trait" ActionDispatcher.
- Se crea un nuevo trait ComponentTrait para definir componentes
  renderizables.
- Se implementan las estructuras Children y Child para gestionar
  componentes hijos dentro de un componente padre.
- Se añade OptionComponent para encapsular de forma segura componentes
  opcionales y poder usarlos en otros componentes.
This commit is contained in:
Manuel Cillero 2025-07-24 08:38:17 +02:00
parent f76a208520
commit 37df2ada75
28 changed files with 1102 additions and 147 deletions

View file

@ -0,0 +1,96 @@
use crate::prelude::*;
/// Tipo de función para determinar si un componente se renderiza o no.
///
/// Se usa en la acción [`IsRenderable`] para controlar dinámicamente la visibilidad del componente
/// `component` según el contexto `cx`. El componente **no se renderiza** en cuanto una de las
/// funciones devuelva `false`.
pub type FnIsRenderable<C> = fn(component: &C, cx: &Context) -> bool;
/// Con la función [`FnIsRenderable`] se puede decidir si se renderiza o no un componente.
pub struct IsRenderable<C: ComponentTrait> {
f: FnIsRenderable<C>,
referer_type_id: Option<UniqueId>,
referer_id: OptionId,
weight: Weight,
}
/// Filtro para despachar [`FnIsRenderable`] para decidir si se renderiza o no un componente `C`.
impl<C: ComponentTrait> ActionDispatcher for IsRenderable<C> {
/// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`.
fn referer_type_id(&self) -> Option<UniqueId> {
self.referer_type_id
}
/// Devuelve el identificador del componente.
fn referer_id(&self) -> Option<String> {
self.referer_id.get()
}
/// Devuelve el peso para definir el orden de aplicación.
fn weight(&self) -> Weight {
self.weight
}
}
impl<C: ComponentTrait> IsRenderable<C> {
/// Permite [registrar](ExtensionTrait::actions) una nueva acción [`FnIsRenderable`].
pub fn new(f: FnIsRenderable<C>) -> Self {
IsRenderable {
f,
referer_type_id: Some(UniqueId::of::<C>()),
referer_id: OptionId::default(),
weight: 0,
}
}
/// Afina el registro para ejecutar la acción [`FnIsRenderable`] sólo para el componente `C`
/// con identificador `id`.
pub fn filter_by_referer_id(mut self, id: impl AsRef<str>) -> Self {
self.referer_id.alter_value(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. Se detiene en cuanto una [`FnIsRenderable`] devuelve `false`.
#[inline]
pub(crate) fn dispatch(component: &C, cx: &mut Context) -> bool {
let mut renderable = true;
dispatch_actions(
&ActionKey::new(
UniqueId::of::<Self>(),
None,
Some(UniqueId::of::<C>()),
None,
),
|action: &Self| {
if renderable && !(action.f)(component, cx) {
renderable = false;
}
},
);
if renderable {
if let Some(id) = component.id() {
dispatch_actions(
&ActionKey::new(
UniqueId::of::<Self>(),
None,
Some(UniqueId::of::<C>()),
Some(id),
),
|action: &Self| {
if renderable && !(action.f)(component, cx) {
renderable = false;
}
},
);
}
}
renderable
}
}