Skip to main content

dioxus_bootstrap_css/
button.rs

1use dioxus::prelude::*;
2
3use crate::types::{Color, Size};
4
5/// Bootstrap Button component.
6///
7/// # Bootstrap HTML → Dioxus
8///
9/// | HTML | Dioxus |
10/// |---|---|
11/// | `<button class="btn btn-primary">` | `Button { color: Color::Primary, "Text" }` |
12/// | `<button class="btn btn-outline-danger btn-sm">` | `Button { color: Color::Danger, outline: true, size: Size::Sm, "Text" }` |
13/// | `<button class="btn btn-success btn-lg" disabled>` | `Button { color: Color::Success, size: Size::Lg, disabled: true, "Text" }` |
14/// | `<button class="btn btn-primary active">` | `Button { color: Color::Primary, active: true, "Text" }` |
15///
16/// ```rust,no_run
17/// rsx! {
18///     Button { color: Color::Primary, "Click me" }
19///     Button { color: Color::Danger, outline: true, size: Size::Sm, "Delete" }
20///     Button { color: Color::Success, disabled: true, "Saved" }
21///     Button { color: Color::Warning, onclick: move |_| { /* handler */ }, "Action" }
22/// }
23/// ```
24#[derive(Clone, PartialEq, Props)]
25pub struct ButtonProps {
26    /// Button color variant.
27    #[props(default)]
28    pub color: Color,
29    /// Use outline style instead of filled.
30    #[props(default)]
31    pub outline: bool,
32    /// Button size.
33    #[props(default)]
34    pub size: Size,
35    /// Whether the button is disabled.
36    #[props(default)]
37    pub disabled: bool,
38    /// HTML button type attribute.
39    #[props(default = "button".to_string())]
40    pub r#type: String,
41    /// Click event handler.
42    #[props(default)]
43    pub onclick: Option<EventHandler<MouseEvent>>,
44    /// Active (pressed) state.
45    #[props(default)]
46    pub active: bool,
47    /// Tooltip text (HTML title attribute).
48    #[props(default)]
49    pub title: String,
50    /// Additional CSS classes.
51    #[props(default)]
52    pub class: String,
53    /// Child elements.
54    pub children: Element,
55}
56
57#[component]
58pub fn Button(props: ButtonProps) -> Element {
59    let style = if props.outline { "btn-outline" } else { "btn" };
60    let color = props.color;
61    let color_class = format!("{style}-{color}");
62
63    let size_class = match props.size {
64        Size::Md => String::new(),
65        s => format!(" btn-{s}"),
66    };
67
68    let active_class = if props.active { " active" } else { "" };
69
70    let full_class = if props.class.is_empty() {
71        format!("btn {color_class}{size_class}{active_class}")
72    } else {
73        format!(
74            "btn {color_class}{size_class}{active_class} {}",
75            props.class
76        )
77    };
78
79    rsx! {
80        button {
81            class: "{full_class}",
82            r#type: "{props.r#type}",
83            disabled: props.disabled,
84            title: if !props.title.is_empty() { "{props.title}" } else { "" },
85            onclick: move |evt| {
86                if let Some(handler) = &props.onclick {
87                    handler.call(evt);
88                }
89            },
90            {props.children}
91        }
92    }
93}
94
95/// Bootstrap ButtonGroup component.
96///
97/// ```rust
98/// rsx! {
99///     ButtonGroup {
100///         Button { color: Color::Primary, "Left" }
101///         Button { color: Color::Primary, "Middle" }
102///         Button { color: Color::Primary, "Right" }
103///     }
104/// }
105/// ```
106#[derive(Clone, PartialEq, Props)]
107pub struct ButtonGroupProps {
108    /// Button group size.
109    #[props(default)]
110    pub size: Size,
111    /// Additional CSS classes.
112    #[props(default)]
113    pub class: String,
114    /// Child elements (buttons).
115    pub children: Element,
116}
117
118#[component]
119pub fn ButtonGroup(props: ButtonGroupProps) -> Element {
120    let size_class = match props.size {
121        Size::Md => String::new(),
122        s => format!(" btn-group-{s}"),
123    };
124
125    let full_class = if props.class.is_empty() {
126        format!("btn-group{size_class}")
127    } else {
128        format!("btn-group{size_class} {}", props.class)
129    };
130
131    rsx! {
132        div {
133            class: "{full_class}",
134            role: "group",
135            {props.children}
136        }
137    }
138}
139
140/// Bootstrap ButtonToolbar — groups multiple ButtonGroups.
141///
142/// ```rust
143/// rsx! {
144///     ButtonToolbar {
145///         ButtonGroup {
146///             Button { color: Color::Primary, "1" }
147///             Button { color: Color::Primary, "2" }
148///         }
149///         ButtonGroup {
150///             Button { color: Color::Secondary, "A" }
151///         }
152///     }
153/// }
154/// ```
155#[derive(Clone, PartialEq, Props)]
156pub struct ButtonToolbarProps {
157    /// Additional CSS classes.
158    #[props(default)]
159    pub class: String,
160    /// Child elements (ButtonGroups).
161    pub children: Element,
162}
163
164#[component]
165pub fn ButtonToolbar(props: ButtonToolbarProps) -> Element {
166    let full_class = if props.class.is_empty() {
167        "btn-toolbar".to_string()
168    } else {
169        format!("btn-toolbar {}", props.class)
170    };
171
172    rsx! {
173        div {
174            class: "{full_class}",
175            role: "toolbar",
176            {props.children}
177        }
178    }
179}