Skip to main content

dioxus_swdir_tree/
view.rs

1//! The flagship [`DirectoryTreeView`] Dioxus component.
2
3use dioxus::prelude::*;
4use dioxus_swdir_tree_core::keyboard::{self, Modifiers as CoreMods, TreeKey};
5use dioxus_swdir_tree_core::{DirectoryTree, DragMsg};
6
7use crate::event::DirectoryTreeEvent;
8use crate::row::{ArcTheme, TreeRow, default_theme};
9use crate::style as s;
10
11/// A lazy-loading, filterable, keyboard-navigable, drag-and-drop,
12/// search-aware directory-tree explorer widget for Dioxus.
13///
14/// # Props
15///
16/// | Prop | Type | Description |
17/// |---|---|---|
18/// | `tree` | `Signal<DirectoryTree>` | The tree state. |
19/// | `on_event` | `EventHandler<DirectoryTreeEvent>` | Receives every interaction. |
20/// | `theme` | `Option<ArcTheme>` | Icon theme; defaults to `UnicodeTheme` (or `LucideTheme` with the `icons` feature). |
21///
22/// See the crate README for a full wiring example.
23#[component]
24pub fn DirectoryTreeView(
25    tree: Signal<DirectoryTree>,
26    on_event: EventHandler<DirectoryTreeEvent>,
27    #[props(optional)] theme: Option<ArcTheme>,
28) -> Element {
29    let theme = theme.unwrap_or_else(default_theme);
30
31    let t = tree.read();
32    let rows: Vec<(dioxus_swdir_tree_core::TreeNode, u32)> = t
33        .visible_rows()
34        .into_iter()
35        .map(|(node, depth)| (node.clone(), depth))
36        .collect();
37    let drag = t.drag_state().cloned();
38    let drag_active = drag.is_some();
39    drop(t);
40
41    #[cfg(feature = "default-style")]
42    let default_style_css = Some(s::DEFAULT_CSS);
43    #[cfg(not(feature = "default-style"))]
44    let default_style_css: Option<&str> = None;
45
46    let on_keydown = move |evt: KeyboardEvent| {
47        let tree_key = match evt.key() {
48            Key::ArrowUp => TreeKey::Up,
49            Key::ArrowDown => TreeKey::Down,
50            Key::Home => TreeKey::Home,
51            Key::End => TreeKey::End,
52            Key::Enter => TreeKey::Enter,
53            Key::ArrowLeft => TreeKey::Left,
54            Key::ArrowRight => TreeKey::Right,
55            Key::Escape => TreeKey::Escape,
56            Key::Character(ref s) if s == " " => TreeKey::Space,
57            _ => return,
58        };
59        let mods = CoreMods {
60            shift: evt.modifiers().shift(),
61            ctrl: evt.modifiers().ctrl(),
62        };
63        if let Some(event) = keyboard::handle_key(&tree.read(), tree_key, mods) {
64            evt.prevent_default();
65            on_event.call(event);
66        }
67    };
68
69    let on_container_mouseup = move |_evt: MouseEvent| {
70        if drag_active {
71            on_event.call(DirectoryTreeEvent::Drag(DragMsg::Cancelled));
72        }
73    };
74
75    rsx! {
76        if let Some(css) = default_style_css {
77            style { "{css}" }
78        }
79
80        if let Some(ref d) = drag {
81            div {
82                style: "
83                    position: fixed; bottom: 1rem; left: 50%;
84                    transform: translateX(-50%);
85                    background: rgba(0,0,0,0.75); color: #fff;
86                    padding: 0.2rem 0.6rem; border-radius: 4px;
87                    font-size: 0.75rem; pointer-events: none; z-index: 999;
88                ",
89                "Dragging {d.sources.len()} item(s)"
90            }
91        }
92
93        div {
94            class: s::CLASS_TREE,
95            tabindex: "0",
96            onkeydown: on_keydown,
97            onmouseup: on_container_mouseup,
98
99            for (node, depth) in rows {
100                TreeRow {
101                    key: "{node.path.display()}",
102                    node,
103                    depth,
104                    on_event,
105                    drag: drag.clone(),
106                    theme: theme.clone(),
107                }
108            }
109        }
110    }
111}