From 885ef9430af2ae78ce46f36f3b0c0dc56b4c61fe Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 7 Feb 2017 17:07:14 -0600 Subject: [PATCH 01/27] drawing basics of radar chart --- packages/charts-vanilla-radar/LICENSE.md | 12 ++ packages/charts-vanilla-radar/README.md | 7 + packages/charts-vanilla-radar/gulpfile.js | 43 ++++++ packages/charts-vanilla-radar/index.html | 17 +++ packages/charts-vanilla-radar/package.json | 21 +++ .../src/__tests__/Chart-test.js | 7 + .../charts-vanilla-radar/src/scripts/data.js | 39 ++++++ .../charts-vanilla-radar/src/scripts/index.js | 124 ++++++++++++++++++ .../src/styles/styles.scss | 10 ++ 9 files changed, 280 insertions(+) create mode 100644 packages/charts-vanilla-radar/LICENSE.md create mode 100644 packages/charts-vanilla-radar/README.md create mode 100644 packages/charts-vanilla-radar/gulpfile.js create mode 100644 packages/charts-vanilla-radar/index.html create mode 100644 packages/charts-vanilla-radar/package.json create mode 100644 packages/charts-vanilla-radar/src/__tests__/Chart-test.js create mode 100644 packages/charts-vanilla-radar/src/scripts/data.js create mode 100644 packages/charts-vanilla-radar/src/scripts/index.js create mode 100644 packages/charts-vanilla-radar/src/styles/styles.scss diff --git a/packages/charts-vanilla-radar/LICENSE.md b/packages/charts-vanilla-radar/LICENSE.md new file mode 100644 index 0000000..aefc709 --- /dev/null +++ b/packages/charts-vanilla-radar/LICENSE.md @@ -0,0 +1,12 @@ +Copyright 2017 IBM +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/charts-vanilla-radar/README.md b/packages/charts-vanilla-radar/README.md new file mode 100644 index 0000000..5c90567 --- /dev/null +++ b/packages/charts-vanilla-radar/README.md @@ -0,0 +1,7 @@ +# `@ibm-design/charts-vanilla-radar` + +## Usage + +```bash +yarn add @ibm-design/charts-vanilla-radar +``` diff --git a/packages/charts-vanilla-radar/gulpfile.js b/packages/charts-vanilla-radar/gulpfile.js new file mode 100644 index 0000000..9ba14f7 --- /dev/null +++ b/packages/charts-vanilla-radar/gulpfile.js @@ -0,0 +1,43 @@ +var gulp = require('gulp'), + babel = require('gulp-babel'), + browserSync = require('browser-sync') + sass = require('gulp-sass'), + plumber = require('gulp-plumber'), + sourcemaps = require('gulp-sourcemaps'); + + + +gulp.task('serve', ['compile-js', 'compile-scss'], function(){ + browserSync.init({ + server: { + baseDir:'./' + } + }) + + gulp.watch(['src/scripts/*.js'], ['compile-js']) + gulp.watch(["src/styles/*.scss"], ['compile-scss']); + gulp.watch(['*.html','src/styles/*.scss','src/scripts/*.js']).on('change', browserSync.reload) + +}) + +gulp.task('compile-js', function(){ + return gulp.src('src/scripts/*.js') + .pipe(plumber()) + .pipe(sourcemaps.init()) + .pipe(babel({ + presets:['es2015'] + })) + .pipe(sourcemaps.write('maps')) + .pipe(gulp.dest('lib')) +}) + +gulp.task('compile-scss', function(){ + return gulp.src('src/styles/*.scss') + .pipe(plumber()) + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(sourcemaps.write('maps')) + .pipe(gulp.dest('lib')) +}) + +gulp.task('default', ['serve']) \ No newline at end of file diff --git a/packages/charts-vanilla-radar/index.html b/packages/charts-vanilla-radar/index.html new file mode 100644 index 0000000..09e9801 --- /dev/null +++ b/packages/charts-vanilla-radar/index.html @@ -0,0 +1,17 @@ + + + + + Radar Chart + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/charts-vanilla-radar/package.json b/packages/charts-vanilla-radar/package.json new file mode 100644 index 0000000..1551148 --- /dev/null +++ b/packages/charts-vanilla-radar/package.json @@ -0,0 +1,21 @@ +{ + "name": "@ibm-design/charts-vanilla-radar", + "version": "0.0.0", + "main": "lib/index.js", + "scripts": { + "build": "npm run clean && babel -d lib src", + "clean": "rimraf lib" + }, + "license": "Apache-2.0", + "devDependencies": { + "babel-cli": "^6.18.0", + "babel-preset-es2015": "^6.22.0", + "browser-sync": "^2.18.7", + "gulp": "^3.9.1", + "gulp-babel": "^6.1.2", + "gulp-plumber": "^1.1.0", + "gulp-sass": "^3.1.0", + "gulp-sourcemaps": "^2.4.1", + "rimraf": "^2.5.4" + } +} diff --git a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js new file mode 100644 index 0000000..890958c --- /dev/null +++ b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js @@ -0,0 +1,7 @@ +import Chart from '../'; + +describe('Radar Component', () => { + it('should render', () => { + expect(true).toBe(true, 'This is a placeholder true') + }); +}); diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js new file mode 100644 index 0000000..a799755 --- /dev/null +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -0,0 +1,39 @@ +const data = [ + [//iPhone + {axis:"Battery Life",value:0.22}, + {axis:"Brand",value:0.28}, + {axis:"Contract Cost",value:0.29}, + {axis:"Design And Quality",value:0.17}, + {axis:"Have Internet Connectivity",value:0.22}, + {axis:"Large Screen",value:0.02}, + {axis:"Price Of Device",value:0.21}, + {axis:"To Be A Smartphone",value:0.50} + ],[//Samsung + {axis:"Battery Life",value:0.27}, + {axis:"Brand",value:0.16}, + {axis:"Contract Cost",value:0.35}, + {axis:"Design And Quality",value:0.13}, + {axis:"Have Internet Connectivity",value:0.20}, + {axis:"Large Screen",value:0.13}, + {axis:"Price Of Device",value:0.35}, + {axis:"To Be A Smartphone",value:0.38} + ],[//Nokia Smartphone + {axis:"Battery Life",value:0.26}, + {axis:"Brand",value:0.10}, + {axis:"Contract Cost",value:0.30}, + {axis:"Design And Quality",value:0.14}, + {axis:"Have Internet Connectivity",value:0.22}, + {axis:"Large Screen",value:0.04}, + {axis:"Price Of Device",value:0.41}, + {axis:"To Be A Smartphone",value:0.30} + ],[//Nokia Smartphone + {axis:"Battery Life",value:0.1}, + {axis:"Brand",value:0.1}, + {axis:"Contract Cost",value:0.1}, + {axis:"Design And Quality",value:0.1}, + {axis:"Have Internet Connectivity",value:0.1}, + {axis:"Large Screen",value:0.1}, + {axis:"Price Of Device",value:0.1}, + {axis:"To Be A Smartphone",value:0.1} + ] +]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js new file mode 100644 index 0000000..e79fd5f --- /dev/null +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -0,0 +1,124 @@ +'use strict' +class Radar { + + constructor(graphNode, data){ + this.data = this.formatData(data) + this.target = d3.select(graphNode) + + this.scale; + this.line; + + this.cfg = (()=>{ + let w = 600, + h = 600, + numAxis = data[0].length; + + return { + w: w, + h: h, + r: w / 2, + margin: {top: 20, right: 20, bottom: 20, left: 20}, + levels: 5, + max: null, // will be determined by data + labelFactor: 60, // how much further outside the radius does the label appears + wrapWidth: null, // word wrap after this number of pixels + opacityArea: 0.4, + strokeWidth: 2, + color: 'blue', + numAxis: numAxis, + angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians + shape: 'circle', //circle, polygon, square + } + })() + } + + + + + + + scalesAndLinesFunctions(){ + let that = this + this.cfg.max = d3.max(d3.merge(this.data), d => d.value) + this.scale = d3.scaleLinear() + .domain([0, this.cfg.max]) + .range([0, this.cfg.r]) + this.line = d3.radialLine() + .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d)=>{ return that.scale(d.value)}) + + } + + // setupRadar(){ + // this.target.style({ + // width: cfg.w + cfg.margin.left + cfg.margin.right, + // height: cfg.h + cfg.margin.top + cfg.margin.bottom, + // }) + + // if(cfg.shape == 'circle'){ + // //draw rings + // for(var i = 0; i < cfg.levels; i++){ + + // } + + // } else if(cfg.shape == 'polygon'){ + // //TODO + // } else if(cfg.shape =='square'){ + // //TODO + // } + // } + + //customize this function to fit data model you want + formatData(data){ + data.map((d)=>{ + d.map((v)=>{ + v.value = Math.round(v.value*100) + }) + }) + return data + } +} + + +let a; +function test(data){ + a = new Radar('svg', data) + a.scalesAndLinesFunctions() + console.log(a.cfg.max) + console.log(a.scale(34)) + a.target.attrs({ + width: a.cfg.w, + height: a.cfg.h + }) + a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) + .append('path') + .datum(data[0]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'red', + 'opacity': 0.4, + 'd' : a.line + }) + a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) + .append('path') + .datum(data[2]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'blue', + 'opacity': 0.4, + 'd' : a.line + }) + a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) + .append('path') + .datum(data[1]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'green', + 'opacity': 0.4, + 'd' : a.line + }) + +} + + +test(data) \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss new file mode 100644 index 0000000..7ad97c1 --- /dev/null +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -0,0 +1,10 @@ +body { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: "Helvetica"; +} + +.radar { + border: 1px solid black; +} \ No newline at end of file From 103844e6cb2152db998967ecc07ee655665f3aba Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Thu, 9 Feb 2017 14:01:22 -0600 Subject: [PATCH 02/27] drawing axis, shapes, and rings --- .../charts-vanilla-radar/src/scripts/data.js | 9 - .../charts-vanilla-radar/src/scripts/index.js | 169 ++++++++++++------ .../src/styles/styles.scss | 8 +- 3 files changed, 119 insertions(+), 67 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index a799755..4e05c5b 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -26,14 +26,5 @@ const data = [ {axis:"Large Screen",value:0.04}, {axis:"Price Of Device",value:0.41}, {axis:"To Be A Smartphone",value:0.30} - ],[//Nokia Smartphone - {axis:"Battery Life",value:0.1}, - {axis:"Brand",value:0.1}, - {axis:"Contract Cost",value:0.1}, - {axis:"Design And Quality",value:0.1}, - {axis:"Have Internet Connectivity",value:0.1}, - {axis:"Large Screen",value:0.1}, - {axis:"Price Of Device",value:0.1}, - {axis:"To Be A Smartphone",value:0.1} ] ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index e79fd5f..49e210a 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -7,17 +7,20 @@ class Radar { this.scale; this.line; + this.axis = {} this.cfg = (()=>{ - let w = 600, - h = 600, - numAxis = data[0].length; + let w = 500, + h = w, + numAxis = data[0].length, + margin = {top: 20, right: 20, bottom: 20, left: 20}, + r = w / 2 - margin.top - margin.bottom; return { w: w, h: h, - r: w / 2, - margin: {top: 20, right: 20, bottom: 20, left: 20}, + r: r, + margin: margin, levels: 5, max: null, // will be determined by data labelFactor: 60, // how much further outside the radius does the label appears @@ -27,46 +30,99 @@ class Radar { color: 'blue', numAxis: numAxis, angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians - shape: 'circle', //circle, polygon, square + shape: 'circle', //circle, polygon, square, + dotSize: 4, //if not set, dots will not be drawn. + axisStyle: { + 'stroke': 'black', + 'stroke-width': 1, + } } })() } + draw(){ - - + } scalesAndLinesFunctions(){ let that = this this.cfg.max = d3.max(d3.merge(this.data), d => d.value) this.scale = d3.scaleLinear() - .domain([0, this.cfg.max]) - .range([0, this.cfg.r]) + .domain([0, this.cfg.max]) + .range([0, this.cfg.r]) + this.line = d3.radialLine() - .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) - .radius((d)=>{ return that.scale(d.value)}) + .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d)=>{ return that.scale(d.value)}) } - // setupRadar(){ - // this.target.style({ - // width: cfg.w + cfg.margin.left + cfg.margin.right, - // height: cfg.h + cfg.margin.top + cfg.margin.bottom, - // }) + setupRadar(){ + this.target.style({ + width: this.cfg.w, + height: this.cfg.h, + }) - // if(cfg.shape == 'circle'){ - // //draw rings - // for(var i = 0; i < cfg.levels; i++){ + this.axis.names = data[0].map( el => el.axis) + this.axis.length = this.axis.names.length - // } - // } else if(cfg.shape == 'polygon'){ - // //TODO - // } else if(cfg.shape =='square'){ - // //TODO - // } - // } + //puts 0,0 in the center of the svg + this.globalGroup = this.target.append('g').attrs({ + 'class': 'chart', + 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` + }) + + // draws the axis + if(this.cfg.axisStyle){ + this.axisGroup = this.globalGroup.append('g').attr('class','axis-lines') + this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') + .attrs({ + 'x1': 0, + 'y1': 0, + // `+ Math.PI/2 to make the Axis line up with the correct Data` + 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'class': 'axis-line', + }).styles(this.cfg.axisStyle) + } + + if(this.cfg.shape == 'circle'){ + //draw rings + let ringInterval = this.cfg.max / this.cfg.levels + var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX + console.log(ringRadiusValues) + let g = this.globalGroup.append('g').attr('class', 'grid-circle') + g.selectAll('.level').data(ringRadiusValues).enter().append('circle').attrs({ + 'fill': 'none', + 'cx': 0, + 'cy': 0, + 'class': 'label', + 'r': (d) => {return this.scale(d)}, + 'stroke': 'black', + 'stroke-width': 1, + 'stroke-dasharray': '1 3', + 'opacity': 0.5 + }) + + g.selectAll('.axis-label').data(ringRadiusValues).enter().append('text').attrs({ + 'class': 'axis-label', + 'x': '0', + 'y': (d) => {return -this.scale(d)}, + 'dy': '-0.3em', + 'dx': '0.5em', + 'font-size': '0.8em', + 'fill': 'grey', + }).text((d) => {return d+'%'}) + + //text indicating % for each level + } else if(this.cfg.shape == 'polygon'){ + //TODO + } else if(this.cfg.shape =='square'){ + //TODO + } + } //customize this function to fit data model you want formatData(data){ @@ -84,40 +140,39 @@ let a; function test(data){ a = new Radar('svg', data) a.scalesAndLinesFunctions() - console.log(a.cfg.max) - console.log(a.scale(34)) a.target.attrs({ width: a.cfg.w, height: a.cfg.h }) - a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) - .append('path') - .datum(data[0]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'red', - 'opacity': 0.4, - 'd' : a.line - }) - a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) - .append('path') - .datum(data[2]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'blue', - 'opacity': 0.4, - 'd' : a.line - }) - a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) - .append('path') - .datum(data[1]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'green', - 'opacity': 0.4, - 'd' : a.line - }) - + a.setupRadar() + + let g = a.globalGroup.append('g').attr('class', 'lines') + g.append('path') + .datum(data[1]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'red', + 'opacity': 1, + 'd' : a.line, + 'class': 'iphone' + }) + g.append('path') + .datum(data[2]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'blue', + 'opacity': 1, + 'd' : a.line + }) + g.append('path') + .datum(data[0]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'green', + 'opacity': 1, + 'd' : a.line, + 'class': 'iphone' + }) } diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss index 7ad97c1..fdc82a6 100644 --- a/packages/charts-vanilla-radar/src/styles/styles.scss +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -7,4 +7,10 @@ body { .radar { border: 1px solid black; -} \ No newline at end of file + + path { + mix-blend-mode: multiply; + opacity: 0.3; + } +} + From eac544838e1f69217f5f4d205884d63e7be58a9c Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Thu, 9 Feb 2017 14:16:57 -0600 Subject: [PATCH 03/27] added draw function --- .../charts-vanilla-radar/src/scripts/index.js | 101 ++++++++++-------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 49e210a..91370a7 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -10,11 +10,11 @@ class Radar { this.axis = {} this.cfg = (()=>{ - let w = 500, + let w = 800, h = w, numAxis = data[0].length, - margin = {top: 20, right: 20, bottom: 20, left: 20}, - r = w / 2 - margin.top - margin.bottom; + margin = {top: 50, right: 50, bottom: 50, left: 40}, + r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]), return { w: w, @@ -42,7 +42,19 @@ class Radar { draw(){ + this.scalesAndLinesFunctions() + this.wrapper = this.setupRadar() + var shapes = this.wrapper.append('g').attr('class','shapes') + for(var i = 0; i < data.length; i++){ + shapes.append('path').datum(data[i]).attrs({ + 'stroke-width': 1.5, + 'fill': 'red', + 'opacity': 1, + 'd' : a.line, + 'class': 'iphone' + }) + } } scalesAndLinesFunctions(){ @@ -59,31 +71,32 @@ class Radar { } setupRadar(){ - this.target.style({ + this.target.styles({ width: this.cfg.w, height: this.cfg.h, }) + let globalGroup = this.target.append('g').attrs({ + 'class': 'chart', + 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` + }) this.axis.names = data[0].map( el => el.axis) this.axis.length = this.axis.names.length //puts 0,0 in the center of the svg - this.globalGroup = this.target.append('g').attrs({ - 'class': 'chart', - 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` - }) + // draws the axis if(this.cfg.axisStyle){ - this.axisGroup = this.globalGroup.append('g').attr('class','axis-lines') + this.axisGroup = globalGroup.append('g').attr('class','axis-lines') this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, - 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, 'class': 'axis-line', }).styles(this.cfg.axisStyle) } @@ -93,7 +106,7 @@ class Radar { let ringInterval = this.cfg.max / this.cfg.levels var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX console.log(ringRadiusValues) - let g = this.globalGroup.append('g').attr('class', 'grid-circle') + let g = globalGroup.append('g').attr('class', 'grid-circle') g.selectAll('.level').data(ringRadiusValues).enter().append('circle').attrs({ 'fill': 'none', 'cx': 0, @@ -122,6 +135,7 @@ class Radar { } else if(this.cfg.shape =='square'){ //TODO } + return globalGroup } //customize this function to fit data model you want @@ -139,40 +153,35 @@ class Radar { let a; function test(data){ a = new Radar('svg', data) - a.scalesAndLinesFunctions() - a.target.attrs({ - width: a.cfg.w, - height: a.cfg.h - }) - a.setupRadar() - - let g = a.globalGroup.append('g').attr('class', 'lines') - g.append('path') - .datum(data[1]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'red', - 'opacity': 1, - 'd' : a.line, - 'class': 'iphone' - }) - g.append('path') - .datum(data[2]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'blue', - 'opacity': 1, - 'd' : a.line - }) - g.append('path') - .datum(data[0]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'green', - 'opacity': 1, - 'd' : a.line, - 'class': 'iphone' - }) + a.draw() + + // let g = a.globalGroup.append('g').attr('class', 'lines') + // g.append('path') + // .datum(data[1]) + // .attrs({ + // 'stroke-width': 1.5, + // 'fill': 'red', + // 'opacity': 1, + // 'd' : a.line, + // 'class': 'iphone' + // }) + // g.append('path') + // .datum(data[2]) + // .attrs({ + // 'stroke-width': 1.5, + // 'fill': 'blue', + // 'opacity': 1, + // 'd' : a.line + // }) + // g.append('path') + // .datum(data[0]) + // .attrs({ + // 'stroke-width': 1.5, + // 'fill': 'green', + // 'opacity': 1, + // 'd' : a.line, + // 'class': 'iphone' + // }) } From a07826c9e237fd0f5f7daed86fb9d6340701c0aa Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Thu, 9 Feb 2017 16:32:30 -0600 Subject: [PATCH 04/27] adding labels --- .../charts-vanilla-radar/src/scripts/index.js | 178 ++++++++++-------- .../src/styles/styles.scss | 7 +- 2 files changed, 105 insertions(+), 80 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 91370a7..51f1c06 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -1,36 +1,38 @@ 'use strict' class Radar { - constructor(graphNode, data){ + constructor(graphNode, data, opts){ this.data = this.formatData(data) this.target = d3.select(graphNode) this.scale; this.line; + this.color = d3.scaleOrdinal() + .range(opts.colors) this.axis = {} this.cfg = (()=>{ - let w = 800, + let w = opts.size || 500, h = w, numAxis = data[0].length, - margin = {top: 50, right: 50, bottom: 50, left: 40}, - r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]), + margin = opts.margins, + r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); return { w: w, h: h, r: r, margin: margin, - levels: 5, + units: opts.units, + levels: opts.levels, max: null, // will be determined by data - labelFactor: 60, // how much further outside the radius does the label appears - wrapWidth: null, // word wrap after this number of pixels - opacityArea: 0.4, + labelFactor: opts.labelFactor, // how much further outside the radius does the label appears + wrapWidth: opts.wrapWidth || 60, // word wrap after this number of pixels + opacityArea: opts.opacityArea, strokeWidth: 2, - color: 'blue', numAxis: numAxis, angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians - shape: 'circle', //circle, polygon, square, + shape: opts.shape || 'circle', //circle, polygon, square, dotSize: 4, //if not set, dots will not be drawn. axisStyle: { 'stroke': 'black', @@ -38,24 +40,38 @@ class Radar { } } })() - } + } // constructor draw(){ + //setup this.scalesAndLinesFunctions() this.wrapper = this.setupRadar() - var shapes = this.wrapper.append('g').attr('class','shapes') - for(var i = 0; i < data.length; i++){ - shapes.append('path').datum(data[i]).attrs({ - 'stroke-width': 1.5, - 'fill': 'red', - 'opacity': 1, - 'd' : a.line, - 'class': 'iphone' - }) + //draw blobs & dots + var blobs = this.wrapper.append('g').attr('class','blobs') + for(let i = 0; i < data.length; i++){ + //blob + let g = blobs.append('g').attr('class','blob-'+i) + g.append('path').datum(data[i]).attrs({ + 'stroke-width': 1.5, + 'd' : a.line, + 'fill': () => { return this.color(i)} + }) + //dots + g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ + 'stroke-width': 1.5, + 'cx': (d,i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, + 'cy': (d,i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, + 'r': this.cfg.dotSize, + 'fill': () => { return this.color(i)}, + 'opacity': 0.4, + 'class': 'dot', + }) } - } + + + }//draw scalesAndLinesFunctions(){ let that = this @@ -68,7 +84,7 @@ class Radar { .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) .radius((d)=>{ return that.scale(d.value)}) - } + }//scalesAndLinesFunctions setupRadar(){ this.target.styles({ @@ -95,8 +111,9 @@ class Radar { 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, - 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'opacity': 0.3, 'class': 'axis-line', }).styles(this.cfg.axisStyle) } @@ -105,38 +122,58 @@ class Radar { //draw rings let ringInterval = this.cfg.max / this.cfg.levels var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX - console.log(ringRadiusValues) + let g = globalGroup.append('g').attr('class', 'grid-circle') - g.selectAll('.level').data(ringRadiusValues).enter().append('circle').attrs({ - 'fill': 'none', - 'cx': 0, - 'cy': 0, - 'class': 'label', - 'r': (d) => {return this.scale(d)}, - 'stroke': 'black', - 'stroke-width': 1, - 'stroke-dasharray': '1 3', - 'opacity': 0.5 - }) + g.append('g').attr('class', 'rings') + .selectAll('.ring').data(ringRadiusValues) + .enter().append('circle').attrs({ + 'fill': 'none', + 'cx': 0, + 'cy': 0, + 'class': 'label', + 'r': (d) => {return this.scale(d)}, + 'stroke': 'black', + 'stroke-width': 1, + 'stroke-dasharray': '1 3', + 'opacity': 0.5, + 'class': 'ring' + }) + + //add the unit labels on the rings + g.append('g').attr('class', 'rings-labels') + .selectAll('.ring-label').data(ringRadiusValues) + .enter().append('text').attrs({ + 'class': 'ring-label', + 'x': '0', + 'y': (d) => {return -this.scale(d)}, + 'dy': '-0.3em', + 'dx': '0.5em', + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4 + }) + .text((d) => {return d + this.cfg.units}) + g.append('g').attr('class', 'legend') + .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ + 'class': 'axis-label', + 'x': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, + 'y': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4, + 'text-anchor': "middle" + }) + .html((d) => { return this.wrap(d.axis)}) + + - g.selectAll('.axis-label').data(ringRadiusValues).enter().append('text').attrs({ - 'class': 'axis-label', - 'x': '0', - 'y': (d) => {return -this.scale(d)}, - 'dy': '-0.3em', - 'dx': '0.5em', - 'font-size': '0.8em', - 'fill': 'grey', - }).text((d) => {return d+'%'}) - - //text indicating % for each level } else if(this.cfg.shape == 'polygon'){ //TODO } else if(this.cfg.shape =='square'){ //TODO } return globalGroup - } + }//setupRadar //customize this function to fit data model you want formatData(data){ @@ -146,42 +183,27 @@ class Radar { }) }) return data - } + } // formatData + + wrap(text) { + return text.split(' ').join('\n') + }//wrap } let a; function test(data){ - a = new Radar('svg', data) + a = new Radar('svg', data, { + size: 800, + margins: {top: 70, right: 70, bottom: 70, left: 70}, + colors: ["#EDC951","#CC333F","#00A0B0"], + units: '%', + levels: 5, + opacityArea: 0.4, + labelFactor: 50, + shape: 'circle', + }) a.draw() - - // let g = a.globalGroup.append('g').attr('class', 'lines') - // g.append('path') - // .datum(data[1]) - // .attrs({ - // 'stroke-width': 1.5, - // 'fill': 'red', - // 'opacity': 1, - // 'd' : a.line, - // 'class': 'iphone' - // }) - // g.append('path') - // .datum(data[2]) - // .attrs({ - // 'stroke-width': 1.5, - // 'fill': 'blue', - // 'opacity': 1, - // 'd' : a.line - // }) - // g.append('path') - // .datum(data[0]) - // .attrs({ - // 'stroke-width': 1.5, - // 'fill': 'green', - // 'opacity': 1, - // 'd' : a.line, - // 'class': 'iphone' - // }) } diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss index fdc82a6..58f6547 100644 --- a/packages/charts-vanilla-radar/src/styles/styles.scss +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -8,9 +8,12 @@ body { .radar { border: 1px solid black; - path { + g[class*='blob-'] { mix-blend-mode: multiply; - opacity: 0.3; + + path { + opacity: 0.5; + } } } From 179346e23dbd02f0ce5ef3f54463afd806b516f2 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Fri, 10 Feb 2017 10:01:05 -0600 Subject: [PATCH 05/27] added label wrapping --- .../charts-vanilla-radar/src/scripts/index.js | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 51f1c06..7c925cc 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -161,9 +161,11 @@ class Radar { 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, - 'text-anchor': "middle" + 'text-anchor': 'middle', }) - .html((d) => { return this.wrap(d.axis)}) + .text((d)=>{return d.axis}) + .call(this.wrap, this.cfg.wrapWidth) + @@ -185,8 +187,33 @@ class Radar { return data } // formatData - wrap(text) { - return text.split(' ').join('\n') + + //Taken from http://bl.ocks.org/mbostock/7555321 + //Wraps SVG text + wrap(text, width) { + text.each(function() { + var text = d3.select(this), + words = text.text().split(/\s+/).reverse(), + word, + line = [], + lineNumber = 0, + lineHeight = 1.4, // ems + y = text.attr("y"), + x = text.attr("x"), + dy = parseFloat(text.attr("dy")) || 0, + tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em"); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(" ")); + if (tspan.node().getComputedTextLength() > width) { + line.pop(); + tspan.text(line.join(" ")); + line = [word]; + tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); + } + } + }); }//wrap } @@ -202,6 +229,7 @@ function test(data){ opacityArea: 0.4, labelFactor: 50, shape: 'circle', + wrapWidth: 60 }) a.draw() } From fb70542ada6dd0c40c910c87744297ead7c07e66 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 15:47:47 -0600 Subject: [PATCH 06/27] removing linting errors --- packages/charts-vanilla-radar/gulpfile.js | 44 ++--- .../src/__tests__/Chart-test.js | 4 +- .../charts-vanilla-radar/src/scripts/data.js | 50 +++--- .../charts-vanilla-radar/src/scripts/index.js | 151 +++++++++--------- .../src/styles/styles.scss | 8 +- 5 files changed, 123 insertions(+), 134 deletions(-) diff --git a/packages/charts-vanilla-radar/gulpfile.js b/packages/charts-vanilla-radar/gulpfile.js index 9ba14f7..b4d3502 100644 --- a/packages/charts-vanilla-radar/gulpfile.js +++ b/packages/charts-vanilla-radar/gulpfile.js @@ -1,43 +1,43 @@ -var gulp = require('gulp'), - babel = require('gulp-babel'), - browserSync = require('browser-sync') - sass = require('gulp-sass'), - plumber = require('gulp-plumber'), - sourcemaps = require('gulp-sourcemaps'); +var gulp = require('gulp'); +var babel = require('gulp-babel'); +var browserSync = require('browser-sync'); +var sass = require('gulp-sass'); +var plumber = require('gulp-plumber'); +var sourcemaps = require('gulp-sourcemaps'); -gulp.task('serve', ['compile-js', 'compile-scss'], function(){ +gulp.task('serve', ['compile-js', 'compile-scss'], function() { browserSync.init({ server: { - baseDir:'./' - } - }) + baseDir:'./', + }, + }); - gulp.watch(['src/scripts/*.js'], ['compile-js']) - gulp.watch(["src/styles/*.scss"], ['compile-scss']); - gulp.watch(['*.html','src/styles/*.scss','src/scripts/*.js']).on('change', browserSync.reload) + gulp.watch(['src/scripts/*.js'], ['compile-js']); + gulp.watch(['src/styles/*.scss'], ['compile-scss']); + gulp.watch(['*.html', 'src/styles/*.scss', 'src/scripts/*.js']).on('change', browserSync.reload); -}) +}); -gulp.task('compile-js', function(){ +gulp.task('compile-js', function() { return gulp.src('src/scripts/*.js') .pipe(plumber()) .pipe(sourcemaps.init()) .pipe(babel({ - presets:['es2015'] + presets:['es2015'], })) .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')) -}) + .pipe(gulp.dest('lib')); +}); -gulp.task('compile-scss', function(){ +gulp.task('compile-scss', function() { return gulp.src('src/styles/*.scss') .pipe(plumber()) .pipe(sourcemaps.init()) .pipe(sass().on('error', sass.logError)) .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')) -}) + .pipe(gulp.dest('lib')); +}); -gulp.task('default', ['serve']) \ No newline at end of file +gulp.task('default', ['serve']); \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js index 890958c..54c9b46 100644 --- a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js +++ b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js @@ -1,7 +1,7 @@ -import Chart from '../'; +// import Chart from '../'; describe('Radar Component', () => { it('should render', () => { - expect(true).toBe(true, 'This is a placeholder true') + expect(true).toBe(true, 'This is a placeholder true'); }); }); diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index 4e05c5b..cfc38c7 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -1,30 +1,26 @@ const data = [ [//iPhone - {axis:"Battery Life",value:0.22}, - {axis:"Brand",value:0.28}, - {axis:"Contract Cost",value:0.29}, - {axis:"Design And Quality",value:0.17}, - {axis:"Have Internet Connectivity",value:0.22}, - {axis:"Large Screen",value:0.02}, - {axis:"Price Of Device",value:0.21}, - {axis:"To Be A Smartphone",value:0.50} - ],[//Samsung - {axis:"Battery Life",value:0.27}, - {axis:"Brand",value:0.16}, - {axis:"Contract Cost",value:0.35}, - {axis:"Design And Quality",value:0.13}, - {axis:"Have Internet Connectivity",value:0.20}, - {axis:"Large Screen",value:0.13}, - {axis:"Price Of Device",value:0.35}, - {axis:"To Be A Smartphone",value:0.38} - ],[//Nokia Smartphone - {axis:"Battery Life",value:0.26}, - {axis:"Brand",value:0.10}, - {axis:"Contract Cost",value:0.30}, - {axis:"Design And Quality",value:0.14}, - {axis:"Have Internet Connectivity",value:0.22}, - {axis:"Large Screen",value:0.04}, - {axis:"Price Of Device",value:0.41}, - {axis:"To Be A Smartphone",value:0.30} - ] + {axis:'Apdex', value:0.80}, + {axis:'Alerts', value:0.66}, + {axis:'Request Volume', value:0.35}, + {axis:'Memory', value:0.54}, + {axis:'CPU', value:0.38}, + {axis:'Disk', value:0.23}, + ], + [//Samsung + {axis:'Apdex', value:0.80}, + {axis:'Alerts', value:0.66}, + {axis:'Request Volume', value:0.35}, + {axis:'Memory', value:0.23}, + {axis:'CPU', value:0.36}, + {axis:'Disk', value:0.13}, + ], + [//Nokia Smartphone + {axis:'Apdex', value:0.26}, + {axis:'Alerts', value:0.10}, + {axis:'Request Volume', value:0.30}, + {axis:'Memory', value:0.14}, + {axis:'CPU', value:0.22}, + {axis:'Disk', value:0.04}, + ], ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 7c925cc..b9d6c21 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -1,22 +1,22 @@ -'use strict' +'use strict'; class Radar { - constructor(graphNode, data, opts){ - this.data = this.formatData(data) - this.target = d3.select(graphNode) + constructor(graphNode, data, opts) { + this.data = this.formatData(data); + this.target = d3.select(graphNode); this.scale; this.line; this.color = d3.scaleOrdinal() - .range(opts.colors) - this.axis = {} + .range(opts.colors); + this.axis = {}; - this.cfg = (()=>{ - let w = opts.size || 500, - h = w, - numAxis = data[0].length, - margin = opts.margins, - r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); + this.cfg = (() => { + let w = opts.size || 500; + let h = w; + let numAxis = data[0].length; + let margin = opts.margins; + let r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); return { w: w, @@ -37,107 +37,104 @@ class Radar { axisStyle: { 'stroke': 'black', 'stroke-width': 1, - } - } - })() + }, + }; + })(); } // constructor - draw(){ + draw() { //setup - this.scalesAndLinesFunctions() - this.wrapper = this.setupRadar() + this.scalesAndLinesFunctions(); + this.wrapper = this.setupRadar(); //draw blobs & dots - var blobs = this.wrapper.append('g').attr('class','blobs') - for(let i = 0; i < data.length; i++){ + let blobs = this.wrapper.append('g').attr('class', 'blobs'); + for (let i = 0; i < data.length; i++) { //blob - let g = blobs.append('g').attr('class','blob-'+i) - g.append('path').datum(data[i]).attrs({ - 'stroke-width': 1.5, - 'd' : a.line, - 'fill': () => { return this.color(i)} - }) - //dots - g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ - 'stroke-width': 1.5, - 'cx': (d,i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, - 'cy': (d,i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, - 'r': this.cfg.dotSize, - 'fill': () => { return this.color(i)}, - 'opacity': 0.4, - 'class': 'dot', - }) + let g = blobs.append('g').attr('class', 'blob-' + i); + g.append('path').datum(data[i]).attrs({ + 'stroke-width': 1.5, + 'd' : a.line, + 'fill': () => { return this.color(i); }, + }); + //dots + g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ + 'stroke-width': 1.5, + 'cx': (d, i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'cy': (d, i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'r': this.cfg.dotSize, + 'fill': () => { return this.color(i);}, + 'opacity': 0.4, + 'class': 'dot', + }); } - - }//draw - scalesAndLinesFunctions(){ - let that = this - this.cfg.max = d3.max(d3.merge(this.data), d => d.value) + scalesAndLinesFunctions() { + let that = this; + this.cfg.max = d3.max(d3.merge(this.data), d => d.value); this.scale = d3.scaleLinear() .domain([0, this.cfg.max]) - .range([0, this.cfg.r]) + .range([0, this.cfg.r]); this.line = d3.radialLine() - .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) - .radius((d)=>{ return that.scale(d.value)}) + .angle((d, i) => { return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d) => { return that.scale(d.value);}); }//scalesAndLinesFunctions - setupRadar(){ + setupRadar() { this.target.styles({ width: this.cfg.w, height: this.cfg.h, - }) + }); let globalGroup = this.target.append('g').attrs({ 'class': 'chart', - 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` - }) + 'transform': `translate(${this.cfg.w / 2} ${this.cfg.h / 2})`, + }); - this.axis.names = data[0].map( el => el.axis) - this.axis.length = this.axis.names.length + this.axis.names = data[0].map( el => el.axis); + this.axis.length = this.axis.names.length; //puts 0,0 in the center of the svg // draws the axis - if(this.cfg.axisStyle){ - this.axisGroup = globalGroup.append('g').attr('class','axis-lines') + if (this.cfg.axisStyle) { + this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, - 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, + 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, 'opacity': 0.3, 'class': 'axis-line', - }).styles(this.cfg.axisStyle) + }).styles(this.cfg.axisStyle); } - if(this.cfg.shape == 'circle'){ + if (this.cfg.shape == 'circle') { //draw rings - let ringInterval = this.cfg.max / this.cfg.levels - var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX + let ringInterval = this.cfg.max / this.cfg.levels; + var ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX - let g = globalGroup.append('g').attr('class', 'grid-circle') + let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') .selectAll('.ring').data(ringRadiusValues) .enter().append('circle').attrs({ 'fill': 'none', 'cx': 0, 'cy': 0, - 'class': 'label', - 'r': (d) => {return this.scale(d)}, + 'r': (d) => { return this.scale(d); }, 'stroke': 'black', 'stroke-width': 1, - 'stroke-dasharray': '1 3', - 'opacity': 0.5, - 'class': 'ring' - }) + 'stroke-dasharray': '5 5', + 'opacity': 0.2, + 'class': 'ring', + }); //add the unit labels on the rings g.append('g').attr('class', 'rings-labels') @@ -145,36 +142,36 @@ class Radar { .enter().append('text').attrs({ 'class': 'ring-label', 'x': '0', - 'y': (d) => {return -this.scale(d)}, + 'y': (d) => {return -this.scale(d);}, 'dy': '-0.3em', 'dx': '0.5em', 'font-size': '0.8em', 'fill': 'grey', - 'opacity': 0.4 + 'opacity': 0.4, }) - .text((d) => {return d + this.cfg.units}) + .text((d) => {return d + this.cfg.units;}); g.append('g').attr('class', 'legend') .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ 'class': 'axis-label', - 'x': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, - 'y': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, + 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, 'text-anchor': 'middle', }) - .text((d)=>{return d.axis}) - .call(this.wrap, this.cfg.wrapWidth) + .text((d) => {return d.axis;}) + .call(this.wrap, this.cfg.wrapWidth); - } else if(this.cfg.shape == 'polygon'){ + } else if (this.cfg.shape == 'polygon') { //TODO - } else if(this.cfg.shape =='square'){ + } else if (this.cfg.shape == 'square') { //TODO } - return globalGroup + return globalGroup; }//setupRadar //customize this function to fit data model you want @@ -188,7 +185,7 @@ class Radar { } // formatData - //Taken from http://bl.ocks.org/mbostock/7555321 + //Adapted from http://bl.ocks.org/mbostock/7555321 //Wraps SVG text wrap(text, width) { text.each(function() { @@ -227,7 +224,7 @@ function test(data){ units: '%', levels: 5, opacityArea: 0.4, - labelFactor: 50, + labelFactor: 30, shape: 'circle', wrapWidth: 60 }) diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss index 58f6547..df66632 100644 --- a/packages/charts-vanilla-radar/src/styles/styles.scss +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -8,12 +8,8 @@ body { .radar { border: 1px solid black; - g[class*='blob-'] { - mix-blend-mode: multiply; - - path { - opacity: 0.5; - } + path { + opacity: 0.5; } } From 59c749a5d82d11c519065ad87bb4f0c9543ea875 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 15:56:38 -0600 Subject: [PATCH 07/27] more linting errors fixed --- .../charts-vanilla-radar/src/scripts/index.js | 95 +++++++++++-------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index b9d6c21..828f879 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -51,6 +51,7 @@ class Radar { //draw blobs & dots let blobs = this.wrapper.append('g').attr('class', 'blobs'); for (let i = 0; i < data.length; i++) { + //blob let g = blobs.append('g').attr('class', 'blob-' + i); g.append('path').datum(data[i]).attrs({ @@ -58,6 +59,7 @@ class Radar { 'd' : a.line, 'fill': () => { return this.color(i); }, }); + //dots g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ 'stroke-width': 1.5, @@ -97,10 +99,6 @@ class Radar { this.axis.names = data[0].map( el => el.axis); this.axis.length = this.axis.names.length; - - //puts 0,0 in the center of the svg - - // draws the axis if (this.cfg.axisStyle) { this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); @@ -109,8 +107,12 @@ class Radar { 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, - 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, + 'x2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice * i + Math.PI / 2); + }, 'opacity': 0.3, 'class': 'axis-line', }).styles(this.cfg.axisStyle); @@ -153,8 +155,14 @@ class Radar { g.append('g').attr('class', 'legend') .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ 'class': 'axis-label', - 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, - 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'x': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.sin(this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.cos(this.cfg.angleSlice * i + Math.PI / 2); + }, 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, @@ -175,13 +183,13 @@ class Radar { }//setupRadar //customize this function to fit data model you want - formatData(data){ - data.map((d)=>{ - d.map((v)=>{ - v.value = Math.round(v.value*100) - }) - }) - return data + formatData(data) { + data.map((d) => { + d.map((v) => { + v.value = Math.round(v.value * 100); + }); + }); + return data; } // formatData @@ -189,47 +197,54 @@ class Radar { //Wraps SVG text wrap(text, width) { text.each(function() { - var text = d3.select(this), - words = text.text().split(/\s+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.4, // ems - y = text.attr("y"), - x = text.attr("x"), - dy = parseFloat(text.attr("dy")) || 0, - tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em"); - - while (word = words.pop()) { - line.push(word); - tspan.text(line.join(" ")); - if (tspan.node().getComputedTextLength() > width) { + var text = d3.select(this), + words = text.text().split(/\s+/).reverse(), + word, + line = [], + lineNumber = 0, + lineHeight = 1.4, // ems + y = text.attr('y'), + x = text.attr('x'), + dy = parseFloat(text.attr('dy')) || 0, + tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(' ')); + if (tspan.node().getComputedTextLength() > width) { line.pop(); - tspan.text(line.join(" ")); + tspan.text(line.join(' ')); line = [word]; - tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); - } + tspan = text.append('tspan') + .attrs({ + 'x': x, + 'y': y, + 'dy': ++lineNumber * lineHeight + dy + 'em', + }) + .text(word); } - }); + } + }); }//wrap } let a; -function test(data){ +function test(data) { a = new Radar('svg', data, { size: 800, margins: {top: 70, right: 70, bottom: 70, left: 70}, - colors: ["#EDC951","#CC333F","#00A0B0"], + colors: ['#EDC951', '#CC333F', '#00A0B0'], units: '%', levels: 5, opacityArea: 0.4, labelFactor: 30, shape: 'circle', - wrapWidth: 60 - }) - a.draw() + wrapWidth: 60, + }); + + a.draw(); } -test(data) \ No newline at end of file +test(data); \ No newline at end of file From 7f47a08ed4f5b6a1321d0ae74a89249d8bdfe726 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 16:09:43 -0600 Subject: [PATCH 08/27] updated data and more lint errors --- .../charts-vanilla-radar/src/scripts/data.js | 8 +++++- .../charts-vanilla-radar/src/scripts/index.js | 26 +++++++++---------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index cfc38c7..1e52c32 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -6,14 +6,18 @@ const data = [ {axis:'Memory', value:0.54}, {axis:'CPU', value:0.38}, {axis:'Disk', value:0.23}, + {axis:'Network', value:0.23}, + {axis:'Heap', value:0.16}, ], [//Samsung - {axis:'Apdex', value:0.80}, + {axis:'Apdex', value:0.58}, {axis:'Alerts', value:0.66}, {axis:'Request Volume', value:0.35}, {axis:'Memory', value:0.23}, {axis:'CPU', value:0.36}, {axis:'Disk', value:0.13}, + {axis:'Network', value:0.11}, + {axis:'Heap', value:0.35}, ], [//Nokia Smartphone {axis:'Apdex', value:0.26}, @@ -22,5 +26,7 @@ const data = [ {axis:'Memory', value:0.14}, {axis:'CPU', value:0.22}, {axis:'Disk', value:0.04}, + {axis:'Network', value:0.23}, + {axis:'Heap', value:0.41}, ], ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 828f879..4f16611 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -56,7 +56,7 @@ class Radar { let g = blobs.append('g').attr('class', 'blob-' + i); g.append('path').datum(data[i]).attrs({ 'stroke-width': 1.5, - 'd' : a.line, + 'd' : this.line, 'fill': () => { return this.color(i); }, }); @@ -101,7 +101,7 @@ class Radar { // draws the axis if (this.cfg.axisStyle) { - this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); + this.axisGroup = globalGroup.append('g').attr('class', 'axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, @@ -121,7 +121,7 @@ class Radar { if (this.cfg.shape == 'circle') { //draw rings let ringInterval = this.cfg.max / this.cfg.levels; - var ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX + let ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse(); let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') @@ -197,16 +197,16 @@ class Radar { //Wraps SVG text wrap(text, width) { text.each(function() { - var text = d3.select(this), - words = text.text().split(/\s+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.4, // ems - y = text.attr('y'), - x = text.attr('x'), - dy = parseFloat(text.attr('dy')) || 0, - tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + let text = d3.select(this); + let words = text.text().split(/\s+/).reverse(); + let word; + let line = []; + let lineNumber = 0; + let lineHeight = 1.4; // ems + let y = text.attr('y'); + let x = text.attr('x'); + let dy = parseFloat(text.attr('dy')) || 0; + let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); while (word = words.pop()) { line.push(word); From ad971e1cb46b59e7632389c93dc1659762775d34 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 7 Feb 2017 17:07:14 -0600 Subject: [PATCH 09/27] drawing basics of radar chart --- packages/charts-vanilla-radar/LICENSE.md | 12 ++ packages/charts-vanilla-radar/README.md | 7 + packages/charts-vanilla-radar/gulpfile.js | 43 ++++++ packages/charts-vanilla-radar/index.html | 17 +++ packages/charts-vanilla-radar/package.json | 21 +++ .../src/__tests__/Chart-test.js | 7 + .../charts-vanilla-radar/src/scripts/data.js | 39 ++++++ .../charts-vanilla-radar/src/scripts/index.js | 124 ++++++++++++++++++ .../src/styles/styles.scss | 10 ++ 9 files changed, 280 insertions(+) create mode 100644 packages/charts-vanilla-radar/LICENSE.md create mode 100644 packages/charts-vanilla-radar/README.md create mode 100644 packages/charts-vanilla-radar/gulpfile.js create mode 100644 packages/charts-vanilla-radar/index.html create mode 100644 packages/charts-vanilla-radar/package.json create mode 100644 packages/charts-vanilla-radar/src/__tests__/Chart-test.js create mode 100644 packages/charts-vanilla-radar/src/scripts/data.js create mode 100644 packages/charts-vanilla-radar/src/scripts/index.js create mode 100644 packages/charts-vanilla-radar/src/styles/styles.scss diff --git a/packages/charts-vanilla-radar/LICENSE.md b/packages/charts-vanilla-radar/LICENSE.md new file mode 100644 index 0000000..aefc709 --- /dev/null +++ b/packages/charts-vanilla-radar/LICENSE.md @@ -0,0 +1,12 @@ +Copyright 2017 IBM +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/charts-vanilla-radar/README.md b/packages/charts-vanilla-radar/README.md new file mode 100644 index 0000000..5c90567 --- /dev/null +++ b/packages/charts-vanilla-radar/README.md @@ -0,0 +1,7 @@ +# `@ibm-design/charts-vanilla-radar` + +## Usage + +```bash +yarn add @ibm-design/charts-vanilla-radar +``` diff --git a/packages/charts-vanilla-radar/gulpfile.js b/packages/charts-vanilla-radar/gulpfile.js new file mode 100644 index 0000000..9ba14f7 --- /dev/null +++ b/packages/charts-vanilla-radar/gulpfile.js @@ -0,0 +1,43 @@ +var gulp = require('gulp'), + babel = require('gulp-babel'), + browserSync = require('browser-sync') + sass = require('gulp-sass'), + plumber = require('gulp-plumber'), + sourcemaps = require('gulp-sourcemaps'); + + + +gulp.task('serve', ['compile-js', 'compile-scss'], function(){ + browserSync.init({ + server: { + baseDir:'./' + } + }) + + gulp.watch(['src/scripts/*.js'], ['compile-js']) + gulp.watch(["src/styles/*.scss"], ['compile-scss']); + gulp.watch(['*.html','src/styles/*.scss','src/scripts/*.js']).on('change', browserSync.reload) + +}) + +gulp.task('compile-js', function(){ + return gulp.src('src/scripts/*.js') + .pipe(plumber()) + .pipe(sourcemaps.init()) + .pipe(babel({ + presets:['es2015'] + })) + .pipe(sourcemaps.write('maps')) + .pipe(gulp.dest('lib')) +}) + +gulp.task('compile-scss', function(){ + return gulp.src('src/styles/*.scss') + .pipe(plumber()) + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(sourcemaps.write('maps')) + .pipe(gulp.dest('lib')) +}) + +gulp.task('default', ['serve']) \ No newline at end of file diff --git a/packages/charts-vanilla-radar/index.html b/packages/charts-vanilla-radar/index.html new file mode 100644 index 0000000..09e9801 --- /dev/null +++ b/packages/charts-vanilla-radar/index.html @@ -0,0 +1,17 @@ + + + + + Radar Chart + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/charts-vanilla-radar/package.json b/packages/charts-vanilla-radar/package.json new file mode 100644 index 0000000..1551148 --- /dev/null +++ b/packages/charts-vanilla-radar/package.json @@ -0,0 +1,21 @@ +{ + "name": "@ibm-design/charts-vanilla-radar", + "version": "0.0.0", + "main": "lib/index.js", + "scripts": { + "build": "npm run clean && babel -d lib src", + "clean": "rimraf lib" + }, + "license": "Apache-2.0", + "devDependencies": { + "babel-cli": "^6.18.0", + "babel-preset-es2015": "^6.22.0", + "browser-sync": "^2.18.7", + "gulp": "^3.9.1", + "gulp-babel": "^6.1.2", + "gulp-plumber": "^1.1.0", + "gulp-sass": "^3.1.0", + "gulp-sourcemaps": "^2.4.1", + "rimraf": "^2.5.4" + } +} diff --git a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js new file mode 100644 index 0000000..890958c --- /dev/null +++ b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js @@ -0,0 +1,7 @@ +import Chart from '../'; + +describe('Radar Component', () => { + it('should render', () => { + expect(true).toBe(true, 'This is a placeholder true') + }); +}); diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js new file mode 100644 index 0000000..a799755 --- /dev/null +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -0,0 +1,39 @@ +const data = [ + [//iPhone + {axis:"Battery Life",value:0.22}, + {axis:"Brand",value:0.28}, + {axis:"Contract Cost",value:0.29}, + {axis:"Design And Quality",value:0.17}, + {axis:"Have Internet Connectivity",value:0.22}, + {axis:"Large Screen",value:0.02}, + {axis:"Price Of Device",value:0.21}, + {axis:"To Be A Smartphone",value:0.50} + ],[//Samsung + {axis:"Battery Life",value:0.27}, + {axis:"Brand",value:0.16}, + {axis:"Contract Cost",value:0.35}, + {axis:"Design And Quality",value:0.13}, + {axis:"Have Internet Connectivity",value:0.20}, + {axis:"Large Screen",value:0.13}, + {axis:"Price Of Device",value:0.35}, + {axis:"To Be A Smartphone",value:0.38} + ],[//Nokia Smartphone + {axis:"Battery Life",value:0.26}, + {axis:"Brand",value:0.10}, + {axis:"Contract Cost",value:0.30}, + {axis:"Design And Quality",value:0.14}, + {axis:"Have Internet Connectivity",value:0.22}, + {axis:"Large Screen",value:0.04}, + {axis:"Price Of Device",value:0.41}, + {axis:"To Be A Smartphone",value:0.30} + ],[//Nokia Smartphone + {axis:"Battery Life",value:0.1}, + {axis:"Brand",value:0.1}, + {axis:"Contract Cost",value:0.1}, + {axis:"Design And Quality",value:0.1}, + {axis:"Have Internet Connectivity",value:0.1}, + {axis:"Large Screen",value:0.1}, + {axis:"Price Of Device",value:0.1}, + {axis:"To Be A Smartphone",value:0.1} + ] +]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js new file mode 100644 index 0000000..e79fd5f --- /dev/null +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -0,0 +1,124 @@ +'use strict' +class Radar { + + constructor(graphNode, data){ + this.data = this.formatData(data) + this.target = d3.select(graphNode) + + this.scale; + this.line; + + this.cfg = (()=>{ + let w = 600, + h = 600, + numAxis = data[0].length; + + return { + w: w, + h: h, + r: w / 2, + margin: {top: 20, right: 20, bottom: 20, left: 20}, + levels: 5, + max: null, // will be determined by data + labelFactor: 60, // how much further outside the radius does the label appears + wrapWidth: null, // word wrap after this number of pixels + opacityArea: 0.4, + strokeWidth: 2, + color: 'blue', + numAxis: numAxis, + angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians + shape: 'circle', //circle, polygon, square + } + })() + } + + + + + + + scalesAndLinesFunctions(){ + let that = this + this.cfg.max = d3.max(d3.merge(this.data), d => d.value) + this.scale = d3.scaleLinear() + .domain([0, this.cfg.max]) + .range([0, this.cfg.r]) + this.line = d3.radialLine() + .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d)=>{ return that.scale(d.value)}) + + } + + // setupRadar(){ + // this.target.style({ + // width: cfg.w + cfg.margin.left + cfg.margin.right, + // height: cfg.h + cfg.margin.top + cfg.margin.bottom, + // }) + + // if(cfg.shape == 'circle'){ + // //draw rings + // for(var i = 0; i < cfg.levels; i++){ + + // } + + // } else if(cfg.shape == 'polygon'){ + // //TODO + // } else if(cfg.shape =='square'){ + // //TODO + // } + // } + + //customize this function to fit data model you want + formatData(data){ + data.map((d)=>{ + d.map((v)=>{ + v.value = Math.round(v.value*100) + }) + }) + return data + } +} + + +let a; +function test(data){ + a = new Radar('svg', data) + a.scalesAndLinesFunctions() + console.log(a.cfg.max) + console.log(a.scale(34)) + a.target.attrs({ + width: a.cfg.w, + height: a.cfg.h + }) + a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) + .append('path') + .datum(data[0]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'red', + 'opacity': 0.4, + 'd' : a.line + }) + a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) + .append('path') + .datum(data[2]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'blue', + 'opacity': 0.4, + 'd' : a.line + }) + a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) + .append('path') + .datum(data[1]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'green', + 'opacity': 0.4, + 'd' : a.line + }) + +} + + +test(data) \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss new file mode 100644 index 0000000..7ad97c1 --- /dev/null +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -0,0 +1,10 @@ +body { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: "Helvetica"; +} + +.radar { + border: 1px solid black; +} \ No newline at end of file From df09e471f25a11b7be8d99e01a7a6cdae63a8037 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Thu, 9 Feb 2017 14:01:22 -0600 Subject: [PATCH 10/27] drawing axis, shapes, and rings --- .../charts-vanilla-radar/src/scripts/data.js | 9 - .../charts-vanilla-radar/src/scripts/index.js | 169 ++++++++++++------ .../src/styles/styles.scss | 8 +- 3 files changed, 119 insertions(+), 67 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index a799755..4e05c5b 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -26,14 +26,5 @@ const data = [ {axis:"Large Screen",value:0.04}, {axis:"Price Of Device",value:0.41}, {axis:"To Be A Smartphone",value:0.30} - ],[//Nokia Smartphone - {axis:"Battery Life",value:0.1}, - {axis:"Brand",value:0.1}, - {axis:"Contract Cost",value:0.1}, - {axis:"Design And Quality",value:0.1}, - {axis:"Have Internet Connectivity",value:0.1}, - {axis:"Large Screen",value:0.1}, - {axis:"Price Of Device",value:0.1}, - {axis:"To Be A Smartphone",value:0.1} ] ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index e79fd5f..49e210a 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -7,17 +7,20 @@ class Radar { this.scale; this.line; + this.axis = {} this.cfg = (()=>{ - let w = 600, - h = 600, - numAxis = data[0].length; + let w = 500, + h = w, + numAxis = data[0].length, + margin = {top: 20, right: 20, bottom: 20, left: 20}, + r = w / 2 - margin.top - margin.bottom; return { w: w, h: h, - r: w / 2, - margin: {top: 20, right: 20, bottom: 20, left: 20}, + r: r, + margin: margin, levels: 5, max: null, // will be determined by data labelFactor: 60, // how much further outside the radius does the label appears @@ -27,46 +30,99 @@ class Radar { color: 'blue', numAxis: numAxis, angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians - shape: 'circle', //circle, polygon, square + shape: 'circle', //circle, polygon, square, + dotSize: 4, //if not set, dots will not be drawn. + axisStyle: { + 'stroke': 'black', + 'stroke-width': 1, + } } })() } + draw(){ - - + } scalesAndLinesFunctions(){ let that = this this.cfg.max = d3.max(d3.merge(this.data), d => d.value) this.scale = d3.scaleLinear() - .domain([0, this.cfg.max]) - .range([0, this.cfg.r]) + .domain([0, this.cfg.max]) + .range([0, this.cfg.r]) + this.line = d3.radialLine() - .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) - .radius((d)=>{ return that.scale(d.value)}) + .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d)=>{ return that.scale(d.value)}) } - // setupRadar(){ - // this.target.style({ - // width: cfg.w + cfg.margin.left + cfg.margin.right, - // height: cfg.h + cfg.margin.top + cfg.margin.bottom, - // }) + setupRadar(){ + this.target.style({ + width: this.cfg.w, + height: this.cfg.h, + }) - // if(cfg.shape == 'circle'){ - // //draw rings - // for(var i = 0; i < cfg.levels; i++){ + this.axis.names = data[0].map( el => el.axis) + this.axis.length = this.axis.names.length - // } - // } else if(cfg.shape == 'polygon'){ - // //TODO - // } else if(cfg.shape =='square'){ - // //TODO - // } - // } + //puts 0,0 in the center of the svg + this.globalGroup = this.target.append('g').attrs({ + 'class': 'chart', + 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` + }) + + // draws the axis + if(this.cfg.axisStyle){ + this.axisGroup = this.globalGroup.append('g').attr('class','axis-lines') + this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') + .attrs({ + 'x1': 0, + 'y1': 0, + // `+ Math.PI/2 to make the Axis line up with the correct Data` + 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'class': 'axis-line', + }).styles(this.cfg.axisStyle) + } + + if(this.cfg.shape == 'circle'){ + //draw rings + let ringInterval = this.cfg.max / this.cfg.levels + var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX + console.log(ringRadiusValues) + let g = this.globalGroup.append('g').attr('class', 'grid-circle') + g.selectAll('.level').data(ringRadiusValues).enter().append('circle').attrs({ + 'fill': 'none', + 'cx': 0, + 'cy': 0, + 'class': 'label', + 'r': (d) => {return this.scale(d)}, + 'stroke': 'black', + 'stroke-width': 1, + 'stroke-dasharray': '1 3', + 'opacity': 0.5 + }) + + g.selectAll('.axis-label').data(ringRadiusValues).enter().append('text').attrs({ + 'class': 'axis-label', + 'x': '0', + 'y': (d) => {return -this.scale(d)}, + 'dy': '-0.3em', + 'dx': '0.5em', + 'font-size': '0.8em', + 'fill': 'grey', + }).text((d) => {return d+'%'}) + + //text indicating % for each level + } else if(this.cfg.shape == 'polygon'){ + //TODO + } else if(this.cfg.shape =='square'){ + //TODO + } + } //customize this function to fit data model you want formatData(data){ @@ -84,40 +140,39 @@ let a; function test(data){ a = new Radar('svg', data) a.scalesAndLinesFunctions() - console.log(a.cfg.max) - console.log(a.scale(34)) a.target.attrs({ width: a.cfg.w, height: a.cfg.h }) - a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) - .append('path') - .datum(data[0]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'red', - 'opacity': 0.4, - 'd' : a.line - }) - a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) - .append('path') - .datum(data[2]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'blue', - 'opacity': 0.4, - 'd' : a.line - }) - a.target.append('g').attr('transform', `translate(${a.cfg.w/2} ${a.cfg.h/2})`) - .append('path') - .datum(data[1]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'green', - 'opacity': 0.4, - 'd' : a.line - }) - + a.setupRadar() + + let g = a.globalGroup.append('g').attr('class', 'lines') + g.append('path') + .datum(data[1]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'red', + 'opacity': 1, + 'd' : a.line, + 'class': 'iphone' + }) + g.append('path') + .datum(data[2]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'blue', + 'opacity': 1, + 'd' : a.line + }) + g.append('path') + .datum(data[0]) + .attrs({ + 'stroke-width': 1.5, + 'fill': 'green', + 'opacity': 1, + 'd' : a.line, + 'class': 'iphone' + }) } diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss index 7ad97c1..fdc82a6 100644 --- a/packages/charts-vanilla-radar/src/styles/styles.scss +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -7,4 +7,10 @@ body { .radar { border: 1px solid black; -} \ No newline at end of file + + path { + mix-blend-mode: multiply; + opacity: 0.3; + } +} + From e17aa164eb2fdf13b806605de67f1addb59ffa00 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Thu, 9 Feb 2017 14:16:57 -0600 Subject: [PATCH 11/27] added draw function --- .../charts-vanilla-radar/src/scripts/index.js | 101 ++++++++++-------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 49e210a..91370a7 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -10,11 +10,11 @@ class Radar { this.axis = {} this.cfg = (()=>{ - let w = 500, + let w = 800, h = w, numAxis = data[0].length, - margin = {top: 20, right: 20, bottom: 20, left: 20}, - r = w / 2 - margin.top - margin.bottom; + margin = {top: 50, right: 50, bottom: 50, left: 40}, + r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]), return { w: w, @@ -42,7 +42,19 @@ class Radar { draw(){ + this.scalesAndLinesFunctions() + this.wrapper = this.setupRadar() + var shapes = this.wrapper.append('g').attr('class','shapes') + for(var i = 0; i < data.length; i++){ + shapes.append('path').datum(data[i]).attrs({ + 'stroke-width': 1.5, + 'fill': 'red', + 'opacity': 1, + 'd' : a.line, + 'class': 'iphone' + }) + } } scalesAndLinesFunctions(){ @@ -59,31 +71,32 @@ class Radar { } setupRadar(){ - this.target.style({ + this.target.styles({ width: this.cfg.w, height: this.cfg.h, }) + let globalGroup = this.target.append('g').attrs({ + 'class': 'chart', + 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` + }) this.axis.names = data[0].map( el => el.axis) this.axis.length = this.axis.names.length //puts 0,0 in the center of the svg - this.globalGroup = this.target.append('g').attrs({ - 'class': 'chart', - 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` - }) + // draws the axis if(this.cfg.axisStyle){ - this.axisGroup = this.globalGroup.append('g').attr('class','axis-lines') + this.axisGroup = globalGroup.append('g').attr('class','axis-lines') this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, - 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.1) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, 'class': 'axis-line', }).styles(this.cfg.axisStyle) } @@ -93,7 +106,7 @@ class Radar { let ringInterval = this.cfg.max / this.cfg.levels var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX console.log(ringRadiusValues) - let g = this.globalGroup.append('g').attr('class', 'grid-circle') + let g = globalGroup.append('g').attr('class', 'grid-circle') g.selectAll('.level').data(ringRadiusValues).enter().append('circle').attrs({ 'fill': 'none', 'cx': 0, @@ -122,6 +135,7 @@ class Radar { } else if(this.cfg.shape =='square'){ //TODO } + return globalGroup } //customize this function to fit data model you want @@ -139,40 +153,35 @@ class Radar { let a; function test(data){ a = new Radar('svg', data) - a.scalesAndLinesFunctions() - a.target.attrs({ - width: a.cfg.w, - height: a.cfg.h - }) - a.setupRadar() - - let g = a.globalGroup.append('g').attr('class', 'lines') - g.append('path') - .datum(data[1]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'red', - 'opacity': 1, - 'd' : a.line, - 'class': 'iphone' - }) - g.append('path') - .datum(data[2]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'blue', - 'opacity': 1, - 'd' : a.line - }) - g.append('path') - .datum(data[0]) - .attrs({ - 'stroke-width': 1.5, - 'fill': 'green', - 'opacity': 1, - 'd' : a.line, - 'class': 'iphone' - }) + a.draw() + + // let g = a.globalGroup.append('g').attr('class', 'lines') + // g.append('path') + // .datum(data[1]) + // .attrs({ + // 'stroke-width': 1.5, + // 'fill': 'red', + // 'opacity': 1, + // 'd' : a.line, + // 'class': 'iphone' + // }) + // g.append('path') + // .datum(data[2]) + // .attrs({ + // 'stroke-width': 1.5, + // 'fill': 'blue', + // 'opacity': 1, + // 'd' : a.line + // }) + // g.append('path') + // .datum(data[0]) + // .attrs({ + // 'stroke-width': 1.5, + // 'fill': 'green', + // 'opacity': 1, + // 'd' : a.line, + // 'class': 'iphone' + // }) } From 7ea67724ac357be7387154b142631889ea6abe5a Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Thu, 9 Feb 2017 16:32:30 -0600 Subject: [PATCH 12/27] adding labels --- .../charts-vanilla-radar/src/scripts/index.js | 178 ++++++++++-------- .../src/styles/styles.scss | 7 +- 2 files changed, 105 insertions(+), 80 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 91370a7..51f1c06 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -1,36 +1,38 @@ 'use strict' class Radar { - constructor(graphNode, data){ + constructor(graphNode, data, opts){ this.data = this.formatData(data) this.target = d3.select(graphNode) this.scale; this.line; + this.color = d3.scaleOrdinal() + .range(opts.colors) this.axis = {} this.cfg = (()=>{ - let w = 800, + let w = opts.size || 500, h = w, numAxis = data[0].length, - margin = {top: 50, right: 50, bottom: 50, left: 40}, - r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]), + margin = opts.margins, + r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); return { w: w, h: h, r: r, margin: margin, - levels: 5, + units: opts.units, + levels: opts.levels, max: null, // will be determined by data - labelFactor: 60, // how much further outside the radius does the label appears - wrapWidth: null, // word wrap after this number of pixels - opacityArea: 0.4, + labelFactor: opts.labelFactor, // how much further outside the radius does the label appears + wrapWidth: opts.wrapWidth || 60, // word wrap after this number of pixels + opacityArea: opts.opacityArea, strokeWidth: 2, - color: 'blue', numAxis: numAxis, angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians - shape: 'circle', //circle, polygon, square, + shape: opts.shape || 'circle', //circle, polygon, square, dotSize: 4, //if not set, dots will not be drawn. axisStyle: { 'stroke': 'black', @@ -38,24 +40,38 @@ class Radar { } } })() - } + } // constructor draw(){ + //setup this.scalesAndLinesFunctions() this.wrapper = this.setupRadar() - var shapes = this.wrapper.append('g').attr('class','shapes') - for(var i = 0; i < data.length; i++){ - shapes.append('path').datum(data[i]).attrs({ - 'stroke-width': 1.5, - 'fill': 'red', - 'opacity': 1, - 'd' : a.line, - 'class': 'iphone' - }) + //draw blobs & dots + var blobs = this.wrapper.append('g').attr('class','blobs') + for(let i = 0; i < data.length; i++){ + //blob + let g = blobs.append('g').attr('class','blob-'+i) + g.append('path').datum(data[i]).attrs({ + 'stroke-width': 1.5, + 'd' : a.line, + 'fill': () => { return this.color(i)} + }) + //dots + g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ + 'stroke-width': 1.5, + 'cx': (d,i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, + 'cy': (d,i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, + 'r': this.cfg.dotSize, + 'fill': () => { return this.color(i)}, + 'opacity': 0.4, + 'class': 'dot', + }) } - } + + + }//draw scalesAndLinesFunctions(){ let that = this @@ -68,7 +84,7 @@ class Radar { .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) .radius((d)=>{ return that.scale(d.value)}) - } + }//scalesAndLinesFunctions setupRadar(){ this.target.styles({ @@ -95,8 +111,9 @@ class Radar { 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, - 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, + 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'opacity': 0.3, 'class': 'axis-line', }).styles(this.cfg.axisStyle) } @@ -105,38 +122,58 @@ class Radar { //draw rings let ringInterval = this.cfg.max / this.cfg.levels var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX - console.log(ringRadiusValues) + let g = globalGroup.append('g').attr('class', 'grid-circle') - g.selectAll('.level').data(ringRadiusValues).enter().append('circle').attrs({ - 'fill': 'none', - 'cx': 0, - 'cy': 0, - 'class': 'label', - 'r': (d) => {return this.scale(d)}, - 'stroke': 'black', - 'stroke-width': 1, - 'stroke-dasharray': '1 3', - 'opacity': 0.5 - }) + g.append('g').attr('class', 'rings') + .selectAll('.ring').data(ringRadiusValues) + .enter().append('circle').attrs({ + 'fill': 'none', + 'cx': 0, + 'cy': 0, + 'class': 'label', + 'r': (d) => {return this.scale(d)}, + 'stroke': 'black', + 'stroke-width': 1, + 'stroke-dasharray': '1 3', + 'opacity': 0.5, + 'class': 'ring' + }) + + //add the unit labels on the rings + g.append('g').attr('class', 'rings-labels') + .selectAll('.ring-label').data(ringRadiusValues) + .enter().append('text').attrs({ + 'class': 'ring-label', + 'x': '0', + 'y': (d) => {return -this.scale(d)}, + 'dy': '-0.3em', + 'dx': '0.5em', + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4 + }) + .text((d) => {return d + this.cfg.units}) + g.append('g').attr('class', 'legend') + .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ + 'class': 'axis-label', + 'x': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, + 'y': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4, + 'text-anchor': "middle" + }) + .html((d) => { return this.wrap(d.axis)}) + + - g.selectAll('.axis-label').data(ringRadiusValues).enter().append('text').attrs({ - 'class': 'axis-label', - 'x': '0', - 'y': (d) => {return -this.scale(d)}, - 'dy': '-0.3em', - 'dx': '0.5em', - 'font-size': '0.8em', - 'fill': 'grey', - }).text((d) => {return d+'%'}) - - //text indicating % for each level } else if(this.cfg.shape == 'polygon'){ //TODO } else if(this.cfg.shape =='square'){ //TODO } return globalGroup - } + }//setupRadar //customize this function to fit data model you want formatData(data){ @@ -146,42 +183,27 @@ class Radar { }) }) return data - } + } // formatData + + wrap(text) { + return text.split(' ').join('\n') + }//wrap } let a; function test(data){ - a = new Radar('svg', data) + a = new Radar('svg', data, { + size: 800, + margins: {top: 70, right: 70, bottom: 70, left: 70}, + colors: ["#EDC951","#CC333F","#00A0B0"], + units: '%', + levels: 5, + opacityArea: 0.4, + labelFactor: 50, + shape: 'circle', + }) a.draw() - - // let g = a.globalGroup.append('g').attr('class', 'lines') - // g.append('path') - // .datum(data[1]) - // .attrs({ - // 'stroke-width': 1.5, - // 'fill': 'red', - // 'opacity': 1, - // 'd' : a.line, - // 'class': 'iphone' - // }) - // g.append('path') - // .datum(data[2]) - // .attrs({ - // 'stroke-width': 1.5, - // 'fill': 'blue', - // 'opacity': 1, - // 'd' : a.line - // }) - // g.append('path') - // .datum(data[0]) - // .attrs({ - // 'stroke-width': 1.5, - // 'fill': 'green', - // 'opacity': 1, - // 'd' : a.line, - // 'class': 'iphone' - // }) } diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss index fdc82a6..58f6547 100644 --- a/packages/charts-vanilla-radar/src/styles/styles.scss +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -8,9 +8,12 @@ body { .radar { border: 1px solid black; - path { + g[class*='blob-'] { mix-blend-mode: multiply; - opacity: 0.3; + + path { + opacity: 0.5; + } } } From fa040b7063224776e7aadce6984a8edbef168d89 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Fri, 10 Feb 2017 10:01:05 -0600 Subject: [PATCH 13/27] added label wrapping --- .../charts-vanilla-radar/src/scripts/index.js | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 51f1c06..7c925cc 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -161,9 +161,11 @@ class Radar { 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, - 'text-anchor': "middle" + 'text-anchor': 'middle', }) - .html((d) => { return this.wrap(d.axis)}) + .text((d)=>{return d.axis}) + .call(this.wrap, this.cfg.wrapWidth) + @@ -185,8 +187,33 @@ class Radar { return data } // formatData - wrap(text) { - return text.split(' ').join('\n') + + //Taken from http://bl.ocks.org/mbostock/7555321 + //Wraps SVG text + wrap(text, width) { + text.each(function() { + var text = d3.select(this), + words = text.text().split(/\s+/).reverse(), + word, + line = [], + lineNumber = 0, + lineHeight = 1.4, // ems + y = text.attr("y"), + x = text.attr("x"), + dy = parseFloat(text.attr("dy")) || 0, + tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em"); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(" ")); + if (tspan.node().getComputedTextLength() > width) { + line.pop(); + tspan.text(line.join(" ")); + line = [word]; + tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); + } + } + }); }//wrap } @@ -202,6 +229,7 @@ function test(data){ opacityArea: 0.4, labelFactor: 50, shape: 'circle', + wrapWidth: 60 }) a.draw() } From de21e2dccc1df9698a3699193cd850403498c978 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 15:47:47 -0600 Subject: [PATCH 14/27] removing linting errors --- packages/charts-vanilla-radar/gulpfile.js | 44 ++--- .../src/__tests__/Chart-test.js | 4 +- .../charts-vanilla-radar/src/scripts/data.js | 50 +++--- .../charts-vanilla-radar/src/scripts/index.js | 151 +++++++++--------- .../src/styles/styles.scss | 8 +- 5 files changed, 123 insertions(+), 134 deletions(-) diff --git a/packages/charts-vanilla-radar/gulpfile.js b/packages/charts-vanilla-radar/gulpfile.js index 9ba14f7..b4d3502 100644 --- a/packages/charts-vanilla-radar/gulpfile.js +++ b/packages/charts-vanilla-radar/gulpfile.js @@ -1,43 +1,43 @@ -var gulp = require('gulp'), - babel = require('gulp-babel'), - browserSync = require('browser-sync') - sass = require('gulp-sass'), - plumber = require('gulp-plumber'), - sourcemaps = require('gulp-sourcemaps'); +var gulp = require('gulp'); +var babel = require('gulp-babel'); +var browserSync = require('browser-sync'); +var sass = require('gulp-sass'); +var plumber = require('gulp-plumber'); +var sourcemaps = require('gulp-sourcemaps'); -gulp.task('serve', ['compile-js', 'compile-scss'], function(){ +gulp.task('serve', ['compile-js', 'compile-scss'], function() { browserSync.init({ server: { - baseDir:'./' - } - }) + baseDir:'./', + }, + }); - gulp.watch(['src/scripts/*.js'], ['compile-js']) - gulp.watch(["src/styles/*.scss"], ['compile-scss']); - gulp.watch(['*.html','src/styles/*.scss','src/scripts/*.js']).on('change', browserSync.reload) + gulp.watch(['src/scripts/*.js'], ['compile-js']); + gulp.watch(['src/styles/*.scss'], ['compile-scss']); + gulp.watch(['*.html', 'src/styles/*.scss', 'src/scripts/*.js']).on('change', browserSync.reload); -}) +}); -gulp.task('compile-js', function(){ +gulp.task('compile-js', function() { return gulp.src('src/scripts/*.js') .pipe(plumber()) .pipe(sourcemaps.init()) .pipe(babel({ - presets:['es2015'] + presets:['es2015'], })) .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')) -}) + .pipe(gulp.dest('lib')); +}); -gulp.task('compile-scss', function(){ +gulp.task('compile-scss', function() { return gulp.src('src/styles/*.scss') .pipe(plumber()) .pipe(sourcemaps.init()) .pipe(sass().on('error', sass.logError)) .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')) -}) + .pipe(gulp.dest('lib')); +}); -gulp.task('default', ['serve']) \ No newline at end of file +gulp.task('default', ['serve']); \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js index 890958c..54c9b46 100644 --- a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js +++ b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js @@ -1,7 +1,7 @@ -import Chart from '../'; +// import Chart from '../'; describe('Radar Component', () => { it('should render', () => { - expect(true).toBe(true, 'This is a placeholder true') + expect(true).toBe(true, 'This is a placeholder true'); }); }); diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index 4e05c5b..cfc38c7 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -1,30 +1,26 @@ const data = [ [//iPhone - {axis:"Battery Life",value:0.22}, - {axis:"Brand",value:0.28}, - {axis:"Contract Cost",value:0.29}, - {axis:"Design And Quality",value:0.17}, - {axis:"Have Internet Connectivity",value:0.22}, - {axis:"Large Screen",value:0.02}, - {axis:"Price Of Device",value:0.21}, - {axis:"To Be A Smartphone",value:0.50} - ],[//Samsung - {axis:"Battery Life",value:0.27}, - {axis:"Brand",value:0.16}, - {axis:"Contract Cost",value:0.35}, - {axis:"Design And Quality",value:0.13}, - {axis:"Have Internet Connectivity",value:0.20}, - {axis:"Large Screen",value:0.13}, - {axis:"Price Of Device",value:0.35}, - {axis:"To Be A Smartphone",value:0.38} - ],[//Nokia Smartphone - {axis:"Battery Life",value:0.26}, - {axis:"Brand",value:0.10}, - {axis:"Contract Cost",value:0.30}, - {axis:"Design And Quality",value:0.14}, - {axis:"Have Internet Connectivity",value:0.22}, - {axis:"Large Screen",value:0.04}, - {axis:"Price Of Device",value:0.41}, - {axis:"To Be A Smartphone",value:0.30} - ] + {axis:'Apdex', value:0.80}, + {axis:'Alerts', value:0.66}, + {axis:'Request Volume', value:0.35}, + {axis:'Memory', value:0.54}, + {axis:'CPU', value:0.38}, + {axis:'Disk', value:0.23}, + ], + [//Samsung + {axis:'Apdex', value:0.80}, + {axis:'Alerts', value:0.66}, + {axis:'Request Volume', value:0.35}, + {axis:'Memory', value:0.23}, + {axis:'CPU', value:0.36}, + {axis:'Disk', value:0.13}, + ], + [//Nokia Smartphone + {axis:'Apdex', value:0.26}, + {axis:'Alerts', value:0.10}, + {axis:'Request Volume', value:0.30}, + {axis:'Memory', value:0.14}, + {axis:'CPU', value:0.22}, + {axis:'Disk', value:0.04}, + ], ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 7c925cc..b9d6c21 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -1,22 +1,22 @@ -'use strict' +'use strict'; class Radar { - constructor(graphNode, data, opts){ - this.data = this.formatData(data) - this.target = d3.select(graphNode) + constructor(graphNode, data, opts) { + this.data = this.formatData(data); + this.target = d3.select(graphNode); this.scale; this.line; this.color = d3.scaleOrdinal() - .range(opts.colors) - this.axis = {} + .range(opts.colors); + this.axis = {}; - this.cfg = (()=>{ - let w = opts.size || 500, - h = w, - numAxis = data[0].length, - margin = opts.margins, - r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); + this.cfg = (() => { + let w = opts.size || 500; + let h = w; + let numAxis = data[0].length; + let margin = opts.margins; + let r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); return { w: w, @@ -37,107 +37,104 @@ class Radar { axisStyle: { 'stroke': 'black', 'stroke-width': 1, - } - } - })() + }, + }; + })(); } // constructor - draw(){ + draw() { //setup - this.scalesAndLinesFunctions() - this.wrapper = this.setupRadar() + this.scalesAndLinesFunctions(); + this.wrapper = this.setupRadar(); //draw blobs & dots - var blobs = this.wrapper.append('g').attr('class','blobs') - for(let i = 0; i < data.length; i++){ + let blobs = this.wrapper.append('g').attr('class', 'blobs'); + for (let i = 0; i < data.length; i++) { //blob - let g = blobs.append('g').attr('class','blob-'+i) - g.append('path').datum(data[i]).attrs({ - 'stroke-width': 1.5, - 'd' : a.line, - 'fill': () => { return this.color(i)} - }) - //dots - g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ - 'stroke-width': 1.5, - 'cx': (d,i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, - 'cy': (d,i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, - 'r': this.cfg.dotSize, - 'fill': () => { return this.color(i)}, - 'opacity': 0.4, - 'class': 'dot', - }) + let g = blobs.append('g').attr('class', 'blob-' + i); + g.append('path').datum(data[i]).attrs({ + 'stroke-width': 1.5, + 'd' : a.line, + 'fill': () => { return this.color(i); }, + }); + //dots + g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ + 'stroke-width': 1.5, + 'cx': (d, i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'cy': (d, i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'r': this.cfg.dotSize, + 'fill': () => { return this.color(i);}, + 'opacity': 0.4, + 'class': 'dot', + }); } - - }//draw - scalesAndLinesFunctions(){ - let that = this - this.cfg.max = d3.max(d3.merge(this.data), d => d.value) + scalesAndLinesFunctions() { + let that = this; + this.cfg.max = d3.max(d3.merge(this.data), d => d.value); this.scale = d3.scaleLinear() .domain([0, this.cfg.max]) - .range([0, this.cfg.r]) + .range([0, this.cfg.r]); this.line = d3.radialLine() - .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) - .radius((d)=>{ return that.scale(d.value)}) + .angle((d, i) => { return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d) => { return that.scale(d.value);}); }//scalesAndLinesFunctions - setupRadar(){ + setupRadar() { this.target.styles({ width: this.cfg.w, height: this.cfg.h, - }) + }); let globalGroup = this.target.append('g').attrs({ 'class': 'chart', - 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` - }) + 'transform': `translate(${this.cfg.w / 2} ${this.cfg.h / 2})`, + }); - this.axis.names = data[0].map( el => el.axis) - this.axis.length = this.axis.names.length + this.axis.names = data[0].map( el => el.axis); + this.axis.length = this.axis.names.length; //puts 0,0 in the center of the svg // draws the axis - if(this.cfg.axisStyle){ - this.axisGroup = globalGroup.append('g').attr('class','axis-lines') + if (this.cfg.axisStyle) { + this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, - 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, + 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, 'opacity': 0.3, 'class': 'axis-line', - }).styles(this.cfg.axisStyle) + }).styles(this.cfg.axisStyle); } - if(this.cfg.shape == 'circle'){ + if (this.cfg.shape == 'circle') { //draw rings - let ringInterval = this.cfg.max / this.cfg.levels - var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX + let ringInterval = this.cfg.max / this.cfg.levels; + var ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX - let g = globalGroup.append('g').attr('class', 'grid-circle') + let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') .selectAll('.ring').data(ringRadiusValues) .enter().append('circle').attrs({ 'fill': 'none', 'cx': 0, 'cy': 0, - 'class': 'label', - 'r': (d) => {return this.scale(d)}, + 'r': (d) => { return this.scale(d); }, 'stroke': 'black', 'stroke-width': 1, - 'stroke-dasharray': '1 3', - 'opacity': 0.5, - 'class': 'ring' - }) + 'stroke-dasharray': '5 5', + 'opacity': 0.2, + 'class': 'ring', + }); //add the unit labels on the rings g.append('g').attr('class', 'rings-labels') @@ -145,36 +142,36 @@ class Radar { .enter().append('text').attrs({ 'class': 'ring-label', 'x': '0', - 'y': (d) => {return -this.scale(d)}, + 'y': (d) => {return -this.scale(d);}, 'dy': '-0.3em', 'dx': '0.5em', 'font-size': '0.8em', 'fill': 'grey', - 'opacity': 0.4 + 'opacity': 0.4, }) - .text((d) => {return d + this.cfg.units}) + .text((d) => {return d + this.cfg.units;}); g.append('g').attr('class', 'legend') .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ 'class': 'axis-label', - 'x': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, - 'y': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, + 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, 'text-anchor': 'middle', }) - .text((d)=>{return d.axis}) - .call(this.wrap, this.cfg.wrapWidth) + .text((d) => {return d.axis;}) + .call(this.wrap, this.cfg.wrapWidth); - } else if(this.cfg.shape == 'polygon'){ + } else if (this.cfg.shape == 'polygon') { //TODO - } else if(this.cfg.shape =='square'){ + } else if (this.cfg.shape == 'square') { //TODO } - return globalGroup + return globalGroup; }//setupRadar //customize this function to fit data model you want @@ -188,7 +185,7 @@ class Radar { } // formatData - //Taken from http://bl.ocks.org/mbostock/7555321 + //Adapted from http://bl.ocks.org/mbostock/7555321 //Wraps SVG text wrap(text, width) { text.each(function() { @@ -227,7 +224,7 @@ function test(data){ units: '%', levels: 5, opacityArea: 0.4, - labelFactor: 50, + labelFactor: 30, shape: 'circle', wrapWidth: 60 }) diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss index 58f6547..df66632 100644 --- a/packages/charts-vanilla-radar/src/styles/styles.scss +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -8,12 +8,8 @@ body { .radar { border: 1px solid black; - g[class*='blob-'] { - mix-blend-mode: multiply; - - path { - opacity: 0.5; - } + path { + opacity: 0.5; } } From d22bbd18c850c27a4b259a8109c8ed9369bbd2ac Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 15:56:38 -0600 Subject: [PATCH 15/27] more linting errors fixed --- .../charts-vanilla-radar/src/scripts/index.js | 95 +++++++++++-------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index b9d6c21..828f879 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -51,6 +51,7 @@ class Radar { //draw blobs & dots let blobs = this.wrapper.append('g').attr('class', 'blobs'); for (let i = 0; i < data.length; i++) { + //blob let g = blobs.append('g').attr('class', 'blob-' + i); g.append('path').datum(data[i]).attrs({ @@ -58,6 +59,7 @@ class Radar { 'd' : a.line, 'fill': () => { return this.color(i); }, }); + //dots g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ 'stroke-width': 1.5, @@ -97,10 +99,6 @@ class Radar { this.axis.names = data[0].map( el => el.axis); this.axis.length = this.axis.names.length; - - //puts 0,0 in the center of the svg - - // draws the axis if (this.cfg.axisStyle) { this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); @@ -109,8 +107,12 @@ class Radar { 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, - 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, + 'x2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice * i + Math.PI / 2); + }, 'opacity': 0.3, 'class': 'axis-line', }).styles(this.cfg.axisStyle); @@ -153,8 +155,14 @@ class Radar { g.append('g').attr('class', 'legend') .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ 'class': 'axis-label', - 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, - 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'x': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.sin(this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.cos(this.cfg.angleSlice * i + Math.PI / 2); + }, 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, @@ -175,13 +183,13 @@ class Radar { }//setupRadar //customize this function to fit data model you want - formatData(data){ - data.map((d)=>{ - d.map((v)=>{ - v.value = Math.round(v.value*100) - }) - }) - return data + formatData(data) { + data.map((d) => { + d.map((v) => { + v.value = Math.round(v.value * 100); + }); + }); + return data; } // formatData @@ -189,47 +197,54 @@ class Radar { //Wraps SVG text wrap(text, width) { text.each(function() { - var text = d3.select(this), - words = text.text().split(/\s+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.4, // ems - y = text.attr("y"), - x = text.attr("x"), - dy = parseFloat(text.attr("dy")) || 0, - tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em"); - - while (word = words.pop()) { - line.push(word); - tspan.text(line.join(" ")); - if (tspan.node().getComputedTextLength() > width) { + var text = d3.select(this), + words = text.text().split(/\s+/).reverse(), + word, + line = [], + lineNumber = 0, + lineHeight = 1.4, // ems + y = text.attr('y'), + x = text.attr('x'), + dy = parseFloat(text.attr('dy')) || 0, + tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(' ')); + if (tspan.node().getComputedTextLength() > width) { line.pop(); - tspan.text(line.join(" ")); + tspan.text(line.join(' ')); line = [word]; - tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); - } + tspan = text.append('tspan') + .attrs({ + 'x': x, + 'y': y, + 'dy': ++lineNumber * lineHeight + dy + 'em', + }) + .text(word); } - }); + } + }); }//wrap } let a; -function test(data){ +function test(data) { a = new Radar('svg', data, { size: 800, margins: {top: 70, right: 70, bottom: 70, left: 70}, - colors: ["#EDC951","#CC333F","#00A0B0"], + colors: ['#EDC951', '#CC333F', '#00A0B0'], units: '%', levels: 5, opacityArea: 0.4, labelFactor: 30, shape: 'circle', - wrapWidth: 60 - }) - a.draw() + wrapWidth: 60, + }); + + a.draw(); } -test(data) \ No newline at end of file +test(data); \ No newline at end of file From 9bf2dfa33e78a043f77676139ccd3210395f529b Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 16:09:43 -0600 Subject: [PATCH 16/27] updated data and more lint errors --- .../charts-vanilla-radar/src/scripts/data.js | 8 +++++- .../charts-vanilla-radar/src/scripts/index.js | 26 +++++++++---------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index cfc38c7..1e52c32 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -6,14 +6,18 @@ const data = [ {axis:'Memory', value:0.54}, {axis:'CPU', value:0.38}, {axis:'Disk', value:0.23}, + {axis:'Network', value:0.23}, + {axis:'Heap', value:0.16}, ], [//Samsung - {axis:'Apdex', value:0.80}, + {axis:'Apdex', value:0.58}, {axis:'Alerts', value:0.66}, {axis:'Request Volume', value:0.35}, {axis:'Memory', value:0.23}, {axis:'CPU', value:0.36}, {axis:'Disk', value:0.13}, + {axis:'Network', value:0.11}, + {axis:'Heap', value:0.35}, ], [//Nokia Smartphone {axis:'Apdex', value:0.26}, @@ -22,5 +26,7 @@ const data = [ {axis:'Memory', value:0.14}, {axis:'CPU', value:0.22}, {axis:'Disk', value:0.04}, + {axis:'Network', value:0.23}, + {axis:'Heap', value:0.41}, ], ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 828f879..4f16611 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -56,7 +56,7 @@ class Radar { let g = blobs.append('g').attr('class', 'blob-' + i); g.append('path').datum(data[i]).attrs({ 'stroke-width': 1.5, - 'd' : a.line, + 'd' : this.line, 'fill': () => { return this.color(i); }, }); @@ -101,7 +101,7 @@ class Radar { // draws the axis if (this.cfg.axisStyle) { - this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); + this.axisGroup = globalGroup.append('g').attr('class', 'axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, @@ -121,7 +121,7 @@ class Radar { if (this.cfg.shape == 'circle') { //draw rings let ringInterval = this.cfg.max / this.cfg.levels; - var ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX + let ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse(); let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') @@ -197,16 +197,16 @@ class Radar { //Wraps SVG text wrap(text, width) { text.each(function() { - var text = d3.select(this), - words = text.text().split(/\s+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.4, // ems - y = text.attr('y'), - x = text.attr('x'), - dy = parseFloat(text.attr('dy')) || 0, - tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + let text = d3.select(this); + let words = text.text().split(/\s+/).reverse(); + let word; + let line = []; + let lineNumber = 0; + let lineHeight = 1.4; // ems + let y = text.attr('y'); + let x = text.attr('x'); + let dy = parseFloat(text.attr('dy')) || 0; + let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); while (word = words.pop()) { line.push(word); From 673a87cb2683c0180092cef305caa14beb15c831 Mon Sep 17 00:00:00 2001 From: James Dow Date: Tue, 21 Feb 2017 18:08:51 -0600 Subject: [PATCH 17/27] updated to use commonjs --- packages/charts-vanilla-radar/src/scripts/data.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index 1e52c32..fc7b136 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -1,4 +1,4 @@ -const data = [ +export default [ [//iPhone {axis:'Apdex', value:0.80}, {axis:'Alerts', value:0.66}, @@ -29,4 +29,4 @@ const data = [ {axis:'Network', value:0.23}, {axis:'Heap', value:0.41}, ], -]; \ No newline at end of file +]; From 25e5c752376db8a8ba80086b8cdc60c64c10b312 Mon Sep 17 00:00:00 2001 From: James Dow Date: Tue, 21 Feb 2017 18:09:39 -0600 Subject: [PATCH 18/27] replaced with the client side build --- .../charts-vanilla-radar/src/scripts/index.js | 266 ++---------------- 1 file changed, 16 insertions(+), 250 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 4f16611..f316877 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -1,250 +1,16 @@ -'use strict'; -class Radar { - - constructor(graphNode, data, opts) { - this.data = this.formatData(data); - this.target = d3.select(graphNode); - - this.scale; - this.line; - this.color = d3.scaleOrdinal() - .range(opts.colors); - this.axis = {}; - - this.cfg = (() => { - let w = opts.size || 500; - let h = w; - let numAxis = data[0].length; - let margin = opts.margins; - let r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); - - return { - w: w, - h: h, - r: r, - margin: margin, - units: opts.units, - levels: opts.levels, - max: null, // will be determined by data - labelFactor: opts.labelFactor, // how much further outside the radius does the label appears - wrapWidth: opts.wrapWidth || 60, // word wrap after this number of pixels - opacityArea: opts.opacityArea, - strokeWidth: 2, - numAxis: numAxis, - angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians - shape: opts.shape || 'circle', //circle, polygon, square, - dotSize: 4, //if not set, dots will not be drawn. - axisStyle: { - 'stroke': 'black', - 'stroke-width': 1, - }, - }; - })(); - } // constructor - - - draw() { - //setup - this.scalesAndLinesFunctions(); - this.wrapper = this.setupRadar(); - - //draw blobs & dots - let blobs = this.wrapper.append('g').attr('class', 'blobs'); - for (let i = 0; i < data.length; i++) { - - //blob - let g = blobs.append('g').attr('class', 'blob-' + i); - g.append('path').datum(data[i]).attrs({ - 'stroke-width': 1.5, - 'd' : this.line, - 'fill': () => { return this.color(i); }, - }); - - //dots - g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ - 'stroke-width': 1.5, - 'cx': (d, i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, - 'cy': (d, i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, - 'r': this.cfg.dotSize, - 'fill': () => { return this.color(i);}, - 'opacity': 0.4, - 'class': 'dot', - }); - } - }//draw - - scalesAndLinesFunctions() { - let that = this; - this.cfg.max = d3.max(d3.merge(this.data), d => d.value); - this.scale = d3.scaleLinear() - .domain([0, this.cfg.max]) - .range([0, this.cfg.r]); - - this.line = d3.radialLine() - .angle((d, i) => { return -(i * that.cfg.angleSlice) + Math.PI / 2;}) - .radius((d) => { return that.scale(d.value);}); - - }//scalesAndLinesFunctions - - setupRadar() { - this.target.styles({ - width: this.cfg.w, - height: this.cfg.h, - }); - let globalGroup = this.target.append('g').attrs({ - 'class': 'chart', - 'transform': `translate(${this.cfg.w / 2} ${this.cfg.h / 2})`, - }); - - this.axis.names = data[0].map( el => el.axis); - this.axis.length = this.axis.names.length; - - // draws the axis - if (this.cfg.axisStyle) { - this.axisGroup = globalGroup.append('g').attr('class', 'axis-lines'); - this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') - .attrs({ - 'x1': 0, - 'y1': 0, - // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d, i) => { - return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice * i + Math.PI / 2); - }, - 'y2': (d, i) => { - return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice * i + Math.PI / 2); - }, - 'opacity': 0.3, - 'class': 'axis-line', - }).styles(this.cfg.axisStyle); - } - - if (this.cfg.shape == 'circle') { - //draw rings - let ringInterval = this.cfg.max / this.cfg.levels; - let ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse(); - - let g = globalGroup.append('g').attr('class', 'grid-circle'); - g.append('g').attr('class', 'rings') - .selectAll('.ring').data(ringRadiusValues) - .enter().append('circle').attrs({ - 'fill': 'none', - 'cx': 0, - 'cy': 0, - 'r': (d) => { return this.scale(d); }, - 'stroke': 'black', - 'stroke-width': 1, - 'stroke-dasharray': '5 5', - 'opacity': 0.2, - 'class': 'ring', - }); - - //add the unit labels on the rings - g.append('g').attr('class', 'rings-labels') - .selectAll('.ring-label').data(ringRadiusValues) - .enter().append('text').attrs({ - 'class': 'ring-label', - 'x': '0', - 'y': (d) => {return -this.scale(d);}, - 'dy': '-0.3em', - 'dx': '0.5em', - 'font-size': '0.8em', - 'fill': 'grey', - 'opacity': 0.4, - }) - .text((d) => {return d + this.cfg.units;}); - g.append('g').attr('class', 'legend') - .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ - 'class': 'axis-label', - 'x': (d, i) => { - return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * - Math.sin(this.cfg.angleSlice * i + Math.PI / 2); - }, - 'y': (d, i) => { - return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * - Math.cos(this.cfg.angleSlice * i + Math.PI / 2); - }, - 'font-size': '0.8em', - 'fill': 'grey', - 'opacity': 0.4, - 'text-anchor': 'middle', - }) - .text((d) => {return d.axis;}) - .call(this.wrap, this.cfg.wrapWidth); - - - - - } else if (this.cfg.shape == 'polygon') { - //TODO - } else if (this.cfg.shape == 'square') { - //TODO - } - return globalGroup; - }//setupRadar - - //customize this function to fit data model you want - formatData(data) { - data.map((d) => { - d.map((v) => { - v.value = Math.round(v.value * 100); - }); - }); - return data; - } // formatData - - - //Adapted from http://bl.ocks.org/mbostock/7555321 - //Wraps SVG text - wrap(text, width) { - text.each(function() { - let text = d3.select(this); - let words = text.text().split(/\s+/).reverse(); - let word; - let line = []; - let lineNumber = 0; - let lineHeight = 1.4; // ems - let y = text.attr('y'); - let x = text.attr('x'); - let dy = parseFloat(text.attr('dy')) || 0; - let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); - - while (word = words.pop()) { - line.push(word); - tspan.text(line.join(' ')); - if (tspan.node().getComputedTextLength() > width) { - line.pop(); - tspan.text(line.join(' ')); - line = [word]; - tspan = text.append('tspan') - .attrs({ - 'x': x, - 'y': y, - 'dy': ++lineNumber * lineHeight + dy + 'em', - }) - .text(word); - } - } - }); - }//wrap -} - - -let a; -function test(data) { - a = new Radar('svg', data, { - size: 800, - margins: {top: 70, right: 70, bottom: 70, left: 70}, - colors: ['#EDC951', '#CC333F', '#00A0B0'], - units: '%', - levels: 5, - opacityArea: 0.4, - labelFactor: 30, - shape: 'circle', - wrapWidth: 60, - }); - - a.draw(); -} - - -test(data); \ No newline at end of file +import data from './data'; +import Radar from '../'; + +const a = new Radar('svg', data, { + 'size': 800, + 'margins': {top: 70, right: 70, bottom: 70, left: 70}, + 'colors': ['#EDC951', '#CC333F', '#00A0B0'], + 'units': '%', + 'levels': 5, + 'opacityArea': 0.4, + 'labelFactor': 30, + 'shape': 'circle', + 'wrapWidth': 60, +}); + +a.draw(); From 747cce4aaef59fb5c65d3e3585d5636745c9053e Mon Sep 17 00:00:00 2001 From: James Dow Date: Tue, 21 Feb 2017 18:10:16 -0600 Subject: [PATCH 19/27] moved the juice of everything to the root of /src --- packages/charts-vanilla-radar/src/index.js | 222 +++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 packages/charts-vanilla-radar/src/index.js diff --git a/packages/charts-vanilla-radar/src/index.js b/packages/charts-vanilla-radar/src/index.js new file mode 100644 index 0000000..1d43a0e --- /dev/null +++ b/packages/charts-vanilla-radar/src/index.js @@ -0,0 +1,222 @@ +import { selection, select } from 'd3-selection'; +import { scaleOrdinal, scaleLinear } from 'd3-scale'; +import { max, merge, range } from 'd3-array'; +import { radialLine } from 'd3-shape'; +import 'd3-selection-multi'; + +export { selection }; // XXX + +export default class Radar { + + constructor(graphNode, data, opts) { + this.data = this.formatData(data); + this.target = select(graphNode); + + this.scale; + this.line; + this.color = scaleOrdinal() + .range(opts.colors); + this.axis = {}; + + this.cfg = (() => { + let w = opts.size || 500; + let h = w; + let numAxis = this.data[0].length; + let margin = opts.margins; + let r = w / 2 - max([margin.top + margin.bottom, margin.right + margin.left]); + + return { + w: w, + h: h, + r: r, + margin: margin, + units: opts.units, + levels: opts.levels, + max: null, // will be determined by data + labelFactor: opts.labelFactor, // how much further outside the radius does the label appears + wrapWidth: opts.wrapWidth || 60, // word wrap after this number of pixels + opacityArea: opts.opacityArea, + strokeWidth: 2, + numAxis: numAxis, + angleSlice: Math.PI * 2 / numAxis, // angle of slice in radians + shape: opts.shape || 'circle', //circle, polygon, square, + dotSize: 4, //if not set, dots will not be drawn. + axisStyle: { + 'stroke': 'black', + 'stroke-width': 1, + }, + }; + })(); + } // constructor + + + draw() { + //setup + this.scalesAndLinesFunctions(); + this.wrapper = this.setupRadar(); + + //draw blobs & dots + let blobs = this.wrapper.append('g').attr('class', 'blobs'); + for (let i = 0; i < this.data.length; i++) { + //blob + let g = blobs.append('g').attr('class', 'blob-' + i); + g.append('path').datum(this.data[i]).attrs({ + 'stroke-width': 1.5, + 'd' : this.line, + 'fill': () => { return this.color(i); }, + }); + //dots + g.selectAll('.dot').data(this.data[i]).enter().append('circle').attrs({ + 'stroke-width': 1.5, + 'cx': (d, i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'cy': (d, i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'r': this.cfg.dotSize, + 'fill': () => { return this.color(i);}, + 'opacity': 0.4, + 'class': 'dot', + }); + } + }//draw + + scalesAndLinesFunctions() { + let that = this; + this.cfg.max = max(merge(this.data), d => d.value); + this.scale = scaleLinear() + .domain([0, this.cfg.max]) + .range([0, this.cfg.r]); + + this.line = radialLine() + .angle((d, i) => { return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d) => { return that.scale(d.value);}); + + }//scalesAndLinesFunctions + + setupRadar() { + this.target.styles({ + width: this.cfg.w, + height: this.cfg.h, + }); + let globalGroup = this.target.append('g').attrs({ + 'class': 'chart', + 'transform': `translate(${this.cfg.w / 2} ${this.cfg.h / 2})`, + }); + + this.axis.names = this.data[0].map( el => el.axis); + this.axis.length = this.axis.names.length; + + + //puts 0,0 in the center of the svg + + + // draws the axis + if (this.cfg.axisStyle) { + this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); + this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') + .attrs({ + 'x1': 0, + 'y1': 0, + // `+ Math.PI/2 to make the Axis line up with the correct Data` + 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, + 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, + 'opacity': 0.3, + 'class': 'axis-line', + }).styles(this.cfg.axisStyle); + } + + if (this.cfg.shape == 'circle') { + //draw rings + let ringInterval = this.cfg.max / this.cfg.levels; + var ringRadiusValues = range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX + + let g = globalGroup.append('g').attr('class', 'grid-circle'); + g.append('g').attr('class', 'rings') + .selectAll('.ring').data(ringRadiusValues) + .enter().append('circle').attrs({ + 'fill': 'none', + 'cx': 0, + 'cy': 0, + 'r': (d) => { return this.scale(d); }, + 'stroke': 'black', + 'stroke-width': 1, + 'stroke-dasharray': '5 5', + 'opacity': 0.2, + 'class': 'ring', + }); + + //add the unit labels on the rings + g.append('g').attr('class', 'rings-labels') + .selectAll('.ring-label').data(ringRadiusValues) + .enter().append('text').attrs({ + 'class': 'ring-label', + 'x': '0', + 'y': (d) => {return -this.scale(d);}, + 'dy': '-0.3em', + 'dx': '0.5em', + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4, + }) + .text((d) => {return d + this.cfg.units;}); + g.append('g').attr('class', 'legend') + .selectAll('.axis-label').data(this.data[0]).enter().append('text').attrs({ + 'class': 'axis-label', + 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4, + 'text-anchor': 'middle', + }) + .text((d) => {return d.axis;}) + .call(this.wrap, this.cfg.wrapWidth); + + + + + } else if (this.cfg.shape == 'polygon') { + //TODO + } else if (this.cfg.shape == 'square') { + //TODO + } + return globalGroup; + }//setupRadar + + //customize this function to fit data model you want + formatData(data) { + data.map((d)=>{ + d.map((v)=>{ + v.value = Math.round(v.value*100) + }) + }) + return data + } // formatData + + + //Adapted from http://bl.ocks.org/mbostock/7555321 + //Wraps SVG text + wrap(text, width) { + text.each(function() { + var text = select(this), + words = text.text().split(/\s+/).reverse(), + word, + line = [], + lineNumber = 0, + lineHeight = 1.4, // ems + y = text.attr('y'), + x = text.attr('x'), + dy = parseFloat(text.attr('dy')) || 0, + tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(' ')); + if (tspan.node().getComputedTextLength() > width) { + line.pop(); + tspan.text(line.join(' ')); + line = [word]; + tspan = text.append('tspan').attr('x', x).attr('y', y).attr('dy', ++lineNumber * lineHeight + dy + 'em').text(word); + } + } + }); + }//wrap +} From afe3ef49714a63ac6237558e5d81f0789077b199 Mon Sep 17 00:00:00 2001 From: James Dow Date: Tue, 21 Feb 2017 18:11:42 -0600 Subject: [PATCH 20/27] simplified the code to compile and run dev --- packages/charts-vanilla-radar/gulpfile.js | 40 +++---------------- packages/charts-vanilla-radar/package.json | 20 ++++++---- .../charts-vanilla-radar/webpack.config.js | 9 +++++ 3 files changed, 28 insertions(+), 41 deletions(-) create mode 100644 packages/charts-vanilla-radar/webpack.config.js diff --git a/packages/charts-vanilla-radar/gulpfile.js b/packages/charts-vanilla-radar/gulpfile.js index b4d3502..1997026 100644 --- a/packages/charts-vanilla-radar/gulpfile.js +++ b/packages/charts-vanilla-radar/gulpfile.js @@ -1,43 +1,15 @@ var gulp = require('gulp'); -var babel = require('gulp-babel'); -var browserSync = require('browser-sync'); var sass = require('gulp-sass'); var plumber = require('gulp-plumber'); var sourcemaps = require('gulp-sourcemaps'); - - -gulp.task('serve', ['compile-js', 'compile-scss'], function() { - browserSync.init({ - server: { - baseDir:'./', - }, - }); - - gulp.watch(['src/scripts/*.js'], ['compile-js']); - gulp.watch(['src/styles/*.scss'], ['compile-scss']); - gulp.watch(['*.html', 'src/styles/*.scss', 'src/scripts/*.js']).on('change', browserSync.reload); - -}); - -gulp.task('compile-js', function() { - return gulp.src('src/scripts/*.js') - .pipe(plumber()) - .pipe(sourcemaps.init()) - .pipe(babel({ - presets:['es2015'], - })) - .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')); -}); - gulp.task('compile-scss', function() { return gulp.src('src/styles/*.scss') - .pipe(plumber()) - .pipe(sourcemaps.init()) - .pipe(sass().on('error', sass.logError)) - .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')); + .pipe(plumber()) + .pipe(sourcemaps.init()) + .pipe(sass().on('error', sass.logError)) + .pipe(sourcemaps.write('maps')) + .pipe(gulp.dest('lib')); }); -gulp.task('default', ['serve']); \ No newline at end of file +gulp.task('default', ['compile-scss']); diff --git a/packages/charts-vanilla-radar/package.json b/packages/charts-vanilla-radar/package.json index 1551148..712fa83 100644 --- a/packages/charts-vanilla-radar/package.json +++ b/packages/charts-vanilla-radar/package.json @@ -1,21 +1,27 @@ { "name": "@ibm-design/charts-vanilla-radar", "version": "0.0.0", - "main": "lib/index.js", + "main": "src/index.js", "scripts": { - "build": "npm run clean && babel -d lib src", + "build": "npm run clean && webpack && gulp", + "start": "npm run build && webpack-dev-server --progress --colors", "clean": "rimraf lib" }, "license": "Apache-2.0", "devDependencies": { - "babel-cli": "^6.18.0", - "babel-preset-es2015": "^6.22.0", - "browser-sync": "^2.18.7", "gulp": "^3.9.1", - "gulp-babel": "^6.1.2", "gulp-plumber": "^1.1.0", "gulp-sass": "^3.1.0", "gulp-sourcemaps": "^2.4.1", - "rimraf": "^2.5.4" + "rimraf": "^2.5.4", + "webpack": "^2.2.1", + "webpack-dev-server": "^2.4.1" + }, + "dependencies": { + "d3-array": "^1.0.2", + "d3-scale": "^1.0.4", + "d3-selection": "^1.0.3", + "d3-selection-multi": "^1.0.1", + "d3-shape": "^1.0.4" } } diff --git a/packages/charts-vanilla-radar/webpack.config.js b/packages/charts-vanilla-radar/webpack.config.js new file mode 100644 index 0000000..0682efb --- /dev/null +++ b/packages/charts-vanilla-radar/webpack.config.js @@ -0,0 +1,9 @@ +const path = require('path'); + +module.exports = { + entry: './src/scripts/index.js', + output: { + filename: 'index.js', + path: path.resolve(__dirname, 'lib'), + }, +}; From a719124ff518d588bee781ed60b13fd0a501e252 Mon Sep 17 00:00:00 2001 From: James Dow Date: Tue, 21 Feb 2017 18:12:23 -0600 Subject: [PATCH 21/27] updated to reflect changes to /lib --- packages/charts-vanilla-radar/index.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/charts-vanilla-radar/index.html b/packages/charts-vanilla-radar/index.html index 09e9801..41a29ce 100644 --- a/packages/charts-vanilla-radar/index.html +++ b/packages/charts-vanilla-radar/index.html @@ -2,16 +2,13 @@ - Radar Chart - - + Vanilla Radar Chart - - \ No newline at end of file + From c5f0378ee6c2b37c93f8a36d50bcdf6f84e5ff7b Mon Sep 17 00:00:00 2001 From: James Dow Date: Tue, 21 Feb 2017 18:12:47 -0600 Subject: [PATCH 22/27] updated the readme.md to better reflect how to develop within this package --- packages/charts-vanilla-radar/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/charts-vanilla-radar/README.md b/packages/charts-vanilla-radar/README.md index 5c90567..40c73af 100644 --- a/packages/charts-vanilla-radar/README.md +++ b/packages/charts-vanilla-radar/README.md @@ -2,6 +2,20 @@ ## Usage +#### Install ```bash +# you can use yarn yarn add @ibm-design/charts-vanilla-radar + +# or npm to add it to your project +npm install @ibm-design/charts-vanilla-radar +``` + +## Development + +#### Start dev environment +```bash +npm start ``` + +Then go to http://localhost:8082/ to see this package rendered. From e665b24aa92eabb3575dde432c1634f7767a3383 Mon Sep 17 00:00:00 2001 From: James Dow Date: Tue, 21 Feb 2017 18:27:15 -0600 Subject: [PATCH 23/27] fixed linting errors --- packages/charts-vanilla-radar/src/index.js | 79 +++++++++++-------- .../charts-vanilla-radar/src/scripts/index.js | 2 +- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/charts-vanilla-radar/src/index.js b/packages/charts-vanilla-radar/src/index.js index 1d43a0e..8fe4d49 100644 --- a/packages/charts-vanilla-radar/src/index.js +++ b/packages/charts-vanilla-radar/src/index.js @@ -110,14 +110,18 @@ export default class Radar { // draws the axis if (this.cfg.axisStyle) { - this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); + this.axisGroup = globalGroup.append('g').attr('class', 'axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, - 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, + 'x2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice * i + Math.PI / 2); + }, 'opacity': 0.3, 'class': 'axis-line', }).styles(this.cfg.axisStyle); @@ -125,8 +129,9 @@ export default class Radar { if (this.cfg.shape == 'circle') { //draw rings - let ringInterval = this.cfg.max / this.cfg.levels; - var ringRadiusValues = range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX + const ringInterval = this.cfg.max / this.cfg.levels; + //add 1 so you include the MAX + const ringRadiusValues = range(ringInterval, this.cfg.max + 1, ringInterval).reverse(); let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') @@ -160,8 +165,14 @@ export default class Radar { g.append('g').attr('class', 'legend') .selectAll('.axis-label').data(this.data[0]).enter().append('text').attrs({ 'class': 'axis-label', - 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, - 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'x': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.sin(this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.cos(this.cfg.angleSlice * i + Math.PI / 2); + }, 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, @@ -183,12 +194,13 @@ export default class Radar { //customize this function to fit data model you want formatData(data) { - data.map((d)=>{ - d.map((v)=>{ - v.value = Math.round(v.value*100) - }) - }) - return data + data.map(d => { + d.map(v => { + v.value = Math.round(v.value * 100); + }); + }); + + return data; } // formatData @@ -196,27 +208,32 @@ export default class Radar { //Wraps SVG text wrap(text, width) { text.each(function() { - var text = select(this), - words = text.text().split(/\s+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.4, // ems - y = text.attr('y'), - x = text.attr('x'), - dy = parseFloat(text.attr('dy')) || 0, - tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); - - while (word = words.pop()) { - line.push(word); - tspan.text(line.join(' ')); - if (tspan.node().getComputedTextLength() > width) { + const text = select(this); + const words = text.text().split(/\s+/).reverse(); + const lineHeight = 1.4; // ems + const y = text.attr('y'); + const x = text.attr('x'); + const dy = parseFloat(text.attr('dy')) || 0; + + let word; + let line = []; + let lineNumber = 0; + let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(' ')); + if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(' ')); line = [word]; - tspan = text.append('tspan').attr('x', x).attr('y', y).attr('dy', ++lineNumber * lineHeight + dy + 'em').text(word); - } + tspan = text.append('tspan') + .attr('x', x) + .attr('y', y) + .attr('dy', ++lineNumber * lineHeight + dy + 'em') + .text(word); } - }); + } + }); }//wrap } diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index f316877..1a23ba3 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -1,5 +1,5 @@ import data from './data'; -import Radar from '../'; +import Radar from '../index'; const a = new Radar('svg', data, { 'size': 800, From 9e2a642394dda35162d4790ebad00c29dfc39bce Mon Sep 17 00:00:00 2001 From: James Dow Date: Wed, 22 Feb 2017 09:33:54 -0600 Subject: [PATCH 24/27] various small little changes --- packages/charts-vanilla-radar/gulpfile.js | 10 +- packages/charts-vanilla-radar/src/index.js | 144 +++++++++++---------- 2 files changed, 78 insertions(+), 76 deletions(-) diff --git a/packages/charts-vanilla-radar/gulpfile.js b/packages/charts-vanilla-radar/gulpfile.js index 1997026..6aed284 100644 --- a/packages/charts-vanilla-radar/gulpfile.js +++ b/packages/charts-vanilla-radar/gulpfile.js @@ -1,9 +1,9 @@ -var gulp = require('gulp'); -var sass = require('gulp-sass'); -var plumber = require('gulp-plumber'); -var sourcemaps = require('gulp-sourcemaps'); +const gulp = require('gulp'); +const sass = require('gulp-sass'); +const plumber = require('gulp-plumber'); +const sourcemaps = require('gulp-sourcemaps'); -gulp.task('compile-scss', function() { +gulp.task('compile-scss', () => { return gulp.src('src/styles/*.scss') .pipe(plumber()) .pipe(sourcemaps.init()) diff --git a/packages/charts-vanilla-radar/src/index.js b/packages/charts-vanilla-radar/src/index.js index 8fe4d49..3ad4ed3 100644 --- a/packages/charts-vanilla-radar/src/index.js +++ b/packages/charts-vanilla-radar/src/index.js @@ -19,11 +19,11 @@ export default class Radar { this.axis = {}; this.cfg = (() => { - let w = opts.size || 500; - let h = w; - let numAxis = this.data[0].length; - let margin = opts.margins; - let r = w / 2 - max([margin.top + margin.bottom, margin.right + margin.left]); + const w = opts.size || 500; + const h = w; + const numAxis = this.data[0].length; + const margin = opts.margins; + const r = w / 2 - max([margin.top + margin.bottom, margin.right + margin.left]); return { w: w, @@ -51,20 +51,22 @@ export default class Radar { draw() { + //setup this.scalesAndLinesFunctions(); this.wrapper = this.setupRadar(); - //draw blobs & dots - let blobs = this.wrapper.append('g').attr('class', 'blobs'); + const blobs = this.wrapper.append('g').attr('class', 'blobs'); //draw blobs & dots + for (let i = 0; i < this.data.length; i++) { - //blob - let g = blobs.append('g').attr('class', 'blob-' + i); + const g = blobs.append('g').attr('class', 'blob-' + i); //blob + g.append('path').datum(this.data[i]).attrs({ 'stroke-width': 1.5, 'd' : this.line, 'fill': () => { return this.color(i); }, }); + //dots g.selectAll('.dot').data(this.data[i]).enter().append('circle').attrs({ 'stroke-width': 1.5, @@ -79,15 +81,16 @@ export default class Radar { }//draw scalesAndLinesFunctions() { - let that = this; + const that = this; + this.cfg.max = max(merge(this.data), d => d.value); this.scale = scaleLinear() - .domain([0, this.cfg.max]) - .range([0, this.cfg.r]); + .domain([0, this.cfg.max]) + .range([0, this.cfg.r]); this.line = radialLine() - .angle((d, i) => { return -(i * that.cfg.angleSlice) + Math.PI / 2;}) - .radius((d) => { return that.scale(d.value);}); + .angle((d, i) => { return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d) => { return that.scale(d.value);}); }//scalesAndLinesFunctions @@ -96,7 +99,8 @@ export default class Radar { width: this.cfg.w, height: this.cfg.h, }); - let globalGroup = this.target.append('g').attrs({ + + const globalGroup = this.target.append('g').attrs({ 'class': 'chart', 'transform': `translate(${this.cfg.w / 2} ${this.cfg.h / 2})`, }); @@ -104,27 +108,25 @@ export default class Radar { this.axis.names = this.data[0].map( el => el.axis); this.axis.length = this.axis.names.length; - //puts 0,0 in the center of the svg - // draws the axis if (this.cfg.axisStyle) { this.axisGroup = globalGroup.append('g').attr('class', 'axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') - .attrs({ - 'x1': 0, - 'y1': 0, - // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d, i) => { - return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice * i + Math.PI / 2); - }, - 'y2': (d, i) => { - return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice * i + Math.PI / 2); - }, - 'opacity': 0.3, - 'class': 'axis-line', - }).styles(this.cfg.axisStyle); + .attrs({ + 'x1': 0, + 'y1': 0, + // `+ Math.PI/2 to make the Axis line up with the correct Data` + 'x2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice * i + Math.PI / 2); + }, + 'opacity': 0.3, + 'class': 'axis-line', + }).styles(this.cfg.axisStyle); } if (this.cfg.shape == 'circle') { @@ -135,51 +137,51 @@ export default class Radar { let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') - .selectAll('.ring').data(ringRadiusValues) - .enter().append('circle').attrs({ - 'fill': 'none', - 'cx': 0, - 'cy': 0, - 'r': (d) => { return this.scale(d); }, - 'stroke': 'black', - 'stroke-width': 1, - 'stroke-dasharray': '5 5', - 'opacity': 0.2, - 'class': 'ring', - }); + .selectAll('.ring').data(ringRadiusValues) + .enter().append('circle').attrs({ + 'fill': 'none', + 'cx': 0, + 'cy': 0, + 'r': (d) => { return this.scale(d); }, + 'stroke': 'black', + 'stroke-width': 1, + 'stroke-dasharray': '5 5', + 'opacity': 0.2, + 'class': 'ring', + }); //add the unit labels on the rings g.append('g').attr('class', 'rings-labels') - .selectAll('.ring-label').data(ringRadiusValues) - .enter().append('text').attrs({ - 'class': 'ring-label', - 'x': '0', - 'y': (d) => {return -this.scale(d);}, - 'dy': '-0.3em', - 'dx': '0.5em', - 'font-size': '0.8em', - 'fill': 'grey', - 'opacity': 0.4, - }) - .text((d) => {return d + this.cfg.units;}); + .selectAll('.ring-label').data(ringRadiusValues) + .enter().append('text').attrs({ + 'class': 'ring-label', + 'x': '0', + 'y': (d) => {return -this.scale(d);}, + 'dy': '-0.3em', + 'dx': '0.5em', + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4, + }) + .text((d) => {return d + this.cfg.units;}); g.append('g').attr('class', 'legend') - .selectAll('.axis-label').data(this.data[0]).enter().append('text').attrs({ - 'class': 'axis-label', - 'x': (d, i) => { - return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * - Math.sin(this.cfg.angleSlice * i + Math.PI / 2); - }, - 'y': (d, i) => { - return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * - Math.cos(this.cfg.angleSlice * i + Math.PI / 2); - }, - 'font-size': '0.8em', - 'fill': 'grey', - 'opacity': 0.4, - 'text-anchor': 'middle', - }) - .text((d) => {return d.axis;}) - .call(this.wrap, this.cfg.wrapWidth); + .selectAll('.axis-label').data(this.data[0]).enter().append('text').attrs({ + 'class': 'axis-label', + 'x': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.sin(this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.cos(this.cfg.angleSlice * i + Math.PI / 2); + }, + 'font-size': '0.8em', + 'fill': 'grey', + 'opacity': 0.4, + 'text-anchor': 'middle', + }) + .text((d) => {return d.axis;}) + .call(this.wrap, this.cfg.wrapWidth); From df9e0b3d14459999f6dfd37d5b35be70c840e8f0 Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 15:47:47 -0600 Subject: [PATCH 25/27] removing linting errors --- packages/charts-vanilla-radar/gulpfile.js | 44 ++--- .../src/__tests__/Chart-test.js | 4 +- .../charts-vanilla-radar/src/scripts/data.js | 50 +++--- .../charts-vanilla-radar/src/scripts/index.js | 151 +++++++++--------- .../src/styles/styles.scss | 8 +- 5 files changed, 123 insertions(+), 134 deletions(-) diff --git a/packages/charts-vanilla-radar/gulpfile.js b/packages/charts-vanilla-radar/gulpfile.js index 9ba14f7..b4d3502 100644 --- a/packages/charts-vanilla-radar/gulpfile.js +++ b/packages/charts-vanilla-radar/gulpfile.js @@ -1,43 +1,43 @@ -var gulp = require('gulp'), - babel = require('gulp-babel'), - browserSync = require('browser-sync') - sass = require('gulp-sass'), - plumber = require('gulp-plumber'), - sourcemaps = require('gulp-sourcemaps'); +var gulp = require('gulp'); +var babel = require('gulp-babel'); +var browserSync = require('browser-sync'); +var sass = require('gulp-sass'); +var plumber = require('gulp-plumber'); +var sourcemaps = require('gulp-sourcemaps'); -gulp.task('serve', ['compile-js', 'compile-scss'], function(){ +gulp.task('serve', ['compile-js', 'compile-scss'], function() { browserSync.init({ server: { - baseDir:'./' - } - }) + baseDir:'./', + }, + }); - gulp.watch(['src/scripts/*.js'], ['compile-js']) - gulp.watch(["src/styles/*.scss"], ['compile-scss']); - gulp.watch(['*.html','src/styles/*.scss','src/scripts/*.js']).on('change', browserSync.reload) + gulp.watch(['src/scripts/*.js'], ['compile-js']); + gulp.watch(['src/styles/*.scss'], ['compile-scss']); + gulp.watch(['*.html', 'src/styles/*.scss', 'src/scripts/*.js']).on('change', browserSync.reload); -}) +}); -gulp.task('compile-js', function(){ +gulp.task('compile-js', function() { return gulp.src('src/scripts/*.js') .pipe(plumber()) .pipe(sourcemaps.init()) .pipe(babel({ - presets:['es2015'] + presets:['es2015'], })) .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')) -}) + .pipe(gulp.dest('lib')); +}); -gulp.task('compile-scss', function(){ +gulp.task('compile-scss', function() { return gulp.src('src/styles/*.scss') .pipe(plumber()) .pipe(sourcemaps.init()) .pipe(sass().on('error', sass.logError)) .pipe(sourcemaps.write('maps')) - .pipe(gulp.dest('lib')) -}) + .pipe(gulp.dest('lib')); +}); -gulp.task('default', ['serve']) \ No newline at end of file +gulp.task('default', ['serve']); \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js index 890958c..54c9b46 100644 --- a/packages/charts-vanilla-radar/src/__tests__/Chart-test.js +++ b/packages/charts-vanilla-radar/src/__tests__/Chart-test.js @@ -1,7 +1,7 @@ -import Chart from '../'; +// import Chart from '../'; describe('Radar Component', () => { it('should render', () => { - expect(true).toBe(true, 'This is a placeholder true') + expect(true).toBe(true, 'This is a placeholder true'); }); }); diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index 4e05c5b..cfc38c7 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -1,30 +1,26 @@ const data = [ [//iPhone - {axis:"Battery Life",value:0.22}, - {axis:"Brand",value:0.28}, - {axis:"Contract Cost",value:0.29}, - {axis:"Design And Quality",value:0.17}, - {axis:"Have Internet Connectivity",value:0.22}, - {axis:"Large Screen",value:0.02}, - {axis:"Price Of Device",value:0.21}, - {axis:"To Be A Smartphone",value:0.50} - ],[//Samsung - {axis:"Battery Life",value:0.27}, - {axis:"Brand",value:0.16}, - {axis:"Contract Cost",value:0.35}, - {axis:"Design And Quality",value:0.13}, - {axis:"Have Internet Connectivity",value:0.20}, - {axis:"Large Screen",value:0.13}, - {axis:"Price Of Device",value:0.35}, - {axis:"To Be A Smartphone",value:0.38} - ],[//Nokia Smartphone - {axis:"Battery Life",value:0.26}, - {axis:"Brand",value:0.10}, - {axis:"Contract Cost",value:0.30}, - {axis:"Design And Quality",value:0.14}, - {axis:"Have Internet Connectivity",value:0.22}, - {axis:"Large Screen",value:0.04}, - {axis:"Price Of Device",value:0.41}, - {axis:"To Be A Smartphone",value:0.30} - ] + {axis:'Apdex', value:0.80}, + {axis:'Alerts', value:0.66}, + {axis:'Request Volume', value:0.35}, + {axis:'Memory', value:0.54}, + {axis:'CPU', value:0.38}, + {axis:'Disk', value:0.23}, + ], + [//Samsung + {axis:'Apdex', value:0.80}, + {axis:'Alerts', value:0.66}, + {axis:'Request Volume', value:0.35}, + {axis:'Memory', value:0.23}, + {axis:'CPU', value:0.36}, + {axis:'Disk', value:0.13}, + ], + [//Nokia Smartphone + {axis:'Apdex', value:0.26}, + {axis:'Alerts', value:0.10}, + {axis:'Request Volume', value:0.30}, + {axis:'Memory', value:0.14}, + {axis:'CPU', value:0.22}, + {axis:'Disk', value:0.04}, + ], ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 7c925cc..b9d6c21 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -1,22 +1,22 @@ -'use strict' +'use strict'; class Radar { - constructor(graphNode, data, opts){ - this.data = this.formatData(data) - this.target = d3.select(graphNode) + constructor(graphNode, data, opts) { + this.data = this.formatData(data); + this.target = d3.select(graphNode); this.scale; this.line; this.color = d3.scaleOrdinal() - .range(opts.colors) - this.axis = {} + .range(opts.colors); + this.axis = {}; - this.cfg = (()=>{ - let w = opts.size || 500, - h = w, - numAxis = data[0].length, - margin = opts.margins, - r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); + this.cfg = (() => { + let w = opts.size || 500; + let h = w; + let numAxis = data[0].length; + let margin = opts.margins; + let r = w / 2 - d3.max([margin.top + margin.bottom, margin.right + margin.left]); return { w: w, @@ -37,107 +37,104 @@ class Radar { axisStyle: { 'stroke': 'black', 'stroke-width': 1, - } - } - })() + }, + }; + })(); } // constructor - draw(){ + draw() { //setup - this.scalesAndLinesFunctions() - this.wrapper = this.setupRadar() + this.scalesAndLinesFunctions(); + this.wrapper = this.setupRadar(); //draw blobs & dots - var blobs = this.wrapper.append('g').attr('class','blobs') - for(let i = 0; i < data.length; i++){ + let blobs = this.wrapper.append('g').attr('class', 'blobs'); + for (let i = 0; i < data.length; i++) { //blob - let g = blobs.append('g').attr('class','blob-'+i) - g.append('path').datum(data[i]).attrs({ - 'stroke-width': 1.5, - 'd' : a.line, - 'fill': () => { return this.color(i)} - }) - //dots - g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ - 'stroke-width': 1.5, - 'cx': (d,i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, - 'cy': (d,i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, - 'r': this.cfg.dotSize, - 'fill': () => { return this.color(i)}, - 'opacity': 0.4, - 'class': 'dot', - }) + let g = blobs.append('g').attr('class', 'blob-' + i); + g.append('path').datum(data[i]).attrs({ + 'stroke-width': 1.5, + 'd' : a.line, + 'fill': () => { return this.color(i); }, + }); + //dots + g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ + 'stroke-width': 1.5, + 'cx': (d, i) => { return this.scale( d.value) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'cy': (d, i) => { return this.scale( d.value) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'r': this.cfg.dotSize, + 'fill': () => { return this.color(i);}, + 'opacity': 0.4, + 'class': 'dot', + }); } - - }//draw - scalesAndLinesFunctions(){ - let that = this - this.cfg.max = d3.max(d3.merge(this.data), d => d.value) + scalesAndLinesFunctions() { + let that = this; + this.cfg.max = d3.max(d3.merge(this.data), d => d.value); this.scale = d3.scaleLinear() .domain([0, this.cfg.max]) - .range([0, this.cfg.r]) + .range([0, this.cfg.r]); this.line = d3.radialLine() - .angle((d, i)=>{ return -(i * that.cfg.angleSlice) + Math.PI / 2;}) - .radius((d)=>{ return that.scale(d.value)}) + .angle((d, i) => { return -(i * that.cfg.angleSlice) + Math.PI / 2;}) + .radius((d) => { return that.scale(d.value);}); }//scalesAndLinesFunctions - setupRadar(){ + setupRadar() { this.target.styles({ width: this.cfg.w, height: this.cfg.h, - }) + }); let globalGroup = this.target.append('g').attrs({ 'class': 'chart', - 'transform': `translate(${this.cfg.w/2} ${this.cfg.h/2})` - }) + 'transform': `translate(${this.cfg.w / 2} ${this.cfg.h / 2})`, + }); - this.axis.names = data[0].map( el => el.axis) - this.axis.length = this.axis.names.length + this.axis.names = data[0].map( el => el.axis); + this.axis.length = this.axis.names.length; //puts 0,0 in the center of the svg // draws the axis - if(this.cfg.axisStyle){ - this.axisGroup = globalGroup.append('g').attr('class','axis-lines') + if (this.cfg.axisStyle) { + this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2)}, - 'y2': (d,i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2)}, + 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, + 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, 'opacity': 0.3, 'class': 'axis-line', - }).styles(this.cfg.axisStyle) + }).styles(this.cfg.axisStyle); } - if(this.cfg.shape == 'circle'){ + if (this.cfg.shape == 'circle') { //draw rings - let ringInterval = this.cfg.max / this.cfg.levels - var ringRadiusValues = d3.range(ringInterval, this.cfg.max+1, ringInterval).reverse() //add 1 so you include the MAX + let ringInterval = this.cfg.max / this.cfg.levels; + var ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX - let g = globalGroup.append('g').attr('class', 'grid-circle') + let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') .selectAll('.ring').data(ringRadiusValues) .enter().append('circle').attrs({ 'fill': 'none', 'cx': 0, 'cy': 0, - 'class': 'label', - 'r': (d) => {return this.scale(d)}, + 'r': (d) => { return this.scale(d); }, 'stroke': 'black', 'stroke-width': 1, - 'stroke-dasharray': '1 3', - 'opacity': 0.5, - 'class': 'ring' - }) + 'stroke-dasharray': '5 5', + 'opacity': 0.2, + 'class': 'ring', + }); //add the unit labels on the rings g.append('g').attr('class', 'rings-labels') @@ -145,36 +142,36 @@ class Radar { .enter().append('text').attrs({ 'class': 'ring-label', 'x': '0', - 'y': (d) => {return -this.scale(d)}, + 'y': (d) => {return -this.scale(d);}, 'dy': '-0.3em', 'dx': '0.5em', 'font-size': '0.8em', 'fill': 'grey', - 'opacity': 0.4 + 'opacity': 0.4, }) - .text((d) => {return d + this.cfg.units}) + .text((d) => {return d + this.cfg.units;}); g.append('g').attr('class', 'legend') .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ 'class': 'axis-label', - 'x': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI/2)}, - 'y': (d,i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI/2)}, + 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, + 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, 'text-anchor': 'middle', }) - .text((d)=>{return d.axis}) - .call(this.wrap, this.cfg.wrapWidth) + .text((d) => {return d.axis;}) + .call(this.wrap, this.cfg.wrapWidth); - } else if(this.cfg.shape == 'polygon'){ + } else if (this.cfg.shape == 'polygon') { //TODO - } else if(this.cfg.shape =='square'){ + } else if (this.cfg.shape == 'square') { //TODO } - return globalGroup + return globalGroup; }//setupRadar //customize this function to fit data model you want @@ -188,7 +185,7 @@ class Radar { } // formatData - //Taken from http://bl.ocks.org/mbostock/7555321 + //Adapted from http://bl.ocks.org/mbostock/7555321 //Wraps SVG text wrap(text, width) { text.each(function() { @@ -227,7 +224,7 @@ function test(data){ units: '%', levels: 5, opacityArea: 0.4, - labelFactor: 50, + labelFactor: 30, shape: 'circle', wrapWidth: 60 }) diff --git a/packages/charts-vanilla-radar/src/styles/styles.scss b/packages/charts-vanilla-radar/src/styles/styles.scss index 58f6547..df66632 100644 --- a/packages/charts-vanilla-radar/src/styles/styles.scss +++ b/packages/charts-vanilla-radar/src/styles/styles.scss @@ -8,12 +8,8 @@ body { .radar { border: 1px solid black; - g[class*='blob-'] { - mix-blend-mode: multiply; - - path { - opacity: 0.5; - } + path { + opacity: 0.5; } } From 25024b5ac9a05eafde843a8323977c82d27880ff Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 15:56:38 -0600 Subject: [PATCH 26/27] more linting errors fixed --- .../charts-vanilla-radar/src/scripts/index.js | 95 +++++++++++-------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index b9d6c21..828f879 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -51,6 +51,7 @@ class Radar { //draw blobs & dots let blobs = this.wrapper.append('g').attr('class', 'blobs'); for (let i = 0; i < data.length; i++) { + //blob let g = blobs.append('g').attr('class', 'blob-' + i); g.append('path').datum(data[i]).attrs({ @@ -58,6 +59,7 @@ class Radar { 'd' : a.line, 'fill': () => { return this.color(i); }, }); + //dots g.selectAll('.dot').data(data[i]).enter().append('circle').attrs({ 'stroke-width': 1.5, @@ -97,10 +99,6 @@ class Radar { this.axis.names = data[0].map( el => el.axis); this.axis.length = this.axis.names.length; - - //puts 0,0 in the center of the svg - - // draws the axis if (this.cfg.axisStyle) { this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); @@ -109,8 +107,12 @@ class Radar { 'x1': 0, 'y1': 0, // `+ Math.PI/2 to make the Axis line up with the correct Data` - 'x2': (d, i) => { return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice*i + Math.PI/2);}, - 'y2': (d, i)=>{ return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice*i + Math.PI/2);}, + 'x2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.sin( this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y2': (d, i) => { + return this.scale(this.cfg.max * 1.05) * Math.cos( this.cfg.angleSlice * i + Math.PI / 2); + }, 'opacity': 0.3, 'class': 'axis-line', }).styles(this.cfg.axisStyle); @@ -153,8 +155,14 @@ class Radar { g.append('g').attr('class', 'legend') .selectAll('.axis-label').data(data[0]).enter().append('text').attrs({ 'class': 'axis-label', - 'x': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.sin(this.cfg.angleSlice * i + Math.PI / 2);}, - 'y': (d, i) => { return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * Math.cos(this.cfg.angleSlice * i + Math.PI / 2);}, + 'x': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.sin(this.cfg.angleSlice * i + Math.PI / 2); + }, + 'y': (d, i) => { + return (this.scale(this.cfg.max) * 1.05 + this.cfg.labelFactor ) * + Math.cos(this.cfg.angleSlice * i + Math.PI / 2); + }, 'font-size': '0.8em', 'fill': 'grey', 'opacity': 0.4, @@ -175,13 +183,13 @@ class Radar { }//setupRadar //customize this function to fit data model you want - formatData(data){ - data.map((d)=>{ - d.map((v)=>{ - v.value = Math.round(v.value*100) - }) - }) - return data + formatData(data) { + data.map((d) => { + d.map((v) => { + v.value = Math.round(v.value * 100); + }); + }); + return data; } // formatData @@ -189,47 +197,54 @@ class Radar { //Wraps SVG text wrap(text, width) { text.each(function() { - var text = d3.select(this), - words = text.text().split(/\s+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.4, // ems - y = text.attr("y"), - x = text.attr("x"), - dy = parseFloat(text.attr("dy")) || 0, - tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em"); - - while (word = words.pop()) { - line.push(word); - tspan.text(line.join(" ")); - if (tspan.node().getComputedTextLength() > width) { + var text = d3.select(this), + words = text.text().split(/\s+/).reverse(), + word, + line = [], + lineNumber = 0, + lineHeight = 1.4, // ems + y = text.attr('y'), + x = text.attr('x'), + dy = parseFloat(text.attr('dy')) || 0, + tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(' ')); + if (tspan.node().getComputedTextLength() > width) { line.pop(); - tspan.text(line.join(" ")); + tspan.text(line.join(' ')); line = [word]; - tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); - } + tspan = text.append('tspan') + .attrs({ + 'x': x, + 'y': y, + 'dy': ++lineNumber * lineHeight + dy + 'em', + }) + .text(word); } - }); + } + }); }//wrap } let a; -function test(data){ +function test(data) { a = new Radar('svg', data, { size: 800, margins: {top: 70, right: 70, bottom: 70, left: 70}, - colors: ["#EDC951","#CC333F","#00A0B0"], + colors: ['#EDC951', '#CC333F', '#00A0B0'], units: '%', levels: 5, opacityArea: 0.4, labelFactor: 30, shape: 'circle', - wrapWidth: 60 - }) - a.draw() + wrapWidth: 60, + }); + + a.draw(); } -test(data) \ No newline at end of file +test(data); \ No newline at end of file From d4ae6515a0a19c2abecdbe3a00cf5b4b95b3741f Mon Sep 17 00:00:00 2001 From: Jamie Skinner Date: Tue, 21 Feb 2017 16:09:43 -0600 Subject: [PATCH 27/27] updated data and more lint errors --- .../charts-vanilla-radar/src/scripts/data.js | 8 +++++- .../charts-vanilla-radar/src/scripts/index.js | 26 +++++++++---------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/charts-vanilla-radar/src/scripts/data.js b/packages/charts-vanilla-radar/src/scripts/data.js index cfc38c7..1e52c32 100644 --- a/packages/charts-vanilla-radar/src/scripts/data.js +++ b/packages/charts-vanilla-radar/src/scripts/data.js @@ -6,14 +6,18 @@ const data = [ {axis:'Memory', value:0.54}, {axis:'CPU', value:0.38}, {axis:'Disk', value:0.23}, + {axis:'Network', value:0.23}, + {axis:'Heap', value:0.16}, ], [//Samsung - {axis:'Apdex', value:0.80}, + {axis:'Apdex', value:0.58}, {axis:'Alerts', value:0.66}, {axis:'Request Volume', value:0.35}, {axis:'Memory', value:0.23}, {axis:'CPU', value:0.36}, {axis:'Disk', value:0.13}, + {axis:'Network', value:0.11}, + {axis:'Heap', value:0.35}, ], [//Nokia Smartphone {axis:'Apdex', value:0.26}, @@ -22,5 +26,7 @@ const data = [ {axis:'Memory', value:0.14}, {axis:'CPU', value:0.22}, {axis:'Disk', value:0.04}, + {axis:'Network', value:0.23}, + {axis:'Heap', value:0.41}, ], ]; \ No newline at end of file diff --git a/packages/charts-vanilla-radar/src/scripts/index.js b/packages/charts-vanilla-radar/src/scripts/index.js index 828f879..4f16611 100644 --- a/packages/charts-vanilla-radar/src/scripts/index.js +++ b/packages/charts-vanilla-radar/src/scripts/index.js @@ -56,7 +56,7 @@ class Radar { let g = blobs.append('g').attr('class', 'blob-' + i); g.append('path').datum(data[i]).attrs({ 'stroke-width': 1.5, - 'd' : a.line, + 'd' : this.line, 'fill': () => { return this.color(i); }, }); @@ -101,7 +101,7 @@ class Radar { // draws the axis if (this.cfg.axisStyle) { - this.axisGroup = globalGroup.append('g').attr('class','axis-lines'); + this.axisGroup = globalGroup.append('g').attr('class', 'axis-lines'); this.axisGroup.selectAll('.axis-line').data(this.axis.names).enter().append('line') .attrs({ 'x1': 0, @@ -121,7 +121,7 @@ class Radar { if (this.cfg.shape == 'circle') { //draw rings let ringInterval = this.cfg.max / this.cfg.levels; - var ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse() //add 1 so you include the MAX + let ringRadiusValues = d3.range(ringInterval, this.cfg.max + 1, ringInterval).reverse(); let g = globalGroup.append('g').attr('class', 'grid-circle'); g.append('g').attr('class', 'rings') @@ -197,16 +197,16 @@ class Radar { //Wraps SVG text wrap(text, width) { text.each(function() { - var text = d3.select(this), - words = text.text().split(/\s+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.4, // ems - y = text.attr('y'), - x = text.attr('x'), - dy = parseFloat(text.attr('dy')) || 0, - tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); + let text = d3.select(this); + let words = text.text().split(/\s+/).reverse(); + let word; + let line = []; + let lineNumber = 0; + let lineHeight = 1.4; // ems + let y = text.attr('y'); + let x = text.attr('x'); + let dy = parseFloat(text.attr('dy')) || 0; + let tspan = text.text(null).append('tspan').attr('x', x).attr('y', y).attr('dy', dy + 'em'); while (word = words.pop()) { line.push(word);