freya_native_core/
dioxus.rs

1//! Integration between Dioxus and the RealDom
2
3use std::str::FromStr;
4
5use dioxus_core::{
6    AttributeValue,
7    ElementId,
8    Template,
9    TemplateNode,
10    WriteMutations,
11};
12use rustc_hash::{
13    FxHashMap,
14    FxHashSet,
15};
16use shipyard::Component;
17
18use crate::{
19    node::{
20        ElementNode,
21        FromAnyValue,
22        NodeType,
23        OwnedAttributeValue,
24    },
25    prelude::*,
26    real_dom::NodeTypeMut,
27    tags::TagName,
28    tree::TreeMut,
29    NodeId,
30};
31
32#[derive(Component)]
33struct ElementIdComponent(ElementId);
34
35/// The state of the Dioxus integration with the RealDom
36pub struct DioxusState {
37    templates: FxHashMap<Template, Vec<NodeId>>,
38    pub stack: Vec<NodeId>,
39    node_id_mapping: Vec<Option<NodeId>>,
40}
41
42impl DioxusState {
43    /// Initialize the DioxusState in the RealDom
44    pub fn create<V: FromAnyValue + Send + Sync>(rdom: &mut RealDom<V>) -> Self {
45        let root_id = rdom.root_id();
46        let mut root = rdom.get_mut(root_id).unwrap();
47        root.insert(ElementIdComponent(ElementId(0)));
48        Self {
49            templates: FxHashMap::default(),
50            stack: vec![root_id],
51            node_id_mapping: vec![Some(root_id)],
52        }
53    }
54
55    /// Convert an ElementId to a NodeId
56    pub fn element_to_node_id(&self, element_id: ElementId) -> NodeId {
57        self.try_element_to_node_id(element_id).unwrap()
58    }
59
60    /// Attempt to convert an ElementId to a NodeId. This will return None if the ElementId is not in the RealDom.
61    pub fn try_element_to_node_id(&self, element_id: ElementId) -> Option<NodeId> {
62        self.node_id_mapping.get(element_id.0).copied().flatten()
63    }
64
65    /// Create a mutation writer for the RealDom
66    pub fn create_mutation_writer<'a, V: FromAnyValue + Send + Sync>(
67        &'a mut self,
68        rdom: &'a mut RealDom<V>,
69    ) -> DioxusNativeCoreMutationWriter<'a, V> {
70        DioxusNativeCoreMutationWriter { rdom, state: self }
71    }
72
73    fn set_element_id<V: FromAnyValue + Send + Sync>(
74        &mut self,
75        mut node: NodeMut<V>,
76        element_id: ElementId,
77    ) {
78        let node_id = node.id();
79        node.insert(ElementIdComponent(element_id));
80        if self.node_id_mapping.len() <= element_id.0 {
81            self.node_id_mapping.resize(element_id.0 + 1, None);
82        } else if let Some(mut node) =
83            self.node_id_mapping[element_id.0].and_then(|id| node.real_dom_mut().get_mut(id))
84        {
85            node.remove();
86        }
87
88        self.node_id_mapping[element_id.0] = Some(node_id);
89    }
90
91    fn load_child<V: FromAnyValue + Send + Sync>(&self, rdom: &RealDom<V>, path: &[u8]) -> NodeId {
92        let mut current = rdom.get(*self.stack.last().unwrap()).unwrap();
93        for i in path {
94            let new_id = current.child_ids()[*i as usize];
95            current = rdom.get(new_id).unwrap();
96        }
97        current.id()
98    }
99}
100
101/// A writer for mutations that can be used with the RealDom.
102pub struct DioxusNativeCoreMutationWriter<'a, V: FromAnyValue + Send + Sync = ()> {
103    /// The realdom associated with this writer
104    pub rdom: &'a mut RealDom<V>,
105
106    /// The state associated with this writer
107    pub state: &'a mut DioxusState,
108}
109
110impl<V: FromAnyValue + Send + Sync> WriteMutations for DioxusNativeCoreMutationWriter<'_, V> {
111    // fn register_template(&mut self, template: dioxus_core::prelude::Template) {
112    //     let mut template_root_ids = Vec::new();
113    //     for root in template.roots {
114    //         let id = create_template_node(self.rdom, root);
115    //         template_root_ids.push(id);
116    //     }
117    //     self.state
118    //         .templates
119    //         .insert(template.name.to_string(), template_root_ids);
120    // }
121
122    fn append_children(&mut self, id: ElementId, m: usize) {
123        let children = self.state.stack.split_off(self.state.stack.len() - m);
124        let parent_id = self.state.element_to_node_id(id);
125        let mut parent = self.rdom.get_mut(parent_id).unwrap();
126        for child in children {
127            parent.add_child(child);
128        }
129    }
130
131    fn assign_node_id(&mut self, path: &'static [u8], id: ElementId) {
132        let node_id = self.state.load_child(self.rdom, path);
133        self.state
134            .set_element_id(self.rdom.get_mut(node_id).unwrap(), id);
135    }
136
137    fn create_placeholder(&mut self, id: ElementId) {
138        let node = NodeType::Placeholder;
139        let node = self.rdom.create_node(node);
140        let node_id = node.id();
141        self.state.set_element_id(node, id);
142        self.state.stack.push(node_id);
143    }
144
145    fn create_text_node(&mut self, value: &str, id: ElementId) {
146        let node_data = NodeType::Text(value.to_string());
147        let node = self.rdom.create_node(node_data);
148        let node_id = node.id();
149        self.state.set_element_id(node, id);
150        self.state.stack.push(node_id);
151    }
152
153    fn load_template(&mut self, template: Template, index: usize, id: ElementId) {
154        let template_entry = self.state.templates.entry(template).or_insert_with(|| {
155            let template_root_ids: Vec<NodeId> = template
156                .roots
157                .iter()
158                .map(|root| create_template_node(self.rdom, root))
159                .collect();
160
161            template_root_ids
162        });
163
164        let template_node_id = template_entry[index];
165        let clone = self.rdom.deep_clone_node(template_node_id);
166        let clone_id = clone.id();
167        self.state.set_element_id(clone, id);
168        self.state.stack.push(clone_id);
169    }
170
171    fn replace_node_with(&mut self, id: ElementId, m: usize) {
172        let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
173        let old_node_id = self.state.element_to_node_id(id);
174        for new in new_nodes {
175            let mut node = self.rdom.get_mut(new).unwrap();
176            node.insert_before(old_node_id);
177        }
178        self.rdom.get_mut(old_node_id).unwrap().remove();
179    }
180
181    fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize) {
182        let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
183        let old_node_id = self.state.load_child(self.rdom, path);
184        for new in new_nodes {
185            let mut node = self.rdom.get_mut(new).unwrap();
186            node.insert_before(old_node_id);
187        }
188        self.rdom.get_mut(old_node_id).unwrap().remove();
189    }
190
191    fn insert_nodes_after(&mut self, id: ElementId, m: usize) {
192        let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
193        let old_node_id = self.state.element_to_node_id(id);
194        for new in new_nodes.into_iter().rev() {
195            let mut node = self.rdom.get_mut(new).unwrap();
196            node.insert_after(old_node_id);
197        }
198    }
199
200    fn insert_nodes_before(&mut self, id: ElementId, m: usize) {
201        let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
202        let old_node_id = self.state.element_to_node_id(id);
203        for new in new_nodes {
204            self.rdom.tree_mut().insert_before(old_node_id, new);
205        }
206    }
207
208    fn set_attribute(
209        &mut self,
210        name: &'static str,
211        _ns: Option<&'static str>,
212        value: &AttributeValue,
213        id: ElementId,
214    ) {
215        let node_id = self.state.element_to_node_id(id);
216        let mut node = self.rdom.get_mut(node_id).unwrap();
217        let mut node_type_mut = node.node_type_mut();
218        if let NodeTypeMut::Element(element) = &mut node_type_mut {
219            let attribute = AttributeName::from_str(name).expect("Unexpected");
220            if let AttributeValue::None = &value {
221                element.remove_attribute(&attribute);
222            } else {
223                element.set_attribute(attribute, OwnedAttributeValue::from(value));
224            }
225        }
226    }
227
228    fn set_node_text(&mut self, value: &str, id: ElementId) {
229        let node_id = self.state.element_to_node_id(id);
230        let mut node = self.rdom.get_mut(node_id).unwrap();
231        let node_type_mut = node.node_type_mut();
232        if let NodeTypeMut::Text(mut text) = node_type_mut {
233            *text.text_mut() = value.to_string();
234        }
235    }
236
237    fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
238        let node_id = self.state.element_to_node_id(id);
239        let mut node = self.rdom.get_mut(node_id).unwrap();
240        node.add_event_listener(EventName::from_str(name).expect("Unexpected."));
241    }
242
243    fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
244        let node_id = self.state.element_to_node_id(id);
245        let mut node = self.rdom.get_mut(node_id).unwrap();
246        node.remove_event_listener(&EventName::from_str(name).expect("Unexpected."));
247    }
248
249    fn remove_node(&mut self, id: ElementId) {
250        let node_id = self.state.element_to_node_id(id);
251        self.rdom.get_mut(node_id).unwrap().remove();
252    }
253
254    fn push_root(&mut self, id: ElementId) {
255        let node_id = self.state.element_to_node_id(id);
256        self.state.stack.push(node_id);
257    }
258}
259
260fn create_template_node<V: FromAnyValue + Send + Sync>(
261    rdom: &mut RealDom<V>,
262    node: &TemplateNode,
263) -> NodeId {
264    match node {
265        TemplateNode::Element {
266            tag,
267            attrs,
268            children,
269            ..
270        } => {
271            let node = NodeType::Element(ElementNode {
272                tag: TagName::from_str(tag).expect("Unexpected."),
273                attributes: attrs
274                    .iter()
275                    .filter_map(|attr| match attr {
276                        dioxus_core::TemplateAttribute::Static { name, value, .. } => Some((
277                            AttributeName::from_str(name).expect("Unexpected."),
278                            OwnedAttributeValue::Text(value.to_string()),
279                        )),
280                        dioxus_core::TemplateAttribute::Dynamic { .. } => None,
281                    })
282                    .collect(),
283                listeners: FxHashSet::default(),
284            });
285            let node_id = rdom.create_node(node).id();
286            for child in *children {
287                let child_id = create_template_node(rdom, child);
288                rdom.get_mut(node_id).unwrap().add_child(child_id);
289            }
290            node_id
291        }
292        TemplateNode::Text { text } => rdom.create_node(NodeType::Text(text.to_string())).id(),
293        TemplateNode::Dynamic { .. } => rdom.create_node(NodeType::Placeholder).id(),
294    }
295}
296
297/// A trait that extends the `NodeImmutable` trait with methods that are useful for dioxus.
298pub trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
299    /// Returns the id of the element that this node is mounted to.
300    /// Not all nodes are mounted to an element, only nodes with dynamic content that have been renderered will have an id.
301    fn mounted_id(&self) -> Option<ElementId> {
302        let id = self.get::<ElementIdComponent>();
303        id.map(|id| id.0)
304    }
305}
306
307impl<T: NodeImmutable<V>, V: FromAnyValue + Send + Sync> NodeImmutableDioxusExt<V> for T {}