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}