This commit is contained in:
Jose Quesada 2022-12-15 12:50:00 -06:00
parent 5405dcd09d
commit 0a8b516182

View file

@ -39,44 +39,36 @@ where
}
}
/// Properties that can be passed to the [A] component, which is an HTML
/// [`a`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
/// progressively enhanced to use client-side routing.
#[derive(TypedBuilder)]
pub struct AProps<H>
where
H: ToHref + 'static,
{
/// Used to calculate the link's `href` attribute. Will be resolved relative
/// to the current route.
pub href: H,
/// If `true`, the link is marked active when the location matches exactly;
/// if false, link is marked active if the current route starts with it.
#[builder(default)]
pub exact: bool,
/// An object of any type that will be pushed to router state
#[builder(default, setter(strip_option))]
pub state: Option<State>,
/// If `true`, the link will not add to the browser's history (so, pressing `Back`
/// will skip this page.)
#[builder(default)]
pub replace: bool,
/// Sets the `class` attribute on the underlying `<a>` tag, making it easier to style.
#[builder(default, setter(strip_option, into))]
pub class: Option<MaybeSignal<String>>,
/// The nodes or elements to be shown inside the link.
pub children: Box<dyn Fn(Scope) -> Fragment>
}
/// An HTML [`a`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
/// progressively enhanced to use client-side routing.
#[allow(non_snake_case)]
pub fn A<H>(cx: Scope, props: AProps<H>) -> impl IntoView
#[component]
pub fn A<H>(
cx: Scope,
/// Used to calculate the link's `href` attribute. Will be resolved relative
/// to the current route.
href: H,
/// If `true`, the link is marked active when the location matches exactly;
/// if false, link is marked active if the current route starts with it.
#[prop(optional)]
exact: bool,
/// An object of any type that will be pushed to router state
#[prop(optional)]
state: Option<State>,
/// If `true`, the link will not add to the browser's history (so, pressing `Back`
/// will skip this page.)
#[prop(optional)]
replace: bool,
/// Sets the `class` attribute on the underlying `<a>` tag, making it easier to style.
#[prop(optional, into)]
class: Option<MaybeSignal<String>>,
/// The nodes or elements to be shown inside the link.
children: Box<dyn Fn(Scope) -> Fragment>,
) -> impl IntoView
where
H: ToHref + 'static,
{
let location = use_location(cx);
let href = use_resolved_path(cx, move || props.href.to_href()());
let href = use_resolved_path(cx, move || href.to_href()());
let is_active = create_memo(cx, move |_| match href.get() {
None => false,
@ -87,7 +79,7 @@ where
.unwrap_or_default()
.to_lowercase();
let loc = location.pathname.get().to_lowercase();
if props.exact {
if exact {
loc == path
} else {
loc.starts_with(&path)
@ -95,33 +87,29 @@ where
}
});
let class = props.class;
Component::new("A", move |cx| {
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
view! { cx,
<a
href=move || href.get().unwrap_or_default()
prop:state={props.state.map(|s| s.to_js_value())}
prop:replace={props.replace}
aria-current=move || if is_active.get() { Some("page") } else { None }
class=move || class.as_ref().map(|class| class.get())
>
{move || (props.children)(cx)}
</a>
}
} else {
view! { cx,
<a
href=move || href().unwrap_or_default()
aria-current=move || if is_active() { Some("page") } else { None }
class=move || class.as_ref().map(|class| class.get())
>
{move || (props.children)(cx)}
</a>
}
cfg_if! {
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
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=move || class.as_ref().map(|class| class.get())
>
{children(cx)}
</a>
}
} else {
view! { cx,
<a
href=move || href().unwrap_or_default()
aria-current=move || if is_active() { Some("page") } else { None }
class=move || class.as_ref().map(|class| class.get())
>
{move || (props.children)(cx)}
</a>
}
}
}).into_view(cx)
}
}