nodebuilder.rs 20 KB

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