use crate::{use_location, use_resolved_path, State};
use leptos::{leptos_dom::IntoView, *};
pub trait ToHref {
fn to_href(&self) -> Box<dyn Fn() -> String + '_>;
}
impl ToHref for &str {
fn to_href(&self) -> Box<dyn Fn() -> String> {
let s = self.to_string();
Box::new(move || s.clone())
}
}
impl ToHref for String {
fn to_href(&self) -> Box<dyn Fn() -> String> {
let s = self.clone();
Box::new(move || s.clone())
}
}
impl<F> ToHref for F
where
F: Fn() -> String + 'static,
{
fn to_href(&self) -> Box<dyn Fn() -> String + '_> {
Box::new(self)
}
}
#[component]
pub fn A<H>(
cx: Scope,
href: H,
#[prop(optional)]
exact: bool,
#[prop(optional)]
state: Option<State>,
#[prop(optional)]
replace: bool,
#[prop(optional, into)]
class: Option<AttributeValue>,
children: Children,
) -> impl IntoView
where
H: ToHref + 'static,
{
fn inner(
cx: Scope,
href: Memo<Option<String>>,
exact: bool,
state: Option<State>,
replace: bool,
class: Option<AttributeValue>,
children: Children,
) -> HtmlElement<leptos::html::A> {
let location = use_location(cx);
let is_active = create_memo(cx, move |_| match href.get() {
None => false,
Some(to) => {
let path = to
.split(['?', '#'])
.next()
.unwrap_or_default()
.to_lowercase();
let loc = location.pathname.get().to_lowercase();
if exact {
loc == path
} else {
loc.starts_with(&path)
}
}
});
view! { cx,
<a
href=move || href.get().unwrap_or_default()
prop:state={state.map(|s| s.to_js_value())}
prop:replace={replace}
aria-current=move || if is_active.get() { Some("page") } else { None }
class=class
>
{children(cx)}
</a>
}
}
let href = use_resolved_path(cx, move || href.to_href()());
inner(cx, href, exact, state, replace, class, children)
}