duid-core 0.1.0

Core crate used for Duid
Documentation
mod dom_builder;
mod patch;
mod dom_diff;
mod apply_patches;

use std::rc::Rc;
use std::cell::RefCell;
use crate::core::duid_events::Dispatch;
use wasm_bindgen::JsCast;
use std::fmt::Debug;
use web_sys::{
    self, Element, Node, Document
};
use indextree::{Arena, NodeId};
use crate::core::{
    v_node::VirtualNode,
    util::document
};
use std::collections::HashMap;
pub use dom_builder::*;
pub(crate) use dom_diff::DomDiff;
pub(crate) use apply_patches::ApplyPatch;




#[derive(Debug, Clone)]
pub(crate) struct Dom<MSG> 
where
    MSG: std::fmt::Debug + Clone + 'static
{
    pub(crate) mount_node: Node,
    pub(crate) replace: bool,
    pub(crate) use_shadow: bool,
    pub(crate) arena: Rc<RefCell<Arena<VirtualNode<MSG>>>>,
    pub(crate) arena_root_node_id: Option<NodeId>,
    pub(crate) document: Document,
    pub(crate) base_styles: HashMap<String, String>,
    pub(crate) styles: HashMap<String, String>
}


impl<MSG> Dom<MSG> 
where
    MSG: std::fmt::Debug + Clone + 'static
{
    pub(crate) fn new<DSP>(
        mount: &str,
        replace: bool,
        use_shadow: bool,
        base_styles: HashMap<String, String>,
        styles: HashMap<String, String>
    ) -> Dom<MSG> 
    where
        DSP: Dispatch<MSG> + Clone + 'static,
    {
        let doc = document();
        let node = 
            doc
            .get_element_by_id(mount)
            .expect(&format!("element with id {:?} not present", mount))
            .unchecked_into::<Node>();
        
        Dom::<MSG> {
            mount_node: node,
            replace,
            use_shadow, 
            arena: Rc::new(RefCell::new(Arena::new())),
            arena_root_node_id: None,
            document: doc,
            base_styles,
            styles
        }
    }
}

impl<MSG> Dom<MSG> 
where
    MSG: std::fmt::Debug + Clone + 'static
{
    pub(crate) fn mount<DSP>(&mut self, program: &DSP, arena: Arena<VirtualNode<MSG>>, root_node_id: NodeId) 
    where
        DSP: Dispatch<MSG> + Clone + 'static,
    {
        let mut style_map: HashMap<String, String> = HashMap::with_capacity(0); 
        self.arena_root_node_id = Some(root_node_id);
        *self.arena.borrow_mut() = arena;
        self.arena.build(program, &self.document, &self.arena_root_node_id.as_ref().unwrap(), &mut style_map);
        
        self.first_mount(&self.mount_styles(style_map, true));
    }

    pub(crate) fn render<DSP>(&mut self, program: &DSP, arena: Arena<VirtualNode<MSG>>, root_node_id: NodeId) 
    where
        DSP: Dispatch<MSG> + Clone + 'static,
    {
        let mut style_map: HashMap<String, String> = HashMap::with_capacity(0);
        let patches: Vec<_> = self.arena.diff(&root_node_id, &arena);
        self.arena.apply_patches(&patches, &arena, program, &self.document, &mut style_map);
        self.inject_styles(&self.mount_styles(style_map, false));
    }

    fn first_mount(&self, styles: &[(String, String)]) {
        let node_borrow = self.arena.borrow();
        let virtual_node = node_borrow.get(*self.arena_root_node_id.as_ref().unwrap()).expect("The node does not exists").get();

        if let Some(node) = &*virtual_node.real_node.borrow() {
            if self.replace {
                let mount_element: &Element = self.mount_node.unchecked_ref();
                mount_element
                    .replace_with_with_node_1(node)
                    .expect("Could not append child to mount");
            } else {
                if self.use_shadow {
                    let mount_element: &web_sys::Element =
                        self.mount_node.unchecked_ref();
                    mount_element
                        .attach_shadow(&web_sys::ShadowRootInit::new(
                            web_sys::ShadowRootMode::Open,
                        ))
                        .expect("unable to attached shadow");
                    let mount_shadow =
                        mount_element.shadow_root().expect("must have a shadow");
    
                    let mount_shadow_node: &web_sys::Node =
                        mount_shadow.unchecked_ref();
    
                    mount_shadow_node
                        .append_child(&node)
                        .expect("could not append child to mount shadow");
                } else {
                    self.mount_node
                        .append_child(&node)
                        .expect("Could not append child to mount");
                }
            }
        };


        self.inject_styles(styles);
    }

    fn inject_styles(&self, styles: &[(String, String)]) {
        styles.iter().for_each(|(name, style)| self.inject_style(name, style));
    }

    fn inject_style(&self, name: &str, style: &str) {

        if let Some(style_to_update) = self.document.get_element_by_id(name) {
            let html_style: web_sys::Node = style_to_update.unchecked_into();
            html_style.set_text_content(Some(style));
        } else {
            let html_style = self.document
                .create_element("style")
                .expect("must be able to create style element");
            html_style
                .set_attribute("id", name)
                .expect("must set attribute");
            let html_style: web_sys::Node = html_style.unchecked_into();
            html_style.set_text_content(Some(style));
            let head = self.document.head().expect("must have a head");
            head.append_child(&html_style).expect("must append style");
        }  
    }
    
    pub(crate) fn mount_styles(&self, styles_map: HashMap<String, String>, is_first_mount: bool) -> Vec<(String, String)> {
        let mut styles_vec: Vec<(String, String)> = Vec::with_capacity(0);

        styles_map.iter().for_each(|(name, style)| {
            if self.styles.contains_key(name) {
                styles_vec.push((name.to_owned(), self.styles.get(name).unwrap().clone()));
            }
            else {
                styles_vec.push((name.to_owned(), style.to_owned()));
            }
        });
        if is_first_mount {
            self.base_styles.iter().for_each(|(name, style)| styles_vec.push((name.clone(), style.clone())));
        }

        styles_vec
    }

}