use crate::{
create_isomorphic_effect, create_rw_signal, runtime::with_owner, Owner,
RwSignal, SignalUpdate, SignalWith,
};
use std::{cell::RefCell, collections::HashMap, hash::Hash, rc::Rc};
#[inline(always)]
pub fn create_selector<T>(
source: impl Fn() -> T + Clone + 'static,
) -> Selector<T>
where
T: PartialEq + Eq + Clone + Hash + 'static,
{
create_selector_with_fn(source, PartialEq::eq)
}
pub fn create_selector_with_fn<T>(
source: impl Fn() -> T + 'static,
f: impl Fn(&T, &T) -> bool + Clone + 'static,
) -> Selector<T>
where
T: PartialEq + Eq + Clone + Hash + 'static,
{
#[allow(clippy::type_complexity)]
let subs: Rc<RefCell<HashMap<T, RwSignal<bool>>>> =
Rc::new(RefCell::new(HashMap::new()));
let v = Rc::new(RefCell::new(None));
let owner = Owner::current()
.expect("create_selector called outside the reactive system");
let f = Rc::new(f) as Rc<dyn Fn(&T, &T) -> bool>;
create_isomorphic_effect({
let subs = Rc::clone(&subs);
let f = Rc::clone(&f);
let v = Rc::clone(&v);
move |prev: Option<T>| {
let next_value = source();
*v.borrow_mut() = Some(next_value.clone());
if prev.as_ref() != Some(&next_value) {
let subs = { subs.borrow().clone() };
for (key, signal) in subs.into_iter() {
if f(&key, &next_value)
|| (prev.is_some() && f(&key, prev.as_ref().unwrap()))
{
signal.update(|n| *n = true);
}
}
}
next_value
}
});
Selector { subs, v, owner, f }
}
#[derive(Clone)]
pub struct Selector<T>
where
T: PartialEq + Eq + Clone + Hash + 'static,
{
subs: Rc<RefCell<HashMap<T, RwSignal<bool>>>>,
v: Rc<RefCell<Option<T>>>,
owner: Owner,
#[allow(clippy::type_complexity)] f: Rc<dyn Fn(&T, &T) -> bool>,
}
impl<T> core::fmt::Debug for Selector<T>
where
T: PartialEq + Eq + Clone + Hash + 'static,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Selector").finish()
}
}
impl<T> Selector<T>
where
T: PartialEq + Eq + Clone + Hash + 'static,
{
#[inline(always)]
#[track_caller]
pub fn new(source: impl Fn() -> T + Clone + 'static) -> Self {
create_selector_with_fn(source, PartialEq::eq)
}
pub fn selected(&self, key: T) -> bool {
let owner = self.owner;
let read = {
let mut subs = self.subs.borrow_mut();
*(subs.entry(key.clone()).or_insert_with(|| {
with_owner(owner, || create_rw_signal(false))
}))
};
_ = read.try_with(|n| *n);
(self.f)(&key, self.v.borrow().as_ref().unwrap())
}
pub fn remove(&self, key: &T) {
let mut subs = self.subs.borrow_mut();
subs.remove(key);
}
}