Skip to main content

orbital_base_components/tree/state/
mod.rs

1//! Tree-specific state extensions and backward-compatible aliases for collection state.
2
3pub use crate::collection::state::{
4    checkbox_state, collection_keyboard_action, CollectionCheckboxState, CollectionExpansionState,
5    CollectionFocusState, CollectionItemDomRegistry, CollectionKeyboardAction, CollectionRegistry,
6    CollectionRegistryEntry, CollectionSelectionMode, CollectionSelectionState, CollectionState,
7    ExpansionTrigger,
8};
9
10pub type TreeCheckboxState = CollectionCheckboxState;
11pub type TreeExpansionState = CollectionExpansionState;
12pub type TreeFocusState = CollectionFocusState;
13pub type TreeItemDomRegistry = CollectionItemDomRegistry;
14pub type TreeKeyboardAction = CollectionKeyboardAction;
15pub type TreeItemRegistry = CollectionRegistry;
16pub type TreeRegistryEntry = CollectionRegistryEntry;
17pub type TreeSelectionMode = CollectionSelectionMode;
18pub type TreeSelectionState = CollectionSelectionState;
19
20pub fn tree_keyboard_action(event: &leptos::ev::KeyboardEvent) -> TreeKeyboardAction {
21    collection_keyboard_action(event)
22}
23
24use leptos::{ev, prelude::*};
25use std::collections::HashSet;
26
27use crate::signals::SignalModel;
28use crate::tree::dnd::TreeDragState;
29use crate::tree::types::TreeSize;
30use crate::Handler;
31
32/// Tree behavior state: shared collection core plus tree-only fields.
33#[derive(Clone)]
34pub struct TreeState {
35    pub expansion: TreeExpansionState,
36    pub selection: TreeSelectionState,
37    pub focus: TreeFocusState,
38    pub registry: TreeItemRegistry,
39    pub dom_registry: TreeItemDomRegistry,
40    pub size: Signal<TreeSize>,
41    pub disabled_items: SignalModel<HashSet<String>>,
42    pub disabled_items_focusable: Signal<bool>,
43    pub editable: Signal<bool>,
44    pub editable_items: SignalModel<HashSet<String>>,
45    pub reorderable: Signal<bool>,
46    pub on_item_click: Option<Handler<(String, ev::MouseEvent)>>,
47    pub on_label_change: Option<Handler<(String, String)>>,
48    pub on_reorder: Option<Handler<(String, String, usize)>>,
49    pub drag_state: TreeDragState,
50}
51
52impl TreeState {
53    pub fn from_collection(collection: CollectionState, tree_fields: TreeStateFields) -> Self {
54        Self {
55            expansion: collection.expansion,
56            selection: collection.selection,
57            focus: collection.focus,
58            registry: collection.registry,
59            dom_registry: collection.dom_registry,
60            disabled_items: collection.disabled_items,
61            disabled_items_focusable: collection.disabled_items_focusable,
62            on_item_click: collection.on_item_click,
63            size: tree_fields.size,
64            editable: tree_fields.editable,
65            editable_items: tree_fields.editable_items,
66            reorderable: tree_fields.reorderable,
67            on_label_change: tree_fields.on_label_change,
68            on_reorder: tree_fields.on_reorder,
69            drag_state: tree_fields.drag_state,
70        }
71    }
72
73    pub fn collection(&self) -> CollectionState {
74        CollectionState {
75            expansion: self.expansion.clone(),
76            selection: self.selection.clone(),
77            focus: self.focus.clone(),
78            registry: self.registry.clone(),
79            dom_registry: self.dom_registry.clone(),
80            disabled_items: self.disabled_items.clone(),
81            disabled_items_focusable: self.disabled_items_focusable,
82            on_item_click: self.on_item_click.clone(),
83        }
84    }
85
86    pub fn is_item_disabled(&self, item_id: &str) -> bool {
87        self.disabled_items.with(|items| items.contains(item_id))
88    }
89
90    pub fn is_item_editable(&self, item_id: &str) -> bool {
91        if !self.editable.get() {
92            return false;
93        }
94        self.editable_items
95            .with(|items| items.is_empty() || items.contains(item_id))
96    }
97
98    pub fn is_item_open(&self, item_id: &str) -> bool {
99        self.expansion.is_open(item_id)
100    }
101
102    pub fn is_item_selected(&self, item_id: &str) -> bool {
103        self.selection.is_selected(item_id)
104    }
105
106    pub fn visible_ordered_ids(&self) -> Vec<String> {
107        let expansion = self.expansion.clone();
108        self.registry
109            .visible_ordered_ids(move |id| expansion.is_open(id))
110    }
111}
112
113#[derive(Clone)]
114pub struct TreeStateFields {
115    pub size: Signal<TreeSize>,
116    pub editable: Signal<bool>,
117    pub editable_items: SignalModel<HashSet<String>>,
118    pub reorderable: Signal<bool>,
119    pub on_label_change: Option<Handler<(String, String)>>,
120    pub on_reorder: Option<Handler<(String, String, usize)>>,
121    pub drag_state: TreeDragState,
122}
123
124#[derive(Clone)]
125pub struct TreeStateInjection(pub TreeState);
126
127impl TreeStateInjection {
128    pub fn expect_context() -> TreeState {
129        expect_context::<Self>().0
130    }
131
132    pub fn use_context() -> Option<TreeState> {
133        use_context::<Self>().map(|injection| injection.0)
134    }
135}