- Añade `#[builder_fn]` a `with_width()`/`with_floating_label()`. - `Rounded::new()` pasa a aplicar el radio por defecto (`Rounded::default()` queda sin redondeo). - `Form::action` usa `Route` en lugar de `AttrValue`, y `check::Item` expone `value` en vez de `name` para generar casillas con `name` compartido valores distintos.
113 lines
4.1 KiB
Rust
113 lines
4.1 KiB
Rust
//! Definiciones para crear áreas de texto en formularios.
|
|
|
|
use pagetop::prelude::*;
|
|
|
|
pub use pagetop::base::component::form::Textarea;
|
|
|
|
const EXTRA_FLOATING_LABEL: &str = "bootsier.form.textarea.floating_label";
|
|
|
|
/// Extensión de Bootsier para [`form::Textarea`].
|
|
///
|
|
/// Proporciona soporte para **etiquetas flotantes** (*floating label*). La etiqueta flotante se
|
|
/// superpone al control mientras no hay ninguna opción seleccionada y permanece flotante cuando hay
|
|
/// una selección activa.
|
|
///
|
|
/// ```rust,no_run
|
|
/// # use pagetop::prelude::*;
|
|
/// use pagetop_bootsier::theme::*;
|
|
///
|
|
/// let comentario = bs::form::Textarea::new()
|
|
/// .with_name("comment")
|
|
/// .with_label(L10n::n("Comment"))
|
|
/// .with_placeholder(L10n::n("Write here..."))
|
|
/// .with_floating_label(true);
|
|
/// ```
|
|
pub trait TextareaBootsier {
|
|
/// Establece si la etiqueta se muestra flotante sobre el campo.
|
|
///
|
|
/// Cuando está activo, la etiqueta se superpone al área de texto y asciende al enfocarlo o
|
|
/// cuando tiene contenido. Requiere que el campo tenga un atributo `placeholder` definido;
|
|
/// si no se especifica, se fuerza `placeholder=""` antes del renderizado.
|
|
///
|
|
/// Si se usa la etiqueta flotante, se anula el valor establecido con
|
|
/// [`with_rows()`](form::Textarea::with_rows) antes del renderizado.
|
|
#[builder_fn]
|
|
fn with_floating_label(self, floating: bool) -> Self;
|
|
}
|
|
|
|
impl TextareaBootsier for Textarea {
|
|
#[builder_fn]
|
|
fn with_floating_label(mut self, floating: bool) -> Self {
|
|
self.alter_prop(PropsOp::set_extra(EXTRA_FLOATING_LABEL, floating));
|
|
self
|
|
}
|
|
}
|
|
|
|
// **< Textarea SETUP >*****************************************************************************
|
|
|
|
pub(crate) fn setup(field: &mut Textarea) {
|
|
if field.props().extra_or(EXTRA_FLOATING_LABEL, false) {
|
|
field.alter_prop(PropsOp::add_classes("form-floating"));
|
|
field.alter_rows(None::<u16>);
|
|
} else {
|
|
field.alter_prop(PropsOp::remove_classes("form-floating"));
|
|
}
|
|
}
|
|
|
|
// **< Textarea RENDER >****************************************************************************
|
|
|
|
pub(crate) fn render(field: &Textarea, cx: &mut Context) -> Result<Markup, ComponentError> {
|
|
let container_id = field.id();
|
|
let textarea_id = container_id
|
|
.as_deref()
|
|
.map(|id| util::join!(id, "-textarea"));
|
|
let floating = field.props().extra_or(EXTRA_FLOATING_LABEL, false);
|
|
// La etiqueta flotante requiere `placeholder` para animar la etiqueta; si no está definido se
|
|
// fuerza `placeholder=""`.
|
|
let placeholder = if floating {
|
|
Some(field.placeholder().lookup(cx).unwrap_or_default())
|
|
} else {
|
|
field.placeholder().lookup(cx)
|
|
};
|
|
let label = match field.label().lookup(cx) {
|
|
Some(text) => html! {
|
|
label for=[textarea_id.as_deref()] class="form-label" {
|
|
(text)
|
|
@if *field.required() {
|
|
span
|
|
class="form-required"
|
|
title=(L10n::l("field_required").using(cx))
|
|
{
|
|
"*"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
None => html! {},
|
|
};
|
|
Ok(html! {
|
|
div (field.props()) {
|
|
@if !floating { (label) }
|
|
textarea
|
|
id=[textarea_id.as_deref()]
|
|
class="form-control"
|
|
name=[field.name().get()]
|
|
rows=[field.rows().get()]
|
|
minlength=[field.minlength().get()]
|
|
maxlength=[field.maxlength().get()]
|
|
placeholder=[placeholder]
|
|
autocomplete=[field.autocomplete().get()]
|
|
autofocus[*field.autofocus()]
|
|
readonly[*field.readonly()]
|
|
required[*field.required()]
|
|
disabled[*field.disabled()]
|
|
{
|
|
@if let Some(value) = field.value().get() { (value) }
|
|
}
|
|
@if floating { (label) }
|
|
@if let Some(description) = field.help_text().lookup(cx) {
|
|
div class="form-text" { (description) }
|
|
}
|
|
}
|
|
})
|
|
}
|