dioxus.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //! Integration between Dioxus and the RealDom
  2. use crate::tree::TreeMut;
  3. use dioxus_core::{BorrowedAttributeValue, ElementId, Mutations, TemplateNode};
  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. fn set_element_id<V: FromAnyValue + Send + Sync>(
  44. &mut self,
  45. mut node: NodeMut<V>,
  46. element_id: ElementId,
  47. ) {
  48. let node_id = node.id();
  49. node.insert(ElementIdComponent(element_id));
  50. if self.node_id_mapping.len() <= element_id.0 {
  51. self.node_id_mapping.resize(element_id.0 + 1, None);
  52. }
  53. self.node_id_mapping[element_id.0] = Some(node_id);
  54. }
  55. fn load_child<V: FromAnyValue + Send + Sync>(&self, rdom: &RealDom<V>, path: &[u8]) -> NodeId {
  56. let mut current = rdom.get(*self.stack.last().unwrap()).unwrap();
  57. for i in path {
  58. let new_id = current.child_ids()[*i as usize];
  59. current = rdom.get(new_id).unwrap();
  60. }
  61. current.id()
  62. }
  63. /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state.
  64. pub fn apply_mutations<V: FromAnyValue + Send + Sync>(
  65. &mut self,
  66. rdom: &mut RealDom<V>,
  67. mutations: Mutations,
  68. ) {
  69. for template in mutations.templates {
  70. let mut template_root_ids = Vec::new();
  71. for root in template.roots {
  72. let id = create_template_node(rdom, root);
  73. template_root_ids.push(id);
  74. }
  75. self.templates
  76. .insert(template.name.to_string(), template_root_ids);
  77. }
  78. for e in mutations.edits {
  79. use dioxus_core::Mutation::*;
  80. match e {
  81. AppendChildren { id, m } => {
  82. let children = self.stack.split_off(self.stack.len() - m);
  83. let parent = self.element_to_node_id(id);
  84. for child in children {
  85. rdom.get_mut(parent).unwrap().add_child(child);
  86. }
  87. }
  88. AssignId { path, id } => {
  89. let node_id = self.load_child(rdom, path);
  90. self.set_element_id(rdom.get_mut(node_id).unwrap(), id);
  91. }
  92. CreatePlaceholder { id } => {
  93. let node = NodeType::Placeholder;
  94. let node = rdom.create_node(node);
  95. let node_id = node.id();
  96. self.set_element_id(node, id);
  97. self.stack.push(node_id);
  98. }
  99. CreateTextNode { value, id } => {
  100. let node_data = NodeType::Text(TextNode {
  101. listeners: FxHashSet::default(),
  102. text: value.to_string(),
  103. });
  104. let node = rdom.create_node(node_data);
  105. let node_id = node.id();
  106. self.set_element_id(node, id);
  107. self.stack.push(node_id);
  108. }
  109. HydrateText { path, value, id } => {
  110. let node_id = self.load_child(rdom, path);
  111. let node = rdom.get_mut(node_id).unwrap();
  112. self.set_element_id(node, id);
  113. let mut node = rdom.get_mut(node_id).unwrap();
  114. let node_type_mut = node.node_type_mut();
  115. if let NodeTypeMut::Text(mut text) = node_type_mut {
  116. *text.text_mut() = value.to_string();
  117. } else {
  118. drop(node_type_mut);
  119. node.set_type(NodeType::Text(TextNode {
  120. text: value.to_string(),
  121. listeners: FxHashSet::default(),
  122. }));
  123. }
  124. }
  125. LoadTemplate { name, index, id } => {
  126. let template_id = self.templates[name][index];
  127. let clone_id = rdom.get_mut(template_id).unwrap().clone_node();
  128. let clone = rdom.get_mut(clone_id).unwrap();
  129. self.set_element_id(clone, id);
  130. self.stack.push(clone_id);
  131. }
  132. ReplaceWith { id, m } => {
  133. let new_nodes = self.stack.split_off(self.stack.len() - m);
  134. let old_node_id = self.element_to_node_id(id);
  135. for new in new_nodes {
  136. let mut node = rdom.get_mut(new).unwrap();
  137. node.insert_before(old_node_id);
  138. }
  139. rdom.get_mut(old_node_id).unwrap().remove();
  140. }
  141. ReplacePlaceholder { path, m } => {
  142. let new_nodes = self.stack.split_off(self.stack.len() - m);
  143. let old_node_id = self.load_child(rdom, path);
  144. for new in new_nodes {
  145. let mut node = rdom.get_mut(new).unwrap();
  146. node.insert_before(old_node_id);
  147. }
  148. rdom.get_mut(old_node_id).unwrap().remove();
  149. }
  150. InsertAfter { id, m } => {
  151. let new_nodes = self.stack.split_off(self.stack.len() - m);
  152. let old_node_id = self.element_to_node_id(id);
  153. for new in new_nodes.into_iter().rev() {
  154. let mut node = rdom.get_mut(new).unwrap();
  155. node.insert_after(old_node_id);
  156. }
  157. }
  158. InsertBefore { id, m } => {
  159. let new_nodes = self.stack.split_off(self.stack.len() - m);
  160. let old_node_id = self.element_to_node_id(id);
  161. for new in new_nodes {
  162. rdom.tree_mut().insert_before(old_node_id, new);
  163. }
  164. }
  165. SetAttribute {
  166. name,
  167. value,
  168. id,
  169. ns,
  170. } => {
  171. let node_id = self.element_to_node_id(id);
  172. let mut node = rdom.get_mut(node_id).unwrap();
  173. let mut node_type_mut = node.node_type_mut();
  174. if let NodeTypeMut::Element(element) = &mut node_type_mut {
  175. if let BorrowedAttributeValue::None = &value {
  176. element.remove_attribute(&OwnedAttributeDiscription {
  177. name: name.to_string(),
  178. namespace: ns.map(|s| s.to_string()),
  179. });
  180. } else {
  181. element.set_attribute(
  182. OwnedAttributeDiscription {
  183. name: name.to_string(),
  184. namespace: ns.map(|s| s.to_string()),
  185. },
  186. OwnedAttributeValue::from(value),
  187. );
  188. }
  189. }
  190. }
  191. SetText { value, id } => {
  192. let node_id = self.element_to_node_id(id);
  193. let mut node = rdom.get_mut(node_id).unwrap();
  194. let node_type_mut = node.node_type_mut();
  195. if let NodeTypeMut::Text(mut text) = node_type_mut {
  196. *text.text_mut() = value.to_string();
  197. }
  198. }
  199. NewEventListener { name, id } => {
  200. let node_id = self.element_to_node_id(id);
  201. let mut node = rdom.get_mut(node_id).unwrap();
  202. node.add_event_listener(name);
  203. }
  204. RemoveEventListener { id, name } => {
  205. let node_id = self.element_to_node_id(id);
  206. let mut node = rdom.get_mut(node_id).unwrap();
  207. node.remove_event_listener(name);
  208. }
  209. Remove { id } => {
  210. let node_id = self.element_to_node_id(id);
  211. rdom.get_mut(node_id).unwrap().remove();
  212. }
  213. PushRoot { id } => {
  214. let node_id = self.element_to_node_id(id);
  215. self.stack.push(node_id);
  216. }
  217. }
  218. }
  219. }
  220. }
  221. fn create_template_node<V: FromAnyValue + Send + Sync>(
  222. rdom: &mut RealDom<V>,
  223. node: &TemplateNode,
  224. ) -> NodeId {
  225. match node {
  226. TemplateNode::Element {
  227. tag,
  228. namespace,
  229. attrs,
  230. children,
  231. } => {
  232. let node = NodeType::Element(ElementNode {
  233. tag: tag.to_string(),
  234. namespace: namespace.map(|s| s.to_string()),
  235. attributes: attrs
  236. .iter()
  237. .filter_map(|attr| match attr {
  238. dioxus_core::TemplateAttribute::Static {
  239. name,
  240. value,
  241. namespace,
  242. } => Some((
  243. OwnedAttributeDiscription {
  244. namespace: namespace.map(|s| s.to_string()),
  245. name: name.to_string(),
  246. },
  247. OwnedAttributeValue::Text(value.to_string()),
  248. )),
  249. dioxus_core::TemplateAttribute::Dynamic { .. } => None,
  250. })
  251. .collect(),
  252. listeners: FxHashSet::default(),
  253. });
  254. let node_id = rdom.create_node(node).id();
  255. for child in *children {
  256. let child_id = create_template_node(rdom, child);
  257. rdom.get_mut(node_id).unwrap().add_child(child_id);
  258. }
  259. node_id
  260. }
  261. TemplateNode::Text { text } => rdom
  262. .create_node(NodeType::Text(TextNode {
  263. text: text.to_string(),
  264. ..Default::default()
  265. }))
  266. .id(),
  267. TemplateNode::Dynamic { .. } => rdom.create_node(NodeType::Placeholder).id(),
  268. TemplateNode::DynamicText { .. } => {
  269. rdom.create_node(NodeType::Text(TextNode::default())).id()
  270. }
  271. }
  272. }
  273. /// A trait that extends the `NodeImmutable` trait with methods that are useful for dioxus.
  274. pub trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
  275. /// Returns the id of the element that this node is mounted to.
  276. /// Not all nodes are mounted to an element, only nodes with dynamic content that have been renderered will have an id.
  277. fn mounted_id(&self) -> Option<ElementId> {
  278. let id = self.get::<ElementIdComponent>();
  279. id.map(|id| id.0)
  280. }
  281. }
  282. impl<T: NodeImmutable<V>, V: FromAnyValue + Send + Sync> NodeImmutableDioxusExt<V> for T {}