(base): Añade componente Messages

Muestra los mensajes acumulados en el Context por severidad.
This commit is contained in:
Manuel Cillero 2026-08-13 17:35:23 +02:00
parent 51e8b11090
commit e5431bc7d7
3 changed files with 124 additions and 0 deletions

View file

@ -16,6 +16,10 @@
--val-color--text--muted: color-mix(in srgb, var(--val-color--text) 50%, var(--val-color--bg)); --val-color--text--muted: color-mix(in srgb, var(--val-color--text) 50%, var(--val-color--bg));
--val-color--border: #212529; --val-color--border: #212529;
--val-color--primary: #0d6efd; --val-color--primary: #0d6efd;
--val-color--secondary: #6c757d;
--val-color--light: #dee2e6;
--val-color--info: #0dcaf0;
--val-color--warning: #ffc107;
--val-color--danger: #dc3545; --val-color--danger: #dc3545;
--val-color--switch-off: #adb5bd; --val-color--switch-off: #adb5bd;
} }
@ -137,6 +141,35 @@ input:disabled + label {
color: var(--val-color--text--muted); color: var(--val-color--text--muted);
} }
/*
* Messages component
*/
.message {
padding: 0.75rem 1rem;
margin-bottom: 1rem;
border: 1px solid transparent;
border-radius: 0.375rem;
}
.message-info {
color: color-mix(in srgb, var(--val-color--info) 40%, black);
background-color: color-mix(in srgb, var(--val-color--info) 15%, var(--val-color--bg));
border-color: color-mix(in srgb, var(--val-color--info) 40%, var(--val-color--bg));
}
.message-warning {
color: color-mix(in srgb, var(--val-color--warning) 40%, black);
background-color: color-mix(in srgb, var(--val-color--warning) 15%, var(--val-color--bg));
border-color: color-mix(in srgb, var(--val-color--warning) 40%, var(--val-color--bg));
}
.message-error {
color: color-mix(in srgb, var(--val-color--danger) 40%, black);
background-color: color-mix(in srgb, var(--val-color--danger) 15%, var(--val-color--bg));
border-color: color-mix(in srgb, var(--val-color--danger) 40%, var(--val-color--bg));
}
/* /*
* Table component * Table component
*/ */

View file

@ -29,6 +29,9 @@ pub use html::Html;
mod intro; mod intro;
pub use intro::{Intro, IntroOpening}; pub use intro::{Intro, IntroOpening};
mod messages;
pub use messages::Messages;
mod pager; mod pager;
pub use pager::{Pager, PagerAlign, PagerVisibility}; pub use pager::{Pager, PagerAlign, PagerVisibility};

View file

@ -0,0 +1,88 @@
use crate::prelude::*;
/// Componente que muestra los **mensajes de usuario** acumulados en el contexto.
///
/// No recibe ningún contenido de quien lo construye. Lo obtiene directamente del [`Context`] (ver
/// [`Context::push_message()`](Context::push_message)) en el momento de renderizarse (ver
/// [`Context::messages()`]). Si no hay ninguno acumulado, no se renderiza nada.
///
/// # Clases CSS
///
/// Cada nivel de severidad renderiza siempre las mismas clases fijas: `.message.message-info`,
/// `.message.message-warning` o `.message.message-error`. Será el tema activo quien decide el
/// aspecto visual de cada nivel, proporcionando las reglas CSS para esas clases.
///
/// # Ejemplo
///
/// ```rust,no_run
/// use pagetop::prelude::*;
///
/// async fn save_settings(cx: &mut Context) {
/// cx.push_message(MessageLevel::Info, Lc::n("Settings saved."));
/// }
///
/// async fn homepage(request: HttpRequest) -> Result<Markup, ErrorPage> {
/// Page::new(request)
/// .with_child(Messages::new())
/// // ... resto del contenido de la página ...
/// .render()
/// .await
/// }
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Messages {
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
props: Props,
}
#[async_trait]
impl Component for Messages {
fn new() -> Self {
Self::default()
}
fn id(&self) -> Option<String> {
self.props.get_id()
}
fn setup(&mut self, _cx: &Context) {
self.alter_prop(PropsOp::prepend_classes("messages"));
}
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
if !cx.has_messages() {
return Ok(html! {});
}
Ok(html! {
div (self.props()) role="alert" {
@for message in cx.messages() {
div class=(match message.level() {
MessageLevel::Info => "message message-info",
MessageLevel::Warning => "message message-warning",
MessageLevel::Error => "message message-error",
}) {
(message.text().using(cx))
}
}
}
})
}
}
impl Messages {
// **< Messages BUILDER >***********************************************************************
/// Establece el identificador único del componente; igual a `with_prop(PropsOp::set_id(id))`.
#[builder_fn]
pub fn with_id(mut self, id: impl Into<CowStr>) -> Self {
self.props.alter_id(id);
self
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
#[builder_fn]
pub fn with_prop(mut self, op: PropsOp) -> Self {
self.props.alter_prop(op);
self
}
}