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    /// Additional CSS classes.
48    #[props(default)]
49    pub class: String,
50    /// Child elements.
51    pub children: Element,
52}
53
54#[component]
55pub fn Button(props: ButtonProps) -> Element {
56    let style = if props.outline { "btn-outline" } else { "btn" };
57    let color = props.color;
58    let color_class = format!("{style}-{color}");
59
60    let size_class = match props.size {
61        Size::Md => String::new(),
62        s => format!(" btn-{s}"),
63    };
64
65    let active_class = if props.active { " active" } else { "" };
66
67    let full_class = if props.class.is_empty() {
68        format!("btn {color_class}{size_class}{active_class}")
69    } else {
70        format!(
71            "btn {color_class}{size_class}{active_class} {}",
72            props.class
73        )
74    };
75
76    rsx! {
77        button {
78            class: "{full_class}",
79            r#type: "{props.r#type}",
80            disabled: props.disabled,
81            onclick: move |evt| {
82                if let Some(handler) = &props.onclick {
83                    handler.call(evt);
84                }
85            },
86            {props.children}
87        }
88    }
89}
90
91/// Bootstrap ButtonGroup component.
92///
93/// ```rust
94/// rsx! {
95///     ButtonGroup {
96///         Button { color: Color::Primary, "Left" }
97///         Button { color: Color::Primary, "Middle" }
98///         Button { color: Color::Primary, "Right" }
99///     }
100/// }
101/// ```
102#[derive(Clone, PartialEq, Props)]
103pub struct ButtonGroupProps {
104    /// Button group size.
105    #[props(default)]
106    pub size: Size,
107    /// Additional CSS classes.
108    #[props(default)]
109    pub class: String,
110    /// Child elements (buttons).
111    pub children: Element,
112}
113
114#[component]
115pub fn ButtonGroup(props: ButtonGroupProps) -> Element {
116    let size_class = match props.size {
117        Size::Md => String::new(),
118        s => format!(" btn-group-{s}"),
119    };
120
121    let full_class = if props.class.is_empty() {
122        format!("btn-group{size_class}")
123    } else {
124        format!("btn-group{size_class} {}", props.class)
125    };
126
127    rsx! {
128        div {
129            class: "{full_class}",
130            role: "group",
131            {props.children}
132        }
133    }
134}
135
136/// Bootstrap ButtonToolbar — groups multiple ButtonGroups.
137///
138/// ```rust
139/// rsx! {
140///     ButtonToolbar {
141///         ButtonGroup {
142///             Button { color: Color::Primary, "1" }
143///             Button { color: Color::Primary, "2" }
144///         }
145///         ButtonGroup {
146///             Button { color: Color::Secondary, "A" }
147///         }
148///     }
149/// }
150/// ```
151#[derive(Clone, PartialEq, Props)]
152pub struct ButtonToolbarProps {
153    /// Additional CSS classes.
154    #[props(default)]
155    pub class: String,
156    /// Child elements (ButtonGroups).
157    pub children: Element,
158}
159
160#[component]
161pub fn ButtonToolbar(props: ButtonToolbarProps) -> Element {
162    let full_class = if props.class.is_empty() {
163        "btn-toolbar".to_string()
164    } else {
165        format!("btn-toolbar {}", props.class)
166    };
167
168    rsx! {
169        div {
170            class: "{full_class}",
171            role: "toolbar",
172            {props.children}
173        }
174    }
175}