dioxus_docs_kit/components/blog/
progress_bar.rs1use dioxus::prelude::*;
2
3#[component]
7pub fn ReadingProgressBar() -> Element {
8 let mut progress = use_signal(|| 0.0f64);
9
10 use_effect(move || {
11 spawn(async move {
12 let mut eval = document::eval(
13 r#"
14 if (window.__dioxusDocsKitReadingProgressCleanup) {
15 window.__dioxusDocsKitReadingProgressCleanup();
16 }
17 let ticking = false;
18 const handleScroll = () => {
19 if (!ticking) {
20 requestAnimationFrame(() => {
21 const docHeight = document.documentElement.scrollHeight - window.innerHeight;
22 const pct = docHeight > 0 ? (window.scrollY / docHeight) * 100 : 0;
23 dioxus.send(Math.min(Math.max(pct, 0), 100));
24 ticking = false;
25 });
26 ticking = true;
27 }
28 };
29 window.addEventListener('scroll', handleScroll, { passive: true });
30 handleScroll();
31 window.__dioxusDocsKitReadingProgressCleanup = () => {
32 window.removeEventListener('scroll', handleScroll);
33 delete window.__dioxusDocsKitReadingProgressCleanup;
34 };
35 while (true) { await new Promise(r => setTimeout(r, 1000000)); }
36 "#,
37 );
38 while let Ok(val) = eval.recv::<f64>().await {
39 progress.set(val);
40 }
41 });
42 });
43
44 use_drop(|| {
45 spawn(async move {
46 let _ = document::eval(
47 r#"
48 if (window.__dioxusDocsKitReadingProgressCleanup) {
49 window.__dioxusDocsKitReadingProgressCleanup();
50 }
51 "#,
52 );
53 });
54 });
55
56 rsx! {
57 div { class: "fixed top-0 left-0 w-full z-[100] pointer-events-none",
58 div {
59 class: "h-0.5 bg-primary transition-[width] duration-75",
60 style: "width: {progress()}%",
61 }
62 }
63 }
64}