-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(decision-tree-widget): implement Operator Decision Tree widget, …
…add to operators.md
- Loading branch information
Showing
10 changed files
with
681 additions
and
3 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"presets": [ | ||
"es2015" | ||
], | ||
"plugins": [ | ||
"transform-object-rest-spread" | ||
] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "decision-tree-widget", | ||
"version": "1.0.0", | ||
"description": "Interactive widget for a decision tree to assist choosing an RxJS operator", | ||
"scripts": { | ||
"yaml2json": "rm -f src/tree.json && node tools/yaml2json tree.yml ./src/tree.json", | ||
"prebrowserify": "mkdir -p dist", | ||
"browserify": "browserify src/main.js -t babelify --outfile dist/decision-tree-widget.js", | ||
"build": "npm run yaml2json && npm run browserify", | ||
"postbuild": "rm -f src/tree.json" | ||
}, | ||
"dependencies": { | ||
"@motorcycle/core": "^1.1.0", | ||
"@motorcycle/dom": "^1.2.1", | ||
"most": "^0.18.1", | ||
"yaml-js": "^0.1.3" | ||
}, | ||
"devDependencies": { | ||
"babel-plugin-transform-object-rest-spread": "^6.5.0", | ||
"babel-preset-es2015": "^6.5.0", | ||
"babelify": "^7.2.0", | ||
"browserify": "^13.0.0" | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import {just, combine, merge} from 'most'; | ||
import {run} from '@motorcycle/core'; | ||
import {makeDOMDriver, div, h4, p, li, ul, a, span} from '@motorcycle/dom'; | ||
const tree = require('./tree.json'); | ||
|
||
function intent(domSource) { | ||
const chooseOption$ = domSource | ||
.select('.option') | ||
.events('click') | ||
.map(ev => ({ | ||
type: 'CHOOSE_OPTION', | ||
payload: parseInt(ev.currentTarget.dataset.index) | ||
})); | ||
|
||
const undo$ = domSource | ||
.select('.undo') | ||
.events('click') | ||
.map(() => ({ | ||
type: 'UNDO' | ||
})); | ||
|
||
const reset$ = domSource | ||
.select('.reset') | ||
.events('click') | ||
.map(() => ({ | ||
type: 'RESET' | ||
})); | ||
|
||
return merge(chooseOption$, undo$, reset$); | ||
} | ||
|
||
function model(action$) { | ||
const initialState = {tree: tree, current: []}; | ||
|
||
const selectReducer$ = action$ | ||
.filter(action => action.type === 'CHOOSE_OPTION') | ||
.map(action => function (state) { | ||
return { | ||
...state, | ||
current: state.current.concat(action.payload) | ||
}; | ||
}); | ||
|
||
const undoReducer$ = action$ | ||
.filter(action => action.type === 'UNDO') | ||
.map(() => function (state) { | ||
let newCurrent = state.current.slice(); | ||
newCurrent.pop(); | ||
return { | ||
...state, | ||
current: newCurrent | ||
}; | ||
}); | ||
|
||
const resetReducer$ = action$ | ||
.filter(action => action.type === 'RESET') | ||
.map(() => function (state) { | ||
return initialState; | ||
}); | ||
|
||
const reducer$ = merge(selectReducer$, undoReducer$, resetReducer$); | ||
return reducer$.scan((state, reducer) => reducer(state), initialState); | ||
} | ||
|
||
function viewModel(state$) { | ||
return state$.map(state => { | ||
let previous = []; | ||
let currentTree = state.tree; | ||
for (let i = 0; i < state.current.length; i++) { | ||
previous.push(currentTree.children[state.current[i]].label); | ||
currentTree = currentTree.children[state.current[i]]; | ||
} | ||
previous = previous.join(' '); | ||
return { | ||
previous, | ||
options: currentTree.children | ||
} | ||
}); | ||
} | ||
|
||
function renderCurrentSentence(state) { | ||
const WELCOME_SENTENCE = 'Do you need to find an operator for your problem? ' + | ||
'Start by choosing an option from the list below:'; | ||
return p('.current-sentence', [ | ||
!state.previous ? WELCOME_SENTENCE : null, | ||
state.previous ? `"${state.previous}${state.options.length === 1 ? '.' : '...'}"` : null, | ||
state.previous ? span('.undo', '\u21A9\u00A0Undo') : null, | ||
state.previous ? span('.reset', 'Or\u00A0reset') : null | ||
]); | ||
} | ||
|
||
function renderOption(option, index) { | ||
const endString = option.children.length > 1 ? '...' : '.'; | ||
return li('.option', | ||
{attrs: {'data-index': index}}, | ||
`${option.label}${endString}` | ||
); | ||
} | ||
|
||
const OBSERVABLE_PATH = './class/es6/Observable.js~Observable.html'; | ||
|
||
function renderStaticDecision(option) { | ||
const label = option.label.replace('Observable.', ''); | ||
return h4('.decision', [ | ||
'\u00BB You want the static operator ', | ||
a( | ||
{attrs: {href: `${OBSERVABLE_PATH}#static-method-${label}`}}, | ||
label | ||
), | ||
'.' | ||
]); | ||
} | ||
|
||
function renderInstanceDecision(option) { | ||
return h4('.decision', [ | ||
'\u00BB You want the instance operator ', | ||
a( | ||
{attrs: {href: `${OBSERVABLE_PATH}#instance-method-${option.label}`}}, | ||
option.label | ||
), | ||
'.' | ||
]); | ||
} | ||
|
||
function renderItem(option, index) { | ||
if (option.children) { | ||
return renderOption(option, index); | ||
} else if (option.label.match(/^Observable\./)) { | ||
return renderStaticDecision(option); | ||
} else { | ||
return renderInstanceDecision(option); | ||
} | ||
} | ||
|
||
function view(state$) { | ||
return state$.map(state => | ||
div([ | ||
renderCurrentSentence(state), | ||
ul(state.options.map(renderItem)) | ||
]) | ||
); | ||
} | ||
|
||
function main(sources) { | ||
const action$ = intent(sources.DOM); | ||
const state$ = model(action$); | ||
const displayState$ = viewModel(state$); | ||
const vdom$ = view(displayState$); | ||
return { | ||
DOM: vdom$ | ||
}; | ||
} | ||
|
||
window.addEventListener('load', () => { | ||
run(main, { | ||
DOM: makeDOMDriver('.decision-tree-widget') | ||
}); | ||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
var yaml = require('yaml-js'); | ||
var fs = require('fs'); | ||
var inFilename = process.argv[2]; | ||
var outFilename = process.argv[3]; | ||
var yamlContent = fs.readFileSync(inFilename, 'utf8'); | ||
var jsonContent = yaml.load(yamlContent); | ||
fs.writeFileSync(outFilename, JSON.stringify(jsonContent, null, ' '), 'utf8'); |
Oops, something went wrong.