🎨 (bootsier): Normaliza id's y revisa Checkbox

Renombra los sufijos de `id` de los controles internos para que cada
tipo tenga su propio identificador: `-checkbox`, `-check-{n}` y
`-radio-{n}`. Elimina además el atributo booleano `switch` inválido en
HTML (basta con `role="switch"` y `class="form-switch"`).
This commit is contained in:
Manuel Cillero 2026-04-24 20:27:31 +02:00
parent 5b7e13e52b
commit ffba175985
4 changed files with 40 additions and 12 deletions

View file

@ -144,9 +144,9 @@ impl Component for Group {
.name()
.get()
.unwrap_or_else(|| cx.required_id::<Self>(self.id(), 3));
let group_id = self.id().unwrap_or_else(|| util::join!("edit-", &name));
let container_id = self.id().unwrap_or_else(|| util::join!("edit-", &name));
Ok(html! {
div id=(&group_id) class=[self.classes().get()] {
div id=(&container_id) class=[self.classes().get()] {
@if let Some(label) = self.label().lookup(cx) {
label class="form-label" { (label) }
}
@ -157,7 +157,7 @@ impl Component for Group {
};
@for (item, i) in self.items().iter().zip(1..) {
@let i = i.to_string();
@let item_id = util::join!(&group_id, "-", &i);
@let item_id = util::join!(&container_id, "-check-", &i);
@let item_name = if let Some(item_name) = item.name().get() {
util::join!(&name, "_", &item_name)
} else {

View file

@ -55,6 +55,8 @@ pub struct Checkbox {
label: Attr<L10n>,
/// Devuelve si el control debe estar marcado/activo por defecto.
checked: bool,
/// Devuelve si el control recibe el foco automáticamente al cargar la página.
autofocus: bool,
/// Devuelve si el campo es obligatorio.
required: bool,
/// Devuelve si el control está deshabilitado.
@ -93,23 +95,24 @@ impl Component for Checkbox {
.name()
.get()
.unwrap_or_else(|| cx.required_id::<Self>(self.id(), 1));
let id = self.id().unwrap_or_else(|| util::join!("edit-", &name));
let container_id = self.id().unwrap_or_else(|| util::join!("edit-", &name));
let checkbox_id = util::join!(&container_id, "-checkbox");
let is_switch = *self.checkbox_kind() == form::CheckboxKind::Switch;
Ok(html! {
div class=[self.classes().get()] {
div id=(&container_id) class=[self.classes().get()] {
input
type="checkbox"
role=[is_switch.then_some("switch")]
id=(&id)
id=(&checkbox_id)
class="form-check-input"
name=(&name)
value="true"
checked[*self.checked()]
autofocus[*self.autofocus()]
required[*self.required()]
disabled[*self.disabled()]
switch[is_switch];
disabled[*self.disabled()];
@if let Some(label) = self.label().lookup(cx) {
label class="form-check-label" for=(&id) {
label class="form-check-label" for=(&checkbox_id) {
(label)
@if *self.required() {
span
@ -187,6 +190,13 @@ impl Checkbox {
self
}
/// Establece si el control recibe el foco automáticamente al cargar la página.
#[builder_fn]
pub fn with_autofocus(mut self, autofocus: bool) -> Self {
self.autofocus = autofocus;
self
}
/// Establece si el campo es obligatorio.
#[builder_fn]
pub fn with_required(mut self, required: bool) -> Self {

View file

@ -3,6 +3,24 @@ use pagetop::prelude::*;
use std::borrow::Cow;
use std::fmt;
// **< CheckboxKind >*******************************************************************************
/// Variante visual para [`form::Checkbox`](crate::theme::form::Checkbox) en un formulario.
///
/// Determina si el control se renderiza como una casilla de verificación estándar o como un
/// interruptor (*toggle switch*).
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
pub enum CheckboxKind {
/// Casilla de verificación estándar. Es el tipo por defecto.
#[default]
Check,
/// Interruptor de encendido/apagado.
Switch,
// TODO: Añadir variante `NativeSwitch` cuando el atributo `switch` de la propuesta WHATWG
// (https://github.com/whatwg/html/issues/9546) sea estándar y tenga soporte amplio. Safari ya
// lo soporta. También se añadiría el constructor `Checkbox::native_switch()`.
}
// **< Autocomplete >*******************************************************************************
/// Valor del atributo HTML `autocomplete`.

View file

@ -134,9 +134,9 @@ impl Component for Group {
.name()
.get()
.unwrap_or_else(|| cx.required_id::<Self>(self.id(), 3));
let group_id = self.id().unwrap_or_else(|| util::join!("edit-", &name));
let container_id = self.id().unwrap_or_else(|| util::join!("edit-", &name));
Ok(html! {
div id=(&group_id) class=[self.classes().get()] {
div id=(&container_id) class=[self.classes().get()] {
@if let Some(label) = self.label().lookup(cx) {
label class="form-label" {
(label)
@ -163,7 +163,7 @@ impl Component for Group {
c
};
@let i = i.to_string();
@let item_id = util::join!(&group_id, "-", &i);
@let item_id = util::join!(&container_id, "-radio-", &i);
div class=(item_classes) {
input
type="radio"