Dataset/JS/ToDoApp_ReactJS/app.tsx (148 lines of code) (raw):
declare var Router;
import * as React from "react";
import * as ReactDOM from "react-dom";
import { TodoModel } from "./todoModel";
import { TodoFooter } from "./footer";
import { TodoItem } from "./todoItem";
import { ALL_TODOS, ACTIVE_TODOS, COMPLETED_TODOS, ENTER_KEY } from "./constants";
class TodoApp extends React.Component<IAppProps, IAppState> {
public state : IAppState;
constructor(props : IAppProps) {
super(props);
this.state = {
nowShowing: ALL_TODOS,
editing: null
};
}
public componentDidMount() {
var setState = this.setState;
var router = Router({
'/': setState.bind(this, {nowShowing: ALL_TODOS}),
'/active': setState.bind(this, {nowShowing: ACTIVE_TODOS}),
'/completed': setState.bind(this, {nowShowing: COMPLETED_TODOS})
});
router.init('/');
}
public handleNewTodoKeyDown(event : React.KeyboardEvent) {
if (event.keyCode !== ENTER_KEY) {
return;
}
event.preventDefault();
var val = (ReactDOM.findDOMNode(this.refs["newField"]) as HTMLInputElement).value.trim();
if (val) {
this.props.model.addTodo(val);
(ReactDOM.findDOMNode(this.refs["newField"]) as HTMLInputElement).value = '';
}
}
public toggleAll(event : React.FormEvent) {
var target : any = event.target;
var checked = target.checked;
this.props.model.toggleAll(checked);
}
public toggle(todoToToggle : ITodo) {
this.props.model.toggle(todoToToggle);
}
public destroy(todo : ITodo) {
this.props.model.destroy(todo);
}
public edit(todo : ITodo) {
this.setState({editing: todo.id});
}
public save(todoToSave : ITodo, text : String) {
this.props.model.save(todoToSave, text);
this.setState({editing: null});
}
public cancel() {
this.setState({editing: null});
}
public clearCompleted() {
this.props.model.clearCompleted();
}
public render() {
var footer;
var main;
const todos = this.props.model.todos;
var shownTodos = todos.filter((todo) => {
switch (this.state.nowShowing) {
case ACTIVE_TODOS:
return !todo.completed;
case COMPLETED_TODOS:
return todo.completed;
default:
return true;
}
});
var todoItems = shownTodos.map((todo) => {
return (
<TodoItem
key={todo.id}
todo={todo}
onToggle={this.toggle.bind(this, todo)}
onDestroy={this.destroy.bind(this, todo)}
onEdit={this.edit.bind(this, todo)}
editing={this.state.editing === todo.id}
onSave={this.save.bind(this, todo)}
onCancel={ e => this.cancel() }
/>
);
});
// 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(), filter() and reduce() everywhere instead of mutating the
// array or todo items themselves.
var activeTodoCount = todos.reduce(function (accum, todo) {
return todo.completed ? accum : accum + 1;
}, 0);
var completedCount = todos.length - activeTodoCount;
if (activeTodoCount || completedCount) {
footer =
<TodoFooter
count={activeTodoCount}
completedCount={completedCount}
nowShowing={this.state.nowShowing}
onClearCompleted={ e=> this.clearCompleted() }
/>;
}
if (todos.length) {
main = (
<section className="main">
<input
id="toggle-all"
className="toggle-all"
type="checkbox"
onChange={ e => this.toggleAll(e) }
checked={activeTodoCount === 0}
/>
<label
htmlFor="toggle-all"
>
Mark all as complete
</label>
<ul className="todo-list">
{todoItems}
</ul>
</section>
);
}
return (
<div>
<header className="header">
<h1>todos</h1>
<input
ref="newField"
className="new-todo"
placeholder="What needs to be done?"
onKeyDown={ e => this.handleNewTodoKeyDown(e) }
autoFocus={true}
/>
</header>
{main}
{footer}
</div>
);
}
}
var model = new TodoModel('react-todos');
function render() {
ReactDOM.render(
<TodoApp model={model}/>,
document.getElementsByClassName('todoapp')[0]
);
}
model.subscribe(render);
render();