| index 26caecd..6e9db15 100644 |
| --- a/web/static/apps/todo.js |
| +++ b/web/static/apps/todo.js |
| @@ -62,7 +62,8 @@ export const app = defineApp({ |
| .todo-add-btn { flex: 0 0 auto; } |
| .todo-count { font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: 0.02em; } |
| .todo-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; } |
| -.todo-item { display: flex; align-items: center; gap: 10px; padding: 6px 2px; } |
| +.todo-item { display: flex; align-items: center; gap: 10px; padding: 6px 8px; margin: 0 -8px; border-radius: 8px; cursor: var(--ui-cursor, pointer); } |
| +.todo-item:hover { background: var(--bubble-them); } |
| .todo-check { |
| flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%; |
| border: 1.5px solid var(--border); background: transparent; color: var(--accent-ink); |
| @@ -114,16 +115,23 @@ export const app = defineApp({ |
| |
| const list = document.createElement("ul"); |
| list.className = "todo-list"; |
| - for (const item of items) { |
| + // Newest on top: the fold appends new items, so render the list reversed. |
| + for (const item of [...items].reverse()) { |
| const li = document.createElement("li"); |
| li.className = "todo-item" + (item.done ? " done" : ""); |
| + // Tapping anywhere on the row checks it off (or reopens it) — the whole item |
| + // is the target, not just the little circle. Remove (×) stops the click from |
| + // also toggling (see its handler below). |
| + li.onclick = () => ctx.emit({ type: item.done ? "todo.reopen" : "todo.done", id: item.id }); |
| |
| const check = document.createElement("button"); |
| check.type = "button"; |
| check.className = "todo-check"; |
| check.setAttribute("aria-label", item.done ? "Reopen" : "Mark done"); |
| check.textContent = item.done ? "✓" : ""; |
| - check.onclick = () => ctx.emit({ type: item.done ? "todo.reopen" : "todo.done", id: item.id }); |
| + // No own handler: the click bubbles to the row's toggle. Keeping it a real |
| + // <button> means it stays keyboard-focusable and labelled, so Space/Enter |
| + // on the circle checks the item off too. |
| |
| const text = document.createElement("span"); |
| text.className = "todo-text"; |
| @@ -134,7 +142,10 @@ export const app = defineApp({ |
| rm.className = "todo-remove"; |
| rm.setAttribute("aria-label", "Remove"); |
| rm.textContent = "×"; |
| - rm.onclick = () => ctx.emit({ type: "todo.remove", id: item.id }); |
| + rm.onclick = (e) => { |
| + e.stopPropagation(); // remove the item — don't also toggle the row |
| + ctx.emit({ type: "todo.remove", id: item.id }); |
| + }; |
| |
| li.append(check, text, rm); |
| list.appendChild(li); |