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::build_nav_items;
11use crate::components::skeletons::archive_skeleton::ArchiveSkeleton;
12use crate::components::skeletons::changelog_skeleton::ChangelogSkeleton;
13use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
14use crate::components::skeletons::friends_skeleton::FriendsSkeleton;
15use crate::components::skeletons::home_skeleton::HomeSkeleton;
16use crate::components::skeletons::post_detail_skeleton::PostDetailSkeleton;
17use crate::components::skeletons::search_skeleton::SearchSkeleton;
18use crate::components::skeletons::tags_skeleton::TagDetailSkeleton;
19use crate::router::Route;
20use crate::theme::ThemeToggle;
21
22/// 根据当前前台路由选择对应的骨架屏组件。
23fn route_skeleton(route: &Route) -> Element {
24    match route {
25        Route::HomePage { page } => rsx! {
26            DelayedSkeleton { HomeSkeleton { current_page: (*page).max(1) } }
27        },
28        Route::Archives {} => rsx! {
29            DelayedSkeleton { ArchiveSkeleton {} }
30        },
31        Route::TagDetail { tag } => rsx! {
32            TagDetailSkeleton { tag: tag.clone() }
33        },
34        Route::Friends {} => rsx! {
35            DelayedSkeleton { FriendsSkeleton {} }
36        },
37        Route::Search {} => rsx! {
38            DelayedSkeleton { SearchSkeleton {} }
39        },
40        Route::PostDetail { .. } => rsx! {
41            DelayedSkeleton { PostDetailSkeleton {} }
42        },
43        Route::Changelog {} => rsx! {
44            DelayedSkeleton { ChangelogSkeleton {} }
45        },
46        Route::NotFound { .. } => rsx! {
47            div { class: "py-20 md:py-28" }
48        },
49        _ => rsx! {
50            DelayedSkeleton { HomeSkeleton {} }
51        },
52    }
53}
54
55/// 前台整体布局组件。
56///
57/// 负责渲染 Header(含前台导航与主题切换)、主内容区与 Footer,
58/// 并在路由内容加载过程中显示与路由匹配的骨架屏。
59#[component]
60pub fn FrontendLayout() -> Element {
61    let route = use_route::<Route>();
62    let nav_items = build_nav_items(route.clone());
63    // 首页与其它前台页面共用阅读宽度,导航和正文保持对齐。
64    let max_width = "max-w-4xl";
65
66    rsx! {
67        div { class: "min-h-screen flex flex-col bg-paper-theme",
68            Header {
69                max_width,
70                nav_items,
71                right_content: rsx! {
72                    SearchIconLink {}
73                    ThemeToggle {}
74                },
75            }
76            main { class: "flex-1 w-full {max_width} mx-auto px-6 py-6 md:py-12 overflow-x-clip",
77                SuspenseBoundary { fallback: move |_| route_skeleton(&route), Outlet::<Route> {} }
78            }
79            Footer {}
80        }
81    }
82}