orbital_base_components/navigation/
context.rs1use 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}