on d01204a help sign in
dodrio/src/node.rs
219 lines 6.0 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>),
15 /// An element with a namespace and potentially with attributes and children.
16 NamespacedElement(&'a str, ElementNode<'a>),
19/// Text nodes are just a string of text. They cannot have attributes or
20/// children.
21#[derive(Debug, Clone)]
22pub struct TextNode<'a> {
23 pub(crate) text: &'a str,
26/// Elements have a tag name, zero or more attributes, and zero or more
27/// children.
28#[derive(Debug, Clone)]
29pub struct ElementNode<'a> {
30 pub(crate) tag_name: &'a str,
31 pub(crate) listeners: &'a [Listener<'a>],
32 pub(crate) attributes: &'a [Attribute<'a>],
33 pub(crate) children: &'a [Node<'a>],
36/// An event listener callback function.
37///
38/// It takes three parameters:
39///
40/// 1. The virtual DOM's root rendering component.
41/// 2. A capability to scheduler virtual DOM re-rendering.
42/// 3. The event that ocurred.
43pub type ListenerCallback<'a> =
44 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
46/// An event listener.
47pub struct Listener<'a> {
48 /// The type of event to listen for.
49 pub(crate) event: &'a str,
50 /// The callback to invoke when the event happens.
51 pub(crate) callback: ListenerCallback<'a>,
54/// An attribute on a DOM node, such as `id="my-thing"` or
55/// `href="https://example.com"`.
56#[derive(Clone, Debug)]
57pub struct Attribute<'a> {
58 pub(crate) name: &'a str,
59 pub(crate) value: &'a str,
62impl fmt::Debug for Listener<'_> {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let (a, b) = self.get_callback_parts();
65 let a = a as *mut u32;
66 let b = b as *mut u32;
67 f.debug_struct("Listener")
68 .field("event", &self.event)
69 .field("callback", &(a, b))
70 .finish()
71 }
74impl<'a> Attribute<'a> {
75 /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
76 #[inline]
77 pub fn name(&self) -> &'a str {
78 self.name
79 }
81 /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
82 #[inline]
83 pub fn value(&self) -> &'a str {
84 self.value
85 }
87 /// Certain attributes are considered "volatile" and can change via user
88 /// input that we can't see when diffing against the old virtual DOM. For
89 /// these attributes, we want to always re-set the attribute on the physical
90 /// DOM node, even if the old and new virtual DOM nodes have the same value.
91 #[inline]
92 pub(crate) fn is_volatile(&self) -> bool {
93 match self.name {
94 "value" | "checked" | "selected" => true,
95 _ => false,
96 }
97 }
100impl<'a> Node<'a> {
101 // Construct a new Node of type element with given tag name and children
102 #[inline]
103 pub(crate) fn element<Listeners, Attributes, Children>(
104 bump: &'a Bump,
105 tag_name: &'a str,
106 listeners: Listeners,
107 attributes: Attributes,
108 children: Children,
109 ) -> Node<'a>
110 where
111 Listeners: 'a + AsRef<[Listener<'a>]>,
112 Attributes: 'a + AsRef<[Attribute<'a>]>,
113 Children: 'a + AsRef<[Node<'a>]>,
114 {
115 let element_node = Node::element_node(bump, tag_name, listeners, attributes, children);
116 Node::Element(element_node)
117 }
119 /// Construct a new element node with the given tag name and children.
120 #[inline]
121 pub(crate) fn element_node<Listeners, Attributes, Children>(
122 bump: &'a Bump,
123 tag_name: &'a str,
124 listeners: Listeners,
125 attributes: Attributes,
126 children: Children,
127 ) -> ElementNode<'a>
128 where
129 Listeners: 'a + AsRef<[Listener<'a>]>,
130 Attributes: 'a + AsRef<[Attribute<'a>]>,
131 Children: 'a + AsRef<[Node<'a>]>,
132 {
133 let children: &'a Children = bump.alloc(children);
134 let children: &'a [Node<'a>] = children.as_ref();
136 let listeners: &'a Listeners = bump.alloc(listeners);
137 let listeners: &'a [Listener<'a>] = listeners.as_ref();
139 let attributes: &'a Attributes = bump.alloc(attributes);
140 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
142 ElementNode {
143 tag_name,
144 listeners,
145 attributes,
146 children,
147 }
148 }
150 /// Is this node a text node?
151 pub fn is_text(&self) -> bool {
152 match self {
153 Node::Text(_) => true,
154 _ => false,
155 }
156 }
158 /// Is this node an element?
159 pub fn is_element(&self) -> bool {
160 match self {
161 Node::Element { .. } => true,
162 _ => false,
163 }
164 }
166 /// Construct a new text node with the given text.
167 #[inline]
168 pub(crate) fn text(text: &'a str) -> Node<'a> {
169 Node::Text(TextNode { text })
170 }
173impl<'a> TextNode<'a> {
174 /// Get this text node's text content.
175 pub fn text(&self) -> &'a str {
176 self.text
177 }
180impl<'a> ElementNode<'a> {
181 /// Get this element's tag name.
182 pub fn tag_name(&self) -> &'a str {
183 self.tag_name
184 }
186 /// Get this element's attributes.
187 pub fn attributes(&self) -> &'a [Attribute<'a>] {
188 self.attributes
189 }
191 /// Get this element's attributes.
192 pub fn children(&self) -> &'a [Node<'a>] {
193 self.children
194 }
197union CallbackFatPtr<'a> {
198 callback: ListenerCallback<'a>,
199 parts: (u32, u32),
202impl Listener<'_> {
203 #[inline]
204 pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
205 assert_eq!(
206 mem::size_of::<ListenerCallback>(),
207 mem::size_of::<CallbackFatPtr>()
208 );
210 unsafe {
211 let fat = CallbackFatPtr {
212 callback: self.callback,
213 };
214 let (a, b) = fat.parts;
215 debug_assert!(a != 0);
216 (a, b)
217 }
218 }