1
0
mirror of https://github.com/morris/vanilla-todo.git synced 2025-08-22 21:52:54 +02:00

add date picker, refactor event names, cleanups

This commit is contained in:
Morris Brodersen
2022-05-10 12:47:17 +02:00
parent 45e0f3899d
commit 77238353f9
18 changed files with 334 additions and 60 deletions

View File

@@ -0,0 +1,158 @@
import { AppIcon } from './AppIcon.js';
import { formatMonth } from './util.js';
const datesCell = `
<td><button class="app-button"></button></td>
`;
const datesRow = `
<tr>
${datesCell}
${datesCell}
${datesCell}
${datesCell}
${datesCell}
${datesCell}
${datesCell}
</tr>
`;
export function AppDatePicker(el) {
const now = new Date();
const state = {
year: now.getFullYear(),
month: now.getMonth() + 1,
show: false,
};
el.innerHTML = `
<h4 class="header">
<button class="app-button -circle previousmonth">
<i class="app-icon" data-id="chevron-left-16"></i>
</button>
<span class="month"></span>
<button class="app-button -circle nextmonth">
<i class="app-icon" data-id="chevron-right-16"></i>
</button>
</h4>
<table class="dates">
<thead>
<tr>
<th>Su</th>
<th>Mo</th>
<th>Tu</th>
<th>We</th>
<th>Th</th>
<th>Fr</th>
<th>Sa</th>
<tr>
</thead>
<tbody>
${datesRow}
${datesRow}
${datesRow}
${datesRow}
${datesRow}
</tbody>
</div>
`;
el.querySelectorAll('.app-icon').forEach(AppIcon);
el.addEventListener('toggleDatePicker', (e) =>
update({ show: e.detail ?? !state.show })
);
el.addEventListener('setMonth', (e) => update(e.detail));
el.querySelector('.previousmonth').addEventListener('click', previousMonth);
el.querySelector('.nextmonth').addEventListener('click', nextMonth);
el.addEventListener('click', (e) => {
if (!e.target.matches('.app-button')) return;
update({ show: false });
el.dispatchEvent(
new CustomEvent('pickDate', {
detail: new Date(
e.target.dataset.year,
e.target.dataset.month - 1,
e.target.dataset.day
),
bubbles: true,
})
);
});
function previousMonth() {
update(
state.month > 1
? {
year: state.year,
month: state.month - 1,
}
: {
year: state.year - 1,
month: 12,
}
);
}
function nextMonth() {
update(
state.month < 12
? {
year: state.year,
month: state.month + 1,
}
: {
year: state.year + 1,
month: 1,
}
);
}
function update(next) {
Object.assign(state, next);
el.classList.toggle('-show', state.show);
const now = new Date();
const first = new Date(state.year, state.month - 1, 1);
el.querySelector('.month').innerHTML = `${formatMonth(
first
)} ${first.getFullYear()}`;
let current = new Date(first);
current.setDate(1 - current.getDay());
const datesBody = el.querySelector('.dates > tbody');
for (let i = 0; i < 35; ++i) {
const row = Math.floor(i / 7);
const column = i % 7;
const cell = datesBody.children[row].children[column];
const button = cell.children[0];
button.innerHTML = current.getDate();
button.dataset.year = current.getFullYear();
button.dataset.month = current.getMonth() + 1;
button.dataset.day = current.getDate();
button.classList.toggle(
'-highlight',
current.getFullYear() === now.getFullYear() &&
current.getMonth() === now.getMonth() &&
current.getDate() === now.getDate()
);
current.setDate(current.getDate() + 1);
}
}
update();
}

View File

