diff --git a/caravel/assets/.eslintrc b/caravel/assets/.eslintrc index 1346231fbe..7a517699fe 100644 --- a/caravel/assets/.eslintrc +++ b/caravel/assets/.eslintrc @@ -8,5 +8,6 @@ "prefer-arrow-callback": 0, "func-names": 0, "react/jsx-no-bind": 0, + "no-confusing-arrow": 0, } } diff --git a/caravel/assets/visualizations/histogram.js b/caravel/assets/visualizations/histogram.js index 10b6aea7a2..a7e10e0dda 100644 --- a/caravel/assets/visualizations/histogram.js +++ b/caravel/assets/visualizations/histogram.js @@ -1,151 +1,151 @@ -// JS -const d3 = require('d3') -const px = window.px || require('../javascripts/modules/caravel.js') +import { category21 } from '../javascripts/modules/colors'; +import d3 from 'd3'; -// CSS -require('./histogram.css') +require('./histogram.css'); function histogram(slice) { - - const div = d3.select(slice.selector) - - const _draw = function(data, numBins) { - - // Set Margins - const margin = { - top: 50, - right: 10, - bottom: 20, - left: 50, - }; - const navBarHeight = 36; - const navBarTitleSize = 12; - const navBarBuffer = 10; - const width = slice.width() - margin.left - margin.right; - const height = slice.height() - margin.top - margin.bottom - navBarHeight - navBarBuffer; - - // Set Histogram objects - const formatNumber = d3.format(',.0f'); - const formatTicks = d3.format(',.00f'); - const x = d3.scale.ordinal(); - const y = d3.scale.linear(); - const xAxis = d3.svg.axis().scale(x).orient('bottom').ticks(numBins).tickFormat(formatTicks); - const yAxis = d3.svg.axis().scale(y).orient('left').ticks(numBins*3); - // Calculate bins for the data - const bins = d3.layout.histogram().bins(numBins)(data); - - // Set the x-values - x.domain(bins.map(function(d) { return d.x;})) - .rangeRoundBands([0, width], .1); - // Set the y-values - y.domain([0, d3.max(bins, function(d) { return d.y;})]) - .range([height, 0]); - - // Create the svg value with the bins - const svg = div.selectAll('svg').data([bins]).enter().append('svg'); - - // Make a rectangular background fill - svg.append('rect') - .attr('width', '100%') - .attr('height', '100%') - .attr('fill', '#f6f6f6'); - - // Transform the svg to make space for the margins - const gEnter = svg - .append('g') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - - // Add the bars and the x axis - gEnter.append('g').attr('class', 'bars'); - gEnter.append('g').attr('class', 'x axis'); - - // Add width and height to the svg - svg.attr('width', slice.width()) - .attr('height', slice.height()); - - // Create the bars in the svg - const bar = svg.select('.bars').selectAll('.bar').data(bins); - bar.enter().append('rect'); - bar.exit().remove(); - // Set the Height and Width for each bar - bar .attr('width', x.rangeBand()) - .attr('x', function(d) { return x(d.x); }) - .attr('y', function(d) { return y(d.y); }) - .attr('height', function(d) { - return y.range()[0] - y(d.y); - }) - .attr('fill', function(d) { return px.color.category21(d.length); }) - .order(); - - // Find maximum length to position the ticks on top of the bar correctly - const maxLength = d3.max(bins, function(d) { return d.length;}); - function textAboveBar(d) { - return d.length/maxLength < 0.1; - } - - // Add a bar text to each bar in the histogram - svg.selectAll('.bartext') - .data(bins) - .enter() - .append('text') - .attr('dy', '.75em') - .attr('y', function(d) { - let padding = 0.0 - if (textAboveBar(d)) { - padding = 12.0 - } else { - padding = -8.0 - } - return y(d.y) - padding; - }) - .attr('x', function(d) { return x(d.x) + (x.rangeBand()/2);}) - .attr('text-anchor', 'middle') - .attr('font-weight', 'bold') - .attr('font-size', '15px') - .text(function(d) { return formatNumber(d.y); }) - .attr('fill', function(d) { - if(textAboveBar(d)) { return 'black'; } else { return 'white'; } - }) - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - - // Update the x-axis - svg.append('g') - .attr('class', 'axis') - .attr('transform', 'translate(' + margin.left + ',' + (height + margin.top) + ')') - .text('values') - .call(xAxis); - - // Update the Y Axis and add minor lines - svg.append('g') - .attr('class', 'axis') - .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') - .text('count') - .call(yAxis) - .selectAll('g') - .filter(function(d) { return d; }) - .classed('minor', true); + const div = d3.select(slice.selector); + + const draw = function (data, numBins) { + // Set Margins + const margin = { + top: 50, + right: 10, + bottom: 20, + left: 50, }; + const navBarHeight = 36; + const navBarBuffer = 10; + const width = slice.width() - margin.left - margin.right; + const height = slice.height() - margin.top - margin.bottom - navBarHeight - navBarBuffer; - const render = function() { - - d3.json(slice.jsonEndpoint(), function(error, json) { - if(error !== null) { - slice.error(error.responseText, error); - return ''; - } - - const numBins = Number(json.form_data.link_length) || 10; + // Set Histogram objects + const formatNumber = d3.format(',.0f'); + const formatTicks = d3.format(',.00f'); + const x = d3.scale.ordinal(); + const y = d3.scale.linear(); + const xAxis = d3.svg.axis() + .scale(x) + .orient('bottom') + .ticks(numBins) + .tickFormat(formatTicks); + const yAxis = d3.svg.axis() + .scale(y) + .orient('left') + .ticks(numBins * 3); + // Calculate bins for the data + const bins = d3.layout.histogram().bins(numBins)(data); - div.selectAll('*').remove(); - _draw(json.data, numBins); - slice.done(json); - }); - }; + // Set the x-values + x.domain(bins.map((d) => d.x)) + .rangeRoundBands([0, width], 0.1); + // Set the y-values + y.domain([0, d3.max(bins, (d) => d.y)]) + .range([height, 0]); - return { - render: render, - resize: render, - }; + // Create the svg value with the bins + const svg = div.selectAll('svg') + .data([bins]) + .enter() + .append('svg'); + + // Make a rectangular background fill + svg.append('rect') + .attr('width', '100%') + .attr('height', '100%') + .attr('fill', '#f6f6f6'); + + // Transform the svg to make space for the margins + const gEnter = svg + .append('g') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + + // Add the bars and the x axis + gEnter.append('g').attr('class', 'bars'); + gEnter.append('g').attr('class', 'x axis'); + + // Add width and height to the svg + svg.attr('width', slice.width()) + .attr('height', slice.height()); + + // Create the bars in the svg + const bar = svg.select('.bars').selectAll('.bar').data(bins); + bar.enter().append('rect'); + bar.exit().remove(); + // Set the Height and Width for each bar + bar.attr('width', x.rangeBand()) + .attr('x', (d) => x(d.x)) + .attr('y', (d) => y(d.y)) + .attr('height', (d) => y.range()[0] - y(d.y)) + .style('fill', (d) => category21(d.length)) + .order(); + + // Find maximum length to position the ticks on top of the bar correctly + const maxLength = d3.max(bins, (d) => d.length); + function textAboveBar(d) { + return d.length / maxLength < 0.1; + } + + // Add a bar text to each bar in the histogram + svg.selectAll('.bartext') + .data(bins) + .enter() + .append('text') + .attr('dy', '.75em') + .attr('y', function (d) { + let padding = 0.0; + if (textAboveBar(d)) { + padding = 12.0; + } else { + padding = -8.0; + } + return y(d.y) - padding; + }) + .attr('x', (d) => x(d.x) + (x.rangeBand() / 2)) + .attr('text-anchor', 'middle') + .attr('font-weight', 'bold') + .attr('font-size', '15px') + .text((d) => formatNumber(d.y)) + .attr('fill', (d) => textAboveBar(d) ? 'black' : 'white') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + + // Update the x-axis + svg.append('g') + .attr('class', 'axis') + .attr('transform', 'translate(' + margin.left + ',' + (height + margin.top) + ')') + .text('values') + .call(xAxis); + + // Update the Y Axis and add minor lines + svg.append('g') + .attr('class', 'axis') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') + .text('count') + .call(yAxis) + .selectAll('g') + .filter(function (d) { return d; }) + .classed('minor', true); + }; + + const render = function () { + d3.json(slice.jsonEndpoint(), function (error, json) { + if (error !== null) { + slice.error(error.responseText, error); + return; + } + + const numBins = Number(json.form_data.link_length) || 10; + + div.selectAll('*').remove(); + draw(json.data, numBins); + slice.done(json); + }); + }; + + return { + render, + resize: render, + }; } module.exports = histogram;