-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.ts
96 lines (85 loc) · 3.1 KB
/
index.ts
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
import {
ensureNotFalsy,
randomCouchString
} from 'rxdb/plugins/core';
import {
RxTodoDocument,
databasePromise
} from './database.js';
import './style.css';
(async () => {
const database = await databasePromise;
// update url in description text
getById('copy-url').innerHTML = window.location.href;
// render reactive todo list
const $todoList = getById('todo-list');
database.todos.find({
sort: [
{ state: 'desc' },
{ lastChange: 'desc' }
]
}).$.subscribe(todos => {
$todoList.innerHTML = '';
todos.forEach(todo => $todoList.append(getHtmlByTodo(todo)));
});
// event: add todo
const $insertInput = getById<HTMLInputElement>('insert-todo');
const addTodo = async () => {
if ($insertInput.value.length < 1) { return; }
await database.todos.insert({
id: randomCouchString(10),
name: $insertInput.value,
state: 'open',
lastChange: Date.now()
});
$insertInput.value = '';
};
$insertInput.onkeydown = async (event) => {
if (isEnterEvent(event)) { addTodo(); }
}
$insertInput.onblur = () => addTodo();
// event: clear completed
getById('clear-completed').onclick = () => database.todos.find({ selector: { state: 'done' } }).remove();
})();
function getById<T = HTMLElement>(id: string): T { return ensureNotFalsy(document.getElementById(id)) as any; }
const escapeForHTML = (s: string) => s.replace(/[&<]/g, c => c === '&' ? '&' : '<');
const isEnterEvent = (ev: KeyboardEvent) => ev.code === 'Enter' || ev.keyCode === 13;
function getHtmlByTodo(todo: RxTodoDocument): HTMLLIElement {
const $liElement = document.createElement('li');
const $viewDiv = document.createElement('div');
const $checkbox = document.createElement('input');
const $label = document.createElement('label');
const $deleteButton = document.createElement('button');
$liElement.append($viewDiv);
$viewDiv.append($checkbox);
$viewDiv.append($label);
$viewDiv.append($deleteButton);
// event: toggle todo state
$checkbox.onclick = () => todo.incrementalPatch({ state: todo.state === 'done' ? 'open' : 'done' });
$checkbox.type = 'checkbox';
$checkbox.classList.add('toggle');
// event: change todo name
$label.contentEditable = 'true';
const updateName = async () => {
let newName = $label.innerText || $label.textContent as string;
newName = newName.replace(/<br>/g, '').replace(/\ /g, ' ').trim();
if (newName !== todo.name) {
await todo.incrementalPatch({ name: newName });
}
}
$label.onblur = () => updateName();
$label.onkeyup = async (ev) => {
if (isEnterEvent(ev)) {
updateName();
}
}
$label.innerHTML = escapeForHTML(todo.name);
// event: delete todo
$deleteButton.classList.add('destroy');
$deleteButton.onclick = () => todo.remove();
if (todo.state === 'done') {
$liElement.classList.add('completed');
$checkbox.checked = true;
}
return $liElement;
}