use super::*;
#[component]
pub fn euv_tree(node: VirtualNode<EuvTreeProps>) -> VirtualNode {
let EuvTreeProps {
nodes,
selected,
on_select,
default_expanded,
}: EuvTreeProps = node.try_get_props().unwrap_or_default();
let expanded: Signal<Vec<String>> = use_euv_tree_expanded(nodes.as_slice(), default_expanded);
let selected_value: String = selected.get();
let expanded_values: Vec<String> = expanded.get();
let tree_nodes: Vec<VirtualNode> = nodes
.into_iter()
.map(|tree_node: EuvTreeNode| {
tree_node_view(
tree_node,
selected,
&selected_value,
expanded,
&expanded_values,
on_select.clone(),
)
})
.collect();
html! {
div {
class: c_euv_tree()
tree_nodes
}
}
}
pub fn use_euv_tree_expanded(nodes: &[EuvTreeNode], default_expanded: bool) -> Signal<Vec<String>> {
let seed: Vec<String> = if default_expanded {
tree_node_values(nodes)
} else {
Vec::new()
};
HookContext::use_hook(move || Signal::create(seed))
}
fn tree_node_values(nodes: &[EuvTreeNode]) -> Vec<String> {
let mut values: Vec<String> = Vec::with_capacity(nodes.len());
collect_tree_node_values(nodes, &mut values);
values
}
fn collect_tree_node_values(nodes: &[EuvTreeNode], values: &mut Vec<String>) {
for node in nodes {
values.push(node.value.to_string());
collect_tree_node_values(&node.children, values);
}
}
fn tree_node_view(
node: EuvTreeNode,
selected: Signal<String>,
selected_value: &str,
expanded: Signal<Vec<String>>,
expanded_values: &[String],
on_select: Option<Rc<dyn Fn(&'static str)>>,
) -> VirtualNode {
let value: &'static str = node.value;
let label: &'static str = node.label;
let has_children: bool = !node.children.is_empty();
let is_selected: bool = value == selected_value;
let is_expanded: bool = expanded_values.iter().any(|entry: &String| entry == value);
let child_nodes: Vec<VirtualNode> = node
.children
.into_iter()
.map(|child: EuvTreeNode| {
tree_node_view(
child,
selected,
selected_value,
expanded,
expanded_values,
on_select.clone(),
)
})
.collect();
html! {
div {
key: value
class: c_euv_tree_node()
div {
class: if is_selected {
c_euv_tree_node_row_active()
} else {
c_euv_tree_node_row()
}
onclick: on_tree_select(selected, on_select.clone(), value)
span {
class: if !has_children {
c_euv_tree_node_toggle_leaf()
} else if is_expanded {
c_euv_tree_node_toggle_open()
} else {
c_euv_tree_node_toggle()
}
onclick: on_tree_toggle(expanded, value)
"▸"
}
span {
class: c_euv_tree_node_label()
label
}
}
if has_children {
div {
class: c_euv_tree_node_children()
class: if is_expanded {
c_euv_tree_node_children_open()
} else {
c_euv_tree_node_children_closed()
}
child_nodes
}
}
}
}
}
pub fn select_tree_node(
selected: Signal<String>,
on_select: Option<Rc<dyn Fn(&'static str)>>,
value: &'static str,
) {
selected.set(value.to_string());
if let Some(callback) = &on_select {
callback(value);
}
}
pub fn toggle_expanded_value(values: &mut Vec<String>, value: &str) {
if let Some(index) = values.iter().position(|entry: &String| entry == value) {
values.remove(index);
} else {
values.push(value.to_string());
}
}
fn on_tree_select(
selected: Signal<String>,
on_select: Option<Rc<dyn Fn(&'static str)>>,
value: &'static str,
) -> Option<Rc<dyn Fn(Event)>> {
Some(Rc::new(move |_: Event| {
select_tree_node(selected, on_select.clone(), value);
}))
}
fn on_tree_toggle(expanded: Signal<Vec<String>>, value: &'static str) -> Option<Rc<dyn Fn(Event)>> {
Some(Rc::new(move |event: Event| {
event.stop_propagation();
let mut values: Vec<String> = expanded.get();
toggle_expanded_value(&mut values, value);
expanded.set(values);
}))
}