Skip to content

Commit

Permalink
Merge pull request #1 from gaearon/master
Browse files Browse the repository at this point in the history
Optimize things a little bit
  • Loading branch information
mweststrate committed Apr 13, 2016
2 parents 51e049d + 4173886 commit 9b12801
Show file tree
Hide file tree
Showing 20 changed files with 390 additions and 310 deletions.
1 change: 1 addition & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"presets": ["es2015", "react"],
"plugins": ["transform-object-rest-spread"],
"env": {
"development": {
"presets": ["react-hmre"]
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
dist/
dist/*.js
npm-debug.log
node_modules/
7 changes: 6 additions & 1 deletion actions/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as types from '../constants/ActionTypes'

let nextId = 0;
export function addTodo(text) {
return { type: types.ADD_TODO, text }
return { type: types.ADD_TODO, text, id: (nextId++).toString() }
}

export function deleteTodo(id) {
Expand All @@ -23,3 +24,7 @@ export function completeAll() {
export function clearCompleted() {
return { type: types.CLEAR_COMPLETED }
}

export function setFilter(filter) {
return { type: types.SET_FILTER, filter }
}
13 changes: 13 additions & 0 deletions components/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React, { PropTypes } from 'react'
import Header from '../components/Header'
import MainSection from '../components/MainSection'
import * as TodoActions from '../actions'

const App = () => (
<div>
<Header />
<MainSection />
</div>
)

export default App
32 changes: 32 additions & 0 deletions components/FilterLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import { connect } from 'react-redux'
import classnames from 'classnames'
import { setFilter } from '../actions'
import { SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED } from '../constants/TodoFilters'

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

const FilterLink = ({ filter, selected, onClick }) => (
<a className={classnames({ selected })}
style={{ cursor: 'pointer' }}
onClick={onClick}>
{FILTER_TITLES[filter]}
</a>
)

const mapStateToProps = (state, ownProps) => ({
selected: state.filter === ownProps.filter
})

const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => dispatch(setFilter(ownProps.filter))
})

export default connect(
mapStateToProps,
mapDispatchToProps
)(FilterLink)
121 changes: 54 additions & 67 deletions components/Footer.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,60 @@
import React, { PropTypes, Component } from 'react'
import classnames from 'classnames'
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import FilterLink from './FilterLink'
import { getCompletedCount, getListedCount } from '../reducers'
import { clearCompleted } from '../actions'
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>
)
}
}

const TodoCount = ({ activeCount }) => (
<span className="todo-count">
<strong>{activeCount || 'No'}</strong>
{' '}
{activeCount === 1 ? 'item' : 'items'} left
</span>
)

const ClearButton = ({ completedCount, clearCompleted }) => (
<button className="clear-completed"
onClick={clearCompleted} >
Clear completed
</button>
)

const Footer = ({ filter, completedCount, listedCount, clearCompleted }) => (
listedCount ? (
<footer className="footer">
<TodoCount activeCount={listedCount - completedCount} />
<ul className="filters">
{[ SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED ].map(filter =>
<li key={filter}>
<FilterLink filter={filter} />
</li>
)}
</ul>
{completedCount > 0 &&
<ClearButton
completedCount={completedCount}
clearCompleted={clearCompleted}
/>
}
</footer>
) : (
<span />
)
)
Footer.propTypes = {
completedCount: PropTypes.number.isRequired,
activeCount: PropTypes.number.isRequired,
filter: PropTypes.string.isRequired,
onClearCompleted: PropTypes.func.isRequired,
onShow: PropTypes.func.isRequired
listedCount: PropTypes.number.isRequired,
completedCount: PropTypes.number.isRequired,
}

export default Footer
const mapStateToProps = (state) => ({
filter: state.filter,
listedCount: getListedCount(state),
completedCount: getCompletedCount(state),
})

export default connect(
mapStateToProps,
{ clearCompleted }
)(Footer)
42 changes: 21 additions & 21 deletions components/Header.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import React, { PropTypes, Component } from 'react'
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import TodoTextInput from './TodoTextInput'
import { addTodo } from '../actions'

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>
)
}
}

const Header = ({ addTodo }) => (
<header className="header">
<h1>todos</h1>
<TodoTextInput
newTodo
placeholder="What needs to be done?"
onSave={text => {
if (text.length !== 0) {
addTodo(text)
}
}}
/>
</header>
)
Header.propTypes = {
addTodo: PropTypes.func.isRequired
}

export default Header
export default connect(
null,
{ addTodo }
)(Header)
105 changes: 24 additions & 81 deletions components/MainSection.js
Original file line number Diff line number Diff line change
@@ -1,83 +1,26 @@
import React, { Component, PropTypes } from 'react'
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import TodoItem from './TodoItem'
import ToggleAll from './ToggleAll'
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() {
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
import { getVisibleTodoIds } from '../reducers'

const MainSection = ({ visibleIds }) => (
<section className="main">
<ToggleAll />
<ul className="todo-list">
{visibleIds.map(id =>
<TodoItem key={id} id={id} />
)}
</ul>
<Footer />
</section>
)

const mapStateToProps = (state) => ({
visibleIds: getVisibleTodoIds(state)
})

export default connect(
mapStateToProps
)(MainSection)
Loading

0 comments on commit 9b12801

Please sign in to comment.