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}