-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathindex.js
133 lines (112 loc) · 3.47 KB
/
index.js
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Get references to DOM elements
export const dom = {
tasksList: document.querySelector("#tasks_list"),
taskTemplate: document.querySelector("#task_template"),
doneCount: document.querySelector("#done_count"),
totalCount: document.querySelector("#total_count"),
saveButton: document.querySelector("#save_button"),
};
// Initialize data. Do we have anything stored?
if (localStorage.tasks) {
let tasks = JSON.parse(localStorage.tasks);
for (let task of tasks) {
addItem(task);
}
}
else {
// Add one empty task to start with
addItem();
}
// Save when the save button is clicked
dom.saveButton.addEventListener("click", e => {
localStorage.tasks = JSON.stringify(getData());
});
// Keyboard shortcuts
dom.tasksList.addEventListener("keyup", e => {
if (!e.target.matches("input.title")) {
// We are only interested in key events on the text field
return;
}
let li = e.target.closest("li");
if (e.key === "Enter") {
addItem();
}
else if (e.key === "Backspace" && e.target.previousValue === "") {
li.querySelector(".delete").click();
}
});
dom.tasksList.addEventListener("keydown", e => {
if (!e.target.matches("input.title")) {
// We are only interested in key events on the text field
return;
}
let li = e.target.closest("li");
if (e.key === "Backspace") {
// Store previous value so we know whether to delete on keyup
// (which is fired after the value has changed)
e.target.previousValue = e.target.value;
}
// Step 3: Arrow keys to move between tasks
else if (e.key === "ArrowUp") {
focusTask(li.previousElementSibling ?? dom.tasksList.lastElementChild);
}
else if (e.key === "ArrowDown") {
focusTask(li.nextElementSibling ?? dom.tasksList.firstElementChild);
}
});
/**
* Add a new item at the end of the todo list
* @param {Object} data data for the item to be added
*/
export function addItem (data = { done: false, title: "" }) {
dom.tasksList.insertAdjacentHTML("beforeend", dom.taskTemplate.innerHTML);
let element = dom.tasksList.lastElementChild;
element.querySelector(".title").value = data.title;
let done = element.querySelector(".done");
done.checked = data.done;
// Step 1: Update the done count when the done checkbox is checked/unchecked
done.addEventListener("input", e => {
updateDoneCount();
});
// Step 2: Implement the delete button
element.querySelector(".delete").addEventListener("click", e => {
let previous = element.closest("li").previousElementSibling;
element.remove();
focusTask(previous);
updateCounts();
});
updateCounts();
focusTask(element);
}
/**
* Delete all tasks that are marked as done
*/
export function clearCompleted () {
for (let deleteButton of dom.tasksList.querySelectorAll("li:has(.done:checked) .delete")) {
deleteButton.click();
}
}
/**
* Focus the title field of the specified task
* @param {Node} element Reference to DOM element of the task to focus (or any of its descendants)
*/
export function focusTask (element) {
element?.closest("li")?.querySelector("input.title").focus();
}
export function getData () {
return Array.from(dom.tasksList.children).map(element => ({
title: element.querySelector(".title").value,
done: element.querySelector(".done").checked
}));
}
function updateDoneCount () {
dom.doneCount.textContent = dom.tasksList.querySelectorAll(".done:checked").length;
}
function updateTotalCount () {
dom.totalCount.textContent = dom.tasksList.children.length;
}
// Update expressions etc when data changes
function updateCounts () {
updateDoneCount();
updateTotalCount();
}