use leptos::prelude::*;
use crate::topology::{BrowserError, NodeId, NodeView, TopologyProvider};
use crate::ui::browser_view::BrowserView;
use crate::ui::column::ColumnSizeConfig;
use crate::ui::icons::{IconRenderer, container_leaf_icon_renderer};
use crate::ui::state::DrillPath;
#[allow(unreachable_pub, clippy::needless_pass_by_value)]
#[component]
pub fn ColumnBrowser<P>(
provider: P,
root_id: NodeId,
#[prop(default = 3)]
visible_cols: usize,
on_open: Callback<String>,
#[prop(default = container_leaf_icon_renderer())]
icon_renderer: IconRenderer,
#[prop(default = ColumnSizeConfig::default())]
size_config: ColumnSizeConfig,
#[prop(optional)]
external_path: Option<RwSignal<DrillPath>>,
) -> impl IntoView
where
P: TopologyProvider + Clone + 'static,
{
let internal_path = RwSignal::new(DrillPath::empty());
let path_signal: RwSignal<DrillPath> = external_path.unwrap_or(internal_path);
let (columns, set_columns) = signal(Vec::<Vec<NodeView>>::new());
let (error, set_error) = signal(Option::<BrowserError>::None);
let provider_root = provider.clone();
let root_id_clone = root_id.clone();
Effect::new(move |_| {
let p = provider_root.clone();
let rid = root_id_clone.clone();
leptos::task::spawn_local(async move {
match p.get_children(&rid).await {
Ok(nodes) => {
let views: Vec<NodeView> = nodes.into_iter().map(NodeView::from).collect();
set_columns.update(|cols| {
cols.clear();
cols.push(views);
});
}
Err(e) => set_error.set(Some(e)),
}
});
});
let on_select = Callback::new(move |(col_idx, id_str): (usize, String)| {
let new_path = path_signal
.get_untracked()
.select(col_idx, NodeId::parse(&id_str));
let depth = new_path.depth();
let parent_id = new_path.segments[depth - 1].clone();
path_signal.set(new_path);
let p = provider.clone();
leptos::task::spawn_local(async move {
match p.get_children(&parent_id).await {
Ok(nodes) => {
let views: Vec<NodeView> = nodes.into_iter().map(NodeView::from).collect();
set_columns.update(|cols| {
cols.truncate(depth);
cols.push(views);
});
}
Err(e) => set_error.set(Some(e)),
}
});
});
let on_pop = Callback::new(move |()| {
let popped = path_signal.get_untracked().pop();
let new_depth = popped.depth();
path_signal.set(popped);
set_columns.update(|cols| {
cols.truncate(new_depth + 1);
});
});
view! {
{move || error.get().map(|e| view! {
<div class="lcb-error" role="alert">{e.to_string()}</div>
})}
<BrowserView
columns_data=columns.into()
path=path_signal.into()
visible_cols=visible_cols
on_select=on_select
on_open=on_open
on_pop=on_pop
icon_renderer=icon_renderer.clone()
size_config=size_config
/>
}
}