208 lines
6.2 KiB
Rust
208 lines
6.2 KiB
Rust
//! 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<ElementId> {
|
|
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(),
|
|
}
|
|
}
|
|
}
|