Skip to content
This repository has been archived by the owner on Dec 2, 2019. It is now read-only.

Commit

Permalink
all the code
Browse files Browse the repository at this point in the history
  • Loading branch information
pgte committed Nov 26, 2015
1 parent d5edf8f commit 9c3659a
Show file tree
Hide file tree
Showing 28 changed files with 699 additions and 0 deletions.
25 changes: 25 additions & 0 deletions actions/todos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as types from '../constants/ActionTypes'

export function addTodo(text) {
return { type: types.ADD_TODO, text }
}

export function deleteTodo(id) {
return { type: types.DELETE_TODO, id }
}

export function editTodo(id, text) {
return { type: types.EDIT_TODO, id, text }
}

export function completeTodo(id) {
return { type: types.COMPLETE_TODO, id }
}

export function completeAll() {
return { type: types.COMPLETE_ALL }
}

export function clearCompleted() {
return { type: types.CLEAR_COMPLETED }
}
73 changes: 73 additions & 0 deletions components/Footer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { PropTypes, Component } from 'react'
import classnames from 'classnames'
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'

const FILTER_TITLES = {
[SHOW_ALL]: 'All',
[SHOW_ACTIVE]: 'Active',
[SHOW_COMPLETED]: 'Completed'
}

class Footer extends Component {
renderTodoCount() {
const { activeCount } = this.props
const itemWord = activeCount === 1 ? 'item' : 'items'

return (
<span className="todo-count">
<strong>{activeCount || 'No'}</strong> {itemWord} left
</span>
)
}

renderFilterLink(filter) {
const title = FILTER_TITLES[filter]
const { filter: selectedFilter, onShow } = this.props

return (
<a className={classnames({ selected: filter === selectedFilter })}
style={{ cursor: 'pointer' }}
onClick={() => onShow(filter)}>
{title}
</a>
)
}

renderClearButton() {
const { completedCount, onClearCompleted } = this.props
if (completedCount > 0) {
return (
<button className="clear-completed"
onClick={onClearCompleted} >
Clear completed
</button>
)
}
}

render() {
return (
<footer className="footer">
{this.renderTodoCount()}
<ul className="filters">
{[ SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED ].map(filter =>
<li key={filter}>
{this.renderFilterLink(filter)}
</li>
)}
</ul>
{this.renderClearButton()}
</footer>
)
}
}

Footer.propTypes = {
completedCount: PropTypes.number.isRequired,
activeCount: PropTypes.number.isRequired,
filter: PropTypes.string.isRequired,
onClearCompleted: PropTypes.func.isRequired,
onShow: PropTypes.func.isRequired
}

export default Footer
27 changes: 27 additions & 0 deletions components/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { PropTypes, Component } from 'react'
import TodoTextInput from './TodoTextInput'

class Header extends Component {
handleSave(text) {
if (text.length !== 0) {
this.props.addTodo(text)
}
}

render() {
return (
<header className="header">
<h1>todos</h1>
<TodoTextInput newTodo
onSave={this.handleSave.bind(this)}
placeholder="What needs to be done?" />
</header>
)
}
}

Header.propTypes = {
addTodo: PropTypes.func.isRequired
}

export default Header
86 changes: 86 additions & 0 deletions components/MainSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { Component, PropTypes } from 'react'
import TodoItem from './TodoItem'
import Footer from './Footer'
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'

const TODO_FILTERS = {
[SHOW_ALL]: () => true,
[SHOW_ACTIVE]: todo => !todo.completed,
[SHOW_COMPLETED]: todo => todo.completed
}

