use cfg_if::cfg_if;
use leptos::leptos_dom::IntoChild;
use leptos::*;
use typed_builder::TypedBuilder;
#[cfg(any(feature = "csr", feature = "hydrate"))]
use wasm_bindgen::JsCast;
use crate::{use_location, use_resolved_path, State};
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)
}
}
#[derive(TypedBuilder)]
pub struct AProps<C, H>
where
C: IntoChild,
H: ToHref + 'static,
{
pub href: H,
#[builder(default)]
pub exact: bool,
#[builder(default, setter(strip_option))]
pub state: Option<State>,
#[builder(default)]
pub replace: bool,
pub children: Box<dyn Fn() -> Vec<C>>,
}
#[allow(non_snake_case)]
pub fn A<C, H>(cx: Scope, props: AProps<C, H>) -> Element
where
C: IntoChild,
H: ToHref + 'static,
{
let location = use_location(cx);
let href = use_resolved_path(cx, move || props.href.to_href()());
let is_active = create_memo(cx, move |_| match href() {
None => false,
Some(to) => {
let path = to
.split(['?', '#'])
.next()
.unwrap_or_default()
.to_lowercase();
let loc = location.pathname.get().to_lowercase();
if props.exact {
loc == path
} else {
loc.starts_with(&path)
}
}
});
let mut children = (props.children)();
if children.len() != 1 {
debug_warn!("[Link] Pass exactly one child to <A/>. If you want to pass more than one child, nest them within an element.");
}
let child = children.remove(0);
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
view! { cx,
<a
href=move || href().unwrap_or_default()
prop:state={props.state.map(|s| s.to_js_value())}
prop:replace={props.replace}
aria-current=move || if is_active() { Some("page") } else { None }
>
{child}
</a>
}
} else {
view! { cx,
<a
href=move || href().unwrap_or_default()
aria-current=move || if is_active() { Some("page") } else { None }
>
{child}
</a>
}
}
}
}