use std::cell::RefCell;
use std::collections::HashMap;
use std::mem::ManuallyDrop;
use reactive_core::{RwSignal, signal};
thread_local! {
static NAMED: ManuallyDrop<RefCell<HashMap<String, RwSignal<bool>>>> =
ManuallyDrop::new(RefCell::new(HashMap::new()));
}
pub fn state(id: &str) -> RwSignal<bool> {
if let Some(existing) = NAMED.with(|named| named.borrow().get(id).cloned()) {
return existing;
}
let created = signal(false);
NAMED.with(|named| {
named
.borrow_mut()
.entry(id.to_string())
.or_insert(created)
.clone()
})
}
pub fn open(id: &str) {
state(id).set(true);
}
pub fn close(id: &str) {
state(id).set(false);
}
#[cfg(test)]
mod tests {
use super::*;
fn reset() {
NAMED.with(|named| named.borrow_mut().clear());
}
#[test]
fn a_name_resolves_to_one_shared_signal() {
reset();
let first = state("confirm");
let second = state("confirm");
first.set(true);
assert!(second.get(), "both handles are the same signal");
assert!(!state("other").get(), "a different name is independent");
}
#[test]
fn opening_a_name_before_its_overlay_is_built_still_opens_it() {
reset();
open("settings");
assert!(state("settings").get());
}
#[test]
fn open_and_close_drive_the_same_state() {
reset();
assert!(!state("panel").peek());
open("panel");
assert!(state("panel").peek());
close("panel");
assert!(!state("panel").peek());
}
#[test]
fn the_state_is_reactive() {
reset();
let seen = std::rc::Rc::new(RefCell::new(Vec::new()));
let s = seen.clone();
let _e = reactive_core::effect(move || s.borrow_mut().push(state("banner").get()));
open("banner");
close("banner");
assert_eq!(*seen.borrow(), vec![false, true, false]);
}
}