use silex_core::{reactivity::Effect, traits::RxRead};
use silex_dom::prelude::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 + 'static,
F: RxRead<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, silex_core::Rx<V, silex_core::RxValueKind>>
where
S: RxRead<Value = T> + Clone + 'static,
Map: Fn(T) -> V + Clone + 'static,
T: Clone + 'static,
V: View + Clone + 'static,
{
let combined_accessor = silex_core::rx!(source.with(|val| map_fn(val.clone())));
Dynamic::new(combined_accessor)
}
}
impl<V, F> View for Dynamic<V, F>
where
V: View + Clone + 'static,
F: RxRead<Value = V> + Clone + 'static,
{
fn mount(self, parent: &Node, attrs: Vec<silex_dom::attribute::PendingAttribute>) {
mount_dynamic_internal(self.view_fn, parent, attrs);
}
fn mount_ref(&self, parent: &Node, attrs: Vec<silex_dom::attribute::PendingAttribute>) {
mount_dynamic_internal(self.view_fn.clone(), parent, attrs);
}
}
fn mount_dynamic_internal<V, F>(
view_fn: F,
parent: &Node,
attrs: Vec<silex_dom::attribute::PendingAttribute>,
) where
V: View + Clone + 'static,
F: RxRead<Value = V> + 'static,
{
let document = silex_dom::document();
let start_marker = document.create_comment("dyn-start");
let start_node: Node = start_marker.into();
let _ = parent.append_child(&start_node);
let end_marker = document.create_comment("dyn-end");
let end_node: Node = end_marker.into();
let _ = parent.append_child(&end_node);
Effect::new(move |_| {
let new_view = silex_core::traits::RxRead::with(&view_fn, 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, attrs.clone());
if let Some(parent) = end_node.parent_node() {
let _ = parent.insert_before(&fragment_node, Some(&end_node));
}
});
}