dioxus-element-plug 0.1.4

Element UI components for Dioxus applications with pure Rust styling system
use dioxus::prelude::*;

// Button CSS class constants
pub const BUTTON: &str = "el-button";
pub const BUTTON_PRIMARY: &str = "el-button--primary";
pub const BUTTON_SUCCESS: &str = "el-button--success";
pub const BUTTON_WARNING: &str = "el-button--warning";
pub const BUTTON_DANGER: &str = "el-button--danger";
pub const BUTTON_INFO: &str = "el-button--info";
pub const BUTTON_LARGE: &str = "el-button--large";
pub const BUTTON_SMALL: &str = "el-button--small";
pub const BUTTON_DISABLED: &str = "is-disabled";

/// Button variants matching theme-chalk styles
#[derive(Clone, PartialEq)]
pub enum ButtonVariant {
    Default,
    Primary,
    Success,
    Warning,
    Danger,
    Info,
}

impl ButtonVariant {
    pub fn as_class(&self) -> &'static str {
        match self {
            ButtonVariant::Default => BUTTON,
            ButtonVariant::Primary => BUTTON_PRIMARY,
            ButtonVariant::Success => BUTTON_SUCCESS,
            ButtonVariant::Warning => BUTTON_WARNING,
            ButtonVariant::Danger => BUTTON_DANGER,
            ButtonVariant::Info => BUTTON_INFO,
        }
    }
}

/// Button sizes matching theme-chalk
#[derive(Clone, PartialEq)]
pub enum ButtonSize {
    Large,
    Medium,
    Small,
    Mini,
}

impl ButtonSize {
    pub fn as_class(&self) -> &'static str {
        match self {
            ButtonSize::Large => "el-button--large",
            ButtonSize::Medium => "el-button--medium",
            ButtonSize::Small => "el-button--small",
            ButtonSize::Mini => "el-button--mini",
        }
    }
}

/// Button props for the theme-chalk styled button component
#[derive(Props, Clone, PartialEq)]
pub struct ButtonProps {
    /// Button content
    #[props(!optional)]
    pub children: Element,

    /// Button variant/style
    #[props(default = ButtonVariant::Default)]
    pub variant: ButtonVariant,

    /// Button size
    #[props(default)]
    pub size: Option<ButtonSize>,

    /// Whether the button is disabled
    #[props(default = false)]
    pub disabled: bool,

    /// Whether the button is round
    #[props(default = false)]
    pub round: bool,

    /// Whether the button is circle
    #[props(default = false)]
    pub circle: bool,

    /// Whether the button is loading
    #[props(default = false)]
    pub loading: bool,

    /// Button icon (CSS class name)
    #[props(default)]
    pub icon: Option<String>,

    /// Native button type
    #[props(default = "button".to_string())]
    pub button_type: String,

    /// Click event handler
    #[props(default)]
    pub on_click: Option<EventHandler<MouseEvent>>,

    /// Additional CSS classes
    #[props(default)]
    pub class: Option<String>,

    /// Inline styles
    #[props(default)]
    pub style: Option<String>,
}

/// A button component styled with theme-chalk CSS
///
/// This component wraps the native HTML button element and applies
/// theme-chalk CSS classes for consistent styling.
///
/// ## Example
///
/// ```rust,ignore
/// use dioxus_theme_chalk::components::button::{Button, ButtonVariant, ButtonSize};
///
/// rsx! {
///     Button {
///         variant: ButtonVariant::Primary,
///         size: Some(ButtonSize::Medium),
///         on_click: move |_| println!("Button clicked!"),
///         "Click me!"
///     }
/// }
/// ```
#[component]
pub fn Button(props: ButtonProps) -> Element {
    let base_class = BUTTON;
    let variant_class = props.variant.as_class();

    let mut classes = vec![base_class, variant_class];

    if let Some(size) = &props.size {
        classes.push(size.as_class());
    }

    if props.disabled {
        classes.push("is-disabled");
    }

    if props.round {
        classes.push("is-round");
    }

    if props.circle {
        classes.push("is-circle");
    }

    if props.loading {
        classes.push("is-loading");
    }

    if let Some(ref custom_class) = props.class {
        classes.push(custom_class);
    }

    let class_string = classes.join(" ");

    let mut icon_element = None;
    if let Some(ref icon) = props.icon {
        icon_element = Some(rsx! {
            i {
                class: "{icon}"
            }
        });
    }

    let mut loading_element = None;
    if props.loading {
        loading_element = Some(rsx! {
            i {
                class: "el-icon-loading"
            }
        });
    }

    rsx! {
        button {
            class: "{class_string}",
            r#type: props.button_type,
            disabled: props.disabled,
            style: props.style.unwrap_or_default(),
            onclick: move |event| {
                if let Some(handler) = props.on_click {
                    handler.call(event);
                }
            },

            {loading_element},
            {icon_element},
            {props.children}
        }
    }
}

/// Outlined button variant
#[component]
pub fn OutlineButton(props: ButtonProps) -> Element {
    let mut props = props;
    let mut class = props.class.unwrap_or_default();
    class.push_str(" is-plain");
    props.class = Some(class);

    Button(props)
}

/// Text button variant
#[component]
pub fn TextButton(props: ButtonProps) -> Element {
    let mut props = props;
    props.variant = ButtonVariant::Default;
    let mut class = props.class.unwrap_or_default();
    class.push_str(" el-button--text");
    props.class = Some(class);

    Button(props)
}

/// Link button variant
#[component]
pub fn LinkButton(props: ButtonProps) -> Element {
    rsx! {
        a {
            class: "el-button el-button--text",
            href: "javascript:void(0)",
            onclick: move |event| {
                if let Some(handler) = props.on_click {
                    handler.call(event);
                }
            },
            {props.children}
        }
    }
}