'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _d2 = require('d3'); var _d3 = _interopRequireDefault(_d2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Default config. */ var defaults = { // target element or selector to contain the svg target: '#chart', // width of chart width: 400, // height of chart height: 400, // margin margin: { top: 15, right: 0, bottom: 35, left: 60 }, // enable axis, when disabled margin is removed axis: true, // axis padding axisPadding: 5, // number of x-axis ticks xTicks: 5, // number of y-axis ticks yTicks: 3, // size of axis ticks tickSize: 5, // tick formatter tickFormat: null, // line interpolation interpolate: 'basis', // color range from 'cold' to 'hot' color: ['rgb(0, 180, 240)', 'rgb(243, 42, 100)'], // color interpolation function colorInterpolate: _d3.default.interpolateHcl, // opacity range for the domain 0-N opacityRange: [0.10, 1], // gap size gap: 1, // bin type: 'circle', 'rect' type: 'rect', // axis type: linear, time axisType: 'linear', // mouseover callback for tooltips or value display mouseover: function mouseover(_) {}, // mouseout callback for tooltips or value display mouseout: function mouseout(_) {} }; /** * Zero margin. */ var zeroMargin = { top: 0, right: 0, bottom: 0, left: 0 }; /** * Heatmap. */ var Heatmap = function () { /** * Construct with the given `config`. */ function Heatmap(config) { _classCallCheck(this, Heatmap); this.set(config); if (!this.axis) this.margin = zeroMargin; this.init(); } /** * Set configuration options. */ _createClass(Heatmap, [{ key: 'set', value: function set(config) { Object.assign(this, defaults, config); } /** * Dimensions without margin. */ }, { key: 'dimensions', value: function dimensions() { var width = this.width; var height = this.height; var margin = this.margin; var w = width - margin.left - margin.right; var h = height - margin.top - margin.bottom; return [w, h]; } /** * Initialize the chart. */ }, { key: 'init', value: function init() { var target = this.target; var width = this.width; var height = this.height; var margin = this.margin; var axisPadding = this.axisPadding; var interpolate = this.interpolate; var axis = this.axis; var tickSize = this.tickSize; var xTicks = this.xTicks; var yTicks = this.yTicks; var axisType = this.axisType; var tickFormat = this.tickFormat; var color = this.color; var colorInterpolate = this.colorInterpolate; var opacityRange = this.opacityRange; var _dimensions = this.dimensions(); var _dimensions2 = _slicedToArray(_dimensions, 2); var w = _dimensions2[0]; var h = _dimensions2[1]; this.chart = _d3.default.select(target).attr('width', width).attr('height', height).append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')'); if (axisType == 'time') { this.x = _d3.default.time.scale().range([0, w]); } else { this.x = _d3.default.scale.linear().range([0, w]); } this.y = _d3.default.scale.linear().range([h, 0]); this.opacity = _d3.default.scale.linear().range(opacityRange); this.color = _d3.default.scale.linear().range(color).interpolate(colorInterpolate); if (!axis) return; this.xAxis = _d3.default.svg.axis().orient('bottom').scale(this.x).ticks(xTicks).tickPadding(8).tickSize(tickSize); this.yAxis = _d3.default.svg.axis().orient('left').scale(this.y).ticks(yTicks).tickPadding(8).tickSize(tickSize).tickFormat(tickFormat); this.chart.append('g').attr('class', 'x axis').attr('transform', 'translate(0, ' + (h + axisPadding) + ')').call(this.xAxis); this.chart.append('g').attr('class', 'y axis').attr('transform', 'translate(' + -axisPadding + ', 0)').call(this.yAxis); } /** * Prepate domains for subsequent render methods. */ }, { key: 'prepare', value: function prepare(data, options) { var x = this.x; var y = this.y; var yMin = _d3.default.min(data, function (d) { return _d3.default.min(d.bins, function (d) { return d.bin; }); }); var yMax = _d3.default.max(data, function (d) { return d.bins[d.bins.length - 1].bin; }); var yStep = yMax / data[0].bins.length; x.domain(_d3.default.extent(data, function (d) { return d.bin; })); y.domain([yMin, yMax + yStep]); this.yStep = yStep; } /** * Render axis. */ }, { key: 'renderAxis', value: function renderAxis(data, options) { var chart = this.chart; var xAxis = this.xAxis; var yAxis = this.yAxis; var c = options.animate ? chart.transition() : chart; c.select('.x.axis').call(xAxis); c.select('.y.axis').call(yAxis); } /** * Render bins. */ }, { key: 'renderBuckets', value: function renderBuckets(data) { var chart = this.chart; var x = this.x; var y = this.y; var color = this.color; var opacity = this.opacity; var gap = this.gap; var type = this.type; var yStep = this.yStep; var _dimensions3 = this.dimensions(); var _dimensions4 = _slicedToArray(_dimensions3, 2); var w = _dimensions4[0]; var h = _dimensions4[1]; // max count var zMax = _d3.default.max(data, function (d) { return _d3.default.max(d.bins, function (d) { return d.count; }); }); // color domain color.domain([0, zMax]); opacity.domain([0, zMax]); // bin dimensions var bw = w / data.length; var bh = h / data[0].bins.length; var col = chart.selectAll('.column').data(data); // enter col.enter().append('g').attr('class', 'column'); // update col.attr('transform', function (d, i) { return 'translate(' + x(d.bin) + ', 0)'; }); // exit col.exit().remove(); switch (type) { case 'rect': this.renderBinRect(col, bw, bh, gap, yStep); break; case 'circle': this.renderBinCircle(col, bw / 2, gap, yStep); break; default: throw new Error('invalid .type ' + type); } } /** * Render circular bin. */ }, { key: 'renderBinCircle', value: function renderBinCircle(col, radius, gap, yStep) { var opacity = this.opacity; var color = this.color; var y = this.y; var mouseover = this.mouseover; var mouseout = this.mouseout; var bin = col.selectAll('.bin').data(function (d) { return d.bins; }); // enter bin.enter().append('circle').attr('class', 'bin'); // update bin.style('fill', function (d) { return color(d.count); }).style('fill-opacity', function (d) { return opacity(d.count); }).attr('r', radius - gap).attr('cx', radius).attr('cy', function (d) { return y(d.bin + yStep) + radius; }); bin.on('mouseover', mouseover).on('mouseleave', mouseout); // exit bin.exit().remove(); } /** * Render rectangular bin. */ }, { key: 'renderBinRect', value: function renderBinRect(col, bw, bh, gap, yStep) { var opacity = this.opacity; var color = this.color; var y = this.y; var mouseover = this.mouseover; var mouseout = this.mouseout; var bin = col.selectAll('.bin').data(function (d) { return d.bins; }); // enter bin.enter().append('rect').attr('class', 'bin'); // update bin.style('fill', function (d) { return color(d.count); }).style('fill-opacity', function (d) { return opacity(d.count); }).attr('width', bw - gap).attr('height', bh - gap).attr('x', 0).attr('y', function (d) { return y(d.bin + yStep); }); bin.on('mouseover', mouseover).on('mouseleave', mouseout); // exit bin.exit().remove(); } /** * Render the chart against the given `data`. * * Data shape is N bins by M bins. The top level bins array * forms the x-axis while the nested .bins is the y-axis. The number * of bins in each axis should be pre-defined to the desired size, * as this library does not compute histograms for you. * * [{ bin: X, bins: [{ bin: Y, count: Z }] }] * */ }, { key: 'render', value: function render(data) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var axis = this.axis; this.prepare(data, options); if (axis) this.renderAxis(data, options); this.renderBuckets(data, options); } /** * Update the chart against the given `data`. */ }, { key: 'update', value: function update(data) { this.render(data, { animate: true }); } }]); return Heatmap; }(); exports.default = Heatmap;