todomvc-nodeps.rs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. use std::{collections::HashMap, rc::Rc};
  2. use dioxus_core as dioxus;
  3. use dioxus_core::prelude::*;
  4. use dioxus_html as dioxus_elements;
  5. use dioxus_web::WebsysRenderer;
  6. static APP_STYLE: &'static str = include_str!("./todomvc/style.css");
  7. fn main() {
  8. wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
  9. }
  10. #[derive(PartialEq)]
  11. pub enum FilterState {
  12. All,
  13. Active,
  14. Completed,
  15. }
  16. #[derive(Debug, PartialEq, Clone)]
  17. pub struct TodoItem {
  18. pub id: uuid::Uuid,
  19. pub checked: bool,
  20. pub contents: String,
  21. }
  22. pub fn App(cx: Context<()>) -> VNode {
  23. let (draft, set_draft) = use_state_classic(cx, || "".to_string());
  24. let (todos, set_todos) = use_state_classic(cx, || HashMap::<uuid::Uuid, Rc<TodoItem>>::new());
  25. let (filter, set_filter) = use_state_classic(cx, || FilterState::All);
  26. let filtered_todos = todos.iter().filter(move |(id, item)| match filter {
  27. FilterState::All => true,
  28. FilterState::Active => !item.checked,
  29. FilterState::Completed => item.checked,
  30. });
  31. let items_left = filtered_todos.clone().count();
  32. let item_text = match items_left {
  33. 1 => "item",
  34. _ => "items",
  35. };
  36. cx.render(rsx! {
  37. div { id: "app"
  38. div {
  39. header { class: "header"
  40. h1 {"todos"}
  41. input {
  42. class: "new-todo"
  43. placeholder: "What needs to be done?"
  44. value: "{draft}"
  45. oninput: move |evt| set_draft(evt.value())
  46. }
  47. }
  48. {filtered_todos.map(|(id, item)| {
  49. rsx!(TodoEntry {
  50. key: "{id}",
  51. item: item.clone()
  52. })
  53. })}
  54. // filter toggle (show only if the list isn't empty)
  55. {(!todos.is_empty()).then(|| rsx!(
  56. footer {
  57. span {
  58. strong {"{items_left}"}
  59. span {"{item_text} left"}
  60. }
  61. ul {
  62. class: "filters"
  63. li { class: "All", a { href: "", onclick: move |_| set_filter(FilterState::All), "All" }}
  64. li { class: "Active", a { href: "active", onclick: move |_| set_filter(FilterState::Active), "Active" }}
  65. li { class: "Completed", a { href: "completed", onclick: move |_| set_filter(FilterState::Completed), "Completed" }}
  66. }
  67. }
  68. ))}
  69. }
  70. // footer
  71. footer {
  72. class: "info"
  73. p {"Double-click to edit a todo"}
  74. p {
  75. "Created by "
  76. a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }
  77. }
  78. p {
  79. "Part of "
  80. a { "TodoMVC", href: "http://todomvc.com" }
  81. }
  82. }
  83. }
  84. })
  85. }
  86. #[derive(PartialEq, Props)]
  87. pub struct TodoEntryProps {
  88. item: Rc<TodoItem>,
  89. }
  90. pub fn TodoEntry(cx: Context<TodoEntryProps>) -> VNode {
  91. let (is_editing, set_is_editing) = use_state_classic(cx, || false);
  92. let contents = "";
  93. let todo = TodoItem {
  94. checked: false,
  95. contents: "asd".to_string(),
  96. id: uuid::Uuid::new_v4(),
  97. };
  98. cx.render(rsx! (
  99. li {
  100. "{todo.id}"
  101. input {
  102. class: "toggle"
  103. r#type: "checkbox"
  104. "{todo.checked}"
  105. }
  106. {is_editing.then(|| rsx!{
  107. input {
  108. value: "{contents}"
  109. }
  110. })}
  111. }
  112. ))
  113. }