use crate::icon::{Icon, IconKind};
use dioxus::prelude::*;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum AffixPosition {
#[default]
BottomRight,
BottomLeft,
TopRight,
TopLeft,
}
impl AffixPosition {
#[must_use]
pub fn class_name(&self) -> &'static str {
match self {
Self::BottomRight => "sd-affix--bottom-right",
Self::BottomLeft => "sd-affix--bottom-left",
Self::TopRight => "sd-affix--top-right",
Self::TopLeft => "sd-affix--top-left",
}
}
}
#[component]
pub fn Affix(
#[props(default)]
position: AffixPosition,
#[props(default)]
scroll_threshold: Option<u32>,
#[props(default = 60)]
z_index: u32,
#[props(default)]
class: String,
children: Element,
) -> Element {
let mut is_visible = use_signal(|| scroll_threshold.is_none());
if let Some(threshold) = scroll_threshold {
use_hook(move || {
spawn(async move {
let mut eval = document::eval(&format!(
r#"
const checkScroll = () => {{
const scrolled = window.scrollY || document.documentElement.scrollTop;
return scrolled >= {threshold};
}};
window.addEventListener('scroll', () => {{
dioxus.send(checkScroll());
}}, {{ passive: true }});
dioxus.send(checkScroll());
"#
));
while let Ok(val) = eval.recv::<bool>().await {
is_visible.set(val);
}
});
});
}
let pos_class = position.class_name();
let visibility_class = if *is_visible.read() {
"sd-affix--visible"
} else {
"sd-affix--hidden"
};
rsx! {
div {
class: "sd-affix {pos_class} {visibility_class} {class}",
style: "z-index: {z_index};",
{children}
}
}
}
#[component]
pub fn BackToTop(
#[props(default = 200)]
scroll_threshold: u32,
#[props(default)]
label: Option<String>,
#[props(default = IconKind::ChevronUp)]
icon: IconKind,
#[props(default)]
position: AffixPosition,
#[props(default)]
class: String,
) -> Element {
let handle_scroll_top = move |_| {
let eval = document::eval("window.scrollTo({ top: 0, behavior: 'smooth' });");
spawn(async move {
let _ = eval.await;
});
};
let icon_only_class = if label.is_none() {
"sd-back-to-top--icon-only"
} else {
""
};
rsx! {
Affix {
position: position,
scroll_threshold: Some(scroll_threshold),
class: class,
button {
r#type: "button",
class: "sd-back-to-top {icon_only_class}",
title: "Torna in cima",
onclick: handle_scroll_top,
Icon { kind: icon, size: 16 }
if let Some(l) = label {
span { "{l}" }
}
}
}
}
}