use super::{El, IntoNodes, Mailbox, Node, Text};
use crate::app::App;
use crate::browser::dom::virtual_dom_bridge;
use web_sys::Document;
mod patch_gen;
use patch_gen::{PatchCommand, PatchGen};
fn append_el<Ms>(
document: &Document,
new: &mut El<Ms>,
parent: &web_sys::Node,
mailbox: &Mailbox<Ms>,
) {
virtual_dom_bridge::assign_ws_nodes_to_el(document, new);
virtual_dom_bridge::attach_el_and_children(new, parent, mailbox);
}
fn append_text(document: &Document, new: &mut Text, parent: &web_sys::Node) {
virtual_dom_bridge::assign_ws_nodes_to_text(document, new);
virtual_dom_bridge::attach_text_node(new, parent);
}
fn insert_el<Ms>(
document: &Document,
new: &mut El<Ms>,
parent: &web_sys::Node,
next_node: web_sys::Node,
mailbox: &Mailbox<Ms>,
) {
virtual_dom_bridge::assign_ws_nodes_to_el(document, new);
virtual_dom_bridge::insert_el_and_children(new, parent, Some(next_node), mailbox);
}
fn insert_text(
document: &Document,
new: &mut Text,
parent: &web_sys::Node,
next_node: web_sys::Node,
) {
virtual_dom_bridge::assign_ws_nodes_to_text(document, new);
let new_node_ws = new
.node_ws
.as_ref()
.expect("new_node_ws missing when patching Empty to Text");
virtual_dom_bridge::insert_node(new_node_ws, parent, Some(next_node));
}
fn patch_el<Ms, Mdl, INodes>(
document: &Document,
mut old: El<Ms>,
new: &mut El<Ms>,
mailbox: &Mailbox<Ms>,
app: &App<Ms, Mdl, INodes>,
) where
INodes: IntoNodes<Ms>,
{
let old_el_ws = old
.node_ws
.as_ref()
.expect("missing old el_ws when patching non-empty el")
.clone();
virtual_dom_bridge::patch_el_details(&mut old, new, &old_el_ws, mailbox);
let old_children_iter = old.children.into_iter();
let new_children_iter = new.children.iter_mut();
patch_els(
document,
mailbox,
app,
&old_el_ws,
old_children_iter,
new_children_iter,
);
new.node_ws = Some(old_el_ws);
}
fn patch_text(mut old: Text, new: &mut Text) {
let old_node_ws = old
.node_ws
.take()
.expect("old_node_ws missing when changing text");
if new != &old {
old_node_ws.set_text_content(Some(&new.text));
}
new.node_ws.replace(old_node_ws);
}
fn replace_by_el<Ms>(
document: &Document,
old_node: &web_sys::Node,
new: &mut El<Ms>,
parent: &web_sys::Node,
mailbox: &Mailbox<Ms>,
) {
let new_node = virtual_dom_bridge::make_websys_el(new, document);
new.node_ws = Some(new_node);
for child in &mut new.children {
virtual_dom_bridge::assign_ws_nodes(document, child);
}
virtual_dom_bridge::attach_el_and_children(new, parent, mailbox);
let new_ws = new.node_ws.as_ref().expect("Missing websys el");
virtual_dom_bridge::replace_child(new_ws, old_node, parent);
}
fn replace_by_text(
document: &Document,
old_node: &web_sys::Node,
new: &mut Text,
parent: &web_sys::Node,
) {
virtual_dom_bridge::assign_ws_nodes_to_text(document, new);
let new_node_ws = new
.node_ws
.as_ref()
.expect("old el_ws missing when replacing with text node");
virtual_dom_bridge::replace_child(new_node_ws, old_node, parent);
}
fn replace_el_by_el<Ms>(
document: &Document,
mut old: El<Ms>,
new: &mut El<Ms>,
parent: &web_sys::Node,
mailbox: &Mailbox<Ms>,
) {
let old_node = old
.node_ws
.take()
.expect("old el_ws missing when replacing element with new element");
replace_by_el(document, &old_node, new, parent, mailbox);
}
fn replace_el_by_text<Ms>(
document: &Document,
mut old: El<Ms>,
new: &mut Text,
parent: &web_sys::Node,
) {
let old_node = old
.node_ws
.take()
.expect("old el_ws missing when replacing element with text node");
replace_by_text(document, &old_node, new, parent);
}
fn replace_text_by_el<Ms>(
document: &Document,
mut old: Text,
new: &mut El<Ms>,
parent: &web_sys::Node,
mailbox: &Mailbox<Ms>,
) {
let old_node = old
.node_ws
.take()
.expect("old el_ws missing when replacing text node with element");
replace_by_el(document, &old_node, new, parent, mailbox);
}
fn remove_el<Ms>(mut old: El<Ms>, parent: &web_sys::Node) {
let old_node = old.node_ws.take().expect("Missing child node_ws");
virtual_dom_bridge::remove_node(&old_node, parent);
old.node_ws.replace(old_node);
}
fn remove_text(mut old: Text, parent: &web_sys::Node) {
let old_node = old.node_ws.take().expect("Missing child node_ws");
virtual_dom_bridge::remove_node(&old_node, parent);
old.node_ws.replace(old_node);
}
pub(crate) fn patch_els<'a, Ms, Mdl, INodes, OI, NI>(
document: &Document,
mailbox: &Mailbox<Ms>,
app: &App<Ms, Mdl, INodes>,
old_el_ws: &web_sys::Node,
old_children_iter: OI,
new_children_iter: NI,
) where
INodes: IntoNodes<Ms>,
OI: Iterator<Item = Node<Ms>>,
NI: Iterator<Item = &'a mut Node<Ms>>,
{
for command in PatchGen::new(old_children_iter, new_children_iter) {
match command {
PatchCommand::AppendEl { el_new } => append_el(document, el_new, old_el_ws, mailbox),
PatchCommand::AppendText { text_new } => append_text(document, text_new, old_el_ws),
PatchCommand::InsertEl { el_new, next_node } => {
insert_el(document, el_new, old_el_ws, next_node, mailbox);
}
PatchCommand::InsertText {
text_new,
next_node,
} => insert_text(document, text_new, old_el_ws, next_node),
PatchCommand::PatchEl { el_old, el_new } => {
patch_el(document, el_old, el_new, mailbox, app);
}
PatchCommand::PatchText { text_old, text_new } => patch_text(text_old, text_new),
PatchCommand::ReplaceElByEl { el_old, el_new } => {
replace_el_by_el(document, el_old, el_new, old_el_ws, mailbox);
}
PatchCommand::ReplaceTextByEl { text_old, el_new } => {
replace_text_by_el(document, text_old, el_new, old_el_ws, mailbox);
}
PatchCommand::ReplaceElByText { el_old, text_new } => {
replace_el_by_text(document, el_old, text_new, old_el_ws);
}
PatchCommand::RemoveEl { el_old } => remove_el(el_old, old_el_ws),
PatchCommand::RemoveText { text_old } => remove_text(text_old, old_el_ws),
};
}
}
#[cfg(test)]
pub(crate) fn patch<'a, Ms, Mdl, INodes: IntoNodes<Ms>>(
document: &Document,
old: Node<Ms>,
new: &'a mut Node<Ms>,
parent: &web_sys::Node,
next_node: Option<web_sys::Node>,
mailbox: &Mailbox<Ms>,
app: &App<Ms, Mdl, INodes>,
) -> Option<&'a web_sys::Node> {
match old {
Node::Element(old_el) => match new {
Node::Element(new_el) => {
if patch_gen::el_can_be_patched(&old_el, new_el) {
patch_el(document, old_el, new_el, mailbox, app);
} else {
replace_el_by_el(document, old_el, new_el, parent, mailbox);
}
}
Node::Text(new_text) => replace_el_by_text(document, old_el, new_text, parent),
Node::Empty => remove_el(old_el, parent),
Node::NoChange => {
*new = Node::Element(old_el);
}
},
Node::Empty => {
match new {
Node::Element(new_el) => {
if let Some(next) = next_node {
insert_el(document, new_el, parent, next, mailbox);
} else {
append_el(document, new_el, parent, mailbox);
}
}
Node::Text(new_text) => {
if let Some(next) = next_node {
insert_text(document, new_text, parent, next);
} else {
append_text(document, new_text, parent);
}
}
Node::Empty => (),
Node::NoChange => {
*new = old;
}
}
}
Node::Text(old_text) => {
virtual_dom_bridge::assign_ws_nodes(document, new);
match new {
Node::Element(new_el) => {
replace_text_by_el(document, old_text, new_el, parent, mailbox);
}
Node::Empty => remove_text(old_text, parent),
Node::Text(new_text) => patch_text(old_text, new_text),
Node::NoChange => {
*new = Node::Text(old_text);
}
}
}
Node::NoChange => panic!("Node::NoChange cannot be an old VDOM node!"),
};
new.node_ws()
}