mirror of
https://github.com/aculix/negotium.git
synced 2026-09-11 07:28:17 +00:00
feat: move a task between Today and Tomorrow
The app is built around two days and had no way to move anything between them. "I won't get to this today" is the most obvious thing someone wants from a two-day list, and the only route was deleting the task and typing it again somewhere else. Each row gets an arrow next to delete. On Today it sends the task forward, on Tomorrow it points back, so one control covers both directions and the label says which one you are getting. Alt+Right and Alt+Left do the same from the keyboard, and each only acts in the direction that makes sense from the day you are on. The move itself is in src/lib/defer.js with tests, since it touches two date keys at once and the failure modes are worth pinning down: a missing id, the same day twice, an empty destination, and a task that somehow already exists on the other side.
This commit is contained in:
+41
-1
@@ -12,6 +12,7 @@
|
||||
import { createUndoStack, applyUndo } from './lib/undo.js';
|
||||
import { shouldHandleUndo } from './lib/shortcuts.js';
|
||||
import { buildExport, serialize, parseImport, mergeImport } from './lib/backup.js';
|
||||
import { moveTaskToDay } from './lib/defer.js';
|
||||
|
||||
const storage = createStorage();
|
||||
const undoStack = createUndoStack();
|
||||
@@ -75,6 +76,15 @@
|
||||
setTasks(taskOps.clearCompleted(tasks));
|
||||
}
|
||||
|
||||
const viewingToday = $derived(selectedKey === todayKey);
|
||||
|
||||
/** "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) {
|
||||
const destination = viewingToday ? tomorrowKey() : todayKey;
|
||||
tasks = moveTaskToDay(storage, selectedKey, destination, taskId);
|
||||
}
|
||||
|
||||
function undo() {
|
||||
const entry = undoStack.pop();
|
||||
if (entry) setTasks(applyUndo(tasks, entry));
|
||||
@@ -183,6 +193,17 @@
|
||||
if (event.altKey && (event.key === 'ArrowUp' || event.key === 'ArrowDown')) {
|
||||
event.preventDefault();
|
||||
moveTask(taskId, event.key === 'ArrowUp' ? -1 : 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Right sends a task forward to Tomorrow, left brings it back. Only the
|
||||
// one that makes sense from here does anything.
|
||||
if (event.altKey && event.key === 'ArrowRight' && viewingToday) {
|
||||
event.preventDefault();
|
||||
deferTask(taskId);
|
||||
} else if (event.altKey && event.key === 'ArrowLeft' && !viewingToday) {
|
||||
event.preventDefault();
|
||||
deferTask(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +616,26 @@
|
||||
<span class="task-text">{task.text}</span>
|
||||
|
||||
<button
|
||||
class="delete-btn"
|
||||
class="row-btn defer-btn"
|
||||
onclick={() => deferTask(task.id)}
|
||||
title={viewingToday ? 'Move to Tomorrow' : 'Move to Today'}
|
||||
aria-label={viewingToday ? `Move ${task.text} to Tomorrow` : `Move ${task.text} to Today`}
|
||||
>
|
||||
{#if viewingToday}
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 12H19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13 6L19 12L13 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 12H5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11 6L5 12L11 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="row-btn delete-btn"
|
||||
onclick={() => deleteTask(task.id)}
|
||||
aria-label="Delete {task.text}"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user