nagix/chartjs-plugin-style

View on GitHub
src/plugins/plugin.styleLegend.js

Summary

Maintainability
A
2 hrs
Test Coverage
'use strict';

import Chart from 'chart.js';
import styleHelpers from '../helpers/helpers.style';

var defaults = Chart.defaults;
var helpers = Chart.helpers;

// For Chart.js 2.7.1 backward compatibility
var layouts = Chart.layouts || Chart.layoutService;

// For Chart.js 2.6.0 backward compatibility
var valueOrDefault = helpers.valueOrDefault || helpers.getValueOrDefault;

// For Chart.js 2.6.0 backward compatibility
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault || helpers.getValueAtIndexOrDefault;

// For Chart.js 2.6.0 backward compatibility
var mergeIf = helpers.mergeIf || function(target, source) {
    return helpers.configMerge.call(this, source, target);
};

var extend = helpers.extend;

// Ported from Chart.js 2.8.0. Modified for style legend.
// Generates labels shown in the legend
defaults.global.legend.labels.generateLabels = function(chart) {
    var data = chart.data;
    var options = chart.options.legend || {};
    var usePointStyle = options.labels && options.labels.usePointStyle;

    return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
        var meta = chart.getDatasetMeta(i);
        var controller = meta.controller;
        var elementOpts = chart.options.elements;
        var element, styleOptions;

        if (usePointStyle) {
            element = meta.data[0] || {};
            styleOptions = styleHelpers.resolvePointStyle(controller, element, i, elementOpts.point);
        } else if (meta.dataset) {
            element = meta.dataset;
            styleOptions = styleHelpers.resolveLineStyle(controller, element, elementOpts.line);
        } else {
            element = meta.data[0] || {};
            styleOptions = styleHelpers.resolveStyle(controller, element, i, meta.bar ? elementOpts.rectangle : elementOpts.point);
        }

        return extend({
            text: dataset.label,
            fillStyle: valueAtIndexOrDefault(dataset.backgroundColor, 0),
            hidden: !chart.isDatasetVisible(i),
            lineCap: dataset.borderCapStyle,
            lineDash: dataset.borderDash,
            lineDashOffset: dataset.borderDashOffset,
            lineJoin: dataset.borderJoinStyle,
            lineWidth: dataset.borderWidth,
            strokeStyle: dataset.borderColor,
            pointStyle: dataset.pointStyle,

            // Below is extra data used for toggling the datasets
            datasetIndex: i
        }, styleOptions);
    }, this) : [];
};

function drawLegendBox(chart, options, drawCallback) {
    var ctx = chart.ctx;

    styleHelpers.drawShadow(chart, options, drawCallback, true);

    if (styleHelpers.opaque(ctx.fillStyle)) {
        ctx.save();

        ctx.strokeStyle = 'rgba(0, 0, 0, 0)';
        drawCallback();

        styleHelpers.drawBackgroundOverlay(chart, options, drawCallback);
        styleHelpers.drawBevel(chart, options, drawCallback);

        ctx.restore();
    }

    styleHelpers.drawInnerGlow(chart, options, drawCallback);
    styleHelpers.drawOuterGlow(chart, options, drawCallback);

    ctx.fillStyle = 'rgba(0, 0, 0, 0)';
    drawCallback();

    ctx.restore();
}

var StyleLegend = Chart.Legend.extend({

    draw: function() {
        var me = this;
        var chart = me.chart;
        var globalDefaults = defaults.global;
        var each = helpers.each;
        var drawPoint = helpers.canvas.drawPoint;
        var ctx = me.ctx;
        var options;

        helpers.each = function(loopable, fn) {
            each(loopable, function(legendItem) {
                options = styleHelpers.mergeStyle({
                    borderColor: valueOrDefault(legendItem.strokeStyle, globalDefaults.defaultColor),
                    borderWidth: valueOrDefault(legendItem.lineWidth, globalDefaults.elements.line.borderWidth)
                }, legendItem);

                fn.apply(null, arguments);
            });
        };

        helpers.canvas.drawPoint = function() {
            var args = arguments;
            var drawCallback = function() {
                drawPoint.apply(null, args);
            };

            drawLegendBox(chart, options, drawCallback);
        };

        ctx.strokeRect = function() {
            // noop
        };

        ctx.fillRect = function(x, y, width, height) {
            var drawCallback = function() {
                ctx.beginPath();
                ctx.rect(x, y, width, height);
                ctx.fill();
                if (options.borderWidth !== 0) {
                    ctx.stroke();
                }
            };

            drawLegendBox(chart, options, drawCallback);
        };

        Chart.Legend.prototype.draw.apply(me, arguments);

        helpers.each = each;
        helpers.canvas.drawPoint = drawPoint;
        delete ctx.fillRect;
        delete ctx.strokeRect;
    }
});

// Ported from Chart.js 2.8.0. Modified for style legend.
function createNewLegendAndAttach(chart, legendOpts) {
    var legend = new StyleLegend({
        ctx: chart.ctx,
        options: legendOpts,
        chart: chart
    });

    layouts.configure(chart, legend, legendOpts);
    layouts.addBox(chart, legend);
    chart.legend = legend;
}

export default {
    id: 'legend',

    _element: StyleLegend,

    // Ported from Chart.js 2.8.0.
    beforeInit: function(chart) {
        var legendOpts = chart.options.legend;

        if (legendOpts) {
            createNewLegendAndAttach(chart, legendOpts);
        }
    },

    // Ported from Chart.js 2.8.0.
    beforeUpdate: function(chart) {
        var legendOpts = chart.options.legend;
        var legend = chart.legend;

        if (legendOpts) {
            mergeIf(legendOpts, defaults.global.legend);

            if (legend) {
                layouts.configure(chart, legend, legendOpts);
                legend.options = legendOpts;
            } else {
                createNewLegendAndAttach(chart, legendOpts);
            }
        } else if (legend) {
            layouts.removeBox(chart, legend);
            delete chart.legend;
        }
    },

    // Ported from Chart.js 2.8.0.
    afterEvent: function(chart, e) {
        var legend = chart.legend;
        if (legend) {
            legend.handleEvent(e);
        }
    }
};