forked from boranton/testcafe-workshop
WIP: setup code splitting
This commit is contained in:
parent
ead26ccab6
commit
6abc8c04cb
3
.babelrc
3
.babelrc
@ -2,7 +2,8 @@
|
|||||||
"presets": [
|
"presets": [
|
||||||
["es2015", {"modules": false}],
|
["es2015", {"modules": false}],
|
||||||
"es2016",
|
"es2016",
|
||||||
"stage-2"
|
"stage-2",
|
||||||
|
"react"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"test": {
|
"test": {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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
20
src/app.css
Normal 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;
|
||||||
|
}
|
||||||
38
src/app.js
38
src/app.js
@ -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')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
48
src/graph/index.js
Normal 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
44
src/graph/render.js
Normal 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,
|
||||||
|
}
|
||||||
14
src/store.js
14
src/store.js
@ -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
41
src/todo.js
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user