├── .gitignore ├── Gruntfile.js ├── README.md ├── docs ├── assets │ └── main.js └── index.html ├── package.json ├── src ├── components │ ├── VisExample1.js │ ├── VisExample2.js │ ├── VisExample3.js │ ├── VisExample4.js │ ├── VisExample5.js │ ├── VisExample6.js │ ├── VisExample7.js │ ├── VisExample8.js │ └── VisExampleApp.js ├── index.html └── styles │ └── main.css ├── webpack.config.js └── webpack.dist.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | ### SublimeText ### 2 | *.sublime-workspace 3 | 4 | ### OSX ### 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear on external disk 14 | .Spotlight-V100 15 | .Trashes 16 | 17 | ### Windows ### 18 | # Windows image file caches 19 | Thumbs.db 20 | ehthumbs.db 21 | 22 | # Folder config file 23 | Desktop.ini 24 | 25 | # Recycle Bin used on file shares 26 | $RECYCLE.BIN/ 27 | 28 | # App specific 29 | 30 | node_modules/ 31 | .tmp 32 | dist 33 | /src/main.js 34 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mountFolder = function (connect, dir) { 4 | return connect.static(require('path').resolve(dir)); 5 | }; 6 | 7 | var webpackDistConfig = require('./webpack.dist.config.js'), 8 | webpackDevConfig = require('./webpack.config.js'); 9 | 10 | module.exports = function (grunt) { 11 | // Let *load-grunt-tasks* require everything 12 | require('load-grunt-tasks')(grunt); 13 | 14 | // Read configuration from package.json 15 | var pkgConfig = grunt.file.readJSON('package.json'); 16 | 17 | grunt.initConfig({ 18 | pkg: pkgConfig, 19 | 20 | webpack: { 21 | options: webpackDistConfig, 22 | dist: { 23 | cache: false 24 | } 25 | }, 26 | 27 | 'webpack-dev-server': { 28 | options: { 29 | hot: true, 30 | port: 8000, 31 | webpack: webpackDevConfig, 32 | publicPath: '/assets/', 33 | contentBase: './<%= pkg.src %>/' 34 | }, 35 | 36 | start: { 37 | keepAlive: true 38 | } 39 | }, 40 | 41 | connect: { 42 | options: { 43 | port: 8000 44 | }, 45 | 46 | dist: { 47 | options: { 48 | keepalive: true, 49 | middleware: function (connect) { 50 | return [ 51 | mountFolder(connect, pkgConfig.dist) 52 | ]; 53 | } 54 | } 55 | } 56 | }, 57 | 58 | open: { 59 | options: { 60 | delay: 500 61 | }, 62 | dev: { 63 | path: 'http://localhost:<%= connect.options.port %>/webpack-dev-server/' 64 | }, 65 | dist: { 66 | path: 'http://localhost:<%= connect.options.port %>/' 67 | } 68 | }, 69 | 70 | copy: { 71 | dist: { 72 | files: [ 73 | // includes files within path 74 | { 75 | flatten: true, 76 | expand: true, 77 | src: ['<%= pkg.src %>/*'], 78 | dest: '<%= pkg.dist %>/', 79 | filter: 'isFile' 80 | }, 81 | { 82 | flatten: true, 83 | expand: true, 84 | src: ['<%= pkg.src %>/images/*'], 85 | dest: '<%= pkg.dist %>/images/' 86 | } 87 | ] 88 | } 89 | }, 90 | 91 | clean: { 92 | dist: { 93 | files: [{ 94 | dot: true, 95 | src: [ 96 | '<%= pkg.dist %>' 97 | ] 98 | }] 99 | } 100 | } 101 | }); 102 | 103 | grunt.registerTask('serve', function (target) { 104 | if (target === 'dist') { 105 | return grunt.task.run(['build', 'open:dist', 'connect:dist']); 106 | } 107 | 108 | grunt.task.run([ 109 | 'open:dev', 110 | 'webpack-dev-server' 111 | ]); 112 | }); 113 | 114 | 115 | grunt.registerTask('build', ['clean', 'copy', 'webpack']); 116 | 117 | grunt.registerTask('default', []); 118 | }; 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-basic-vis-examples 2 | A few basic visualization examples that build on each other, 3 | using [React](https://facebook.github.io/react/) and 4 | [D3.js](http://d3js.org/). The project base 5 | was generated using [Yeoman](http://yeoman.io/)'s [react-webpack generator](https://github.com/newtriks/generator-react-webpack). 6 | 7 | **Demo** http://pbeshai.github.io/react-basic-vis-examples/ 8 | 9 | ## Installation 10 | 11 | Install npm packages 12 | 13 | ```npm install``` 14 | 15 | 16 | ## Usage 17 | 18 | Use grunt to start the web server 19 | 20 | ```grunt serve``` 21 | 22 | This will start the `webpack-dev-server` and open a browser to the locally running connect server. 23 | 24 | 25 | ## Author 26 | 27 | By Peter Beshai [@pbesh](http://twitter.com/pbesh) 28 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Basic Vis Examples with React 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-basic-vis-examples", 3 | "version": "0.1.0", 4 | "description": "A few basic visualization examples using React and D3", 5 | "author": { 6 | "name": "Peter Beshai", 7 | "email": "peter.beshai@gmail.com" 8 | }, 9 | "repository": "", 10 | "private": true, 11 | "src": "src", 12 | "dist": "dist", 13 | "mainInput": "VisExampleApp", 14 | "mainOutput": "main", 15 | "dependencies": { 16 | "normalize.css": "~3.0.3", 17 | "react": "^0.14.6", 18 | "react-addons-pure-render-mixin": "^0.14.6", 19 | "react-dom": "^0.14.6" 20 | }, 21 | "devDependencies": { 22 | "babel": "^4.0.0", 23 | "babel-loader": "^4.0.0", 24 | "css-loader": "~0.9.0", 25 | "d3": "^3.5.5", 26 | "grunt": "~0.4.5", 27 | "grunt-contrib-clean": "~0.6.0", 28 | "grunt-contrib-connect": "~0.8.0", 29 | "grunt-contrib-copy": "~0.5.0", 30 | "grunt-open": "~0.2.3", 31 | "grunt-webpack": "~1.0.8", 32 | "jshint-loader": "~0.8.0", 33 | "jsxhint-loader": "~0.2.0", 34 | "load-grunt-tasks": "~0.6.0", 35 | "react-auto-width": "^0.1.0", 36 | "react-hot-loader": "^1.0.7", 37 | "style-loader": "~0.8.0", 38 | "url-loader": "~0.5.5", 39 | "webpack": "~1.4.3", 40 | "webpack-dev-server": "~1.6.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/components/VisExample1.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const VisExample1 = React.createClass({ 4 | propTypes: { 5 | height: React.PropTypes.number.isRequired, 6 | width: React.PropTypes.number.isRequired 7 | }, 8 | 9 | render() { 10 | const { width, height } = this.props; 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | } 20 | }); 21 | 22 | export default VisExample1; 23 | -------------------------------------------------------------------------------- /src/components/VisExample2.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const VisExample2 = React.createClass({ 4 | propTypes: { 5 | height: React.PropTypes.number.isRequired, 6 | width: React.PropTypes.number.isRequired 7 | }, 8 | 9 | render() { 10 | const { width, height } = this.props; 11 | 12 | const data = [ 13 | { x: 30, y: 80, r: 25, color: 'red' }, 14 | { x: 130, y: 80, r: 60, color: 'green' }, 15 | { x: 260, y: 80, r: 40, color: 'blue' } 16 | ]; 17 | 18 | return ( 19 | 20 | {data.map((d, i) => { 21 | return ; 23 | })} 24 | 25 | ); 26 | } 27 | }); 28 | 29 | export default VisExample2; 30 | -------------------------------------------------------------------------------- /src/components/VisExample3.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const VisExample3 = React.createClass({ 4 | propTypes: { 5 | data: React.PropTypes.array.isRequired, 6 | height: React.PropTypes.number.isRequired, 7 | width: React.PropTypes.number.isRequired 8 | }, 9 | 10 | _chartComponents() { 11 | /* data is of format: 12 | data = [{ locationX, locationY, frequency, accuracy, rank }, ...] */ 13 | const { data } = this.props; 14 | 15 | // convert data points into chart points 16 | const points = data.map(d => { 17 | return { 18 | x: d.locationX * 10, 19 | y: d.locationY * 10, 20 | r: d.frequency / 5, 21 | color: d.accuracy > 0.5 ? 'red' : 'blue', 22 | datum: d 23 | }; 24 | }); 25 | 26 | return { 27 | points 28 | }; 29 | }, 30 | 31 | render() { 32 | const { width, height } = this.props; 33 | const { points } = this._chartComponents(); 34 | 35 | return ( 36 | 37 | {points.map((d, i) => { 38 | return ; 40 | })} 41 | 42 | ); 43 | } 44 | }); 45 | 46 | export default VisExample3; 47 | -------------------------------------------------------------------------------- /src/components/VisExample4.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import d3 from 'd3'; 3 | 4 | const VisExample4 = React.createClass({ 5 | propTypes: { 6 | data: React.PropTypes.array.isRequired, 7 | height: React.PropTypes.number.isRequired, 8 | width: React.PropTypes.number.isRequired 9 | }, 10 | 11 | _chartComponents() { 12 | /* data is of format: 13 | data = [{ locationX, locationY, frequency, accuracy, rank }, ...] */ 14 | const { width, height, data } = this.props; 15 | 16 | // convert data points into chart points 17 | const points = data.map(d => { 18 | return { 19 | x: d.locationX, 20 | y: d.locationY, 21 | r: d.frequency, 22 | color: d.accuracy, 23 | datum: d 24 | }; 25 | }); 26 | 27 | // get the range of values from the x and y attributes in the data 28 | const xDomain = d3.extent(points, d => d.x); 29 | const yDomain = d3.extent(points, d => d.y); 30 | 31 | // use d3 to create scales based on the dimensions and domains 32 | const x = d3.scale.linear().domain(xDomain).range([0, width]); 33 | const y = d3.scale.linear().domain(yDomain).range([0, height]); 34 | 35 | // create radius scale based on data 36 | const rDomain = d3.extent(points, d => d.r); 37 | const numCols = xDomain[1]; 38 | const r = d3.scale.linear().domain(rDomain) 39 | .range([0, (width / numCols) / 2 ]); 40 | 41 | // create color scale (colors from http://colorbrewer2.org) 42 | const color = d3.scale.linear().domain([0.3, 0.45, 0.6]) 43 | .range(['#0571b0', '#f7f7f7', '#ca0020']).clamp(true); 44 | 45 | return { 46 | points, 47 | width, 48 | height, 49 | x, 50 | y, 51 | r, 52 | color 53 | }; 54 | }, 55 | 56 | render() { 57 | const { points, width, height, x, y, r, color } = this._chartComponents(); 58 | 59 | return ( 60 | 61 | {points.map((d, i) => { 62 | return ; 64 | })} 65 | 66 | ); 67 | } 68 | }); 69 | 70 | export default VisExample4; 71 | -------------------------------------------------------------------------------- /src/components/VisExample5.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import d3 from 'd3'; 3 | 4 | const VisExample5 = React.createClass({ 5 | propTypes: { 6 | data: React.PropTypes.array.isRequired, 7 | height: React.PropTypes.number.isRequired, 8 | width: React.PropTypes.number.isRequired 9 | }, 10 | 11 | _chartComponents() { 12 | /* data is of format: 13 | data = [{ locationX, locationY, frequency, accuracy, rank }, ...] */ 14 | const { width, height, data } = this.props; 15 | 16 | // convert data points into chart points 17 | const points = data.map(d => { 18 | return { 19 | x: d.locationX, 20 | y: d.locationY, 21 | r: d.frequency, 22 | color: d.accuracy, 23 | datum: d 24 | }; 25 | }); 26 | 27 | // define the inner margins of the chart 28 | const innerMargin = { top: 10, bottom: 25, left: 10, right: 10 }; 29 | const innerWidth = width - innerMargin.left - innerMargin.right; 30 | const innerHeight = height - innerMargin.top - innerMargin.bottom; 31 | 32 | // get the range of values from the x and y attributes in the data 33 | const xDomain = d3.extent(points, d => d.x); 34 | const yDomain = d3.extent(points, d => d.y); 35 | 36 | // use d3 to create scales based on the dimensions and domains 37 | const x = d3.scale.linear().domain(xDomain) 38 | .range([innerMargin.left, innerMargin.left + innerWidth]); 39 | 40 | const y = d3.scale.linear().domain(yDomain) 41 | .range([innerMargin.top, innerMargin.top + innerHeight]); 42 | 43 | // create radius scale based on data 44 | const rDomain = d3.extent(points, d => d.r); 45 | const numCols = xDomain[1]; 46 | const r = d3.scale.linear().domain(rDomain) 47 | .range([0, (innerWidth / numCols) / 2]); 48 | 49 | // create color scale (colors from http://colorbrewer2.org) 50 | const color = d3.scale.linear().domain([0.3, 0.45, 0.6]) 51 | .range(['#0571b0', '#f7f7f7', '#ca0020']).clamp(true); 52 | 53 | return { 54 | points, 55 | width, 56 | height, 57 | innerMargin, 58 | x, 59 | y, 60 | r, 61 | color 62 | }; 63 | }, 64 | 65 | render() { 66 | const { points, width, height, x, y, r, color } = this._chartComponents(); 67 | 68 | return ( 69 | 70 | {points.map((d, i) => { 71 | return ; 73 | })} 74 | 75 | ); 76 | } 77 | }); 78 | 79 | export default VisExample5; 80 | -------------------------------------------------------------------------------- /src/components/VisExample6.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import d3 from 'd3'; 3 | 4 | const VisExample6 = React.createClass({ 5 | propTypes: { 6 | data: React.PropTypes.array.isRequired, 7 | height: React.PropTypes.number.isRequired, 8 | width: React.PropTypes.number.isRequired 9 | }, 10 | 11 | getInitialState() { 12 | return {}; 13 | }, 14 | 15 | _chartComponents() { 16 | /* data is of format: 17 | data = [{ locationX, locationY, frequency, accuracy, rank }, ...] */ 18 | const { width, height, data } = this.props; 19 | 20 | // convert data points into chart points 21 | const points = data.map(d => { 22 | return { 23 | x: d.locationX, 24 | y: d.locationY, 25 | r: d.frequency, 26 | color: d.accuracy, 27 | datum: d 28 | }; 29 | }); 30 | 31 | // define the inner margins of the chart 32 | const innerMargin = { top: 10, bottom: 25, left: 10, right: 10 }; 33 | const innerWidth = width - innerMargin.left - innerMargin.right; 34 | const innerHeight = height - innerMargin.top - innerMargin.bottom; 35 | 36 | // get the range of values from the x and y attributes in the data 37 | const xDomain = d3.extent(points, d => d.x); 38 | const yDomain = d3.extent(points, d => d.y); 39 | 40 | // use d3 to create scales based on the dimensions and domains 41 | const x = d3.scale.linear().domain(xDomain) 42 | .range([innerMargin.left, innerMargin.left + innerWidth]); 43 | 44 | const y = d3.scale.linear().domain(yDomain) 45 | .range([innerMargin.top, innerMargin.top + innerHeight]); 46 | 47 | // create radius scale based on data 48 | const rDomain = d3.extent(points, d => d.r); 49 | const numCols = xDomain[1]; 50 | const r = d3.scale.linear().domain(rDomain) 51 | .range([0, (innerWidth / numCols) / 2]); 52 | 53 | // create color scale (colors from http://colorbrewer2.org) 54 | const color = d3.scale.linear().domain([0.3, 0.45, 0.6]) 55 | .range(['#0571b0', '#f7f7f7', '#ca0020']).clamp(true); 56 | 57 | return { 58 | points, 59 | width, 60 | height, 61 | innerMargin, 62 | x, 63 | y, 64 | r, 65 | color 66 | }; 67 | }, 68 | 69 | _handleHighlight(d) { 70 | this.setState({ highlight: d }); 71 | }, 72 | 73 | _renderHighlight(chartComponents) { 74 | const { highlight } = this.state; 75 | const { innerMargin, height, x, y, r, color } = chartComponents; 76 | 77 | // no highlight, so don't show anything 78 | if (!highlight) { 79 | return null; 80 | } 81 | 82 | // show a circle around the point and the text details of the point 83 | return ( 84 | 85 | 88 | 89 | {`Rank #${highlight.datum.rank}, 90 | Frequency ${d3.format('0.1f')(highlight.datum.frequency)}, 91 | Accuracy ${d3.format('0.1%')(highlight.datum.accuracy)}`} 92 | 93 | 94 | ); 95 | }, 96 | 97 | render() { 98 | const chartComponents = this._chartComponents(); 99 | const { points, width, height, x, y, r, color } = chartComponents; 100 | return ( 101 | 102 | 103 | {points.map((d, i) => { 104 | return ; 108 | })} 109 | 110 | {this._renderHighlight(chartComponents)} 111 | 112 | ); 113 | } 114 | }); 115 | 116 | export default VisExample6; 117 | -------------------------------------------------------------------------------- /src/components/VisExample7.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import d3 from 'd3'; 3 | 4 | const VisExample7 = React.createClass({ 5 | propTypes: { 6 | data: React.PropTypes.array.isRequired, 7 | height: React.PropTypes.number.isRequired, 8 | width: React.PropTypes.number.isRequired 9 | }, 10 | 11 | getInitialState() { 12 | return {}; 13 | }, 14 | 15 | _chartComponents() { 16 | /* data is of format: 17 | data = [{ locationX, locationY, frequency, accuracy, rank }, ...] */ 18 | const { width, height, data } = this.props; 19 | 20 | // convert data points into chart points 21 | const points = data.map(d => { 22 | return { 23 | x: d.locationX, 24 | y: d.locationY, 25 | r: d.frequency, 26 | color: d.accuracy, 27 | datum: d 28 | }; 29 | }); 30 | 31 | // define the inner margins of the chart 32 | const innerMargin = { top: 10, bottom: 25, left: 10, right: 10 }; 33 | const innerWidth = width - innerMargin.left - innerMargin.right; 34 | const innerHeight = height - innerMargin.top - innerMargin.bottom; 35 | 36 | // get the range of values from the x and y attributes in the data 37 | const xDomain = d3.extent(points, d => d.x); 38 | const yDomain = d3.extent(points, d => d.y); 39 | 40 | // use d3 to create scales based on the dimensions and domains 41 | const x = d3.scale.linear().domain(xDomain) 42 | .range([innerMargin.left, innerMargin.left + innerWidth]); 43 | 44 | const y = d3.scale.linear().domain(yDomain) 45 | .range([innerMargin.top, innerMargin.top + innerHeight]); 46 | 47 | // create radius scale based on data 48 | const rDomain = d3.extent(points, d => d.r); 49 | const numCols = xDomain[1]; 50 | const r = d3.scale.linear().domain(rDomain) 51 | .range([0, (innerWidth / numCols) / 2]); 52 | 53 | // create color scale (colors from http://colorbrewer2.org) 54 | const color = d3.scale.linear().domain([0.3, 0.45, 0.6]) 55 | .range(['#0571b0', '#f7f7f7', '#ca0020']).clamp(true); 56 | 57 | return { 58 | points, 59 | width, 60 | height, 61 | innerMargin, 62 | x, 63 | y, 64 | r, 65 | color 66 | }; 67 | }, 68 | 69 | _handleHighlight(d) { 70 | this.setState({ highlight: d }); 71 | }, 72 | 73 | _renderHighlight(chartComponents) { 74 | const { highlight } = this.state; 75 | const { innerMargin, height, x, y, r, color } = chartComponents; 76 | 77 | // no highlight, so don't show anything 78 | if (!highlight) { 79 | return null; 80 | } 81 | 82 | // show a circle around the point and the text details of the point 83 | return ( 84 | 85 | 88 | 89 | {`Rank #${highlight.datum.rank}, 90 | Frequency ${d3.format('0.1f')(highlight.datum.frequency)}, 91 | Accuracy ${d3.format('0.1%')(highlight.datum.accuracy)}`} 92 | 93 | 94 | ); 95 | }, 96 | 97 | render() { 98 | const chartComponents = this._chartComponents(); 99 | const { points, width, height, x, y, r, color } = chartComponents; 100 | const maxRadius = r.range()[1]; 101 | return ( 102 | 103 | 104 | {points.map((d, i) => { 105 | return ( 106 | 107 | 109 | 115 | 116 | ); 117 | })} 118 | 119 | {this._renderHighlight(chartComponents)} 120 | 121 | ); 122 | } 123 | }); 124 | 125 | export default VisExample7; 126 | -------------------------------------------------------------------------------- /src/components/VisExample8.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PureRenderMixin from 'react-addons-pure-render-mixin'; 3 | import d3 from 'd3'; 4 | 5 | const VisExample8Points = React.createClass({ 6 | mixins: [PureRenderMixin], 7 | 8 | propTypes: { 9 | chartComponents: React.PropTypes.object.isRequired, 10 | onHighlight: React.PropTypes.func 11 | }, 12 | 13 | _handleHighlight(d) { 14 | if (this.props.onHighlight) { 15 | this.props.onHighlight(d); 16 | } 17 | }, 18 | 19 | render() { 20 | const { points, x, y, r, color } = this.props.chartComponents; 21 | const maxRadius = r.range()[1]; 22 | 23 | return ( 24 | 25 | {points.map((d, i) => { 26 | return ( 27 | 28 | 30 | 36 | 37 | ); 38 | })} 39 | 40 | ); 41 | } 42 | }); 43 | 44 | const VisExample8Highlight = React.createClass({ 45 | mixins: [PureRenderMixin], 46 | 47 | propTypes: { 48 | chartComponents: React.PropTypes.object.isRequired, 49 | highlight: React.PropTypes.object 50 | }, 51 | 52 | render() { 53 | const { highlight, chartComponents } = this.props; 54 | const { innerMargin, height, x, y, r, color } = chartComponents; 55 | 56 | // no highlight, so don't show anything 57 | if (!highlight) { 58 | return null; 59 | } 60 | 61 | // show a circle around the point and the text details of the point 62 | return ( 63 | 64 | 67 | 68 | {`Rank #${highlight.datum.rank}, 69 | Frequency ${d3.format('0.1f')(highlight.datum.frequency)}, 70 | Accuracy ${d3.format('0.1%')(highlight.datum.accuracy)}`} 71 | 72 | 73 | ); 74 | } 75 | }); 76 | 77 | 78 | const VisExample8 = React.createClass({ 79 | propTypes: { 80 | data: React.PropTypes.array.isRequired, 81 | height: React.PropTypes.number.isRequired, 82 | width: React.PropTypes.number // do not put isRequired to prevent render on null 83 | }, 84 | 85 | getInitialState() { 86 | return { 87 | chartComponents: this._chartComponents() 88 | }; 89 | }, 90 | 91 | componentWillReceiveProps(nextProps) { 92 | this.setState({ 93 | chartComponents: this._chartComponents(nextProps) 94 | }); 95 | }, 96 | 97 | _chartComponents(props = this.props) { 98 | /* data is of format: 99 | data = [{ locationX, locationY, frequency, accuracy, rank }, ...] */ 100 | const { width, height, data } = props; 101 | 102 | // convert data points into chart points 103 | const points = data.map(d => { 104 | return { 105 | x: d.locationX, 106 | y: d.locationY, 107 | r: d.frequency, 108 | color: d.accuracy, 109 | datum: d 110 | }; 111 | }); 112 | 113 | // define the inner margins of the chart 114 | const innerMargin = { top: 10, bottom: 25, left: 10, right: 10 }; 115 | const innerWidth = width - innerMargin.left - innerMargin.right; 116 | const innerHeight = height - innerMargin.top - innerMargin.bottom; 117 | 118 | // get the range of values from the x and y attributes in the data 119 | const xDomain = d3.extent(points, d => d.x); 120 | const yDomain = d3.extent(points, d => d.y); 121 | 122 | // use d3 to create scales based on the dimensions and domains 123 | const x = d3.scale.linear().domain(xDomain) 124 | .range([innerMargin.left, innerMargin.left + innerWidth]); 125 | 126 | const y = d3.scale.linear().domain(yDomain) 127 | .range([innerMargin.top, innerMargin.top + innerHeight]); 128 | 129 | // create radius scale based on data 130 | const rDomain = d3.extent(points, d => d.r); 131 | const numCols = xDomain[1]; 132 | const r = d3.scale.linear().domain(rDomain) 133 | .range([0, (innerWidth / numCols) / 2]); 134 | 135 | // create color scale (colors from http://colorbrewer2.org) 136 | const color = d3.scale.linear().domain([0.3, 0.45, 0.6]) 137 | .range(['#0571b0', '#f7f7f7', '#ca0020']).clamp(true); 138 | 139 | return { 140 | points, 141 | width, 142 | height, 143 | innerMargin, 144 | x, 145 | y, 146 | r, 147 | color 148 | }; 149 | }, 150 | 151 | _handleHighlight(d) { 152 | this.setState({ highlight: d }); 153 | }, 154 | 155 | _renderHighlight() { 156 | const { highlight, chartComponents } = this.state; 157 | 158 | // no highlight, so don't show anything 159 | if (!highlight) { 160 | return null; 161 | } 162 | 163 | // show a circle around the point and the text details of the point 164 | return ; 166 | }, 167 | 168 | render() { 169 | const { chartComponents } = this.state; 170 | const { width, height } = chartComponents; 171 | 172 | if (!width) { 173 | return null; 174 | } 175 | 176 | return ( 177 | 178 | 180 | {this._renderHighlight()} 181 | 182 | ); 183 | } 184 | }); 185 | 186 | export default VisExample8; 187 | -------------------------------------------------------------------------------- /src/components/VisExampleApp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import d3 from 'd3'; 4 | 5 | import VisExample1 from './VisExample1'; 6 | import VisExample2 from './VisExample2'; 7 | import VisExample3 from './VisExample3'; 8 | import VisExample4 from './VisExample4'; 9 | import VisExample5 from './VisExample5'; 10 | import VisExample6 from './VisExample6'; 11 | import VisExample7 from './VisExample7'; 12 | import VisExample8 from './VisExample8'; 13 | import AutoWidth from 'react-auto-width'; 14 | 15 | // CSS via webpack 16 | require('normalize.css'); 17 | require('../styles/main.css'); 18 | 19 | const VisExampleApp = React.createClass({ 20 | getInitialState() { 21 | // make up some data 22 | const data = []; 23 | const numRows = 30; 24 | const numCols = 50; 25 | const freqRng = d3.random.normal(15, 15); 26 | const accRng = d3.random.normal(0.45, 0.1); 27 | const rankRng = () => Math.ceil(Math.random() * 300); 28 | 29 | for (let x = 0; x < numCols; x++) { 30 | for (let y = 0; y < numRows; y++) { 31 | const freq = freqRng(); 32 | data.push({ locationX: x, locationY: y, frequency: freq < 0 ? 0 : freq, accuracy: accRng(), rank: rankRng() }); 33 | } 34 | } 35 | 36 | return { 37 | data: data 38 | }; 39 | }, 40 | 41 | // render the line chart and radial heatmap 42 | render() { 43 | const { data } = this.state; 44 | 45 | return ( 46 |
47 |
48 |

