Skip to main content

dioxus_docs_kit/components/blog/
mobile_drawer.rs

1use dioxus::prelude::*;
2use dioxus_free_icons::Icon;
3use dioxus_free_icons::icons::ld_icons::LdX;
4
5use crate::BlogContext;
6use crate::blog::registry::BlogRegistry;
7
8use super::tag_filter::TagFilter;
9
10/// Slide-in drawer for mobile screens (blog variant).
11#[component]
12pub fn BlogMobileDrawer(mut open: Signal<bool>) -> Element {
13    let ctx = use_context::<BlogContext>();
14    let registry = use_context::<&'static BlogRegistry>();
15
16    let current_slug = ctx.current_slug;
17    let current_category = ctx.current_category;
18    use_effect(move || {
19        let _ = current_slug();
20        let _ = current_category.map(|slug| slug());
21        open.set(false);
22    });
23
24    let is_open = open();
25
26    let backdrop_class = if is_open {
27        "fixed inset-0 z-[60] bg-black/50 lg:hidden transition-opacity duration-300 opacity-100"
28    } else {
29        "fixed inset-0 z-[60] bg-black/50 lg:hidden transition-opacity duration-300 opacity-0 pointer-events-none"
30    };
31
32    let panel_class = if is_open {
33        "fixed left-0 top-0 bottom-0 z-[70] w-72 bg-base-200 lg:hidden transition-transform duration-300 translate-x-0 shadow-2xl"
34    } else {
35        "fixed left-0 top-0 bottom-0 z-[70] w-72 bg-base-200 lg:hidden transition-transform duration-300 -translate-x-full shadow-2xl"
36    };
37
38    rsx! {
39        div {
40            class: "{backdrop_class}",
41            onclick: move |_| open.set(false),
42        }
43
44        div {
45            class: "{panel_class}",
46            onclick: move |e| e.stop_propagation(),
47
48            div { class: "flex items-center justify-between px-4 py-3 border-b border-base-300",
49                span { class: "font-semibold text-sm", "Blog" }
50                button {
51                    class: "btn btn-ghost btn-xs btn-square",
52                    onclick: move |_| open.set(false),
53                    Icon { class: "size-4", icon: LdX }
54                }
55            }
56
57            div { class: "overflow-y-auto h-[calc(100%-3.5rem)] p-4",
58                if !registry.all_tags().is_empty() {
59                    div { class: "mb-6",
60                        h3 { class: "font-semibold text-sm text-base-content/70 uppercase tracking-wider mb-3",
61                            "Tags"
62                        }
63                        TagFilter {}
64                    }
65                }
66
67                div {
68                    h3 { class: "font-semibold text-sm text-base-content/70 uppercase tracking-wider mb-3",
69                        "Recent Posts"
70                    }
71                    ul { class: "space-y-1",
72                        for post in registry.all_posts().iter().take(10) {
73                            {
74                                let href = format!("{}/{}", ctx.base_path, post.slug);
75                                let is_active = current_slug() == post.slug;
76                                let active_class = if is_active {
77                                    "bg-primary/10 text-primary font-medium"
78                                } else {
79                                    "text-base-content/70 hover:text-base-content hover:bg-base-200"
80                                };
81                                rsx! {
82                                    li {
83                                        Link {
84                                            to: NavigationTarget::Internal(href),
85                                            class: "block px-3 py-2 text-sm rounded-lg transition-colors {active_class}",
86                                            "{post.frontmatter.title}"
87                                        }
88                                    }
89                                }
90                            }
91                        }
92                    }
93                }
94            }
95        }
96    }
97}