├── .env
├── .gitignore
├── assets
├── fork.png
└── favicon.ico
├── scripts
└── production.sh
├── src
├── browser.jsx
├── chart.jsx
└── radar.jsx
├── data
└── monsters.json
├── webpack.config.js
├── readme.md
├── package.json
├── server
├── config.js
└── index.js
└── views
└── index.html
/.env:
--------------------------------------------------------------------------------
1 | PORT=3000
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | assets/*.js
2 | assets/*.map
3 |
--------------------------------------------------------------------------------
/assets/fork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vigetlabs/an-isomorphic-chart/HEAD/assets/fork.png
--------------------------------------------------------------------------------
/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vigetlabs/an-isomorphic-chart/HEAD/assets/favicon.ico
--------------------------------------------------------------------------------
/scripts/production.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | echo
3 | echo "Building for production..."
4 | echo
5 | NODE_ENV=production
6 | webpack -vcp --optimize-minimize --optimize-occurence-order --optimize-dedupe
7 |
--------------------------------------------------------------------------------
/src/browser.jsx:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var Chart = require('./chart');
3 | var data = require('../data/monsters.json');
4 |
5 | React.render(, document.getElementById('chart'));
6 |
--------------------------------------------------------------------------------
/data/monsters.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "attack": 60,
4 | "defense": 80,
5 | "hp": 45,
6 | "sp_attack": 60,
7 | "sp_defense": 65,
8 | "speed": 45
9 | }
10 | ]
11 |
--------------------------------------------------------------------------------
/src/chart.jsx:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var Radar = require('./radar.jsx');
3 |
4 | var Chart = React.createClass({
5 |
6 | getDefaultProps() {
7 | return {
8 | data : [],
9 | height : 250,
10 | width : 400
11 | }
12 | },
13 |
14 | render() {
15 | var { data, width, height } = this.props;
16 |
17 | var r = Math.min(width, height) / 2;
18 |
19 | return (
20 |
24 | );
25 | }
26 |
27 | });
28 |
29 | module.exports = Chart;
30 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var Path = require('path');
2 |
3 | module.exports = {
4 | entry: {
5 | bundle: './src/browser.jsx'
6 | },
7 |
8 | output: {
9 | path: Path.resolve(__dirname, 'assets'),
10 | filename: '[name].js',
11 | sourceMapFilename: '[name].map'
12 | },
13 |
14 | resolve: {
15 | extensions: ['', '.js', '.jsx', '.json']
16 | },
17 |
18 | module: {
19 | loaders: [
20 | {
21 | test : /\.jsx*$/,
22 | loader : 'envify-loader'
23 | },
24 | {
25 | test : /\.jsx*$/,
26 | loader : 'jsx-loader',
27 | query : { harmony: true }
28 | },
29 | {
30 | test : /\.json$/, loader : 'json-loader'
31 | }
32 | ]
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # An Isomorphic Chart
2 |
3 | Data visualization has become a more frequent element of our work at Viget. Be it simple pie charts or beautiful maps displaying jersey sales, visually representing data in a compelling way is a great device for storytelling and provides rich fuel for social sharing.
4 |
5 | [Visualization is for sharing, read the post about it here] (http://viget.com/extend/visualization-is-for-sharing-using-react-for-portable-data-visualization)
6 |
7 | # Run it locally
8 |
9 | After making sure you have `node 0.10.x`, open up a terminal and run:
10 |
11 | ```bash
12 | npm install -d
13 | npm start
14 | ```
15 |
16 | Next, create a new terminal window or tab and run the following to
17 | compile assets:
18 |
19 | ```bash
20 | npm run watch
21 | ```
22 |
23 | Don't forget this step, it's easy to miss when everything renders in
24 | both the browser and on the server!
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-chart",
3 | "version": "1.0.0",
4 | "description": "A demonstration of isomorphic data visualization.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node server",
8 | "watch": "webpack -w -d",
9 | "postinstall": "./scripts/production.sh"
10 | },
11 | "engines": {
12 | "node": "0.10.x"
13 | },
14 | "repository" : {
15 | "type" : "git",
16 | "url" : "http://github.com/nhunzaker/an-isomorphic-chart.git"
17 | },
18 | "author": "Nate Hunzaker, Viget Labs",
19 | "license": "MIT",
20 | "dependencies": {
21 | "compression": "^1.2.0",
22 | "envify": "^1.2.1",
23 | "envify-loader": "^0.1.0",
24 | "express": "^4.10.1",
25 | "json-loader": "^0.5.1",
26 | "jsx-loader": "^0.12.1",
27 | "node-env-file": "^0.1.4",
28 | "node-jsx": "^0.12.0",
29 | "paths-js": "^0.3.0",
30 | "react": "^0.12.0",
31 | "swig": "^1.4.2",
32 | "webpack": "^1.4.13"
33 | },
34 | "devDependencies": {}
35 | }
36 |
--------------------------------------------------------------------------------
/server/config.js:
--------------------------------------------------------------------------------
1 | var swig = require('swig');
2 | var path = require('path');
3 | var express = require('express');
4 | var compression = require('compression');
5 |
6 | // Quick references to paths to clean up configuration
7 | var assetsPath = path.resolve(__dirname, '..', 'assets');
8 | var viewsPath = path.resolve(__dirname, '..', 'views');
9 |
10 | // Alloted time for static caching
11 | var hourly = 1000 * 60 * 60
12 |
13 | module.exports = function(app) {
14 | // Add gzip compression. In an ideal world, this would be
15 | // handled by Apache or Nginx, however we don't have that luxury
16 | // on Heroku.
17 | app.use(compression());
18 |
19 | // Configure where static assets, such as the client-side
20 | // javascript payload lives.
21 | app.use(express.static(assetsPath, { maxAge: hourly }));
22 |
23 | // Configure views
24 | //
25 | // The swig templating engine is used on this project,
26 | // see ../views/index.html
27 | app.set('views', viewsPath);
28 | app.set('view engine', 'html');
29 | app.engine('html', swig.renderFile);
30 | };
31 |
--------------------------------------------------------------------------------
/src/radar.jsx:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var RadarPaths = require('paths-js/radar');
3 |
4 | var Radar = React.createClass({
5 |
6 | getDefaultProps() {
7 | return {
8 | fill : "#0D95BC",
9 | stroke : "#777",
10 | data : [],
11 | max : 100,
12 | r : 100,
13 | rings : 5,
14 | x : 0,
15 | y : 0
16 | }
17 | },
18 |
19 | getCurve(curve, i) {
20 | var path = curve.polygon.path.print();
21 |
22 | return (
23 |
24 | );
25 | },
26 |
27 | getRing(ring, i) {
28 | var path = ring.path.print();
29 |
30 | return (
31 |
32 | );
33 | },
34 |
35 | render() {
36 | var { data, max, r, rings, x, y } = this.props;
37 | var paths = RadarPaths({ data, max, r, rings, center: [ x, y ] });
38 |
39 | return (
40 |
41 | { paths.rings.map(this.getRing) }
42 |
43 | { paths.curves.map(this.getCurve) }
44 |
45 |
46 | );
47 | }
48 |
49 | });
50 |
51 | module.exports = Radar;
52 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | // Pull in environment variables
2 | require('node-env-file')('.env');
3 |
4 | // Next teach node how to parse JSX
5 | require('node-jsx').install({
6 | // ES6 goodness
7 | harmony: true
8 | });
9 |
10 | var app = require('express')();
11 | var monsters = require('../data/monsters.json');
12 | var React = require('react');
13 | var Chart = require('../src/chart.jsx');
14 |
15 | require('./config')(app);
16 |
17 |
18 | function buildChart() {
19 | return React.createElement(Chart, { data: monsters });
20 | }
21 |
22 | app.get('/', function(req, res) {
23 | // Render a stringified version of the React output, which leaves hooks
24 | // that help React to "awaken" on page load
25 | res.render('index', {
26 | chart: React.renderToString(buildChart())
27 | });
28 | });
29 |
30 | app.get('/chart.svg', function(req, res) {
31 | // Rendering to static markup cuts React specific hooks
32 | var payload = React.renderToStaticMarkup(buildChart());
33 |
34 | res.set({
35 | 'Content-Type': 'text/svg+xml'
36 | });
37 |
38 | res.end(payload);
39 | });
40 |
41 | app.listen(process.env.PORT, function() {
42 | console.log("This example is running on port %s", process.env.PORT);
43 | });
44 |
--------------------------------------------------------------------------------
/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Isomorphic Charts
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
27 |
28 |
29 |
30 | Why I think isomorphic charts are the best
31 |
32 |
33 | {% autoescape false %}
34 | {{ chart }}
35 | {% endautoescape %}
36 |
37 |
38 | Because they work everywhere! Download this chart for illustrator or your favorite svg editing tool.
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------