use crate::icon::{Icon, IconKind};
use dioxus::prelude::*;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum SpeedDialDirection {
#[default]
Up,
Down,
Left,
Right,
}
impl SpeedDialDirection {
#[must_use]
pub fn class_name(&self) -> &'static str {
match self {
Self::Up => "sd-speed-dial--up",
Self::Down => "sd-speed-dial--down",
Self::Left => "sd-speed-dial--left",
Self::Right => "sd-speed-dial--right",
}
}
}
#[component]
pub fn Fab(
icon: IconKind,
#[props(default)]
title: Option<String>,
#[props(default)]
onclick: Option<EventHandler<()>>,
#[props(default)]
class: String,
) -> Element {
rsx! {
button {
r#type: "button",
class: "sd-fab {class}",
title: title,
onclick: move |_| {
if let Some(h) = onclick {
h.call(());
}
},
Icon { kind: icon, size: 22 }
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub struct SpeedDialContext {
pub is_open: Signal<bool>,
pub close_on_action: bool,
}
#[component]
pub fn SpeedDialAction(
icon: IconKind,
#[props(default)]
label: Option<String>,
#[props(default)]
onclick: Option<EventHandler<()>>,
#[props(default)]
class: String,
) -> Element {
let ctx = try_use_context::<SpeedDialContext>();
let handle_click = move |e: MouseEvent| {
e.stop_propagation();
if let Some(h) = onclick {
h.call(());
}
if let Some(mut c) = ctx {
if c.close_on_action {
c.is_open.set(false);
}
}
};
rsx! {
div { class: "sd-speed-dial-action-item",
if let Some(l) = label {
span {
class: "sd-speed-dial-label",
onclick: handle_click,
"{l}"
}
}
button {
r#type: "button",
class: "sd-speed-dial-btn {class}",
onclick: handle_click,
Icon { kind: icon, size: 18 }
}
}
}
}
#[component]
pub fn SpeedDial(
#[props(default = IconKind::Plus)]
icon: IconKind,
#[props(default)]
active_icon: Option<IconKind>,
#[props(default)]
direction: SpeedDialDirection,
#[props(default)]
opened: Option<Signal<bool>>,
#[props(default = true)]
close_on_action: bool,
#[props(default = true)]
close_on_click_outside: bool,
#[props(default = false)]
fixed: bool,
#[props(default)]
class: String,
children: Element,
) -> Element {
let internal_open = use_signal(|| false);
let mut effective_sig = opened.unwrap_or(internal_open);
let is_open = *effective_sig.read();
use_context_provider(|| SpeedDialContext {
is_open: effective_sig,
close_on_action,
});
let dir_class = direction.class_name();
let fixed_class = if fixed { "sd-speed-dial--fixed" } else { "" };
let open_container_class = if is_open { "sd-speed-dial--open" } else { "" };
let open_class = if is_open {
if active_icon.is_some() {
"sd-fab--open-custom"
} else {
"sd-fab--open"
}
} else {
""
};
let toggle = move |_| {
let next = !*effective_sig.read();
effective_sig.set(next);
};
rsx! {
div { class: "sd-speed-dial {dir_class} {fixed_class} {open_container_class} {class}",
if is_open && close_on_click_outside {
div {
class: "sd-speed-dial-backdrop",
onclick: move |e: MouseEvent| {
e.stop_propagation();
effective_sig.set(false);
},
}
}
button {
r#type: "button",
class: "sd-fab {open_class}",
onclick: toggle,
"aria-expanded": "{is_open}",
if is_open && active_icon.is_some() {
Icon { kind: active_icon.unwrap(), size: 22 }
} else {
Icon { kind: icon, size: 22 }
}
}
if is_open {
div { class: "sd-speed-dial-actions",
{children}
}
}
}
}
}