1
0

nodebuilder.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. //! Helpers for building virtual DOM VNodes.
  2. use std::{
  3. any::Any,
  4. borrow::BorrowMut,
  5. cell::{Cell, RefCell},
  6. fmt::Arguments,
  7. intrinsics::transmute,
  8. u128,
  9. };
  10. use bumpalo::Bump;
  11. use crate::{
  12. events::VirtualEvent,
  13. innerlude::{Properties, VComponent, FC},
  14. nodes::{Attribute, Listener, NodeKey, VNode},
  15. prelude::{VElement, VFragment},
  16. virtual_dom::{RealDomNode, Scope},
  17. };
  18. /// A virtual DOM element builder.
  19. ///
  20. /// Typically constructed with element-specific constructors, eg the `div`
  21. /// function for building `<div>` elements or the `button` function for building
  22. /// `<button>` elements.
  23. #[derive(Debug)]
  24. pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
  25. where
  26. Listeners: 'a + AsRef<[Listener<'a>]>,
  27. Attributes: 'a + AsRef<[Attribute<'a>]>,
  28. Children: 'a + AsRef<[VNode<'a>]>,
  29. {
  30. cx: &'b NodeFactory<'a>,
  31. key: NodeKey<'a>,
  32. tag_name: &'static str,
  33. // tag_name: &'a str,
  34. listeners: Listeners,
  35. attributes: Attributes,
  36. children: Children,
  37. namespace: Option<&'static str>,
  38. }
  39. impl<'a, 'b>
  40. ElementBuilder<
  41. 'a,
  42. 'b,
  43. bumpalo::collections::Vec<'a, Listener<'a>>,
  44. bumpalo::collections::Vec<'a, Attribute<'a>>,
  45. bumpalo::collections::Vec<'a, VNode<'a>>,
  46. >
  47. {
  48. /// Create a new `ElementBuilder` for an element with the given tag name.
  49. ///
  50. /// In general, only use this constructor if the tag is dynamic (i.e. you
  51. /// might build a `<div>` or you might build a `<span>` and you don't know
  52. /// until runtime). Prefer using the tag-specific constructors instead:
  53. /// `div(bump)` or `span(bump)`, etc.
  54. ///
  55. /// # Example
  56. ///
  57. /// ```
  58. /// use dioxus::{builder::*, bumpalo::Bump};
  59. ///
  60. /// let b = Bump::new();
  61. ///
  62. /// let tag_name = if flip_coin() {
  63. /// "div"
  64. /// } else {
  65. /// "span"
  66. /// };
  67. ///
  68. /// let my_element_builder = ElementBuilder::new(&b, tag_name);
  69. /// # fn flip_coin() -> bool { true }
  70. /// ```
  71. pub fn new(cx: &'b NodeFactory<'a>, tag_name: &'static str) -> Self {
  72. let bump = cx.bump();
  73. ElementBuilder {
  74. cx,
  75. key: NodeKey::NONE,
  76. tag_name,
  77. listeners: bumpalo::collections::Vec::new_in(bump),
  78. attributes: bumpalo::collections::Vec::new_in(bump),
  79. children: bumpalo::collections::Vec::new_in(bump),
  80. namespace: None,
  81. }
  82. }
  83. }
  84. impl<'a, 'b, Listeners, Attributes, Children>
  85. ElementBuilder<'a, 'b, Listeners, Attributes, Children>
  86. where
  87. Listeners: 'a + AsRef<[Listener<'a>]>,
  88. Attributes: 'a + AsRef<[Attribute<'a>]>,
  89. Children: 'a + AsRef<[VNode<'a>]>,
  90. {
  91. /// Set the listeners for this element.
  92. ///
  93. /// You can use this method to customize the backing storage for listeners,
  94. /// for example to use a fixed-size array instead of the default
  95. /// dynamically-sized `bumpalo::collections::Vec`.
  96. ///
  97. /// Any listeners already added to the builder will be overridden.
  98. ///
  99. /// # Example
  100. ///
  101. /// ```no_run
  102. /// use dioxus::{builder::*, bumpalo::Bump};
  103. ///
  104. /// let b = Bump::new();
  105. ///
  106. /// // Create a `<div>` with a fixed-size array of two listeners.
  107. /// let my_div = div(&b)
  108. /// .listeners([
  109. /// on(&b, "click", |root, vdom, event| {
  110. /// // ...
  111. /// }),
  112. /// on(&b, "dblclick", |root, vdom, event| {
  113. /// // ...
  114. /// }),
  115. /// ])
  116. /// .finish();
  117. /// ```
  118. #[inline]
  119. pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
  120. where
  121. L: 'a + AsRef<[Listener<'a>]>,
  122. {
  123. ElementBuilder {
  124. cx: self.cx,
  125. key: self.key,
  126. tag_name: self.tag_name,
  127. listeners,
  128. attributes: self.attributes,
  129. children: self.children,
  130. namespace: self.namespace,
  131. }
  132. }
  133. /// Set the attributes for this element.
  134. ///
  135. /// You can use this method to customize the backing storage for attributes,
  136. /// for example to use a fixed-size array instead of the default
  137. /// dynamically-sized `bumpalo::collections::Vec`.
  138. ///
  139. /// Any attributes already added to the builder will be overridden.
  140. ///
  141. /// # Example
  142. ///
  143. /// ```no_run
  144. /// use dioxus::{builder::*, bumpalo::Bump, Attribute};
  145. ///
  146. /// let b = Bump::new();
  147. ///
  148. /// // Create a `<div>` with a fixed-size array of two attributes.
  149. /// let my_div = div(&b)
  150. /// .attributes([
  151. /// attr("id", "my-div"),
  152. /// attr("class", "notification"),
  153. /// ])
  154. /// .finish();
  155. /// ```
  156. #[inline]
  157. pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
  158. where
  159. A: 'a + AsRef<[Attribute<'a>]>,
  160. {
  161. ElementBuilder {
  162. cx: self.cx,
  163. key: self.key,
  164. tag_name: self.tag_name,
  165. listeners: self.listeners,
  166. attributes,
  167. children: self.children,
  168. namespace: self.namespace,
  169. }
  170. }
  171. /// Set the children for this element.
  172. ///
  173. /// You can use this method to customize the backing storage for children,
  174. /// for example to use a fixed-size array instead of the default
  175. /// dynamically-sized `bumpalo::collections::Vec`.
  176. ///
  177. /// Any children already added to the builder will be overridden.
  178. ///
  179. /// # Example
  180. ///
  181. /// ```no_run
  182. /// use dioxus::{builder::*, bumpalo::Bump};
  183. ///
  184. /// let b = Bump::new();
  185. ///
  186. /// // Create a `<div>` with a fixed-size array of two `<span>` children.
  187. /// let my_div = div(&b)
  188. /// .children([
  189. /// span(&b).finish(),
  190. /// span(&b).finish(),
  191. /// ])
  192. /// .finish();
  193. /// ```
  194. #[inline]
  195. pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
  196. where
  197. C: 'a + AsRef<[VNode<'a>]>,
  198. {
  199. ElementBuilder {
  200. cx: self.cx,
  201. key: self.key,
  202. tag_name: self.tag_name,
  203. listeners: self.listeners,
  204. attributes: self.attributes,
  205. children,
  206. namespace: self.namespace,
  207. }
  208. }
  209. /// Set the namespace for this element.
  210. ///
  211. /// # Example
  212. ///
  213. /// ```no_run
  214. /// use dioxus::{builder::*, bumpalo::Bump};
  215. ///
  216. /// let b = Bump::new();
  217. ///
  218. /// // Create a `<td>` tag with an xhtml namespace
  219. /// let my_td = td(&b)
  220. /// .namespace(Some("http://www.w3.org/1999/xhtml"))
  221. /// .finish();
  222. /// ```
  223. #[inline]
  224. pub fn namespace(self, namespace: Option<&'static str>) -> Self {
  225. ElementBuilder {
  226. cx: self.cx,
  227. key: self.key,
  228. tag_name: self.tag_name,
  229. listeners: self.listeners,
  230. attributes: self.attributes,
  231. children: self.children,
  232. namespace,
  233. }
  234. }
  235. /// Set this element's key.
  236. ///
  237. /// When diffing sets of siblings, if an old sibling and new sibling share a
  238. /// key, then they will always reuse the same physical DOM VNode. This is
  239. /// important when using CSS animations, web components, third party JS, or
  240. /// anything else that makes the diffing implementation observable.
  241. ///
  242. /// Do not use keys if such a scenario does not apply. Keyed diffing is
  243. /// generally more expensive than not, since it is putting greater
  244. /// constraints on the diffing algorithm.
  245. ///
  246. /// # Invariants You Must Uphold
  247. ///
  248. /// The key may not be `u32::MAX`, which is a reserved key value.
  249. ///
  250. /// Keys must be unique among siblings.
  251. ///
  252. /// All sibling VNodes must be keyed, or they must all not be keyed. You may
  253. /// not mix keyed and unkeyed siblings.
  254. ///
  255. /// # Example
  256. ///
  257. /// ```no_run
  258. /// use dioxus::{builder::*, bumpalo::Bump};
  259. ///
  260. /// let b = Bump::new();
  261. ///
  262. /// let my_li = li(&b)
  263. /// .key(1337)
  264. /// .finish();
  265. /// ```
  266. #[inline]
  267. pub fn key(mut self, key: &'a str) -> Self {
  268. self.key = NodeKey(Some(key));
  269. self
  270. }
  271. pub fn key2(mut self, args: Arguments) -> Self {
  272. let key = match args.as_str() {
  273. Some(static_str) => static_str,
  274. None => {
  275. use bumpalo::core_alloc::fmt::Write;
  276. let mut s = bumpalo::collections::String::new_in(self.cx.bump());
  277. s.write_fmt(args).unwrap();
  278. s.into_bump_str()
  279. }
  280. };
  281. self.key = NodeKey(Some(key));
  282. self
  283. }
  284. /// Create the virtual DOM VNode described by this builder.
  285. ///
  286. /// # Example
  287. ///
  288. /// ```no_run
  289. /// use dioxus::{builder::*, bumpalo::Bump, VNode};
  290. ///
  291. /// let b = Bump::new();
  292. ///
  293. /// // Start with a builder...
  294. /// let builder: ElementBuilder<_, _, _> = div(&b);
  295. ///
  296. /// // ...and finish it to create a virtual DOM VNode!
  297. /// let my_div: VNode = builder.finish();
  298. /// ```
  299. #[inline]
  300. pub fn finish(self) -> VNode<'a> {
  301. let bump = self.cx.bump();
  302. let children: &'a Children = bump.alloc(self.children);
  303. let children: &'a [VNode<'a>] = children.as_ref();
  304. let listeners: &'a Listeners = bump.alloc(self.listeners);
  305. let listeners: &'a [Listener<'a>] = listeners.as_ref();
  306. let attributes: &'a Attributes = bump.alloc(self.attributes);
  307. let attributes: &'a [Attribute<'a>] = attributes.as_ref();
  308. VNode::element(
  309. bump,
  310. self.key,
  311. self.tag_name,
  312. listeners,
  313. attributes,
  314. children,
  315. self.namespace,
  316. )
  317. }
  318. }
  319. impl<'a, 'b, Attributes, Children>
  320. ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
  321. where
  322. Attributes: 'a + AsRef<[Attribute<'a>]>,
  323. Children: 'a + AsRef<[VNode<'a>]>,
  324. {
  325. /// Add a new event listener to this element.
  326. ///
  327. /// The `event` string specifies which event will be listened for. The
  328. /// `callback` function is the function that will be invoked if the
  329. /// specified event occurs.
  330. ///
  331. /// # Example
  332. ///
  333. /// ```no_run
  334. /// use dioxus::{builder::*, bumpalo::Bump};
  335. ///
  336. /// let b = Bump::new();
  337. ///
  338. /// // A button that does something when clicked!
  339. /// let my_button = button(&b)
  340. /// .on("click", |event| {
  341. /// // ...
  342. /// })
  343. /// .finish();
  344. /// ```
  345. pub fn on(self, event: &'static str, callback: impl Fn(VirtualEvent) + 'a) -> Self {
  346. let bump = &self.cx.bump();
  347. let listener = Listener {
  348. event,
  349. callback: bump.alloc(callback),
  350. scope: self.cx.scope_ref.arena_idx,
  351. mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
  352. };
  353. self.add_listener(listener)
  354. }
  355. pub fn add_listener(mut self, listener: Listener<'a>) -> Self {
  356. self.listeners.push(listener);
  357. // bump the context id forward
  358. let id = self.cx.listener_id.get();
  359. self.cx.listener_id.set(id + 1);
  360. // Add this listener to the context list
  361. // This casts the listener to a self-referential pointer
  362. // This is okay because the bump arena is stable
  363. self.listeners.last().map(|g| {
  364. let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(g) };
  365. self.cx
  366. .scope_ref
  367. .listeners
  368. .borrow_mut()
  369. .push((r.mounted_node as *const _, r.callback as *const _));
  370. });
  371. self
  372. }
  373. }
  374. impl<'a, 'b, Listeners, Children>
  375. ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
  376. where
  377. Listeners: 'a + AsRef<[Listener<'a>]>,
  378. Children: 'a + AsRef<[VNode<'a>]>,
  379. {
  380. /// Add a new attribute to this element.
  381. ///
  382. /// # Example
  383. ///
  384. /// ```no_run
  385. /// use dioxus::{builder::*, bumpalo::Bump};
  386. ///
  387. /// let b = Bump::new();
  388. ///
  389. /// // Create the `<div id="my-div"/>` element.
  390. /// let my_div = div(&b).attr("id", "my-div").finish();
  391. /// ```
  392. pub fn attr(mut self, name: &'static str, args: std::fmt::Arguments) -> Self {
  393. let value = match args.as_str() {
  394. Some(static_str) => static_str,
  395. None => {
  396. use bumpalo::core_alloc::fmt::Write;
  397. let mut s = bumpalo::collections::String::new_in(self.cx.bump());
  398. s.write_fmt(args).unwrap();
  399. s.into_bump_str()
  400. }
  401. };
  402. self.attributes.push(Attribute { name, value });
  403. self
  404. }
  405. /// Conditionally add a "boolean-style" attribute to this element.
  406. ///
  407. /// If the `should_add` parameter is true, then adds an attribute with the
  408. /// given `name` and an empty string value. If the `should_add` parameter is
  409. /// false, then the attribute is not added.
  410. ///
  411. /// This method is useful for attributes whose semantics are defined by
  412. /// whether or not the attribute is present or not, and whose value is
  413. /// ignored. Example attributes like this include:
  414. ///
  415. /// * `checked`
  416. /// * `hidden`
  417. /// * `selected`
  418. ///
  419. /// # Example
  420. ///
  421. /// ```no_run
  422. /// use dioxus::{builder::*, bumpalo::Bump};
  423. /// use js_sys::Math;
  424. ///
  425. /// let b = Bump::new();
  426. ///
  427. /// // Create the `<div>` that is randomly hidden 50% of the time.
  428. /// let my_div = div(&b)
  429. /// .bool_attr("hidden", Math::random() >= 0.5)
  430. /// .finish();
  431. /// ```
  432. pub fn bool_attr(mut self, name: &'static str, should_add: bool) -> Self {
  433. if should_add {
  434. self.attributes.push(Attribute { name, value: "" });
  435. }
  436. self
  437. }
  438. }
  439. impl<'a, 'b, Listeners, Attributes>
  440. ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
  441. where
  442. Listeners: 'a + AsRef<[Listener<'a>]>,
  443. Attributes: 'a + AsRef<[Attribute<'a>]>,
  444. {
  445. /// Add a new child to this element.
  446. ///
  447. /// # Example
  448. ///
  449. /// ```no_run
  450. /// use dioxus::{builder::*, bumpalo::Bump};
  451. /// use js_sys::Math;
  452. ///
  453. /// let b = Bump::new();
  454. ///
  455. /// // Create `<p><span></span></p>`.
  456. /// let my_div = p(&b)
  457. /// .child(span(&b).finish())
  458. /// .finish();
  459. /// ```
  460. #[inline]
  461. pub fn child(mut self, child: VNode<'a>) -> Self {
  462. self.children.push(child);
  463. self
  464. }
  465. /// Add multiple children to this element from an iterator.
  466. ///
  467. /// # Example
  468. ///
  469. /// ```no_run
  470. /// use dioxus::{builder::*, bumpalo::Bump};
  471. ///
  472. /// let b = Bump::new();
  473. ///
  474. /// let my_div = p(&b)
  475. /// .iter_child((0..10).map(|f| span(&b).finish())
  476. /// .finish();
  477. /// ```
  478. pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
  479. let len_before = self.children.len();
  480. for item in nodes {
  481. let child = item.into_vnode(&self.cx);
  482. self.children.push(child);
  483. }
  484. if cfg!(debug_assertions) {
  485. if self.children.len() > len_before + 1 {
  486. if self.children.last().unwrap().key().is_none() {
  487. log::error!(
  488. r#"
  489. Warning: Each child in an array or iterator should have a unique "key" prop.
  490. Not providing a key will lead to poor performance with lists.
  491. See docs.rs/dioxus for more information.
  492. ---
  493. To help you identify where this error is coming from, we've generated a backtrace.
  494. "#,
  495. );
  496. }
  497. }
  498. }
  499. self
  500. }
  501. }
  502. impl<'a> IntoIterator for VNode<'a> {
  503. type Item = VNode<'a>;
  504. type IntoIter = std::iter::Once<Self::Item>;
  505. fn into_iter(self) -> Self::IntoIter {
  506. std::iter::once(self)
  507. }
  508. }
  509. impl<'a> IntoVNode<'a> for VNode<'a> {
  510. fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
  511. self
  512. }
  513. }
  514. impl<'a> IntoVNode<'a> for &VNode<'a> {
  515. fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
  516. // cloning is cheap since vnodes are just references into bump arenas
  517. self.clone()
  518. }
  519. }
  520. pub trait IntoVNode<'a> {
  521. fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a>;
  522. }
  523. pub trait VNodeBuilder<'a, G>: IntoIterator<Item = G>
  524. where
  525. G: IntoVNode<'a>,
  526. {
  527. }
  528. impl<'a, F> VNodeBuilder<'a, LazyNodes<'a, F>> for LazyNodes<'a, F> where
  529. F: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a
  530. {
  531. }
  532. // Wrap the the node-builder closure in a concrete type.
  533. // ---
  534. // This is a bit of a hack to implement the IntoVNode trait for closure types.
  535. pub struct LazyNodes<'a, G>
  536. where
  537. G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
  538. {
  539. inner: G,
  540. _p: std::marker::PhantomData<&'a ()>,
  541. }
  542. impl<'a, G> LazyNodes<'a, G>
  543. where
  544. G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
  545. {
  546. pub fn new(f: G) -> Self {
  547. Self {
  548. inner: f,
  549. _p: std::default::Default::default(),
  550. }
  551. }
  552. }
  553. // Cover the cases where nodes are used by macro.
  554. // Likely used directly.
  555. // ---
  556. // let nodes = rsx!{ ... };
  557. // rsx! { {nodes } }
  558. impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
  559. where
  560. G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
  561. {
  562. fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
  563. (self.inner)(cx)
  564. }
  565. }
  566. // Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
  567. impl<'a, G> IntoIterator for LazyNodes<'a, G>
  568. where
  569. G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
  570. {
  571. type Item = Self;
  572. type IntoIter = std::iter::Once<Self::Item>;
  573. fn into_iter(self) -> Self::IntoIter {
  574. std::iter::once(self)
  575. }
  576. }
  577. impl<'a> IntoVNode<'a> for () {
  578. fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
  579. todo!();
  580. VNode::Suspended {
  581. real: Cell::new(RealDomNode::empty()),
  582. }
  583. }
  584. }
  585. impl<'a> IntoVNode<'a> for Option<()> {
  586. fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
  587. todo!();
  588. VNode::Suspended {
  589. real: Cell::new(RealDomNode::empty()),
  590. }
  591. }
  592. }
  593. /// Construct a text VNode.
  594. ///
  595. /// This is `dioxus`'s virtual DOM equivalent of `document.createTextVNode`.
  596. ///
  597. /// # Example
  598. ///
  599. /// ```no_run
  600. /// use dioxus::builder::*;
  601. ///
  602. /// let my_text = text("hello, dioxus!");
  603. /// ```
  604. #[inline]
  605. pub fn text<'a>(contents: &'a str) -> VNode<'a> {
  606. VNode::text(contents)
  607. }
  608. pub fn text2<'a>(contents: bumpalo::collections::String<'a>) -> VNode<'a> {
  609. let f: &'a str = contents.into_bump_str();
  610. VNode::text(f)
  611. }
  612. pub fn text3<'a>(bump: &'a bumpalo::Bump, args: std::fmt::Arguments) -> VNode<'a> {
  613. // This is a cute little optimization
  614. //
  615. // We use format_args! on everything. However, not all textnodes have dynamic content, and thus are completely static.
  616. // we can just short-circuit to the &'static str version instead of having to allocate in the bump arena.
  617. //
  618. // In the most general case, this prevents the need for any string allocations for simple code IE:
  619. // ```
  620. // div {"abc"}
  621. // ```
  622. match args.as_str() {
  623. Some(static_str) => VNode::text(static_str),
  624. None => {
  625. use bumpalo::core_alloc::fmt::Write;
  626. let mut s = bumpalo::collections::String::new_in(bump);
  627. s.write_fmt(args).unwrap();
  628. VNode::text(s.into_bump_str())
  629. }
  630. }
  631. }
  632. /// Construct an attribute for an element.
  633. ///
  634. /// # Example
  635. ///
  636. /// This example creates the `id="my-id"` for some element like `<div
  637. /// id="my-id"/>`.
  638. ///
  639. /// ```no_run
  640. /// use dioxus::builder::*;
  641. ///
  642. /// let my_id_attr = attr("id", "my-id");
  643. /// ```
  644. pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
  645. Attribute { name, value }
  646. }
  647. pub fn virtual_child<'a, T: Properties + 'a>(
  648. cx: &NodeFactory<'a>,
  649. f: FC<T>,
  650. props: T,
  651. key: Option<&'a str>, // key: NodeKey<'a>,
  652. children: &'a [VNode<'a>],
  653. ) -> VNode<'a> {
  654. // currently concerned about if props have a custom drop implementation
  655. // might override it with the props macro
  656. // todo!()
  657. VNode::Component(
  658. cx.bump()
  659. .alloc(crate::nodes::VComponent::new(cx, f, props, key, children)),
  660. // cx.bump()
  661. // .alloc(crate::nodes::VComponent::new(f, props, key)),
  662. )
  663. }
  664. pub fn vfragment<'a>(
  665. cx: &NodeFactory<'a>,
  666. key: Option<&'a str>, // key: NodeKey<'a>,
  667. children: &'a [VNode<'a>],
  668. ) -> VNode<'a> {
  669. VNode::Fragment(cx.bump().alloc(VFragment::new(key, children)))
  670. }
  671. pub struct ChildrenList<'a, 'b> {
  672. cx: &'b NodeFactory<'a>,
  673. children: bumpalo::collections::Vec<'a, VNode<'a>>,
  674. }
  675. impl<'a, 'b> ChildrenList<'a, 'b> {
  676. pub fn new(cx: &'b NodeFactory<'a>) -> Self {
  677. Self {
  678. cx,
  679. children: bumpalo::collections::Vec::new_in(cx.bump()),
  680. }
  681. }
  682. pub fn add_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
  683. for item in nodes {
  684. let child = item.into_vnode(&self.cx);
  685. self.children.push(child);
  686. }
  687. self
  688. }
  689. pub fn finish(self) -> &'a [VNode<'a>] {
  690. self.children.into_bump_slice()
  691. }
  692. }
  693. /// This struct provides an ergonomic API to quickly build VNodes.
  694. ///
  695. /// NodeFactory is used to build VNodes in the component's memory space.
  696. /// This struct adds metadata to the final VNode about listeners, attributes, and children
  697. #[derive(Clone)]
  698. pub struct NodeFactory<'a> {
  699. pub scope_ref: &'a Scope,
  700. pub listener_id: Cell<usize>,
  701. }
  702. impl<'a> NodeFactory<'a> {
  703. #[inline]
  704. pub fn bump(&self) -> &'a bumpalo::Bump {
  705. &self.scope_ref.cur_frame().bump
  706. }
  707. /// Create some text that's allocated along with the other vnodes
  708. pub fn text(&self, args: Arguments) -> VNode<'a> {
  709. text3(self.bump(), args)
  710. }
  711. /// Create an element builder
  712. pub fn element<'b>(
  713. &'b self,
  714. tag_name: &'static str,
  715. ) -> ElementBuilder<
  716. 'a,
  717. 'b,
  718. bumpalo::collections::Vec<'a, Listener<'a>>,
  719. bumpalo::collections::Vec<'a, Attribute<'a>>,
  720. bumpalo::collections::Vec<'a, VNode<'a>>,
  721. > {
  722. ElementBuilder::new(self, tag_name)
  723. }
  724. pub fn child_list(&self) -> ChildrenList {
  725. ChildrenList::new(&self)
  726. }
  727. pub fn fragment_builder<'b>(
  728. &'b self,
  729. key: Option<&'a str>,
  730. builder: impl FnOnce(ChildrenList<'a, 'b>) -> &'a [VNode<'a>],
  731. ) -> VNode<'a> {
  732. self.fragment(builder(ChildrenList::new(&self)), key)
  733. }
  734. pub fn fragment(&self, children: &'a [VNode<'a>], key: Option<&'a str>) -> VNode<'a> {
  735. VNode::Fragment(self.bump().alloc(VFragment {
  736. children,
  737. key: NodeKey::new_opt(key),
  738. }))
  739. }
  740. pub fn fragment_from_iter(
  741. &self,
  742. node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
  743. ) -> VNode<'a> {
  744. let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
  745. for node in node_iter.into_iter() {
  746. nodes.push(node.into_vnode(&self));
  747. }
  748. VNode::Fragment(
  749. self.bump()
  750. .alloc(VFragment::new(None, nodes.into_bump_slice())),
  751. )
  752. }
  753. }
  754. use std::fmt::Debug;
  755. impl Debug for NodeFactory<'_> {
  756. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  757. Ok(())
  758. }
  759. }