1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
use crate::components::scrim::*;
use crate::futures_signals::signal::SignalExt;
use dominator::{clone, events, html, Dom};
use std::sync::Arc;
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum DrawerWidth {
Full,
Narrow,
}
/// Navigation drawer: <https://material.io/components/navigation-drawer>
///
/// # Examples
///
/// ```no_run
/// use dominator::{Dom, html};
/// use dmat_components::components::*;
///
/// fn retracting(modal: bool) -> Dom {
/// navigation_drawer!({
/// .expanded_signal(expanded.signal_cloned())
/// .with_scrim(true)
/// .drawer_content(Some(html!("div", {
/// .children(&mut[mock_view_select(), toggle_button(&expanded, "Close")])
/// })))
/// .retracts(true)
/// .modal(modal)
/// .main_content(Some(html!("div", {
/// .children(&mut[
/// html!("div", {
/// .text(lipsum(100).as_str())
/// }),
/// toggle_button(&expanded, "Show")
/// ])
/// })))
/// })
/// }
/// ```
#[component(render_fn = navigation_drawer)]
pub struct NavigationDrawer<TOnExtendedChange: Fn(bool) = fn(bool) -> ()> {
#[signal]
#[default(true)]
pub expanded: bool,
#[signal]
#[default(true)]
pub extended: bool,
pub on_extended_change: TOnExtendedChange,
/// If true, a scrim will be rendered on top of the contained UI when the drawer is expanded
#[default(false)]
pub with_scrim: bool,
#[signal]
#[default(DrawerWidth::Full)]
pub width: DrawerWidth,
/// Determines if the drawer will collapse and extend based on mouse hover
#[default(false)]
pub retracts: bool,
/// Determines if the drawer overlays the held UI, or if it is render side by side with it
#[signal]
#[default(false)]
pub modal: bool,
/// The content of the navigation drawer
#[signal]
#[default(None)]
pub drawer_content: Option<Dom>,
/// The main view which the drawer is attached to
#[signal]
#[default(None)]
pub main_content: Option<Dom>,
}
pub fn navigation_drawer(props: impl NavigationDrawerPropsTrait + 'static) -> Dom {
let NavigationDrawerProps {
expanded,
extended,
on_extended_change,
with_scrim,
width,
retracts,
modal,
drawer_content,
main_content,
apply,
} = props.take();
let main_content = scrim(
ScrimProps::new()
.content_signal(main_content)
.hide(!with_scrim),
);
let extend_cb = on_extended_change.map(Arc::new);
let width_bc = width.broadcast();
html!("div", {
.class("dmat-navigation-drawer")
.apply_if(apply.is_some(), |d| d.apply(apply.unwrap()))
.class_signal("-expanded", expanded)
.class_signal("-extended", extended)
.class_signal("-modal", modal)
.class_signal("-narrow", width_bc.signal_ref(|w| *w == DrawerWidth::Narrow))
.class_signal("-full", width_bc.signal_ref(|w| *w == DrawerWidth::Full))
.apply_if(retracts, |d| d.class("-retracting"))
.children(&mut [
html!("div", {
.class("drawer")
.apply_if(retracts, clone!(extend_cb => move |d| {
d.event(clone!(extend_cb => move |_:events::MouseEnter| {
if let Some(cb) = extend_cb.as_ref() {
cb(true);
}
}))
.event(clone!(extend_cb => move |_:events::MouseMove| {
if let Some(cb) = extend_cb.as_ref() {
cb(true);
}
}))
.event(clone!(extend_cb=> move |_:events::MouseLeave| {
if let Some(cb) = extend_cb.as_ref() {
cb(false);
}
}))
}))
.child_signal(drawer_content)
}),
main_content
])
})
}