v2: prepare to move

This commit is contained in:
endiliey 2018-09-17 11:16:07 +08:00
parent dc7ef96849
commit 45736200b0
172 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,94 @@
import React from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
const ESCAPE_KEY = 27;
const ENTER_KEY = 13;
export default class TodoItem extends React.Component {
constructor(props) {
super(props);
this.state = {
editText: props.todo.title
};
this.handleEdit = this.handleEdit.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleChange = this.handleChange.bind(this);
}
shouldComponentUpdate(nextProps, nextState) {
return (
nextProps.todo !== this.props.todo ||
nextProps.editing !== this.props.editing ||
nextState.editText !== this.state.editText
);
}
componentDidUpdate(prevProps) {
if (!prevProps.editing && this.props.editing) {
const node = ReactDOM.findDOMNode(this.refs.editField);
node.focus();
node.setSelectionRange(node.value.length, node.value.length);
}
}
handleSubmit() {
const val = this.state.editText.trim();
if (val) {
this.props.onSave(val);
this.setState({editText: val});
} else {
this.props.onDestroy();
}
}
handleEdit() {
this.props.onEdit();
this.setState({editText: this.props.todo.title});
}
handleKeyDown(event) {
if (event.which === ESCAPE_KEY) {
this.setState({editText: this.props.todo.title});
this.props.onCancel(event);
} else if (event.which === ENTER_KEY) {
this.handleSubmit(event);
}
}
handleChange(event) {
if (this.props.editing) {
this.setState({editText: event.target.value});
}
}
render() {
return (
<li
className={classNames({
completed: this.props.todo.completed,
editing: this.props.editing
})}>
<div className="view">
<input
className="toggle"
type="checkbox"
checked={this.props.todo.completed}
onChange={this.props.onToggle}
/>
<label onDoubleClick={this.handleEdit}>{this.props.todo.title}</label>
<button className="destroy" onClick={this.props.onDestroy} />
</div>
<input
ref="editField"
className="edit"
value={this.state.editText}
onBlur={this.handleSubmit}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
/>
</li>
);
}
}

View file

@ -0,0 +1,29 @@
import React from 'react';
import TodoItem from './TodoItem';
export default function TodoList(props) {
const todoItems = props.todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={() => {
props.onToggle(todo);
}}
onDestroy={() => {
props.onDestroy(todo);
}}
onEdit={() => {
props.onEdit(todo);
}}
editing={props.editing(todo)}
onSave={text => {
props.onSave(todo, text);
}}
onCancel={() => {
props.onCancel();
}}
/>
));
return <div>{todoItems}</div>;
}

View file

@ -0,0 +1,205 @@
import React from 'react';
import Helmet from 'react-helmet';
import TodoList from './TodoList';
const ENTER_KEY = 13;
function uuid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
}
const todos = [
{
id: 'ed0bcc48-bbbe-5f06-c7c9-2ccb0456ceba',
title: 'Build this Todo App.',
completed: true
},
{
id: '42582304-3c6e-311e-7f88-7e3791caf88c',
title: '?????',
completed: true
},
{
id: '1cf63885-5f75-8deb-19dc-9b6765deae6c',
title: '1,000 stars on GitHub.',
completed: false
},
{
id: '63a871b2-0b6f-4427-9c35-304bc680a4b7',
title: 'Write a popular medium post.',
completed: false
},
{
id: '63a871b2-0b6f-4422-9c35-304bc680a4b7',
title: 'Contribute to open source.',
completed: false
},
{
id: '036af7f9-1181-fb8f-258f-3f06034c020f',
title: 'Write a blog post.',
completed: false
}
];
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: null,
newTodo: '',
todos: todos
};
}
handleChange(event) {
this.setState({newTodo: event.target.value});
}
handleNewTodoKeyDown(event) {
if (event.keyCode !== ENTER_KEY) {
return;
}
event.preventDefault();
const val = this.state.newTodo.trim();
if (val) {
this.setState({
todos: this.state.todos.concat({
id: uuid(),
title: val,
completed: false
}),
newTodo: ''
});
}
}
toggleAll(event) {
const {checked} = event.target;
this.setState({
todos: this.state.todos.map(todo =>
Object.assign({}, todo, {completed: checked})
)
});
}
toggle(todoToToggle) {
this.setState({
todos: this.state.todos.map(todo => {
if (todo === todoToToggle) {
return Object.assign({}, todo, {
completed: !todo.completed
});
}
return todo;
})
});
}
destroy(passedTodo) {
this.setState({
todos: this.state.todos.filter(todo => todo !== passedTodo)
});
}
edit(todo) {
this.setState({editing: todo.id});
}
save(todoToSave, text) {
this.setState({
todos: this.state.todos.map(todo => {
if (todo === todoToSave) {
return Object.assign({}, todo, {
title: text
});
}
return todo;
}),
editing: null
});
}
cancel() {
this.setState({editing: null});
}
clearCompleted() {
this.setState({
todos: this.state.todos.filter(todo => !todo.completed)
});
}
render() {
let main;
const {todos} = this.state;
const activeTodoCount = todos.reduce(
(accum, todo) => (todo.completed ? accum : accum + 1),
0
);
if (todos.length) {
main = (
<section className="main">
<input
className="toggle-all"
type="checkbox"
onChange={this.toggleAll}
checked={activeTodoCount === 0}
/>
<ul className="todo-list">
<TodoList
todos={todos}
onToggle={todo => {
this.toggle(todo);
}}
onDestroy={todo => {
this.destroy(todo);
}}
onEdit={todo => {
this.edit(todo);
}}
editing={todo => this.state.editing === todo.id}
onSave={(todo, text) => {
this.save(todo, text);
}}
onCancel={() => this.cancel()}
/>
</ul>
</section>
);
}
return (
<div className="todoapp">
<header className="header">
<h1>todos</h1>
<input
className="new-todo"
placeholder="What needs to be done?"
value={this.state.newTodo}
onKeyDown={event => {
this.handleNewTodoKeyDown(event);
}}
onChange={event => {
this.handleChange(event);
}}
autoFocus
/>
</header>
{main}
</div>
);
}
}
export default TodoApp;