Created
July 6, 2016 17:42
-
-
Save anonymous/e7c50b03f3b45101e0ab156542495b49 to your computer and use it in GitHub Desktop.
JS Bin // source http://jsbin.com/jozihiluki
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>JS Bin</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.5.2/redux.min.js"></script> | |
<script src="https://wzrd.in/standalone/deep-freeze"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script> | |
<style> | |
#tabs span { | |
border: 1px solid black; | |
border-radius: 7px 7px 0 0; | |
padding: 3px; | |
cursor: pointer; | |
} | |
#tabs span.active { | |
background-color: lightgray; | |
} | |
#year input { | |
width: 40px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="tabs"></div> | |
<p id="year"> | |
Year: | |
<input type="text" maxlength="4"/> | |
<button>Set</button> | |
</p> | |
<p> | |
<span>foo:</span> | |
<input id="sankey-chart" type="range" min="-50" max="150" value="25"/> | |
<span id="sankey-value"></span> | |
</p> | |
<p> | |
<span>total:</span> | |
<span id="total-value"></span> | |
</p> | |
<script id="jsbin-javascript"> | |
/* jshint esnext: true */ | |
/* jshint asi: true */ | |
/* jshint laxcomma: true */ | |
/* | |
ES6: (state = [], action) => { ... } | |
ES5: function(state, action) { if (!state) state = []; ... } | |
Functions with a single expression using ES6 arrow functions don't need {}: | |
[1,2,3].map(elem => elem * 2) // returns [2,4,6] | |
The const keyword declares a constant. Reusing the same name will throw an error: | |
const foo = 'bar' | |
const foo = 'baz' // throws "SyntaxError: Identifier 'foo' has already been declared" | |
*/ | |
const { createStore, combineReducers } = Redux; | |
const extractId = (id) => parseInt(id.match(/\d+/g)[0]) | |
const sankeyChart = document.getElementById('sankey-chart') | |
, sankeyValue = document.getElementById('sankey-value') | |
, totalValue = document.getElementById('total-value') | |
const defaultYear = document.querySelector('#year input') | |
, setYear = document.querySelector('#year button') | |
//------------------------------------------------------------------------------ | |
var forecastId = 0; | |
const forecastModel = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_MODEL': | |
return state.map(e => { e.active = false; return e }).concat({ | |
id: forecastId++, | |
active: true, | |
defaultYear: action.defaultYear || state[0].defaultYear, | |
drivers: !state[0] ? action.drivers : | |
Object.assign({}, state[0].drivers, action.drivers || {}) | |
}) | |
case 'SELECT_MODEL': | |
return state.map(e => { | |
e.active = e.id === action.id ? true : false | |
return e | |
}) | |
case 'UPDATE_MODEL': | |
const modelIndex = state.findIndex(e => e.active) | |
return state.slice(0, modelIndex) | |
.concat([{ | |
id: state[modelIndex].id, | |
active: true, | |
defaultYear: action.defaultYear || | |
state[modelIndex].defaultYear, | |
drivers: Object.assign( {} | |
, state[0].drivers | |
, action.drivers || {} ) | |
}]) | |
.concat(state.slice(modelIndex + 1)) | |
case 'DELETE_MODEL': | |
return modelIndex === 0 ? state | |
: state.slice(0, modelIndex) | |
.concat(state.slice(modelIndex + 1)) | |
default: | |
return state | |
} | |
} | |
forecastStore = createStore(forecastModel) | |
// render functions | |
const renderTabs = () => { | |
const tabs = forecastStore.getState().map(e => { | |
return `<span id="model${e.id}" class="${e.active ? 'active' : ''}" onClick="selectModel(event)">` + | |
`model${e.id}` + | |
`</span>` | |
}).join() | |
document.getElementById('tabs').innerHTML = tabs | |
} | |
const renderModelValues = (activeModel) => { | |
sankeyChart.value = activeModel.drivers.foo | |
defaultYear.value = activeModel.defaultYear | |
totalValue.innerHTML = activeModel.drivers.total() | |
} | |
const toggleEditYear = (activeModel) => { | |
if (activeModel.id === 0) { | |
setYear.setAttribute('disabled', 'disabled') | |
defaultYear.setAttribute('disabled', 'disabled') | |
} | |
else { | |
setYear.removeAttribute('disabled') | |
defaultYear.removeAttribute('disabled') | |
} | |
} | |
// render the view | |
forecastStore.subscribe(() => { | |
const activeModel = forecastStore.getState().find(e => e.active) | |
renderTabs() | |
renderModelValues(activeModel) | |
toggleEditYear(activeModel) | |
}) | |
// dispatch base model | |
forecastStore.dispatch({ | |
type: 'ADD_MODEL', | |
active: true, | |
defaultYear: 2016, | |
drivers: { | |
foo: 22, | |
total: function() { return this.foo * 3 } | |
} | |
}) | |
function selectModel(event) { | |
const modelId = extractId(event.target.id) | |
forecastStore.dispatch({ | |
type: 'SELECT_MODEL', | |
id: modelId | |
}) | |
} | |
const sankey = (state = sankeyChart.value, action) => { | |
switch (action.type) { | |
case 'SANKEY_DRAG': | |
return action.sankeyValue | |
default: | |
return state | |
} | |
} | |
const sankeyStore = createStore(sankey) | |
sankeyStore.subscribe(() => { | |
sankeyValue.innerHTML = sankeyStore.getState() | |
}) | |
sankeyStore.dispatch({ | |
type: 'SANKEY_DRAG', | |
sankeyValue: sankeyChart.value | |
}) | |
// sankey chart arm dragging | |
sankeyChart.addEventListener('input', (event) => { | |
sankeyStore.dispatch({ | |
type: 'SANKEY_DRAG', | |
sankeyValue: event.target.value | |
}) | |
}) | |
// sankey chart arm let go | |
sankeyChart.addEventListener('mouseup', (event) => { | |
if (forecastStore.getState().find(e => e.active).id === 0) { | |
forecastStore.dispatch({ | |
type: 'ADD_MODEL', | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
} | |
else { | |
forecastStore.dispatch({ | |
type: 'UPDATE_MODEL', | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
} | |
}) | |
setYear.addEventListener('click', (event) => { | |
forecastStore.dispatch({ | |
type: 'UPDATE_MODEL', | |
defaultYear: defaultYear.value, | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
}) | |
/* | |
const todo = (state, action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
return state.id !== action.id ? state : { | |
...state, | |
completed: !state.completed | |
}; | |
default: | |
return state; | |
} | |
}; | |
const todos = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return [ | |
...state, | |
{ | |
id: action.id, | |
text: action.text, | |
completed: false | |
} | |
]; | |
case 'TOGGLE_TODO': | |
return state.map(t => todo(t, action)); | |
default: | |
return state; | |
} | |
} | |
const visibilityFilter = (state = 'SHOW_ALL', action) => { | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
} | |
const todoApp = combineReducers({ | |
todos, | |
visibilityFilter | |
}); | |
const store = createStore(todos); | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id:0, | |
text: 'Learn Redux' | |
}); | |
*/ | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript">/* jshint esnext: true */ | |
/* jshint asi: true */ | |
/* jshint laxcomma: true */ | |
/* | |
ES6: (state = [], action) => { ... } | |
ES5: function(state, action) { if (!state) state = []; ... } | |
Functions with a single expression using ES6 arrow functions don't need {}: | |
[1,2,3].map(elem => elem * 2) // returns [2,4,6] | |
The const keyword declares a constant. Reusing the same name will throw an error: | |
const foo = 'bar' | |
const foo = 'baz' // throws "SyntaxError: Identifier 'foo' has already been declared" | |
*/ | |
const { createStore, combineReducers } = Redux; | |
const extractId = (id) => parseInt(id.match(/\d+/g)[0]) | |
const sankeyChart = document.getElementById('sankey-chart') | |
, sankeyValue = document.getElementById('sankey-value') | |
, totalValue = document.getElementById('total-value') | |
const defaultYear = document.querySelector('#year input') | |
, setYear = document.querySelector('#year button') | |
//------------------------------------------------------------------------------ | |
var forecastId = 0; | |
const forecastModel = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_MODEL': | |
return state.map(e => { e.active = false; return e }).concat({ | |
id: forecastId++, | |
active: true, | |
defaultYear: action.defaultYear || state[0].defaultYear, | |
drivers: !state[0] ? action.drivers : | |
Object.assign({}, state[0].drivers, action.drivers || {}) | |
}) | |
case 'SELECT_MODEL': | |
return state.map(e => { | |
e.active = e.id === action.id ? true : false | |
return e | |
}) | |
case 'UPDATE_MODEL': | |
const modelIndex = state.findIndex(e => e.active) | |
return state.slice(0, modelIndex) | |
.concat([{ | |
id: state[modelIndex].id, | |
active: true, | |
defaultYear: action.defaultYear || | |
state[modelIndex].defaultYear, | |
drivers: Object.assign( {} | |
, state[0].drivers | |
, action.drivers || {} ) | |
}]) | |
.concat(state.slice(modelIndex + 1)) | |
case 'DELETE_MODEL': | |
return modelIndex === 0 ? state | |
: state.slice(0, modelIndex) | |
.concat(state.slice(modelIndex + 1)) | |
default: | |
return state | |
} | |
} | |
forecastStore = createStore(forecastModel) | |
// render functions | |
const renderTabs = () => { | |
const tabs = forecastStore.getState().map(e => { | |
return `<span id="model${e.id}" class="${e.active ? 'active' : ''}" onClick="selectModel(event)">` + | |
`model${e.id}` + | |
`</span>` | |
}).join() | |
document.getElementById('tabs').innerHTML = tabs | |
} | |
const renderModelValues = (activeModel) => { | |
sankeyChart.value = activeModel.drivers.foo | |
defaultYear.value = activeModel.defaultYear | |
totalValue.innerHTML = activeModel.drivers.total() | |
} | |
const toggleEditYear = (activeModel) => { | |
if (activeModel.id === 0) { | |
setYear.setAttribute('disabled', 'disabled') | |
defaultYear.setAttribute('disabled', 'disabled') | |
} | |
else { | |
setYear.removeAttribute('disabled') | |
defaultYear.removeAttribute('disabled') | |
} | |
} | |
// render the view | |
forecastStore.subscribe(() => { | |
const activeModel = forecastStore.getState().find(e => e.active) | |
renderTabs() | |
renderModelValues(activeModel) | |
toggleEditYear(activeModel) | |
}) | |
// dispatch base model | |
forecastStore.dispatch({ | |
type: 'ADD_MODEL', | |
active: true, | |
defaultYear: 2016, | |
drivers: { | |
foo: 22, | |
total: function() { return this.foo * 3 } | |
} | |
}) | |
function selectModel(event) { | |
const modelId = extractId(event.target.id) | |
forecastStore.dispatch({ | |
type: 'SELECT_MODEL', | |
id: modelId | |
}) | |
} | |
const sankey = (state = sankeyChart.value, action) => { | |
switch (action.type) { | |
case 'SANKEY_DRAG': | |
return action.sankeyValue | |
default: | |
return state | |
} | |
} | |
const sankeyStore = createStore(sankey) | |
sankeyStore.subscribe(() => { | |
sankeyValue.innerHTML = sankeyStore.getState() | |
}) | |
sankeyStore.dispatch({ | |
type: 'SANKEY_DRAG', | |
sankeyValue: sankeyChart.value | |
}) | |
// sankey chart arm dragging | |
sankeyChart.addEventListener('input', (event) => { | |
sankeyStore.dispatch({ | |
type: 'SANKEY_DRAG', | |
sankeyValue: event.target.value | |
}) | |
}) | |
// sankey chart arm let go | |
sankeyChart.addEventListener('mouseup', (event) => { | |
if (forecastStore.getState().find(e => e.active).id === 0) { | |
forecastStore.dispatch({ | |
type: 'ADD_MODEL', | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
} | |
else { | |
forecastStore.dispatch({ | |
type: 'UPDATE_MODEL', | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
} | |
}) | |
setYear.addEventListener('click', (event) => { | |
forecastStore.dispatch({ | |
type: 'UPDATE_MODEL', | |
defaultYear: defaultYear.value, | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
}) | |
/* | |
const todo = (state, action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
return state.id !== action.id ? state : { | |
...state, | |
completed: !state.completed | |
}; | |
default: | |
return state; | |
} | |
}; | |
const todos = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return [ | |
...state, | |
{ | |
id: action.id, | |
text: action.text, | |
completed: false | |
} | |
]; | |
case 'TOGGLE_TODO': | |
return state.map(t => todo(t, action)); | |
default: | |
return state; | |
} | |
} | |
const visibilityFilter = (state = 'SHOW_ALL', action) => { | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
} | |
const todoApp = combineReducers({ | |
todos, | |
visibilityFilter | |
}); | |
const store = createStore(todos); | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id:0, | |
text: 'Learn Redux' | |
}); | |
*/</script></body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* jshint esnext: true */ | |
/* jshint asi: true */ | |
/* jshint laxcomma: true */ | |
/* | |
ES6: (state = [], action) => { ... } | |
ES5: function(state, action) { if (!state) state = []; ... } | |
Functions with a single expression using ES6 arrow functions don't need {}: | |
[1,2,3].map(elem => elem * 2) // returns [2,4,6] | |
The const keyword declares a constant. Reusing the same name will throw an error: | |
const foo = 'bar' | |
const foo = 'baz' // throws "SyntaxError: Identifier 'foo' has already been declared" | |
*/ | |
const { createStore, combineReducers } = Redux; | |
const extractId = (id) => parseInt(id.match(/\d+/g)[0]) | |
const sankeyChart = document.getElementById('sankey-chart') | |
, sankeyValue = document.getElementById('sankey-value') | |
, totalValue = document.getElementById('total-value') | |
const defaultYear = document.querySelector('#year input') | |
, setYear = document.querySelector('#year button') | |
//------------------------------------------------------------------------------ | |
var forecastId = 0; | |
const forecastModel = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_MODEL': | |
return state.map(e => { e.active = false; return e }).concat({ | |
id: forecastId++, | |
active: true, | |
defaultYear: action.defaultYear || state[0].defaultYear, | |
drivers: !state[0] ? action.drivers : | |
Object.assign({}, state[0].drivers, action.drivers || {}) | |
}) | |
case 'SELECT_MODEL': | |
return state.map(e => { | |
e.active = e.id === action.id ? true : false | |
return e | |
}) | |
case 'UPDATE_MODEL': | |
const modelIndex = state.findIndex(e => e.active) | |
return state.slice(0, modelIndex) | |
.concat([{ | |
id: state[modelIndex].id, | |
active: true, | |
defaultYear: action.defaultYear || | |
state[modelIndex].defaultYear, | |
drivers: Object.assign( {} | |
, state[0].drivers | |
, action.drivers || {} ) | |
}]) | |
.concat(state.slice(modelIndex + 1)) | |
case 'DELETE_MODEL': | |
return modelIndex === 0 ? state | |
: state.slice(0, modelIndex) | |
.concat(state.slice(modelIndex + 1)) | |
default: | |
return state | |
} | |
} | |
forecastStore = createStore(forecastModel) | |
// render functions | |
const renderTabs = () => { | |
const tabs = forecastStore.getState().map(e => { | |
return `<span id="model${e.id}" class="${e.active ? 'active' : ''}" onClick="selectModel(event)">` + | |
`model${e.id}` + | |
`</span>` | |
}).join() | |
document.getElementById('tabs').innerHTML = tabs | |
} | |
const renderModelValues = (activeModel) => { | |
sankeyChart.value = activeModel.drivers.foo | |
defaultYear.value = activeModel.defaultYear | |
totalValue.innerHTML = activeModel.drivers.total() | |
} | |
const toggleEditYear = (activeModel) => { | |
if (activeModel.id === 0) { | |
setYear.setAttribute('disabled', 'disabled') | |
defaultYear.setAttribute('disabled', 'disabled') | |
} | |
else { | |
setYear.removeAttribute('disabled') | |
defaultYear.removeAttribute('disabled') | |
} | |
} | |
// render the view | |
forecastStore.subscribe(() => { | |
const activeModel = forecastStore.getState().find(e => e.active) | |
renderTabs() | |
renderModelValues(activeModel) | |
toggleEditYear(activeModel) | |
}) | |
// dispatch base model | |
forecastStore.dispatch({ | |
type: 'ADD_MODEL', | |
active: true, | |
defaultYear: 2016, | |
drivers: { | |
foo: 22, | |
total: function() { return this.foo * 3 } | |
} | |
}) | |
function selectModel(event) { | |
const modelId = extractId(event.target.id) | |
forecastStore.dispatch({ | |
type: 'SELECT_MODEL', | |
id: modelId | |
}) | |
} | |
const sankey = (state = sankeyChart.value, action) => { | |
switch (action.type) { | |
case 'SANKEY_DRAG': | |
return action.sankeyValue | |
default: | |
return state | |
} | |
} | |
const sankeyStore = createStore(sankey) | |
sankeyStore.subscribe(() => { | |
sankeyValue.innerHTML = sankeyStore.getState() | |
}) | |
sankeyStore.dispatch({ | |
type: 'SANKEY_DRAG', | |
sankeyValue: sankeyChart.value | |
}) | |
// sankey chart arm dragging | |
sankeyChart.addEventListener('input', (event) => { | |
sankeyStore.dispatch({ | |
type: 'SANKEY_DRAG', | |
sankeyValue: event.target.value | |
}) | |
}) | |
// sankey chart arm let go | |
sankeyChart.addEventListener('mouseup', (event) => { | |
if (forecastStore.getState().find(e => e.active).id === 0) { | |
forecastStore.dispatch({ | |
type: 'ADD_MODEL', | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
} | |
else { | |
forecastStore.dispatch({ | |
type: 'UPDATE_MODEL', | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
} | |
}) | |
setYear.addEventListener('click', (event) => { | |
forecastStore.dispatch({ | |
type: 'UPDATE_MODEL', | |
defaultYear: defaultYear.value, | |
drivers: { | |
foo: parseInt(sankeyChart.value) | |
} | |
}) | |
}) | |
/* | |
const todo = (state, action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
return state.id !== action.id ? state : { | |
...state, | |
completed: !state.completed | |
}; | |
default: | |
return state; | |
} | |
}; | |
const todos = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return [ | |
...state, | |
{ | |
id: action.id, | |
text: action.text, | |
completed: false | |
} | |
]; | |
case 'TOGGLE_TODO': | |
return state.map(t => todo(t, action)); | |
default: | |
return state; | |
} | |
} | |
const visibilityFilter = (state = 'SHOW_ALL', action) => { | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
} | |
const todoApp = combineReducers({ | |
todos, | |
visibilityFilter | |
}); | |
const store = createStore(todos); | |
store.dispatch({ | |
type: 'ADD_TODO', | |
id:0, | |
text: 'Learn Redux' | |
}); | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment