WIP: setup code splitting

This commit is contained in:
Kent C. Dodds 2016-07-26 22:33:15 -06:00
parent ead26ccab6
commit 6abc8c04cb
12 changed files with 206 additions and 21 deletions

View File

@ -2,7 +2,8 @@
"presets": [ "presets": [
["es2015", {"modules": false}], ["es2015", {"modules": false}],
"es2016", "es2016",
"stage-2" "stage-2",
"react"
], ],
"env": { "env": {
"test": { "test": {

View File

@ -8,6 +8,7 @@
"kentcdodds/import/possible-errors", "kentcdodds/import/possible-errors",
"kentcdodds/mocha", "kentcdodds/mocha",
"kentcdodds/webpack", "kentcdodds/webpack",
"kentcdodds/react",
], ],
"rules": { "rules": {
// these are only here because I did not // these are only here because I did not

View File

@ -18,6 +18,11 @@
</section> </section>
<footer class="footer"> <footer class="footer">
<span class="todo-count"></span> <span class="todo-count"></span>
<button class="toggle-graph">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32">
<path d="M14 18v-14c-7.732 0-14 6.268-14 14s6.268 14 14 14 14-6.268 14-14c0-2.251-0.532-4.378-1.476-6.262l-12.524 6.262zM28.524 7.738c-2.299-4.588-7.043-7.738-12.524-7.738v14l12.524-6.262z"></path>
</svg>
</button>
<ul class="filters"> <ul class="filters">
<li> <li>
<a href="#/" class="selected">All</a> <a href="#/" class="selected">All</a>
@ -32,6 +37,7 @@
<button class="clear-completed">Clear completed</button> <button class="clear-completed">Clear completed</button>
</footer> </footer>
</section> </section>
<section class="graph-area-container"></section>
<footer class="info"> <footer class="info">
<p>Double-click to edit a todo</p> <p>Double-click to edit a todo</p>
<p>Created by <a href="http://twitter.com/oscargodson">Oscar Godson</a></p> <p>Created by <a href="http://twitter.com/oscargodson">Oscar Godson</a></p>

View File

@ -1,6 +1,11 @@
{ {
"private": true, "private": true,
"dependencies": { "dependencies": {
"d3": "3.5.17",
"lodash": "4.14.1",
"rd3": "0.7.0",
"react": "15.3.0",
"react-dom": "15.3.0",
"todomvc-app-css": "2.0.6" "todomvc-app-css": "2.0.6"
}, },
"devDependencies": { "devDependencies": {
@ -9,6 +14,7 @@
"babel-plugin-__coverage__": "11.0.0", "babel-plugin-__coverage__": "11.0.0",
"babel-preset-es2015": "6.13.2", "babel-preset-es2015": "6.13.2",
"babel-preset-es2016": "6.11.3", "babel-preset-es2016": "6.11.3",
"babel-preset-react": "6.11.1",
"babel-preset-stage-2": "6.13.0", "babel-preset-stage-2": "6.13.0",
"chai": "3.5.0", "chai": "3.5.0",
"css-loader": "0.23.1", "css-loader": "0.23.1",
@ -25,6 +31,7 @@
"mocha": "3.0.1", "mocha": "3.0.1",
"npm-run-all": "2.3.0", "npm-run-all": "2.3.0",
"opt-cli": "1.5.1", "opt-cli": "1.5.1",
"progress-bar-webpack-plugin": "1.9.0",
"rimraf": "2.5.4", "rimraf": "2.5.4",
"style-loader": "0.13.1", "style-loader": "0.13.1",
"webpack": "2.1.0-beta.20", "webpack": "2.1.0-beta.20",

20
src/app.css Normal file
View File

@ -0,0 +1,20 @@
.toggle-graph {
float: left;
margin-left: 16px;
cursor: pointer;
position: relative;
z-index: 1;
}
.toggle-graph svg {
height: 20px;
width: 20px;
}
.toggle-graph svg path {
fill: #777;
}
.toggle-graph.active svg path,
.toggle-graph:hover svg path,
.toggle-graph:focus svg path {
fill: black;
}

View File

@ -1,25 +1,23 @@
import 'todomvc-app-css/index.css' import 'todomvc-app-css/index.css'
import './app.css'
import View from './view' import {$on} from './helpers'
import Controller from './controller' import {updateTodo} from './todo'
import Model from './model' import toggleGraph from './graph'
import Store from './store'
import Template from './template'
/**
* Sets up a brand new Todo list.
*
* @param {string} name The name of your new to do list.
*/
function Todo(name) {
this.storage = new Store(name)
this.model = new Model(this.storage)
this.template = new Template()
this.view = new View(this.template)
this.controller = new Controller(this.model, this.view)
}
export function onLoad() { // eslint-disable-line import/prefer-default-export export function onLoad() { // eslint-disable-line import/prefer-default-export
const todo = new Todo('todos-vanillajs') updateTodo()
todo.controller.setView(document.location.hash) const toggleGraphButton = document.querySelector('.toggle-graph')
$on(
toggleGraphButton,
'click',
() => {
const active = toggleGraph()
if (active) {
toggleGraphButton.classList.add('active')
} else {
toggleGraphButton.classList.remove('active')
}
},
)
} }

View File

@ -250,6 +250,7 @@ Controller.prototype._filter = function(force) {
Controller.prototype._updateFilterState = function(currentPage) { Controller.prototype._updateFilterState = function(currentPage) {
// Store a reference to the active route, allowing us to re-filter todo // Store a reference to the active route, allowing us to re-filter todo
// items as they are marked complete or incomplete. // items as they are marked complete or incomplete.
currentPage = currentPage.split('?')[0]
this._activeRoute = currentPage this._activeRoute = currentPage
if (currentPage === '') { if (currentPage === '') {

48
src/graph/index.js Normal file
View File

@ -0,0 +1,48 @@
import {subscribe, getTodo} from '../todo'
import renderGraph from './render'
let graphArea
const unsubscribe = {
store: null,
todo: null,
}
export default toggleGraph
function toggleGraph() {
if (graphArea) {
graphArea.remove()
graphArea = null
unsubscribe.store()
unsubscribe.todo()
return false
} else {
graphArea = document.createElement('div')
document.body.querySelector('.graph-area-container').appendChild(graphArea)
const {storage} = getTodo()
renderGraph(graphArea, storage)
updateTodoSubscription()
updateStoreSubscription(storage)
return true
}
}
function updateTodoSubscription() {
if (unsubscribe.todo) {
unsubscribe.todo()
}
unsubscribe.todo = subscribe(function onTodoUpdate() {
const {storage} = getTodo()
updateStoreSubscription(storage)
renderGraph(graphArea, storage)
})
}
function updateStoreSubscription(store) {
if (unsubscribe.store) {
unsubscribe.store()
}
unsubscribe.store = store.subscribe(function onStoreUpdate() {
renderGraph(graphArea, store)
})
}

44
src/graph/render.js Normal file
View File

@ -0,0 +1,44 @@
import React from 'react'
import ReactDOM from 'react-dom'
import {PieChart} from 'rd3'
import {chain} from 'lodash'
export default updateGraph
function updateGraph(node, store) {
store.findAll(todos => {
ReactDOM.render(
<Graph todos={todos} />,
node,
)
})
}
function Graph({todos}) {
const data = chain(todos)
.groupBy('completed')
.map((group, complete) => ({
label: complete === 'true' ? 'Complete' : 'Incomplete',
value: Math.round(group.length / todos.length * 10000) / 100
}))
.value()
return (
<div>
There are {todos.length} total todos
<div>
<PieChart
data={data}
width={450}
height={310}
radius={110}
innerRadius={20}
sectorBorderColor="white"
title="Todo Data"
/>
</div>
</div>
)
}
Graph.propTypes = {
todos: React.PropTypes.array,
}

View File

@ -1,3 +1,4 @@
import {remove} from './helpers'
export default Store export default Store
/** /**
@ -23,6 +24,16 @@ function Store(name, callback) {
} }
callback.call(this, JSON.parse(localStorage[name])) callback.call(this, JSON.parse(localStorage[name]))
this.subscribers = []
}
Store.prototype.subscribe = function(subscriber) {
this.subscribers.push(subscriber)
return () => remove(this.subscribers, subscriber)
}
Store.prototype._notify = function() {
this.subscribers.forEach(s => s())
} }
/** /**
@ -102,6 +113,7 @@ Store.prototype.save = function(updateData, callback, id) {
localStorage[this._dbName] = JSON.stringify(data) localStorage[this._dbName] = JSON.stringify(data)
callback.call(this, [updateData]) callback.call(this, [updateData])
} }
this._notify()
} }
/** /**
@ -123,6 +135,7 @@ Store.prototype.remove = function(id, callback) {
localStorage[this._dbName] = JSON.stringify(data) localStorage[this._dbName] = JSON.stringify(data)
callback.call(this, JSON.parse(localStorage[this._dbName]).todos) callback.call(this, JSON.parse(localStorage[this._dbName]).todos)
this._notify()
} }
/** /**
@ -133,4 +146,5 @@ Store.prototype.remove = function(id, callback) {
Store.prototype.drop = function(callback) { Store.prototype.drop = function(callback) {
localStorage[this._dbName] = JSON.stringify({todos: []}) localStorage[this._dbName] = JSON.stringify({todos: []})
callback.call(this, JSON.parse(localStorage[this._dbName]).todos) callback.call(this, JSON.parse(localStorage[this._dbName]).todos)
this._notify()
} }

41
src/todo.js Normal file
View File

@ -0,0 +1,41 @@
import View from './view'
import Controller from './controller'
import Model from './model'
import Store from './store'
import Template from './template'
import {remove} from './helpers'
export {updateTodo, getTodo, subscribe}
let todo
const subscribers = []
/**
* Sets up a brand new Todo list.
*
* @param {string} name The name of your new to do list.
*/
function Todo(name) {
this.storage = new Store(name)
this.model = new Model(this.storage)
this.template = new Template()
this.view = new View(this.template)
this.controller = new Controller(this.model, this.view)
}
function updateTodo() {
todo = new Todo('todos-vanillajs')
todo.controller.setView(document.location.hash)
subscribers.forEach(s => s())
}
function getTodo() {
return todo
}
function subscribe(cb) {
subscribers.push(cb)
return function unsubscribe() {
remove(subscribers, cb)
}
}

View File

@ -1,5 +1,6 @@
/* eslint no-console:"off" */ /* eslint no-console:"off" */
const {resolve} = require('path') const {resolve} = require('path')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const webpackValidator = require('webpack-validator') const webpackValidator = require('webpack-validator')
const {getIfUtils} = require('webpack-config-utils') const {getIfUtils} = require('webpack-config-utils')
@ -21,6 +22,9 @@ module.exports = env => {
{test: /\.css$/, loaders: ['style', 'css']}, {test: /\.css$/, loaders: ['style', 'css']},
], ],
}, },
plugins: [
new ProgressBarPlugin()
],
}) })
if (env.debug) { if (env.debug) {
console.log(config) console.log(config)