diff options
Diffstat (limited to 'third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js')
4 files changed, 407 insertions, 0 deletions
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/app.jsx b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/app.jsx new file mode 100644 index 0000000000..1e1e2a3e12 --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/app.jsx @@ -0,0 +1,207 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark:false */ +/*jshint white:false */ +/*jshint trailing:false */ +/*jshint newcap:false */ +/*global Utils, ALL_TODOS, ACTIVE_TODOS, COMPLETED_TODOS, + TodoItem, TodoFooter, React, Router*/ + +(function (window, React) { + 'use strict'; + + window.ALL_TODOS = 'all'; + window.ACTIVE_TODOS = 'active'; + window.COMPLETED_TODOS = 'completed'; + + var ENTER_KEY = 13; + + var TodoApp = React.createClass({ + getInitialState: function () { + var todos = Utils.store('react-todos'); + return { + todos: todos, + nowShowing: ALL_TODOS, + editing: null + }; + }, + + componentDidMount: function () { + 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}) + }); + router.init(); + this.refs.newField.getDOMNode().focus(); + }, + + handleNewTodoKeyDown: function (event) { + if (event.which !== ENTER_KEY) { + return; + } + + var val = this.refs.newField.getDOMNode().value.trim(); + var todos; + var newTodo; + + if (val) { + todos = this.state.todos; + newTodo = { + id: Utils.uuid(), + title: val, + completed: false + }; + this.setState({todos: todos.concat([newTodo])}); + this.refs.newField.getDOMNode().value = ''; + } + + return false; + }, + + toggleAll: function (event) { + var checked = event.target.checked; + + this.state.todos.forEach(function (todo) { + todo.completed = checked; + }); + + this.setState({todos: this.state.todos}); + }, + + toggle: function (todo) { + todo.completed = !todo.completed; + this.setState({todos: this.state.todos}); + }, + + destroy: function (todo) { + var newTodos = this.state.todos.filter(function (candidate) { + return candidate.id !== todo.id; + }); + + this.setState({todos: newTodos}); + }, + + edit: function (todo, callback) { + // refer to todoItem.js `handleEdit` for the reasoning behind the + // callback + this.setState({editing: todo.id}, function () { + callback(); + }); + }, + + save: function (todo, text) { + todo.title = text; + this.setState({todos: this.state.todos, editing: null}); + }, + + cancel: function () { + this.setState({editing: null}); + }, + + clearCompleted: function () { + var newTodos = this.state.todos.filter(function (todo) { + return !todo.completed; + }); + + this.setState({todos: newTodos}); + }, + + componentDidUpdate: function () { + Utils.store('react-todos', this.state.todos); + }, + + render: function () { + var footer = null; + var main = null; + var todoItems = {}; + var activeTodoCount; + var completedCount; + + var shownTodos = this.state.todos.filter(function (todo) { + switch (this.state.nowShowing) { + case ACTIVE_TODOS: + return !todo.completed; + case COMPLETED_TODOS: + return todo.completed; + default: + return true; + } + }.bind(this)); + + shownTodos.forEach(function (todo) { + todoItems[todo.id] = ( + <TodoItem + 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={this.cancel} + /> + ); + }.bind(this)); + + activeTodoCount = this.state.todos.filter(function (todo) { + return !todo.completed; + }).length; + + completedCount = this.state.todos.length - activeTodoCount; + + if (activeTodoCount || completedCount) { + footer = + <TodoFooter + count={activeTodoCount} + completedCount={completedCount} + nowShowing={this.state.nowShowing} + onClearCompleted={this.clearCompleted} + />; + } + + if (this.state.todos.length) { + main = ( + <section id="main"> + <input + id="toggle-all" + type="checkbox" + onChange={this.toggleAll} + checked={activeTodoCount === 0} + /> + <ul id="todo-list"> + {todoItems} + </ul> + </section> + ); + } + + return ( + <div> + <header id="header"> + <h1>todos</h1> + <input + ref="newField" + id="new-todo" + placeholder="What needs to be done?" + onKeyDown={this.handleNewTodoKeyDown} + /> + </header> + {main} + {footer} + </div> + ); + } + }); + + React.renderComponent(<TodoApp />, document.getElementById('todoapp')); + 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); diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/footer.jsx b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/footer.jsx new file mode 100644 index 0000000000..5a896553da --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/footer.jsx @@ -0,0 +1,58 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark:false */ +/*jshint white:false */ +/*jshint trailing:false */ +/*jshint newcap:false */ +/*global React, ALL_TODOS, ACTIVE_TODOS, Utils, COMPLETED_TODOS */ +(function (window) { + 'use strict'; + + window.TodoFooter = React.createClass({ + render: function () { + var activeTodoWord = Utils.pluralize(this.props.count, 'item'); + var clearButton = null; + + if (this.props.completedCount > 0) { + clearButton = ( + <button + id="clear-completed" + onClick={this.props.onClearCompleted}> + {''}Clear completed ({this.props.completedCount}){''} + </button> + ); + } + + var show = { + ALL_TODOS: '', + ACTIVE_TODOS: '', + COMPLETED_TODOS: '' + }; + show[this.props.nowShowing] = 'selected'; + + return ( + <footer id="footer"> + <span id="todo-count"> + <strong>{this.props.count}</strong> + {' '}{activeTodoWord}{' '}left{''} + </span> + <ul id="filters"> + <li> + <a href="#/" class={show[ALL_TODOS]}>All</a> + </li> + {' '} + <li> + <a href="#/active" class={show[ACTIVE_TODOS]}>Active</a> + </li> + {' '} + <li> + <a href="#/completed" class={show[COMPLETED_TODOS]}>Completed</a> + </li> + </ul> + {clearButton} + </footer> + ); + } + }); +})(window); diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/todoItem.jsx b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/todoItem.jsx new file mode 100644 index 0000000000..87f6c1c6b3 --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/todoItem.jsx @@ -0,0 +1,93 @@ +/** + * @jsx React.DOM + */ +/*jshint quotmark: false */ +/*jshint white: false */ +/*jshint trailing: false */ +/*jshint newcap: false */ +/*global React, Utils */ +(function (window) { + 'use strict'; + + var ESCAPE_KEY = 27; + var ENTER_KEY = 13; + + window.TodoItem = React.createClass({ + handleSubmit: function () { + var val = this.state.editText.trim(); + if (val) { + this.props.onSave(val); + this.setState({editText: val}); + } else { + this.props.onDestroy(); + } + return false; + }, + handleEdit: function () { + // react optimizes renders by batching them. This means you can't call + // parent's `onEdit` (which in this case triggeres a re-render), and + // immediately manipulate the DOM as if the rendering's over. Put it as a + // callback. Refer to app.js' `edit` method + this.props.onEdit(function () { + var node = this.refs.editField.getDOMNode(); + node.focus(); + node.setSelectionRange(node.value.length, node.value.length); + }.bind(this)); + }, + + handleKeyDown: function (event) { + if (event.keyCode === ESCAPE_KEY) { + this.setState({editText: this.props.todo.title}); + this.props.onCancel(); + } else if (event.keyCode === ENTER_KEY) { + this.handleSubmit(); + } else { + this.setState({editText: event.target.value}); + } + }, + + handleChange: function (event) { + this.setState({editText: event.target.value}); + }, + + getInitialState: function () { + return {editText: this.props.todo.title}; + }, + + componentWillReceiveProps: function (nextProps) { + if (nextProps.todo.title !== this.props.todo.title) { + this.setState(this.getInitialState()); + } + }, + + render: function () { + return ( + <li class={Utils.stringifyObjKeys({ + completed: this.props.todo.completed, + editing: this.props.editing + })}> + <div class="view"> + <input + class="toggle" + type="checkbox" + checked={this.props.todo.completed ? 'checked' : null} + onChange={this.props.onToggle} + /> + <label onDoubleClick={this.handleEdit}> + {this.props.todo.title} + </label> + <button class='destroy' onClick={this.props.onDestroy} /> + </div> + <input + ref="editField" + class="edit" + value={this.state.editText} + onBlur={this.handleSubmit} + onChange={this.handleChange} + onKeyDown={this.handleKeyDown} + /> + </li> + ); + } + }); +})(window); diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/utils.jsx b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/utils.jsx new file mode 100644 index 0000000000..3ce698563a --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/labs/architecture-examples/react/js/utils.jsx @@ -0,0 +1,49 @@ +(function (window) { + 'use strict'; + + window.Utils = { + uuid: function () { + /*jshint bitwise:false */ + var i, random; + var uuid = ''; + + for (i = 0; i < 32; i++) { + random = Math.random() * 16 | 0; + if (i === 8 || i === 12 || i === 16 || i === 20) { + uuid += '-'; + } + uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)) + .toString(16); + } + + return uuid; + }, + + pluralize: function (count, word) { + return count === 1 ? word : word + 's'; + }, + + store: function (namespace, data) { + if (data) { + return localStorage.setItem(namespace, JSON.stringify(data)); + } + + var store = localStorage.getItem(namespace); + return (store && JSON.parse(store)) || []; + }, + + stringifyObjKeys: function (obj) { + var s = ''; + var key; + + for (key in obj) { + if (obj.hasOwnProperty(key) && obj[key]) { + s += key + ' '; + } + } + + return s.trim(); + } + }; + +})(window); |