Skip to main content

orbital_base_components/navigation/
context.rs

1use leptos::prelude::*;
2use std::collections::HashSet;
3
4use crate::collection::state::CollectionState;
5use crate::signals::SignalModel;
6
7use super::collection::{
8    build_navigation_collection, sync_open_categories_to_vec, sync_option_to_selected_value,
9    sync_selected_value_to_option, sync_vec_to_open_categories,
10};
11use super::types::NavigationDensity;
12
13#[derive(Clone)]
14pub struct NavigationInjection {
15    pub collection: CollectionState,
16    open_categories: RwSignal<Vec<String>>,
17    selected_value: RwSignal<Option<String>>,
18    open_set: SignalModel<HashSet<String>>,
19    selected_set: SignalModel<HashSet<String>>,
20    pub selected_category_value: RwSignal<Option<String>>,
21    pub multiple: Signal<bool>,
22    pub density: Signal<NavigationDensity>,
23    pub collapsed: RwSignal<bool>,
24    pub open: Signal<bool>,
25}
26
27impl NavigationInjection {
28    pub fn new(
29        selected_value: RwSignal<Option<String>>,
30        selected_category_value: RwSignal<Option<String>>,
31        open_categories: RwSignal<Vec<String>>,
32        multiple: Signal<bool>,
33        density: Signal<NavigationDensity>,
34        collapsed: RwSignal<bool>,
35        open: Signal<bool>,
36    ) -> Self {
37        let open_set = SignalModel::new(open_categories.get_untracked().into_iter().collect());
38        let selected_set = SignalModel::new(selected_value.get_untracked().into_iter().collect());
39        let collection = build_navigation_collection(open_set.clone(), selected_set.clone());
40
41        let injection = Self {
42            collection,
43            open_categories,
44            selected_value,
45            open_set,
46            selected_set,
47            selected_category_value,
48            multiple,
49            density,
50            collapsed,
51            open,
52        };
53        injection.install_sync_effects();
54        injection
55    }
56
57    fn install_sync_effects(&self) {
58        let open_categories = self.open_categories;
59        let selected_value = self.selected_value;
60        let open_set = self.open_set.clone();
61        let selected_set = self.selected_set.clone();
62
63        Effect::new(move |_| {
64            sync_vec_to_open_categories(&open_categories, &open_set);
65        });
66        Effect::new(move |_| {
67            sync_option_to_selected_value(&selected_value, &selected_set);
68        });
69    }
70
71    pub fn expect_context() -> Self {
72        expect_context()
73    }
74
75    pub fn selected_value(&self) -> RwSignal<Option<String>> {
76        self.selected_value
77    }
78
79    pub fn open_categories(&self) -> RwSignal<Vec<String>> {
80        self.open_categories
81    }
82
83    pub fn sync_selected_from_collection(&self) {
84        sync_selected_value_to_option(&self.selected_value, &self.selected_set);
85    }
86
87    pub fn is_selected_category(&self, value: &str) -> bool {
88        self.selected_category_value
89            .with(|selected_category_value| selected_category_value.as_deref() == Some(value))
90    }
91
92    pub fn is_category_open(&self, value: &str) -> bool {
93        self.collection.expansion.is_open(value)
94    }
95
96    pub fn on_request_category_toggle(&self, category_value: String) {
97        let is_open = self.collection.expansion.is_open(&category_value);
98        if self.multiple.get_untracked() {
99            self.collection
100                .expansion
101                .set_expansion(category_value, !is_open);
102        } else if is_open {
103            self.collection
104                .expansion
105                .set_expansion(category_value.clone(), false);
106        } else {
107            self.open_set.update(|open| {
108                open.clear();
109                open.insert(category_value.clone());
110            });
111            self.collection
112                .expansion
113                .set_expansion(category_value, true);
114        }
115        sync_open_categories_to_vec(&self.open_categories, &self.open_set);
116    }
117}
118
119#[derive(Clone, Copy)]
120pub struct NavigationCategoryInjection {
121    pub value: Signal<String>,
122}
123
124impl NavigationCategoryInjection {
125    pub fn use_context() -> Option<Self> {
126        use_context()
127    }
128}
129
130#[derive(Clone, Copy)]
131pub struct NavigationSubItemGroupInjection;
132
133impl NavigationSubItemGroupInjection {
134    pub fn use_context() -> Option<Self> {
135        use_context()
136    }
137}