Commit 156d0859 authored by Cheng Lou's avatar Cheng Lou

[React] New main todomvc with fixes and React version bump

Fixes all the stuff from #785 too.
parent 162f06ea
......@@ -3,7 +3,7 @@
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.1.7",
"react": "~0.8.0",
"director": "~1.2.0"
"director": "~1.2.0",
"react": "~0.9.0"
}
}
......@@ -8,15 +8,22 @@
<body>
<section id="todoapp"></section>
<footer id="info"></footer>
<div id="benchmark"></div>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://github.com/petehunt/">petehunt</a></p>
<p>Part of<a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/react/react-with-addons.js"></script>
<script src="bower_components/react/JSXTransformer.js"></script>
<script src="bower_components/director/build/director.js"></script>
<script type="text/jsx" src="js/utils.jsx"></script>
<script src="js/utils.js"></script>
<script src="js/todoModel.js"></script>
<!-- jsx is an optional syntactic sugar that transforms methods in React's
`render` into an HTML-looking format. Since the two models above are
unrelated to React, we didn't need those transforms. -->
<script type="text/jsx" src="js/todoItem.jsx"></script>
<script type="text/jsx" src="js/footer.jsx"></script>
<script type="text/jsx" src="js/app.jsx"></script>
......
......@@ -5,35 +5,36 @@
/*jshint white:false */
/*jshint trailing:false */
/*jshint newcap:false */
/*global Utils, TodoModel, ALL_TODOS, ACTIVE_TODOS,
COMPLETED_TODOS, TodoItem, TodoFooter, React, Router*/
/*global React, Router*/
var app = app || {};
(function (window, React) {
(function () {
'use strict';
window.ALL_TODOS = 'all';
window.ACTIVE_TODOS = 'active';
window.COMPLETED_TODOS = 'completed';
app.ALL_TODOS = 'all';
app.ACTIVE_TODOS = 'active';
app.COMPLETED_TODOS = 'completed';
var TodoFooter = app.TodoFooter;
var TodoItem = app.TodoItem;
var ENTER_KEY = 13;
var TodoApp = React.createClass({
getInitialState: function () {
return {
nowShowing: ALL_TODOS,
nowShowing: app.ALL_TODOS,
editing: null
};
},
componentDidMount: function () {
var setState = this.setState;
var router = Router({
'/': this.setState.bind(this, {nowShowing: ALL_TODOS}),
'/active': this.setState.bind(this, {nowShowing: ACTIVE_TODOS}),
'/completed': this.setState.bind(this, {nowShowing: COMPLETED_TODOS})
'/': setState.bind(this, {nowShowing: app.ALL_TODOS}),
'/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}),
'/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS})
});
router.init();
this.refs.newField.getDOMNode().focus();
router.init('/');
},
handleNewTodoKeyDown: function (event) {
......@@ -86,14 +87,15 @@
},
render: function () {
var footer = null;
var main = null;
var footer;
var main;
var todos = this.props.model.todos;
var shownTodos = this.props.model.todos.filter(function (todo) {
var shownTodos = todos.filter(function (todo) {
switch (this.state.nowShowing) {
case ACTIVE_TODOS:
case app.ACTIVE_TODOS:
return !todo.completed;
case COMPLETED_TODOS:
case app.COMPLETED_TODOS:
return todo.completed;
default:
return true;
......@@ -115,11 +117,11 @@
);
}, this);
var activeTodoCount = this.props.model.todos.reduce(function(accum, todo) {
var activeTodoCount = todos.reduce(function (accum, todo) {
return todo.completed ? accum : accum + 1;
}, 0);
var completedCount = this.props.model.todos.length - activeTodoCount;
var completedCount = todos.length - activeTodoCount;
if (activeTodoCount || completedCount) {
footer =
......@@ -131,7 +133,7 @@
/>;
}
if (this.props.model.todos.length) {
if (todos.length) {
main = (
<section id="main">
<input
......@@ -156,6 +158,7 @@
id="new-todo"
placeholder="What needs to be done?"
onKeyDown={this.handleNewTodoKeyDown}
autoFocus={true}
/>
</header>
{main}
......@@ -165,7 +168,7 @@
}
});
var model = new TodoModel('react-todos');
var model = new app.TodoModel('react-todos');
function render() {
React.renderComponent(
......@@ -176,14 +179,4 @@
model.subscribe(render);
render();
React.renderComponent(
<div>
<p>Double-click to edit a todo</p>
<p>Created by{' '}
<a href="http://github.com/petehunt/">petehunt</a>
</p>
<p>Part of{' '}<a href="http://todomvc.com">TodoMVC</a></p>
</div>,
document.getElementById('info'));
})(window, React);
})();
......@@ -5,13 +5,15 @@
/*jshint white:false */
/*jshint trailing:false */
/*jshint newcap:false */
/*global React, ALL_TODOS, ACTIVE_TODOS, Utils, COMPLETED_TODOS */
(function (window) {
/*global React */
var app = app || {};
(function () {
'use strict';
window.TodoFooter = React.createClass({
app.TodoFooter = React.createClass({
render: function () {
var activeTodoWord = Utils.pluralize(this.props.count, 'item');
var activeTodoWord = app.Utils.pluralize(this.props.count, 'item');
var clearButton = null;
if (this.props.completedCount > 0) {
......@@ -19,35 +21,42 @@
<button
id="clear-completed"
onClick={this.props.onClearCompleted}>
{''}Clear completed ({this.props.completedCount}){''}
Clear completed ({this.props.completedCount})
</button>
);
}
var show = {
ALL_TODOS: '',
ACTIVE_TODOS: '',
COMPLETED_TODOS: ''
};
show[this.props.nowShowing] = 'selected';
// React idiom for shortcutting to `classSet` since it'll be used often
var cx = React.addons.classSet;
var nowShowing = this.props.nowShowing;
return (
<footer id="footer">
<span id="todo-count">
<strong>{this.props.count}</strong>
{' '}{activeTodoWord}{' '}left{''}
<strong>{this.props.count}</strong> {activeTodoWord} left
</span>
<ul id="filters">
<li>
<a href="#/" className={show[ALL_TODOS]}>All</a>
<a
href="#/"
className={cx({selected: nowShowing === app.ALL_TODOS})}>
All
</a>
</li>
{' '}
<li>
<a href="#/active" className={show[ACTIVE_TODOS]}>Active</a>
<a
href="#/active"
className={cx({selected: nowShowing === app.ACTIVE_TODOS})}>
Active
</a>
</li>
{' '}
<li>
<a href="#/completed" className={show[COMPLETED_TODOS]}>Completed</a>
<a
href="#/completed"
className={cx({selected: nowShowing === app.COMPLETED_TODOS})}>
Completed
</a>
</li>
</ul>
{clearButton}
......@@ -55,4 +64,4 @@
);
}
});
})(window);
})();
......@@ -5,14 +5,16 @@
/*jshint white: false */
/*jshint trailing: false */
/*jshint newcap: false */
/*global React, Utils */
(function (window) {
/*global React */
var app = app || {};
(function () {
'use strict';
var ESCAPE_KEY = 27;
var ENTER_KEY = 13;
window.TodoItem = React.createClass({
app.TodoItem = React.createClass({
handleSubmit: function () {
var val = this.state.editText.trim();
if (val) {
......@@ -38,10 +40,10 @@
},
handleKeyDown: function (event) {
if (event.keyCode === ESCAPE_KEY) {
if (event.which === ESCAPE_KEY) {
this.setState({editText: this.props.todo.title});
this.props.onCancel();
} else if (event.keyCode === ENTER_KEY) {
} else if (event.which === ENTER_KEY) {
this.handleSubmit();
}
},
......@@ -98,4 +100,4 @@
);
}
});
})(window);
})();
......@@ -5,32 +5,33 @@
/*jshint white:false */
/*jshint trailing:false */
/*jshint newcap:false */
/*global Utils */
var app = app || {};
(function (window) {
(function () {
'use strict';
var Utils = app.Utils;
// Generic "model" object. You can use whatever
// framework you want. For this application it
// may not even be worth separating this logic
// out, but we do this to demonstrate one way to
// separate out parts of your application.
window.TodoModel = function (key) {
app.TodoModel = function (key) {
this.key = key;
this.todos = Utils.store(key);
this.onChanges = [];
};
window.TodoModel.prototype.subscribe = function (onChange) {
app.TodoModel.prototype.subscribe = function (onChange) {
this.onChanges.push(onChange);
};
window.TodoModel.prototype.inform = function () {
app.TodoModel.prototype.inform = function () {
Utils.store(this.key, this.todos);
this.onChanges.forEach(function (cb) { cb(); });
};
window.TodoModel.prototype.addTodo = function (title) {
app.TodoModel.prototype.addTodo = function (title) {
this.todos = this.todos.concat({
id: Utils.uuid(),
title: title,
......@@ -40,10 +41,11 @@
this.inform();
};
window.TodoModel.prototype.toggleAll = function (checked) {
// Note: it's usually better to use immutable data structures since they're easier to
// reason about and React works very well with them. That's why we use map() and filter()
// everywhere instead of mutating the array or todo items themselves.
app.TodoModel.prototype.toggleAll = function (checked) {
// Note: it's usually better to use immutable data structures since they're
// easier to reason about and React works very well with them. That's why we
// use map() and filter() everywhere instead of mutating the array or todo
// items themselves.
this.todos = this.todos.map(function (todo) {
return Utils.extend({}, todo, {completed: checked});
});
......@@ -51,15 +53,17 @@
this.inform();
};
window.TodoModel.prototype.toggle = function (todoToToggle) {
app.TodoModel.prototype.toggle = function (todoToToggle) {
this.todos = this.todos.map(function (todo) {
return todo !== todoToToggle ? todo : Utils.extend({}, todo, {completed: !todo.completed});
return todo !== todoToToggle ?
todo :
Utils.extend({}, todo, {completed: !todo.completed});
});
this.inform();
};
window.TodoModel.prototype.destroy = function (todo) {
app.TodoModel.prototype.destroy = function (todo) {
this.todos = this.todos.filter(function (candidate) {
return candidate !== todo;
});
......@@ -67,7 +71,7 @@
this.inform();
};
window.TodoModel.prototype.save = function (todoToSave, text) {
app.TodoModel.prototype.save = function (todoToSave, text) {
this.todos = this.todos.map(function (todo) {
return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text});
});
......@@ -75,7 +79,7 @@
this.inform();
};
window.TodoModel.prototype.clearCompleted = function () {
app.TodoModel.prototype.clearCompleted = function () {
this.todos = this.todos.filter(function (todo) {
return !todo.completed;
});
......@@ -83,4 +87,4 @@
this.inform();
};
})(this);
\ No newline at end of file
})();
(function (window) {
var app = app || {};
(function () {
'use strict';
window.Utils = {
app.Utils = {
uuid: function () {
/*jshint bitwise:false */
var i, random;
......@@ -45,5 +47,4 @@
return newObj;
}
};
})(window);
})();
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment