nodebuilder.rs 21 KB

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