yggdrasil/components/post/
post_toc.rs1use dioxus::prelude::*;
14
15#[cfg(target_arch = "wasm32")]
16use crate::utils::js::invoke_optional_global;
17
18#[component]
29pub fn PostToc(
30 toc_html: String,
31 #[props(default = "Table of Contents")] title: &'static str,
32 #[props(default)] nav_id: Option<&'static str>,
33 #[props(default)] content_id: Option<&'static str>,
34 #[props(default)] scroll_id: Option<&'static str>,
35) -> Element {
36 let mut pinned = use_signal(|| nav_id.is_some());
38
39 #[cfg(target_arch = "wasm32")]
43 use_effect(move || {
44 let window =
45 web_sys::window().expect("post_toc use_effect 仅在 WASM 浏览器上下文执行:无 window");
46 if let (Some(nav_id), Some(content_id), Some(scroll_id)) = (nav_id, content_id, scroll_id) {
47 invoke_optional_global(
48 &window,
49 "__initTocSidebar",
50 &[
51 format!("#{nav_id}").into(),
52 format!("#{content_id}").into(),
53 format!("#{scroll_id}").into(),
54 ],
55 );
56 } else {
57 invoke_optional_global(&window, "__initTocSidebar", &[]);
58 }
59 });
60
61 #[cfg(target_arch = "wasm32")]
62 use_drop(move || {
63 if let Some(window) = web_sys::window() {
64 if let Some(nav_id) = nav_id {
65 invoke_optional_global(
66 &window,
67 "__disposeTocSidebar",
68 &[format!("#{nav_id}").into()],
69 );
70 } else {
71 invoke_optional_global(&window, "__disposeTocSidebar", &[]);
72 }
73 }
74 });
75
76 rsx! {
77 details { class: "toc",
79 summary { accesskey: "c", title: "(Alt + C)",
80 span { class: "title", "{title}" }
81 }
82 div { class: "inner", dangerous_inner_html: "{toc_html}" }
83 }
84
85 nav {
89 id: nav_id,
90 class: if pinned() { "toc-sidebar pinned" } else { "toc-sidebar" },
91 aria_label: "{title}",
92 div { class: "toc-sidebar-panel",
93 div { class: "toc-sidebar-head",
94 span { class: "toc-sidebar-title", "{title}" }
95 button {
96 class: "toc-sidebar-pin",
97 aria_label: if pinned() { "取消固定目录" } else { "固定目录" },
98 aria_pressed: "{pinned}",
99 onclick: move |_| pinned.set(!pinned()),
100 svg {
102 view_box: "0 0 24 24",
103 fill: "none",
104 stroke: "currentColor",
105 stroke_width: "2",
106 stroke_linecap: "round",
107 stroke_linejoin: "round",
108 path { d: "M12 17v5" }
109 path { d: "M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 8 15.24V17h8v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 12 10.76V6h1a2 2 0 0 0 0-4h-2a2 2 0 0 0 0 4h1z" }
110 }
111 }
112 }
113 div {
114 class: "toc-sidebar-body",
115 dangerous_inner_html: "{toc_html}",
116 }
117 }
118 }
119 }
120}