oldbuilder.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /// Typically constructed with element-specific constructors, eg the `div`
  2. /// function for building `<div>` elements or the `button` function for building
  3. /// `<button>` elements.
  4. #[derive(Debug)]
  5. pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
  6. where
  7. Listeners: 'a + AsRef<[Listener<'a>]>,
  8. Attributes: 'a + AsRef<[Attribute<'a>]>,
  9. Children: 'a + AsRef<[VNode<'a>]>,
  10. {
  11. cx: &'b NodeFactory<'a>,
  12. key: NodeKey<'a>,
  13. tag_name: &'static str,
  14. listeners: Listeners,
  15. attributes: Attributes,
  16. children: Children,
  17. namespace: Option<&'static str>,
  18. }
  19. impl<'a, 'b>
  20. ElementBuilder<
  21. 'a,
  22. 'b,
  23. bumpalo::collections::Vec<'a, Listener<'a>>,
  24. bumpalo::collections::Vec<'a, Attribute<'a>>,
  25. bumpalo::collections::Vec<'a, VNode<'a>>,
  26. >
  27. {
  28. /// Create a new `ElementBuilder` for an element with the given tag name.
  29. ///
  30. /// In general, only use this constructor if the tag is dynamic (i.e. you
  31. /// might build a `<div>` or you might build a `<span>` and you don't know
  32. /// until runtime). Prefer using the tag-specific constructors instead:
  33. /// `div(bump)` or `span(bump)`, etc.
  34. ///
  35. /// # Example
  36. ///
  37. /// ```
  38. /// use dioxus::{builder::*, bumpalo::Bump};
  39. ///
  40. /// let b = Bump::new();
  41. ///
  42. /// let tag_name = if flip_coin() {
  43. /// "div"
  44. /// } else {
  45. /// "span"
  46. /// };
  47. ///
  48. /// let my_element_builder = ElementBuilder::new(&b, tag_name);
  49. /// # fn flip_coin() -> bool { true }
  50. /// ```
  51. pub fn new(cx: &'b NodeFactory<'a>, tag_name: &'static str) -> Self {
  52. let bump = cx.bump();
  53. ElementBuilder {
  54. cx,
  55. key: NodeKey::NONE,
  56. tag_name,
  57. listeners: bumpalo::collections::Vec::new_in(bump),
  58. attributes: bumpalo::collections::Vec::new_in(bump),
  59. children: bumpalo::collections::Vec::new_in(bump),
  60. namespace: None,
  61. }
  62. }
  63. }
  64. impl<'a, 'b, Listeners, Attributes, Children>
  65. ElementBuilder<'a, 'b, Listeners, Attributes, Children>
  66. where
  67. Listeners: 'a + AsRef<[Listener<'a>]>,
  68. Attributes: 'a + AsRef<[Attribute<'a>]>,
  69. Children: 'a + AsRef<[VNode<'a>]>,
  70. {
  71. /// Set the listeners for this element.
  72. ///
  73. /// You can use this method to customize the backing storage for listeners,
  74. /// for example to use a fixed-size array instead of the default
  75. /// dynamically-sized `bumpalo::collections::Vec`.
  76. ///
  77. /// Any listeners already added to the builder will be overridden.
  78. ///
  79. /// # Example
  80. ///
  81. /// ```no_run
  82. /// use dioxus::{builder::*, bumpalo::Bump};
  83. ///
  84. /// let b = Bump::new();
  85. ///
  86. /// // Create a `<div>` with a fixed-size array of two listeners.
  87. /// let my_div = div(&b)
  88. /// .listeners([
  89. /// on(&b, "click", |root, vdom, event| {
  90. /// // ...
  91. /// }),
  92. /// on(&b, "dblclick", |root, vdom, event| {
  93. /// // ...
  94. /// }),
  95. /// ])
  96. /// .finish();
  97. /// ```
  98. #[inline]
  99. pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
  100. where
  101. L: 'a + AsRef<[Listener<'a>]>,
  102. {
  103. ElementBuilder {
  104. cx: self.cx,
  105. key: self.key,
  106. tag_name: self.tag_name,
  107. listeners,
  108. attributes: self.attributes,
  109. children: self.children,
  110. namespace: self.namespace,
  111. }
  112. }
  113. /// Set the attributes for this element.
  114. ///
  115. /// You can use this method to customize the backing storage for attributes,
  116. /// for example to use a fixed-size array instead of the default
  117. /// dynamically-sized `bumpalo::collections::Vec`.
  118. ///
  119. /// Any attributes already added to the builder will be overridden.
  120. ///
  121. /// # Example
  122. ///
  123. /// ```no_run
  124. /// use dioxus::{builder::*, bumpalo::Bump, Attribute};
  125. ///
  126. /// let b = Bump::new();
  127. ///
  128. /// // Create a `<div>` with a fixed-size array of two attributes.
  129. /// let my_div = div(&b)
  130. /// .attributes([
  131. /// attr("id", "my-div"),
  132. /// attr("class", "notification"),
  133. /// ])
  134. /// .finish();
  135. /// ```
  136. #[inline]
  137. pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
  138. where
  139. A: 'a + AsRef<[Attribute<'a>]>,
  140. {
  141. ElementBuilder {
  142. cx: self.cx,
  143. key: self.key,
  144. tag_name: self.tag_name,
  145. listeners: self.listeners,
  146. attributes,
  147. children: self.children,
  148. namespace: self.namespace,
  149. }
  150. }
  151. /// Set the children for this element.
  152. ///
  153. /// You can use this method to customize the backing storage for children,
  154. /// for example to use a fixed-size array instead of the default
  155. /// dynamically-sized `bumpalo::collections::Vec`.
  156. ///
  157. /// Any children already added to the builder will be overridden.
  158. ///
  159. /// # Example
  160. ///
  161. /// ```no_run
  162. /// use dioxus::{builder::*, bumpalo::Bump};
  163. ///
  164. /// let b = Bump::new();
  165. ///
  166. /// // Create a `<div>` with a fixed-size array of two `<span>` children.
  167. /// let my_div = div(&b)
  168. /// .children([
  169. /// span(&b).finish(),
  170. /// span(&b).finish(),
  171. /// ])
  172. /// .finish();
  173. /// ```
  174. #[inline]
  175. pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
  176. where
  177. C: 'a + AsRef<[VNode<'a>]>,
  178. {
  179. ElementBuilder {
  180. cx: self.cx,
  181. key: self.key,
  182. tag_name: self.tag_name,
  183. listeners: self.listeners,
  184. attributes: self.attributes,
  185. children,
  186. namespace: self.namespace,
  187. }
  188. }
  189. /// Set the namespace for this element.
  190. ///
  191. /// # Example
  192. ///
  193. /// ```no_run
  194. /// use dioxus::{builder::*, bumpalo::Bump};
  195. ///
  196. /// let b = Bump::new();
  197. ///
  198. /// // Create a `<td>` tag with an xhtml namespace
  199. /// let my_td = td(&b)
  200. /// .namespace(Some("http://www.w3.org/1999/xhtml"))
  201. /// .finish();
  202. /// ```
  203. #[inline]
  204. pub fn namespace(self, namespace: Option<&'static str>) -> Self {
  205. ElementBuilder {
  206. cx: self.cx,
  207. key: self.key,
  208. tag_name: self.tag_name,
  209. listeners: self.listeners,
  210. attributes: self.attributes,
  211. children: self.children,
  212. namespace,
  213. }
  214. }
  215. /// Set this element's key.
  216. ///
  217. /// When diffing sets of siblings, if an old sibling and new sibling share a
  218. /// key, then they will always reuse the same physical DOM VNode. This is
  219. /// important when using CSS animations, web components, third party JS, or
  220. /// anything else that makes the diffing implementation observable.
  221. ///
  222. /// Do not use keys if such a scenario does not apply. Keyed diffing is
  223. /// generally more expensive than not, since it is putting greater
  224. /// constraints on the diffing algorithm.
  225. ///
  226. /// # Invariants You Must Uphold
  227. ///
  228. /// The key may not be `u32::MAX`, which is a reserved key value.
  229. ///
  230. /// Keys must be unique among siblings.
  231. ///
  232. /// All sibling VNodes must be keyed, or they must all not be keyed. You may
  233. /// not mix keyed and unkeyed siblings.
  234. ///
  235. /// # Example
  236. ///
  237. /// ```no_run
  238. /// use dioxus::{builder::*, bumpalo::Bump};
  239. ///
  240. /// let b = Bump::new();
  241. ///
  242. /// let my_li = li(&b)
  243. /// .key(1337)
  244. /// .finish();
  245. /// ```
  246. #[inline]
  247. pub fn key(mut self, key: &'a str) -> Self {
  248. self.key = NodeKey(Some(key));
  249. self
  250. }
  251. pub fn key2(mut self, args: Arguments) -> Self {
  252. let key = match args.as_str() {
  253. Some(static_str) => static_str,
  254. None => {
  255. use bumpalo::core_alloc::fmt::Write;
  256. let mut s = bumpalo::collections::String::new_in(self.cx.bump());
  257. s.write_fmt(args).unwrap();
  258. s.into_bump_str()
  259. }
  260. };
  261. self.key = NodeKey(Some(key));
  262. self
  263. }
  264. /// Create the virtual DOM VNode described by this builder.
  265. ///
  266. /// # Example
  267. ///
  268. /// ```no_run
  269. /// use dioxus::{builder::*, bumpalo::Bump, VNode};
  270. ///
  271. /// let b = Bump::new();
  272. ///
  273. /// // Start with a builder...
  274. /// let builder: ElementBuilder<_, _, _> = div(&b);
  275. ///
  276. /// // ...and finish it to create a virtual DOM VNode!
  277. /// let my_div: VNode = builder.finish();
  278. /// ```
  279. #[inline]
  280. pub fn finish(mut self) -> VNode<'a> {
  281. let bump = self.cx.bump();
  282. let children: &'a Children = bump.alloc(self.children);
  283. let children: &'a [VNode<'a>] = children.as_ref();
  284. let listeners: &'a Listeners = bump.alloc(self.listeners);
  285. let listeners: &'a [Listener<'a>] = listeners.as_ref();
  286. for listener in listeners {
  287. // bump the context id forward
  288. let id = self.cx.listener_id.get();
  289. self.cx.listener_id.set(id + 1);
  290. // Add this listener to the context list
  291. // This casts the listener to a self-referential pointer
  292. // This is okay because the bump arena is stable
  293. // TODO: maybe not add it into CTX immediately
  294. let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(listener) };
  295. self.cx.scope_ref.listeners.borrow_mut().push((
  296. r.mounted_node as *const _ as *mut _,
  297. r.callback as *const _ as *mut _,
  298. ));
  299. }
  300. let attributes: &'a Attributes = bump.alloc(self.attributes);
  301. let attributes: &'a [Attribute<'a>] = attributes.as_ref();
  302. VNode::element(
  303. bump,
  304. self.key,
  305. self.tag_name,
  306. listeners,
  307. attributes,
  308. children,
  309. self.namespace,
  310. )
  311. }
  312. }
  313. impl<'a, 'b, Attributes, Children>
  314. ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
  315. where
  316. Attributes: 'a + AsRef<[Attribute<'a>]>,
  317. Children: 'a + AsRef<[VNode<'a>]>,
  318. {
  319. // / Add a new event listener to this element.
  320. // /
  321. // / The `event` string specifies which event will be listened for. The
  322. // / `callback` function is the function that will be invoked if the
  323. // / specified event occurs.
  324. // /
  325. // / # Example
  326. // /
  327. // / ```no_run
  328. // / use dioxus::{builder::*, bumpalo::Bump};
  329. // /
  330. // / let b = Bump::new();
  331. // /
  332. // / // A button that does something when clicked!
  333. // / let my_button = button(&b)
  334. // / .on("click", |event| {
  335. // / // ...
  336. // / })
  337. // / .finish();
  338. // / ```
  339. // pub fn on(self, event: &'static str, callback: impl FnMut(VirtualEvent) + 'a) -> Self {
  340. // let bump = &self.cx.bump();
  341. // let listener = Listener {
  342. // event,
  343. // callback: bump.alloc(callback),
  344. // scope: self.cx.scope_ref.arena_idx,
  345. // mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
  346. // };
  347. // self.add_listener(listener)
  348. // }
  349. // pub fn add_listener(mut self, listener: Listener<'a>) -> Self {
  350. // self.listeners.push(listener);
  351. // // bump the context id forward
  352. // let id = self.cx.listener_id.get();
  353. // self.cx.listener_id.set(id + 1);
  354. // // Add this listener to the context list
  355. // // This casts the listener to a self-referential pointer
  356. // // This is okay because the bump arena is stable
  357. // self.listeners.last().map(|g| {
  358. // let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(g) };
  359. // self.cx.scope_ref.listeners.borrow_mut().push((
  360. // r.mounted_node as *const _ as *mut _,
  361. // r.callback as *const _ as *mut _,
  362. // ));
  363. // });
  364. // self
  365. // }
  366. }
  367. impl<'a, 'b, Listeners, Children>
  368. ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
  369. where
  370. Listeners: 'a + AsRef<[Listener<'a>]>,
  371. Children: 'a + AsRef<[VNode<'a>]>,
  372. {
  373. /// Add a new attribute to this element.
  374. ///
  375. /// # Example
  376. ///
  377. /// ```no_run
  378. /// use dioxus::{builder::*, bumpalo::Bump};
  379. ///
  380. /// let b = Bump::new();
  381. ///
  382. /// // Create the `<div id="my-div"/>` element.
  383. /// let my_div = div(&b).attr("id", "my-div").finish();
  384. /// ```
  385. pub fn attr(mut self, name: &'static str, args: std::fmt::Arguments) -> Self {
  386. let (value, is_static) = raw_text(self.cx.bump(), args);
  387. self.attributes.push(Attribute {
  388. name,
  389. value,
  390. is_static,
  391. namespace: None,
  392. });
  393. self
  394. }
  395. }
  396. impl<'a, 'b, Listeners, Attributes>
  397. ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
  398. where
  399. Listeners: 'a + AsRef<[Listener<'a>]>,
  400. Attributes: 'a + AsRef<[Attribute<'a>]>,
  401. {
  402. /// Add a new child to this element.
  403. ///
  404. /// # Example
  405. ///
  406. /// ```no_run
  407. /// use dioxus::{builder::*, bumpalo::Bump};
  408. /// use js_sys::Math;
  409. ///
  410. /// let b = Bump::new();
  411. ///
  412. /// // Create `<p><span></span></p>`.
  413. /// let my_div = p(&b)
  414. /// .child(span(&b).finish())
  415. /// .finish();
  416. /// ```
  417. #[inline]
  418. pub fn child(mut self, child: VNode<'a>) -> Self {
  419. self.children.push(child);
  420. self
  421. }
  422. /// Add multiple children to this element from an iterator.
  423. ///
  424. /// # Example
  425. ///
  426. /// ```no_run
  427. /// use dioxus::{builder::*, bumpalo::Bump};
  428. ///
  429. /// let b = Bump::new();
  430. ///
  431. /// let my_div = p(&b)
  432. /// .iter_child((0..10).map(|f| span(&b).finish())
  433. /// .finish();
  434. /// ```
  435. pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
  436. todo!();
  437. let len_before = self.children.len();
  438. for item in nodes {
  439. todo!()
  440. // let child = item.into_vnode(&self.cx);
  441. // self.children.push(child);
  442. }
  443. if cfg!(debug_assertions) {
  444. if self.children.len() > len_before + 1 {
  445. if self.children.last().unwrap().key().is_none() {
  446. log::error!(
  447. r#"
  448. Warning: Each child in an array or iterator should have a unique "key" prop.
  449. Not providing a key will lead to poor performance with lists.
  450. See docs.rs/dioxus for more information.
  451. ---
  452. To help you identify where this error is coming from, we've generated a backtrace.
  453. "#,
  454. );
  455. }
  456. }
  457. }
  458. self
  459. }
  460. }