♻️ (pagetop): setup() recibe &mut Context
Antes sólo mutaba el propio componente; ahora también puede aplicar ajustes síncronos sobre el Context, visibles para las extensiones que intercepten BeforeRender. La frontera con prepare() pasa a ser de sincronía, no de qué puede mutar cada uno.
This commit is contained in:
parent
f2a7507317
commit
1e2d171805
31 changed files with 71 additions and 59 deletions
|
|
@ -31,7 +31,7 @@ impl Component for Icon {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if !matches!(self.icon_kind(), IconKind::None) {
|
||||
self.alter_prop(PropsOp::prepend_classes("icon"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ impl Component for Offcanvas {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura que el panel tiene un identificador único.
|
||||
self.alter_prop(PropsOp::ensure_id(cx.build_id::<Self>(1)));
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ impl Component for RoleTable {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::set_id("role-table-wrapper"));
|
||||
self.alter_prop(PropsOp::prepend_classes("user-admin-table-wrapper"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ impl Component for UserTable {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::set_id("user-table-wrapper"));
|
||||
self.alter_prop(PropsOp::prepend_classes("user-admin-table-wrapper"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ impl Component for Badge {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes(util::join!(
|
||||
"badge badge-",
|
||||
self.intent().color(cx)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl Component for Block {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura que el bloque tiene un identificador único.
|
||||
self.alter_prop(PropsOp::ensure_id(cx.build_id::<Self>(1)));
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ impl Component for Brand {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes("brand"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ impl Component for Breadcrumb {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
for crumb in self.crumbs.iter_mut() {
|
||||
crumb.setup(cx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl Crumb {
|
|||
}
|
||||
|
||||
// Normaliza la clase base según el papel del elemento. Sólo lo usa `Breadcrumb`.
|
||||
pub(super) fn setup(&mut self, _cx: &Context) {
|
||||
pub(super) fn setup(&mut self, _cx: &mut Context) {
|
||||
if *self.is_current() {
|
||||
self.alter_prop(PropsOp::prepend_classes("active"))
|
||||
.alter_prop(PropsOp::set("aria-current", "page"));
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ impl Component for Button {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
use button::{Size, Style};
|
||||
|
||||
self.alter_prop(PropsOp::prepend_classes(match self.size() {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ impl Component for Container {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if let Some(flex) = self.flex() {
|
||||
flex.apply_to(&mut self.props);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ impl Component for Dialog {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura que el diálogo tiene un identificador único con el que abrirlo.
|
||||
self.alter_prop(PropsOp::ensure_id(cx.build_id::<Self>(1)));
|
||||
self.alter_prop(PropsOp::prepend_classes("dialog"));
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl Component for Dropdown {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes("dropdown"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ impl Component for Field {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura `name` e `id`.
|
||||
// Si falta uno se deriva del otro; si faltan ambos se genera un valor único.
|
||||
let name = self
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ impl Component for Checkbox {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura `name` e `id`.
|
||||
// Si falta uno se deriva del otro; si faltan ambos se genera un valor único.
|
||||
let name = self
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ impl Component for Form {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes("form"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ impl Component for Field {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if let Some(container_id) = self
|
||||
.id()
|
||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ impl Component for Number {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if let Some(container_id) = self
|
||||
.id()
|
||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ impl Component for Field {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura `name` e `id`.
|
||||
// Si falta uno se deriva del otro; si faltan ambos se genera un valor único.
|
||||
let name = self
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ impl Component for Range {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if let Some(container_id) = self
|
||||
.id()
|
||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ impl Component for Field {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if let Some(container_id) = self
|
||||
.id()
|
||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ impl Component for Textarea {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if let Some(container_id) = self
|
||||
.id()
|
||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ impl Component for Image {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes(match self.source() {
|
||||
image::Source::Logo(_) => "image image-fluid",
|
||||
image::Source::Responsive(_) => "image image-fluid",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl Component for Messages {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes("messages"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ impl Component for Nav {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes(match self.nav_layout() {
|
||||
nav::Layout::Default => "nav",
|
||||
nav::Layout::Start => "nav nav-start",
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl Component for Item {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes(self.item_kind().as_str()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ impl Component for Navbar {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura que la barra de navegación tiene un identificador único: lo necesita el botón de
|
||||
// despliegue para referenciar el contenido colapsable con `aria-controls`.
|
||||
self.alter_prop(PropsOp::ensure_id(cx.build_id::<Self>(1)));
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ impl Component for Item {
|
|||
}
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
if let Self::Nav(nav) = self
|
||||
&& let Some(nav) = nav.get_mut()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ impl Component for Pager {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, cx: &Context) {
|
||||
fn setup(&mut self, cx: &mut Context) {
|
||||
// Asegura un `id` propio si no está definido. El formulario de salto a página deriva sus
|
||||
// identificadores de éste para no colisionar si hay varios paginadores en la misma página.
|
||||
let id = cx.required_id::<Self>(self.id(), 1);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ impl Component for Table {
|
|||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
fn setup(&mut self, _cx: &mut Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes("table"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,24 +89,30 @@ pub trait Component: AnyInfo + ComponentClone + ComponentRender + Send + Sync {
|
|||
|
||||
/// Configura el estado interno del componente antes de generar el marcado.
|
||||
///
|
||||
/// Segundo paso del [ciclo de renderizado](ComponentRender): se ejecuta tras comprobar
|
||||
/// [`is_renderable()`](Self::is_renderable) y antes de la acción
|
||||
/// [`BeforeRender`](crate::base::action::component::BeforeRender) y de
|
||||
/// [`prepare()`](Self::prepare). Recibe sólo una referencia compartida al contexto porque su
|
||||
/// propósito es mutar el propio componente, no el contexto. Por defecto no hace nada.
|
||||
/// Segundo paso del [ciclo de renderizado](ComponentRender). Se ejecuta tras comprobar
|
||||
/// [`is_renderable()`] y antes de la acción [`BeforeRender`] y de [`prepare()`]. Por defecto no
|
||||
/// hace nada.
|
||||
///
|
||||
/// Está pensado para **normalizar el estado interno** del componente antes de renderizarlo. Por
|
||||
/// ejemplo, calcular clases CSS, ajustar valores de campos, derivar atributos a partir del
|
||||
/// contexto, etc. Se desaconseja utilizar para operaciones de E/S o consultas a base de datos;
|
||||
/// es intencionadamente síncrono.
|
||||
/// contexto, etc. Recibe `&mut Context` donde también puede aplicar ajustes que dependan del
|
||||
/// propio componente (p. ej. estilos o *assets*); cualquier extensión que intercepte
|
||||
/// [`BeforeRender`] verá ya aplicados esos cambios, porque `setup()` se ejecuta antes. Se
|
||||
/// desaconseja utilizar para operaciones de E/S o consultas a base de datos; es
|
||||
/// intencionadamente síncrono.
|
||||
///
|
||||
/// La carga y el acceso a datos en general corresponden a [`prepare()`](Self::prepare), que es
|
||||
/// `async` precisamente para ello.
|
||||
/// La frontera con [`prepare()`] es de **sincronía**, no de qué puede modificar cada uno. Los
|
||||
/// dos reciben `&mut Context` (ambos pueden realizar ajustes en él), pero sólo `setup()` recibe
|
||||
/// además `&mut self` (`prepare()` sólo recibe `&self`, así que no puede normalizar el propio
|
||||
/// componente). La carga y el acceso a datos siguen correspondiendo a `prepare()`, que es
|
||||
/// `async` precisamente para permitir E/S; `setup()` se mantiene síncrono a propósito. Esta
|
||||
/// separación es deliberada y no debe fusionarse.
|
||||
///
|
||||
/// La separación entre `setup()` (mutación de estado) y [`prepare()`](Self::prepare)
|
||||
/// (generación de HTML) es deliberada y no debe fusionarse.
|
||||
/// [`is_renderable()`]: Self::is_renderable
|
||||
/// [`prepare()`]: Self::prepare
|
||||
/// [`BeforeRender`]: crate::base::action::component::BeforeRender
|
||||
#[allow(unused_variables)]
|
||||
fn setup(&mut self, cx: &Context) {}
|
||||
fn setup(&mut self, cx: &mut Context) {}
|
||||
|
||||
/// Genera el marcado HTML del componente cuando ningún tema lo sobrescribe.
|
||||
///
|
||||
|
|
@ -154,27 +160,33 @@ impl<T: Component + Clone + 'static> ComponentClone for T {
|
|||
|
||||
// *************************************************************************************************
|
||||
|
||||
/// Implementa [`render()`](ComponentRender::render) para todos los componentes.
|
||||
/// Implementa [`render()`] para todos los componentes.
|
||||
///
|
||||
/// El proceso de renderizado de cada componente sigue esta secuencia:
|
||||
///
|
||||
/// 1. Ejecuta [`is_renderable()`](Component::is_renderable) para ver si puede renderizarse en el
|
||||
/// contexto actual. Si no es así, devuelve un [`Markup`] vacío.
|
||||
/// 2. Ejecuta [`setup()`](Component::setup) para que el componente
|
||||
/// pueda ajustar su estructura interna.
|
||||
/// 3. Despacha [`action::component::BeforeRender<C>`](crate::base::action::component::BeforeRender)
|
||||
/// para que las extensiones puedan hacer ajustes previos.
|
||||
/// 1. Ejecuta [`is_renderable()`] para ver si puede renderizarse en el contexto actual. Si no es
|
||||
/// así, devuelve un [`Markup`] vacío.
|
||||
/// 2. Ejecuta [`setup()`] para que el componente pueda ajustar su estado interno y, de forma
|
||||
/// síncrona, el propio [`Context`].
|
||||
/// 3. Despacha [`action::component::BeforeRender<C>`] para que las extensiones puedan hacer ajustes
|
||||
/// previos.
|
||||
/// 4. Prepara el renderizado del componente, recorre la cadena de temas (hijo > padre > abuelo...)
|
||||
/// llamando a [`Theme::handle_component()`](crate::core::theme::Theme::handle_component) en cada
|
||||
/// nivel hasta que uno devuelva `Some`. Si ninguno lo sobrescribe, llama al
|
||||
/// [`Component::prepare()`](Component::prepare) del propio componente.
|
||||
/// 5. Despacha [`action::component::AfterRender<C>`](crate::base::action::component::AfterRender)
|
||||
/// para que las extensiones puedan reaccionar con sus últimos ajustes.
|
||||
/// 6. Finalmente despacha
|
||||
/// [`action::component::TransformMarkup<C>`](crate::base::action::component::TransformMarkup)
|
||||
/// para que las extensiones puedan trabajar sobre el HTML final para modificarlo antes de
|
||||
/// devolverlo.
|
||||
/// llamando a [`Theme::handle_component()`] en cada nivel hasta que uno devuelva `Some`. Si
|
||||
/// ninguno lo sobrescribe, llama al [`Component::prepare()`] del propio componente.
|
||||
/// 5. Despacha [`action::component::AfterRender<C>`] para que las extensiones puedan reaccionar con
|
||||
/// sus últimos ajustes.
|
||||
/// 6. Finalmente despacha [`action::component::TransformMarkup<C>`] para que las extensiones puedan
|
||||
/// trabajar sobre el HTML final para modificarlo antes de devolverlo.
|
||||
/// 7. Devuelve el [`Markup`] resultante.
|
||||
///
|
||||
/// [`render()`]: ComponentRender::render
|
||||
/// [`is_renderable()`]: Component::is_renderable
|
||||
/// [`setup()`]: Component::setup
|
||||
/// [`Component::prepare()`]: Component::prepare
|
||||
/// [`action::component::BeforeRender<C>`]: crate::base::action::component::BeforeRender
|
||||
/// [`action::component::AfterRender<C>`]: crate::base::action::component::AfterRender
|
||||
/// [`action::component::TransformMarkup<C>`]: crate::base::action::component::TransformMarkup
|
||||
/// [`Theme::handle_component()`]: crate::core::theme::Theme::handle_component
|
||||
#[async_trait]
|
||||
impl<C: Component> ComponentRender for C {
|
||||
async fn render(&mut self, cx: &mut Context) -> Markup {
|
||||
|
|
@ -183,7 +195,7 @@ impl<C: Component> ComponentRender for C {
|
|||
return html! {};
|
||||
}
|
||||
|
||||
// Configura el componente antes de preparar.
|
||||
// Configura el componente (y, de forma síncrona, el contexto) antes de preparar.
|
||||
self.setup(cx);
|
||||
|
||||
// Acciones de las extensiones antes de renderizar el componente.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue