Skip to main content

yggdrasil/components/
frontend_layout.rs

1//! 前台布局组件
2//!
3//! 包裹所有前台路由,提供统一的 Header、Footer 与主内容区容器,
4//! 并为不同路由在 SuspenseBoundary 中展示对应的骨架屏。
5
6use dioxus::prelude::*;
7
8use crate::components::footer::Footer;
9use crate::components::header::{Header, SearchIconLink};
10use crate::components::nav::use_nav_items;
11use crate::components::skeletons::archive_skeleton::ArchiveSkeleton;
12use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
13use crate::components::skeletons::friends_skeleton::FriendsSkeleton;
14use crate::components::skeletons::home_skeleton::HomeSkeleton;
15use crate::components::skeletons::post_detail_skeleton::PostDetailSkeleton;
16use crate::components::skeletons::search_skeleton::SearchSkeleton;
17use crate::components::skeletons::tags_skeleton::TagsSkeleton;
18use crate::router::Route;
19use crate::theme::ThemeToggle;
20
21/// 根据当前前台路由选择对应的骨架屏组件。
22fn route_skeleton(route: &Route) -> Element {
23    match route {
24        Route::Archives {} => rsx! {
25            DelayedSkeleton { ArchiveSkeleton {} }
26        },
27        Route::Tags {} | Route::TagDetail { .. } => rsx! {
28            DelayedSkeleton { TagsSkeleton {} }
29        },
30        Route::Friends {} => rsx! {
31            DelayedSkeleton { FriendsSkeleton {} }
32        },
33        Route::Search {} => rsx! {
34            DelayedSkeleton { SearchSkeleton {} }
35        },
36        Route::PostDetail { .. } | Route::Changelog { .. } => rsx! {
37            DelayedSkeleton { PostDetailSkeleton {} }
38        },
39        Route::NotFound { .. } => rsx! {
40            div { class: "py-20 md:py-28" }
41        },
42        _ => rsx! {
43            DelayedSkeleton { HomeSkeleton {} }
44        },
45    }
46}
47
48/// 前台整体布局组件。
49///
50/// 负责渲染 Header(含前台导航与主题切换)、主内容区与 Footer,
51/// 并在路由内容加载过程中显示与路由匹配的骨架屏。
52#[component]
53pub fn FrontendLayout() -> Element {
54    let route = use_route::<Route>();
55    let nav_items = use_nav_items(route.clone());
56
57    rsx! {
58        div { class: "min-h-screen flex flex-col bg-paper-theme",
59            Header {
60                max_width: "max-w-4xl",
61                nav_items,
62                right_content: rsx! {
63                    SearchIconLink {}
64                    ThemeToggle {}
65                },
66            }
67            main { class: "flex-1 w-full max-w-4xl mx-auto px-6 py-6 md:py-12 overflow-x-clip",
68                SuspenseBoundary { fallback: move |_| route_skeleton(&route), Outlet::<Route> {} }
69            }
70            Footer {}
71        }
72    }
73}