diff --git a/README.md b/README.md index 96a215e..94b4407 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Built with Svelte for speed and simplicity. No overwhelming features, no endless - ↩️ **Undo** - `Cmd/Ctrl+Z` brings back a deleted task, in its original position - 📅 **Today & Tomorrow lists** - Plan ahead with separate task lists - 🔄 **Unfinished work carries over** - When a new day begins, tasks you didn't finish move to Today; completed ones are cleared away +- ✏️ **Edit a task** - Fix a typo without deleting and retyping it - ➡️ **Push a task to tomorrow** - Didn't get to it? Move it across without retyping it - 🎯 **Reorder by drag or keyboard** - Drag with a mouse, long-press and drag on touch, or use `Alt+↑`/`Alt+↓` - 💾 **Persistent storage** - All tasks saved locally in your browser @@ -89,6 +90,7 @@ The date, storage, rollover, task and undo logic lives in `src/lib/` as plain mo ### Managing Tasks - **Add a task**: Type in the input field and press Enter - **Complete a task**: Click the checkbox next to the task +- **Edit a task**: Click its text. Enter saves, Escape cancels, and clicking away saves too - **Delete a task**: Click the delete icon on the task (it shows on hover, and is always visible on touch), or press `Delete` with the task focused - **Undo a delete**: Press `Cmd/Ctrl+Z`. The task returns to where it was - **Move a task to Tomorrow**: Click the arrow on the task, or press `Alt+→`. From Tomorrow, the arrow points back and `Alt+←` returns it to Today diff --git a/src/App.svelte b/src/App.svelte index 80e9591..246e3ee 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -78,9 +78,53 @@ const viewingToday = $derived(selectedKey === todayKey); + let editingId = $state(null); + let editingText = $state(''); + // A drag ends with a click event, which would otherwise drop the row you + // just moved straight into edit mode. + let suppressClick = false; + + function startEditing(task) { + if (suppressClick || draggedId) return; + editingId = task.id; + editingText = task.text; + } + + function commitEdit() { + if (editingId === null) return; + // renameTask refuses blank input, so an accidental select-all and enter + // leaves the task as it was rather than wiping it. + setTasks(taskOps.renameTask(tasks, editingId, editingText)); + editingId = null; + } + + function cancelEdit() { + editingId = null; + } + + function handleEditKeydown(event) { + // Stop these reaching the row, which would delete or reorder the task + // being typed into. + event.stopPropagation(); + + if (event.key === 'Enter') { + event.preventDefault(); + commitEdit(); + } else if (event.key === 'Escape') { + event.preventDefault(); + cancelEdit(); + } + } + + function focusOnMount(node) { + node.focus(); + node.select(); + } + /** "Not today, tomorrow" and its reverse. Which direction depends only on * which day you are looking at, so one control covers both. */ function deferTask(taskId) { + cancelEdit(); const destination = viewingToday ? tomorrowKey() : todayKey; tasks = moveTaskToDay(storage, selectedKey, destination, taskId); } @@ -305,6 +349,7 @@ if (event.pointerType === 'mouse' && event.button !== 0) return; // Let the checkbox and delete button have their clicks. if (event.target.closest('button')) return; + if (event.target.closest('.task-edit')) return; drag = { id: tasks[index].id, @@ -369,6 +414,10 @@ if (!wasActive) return; + // Swallow the click the browser fires after this drag. + suppressClick = true; + setTimeout(() => { suppressClick = false; }, 0); + // Clearing the inline transform while the settling class supplies a // transition eases the card into its slot. Nothing else on the list moves, // because nothing else changed. @@ -398,6 +447,7 @@ } function switchDate() { + cancelEdit(); selectedKey = selectedKey === todayKey ? tomorrowKey() : todayKey; tasks = storage.loadTasks(selectedKey); } @@ -411,6 +461,7 @@ * they follow it there, which preserves the existing mental model. */ function runRollover() { + cancelEdit(); const now = new Date(); const wasViewingToday = selectedKey === todayKey; @@ -613,7 +664,21 @@ {/if} - {task.text} + {#if task.id === editingId} + + {:else} + + {/if}