use super::*;
#[component]
pub fn euv_sidebar(node: VirtualNode<EuvSidebarProps>) -> VirtualNode {
let EuvSidebarProps {
route_signal,
collapsed,
items,
prefix,
on_navigate,
}: EuvSidebarProps = node.try_get_props().unwrap_or_default();
html! {
div {
for item in items.iter() {
euv_sidebar_item {
route_signal
collapsed
item: *item
prefix: prefix.clone()
on_navigate: on_navigate.clone()
}
}
}
}
}
#[component]
pub fn euv_sidebar_item(node: VirtualNode<EuvSidebarItemProps>) -> VirtualNode {
let EuvSidebarItemProps {
route_signal,
collapsed,
item,
prefix,
on_navigate,
}: EuvSidebarItemProps = node.try_get_props().unwrap_or_default();
let route: String = route_signal.get();
let path: &str = strip_hash_anchor(&route);
if item.children.is_empty() {
let Some(link) = item.link else {
return html! {
""
};
};
let active: bool = path == link;
let link_class: fn() -> &'static Css = if active {
c_euv_sidebar_link_active
} else {
c_euv_sidebar_link
};
return html! {
a {
class: {
link_class()
}
href: format!("#{link}")
onclick: navigate_link(on_navigate.clone(), link)
{
item.text
}
}
};
}
let key: String = format!("{prefix}/{}", item.text);
let open: bool = !collapsed.get().contains(&key);
let arrow_class: fn() -> &'static Css = if open {
c_euv_sidebar_group_arrow_open
} else {
c_euv_sidebar_group_arrow
};
let title_node: VirtualNode = match item.link {
Some(link) => html! {
a {
href: format!("#{link}")
onclick: navigate_group_link(on_navigate.clone(), link)
{
item.text
}
}
},
None => html! {
{
item.text
}
},
};
html! {
div {
class: c_euv_sidebar_group()
div {
class: c_euv_sidebar_group_title()
onclick: toggle_group(collapsed, key.clone())
span {
title_node
}
span {
class: arrow_class()
"▸"
}
}
if open {
div {
class: c_euv_sidebar_children()
euv_sidebar {
route_signal
collapsed
items: item.children
prefix: key.clone()
on_navigate: on_navigate.clone()
}
}
}
}
}
}
fn navigate_link(
on_navigate: Option<Rc<dyn Fn(&'static str)>>,
link: &'static str,
) -> Option<Rc<dyn Fn(Event)>> {
match on_navigate {
Some(interceptor) => Some(Rc::new(move |event: Event| {
event.prevent_default();
interceptor(link);
})),
None => Some(Rc::new(move |event: Event| {
event.prevent_default();
Router::navigate(link);
})),
}
}
fn navigate_group_link(
on_navigate: Option<Rc<dyn Fn(&'static str)>>,
link: &'static str,
) -> Option<Rc<dyn Fn(Event)>> {
Some(Rc::new(move |event: Event| {
event.prevent_default();
event.stop_propagation();
match &on_navigate {
Some(interceptor) => interceptor(link),
None => Router::navigate(link),
}
}))
}
fn toggle_group(collapsed: Signal<Vec<String>>, key: String) -> Option<Rc<dyn Fn(Event)>> {
Some(Rc::new(move |_| {
let mut keys: Vec<String> = collapsed.get();
if let Some(index) = keys.iter().position(|k| k == &key) {
keys.remove(index);
} else {
keys.push(key.clone());
}
collapsed.set(keys);
}))
}
fn strip_hash_anchor(route: &str) -> &str {
match route.split_once('#') {
Some((path, _)) => path,
None => route,
}
}