use dioxus::prelude::*;
#[derive(Clone, PartialEq, Props)]
pub struct ScrollspyProps {
#[props(default = "body".to_string())]
pub target: String,
pub active: Signal<String>,
#[props(default = 0)]
pub offset: i32,
}
#[component]
pub fn Scrollspy(props: ScrollspyProps) -> Element {
let mut active_signal = props.active;
let target = props.target.clone();
let offset = props.offset;
use_effect(move || {
let target = target.clone();
document::eval(&format!(
r#"
(function() {{
var container = document.querySelector('{target}');
if (!container || container === document.body) container = document;
var sections = document.querySelectorAll('[id]');
if (sections.length === 0) return;
function update() {{
var scrollTop = (container === document)
? window.scrollY || document.documentElement.scrollTop
: container.scrollTop;
var offset = {offset};
var active = '';
sections.forEach(function(section) {{
var rect = section.getBoundingClientRect();
if (rect.top <= offset + 10) {{
active = section.id;
}}
}});
if (active && window.__dioxus_scrollspy_active !== active) {{
window.__dioxus_scrollspy_active = active;
// Dispatch a custom event that Dioxus can listen to
window.dispatchEvent(new CustomEvent('scrollspy', {{ detail: active }}));
}}
}}
var scrollTarget = (container === document) ? window : container;
scrollTarget.addEventListener('scroll', update, {{ passive: true }});
update();
}})();
"#
));
});
use_effect(move || {
let eval_handle = document::eval(
r#"
(function() {
return new Promise(function(resolve) {
var current = window.__dioxus_scrollspy_active || '';
// Set up listener for changes
window.addEventListener('scrollspy', function handler(e) {
resolve(e.detail);
window.removeEventListener('scrollspy', handler);
});
// If already set, resolve immediately
if (current) resolve(current);
});
})()
"#,
);
spawn(async move {
if let Ok(value) = eval_handle.await {
if let Some(id) = value.as_str() {
active_signal.set(id.to_string());
}
}
});
});
rsx! {}
}