on 04bfc33 help sign in
dodrio/src/node.rs
198 lines 5.3 kB history raw
1use crate::{RootRender, VdomWeak};
2use bumpalo::Bump;
3use std::fmt;
4use std::mem;
6/// A node is either a text node or an element.
7#[derive(Debug, Clone)]
8pub enum Node<'a> {
9 /// A text node.
10 Text(TextNode<'a>),
12 /// An element potentially with attributes and children.
13 Element(ElementNode<'a>),
16/// Text nodes are just a string of text. They cannot have attributes or
17/// children.
18#[derive(Debug, Clone)]
19pub struct TextNode<'a> {
20 pub(crate) text: &'a str,
23/// Elements have a tag name, zero or more attributes, and zero or more
24/// children.
25#[derive(Debug, Clone)]
26pub struct ElementNode<'a> {
27 pub(crate) tag_name: &'a str,
28 pub(crate) listeners: &'a [Listener<'a>],
29 pub(crate) attributes: &'a [Attribute<'a>],
30 pub(crate) children: &'a [Node<'a>],
33/// An event listener callback function.
34///
35/// It takes three parameters:
36///
37/// 1. The virtual DOM's root rendering component.
38/// 2. A capability to scheduler virtual DOM re-rendering.
39/// 3. The event that ocurred.
40pub type ListenerCallback<'a> =
41 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
43/// An event listener.
44pub struct Listener<'a> {
45 /// The type of event to listen for.
46 pub(crate) event: &'a str,
47 /// The callback to invoke when the event happens.
48 pub(crate) callback: ListenerCallback<'a>,
51/// An attribute on a DOM node, such as `id="my-thing"` or
52/// `href="https://example.com"`.
53#[derive(Clone, Debug)]
54pub struct Attribute<'a> {
55 pub(crate) name: &'a str,
56 pub(crate) value: &'a str,
59impl fmt::Debug for Listener<'_> {
60 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61 let (a, b) = self.get_callback_parts();
62 let a = a as *mut u32;
63 let b = b as *mut u32;
64 f.debug_struct("Listener")
65 .field("event", &self.event)
66 .field("callback", &(a, b))
67 .finish()
68 }
71impl<'a> Attribute<'a> {
72 /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
73 #[inline]
74 pub fn name(&self) -> &'a str {
75 self.name
76 }
78 /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
79 #[inline]
80 pub fn value(&self) -> &'a str {
81 self.value
82 }
84 /// Certain attributes are considered "volatile" and can change via user
85 /// input that we can't see when diffing against the old virtual DOM. For
86 /// these attributes, we want to always re-set the attribute on the physical
87 /// DOM node, even if the old and new virtual DOM nodes have the same value.
88 #[inline]
89 pub(crate) fn is_volatile(&self) -> bool {
90 match self.name {
91 "value" | "checked" | "selected" => true,
92 _ => false,
93 }
94 }
97impl<'a> Node<'a> {
98 /// Construct a new element node with the given tag name and children.
99 #[inline]
100 pub(crate) fn element<Listeners, Attributes, Children>(
101 bump: &'a Bump,
102 tag_name: &'a str,
103 listeners: Listeners,
104 attributes: Attributes,
105 children: Children,
106 ) -> Node<'a>
107 where
108 Listeners: 'a + AsRef<[Listener<'a>]>,
109 Attributes: 'a + AsRef<[Attribute<'a>]>,
110 Children: 'a + AsRef<[Node<'a>]>,
111 {
112 let children: &'a Children = bump.alloc(children);
113 let children: &'a [Node<'a>] = children.as_ref();
115 let listeners: &'a Listeners = bump.alloc(listeners);
116 let listeners: &'a [Listener<'a>] = listeners.as_ref();
118 let attributes: &'a Attributes = bump.alloc(attributes);
119 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
121 Node::Element(ElementNode {
122 tag_name,
123 listeners,
124 attributes,
125 children,
126 })
127 }
129 /// Is this node a text node?
130 pub fn is_text(&self) -> bool {
131 match self {
132 Node::Text(_) => true,
133 _ => false,
134 }
135 }
137 /// Is this node an element?
138 pub fn is_element(&self) -> bool {
139 match self {
140 Node::Element { .. } => true,
141 _ => false,
142 }
143 }
145 /// Construct a new text node with the given text.
146 #[inline]
147 pub(crate) fn text(text: &'a str) -> Node<'a> {
148 Node::Text(TextNode { text })
149 }
152impl<'a> TextNode<'a> {
153 /// Get this text node's text content.
154 pub fn text(&self) -> &'a str {
155 self.text
156 }
159impl<'a> ElementNode<'a> {
160 /// Get this element's tag name.
161 pub fn tag_name(&self) -> &'a str {
162 self.tag_name
163 }
165 /// Get this element's attributes.
166 pub fn attributes(&self) -> &'a [Attribute<'a>] {
167 self.attributes
168 }
170 /// Get this element's attributes.
171 pub fn children(&self) -> &'a [Node<'a>] {
172 self.children
173 }
176union CallbackFatPtr<'a> {
177 callback: ListenerCallback<'a>,
178 parts: (u32, u32),
181impl Listener<'_> {
182 #[inline]
183 pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
184 assert_eq!(
185 mem::size_of::<ListenerCallback>(),
186 mem::size_of::<CallbackFatPtr>()
187 );
189 unsafe {
190 let fat = CallbackFatPtr {
191 callback: self.callback,
192 };
193 let (a, b) = fat.parts;
194 debug_assert!(a != 0);
195 (a, b)
196 }
197 }