use yew::prelude::*;
use yew_router::prelude::*;
use super::{
mode::Match,
props::NavLinkProps,
utils::{build_class, is_path_prefix}
};
#[component]
pub fn NavLink<R: Routable + PartialEq + Clone + 'static>(props: &NavLinkProps<R>) -> Html {
let current_route = use_route::<R>();
let navigator = use_navigator();
let is_active = current_route.is_some_and(|route| {
if props.partial {
is_path_prefix(&props.to.to_path(), &route.to_path())
} else {
route == props.to
}
});
let path = props.to.to_path();
let href = navigator.as_ref().map_or_else(
|| path.clone(),
|nav| match nav.basename() {
Some(base) if !base.is_empty() => format!("{base}{path}"),
_ => path.clone()
}
);
let onclick = {
let to = props.to.clone();
Callback::from(move |event: MouseEvent| {
if event.meta_key() || event.ctrl_key() || event.shift_key() || event.alt_key() {
return;
}
event.prevent_default();
if let Some(nav) = &navigator {
nav.push(&to);
}
})
};
let class = build_class(is_active, props.class, props.active_class);
let aria_current = if is_active { Some("page") } else { None };
html! {
<a class={class} href={href} onclick={onclick} aria-current={aria_current}>
{ for props.children.iter() }
</a>
}
}
pub fn nav_link<R: Routable + PartialEq + Clone + 'static>(
to: R,
children: &str,
match_mode: Match
) -> Html {
let partial = match_mode == Match::Partial;
html! {
<NavLink<R> to={to} {partial}>{ Html::from(children) }</NavLink<R>>
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, PartialEq, Debug, Routable)]
enum TestRoute {
#[at("/")]
Home
}
#[test]
fn nav_link_exact_returns_html() {
let html = nav_link(TestRoute::Home, "Home", Match::Exact);
assert!(matches!(html, Html::VComp(_)));
}
#[test]
fn nav_link_empty_text() {
let html = nav_link(TestRoute::Home, "", Match::Exact);
assert!(matches!(html, Html::VComp(_)));
}
#[test]
fn nav_link_partial_match() {
let html = nav_link(TestRoute::Home, "Home", Match::Partial);
assert!(matches!(html, Html::VComp(_)));
}
}