1
0
mirror of https://github.com/morris/vanilla-todo.git synced 2025-08-20 04:41:26 +02:00
Files
vanilla-todo/public/scripts/TodoCustomList.js
2023-12-06 19:14:05 +01:00

162 lines
3.6 KiB
JavaScript

import { AppDraggable } from './AppDraggable.js';
import { AppIcon } from './AppIcon.js';
import { TodoList } from './TodoList.js';
/**
* @param {HTMLElement} el
*/
export function TodoCustomList(el) {
let list;
let editing = false;
let startEditing = false;
let saveOnBlur = true;
el.innerHTML = /* html */ `
<div class="header">
<h2 class="title"></h2>
<p class="form">
<input type="text" class="input use-focus-other" aria-label="Title">
<button class="app-button delete" title="Delete">
<i class="app-icon" data-id="trash-16"></i>
</button>
</p>
</div>
<div class="todo-list"></div>
`;
const titleEl = el.querySelector('.title');
const inputEl = el.querySelector('.input');
const deleteEl = el.querySelector('.delete');
AppDraggable(titleEl, {
dropSelector: '.todo-frame.-custom .container',
});
el.querySelectorAll('.app-icon').forEach(AppIcon);
TodoList(el.querySelector('.todo-list'));
titleEl.addEventListener('click', () => {
startEditing = true;
editing = true;
update();
});
deleteEl.addEventListener(
'touchstart',
() => {
saveOnBlur = false;
},
{ passive: true },
);
deleteEl.addEventListener('mousedown', () => {
saveOnBlur = false;
});
inputEl.addEventListener('blur', () => {
if (saveOnBlur) save();
saveOnBlur = true;
});
inputEl.addEventListener('focusOther', () => {
if (editing) save();
});
inputEl.addEventListener('keyup', (e) => {
switch (e.keyCode) {
case 13: // enter
save();
break;
case 27: // escape
cancelEdit();
break;
}
});
deleteEl.addEventListener('click', () => {
if (list.items.length > 0) {
if (
!confirm(
'Deleting this list will delete its items as well. Are you sure?',
)
) {
return;
}
}
el.dispatchEvent(
new CustomEvent('deleteCustomTodoList', {
detail: list,
bubbles: true,
}),
);
});
el.addEventListener('draggableStart', (e) => {
if (e.target !== titleEl) return;
e.detail.data.list = list;
e.detail.data.key = list.id;
// Update image (default would only be title element).
e.detail.setImage(el);
// Override for horizontal dragging only.
e.detail.image.addEventListener('draggableDrag', (e) => {
const x = e.detail.clientX - e.detail.imageX;
const y = e.detail.originY - e.detail.imageY;
e.detail.image.style.transform = `translate(${x}px, ${y}px)`;
});
});
el.addEventListener('addTodoItem', (e) => {
e.detail.listId = list.id;
});
el.addEventListener('moveTodoItem', (e) => {
e.detail.listId = list.id;
e.detail.index = e.detail.index ?? 0;
});
el.addEventListener('customTodoList', (e) => {
list = e.detail;
update();
});
function save() {
el.dispatchEvent(
new CustomEvent('editCustomTodoList', {
detail: { ...list, title: inputEl.value.trim() },
bubbles: true,
}),
);
editing = false;
update();
}
function cancelEdit() {
saveOnBlur = false;
editing = false;
update();
}
function update() {
titleEl.innerText = list.title || '...';
el.querySelector('.todo-list').dispatchEvent(
new CustomEvent('todoItems', { detail: list.items }),
);
el.querySelector('.todo-list > .todo-item-input').dataset.key =
`todo-item-input${list.id}`;
el.classList.toggle('-editing', editing);
if (editing && startEditing) {
inputEl.value = list.title;
inputEl.focus();
inputEl.select();
startEditing = false;
}
}
}