use maud::{html, Markup};
use super::{field, native_select, separator, switch};
#[derive(Clone, Debug)]
pub enum Side {
Left,
Right,
Top,
Bottom,
}
impl Side {
fn class_name(&self) -> &'static str {
match self {
Side::Left => "mui-drawer--left",
Side::Right => "mui-drawer--right",
Side::Top => "mui-drawer--top",
Side::Bottom => "mui-drawer--bottom",
}
}
}
impl Default for Side {
fn default() -> Self {
Side::Right
}
}
#[derive(Clone, Debug)]
pub struct Props {
pub id: String,
pub title: String,
pub description: Option<String>,
pub children: Markup,
pub footer: Option<Markup>,
pub side: Side,
}
impl Default for Props {
fn default() -> Self {
Self {
id: "drawer".to_string(),
title: "Drawer".to_string(),
description: None,
children: html! {},
footer: None,
side: Side::Right,
}
}
}
pub fn trigger(target_id: &str, label: &str) -> Markup {
html! {
button type="button"
class="mui-btn mui-btn--default mui-btn--md"
data-mui="drawer-trigger"
data-target=(target_id)
{
(label)
}
}
}
pub fn close_button(label: &str) -> Markup {
html! {
button type="button"
class="mui-drawer__close"
data-mui-close
aria-label=(label)
{
"×"
}
}
}
pub fn render(props: Props) -> Markup {
let title_id = format!("{}-title", props.id);
let desc_id = format!("{}-desc", props.id);
let has_desc = props.description.is_some();
let show_handle = matches!(props.side, Side::Bottom | Side::Top);
html! {
dialog class={"mui-drawer " (props.side.class_name())}
id=(props.id)
data-mui="drawer"
aria-labelledby=(title_id)
aria-describedby=[if has_desc { Some(desc_id.as_str()) } else { None }]
{
@if show_handle {
div class="mui-drawer__handle" {
div class="mui-drawer__handle-bar" {}
}
}
div class="mui-drawer__header" {
h2 class="mui-drawer__title" id=(title_id) {
(props.title)
}
(close_button("Close"))
}
@if let Some(desc) = props.description {
p class="mui-drawer__description" id=(desc_id) {
(desc)
}
}
div class="mui-drawer__body" {
(props.children)
}
@if let Some(footer) = props.footer {
div class="mui-drawer__footer" {
(footer)
}
}
}
}
}
pub fn showcase() -> Markup {
html! {
div.mui-showcase__grid {
section {
h2 { "Right (default)" }
div.mui-showcase__row {
(trigger("demo-drawer-1", "Open drawer"))
}
}
(render(Props {
id: "demo-drawer-1".to_string(),
title: "Settings".to_string(),
description: Some("Adjust your preferences here.".to_string()),
children: html! {
div style="display: flex; flex-direction: column; gap: 1rem;" {
(field::render(field::Props {
label: "Theme".to_string(),
id: "demo-theme".to_string(),
description: Some("Choose your preferred appearance.".to_string()),
children: html! {
(native_select::render(native_select::NativeSelectProps {
name: "theme".to_string(),
id: "demo-theme".to_string(),
options: vec![
native_select::NativeOption { value: "light".to_string(), label: "Light".to_string(), disabled: false },
native_select::NativeOption { value: "dark".to_string(), label: "Dark".to_string(), disabled: false },
native_select::NativeOption { value: "auto".to_string(), label: "System".to_string(), disabled: false },
],
selected: Some("auto".to_string()),
disabled: false,
placeholder: None,
}))
},
..Default::default()
}))
(separator::render(separator::Props {
orientation: separator::Orientation::Horizontal,
decorative: true,
}))
(switch::render(switch::Props {
name: "notifications".to_string(),
id: "demo-notifications".to_string(),
label: "Enable notifications".to_string(),
checked: true,
disabled: false,
aria_label: None,
}))
(switch::render(switch::Props {
name: "sounds".to_string(),
id: "demo-sounds".to_string(),
label: "Sound effects".to_string(),
checked: false,
disabled: false,
aria_label: None,
}))
}
},
footer: Some(html! {
button class="mui-btn mui-btn--default mui-btn--md" data-mui-close { "Cancel" }
button class="mui-btn mui-btn--primary mui-btn--md" { "Save changes" }
}),
side: Side::Right,
}))
section {
h2 { "Left (navigation)" }
div.mui-showcase__row {
(trigger("demo-drawer-2", "Open drawer"))
}
}
(render(Props {
id: "demo-drawer-2".to_string(),
title: "Navigation".to_string(),
description: None,
children: html! {
nav style="display: flex; flex-direction: column; gap: 0.25rem;" {
a class="mui-btn mui-btn--ghost mui-btn--md" style="justify-content: flex-start; width: 100%;" href="#" { "Home" }
a class="mui-btn mui-btn--ghost mui-btn--md" style="justify-content: flex-start; width: 100%;" href="#" { "Products" }
a class="mui-btn mui-btn--ghost mui-btn--md" style="justify-content: flex-start; width: 100%;" href="#" { "Documentation" }
(separator::render(separator::Props {
orientation: separator::Orientation::Horizontal,
decorative: true,
}))
a class="mui-btn mui-btn--ghost mui-btn--md" style="justify-content: flex-start; width: 100%;" href="#" { "Settings" }
a class="mui-btn mui-btn--ghost mui-btn--md" style="justify-content: flex-start; width: 100%;" href="#" { "Contact" }
}
},
footer: None,
side: Side::Left,
}))
section {
h2 { "Bottom (sheet with grab handle)" }
div.mui-showcase__row {
(trigger("demo-drawer-3", "Open drawer"))
}
}
(render(Props {
id: "demo-drawer-3".to_string(),
title: "Share".to_string(),
description: Some("Share this document with others.".to_string()),
children: html! {
(field::render(field::Props {
label: "Email address".to_string(),
id: "demo-share-email".to_string(),
description: Some("Enter the recipient's email.".to_string()),
children: html! {
input.mui-input type="email" id="demo-share-email" name="email"
placeholder="colleague@example.com";
},
..Default::default()
}))
},
footer: Some(html! {
button class="mui-btn mui-btn--default mui-btn--md" data-mui-close { "Cancel" }
button class="mui-btn mui-btn--primary mui-btn--md" { "Send invite" }
}),
side: Side::Bottom,
}))
}
}
}