Example 1 - Basic SVG Drawing

49 | 50 |
51 |
52 |

Example 2 - SVG Drawing from Data Array

53 | 54 |
55 |
56 |

Example 3 - External Data Points

57 | 58 |
59 |
60 |

Example 4 - D3 Scales

61 | 62 |
63 |
64 |

Example 5 - Inner Margin

65 | 66 |
67 |
68 |

Example 6 - Highlight Behaviour (flickers)

69 | 70 |
71 |
72 |

Example 7 - Highlight Behaviour (slow)

73 | 74 |
75 |
76 |

Example 8 - Highlight Behaviour (optimized)

77 | 78 |
79 |
80 |

Example 9 - Highlight Behaviour (optimized, auto width)

81 | 82 | 83 | 84 |
85 |
86 | ); 87 | } 88 | }); 89 | 90 | ReactDOM.render(, document.getElementById('content')); 91 | 92 | export default VisExampleApp; 93 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Basic Vis Examples with React 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fff; 3 | color: #333; 4 | padding: 10px; 5 | } 6 | 7 | h4 { 8 | margin: 0; 9 | } 10 | 11 | .chart { 12 | margin: 10px 0; 13 | border: 1px solid #aaa; 14 | } 15 | 16 | text { 17 | dominant-baseline: hanging; 18 | } 19 | 20 | .highlight { 21 | pointer-events: none; 22 | } 23 | 24 | .example { 25 | margin-bottom: 50px; 26 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Webpack development server configuration 3 | * 4 | * This file is set up for serving the webpack-dev-server, which will watch for changes and recompile as required if 5 | * the subfolder /webpack-dev-server/ is visited. Visiting the root will not automatically reload. 6 | */ 7 | 'use strict'; 8 | var webpack = require('webpack'); 9 | 10 | module.exports = { 11 | 12 | output: { 13 | filename: 'main.js', 14 | publicPath: '/assets/' 15 | }, 16 | 17 | cache: true, 18 | debug: true, 19 | devtool: false, 20 | entry: [ 21 | 'webpack/hot/only-dev-server', 22 | './src/components/VisExampleApp.js' 23 | ], 24 | 25 | stats: { 26 | colors: true, 27 | reasons: true 28 | }, 29 | 30 | resolve: { 31 | extensions: ['', '.js'], 32 | alias: { 33 | 'styles': __dirname + '/src/styles', 34 | 'mixins': __dirname + '/src/mixins', 35 | 'components': __dirname + '/src/components/', 36 | 'stores': __dirname + '/src/stores/', 37 | 'actions': __dirname + '/src/actions/' 38 | } 39 | }, 40 | module: { 41 | loaders: [{ 42 | test: /\.js$/, 43 | exclude: /node_modules/, 44 | loader: 'react-hot!babel-loader' 45 | }, { 46 | test: /\.css$/, 47 | loader: 'style-loader!css-loader' 48 | }, { 49 | test: /\.(png|jpg)$/, 50 | loader: 'url-loader?limit=8192' 51 | }] 52 | }, 53 | 54 | plugins: [ 55 | new webpack.HotModuleReplacementPlugin(), 56 | new webpack.NoErrorsPlugin() 57 | ] 58 | 59 | }; 60 | -------------------------------------------------------------------------------- /webpack.dist.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Webpack distribution configuration 3 | * 4 | * This file is set up for serving the distribution version. It will be compiled to dist/ by default 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var webpack = require('webpack'); 10 | 11 | module.exports = { 12 | 13 | output: { 14 | publicPath: '/assets/', 15 | path: 'dist/assets/', 16 | filename: 'main.js' 17 | }, 18 | 19 | debug: false, 20 | devtool: false, 21 | entry: './src/components/VisExampleApp.js', 22 | 23 | stats: { 24 | colors: true, 25 | reasons: false 26 | }, 27 | 28 | plugins: [ 29 | new webpack.optimize.DedupePlugin(), 30 | new webpack.optimize.UglifyJsPlugin(), 31 | new webpack.optimize.OccurenceOrderPlugin(), 32 | new webpack.optimize.AggressiveMergingPlugin() 33 | ], 34 | 35 | resolve: { 36 | extensions: ['', '.js'], 37 | alias: { 38 | 'styles': __dirname + '/src/styles', 39 | 'mixins': __dirname + '/src/mixins', 40 | 'components': __dirname + '/src/components/', 41 | 'stores': __dirname + '/src/stores/', 42 | 'actions': __dirname + '/src/actions/' 43 | } 44 | }, 45 | 46 | module: { 47 | preLoaders: [{ 48 | test: /\.js$/, 49 | exclude: /node_modules/, 50 | loader: 'jsxhint' 51 | }], 52 | 53 | loaders: [{ 54 | test: /\.js$/, 55 | exclude: /node_modules/, 56 | loader: 'babel-loader' 57 | }, { 58 | test: /\.css$/, 59 | loader: 'style-loader!css-loader' 60 | }, { 61 | test: /\.(png|jpg)$/, 62 | loader: 'url-loader?limit=8192' 63 | }] 64 | } 65 | }; 66 | --------------------------------------------------------------------------------