/*
Example of a TODO app built using the Preact library
*/
import * as React from "preact/compat"
// Core
// Global application state
let state
/*
Application logic is structured as pure functions with the signature `(state, ...args) => state`.
This helper function wraps such a function so that its result updates the global state
and can be used as an event handler.
*/
const handler =
fn =>
(...args) => {
state = fn(state, ...args)
render()
}
// Higher-order function that injects the current state into a component
const connect = comp => props => comp(props, state)
const render = () => React.render(, document.body)
// Initialize application state if not already restored from storage
if (state == null) {
state = {
todos: [],
text: "",
filter: "ALL",
}
}
window.addEventListener("load", render)
// Components
const App = () => (
)
})
// Selectors
// Returns a filtered list of TODOs based on the current filter state
function visibleTodos(state) {
if (state.filter == "ALL") {
return state.todos
} else if (state.filter == "ACTIVE") {
return state.todos.filter(t => !t.completed)
} else if (state.filter == "COMPLETED") {
return state.todos.filter(t => t.completed)
} else {
throw new Error("Unknown filter")
}
}
// Reducers
// Updates the input text state
function changeText(state, e) {
return { ...state, text: e.target.value }
}
// Updates the active filter state
function changeFilter(filter, state) {
return { ...state, filter }
}
// Creates a new TODO item if the input text is not empty
function createTodo(state, e) {
e.preventDefault()
if (!state.text.trim()) {
return state
}
return {
...state,
todos: [...state.todos, { text: state.text }],
text: "",
}
}
// Toggles the completion state of a TODO item
function toggleTodo(todo, state) {
return {
...state,
todos: state.todos.map(t =>
t == todo ? { ...todo, completed: !todo.completed } : t,
),
}
}