use rnk::components::{TreeNode, TreeState, TreeStyle};
fn main() {
println!("=== Tree Component Demo ===\n");
let tree = create_sample_tree();
println!("--- TreeNode API ---");
println!("TreeNode::new(id, label) - Create a node");
println!("TreeNode::leaf(id, label) - Create a leaf node");
println!("TreeNode::with_data(id, label, data) - Create with data");
println!(".child(node) - Add a child");
println!(".children(iter) - Add multiple children");
println!();
println!("Sample tree structure:");
println!(" node_count: {}", tree.node_count());
println!(" is_leaf: {}", tree.is_leaf());
println!(" has_children: {}", tree.has_children());
println!();
println!("--- TreeState API ---");
let mut state = TreeState::new(&tree);
println!("TreeState::new(&tree):");
println!(" visible_count: {} (only root)", state.visible_count());
println!(" cursor: {}", state.cursor());
println!(" focused: {:?}", state.focused());
println!();
state.expand("root");
state.rebuild_visible(&tree);
println!("After expand(\"root\"):");
println!(" visible_count: {}", state.visible_count());
println!();
state.expand_all(&tree);
state.rebuild_visible(&tree);
println!("After expand_all():");
println!(" visible_count: {} (all nodes)", state.visible_count());
println!();
println!("--- Navigation ---");
state.cursor_down();
println!("cursor_down(): focused = {:?}", state.focused());
state.cursor_down();
state.cursor_down();
println!("cursor_down() x2: focused = {:?}", state.focused());
state.cursor_up();
println!("cursor_up(): focused = {:?}", state.focused());
state.cursor_last();
println!("cursor_last(): focused = {:?}", state.focused());
state.cursor_first();
println!("cursor_first(): focused = {:?}", state.focused());
println!();
println!("--- Toggle Expand/Collapse ---");
state.toggle("src");
state.rebuild_visible(&tree);
println!("toggle(\"src\"): visible_count = {}", state.visible_count());
state.toggle("src");
state.rebuild_visible(&tree);
println!(
"toggle(\"src\") again: visible_count = {}",
state.visible_count()
);
println!();
println!("--- Style Presets ---");
println!();
println!("TreeStyle::default() (arrows):");
print_tree_visual(
&tree,
&TreeState::all_expanded(&tree),
&TreeStyle::default(),
);
println!("\nTreeStyle::folder_icons():");
print_tree_visual(
&tree,
&TreeState::all_expanded(&tree),
&TreeStyle::folder_icons(),
);
println!("\nTreeStyle::plus_minus_icons():");
print_tree_visual(
&tree,
&TreeState::all_expanded(&tree),
&TreeStyle::plus_minus_icons(),
);
println!("\nTreeStyle::minimal() (no lines):");
print_tree_visual(
&tree,
&TreeState::all_expanded(&tree),
&TreeStyle::minimal(),
);
println!("\n--- Collapsed View ---");
let mut state = TreeState::new(&tree);
state.expand("root");
state.rebuild_visible(&tree);
print_tree_visual(&tree, &state, &TreeStyle::default());
println!("\n--- Usage in TUI App ---");
println!("```rust");
println!("use rnk::components::{{Tree, TreeNode, TreeState, handle_tree_input}};");
println!("use rnk::hooks::{{use_signal, use_input}};");
println!();
println!("fn app() -> Element {{");
println!(" let tree = TreeNode::new(\"root\", \"Project\")");
println!(" .child(TreeNode::new(\"src\", \"src\")");
println!(" .child(TreeNode::leaf(\"main\", \"main.rs\")))");
println!(" .child(TreeNode::leaf(\"cargo\", \"Cargo.toml\"));");
println!();
println!(" let state = use_signal(|| TreeState::with_root_expanded(&tree));");
println!();
println!(" use_input(move |input, key| {{");
println!(" let mut s = state.get();");
println!(" if handle_tree_input(&mut s, &tree, input, key) {{");
println!(" state.set(s);");
println!(" }}");
println!(" }});");
println!();
println!(" Tree::new(&tree, &state.get())");
println!(" .style(TreeStyle::folder_icons())");
println!(" .into_element()");
println!("}}");
println!("```");
}
fn create_sample_tree() -> TreeNode<()> {
TreeNode::new("root", "my-project")
.child(
TreeNode::new("src", "src")
.child(
TreeNode::new("components", "components")
.child(TreeNode::leaf("button", "button.rs"))
.child(TreeNode::leaf("input", "input.rs"))
.child(TreeNode::leaf("mod", "mod.rs")),
)
.child(TreeNode::leaf("lib", "lib.rs"))
.child(TreeNode::leaf("main", "main.rs")),
)
.child(
TreeNode::new("tests", "tests")
.child(TreeNode::leaf("integration", "integration_test.rs")),
)
.child(TreeNode::leaf("cargo", "Cargo.toml"))
.child(TreeNode::leaf("readme", "README.md"))
}
fn print_tree_visual(tree: &TreeNode<()>, state: &TreeState, style: &TreeStyle) {
print_node(tree, state, style, 0, vec![]);
}
fn print_node(
node: &TreeNode<()>,
state: &TreeState,
style: &TreeStyle,
depth: usize,
parent_is_last: Vec<bool>,
) {
let is_expanded = state.is_expanded(&node.id);
let is_focused = state.focused() == Some(&node.id);
let mut prefix = String::new();
if style.show_lines && depth > 0 {
for &is_last in &parent_is_last[..parent_is_last.len().saturating_sub(1)] {
if is_last {
prefix.push_str(" ");
} else {
prefix.push_str(&style.vertical_line);
}
}
if let Some(&is_last) = parent_is_last.last() {
if is_last {
prefix.push_str(&style.last_connector);
} else {
prefix.push_str(&style.connector);
}
}
} else {
prefix = " ".repeat(depth);
}
let icon = if node.is_leaf() {
&style.leaf_icon
} else if is_expanded {
&style.expanded_icon
} else {
&style.collapsed_icon
};
let (color_start, color_end) = if is_focused {
("\x1b[1;36m", "\x1b[0m")
} else if style.icon_color.is_some() {
("\x1b[36m", "\x1b[0m")
} else {
("", "")
};
println!(
" {}{}{} {}{}",
prefix, color_start, icon, node.label, color_end
);
if is_expanded {
let child_count = node.children.len();
for (i, child) in node.children.iter().enumerate() {
let is_last = i == child_count - 1;
let mut child_is_last = parent_is_last.clone();
child_is_last.push(is_last);
print_node(child, state, style, depth + 1, child_is_last);
}
}
}