sidebar/yew/
sidebar.rs

1use crate::yew::logo::Logo;
2use crate::yew::menu::Menu;
3use crate::yew::{context::*, profile::Profile};
4use web_sys::window;
5use yew::prelude::*;
6
7#[derive(Properties, Clone, PartialEq)]
8pub struct SidebarProps {
9    #[prop_or_default]
10    pub children: ChildrenWithProps<Menu>,
11
12    #[prop_or(true)]
13    pub show_profile: bool,
14
15    #[prop_or_default]
16    pub user_name: &'static str,
17    #[prop_or_default]
18    pub designation: &'static str,
19    #[prop_or_default]
20    pub user_img: &'static str,
21
22    #[prop_or_default]
23    pub on_logout: Callback<()>,
24
25    #[prop_or("width: 270px; background: white;")]
26    pub style: &'static str,
27    #[prop_or_default]
28    pub class: &'static str,
29
30    #[prop_or(
31        "display: flex; justify-content: space-between; align-items: center; padding: 1rem;"
32    )]
33    pub header_style: &'static str,
34    #[prop_or_default]
35    pub header_class: &'static str,
36
37    #[prop_or_default]
38    pub logo_img_url: &'static str,
39    #[prop_or_default]
40    pub logo_href: &'static str,
41}
42#[function_component(Sidebar)]
43pub fn sidebar(props: &SidebarProps) -> Html {
44    let is_mobile = use_state(|| {
45        let width = window().unwrap().inner_width().unwrap().as_f64().unwrap();
46        width < 768.0
47    });
48
49    let config = use_state(|| SidebarConfig {
50        is_collapsed: *is_mobile,
51    });
52
53    let toggle_sidebar = {
54        let config = config.clone();
55        Callback::from(move |_| {
56            config.set(SidebarConfig {
57                is_collapsed: !config.is_collapsed,
58            });
59        })
60    };
61    let sidebar_style = if config.is_collapsed {
62        "width: 80px; background: white;"
63    } else {
64        props.style
65    };
66
67    html! {
68        <ContextProvider<SidebarContext> context={SidebarContext(config.clone().into())}>
69            <aside class={props.class} style={format!("display: flex; flex-direction: column; height: 100vh; {}", sidebar_style)} aria-label="Sidebar Navigation">
70                <div class={props.header_class} style={props.header_style}>
71                    <Logo img_url={props.logo_img_url} href={props.logo_href} />
72                    <button onclick={toggle_sidebar} aria-label="Toggle Sidebar">
73                        { if config.is_collapsed { "▶" } else { "◀" } }
74                    </button>
75                </div>
76
77                <div style="flex-grow: 1; overflow-y: auto;">
78                    { for props.children.iter() }
79                </div>
80
81                if props.show_profile {
82                    <Profile
83                        user_name={props.user_name}
84                        designation={props.designation}
85                        user_img={props.user_img}
86                        is_collapsed={config.is_collapsed}
87                        on_logout={props.on_logout.clone()}
88                    />
89                }
90            </aside>
91        </ContextProvider<SidebarContext>>
92    }
93}