2023-10-02 03:36:13 +03:00
|
|
|
/*
|
|
|
|
|
Example of TODO HTML5 app built using preact library
|
|
|
|
|
*/
|
|
|
|
|
|
2023-06-19 07:59:21 +03:00
|
|
|
import {h, render} from 'https://unpkg.com/preact?module';
|
|
|
|
|
|
|
|
|
|
import {createApp, handler, connect} from './app.js'
|
|
|
|
|
|
|
|
|
|
// Components
|
|
|
|
|
|
|
|
|
|
const App = () => (
|
|
|
|
|
h('div', null,
|
|
|
|
|
h(AddTodo),
|
|
|
|
|
h(TodoList),
|
|
|
|
|
h(Footer),
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const Footer = () => (
|
|
|
|
|
h('div', null,
|
|
|
|
|
h('span', null, 'Show: '),
|
|
|
|
|
h(FilterLink, {filter: 'ALL'}, 'All'),
|
|
|
|
|
h(FilterLink, {filter: 'ACTIVE'}, 'Active'),
|
|
|
|
|
h(FilterLink, {filter: 'COMPLETED'}, 'Completed'),
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const FilterLink = connect(({filter, children}, state) => {
|
|
|
|
|
const disabled = state.filter == filter
|
|
|
|
|
return h('button', {
|
|
|
|
|
onClick: handler(changeFilter.bind(null, filter)),
|
|
|
|
|
disabled,
|
|
|
|
|
style:{
|
|
|
|
|
marginLeft: '4px',
|
|
|
|
|
}
|
|
|
|
|
}, children)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const TodoList = connect( (_, state) =>
|
|
|
|
|
h('ul', null,
|
|
|
|
|
visibleTodos(state).map(todo =>
|
|
|
|
|
h(Todo, { todo })
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const Todo = ({ onClick, todo }) => (
|
|
|
|
|
h('li', {
|
|
|
|
|
onClick: handler(toggleTodo.bind(null, todo)),
|
|
|
|
|
style: {
|
|
|
|
|
textDecoration: todo.completed ? 'line-through' : 'none'
|
|
|
|
|
},
|
|
|
|
|
}, todo.text)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const AddTodo = connect((_, state) => {
|
|
|
|
|
return (
|
|
|
|
|
h('div', null,
|
|
|
|
|
h('form', {
|
|
|
|
|
onSubmit: handler(createTodo),
|
|
|
|
|
},
|
|
|
|
|
h('input', {
|
|
|
|
|
value: state.text,
|
|
|
|
|
onChange: handler(changeText),
|
|
|
|
|
autoFocus: true,
|
|
|
|
|
}),
|
|
|
|
|
h('button', {type: 'submit'}, 'Add Todo')
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Selectors
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
function changeText(state, e) {
|
|
|
|
|
return {...state, text: e.target.value}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function changeFilter(filter, state) {
|
|
|
|
|
return {...state, filter}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createTodo(state, e) {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
|
|
|
|
|
if(!state.text.trim()) {
|
|
|
|
|
return state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...state,
|
|
|
|
|
todos: [...state.todos, {text: state.text}],
|
|
|
|
|
text: '',
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleTodo(todo, state) {
|
|
|
|
|
return {
|
|
|
|
|
...state,
|
|
|
|
|
todos: state.todos.map(t =>
|
|
|
|
|
(t == todo)
|
|
|
|
|
? {...todo, completed: !todo.completed}
|
|
|
|
|
: t
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createApp({
|
|
|
|
|
|
|
|
|
|
initialState: {
|
|
|
|
|
todos: [],
|
|
|
|
|
text: '',
|
|
|
|
|
filter: 'ALL',
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
component: App,
|
|
|
|
|
|
|
|
|
|
root: document.body,
|
|
|
|
|
})
|
|
|
|
|
|