mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
feat: add active_class
prop on <A/>
(#1323)
This commit is contained in:
parent
d4b1ceda90
commit
e9665b34e5
2 changed files with 94 additions and 16 deletions
|
@ -289,4 +289,4 @@ fn None(cx: Scope) -> impl IntoView {
|
|||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{use_location, use_resolved_path, State};
|
||||
use leptos::{leptos_dom::IntoView, *};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Describes a value that is either a static or a reactive URL, i.e.,
|
||||
/// a [String], a [&str], or a reactive `Fn() -> String`.
|
||||
|
@ -56,6 +57,16 @@ pub fn A<H>(
|
|||
/// if false, link is marked active if the current route starts with it.
|
||||
#[prop(optional)]
|
||||
exact: bool,
|
||||
/// Provides a class to be added when the link is active. If provided, it will
|
||||
/// be added at the same time that the `aria-current` attribute is set.
|
||||
///
|
||||
/// This supports multiple space-separated class names.
|
||||
///
|
||||
/// **Performance**: If it’s possible to style the link using the CSS with the
|
||||
/// `[aria-current=page]` selector, you should prefer that, as it enables significant
|
||||
/// SSR optimizations.
|
||||
#[prop(optional, into)]
|
||||
active_class: Option<Cow<'static, str>>,
|
||||
/// An object of any type that will be pushed to router state
|
||||
#[prop(optional)]
|
||||
state: Option<State>,
|
||||
|
@ -83,12 +94,13 @@ where
|
|||
cx: Scope,
|
||||
href: Memo<Option<String>>,
|
||||
exact: bool,
|
||||
state: Option<State>,
|
||||
replace: bool,
|
||||
#[allow(unused)] state: Option<State>,
|
||||
#[allow(unused)] replace: bool,
|
||||
class: Option<AttributeValue>,
|
||||
#[allow(unused)] active_class: Option<Cow<'static, str>>,
|
||||
id: Option<String>,
|
||||
children: Children,
|
||||
) -> HtmlElement<leptos::html::A> {
|
||||
) -> View {
|
||||
#[cfg(not(any(feature = "hydrate", feature = "csr")))]
|
||||
{
|
||||
_ = state;
|
||||
|
@ -118,20 +130,86 @@ where
|
|||
}
|
||||
});
|
||||
|
||||
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
|
||||
id=id
|
||||
>
|
||||
{children(cx)}
|
||||
</a>
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
// if we have `active_class`, the SSR optimization doesn't play nicely
|
||||
// so we use the builder instead
|
||||
if let Some(active_class) = active_class {
|
||||
let mut a = leptos::html::a(cx)
|
||||
.attr("href", move || href.get().unwrap_or_default())
|
||||
.attr("aria-current", move || {
|
||||
if is_active.get() {
|
||||
Some("page")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.attr(
|
||||
"class",
|
||||
class.map(|class| class.into_attribute_boxed(cx)),
|
||||
);
|
||||
|
||||
for class_name in active_class.split_ascii_whitespace() {
|
||||
a = a.class(class_name.to_string(), move || is_active.get())
|
||||
}
|
||||
|
||||
a.attr("id", id).child(children(cx)).into_view(cx)
|
||||
}
|
||||
// but keep the nice SSR optimization in most cases
|
||||
else {
|
||||
view! { cx,
|
||||
<a
|
||||
href=move || href.get().unwrap_or_default()
|
||||
aria-current=move || if is_active.get() { Some("page") } else { None }
|
||||
class=class
|
||||
id=id
|
||||
>
|
||||
{children(cx)}
|
||||
</a>
|
||||
}
|
||||
.into_view(cx)
|
||||
}
|
||||
}
|
||||
|
||||
// the non-SSR version doesn't need the SSR optimizations
|
||||
// DRY here to avoid WASM binary size bloat
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
{
|
||||
let a = 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
|
||||
id=id
|
||||
>
|
||||
{children(cx)}
|
||||
</a>
|
||||
};
|
||||
if let Some(active_class) = active_class {
|
||||
let mut a = a;
|
||||
for class_name in active_class.split_ascii_whitespace() {
|
||||
a = a.class(class_name.to_string(), move || is_active.get())
|
||||
}
|
||||
a
|
||||
} else {
|
||||
a
|
||||
}
|
||||
.into_view(cx)
|
||||
}
|
||||
}
|
||||
|
||||
let href = use_resolved_path(cx, move || href.to_href()());
|
||||
inner(cx, href, exact, state, replace, class, id, children)
|
||||
inner(
|
||||
cx,
|
||||
href,
|
||||
exact,
|
||||
state,
|
||||
replace,
|
||||
class,
|
||||
active_class,
|
||||
id,
|
||||
children,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue