orbital_core_components/tree/
api.rs1use leptos::prelude::*;
2use orbital_base_components::{
3 apply_sibling_dom_reorder, install_tree_drag_listeners, TreeDropPosition, TreeExpansionState,
4 TreeFocusState, TreeItemDomRegistry, TreeItemRegistry, TreeSelectionState, TreeState,
5};
6
7use super::types::{TreeAppearance, TreeBehavior, TreeExpansion, TreeSelection};
8
9#[derive(Clone, Default)]
10pub struct TreeApiRef(RwSignal<Option<TreeApiHandle>>);
11
12#[derive(Clone)]
13pub struct TreeApiHandle {
14 state: TreeState,
15}
16
17impl TreeApiRef {
18 pub fn new() -> Self {
19 Self(RwSignal::new(None))
20 }
21
22 pub fn load(&self, handle: TreeApiHandle) {
23 self.0.set(Some(handle));
24 }
25
26 pub fn with<F, R>(&self, f: F) -> Option<R>
27 where
28 F: FnOnce(&TreeApiHandle) -> R,
29 {
30 self.0.with(|handle| handle.as_ref().map(f))
31 }
32}
33
34impl TreeApiHandle {
35 pub fn from_state(state: TreeState) -> Self {
36 Self { state }
37 }
38
39 pub fn get_item(&self, item_id: &str) -> Option<orbital_base_components::TreeRegistryEntry> {
40 self.state.registry.get_entry(item_id)
41 }
42
43 pub fn get_item_dom_element(&self, item_id: &str) -> Option<web_sys::HtmlElement> {
44 self.state.dom_registry.get_dom_element(item_id)
45 }
46
47 pub fn get_item_tree(&self) -> Vec<(String, Vec<String>)> {
48 self.state.registry.item_tree()
49 }
50
51 pub fn get_item_ordered_children_ids(&self, item_id: &str) -> Vec<String> {
52 self.state.registry.ordered_children_ids(item_id)
53 }
54
55 pub fn select_item(
56 &self,
57 item_id: String,
58 keep_existing: bool,
59 should_be_selected: Option<bool>,
60 shift_range: Option<Vec<String>>,
61 ) {
62 self.state.selection.select_item(
63 item_id,
64 keep_existing,
65 should_be_selected,
66 shift_range,
67 Some(&self.state.registry),
68 );
69 }
70
71 pub fn set_item_expansion(&self, item_id: String, expanded: bool) {
72 self.state.expansion.set_expansion(item_id, expanded);
73 }
74
75 pub fn focus_item(&self, item_id: String) {
76 self.state.focus.focus_item(item_id.clone());
77 self.state.dom_registry.focus_dom_element(&item_id);
78 }
79
80 pub fn update_item_label(&self, item_id: String, label: String) {
81 self.state.registry.update_label(&item_id, label);
82 }
83}
84
85pub fn build_tree_state(
86 expansion: TreeExpansion,
87 selection: TreeSelection,
88 behavior: TreeBehavior,
89 appearance: TreeAppearance,
90) -> TreeState {
91 let mut expansion_state = TreeExpansionState::new(expansion.open_items)
92 .with_default_open(expansion.default_open)
93 .with_expansion_trigger(expansion.expansion_trigger);
94 if let Some(handler) = expansion.on_expansion_toggle {
95 expansion_state = expansion_state.with_on_expansion_toggle(handler);
96 }
97
98 let mut selection_state = TreeSelectionState::new(selection.mode, selection.selected_items)
99 .with_default_selected(selection.default_selected)
100 .with_cascade(selection.cascade);
101 if let Some(handler) = selection.on_select {
102 selection_state = selection_state.with_on_select(handler);
103 }
104 if let Some(handler) = selection.on_selection_toggle {
105 selection_state = selection_state.with_on_selection_toggle(handler);
106 }
107
108 TreeState {
109 expansion: expansion_state,
110 selection: selection_state,
111 focus: TreeFocusState::new(),
112 registry: TreeItemRegistry::new(),
113 dom_registry: TreeItemDomRegistry::new(),
114 size: appearance.size,
115 disabled_items: behavior.disabled_items,
116 disabled_items_focusable: behavior.disabled_items_focusable,
117 editable: behavior.editable,
118 editable_items: behavior.editable_items,
119 reorderable: behavior.reorderable,
120 on_item_click: behavior.on_item_click,
121 on_label_change: behavior.on_label_change,
122 on_reorder: behavior.on_reorder,
123 drag_state: orbital_base_components::TreeDragState::new(),
124 }
125}
126
127pub fn install_tree_reorder_listeners(
128 state: TreeState,
129) -> orbital_base_components::TreeDragListenerHandle {
130 let reorderable = state.reorderable;
131 let on_reorder = state.on_reorder.clone();
132 let registry = state.registry.clone();
133 let dom_registry = state.dom_registry.clone();
134 let drag_state = state.drag_state.clone();
135 install_tree_drag_listeners(
136 drag_state,
137 reorderable,
138 Callback::new(
139 move |(source_id, target_id, position): (String, String, TreeDropPosition)| {
140 let order = match position {
141 TreeDropPosition::Before => 0,
142 TreeDropPosition::After => 1,
143 };
144 if let Some(handler) = &on_reorder {
145 handler.run((source_id.clone(), target_id.clone(), order));
146 } else if registry.reorder_siblings(&source_id, &target_id, order) {
147 apply_sibling_dom_reorder(&dom_registry, &source_id, &target_id, order);
148 }
149 },
150 ),
151 )
152}