class MainSection extends Component {
constructor(props, context) {
super(props, context)
this.state = { filter: SHOW_ALL }
}

handleClearCompleted() {
const atLeastOneCompleted = this.props.todos.some(todo => todo.completed)
if (atLeastOneCompleted) {
this.props.actions.clearCompleted()
}
}

handleShow(filter) {
this.setState({ filter })
}

renderToggleAll(completedCount) {
const { todos, actions } = this.props
if (todos.length > 0) {
return (
<input className="toggle-all"
type="checkbox"
checked={completedCount === todos.length}
onChange={actions.completeAll} />
)
}
}

renderFooter(completedCount) {
const { todos } = this.props
const { filter } = this.state
const activeCount = todos.length - completedCount

if (todos.length) {
return (
<Footer completedCount={completedCount}
activeCount={activeCount}
filter={filter}
onClearCompleted={this.handleClearCompleted.bind(this)}
onShow={this.handleShow.bind(this)} />
)
}
}

render() {
const { todos, actions } = this.props
const { filter } = this.state

const filteredTodos = todos.filter(TODO_FILTERS[filter])
const completedCount = todos.reduce((count, todo) =>
todo.completed ? count + 1 : count,
0
)

return (
<section className="main">
{this.renderToggleAll(completedCount)}
<ul className="todo-list">
{filteredTodos.map(todo =>
<TodoItem key={todo._id} todo={todo} {...actions} />
)}
</ul>
{this.renderFooter(completedCount)}
</section>
)
}
}

MainSection.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
}

export default MainSection
17 changes: 17 additions & 0 deletions components/SyncStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { PropTypes, Component } from 'react'
import classnames from 'classnames'
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'

class SyncStatus extends Component {

render() {
const { text } = this.props.status
return (
<p style={{padding: "1em"}}>
syncStatus: {text}
</p>
)
}
}

export default SyncStatus
70 changes: 70 additions & 0 deletions components/TodoItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
import TodoTextInput from './TodoTextInput'

class TodoItem extends Component {
constructor(props, context) {
super(props, context)
this.state = {
editing: false
}
}

handleDoubleClick() {
this.setState({ editing: true })
}

handleSave(id, text) {
if (text.length === 0) {
this.props.deleteTodo(id)
} else {
this.props.editTodo(id, text)
}
this.setState({ editing: false })
}

render() {
const { todo, completeTodo, deleteTodo } = this.props

let element
if (this.state.editing) {
element = (
<TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) => this.handleSave(todo._id, text)} />
)
} else {
element = (
<div className="view">
<input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() => completeTodo(todo._id)} />
<label onDoubleClick={this.handleDoubleClick.bind(this)}>
{todo.text}
</label>
<button className="destroy"
onClick={() => deleteTodo(todo._id)} />
</div>
)
}

return (
<li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}>
{element}
</li>
)
}
}

TodoItem.propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired
}

export default TodoItem
58 changes: 58 additions & 0 deletions components/TodoTextInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'

class TodoTextInput extends Component {
constructor(props, context) {
super(props, context)
this.state = {
text: this.props.text || ''
}
}

handleSubmit(e) {
const text = e.target.value.trim()
if (e.which === 13) {
this.props.onSave(text)
if (this.props.newTodo) {
this.setState({ text: '' })
}
}
}

handleChange(e) {
this.setState({ text: e.target.value })
}

handleBlur(e) {
if (!this.props.newTodo) {
this.props.onSave(e.target.value)
}
}

render() {
return (
<input className={
classnames({
edit: this.props.editing,
'new-todo': this.props.newTodo
})}
type="text"
placeholder={this.props.placeholder}
autoFocus="true"
value={this.state.text}
onBlur={this.handleBlur.bind(this)}
onChange={this.handleChange.bind(this)}
onKeyDown={this.handleSubmit.bind(this)} />
)
}
}

TodoTextInput.propTypes = {
onSave: PropTypes.func.isRequired,
text: PropTypes.string,
placeholder: PropTypes.string,
editing: PropTypes.bool,
newTodo: PropTypes.bool
}

export default TodoTextInput
10 changes: 10 additions & 0 deletions constants/ActionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const ERROR = 'ERROR'
export const ADD_TODO = 'ADD_TODO'
export const INSERT_TODO = 'INSERT_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const EDIT_TODO = 'EDIT_TODO'
export const UPDATE_TODO = 'UPDATE_TODO'
export const COMPLETE_TODO = 'COMPLETE_TODO'
export const COMPLETE_ALL = 'COMPLETE_ALL'
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
export const SET_SYNC_STATE = 'SET_SYNC_STATE'
3 changes: 3 additions & 0 deletions constants/TodoFilters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const SHOW_ALL = 'show_all'
export const SHOW_COMPLETED = 'show_completed'
export const SHOW_ACTIVE = 'show_active'
Loading

0 comments on commit 9c3659a

Please sign in to comment.