commit 367246067ae003855bfcf39dc01b51dcab77f11e
parent 3bd3c169abaeb7b74955a03ee3181cb2191953cf
Author: Hunter
Date: Tue, 30 Jul 2024 17:32:03 -0400
allow deletion of currently viewed parent task
Diffstat:
| M | todo.html | | | 202 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
1 file changed, 112 insertions(+), 90 deletions(-)
diff --git a/todo.html b/todo.html
@@ -51,11 +51,11 @@
let taskPath = [currentTask];
let globalBackspaceBlock = false;
- function createTaskElement(task, isParent = false) {
+ function createTaskElement(task, isParentTask = false) {
const taskContainer = document.createElement('div');
taskContainer.className = 'task-container';
taskContainer.dataset.id = task.id;
- if (isParent) taskContainer.classList.add('parent-task');
+ if (isParentTask) taskContainer.classList.add('parent-task');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
@@ -79,10 +79,16 @@
e.preventDefault();
return;
}
- if (taskInput.value === '' && canDelete && task !== taskPath[0]) {
+ if (taskInput.value === '' && canDelete) {
e.preventDefault();
- globalBackspaceBlock = true;
- deleteTask(task);
+ if (task !== taskPath[0]) {
+ globalBackspaceBlock = true;
+ if (task === currentTask) {
+ deleteCurrentParentTask();
+ } else {
+ deleteSubtask(task);
+ }
+ }
} else if (taskInput.value !== '') {
canDelete = false;
}
@@ -118,17 +124,14 @@
let currentTask = rootTask;
let currentDepth = 0;
- // Add empty circles for levels before the current one
for (let i = 0; i < currentPath.length - 1; i++) {
breadcrumbs += '○ ';
currentTask = currentTask.subtasks.find(t => t.id === currentPath[i + 1].id);
}
- // Add filled circle for the current level
breadcrumbs += '● ';
currentDepth = currentPath.length - 1;
- // If the selected task is not the current view's parent task, add empty circles for potential deeper levels
if (selectedTaskId !== currentTask.id) {
let selectedTask = currentTask.subtasks.find(t => t.id === selectedTaskId);
@@ -140,7 +143,6 @@
let maxDepth = calculateMaxDepth(selectedTask, currentDepth + 1);
- // Add empty circles for potential deeper levels
for (let i = currentDepth + 1; i < maxDepth; i++) {
breadcrumbs += '○ ';
}
@@ -157,11 +159,9 @@
function toggleTaskState(task) {
if (task.state === 1) {
- // If the task is checked, only uncheck it if it's not a parent with all checked direct children
if (task.subtasks.length === 0 || !task.subtasks.every(t => t.state === 1)) {
task.state = 0;
if (task.subtasks.length > 0) {
- // If it's a parent task, set indeterminate subtasks to unchecked
task.subtasks.forEach(subtask => {
if (subtask.state === 2) {
subtask.state = 0;
@@ -169,38 +169,34 @@
});
}
}
- // If it's a parent with all direct children checked, do nothing (stay checked)
} else {
- // If the task is unchecked or indeterminate, check it
task.state = 1;
if (task.subtasks.length > 0) {
- // If it's a parent task, set unchecked subtasks to indeterminate
task.subtasks.forEach(subtask => {
if (subtask.state === 0) {
- subtask.state = 2; // Set to indeterminate
+ subtask.state = 2;
}
});
}
}
- updateParentState(task);
+ updateParentTaskState(task);
renderCurrentView();
selectAndFocusTask(task);
}
- function updateParentState(task) {
+ function updateParentTaskState(task) {
const parent = findParentTask(task);
if (parent) {
const allChecked = parent.subtasks.every(t => t.state === 1);
const anyUnchecked = parent.subtasks.some(t => t.state === 0);
if (allChecked) {
- parent.state = 1; // Checked
+ parent.state = 1;
} else if (anyUnchecked) {
- parent.state = 0; // Unchecked
+ parent.state = 0;
}
- // If all children are either checked or indeterminate, don't change the parent's state
- updateParentState(parent);
+ updateParentTaskState(parent);
}
}
@@ -226,13 +222,17 @@
function handleKeyDown(e, task) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
- addNewTask(currentTask, task);
+ addNewSubtask(currentTask, task);
} else if (e.key === 'Enter' && e.shiftKey) {
e.preventDefault();
toggleTaskState(task);
} else if (e.key === 'Backspace' && e.target.value === '' && task !== currentTask) {
e.preventDefault();
- deleteTask(task);
+ if (task === currentTask) {
+ deleteCurrentParentTask();
+ } else {
+ deleteSubtask(task);
+ }
} else if (e.key === 'ArrowUp' && !e.shiftKey) {
e.preventDefault();
navigateTasks('up');
@@ -241,47 +241,45 @@
navigateTasks('down');
} else if (e.key === 'ArrowUp' && e.shiftKey) {
e.preventDefault();
- moveTask(task, 'up');
+ moveSubtask(task, 'up');
} else if (e.key === 'ArrowDown' && e.shiftKey) {
e.preventDefault();
- moveTask(task, 'down');
+ moveSubtask(task, 'down');
} else if (e.key === 'ArrowRight' && e.shiftKey) {
e.preventDefault();
if (task !== currentTask) {
- navigateInto(task);
+ navigateIntoSubtask(task);
}
} else if (e.key === 'ArrowLeft' && e.shiftKey) {
e.preventDefault();
- navigateOut();
+ navigateToParentTask();
}
}
- function moveTask(task, direction) {
- const parent = findParentTask(task);
- if (!parent) return; // Can't move the root task
+ function moveSubtask(subtask, direction) {
+ const parentTask = findParentTask(subtask);
+ if (!parentTask) return;
- const index = parent.subtasks.findIndex(t => t.id === task.id);
- if (index === -1) return; // Task not found in parent's subtasks
+ const index = parentTask.subtasks.findIndex(t => t.id === subtask.id);
+ if (index === -1) return;
if (direction === 'up' && index > 0) {
- // Move task up
- [parent.subtasks[index - 1], parent.subtasks[index]] = [parent.subtasks[index], parent.subtasks[index - 1]];
- } else if (direction === 'down' && index < parent.subtasks.length - 1) {
- // Move task down
- [parent.subtasks[index], parent.subtasks[index + 1]] = [parent.subtasks[index + 1], parent.subtasks[index]];
+ [parentTask.subtasks[index - 1], parentTask.subtasks[index]] = [parentTask.subtasks[index], parentTask.subtasks[index - 1]];
+ } else if (direction === 'down' && index < parentTask.subtasks.length - 1) {
+ [parentTask.subtasks[index], parentTask.subtasks[index + 1]] = [parentTask.subtasks[index + 1], parentTask.subtasks[index]];
}
renderCurrentView();
- selectAndFocusTask(task);
+ selectAndFocusTask(subtask);
}
- function addNewTask(parentTask, currentSubtask = null) {
- const newTask = { id: Date.now(), text: '', state: 0, subtasks: [], selectedSubtaskId: null };
+ function addNewSubtask(parentTask, currentSubtask = null) {
+ const newSubtask = { id: Date.now(), text: '', state: 0, subtasks: [], selectedSubtaskId: null };
if (currentSubtask) {
const index = parentTask.subtasks.findIndex(t => t.id === currentSubtask.id);
- parentTask.subtasks.splice(index + 1, 0, newTask);
+ parentTask.subtasks.splice(index + 1, 0, newSubtask);
} else {
- parentTask.subtasks.push(newTask);
+ parentTask.subtasks.push(newSubtask);
}
parentTask.state = 0;
@@ -292,7 +290,7 @@
});
renderCurrentView();
- selectAndFocusTask(newTask);
+ selectAndFocusTask(newSubtask);
}
function selectAndFocusTask(task) {
@@ -303,26 +301,60 @@
}
}
- function deleteTask(task) {
- const parent = taskPath[taskPath.length - 1];
- const index = parent.subtasks.findIndex(t => t.id === task.id);
+ function deleteSubtask(subtask) {
+ const parentTask = taskPath[taskPath.length - 1];
+ const index = parentTask.subtasks.findIndex(t => t.id === subtask.id);
- if (parent.id === 'root' && parent.subtasks.length === 1) {
+ if (parentTask.id === 'root' && parentTask.subtasks.length === 1) {
return;
}
- parent.subtasks = parent.subtasks.filter(t => t.id !== task.id);
- updateParentState(parent);
+ parentTask.subtasks = parentTask.subtasks.filter(t => t.id !== subtask.id);
+ updateParentTaskState(parentTask);
- if (parent.subtasks.length === 0 && taskPath.length > 1) {
- navigateOut();
+ if (parentTask.subtasks.length === 0 && taskPath.length > 1) {
+ navigateToParentTask();
} else {
renderCurrentView();
- if (parent.subtasks.length > 0) {
+ if (parentTask.subtasks.length > 0) {
const targetIndex = Math.max(0, index - 1);
- selectAndFocusTask(parent.subtasks[targetIndex]);
+ selectAndFocusTask(parentTask.subtasks[targetIndex]);
} else {
- selectAndFocusTask(parent);
+ selectAndFocusTask(parentTask);
+ }
+ }
+ }
+
+ function deleteCurrentParentTask() {
+ if (taskPath.length <= 1) return; // Don't delete root task
+
+ const currentParentTask = taskPath[taskPath.length - 1];
+ const grandparentTask = taskPath[taskPath.length - 2];
+
+ // Check if we're trying to delete the sole child of the root
+ if (grandparentTask.id === 'root' && grandparentTask.subtasks.length === 1) {
+ return; // Don't allow deletion of the sole child of root
+ }
+
+ const index = grandparentTask.subtasks.findIndex(t => t.id === currentParentTask.id);
+
+ grandparentTask.subtasks = grandparentTask.subtasks.filter(t => t.id !== currentParentTask.id);
+ updateParentTaskState(grandparentTask);
+
+ taskPath.pop(); // Remove the deleted task from the path
+ currentTask = grandparentTask;
+
+ if (grandparentTask.subtasks.length === 0 && taskPath.length > 1) {
+ // If we've just deleted the last subtask, navigate up another level
+ navigateToParentTask();
+ } else {
+ renderCurrentView();
+ if (grandparentTask.subtasks.length > 0) {
+ // Select the subtask before the deleted one, or the one after if at the start
+ const targetIndex = Math.max(0, index - 1);
+ selectAndFocusTask(grandparentTask.subtasks[targetIndex]);
+ } else {
+ selectAndFocusTask(grandparentTask);
}
}
}
@@ -340,60 +372,53 @@
}
} else {
if (currentIndex === tasks.length) {
- addNewTask(currentTask);
+ addNewSubtask(currentTask);
} else {
selectAndFocusTask(tasks[currentIndex]);
}
}
}
- function navigateInto(task) {
- if (task.subtasks.length > 0) {
- currentTask.selectedSubtaskId = task.id;
- taskPath.push(task);
- currentTask = task;
+ function navigateIntoSubtask(subtask) {
+ if (subtask.subtasks.length > 0) {
+ currentTask.selectedSubtaskId = subtask.id;
+ taskPath.push(subtask);
+ currentTask = subtask;
updateBreadcrumbs(currentTask);
renderCurrentView();
- const selectedTask = task.selectedSubtaskId ?
- task.subtasks.find(t => t.id === task.selectedSubtaskId) :
- task.subtasks[0];
- selectAndFocusTask(selectedTask);
+ const selectedSubtask = subtask.selectedSubtaskId ?
+ subtask.subtasks.find(t => t.id === subtask.selectedSubtaskId) :
+ subtask.subtasks[0];
+ selectAndFocusTask(selectedSubtask);
} else {
- addNewTask(task);
- currentTask.selectedSubtaskId = task.id;
- taskPath.push(task);
- currentTask = task;
+ addNewSubtask(subtask);
+ currentTask.selectedSubtaskId = subtask.id;
+ taskPath.push(subtask);
+ currentTask = subtask;
updateBreadcrumbs(currentTask);
renderCurrentView();
- selectAndFocusTask(task.subtasks[0]);
+ selectAndFocusTask(subtask.subtasks[0]);
}
}
- function navigateOut() {
+ function navigateToParentTask() {
if (taskPath.length > 1) {
const currentTaskId = currentTask.id;
taskPath.pop();
currentTask = taskPath[taskPath.length - 1];
- updateParentState(currentTask);
+ updateParentTaskState(currentTask);
updateBreadcrumbs(currentTask);
renderCurrentView();
- const selectedTask = currentTask.subtasks.find(t => t.id === currentTaskId);
- selectAndFocusTask(selectedTask || currentTask.subtasks[0]);
- currentTask.selectedSubtaskId = currentTaskId;
- }
- }
-
- function reevaluateTaskState(task) {
- if (task.subtasks.length > 0) {
- const allChecked = task.subtasks.every(t => t.state === 1);
- const anyChecked = task.subtasks.some(t => t.state === 1);
-
- if (allChecked) {
- task.state = 1; // Checked
- } else if (!anyChecked) {
- task.state = 0; // Unchecked
+ const selectedSubtask = currentTask.subtasks.find(t => t.id === currentTaskId);
+ if (selectedSubtask) {
+ selectAndFocusTask(selectedSubtask);
+ } else if (currentTask.subtasks.length > 0) {
+ // If for some reason the previous subtask isn't found, select the first subtask
+ selectAndFocusTask(currentTask.subtasks[0]);
+ } else {
+ selectAndFocusTask(currentTask);
}
- // If some subtasks are checked and some are not, we don't change the parent's state
+ currentTask.selectedSubtaskId = currentTaskId;
}
}
@@ -403,7 +428,6 @@
if (task !== currentTask) {
currentTask.selectedSubtaskId = task.id;
}
- // Update breadcrumbs when active task changes
updateBreadcrumbs(task);
}
@@ -417,7 +441,6 @@
appContainer.innerHTML = '';
currentTask = taskPath[taskPath.length - 1];
- // Generate and display breadcrumbs
updateBreadcrumbs(currentTask);
const parentElement = createTaskElement(currentTask, true);
@@ -434,7 +457,6 @@
const parentCheckbox = parentElement.querySelector('input[type="checkbox"]');
updateCheckboxState(parentCheckbox, currentTask.state);
- // Maintain focus on the selected task
if (currentTask.selectedSubtaskId) {
const selectedTask = currentTask.subtasks.find(t => t.id === currentTask.selectedSubtaskId);
if (selectedTask) {