@@ -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);
diff --git a/public/scripts/TodoCustomList.js b/public/scripts/TodoCustomList.js
index 80946bc..dfdade0 100644
--- a/public/scripts/TodoCustomList.js
+++ b/public/scripts/TodoCustomList.js
@@ -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,
})
diff --git a/public/scripts/TodoDay.js b/public/scripts/TodoDay.js
index 5392ce7..c54c74a 100644
--- a/public/scripts/TodoDay.js
+++ b/public/scripts/TodoDay.js
@@ -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;
});
diff --git a/public/scripts/TodoFrameCustom.js b/public/scripts/TodoFrameCustom.js
index d5e64a2..9ea43e5 100644
--- a/public/scripts/TodoFrameCustom.js
+++ b/public/scripts/TodoFrameCustom.js
@@ -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,
diff --git a/public/scripts/TodoFrameDays.js b/public/scripts/TodoFrameDays.js
index 3beccb1..4f6ecb3 100644
--- a/public/scripts/TodoFrameDays.js
+++ b/public/scripts/TodoFrameDays.js
@@ -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 = `
`;
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));
diff --git a/public/scripts/TodoItem.js b/public/scripts/TodoItem.js
index 5d4edb6..46ca4b8 100644
--- a/public/scripts/TodoItem.js
+++ b/public/scripts/TodoItem.js
@@ -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,
diff --git a/public/scripts/TodoItemInput.js b/public/scripts/TodoItemInput.js
index 805dd81..1493238 100644
--- a/public/scripts/TodoItemInput.js
+++ b/public/scripts/TodoItemInput.js
@@ -48,7 +48,7 @@ export function TodoItemInput(el) {
inputEl.value = '';
el.dispatchEvent(
- new CustomEvent('addItem', {
+ new CustomEvent('addTodoItem', {
detail: { label },
bubbles: true,
})
diff --git a/public/scripts/TodoList.js b/public/scripts/TodoList.js
index 3a3b648..aa7d416 100644
--- a/public/scripts/TodoList.js
+++ b/public/scripts/TodoList.js
@@ -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,
diff --git a/public/scripts/TodoStore.js b/public/scripts/TodoStore.js
index c9b3997..778ed7c 100644
--- a/public/scripts/TodoStore.js
+++ b/public/scripts/TodoStore.js
@@ -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,
diff --git a/public/styles/app-button.css b/public/styles/app-button.css
index 74fa0a1..39e7914 100644
--- a/public/styles/app-button.css
+++ b/public/styles/app-button.css
@@ -38,6 +38,15 @@
font-size: 1.5em;
}
+.app-button.-highlight {
+ background: #111;
+ color: #fff;
+}
+
+.app-button.-highlight:hover {
+ color: #eee;
+}
+
@media (min-width: 600px) {
.app-button.-xl {
font-size: 2em;
diff --git a/public/styles/app-date-picker.css b/public/styles/app-date-picker.css
new file mode 100644
index 0000000..eefe4a1
--- /dev/null
+++ b/public/styles/app-date-picker.css
@@ -0,0 +1,51 @@
+.app-date-picker {
+ width: 260px;
+ background: #fff;
+ border-radius: 4px;
+ box-shadow: rgba(0, 0, 0, 10%) 0 4px 12px;
+ padding: 8px;
+ transform: translate(110%, 0);
+ transition: all 0.2s ease-in-out;
+ text-align: center;
+}
+
+.app-date-picker.-show {
+ transform: translate(0, 0);
+}
+
+.app-date-picker > .header {
+ display: flex;
+ font-size: 1em;
+ margin: 0 0 1em 0;
+ line-height: 1.5em;
+}
+
+.app-date-picker > .header > .month {
+ flex-grow: 1;
+ font-weight: bold;
+ text-transform: uppercase;
+}
+
+.app-date-picker > .dates {
+ width: 100%;
+}
+
+.app-date-picker > .dates > thead > tr > th {
+ font-weight: normal;
+ padding: 0;
+}
+
+.app-date-picker > .dates > tbody > tr > td {
+ padding: 0;
+}
+
+.app-date-picker > .dates > tbody > tr > td > button {
+ width: 100%;
+ height: 1.9em;
+}
+
+@media (min-width: 320px) {
+ .app-date-picker {
+ width: 300px;
+ }
+}
diff --git a/public/styles/todo-app.css b/public/styles/todo-app.css
new file mode 100644
index 0000000..c7454c6
--- /dev/null
+++ b/public/styles/todo-app.css
@@ -0,0 +1,3 @@
+.todo-app {
+ overflow: hidden;
+}
diff --git a/public/styles/todo-frame.css b/public/styles/todo-frame.css
index 29d2111..34d293b 100644
--- a/public/styles/todo-frame.css
+++ b/public/styles/todo-frame.css
@@ -1,6 +1,5 @@
.todo-frame {
position: relative;
- overflow: hidden;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
@@ -31,6 +30,11 @@
margin: 0 0 0.5em 0;
}
+.todo-frame > .rightcontrols > .datepicker {
+ position: absolute;
+ right: 10px;
+}
+
.todo-frame > .container {
position: absolute;
overflow: hidden;