|
@@ -0,0 +1,219 @@
|
|
|
+import React, { Component } from 'react';
|
|
|
+import logo from './logo.svg';
|
|
|
+import './App.css';
|
|
|
+import { connect, Provider } from 'react-redux';
|
|
|
+import { createStore, combineReducers } from 'redux';
|
|
|
+let todoList = [
|
|
|
+ {
|
|
|
+ title: 'todo one',
|
|
|
+ text: 'todo one text',
|
|
|
+ timestamp: (new Date()).getTime()
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'todo two',
|
|
|
+ text: 'todo two text',
|
|
|
+ timestamp: (new Date()).getTime()
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'todo three',
|
|
|
+ text: 'todo three text',
|
|
|
+ timestamp: (new Date()).getTime()
|
|
|
+ }
|
|
|
+];
|
|
|
+function todoReducer(state, action){
|
|
|
+ if (typeof state === 'undefined'){
|
|
|
+ return {items: JSON.parse(localStorage.todo)};
|
|
|
+ }
|
|
|
+ if (action.type == 'NEW_TODO'){
|
|
|
+ console.log(state);
|
|
|
+ return {items: [...state.items, {title: action.title, text: action.text, timestamp: (new Date()).getTime()}]}
|
|
|
+ }
|
|
|
+ if (action.type == 'DELETE_TODO'){
|
|
|
+ let items = [];
|
|
|
+ for (let i=0;i<state.items.length;i++){
|
|
|
+ if (state.items[i].timestamp != action.timestamp){
|
|
|
+ items.push(state.items[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {items};
|
|
|
+ }
|
|
|
+}
|
|
|
+const reducers = combineReducers({
|
|
|
+ todo: todoReducer,
|
|
|
+})
|
|
|
+var store = createStore(reducers)
|
|
|
+store.subscribe(() => {
|
|
|
+ let state = store.getState();
|
|
|
+ localStorage.todo = JSON.stringify(state.todo.items);
|
|
|
+})
|
|
|
+class ToDoItem extends Component {
|
|
|
+ (props){
|
|
|
+ super(props);
|
|
|
+ this.delete = this.delete.bind(this);
|
|
|
+ }
|
|
|
+ delete() {
|
|
|
+ store.dispatch({type: "DELETE_TODO", timestamp: this.props.item.timestamp})
|
|
|
+ }
|
|
|
+ render() {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <h3> { this.props.item.title } </h3>
|
|
|
+ <p> { this.props.item.text }</p>
|
|
|
+ <span> { (new Date(this.props.item.timestamp)).toLocaleString() } {this.props.item.timestamp} </span>
|
|
|
+ <button onClick={this.delete} >x</button>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+class ToDoForm extends Component {
|
|
|
+ constructor (props) {
|
|
|
+ super(props);
|
|
|
+ this.save = this.save.bind(this);
|
|
|
+ this.state = {valid: true};
|
|
|
+ }
|
|
|
+ validator(){
|
|
|
+ return this.title.value && this.text.value;
|
|
|
+ }
|
|
|
+ save() {
|
|
|
+ let valid = this.validator();
|
|
|
+ this.setState({valid})
|
|
|
+ if (valid){
|
|
|
+ store.dispatch({type: 'NEW_TODO',
|
|
|
+ title: this.title.value,
|
|
|
+ text: this.text.value})
|
|
|
+ this.title.value = '';
|
|
|
+ this.text.value = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ render (){
|
|
|
+ let style = {
|
|
|
+ backgroundColor: this.state.valid ? '' : 'red'
|
|
|
+ }
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <input style={style} placeholder='title' ref={ c => this.title = c}/>
|
|
|
+ <input style={style} placeholder='text' ref={ c => this.text = c} />
|
|
|
+ <button onClick = { this.save } >Save</button>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+class ToDoList extends Component {
|
|
|
+ render (){
|
|
|
+ return (
|
|
|
+ <div> <h1>TODO LIST</h1>
|
|
|
+ <ToDoForm />
|
|
|
+ { this.props.items.map( (item, index) => <ToDoItem key={index} item={item} />) }
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+const mapStateToProps = function(store){
|
|
|
+ return {
|
|
|
+ items: store.todo.items
|
|
|
+ };
|
|
|
+}
|
|
|
+ToDoList = connect(mapStateToProps)(ToDoList);
|
|
|
+class App extends Component {
|
|
|
+ render() {
|
|
|
+ return (
|
|
|
+ <div className="App">
|
|
|
+ <Provider store={store} >
|
|
|
+ <ToDoList />
|
|
|
+ </Provider>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+export default App;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+var { Router, Route, IndexRoute, Link, browserHistory } = ReactRouter
|
|
|
+
|
|
|
+var MainLayout = React.createClass({
|
|
|
+ render: function() {
|
|
|
+ return (
|
|
|
+ <div className="app">
|
|
|
+ <header className="primary-header"></header>
|
|
|
+ <aside className="primary-aside">
|
|
|
+ <ul>
|
|
|
+ <li><Link to="/">Home</Link></li>
|
|
|
+ <li><Link to="/users">Users</Link></li>
|
|
|
+ <li><Link to="/widgets">Widgets</Link></li>
|
|
|
+ </ul>
|
|
|
+ </aside>
|
|
|
+ <main>
|
|
|
+ {this.props.children}
|
|
|
+ </main>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+var Home = React.createClass({
|
|
|
+ render: function() {
|
|
|
+ return (<h1>Home Page</h1>)
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+var SearchLayout = React.createClass({
|
|
|
+ render: function() {
|
|
|
+ return (
|
|
|
+ <div className="search">
|
|
|
+ <header className="search-header"></header>
|
|
|
+ <div className="results">
|
|
|
+ {this.props.children}
|
|
|
+ </div>
|
|
|
+ <div className="search-footer pagination"></div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+var UserList = React.createClass({
|
|
|
+ render: function() {
|
|
|
+ return (
|
|
|
+ <ul className="user-list">
|
|
|
+ <li>Dan</li>
|
|
|
+ <li>Ryan</li>
|
|
|
+ <li>Michael</li>
|
|
|
+ </ul>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+var WidgetList = React.createClass({
|
|
|
+ render: function() {
|
|
|
+ return (
|
|
|
+ <ul className="widget-list">
|
|
|
+ <li>Widget 1</li>
|
|
|
+ <li>Widget 2</li>
|
|
|
+ <li>Widget 3</li>
|
|
|
+ </ul>
|
|
|
+ )
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+ReactDOM.render((
|
|
|
+ <Router>
|
|
|
+ <Route path="/" component={MainLayout}>
|
|
|
+ <IndexRoute component={Home} />
|
|
|
+ <Route component={SearchLayout}>
|
|
|
+ <Route path="users" component={UserList} />
|
|
|
+ <Route path="widgets" component={WidgetList} />
|
|
|
+ </Route>
|
|
|
+ </Route>
|
|
|
+ </Router>
|
|
|
+), document.getElementById('root'))
|
|
|
+
|
|
|
+
|