-
Notifications
You must be signed in to change notification settings - Fork 53
/
TodoApp.js
127 lines (106 loc) · 3.97 KB
/
TodoApp.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
import { AppCollapsible } from './AppCollapsible.js';
import { AppFlip } from './AppFlip.js';
import { AppIcon } from './AppIcon.js';
import { TodoController } from './TodoController.js';
import { TodoFrameCustom } from './TodoFrameCustom.js';
import { TodoFrameDays } from './TodoFrameDays.js';
import { TodoLogic } from './TodoLogic.js';
/**
* @param {HTMLElement} el
*/
export function TodoApp(el) {
let todoData = TodoLogic.initTodoData();
el.innerHTML = /* html */ `
<header class="app-header">
<h1 class="title">
VANILLA TODO
</h1>
</header>
<div class="todo-frame -days"></div>
<div class="app-collapsible">
<p class="bar">
<button class="app-button -circle toggle" title="Show/hide custom todo-lists">
<i class="app-icon" data-id="chevron-up-24"></i>
</button>
</p>
<div class="body">
<div class="todo-frame -custom"></div>
</div>
</div>
<footer class="app-footer">
<p>
VANILLA TODO © 2020–2024 <a href="https://morrisbrodersen.de">Morris Brodersen</a>
— A case study on viable techniques for vanilla web development.
<a href="https://github.com/morris/vanilla-todo">About →</a>
</p>
</footer>
`;
AppFlip(el, {
selector: '.todo-item, .todo-item-input, .todo-day, .todo-custom-list',
removeTimeout: 200,
});
TodoController(el);
el.querySelectorAll('.app-collapsible').forEach(AppCollapsible);
el.querySelectorAll('.app-icon').forEach(AppIcon);
TodoFrameDays(el.querySelector('.todo-frame.-days'));
TodoFrameCustom(el.querySelector('.todo-frame.-custom'));
// Each of these events make changes to the HTML to be animated using FLIP.
// Listening to them using "capture" dispatches "beforeFlip" before any changes.
el.addEventListener('todoData', beforeFlip, true);
el.addEventListener('sortableUpdate', beforeFlip, true);
el.addEventListener('draggableCancel', beforeFlip, true);
el.addEventListener('draggableDrop', beforeFlip, true);
// Some necessary work to orchestrate drag & drop with FLIP animations
el.addEventListener('draggableStart', (e) => {
e.detail.image.classList.add('_noflip');
el.appendChild(e.detail.image);
});
el.addEventListener('draggableCancel', (e) => {
e.detail.image.classList.remove('_noflip');
update();
});
el.addEventListener('draggableDrop', (e) => {
e.detail.image.classList.remove('_noflip');
});
el.addEventListener('sortableUpdate', (e) => {
e.detail.placeholder.classList.add('_noflip');
});
// Dispatch "focusOther" on .use-focus-other inputs if they are not active.
// Ensures only one edit input is active.
el.addEventListener('focusin', (e) => {
if (!e.target.classList.contains('use-focus-other')) return;
document.querySelectorAll('.use-focus-other').forEach((el) => {
if (el === e.target) return;
el.dispatchEvent(new CustomEvent('focusOther'));
});
});
// Listen to the TodoController's data.
// This is the main update.
// Everything else is related to drag & drop or FLIP animations.
el.addEventListener('todoData', (e) => {
todoData = e.detail;
update();
});
// Dispatch "flip" after HTML changes from the following events.
// This plays the FLIP animations.
el.addEventListener('todoData', flip);
el.addEventListener('sortableUpdate', flip);
el.addEventListener('draggableCancel', flip);
el.addEventListener('draggableDrop', flip);
el.dispatchEvent(new CustomEvent('loadTodoData'));
function update() {
el.querySelectorAll('.todo-frame').forEach((el) =>
el.dispatchEvent(new CustomEvent('todoData', { detail: todoData })),
);
el.querySelectorAll('.app-collapsible').forEach((el) =>
el.dispatchEvent(new CustomEvent('collapse')),
);
}
function beforeFlip(e) {
if (e.type === 'todoData' && e.target !== el) return;
el.dispatchEvent(new CustomEvent('beforeFlip'));
}
function flip() {
el.dispatchEvent(new CustomEvent('flip'));
}
}