nodebuilder.rs 23 KB

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