nodes.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. use crate::{
  2. any_props::AnyProps, arena::ElementId, Element, Event, LazyNodes, ScopeId, ScopeState,
  3. };
  4. use bumpalo::boxed::Box as BumpBox;
  5. use bumpalo::Bump;
  6. use std::{
  7. any::{Any, TypeId},
  8. cell::{Cell, RefCell},
  9. fmt::Arguments,
  10. future::Future,
  11. };
  12. pub type TemplateId = &'static str;
  13. /// The actual state of the component's most recent computation
  14. ///
  15. /// Because Dioxus accepts components in the form of `async fn(Scope) -> Result<VNode>`, we need to support both
  16. /// sync and async versions.
  17. ///
  18. /// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
  19. /// you might need to handle the case where there's no node immediately ready.
  20. pub enum RenderReturn<'a> {
  21. /// A currently-available element
  22. Sync(Element<'a>),
  23. /// An ongoing future that will resolve to a [`Element`]
  24. Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
  25. }
  26. /// A reference to a template along with any context needed to hydrate it
  27. ///
  28. /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
  29. /// static parts of the template.
  30. #[derive(Debug, Clone)]
  31. pub struct VNode<'a> {
  32. /// The key given to the root of this template.
  33. ///
  34. /// In fragments, this is the key of the first child. In other cases, it is the key of the root.
  35. pub key: Option<&'a str>,
  36. /// When rendered, this template will be linked to its parent manually
  37. pub parent: Option<ElementId>,
  38. /// The static nodes and static descriptor of the template
  39. pub template: Template<'static>,
  40. /// The IDs for the roots of this template - to be used when moving the template around and removing it from
  41. /// the actual Dom
  42. pub root_ids: &'a [Cell<ElementId>],
  43. /// The dynamic parts of the template
  44. pub dynamic_nodes: &'a [DynamicNode<'a>],
  45. /// The dynamic parts of the template
  46. pub dynamic_attrs: &'a [Attribute<'a>],
  47. }
  48. impl<'a> VNode<'a> {
  49. /// Create a template with no nodes that will be skipped over during diffing
  50. pub fn empty() -> Element<'a> {
  51. Ok(VNode {
  52. key: None,
  53. parent: None,
  54. root_ids: &[],
  55. dynamic_nodes: &[],
  56. dynamic_attrs: &[],
  57. template: Template {
  58. name: "dioxus-empty",
  59. roots: &[],
  60. node_paths: &[],
  61. attr_paths: &[],
  62. },
  63. })
  64. }
  65. /// Load a dynamic root at the given index
  66. ///
  67. /// Returns [`None`] if the root is actually a static node (Element/Text)
  68. pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode<'a>> {
  69. match &self.template.roots[idx] {
  70. TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
  71. TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
  72. Some(&self.dynamic_nodes[*id])
  73. }
  74. }
  75. }
  76. pub(crate) fn clear_listeners(&self) {
  77. for attr in self.dynamic_attrs {
  78. if let AttributeValue::Listener(l) = &attr.value {
  79. l.borrow_mut().take();
  80. }
  81. }
  82. }
  83. }
  84. /// A static layout of a UI tree that describes a set of dynamic and static nodes.
  85. ///
  86. /// This is the core innovation in Dioxus. Most UIs are made of static nodes, yet participate in diffing like any
  87. /// dynamic node. This struct can be created at compile time. It promises that its name is unique, allow Dioxus to use
  88. /// its static description of the UI to skip immediately to the dynamic nodes during diffing.
  89. ///
  90. /// For this to work properly, the [`Template::name`] *must* be unique across your entire project. This can be done via variety of
  91. /// ways, with the suggested approach being the unique code location (file, line, col, etc).
  92. #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
  93. #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
  94. pub struct Template<'a> {
  95. /// The name of the template. This must be unique across your entire program for template diffing to work properly
  96. ///
  97. /// If two templates have the same name, it's likely that Dioxus will panic when diffing.
  98. pub name: &'a str,
  99. /// The list of template nodes that make up the template
  100. ///
  101. /// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
  102. pub roots: &'a [TemplateNode<'a>],
  103. /// The paths of each node relative to the root of the template.
  104. ///
  105. /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
  106. /// topmost element, not the `roots` field.
  107. pub node_paths: &'a [&'a [u8]],
  108. /// The paths of each dynamic attribute relative to the root of the template
  109. ///
  110. /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
  111. /// topmost element, not the `roots` field.
  112. pub attr_paths: &'a [&'a [u8]],
  113. }
  114. /// A statically known node in a layout.
  115. ///
  116. /// This can be created at compile time, saving the VirtualDom time when diffing the tree
  117. #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
  118. #[cfg_attr(feature = "serialize", derive(serde::Serialize), serde(tag = "type"))]
  119. pub enum TemplateNode<'a> {
  120. /// An statically known element in the dom.
  121. ///
  122. /// In HTML this would be something like `<div id="123"> </div>`
  123. Element {
  124. /// The name of the element
  125. ///
  126. /// IE for a div, it would be the string "div"
  127. tag: &'a str,
  128. /// The namespace of the element
  129. ///
  130. /// In HTML, this would be a valid URI that defines a namespace for all elements below it
  131. /// SVG is an example of this namespace
  132. namespace: Option<&'a str>,
  133. /// A list of possibly dynamic attribues for this element
  134. ///
  135. /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
  136. attrs: &'a [TemplateAttribute<'a>],
  137. /// A list of template nodes that define another set of template nodes
  138. children: &'a [TemplateNode<'a>],
  139. },
  140. /// This template node is just a piece of static text
  141. Text {
  142. /// The actual text
  143. text: &'a str,
  144. },
  145. /// This template node is unknown, and needs to be created at runtime.
  146. Dynamic {
  147. /// The index of the dynamic node in the VNode's dynamic_nodes list
  148. id: usize,
  149. },
  150. /// This template node is known to be some text, but needs to be created at runtime
  151. ///
  152. /// This is separate from the pure Dynamic variant for various optimizations
  153. DynamicText {
  154. /// The index of the dynamic node in the VNode's dynamic_nodes list
  155. id: usize,
  156. },
  157. }
  158. /// A node created at runtime
  159. ///
  160. /// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
  161. #[derive(Debug)]
  162. pub enum DynamicNode<'a> {
  163. /// A component node
  164. ///
  165. /// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
  166. /// assigned scope are dynamic.
  167. ///
  168. /// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
  169. /// the render function at runtime
  170. Component(VComponent<'a>),
  171. /// A text node
  172. Text(VText<'a>),
  173. /// A placeholder
  174. ///
  175. /// Used by suspense when a node isn't ready and by fragments that don't render anything
  176. ///
  177. /// In code, this is just an ElementId whose initial value is set to 0 upon creation
  178. Placeholder(Cell<ElementId>),
  179. /// A list of VNodes.
  180. ///
  181. /// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
  182. /// or iterators.
  183. Fragment(&'a [VNode<'a>]),
  184. }
  185. impl Default for DynamicNode<'_> {
  186. fn default() -> Self {
  187. Self::Placeholder(Default::default())
  188. }
  189. }
  190. /// An instance of a child component
  191. pub struct VComponent<'a> {
  192. /// The name of this component
  193. pub name: &'static str,
  194. /// Are the props valid for the 'static lifetime?
  195. ///
  196. /// Internally, this is used as a guarantee. Externally, this might be incorrect, so don't count on it.
  197. ///
  198. /// This flag is assumed by the [`crate::Properties`] trait which is unsafe to implement
  199. pub static_props: bool,
  200. /// The assigned Scope for this component
  201. pub scope: Cell<Option<ScopeId>>,
  202. /// The function pointer of the component, known at compile time
  203. ///
  204. /// It is possible that components get folded at comppile time, so these shouldn't be really used as a key
  205. pub render_fn: *const (),
  206. pub(crate) props: Cell<Option<Box<dyn AnyProps<'a> + 'a>>>,
  207. }
  208. impl<'a> std::fmt::Debug for VComponent<'a> {
  209. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  210. f.debug_struct("VComponent")
  211. .field("name", &self.name)
  212. .field("static_props", &self.static_props)
  213. .field("scope", &self.scope)
  214. .finish()
  215. }
  216. }
  217. /// An instance of some text, mounted to the DOM
  218. #[derive(Debug)]
  219. pub struct VText<'a> {
  220. /// The actual text itself
  221. pub value: &'a str,
  222. /// The ID of this node in the real DOM
  223. pub id: Cell<ElementId>,
  224. }
  225. /// An attribute of the TemplateNode, created at compile time
  226. #[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
  227. #[cfg_attr(
  228. feature = "serialize",
  229. derive(serde::Serialize, serde::Deserialize),
  230. serde(tag = "type")
  231. )]
  232. pub enum TemplateAttribute<'a> {
  233. /// This attribute is entirely known at compile time, enabling
  234. Static {
  235. /// The name of this attribute.
  236. ///
  237. /// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
  238. name: &'a str,
  239. /// The value of this attribute, known at compile time
  240. ///
  241. /// Currently this only accepts &str, so values, even if they're known at compile time, are not known
  242. value: &'a str,
  243. /// The namespace of this attribute. Does not exist in the HTML spec
  244. namespace: Option<&'a str>,
  245. },
  246. /// The attribute in this position is actually determined dynamically at runtime
  247. ///
  248. /// This is the index into the dynamic_attributes field on the container VNode
  249. Dynamic {
  250. /// The index
  251. id: usize,
  252. },
  253. }
  254. /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
  255. #[derive(Debug)]
  256. pub struct Attribute<'a> {
  257. /// The name of the attribute.
  258. pub name: &'a str,
  259. /// The value of the attribute
  260. pub value: AttributeValue<'a>,
  261. /// The namespace of the attribute.
  262. ///
  263. /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
  264. pub namespace: Option<&'static str>,
  265. /// The element in the DOM that this attribute belongs to
  266. pub mounted_element: Cell<ElementId>,
  267. /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
  268. pub volatile: bool,
  269. }
  270. /// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
  271. ///
  272. /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
  273. /// variant.
  274. pub enum AttributeValue<'a> {
  275. /// Text attribute
  276. Text(&'a str),
  277. /// A float
  278. Float(f64),
  279. /// Signed integer
  280. Int(i64),
  281. /// Boolean
  282. Bool(bool),
  283. /// A listener, like "onclick"
  284. Listener(RefCell<Option<ListenerCb<'a>>>),
  285. /// An arbitrary value that implements PartialEq and is static
  286. Any(BumpBox<'a, dyn AnyValue>),
  287. /// A "none" value, resulting in the removal of an attribute from the dom
  288. None,
  289. }
  290. type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
  291. impl<'a> std::fmt::Debug for AttributeValue<'a> {
  292. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  293. match self {
  294. Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
  295. Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
  296. Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
  297. Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
  298. Self::Listener(_) => f.debug_tuple("Listener").finish(),
  299. Self::Any(_) => f.debug_tuple("Any").finish(),
  300. Self::None => write!(f, "None"),
  301. }
  302. }
  303. }
  304. impl<'a> PartialEq for AttributeValue<'a> {
  305. fn eq(&self, other: &Self) -> bool {
  306. match (self, other) {
  307. (Self::Text(l0), Self::Text(r0)) => l0 == r0,
  308. (Self::Float(l0), Self::Float(r0)) => l0 == r0,
  309. (Self::Int(l0), Self::Int(r0)) => l0 == r0,
  310. (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
  311. (Self::Listener(_), Self::Listener(_)) => true,
  312. (Self::Any(l0), Self::Any(r0)) => l0.any_cmp(r0.as_ref()),
  313. _ => core::mem::discriminant(self) == core::mem::discriminant(other),
  314. }
  315. }
  316. }
  317. #[doc(hidden)]
  318. pub trait AnyValue {
  319. fn any_cmp(&self, other: &dyn AnyValue) -> bool;
  320. fn our_typeid(&self) -> TypeId;
  321. }
  322. impl<T: PartialEq + Any> AnyValue for T {
  323. fn any_cmp(&self, other: &dyn AnyValue) -> bool {
  324. if self.type_id() != other.our_typeid() {
  325. return false;
  326. }
  327. self == unsafe { &*(other as *const _ as *const T) }
  328. }
  329. fn our_typeid(&self) -> TypeId {
  330. self.type_id()
  331. }
  332. }
  333. #[doc(hidden)]
  334. pub trait ComponentReturn<'a, A = ()> {
  335. fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
  336. }
  337. impl<'a> ComponentReturn<'a> for Element<'a> {
  338. fn into_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
  339. RenderReturn::Sync(self)
  340. }
  341. }
  342. #[doc(hidden)]
  343. pub struct AsyncMarker;
  344. impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
  345. where
  346. F: Future<Output = Element<'a>> + 'a,
  347. {
  348. fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a> {
  349. let f: &mut dyn Future<Output = Element<'a>> = cx.bump().alloc(self);
  350. RenderReturn::Async(unsafe { BumpBox::from_raw(f) })
  351. }
  352. }
  353. impl<'a> RenderReturn<'a> {
  354. pub(crate) unsafe fn extend_lifetime_ref<'c>(&self) -> &'c RenderReturn<'c> {
  355. unsafe { std::mem::transmute(self) }
  356. }
  357. pub(crate) unsafe fn extend_lifetime<'c>(self) -> RenderReturn<'c> {
  358. unsafe { std::mem::transmute(self) }
  359. }
  360. }
  361. /// A trait that allows various items to be converted into a dynamic node for the rsx macro
  362. pub trait IntoDynNode<'a, A = ()> {
  363. /// Consume this item along with a scopestate and produce a DynamicNode
  364. ///
  365. /// You can use the bump alloactor of the scopestate to creat the dynamic node
  366. fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a>;
  367. }
  368. impl<'a> IntoDynNode<'a> for () {
  369. fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
  370. DynamicNode::default()
  371. }
  372. }
  373. impl<'a> IntoDynNode<'a> for VNode<'a> {
  374. fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
  375. DynamicNode::Fragment(_cx.bump().alloc([self]))
  376. }
  377. }
  378. // An element that's an error is currently lost into the ether
  379. impl<'a> IntoDynNode<'a> for Element<'a> {
  380. fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
  381. match self {
  382. Ok(val) => val.into_vnode(_cx),
  383. _ => DynamicNode::default(),
  384. }
  385. }
  386. }
  387. impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
  388. fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
  389. match self {
  390. Some(val) => val.into_vnode(_cx),
  391. None => DynamicNode::default(),
  392. }
  393. }
  394. }
  395. impl<'a> IntoDynNode<'a> for &Element<'a> {
  396. fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
  397. match self.as_ref() {
  398. Ok(val) => val.clone().into_vnode(_cx),
  399. _ => DynamicNode::default(),
  400. }
  401. }
  402. }
  403. impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
  404. fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
  405. DynamicNode::Fragment(cx.bump().alloc([self.call(cx)]))
  406. }
  407. }
  408. impl<'a> IntoDynNode<'_> for &'a str {
  409. fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
  410. cx.text_node(format_args!("{}", self))
  411. }
  412. }
  413. impl IntoDynNode<'_> for String {
  414. fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
  415. cx.text_node(format_args!("{}", self))
  416. }
  417. }
  418. impl<'b> IntoDynNode<'b> for Arguments<'_> {
  419. fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> {
  420. cx.text_node(self)
  421. }
  422. }
  423. impl<'a> IntoDynNode<'a> for &'a VNode<'a> {
  424. fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
  425. DynamicNode::Fragment(_cx.bump().alloc([VNode {
  426. parent: self.parent,
  427. template: self.template,
  428. root_ids: self.root_ids,
  429. key: self.key,
  430. dynamic_nodes: self.dynamic_nodes,
  431. dynamic_attrs: self.dynamic_attrs,
  432. }]))
  433. }
  434. }
  435. pub trait IntoTemplate<'a> {
  436. fn into_template(self, _cx: &'a ScopeState) -> VNode<'a>;
  437. }
  438. impl<'a> IntoTemplate<'a> for VNode<'a> {
  439. fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> {
  440. self
  441. }
  442. }
  443. impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> {
  444. fn into_template(self, cx: &'a ScopeState) -> VNode<'a> {
  445. self.call(cx)
  446. }
  447. }
  448. // Note that we're using the E as a generic but this is never crafted anyways.
  449. #[doc(hidden)]
  450. pub struct FromNodeIterator;
  451. impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
  452. where
  453. T: Iterator<Item = I>,
  454. I: IntoTemplate<'a>,
  455. {
  456. fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
  457. let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
  458. nodes.extend(self.into_iter().map(|node| node.into_template(cx)));
  459. match nodes.into_bump_slice() {
  460. children if children.is_empty() => DynamicNode::default(),
  461. children => DynamicNode::Fragment(children),
  462. }
  463. }
  464. }
  465. /// A value that can be converted into an attribute value
  466. pub trait IntoAttributeValue<'a> {
  467. /// Convert into an attribute value
  468. fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
  469. }
  470. impl<'a> IntoAttributeValue<'a> for &'a str {
  471. fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
  472. AttributeValue::Text(self)
  473. }
  474. }
  475. impl<'a> IntoAttributeValue<'a> for f64 {
  476. fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
  477. AttributeValue::Float(self)
  478. }
  479. }
  480. impl<'a> IntoAttributeValue<'a> for i64 {
  481. fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
  482. AttributeValue::Int(self)
  483. }
  484. }
  485. impl<'a> IntoAttributeValue<'a> for bool {
  486. fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
  487. AttributeValue::Bool(self)
  488. }
  489. }
  490. impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
  491. fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
  492. use bumpalo::core_alloc::fmt::Write;
  493. let mut str_buf = bumpalo::collections::String::new_in(bump);
  494. str_buf.write_fmt(self).unwrap();
  495. AttributeValue::Text(str_buf.into_bump_str())
  496. }
  497. }