use crate::{node::NodeKey, Attribute, Listener, Node, RootRender, VdomWeak};
use bumpalo::Bump;
#[derive(Clone, Debug)]
pub struct ElementBuilder<'a, Listeners, Attributes, Children>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[Node<'a>]>,
{
bump: &'a Bump,
key: NodeKey,
tag_name: &'a str,
listeners: Listeners,
attributes: Attributes,
children: Children,
namespace: Option<&'a str>,
}
impl<'a>
ElementBuilder<
'a,
bumpalo::collections::Vec<'a, Listener<'a>>,
bumpalo::collections::Vec<'a, Attribute<'a>>,
bumpalo::collections::Vec<'a, Node<'a>>,
>
{
pub fn new<B>(bump: B, tag_name: &'a str) -> Self
where
B: Into<&'a Bump>,
{
let bump = bump.into();
ElementBuilder {
bump,
key: NodeKey::NONE,
tag_name,
listeners: bumpalo::collections::Vec::new_in(bump),
attributes: bumpalo::collections::Vec::new_in(bump),
children: bumpalo::collections::Vec::new_in(bump),
namespace: None,
}
}
}
impl<'a, Listeners, Attributes, Children> ElementBuilder<'a, Listeners, Attributes, Children>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[Node<'a>]>,
{
#[inline]
pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, L, Attributes, Children>
where
L: 'a + AsRef<[Listener<'a>]>,
{
ElementBuilder {
bump: self.bump,
key: self.key,
tag_name: self.tag_name,
listeners,
attributes: self.attributes,
children: self.children,
namespace: self.namespace,
}
}
#[inline]
pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, Listeners, A, Children>
where
A: 'a + AsRef<[Attribute<'a>]>,
{
ElementBuilder {
bump: self.bump,
key: self.key,
tag_name: self.tag_name,
listeners: self.listeners,
attributes,
children: self.children,
namespace: self.namespace,
}
}
#[inline]
pub fn children<C>(self, children: C) -> ElementBuilder<'a, Listeners, Attributes, C>
where
C: 'a + AsRef<[Node<'a>]>,
{
ElementBuilder {
bump: self.bump,
key: self.key,
tag_name: self.tag_name,
listeners: self.listeners,
attributes: self.attributes,
children,
namespace: self.namespace,
}
}
#[inline]
pub fn namespace(self, namespace: Option<&'a str>) -> Self {
ElementBuilder {
bump: self.bump,
key: self.key,
tag_name: self.tag_name,
listeners: self.listeners,
attributes: self.attributes,
children: self.children,
namespace,
}
}
#[inline]
pub fn key(mut self, key: u32) -> Self {
use std::u32;
debug_assert!(key != u32::MAX);
self.key = NodeKey(key);
self
}
#[inline]
pub fn finish(self) -> Node<'a> {
let children: &'a Children = self.bump.alloc(self.children);
let children: &'a [Node<'a>] = children.as_ref();
let listeners: &'a Listeners = self.bump.alloc(self.listeners);
let listeners: &'a [Listener<'a>] = listeners.as_ref();
let attributes: &'a Attributes = self.bump.alloc(self.attributes);
let attributes: &'a [Attribute<'a>] = attributes.as_ref();
Node::element(
self.bump,
self.key,
self.tag_name,
listeners,
attributes,
children,
self.namespace,
)
}
}
impl<'a, Attributes, Children>
ElementBuilder<'a, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
where
Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[Node<'a>]>,
{
#[inline]
pub fn on<F>(mut self, event: &'a str, callback: F) -> Self
where
F: 'static + Fn(&mut dyn RootRender, VdomWeak, web_sys::Event),
{
self.listeners.push(Listener {
event,
callback: self.bump.alloc(callback),
});
self
}
}
impl<'a, Listeners, Children>
ElementBuilder<'a, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Children: 'a + AsRef<[Node<'a>]>,
{
#[inline]
pub fn attr(mut self, name: &'a str, value: &'a str) -> Self {
self.attributes.push(Attribute { name, value });
self
}
pub fn bool_attr(mut self, name: &'a str, should_add: bool) -> Self {
if should_add {
self.attributes.push(Attribute { name, value: "" });
}
self
}
}
impl<'a, Listeners, Attributes>
ElementBuilder<'a, Listeners, Attributes, bumpalo::collections::Vec<'a, Node<'a>>>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>,
{
#[inline]
pub fn child(mut self, child: Node<'a>) -> Self {
self.children.push(child);
self
}
}
macro_rules! builder_constructors {
( $(
$(#[$attr:meta])*
$name:ident;
)* ) => {
$(
$(#[$attr])*
#[inline]
pub fn $name<'a, B>(
bump: B,
) -> ElementBuilder<
'a,
bumpalo::collections::Vec<'a, Listener<'a>>,
bumpalo::collections::Vec<'a, Attribute<'a>>,
bumpalo::collections::Vec<'a, Node<'a>>,
>
where
B: Into<&'a Bump>
{
ElementBuilder::new(bump, stringify!($name))
}
)*
};
( $(
$(#[$attr:meta])*
$name:ident <> $namespace:tt;
)* ) => {
$(
$(#[$attr])*
#[inline]
pub fn $name<'a>(
bump: &'a Bump,
) -> ElementBuilder<
'a,
bumpalo::collections::Vec<'a, Listener<'a>>,
bumpalo::collections::Vec<'a, Attribute<'a>>,
bumpalo::collections::Vec<'a, Node<'a>>,
> {
let builder = ElementBuilder::new(bump, stringify!($name));
builder.namespace(Some($namespace))
}
)*
}
}
builder_constructors! {
base;
head;
link;
meta;
style;
title;
body;
address;
article;
aside;
footer;
header;
h1;
h2;
h3;
h4;
h5;
h6;
hgroup;
main;
nav;
section;
blockquote;
dd;
div;
dl;
dt;
figcaption;
figure;
hr;
li;
ol;
p;
pre;
ul;
a;
abbr;
b;
bdi;
bdo;
br;
cite;
code;
data;
dfn;
em;
i;
kbd;
mark;
q;
rb;
rp;
rt;
rtc;
ruby;
s;
samp;
small;
span;
strong;
sub;
sup;
time;
u;
var;
wbr;
area;
audio;
img;
map;
track;
video;
embed;
iframe;
object;
param;
picture;
source;
canvas;
noscript;
script;
del;
ins;
caption;
col;
colgroup;
table;
tbody;
td;
tfoot;
th;
thead;
tr;
button;
datalist;
fieldset;
form;
input;
label;
legend;
meter;
optgroup;
option;
output;
progress;
select;
textarea;
details;
dialog;
menu;
menuitem;
summary;
slot;
template;
}
builder_constructors! {
svg <> "http://www.w3.org/2000/svg" ;
path <> "http://www.w3.org/2000/svg";
circle <> "http://www.w3.org/2000/svg";
ellipse <> "http://www.w3.org/2000/svg";
line <> "http://www.w3.org/2000/svg";
polygon <> "http://www.w3.org/2000/svg";
polyline <> "http://www.w3.org/2000/svg";
rect <> "http://www.w3.org/2000/svg";
image <> "http://www.w3.org/2000/svg";
}
#[inline]
pub fn text<'a>(contents: &'a str) -> Node<'a> {
Node::text(contents)
}
pub fn attr<'a>(name: &'a str, value: &'a str) -> Attribute<'a> {
Attribute { name, value }
}
pub fn on<'a, F>(bump: &'a Bump, event: &'a str, callback: F) -> Listener<'a>
where
F: Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static,
{
Listener {
event,
callback: bump.alloc(callback),
}
}