diff options
Diffstat (limited to 'third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app')
5 files changed, 329 insertions, 0 deletions
diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/footer.js b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/footer.js new file mode 100644 index 0000000000..814bcb8976 --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/footer.js @@ -0,0 +1,33 @@ +import cx from 'classnames'; +import { h, Component } from 'preact'; +import { pluralize } from './util'; + +export default class TodoFooter extends Component { + render({ nowShowing, count, completedCount, onClearCompleted }) { + return ( + <footer class="footer"> + <span class="todo-count"> + <strong>{count}</strong> {pluralize(count, 'item')} left + </span> + <ul class="filters"> + <li> + <a href="#/" class={cx({ selected: nowShowing == 'all' })}>All</a> + </li> + {' '} + <li> + <a href="#/active" class={cx({ selected: nowShowing == 'active' })}>Active</a> + </li> + {' '} + <li> + <a href="#/completed" class={cx({ selected: nowShowing == 'completed' })}>Completed</a> + </li> + </ul> + { completedCount > 0 && ( + <button class="clear-completed" onClick={onClearCompleted}> + Clear completed + </button> + ) } + </footer> + ); + } +} diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/index.js b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/index.js new file mode 100644 index 0000000000..64390be17d --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/index.js @@ -0,0 +1,134 @@ +import { h, Component } from 'preact'; +import linkState from 'linkstate'; + +import TodoModel from './model'; +import TodoFooter from './footer'; +import TodoItem from './item'; + +const ENTER_KEY = 13; + +const FILTERS = { + all: todo => true, + active: todo => !todo.completed, + completed: todo => todo.completed +}; + +export default class App extends Component { + constructor() { + super(); + this.model = new TodoModel('preact-todos', () => this.setState({}) ); + addEventListener('hashchange', this.handleRoute.bind(this)); + this.handleRoute(); + } + + handleRoute() { + let nowShowing = String(location.hash || '').split('/').pop(); + if (!FILTERS[nowShowing]) { + nowShowing = 'all'; + } + this.setState({ nowShowing }); + } + + handleNewTodoKeyDown = (e) => { + if (e.keyCode !== ENTER_KEY) return; + e.preventDefault(); + + // let val = ''; + // if (this.state.newTodo === undefined) { + // val = e.target.value.trim(); + // } + + let val = e.target.value.trim(); + + if (val) { + this.model.addTodo(val); + this.setState({ newTodo: '' }); + } + }; + + toggleAll = event => { + let checked = event.target.checked; + this.model.toggleAll(checked); + }; + + toggle = todo => { + this.model.toggle(todo); + }; + + destroy = todo => { + this.model.destroy(todo); + }; + + edit = todo => { + this.setState({ editing: todo.id }); + }; + + save = (todoToSave, text) => { + this.model.save(todoToSave, text); + this.setState({ editing: null }); + }; + + cancel = () => { + this.setState({ editing: null }); + }; + + clearCompleted = () => { + this.model.clearCompleted(); + }; + + render({ }, { nowShowing = ALL_TODOS, newTodo, editing }) { + let { todos } = this.model, + shownTodos = todos.filter( FILTERS[nowShowing] ), + activeTodoCount = todos.reduce( (a, todo) => a + (todo.completed ? 0 : 1), 0), + completedCount = todos.length - activeTodoCount; + + return ( + <div> + <header class="header"> + <h1>todos</h1> + <input + class="new-todo" + placeholder="What needs to be done?" + value={newTodo} + onKeyDown={this.handleNewTodoKeyDown} + onInput={linkState(this, 'newTodo')} + autoFocus={true} + /> + </header> + + { todos.length ? ( + <section class="main"> + <input + class="toggle-all" + type="checkbox" + onChange={this.toggleAll} + checked={activeTodoCount === 0} + /> + <ul class="todo-list"> + { shownTodos.map( todo => ( + <TodoItem + todo={todo} + onToggle={this.toggle} + onDestroy={this.destroy} + onEdit={this.edit} + editing={editing === todo.id} + onSave={this.save} + onCancel={this.cancel} + /> + )) } + </ul> + </section> + ) : null } + + { (activeTodoCount || completedCount) ? ( + <TodoFooter + count={activeTodoCount} + completedCount={completedCount} + nowShowing={nowShowing} + onClearCompleted={this.clearCompleted} + /> + ) : null } + </div> + ); + } +} diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/item.js b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/item.js new file mode 100644 index 0000000000..eed31ba8e1 --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/item.js @@ -0,0 +1,85 @@ +import cx from 'classnames'; +import { h, Component } from 'preact'; + +const ESCAPE_KEY = 27; +const ENTER_KEY = 13; + +export default class TodoItem extends Component { + handleSubmit = () => { + let { onSave, onDestroy, todo } = this.props, + val = this.state.editText.trim(); + if (val) { + onSave(todo, val); + this.setState({ editText: val }); + } + else { + onDestroy(todo); + } + }; + + handleEdit = () => { + let { onEdit, todo } = this.props; + onEdit(todo); + this.setState({ editText: todo.title }); + }; + + toggle = e => { + let { onToggle, todo } = this.props; + onToggle(todo); + e.preventDefault(); + }; + + handleKeyDown = e => { + if (e.which===ESCAPE_KEY) { + let { todo } = this.props; + this.setState({ editText: todo.title }); + this.props.onCancel(todo); + } + else if (e.which===ENTER_KEY) { + this.handleSubmit(); + } + }; + + handleDestroy = () => { + this.props.onDestroy(this.props.todo); + }; + + // shouldComponentUpdate({ todo, editing, editText }) { + // return ( + // todo !== this.props.todo || + // editing !== this.props.editing || + // editText !== this.state.editText + // ); + // } + + componentDidUpdate() { + let node = this.base && this.base.querySelector('.edit'); + if (node) node.focus(); + } + + render({ todo:{ title, completed }, onToggle, onDestroy, editing }, { editText }) { + return ( + <li class={cx({ completed, editing })}> + <div class="view"> + <input + class="toggle" + type="checkbox" + checked={completed} + onChange={this.toggle} + /> + <label onDblClick={this.handleEdit}>{title}</label> + <button class="destroy" onClick={this.handleDestroy} /> + </div> + { editing && ( + <input + class="edit" + value={editText} + onBlur={this.handleSubmit} + onInput={this.linkState('editText')} + onKeyDown={this.handleKeyDown} + /> + ) } + </li> + ); + } +} diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/model.js b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/model.js new file mode 100644 index 0000000000..2c8aae1d51 --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/model.js @@ -0,0 +1,54 @@ +import { uuid, store } from './util'; + +export default class TodoModel { + constructor(key, sub) { + this.key = key; + this.todos = store(key) || []; + this.onChanges = [sub]; + } + + inform() { + store(this.key, this.todos); + this.onChanges.forEach( cb => cb() ); + } + + addTodo(title) { + this.todos = this.todos.concat({ + id: uuid(), + title, + completed: false + }); + this.inform(); + } + + toggleAll(completed) { + this.todos = this.todos.map( + todo => ({ ...todo, completed }) + ); + this.inform(); + } + + toggle(todoToToggle) { + this.todos = this.todos.map( todo => ( + todo !== todoToToggle ? todo : ({ ...todo, completed: !todo.completed }) + ) ); + this.inform(); + } + + destroy(todo) { + this.todos = this.todos.filter( t => t !== todo ); + this.inform(); + } + + save(todoToSave, title) { + this.todos = this.todos.map( todo => ( + todo !== todoToSave ? todo : ({ ...todo, title }) + )); + this.inform(); + } + + clearCompleted() { + this.todos = this.todos.filter( todo => !todo.completed ); + this.inform(); + } +} diff --git a/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/util.js b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/util.js new file mode 100644 index 0000000000..241ec1ad8b --- /dev/null +++ b/third_party/webkit/PerformanceTests/Speedometer/resources/todomvc/architecture-examples/preact/src/app/util.js @@ -0,0 +1,23 @@ +export function uuid() { + let uuid = ''; + for (let i = 0; i < 32; i++) { + let 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; +} + +export function pluralize(count, word) { + return count === 1 ? word : word + 's'; +} + +export function store(namespace, data) { + // if (data) return localStorage[namespace] = JSON.stringify(data); + + // let store = localStorage[namespace]; + // return store && JSON.parse(store) || []; + return []; +} |