✨ 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:
parent
f76a208520
commit
37df2ada75
28 changed files with 1102 additions and 147 deletions
|
|
@ -1,7 +1,9 @@
|
|||
use crate::core::action::{ActionBox, ActionKey, ActionTrait, ActionsList};
|
||||
use crate::core::action::{ActionBox, ActionDispatcher, ActionKey, ActionsList};
|
||||
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{LazyLock, RwLock};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
// ACCIONES ****************************************************************************************
|
||||
|
||||
|
|
@ -12,20 +14,19 @@ static ACTIONS: LazyLock<RwLock<HashMap<ActionKey, ActionsList>>> =
|
|||
|
||||
// Registra una nueva acción en el sistema.
|
||||
//
|
||||
// Si ya existen acciones con la misma `ActionKey`, la acción se añade a la lista existente. Si no,
|
||||
// se crea una nueva lista.
|
||||
//
|
||||
// # Uso típico
|
||||
// Si ya existen acciones con la misma `ActionKey`, la acción se añade a la misma lista. Si no, se
|
||||
// crea una nueva lista.
|
||||
//
|
||||
// Las extensiones llamarán a esta función durante su inicialización para instalar acciones
|
||||
// personalizadas que modifiquen el comportamiento del núcleo o de otros componentes.
|
||||
//
|
||||
// ```rust,ignore
|
||||
// add_action(Box::new(MyCustomAction::new()));
|
||||
// ```
|
||||
// personalizadas que modifiquen el comportamiento del *core* o de otros componentes.
|
||||
pub fn add_action(action: ActionBox) {
|
||||
let key = action.key();
|
||||
let mut actions = ACTIONS.write().unwrap();
|
||||
let key = ActionKey::new(
|
||||
action.type_id(),
|
||||
action.theme_type_id(),
|
||||
action.referer_type_id(),
|
||||
action.referer_id(),
|
||||
);
|
||||
let mut actions = ACTIONS.write();
|
||||
if let Some(list) = actions.get_mut(&key) {
|
||||
list.add(action);
|
||||
} else {
|
||||
|
|
@ -37,30 +38,35 @@ pub fn add_action(action: ActionBox) {
|
|||
|
||||
// DESPLEGAR ACCIONES ******************************************************************************
|
||||
|
||||
/// Despacha las funciones asociadas a un [`ActionKey`] y las ejecuta.
|
||||
/// Despacha y ejecuta las funciones asociadas a una [`ActionKey`].
|
||||
///
|
||||
/// Permite recorrer de forma segura y ordenada (por peso) la lista de funciones asociadas a una
|
||||
/// acción específica.
|
||||
///
|
||||
/// # Parámetros genéricos
|
||||
/// - `A`: Tipo de acción que esperamos procesar. Debe implementar [`ActionTrait`].
|
||||
/// - `F`: Función o cierre que recibe cada acción y devuelve un valor de tipo `B`.
|
||||
/// - `A`: Tipo de acción que esperamos procesar. Debe implementar [`ActionDispatcher`].
|
||||
/// - `F`: Función asociada a cada acción, devuelve un valor de tipo `B`.
|
||||
///
|
||||
/// # Ejemplo de uso
|
||||
/// ```rust,ignore
|
||||
/// dispatch_actions::<MyCustomAction, _>(&some_key, |action| {
|
||||
/// action.do_something();
|
||||
/// });
|
||||
/// 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),
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Esto permite a PageTop o a otros módulos aplicar lógica específica a las acciones de un contexto
|
||||
/// determinado, manteniendo la flexibilidad del sistema.
|
||||
pub fn dispatch_actions<A, B, F>(key: &ActionKey, f: F)
|
||||
where
|
||||
A: ActionTrait,
|
||||
A: ActionDispatcher,
|
||||
F: FnMut(&A) -> B,
|
||||
{
|
||||
if let Some(list) = ACTIONS.read().unwrap().get(key) {
|
||||
if let Some(list) = ACTIONS.read().get(key) {
|
||||
list.iter_map(f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
use crate::core::AnyInfo;
|
||||
use crate::{UniqueId, Weight};
|
||||
|
||||
/// Tipo dinámico para encapsular cualquier acción que implementa [`ActionTrait`].
|
||||
pub type ActionBox = Box<dyn ActionTrait>;
|
||||
/// Tipo dinámico para encapsular cualquier acción que implementa [`ActionDispatcher`].
|
||||
pub type ActionBox = Box<dyn ActionDispatcher>;
|
||||
|
||||
/// Identifica una acción con una clave que define las condiciones de selección de las funciones
|
||||
/// asociadas a esa acción.
|
||||
/// Clave para registrar las acciones y seleccionar las funciones asociadas.
|
||||
///
|
||||
/// Las funciones seleccionadas se van a [despachar](crate::core::action::dispatch_actions) y
|
||||
/// ejecutar en un punto concreto del flujo de ejecución.
|
||||
///
|
||||
/// # Campos
|
||||
///
|
||||
/// - `action_type_id`: Tipo de la acción.
|
||||
/// - `theme_type_id`: Opcional, filtra las funciones para un tema dado.
|
||||
/// - `referer_type_id`: Opcional, filtra las funciones para un tipo dado (p.ej. para un tipo de
|
||||
/// componente).
|
||||
/// - `referer_id`: Opcional, filtra las funciones por el identificador de una instancia (p.ej. para
|
||||
/// un formulario concreto).
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
pub struct ActionKey {
|
||||
action_type_id: UniqueId,
|
||||
|
|
@ -29,8 +19,15 @@ pub struct ActionKey {
|
|||
impl ActionKey {
|
||||
/// Crea una nueva clave para un tipo de acción.
|
||||
///
|
||||
/// Esta clave permite seleccionar las funciones a ejecutar para ese tipo de acción con filtros
|
||||
/// opcionales por tema, un tipo de referencia, o una instancia concreta según su identificador.
|
||||
/// Se crea con los siguientes campos:
|
||||
///
|
||||
/// - `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_id`: Opcional, identificador de la instancia (p.ej. para un formulario concreto).
|
||||
///
|
||||
/// 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.
|
||||
pub fn new(
|
||||
action_type_id: UniqueId,
|
||||
theme_type_id: Option<UniqueId>,
|
||||
|
|
@ -46,57 +43,28 @@ impl ActionKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// Trait base que permite obtener la clave ([`ActionKey`]) asociada a una acción.
|
||||
/// Implementa el filtro predeterminado para despachar las funciones de una acción dada.
|
||||
///
|
||||
/// Implementado automáticamente para cualquier tipo que cumpla [`ActionTrait`].
|
||||
pub trait ActionBase {
|
||||
fn key(&self) -> ActionKey;
|
||||
}
|
||||
|
||||
/// Interfaz común que deben implementar las acciones del código que pueden ser modificadas.
|
||||
///
|
||||
/// Este trait combina:
|
||||
/// - [`AnyInfo`] para identificación única del tipo en tiempo de ejecución.
|
||||
/// - `Send + Sync` para permitir uso concurrente seguro.
|
||||
///
|
||||
/// # Métodos personalizables
|
||||
/// - `theme_type_id()`: Asocia la acción a un tipo concreto de tema (si aplica).
|
||||
/// - `referer_type_id()`: Asocia la acción a un tipo de objeto referente (si aplica).
|
||||
/// - `referer_id()`: Asocia la acción a un identificador concreto.
|
||||
/// - `weight()`: Controla el orden de aplicación de acciones; valores más bajos se ejecutan antes.
|
||||
pub trait ActionTrait: ActionBase + AnyInfo + Send + Sync {
|
||||
/// Especifica el tipo de tema asociado. Por defecto `None`.
|
||||
/// Las acciones tienen que sobrescribir los métodos para el filtro que apliquen. Por defecto
|
||||
/// implementa un filtro nulo.
|
||||
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
|
||||
}
|
||||
|
||||
/// Especifica el tipo del objeto referente. Por defecto `None`.
|
||||
/// Identificador de tipo ([`UniqueId`]) del objeto referido. En este caso devuelve `None`.
|
||||
fn referer_type_id(&self) -> Option<UniqueId> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Especifica un identificador único del objeto referente. Por defecto `None`.
|
||||
/// Identificador del objeto referido. En este caso devuelve `None`.
|
||||
fn referer_id(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Define el peso lógico de la acción para determinar el orden de aplicación.
|
||||
///
|
||||
/// Acciones con pesos más bajos se aplicarán antes. Se pueden usar valores negativos. Por
|
||||
/// defecto es `0`.
|
||||
/// Funciones con pesos más bajos se aplican antes. En este caso siempre devuelve `0`.
|
||||
fn weight(&self) -> Weight {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
// Implementación automática que construye la clave `ActionKey` a partir de los métodos definidos.
|
||||
impl<A: ActionTrait> ActionBase for A {
|
||||
fn key(&self) -> ActionKey {
|
||||
ActionKey {
|
||||
action_type_id: self.type_id(),
|
||||
theme_type_id: self.theme_type_id(),
|
||||
referer_type_id: self.referer_type_id(),
|
||||
referer_id: self.referer_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::core::action::{ActionBox, ActionTrait};
|
||||
use crate::core::action::{ActionBox, ActionDispatcher};
|
||||
use crate::core::AnyCast;
|
||||
use crate::trace;
|
||||
use crate::AutoDefault;
|
||||
|
||||
use std::sync::RwLock;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub struct ActionsList(RwLock<Vec<ActionBox>>);
|
||||
|
|
@ -14,7 +14,7 @@ impl ActionsList {
|
|||
}
|
||||
|
||||
pub fn add(&mut self, action: ActionBox) {
|
||||
let mut list = self.0.write().unwrap();
|
||||
let mut list = self.0.write();
|
||||
list.push(action);
|
||||
list.sort_by_key(|a| a.weight());
|
||||
}
|
||||
|
|
@ -22,13 +22,12 @@ impl ActionsList {
|
|||
pub fn iter_map<A, B, F>(&self, mut f: F)
|
||||
where
|
||||
Self: Sized,
|
||||
A: ActionTrait,
|
||||
A: ActionDispatcher,
|
||||
F: FnMut(&A) -> B,
|
||||
{
|
||||
let _: Vec<_> = self
|
||||
.0
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|a| {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue