All effects attached by dynatos are bundled in the same array variable.

This commit is contained in:
Filipe Rodrigues 2024-02-06 13:33:56 +00:00
parent f32ec1d4ec
commit 7c2ad3d2da
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
2 changed files with 74 additions and 22 deletions

View File

@ -16,7 +16,11 @@ pub use self::{
};
// Imports
use {js_sys::Reflect, std::fmt, wasm_bindgen::JsValue};
use {
js_sys::Reflect,
std::fmt,
wasm_bindgen::{JsCast, JsValue},
};
/// Extension trait to be able to use `.context` on `Result<T, JsValue>`.
#[extend::ext(name = JsResultContext)]
@ -43,3 +47,32 @@ pub impl js_sys::Object {
Reflect::set(self, &property.into(), &value.into()).expect("Unable to set object property");
}
}
/// Error for [`ObjectGet::get`]
#[derive(Clone, Debug)]
pub enum GetError {
/// Property was missing
Missing,
/// Property was the wrong type
WrongType(JsValue),
}
/// Extension trait to get a property of an object
#[extend::ext(name = ObjectGet)]
pub impl js_sys::Object {
// TODO: Differentiate between missing value and wrong type?
fn get<T>(&self, property: &str) -> Result<T, GetError>
where
T: JsCast,
{
// Note: This returning `Err` should only happen if `self` isn't an object,
// which we guarantee, so no errors can occur.
let value = Reflect::get(self, &property.into()).expect("Unable to get object property");
if value.is_undefined() {
return Err(GetError::Missing);
}
value.dyn_into().map_err(GetError::WrongType)
}
}

View File

@ -1,8 +1,5 @@
//! Dynatos framework
// TODO: Use a single object, `__dynatos` with all of the effects, instead of a
// property for each?
// Features
#![feature(let_chains)]
@ -10,14 +7,44 @@
use {
dynatos_html::html,
dynatos_reactive::Effect,
dynatos_util::{ObjectSet, TryOrReturnExt, WeakRef},
std::{
cell::RefCell,
sync::atomic::{self, AtomicUsize},
},
dynatos_util::{ObjectGet, ObjectSet, TryOrReturnExt, WeakRef},
std::cell::RefCell,
wasm_bindgen::prelude::wasm_bindgen,
};
/// Extension trait to add an effect to a node
// TODO: Allow removing effects?
#[extend::ext(name = NodeAttachEffect)]
pub impl js_sys::Object {
/// Attaches an effect to this node
fn attach_effect(&self, effect: Effect) {
// Get the effects array, or create it, if it doesn't exist
// TODO: Use an static anonymous symbol?
let prop_name: &str = "__dynatos_effects";
let effects = match self.get::<js_sys::Array>(prop_name) {
Ok(effects) => effects,
Err(dynatos_util::GetError::WrongType(err)) => panic!("Effects array was the wrong type: {err:?}"),
Err(dynatos_util::GetError::Missing) => {
let effects = js_sys::Array::new();
self.set(prop_name, &effects);
effects
},
};
// Then push the effects
let effect = WasmEffect(effect);
effects.push(&effect.into());
}
/// Attaches an effect to this node.
///
/// Returns the node, for chaining
fn with_effect(self, effect: Effect) -> Self {
self.attach_effect(effect);
self
}
}
/// Extension trait to add a reactive child to an node
#[extend::ext(name = NodeDynChild)]
pub impl<T> T
@ -98,15 +125,8 @@ where
})
.or_return()?;
// Otherwise get a unique id for the property name
// Note: Since a node may have multiple reactive children,
// we can't use a single property name for this
static PROP_IDX: AtomicUsize = AtomicUsize::new(0);
let prop_idx = PROP_IDX.fetch_add(1, atomic::Ordering::AcqRel);
// Then set it
let prop = format!("__dynatos_child_effect_{}", prop_idx);
self.as_ref().set(&prop, WasmEffect(child_effect));
self.as_ref().attach_effect(child_effect);
}
/// Adds a dynamic child to this node.
@ -151,9 +171,8 @@ where
})
.or_return()?;
// Otherwise set it
self.as_ref()
.set("__dynatos_text_content_effect", WasmEffect(text_content_effect));
// Then set it
self.as_ref().attach_effect(text_content_effect);
}
/// Adds dynamic text to this node.
@ -208,8 +227,8 @@ where
})
.or_return()?;
// Otherwise set it
self.as_ref().set("__dynatos_attr_effect", WasmEffect(attr_effect));
// Then set it
self.as_ref().attach_effect(attr_effect);
}
/// Adds a dynamic attribute to this element.