use dioxus::prelude::*;
use super::size::*;
use dioxus_router::navigation::NavigationTarget;
use dioxus_router::components::Link;
#[derive(Clone, Copy, PartialEq)]
pub enum ButtonVariant {
Basic,
Primary,
Secondary,
Success,
Danger,
Warning,
Info,
Light,
Dark,
Link,
}
impl Into<&'static str> for ButtonVariant {
fn into(self) -> &'static str {
match self {
ButtonVariant::Primary => "primary",
ButtonVariant::Secondary => "secondary",
ButtonVariant::Success => "success",
ButtonVariant::Danger => "danger",
ButtonVariant::Warning => "warning",
ButtonVariant::Info => "info",
ButtonVariant::Light => "light",
ButtonVariant::Dark => "dark",
ButtonVariant::Link => "link",
_ => "",
}
}
}
#[derive(Clone, Copy, Default, PartialEq)]
pub enum ButtonType {
#[default]
Button,
Reset,
Submit,
}
impl Into<&'static str> for ButtonType {
fn into(self) -> &'static str {
match self {
ButtonType::Button => "button",
ButtonType::Reset => "reset",
ButtonType::Submit => "submit",
}
}
}
#[derive(Clone, Props, PartialEq)]
pub struct ButtonProps {
#[props(optional)]
id: String,
#[props(optional, default = ButtonVariant::Basic)]
variant: ButtonVariant,
#[props(optional, default = Size::Normal)]
size: Size,
#[props(optional, default = false)]
disabled: bool,
#[props(optional, default = false)]
outline: bool,
#[props(optional, default = false)]
nowrap: bool,
#[props(optional, default = false)]
toggle: bool,
#[props(optional, default = false)]
active: bool,
#[props(optional, default = "".to_string())]
style: String,
#[props(optional, default = ButtonType::Button)]
button_type: ButtonType,
#[props(optional, default = "".to_string())]
class: String,
#[props(optional, default = None)]
text: Option<String>,
#[props(optional, default = false)]
loading: bool,
#[props(optional, default = false)]
close: bool,
#[props(optional, default = false)]
floating: bool,
#[props(optional, default = None)]
link_to: Option<NavigationTarget>,
children: Element,
#[props(optional)]
onclick: EventHandler<MouseEvent>,
#[props(optional)]
onmounted: EventHandler<MountedEvent>,
}
#[component]
pub fn Button(props: ButtonProps) -> Element {
let mut class_list = vec!["btn".to_string()];
if props.disabled { class_list.push("btn-disabled".into()) }
if props.toggle && props.active { class_list.push("active".into()) }
let variant: &str = props.variant.into();
if variant.len() > 0 {
if props.outline {
class_list.push(format!("btn-outline-{}", variant))
} else {
class_list.push(format!("btn-{}", variant))
}
}
let size: &str = props.size.into();
if props.size != Size::Normal {
class_list.push(format!("btn-{}", size));
}
if !props.class.is_empty() {
class_list.push(props.class.clone());
}
if props.loading {
class_list.push("btn-loading".to_string());
}
if props.floating {
class_list.push("btn-floating".to_string());
}
let class_list = class_list.join(" ");
if props.toggle {
return rsx! {
button { id: props.id, r#type: "button", style: props.style, onclick: props.onclick, class: class_list, "data-bs-toggle": "button", "aria-pressed": true, onmounted: props.onmounted, {props.children} }
}
}
if props.close {
return rsx! {
button {
id: props.id,
r#type: "button",
class: "btn-close",
style: props.style,
onclick: props.onclick,
disabled: props.disabled,
"aria-label": "Close",
onmounted: props.onmounted,
}
};
}
let button_type_str: &str = props.button_type.into();
match props.button_type {
ButtonType::Submit | ButtonType::Reset => rsx!{
input {
id: props.id,
r#type: button_type_str,
value: if props.button_type == ButtonType::Submit { "Submit" } else { "Reset" },
style: props.style,
onclick: props.onclick,
class: class_list,
disabled: props.disabled,
onmounted: props.onmounted
}
},
_ => match props.link_to {
Some(t) => rsx!{
Link {
to: t,
id: props.id,
onclick: props.onclick,
style: props.style,
class: class_list,
"aria-disabled": props.disabled,
{if let Some(text) = props.text { rsx! { "{text}" } } else { props.children }}
}
},
_ => rsx! {
button {
id: props.id,
r#type: button_type_str,
style: props.style,
onclick: props.onclick,
class: class_list,
disabled: props.disabled,
onmounted: props.onmounted,
{if let Some(text) = props.text { rsx! { "{text}" } } else { props.children }}
}
}
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum ButtonGroupOrientation {
Horizontal,
Vertical,
}
#[derive(Clone, Props, PartialEq)]
pub struct ButtonGroupProps {
#[props(optional, default = "Button group".to_string())]
label: String,
#[props(optional, default = Size::Normal)]
size: Size,
#[props(optional, default = ButtonGroupOrientation::Horizontal)]
orientation: ButtonGroupOrientation,
#[props(optional, default = false)]
toolbar: bool,
children: Element,
}
#[component]
pub fn ButtonGroup(props: ButtonGroupProps) -> Element {
let mut class_list = vec!["btn-group".to_string()];
if props.orientation == ButtonGroupOrientation::Vertical {
class_list = vec!["btn-group-vertical".to_string()];
}
let size: &str = props.size.into();
if props.size != Size::Normal {
class_list.push(format!("btn-group-{}", size));
}
if props.toolbar {
class_list = vec!["btn-toolbar".to_string()];
}
let class_list = class_list.join(" ");
let role = if props.toolbar { "toolbar" } else { "group" };
rsx! {
div {
class: class_list,
role: role,
"aria-label": props.label,
{props.children}
}
}
}
#[derive(Clone, Props, PartialEq)]
pub struct DropdownButtonProps {
#[props(optional)]
id: String,
#[props(optional, default = "".to_string())]
class: String,
#[props(optional, default = ButtonVariant::Primary)]
variant: ButtonVariant,
#[props(optional, default = Size::Normal)]
size: Size,
#[props(optional, default = false)]
disabled: bool,
#[props(optional, default = false)]
outline: bool,
#[props(optional, default = false)]
split: bool,
#[props(optional, default = "Dropdown".to_string())]
text: String,
children: Element,
}
#[component]
pub fn DropdownButton(props: DropdownButtonProps) -> Element {
let mut class_list = vec!["btn".to_string(), "dropdown-toggle".to_string()];
let variant: &str = props.variant.into();
if !variant.is_empty() {
if props.outline {
class_list.push(format!("btn-outline-{}", variant));
} else {
class_list.push(format!("btn-{}", variant));
}
}
let size: &str = props.size.into();
if props.size != Size::Normal {
class_list.push(format!("btn-{}", size));
}
if !props.class.is_empty() {
class_list.push(props.class.clone());
}
let class_list = class_list.join(" ");
if props.split {
rsx! {
div {
class: "btn-group",
button {
r#type: "button",
class: class_list.replace("dropdown-toggle", "").trim(),
disabled: props.disabled,
"{props.text}"
}
button {
r#type: "button",
class: format!("{} dropdown-toggle dropdown-toggle-split", class_list.replace("dropdown-toggle", "").trim()),
"data-bs-toggle": "dropdown",
"aria-expanded": "false",
disabled: props.disabled,
span {
class: "visually-hidden",
"Toggle Dropdown"
}
}
ul {
class: "dropdown-menu",
{props.children}
}
}
}
} else {
rsx! {
div {
class: "dropdown",
button {
id: props.id,
r#type: "button",
class: class_list,
"data-bs-toggle": "dropdown",
"aria-expanded": "false",
disabled: props.disabled,
"{props.text}"
}
ul {
class: "dropdown-menu",
{props.children}
}
}
}
}
}