Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/sandbox #4

Merged
merged 15 commits into from
Apr 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,11 @@ typings/
# dotenv environment variables file
.env

# PROJECT configurations
dist/

# End of https://www.gitignore.io/api/node


src/mock/mock.js
src/mock/mock.xl.js
# PROJECT configurations
dist/
DOCUMENTATION.md
docs
sandbox/rd3g.sandbox.bundle.js
sandbox/rd3g.sandbox.bundle.js.map
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,11 @@ export default {
}
};
```

## TODOs
This are some ideas to further development:
- Expose a graph property **background-color** that is applied to the svg graph container.

## Contributions
Contributions are welcome fell free to submit new features or simply grab something from
the above TODO list.
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@
"babel-preset-es2015": "6.16.0",
"babel-preset-react": "6.16.0",
"babel-preset-stage-0": "6.16.0",
"css-loader": "^0.28.0",
"documentation": "4.0.0-beta.18",
"eslint": "3.18.0",
"eslint-config-recommended": "1.5.0",
"eslint-plugin-promise": "3.5.0",
"eslint-plugin-standard": "2.1.1",
"eslint": "3.18.0",
"html-webpack-plugin": "2.28.0",
"npm-run-all": "4.0.2",
"react-dom": "15.4.2",
"react-jsonschema-form": "0.46.0",
"react-router-dom": "4.0.0",
"style-loader": "^0.16.1",
"webpack": "2.3.2",
"webpack-dev-server": "2.4.2"
},
Expand All @@ -45,8 +49,10 @@
"visualization"
],
"scripts": {
"dev": "node_modules/.bin/webpack-dev-server -d --content-base src --inline --hot --port 3002",
"dist": "webpack -p",
"dev": "node_modules/.bin/webpack-dev-server -d --content-base sandbox --inline --hot --port 3002",
"dist": "node_modules/.bin/npm-run-all --parallel dist:*",
"dist:rd3g": "webpack --config webpack.config.dist.js -p --display-modules",
"dist:sandbox": "webpack --config webpack.config.js -p --display-modules",
"docs": "node_modules/documentation/bin/documentation.js build src/**/*.js -f html -o docs && node_modules/documentation/bin/documentation.js build src/**/*.js -f md > DOCUMENTATION.md",
"lint": "node_modules/eslint/bin/eslint.js --config=.eslintrc.js \"src/**/*.js\"",
"test": "echo \"Error: no test specified\" && exit 1"
Expand Down
167 changes: 167 additions & 0 deletions sandbox/Sandbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import React from 'react';

import Form from 'react-jsonschema-form';

import './styles.css';

import defaultConfig from '../src/components/Graph/config';
import { Graph } from '../src';
import mock from './miserables';
import Utils from './utils';
import ReactD3GraphUtils from '../src/utils';

export default class Sandbox extends React.Component {
constructor(props) {
super(props);

const schemaProps = Utils.generateFormSchema(defaultConfig, '', {});

const schema = {
type: 'object',
properties: schemaProps
};

const uiSchema = {
height: {'ui:readonly': 'true'},
width: {'ui:readonly': 'true'}
};

this.uiSchema = uiSchema;

this.state = {
config: defaultConfig,
generatedConfig: {},
schema
};
}

onClickNode = (id) => window.alert(`clicked node ${id}`);

onClickLink = (source, target) => window.alert(`clicked link between ${source} and ${target}`);

onMouseOverNode = () => {
// Do something with the node identifier ...
}

onMouseOutNode = () => {
// Do something with the node identifier ...
}

pauseGraphSimulation = () => this.refs.graph.pauseSimulation();

restartGraphSimulation = () => this.refs.graph.restartSimulation();

resetNodesPositions = () => this.refs.graph.resetNodesPositions();

_buildGraphConfig = (data) => {
let config = {};
let schemaPropsValues = {};

for(let k of Object.keys(data.formData)) {
// Set value mapping correctly for config object of react-d3-graph
Utils.setValue(config, k, data.formData[k]);
// Set new values for schema of jsonform
schemaPropsValues[k] = {};
schemaPropsValues[k]['default'] = data.formData[k]
}

return {config, schemaPropsValues};
}

refreshGraph = (data) => {
const {config, schemaPropsValues} = this._buildGraphConfig(data);

this.state.schema.properties = ReactD3GraphUtils.merge(this.state.schema.properties, schemaPropsValues);

this.setState({
config
});
}

// Generate graph configuration file ready to use!
onSubmit = (data) => {
const {config, schemaPropsValues} = this._buildGraphConfig(data);

this.setState({
generatedConfig: config
});
}

onClickSubmit = () => {
// Hack for allow submit button to live outside jsonform
document.body.querySelector('.invisible-button').click();
}

resetGraphConfig = () => {
const schemaProps = Utils.generateFormSchema(defaultConfig, '', {});

const schema = {
type: 'object',
properties: schemaProps
};

this.setState({
config: defaultConfig,
schema
});
}

render() {
const graphProps = {
id: 'graph',
data: mock,
config: this.state.config,
onClickNode: this.onClickNode,
onClickLink: this.onClickLink,
onMouseOverNode: this.onMouseOverNode,
onMouseOutNode: this.onMouseOutNode
};

const btnStyle = {
cursor: this.state.config.staticGraph ? 'not-allowed' : 'pointer'
};

return (
<div className='container'>
<div className='container__graph'>
<button onClick={this.restartGraphSimulation} className='btn btn-default' style={btnStyle} disabled={this.state.config.staticGraph}>▶️</button>
<button onClick={this.pauseGraphSimulation} className='btn btn-default' style={btnStyle} disabled={this.state.config.staticGraph}>⏸️</button>
<button onClick={this.resetNodesPositions} className='btn btn-default' style={btnStyle} disabled={this.state.config.staticGraph}>Unstick nodes</button>
<Graph ref='graph' {...graphProps}/>
</div>
<div className='container__form'>
<h4>Graph configurations</h4>
<Form className='form-wrapper'
schema={this.state.schema}
uiSchema={this.uiSchema}
onChange={this.refreshGraph}
onSubmit={this.onSubmit}>
<button className='invisible-button' type='submit'></button>
</Form>
<button className='submit-button btn btn-primary' onClick={this.onClickSubmit}>Generate config</button>
<button className='reset-button btn btn-danger' onClick={this.resetGraphConfig}>Reset config</button>
</div>
<div className='container__graph-config'>
<h4>Your config</h4>
<JSONContainer data={this.state.generatedConfig} />
</div>
<div className='container__graph-data'>
<h4>Graph data</h4>
<JSONContainer data={mock} />
</div>
</div>
);
}
}

class JSONContainer extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return JSON.stringify(nextProps.data) !== JSON.stringify(this.props.data);
}

render() {
return (
<pre className='json-data-container'>{JSON.stringify(this.props.data, null, 2)}</pre>
);
}
}
13 changes: 13 additions & 0 deletions sandbox/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>react-d3-graph</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>

<body>
<div id="app"></div>
<script src="rd3g.sandbox.bundle.js"></script>
</body>
</html>
6 changes: 3 additions & 3 deletions src/js/app.js → sandbox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {
Route
} from 'react-router-dom';

import Demo from './components/Demo';
import Sandbox from './Sandbox';

const app = document.getElementById('app');

ReactDOM.render(
<Router>
<div>
<Route path='/' component={Demo}/>
<Route path='/' component={Sandbox}/>
</div>
</Router>
</Router>
, app);
File renamed without changes.
61 changes: 61 additions & 0 deletions sandbox/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
* {box-sizing: border-box;}
.container {
margin: 4px;
overflow: hidden;
position: fixed;
}

.container > div {
padding: 1em;
}.container {
display: grid;
grid-template-columns: repeat(3, 2fr);
grid-auto-rows: minmax(100px, auto);
}

.container__graph {
grid-column: 1 / 5;
grid-row: 1 / 2;

border: 1px dashed black;
}

.container__graph-data {
grid-column: 1 / 2;
grid-row: 2 / 3;
}

.container__graph-config {
grid-column: 2 / 5;
grid-row: 2 / 3;
}

.container__form {
grid-column: 5/ 6;
grid-row: 1 / 4;
}

.form-wrapper {
overflow-y: scroll;
max-height: 600px;
}

.json-data-container {
max-height: 250px;
overflow: scroll;
}

.submit-button {
margin-top: 22px;
}

.reset-button {
margin-top: 22px;
margin-left: 8px;
}

.invisible-button {
background: transparent;
border: none !important;
font-size:0;
}
38 changes: 38 additions & 0 deletions sandbox/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
function setValue(obj, access, value) {
if (typeof(access) == 'string') {
access = access.split('.');
}

// Check for non existence of root property before advancing
if (!obj[access[0]]) {
obj[access[0]] = {};
}

access.length > 1 ? setValue(obj[access.shift()],access,value) : obj[access[0]] = value;
}

/**
This two functions generate the react-jsonschema-form
schema from some passed graph configuration.
*/
function formMap(k,v) {
return {
title: k,
type: typeof v,
default: v
};
}

function generateFormSchema(o, rootSpreadProp, accum={}) {
for(let k of Object.keys(o)) {
const kk = rootSpreadProp ? `${rootSpreadProp}.${k}` : k;
typeof o[k] === 'object' ? generateFormSchema(o[kk], kk, accum) : accum[kk] = formMap(kk, o[k]);
}

return accum;
}

export default {
setValue,
generateFormSchema
};
Loading