diff --git a/example/index.js b/example/index.js index 08fdba8..d33aad6 100644 --- a/example/index.js +++ b/example/index.js @@ -1,385 +1,19 @@ -var Microbounce = require('microbounce') -var Microframe = require('microframe') -var mutate = require('xtend/mutable') -var expose = require('choo-expose') var css = require('sheetify') -var html = require('bel') var choo = require('../') css('todomvc-common/base.css') css('todomvc-app-css/index.css') -// we export this so tests can run -if (module.parent) { - exports.todoStore = todoStore -} else { - var app = choo() - - if (process.env.NODE_ENV !== 'production') { - app.use(require('choo-persist')()) - app.use(require('choo-log')()) - } - app.use(expose()) - app.use(todoStore) - - app.route('/', mainView) - app.route('#active', mainView) - app.route('#completed', mainView) - app.route('*', mainView) - app.mount('body') +var app = choo() +if (process.env.NODE_ENV !== 'production') { + app.use(require('choo-devtools')()) } +app.use(require('./store')) -function mainView (state, emit) { - emit('log:debug', 'Rendering main view') - return html` - -
- ${Header(state, emit)} - ${TodoList(state, emit)} - ${Footer(state, emit)} -
- - - ` -} +app.route('/', require('./view')) +app.route('#active', require('./view')) +app.route('#completed', require('./view')) +app.route('*', require('./view')) -function todoStore (state, emitter) { - // Default values - if (!state.todos) { - state.todos = {} - - state.todos.active = [] - state.todos.done = [] - state.todos.all = [] - - state.todos.idCounter = 0 - } - - // Always reset when application boots - state.todos.input = '' - - // Register emitters after DOM is loaded to speed up DOM loading - emitter.on('DOMContentLoaded', function () { - // CRUD - emitter.on('todos:create', create) - emitter.on('todos:update', update) - emitter.on('todos:delete', del) - - // Special - emitter.on('todos:input', oninput) - - // Shorthand - emitter.on('todos:edit', edit) - emitter.on('todos:unedit', unedit) - emitter.on('todos:toggle', toggle) - emitter.on('todos:toggleAll', toggleAll) - emitter.on('todos:deleteCompleted', deleteCompleted) - }) - - function oninput (text) { - state.todos.input = text - } - - function create (name) { - var item = { - id: state.todos.idCounter, - editing: false, - done: false, - name: name - } - - state.todos.idCounter += 1 - state.todos.active.push(item) - state.todos.all.push(item) - emitter.emit('render') - } - - function edit (id) { - state.todos.all.forEach(function (todo) { - if (todo.id === id) todo.editing = true - }) - emitter.emit('render') - } - - function unedit (id) { - state.todos.all.forEach(function (todo) { - if (todo.id === id) todo.editing = false - }) - emitter.emit('render') - } - - function update (newTodo) { - var todo = state.todos.all.filter(function (todo) { - return todo.id === newTodo.id - })[0] - - if (newTodo.done && todo.done === false) { - state.todos.active.splice(state.todos.active.indexOf(todo), 1) - state.todos.done.push(todo) - } else if (newTodo.done === false && todo.done) { - state.todos.done.splice(state.todos.done.indexOf(todo), 1) - state.todos.active.push(todo) - } - - mutate(todo, newTodo) - emitter.emit('render') - } - - function del (id) { - var i = null - var todo = null - state.todos.all.forEach(function (_todo, j) { - if (_todo.id === id) { - i = j - todo = _todo - } - }) - state.todos.all.splice(i, 1) - - if (todo.done) { - var done = state.todos.done - var doneIndex - done.forEach(function (_todo, j) { - if (_todo.id === id) { - doneIndex = j - } - }) - done.splice(doneIndex, 1) - } else { - var active = state.todos.active - var activeIndex - active.forEach(function (_todo, j) { - if (_todo.id === id) { - activeIndex = j - } - }) - active.splice(activeIndex, 1) - } - emitter.emit('render') - } - - function deleteCompleted (data) { - var done = state.todos.done - done.forEach(function (todo) { - var index = state.todos.all.indexOf(todo) - state.todos.all.splice(index, 1) - }) - state.todos.done = [] - emitter.emit('render') - } - - function toggle (id) { - var todo = state.todos.all.filter(function (todo) { - return todo.id === id - })[0] - var done = todo.done - todo.done = !done - var arr = done ? state.todos.done : state.todos.active - var target = done ? state.todos.active : state.todos.done - var index = arr.indexOf(todo) - arr.splice(index, 1) - target.push(todo) - emitter.emit('render') - } - - function toggleAll (data) { - var todos = state.todos.all - var allDone = state.todos.all.length && - state.todos.done.length === state.todos.all.length - - todos.forEach(function (todo) { - todo.done = !allDone - }) - - if (allDone) { - state.todos.done = [] - state.todos.active = state.todos.all - } else { - state.todos.done = state.todos.all - state.todos.active = [] - } - - emitter.emit('render') - } -} - -function Footer (state, emit) { - var filter = window.location.hash.replace(/^#/, '') - var activeCount = state.todos.active.length - var hasDone = state.todos.done.length - - return html` - - ` - - function filterButton (name, filter, currentFilter, emit) { - var filterClass = filter === currentFilter - ? 'selected' - : '' - - var uri = '#' + name.toLowerCase() - if (uri === '#all') uri = '/' - return html` -
  • - - ${name} - -
  • - ` - } - - function deleteCompleted (emit) { - return html` - - ` - - function deleteAllCompleted () { - emit('todos:deleteCompleted') - } - } -} - -var debounce = Microbounce(256) -var nextFrame = Microframe() -function Header (state, emit) { - return html` -
    -

    todos

    - -
    - ` - - function createTodo (e) { - var value = e.target.value - if (!value) return - if (e.keyCode === 13) { - nextFrame(function () { - emit('todos:input', '') - emit('todos:create', value) - }) - } else { - debounce(function () { - nextFrame(function () { - emit('todos:input', value) - }) - }) - } - } -} - -function TodoItem (todo, emit) { - var clx = classList({ completed: todo.done, editing: todo.editing }) - return html` -
  • -
    - - - -
    - -
  • - ` - - function toggle (e) { - emit('todos:toggle', todo.id) - } - - function edit (e) { - emit('todos:edit', todo.id) - } - - function destroy (e) { - emit('todos:delete', todo.id) - } - - function update (e) { - emit('todos:update', { - id: todo.id, - editing: false, - name: e.target.value - }) - } - - function handleEditKeydown (e) { - if (e.keyCode === 13) update(e) // Enter - else if (e.code === 27) emit('todos:unedit') // Escape - } - - function classList (classes) { - var str = '' - var keys = Object.keys(classes) - for (var i = 0, len = keys.length; i < len; i++) { - var key = keys[i] - var val = classes[key] - if (val) str += (key + ' ') - } - return str - } -} - -function TodoList (state, emit) { - var filter = window.location.hash.replace(/^#/, '') - var items = filter === 'completed' - ? state.todos.done - : filter === 'active' - ? state.todos.active - : state.todos.all - - var allDone = state.todos.done.length === state.todos.all.length - - var nodes = items.map(function (todo) { - return TodoItem(todo, emit) - }) - - return html` -
    - - - -
    - ` - - function toggleAll () { - emit('todos:toggleAll') - } -} +if (module.parent) module.exports = app +else app.mount('body') diff --git a/example/package.json b/example/package.json index 111ca0b..5a4bbcb 100644 --- a/example/package.json +++ b/example/package.json @@ -3,26 +3,19 @@ "version": "1.0.0", "private": true, "scripts": { - "deps": "dependency-check . && dependency-check . --extra --no-dev", - "start": "bankai start --open", - "build": "bankai build --optimize", - "test": "standard && npm run deps && node test.js" + "start": "bankai start", + "build": "bankai build", + "inspect": "bankai inspect", + "test": "standard && node test.js" }, "dependencies": { - "bel": "^4.5.1", - "choo-expose": "^1.0.0", - "choo-log": "^7.0.0-0", - "choo-persist": "^3.0.0", - "microbounce": "^1.0.0", - "microframe": "^1.0.0", + "choo-devtools": "1.7.0", "sheetify": "^6.0.1", "todomvc-app-css": "^2.0.6", - "todomvc-common": "^1.0.3", - "xtend": "^4.0.1" + "todomvc-common": "^1.0.3" }, "devDependencies": { - "bankai": "^5.4.0", - "dependency-check": "^2.8.0", + "bankai": "^9.0.0-rc6", "standard": "^9.0.1" } } diff --git a/example/store.js b/example/store.js new file mode 100644 index 0000000..63f206d --- /dev/null +++ b/example/store.js @@ -0,0 +1,160 @@ +module.exports = todoStore + +function todoStore (state, emitter) { + if (!state.todos) { + state.todos = {} + + state.todos.active = [] + state.todos.done = [] + state.todos.all = [] + + state.todos.idCounter = 0 + } + + // Always reset when application boots + state.todos.input = '' + + // Register emitters after DOM is loaded to speed up DOM loading + emitter.on('DOMContentLoaded', function () { + // CRUD + emitter.on('todos:create', create) + emitter.on('todos:update', update) + emitter.on('todos:delete', del) + + // Special + emitter.on('todos:input', oninput) + + // Shorthand + emitter.on('todos:edit', edit) + emitter.on('todos:unedit', unedit) + emitter.on('todos:toggle', toggle) + emitter.on('todos:toggleAll', toggleAll) + emitter.on('todos:deleteCompleted', deleteCompleted) + }) + + function oninput (text) { + state.todos.input = text + } + + function create (name) { + var item = { + id: state.todos.idCounter, + editing: false, + done: false, + name: name + } + + state.todos.idCounter += 1 + state.todos.active.push(item) + state.todos.all.push(item) + emitter.emit('render') + } + + function edit (id) { + state.todos.all.forEach(function (todo) { + if (todo.id === id) todo.editing = true + }) + emitter.emit('render') + } + + function unedit (id) { + state.todos.all.forEach(function (todo) { + if (todo.id === id) todo.editing = false + }) + emitter.emit('render') + } + + function update (newTodo) { + var todo = state.todos.all.filter(function (todo) { + return todo.id === newTodo.id + })[0] + + if (newTodo.done && todo.done === false) { + state.todos.active.splice(state.todos.active.indexOf(todo), 1) + state.todos.done.push(todo) + } else if (newTodo.done === false && todo.done) { + state.todos.done.splice(state.todos.done.indexOf(todo), 1) + state.todos.active.push(todo) + } + + Object.assign(todo, newTodo) + emitter.emit('render') + } + + function del (id) { + var i = null + var todo = null + state.todos.all.forEach(function (_todo, j) { + if (_todo.id === id) { + i = j + todo = _todo + } + }) + state.todos.all.splice(i, 1) + + if (todo.done) { + var done = state.todos.done + var doneIndex + done.forEach(function (_todo, j) { + if (_todo.id === id) { + doneIndex = j + } + }) + done.splice(doneIndex, 1) + } else { + var active = state.todos.active + var activeIndex + active.forEach(function (_todo, j) { + if (_todo.id === id) { + activeIndex = j + } + }) + active.splice(activeIndex, 1) + } + emitter.emit('render') + } + + function deleteCompleted (data) { + var done = state.todos.done + done.forEach(function (todo) { + var index = state.todos.all.indexOf(todo) + state.todos.all.splice(index, 1) + }) + state.todos.done = [] + emitter.emit('render') + } + + function toggle (id) { + var todo = state.todos.all.filter(function (todo) { + return todo.id === id + })[0] + var done = todo.done + todo.done = !done + var arr = done ? state.todos.done : state.todos.active + var target = done ? state.todos.active : state.todos.done + var index = arr.indexOf(todo) + arr.splice(index, 1) + target.push(todo) + emitter.emit('render') + } + + function toggleAll (data) { + var todos = state.todos.all + var allDone = state.todos.all.length && + state.todos.done.length === state.todos.all.length + + todos.forEach(function (todo) { + todo.done = !allDone + }) + + if (allDone) { + state.todos.done = [] + state.todos.active = state.todos.all + } else { + state.todos.done = state.todos.all + state.todos.active = [] + } + + emitter.emit('render') + } +} diff --git a/example/test.js b/example/test.js index dd2049a..116e220 100644 --- a/example/test.js +++ b/example/test.js @@ -2,8 +2,7 @@ var EventEmitter = require('events').EventEmitter var spok = require('spok') var tape = require('tape') -var example = require('./') -var todoStore = example.todoStore +var todoStore = require('./store') tape('should initialize empty state', function (t) { var emitter = new EventEmitter() diff --git a/example/view.js b/example/view.js new file mode 100644 index 0000000..9f120b5 --- /dev/null +++ b/example/view.js @@ -0,0 +1,190 @@ +var html = require('bel') // cannot require choo/html because it's a nested repo + +module.exports = mainView + +function mainView (state, emit) { + emit('log:debug', 'Rendering main view') + return html` + +
    + ${Header(state, emit)} + ${TodoList(state, emit)} + ${Footer(state, emit)} +
    + + + ` +} + +function Header (state, emit) { + return html` +
    +

    todos

    + +
    + ` + + function createTodo (e) { + var value = e.target.value + if (!value) return + if (e.keyCode === 13) { + emit('todos:input', '') + emit('todos:create', value) + } else { + emit('todos:input', value) + } + } +} + +function Footer (state, emit) { + var filter = state.href.replace(/^\//, '') || '' + var activeCount = state.todos.active.length + var hasDone = state.todos.done.length + + return html` + + ` + + function filterButton (name, filter, currentFilter, emit) { + var filterClass = filter === currentFilter + ? 'selected' + : '' + + var uri = '#' + name.toLowerCase() + if (uri === '#all') uri = '/' + return html` +
  • + + ${name} + +
  • + ` + } + + function deleteCompleted (emit) { + return html` + + ` + + function deleteAllCompleted () { + emit('todos:deleteCompleted') + } + } +} + +function TodoItem (todo, emit) { + var clx = classList({ completed: todo.done, editing: todo.editing }) + return html` +
  • +
    + + + +
    + +
  • + ` + + function toggle (e) { + emit('todos:toggle', todo.id) + } + + function edit (e) { + emit('todos:edit', todo.id) + } + + function destroy (e) { + emit('todos:delete', todo.id) + } + + function update (e) { + emit('todos:update', { + id: todo.id, + editing: false, + name: e.target.value + }) + } + + function handleEditKeydown (e) { + if (e.keyCode === 13) update(e) // Enter + else if (e.code === 27) emit('todos:unedit') // Escape + } + + function classList (classes) { + var str = '' + var keys = Object.keys(classes) + for (var i = 0, len = keys.length; i < len; i++) { + var key = keys[i] + var val = classes[key] + if (val) str += (key + ' ') + } + return str + } +} + +function TodoList (state, emit) { + var filter = state.href.replace(/^\//, '') || '' + var items = filter === 'completed' + ? state.todos.done + : filter === 'active' + ? state.todos.active + : state.todos.all + + var allDone = state.todos.done.length === state.todos.all.length + + var nodes = items.map(function (todo) { + return TodoItem(todo, emit) + }) + + return html` +
    + + + +
    + ` + + function toggleAll () { + emit('todos:toggleAll') + } +}