Skip to main content

dioxus_element_plug/components/
button.rs

1use dioxus::prelude::*;
2
3// Button CSS class constants
4pub const BUTTON: &str = "el-button";
5pub const BUTTON_PRIMARY: &str = "el-button--primary";
6pub const BUTTON_SUCCESS: &str = "el-button--success";
7pub const BUTTON_WARNING: &str = "el-button--warning";
8pub const BUTTON_DANGER: &str = "el-button--danger";
9pub const BUTTON_INFO: &str = "el-button--info";
10pub const BUTTON_LARGE: &str = "el-button--large";
11pub const BUTTON_SMALL: &str = "el-button--small";
12pub const BUTTON_DISABLED: &str = "is-disabled";
13
14/// Button variants matching theme-chalk styles
15#[derive(Clone, PartialEq)]
16pub enum ButtonVariant {
17    Default,
18    Primary,
19    Success,
20    Warning,
21    Danger,
22    Info,
23}
24
25impl ButtonVariant {
26    pub fn as_class(&self) -> &'static str {
27        match self {
28            ButtonVariant::Default => BUTTON,
29            ButtonVariant::Primary => BUTTON_PRIMARY,
30            ButtonVariant::Success => BUTTON_SUCCESS,
31            ButtonVariant::Warning => BUTTON_WARNING,
32            ButtonVariant::Danger => BUTTON_DANGER,
33            ButtonVariant::Info => BUTTON_INFO,
34        }
35    }
36}
37
38/// Button sizes matching theme-chalk
39#[derive(Clone, PartialEq)]
40pub enum ButtonSize {
41    Large,
42    Medium,
43    Small,
44    Mini,
45}
46
47impl ButtonSize {
48    pub fn as_class(&self) -> &'static str {
49        match self {
50            ButtonSize::Large => "el-button--large",
51            ButtonSize::Medium => "el-button--medium",
52            ButtonSize::Small => "el-button--small",
53            ButtonSize::Mini => "el-button--mini",
54        }
55    }
56}
57
58/// Button props for the theme-chalk styled button component
59#[derive(Props, Clone, PartialEq)]
60pub struct ButtonProps {
61    /// Button content
62    #[props(!optional)]
63    pub children: Element,
64
65    /// Button variant/style
66    #[props(default = ButtonVariant::Default)]
67    pub variant: ButtonVariant,
68
69    /// Button size
70    #[props(default)]
71    pub size: Option<ButtonSize>,
72
73    /// Whether the button is disabled
74    #[props(default = false)]
75    pub disabled: bool,
76
77    /// Whether the button is round
78    #[props(default = false)]
79    pub round: bool,
80
81    /// Whether the button is circle
82    #[props(default = false)]
83    pub circle: bool,
84
85    /// Whether the button is loading
86    #[props(default = false)]
87    pub loading: bool,
88
89    /// Button icon (CSS class name)
90    #[props(default)]
91    pub icon: Option<String>,
92
93    /// Native button type
94    #[props(default = "button".to_string())]
95    pub button_type: String,
96
97    /// Click event handler
98    #[props(default)]
99    pub on_click: Option<EventHandler<MouseEvent>>,
100
101    /// Additional CSS classes
102    #[props(default)]
103    pub class: Option<String>,
104
105    /// Inline styles
106    #[props(default)]
107    pub style: Option<String>,
108}
109
110/// A button component styled with theme-chalk CSS
111///
112/// This component wraps the native HTML button element and applies
113/// theme-chalk CSS classes for consistent styling.
114///
115/// ## Example
116///
117/// ```rust,ignore
118/// use dioxus_theme_chalk::components::button::{Button, ButtonVariant, ButtonSize};
119///
120/// rsx! {
121///     Button {
122///         variant: ButtonVariant::Primary,
123///         size: Some(ButtonSize::Medium),
124///         on_click: move |_| println!("Button clicked!"),
125///         "Click me!"
126///     }
127/// }
128/// ```
129#[component]
130pub fn Button(props: ButtonProps) -> Element {
131    let base_class = BUTTON;
132    let variant_class = props.variant.as_class();
133
134    let mut classes = vec![base_class, variant_class];
135
136    if let Some(size) = &props.size {
137        classes.push(size.as_class());
138    }
139
140    if props.disabled {
141        classes.push("is-disabled");
142    }
143
144    if props.round {
145        classes.push("is-round");
146    }
147
148    if props.circle {
149        classes.push("is-circle");
150    }
151
152    if props.loading {
153        classes.push("is-loading");
154    }
155
156    if let Some(ref custom_class) = props.class {
157        classes.push(custom_class);
158    }
159
160    let class_string = classes.join(" ");
161
162    let mut icon_element = None;
163    if let Some(ref icon) = props.icon {
164        icon_element = Some(rsx! {
165            i {
166                class: "{icon}"
167            }
168        });
169    }
170
171    let mut loading_element = None;
172    if props.loading {
173        loading_element = Some(rsx! {
174            i {
175                class: "el-icon-loading"
176            }
177        });
178    }
179
180    rsx! {
181        button {
182            class: "{class_string}",
183            r#type: props.button_type,
184            disabled: props.disabled,
185            style: props.style.unwrap_or_default(),
186            onclick: move |event| {
187                if let Some(handler) = props.on_click {
188                    handler.call(event);
189                }
190            },
191
192            {loading_element},
193            {icon_element},
194            {props.children}
195        }
196    }
197}
198
199/// Outlined button variant
200#[component]
201pub fn OutlineButton(props: ButtonProps) -> Element {
202    let mut props = props;
203    let mut class = props.class.unwrap_or_default();
204    class.push_str(" is-plain");
205    props.class = Some(class);
206
207    Button(props)
208}
209
210/// Text button variant
211#[component]
212pub fn TextButton(props: ButtonProps) -> Element {
213    let mut props = props;
214    props.variant = ButtonVariant::Default;
215    let mut class = props.class.unwrap_or_default();
216    class.push_str(" el-button--text");
217    props.class = Some(class);
218
219    Button(props)
220}
221
222/// Link button variant
223#[component]
224pub fn LinkButton(props: ButtonProps) -> Element {
225    rsx! {
226        a {
227            class: "el-button el-button--text",
228            href: "javascript:void(0)",
229            onclick: move |event| {
230                if let Some(handler) = props.on_click {
231                    handler.call(event);
232                }
233            },
234            {props.children}
235        }
236    }
237}