nesquirk
Version:
Ties Nes + minimongo together for gloryful reactive apps.
110 lines (99 loc) • 3.26 kB
JavaScript
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'
import { createContainer, withClient } from 'nesquirk'
import Todos from './domain/Todos'
class TodoList extends Component {
static propTypes = {
todos: PropTypes.array.isRequired,
onDone: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
loading: PropTypes.bool
}
onDoneChange = (e) => {
const target = e.currentTarget
this.props.onDone(target.getAttribute('data-id'), target.checked)
}
onRemoveClick = (e) => {
if (!window.confirm('Are you sure?')) return
this.props.onRemove(e.currentTarget.getAttribute('data-id'))
}
render () {
const { todos, loading } = this.props
return (
<div>
<div className='row mt-3 mb-2'>
<div className='col-6'>
<h1>TODO list</h1>
</div>
<div className='col-6 text-right'>
<Link to='/add' className='btn btn-primary'>Add todo</Link>
</div>
</div>
{loading ? (
<p>Loading...</p>
) : (
<List todos={todos} onDoneChange={this.onDoneChange} onRemoveClick={this.onRemoveClick} />
)}
</div>
)
}
}
const List = ({ todos, onDoneChange, onRemoveClick }) => todos.length ? (
<ol className='list-group'>
{todos.map((todo) => (
<li key={todo._id} className={`list-group-item justify-content-between ${todo.done ? 'list-group-item-success' : ''}`}>
<div>
<label className='p-1 mr-2 mb-0' title={todo.done ? 'Not done?' : 'Done?'}>
<input type='checkbox' checked={!!todo.done} onChange={onDoneChange} data-id={todo._id} />
</label>
<Link to={`/view/${todo._id}`}>{todo.title || 'Untitled'}</Link>
</div>
<div>
<Link to={`/edit/${todo._id}`} className='btn btn-secondary btn-sm mr-2'>Edit</Link>
<button type='button' className='btn btn-danger btn-sm' onClick={onRemoveClick} aria-label='Remove' data-id={todo._id}>
<span aria-hidden='true'>×</span>
</button>
</div>
</li>
))}
</ol>
) : (
<p>No todos yet!</p>
)
class TodoListContainer extends Component {
static propTypes = {
todos: PropTypes.array.isRequired,
client: PropTypes.object.isRequired,
loading: PropTypes.bool
}
onDone = (todoId, done) => {
this.props.client.request({
path: `/todo/${todoId}`,
method: 'PATCH',
payload: { done }
}, (err) => {
if (err) return console.error('Failed to update todo', err)
})
}
onRemove = (todoId) => {
this.props.client.request({
path: `/todo/${todoId}`,
method: 'DELETE'
}, (err) => {
if (err) return console.error('Failed to remove todo', err)
})
}
render () {
const { todos, loading } = this.props
const { onDone, onRemove } = this
return <TodoList todos={todos} onDone={onDone} onRemove={onRemove} laoding={loading} />
}
}
export default withClient(createContainer(function (props) {
const handle = this.subscribe('/todos', Todos)
return {
todos: Todos.find({}).sort({ createdAt: -1 }).all(),
loading: !handle.ready()
}
}, TodoListContainer))