Add loading state to Button

- Add loading: bool prop to Button - Disable button when loading or
disabled - Show spinner and wrap content in a ui-button-content span
during loading - Add data-loading and aria-busy attributes for
accessibility - Prevent click handlers from firing while loading
This commit is contained in:
tommy
2025-11-05 18:40:40 +08:00
parent 09e96859d6
commit f551a41a5b

View File

@@ -65,6 +65,7 @@ pub fn Button(
#[props(default)] size: ButtonSize, #[props(default)] size: ButtonSize,
#[props(into, default)] class: Option<String>, #[props(into, default)] class: Option<String>,
#[props(default)] disabled: bool, #[props(default)] disabled: bool,
#[props(default)] loading: bool,
#[props(default = "button".to_string())] #[props(default = "button".to_string())]
#[props(into)] #[props(into)]
r#type: String, r#type: String,
@@ -78,20 +79,36 @@ pub fn Button(
} }
let click_handler = on_click.clone(); let click_handler = on_click.clone();
let is_disabled = disabled || loading;
rsx! { rsx! {
button { button {
class: classes, class: classes,
disabled, disabled: is_disabled,
r#type: r#type, r#type: r#type,
"data-variant": variant.as_str(), "data-variant": variant.as_str(),
"data-size": size.as_str(), "data-size": size.as_str(),
"data-loading": if loading { "true" } else { "false" },
"aria-busy": if loading { "true" } else { "false" },
onclick: move |event| { onclick: move |event| {
if loading {
event.stop_propagation();
return;
}
if let Some(handler) = click_handler.clone() { if let Some(handler) = click_handler.clone() {
handler.call(event); handler.call(event);
} }
}, },
{children} if loading {
span {
class: "ui-button-spinner",
"aria-hidden": "true",
}
}
span {
class: "ui-button-content",
{children}
}
} }
} }
} }