1//! Helpers for building virtual DOM nodes.
3use crate::{Attribute, Listener, Node, RootRender, VdomWeak};
6/// A virtual DOM element builder.
8/// Typically constructed with element-specific constructors, eg the `div`
9/// function for building `<div>` elements or the `button` function for building
10/// `<button>` elements.
11#[derive(Clone, Debug)]
12pub struct ElementBuilder<'a, Listeners, Attributes, Children>
14 Listeners: 'a + AsRef<[Listener<'a>]>,
15 Attributes: 'a + AsRef<[Attribute<'a>]>,
16 Children: 'a + AsRef<[Node<'a>]>,
21 attributes: Attributes,
25impl<'a, Listeners, Attributes, Children> ElementBuilder<'a, Listeners, Attributes, Children>
27 Listeners: 'a + AsRef<[Listener<'a>]>,
28 Attributes: 'a + AsRef<[Attribute<'a>]>,
29 Children: 'a + AsRef<[Node<'a>]>,
31 /// Set the listeners for this element.
33 /// You can use this method to customize the backing storage for listeners,
34 /// for example to use a fixed-size array instead of the default
35 /// dynamically-sized `bumpalo::collections::Vec`.
37 /// Any listeners already added to the builder will be overridden.
42 /// use dodrio::{builder::*, bumpalo::Bump};
44 /// let b = Bump::new();
46 /// // Create a `<div>` with a fixed-size array of two listeners.
47 /// let my_div = div(&b)
49 /// on(&b, "click", |root, vdom, event| {
52 /// on(&b, "dblclick", |root, vdom, event| {
59 pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, L, Attributes, Children>
61 L: 'a + AsRef<[Listener<'a>]>,
65 tag_name: self.tag_name,
67 attributes: self.attributes,
68 children: self.children,
72 /// Set the attributes for this element.
74 /// You can use this method to customize the backing storage for attributes,
75 /// for example to use a fixed-size array instead of the default
76 /// dynamically-sized `bumpalo::collections::Vec`.
78 /// Any attributes already added to the builder will be overridden.
83 /// use dodrio::{builder::*, bumpalo::Bump, Attribute};
85 /// let b = Bump::new();
87 /// // Create a `<div>` with a fixed-size array of two attributes.
88 /// let my_div = div(&b)
90 /// attr("id", "my-div"),
91 /// attr("class", "notification"),
96 pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, Listeners, A, Children>
98 A: 'a + AsRef<[Attribute<'a>]>,
102 tag_name: self.tag_name,
103 listeners: self.listeners,
105 children: self.children,
109 /// Set the children for this element.
111 /// You can use this method to customize the backing storage for children,
112 /// for example to use a fixed-size array instead of the default
113 /// dynamically-sized `bumpalo::collections::Vec`.
115 /// Any children already added to the builder will be overridden.
120 /// use dodrio::{builder::*, bumpalo::Bump};
122 /// let b = Bump::new();
124 /// // Create a `<div>` with a fixed-size array of two `<span>` children.
125 /// let my_div = div(&b)
127 /// span(&b).finish(),
128 /// span(&b).finish(),
133 pub fn children<C>(self, children: C) -> ElementBuilder<'a, Listeners, Attributes, C>
135 C: 'a + AsRef<[Node<'a>]>,
139 tag_name: self.tag_name,
140 listeners: self.listeners,
141 attributes: self.attributes,
146 /// Create the virtual DOM node described by this builder.
151 /// use dodrio::{builder::*, bumpalo::Bump, Node};
153 /// let b = Bump::new();
155 /// // Start with a builder...
156 /// let builder: ElementBuilder<_, _, _> = div(&b);
158 /// // ...and finish it to create a virtual DOM node!
159 /// let my_div: Node = builder.finish();
162 pub fn finish(self) -> Node<'a> {
173impl<'a, Attributes, Children>
174 ElementBuilder<'a, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
176 Attributes: 'a + AsRef<[Attribute<'a>]>,
177 Children: 'a + AsRef<[Node<'a>]>,
179 /// Add a new event listener to this element.
181 /// The `event` string specifies which event will be listened for. The
182 /// `callback` function is the function that will be invoked if the
183 /// specified event occurs.
188 /// use dodrio::{builder::*, bumpalo::Bump};
190 /// let b = Bump::new();
192 /// // A button that does something when clicked!
193 /// let my_button = button(&b)
194 /// .on("click", |root, vdom, event| {
200 pub fn on<F>(mut self, event: &'a str, callback: F) -> Self
202 F: 'static + Fn(&mut dyn RootRender, VdomWeak, web_sys::Event),
204 self.listeners.push(Listener {
206 callback: self.bump.alloc(callback),
212impl<'a, Listeners, Children>
213 ElementBuilder<'a, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
215 Listeners: 'a + AsRef<[Listener<'a>]>,
216 Children: 'a + AsRef<[Node<'a>]>,
218 /// Add a new attribute to this element.
223 /// use dodrio::{builder::*, bumpalo::Bump};
225 /// let b = Bump::new();
227 /// // Create the `<div id="my-div"/>` element.
228 /// let my_div = div(&b).attr("id", "my-div").finish();
231 pub fn attr(mut self, name: &'a str, value: &'a str) -> Self {
232 self.attributes.push(Attribute { name, value });
236 /// Conditionally add a "boolean-style" attribute to this element.
238 /// If the `should_add` parameter is true, then adds an attribute with the
239 /// given `name` and an empty string value. If the `should_add` parameter is
240 /// false, then the attribute is not added.
242 /// This method is useful for attributes whose semantics are defined by
243 /// whether or not the attribute is present or not, and whose value is
244 /// ignored. Example attributes like this include:
253 /// use dodrio::{builder::*, bumpalo::Bump};
254 /// use js_sys::Math;
256 /// let b = Bump::new();
258 /// // Create the `<div>` that is randomly hidden 50% of the time.
259 /// let my_div = div(&b)
260 /// .bool_attr("hidden", Math::random() >= 0.5)
263 pub fn bool_attr(mut self, name: &'a str, should_add: bool) -> Self {
265 self.attributes.push(Attribute { name, value: "" });
271impl<'a, Listeners, Attributes>
272 ElementBuilder<'a, Listeners, Attributes, bumpalo::collections::Vec<'a, Node<'a>>>
274 Listeners: 'a + AsRef<[Listener<'a>]>,
275 Attributes: 'a + AsRef<[Attribute<'a>]>,
277 /// Add a new child to this element.
282 /// use dodrio::{builder::*, bumpalo::Bump};
283 /// use js_sys::Math;
285 /// let b = Bump::new();
287 /// // Create `<p><span></span></p>`.
288 /// let my_div = p(&b)
289 /// .child(span(&b).finish())
293 pub fn child(mut self, child: Node<'a>) -> Self {
294 self.children.push(child);
299macro_rules! builder_constructors {
311 bumpalo::collections::Vec<'a, Listener<'a>>,
312 bumpalo::collections::Vec<'a, Attribute<'a>>,
313 bumpalo::collections::Vec<'a, Node<'a>>,
317 tag_name: stringify!($name),
318 listeners: bumpalo::collections::Vec::new_in(bump),
319 attributes: bumpalo::collections::Vec::new_in(bump),
320 children: bumpalo::collections::Vec::new_in(bump),
327// Organized in the same order as
328// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
330// Does not include obsolete elements.
331builder_constructors! {
335 /// [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
339 /// [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
343 /// [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
347 /// [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
351 /// [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
355 /// [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
362 /// [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
366 // Content sectioning
369 /// [`<address>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address)
373 /// [`<article>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article)
377 /// [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
381 /// [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)
385 /// [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)
389 /// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
393 /// [`<h2>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2)
397 /// [`<h3>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3)
401 /// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
405 /// [`<h5>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5)
409 /// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
413 /// [`<hgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup)
417 /// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
421 /// [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)
425 /// [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)
432 /// [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)
436 /// [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)
440 /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
444 /// [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)
448 /// [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)
452 /// [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)
456 /// [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)
460 /// [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)
464 /// [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
468 /// [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)
472 /// [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)
476 /// [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)
480 /// [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
484 // Inline text semantics
487 /// [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
491 /// [`<abbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr)
495 /// [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)
499 /// [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)
503 /// [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)
507 /// [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)
511 /// [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)
515 /// [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)
519 /// [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)
523 /// [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)
527 /// [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)
531 /// [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)
535 /// [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)
539 /// [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)
543 /// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
547 /// [`<rb>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rb)
551 /// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
555 /// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
559 /// [`<rtc>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rtc)
563 /// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
567 /// [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)
571 /// [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)
575 /// [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)
579 /// [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)
583 /// [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)
587 /// [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)
591 /// [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)
595 /// [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)
599 /// [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)
603 /// [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)
607 /// [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)
611 // Image and multimedia
614 /// [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
618 /// [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
622 /// [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
626 /// [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)
630 /// [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)
634 /// [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
641 /// [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)
645 /// [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
649 /// [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)
653 /// [`<param>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param)
657 /// [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
661 /// [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)
668 /// [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
672 /// [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)
676 /// [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
683 /// [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)
687 /// [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)
694 /// [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)
698 /// [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)
702 /// [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
706 /// [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)
710 /// [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)
714 /// [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
718 /// [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)
722 /// [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)
726 /// [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)
730 /// [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)
737 /// [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
741 /// [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)
745 /// [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)
749 /// [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
753 /// [`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
757 /// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
761 /// [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)
765 /// [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)
769 /// [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)
773 /// [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
777 /// [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)
781 /// [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
785 /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
789 /// [`<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
793 // Interactive elements
796 /// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
800 /// [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
804 /// [`<menu>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu)
808 /// [`<menuitem>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menuitem)
812 /// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
819 /// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
823 /// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
828/// Construct a text node.
830/// This is `dodrio`'s virtual DOM equivalent of `document.createTextNode`.
835/// use dodrio::builder::*;
837/// let my_text = text("hello, dodrio!");
840pub fn text<'a>(contents: &'a str) -> Node<'a> {
844/// Construct an attribute for an element.
848/// This example creates the `id="my-id"` for some element like `<div
852/// use dodrio::builder::*;
854/// let my_id_attr = attr("id", "my-id");
856pub fn attr<'a>(name: &'a str, value: &'a str) -> Attribute<'a> {
857 Attribute { name, value }
860/// Create an event listener.
862/// `event` is the type of event to listen for, e.g. `"click"`. The `callback`
863/// is the function that will be invoked when the event occurs.
868/// use dodrio::{builder::*, bumpalo::Bump};
870/// let b = Bump::new();
872/// let listener = on(&b, "click", |root, vdom, event| {
873/// // do something when a click happens...
876pub fn on<'a, F>(bump: &'a Bump, event: &'a str, callback: F) -> Listener<'a>
878 F: Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static,
882 callback: bump.alloc(callback),
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list