on 80c4d59 help sign in
dodrio/src/node.rs
186 lines 4.8 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 event: &'a str,
47 /// The callback to invoke when the event happens.
48 pub callback: ListenerCallback<'a>,
51#[derive(Clone, Debug)]
52pub struct Attribute<'a> {
53 pub name: &'a str,
54 pub value: &'a str,
57impl fmt::Debug for Listener<'_> {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 let (a, b) = self.get_callback_parts();
60 let a = a as *mut u32;
61 let b = b as *mut u32;
62 f.debug_struct("Listener")
63 .field("event", &self.event)
64 .field("callback", &(a, b))
65 .finish()
66 }
69impl<'a> Node<'a> {
70 /// Construct a new element node with the given tag name and children.
71 #[inline]
72 pub fn element<Listeners, Attributes, Children>(
73 bump: &'a Bump,
74 tag_name: &'a str,
75 listeners: Listeners,
76 attributes: Attributes,
77 children: Children,
78 ) -> Node<'a>
79 where
80 Listeners: 'a + AsRef<[Listener<'a>]>,
81 Attributes: 'a + AsRef<[Attribute<'a>]>,
82 Children: 'a + AsRef<[Node<'a>]>,
83 {
84 let children: &'a Children = bump.alloc(children);
85 let children: &'a [Node<'a>] = children.as_ref();
87 let listeners: &'a Listeners = bump.alloc(listeners);
88 let listeners: &'a [Listener<'a>] = listeners.as_ref();
90 let attributes: &'a Attributes = bump.alloc(attributes);
91 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
93 Node::Element(ElementNode {
94 tag_name,
95 listeners,
96 attributes,
97 children,
98 })
99 }
101 /// Is this node a text node?
102 pub fn is_text(&self) -> bool {
103 match self {
104 Node::Text(_) => true,
105 _ => false,
106 }
107 }
109 /// Is this node an element?
110 pub fn is_element(&self) -> bool {
111 match self {
112 Node::Element { .. } => true,
113 _ => false,
114 }
115 }
117 /// Construct a new text node with the given text.
118 #[inline]
119 pub fn text(text: &'a str) -> Node<'a> {
120 Node::Text(TextNode { text })
121 }
124impl<'a> TextNode<'a> {
125 /// Get this text node's text content.
126 pub fn text(&self) -> &'a str {
127 self.text
128 }
131impl<'a> ElementNode<'a> {
132 /// Get this element's tag name.
133 pub fn tag_name(&self) -> &'a str {
134 self.tag_name
135 }
137 /// Get this element's attributes.
138 pub fn attributes(&self) -> &'a [Attribute<'a>] {
139 self.attributes
140 }
142 /// Get this element's attributes.
143 pub fn children(&self) -> &'a [Node<'a>] {
144 self.children
145 }
148union CallbackFatPtr<'a> {
149 callback: ListenerCallback<'a>,
150 parts: (u32, u32),
153impl Listener<'_> {
154 #[inline]
155 pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
156 assert_eq!(
157 mem::size_of::<ListenerCallback>(),
158 mem::size_of::<CallbackFatPtr>()
159 );
161 unsafe {
162 let fat = CallbackFatPtr {
163 callback: self.callback,
164 };
165 let (a, b) = fat.parts;
166 debug_assert!(a != 0);
167 (a, b)
168 }
169 }
172/// Utility function for creating event listeners and downcasting the component
173/// to its `RootRender` concrete type.
174pub fn on<'a, F>(bump: &'a Bump, event: &'a str, callback: F) -> Listener<'a>
175where
176 F: Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static,
178 Listener {
179 event,
180 callback: bump.alloc(
181 move |component: &mut dyn RootRender, vdom: VdomWeak, event: web_sys::Event| {
182 callback(component, vdom, event);
183 },
184 ),
185 }