1use dioxus::prelude::*;
2use std::collections::HashMap;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum OverlayKind {
11 Dropdown,
14 Tooltip,
16 Popup,
18 Message,
19 Notification,
20 Modal,
21 Drawer,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26pub struct OverlayKey(u64);
27
28impl OverlayKey {
29 pub fn as_u64(self) -> u64 {
31 self.0
32 }
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub struct OverlayMeta {
38 pub kind: OverlayKind,
39 pub z_index: i32,
40 pub has_mask: bool,
41}
42
43#[derive(Clone, Debug)]
49pub struct OverlayManager {
50 next_key: u64,
51 base_z_index: i32,
52 step: i32,
53 entries: HashMap<OverlayKey, OverlayMeta>,
54}
55
56impl Default for OverlayManager {
57 fn default() -> Self {
58 Self {
59 next_key: 1,
60 base_z_index: 1000,
63 step: 10,
65 entries: HashMap::new(),
66 }
67 }
68}
69
70impl OverlayManager {
71 pub fn open(&mut self, kind: OverlayKind, has_mask: bool) -> (OverlayKey, OverlayMeta) {
76 let key = OverlayKey(self.next_key);
77 self.next_key += 1;
78
79 let z_index = self.next_z_index();
80 let meta = OverlayMeta {
81 kind,
82 z_index,
83 has_mask,
84 };
85 self.entries.insert(key, meta);
86 (key, meta)
87 }
88
89 pub fn update(&mut self, key: OverlayKey, has_mask: Option<bool>) -> Option<OverlayMeta> {
91 if let Some(entry) = self.entries.get_mut(&key) {
92 if let Some(mask) = has_mask {
93 entry.has_mask = mask;
94 }
95 return Some(*entry);
96 }
97 None
98 }
99
100 pub fn close(&mut self, key: OverlayKey) {
102 self.entries.remove(&key);
103 }
104
105 pub fn close_all(&mut self) {
107 self.entries.clear();
108 }
109
110 pub fn entries(&self) -> impl Iterator<Item = (&OverlayKey, &OverlayMeta)> {
114 self.entries.iter()
115 }
116
117 pub fn current_top_z_index(&self) -> i32 {
120 self.entries
121 .values()
122 .map(|m| m.z_index)
123 .max()
124 .unwrap_or(self.base_z_index)
125 }
126
127 fn next_z_index(&self) -> i32 {
128 let top = self
129 .entries
130 .values()
131 .map(|m| m.z_index)
132 .max()
133 .unwrap_or(self.base_z_index - self.step);
134 top + self.step
135 }
136}
137
138#[derive(Clone)]
143pub struct OverlayHandle {
144 state: Signal<OverlayManager>,
145}
146
147impl OverlayHandle {
148 pub fn open(&self, kind: OverlayKind, has_mask: bool) -> (OverlayKey, OverlayMeta) {
150 let mut state = self.state;
151 state.write().open(kind, has_mask)
152 }
153
154 pub fn update(&self, key: OverlayKey, has_mask: Option<bool>) -> Option<OverlayMeta> {
156 let mut state = self.state;
157 state.write().update(key, has_mask)
158 }
159
160 pub fn close(&self, key: OverlayKey) {
162 let mut state = self.state;
163 state.write().close(key);
164 }
165
166 pub fn close_all(&self) {
168 let mut state = self.state;
169 state.write().close_all();
170 }
171
172 pub fn snapshot(&self) -> OverlayManager {
175 self.state.read().clone()
176 }
177}
178
179pub fn use_overlay_provider() -> OverlayHandle {
185 let state = use_signal(OverlayManager::default);
186 let handle = OverlayHandle { state };
187 use_context_provider(|| handle.clone());
188 handle
189}
190
191pub fn use_overlay() -> Option<OverlayHandle> {
196 try_use_context::<OverlayHandle>()
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn overlay_manager_allocates_monotonic_z_indices() {
205 let mut mgr = OverlayManager::default();
206 let (_k1, m1) = mgr.open(OverlayKind::Modal, true);
207 let (_k2, m2) = mgr.open(OverlayKind::Drawer, false);
208 assert!(m2.z_index > m1.z_index);
209 assert_eq!(mgr.current_top_z_index(), m2.z_index);
210 }
211
212 #[test]
213 fn overlay_manager_update_and_close_work() {
214 let mut mgr = OverlayManager::default();
215 let (key, meta) = mgr.open(OverlayKind::Message, false);
216 assert!(!meta.has_mask);
217 let updated = mgr.update(key, Some(true)).unwrap();
218 assert!(updated.has_mask);
219 mgr.close(key);
220 assert_eq!(mgr.entries().count(), 0);
221 }
222
223 #[test]
224 fn overlay_manager_close_all() {
225 let mut mgr = OverlayManager::default();
226 mgr.open(OverlayKind::Modal, true);
227 mgr.open(OverlayKind::Drawer, false);
228 mgr.open(OverlayKind::Tooltip, false);
229 assert_eq!(mgr.entries().count(), 3);
230 mgr.close_all();
231 assert_eq!(mgr.entries().count(), 0);
232 }
233
234 #[test]
235 fn overlay_manager_current_top_z_index_with_no_overlays() {
236 let mgr = OverlayManager::default();
237 assert_eq!(mgr.current_top_z_index(), 1000);
238 }
239
240 #[test]
241 fn overlay_manager_current_top_z_index_with_overlays() {
242 let mut mgr = OverlayManager::default();
243 let (_k1, m1) = mgr.open(OverlayKind::Modal, true);
244 let (_k2, m2) = mgr.open(OverlayKind::Drawer, false);
245 let (_k3, m3) = mgr.open(OverlayKind::Tooltip, false);
246 assert_eq!(mgr.current_top_z_index(), m3.z_index);
247 assert!(m3.z_index > m2.z_index);
248 assert!(m2.z_index > m1.z_index);
249 }
250
251 #[test]
252 fn overlay_manager_update_nonexistent_key() {
253 let mut mgr = OverlayManager::default();
254 let fake_key = OverlayKey(999);
255 assert!(mgr.update(fake_key, Some(true)).is_none());
256 }
257
258 #[test]
259 fn overlay_manager_close_nonexistent_key() {
260 let mut mgr = OverlayManager::default();
261 let fake_key = OverlayKey(999);
262 mgr.close(fake_key);
263 assert_eq!(mgr.entries().count(), 0);
264 }
265
266 #[test]
267 fn overlay_manager_update_without_mask_change() {
268 let mut mgr = OverlayManager::default();
269 let (key, _meta) = mgr.open(OverlayKind::Popup, true);
270 let updated = mgr.update(key, None);
271 assert!(updated.is_some());
272 assert_eq!(updated.unwrap().has_mask, true);
273 }
274
275 #[test]
276 fn overlay_key_as_u64() {
277 let mut mgr = OverlayManager::default();
278 let (key, _) = mgr.open(OverlayKind::Modal, true);
279 assert_eq!(key.as_u64(), 1);
280 let (key2, _) = mgr.open(OverlayKind::Drawer, false);
281 assert_eq!(key2.as_u64(), 2);
282 }
283
284 #[test]
285 fn overlay_kind_all_variants() {
286 assert_eq!(OverlayKind::Dropdown, OverlayKind::Dropdown);
287 assert_eq!(OverlayKind::Tooltip, OverlayKind::Tooltip);
288 assert_eq!(OverlayKind::Popup, OverlayKind::Popup);
289 assert_eq!(OverlayKind::Message, OverlayKind::Message);
290 assert_eq!(OverlayKind::Notification, OverlayKind::Notification);
291 assert_eq!(OverlayKind::Modal, OverlayKind::Modal);
292 assert_eq!(OverlayKind::Drawer, OverlayKind::Drawer);
293 }
294
295 #[test]
296 fn overlay_meta_clone() {
297 let meta = OverlayMeta {
298 kind: OverlayKind::Modal,
299 z_index: 1000,
300 has_mask: true,
301 };
302 let cloned = meta;
303 assert_eq!(meta, cloned);
304 }
305}