Compare commits
No commits in common. "ed25a17e80279f72160d91db3166c258c95a39fe" and "f76a208520945e5ff7b68ce97805e5ea27434804" have entirely different histories.
ed25a17e80
...
f76a208520
30 changed files with 147 additions and 1135 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -1413,7 +1413,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pagetop"
|
name = "pagetop"
|
||||||
version = "0.0.12"
|
version = "0.0.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -1426,7 +1426,6 @@ dependencies = [
|
||||||
"fluent-templates",
|
"fluent-templates",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pagetop-macros",
|
"pagetop-macros",
|
||||||
"parking_lot",
|
|
||||||
"pastey",
|
"pastey",
|
||||||
"serde",
|
"serde",
|
||||||
"static-files",
|
"static-files",
|
||||||
|
@ -2213,9 +2212,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-actix-web"
|
name = "tracing-actix-web"
|
||||||
version = "0.7.19"
|
version = "0.7.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5360edd490ec8dee9fedfc6a9fd83ac2f01b3e1996e3261b9ad18a61971fe064"
|
checksum = "2340b7722695166c7fc9b3e3cd1166e7c74fedb9075b8f0c74d3822d2e41caf5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"mutually_exclusive_features",
|
"mutually_exclusive_features",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "pagetop"
|
name = "pagetop"
|
||||||
version = "0.0.12"
|
version = "0.0.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
description = """\
|
description = """\
|
||||||
|
@ -21,7 +21,6 @@ concat-string = "1.0.1"
|
||||||
config = { version = "0.15.13", default-features = false, features = ["toml"] }
|
config = { version = "0.15.13", default-features = false, features = ["toml"] }
|
||||||
figlet-rs = "0.1.5"
|
figlet-rs = "0.1.5"
|
||||||
itoa = "1.0.15"
|
itoa = "1.0.15"
|
||||||
parking_lot = "0.12.4"
|
|
||||||
paste = { package = "pastey", version = "0.1.0" }
|
paste = { package = "pastey", version = "0.1.0" }
|
||||||
substring = "1.4.5"
|
substring = "1.4.5"
|
||||||
terminal_size = "0.4.2"
|
terminal_size = "0.4.2"
|
||||||
|
@ -29,7 +28,7 @@ terminal_size = "0.4.2"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-appender = "0.2.3"
|
tracing-appender = "0.2.3"
|
||||||
tracing-subscriber = { version = "0.3.19", features = ["json", "env-filter"] }
|
tracing-subscriber = { version = "0.3.19", features = ["json", "env-filter"] }
|
||||||
tracing-actix-web = "0.7.19"
|
tracing-actix-web = "0.7.18"
|
||||||
|
|
||||||
fluent-templates = "0.13.0"
|
fluent-templates = "0.13.0"
|
||||||
unic-langid = { version = "0.9.6", features = ["macros"] }
|
unic-langid = { version = "0.9.6", features = ["macros"] }
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
//! Reúne acciones, componentes y temas listos para usar.
|
//! Reúne temas listos para usar.
|
||||||
|
|
||||||
pub mod action;
|
|
||||||
|
|
||||||
pub mod component;
|
|
||||||
|
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
//! 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<C> = fn(component: &mut C, cx: &mut Context);
|
|
||||||
|
|
||||||
pub mod component;
|
|
||||||
|
|
||||||
pub mod theme;
|
|
|
@ -1,10 +0,0 @@
|
||||||
//! Acciones que operan sobre componentes.
|
|
||||||
|
|
||||||
mod is_renderable;
|
|
||||||
pub use is_renderable::*;
|
|
||||||
|
|
||||||
mod before_render_component;
|
|
||||||
pub use before_render_component::*;
|
|
||||||
|
|
||||||
mod after_render_component;
|
|
||||||
pub use after_render_component::*;
|
|
|
@ -1,81 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use crate::base::action::FnActionWithComponent;
|
|
||||||
|
|
||||||
/// Ejecuta [`FnActionWithComponent`] después de renderizar un componente.
|
|
||||||
pub struct AfterRender<C: ComponentTrait> {
|
|
||||||
f: FnActionWithComponent<C>,
|
|
||||||
referer_type_id: Option<UniqueId>,
|
|
||||||
referer_id: OptionId,
|
|
||||||
weight: Weight,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Filtro para despachar [`FnActionWithComponent`] después de renderizar un componente `C`.
|
|
||||||
impl<C: ComponentTrait> ActionDispatcher for AfterRender<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> AfterRender<C> {
|
|
||||||
/// Permite [registrar](ExtensionTrait::actions) una nueva acción [`FnActionWithComponent`].
|
|
||||||
pub fn new(f: FnActionWithComponent<C>) -> Self {
|
|
||||||
AfterRender {
|
|
||||||
f,
|
|
||||||
referer_type_id: Some(UniqueId::of::<C>()),
|
|
||||||
referer_id: OptionId::default(),
|
|
||||||
weight: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Afina el registro para ejecutar la acción [`FnActionWithComponent`] 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.
|
|
||||||
#[inline]
|
|
||||||
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::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
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::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
Some(id),
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use crate::base::action::FnActionWithComponent;
|
|
||||||
|
|
||||||
/// Ejecuta [`FnActionWithComponent`] antes de renderizar el componente.
|
|
||||||
pub struct BeforeRender<C: ComponentTrait> {
|
|
||||||
f: FnActionWithComponent<C>,
|
|
||||||
referer_type_id: Option<UniqueId>,
|
|
||||||
referer_id: OptionId,
|
|
||||||
weight: Weight,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Filtro para despachar [`FnActionWithComponent`] antes de renderizar un componente `C`.
|
|
||||||
impl<C: ComponentTrait> ActionDispatcher for BeforeRender<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> BeforeRender<C> {
|
|
||||||
/// Permite [registrar](ExtensionTrait::actions) una nueva acción [`FnActionWithComponent`].
|
|
||||||
pub fn new(f: FnActionWithComponent<C>) -> Self {
|
|
||||||
BeforeRender {
|
|
||||||
f,
|
|
||||||
referer_type_id: Some(UniqueId::of::<C>()),
|
|
||||||
referer_id: OptionId::default(),
|
|
||||||
weight: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Afina el registro para ejecutar la acción [`FnActionWithComponent`] 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.
|
|
||||||
#[inline]
|
|
||||||
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::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
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::<Self>(),
|
|
||||||
None,
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
Some(id),
|
|
||||||
),
|
|
||||||
|action: &Self| (action.f)(component, cx),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +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::*;
|
|
||||||
|
|
||||||
mod prepare_render;
|
|
||||||
pub use prepare_render::*;
|
|
|
@ -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: ComponentTrait> {
|
|
||||||
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: ComponentTrait> 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: ComponentTrait> AfterRender<C> {
|
|
||||||
/// Permite [registrar](ExtensionTrait::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: ComponentTrait> {
|
|
||||||
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: ComponentTrait> 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: ComponentTrait> BeforeRender<C> {
|
|
||||||
/// Permite [registrar](ExtensionTrait::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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
/// Tipo de función para alterar el renderizado de un componente.
|
|
||||||
///
|
|
||||||
/// Permite a un [tema](crate::base::action::theme) sobreescribir el renderizado predeterminado de
|
|
||||||
/// los componentes.
|
|
||||||
///
|
|
||||||
/// Recibe una referencia al componente `component` y una referencia mutable al contexto `cx`.
|
|
||||||
pub type FnPrepareRender<C> = fn(component: &C, cx: &mut Context) -> PrepareMarkup;
|
|
||||||
|
|
||||||
/// Ejecuta [`FnPrepareRender`] para preparar el renderizado de un componente.
|
|
||||||
///
|
|
||||||
/// Permite a un tema hacer una implementación nueva del renderizado de un componente.
|
|
||||||
pub struct PrepareRender<C: ComponentTrait> {
|
|
||||||
f: FnPrepareRender<C>,
|
|
||||||
theme_type_id: Option<UniqueId>,
|
|
||||||
referer_type_id: Option<UniqueId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Filtro para despachar [`FnPrepareRender`] que modifica el renderizado de un componente `C`.
|
|
||||||
impl<C: ComponentTrait> ActionDispatcher for PrepareRender<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: ComponentTrait> PrepareRender<C> {
|
|
||||||
/// Permite [registrar](ExtensionTrait::actions) una nueva acción [`FnPrepareRender`] para un
|
|
||||||
/// tema dado.
|
|
||||||
pub fn new(theme: ThemeRef, f: FnPrepareRender<C>) -> Self {
|
|
||||||
PrepareRender {
|
|
||||||
f,
|
|
||||||
theme_type_id: Some(theme.type_id()),
|
|
||||||
referer_type_id: Some(UniqueId::of::<C>()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Despacha las acciones. Se detiene en cuanto una renderiza.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn dispatch(component: &C, cx: &mut Context) -> PrepareMarkup {
|
|
||||||
let mut render_component = PrepareMarkup::None;
|
|
||||||
dispatch_actions(
|
|
||||||
&ActionKey::new(
|
|
||||||
UniqueId::of::<Self>(),
|
|
||||||
Some(cx.theme().type_id()),
|
|
||||||
Some(UniqueId::of::<C>()),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
|action: &Self| {
|
|
||||||
if render_component.is_empty() {
|
|
||||||
render_component = (action.f)(component, cx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
render_component
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
//! Componentes nativos proporcionados por `PageTop`.
|
|
||||||
|
|
||||||
mod html;
|
|
||||||
pub use html::Html;
|
|
|
@ -1,28 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
/// Componente básico para renderizar directamente código HTML.
|
|
||||||
#[derive(AutoDefault)]
|
|
||||||
pub struct Html(Markup);
|
|
||||||
|
|
||||||
impl ComponentTrait for Html {
|
|
||||||
fn new() -> Self {
|
|
||||||
Html::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup {
|
|
||||||
PrepareMarkup::With(html! { (self.0) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Html {
|
|
||||||
/// Crear una instancia con el código HTML del argumento.
|
|
||||||
pub fn with(html: Markup) -> Self {
|
|
||||||
Html(html)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modifica el código HTML de la instancia con el nuevo código del argumento.
|
|
||||||
pub fn alter_html(&mut self, html: Markup) -> &mut Self {
|
|
||||||
self.0 = html;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! Tipos y funciones esenciales para crear acciones, componentes, extensiones y temas.
|
//! Tipos y funciones esenciales para crear acciones, extensiones y temas.
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
|
@ -204,9 +204,6 @@ impl<T: ?Sized + AnyInfo> AnyCast for T {}
|
||||||
// API para definir acciones que alteran el comportamiento predeterminado del código.
|
// API para definir acciones que alteran el comportamiento predeterminado del código.
|
||||||
pub mod action;
|
pub mod action;
|
||||||
|
|
||||||
// API para construir nuevos componentes.
|
|
||||||
pub mod component;
|
|
||||||
|
|
||||||
// API para añadir nuevas funcionalidades usando extensiones.
|
// API para añadir nuevas funcionalidades usando extensiones.
|
||||||
pub mod extension;
|
pub mod extension;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! API para definir acciones que inyectan código en el flujo de la aplicación.
|
//! API para definir acciones que inyectan código en el flujo de la aplicación.
|
||||||
//!
|
//!
|
||||||
//! Permite crear acciones para que otros *crates* puedan inyectar código usando funciones *ad hoc*
|
//! Permite crear acciones en las librerías para que otros *crates* puedan inyectar código usando
|
||||||
//! que modifican el comportamiento predefinido en puntos concretos del flujo de ejecución de la
|
//! funciones *ad hoc* que modifican el comportamiento predefinido en puntos concretos del flujo de
|
||||||
//! aplicación.
|
//! ejecución de la aplicación.
|
||||||
|
|
||||||
mod definition;
|
mod definition;
|
||||||
pub use definition::{ActionBox, ActionDispatcher, ActionKey};
|
pub use definition::{ActionBase, ActionBox, ActionKey, ActionTrait};
|
||||||
|
|
||||||
mod list;
|
mod list;
|
||||||
use list::ActionsList;
|
use list::ActionsList;
|
||||||
|
@ -14,33 +14,49 @@ mod all;
|
||||||
pub(crate) use all::add_action;
|
pub(crate) use all::add_action;
|
||||||
pub use all::dispatch_actions;
|
pub use all::dispatch_actions;
|
||||||
|
|
||||||
/// Facilita la implementación del método
|
/// Crea una lista de acciones para facilitar la implementación del método
|
||||||
/// [`actions()`](crate::core::extension::ExtensionTrait::actions).
|
/// [`actions`](crate::core::extension::ExtensionTrait#method.actions).
|
||||||
///
|
///
|
||||||
/// Evita escribir repetidamente `Box::new(...)` para cada acción de la lista, manteniendo el código
|
/// Esta macro crea vectores de [`ActionBox`], el tipo dinámico que encapsula cualquier acción que
|
||||||
/// más limpio.
|
/// implemente [`ActionTrait`]. Evita escribir repetidamente `Box::new(...)` para cada acción
|
||||||
|
/// inyectada, manteniendo el código más limpio.
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// Puede llamarse sin argumentos para crear un vector vacío:
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// use pagetop::prelude::*;
|
/// let my_actions = inject_actions![];
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// impl ExtensionTrait for MyTheme {
|
/// O con una lista de acciones concretas:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let my_actions = inject_actions![
|
||||||
|
/// MyFirstAction::new(),
|
||||||
|
/// MySecondAction::new().with_weight(10),
|
||||||
|
/// ];
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Internamente, expande a un `vec![Box::new(...), ...]`.
|
||||||
|
///
|
||||||
|
/// # Ejemplo típico en una extensión
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// impl ExtensionTrait for MyExtension {
|
||||||
/// fn actions(&self) -> Vec<ActionBox> {
|
/// fn actions(&self) -> Vec<ActionBox> {
|
||||||
/// actions_boxed![
|
/// inject_actions![
|
||||||
/// action::theme::BeforeRender::<Button>::new(&Self, before_render_button),
|
/// CustomizeLoginAction::new(),
|
||||||
/// action::theme::PrepareRender::<Error404>::new(&Self, render_error404),
|
/// ModifyHeaderAction::new().with_weight(-5),
|
||||||
/// ]
|
/// ]
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
|
||||||
/// impl ThemeTrait for MyTheme {}
|
|
||||||
///
|
|
||||||
/// fn before_render_button(c: &mut Button, cx: &mut Context) { todo!() }
|
|
||||||
/// fn render_error404(c: &Error404, cx: &mut Context) -> PrepareMarkup { todo!() }
|
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// Así, `PageTop` podrá registrar todas estas acciones durante la inicialización de la extensión y
|
||||||
|
/// posteriormente despacharlas según corresponda.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! actions_boxed {
|
macro_rules! inject_actions {
|
||||||
() => {
|
() => {
|
||||||
Vec::<ActionBox>::new()
|
Vec::<ActionBox>::new()
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::core::action::{ActionBox, ActionDispatcher, ActionKey, ActionsList};
|
use crate::core::action::{ActionBox, ActionKey, ActionTrait, ActionsList};
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::{LazyLock, RwLock};
|
||||||
|
|
||||||
// ACCIONES ****************************************************************************************
|
// ACCIONES ****************************************************************************************
|
||||||
|
|
||||||
|
@ -14,19 +12,20 @@ static ACTIONS: LazyLock<RwLock<HashMap<ActionKey, ActionsList>>> =
|
||||||
|
|
||||||
// Registra una nueva acción en el sistema.
|
// Registra una nueva acción en el sistema.
|
||||||
//
|
//
|
||||||
// Si ya existen acciones con la misma `ActionKey`, la acción se añade a la misma lista. Si no, se
|
// Si ya existen acciones con la misma `ActionKey`, la acción se añade a la lista existente. Si no,
|
||||||
// crea una nueva lista.
|
// se crea una nueva lista.
|
||||||
|
//
|
||||||
|
// # Uso típico
|
||||||
//
|
//
|
||||||
// Las extensiones llamarán a esta función durante su inicialización para instalar acciones
|
// Las extensiones llamarán a esta función durante su inicialización para instalar acciones
|
||||||
// personalizadas que modifiquen el comportamiento del *core* o de otros componentes.
|
// personalizadas que modifiquen el comportamiento del núcleo o de otros componentes.
|
||||||
|
//
|
||||||
|
// ```rust,ignore
|
||||||
|
// add_action(Box::new(MyCustomAction::new()));
|
||||||
|
// ```
|
||||||
pub fn add_action(action: ActionBox) {
|
pub fn add_action(action: ActionBox) {
|
||||||
let key = ActionKey::new(
|
let key = action.key();
|
||||||
action.type_id(),
|
let mut actions = ACTIONS.write().unwrap();
|
||||||
action.theme_type_id(),
|
|
||||||
action.referer_type_id(),
|
|
||||||
action.referer_id(),
|
|
||||||
);
|
|
||||||
let mut actions = ACTIONS.write();
|
|
||||||
if let Some(list) = actions.get_mut(&key) {
|
if let Some(list) = actions.get_mut(&key) {
|
||||||
list.add(action);
|
list.add(action);
|
||||||
} else {
|
} else {
|
||||||
|
@ -38,35 +37,30 @@ pub fn add_action(action: ActionBox) {
|
||||||
|
|
||||||
// DESPLEGAR ACCIONES ******************************************************************************
|
// DESPLEGAR ACCIONES ******************************************************************************
|
||||||
|
|
||||||
/// Despacha y ejecuta las funciones asociadas a una [`ActionKey`].
|
/// Despacha las funciones asociadas a un [`ActionKey`] y las ejecuta.
|
||||||
///
|
///
|
||||||
/// Permite recorrer de forma segura y ordenada (por peso) la lista de funciones asociadas a una
|
/// Permite recorrer de forma segura y ordenada (por peso) la lista de funciones asociadas a una
|
||||||
/// acción específica.
|
/// acción específica.
|
||||||
///
|
///
|
||||||
/// # Parámetros genéricos
|
/// # Parámetros genéricos
|
||||||
/// - `A`: Tipo de acción que esperamos procesar. Debe implementar [`ActionDispatcher`].
|
/// - `A`: Tipo de acción que esperamos procesar. Debe implementar [`ActionTrait`].
|
||||||
/// - `F`: Función asociada a cada acción, devuelve un valor de tipo `B`.
|
/// - `F`: Función o cierre que recibe cada acción y devuelve un valor de tipo `B`.
|
||||||
///
|
///
|
||||||
/// # Ejemplo de uso
|
/// # Ejemplo de uso
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// pub(crate) fn dispatch(component: &mut C, cx: &mut Context) {
|
/// dispatch_actions::<MyCustomAction, _>(&some_key, |action| {
|
||||||
/// dispatch_actions(
|
/// action.do_something();
|
||||||
/// &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)
|
pub fn dispatch_actions<A, B, F>(key: &ActionKey, f: F)
|
||||||
where
|
where
|
||||||
A: ActionDispatcher,
|
A: ActionTrait,
|
||||||
F: FnMut(&A) -> B,
|
F: FnMut(&A) -> B,
|
||||||
{
|
{
|
||||||
if let Some(list) = ACTIONS.read().get(key) {
|
if let Some(list) = ACTIONS.read().unwrap().get(key) {
|
||||||
list.iter_map(f);
|
list.iter_map(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
use crate::core::AnyInfo;
|
use crate::core::AnyInfo;
|
||||||
use crate::{UniqueId, Weight};
|
use crate::{UniqueId, Weight};
|
||||||
|
|
||||||
/// Tipo dinámico para encapsular cualquier acción que implementa [`ActionDispatcher`].
|
/// Tipo dinámico para encapsular cualquier acción que implementa [`ActionTrait`].
|
||||||
pub type ActionBox = Box<dyn ActionDispatcher>;
|
pub type ActionBox = Box<dyn ActionTrait>;
|
||||||
|
|
||||||
/// Clave para registrar las acciones y seleccionar las funciones asociadas.
|
/// Identifica una acción con una clave que define las condiciones de selección de las funciones
|
||||||
|
/// asociadas a esa acción.
|
||||||
///
|
///
|
||||||
/// Las funciones seleccionadas se van a [despachar](crate::core::action::dispatch_actions) y
|
/// Las funciones seleccionadas se van a [despachar](crate::core::action::dispatch_actions) y
|
||||||
/// ejecutar en un punto concreto del flujo de ejecución.
|
/// 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)]
|
#[derive(Eq, PartialEq, Hash)]
|
||||||
pub struct ActionKey {
|
pub struct ActionKey {
|
||||||
action_type_id: UniqueId,
|
action_type_id: UniqueId,
|
||||||
|
@ -19,15 +29,8 @@ pub struct ActionKey {
|
||||||
impl ActionKey {
|
impl ActionKey {
|
||||||
/// Crea una nueva clave para un tipo de acción.
|
/// Crea una nueva clave para un tipo de acción.
|
||||||
///
|
///
|
||||||
/// Se crea con los siguientes campos:
|
/// 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.
|
||||||
/// - `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(
|
pub fn new(
|
||||||
action_type_id: UniqueId,
|
action_type_id: UniqueId,
|
||||||
theme_type_id: Option<UniqueId>,
|
theme_type_id: Option<UniqueId>,
|
||||||
|
@ -43,28 +46,57 @@ impl ActionKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementa el filtro predeterminado para despachar las funciones de una acción dada.
|
/// Trait base que permite obtener la clave ([`ActionKey`]) asociada a una acción.
|
||||||
///
|
///
|
||||||
/// Las acciones tienen que sobrescribir los métodos para el filtro que apliquen. Por defecto
|
/// Implementado automáticamente para cualquier tipo que cumpla [`ActionTrait`].
|
||||||
/// implementa un filtro nulo.
|
pub trait ActionBase {
|
||||||
pub trait ActionDispatcher: AnyInfo + Send + Sync {
|
fn key(&self) -> ActionKey;
|
||||||
/// Identificador de tipo ([`UniqueId`]) del tema asociado. En este caso devuelve `None`.
|
}
|
||||||
|
|
||||||
|
/// 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`.
|
||||||
fn theme_type_id(&self) -> Option<UniqueId> {
|
fn theme_type_id(&self) -> Option<UniqueId> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identificador de tipo ([`UniqueId`]) del objeto referido. En este caso devuelve `None`.
|
/// Especifica el tipo del objeto referente. Por defecto `None`.
|
||||||
fn referer_type_id(&self) -> Option<UniqueId> {
|
fn referer_type_id(&self) -> Option<UniqueId> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identificador del objeto referido. En este caso devuelve `None`.
|
/// Especifica un identificador único del objeto referente. Por defecto `None`.
|
||||||
fn referer_id(&self) -> Option<String> {
|
fn referer_id(&self) -> Option<String> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Funciones con pesos más bajos se aplican antes. En este caso siempre devuelve `0`.
|
/// 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`.
|
||||||
fn weight(&self) -> Weight {
|
fn weight(&self) -> Weight {
|
||||||
0
|
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, ActionDispatcher};
|
use crate::core::action::{ActionBox, ActionTrait};
|
||||||
use crate::core::AnyCast;
|
use crate::core::AnyCast;
|
||||||
use crate::trace;
|
use crate::trace;
|
||||||
use crate::AutoDefault;
|
use crate::AutoDefault;
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
#[derive(AutoDefault)]
|
#[derive(AutoDefault)]
|
||||||
pub struct ActionsList(RwLock<Vec<ActionBox>>);
|
pub struct ActionsList(RwLock<Vec<ActionBox>>);
|
||||||
|
@ -14,7 +14,7 @@ impl ActionsList {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, action: ActionBox) {
|
pub fn add(&mut self, action: ActionBox) {
|
||||||
let mut list = self.0.write();
|
let mut list = self.0.write().unwrap();
|
||||||
list.push(action);
|
list.push(action);
|
||||||
list.sort_by_key(|a| a.weight());
|
list.sort_by_key(|a| a.weight());
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,13 @@ impl ActionsList {
|
||||||
pub fn iter_map<A, B, F>(&self, mut f: F)
|
pub fn iter_map<A, B, F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
A: ActionDispatcher,
|
A: ActionTrait,
|
||||||
F: FnMut(&A) -> B,
|
F: FnMut(&A) -> B,
|
||||||
{
|
{
|
||||||
let _: Vec<_> = self
|
let _: Vec<_> = self
|
||||||
.0
|
.0
|
||||||
.read()
|
.read()
|
||||||
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|a| {
|
.map(|a| {
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
//! API para construir nuevos componentes.
|
|
||||||
|
|
||||||
mod definition;
|
|
||||||
pub use definition::{ComponentRender, ComponentTrait};
|
|
||||||
|
|
||||||
mod children;
|
|
||||||
pub use children::Children;
|
|
||||||
pub use children::{Child, ChildOp};
|
|
||||||
pub use children::{Typed, TypedOp};
|
|
|
@ -1,327 +0,0 @@
|
||||||
use crate::core::component::ComponentTrait;
|
|
||||||
use crate::html::{html, Context, Markup};
|
|
||||||
use crate::{builder_fn, UniqueId};
|
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::vec::IntoIter;
|
|
||||||
|
|
||||||
/// Representa un componente encapsulado de forma segura y compartida.
|
|
||||||
///
|
|
||||||
/// Esta estructura permite manipular y renderizar cualquier tipo que implemente [`ComponentTrait`],
|
|
||||||
/// garantizando acceso concurrente a través de [`Arc<RwLock<_>>`].
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Child(Arc<RwLock<dyn ComponentTrait>>);
|
|
||||||
|
|
||||||
impl Child {
|
|
||||||
/// Crea un nuevo [`Child`] a partir de un componente.
|
|
||||||
pub fn with(component: impl ComponentTrait) -> Self {
|
|
||||||
Child(Arc::new(RwLock::new(component)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Child GETTERS *******************************************************************************
|
|
||||||
|
|
||||||
/// Devuelve el identificador del componente, si está definido.
|
|
||||||
pub fn id(&self) -> Option<String> {
|
|
||||||
self.0.read().id()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Child RENDER ********************************************************************************
|
|
||||||
|
|
||||||
/// Renderiza el componente con el contexto proporcionado.
|
|
||||||
pub fn render(&self, cx: &mut Context) -> Markup {
|
|
||||||
self.0.write().render(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Child HELPERS *******************************************************************************
|
|
||||||
|
|
||||||
// Devuelve el [`UniqueId`] del tipo del componente.
|
|
||||||
fn type_id(&self) -> UniqueId {
|
|
||||||
self.0.read().type_id()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *************************************************************************************************
|
|
||||||
|
|
||||||
/// Variante tipada de [`Child`] para evitar conversiones durante el uso.
|
|
||||||
///
|
|
||||||
/// Facilita el acceso a componentes del mismo tipo sin necesidad de hacer `downcast`.
|
|
||||||
pub struct Typed<C: ComponentTrait>(Arc<RwLock<C>>);
|
|
||||||
|
|
||||||
impl<C: ComponentTrait> Clone for Typed<C> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: ComponentTrait> Typed<C> {
|
|
||||||
/// Crea un nuevo [`Typed`] a partir de un componente.
|
|
||||||
pub fn with(component: C) -> Self {
|
|
||||||
Typed(Arc::new(RwLock::new(component)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typed GETTERS *******************************************************************************
|
|
||||||
|
|
||||||
/// Devuelve el identificador del componente, si está definido.
|
|
||||||
pub fn id(&self) -> Option<String> {
|
|
||||||
self.0.read().id()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typed RENDER ********************************************************************************
|
|
||||||
|
|
||||||
/// Renderiza el componente con el contexto proporcionado.
|
|
||||||
pub fn render(&self, cx: &mut Context) -> Markup {
|
|
||||||
self.0.write().render(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typed HELPERS *******************************************************************************
|
|
||||||
|
|
||||||
/// Convierte el componente tipado en un [`Child`].
|
|
||||||
fn to_child(&self) -> Child {
|
|
||||||
Child(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// *************************************************************************************************
|
|
||||||
|
|
||||||
/// Operaciones con un componente [`Child`] en una lista [`Children`].
|
|
||||||
pub enum ChildOp {
|
|
||||||
Add(Child),
|
|
||||||
InsertAfterId(&'static str, Child),
|
|
||||||
InsertBeforeId(&'static str, Child),
|
|
||||||
Prepend(Child),
|
|
||||||
RemoveById(&'static str),
|
|
||||||
ReplaceById(&'static str, Child),
|
|
||||||
Reset,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Operaciones con un componente tipado [`Typed<C>`] en una lista [`Children`].
|
|
||||||
pub enum TypedOp<C: ComponentTrait> {
|
|
||||||
Add(Typed<C>),
|
|
||||||
InsertAfterId(&'static str, Typed<C>),
|
|
||||||
InsertBeforeId(&'static str, Typed<C>),
|
|
||||||
Prepend(Typed<C>),
|
|
||||||
RemoveById(&'static str),
|
|
||||||
ReplaceById(&'static str, Typed<C>),
|
|
||||||
Reset,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lista ordenada de los componentes hijo ([`Child`]) asociados a un componente padre.
|
|
||||||
///
|
|
||||||
/// Esta colección permite añadir, modificar, renderizar y consultar componentes hijos en orden de
|
|
||||||
/// inserción, soportando operaciones avanzadas como inserción relativa o reemplazo por
|
|
||||||
/// identificador.
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
pub struct Children(Vec<Child>);
|
|
||||||
|
|
||||||
impl Children {
|
|
||||||
/// Crea una lista vacía.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Children::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Crea una lista con un único hijo inicial.
|
|
||||||
pub fn with(child: Child) -> Self {
|
|
||||||
Children::default().with_child(ChildOp::Add(child))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fusiona varias listas de `Children` en una sola.
|
|
||||||
pub(crate) fn merge(mixes: &[Option<&Children>]) -> Self {
|
|
||||||
let mut opt = Children::default();
|
|
||||||
for m in mixes.iter().flatten() {
|
|
||||||
opt.0.extend(m.0.iter().cloned());
|
|
||||||
}
|
|
||||||
opt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Children BUILDER ****************************************************************************
|
|
||||||
|
|
||||||
/// Ejecuta una operación con [`ChildOp`] en la lista.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_child(mut self, op: ChildOp) -> Self {
|
|
||||||
match op {
|
|
||||||
ChildOp::Add(any) => self.add(any),
|
|
||||||
ChildOp::InsertAfterId(id, any) => self.insert_after_id(id, any),
|
|
||||||
ChildOp::InsertBeforeId(id, any) => self.insert_before_id(id, any),
|
|
||||||
ChildOp::Prepend(any) => self.prepend(any),
|
|
||||||
ChildOp::RemoveById(id) => self.remove_by_id(id),
|
|
||||||
ChildOp::ReplaceById(id, any) => self.replace_by_id(id, any),
|
|
||||||
ChildOp::Reset => self.reset(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ejecuta una operación con [`TypedOp`] en la lista.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_typed<C: ComponentTrait + Default>(mut self, op: TypedOp<C>) -> Self {
|
|
||||||
match op {
|
|
||||||
TypedOp::Add(typed) => self.add(typed.to_child()),
|
|
||||||
TypedOp::InsertAfterId(id, typed) => self.insert_after_id(id, typed.to_child()),
|
|
||||||
TypedOp::InsertBeforeId(id, typed) => self.insert_before_id(id, typed.to_child()),
|
|
||||||
TypedOp::Prepend(typed) => self.prepend(typed.to_child()),
|
|
||||||
TypedOp::RemoveById(id) => self.remove_by_id(id),
|
|
||||||
TypedOp::ReplaceById(id, typed) => self.replace_by_id(id, typed.to_child()),
|
|
||||||
TypedOp::Reset => self.reset(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Añade un hijo al final de la lista.
|
|
||||||
#[inline]
|
|
||||||
pub fn add(&mut self, child: Child) -> &mut Self {
|
|
||||||
self.0.push(child);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserta un hijo después del componente con el `id` dado, o al final si no se encuentra.
|
|
||||||
#[inline]
|
|
||||||
fn insert_after_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self {
|
|
||||||
let id = Some(id.as_ref());
|
|
||||||
match self.0.iter().position(|c| c.id().as_deref() == id) {
|
|
||||||
Some(index) => self.0.insert(index + 1, child),
|
|
||||||
_ => self.0.push(child),
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserta un hijo antes del componente con el `id` dado, o al principio si no se encuentra.
|
|
||||||
#[inline]
|
|
||||||
fn insert_before_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self {
|
|
||||||
let id = Some(id.as_ref());
|
|
||||||
match self.0.iter().position(|c| c.id().as_deref() == id) {
|
|
||||||
Some(index) => self.0.insert(index, child),
|
|
||||||
_ => self.0.insert(0, child),
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserta un hijo al principio de la colección.
|
|
||||||
#[inline]
|
|
||||||
fn prepend(&mut self, child: Child) -> &mut Self {
|
|
||||||
self.0.insert(0, child);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elimina el primer hijo con el `id` dado.
|
|
||||||
#[inline]
|
|
||||||
fn remove_by_id(&mut self, id: impl AsRef<str>) -> &mut Self {
|
|
||||||
let id = Some(id.as_ref());
|
|
||||||
if let Some(index) = self.0.iter().position(|c| c.id().as_deref() == id) {
|
|
||||||
self.0.remove(index);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sustituye el primer hijo con el `id` dado por otro componente.
|
|
||||||
#[inline]
|
|
||||||
fn replace_by_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self {
|
|
||||||
let id = Some(id.as_ref());
|
|
||||||
for c in &mut self.0 {
|
|
||||||
if c.id().as_deref() == id {
|
|
||||||
*c = child;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elimina todos los componentes hijo de la lista.
|
|
||||||
#[inline]
|
|
||||||
fn reset(&mut self) -> &mut Self {
|
|
||||||
self.0.clear();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Children GETTERS ****************************************************************************
|
|
||||||
|
|
||||||
/// Devuelve el número de componentes hijo de la lista.
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.0.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Indica si la lista está vacía.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.0.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve el primer componente hijo con el identificador indicado, si existe.
|
|
||||||
pub fn get_by_id(&self, id: impl AsRef<str>) -> Option<&Child> {
|
|
||||||
let id = Some(id.as_ref());
|
|
||||||
self.0.iter().find(|c| c.id().as_deref() == id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve un iterador sobre los componentes hijo con el identificador indicado.
|
|
||||||
pub fn iter_by_id<'a>(&'a self, id: &'a str) -> impl Iterator<Item = &'a Child> + 'a {
|
|
||||||
self.0.iter().filter(move |c| c.id().as_deref() == Some(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve un iterador sobre los componentes hijo con el identificador tipo ([`UniqueId`])
|
|
||||||
/// indicado.
|
|
||||||
pub fn iter_by_type_id(&self, type_id: UniqueId) -> impl Iterator<Item = &Child> {
|
|
||||||
self.0.iter().filter(move |&c| c.type_id() == type_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Children RENDER *****************************************************************************
|
|
||||||
|
|
||||||
/// Renderiza todos los componentes hijo, en orden.
|
|
||||||
pub fn render(&self, cx: &mut Context) -> Markup {
|
|
||||||
html! {
|
|
||||||
@for c in &self.0 {
|
|
||||||
(c.render(cx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for Children {
|
|
||||||
type Item = Child;
|
|
||||||
type IntoIter = IntoIter<Child>;
|
|
||||||
|
|
||||||
/// Consume la estructura `Children`, devolviendo un iterador que consume los elementos.
|
|
||||||
///
|
|
||||||
/// ### Ejemplo de uso:
|
|
||||||
/// ```rust#ignore
|
|
||||||
/// let children = Children::new().with(child1).with(child2);
|
|
||||||
/// for child in children {
|
|
||||||
/// println!("{:?}", child.id());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a Children {
|
|
||||||
type Item = &'a Child;
|
|
||||||
type IntoIter = std::slice::Iter<'a, Child>;
|
|
||||||
|
|
||||||
/// Itera sobre una referencia inmutable de `Children`, devolviendo un iterador de referencia.
|
|
||||||
///
|
|
||||||
/// ### Ejemplo de uso:
|
|
||||||
/// ```rust#ignore
|
|
||||||
/// let children = Children::new().with(child1).with(child2);
|
|
||||||
/// for child in &children {
|
|
||||||
/// println!("{:?}", child.id());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a mut Children {
|
|
||||||
type Item = &'a mut Child;
|
|
||||||
type IntoIter = std::slice::IterMut<'a, Child>;
|
|
||||||
|
|
||||||
/// Itera sobre una referencia mutable de `Children`, devolviendo un iterador mutable.
|
|
||||||
///
|
|
||||||
/// ### Ejemplo de uso:
|
|
||||||
/// ```rust#ignore
|
|
||||||
/// let mut children = Children::new().with(child1).with(child2);
|
|
||||||
/// for child in &mut children {
|
|
||||||
/// child.render(&mut context);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.iter_mut()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
use crate::base::action;
|
|
||||||
use crate::core::{AnyInfo, TypeInfo};
|
|
||||||
use crate::html::{html, Context, Markup, PrepareMarkup, Render};
|
|
||||||
|
|
||||||
/// Define la función de renderizado para todos los componentes.
|
|
||||||
///
|
|
||||||
/// Este *trait* se implementa automáticamente en cualquier tipo (componente) que implemente
|
|
||||||
/// [`ComponentTrait`], por lo que no requiere ninguna codificación manual.
|
|
||||||
pub trait ComponentRender {
|
|
||||||
/// Renderiza el componente usando el contexto proporcionado.
|
|
||||||
fn render(&mut self, cx: &mut Context) -> Markup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interfaz común que debe implementar un componente renderizable en `PageTop`.
|
|
||||||
///
|
|
||||||
/// Se recomienda que los componentes deriven [`AutoDefault`](crate::AutoDefault). También deben
|
|
||||||
/// implementar explícitamente el método [`new`](Self::new) y pueden sobrescribir los otros métodos
|
|
||||||
/// para personalizar su comportamiento.
|
|
||||||
pub trait ComponentTrait: AnyInfo + ComponentRender + Send + Sync {
|
|
||||||
/// Crea una nueva instancia del componente.
|
|
||||||
fn new() -> Self
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
/// Devuelve el nombre del componente.
|
|
||||||
///
|
|
||||||
/// Por defecto se obtiene del nombre corto del tipo usando [`TypeInfo::ShortName`].
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
TypeInfo::ShortName.of::<Self>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve una descripción opcional del componente.
|
|
||||||
///
|
|
||||||
/// Por defecto, no se proporciona ninguna descripción (`None`).
|
|
||||||
fn description(&self) -> Option<String> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve un identificador opcional para el componente.
|
|
||||||
///
|
|
||||||
/// Este identificador puede usarse para referenciar el componente en el HTML. Por defecto, no
|
|
||||||
/// tiene ningún identificador (`None`).
|
|
||||||
fn id(&self) -> Option<String> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configura el componente justo antes de preparar el renderizado.
|
|
||||||
///
|
|
||||||
/// Este método puede sobrescribirse para modificar la estructura interna del componente o el
|
|
||||||
/// contexto antes de preparar la renderización del componente. Por defecto no hace nada.
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn setup_before_prepare(&mut self, cx: &mut Context) {}
|
|
||||||
|
|
||||||
/// Devuelve una representación estructurada del componente lista para renderizar.
|
|
||||||
///
|
|
||||||
/// Puede sobrescribirse para generar dinámicamente el contenido HTML. Por defecto, devuelve
|
|
||||||
/// [`PrepareMarkup::None`].
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
|
||||||
PrepareMarkup::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementa [`render()`](ComponentRender::render) para todos los componentes.
|
|
||||||
///
|
|
||||||
/// Y para cada componente ejecuta la siguiente secuencia:
|
|
||||||
///
|
|
||||||
/// 1. Despacha [`action::component::IsRenderable`](crate::base::action::component::IsRenderable)
|
|
||||||
/// para ver si se puede renderizar. Si no es así, devuelve un [`Markup`] vacío.
|
|
||||||
/// 2. Ejecuta [`setup_before_prepare()`](ComponentTrait::setup_before_prepare) para que el
|
|
||||||
/// componente pueda ajustar su estructura interna o modificar el contexto.
|
|
||||||
/// 3. Despacha [`action::theme::BeforeRender<C>`](crate::base::action::theme::BeforeRender) para
|
|
||||||
/// que el tema pueda hacer ajustes en el componente o el contexto.
|
|
||||||
/// 4. Despacha [`action::component::BeforeRender<C>`](crate::base::action::component::BeforeRender)
|
|
||||||
/// para que otras extensiones puedan hacer ajustes.
|
|
||||||
/// 5. **Prepara el renderizado del componente**:
|
|
||||||
/// - Despacha [`action::theme::PrepareRender<C>`](crate::base::action::theme::PrepareRender)
|
|
||||||
/// para permitir al tema preparar un renderizado diferente al predefinido.
|
|
||||||
/// - Si no es así, ejecuta [`prepare_component()`](ComponentTrait::prepare_component) para
|
|
||||||
/// preparar el renderizado predefinido del componente.
|
|
||||||
/// 6. Despacha [`action::theme::AfterRender<C>`](crate::base::action::theme::AfterRender) para
|
|
||||||
/// que el tema pueda hacer sus últimos ajustes.
|
|
||||||
/// 7. Despacha [`action::component::AfterRender<C>`](crate::base::action::component::AfterRender)
|
|
||||||
/// para que otras extensiones puedan hacer sus últimos ajustes.
|
|
||||||
/// 8. Finalmente devuelve un [`Markup`] del renderizado preparado en el paso 5.
|
|
||||||
impl<C: ComponentTrait> ComponentRender for C {
|
|
||||||
fn render(&mut self, cx: &mut Context) -> Markup {
|
|
||||||
// Si no es renderizable, devuelve un bloque HTML vacío.
|
|
||||||
if !action::component::IsRenderable::dispatch(self, cx) {
|
|
||||||
return html! {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configura el componente antes de preparar.
|
|
||||||
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.
|
|
||||||
action::component::BeforeRender::dispatch(self, cx);
|
|
||||||
|
|
||||||
// Prepara el renderizado del componente.
|
|
||||||
let prepare = action::theme::PrepareRender::dispatch(self, cx);
|
|
||||||
let prepare = if prepare.is_empty() {
|
|
||||||
self.prepare_component(cx)
|
|
||||||
} else {
|
|
||||||
prepare
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
action::component::AfterRender::dispatch(self, cx);
|
|
||||||
|
|
||||||
// Devuelve el marcado final.
|
|
||||||
prepare.render()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,9 @@
|
||||||
use crate::core::action::add_action;
|
use crate::core::action::add_action;
|
||||||
use crate::core::extension::ExtensionRef;
|
use crate::core::extension::ExtensionRef;
|
||||||
use crate::core::theme::all::THEMES;
|
use crate::core::theme::all::THEMES;
|
||||||
use crate::{/*global, include_files, include_files_service, */ service, trace};
|
use crate::{service, trace};
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
use std::sync::{LazyLock, RwLock};
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
// EXTENSIONES *************************************************************************************
|
// EXTENSIONES *************************************************************************************
|
||||||
|
|
||||||
|
@ -29,11 +27,11 @@ pub fn register_extensions(root_extension: Option<ExtensionRef>) {
|
||||||
add_to_enabled(&mut enabled_list, extension);
|
add_to_enabled(&mut enabled_list, extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Añade la página de bienvenida por defecto a la lista de extensiones habilitadas.
|
|
||||||
add_to_enabled(&mut enabled_list, &crate::base::extension::Welcome); */
|
|
||||||
|
|
||||||
// Guarda la lista final de extensiones habilitadas.
|
// Guarda la lista final de extensiones habilitadas.
|
||||||
ENABLED_EXTENSIONS.write().append(&mut enabled_list);
|
ENABLED_EXTENSIONS
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.append(&mut enabled_list);
|
||||||
|
|
||||||
// Prepara una lista de extensiones deshabilitadas.
|
// Prepara una lista de extensiones deshabilitadas.
|
||||||
let mut dropped_list: Vec<ExtensionRef> = Vec::new();
|
let mut dropped_list: Vec<ExtensionRef> = Vec::new();
|
||||||
|
@ -44,7 +42,10 @@ pub fn register_extensions(root_extension: Option<ExtensionRef>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guarda la lista final de extensiones deshabilitadas.
|
// Guarda la lista final de extensiones deshabilitadas.
|
||||||
DROPPED_EXTENSIONS.write().append(&mut dropped_list);
|
DROPPED_EXTENSIONS
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.append(&mut dropped_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_enabled(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
|
fn add_to_enabled(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
|
||||||
|
@ -60,7 +61,7 @@ fn add_to_enabled(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
|
||||||
|
|
||||||
// Comprueba si la extensión tiene un tema asociado que deba registrarse.
|
// Comprueba si la extensión tiene un tema asociado que deba registrarse.
|
||||||
if let Some(theme) = extension.theme() {
|
if let Some(theme) = extension.theme() {
|
||||||
let mut registered_themes = THEMES.write();
|
let mut registered_themes = THEMES.write().unwrap();
|
||||||
// Asegura que el tema no esté ya registrado para evitar duplicados.
|
// Asegura que el tema no esté ya registrado para evitar duplicados.
|
||||||
if !registered_themes
|
if !registered_themes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -83,6 +84,7 @@ fn add_to_dropped(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
|
||||||
// Comprueba si la extensión está habilitada. Si es así, registra una advertencia.
|
// Comprueba si la extensión está habilitada. Si es así, registra una advertencia.
|
||||||
if ENABLED_EXTENSIONS
|
if ENABLED_EXTENSIONS
|
||||||
.read()
|
.read()
|
||||||
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|e| e.type_id() == extension.type_id())
|
.any(|e| e.type_id() == extension.type_id())
|
||||||
{
|
{
|
||||||
|
@ -107,7 +109,7 @@ fn add_to_dropped(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) {
|
||||||
// REGISTRO DE LAS ACCIONES ************************************************************************
|
// REGISTRO DE LAS ACCIONES ************************************************************************
|
||||||
|
|
||||||
pub fn register_actions() {
|
pub fn register_actions() {
|
||||||
for extension in ENABLED_EXTENSIONS.read().iter() {
|
for extension in ENABLED_EXTENSIONS.read().unwrap().iter() {
|
||||||
for a in extension.actions().into_iter() {
|
for a in extension.actions().into_iter() {
|
||||||
add_action(a);
|
add_action(a);
|
||||||
}
|
}
|
||||||
|
@ -118,20 +120,15 @@ pub fn register_actions() {
|
||||||
|
|
||||||
pub fn initialize_extensions() {
|
pub fn initialize_extensions() {
|
||||||
trace::info!("Calling application bootstrap");
|
trace::info!("Calling application bootstrap");
|
||||||
for extension in ENABLED_EXTENSIONS.read().iter() {
|
for extension in ENABLED_EXTENSIONS.read().unwrap().iter() {
|
||||||
extension.initialize();
|
extension.initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONFIGURA LOS SERVICIOS *************************************************************************
|
// CONFIGURA LOS SERVICIOS *************************************************************************
|
||||||
|
|
||||||
//include_files!(assets);
|
|
||||||
|
|
||||||
pub fn configure_services(scfg: &mut service::web::ServiceConfig) {
|
pub fn configure_services(scfg: &mut service::web::ServiceConfig) {
|
||||||
for extension in ENABLED_EXTENSIONS.read().iter() {
|
for extension in ENABLED_EXTENSIONS.read().unwrap().iter() {
|
||||||
extension.configure_service(scfg);
|
extension.configure_service(scfg);
|
||||||
}
|
}
|
||||||
/*include_files_service!(
|
|
||||||
scfg, assets => "/", [&global::SETTINGS.dev.pagetop_project_dir, "static"]
|
|
||||||
);*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::{inject_actions, service};
|
||||||
|
|
||||||
/// Representa una referencia a una extensión.
|
/// Representa una referencia a una extensión.
|
||||||
///
|
///
|
||||||
|
@ -77,7 +77,7 @@ pub trait ExtensionTrait: AnyInfo + Send + Sync {
|
||||||
/// [peso](crate::Weight), permitiendo personalizar el comportamiento de la aplicación en puntos
|
/// [peso](crate::Weight), permitiendo personalizar el comportamiento de la aplicación en puntos
|
||||||
/// específicos.
|
/// específicos.
|
||||||
fn actions(&self) -> Vec<ActionBox> {
|
fn actions(&self) -> Vec<ActionBox> {
|
||||||
actions_boxed![]
|
inject_actions![]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inicializa la extensión durante la lógica de arranque de la aplicación.
|
/// Inicializa la extensión durante la lógica de arranque de la aplicación.
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::core::theme::ThemeRef;
|
use crate::core::theme::ThemeRef;
|
||||||
use crate::global;
|
use crate::global;
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
use std::sync::{LazyLock, RwLock};
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
// TEMAS *******************************************************************************************
|
// TEMAS *******************************************************************************************
|
||||||
|
|
||||||
|
@ -19,11 +17,11 @@ pub static DEFAULT_THEME: LazyLock<ThemeRef> =
|
||||||
|
|
||||||
// TEMA POR NOMBRE *********************************************************************************
|
// TEMA POR NOMBRE *********************************************************************************
|
||||||
|
|
||||||
/// Devuelve el tema identificado por su [`short_name`](AnyInfo::short_name).
|
|
||||||
pub fn theme_by_short_name(short_name: impl AsRef<str>) -> Option<ThemeRef> {
|
pub fn theme_by_short_name(short_name: impl AsRef<str>) -> Option<ThemeRef> {
|
||||||
let short_name = short_name.as_ref().to_lowercase();
|
let short_name = short_name.as_ref().to_lowercase();
|
||||||
match THEMES
|
match THEMES
|
||||||
.read()
|
.read()
|
||||||
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|t| t.short_name().to_lowercase() == short_name)
|
.find(|t| t.short_name().to_lowercase() == short_name)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub type ThemeRef = &'static dyn ThemeTrait;
|
||||||
/// Interfaz común que debe implementar cualquier tema de `PageTop`.
|
/// Interfaz común que debe implementar cualquier tema de `PageTop`.
|
||||||
///
|
///
|
||||||
/// Un tema implementará [`ThemeTrait`] y los métodos que sean necesarios de [`ExtensionTrait`],
|
/// Un tema implementará [`ThemeTrait`] y los métodos que sean necesarios de [`ExtensionTrait`],
|
||||||
/// aunque el único obligatorio es [`theme()`](ExtensionTrait::theme).
|
/// aunque el único obligatorio es [`theme()`](crate::core::extension::ExtensionTrait#method.theme).
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
|
|
|
@ -27,9 +27,6 @@ pub use opt_translated::OptionTranslated;
|
||||||
mod opt_classes;
|
mod opt_classes;
|
||||||
pub use opt_classes::{ClassesOp, OptionClasses};
|
pub use opt_classes::{ClassesOp, OptionClasses};
|
||||||
|
|
||||||
mod opt_component;
|
|
||||||
pub use opt_component::OptionComponent;
|
|
||||||
|
|
||||||
use crate::AutoDefault;
|
use crate::AutoDefault;
|
||||||
|
|
||||||
/// Prepara contenido HTML para su conversión a [`Markup`].
|
/// Prepara contenido HTML para su conversión a [`Markup`].
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
use crate::builder_fn;
|
|
||||||
use crate::core::component::{ComponentTrait, Typed};
|
|
||||||
use crate::html::{html, Context, Markup};
|
|
||||||
|
|
||||||
/// Contenedor de componente para incluir en otros componentes.
|
|
||||||
///
|
|
||||||
/// Este tipo encapsula `Option<Typed<C>>` para incluir un componente de manera segura en otros
|
|
||||||
/// componentes, útil para representar estructuras complejas.
|
|
||||||
///
|
|
||||||
/// # Ejemplo
|
|
||||||
///
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// use pagetop::prelude::*;
|
|
||||||
///
|
|
||||||
/// let comp = MyComponent::new();
|
|
||||||
/// let opt = OptionComponent::new(comp);
|
|
||||||
/// assert!(opt.get().is_some());
|
|
||||||
/// ```
|
|
||||||
pub struct OptionComponent<C: ComponentTrait>(Option<Typed<C>>);
|
|
||||||
|
|
||||||
impl<C: ComponentTrait> Default for OptionComponent<C> {
|
|
||||||
fn default() -> Self {
|
|
||||||
OptionComponent(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: ComponentTrait> OptionComponent<C> {
|
|
||||||
/// Crea un nuevo [`OptionComponent`].
|
|
||||||
///
|
|
||||||
/// El componente se envuelve automáticamente en un [`Typed`] y se almacena.
|
|
||||||
pub fn new(component: C) -> Self {
|
|
||||||
OptionComponent::default().with_value(Some(component))
|
|
||||||
}
|
|
||||||
|
|
||||||
// OptionComponent BUILDER *********************************************************************
|
|
||||||
|
|
||||||
/// Establece un componente nuevo, o lo vacía.
|
|
||||||
///
|
|
||||||
/// Si se proporciona `Some(component)`, se guarda en [`Typed`]; y si es `None`, se limpia.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_value(mut self, component: Option<C>) -> Self {
|
|
||||||
if let Some(component) = component {
|
|
||||||
self.0 = Some(Typed::with(component));
|
|
||||||
} else {
|
|
||||||
self.0 = None;
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// OptionComponent GETTERS *********************************************************************
|
|
||||||
|
|
||||||
/// Devuelve el componente, si existe.
|
|
||||||
pub fn get(&self) -> Option<Typed<C>> {
|
|
||||||
if let Some(value) = &self.0 {
|
|
||||||
return Some(value.clone());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renderiza el componente, si existe.
|
|
||||||
pub fn render(&self, cx: &mut Context) -> Markup {
|
|
||||||
if let Some(component) = &self.0 {
|
|
||||||
component.render(cx)
|
|
||||||
} else {
|
|
||||||
html! {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -92,11 +92,11 @@ pub mod html;
|
||||||
pub mod locale;
|
pub mod locale;
|
||||||
// Soporte a fechas y horas.
|
// Soporte a fechas y horas.
|
||||||
pub mod datetime;
|
pub mod datetime;
|
||||||
// Tipos y funciones esenciales para crear acciones, componentes, extensiones y temas.
|
// Tipos y funciones esenciales para crear acciones, extensiones y temas.
|
||||||
pub mod core;
|
pub mod core;
|
||||||
// Gestión del servidor y servicios web.
|
// Gestión del servidor y servicios web.
|
||||||
pub mod service;
|
pub mod service;
|
||||||
// Reúne acciones, componentes y temas listos para usar.
|
// Reúne temas listos para usar.
|
||||||
pub mod base;
|
pub mod base;
|
||||||
// Prepara y ejecuta la aplicación.
|
// Prepara y ejecuta la aplicación.
|
||||||
pub mod app;
|
pub mod app;
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub use crate::include_locales;
|
||||||
// crate::service
|
// crate::service
|
||||||
pub use crate::{include_files, include_files_service};
|
pub use crate::{include_files, include_files_service};
|
||||||
// crate::core::action
|
// crate::core::action
|
||||||
pub use crate::actions_boxed;
|
pub use crate::inject_actions;
|
||||||
|
|
||||||
// API.
|
// API.
|
||||||
|
|
||||||
|
@ -38,12 +38,9 @@ pub use crate::service;
|
||||||
pub use crate::core::{AnyCast, AnyInfo, TypeInfo};
|
pub use crate::core::{AnyCast, AnyInfo, TypeInfo};
|
||||||
|
|
||||||
pub use crate::core::action::*;
|
pub use crate::core::action::*;
|
||||||
pub use crate::core::component::*;
|
|
||||||
pub use crate::core::extension::*;
|
pub use crate::core::extension::*;
|
||||||
pub use crate::core::theme::*;
|
pub use crate::core::theme::*;
|
||||||
|
|
||||||
pub use crate::base::action;
|
|
||||||
pub use crate::base::component::*;
|
|
||||||
pub use crate::base::theme;
|
pub use crate::base::theme;
|
||||||
|
|
||||||
pub use crate::app::Application;
|
pub use crate::app::Application;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue