all repos — h3 @ af1f1a5943e809b4a5ebd7a4ef13c8dc612ed5dc

A tiny, extremely minimalist JavaScript microframework.

Refactored by adding more components.
h3rald h3rald@h3rald.com
Fri, 10 Apr 2020 18:11:41 +0200
commit

af1f1a5943e809b4a5ebd7a4ef13c8dc612ed5dc

parent

d253248136ef08dce340f4c57341861b9f6d1deb

M example/assets/js/app.jsexample/assets/js/app.js

@@ -1,11 +1,13 @@

import h3, { mount } from "./h3.js"; -import Paginator from "./paginator.js"; -import Todo from "./todo.js"; +import AddTodoForm from "./components/addTodoForm.js"; +import EmptyTodoError from "./components/emptyTodoError.js"; +import NavigationBar from "./components/navigationBar.js"; +import TodoList from "./components/todoList.js"; let todos = []; let filteredTodos = []; let app; -let emptyTodoError = false; +let displayEmptyTodoError = false; let filter = ""; let pagesize = 10; let page = 1;

@@ -27,26 +29,17 @@ save();

app.update({ vnode: build() }); }; -const toggle = (todo) => { - todo.done = !todo.done; - update(); -}; -const remove = (todo) => { - todos = todos.filter(({ key }) => key !== todo.key); - update(); -}; - // UI Methods // Add a todo item const addTodo = () => { const newTodo = document.getElementById("new-todo"); if (!newTodo.value) { - emptyTodoError = true; + displayEmptyTodoError = true; update(); document.getElementById("new-todo").focus(); return; } - emptyTodoError = false; + displayEmptyTodoError = false; todos.unshift({ key: `todo_${Date.now()}__${newTodo.value}`, // Make todos "unique-enough" to ensure they are processed correctly text: newTodo.value,

@@ -64,6 +57,15 @@ event.preventDefault();

} }; +const toggleTodo = (todo) => { + todo.done = !todo.done; + update(); +}; +const removeTodo = (todo) => { + todos = todos.filter(({ key }) => key !== todo.key); + update(); +}; + // Set the todo filter. const setFilter = (event) => { let f = document.getElementById("filter-text");

@@ -73,16 +75,9 @@ f = document.getElementById("filter-text");

f.focus(); }; -// Display todo items -const displayTodos = () => { - const start = (page - 1) * pagesize; - const end = Math.min(start + pagesize, filteredTodos.length); - return filteredTodos.slice(start, end).map((t) => Todo(t, {toggle, remove})); -}; - // Clear error message const clearError = () => { - emptyTodoError = false; + displayEmptyTodoError = false; update(); document.getElementById("new-todo").focus(); };

@@ -99,49 +94,19 @@ page = parseInt(hash.match(/page=(\d+)/)[1]);

} // Recalculate page in case data is filtered. page = Math.min(Math.ceil(filteredTodos.length / pagesize), page) || 1; - const emptyTodoErrorClass = emptyTodoError ? "" : ".hidden"; const paginatorData = { size: pagesize, page: page, total: filteredTodos.length, }; + const start = (page - 1) * pagesize; + const end = Math.min(start + pagesize, filteredTodos.length); return h3("div#todolist.todo-list-container", [ h3("h1", ["To Do List"]), - h3("form.add-todo-form", [ - h3("input", { - id: "new-todo", - placeholder: "What do you want to do?", - autofocus: true, - onkeydown: addTodoOnEnter, - }), - h3( - "span.submit-todo", - { - onclick: addTodo, - }, - ["+"] - ), - ]), - h3(`div.error${emptyTodoErrorClass}`, [ - h3("span.error-message", ["Please enter a non-empty todo item."]), - h3( - "span.dismiss-error", - { - onclick: clearError, - }, - ["✘"] - ), - ]), - h3("div.navigation-bar", [ - h3("input", { - id: "filter-text", - placeholder: "Type to filter todo items...", - onkeyup: setFilter, - value: filter, - }), - Paginator(paginatorData, {update}), - ]), - h3("div.todo-list", displayTodos()), + AddTodoForm({ addTodo, addTodoOnEnter }), + EmptyTodoError({ displayEmptyTodoError }, { clearError }), + NavigationBar({ filter, paginatorData }, { update, setFilter }), + TodoList({ filteredTodos, start, end }, { toggleTodo, removeTodo }), ]); }; load();
A example/assets/js/components/addTodoForm.js

