/** * Legend is a attachable widget that can be added to other dc charts to render horizontal legend * labels. * * Examples: * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} * - {@link http://dc-js.github.com/dc.js/crime/index.html Canadian City Crime Stats} * @class legend * @memberof dc * @example * chart.legend(dc.legend().x(400).y(10).itemHeight(13).gap(5)) * @returns {dc.legend} */ dc.legend = function () { var LABEL_GAP = 2; var _legend = {}, _parent, _x = 0, _y = 0, _itemHeight = 12, _gap = 5, _horizontal = false, _legendWidth = 560, _itemWidth = 70, _autoItemWidth = false, _legendText = dc.pluck('name'), _maxItems, _highlightSelected = false; var _g; _legend.parent = function (p) { if (!arguments.length) { return _parent; } _parent = p; return _legend; }; _legend.render = function () { _parent.svg().select('g.dc-legend').remove(); _g = _parent.svg().append('g') .attr('class', 'dc-legend') .attr('transform', 'translate(' + _x + ',' + _y + ')'); var legendables = _parent.legendables(); var filters = _parent.filters(); if (_maxItems !== undefined) { legendables = legendables.slice(0, _maxItems); } var itemEnter = _g.selectAll('g.dc-legend-item') .data(legendables) .enter() .append('g') .attr('class', 'dc-legend-item') .on('mouseover', function (d) { _parent.legendHighlight(d); }) .on('mouseout', function (d) { _parent.legendReset(d); }) .on('click', function (d) { d.chart.legendToggle(d); }); if (_highlightSelected) { itemEnter.classed(dc.constants.SELECTED_CLASS, function (d) { return filters.indexOf(d.name) !== -1; }); } _g.selectAll('g.dc-legend-item') .classed('fadeout', function (d) { return d.chart.isLegendableHidden(d); }); if (legendables.some(dc.pluck('dashstyle'))) { itemEnter .append('line') .attr('x1', 0) .attr('y1', _itemHeight / 2) .attr('x2', _itemHeight) .attr('y2', _itemHeight / 2) .attr('stroke-width', 2) .attr('stroke-dasharray', dc.pluck('dashstyle')) .attr('stroke', dc.pluck('color')); } else { itemEnter .append('rect') .attr('width', _itemHeight) .attr('height', _itemHeight) .attr('fill', function (d) {return d ? d.color : 'blue';}); } itemEnter.append('text') .text(_legendText) .attr('x', _itemHeight + LABEL_GAP) .attr('y', function () { return _itemHeight / 2 + (this.clientHeight ? this.clientHeight : 13) / 2 - 2; }); var _cumulativeLegendTextWidth = 0; var row = 0; itemEnter.attr('transform', function (d, i) { if (_horizontal) { var itemWidth = _autoItemWidth === true ? this.getBBox().width + _gap : _itemWidth; if ((_cumulativeLegendTextWidth + itemWidth) > _legendWidth && _cumulativeLegendTextWidth > 0) { ++row; _cumulativeLegendTextWidth = 0; } var translateBy = 'translate(' + _cumulativeLegendTextWidth + ',' + row * legendItemHeight() + ')'; _cumulativeLegendTextWidth += itemWidth; return translateBy; } else { return 'translate(0,' + i * legendItemHeight() + ')'; } }); }; function legendItemHeight () { return _gap + _itemHeight; } /** * Set or get x coordinate for legend widget. * @method x * @memberof dc.legend * @instance * @param {Number} [x=0] * @returns {Number|dc.legend} */ _legend.x = function (x) { if (!arguments.length) { return _x; } _x = x; return _legend; }; /** * Set or get y coordinate for legend widget. * @method y * @memberof dc.legend * @instance * @param {Number} [y=0] * @returns {Number|dc.legend} */ _legend.y = function (y) { if (!arguments.length) { return _y; } _y = y; return _legend; }; /** * Set or get gap between legend items. * @method gap * @memberof dc.legend * @instance * @param {Number} [gap=5] * @returns {Number|dc.legend} */ _legend.gap = function (gap) { if (!arguments.length) { return _gap; } _gap = gap; return _legend; }; /** * This can be optionally used to enable highlighting legends for the selections/filters for the * chart. * @method highlightSelected * @memberof dc.legend * @instance * @param {String} [highlightSelected] * @return {String|dc.legend} **/ _legend.highlightSelected = function (highlightSelected) { if (!arguments.length) { return _highlightSelected; } _highlightSelected = highlightSelected; return _legend; }; /** * Set or get legend item height. * @method itemHeight * @memberof dc.legend * @instance * @param {Number} [itemHeight=12] * @returns {Number|dc.legend} */ _legend.itemHeight = function (itemHeight) { if (!arguments.length) { return _itemHeight; } _itemHeight = itemHeight; return _legend; }; /** * Position legend horizontally instead of vertically. * @method horizontal * @memberof dc.legend * @instance * @param {Boolean} [horizontal=false] * @returns {Boolean|dc.legend} */ _legend.horizontal = function (horizontal) { if (!arguments.length) { return _horizontal; } _horizontal = horizontal; return _legend; }; /** * Maximum width for horizontal legend. * @method legendWidth * @memberof dc.legend * @instance * @param {Number} [legendWidth=500] * @returns {Number|dc.legend} */ _legend.legendWidth = function (legendWidth) { if (!arguments.length) { return _legendWidth; } _legendWidth = legendWidth; return _legend; }; /** * Legend item width for horizontal legend. * @method itemWidth * @memberof dc.legend * @instance * @param {Number} [itemWidth=70] * @returns {Number|dc.legend} */ _legend.itemWidth = function (itemWidth) { if (!arguments.length) { return _itemWidth; } _itemWidth = itemWidth; return _legend; }; /** * Turn automatic width for legend items on or off. If true, {@link dc.legend#itemWidth itemWidth} is ignored. * This setting takes into account the {@link dc.legend#gap gap}. * @method autoItemWidth * @memberof dc.legend * @instance * @param {Boolean} [autoItemWidth=false] * @returns {Boolean|dc.legend} */ _legend.autoItemWidth = function (autoItemWidth) { if (!arguments.length) { return _autoItemWidth; } _autoItemWidth = autoItemWidth; return _legend; }; /** * Set or get the legend text function. The legend widget uses this function to render the legend * text for each item. If no function is specified the legend widget will display the names * associated with each group. * @method legendText * @memberof dc.legend * @instance * @param {Function} [legendText] * @returns {Function|dc.legend} * @example * // default legendText * legend.legendText(dc.pluck('name')) * * // create numbered legend items * chart.legend(dc.legend().legendText(function(d, i) { return i + '. ' + d.name; })) * * // create legend displaying group counts * chart.legend(dc.legend().legendText(function(d) { return d.name + ': ' d.data; })) **/ _legend.legendText = function (legendText) { if (!arguments.length) { return _legendText; } _legendText = legendText; return _legend; }; /** * Maximum number of legend items to display * @method maxItems * @memberof dc.legend * @instance * @param {Number} [maxItems] * @return {dc.legend} */ _legend.maxItems = function (maxItems) { if (!arguments.length) { return _maxItems; } _maxItems = dc.utils.isNumber(maxItems) ? maxItems : undefined; return _legend; }; return _legend; };