@@ -23,7 +23,9 @@ export function TodoApp(el) {
<div class="todo-frame -days"></div>
<div class="app-collapsible">
<p class="bar">
<button class="app-button -circle toggle"><i class="app-icon" data-id="chevron-up-24"></i></button>
<button class="app-button -circle toggle">
<i class="app-icon" data-id="chevron-up-24"></i>
</button>
</p>
<div class="body">
<div class="todo-frame -custom"></div>
@@ -101,7 +103,7 @@ export function TodoApp(el) {
el.addEventListener('draggableCancel', flip);
el.addEventListener('draggableDrop', flip);
el.dispatchEvent(new CustomEvent('loadStore'));
el.dispatchEvent(new CustomEvent('loadTodoStore'));
function update(next) {
Object.assign(state, next);

View File

@@ -76,7 +76,7 @@ export function TodoCustomList(el) {
}
el.dispatchEvent(
new CustomEvent('deleteList', {
new CustomEvent('deleteTodoList', {
detail: state.list,
bubbles: true,
})
@@ -100,11 +100,11 @@ export function TodoCustomList(el) {
});
});
el.addEventListener('addItem', (e) => {
el.addEventListener('addTodoItem', (e) => {
e.detail.listId = state.list.id;
});
el.addEventListener('moveItem', (e) => {
el.addEventListener('moveTodoItem', (e) => {
e.detail.listId = state.list.id;
e.detail.index = e.detail.index ?? 0;
});
@@ -113,7 +113,7 @@ export function TodoCustomList(el) {
function save() {
el.dispatchEvent(
new CustomEvent('saveList', {
new CustomEvent('saveTodoList', {
detail: { list: state.list, title: inputEl.value.trim() },
bubbles: true,
})

View File

@@ -17,11 +17,11 @@ export function TodoDay(el) {
TodoList(el.querySelector('.todo-list'));
el.addEventListener('addItem', (e) => {
el.addEventListener('addTodoItem', (e) => {
e.detail.listId = state.dateId;
});
el.addEventListener('moveItem', (e) => {
el.addEventListener('moveTodoItem', (e) => {
e.detail.listId = state.dateId;
e.detail.index = e.detail.index ?? 0;
});

View File

@@ -31,13 +31,13 @@ export function TodoFrameCustom(el) {
el.querySelector('.back').addEventListener('click', () => {
el.dispatchEvent(
new CustomEvent('customSeek', { detail: -1, bubbles: true })
new CustomEvent('seekCustomTodoLists', { detail: -1, bubbles: true })
);
});
el.querySelector('.forward').addEventListener('click', () => {
el.dispatchEvent(
new CustomEvent('customSeek', { detail: 1, bubbles: true })
new CustomEvent('seekCustomTodoLists', { detail: 1, bubbles: true })
);
});
@@ -50,7 +50,7 @@ export function TodoFrameCustom(el) {
if (!e.detail.data.list) return;
el.dispatchEvent(
new CustomEvent('moveList', {
new CustomEvent('moveTodoList', {
detail: {
list: e.detail.data.list,
index: e.detail.index,

View File

@@ -1,3 +1,4 @@
import { AppDatePicker } from './AppDatePicker.js';
import { AppIcon } from './AppIcon.js';
import { TodoDay } from './TodoDay.js';
import { formatDateId } from './util.js';
@@ -11,39 +12,78 @@ export function TodoFrameDays(el) {
el.innerHTML = `
<nav class="leftcontrols">
<p><button class="app-button -circle -xl backward"><i class="app-icon" data-id="chevron-left-24"></i></button></p>
<p><button class="app-button fastbackward"><i class="app-icon -double" data-id="chevron-left-16"></i></i></button></p>
<p><button class="app-button home"><i class="app-icon" data-id="home-16"></i></button></p>
<p>
<button class="app-button -circle -xl backward">
<i class="app-icon" data-id="chevron-left-24"></i>
</button>
</p>
<p>
<button class="app-button fastbackward">
<i class="app-icon -double" data-id="chevron-left-16"></i>
</button>
</p>
<p>
<button class="app-button home">
<i class="app-icon" data-id="home-16"></i>
</button>
</p>
</nav>
<div class="container"></div>
<nav class="rightcontrols">
<p><button class="app-button -circle -xl forward"><i class="app-icon" data-id="chevron-right-24"></i></button></p>
<p><button class="app-button fastforward"><i class="app-icon -double" data-id="chevron-right-16"></i></button></p>
<p>
<button class="app-button -circle -xl forward">
<i class="app-icon" data-id="chevron-right-24"></i>
</button>
</p>
<p>
<button class="app-button fastforward">
<i class="app-icon -double" data-id="chevron-right-16"></i>
</button>
</p>
<p>
<button class="app-button pickdate">
<i class="app-icon" data-id="calendar-16"></i>
</button>
</p>
<div class="app-dropdown app-date-picker datepicker"></div>
</nav>
`;
setTimeout(() => el.classList.add('-animated'), 200);
el.querySelectorAll('.app-icon').forEach(AppIcon);
el.querySelectorAll('.app-date-picker').forEach(AppDatePicker);
el.querySelector('.backward').addEventListener('click', () =>
el.dispatchEvent(new CustomEvent('seek', { detail: -1, bubbles: true }))
el.dispatchEvent(new CustomEvent('seekDays', { detail: -1, bubbles: true }))
);
el.querySelector('.forward').addEventListener('click', () =>
el.dispatchEvent(new CustomEvent('seek', { detail: 1, bubbles: true }))
el.dispatchEvent(new CustomEvent('seekDays', { detail: 1, bubbles: true }))
);
el.querySelector('.fastbackward').addEventListener('click', () =>
el.dispatchEvent(new CustomEvent('seek', { detail: -5, bubbles: true }))
el.dispatchEvent(new CustomEvent('seekDays', { detail: -5, bubbles: true }))
);
el.querySelector('.fastforward').addEventListener('click', () =>
el.dispatchEvent(new CustomEvent('seek', { detail: 5, bubbles: true }))
el.dispatchEvent(new CustomEvent('seekDays', { detail: 5, bubbles: true }))
);
el.querySelector('.home').addEventListener('click', () =>
el.dispatchEvent(new CustomEvent('seekHome', { bubbles: true }))
el.dispatchEvent(new CustomEvent('seekToToday', { bubbles: true }))
);
el.querySelector('.pickdate').addEventListener('click', () =>
el
.querySelector('.datepicker')
.dispatchEvent(new CustomEvent('toggleDatePicker'))
);
el.querySelector('.datepicker').addEventListener('pickDate', (e) =>
el.dispatchEvent(
new CustomEvent('seekToDate', { detail: e.detail, bubbles: true })
)
);
el.addEventListener('todoData', (e) => update(e.detail));

View File

@@ -44,7 +44,7 @@ export function TodoItem(el) {
if (state.editing) save();
el.dispatchEvent(
new CustomEvent('checkItem', {
new CustomEvent('checkTodoItem', {
detail: {
item: state.item,
done: !state.item.done,
@@ -102,7 +102,7 @@ export function TodoItem(el) {
// event handler?
requestAnimationFrame(() => {
el.dispatchEvent(
new CustomEvent('deleteItem', {
new CustomEvent('deleteTodoItem', {
detail: state.item,
bubbles: true,
})
@@ -113,7 +113,7 @@ export function TodoItem(el) {
}
el.dispatchEvent(
new CustomEvent('saveItem', {
new CustomEvent('saveTodoItem', {
detail: {
item: state.item,
label,

View File

@@ -48,7 +48,7 @@ export function TodoItemInput(el) {
inputEl.value = '';
el.dispatchEvent(
new CustomEvent('addItem', {
new CustomEvent('addTodoItem', {
detail: { label },
bubbles: true,
})

View File

@@ -17,7 +17,7 @@ export function TodoList(el) {
el.addEventListener('sortableDrop', (e) =>
el.dispatchEvent(
new CustomEvent('moveItem', {
new CustomEvent('moveTodoItem', {
detail: {
item: e.detail.data.item,
index: e.detail.index,

View File

@@ -10,9 +10,9 @@ export function TodoStore(el) {
let storeTimeout;
el.addEventListener('loadStore', load);
el.addEventListener('loadTodoStore', load);
el.addEventListener('addItem', (e) => {
el.addEventListener('addTodoItem', (e) => {
let index = 0;
for (const item of state.items) {
@@ -32,21 +32,21 @@ export function TodoStore(el) {
dispatch({ items: state.items });
});
el.addEventListener('checkItem', (e) => {
el.addEventListener('checkTodoItem', (e) => {
if (e.detail.item.done === e.detail.done) return;
e.detail.item.done = e.detail.done;
dispatch({ items: state.items });
});
el.addEventListener('saveItem', (e) => {
el.addEventListener('saveTodoItem', (e) => {
if (e.detail.item.label === e.detail.label) return;
e.detail.item.label = e.detail.label;
dispatch({ items: state.items });
});
el.addEventListener('moveItem', (e) => {
el.addEventListener('moveTodoItem', (e) => {
const movedItem = state.items.find((item) => item.id === e.detail.item.id);
const listItems = state.items.filter(
@@ -65,11 +65,11 @@ export function TodoStore(el) {
dispatch({ items: state.items });
});
el.addEventListener('deleteItem', (e) => {
el.addEventListener('deleteTodoItem', (e) => {
dispatch({ items: state.items.filter((item) => item.id !== e.detail.id) });
});
el.addEventListener('addList', (e) => {
el.addEventListener('addTodoList', (e) => {
let index = 0;
for (const customList of state.customLists) {
@@ -85,7 +85,7 @@ export function TodoStore(el) {
dispatch({ customLists: state.customLists });
});
el.addEventListener('saveList', (e) => {
el.addEventListener('saveTodoList', (e) => {
const list = state.customLists.find((l) => l.id === e.detail.list.id);
if (list.title === e.detail.title) return;
@@ -95,7 +95,7 @@ export function TodoStore(el) {
dispatch({ customLists: state.customLists });
});
el.addEventListener('moveList', (e) => {
el.addEventListener('moveTodoList', (e) => {
const movedListIndex = state.customLists.findIndex(
(list) => list.id === e.detail.list.id
);
@@ -112,7 +112,7 @@ export function TodoStore(el) {
dispatch({ customLists: state.customLists });
});
el.addEventListener('deleteList', (e) => {
el.addEventListener('deleteTodoList', (e) => {
dispatch({
customLists: state.customLists.filter(
(customList) => customList.id !== e.detail.id
@@ -120,18 +120,22 @@ export function TodoStore(el) {
});
});
el.addEventListener('seek', (e) => {
el.addEventListener('seekDays', (e) => {
const t = new Date(`${state.at} 00:00:00`);
t.setDate(t.getDate() + e.detail);
dispatch({ at: formatDateId(t) });
});
el.addEventListener('seekHome', () =>
el.addEventListener('seekToToday', () =>
dispatch({ at: formatDateId(new Date()) })
);
el.addEventListener('customSeek', (e) => {
el.addEventListener('seekToDate', (e) =>
dispatch({ at: formatDateId(e.detail) })
);
el.addEventListener('seekCustomTodoLists', (e) => {
dispatch({
customAt: Math.max(
0,