Menu
Public repository / master
AkurAI-Framework / crates/ui/src/kit/button.rs
Latest change 3aae9f4e85241bcb775c31f5c03094becd3da56a - Pure-Rust UI layer: akurai-ui, akurai-crypto, Tailwind-grade akurai-css by Ólafur Búi Ólafsson
use crate::dom::El;
use crate::html;
/// Visual weight of a [`button`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Variant {
Primary,
Secondary,
Outline,
Ghost,
Destructive,
Link,
}
/// Height and padding of a [`button`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Size {
Sm,
Md,
Lg,
Icon,
}
const BASE: &str = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium select-none transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg disabled:pointer-events-none disabled:opacity-50";
fn variant_class(v: Variant) -> &'static str {
match v {
Variant::Primary => "bg-accent text-bg shadow-sm hover:bg-accent/90",
Variant::Secondary => "bg-panel text-fg hover:bg-panel-2",
Variant::Outline => "border border-border bg-transparent text-fg hover:bg-panel",
Variant::Ghost => "text-fg hover:bg-panel",
Variant::Destructive => "bg-danger text-bg shadow-sm hover:bg-danger/90",
Variant::Link => "text-accent underline-offset-4 hover:underline",
}
}
fn size_class(s: Size) -> &'static str {
match s {
Size::Sm => "h-8 px-3 text-xs",
Size::Md => "h-9 px-4",
Size::Lg => "h-10 px-6",
Size::Icon => "size-9",
}
}
/// A `<button type="button">`; set `.typ("submit")` inside forms.
pub fn button(variant: Variant, size: Size) -> El {
html::button()
.typ("button")
.class(super::cn(&[BASE, variant_class(variant), size_class(size)]))
}
/// An icon-only button. `label` becomes its accessible name.
pub fn icon_button(variant: Variant, label: &str, icon: El) -> El {
button(variant, Size::Icon)
.attr("aria-label", label.to_string())
.attr("title", label.to_string())
.child(icon)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing;
#[test]
fn button_has_type_and_classes() {
testing::render(|| button(Variant::Primary, Size::Md).child("Save"));
let b = testing::get("button:text(Save)");
assert_eq!(testing::attr(b, "type").as_deref(), Some("button"));
assert!(testing::attr(b, "class").unwrap().contains("bg-accent"));
}
#[test]
fn icon_button_is_labelled() {
testing::render(|| icon_button(Variant::Ghost, "Close", crate::kit::icons::x()));
assert!(testing::find("button[aria-label=Close]").is_some());
}
}