use silex_core::{reactivity::Effect, traits::With};
use silex_dom::View;
use web_sys::Node;
#[derive(Clone)]
pub struct Dynamic<V, F> {
view_fn: F,
_marker: std::marker::PhantomData<V>,
}
impl<V, F> Dynamic<V, F>
where
V: View + Clone,
F: With<Value = V> + 'static,
{
pub fn new(f: F) -> Self {
Self {
view_fn: f,
_marker: std::marker::PhantomData,
}
}
}
impl<V> Dynamic<V, ()>
where
V: View + 'static,
{
pub fn bind<S, T, Map>(source: S, map_fn: Map) -> Dynamic<V, impl Fn() -> V>
where
S: With<Value = T> + 'static,
Map: Fn(T) -> V + 'static,
T: Clone + 'static,
V: Clone,
{
let combined_accessor = move || source.with(|val| map_fn(val.clone()));
Dynamic::new(combined_accessor)
}
}
impl<V, F> View for Dynamic<V, F>
where
V: View + Clone,
F: With<Value = V> + 'static,
{
fn mount(self, parent: &Node) {
let document = silex_dom::document();
let start_marker = document.create_comment("dyn-start");
let start_node: Node = start_marker.into();
if let Err(e) = parent
.append_child(&start_node)
.map_err(crate::SilexError::from)
{
silex_core::error::handle_error(e);
return;
}
let end_marker = document.create_comment("dyn-end");
let end_node: Node = end_marker.into();
if let Err(e) = parent
.append_child(&end_node)
.map_err(crate::SilexError::from)
{
silex_core::error::handle_error(e);
return;
}
let view_fn = self.view_fn;
Effect::new(move |_| {
let new_view = view_fn.with(Clone::clone);
if let Some(parent) = start_node.parent_node() {
while let Some(sibling) = start_node.next_sibling() {
if sibling == end_node {
break;
}
let _ = parent.remove_child(&sibling);
}
}
let fragment = document.create_document_fragment();
let fragment_node: Node = fragment.clone().into();
new_view.mount(&fragment_node);
if let Some(parent) = end_node.parent_node() {
let _ = parent.insert_before(&fragment_node, Some(&end_node));
}
});
}
}