nodes.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. //! Virtual Node Support
  2. //!
  3. //! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
  4. //! cheap and *very* fast to construct - building a full tree should be quick.
  5. use crate::{
  6. innerlude::{Context, Element, Properties, Scope, ScopeId},
  7. lazynodes::LazyNodes,
  8. };
  9. use bumpalo::{boxed::Box as BumpBox, Bump};
  10. use std::{
  11. any::Any,
  12. cell::{Cell, RefCell},
  13. fmt::{Arguments, Debug, Formatter},
  14. sync::Arc,
  15. };
  16. /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
  17. ///
  18. /// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
  19. ///
  20. /// - the [`rsx`] macro
  21. /// - the [`NodeFactory`] API
  22. pub enum VNode<'src> {
  23. /// Text VNodes simply bump-allocated (or static) string slices
  24. ///
  25. /// # Example
  26. ///
  27. /// ```rust, ignore
  28. /// let mut vdom = VirtualDom::new();
  29. /// let node = vdom.render_vnode(rsx!( "hello" ));
  30. ///
  31. /// if let VNode::Text(vtext) = node {
  32. /// assert_eq!(vtext.text, "hello");
  33. /// assert_eq!(vtext.dom_id.get(), None);
  34. /// assert_eq!(vtext.is_static, true);
  35. /// }
  36. /// ```
  37. Text(&'src VText<'src>),
  38. /// Element VNodes are VNodes that may contain attributes, listeners, a key, a tag, and children.
  39. ///
  40. /// # Example
  41. ///
  42. /// ```rust, ignore
  43. /// let mut vdom = VirtualDom::new();
  44. ///
  45. /// let node = vdom.render_vnode(rsx!{
  46. /// div {
  47. /// key: "a",
  48. /// onclick: |e| log::info!("clicked"),
  49. /// hidden: "true",
  50. /// style: { background_color: "red" }
  51. /// "hello"
  52. /// }
  53. /// });
  54. ///
  55. /// if let VNode::Element(velement) = node {
  56. /// assert_eq!(velement.tag_name, "div");
  57. /// assert_eq!(velement.namespace, None);
  58. /// assert_eq!(velement.key, Some("a));
  59. /// }
  60. /// ```
  61. Element(&'src VElement<'src>),
  62. /// Fragment nodes may contain many VNodes without a single root.
  63. ///
  64. /// # Example
  65. ///
  66. /// ```rust, ignore
  67. /// rsx!{
  68. /// a {}
  69. /// link {}
  70. /// style {}
  71. /// "asd"
  72. /// Example {}
  73. /// }
  74. /// ```
  75. Fragment(VFragment<'src>),
  76. /// Component nodes represent a mounted component with props, children, and a key.
  77. ///
  78. /// # Example
  79. ///
  80. /// ```rust, ignore
  81. /// fn Example(cx: Context, props: &()) -> Element {
  82. /// todo!()
  83. /// }
  84. ///
  85. /// let mut vdom = VirtualDom::new();
  86. ///
  87. /// let node = vdom.render_vnode(rsx!( Example {} ));
  88. ///
  89. /// if let VNode::Component(vcomp) = node {
  90. /// assert_eq!(vcomp.user_fc, Example as *const ());
  91. /// }
  92. /// ```
  93. Component(&'src VComponent<'src>),
  94. /// Placeholders are a type of placeholder VNode used when fragments don't contain any children.
  95. ///
  96. /// Placeholders cannot be directly constructed via public APIs.
  97. ///
  98. /// # Example
  99. ///
  100. /// ```rust, ignore
  101. /// let mut vdom = VirtualDom::new();
  102. ///
  103. /// let node = vdom.render_vnode(rsx!( Fragment {} ));
  104. ///
  105. /// if let VNode::Fragment(frag) = node {
  106. /// let root = &frag.children[0];
  107. /// assert_eq!(root, VNode::Anchor);
  108. /// }
  109. /// ```
  110. Placeholder(&'src VPlaceholder),
  111. /// A VNode that is actually a pointer to some nodes rather than the nodes directly. Useful when rendering portals
  112. /// or eliding lifetimes on VNodes through runtime checks.
  113. ///
  114. /// Linked VNodes can only be made through the [`Context::render`] method
  115. ///
  116. /// Typically, linked nodes are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
  117. /// an `rsx!` call.
  118. ///
  119. /// # Example
  120. /// ```rust, ignore
  121. /// let mut vdom = VirtualDom::new();
  122. ///
  123. /// let node: NodeLink = vdom.render_vnode(rsx!( "hello" ));
  124. /// ```
  125. Portal(VPortal),
  126. }
  127. impl<'src> VNode<'src> {
  128. /// Get the VNode's "key" used in the keyed diffing algorithm.
  129. pub fn key(&self) -> Option<&'src str> {
  130. match &self {
  131. VNode::Element(el) => el.key,
  132. VNode::Component(c) => c.key,
  133. VNode::Fragment(f) => f.key,
  134. VNode::Text(_t) => None,
  135. VNode::Placeholder(_f) => None,
  136. VNode::Portal(_c) => None,
  137. }
  138. }
  139. /// Get the ElementID of the mounted VNode.
  140. ///
  141. /// Panics if the mounted ID is None or if the VNode is not represented by a single Element.
  142. pub fn mounted_id(&self) -> ElementId {
  143. self.try_mounted_id().unwrap()
  144. }
  145. /// Try to get the ElementID of the mounted VNode.
  146. ///
  147. /// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component)
  148. pub fn try_mounted_id(&self) -> Option<ElementId> {
  149. match &self {
  150. VNode::Text(el) => el.dom_id.get(),
  151. VNode::Element(el) => el.dom_id.get(),
  152. VNode::Placeholder(el) => el.dom_id.get(),
  153. VNode::Portal(_) => None,
  154. VNode::Fragment(_) => None,
  155. VNode::Component(_) => None,
  156. }
  157. }
  158. pub(crate) fn children(&self) -> &[VNode<'src>] {
  159. match &self {
  160. VNode::Fragment(f) => f.children,
  161. _ => &[],
  162. }
  163. }
  164. // Create an "owned" version of the vnode.
  165. pub fn decouple(&self) -> VNode<'src> {
  166. match self {
  167. VNode::Text(t) => VNode::Text(*t),
  168. VNode::Element(e) => VNode::Element(*e),
  169. VNode::Component(c) => VNode::Component(*c),
  170. VNode::Placeholder(a) => VNode::Placeholder(*a),
  171. VNode::Fragment(f) => VNode::Fragment(VFragment {
  172. children: f.children,
  173. key: f.key,
  174. }),
  175. VNode::Portal(c) => VNode::Portal(VPortal {
  176. scope_id: c.scope_id.clone(),
  177. link_idx: c.link_idx.clone(),
  178. node: c.node,
  179. }),
  180. }
  181. }
  182. }
  183. impl Debug for VNode<'_> {
  184. fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
  185. match &self {
  186. VNode::Element(el) => s
  187. .debug_struct("VNode::VElement")
  188. .field("name", &el.tag_name)
  189. .field("key", &el.key)
  190. .finish(),
  191. VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
  192. VNode::Placeholder(_) => write!(s, "VNode::VAnchor"),
  193. VNode::Fragment(frag) => {
  194. write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
  195. }
  196. VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
  197. VNode::Portal(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
  198. }
  199. }
  200. }
  201. /// An Element's unique identifier.
  202. ///
  203. /// `ElementId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
  204. /// unmounted, then the `ElementId` will be reused for a new component.
  205. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
  206. pub struct ElementId(pub usize);
  207. impl std::fmt::Display for ElementId {
  208. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  209. write!(f, "{}", self.0)
  210. }
  211. }
  212. impl ElementId {
  213. pub fn as_u64(self) -> u64 {
  214. self.0 as u64
  215. }
  216. }
  217. fn empty_cell() -> Cell<Option<ElementId>> {
  218. Cell::new(None)
  219. }
  220. /// A placeholder node only generated when Fragments don't have any children.
  221. pub struct VPlaceholder {
  222. pub dom_id: Cell<Option<ElementId>>,
  223. }
  224. /// A bump-allocated string slice and metadata.
  225. pub struct VText<'src> {
  226. pub text: &'src str,
  227. pub dom_id: Cell<Option<ElementId>>,
  228. pub is_static: bool,
  229. }
  230. /// A list of VNodes with no single root.
  231. pub struct VFragment<'src> {
  232. pub key: Option<&'src str>,
  233. pub children: &'src [VNode<'src>],
  234. }
  235. /// An element like a "div" with children, listeners, and attributes.
  236. pub struct VElement<'a> {
  237. pub tag_name: &'static str,
  238. pub namespace: Option<&'static str>,
  239. pub key: Option<&'a str>,
  240. pub dom_id: Cell<Option<ElementId>>,
  241. pub parent_id: Cell<Option<ElementId>>,
  242. pub listeners: &'a [Listener<'a>],
  243. pub attributes: &'a [Attribute<'a>],
  244. pub children: &'a [VNode<'a>],
  245. }
  246. impl Debug for VElement<'_> {
  247. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  248. f.debug_struct("VElement")
  249. .field("tag_name", &self.tag_name)
  250. .field("namespace", &self.namespace)
  251. .field("key", &self.key)
  252. .field("dom_id", &self.dom_id)
  253. .field("parent_id", &self.parent_id)
  254. .field("listeners", &self.listeners.len())
  255. .field("attributes", &self.attributes)
  256. .field("children", &self.children)
  257. .finish()
  258. }
  259. }
  260. /// A trait for any generic Dioxus Element.
  261. ///
  262. /// This trait provides the ability to use custom elements in the `rsx!` macro.
  263. ///
  264. /// ```rust, ignore
  265. /// struct my_element;
  266. ///
  267. /// impl DioxusElement for my_element {
  268. /// const TAG_NAME: "my_element";
  269. /// const NAME_SPACE: None;
  270. /// }
  271. ///
  272. /// let _ = rsx!{
  273. /// my_element {}
  274. /// };
  275. /// ```
  276. pub trait DioxusElement {
  277. const TAG_NAME: &'static str;
  278. const NAME_SPACE: Option<&'static str>;
  279. #[inline]
  280. fn tag_name(&self) -> &'static str {
  281. Self::TAG_NAME
  282. }
  283. #[inline]
  284. fn namespace(&self) -> Option<&'static str> {
  285. Self::NAME_SPACE
  286. }
  287. }
  288. /// An attribute on a DOM node, such as `id="my-thing"` or
  289. /// `href="https://example.com"`.
  290. #[derive(Clone, Debug)]
  291. pub struct Attribute<'a> {
  292. pub name: &'static str,
  293. pub value: &'a str,
  294. pub is_static: bool,
  295. pub is_volatile: bool,
  296. // Doesn't exist in the html spec.
  297. // Used in Dioxus to denote "style" tags.
  298. pub namespace: Option<&'static str>,
  299. }
  300. /// An event listener.
  301. /// IE onclick, onkeydown, etc
  302. pub struct Listener<'bump> {
  303. /// The ID of the node that this listener is mounted to
  304. /// Used to generate the event listener's ID on the DOM
  305. pub mounted_node: Cell<Option<ElementId>>,
  306. /// The type of event to listen for.
  307. ///
  308. /// IE "click" - whatever the renderer needs to attach the listener by name.
  309. pub event: &'static str,
  310. /// The actual callback that the user specified
  311. pub(crate) callback: EventHandler<'bump>,
  312. }
  313. /// The callback based into element event listeners.
  314. pub struct EventHandler<'bump> {
  315. pub callback: &'bump RefCell<Option<ListenerCallback<'bump>>>,
  316. }
  317. impl EventHandler<'_> {
  318. pub fn call(&self, event: Arc<dyn Any + Send + Sync>) {
  319. if let Some(callback) = self.callback.borrow_mut().as_mut() {
  320. callback(event);
  321. }
  322. }
  323. pub fn release(&self) {
  324. self.callback.replace(None);
  325. }
  326. }
  327. type ListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(Arc<dyn Any + Send + Sync>) + 'bump>;
  328. impl Copy for EventHandler<'_> {}
  329. impl Clone for EventHandler<'_> {
  330. fn clone(&self) -> Self {
  331. Self {
  332. callback: self.callback,
  333. }
  334. }
  335. }
  336. /// A cached node is a "pointer" to a "rendered" node in a particular scope
  337. ///
  338. /// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
  339. ///
  340. /// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
  341. /// is essentially a unique key to guarantee safe usage of the Node.
  342. ///
  343. /// Linked VNodes can only be made through the [`Context::render`] method
  344. ///
  345. /// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
  346. /// an `rsx!` call.
  347. ///
  348. /// todo: remove the raw pointer and use runtime checks instead
  349. #[derive(Debug)]
  350. pub struct VPortal {
  351. pub(crate) link_idx: Cell<usize>,
  352. pub(crate) scope_id: Cell<Option<ScopeId>>,
  353. pub(crate) node: *const VNode<'static>,
  354. }
  355. impl PartialEq for VPortal {
  356. fn eq(&self, other: &Self) -> bool {
  357. self.node == other.node
  358. }
  359. }
  360. /// Virtual Components for custom user-defined components
  361. /// Only supports the functional syntax
  362. pub struct VComponent<'src> {
  363. pub key: Option<&'src str>,
  364. pub associated_scope: Cell<Option<ScopeId>>,
  365. // Function pointer to the FC that was used to generate this component
  366. pub user_fc: *const (),
  367. pub(crate) can_memoize: bool,
  368. pub(crate) _hard_allocation: Cell<Option<*const ()>>,
  369. // Raw pointer into the bump arena for the props of the component
  370. pub(crate) bump_props: *const (),
  371. // during the "teardown" process we'll take the caller out so it can be dropped properly
  372. pub(crate) caller: &'src dyn Fn(&'src Scope) -> Element,
  373. pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
  374. // todo: re-add this
  375. pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
  376. }
  377. /// This struct provides an ergonomic API to quickly build VNodes.
  378. ///
  379. /// NodeFactory is used to build VNodes in the component's memory space.
  380. /// This struct adds metadata to the final VNode about listeners, attributes, and children
  381. #[derive(Copy, Clone)]
  382. pub struct NodeFactory<'a> {
  383. pub(crate) bump: &'a Bump,
  384. }
  385. impl<'a> NodeFactory<'a> {
  386. pub fn new(bump: &'a Bump) -> NodeFactory<'a> {
  387. NodeFactory { bump }
  388. }
  389. #[inline]
  390. pub fn bump(&self) -> &'a bumpalo::Bump {
  391. self.bump
  392. }
  393. /// Directly pass in text blocks without the need to use the format_args macro.
  394. pub fn static_text(&self, text: &'static str) -> VNode<'a> {
  395. VNode::Text(self.bump.alloc(VText {
  396. dom_id: empty_cell(),
  397. text,
  398. is_static: true,
  399. }))
  400. }
  401. /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
  402. ///
  403. /// Text that's static may be pointer compared, making it cheaper to diff
  404. pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
  405. match args.as_str() {
  406. Some(static_str) => (static_str, true),
  407. None => {
  408. use bumpalo::core_alloc::fmt::Write;
  409. let mut str_buf = bumpalo::collections::String::new_in(self.bump);
  410. str_buf.write_fmt(args).unwrap();
  411. (str_buf.into_bump_str(), false)
  412. }
  413. }
  414. }
  415. /// Create some text that's allocated along with the other vnodes
  416. ///
  417. pub fn text(&self, args: Arguments) -> VNode<'a> {
  418. let (text, is_static) = self.raw_text(args);
  419. VNode::Text(self.bump.alloc(VText {
  420. text,
  421. is_static,
  422. dom_id: empty_cell(),
  423. }))
  424. }
  425. pub fn element<L, A, V>(
  426. &self,
  427. el: impl DioxusElement,
  428. listeners: L,
  429. attributes: A,
  430. children: V,
  431. key: Option<Arguments>,
  432. ) -> VNode<'a>
  433. where
  434. L: 'a + AsRef<[Listener<'a>]>,
  435. A: 'a + AsRef<[Attribute<'a>]>,
  436. V: 'a + AsRef<[VNode<'a>]>,
  437. {
  438. self.raw_element(
  439. el.tag_name(),
  440. el.namespace(),
  441. listeners,
  442. attributes,
  443. children,
  444. key,
  445. )
  446. }
  447. pub fn raw_element<L, A, V>(
  448. &self,
  449. tag_name: &'static str,
  450. namespace: Option<&'static str>,
  451. listeners: L,
  452. attributes: A,
  453. children: V,
  454. key: Option<Arguments>,
  455. ) -> VNode<'a>
  456. where
  457. L: 'a + AsRef<[Listener<'a>]>,
  458. A: 'a + AsRef<[Attribute<'a>]>,
  459. V: 'a + AsRef<[VNode<'a>]>,
  460. {
  461. let listeners: &'a L = self.bump.alloc(listeners);
  462. let listeners = listeners.as_ref();
  463. let attributes: &'a A = self.bump.alloc(attributes);
  464. let attributes = attributes.as_ref();
  465. let children: &'a V = self.bump.alloc(children);
  466. let children = children.as_ref();
  467. let key = key.map(|f| self.raw_text(f).0);
  468. VNode::Element(self.bump.alloc(VElement {
  469. tag_name,
  470. key,
  471. namespace,
  472. listeners,
  473. attributes,
  474. children,
  475. dom_id: empty_cell(),
  476. parent_id: empty_cell(),
  477. }))
  478. }
  479. pub fn attr(
  480. &self,
  481. name: &'static str,
  482. val: Arguments,
  483. namespace: Option<&'static str>,
  484. is_volatile: bool,
  485. ) -> Attribute<'a> {
  486. let (value, is_static) = self.raw_text(val);
  487. Attribute {
  488. name,
  489. value,
  490. is_static,
  491. namespace,
  492. is_volatile,
  493. }
  494. }
  495. pub fn component<P>(
  496. &self,
  497. component: fn(Context<'a, P>) -> Element,
  498. props: P,
  499. key: Option<Arguments>,
  500. ) -> VNode<'a>
  501. where
  502. P: Properties + 'a,
  503. {
  504. let bump = self.bump;
  505. let props = bump.alloc(props);
  506. let bump_props = props as *mut P as *mut ();
  507. let user_fc = component as *const ();
  508. let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
  509. move |other: &VComponent| {
  510. if user_fc == other.user_fc {
  511. // Safety
  512. // - We guarantee that FC<P> is the same by function pointer
  513. // - Because FC<P> is the same, then P must be the same (even with generics)
  514. // - Non-static P are autoderived to memoize as false
  515. // - This comparator is only called on a corresponding set of bumpframes
  516. //
  517. // It's only okay to memoize if there are no children and the props can be memoized
  518. // Implementing memoize is unsafe and done automatically with the props trait
  519. unsafe {
  520. let real_other: &P = &*(other.bump_props as *const _ as *const P);
  521. props.memoize(real_other)
  522. }
  523. } else {
  524. false
  525. }
  526. }
  527. });
  528. let drop_props = {
  529. // create a closure to drop the props
  530. let mut has_dropped = false;
  531. let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
  532. move || unsafe {
  533. if !has_dropped {
  534. let real_other = bump_props as *mut _ as *mut P;
  535. let b = BumpBox::from_raw(real_other);
  536. std::mem::drop(b);
  537. has_dropped = true;
  538. } else {
  539. panic!("Drop props called twice - this is an internal failure of Dioxus");
  540. }
  541. }
  542. });
  543. let drop_props = unsafe { BumpBox::from_raw(drop_props) };
  544. RefCell::new(Some(drop_props))
  545. };
  546. let key = key.map(|f| self.raw_text(f).0);
  547. let caller: &'a mut dyn Fn(&'a Scope) -> Element =
  548. bump.alloc(move |scope: &Scope| -> Element {
  549. let props: &'_ P = unsafe { &*(bump_props as *const P) };
  550. component(Context { scope, props })
  551. });
  552. let can_memoize = P::IS_STATIC;
  553. VNode::Component(bump.alloc(VComponent {
  554. user_fc,
  555. comparator: Some(comparator),
  556. bump_props,
  557. caller,
  558. key,
  559. can_memoize,
  560. drop_props,
  561. associated_scope: Cell::new(None),
  562. _hard_allocation: Cell::new(None),
  563. }))
  564. }
  565. pub fn listener(self, event: &'static str, callback: EventHandler<'a>) -> Listener<'a> {
  566. Listener {
  567. event,
  568. mounted_node: Cell::new(None),
  569. callback,
  570. }
  571. }
  572. pub fn fragment_root<'b, 'c>(
  573. self,
  574. node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
  575. ) -> VNode<'a> {
  576. let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
  577. for node in node_iter {
  578. nodes.push(node.into_vnode(self));
  579. }
  580. if nodes.is_empty() {
  581. nodes.push(VNode::Placeholder(self.bump.alloc(VPlaceholder {
  582. dom_id: empty_cell(),
  583. })));
  584. }
  585. VNode::Fragment(VFragment {
  586. children: nodes.into_bump_slice(),
  587. key: None,
  588. })
  589. }
  590. pub fn fragment_from_iter<'b, 'c>(
  591. self,
  592. node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
  593. ) -> VNode<'a> {
  594. let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
  595. for node in node_iter {
  596. nodes.push(node.into_vnode(self));
  597. }
  598. if nodes.is_empty() {
  599. nodes.push(VNode::Placeholder(self.bump.alloc(VPlaceholder {
  600. dom_id: empty_cell(),
  601. })));
  602. }
  603. let children = nodes.into_bump_slice();
  604. if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
  605. {
  606. // todo: make the backtrace prettier or remove it altogether
  607. log::error!(
  608. r#"
  609. Warning: Each child in an array or iterator should have a unique "key" prop.
  610. Not providing a key will lead to poor performance with lists.
  611. See docs.rs/dioxus for more information.
  612. -------------
  613. {:?}
  614. "#,
  615. backtrace::Backtrace::new()
  616. );
  617. }
  618. VNode::Fragment(VFragment {
  619. children,
  620. key: None,
  621. })
  622. }
  623. // this isn't quite feasible yet
  624. // I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
  625. pub fn create_children(
  626. self,
  627. node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
  628. ) -> Element {
  629. let bump = self.bump;
  630. let mut nodes = bumpalo::collections::Vec::new_in(bump);
  631. for node in node_iter {
  632. nodes.push(node.into_vnode(self));
  633. }
  634. if nodes.is_empty() {
  635. nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
  636. dom_id: empty_cell(),
  637. })));
  638. }
  639. let children = nodes.into_bump_slice();
  640. // TODO
  641. // We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
  642. let frag = VNode::Fragment(VFragment {
  643. children,
  644. key: None,
  645. });
  646. let ptr = self.bump.alloc(frag) as *const _;
  647. Some(VPortal {
  648. link_idx: Default::default(),
  649. scope_id: Default::default(),
  650. node: unsafe { std::mem::transmute(ptr) },
  651. })
  652. }
  653. }
  654. impl Debug for NodeFactory<'_> {
  655. fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  656. Ok(())
  657. }
  658. }
  659. /// Trait implementations for use in the rsx! and html! macros.
  660. ///
  661. /// ## Details
  662. ///
  663. /// This section provides convenience methods and trait implementations for converting common structs into a format accepted
  664. /// by the macros.
  665. ///
  666. /// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
  667. /// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
  668. /// ```rust, ignore
  669. /// impl IntoIterator<Item = impl IntoVNode<'a>>
  670. /// ```
  671. ///
  672. /// As such, all node creation must go through the factory, which is only available in the component context.
  673. /// These strict requirements make it possible to manage lifetimes and state.
  674. pub trait IntoVNode<'a> {
  675. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
  676. }
  677. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  678. impl<'a> IntoIterator for VNode<'a> {
  679. type Item = VNode<'a>;
  680. type IntoIter = std::iter::Once<Self::Item>;
  681. fn into_iter(self) -> Self::IntoIter {
  682. std::iter::once(self)
  683. }
  684. }
  685. // TODO: do we even need this? It almost seems better not to
  686. // // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  687. impl<'a> IntoVNode<'a> for VNode<'a> {
  688. fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
  689. self
  690. }
  691. }
  692. // Conveniently, we also support "null" (nothing) passed in
  693. impl IntoVNode<'_> for () {
  694. fn into_vnode(self, cx: NodeFactory) -> VNode {
  695. cx.fragment_from_iter(None as Option<VNode>)
  696. }
  697. }
  698. // Conveniently, we also support "None"
  699. impl IntoVNode<'_> for Option<()> {
  700. fn into_vnode(self, cx: NodeFactory) -> VNode {
  701. cx.fragment_from_iter(None as Option<VNode>)
  702. }
  703. }
  704. impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
  705. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  706. self.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
  707. }
  708. }
  709. impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
  710. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  711. match self {
  712. Some(lazy) => lazy.call(cx),
  713. None => VNode::Fragment(VFragment {
  714. children: &[],
  715. key: None,
  716. }),
  717. }
  718. }
  719. }
  720. impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
  721. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  722. self.call(cx)
  723. }
  724. }
  725. impl IntoVNode<'_> for &'static str {
  726. fn into_vnode(self, cx: NodeFactory) -> VNode {
  727. cx.static_text(self)
  728. }
  729. }
  730. impl IntoVNode<'_> for Arguments<'_> {
  731. fn into_vnode(self, cx: NodeFactory) -> VNode {
  732. cx.text(self)
  733. }
  734. }
  735. // called cx.render from a helper function
  736. impl IntoVNode<'_> for Option<VPortal> {
  737. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  738. match self {
  739. Some(node) => VNode::Portal(node),
  740. None => {
  741. todo!()
  742. }
  743. }
  744. }
  745. }
  746. // essentially passing elements through props
  747. // just build a new element in place
  748. impl IntoVNode<'_> for &Option<VPortal> {
  749. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  750. match self {
  751. Some(node) => VNode::Portal(VPortal {
  752. link_idx: node.link_idx.clone(),
  753. scope_id: node.scope_id.clone(),
  754. node: node.node,
  755. }),
  756. None => {
  757. //
  758. todo!()
  759. }
  760. }
  761. }
  762. }
  763. impl IntoVNode<'_> for VPortal {
  764. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  765. VNode::Portal(self)
  766. }
  767. }
  768. impl IntoVNode<'_> for &VPortal {
  769. fn into_vnode(self, _cx: NodeFactory) -> VNode {
  770. VNode::Portal(VPortal {
  771. link_idx: self.link_idx.clone(),
  772. scope_id: self.scope_id.clone(),
  773. node: self.node,
  774. })
  775. }
  776. }