Files
livevue-rs/examples/todo/todo_list.rs
T
2026-03-08 20:10:15 +01:00

89 lines
2.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use crate::queries::{ListTodos, Todo};
use livevue_rs::{RenderContext, global, mangle};
use maud::{html, Markup};
pub async fn todo_list(cx: &mut RenderContext) -> anyhow::Result<Markup> {
// Fetch all todos (registers CacheKey::Table { table: "todos" })
let todos = cx.run(ListTodos).await?;
// Read the clock from KV (registers CacheKey::Channel { key: "clock" })
let clock = cx
.kv_run("clock")
.await
.and_then(|v| v.as_str().map(|s| s.to_string()))
.unwrap_or_else(|| "—".to_string());
let new_todo = cx.signal("newTodo", global());
let todo_count = todos.len();
let done_count = todos.iter().filter(|t| t.completed).count();
Ok(html! {
div.todo-app {
h1 { "LiveVue.rs — Todo List" }
nav {
span.nav-current { "Todos" }
" | "
button.nav-link
data-on:click="$tree = {page: 'About'}" {
"About"
}
}
p.clock { "🕐 " (clock) }
div.add-form {
input
type="text"
placeholder="What needs to be done?"
data-bind="newTodo"
;
button
data-on:click="@post('/action/add_todo')"
data-attr=(format!("{{disabled: {}.trim() === ''}}", new_todo.val)) {
"Add"
}
}
p.stats {
(format!("{} items, {} completed", todo_count, done_count))
}
ul.todo-list {
@for todo in &todos {
(todo_item(cx, todo))
}
}
@if todos.is_empty() {
p.empty { "No todos yet. Add one above!" }
}
}
})
}
fn todo_item(cx: &mut RenderContext, todo: &Todo) -> Markup {
let _editing = cx.signal("editing", mangle(todo.id).client_only());
let title_class = if todo.completed {
"todo-title completed"
} else {
"todo-title"
};
html! {
li.todo-item id=(format!("todo-{}", todo.id)) {
input
type="checkbox"
checked[todo.completed]
data-on:click=(format!("@post('/action/toggle_todo?id={}')", todo.id))
;
span class=(title_class) { (todo.title) }
button.delete-btn
data-on:click=(format!("@post('/action/delete_todo?id={}')", todo.id)) {
"×"
}
}
}
}