2021-08-23 14:43:49 +00:00
|
|
|
//! Instructions returned by the VirtualDOM on how to modify the Real DOM.
|
|
|
|
//!
|
2021-09-02 04:37:57 +00:00
|
|
|
//! This module contains an internal API to generate these instructions.
|
2021-08-22 21:08:25 +00:00
|
|
|
|
|
|
|
use crate::innerlude::*;
|
2021-08-23 14:43:49 +00:00
|
|
|
use std::any::Any;
|
2021-08-22 21:08:25 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2021-08-23 14:43:49 +00:00
|
|
|
pub struct Mutations<'a> {
|
|
|
|
pub edits: Vec<DomEdit<'a>>,
|
|
|
|
pub noderefs: Vec<NodeRefMutation<'a>>,
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-22 21:08:25 +00:00
|
|
|
use DomEdit::*;
|
|
|
|
|
2021-08-23 14:43:49 +00:00
|
|
|
impl<'a> Mutations<'a> {
|
2021-08-26 21:05:28 +00:00
|
|
|
pub(crate) fn new() -> Self {
|
2021-08-22 21:08:25 +00:00
|
|
|
let edits = Vec::new();
|
|
|
|
let noderefs = Vec::new();
|
|
|
|
Self { edits, noderefs }
|
|
|
|
}
|
|
|
|
|
|
|
|
// Navigation
|
|
|
|
pub(crate) fn push_root(&mut self, root: ElementId) {
|
|
|
|
let id = root.as_u64();
|
2021-10-05 07:37:15 +00:00
|
|
|
self.edits.push(PushRoot { root: id });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-23 16:11:45 +00:00
|
|
|
pub(crate) fn replace_with(&mut self, root: ElementId, m: u32) {
|
|
|
|
let root = root.as_u64();
|
|
|
|
self.edits.push(ReplaceWith { m, root });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-23 16:11:45 +00:00
|
|
|
pub(crate) fn insert_after(&mut self, root: ElementId, n: u32) {
|
|
|
|
let root = root.as_u64();
|
|
|
|
self.edits.push(InsertAfter { n, root });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-23 16:11:45 +00:00
|
|
|
pub(crate) fn insert_before(&mut self, root: ElementId, n: u32) {
|
|
|
|
let root = root.as_u64();
|
|
|
|
self.edits.push(InsertBefore { n, root });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-10-24 17:30:36 +00:00
|
|
|
// Remove Nodes from the dom
|
2021-08-23 14:43:49 +00:00
|
|
|
pub(crate) fn remove(&mut self, id: u64) {
|
|
|
|
self.edits.push(Remove { root: id });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-22 21:08:25 +00:00
|
|
|
// Create
|
2021-08-23 14:43:49 +00:00
|
|
|
pub(crate) fn create_text_node(&mut self, text: &'a str, id: ElementId) {
|
2021-08-22 21:08:25 +00:00
|
|
|
let id = id.as_u64();
|
2021-10-05 07:37:15 +00:00
|
|
|
self.edits.push(CreateTextNode { text, root: id });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-22 21:08:25 +00:00
|
|
|
pub(crate) fn create_element(
|
|
|
|
&mut self,
|
|
|
|
tag: &'static str,
|
|
|
|
ns: Option<&'static str>,
|
|
|
|
id: ElementId,
|
|
|
|
) {
|
|
|
|
let id = id.as_u64();
|
|
|
|
match ns {
|
2021-10-05 07:37:15 +00:00
|
|
|
Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
|
|
|
|
None => self.edits.push(CreateElement { root: id, tag }),
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
|
|
|
pub(crate) fn create_placeholder(&mut self, id: ElementId) {
|
|
|
|
let id = id.as_u64();
|
2021-10-05 07:37:15 +00:00
|
|
|
self.edits.push(CreatePlaceholder { root: id });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-22 21:08:25 +00:00
|
|
|
// events
|
|
|
|
pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
|
|
|
|
let Listener {
|
|
|
|
event,
|
|
|
|
mounted_node,
|
|
|
|
..
|
|
|
|
} = listener;
|
|
|
|
|
|
|
|
let element_id = mounted_node.get().unwrap().as_u64();
|
|
|
|
|
|
|
|
self.edits.push(NewEventListener {
|
|
|
|
scope,
|
|
|
|
event_name: event,
|
2021-10-05 07:37:15 +00:00
|
|
|
root: element_id,
|
2021-08-22 21:08:25 +00:00
|
|
|
});
|
|
|
|
}
|
2021-10-11 19:35:20 +00:00
|
|
|
pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: u64) {
|
|
|
|
self.edits.push(RemoveEventListener { event, root });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-08-22 21:08:25 +00:00
|
|
|
// modify
|
2021-10-11 19:35:20 +00:00
|
|
|
pub(crate) fn set_text(&mut self, text: &'a str, root: u64) {
|
|
|
|
self.edits.push(SetText { text, root });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-10-11 19:35:20 +00:00
|
|
|
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
|
2021-08-22 21:08:25 +00:00
|
|
|
let Attribute {
|
|
|
|
name,
|
|
|
|
value,
|
|
|
|
namespace,
|
|
|
|
..
|
|
|
|
} = attribute;
|
|
|
|
|
|
|
|
self.edits.push(SetAttribute {
|
|
|
|
field: name,
|
|
|
|
value,
|
2021-09-02 04:37:57 +00:00
|
|
|
ns: *namespace,
|
2021-10-11 19:35:20 +00:00
|
|
|
root,
|
2021-08-22 21:08:25 +00:00
|
|
|
});
|
|
|
|
}
|
2021-08-23 14:43:49 +00:00
|
|
|
|
2021-10-11 19:35:20 +00:00
|
|
|
pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: u64) {
|
2021-08-22 21:08:25 +00:00
|
|
|
let name = attribute.name;
|
2021-10-11 19:35:20 +00:00
|
|
|
self.edits.push(RemoveAttribute { name, root });
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// refs are only assigned once
|
|
|
|
pub struct NodeRefMutation<'a> {
|
2021-08-24 19:12:20 +00:00
|
|
|
pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
|
|
|
|
pub element_id: ElementId,
|
2021-08-22 21:08:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("NodeRefMutation")
|
|
|
|
.field("element_id", &self.element_id)
|
|
|
|
.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> NodeRefMutation<'a> {
|
|
|
|
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
|
|
|
|
self.element
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|f| f.get())
|
|
|
|
.and_then(|f| f.downcast_ref::<T>())
|
|
|
|
}
|
|
|
|
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
|
|
|
|
self.element
|
|
|
|
.as_mut()
|
|
|
|
.and_then(|f| f.get_mut())
|
|
|
|
.and_then(|f| f.downcast_mut::<T>())
|
|
|
|
}
|
|
|
|
}
|
2021-08-27 13:40:04 +00:00
|
|
|
|
2021-10-24 17:30:36 +00:00
|
|
|
/// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
|
2021-08-27 13:40:04 +00:00
|
|
|
/// network or through FFI boundaries.
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
#[cfg_attr(
|
|
|
|
feature = "serialize",
|
|
|
|
derive(serde::Serialize, serde::Deserialize),
|
|
|
|
serde(tag = "type")
|
|
|
|
)]
|
|
|
|
pub enum DomEdit<'bump> {
|
|
|
|
PushRoot {
|
2021-10-05 07:37:15 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
},
|
|
|
|
PopRoot,
|
|
|
|
|
|
|
|
AppendChildren {
|
|
|
|
many: u32,
|
|
|
|
},
|
|
|
|
|
2021-10-24 17:30:36 +00:00
|
|
|
// "Root" refers to the item directly
|
2021-08-27 13:40:04 +00:00
|
|
|
// it's a waste of an instruction to push the root directly
|
|
|
|
ReplaceWith {
|
|
|
|
root: u64,
|
|
|
|
m: u32,
|
|
|
|
},
|
|
|
|
InsertAfter {
|
|
|
|
root: u64,
|
|
|
|
n: u32,
|
|
|
|
},
|
|
|
|
InsertBefore {
|
|
|
|
root: u64,
|
|
|
|
n: u32,
|
|
|
|
},
|
|
|
|
Remove {
|
|
|
|
root: u64,
|
|
|
|
},
|
|
|
|
CreateTextNode {
|
|
|
|
text: &'bump str,
|
2021-10-05 07:37:15 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
},
|
|
|
|
CreateElement {
|
|
|
|
tag: &'bump str,
|
2021-10-05 07:37:15 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
},
|
|
|
|
CreateElementNs {
|
|
|
|
tag: &'bump str,
|
2021-10-05 07:37:15 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
ns: &'static str,
|
|
|
|
},
|
|
|
|
CreatePlaceholder {
|
2021-10-05 07:37:15 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
},
|
|
|
|
NewEventListener {
|
|
|
|
event_name: &'static str,
|
|
|
|
scope: ScopeId,
|
2021-10-05 07:37:15 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
},
|
|
|
|
RemoveEventListener {
|
2021-10-11 19:35:20 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
event: &'static str,
|
|
|
|
},
|
|
|
|
SetText {
|
2021-10-11 19:35:20 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
text: &'bump str,
|
|
|
|
},
|
|
|
|
SetAttribute {
|
2021-10-11 19:35:20 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
field: &'static str,
|
|
|
|
value: &'bump str,
|
|
|
|
ns: Option<&'bump str>,
|
|
|
|
},
|
|
|
|
RemoveAttribute {
|
2021-10-11 19:35:20 +00:00
|
|
|
root: u64,
|
2021-08-27 13:40:04 +00:00
|
|
|
name: &'static str,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
impl DomEdit<'_> {
|
|
|
|
pub fn is(&self, id: &'static str) -> bool {
|
|
|
|
match self {
|
|
|
|
DomEdit::InsertAfter { .. } => id == "InsertAfter",
|
|
|
|
DomEdit::InsertBefore { .. } => id == "InsertBefore",
|
|
|
|
DomEdit::PushRoot { .. } => id == "PushRoot",
|
|
|
|
DomEdit::PopRoot => id == "PopRoot",
|
|
|
|
DomEdit::AppendChildren { .. } => id == "AppendChildren",
|
|
|
|
DomEdit::ReplaceWith { .. } => id == "ReplaceWith",
|
|
|
|
DomEdit::Remove { .. } => id == "Remove",
|
|
|
|
DomEdit::CreateTextNode { .. } => id == "CreateTextNode",
|
|
|
|
DomEdit::CreateElement { .. } => id == "CreateElement",
|
|
|
|
DomEdit::CreateElementNs { .. } => id == "CreateElementNs",
|
|
|
|
DomEdit::CreatePlaceholder { .. } => id == "CreatePlaceholder",
|
|
|
|
DomEdit::NewEventListener { .. } => id == "NewEventListener",
|
|
|
|
DomEdit::RemoveEventListener { .. } => id == "RemoveEventListener",
|
|
|
|
DomEdit::SetText { .. } => id == "SetText",
|
|
|
|
DomEdit::SetAttribute { .. } => id == "SetAttribute",
|
|
|
|
DomEdit::RemoveAttribute { .. } => id == "RemoveAttribute",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|