todomvc-nodeps.rs 3.7 KB

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