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 /// [`Component`], 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 Component: 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::() } /// Devuelve una descripción opcional del componente. /// /// Por defecto, no se proporciona ninguna descripción (`None`). fn description(&self) -> Option { 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 { 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 preparada para el renderizado. /// /// Este método forma parte del ciclo de vida de los componentes y se invoca automáticamente /// durante el proceso de construcción del documento. Puede sobrescribirse para generar /// dinámicamente el contenido HTML con acceso al contexto de renderizado. /// /// Este método debe ser capaz de preparar el renderizado del componente con los métodos del /// propio componente y el contexto proporcionado, no debería hacerlo accediendo directamente a /// los campos de la estructura del componente. Es una forma de garantizar que los programadores /// podrán sobrescribir este método sin preocuparse por los detalles internos del componente. /// /// 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()`](Component::setup_before_prepare) para que el componente /// pueda ajustar su estructura interna o modificar el contexto. /// 3. Despacha [`action::theme::BeforeRender`](crate::base::action::theme::BeforeRender) para /// que el tema pueda hacer ajustes en el componente o el contexto. /// 4. Despacha [`action::component::BeforeRender`](crate::base::action::component::BeforeRender) /// para que otras extensiones puedan hacer ajustes. /// 5. **Prepara el renderizado del componente**: /// - Despacha [`action::theme::PrepareRender`](crate::base::action::theme::PrepareRender) /// para permitir al tema preparar un renderizado diferente al predefinido. /// - Si no es así, ejecuta [`prepare_component()`](Component::prepare_component) para preparar /// el renderizado predefinido del componente. /// 6. Despacha [`action::theme::AfterRender`](crate::base::action::theme::AfterRender) para /// que el tema pueda hacer sus últimos ajustes. /// 7. Despacha [`action::component::AfterRender`](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 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() } }