use crate::html::{element::ElementType, node_ref::NodeRefContainer};
use reactive_graph::{
effect::Effect,
graph::untrack,
signal::{
guards::{Derefable, ReadGuard},
RwSignal,
},
traits::{
DefinedAt, Get, IsDisposed, Notify, ReadUntracked, Set, Track,
UntrackableGuard, Write,
},
};
use send_wrapper::SendWrapper;
use std::{cell::Cell, ops::DerefMut};
use wasm_bindgen::JsCast;
#[derive(Debug)]
pub struct NodeRef<E>(RwSignal<Option<SendWrapper<E::Output>>>)
where
E: ElementType,
E::Output: 'static;
impl<E> NodeRef<E>
where
E: ElementType,
E::Output: 'static,
{
#[track_caller]
pub fn new() -> Self {
Self(RwSignal::new(None))
}
#[inline(always)]
pub fn on_load<F>(self, f: F)
where
E: 'static,
F: FnOnce(E::Output) + 'static,
E: ElementType,
E::Output: JsCast + Clone + 'static,
{
let f = Cell::new(Some(f));
Effect::new(move |_| {
if let Some(node_ref) = self.get() {
let f = f.take().unwrap();
untrack(move || {
f(node_ref);
});
}
});
}
}
impl<E> Default for NodeRef<E>
where
E: ElementType,
E::Output: 'static,
{
fn default() -> Self {
Self::new()
}
}
impl<E> Clone for NodeRef<E>
where
E: ElementType,
E::Output: 'static,
{
fn clone(&self) -> Self {
*self
}
}
impl<E> Copy for NodeRef<E>
where
E: ElementType,
E::Output: 'static,
{
}
impl<E> NodeRefContainer<E> for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + 'static,
{
fn load(self, el: &crate::renderer::types::Element) {
self.0
.set(Some(SendWrapper::new(el.clone().unchecked_into())));
}
}
impl<E> DefinedAt for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + 'static,
{
fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
self.0.defined_at()
}
}
impl<E> Notify for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + Clone + 'static,
{
fn notify(&self) {
self.0.notify();
}
}
impl<E> Write for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + Clone + 'static,
{
type Value = Option<SendWrapper<E::Output>>;
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
self.0.try_write()
}
fn try_write_untracked(
&self,
) -> Option<impl DerefMut<Target = Self::Value>> {
self.0.try_write_untracked()
}
}
impl<E> ReadUntracked for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + Clone + 'static,
{
type Value = ReadGuard<Option<E::Output>, Derefable<Option<E::Output>>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
Some(ReadGuard::new(Derefable(
self.0.try_read_untracked()?.as_deref().cloned(),
)))
}
}
impl<E> Track for NodeRef<E>
where
E: ElementType,
E::Output: JsCast + 'static,
{
fn track(&self) {
self.0.track();
}
}
impl<E> IsDisposed for NodeRef<E>
where
E: ElementType,
E::Output: 'static,
{
fn is_disposed(&self) -> bool {
self.0.is_disposed()
}
}
#[inline(always)]
#[track_caller]
#[deprecated = "This function is being removed to conform to Rust idioms. \
Please use `NodeRef::new()` instead."]
pub fn create_node_ref<E>() -> NodeRef<E>
where
E: ElementType,
E::Output: 'static,
{
NodeRef::new()
}