@@ -7,7 +7,7 @@ use crate::visibility::Visibility;
77use crate::{keys, utils};
88use dodrio::{
99 bumpalo::{self, Bump},
10− on, Attribute, Node, Render, RootRender, VdomWeak,
10+ Node, Render, RootRender, VdomWeak,
1111};
1212use serde::{Deserialize, Serialize};
1313use std::marker::PhantomData;
@@ -140,65 +140,42 @@ impl<C: TodosActions> Todos<C> {
140140 where
141141 'a: 'bump,
142142 {
143− Node::element(
144− bump,
145− "header",
146− [],
147− [Attribute {
148− name: "class",
149− value: "header",
150− }],
151− [
152− Node::element(bump, "h1", [], [], [Node::text("todos")]),
153− Node::element(
154− bump,
155− "input",
156− [
157− on(bump, "input", |root, vdom, event| {
158− let input = event
159− .target()
160− .unwrap_throw()
161− .unchecked_into::<web_sys::HtmlInputElement>();
162− C::update_draft(root, vdom, input.value());
163− }),
164− on(bump, "keydown", |root, vdom, event| {
165− let event = event.unchecked_into::<web_sys::KeyboardEvent>();
166− if event.key_code() == keys::ENTER {
167− C::finish_draft(root, vdom);
168− }
169− }),
170− ],
171− [
172− Attribute {
173− name: "class",
174− value: "new-todo",
175− },
176− Attribute {
177− name: "placeholder",
178− value: "What needs to be done?",
179− },
180− Attribute {
181− name: "autofocus",
182− value: "",
183− },
184− Attribute {
185− name: "value",
186− value: &self.draft,
187− },
188− ],
189− [],
190− ),
191− ],
192− )
143+ use dodrio::builder::*;
144+
145+ header(bump)
146+ .attr("class", "header")
147+ .children([
148+ h1(bump).children([text("todos")]).finish(),
149+ input(bump)
150+ .on("input", |root, vdom, event| {
151+ let input = event
152+ .target()
153+ .unwrap_throw()
154+ .unchecked_into::<web_sys::HtmlInputElement>();
155+ C::update_draft(root, vdom, input.value());
156+ })
157+ .on("keydown", |root, vdom, event| {
158+ let event = event.unchecked_into::<web_sys::KeyboardEvent>();
159+ if event.key_code() == keys::ENTER {
160+ C::finish_draft(root, vdom);
161+ }
162+ })
163+ .attr("class", "new-todo")
164+ .attr("placeholder", "What needs to be done?")
165+ .attr("autofocus", "")
166+ .attr("value", &self.draft)
167+ .finish(),
168+ ])
169+ .finish()
193170 }
194171
195172 fn todos_list<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
196173 where
197174 'a: 'bump,
198175 {
199− use dodrio::bumpalo::collections::Vec;
176+ use dodrio::{builder::*, bumpalo::collections::Vec};
200177
201− let mut todos = Vec::new_in(bump);
178+ let mut todos = Vec::with_capacity_in(self.todos.len(), bump);
202179 todos.extend(
203180 self.todos
204181 .iter()
@@ -209,91 +186,43 @@ impl<C: TodosActions> Todos<C> {
209186 })
210187 .map(|t| t.render(bump)),
211188 );
212− let todos = todos.into_bump_slice();
213−
214− let mut input_attrs = bumpalo::vec![
215− in bump;
216− Attribute {
217− name: "class",
218− value: "toggle-all",
219− },
220− Attribute {
221− name: "id",
222− value: "toggle-all",
223− },
224− Attribute {
225− name: "type",
226− value: "checkbox",
227− },
228− Attribute {
229− name: "name",
230− value: "toggle",
231− }
232− ];
233− if self.todos.iter().all(|t| t.is_complete()) {
234− input_attrs.push(Attribute {
235− name: "checked",
236− value: "",
237− });
238− }
239− let input_attrs = input_attrs.into_bump_slice();
240−
241− Node::element(
242− bump,
243− "section",
244− [],
245− [
246− Attribute {
247− name: "class",
248− value: "main",
249− },
250− Attribute {
251− name: "visibility",
252− value: if self.todos.is_empty() {
253− "hidden"
254− } else {
255− "visible"
256− },
189+
190+ section(bump)
191+ .attr("class", "main")
192+ .attr(
193+ "visibility",
194+ if self.todos.is_empty() {
195+ "hidden"
196+ } else {
197+ "visible"
257198 },
258− ],
259− [
260− Node::element(
261− bump,
262− "input",
263− [on(bump, "click", |root, vdom, _event| {
199+ )
200+ .children([
201+ input(bump)
202+ .attr("class", "toggle-all")
203+ .attr("id", "toggle-all")
204+ .attr("type", "checkbox")
205+ .attr("name", "toggle")
206+ .bool_attr("checked", self.todos.iter().all(|t| t.is_complete()))
207+ .on("click", |root, vdom, _event| {
264208 C::toggle_all(root, vdom);
265− })],
266− input_attrs,
267− [],
268− ),
269− Node::element(
270− bump,
271− "label",
272− [],
273− [Attribute {
274− name: "for",
275− value: "toggle-all",
276− }],
277− [Node::text("Mark all as complete")],
278− ),
279− Node::element(
280− bump,
281− "ul",
282− [],
283− [Attribute {
284− name: "class",
285− value: "todo-list",
286− }],
287− todos,
288− ),
289− ],
290− )
209+ })
210+ .finish(),
211+ label(bump)
212+ .attr("for", "toggle-all")
213+ .children([text("Mark all as complete")])
214+ .finish(),
215+ ul(bump).attr("class", "todo-list").children(todos).finish(),
216+ ])
217+ .finish()
291218 }
292219
293220 fn footer<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
294221 where
295222 'a: 'bump,
296223 {
224+ use dodrio::builder::*;
225+
297226 let completed_count = self.todos.iter().filter(|t| t.is_complete()).count();
298227 let incomplete_count = self.todos.len() - completed_count;
299228 let items_left = if incomplete_count == 1 {
@@ -303,95 +232,43 @@ impl<C: TodosActions> Todos<C> {
303232 };
304233 let incomplete_count = bumpalo::format!(in bump, "{}", incomplete_count);
305234
306− let attrs = if self.todos.is_empty() {
307− &bump.alloc([
308− Attribute {
309− name: "class",
310− value: "footer",
311− },
312− Attribute {
313− name: "hidden",
314− value: "",
315− },
316− ])[..]
317− } else {
318− &bump.alloc([Attribute {
319− name: "class",
320− value: "footer",
321− }])[..]
322− };
323−
324235 let clear_completed_text = bumpalo::format!(
325236 in bump,
326237 "Clear completed ({})",
327238 self.todos.iter().filter(|t| t.is_complete()).count()
328239 );
329240
330− Node::element(
331− bump,
332− "footer",
333− [],
334− attrs,
335− [
336− Node::element(
337− bump,
338− "span",
339− [],
340− [Attribute {
341− name: "class",
342− value: "todo-count",
343− }],
344− [
345− Node::element(
346− bump,
347− "strong",
348− [],
349− [],
350− [Node::text(incomplete_count.into_bump_str())],
351− ),
352− Node::text(items_left),
353− ],
354− ),
355− Node::element(
356− bump,
357− "ul",
358− [],
359− [Attribute {
360− name: "class",
361− value: "filters",
362− }],
363− [
241+ footer(bump)
242+ .attr("class", "footer")
243+ .bool_attr("hidden", self.todos.is_empty())
244+ .children([
245+ span(bump)
246+ .attr("class", "todo-count")
247+ .children([
248+ strong(bump)
249+ .children([text(incomplete_count.into_bump_str())])
250+ .finish(),
251+ text(items_left),
252+ ])
253+ .finish(),
254+ ul(bump)
255+ .attr("class", "filters")
256+ .children([
364257 self.visibility_swap(bump, "#/", Visibility::All),
365258 self.visibility_swap(bump, "#/active", Visibility::Active),
366259 self.visibility_swap(bump, "#/completed", Visibility::Completed),
367− ],
368− ),
369− Node::element(
370− bump,
371− "button",
372− [on(bump, "click", |root, vdom, _event| {
260+ ])
261+ .finish(),
262+ button(bump)
263+ .on("click", |root, vdom, _event| {
373264 C::delete_completed(root, vdom);
374− })],
375− {
376− let mut attrs = bumpalo::vec![
377− in bump;
378− Attribute {
379− name: "class",
380− value: "clear-completed",
381− }
382− ];
383− if self.todos.iter().all(|t| !t.is_complete()) {
384− attrs.push(Attribute {
385− name: "hidden",
386− value: "",
387− });
388− }
389− attrs
390− },
391− [Node::text(clear_completed_text.into_bump_str())],
392− ),
393− ],
394− )
265+ })
266+ .attr("class", "clear-completed")
267+ .bool_attr("hidden", completed_count == 0)
268+ .children([text(clear_completed_text.into_bump_str())])
269+ .finish(),
270+ ])
271+ .finish()
395272 }
396273
397274 fn visibility_swap<'a, 'bump>(
@@ -403,34 +280,25 @@ impl<C: TodosActions> Todos<C> {
403280 where
404281 'a: 'bump,
405282 {
406− Node::element(
407− bump,
408− "li",
409− [on(bump, "click", move |root, vdom, _event| {
283+ use dodrio::builder::*;
284+
285+ li(bump)
286+ .on("click", move |root, vdom, _event| {
410287 C::change_visibility(root, vdom, target_vis);
411− })],
412− [],
413− [Node::element(
414− bump,
415− "a",
416− [],
417− [
418− Attribute {
419− name: "href",
420− value: url,
421− },
422− Attribute {
423− name: "class",
424− value: if self.visibility == target_vis {
425− "selected"
426− } else {
427− ""
428− },
288+ })
289+ .children([a(bump)
290+ .attr("href", url)
291+ .attr(
292+ "class",
293+ if self.visibility == target_vis {
294+ "selected"
295+ } else {
296+ ""
429297 },
430− ],
431− [Node::text(target_vis.label())],
432− )],
433− )
298+ )
299+ .children([text(target_vis.label())])
300+ .finish()])
301+ .finish()
434302 }
435303}
436304
@@ -439,12 +307,10 @@ impl<C: TodosActions> Render for Todos<C> {
439307 where
440308 'a: 'bump,
441309 {
442− Node::element(
443− bump,
444− "div",
445− [],
446− [],
447− [self.header(bump), self.todos_list(bump), self.footer(bump)],
448− )
310+ use dodrio::builder::*;
311+
312+ div(bump)
313+ .children([self.header(bump), self.todos_list(bump), self.footer(bump)])
314+ .finish()
449315 }
450316}