dioxus_core/mutations.rs
1use crate::{arena::ElementId, AttributeValue, Template};
2
3/// Something that can handle the mutations that are generated by the diffing process and apply them to the Real DOM
4///
5/// This object provides a bunch of important information for a renderer to use patch the Real Dom with the state of the
6/// VirtualDom. This includes the scopes that were modified, the templates that were discovered, and a list of changes
7/// in the form of a [`Mutation`].
8///
9/// These changes are specific to one subtree, so to patch multiple subtrees, you'd need to handle each set separately.
10///
11/// Templates, however, apply to all subtrees, not just target subtree.
12///
13/// Mutations are the only link between the RealDOM and the VirtualDOM.
14pub trait WriteMutations {
15 /// Add these m children to the target element
16 ///
17 /// Id: The ID of the element being mounted to
18 /// M: The number of nodes on the stack to append to the target element
19 fn append_children(&mut self, id: ElementId, m: usize);
20
21 /// Assign the element at the given path the target ElementId.
22 ///
23 /// The path is in the form of a list of indices based on children. Templates cannot have more than 255 children per
24 /// element, hence the use of a single byte.
25 ///
26 /// Path: The path of the child of the topmost node on the stack. A path of `[]` represents the topmost node. A path of `[0]` represents the first child. `[0,1,2]` represents 1st child's 2nd child's 3rd child.
27 /// Id: The ID we're assigning to this element/placeholder. This will be used later to modify the element or replace it with another element.
28 fn assign_node_id(&mut self, path: &'static [u8], id: ElementId);
29
30 /// Create a placeholder in the DOM that we will use later.
31 ///
32 /// Dioxus currently requires the use of placeholders to maintain a re-entrance point for things like list diffing
33 ///
34 /// Id: The ID we're assigning to this element/placeholder. This will be used later to modify the element or replace it with another element.
35 fn create_placeholder(&mut self, id: ElementId);
36
37 /// Create a node specifically for text with the given value
38 ///
39 /// Value: The text content of this text node
40 /// Id: The ID we're assigning to this specific text nodes. This will be used later to modify the element or replace it with another element.
41 fn create_text_node(&mut self, value: &str, id: ElementId);
42
43 /// Load and clone an existing node from a template saved under that specific name
44 ///
45 /// Dioxus guarantees that the renderer will have already been provided the template.
46 /// When the template is picked up in the template list, it should be saved under its "name" - here, the name
47 ///
48 /// Name: The unique "name" of the template based on the template location. When paired with `rsx!`, this is autogenerated
49 /// Index: The index root we loading from the template. The template is stored as a list of nodes. This index represents the position of that root
50 /// Id: The ID we're assigning to this element being loaded from the template (This will be used later to move the element around in lists)
51 fn load_template(&mut self, template: Template, index: usize, id: ElementId);
52
53 /// Replace the target element (given by its ID) with the topmost m nodes on the stack
54 ///
55 /// id: The ID of the node we're going to replace with new nodes
56 /// m: The number of nodes on the stack to replace the target element with
57 fn replace_node_with(&mut self, id: ElementId, m: usize);
58
59 /// Replace an existing element in the template at the given path with the m nodes on the stack
60 ///
61 /// Path: The path of the child of the topmost node on the stack. A path of `[]` represents the topmost node. A path of `[0]` represents the first child. `[0,1,2]` represents 1st child's 2nd child's 3rd child.
62 /// M: The number of nodes on the stack to replace the target element with
63 fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize);
64
65 /// Insert a number of nodes after a given node.
66 ///
67 /// Id: The ID of the node to insert after.
68 /// M: The number of nodes on the stack to insert after the target node.
69 fn insert_nodes_after(&mut self, id: ElementId, m: usize);
70
71 /// Insert a number of nodes before a given node.
72 ///
73 /// Id: The ID of the node to insert before.
74 /// M: The number of nodes on the stack to insert before the target node.
75 fn insert_nodes_before(&mut self, id: ElementId, m: usize);
76
77 /// Set the value of a node's attribute.
78 ///
79 /// Name: The name of the attribute to set.
80 /// NS: The (optional) namespace of the attribute. For instance, "style" is in the "style" namespace.
81 /// Value: The value of the attribute.
82 /// Id: The ID of the node to set the attribute of.
83 fn set_attribute(
84 &mut self,
85 name: &'static str,
86 ns: Option<&'static str>,
87 value: &AttributeValue,
88 id: ElementId,
89 );
90
91 /// Set the text content of a node.
92 ///
93 /// Value: The textcontent of the node
94 /// Id: The ID of the node to set the textcontent of.
95 fn set_node_text(&mut self, value: &str, id: ElementId);
96
97 /// Create a new Event Listener.
98 ///
99 /// Name: The name of the event to listen for.
100 /// Id: The ID of the node to attach the listener to.
101 fn create_event_listener(&mut self, name: &'static str, id: ElementId);
102
103 /// Remove an existing Event Listener.
104 ///
105 /// Name: The name of the event to remove.
106 /// Id: The ID of the node to remove.
107 fn remove_event_listener(&mut self, name: &'static str, id: ElementId);
108
109 /// Remove a particular node from the DOM
110 ///
111 /// Id: The ID of the node to remove.
112 fn remove_node(&mut self, id: ElementId);
113
114 /// Push the given root node onto our stack.
115 ///
116 /// Id: The ID of the root node to push.
117 fn push_root(&mut self, id: ElementId);
118}
119
120/// A `Mutation` represents a single instruction for the renderer to use to modify the UI tree to match the state
121/// of the Dioxus VirtualDom.
122///
123/// These edits can be serialized and sent over the network or through any interface
124#[derive(Debug, PartialEq)]
125pub enum Mutation {
126 /// Add these m children to the target element
127 AppendChildren {
128 /// The ID of the element being mounted to
129 id: ElementId,
130
131 /// The number of nodes on the stack to append to the target element
132 m: usize,
133 },
134
135 /// Assign the element at the given path the target ElementId.
136 ///
137 /// The path is in the form of a list of indices based on children. Templates cannot have more than 255 children per
138 /// element, hence the use of a single byte.
139 AssignId {
140 /// The path of the child of the topmost node on the stack
141 ///
142 /// A path of `[]` represents the topmost node. A path of `[0]` represents the first child.
143 /// `[0,1,2]` represents 1st child's 2nd child's 3rd child.
144 path: &'static [u8],
145
146 /// The ID we're assigning to this element/placeholder.
147 ///
148 /// This will be used later to modify the element or replace it with another element.
149 id: ElementId,
150 },
151
152 /// Create a placeholder in the DOM that we will use later.
153 ///
154 /// Dioxus currently requires the use of placeholders to maintain a re-entrance point for things like list diffing
155 CreatePlaceholder {
156 /// The ID we're assigning to this element/placeholder.
157 ///
158 /// This will be used later to modify the element or replace it with another element.
159 id: ElementId,
160 },
161
162 /// Create a node specifically for text with the given value
163 CreateTextNode {
164 /// The text content of this text node
165 value: String,
166
167 /// The ID we're assigning to this specific text nodes
168 ///
169 /// This will be used later to modify the element or replace it with another element.
170 id: ElementId,
171 },
172
173 /// Load and clone an existing node from a template with a given ID
174 ///
175 /// Dioxus guarantees that the renderer will have already been provided the template.
176 /// When the template is picked up in the template list, it should be saved under its "name" - here, the name
177 LoadTemplate {
178 /// Which root are we loading from the template?
179 ///
180 /// The template is stored as a list of nodes. This index represents the position of that root
181 index: usize,
182
183 /// The ID we're assigning to this element being loaded from the template
184 ///
185 /// This will be used later to move the element around in lists
186 id: ElementId,
187 },
188
189 /// Replace the target element (given by its ID) with the topmost m nodes on the stack
190 ReplaceWith {
191 /// The ID of the node we're going to replace with
192 id: ElementId,
193
194 /// The number of nodes on the stack to replace the target element with
195 m: usize,
196 },
197
198 /// Replace an existing element in the template at the given path with the m nodes on the stack
199 ReplacePlaceholder {
200 /// The path of the child of the topmost node on the stack
201 ///
202 /// A path of `[]` represents the topmost node. A path of `[0]` represents the first child.
203 /// `[0,1,2]` represents 1st child's 2nd child's 3rd child.
204 path: &'static [u8],
205
206 /// The number of nodes on the stack to replace the target element with
207 m: usize,
208 },
209
210 /// Insert a number of nodes after a given node.
211 InsertAfter {
212 /// The ID of the node to insert after.
213 id: ElementId,
214
215 /// The number of nodes on the stack to insert after the target node.
216 m: usize,
217 },
218
219 /// Insert a number of nodes before a given node.
220 InsertBefore {
221 /// The ID of the node to insert before.
222 id: ElementId,
223
224 /// The number of nodes on the stack to insert before the target node.
225 m: usize,
226 },
227
228 /// Set the value of a node's attribute.
229 SetAttribute {
230 /// The name of the attribute to set.
231 name: &'static str,
232
233 /// The (optional) namespace of the attribute.
234 /// For instance, "style" is in the "style" namespace.
235 ns: Option<&'static str>,
236
237 /// The value of the attribute.
238 value: AttributeValue,
239
240 /// The ID of the node to set the attribute of.
241 id: ElementId,
242 },
243
244 /// Set the textcontent of a node.
245 SetText {
246 /// The textcontent of the node
247 value: String,
248
249 /// The ID of the node to set the textcontent of.
250 id: ElementId,
251 },
252
253 /// Create a new Event Listener.
254 NewEventListener {
255 /// The name of the event to listen for.
256 name: String,
257
258 /// The ID of the node to attach the listener to.
259 id: ElementId,
260 },
261
262 /// Remove an existing Event Listener.
263 RemoveEventListener {
264 /// The name of the event to remove.
265 name: String,
266
267 /// The ID of the node to remove.
268 id: ElementId,
269 },
270
271 /// Remove a particular node from the DOM
272 Remove {
273 /// The ID of the node to remove.
274 id: ElementId,
275 },
276
277 /// Push the given root node onto our stack.
278 PushRoot {
279 /// The ID of the root node to push.
280 id: ElementId,
281 },
282}
283
284/// A static list of mutations that can be applied to the DOM. Note: this list does not contain any `Any` attribute values
285#[derive(Debug, PartialEq, Default)]
286pub struct Mutations {
287 /// Any mutations required to patch the renderer to match the layout of the VirtualDom
288 pub edits: Vec<Mutation>,
289}
290
291impl WriteMutations for Mutations {
292 fn append_children(&mut self, id: ElementId, m: usize) {
293 self.edits.push(Mutation::AppendChildren { id, m })
294 }
295
296 fn assign_node_id(&mut self, path: &'static [u8], id: ElementId) {
297 self.edits.push(Mutation::AssignId { path, id })
298 }
299
300 fn create_placeholder(&mut self, id: ElementId) {
301 self.edits.push(Mutation::CreatePlaceholder { id })
302 }
303
304 fn create_text_node(&mut self, value: &str, id: ElementId) {
305 self.edits.push(Mutation::CreateTextNode {
306 value: value.into(),
307 id,
308 })
309 }
310
311 fn load_template(&mut self, _template: Template, index: usize, id: ElementId) {
312 self.edits.push(Mutation::LoadTemplate { index, id })
313 }
314
315 fn replace_node_with(&mut self, id: ElementId, m: usize) {
316 self.edits.push(Mutation::ReplaceWith { id, m })
317 }
318
319 fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize) {
320 self.edits.push(Mutation::ReplacePlaceholder { path, m })
321 }
322
323 fn insert_nodes_after(&mut self, id: ElementId, m: usize) {
324 self.edits.push(Mutation::InsertAfter { id, m })
325 }
326
327 fn insert_nodes_before(&mut self, id: ElementId, m: usize) {
328 self.edits.push(Mutation::InsertBefore { id, m })
329 }
330
331 fn set_attribute(
332 &mut self,
333 name: &'static str,
334 ns: Option<&'static str>,
335 value: &AttributeValue,
336 id: ElementId,
337 ) {
338 self.edits.push(Mutation::SetAttribute {
339 name,
340 ns,
341 value: match value {
342 AttributeValue::Text(s) => AttributeValue::Text(s.clone()),
343 AttributeValue::Bool(b) => AttributeValue::Bool(*b),
344 AttributeValue::Float(n) => AttributeValue::Float(*n),
345 AttributeValue::Int(n) => AttributeValue::Int(*n),
346 AttributeValue::None => AttributeValue::None,
347 _ => panic!("Cannot serialize attribute value"),
348 },
349 id,
350 })
351 }
352
353 fn set_node_text(&mut self, value: &str, id: ElementId) {
354 self.edits.push(Mutation::SetText {
355 value: value.into(),
356 id,
357 })
358 }
359
360 fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
361 self.edits.push(Mutation::NewEventListener {
362 name: name.into(),
363 id,
364 })
365 }
366
367 fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
368 self.edits.push(Mutation::RemoveEventListener {
369 name: name.into(),
370 id,
371 })
372 }
373
374 fn remove_node(&mut self, id: ElementId) {
375 self.edits.push(Mutation::Remove { id })
376 }
377
378 fn push_root(&mut self, id: ElementId) {
379 self.edits.push(Mutation::PushRoot { id })
380 }
381}
382
383/// A struct that ignores all mutations
384pub struct NoOpMutations;
385
386impl WriteMutations for NoOpMutations {
387 fn append_children(&mut self, _: ElementId, _: usize) {}
388
389 fn assign_node_id(&mut self, _: &'static [u8], _: ElementId) {}
390
391 fn create_placeholder(&mut self, _: ElementId) {}
392
393 fn create_text_node(&mut self, _: &str, _: ElementId) {}
394
395 fn load_template(&mut self, _: Template, _: usize, _: ElementId) {}
396
397 fn replace_node_with(&mut self, _: ElementId, _: usize) {}
398
399 fn replace_placeholder_with_nodes(&mut self, _: &'static [u8], _: usize) {}
400
401 fn insert_nodes_after(&mut self, _: ElementId, _: usize) {}
402
403 fn insert_nodes_before(&mut self, _: ElementId, _: usize) {}
404
405 fn set_attribute(
406 &mut self,
407 _: &'static str,
408 _: Option<&'static str>,
409 _: &AttributeValue,
410 _: ElementId,
411 ) {
412 }
413
414 fn set_node_text(&mut self, _: &str, _: ElementId) {}
415
416 fn create_event_listener(&mut self, _: &'static str, _: ElementId) {}
417
418 fn remove_event_listener(&mut self, _: &'static str, _: ElementId) {}
419
420 fn remove_node(&mut self, _: ElementId) {}
421
422 fn push_root(&mut self, _: ElementId) {}
423}