fleshing out main functionality

This commit is contained in:
Adam Brown 2016-11-28 12:47:47 -05:00
parent afce2f47a4
commit dc96ac104c
17 changed files with 699 additions and 965 deletions

12
bower.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "dubdiff",
"version": "2.0.1",
"dependencies": {
"jquery": "~1.11.0",
"bootstrap": "~3.1.1",
"bootstrap-sass-official": "~3.1.1",
"font-awesome": ">=4.1.0"
},
"devDependencies": {
}
}

1154
dist/browser-bundle.js vendored

File diff suppressed because one or more lines are too long

18
dist/old-dubdiff.css vendored
View File

@ -1,4 +1,22 @@
@charset "UTF-8";
/*
.hero-unit {
color: #fff2dc;
background: #fea539 !important;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open > .btn-primary.dropdown-toggle {
color: #fff;
background-color: #688bb5 !important;
border-color: #08246b !important; }
.btn-primary {
color: #fff;
background-color: #587ba5 !important;
border-color: #08246b !important; }
*/
/*! normalize.css v3.0.1 | MIT License | git.io/normalize */
html {
font-family: sans-serif;

View File

@ -4,18 +4,24 @@
"description": "",
"main": "index.js",
"scripts": {
"build": "npm run webpack --colors",
"start": "npm run webpack --progress --colors --watch",
"deploy": "git subtree push --prefix dist origin gh-pages",
"build": "ebpack --colors",
"start": "webpack --progress --colors --watch",
"serve": "node src/server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "BSD-2-Clause",
"dependencies": {
"babel-preset-es2015-mod": "^6.6.0",
"babel-preset-es3": "^1.0.1",
"diff": "^3.0.1",
"express": "^4.14.0",
"react": "^0.14.5",
"react-dom": "^0.14.5",
"react-redux": "^4.4.6",
"react-router": "^1.0.0",
"redux": "^3.5.1",
"redux-router": "^1.0.0-beta5",
"reselect": "^2.5.1"
},
"devDependencies": {

View File

@ -1,3 +1,10 @@
export const updateOriginalInput = (data) => ({ type: 'UPDATE_ORIGINAL_INPUT', data})
export const updateFinalInput = (data) => ({ type: 'UPDATE_FINAL_INPUT', data})
export const resetInput = () => ({ type: 'RESET_INPUT' })
export const updateOriginalInput = (text) => ({ type: 'UPDATE_ORIGINAL_INPUT', data:text})
export const updateFinalInput = (text) => ({ type: 'UPDATE_FINAL_INPUT', data:text})
export const setPlaintextFormat = () => ({ type: 'SET_PLAINTEXT_FORMAT'})
export const setMarkdownFormat = () => ({ type: 'SET_MARKDOWN_FORMAT'})
export const showOriginal = () => ({ type: 'SHOW_ORIGINAL'})
export const showFinal = () => ({ type: 'SHOW_FINAL'})
export const showDifference = () => ({ type: 'SHOW_DIFFERENCE'})

View File

@ -9,13 +9,18 @@ import * as localStore from './localStore'
import * as reducers from './reducers'
import Main from './components/Main'
import Compare from './components/Compare'
import { Router } from 'react-router'
import createBrowserHistory from 'history/lib/createBrowserHistory'
import {Route, IndexRoute, Redirect } from 'react-router'
//create the redux store
//initial state is retrieved from localStore
const store = Redux.createStore(
Redux.combineReducers(reducers),
localStore.get(),
localStore.get("dubdiff"),
window.devToolsExtension ? window.devToolsExtension() : undefined
)
@ -23,7 +28,7 @@ const store = Redux.createStore(
function saveState() {
let state = store.getState()
//pass the elements of state that should be persisted to the local store as an array of element name strings
localStore.set(state, [])
localStore.set(state, ["input"], "dubdiff")
}
store.subscribe(saveState)
@ -31,7 +36,10 @@ store.subscribe(saveState)
function render() {
ReactDOM.render(
<Provider store={store}>
<Main/>
<Router history={createBrowserHistory()}>
<Route path="/" component={Main} />
<Route path="/**" component={Compare}/>
</Router>
</Provider>
, document.getElementById('root'))
}

85
src/components/Compare.js Normal file
View File

@ -0,0 +1,85 @@
import React from 'react'
import {connect} from 'react-redux'
import * as Actions from '../actions'
import * as Selectors from '../selectors'
import Header from './Header'
import CompareControls from './CompareControls'
import Show from './Show'
const mapStateToProps = (state) => ({
isMarkdownFormat: Selectors.isMarkdownFormat(state),
isShowOriginal: Selectors.isShowOriginal(state),
isShowFinal: Selectors.isShowFinal(state),
isShowDifference: Selectors.isShowDifference(state),
safeInput: Selectors.safeInput(state),
diff: Selectors.diff(state)
})
const mapDispatchToProps = dispatch => ({
})
class Compare extends React.Component {
render() {
console.log(this.props.safeInput)
return (
<div>
<Header/>
<div className="container">
<form className="row">
<div className="col-md-2 col-sm-12">
<CompareControls/>
</div>
<div className="col-md-10 col-sm-12 content-well">
{ this.props.isShowDifference ?
<div>{this.props.diff}</div>:
<Show
text={this.props.isShowOriginal? this.props.safeInput.original: this.props.safeInput.final}
isMarkdownFormat={this.props.isMarkdownFormat}
/>
}
</div>
</form>
</div>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Compare)
/* <div ng-if="isMarkdownFormat">
<div ng-show="isShowBefore" class="col-md-10 col-sm-12 content-well">
<div btf-markdown="before" class="before">
</div>
</div>
<div ng-show="isShowWdiff" class="col-md-10 col-sm-12 content-well">
<div btf-markdown="wdiff" class="wdiff">
</div>
</div>
<div ng-show="isShowAfter" class="col-md-10 col-sm-12 content-well">
<div btf-markdown="after" class="after">
</div>
</div>
</div>
<div ng-if="!isMarkdownFormat">
<div ng-show="isShowBefore" class="col-md-10 col-sm-12 content-well">
<div ng-bind-html="before" class="content-pre before"></div>
</div>
<div ng-show="isShowWdiff" class="col-md-10 col-sm-12 content-well">
<div ng-bind-html="wdiff" class="content-pre wdiff"></div>
</div>
<div ng-show="isShowAfter" class="col-md-10 col-sm-12 content-well">
<div ng-bind-html="after" class="content-pre after"></div>
</div>
</div>
*/

View File

@ -0,0 +1,59 @@
import React from 'react'
import {connect} from 'react-redux'
import {Link} from 'react-router'
import * as Actions from '../actions'
import * as Selectors from '../selectors'
const mapStateToProps = (state) => ({
isMarkdownFormat: Selectors.isMarkdownFormat(state),
isShowOriginal: Selectors.isShowOriginal(state),
isShowFinal: Selectors.isShowFinal(state),
isShowDifference: Selectors.isShowDifference(state),
})
const mapDispatchToProps = dispatch => ({
onSetPlaintextFormat: () => dispatch(Actions.setPlaintextFormat()),
onSetMarkdownFormat: () => dispatch(Actions.setMarkdownFormat()),
onShowOriginal: () => dispatch(Actions.showOriginal()),
onShowFinal: () => dispatch(Actions.showFinal()),
onShowDifference: () => dispatch(Actions.showDifference()),
})
class CompareControls extends React.Component {
onClickCompare() {
}
onClickMarkdownFormat() {
if (this.props.isMarkdownFormat)
this.props.onSetPlaintextFormat()
else
this.props.onSetMarkdownFormat()
}
render() {
return (
<div className="form-group">
<div className="controls well btn-group col-lg-12">
<a type="submit" onClick={this.props.onShowOriginal} className={(this.props.isShowOriginal?'active ':'')+'btn btn-block btn-primary'}>Original</a>
<a type="submit" onClick={this.props.onShowFinal} className={(this.props.isShowFinal?'active ':'')+'btn btn-block btn-primary'}>Final</a>
<a type="submit" onClick={this.props.onShowDifference} className={(this.props.isShowDifference?'active ':'')+'btn btn-block btn-primary'}>Difference</a>
</div>
<div className="controls well btn-group col-lg-12">
<a className={(this.props.isMarkdownFormat ? "active " : "")+"btn btn-block btn-primary"} type="submit" onClick={this.onClickMarkdownFormat.bind(this)}>
<span className={(this.props.isMarkdownFormat ? "glyphicon-ok " : "") + "glyphicon"}></span>
&nbsp; As Markdown
</a>
</div>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CompareControls)

16
src/components/Header.js Normal file
View File

@ -0,0 +1,16 @@
import React from 'react'
import {Link} from 'react-router'
const Header = (props) => (
<nav>
<header id="banner" className="hero-unit">
<div className="container">
<h1><Link to="/">dubdiff</Link></h1>
<h3></h3>
</div>
</header>
</nav>
)
export default Header

View File

@ -5,15 +5,18 @@ import {connect} from 'react-redux'
import * as Actions from '../actions'
import * as Selectors from '../selectors'
import Header from './Header'
import MainControls from './MainControls'
const mapStateToProps = (state) => ({
input: state.input,
input: state.input,
safeInput: Selectors.safeInput(state),
})
const mapDispatchToProps = dispatch => ({
onChangeOriginal: (text) => dispatch(Actions.updateOriginalInput(text)),
onChangeFinal: (text) => dispatch(Actions.updateFinalInput(text))
onChangeFinal: (text) => dispatch(Actions.updateFinalInput(text)),
})
@ -21,70 +24,31 @@ class Main extends React.Component {
constructor() {
super()
}
render () {
return (
<div class="container">
<form class="row">
<div class="col-md-2 col-sm-12 form-group">
<div class="controls well col-lg-12">
<a type="button" ng-click="compare()" class="btn btn-block btn-primary">compare</a>
<div>
<Header/>
<div className="container">
<form className="row">
<div className="col-md-2 col-sm-12">
<MainControls/>
</div>
<div class="controls well btn-group col-lg-12">
<a ng-class="{&quot;active&quot;: isMarkdownFormat}" type="submit" ng-click="toggleMarkdownFormat()" class="btn btn-block btn-primary active">
<span ng-class="{&quot;glyphicon-ok&quot;: isMarkdownFormat}" class="glyphicon glyphicon-ok"></span>
&nbsp; As Markdown
</a>
<div className="col-lg-5 col-sm-12 form-group">
<label htmlFor="docA">Original</label>
<textarea id="docA" value={this.props.input.original} onChange={event => this.props.onChangeOriginal(event.target.value)} className="form-control"></textarea>
</div>
</div>
<div class="col-lg-5 col-sm-12 form-group">
<label for="docA">Original</label>
<textarea id="docA" ng-model="docA" class="form-control"></textarea>
</div>
<div class="col-lg-5 col-sm-12 form-group">
<label for="docB">Final</label>
<textarea id="docB" ng-model="docB" class="form-control"></textarea>
</div>
</form>
<div className="col-lg-5 col-sm-12 form-group">
<label htmlFor="docB">Final</label>
<textarea id="docB" value={this.props.input.final} onChange={event => this.props.onChangeFinal(event.target.value)} className="form-control"></textarea>
</div>
</form>
</div>
</div>
)
}
}
function renderDate(props) {
return <div>
<DateInputForm/>
<div>
<p><strong>Data for {props.formattedDate}</strong></p>
<DateResultsSummary/>
<DateResultsChart/>
</div>
</div>
}
function renderMonthly(props) {
return <div>
<p><strong>Yearly Data</strong></p>
<MonthlyResultsSummary/>
<MonthlyResultsChart/>
</div>
}
export default connect(mapStateToProps, mapDispatchToProps)(Main)
/*
{this.props.view == "RESULTS" ?
(this.props.inputIsValid.valid ?
<div>
<p><strong>{this.props.formattedDate}</strong></p>
<DateResultsSummary/>
<DateResultsTable/>
</div> :
<InvalidResults/>
):
*/

View File

@ -0,0 +1,53 @@
import React from 'react'
import {connect} from 'react-redux'
import {Link} from 'react-router'
import * as Actions from '../actions'
import * as Selectors from '../selectors'
const mapStateToProps = (state) => ({
format: state.format,
isMarkdownFormat: Selectors.isMarkdownFormat(state)
})
const mapDispatchToProps = dispatch => ({
onSetPlaintextFormat: (format) => dispatch(Actions.setPlaintextFormat()),
onSetMarkdownFormat: (format) => dispatch(Actions.setMarkdownFormat())
})
class MainControls extends React.Component {
onClickCompare() {
}
onClickMarkdownFormat() {
if (this.props.isMarkdownFormat)
this.props.onSetPlaintextFormat()
else
this.props.onSetMarkdownFormat()
}
render() {
return (
<div className="form-group">
<div className="controls well col-lg-12">
<Link to="compare" className="btn btn-block btn-primary">Compare</Link>
</div>
<div className="controls well btn-group col-lg-12">
<a className={(this.props.isMarkdownFormat ? "active " : "")+"btn btn-block btn-primary"} type="submit" onClick={this.onClickMarkdownFormat.bind(this)}>
<span className={(this.props.isMarkdownFormat ? "glyphicon-ok " : "") + "glyphicon"}></span>
&nbsp; As Markdown
</a>
</div>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MainControls)
/*
<a type="button" onClick={this.onClickCompare.bind(this)} className="btn btn-block btn-primary">compare</a>*/

11
src/components/Show.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react'
const Show = (props) => (
<div>
<pre>
{props.text}
</pre>
</div>
)
export default Show

View File

@ -1,7 +1,10 @@
export const get = () => JSON.parse(localStorage.getItem('state')) || undefined;
let stateName = (suffix) => 'state'+(suffix?suffix:"")
export function set (state, props) {
export const get = (suffix) => JSON.parse(localStorage.getItem(stateName(suffix))) || undefined;
export function set (state, props, suffix) {
let toSave = {}
props.forEach(p => toSave[p] = state[p])
localStorage.setItem('state', JSON.stringify(toSave))
}
localStorage.setItem(stateName(suffix), JSON.stringify(toSave))
}

View File

@ -1,40 +1,50 @@
export function textInput (state, action ) {
export function input (state, action ) {
switch (action.type) {
case 'UPDATE_ORIGINAL_INPUT':
return Object.assign({}, state, {original:action.data})
case 'UPDATE_FINAL_INPUT':
return Object.assign({}, state, {final:action.data})
case 'RESET_INPUT':
return {original:'', final:''}
default:
return state || {original:"", final:""}
return state || {original:'', final:''}
}
}
export function locationInput (state, action) {
switch (action.type) {
case 'UPDATE_LOCATION_INPUT':
return Object.assign({}, state, action.data)
default:
return state || {latitude:"0", longitude:"0"}
}
export const Format = {
PLAINTEXT: 'PLAINTEXT',
MARKDOWN: 'MARKDOWN'
}
export function dateInput (state, action) {
export function format (state, action) {
switch (action.type) {
case 'UPDATE_DATE_INPUT':
return action.data
case 'SET_PLAINTEXT_FORMAT':
return Format.PLAINTEXT
case 'SET_MARKDOWN_FORMAT':
return Format.MARKDOWN
default:
return state || "Jun 21"
return state || Format.PLAINTEXT
}
}
export const Show = {
ORIGINAL:'ORIGINAL',
FINAL:'FINAL',
DIFFERENCE:'DIFFERENCE'
}
export function view (state, action) {
export function show (state, action) {
switch (action.type) {
case 'VIEW_DATE':
return 'DATE'
case 'VIEW_MONTHLY':
return 'MONTHLY'
case 'SHOW_ORIGINAL':
return Show.ORIGINAL
case 'SHOW_FINAL':
return Show.FINAL
case 'SHOW_DIFFERENCE':
return Show.DIFFERENCE
default:
return state || 'MONTHLY'
return state || Show.DIFFERENCE
}
}
}

View File

@ -2,7 +2,16 @@
import { createSelector } from 'reselect'
import React from 'react'
import * as JsDiff from 'diff'
import {Format, Show} from './reducers'
const input = (state) => state.input
const format = (state) => state.format
const show = (state) => state.show
export const safeInput = createSelector(
[input],
@ -10,4 +19,55 @@ export const safeInput = createSelector(
//!!! sanitize the input here and return
return input
}
)
)
export const isMarkdownFormat = createSelector(
[format],
(format) => {
console.log(format, Format.MARKDOWN)
return format == Format.MARKDOWN
}
)
const isShow = (type) => createSelector(
[show],
(show) => {
return show == type
}
)
export const isShowOriginal = isShow(Show.ORIGINAL)
export const isShowFinal = isShow(Show.FINAL)
export const isShowDifference= isShow(Show.DIFFERENCE)
export const diff = createSelector(
[format, input],
(format, input) => {
let diff = JsDiff.diffLines(input.original.replace(/ /g, '###\n'), input.final.replace(/ /g, '###\n'))
console.log(diff, diff.map(({added, removed, value})=>({added, removed, value:value.replace(/###\n/g, ' ')})))
return diff.map(({added, removed, value})=>({added, removed, value:value.replace(/###\n/g, ' ')})).map(part => (
part.added ? <ins>{part.value}</ins> :
part.removed ? <del>{part.value}</del> :
<span>{part.value}</span>
))
/*
let diff = JsDiff.diffWords (input.original.replace(/ /g, ' '), input.final.replace(/ /g, ' '))
return diff.map(({added, removed, value})=>({added, removed, value:value.replace(/ /g, ' ')})).map(part => (
part.added ? <ins>{part.value}</ins> :
part.removed ? <del>{part.value}</del> :
<span>{part.value}</span>
))
*/
}
)
/*
html diff
---
diffHtml(parentOriginal, parentFinal) {
create stringOriginal, stringFinal consisting of
}
*/

16
src/server.js Normal file
View File

@ -0,0 +1,16 @@
var express = require('express')
var path = require('path')
var app = express()
app.use(express.static('dist'))
app.use('bower_components/*', express.static('bower_components'))
app.route('/*')
.get(function(req, res) {
res.sendFile(path.join(__dirname, '..', 'dist', 'index.html'));
});
app.listen(8080, function () {
console.log('Server listening on port 8080.')
})

View File

@ -5,7 +5,7 @@ module.exports = {
filename: './dist/browser-bundle.js'
},
target: 'web',
devtool: 'eval-cheap-module-source-map',
devtool: 'eval-module-source-map',
module: {
loaders: [
{