use crate::icon::{Icon, IconKind};
use crate::locale::use_locale;
use dioxus::prelude::*;
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub enum DrawerPosition {
Left,
#[default]
Right,
}
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub enum DrawerSize {
Sm,
#[default]
Md,
Lg,
}
impl DrawerSize {
#[must_use]
pub fn scss_class(&self) -> &'static str {
match self {
DrawerSize::Sm => "sd-drawer-panel--sm",
DrawerSize::Md => "sd-drawer-panel--md",
DrawerSize::Lg => "sd-drawer-panel--lg",
}
}
}
#[component]
pub fn Drawer(
is_open: Signal<bool>,
#[props(default)]
id: Option<String>,
#[props(default)]
title: Option<String>,
#[props(default)]
aria_label: Option<String>,
#[props(default)]
position: DrawerPosition,
#[props(default)]
size: DrawerSize,
#[props(default = true)]
close_on_escape: bool,
#[props(default = true)]
close_on_backdrop: bool,
#[props(default)]
close_label: Option<String>,
#[props(default)]
glow: Option<bool>,
#[props(default)]
class: String,
children: Element,
) -> Element {
#[cfg(feature = "theme")]
let theme_val = crate::theme::use_theme()();
let open = *is_open.read();
let locale = use_locale();
let drawer_id = crate::a11y::use_unique_id("sd-drawer", id);
let title_id = format!("{drawer_id}-title");
use_effect(move || {
let is_currently_open = *is_open.read();
if is_currently_open {
let _ = document::eval("document.body.style.overflow = 'hidden'");
} else {
let _ = document::eval("document.body.style.overflow = ''");
}
});
use_drop(move || {
let _ = document::eval("document.body.style.overflow = ''");
});
if !open {
return rsx! {};
}
#[cfg(feature = "theme")]
let base_override = theme_val.resolve_component_base("drawer").unwrap_or("");
#[cfg(feature = "theme")]
let class_modifiers = theme_val.resolve_class_modifiers("drawer", &class);
#[cfg(feature = "theme")]
let glow_class = theme_val.resolve_elevation_class("drawer", glow);
#[cfg(not(feature = "theme"))]
let base_override = "";
#[cfg(not(feature = "theme"))]
let class_modifiers = String::new();
#[cfg(not(feature = "theme"))]
let glow_class = if glow.unwrap_or(false) {
"sd-drawer-panel--glow".to_string()
} else {
String::new()
};
#[cfg(feature = "animation")]
let anim_class = match position {
DrawerPosition::Left => crate::animation::Animations::DRAWER_LEFT,
DrawerPosition::Right => crate::animation::Animations::DRAWER_RIGHT,
};
#[cfg(not(feature = "animation"))]
let anim_class = match position {
DrawerPosition::Left => "animate-drawer-slide-left",
DrawerPosition::Right => "animate-drawer-slide-right",
};
let pos_class = match position {
DrawerPosition::Left => "sd-drawer-panel--left",
DrawerPosition::Right => "sd-drawer-panel--right",
};
let size_class = size.scss_class();
let close_btn_label = close_label.unwrap_or_else(|| locale.close.clone());
rsx! {
div {
class: "sd-drawer-backdrop",
tabindex: "-1",
onclick: move |_| {
if close_on_backdrop {
*is_open.write() = false;
}
},
onkeydown: move |e: KeyboardEvent| {
if close_on_escape && e.key() == Key::Escape {
e.stop_propagation();
*is_open.write() = false;
}
},
div {
role: "dialog",
id: "{drawer_id}",
tabindex: "-1",
"aria-modal": "true",
"aria-labelledby": if title.is_some() { Some(title_id.as_str()) } else { None },
"aria-label": if title.is_none() { aria_label.as_deref() } else { None },
class: "sd-drawer-panel {base_override} {pos_class} {size_class} {glow_class} {anim_class} {class_modifiers} {class}",
onmounted: move |m| {
let data = m.data();
spawn(async move {
let _ = data.set_focus(true).await;
});
},
onclick: move |e| e.stop_propagation(),
div {
class: "sd-drawer__header",
if let Some(t) = &title {
h3 {
id: "{title_id}",
class: "sd-drawer__title",
"{t}"
}
}
button {
r#type: "button",
class: "sd-drawer__close",
"aria-label": "{close_btn_label}",
title: "{close_btn_label}",
onclick: move |_| *is_open.write() = false,
Icon { kind: IconKind::X, size: 16 }
}
}
div {
class: "sd-drawer__body",
{children}
}
}
}
}
}