123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- //! Instructions returned by the VirtualDOM on how to modify the Real DOM.
- //!
- //! This module contains an internal API to generate these instructions.
- //!
- //! Beware that changing code in this module will break compatibility with
- //! interpreters for these types of DomEdits.
- use crate::innerlude::*;
- use std::{any::Any, fmt::Debug};
- /// ## Mutations
- ///
- /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
- /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
- /// applied the edits.
- ///
- /// Mutations are the only link between the RealDOM and the VirtualDOM.
- pub struct Mutations<'a> {
- pub edits: Vec<DomEdit<'a>>,
- pub dirty_scopes: FxHashSet<ScopeId>,
- pub refs: Vec<NodeRefMutation<'a>>,
- }
- impl Debug for Mutations<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("Mutations")
- .field("edits", &self.edits)
- .field("noderefs", &self.refs)
- .finish()
- }
- }
- /// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
- /// 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 {
- root: u64,
- },
- AppendChildren {
- many: u32,
- },
- // // save a possibly-fragment node as a template
- // SaveAsTemplate {
- // many: u32,
- // },
- // "Root" refers to the item directly
- // 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,
- root: u64,
- },
- CreateElement {
- tag: &'bump str,
- root: u64,
- },
- CreateElementNs {
- tag: &'bump str,
- root: u64,
- ns: &'static str,
- },
- CreatePlaceholder {
- root: u64,
- },
- NewEventListener {
- event_name: &'static str,
- scope: ScopeId,
- root: u64,
- },
- RemoveEventListener {
- root: u64,
- event: &'static str,
- },
- SetText {
- root: u64,
- text: &'bump str,
- },
- SetAttribute {
- root: u64,
- field: &'static str,
- value: &'bump str,
- ns: Option<&'bump str>,
- },
- RemoveAttribute {
- root: u64,
- name: &'static str,
- ns: Option<&'bump str>,
- },
- }
- use fxhash::FxHashSet;
- use DomEdit::*;
- impl<'a> Mutations<'a> {
- pub(crate) fn new() -> Self {
- Self {
- edits: Vec::new(),
- refs: Vec::new(),
- dirty_scopes: Default::default(),
- }
- }
- // Navigation
- pub(crate) fn push_root(&mut self, root: ElementId) {
- let id = root.as_u64();
- self.edits.push(PushRoot { root: id });
- }
- pub(crate) fn replace_with(&mut self, root: ElementId, m: u32) {
- let root = root.as_u64();
- self.edits.push(ReplaceWith { m, root });
- }
- pub(crate) fn insert_after(&mut self, root: ElementId, n: u32) {
- let root = root.as_u64();
- self.edits.push(InsertAfter { n, root });
- }
- pub(crate) fn insert_before(&mut self, root: ElementId, n: u32) {
- let root = root.as_u64();
- self.edits.push(InsertBefore { n, root });
- }
- pub(crate) fn append_children(&mut self, n: u32) {
- self.edits.push(AppendChildren { many: n });
- }
- // Remove Nodes from the dom
- pub(crate) fn remove(&mut self, id: u64) {
- self.edits.push(Remove { root: id });
- }
- // Create
- pub(crate) fn create_text_node(&mut self, text: &'a str, id: ElementId) {
- let id = id.as_u64();
- self.edits.push(CreateTextNode { text, root: id });
- }
- pub(crate) fn create_element(
- &mut self,
- tag: &'static str,
- ns: Option<&'static str>,
- id: ElementId,
- ) {
- let id = id.as_u64();
- match ns {
- Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
- None => self.edits.push(CreateElement { root: id, tag }),
- }
- }
- // 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();
- self.edits.push(CreatePlaceholder { root: id });
- }
- // 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,
- root: element_id,
- });
- }
- pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: u64) {
- self.edits.push(RemoveEventListener { event, root });
- }
- // modify
- pub(crate) fn set_text(&mut self, text: &'a str, root: u64) {
- self.edits.push(SetText { text, root });
- }
- pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
- let Attribute {
- name,
- value,
- namespace,
- ..
- } = attribute;
- self.edits.push(SetAttribute {
- field: name,
- value,
- ns: *namespace,
- root,
- });
- }
- pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: u64) {
- let Attribute {
- name, namespace, ..
- } = attribute;
- self.edits.push(RemoveAttribute {
- name,
- ns: *namespace,
- root,
- });
- }
- pub(crate) fn mark_dirty_scope(&mut self, scope: ScopeId) {
- self.dirty_scopes.insert(scope);
- }
- }
- // refs are only assigned once
- pub struct NodeRefMutation<'a> {
- pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
- pub element_id: ElementId,
- }
- 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>())
- }
- }
|