chromium/third_party/blink/perf_tests/speedometer21/resources/todomvc/architecture-examples/preact/src/app/index.js

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>
        );
    }
}