//! Virtual Node Support //! //! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very* //! cheap and *very* fast to construct - building a full tree should be quick. use std::fmt::{Debug, Formatter}; mod arbitrary_value; mod component; mod element; mod factory; mod fragment; mod placeholder; mod suspense; mod template; mod text; pub use arbitrary_value::*; pub use component::*; pub use element::*; pub use factory::*; pub use fragment::*; pub use suspense::*; pub use template::*; pub use text::*; use self::placeholder::VPlaceholder; /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM. /// /// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of: /// /// - the `rsx!` macro /// - the [`NodeFactory`] API pub enum VNode<'src> { /// Text VNodes are simply bump-allocated (or static) string slices /// /// # Example /// /// ```rust, ignore /// let mut vdom = VirtualDom::new(); /// let node = vdom.render_vnode(rsx!( "hello" )); /// /// if let VNode::Text(vtext) = node { /// assert_eq!(vtext.text, "hello"); /// assert_eq!(vtext.dom_id.get(), None); /// assert_eq!(vtext.is_static, true); /// } /// ``` Text(&'src VText<'src>), /// Element VNodes are VNodes that may contain attributes, listeners, a key, a tag, and children. /// /// # Example /// /// ```rust, ignore /// let mut vdom = VirtualDom::new(); /// /// let node = vdom.render_vnode(rsx!{ /// div { /// key: "a", /// onclick: |e| log::info!("clicked"), /// hidden: "true", /// style: { background_color: "red" }, /// "hello" /// } /// }); /// /// if let VNode::Element(velement) = node { /// assert_eq!(velement.tag_name, "div"); /// assert_eq!(velement.namespace, None); /// assert_eq!(velement.key, Some("a")); /// } /// ``` Element(&'src VElement<'src>), /// Fragment nodes may contain many VNodes without a single root. /// /// # Example /// /// ```rust, ignore /// rsx!{ /// a {} /// link {} /// style {} /// "asd" /// Example {} /// } /// ``` Fragment(&'src VFragment<'src>), /// Component nodes represent a mounted component with props, children, and a key. /// /// # Example /// /// ```rust, ignore /// fn Example(cx: Scope) -> Element { /// ... /// } /// /// let mut vdom = VirtualDom::new(); /// /// let node = vdom.render_vnode(rsx!( Example {} )); /// /// if let VNode::Component(vcomp) = node { /// assert_eq!(vcomp.user_fc, Example as *const ()); /// } /// ``` Component(&'src VComponent<'src>), /// Templetes ase generated by the rsx macro to eleminate diffing static nodes. /// /// /// /// /// /// Template(&'src VTemplate<'src>), } /// An Element's unique identifier. /// /// `ElementId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is /// unmounted, then the `ElementId` will be reused for a new component. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serialize", serde(transparent))] pub struct ElementId(pub usize); impl<'src> VNode<'src> { /// Get the VNode's "key" used in the keyed diffing algorithm. pub fn key(&self) -> Option<&'src str> { match &self { VNode::Element(el) => el.key, VNode::Component(c) => c.key, VNode::Fragment(f) => f.key, VNode::Template(t) => t.key, VNode::Text(_t) => None, } } /// Get the ElementID of the mounted VNode. /// /// Panics if the mounted ID is None or if the VNode is not represented by a single Element. pub fn mounted_id(&self) -> ElementId { self.try_mounted_id().unwrap() } /// Try to get the ElementID of the mounted VNode. /// /// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component) pub fn try_mounted_id(&self) -> Option { match &self { VNode::Text(el) => el.id.get(), VNode::Element(el) => el.id.get(), VNode::Fragment(_) => None, VNode::Component(_) => None, VNode::Template(_) => None, } } // Create an "owned" version of the vnode. pub(crate) fn decouple(&self) -> VNode<'src> { match *self { VNode::Text(t) => VNode::Text(t), VNode::Element(e) => VNode::Element(e), VNode::Component(c) => VNode::Component(c), VNode::Fragment(f) => VNode::Fragment(f), VNode::Template(t) => VNode::Template(t), } } } impl Debug for VNode<'_> { fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { match &self { VNode::Element(el) => s .debug_struct("VNode::Element") .field("name", &el.tag) .field("key", &el.key) .field("attrs", &el.attributes) .field("children", &el.children) .field("id", &el.id) .finish(), VNode::Text(t) => s .debug_struct("VNode::Text") .field("text", &t.text) .field("id", &t.id) .finish(), VNode::Fragment(frag) => s .debug_struct("VNode::Fragment") .field("children", &frag.children) .finish(), VNode::Component(comp) => s .debug_struct("VNode::Component") .field("name", &comp.fn_name) .field("fnptr", &comp.user_fc) .field("key", &comp.key) .field("scope", &comp.scope) .finish(), VNode::Template(temp) => s .debug_struct("VNode::Templates") .field("template_id", &temp.template.id) .finish(), } } }