dioxus.rs 12 KB

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