use crate::prelude::*;
use crate::router::*;
use serde_json::Value;
use web_sys::window;
use web_sys::{ScrollBehavior, ScrollToOptions};
#[derive(Properties, Clone, PartialEq)]
pub struct LinkProps {
#[prop_or_default]
pub to: &'static str,
#[prop_or_default]
pub class: &'static str,
#[prop_or("_blank")]
pub target: &'static str,
#[prop_or("noreferrer")]
pub rel: &'static str,
#[prop_or_default]
pub query: Value,
#[prop_or_default]
pub state: &'static str,
#[prop_or_default]
pub children: Html,
#[prop_or_default]
pub scroll: bool,
#[prop_or_default]
pub prefetch: bool,
#[prop_or_default]
pub scroll_offset: f64,
#[prop_or("auto")]
pub scroll_behavior: &'static str,
#[prop_or_default]
pub aria_current: &'static str,
#[prop_or_default]
pub aria_describedby: &'static str,
#[prop_or_default]
pub aria_expanded: &'static str,
#[prop_or_default]
pub aria_hidden: &'static str,
#[prop_or_default]
pub aria_live: &'static str,
#[prop_or_default]
pub aria_pressed: &'static str,
#[prop_or_default]
pub aria_controls: &'static str,
#[prop_or_default]
pub aria_labelledby: &'static str,
}
#[func]
pub fn Link(props: &LinkProps) -> Html {
let props = props.clone();
let to = props.to;
#[allow(unused_variables)]
let state = props.state;
#[allow(unused_variables)]
let query = props.query;
let router = use_router();
let router_clone = router.clone();
let (target, href) = if props.to.starts_with("/#") {
("_self", &props.to[1..])
} else if props.to.starts_with('#') {
("_self", props.to)
} else {
(props.target, props.to)
};
let onclick = Callback::from(move |event: MouseEvent| {
let mut router = router.clone();
let query = query.clone();
match (props.state, query) {
("", Value::Null) => {
if target != "_blank" {
router.push(to);
}
}
(state, Value::Null) => {
event.prevent_default();
router.push_with_state(to, state);
}
("", query) => {
event.prevent_default();
router
.push_with_query(to, &query)
.expect("failed push history with query");
}
(state, query) => {
event.prevent_default();
router
.push_with_query_and_state(to, &query, state)
.expect("failed push history with query and state");
}
}
if props.scroll {
let scroll_behavior = match props.scroll_behavior {
"auto" => ScrollBehavior::Auto,
"instant" => ScrollBehavior::Instant,
"smooth" => ScrollBehavior::Smooth,
_ => ScrollBehavior::Auto,
};
if props.to.starts_with('#') || props.to.starts_with("/#") {
event.prevent_default();
if let Some(element) = window()
.and_then(|win| win.document())
.and_then(|doc| doc.get_element_by_id(&href[1..]))
{
let offset_top = element.get_bounding_client_rect().y();
window()
.map(|win| {
win.scroll_to_with_scroll_to_options(
ScrollToOptions::new()
.top(offset_top)
.behavior(scroll_behavior),
)
})
.expect("Failed to scroll to local anchor link");
} else {
window()
.map(|win| {
win.scroll_to_with_scroll_to_options(
ScrollToOptions::new()
.top(props.scroll_offset)
.behavior(scroll_behavior),
)
})
.expect("Failed to scroll to fallback offset");
}
} else {
window()
.map(|win| {
win.scroll_to_with_scroll_to_options(
ScrollToOptions::new()
.top(props.scroll_offset)
.behavior(scroll_behavior),
)
})
.expect("Failed to scroll to external link");
}
}
});
use_effect_with((), move |_| {
if props.prefetch {
let mut router = router_clone.clone();
router.prefetch(href);
}
});
let aria_label = "Link to ".to_string() + href;
let tabindex = if props.scroll { "0" } else { "-1" };
rsx! {
<a
href={href}
target={target}
rel={props.rel}
class={props.class}
onclick={onclick}
role="link"
tabindex={tabindex}
aria-label={aria_label.clone()}
title={aria_label.clone()}
aria-haspopup="true"
aria-current={props.aria_current}
aria-describedby={props.aria_describedby}
aria-expanded={props.aria_expanded}
aria-hidden={props.aria_hidden}
aria-live={props.aria_live}
aria-pressed={props.aria_pressed}
aria-controls={props.aria_controls}
aria-labelledby={props.aria_labelledby}
>{ props.children.clone() }</a>
}
}