Skip to main content

dioxus_docs_kit/components/blog/
progress_bar.rs

1use dioxus::prelude::*;
2
3/// A thin reading progress bar fixed at the top of the viewport.
4///
5/// Uses a scroll event listener (RAF-throttled) to track reading progress.
6#[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}