@@ -0,0 +1,20 @@

+import h3 from "../h3.js"; + +export default function AddTodoForm(actions) { + const { addTodo, addTodoOnEnter } = actions; + return h3("form.add-todo-form", [ + h3("input", { + id: "new-todo", + placeholder: "What do you want to do?", + autofocus: true, + onkeydown: addTodoOnEnter, + }), + h3( + "span.submit-todo", + { + onclick: addTodo, + }, + ["+"] + ), + ]); +}
A example/assets/js/components/emptyTodoError.js

@@ -0,0 +1,17 @@

+import h3 from "../h3.js"; + +export default function EmptyTodoError(data, actions) { + const { clearError } = actions; + const { displayEmptyTodoError } = data; + const emptyTodoErrorClass = displayEmptyTodoError ? "" : ".hidden"; + return h3(`div.error${emptyTodoErrorClass}`, [ + h3("span.error-message", ["Please enter a non-empty todo item."]), + h3( + "span.dismiss-error", + { + onclick: clearError, + }, + ["✘"] + ), + ]); +}
A example/assets/js/components/navigationBar.js

@@ -0,0 +1,16 @@

+import h3 from "../h3.js"; +import Paginator from "./paginator.js"; + +export default function NavigationBar(data, actions) { + const { filter, paginatorData } = data; + const { setFilter, update } = actions; + return h3("div.navigation-bar", [ + h3("input", { + id: "filter-text", + placeholder: "Type to filter todo items...", + onkeyup: setFilter, + value: filter, + }), + Paginator(paginatorData, { update }), + ]); +}
A example/assets/js/components/todo.js

@@ -0,0 +1,12 @@

+import h3 from "../h3.js"; + +export default function Todo(data, actions) { + const { toggleTodo, removeTodo } = actions; + const todoStateClass = data.done ? ".done" : ".todo"; + return h3("div.todo-item", { id: data.key }, [ + h3(`div.todo-content${todoStateClass}`, [ + h3("span.todo-text", { onclick: () => toggleTodo(data) }, [data.text]), + ]), + h3("span.delete-todo", { onclick: () => removeTodo(data) }, ["✘"]), + ]); +};
A example/assets/js/components/todoList.js

@@ -0,0 +1,13 @@

+import h3 from "../h3.js"; +import Todo from "./todo.js"; + +export default function TodoList(data, actions) { + const { start, end, filteredTodos } = data; + const { toggleTodo, removeTodo } = actions; + return h3( + "div.todo-list", + filteredTodos + .slice(start, end) + .map((t) => Todo(t, { toggleTodo, removeTodo })) + ); +}
M example/assets/js/paginator.jsexample/assets/js/components/paginator.js

@@ -1,4 +1,4 @@

-import h3 from "./h3.js"; +import h3 from "../h3.js"; export default function Paginator(data, actions) { const { update } = actions;
D example/assets/js/todo.js

@@ -1,12 +0,0 @@

-import h3 from "./h3.js"; - -export default function Todo(data, actions) { - const { toggle, remove } = actions; - const todoStateClass = data.done ? ".done" : ".todo"; - return h3("div.todo-item", { id: data.key }, [ - h3(`div.todo-content${todoStateClass}`, [ - h3("span.todo-text", { onclick: () => toggle(data) }, [data.text]), - ]), - h3("span.delete-todo", { onclick: () => remove(data) }, ["✘"]), - ]); -};