Readme

Every web project needs a todo list, right? This is val.town's first (maybe?) With a healthy dose of htmx and web fundamentals, we're packing the essentials of a TODO list with server persistence and animations into about 60 lines. The data is stored in the @tmcw.todos val, for now. Try it out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
let { todos } = await import("https://esm.town/v/tmcw/todos");
export let todo_list = (async (req, res) => {
const { default: htm } = await import("npm:htm");
const { default: vhtml } = await import("npm:vhtml");
const html = htm.bind(vhtml);
const renderTodo = (todo) => {
return html`<li data-id=${todo.id}>
<label>
<input hx-delete="/" hx-target="[data-id='${todo.id}']" hx-swap="delete" style='display:inline;margin-right:0.5rem;' type='checkbox' name="id" value=${todo.id} />
${todo.name}
</label>
</li>`;
};
switch (req.method) {
case "POST": {
const name = req.body.name.substring(0, 256).trim();
if (!name)
return res.end();
const todo = {
id: crypto.randomUUID(),
name,
};
todos = [...todos, todo];
if (todos.length > 10) {
todos = todos.slice(-10);
res.set("HX-Trigger", JSON.stringify({ reload: true }));
}
return res.send(renderTodo(todo));
}
case "DELETE": {
todos = todos.filter((todo) =>
todo.id !== req.body.id
);
return res.end();
}
}
if (req.path === "/list") {
return res.send(html`<>${todos.map(renderTodo)}</>`);
}
return res.send(html`<html><head><title>TODO</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://the.missing.style" />
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
</head>
<body>
<main>
<h2>Todo list</h2>
<ul style='list-style-type:none;padding-left:0;' hx-trigger="reload from:body" hx-get="/list">
${todos.map(renderTodo)}
</ul>
<form method="post" class='f-row' hx-post="/" hx-target="ul" hx-swap="beforeend settle:1s">
<input required name="name" type="text" value="" /><button type="submit">Add</button>
</form>
</main>
<footer>
<p>Powered by Val Town: <a href='https://www.val.town/v/tmcw.todo_list'>tmcw.todo_list</a></p>
</footer>
<style>
form.htmx-request { opacity: .5; transition: opacity 300ms linear; }
li { transition: background 1s ease-out; }
li.htmx-added { background: yellow; }
</style>
</body>
</html>`);
});
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.