nodebuilder.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. //! Helpers for building virtual DOM VNodes.
  2. use std::{any::Any, borrow::BorrowMut, intrinsics::transmute, u128};
  3. use crate::{
  4. context::NodeCtx,
  5. events::VirtualEvent,
  6. innerlude::{Properties, VComponent, FC},
  7. nodes::{Attribute, Listener, NodeKey, VNode},
  8. prelude::VElement,
  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,
  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. // pub fn new<B>(ctx: &'a mut NodeCtx<'a>, tag_name: &'a str) -> Self {
  64. let bump = ctx.bump;
  65. ElementBuilder {
  66. ctx,
  67. key: NodeKey::NONE,
  68. tag_name,
  69. listeners: bumpalo::collections::Vec::new_in(bump),
  70. attributes: bumpalo::collections::Vec::new_in(bump),
  71. children: bumpalo::collections::Vec::new_in(bump),
  72. namespace: None,
  73. }
  74. }
  75. }
  76. impl<'a, 'b, Listeners, Attributes, Children>
  77. ElementBuilder<'a, 'b, Listeners, Attributes, Children>
  78. where
  79. Listeners: 'a + AsRef<[Listener<'a>]>,
  80. Attributes: 'a + AsRef<[Attribute<'a>]>,
  81. Children: 'a + AsRef<[VNode<'a>]>,
  82. {
  83. /// Set the listeners for this element.
  84. ///
  85. /// You can use this method to customize the backing storage for listeners,
  86. /// for example to use a fixed-size array instead of the default
  87. /// dynamically-sized `bumpalo::collections::Vec`.
  88. ///
  89. /// Any listeners already added to the builder will be overridden.
  90. ///
  91. /// # Example
  92. ///
  93. /// ```no_run
  94. /// use dioxus::{builder::*, bumpalo::Bump};
  95. ///
  96. /// let b = Bump::new();
  97. ///
  98. /// // Create a `<div>` with a fixed-size array of two listeners.
  99. /// let my_div = div(&b)
  100. /// .listeners([
  101. /// on(&b, "click", |root, vdom, event| {
  102. /// // ...
  103. /// }),
  104. /// on(&b, "dblclick", |root, vdom, event| {
  105. /// // ...
  106. /// }),
  107. /// ])
  108. /// .finish();
  109. /// ```
  110. #[inline]
  111. pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
  112. where
  113. L: 'a + AsRef<[Listener<'a>]>,
  114. {
  115. ElementBuilder {
  116. ctx: self.ctx,
  117. key: self.key,
  118. tag_name: self.tag_name,
  119. listeners,
  120. attributes: self.attributes,
  121. children: self.children,
  122. namespace: self.namespace,
  123. }
  124. }
  125. /// Set the attributes for this element.
  126. ///
  127. /// You can use this method to customize the backing storage for attributes,
  128. /// for example to use a fixed-size array instead of the default
  129. /// dynamically-sized `bumpalo::collections::Vec`.
  130. ///
  131. /// Any attributes already added to the builder will be overridden.
  132. ///
  133. /// # Example
  134. ///
  135. /// ```no_run
  136. /// use dioxus::{builder::*, bumpalo::Bump, Attribute};
  137. ///
  138. /// let b = Bump::new();
  139. ///
  140. /// // Create a `<div>` with a fixed-size array of two attributes.
  141. /// let my_div = div(&b)
  142. /// .attributes([
  143. /// attr("id", "my-div"),
  144. /// attr("class", "notification"),
  145. /// ])
  146. /// .finish();
  147. /// ```
  148. #[inline]
  149. pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
  150. where
  151. A: 'a + AsRef<[Attribute<'a>]>,
  152. {
  153. ElementBuilder {
  154. ctx: self.ctx,
  155. key: self.key,
  156. tag_name: self.tag_name,
  157. listeners: self.listeners,
  158. attributes,
  159. children: self.children,
  160. namespace: self.namespace,
  161. }
  162. }
  163. /// Set the children for this element.
  164. ///
  165. /// You can use this method to customize the backing storage for children,
  166. /// for example to use a fixed-size array instead of the default
  167. /// dynamically-sized `bumpalo::collections::Vec`.
  168. ///
  169. /// Any children already added to the builder will be overridden.
  170. ///
  171. /// # Example
  172. ///
  173. /// ```no_run
  174. /// use dioxus::{builder::*, bumpalo::Bump};
  175. ///
  176. /// let b = Bump::new();
  177. ///
  178. /// // Create a `<div>` with a fixed-size array of two `<span>` children.
  179. /// let my_div = div(&b)
  180. /// .children([
  181. /// span(&b).finish(),
  182. /// span(&b).finish(),
  183. /// ])
  184. /// .finish();
  185. /// ```
  186. #[inline]
  187. pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
  188. where
  189. C: 'a + AsRef<[VNode<'a>]>,
  190. {
  191. ElementBuilder {
  192. ctx: self.ctx,
  193. key: self.key,
  194. tag_name: self.tag_name,
  195. listeners: self.listeners,
  196. attributes: self.attributes,
  197. children,
  198. namespace: self.namespace,
  199. }
  200. }
  201. /// Set the namespace for this element.
  202. ///
  203. /// # Example
  204. ///
  205. /// ```no_run
  206. /// use dioxus::{builder::*, bumpalo::Bump};
  207. ///
  208. /// let b = Bump::new();
  209. ///
  210. /// // Create a `<td>` tag with an xhtml namespace
  211. /// let my_td = td(&b)
  212. /// .namespace(Some("http://www.w3.org/1999/xhtml"))
  213. /// .finish();
  214. /// ```
  215. #[inline]
  216. pub fn namespace(self, namespace: Option<&'a str>) -> Self {
  217. ElementBuilder {
  218. ctx: self.ctx,
  219. key: self.key,
  220. tag_name: self.tag_name,
  221. listeners: self.listeners,
  222. attributes: self.attributes,
  223. children: self.children,
  224. namespace,
  225. }
  226. }
  227. /// Set this element's key.
  228. ///
  229. /// When diffing sets of siblings, if an old sibling and new sibling share a
  230. /// key, then they will always reuse the same physical DOM VNode. This is
  231. /// important when using CSS animations, web components, third party JS, or
  232. /// anything else that makes the diffing implementation observable.
  233. ///
  234. /// Do not use keys if such a scenario does not apply. Keyed diffing is
  235. /// generally more expensive than not, since it is putting greater
  236. /// constraints on the diffing algorithm.
  237. ///
  238. /// # Invariants You Must Uphold
  239. ///
  240. /// The key may not be `u32::MAX`, which is a reserved key value.
  241. ///
  242. /// Keys must be unique among siblings.
  243. ///
  244. /// All sibling VNodes must be keyed, or they must all not be keyed. You may
  245. /// not mix keyed and unkeyed siblings.
  246. ///
  247. /// # Example
  248. ///
  249. /// ```no_run
  250. /// use dioxus::{builder::*, bumpalo::Bump};
  251. ///
  252. /// let b = Bump::new();
  253. ///
  254. /// let my_li = li(&b)
  255. /// .key(1337)
  256. /// .finish();
  257. /// ```
  258. #[inline]
  259. pub fn key(mut self, key: u32) -> Self {
  260. use std::u32;
  261. debug_assert!(key != u32::MAX);
  262. self.key = NodeKey(key);
  263. self
  264. }
  265. /// Create the virtual DOM VNode described by this builder.
  266. ///
  267. /// # Example
  268. ///
  269. /// ```no_run
  270. /// use dioxus::{builder::*, bumpalo::Bump, VNode};
  271. ///
  272. /// let b = Bump::new();
  273. ///
  274. /// // Start with a builder...
  275. /// let builder: ElementBuilder<_, _, _> = div(&b);
  276. ///
  277. /// // ...and finish it to create a virtual DOM VNode!
  278. /// let my_div: VNode = builder.finish();
  279. /// ```
  280. #[inline]
  281. pub fn finish(self) -> VNode<'a> {
  282. let children: &'a Children = self.ctx.bump.alloc(self.children);
  283. let children: &'a [VNode<'a>] = children.as_ref();
  284. let listeners: &'a Listeners = self.ctx.bump.alloc(self.listeners);
  285. let listeners: &'a [Listener<'a>] = listeners.as_ref();
  286. let attributes: &'a Attributes = self.ctx.bump.alloc(self.attributes);
  287. let attributes: &'a [Attribute<'a>] = attributes.as_ref();
  288. VNode::element(
  289. self.ctx.bump,
  290. self.key,
  291. self.tag_name,
  292. listeners,
  293. attributes,
  294. children,
  295. self.namespace,
  296. )
  297. }
  298. }
  299. impl<'a, 'b, Attributes, Children>
  300. ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
  301. where
  302. Attributes: 'a + AsRef<[Attribute<'a>]>,
  303. Children: 'a + AsRef<[VNode<'a>]>,
  304. {
  305. /// Add a new event listener to this element.
  306. ///
  307. /// The `event` string specifies which event will be listened for. The
  308. /// `callback` function is the function that will be invoked if the
  309. /// specified event occurs.
  310. ///
  311. /// # Example
  312. ///
  313. /// ```no_run
  314. /// use dioxus::{builder::*, bumpalo::Bump};
  315. ///
  316. /// let b = Bump::new();
  317. ///
  318. /// // A button that does something when clicked!
  319. /// let my_button = button(&b)
  320. /// .on("click", |event| {
  321. /// // ...
  322. /// })
  323. /// .finish();
  324. /// ```
  325. pub fn on(self, event: &'static str, callback: impl Fn(VirtualEvent) + 'a) -> Self {
  326. let listener = Listener {
  327. event,
  328. callback: self.ctx.bump.alloc(callback),
  329. id: *self.ctx.idx.borrow(),
  330. scope: self.ctx.scope,
  331. };
  332. self.add_listener(listener)
  333. }
  334. pub fn add_listener(mut self, listener: Listener<'a>) -> Self {
  335. self.listeners.push(listener);
  336. // bump the context id forward
  337. *self.ctx.idx.borrow_mut() += 1;
  338. // Add this listener to the context list
  339. // This casts the listener to a self-referential pointer
  340. // This is okay because the bump arena is stable
  341. self.listeners.last().map(|g| {
  342. let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(g) };
  343. self.ctx.listeners.borrow_mut().push(r.callback as *const _);
  344. });
  345. self
  346. }
  347. }
  348. impl<'a, 'b, Listeners, Children>
  349. ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
  350. where
  351. Listeners: 'a + AsRef<[Listener<'a>]>,
  352. Children: 'a + AsRef<[VNode<'a>]>,
  353. {
  354. /// Add a new attribute to this element.
  355. ///
  356. /// # Example
  357. ///
  358. /// ```no_run
  359. /// use dioxus::{builder::*, bumpalo::Bump};
  360. ///
  361. /// let b = Bump::new();
  362. ///
  363. /// // Create the `<div id="my-div"/>` element.
  364. /// let my_div = div(&b).attr("id", "my-div").finish();
  365. /// ```
  366. #[inline]
  367. pub fn attr(mut self, name: &'static str, value: &'a str) -> Self {
  368. self.attributes.push(Attribute { name, value });
  369. self
  370. }
  371. /// Conditionally add a "boolean-style" attribute to this element.
  372. ///
  373. /// If the `should_add` parameter is true, then adds an attribute with the
  374. /// given `name` and an empty string value. If the `should_add` parameter is
  375. /// false, then the attribute is not added.
  376. ///
  377. /// This method is useful for attributes whose semantics are defined by
  378. /// whether or not the attribute is present or not, and whose value is
  379. /// ignored. Example attributes like this include:
  380. ///
  381. /// * `checked`
  382. /// * `hidden`
  383. /// * `selected`
  384. ///
  385. /// # Example
  386. ///
  387. /// ```no_run
  388. /// use dioxus::{builder::*, bumpalo::Bump};
  389. /// use js_sys::Math;
  390. ///
  391. /// let b = Bump::new();
  392. ///
  393. /// // Create the `<div>` that is randomly hidden 50% of the time.
  394. /// let my_div = div(&b)
  395. /// .bool_attr("hidden", Math::random() >= 0.5)
  396. /// .finish();
  397. /// ```
  398. pub fn bool_attr(mut self, name: &'static str, should_add: bool) -> Self {
  399. if should_add {
  400. self.attributes.push(Attribute { name, value: "" });
  401. }
  402. self
  403. }
  404. }
  405. impl<'a, 'b, Listeners, Attributes>
  406. ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
  407. where
  408. Listeners: 'a + AsRef<[Listener<'a>]>,
  409. Attributes: 'a + AsRef<[Attribute<'a>]>,
  410. {
  411. /// Add a new child to this element.
  412. ///
  413. /// # Example
  414. ///
  415. /// ```no_run
  416. /// use dioxus::{builder::*, bumpalo::Bump};
  417. /// use js_sys::Math;
  418. ///
  419. /// let b = Bump::new();
  420. ///
  421. /// // Create `<p><span></span></p>`.
  422. /// let my_div = p(&b)
  423. /// .child(span(&b).finish())
  424. /// .finish();
  425. /// ```
  426. #[inline]
  427. pub fn child(mut self, child: VNode<'a>) -> Self {
  428. self.children.push(child);
  429. self
  430. }
  431. // pub fn virtual_child(mut self)
  432. }
  433. /// Construct a text VNode.
  434. ///
  435. /// This is `dioxus`'s virtual DOM equivalent of `document.createTextVNode`.
  436. ///
  437. /// # Example
  438. ///
  439. /// ```no_run
  440. /// use dioxus::builder::*;
  441. ///
  442. /// let my_text = text("hello, dioxus!");
  443. /// ```
  444. #[inline]
  445. pub fn text<'a>(contents: &'a str) -> VNode<'a> {
  446. VNode::text(contents)
  447. }
  448. pub fn text2<'a>(contents: bumpalo::collections::String<'a>) -> VNode<'a> {
  449. let f: &'a str = contents.into_bump_str();
  450. VNode::text(f)
  451. }
  452. // pub fn text<'a>(contents: &'a str) -> VNode<'a> {
  453. // VNode::text(contents)
  454. // }
  455. /// Construct an attribute for an element.
  456. ///
  457. /// # Example
  458. ///
  459. /// This example creates the `id="my-id"` for some element like `<div
  460. /// id="my-id"/>`.
  461. ///
  462. /// ```no_run
  463. /// use dioxus::builder::*;
  464. ///
  465. /// let my_id_attr = attr("id", "my-id");
  466. /// ```
  467. pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
  468. Attribute { name, value }
  469. }
  470. // /// Create an event listener.
  471. // ///
  472. // /// `event` is the type of event to listen for, e.g. `"click"`. The `callback`
  473. // /// is the function that will be invoked when the event occurs.
  474. // ///
  475. // /// # Example
  476. // ///
  477. // /// ```no_run
  478. // /// use dioxus::{builder::*, bumpalo::Bump};
  479. // ///
  480. // /// let b = Bump::new();
  481. // ///
  482. // /// let listener = on(&b, "click", |root, vdom, event| {
  483. // /// // do something when a click happens...
  484. // /// });
  485. // /// ```
  486. // pub fn on<'a, 'b>(
  487. // // pub fn on<'a, 'b, F: 'static>(
  488. // bump: &'a Bump,
  489. // event: &'static str,
  490. // callback: impl Fn(VirtualEvent) + 'a,
  491. // ) -> Listener<'a> {
  492. // Listener {
  493. // event,
  494. // callback: bump.alloc(callback),
  495. // }
  496. // }
  497. pub fn virtual_child<'a, T: Properties>(ctx: &NodeCtx<'a>, f: FC<T>, p: T) -> VNode<'a> {
  498. VNode::Component(crate::nodes::VComponent::new(f, ctx.bump.alloc(p)))
  499. }
  500. trait Bany {
  501. // fn compare_to<P: Properties>(other: &P) -> bool;
  502. }
  503. pub fn test_vchild<T: Properties>(p: &T) {
  504. // let r = p as *const dyn Bany;
  505. // let r = p as *const dyn Bany;
  506. // std::any::Any
  507. // let r = p as &dyn Any;
  508. // let g = p as *const u128;
  509. // let l = unsafe { std::mem::transmute::<&T, &dyn Any>(p) };
  510. }
  511. /*
  512. Problem:
  513. compare two props that we know are the same type without transmute
  514. */