| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /**
- * @see https://github.com/chartjs/Chart.js/issues/4176
- */
- 'use strict';
- import Chart from 'chart.js';
- import Label from './label';
- import utils from './utils';
- import defaults from './defaults';
- var helpers = Chart.helpers;
- var EXPANDO_KEY = '$datalabels';
- Chart.defaults.global.plugins.datalabels = defaults;
- function configure(dataset, options) {
- var override = dataset.datalabels;
- var config = {};
- if (override === false) {
- return null;
- }
- if (override === true) {
- override = {};
- }
- return helpers.merge(config, [options, override]);
- }
- function drawLabels(chart, datasetIndex) {
- var meta = chart.getDatasetMeta(datasetIndex);
- var elements = meta.data || [];
- var ilen = elements.length;
- var i, el, label;
- for (i = 0; i < ilen; ++i) {
- el = elements[i];
- label = el[EXPANDO_KEY];
- if (label) {
- label.draw(chart.ctx);
- }
- }
- }
- function labelAtXY(chart, x, y) {
- var items = chart[EXPANDO_KEY].labels;
- var i, j, labels, label;
- // Until we support z-index, let's hit test in the drawing reverse order
- for (i = items.length - 1; i >= 0; --i) {
- labels = items[i] || [];
- for (j = labels.length - 1; j >= 0; --j) {
- label = labels[j];
- if (label.contains(x, y)) {
- return {dataset: i, label: label};
- }
- }
- }
- return null;
- }
- function dispatchEvent(chart, listeners, target) {
- var callback = listeners && listeners[target.dataset];
- if (!callback) {
- return;
- }
- var label = target.label;
- var context = label.$context;
- if (helpers.callback(callback, [context]) === true) {
- // Users are allowed to tweak the given context by injecting values that can be
- // used in scriptable options to display labels differently based on the current
- // event (e.g. highlight an hovered label). That's why we update the label with
- // the output context and schedule a new chart render by setting it dirty.
- chart[EXPANDO_KEY].dirty = true;
- label.update(context);
- }
- }
- function dispatchMoveEvents(chart, listeners, previous, target) {
- var enter, leave;
- if (!previous && !target) {
- return;
- }
- if (!previous) {
- enter = true;
- } else if (!target) {
- leave = true;
- } else if (previous.label !== target.label) {
- leave = enter = true;
- }
- if (leave) {
- dispatchEvent(chart, listeners.leave, previous);
- }
- if (enter) {
- dispatchEvent(chart, listeners.enter, target);
- }
- }
- function handleMoveEvents(chart, event) {
- var expando = chart[EXPANDO_KEY];
- var listeners = expando.listeners;
- var previous, target;
- if (!listeners.enter && !listeners.leave) {
- return;
- }
- if (event.type === 'mousemove') {
- target = labelAtXY(chart, event.x, event.y);
- } else if (event.type !== 'mouseout') {
- return;
- }
- previous = expando.hovered;
- expando.hovered = target;
- dispatchMoveEvents(chart, listeners, previous, target);
- }
- function handleClickEvents(chart, event) {
- var handlers = chart[EXPANDO_KEY].listeners.click;
- var target = handlers && labelAtXY(chart, event.x, event.y);
- if (target) {
- dispatchEvent(chart, handlers, target);
- }
- }
- Chart.defaults.global.plugins.datalabels = defaults;
- Chart.plugins.register({
- id: 'datalabels',
- beforeInit: function(chart) {
- chart[EXPANDO_KEY] = {
- actives: []
- };
- },
- beforeUpdate: function(chart) {
- var expando = chart[EXPANDO_KEY];
- expando.listened = false;
- expando.listeners = {}; // {event-type: {dataset-index: function}}
- expando.labels = []; // [dataset-index: [labels]]
- },
- afterDatasetUpdate: function(chart, args, options) {
- var datasetIndex = args.index;
- var expando = chart[EXPANDO_KEY];
- var labels = expando.labels[datasetIndex] = [];
- var dataset = chart.data.datasets[datasetIndex];
- var config = configure(dataset, options);
- var elements = args.meta.data || [];
- var ilen = elements.length;
- var ctx = chart.ctx;
- var i, el, label;
- ctx.save();
- for (i = 0; i < ilen; ++i) {
- el = elements[i];
- if (el && !el.hidden && !el._model.skip) {
- labels.push(label = new Label(config, ctx, el, i));
- label.update(label.$context = {
- active: false,
- chart: chart,
- dataIndex: i,
- dataset: dataset,
- datasetIndex: datasetIndex
- });
- } else {
- label = null;
- }
- el[EXPANDO_KEY] = label;
- }
- ctx.restore();
- // Store listeners at the chart level and per event type to optimize
- // cases where no listeners are registered for a specific event
- helpers.merge(expando.listeners, config.listeners || {}, {
- merger: function(key, target, source) {
- target[key] = target[key] || {};
- target[key][args.index] = source[key];
- expando.listened = true;
- }
- });
- },
- // Draw labels on top of all dataset elements
- // https://github.com/chartjs/chartjs-plugin-datalabels/issues/29
- // https://github.com/chartjs/chartjs-plugin-datalabels/issues/32
- afterDatasetsDraw: function(chart) {
- for (var i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
- drawLabels(chart, i);
- }
- },
- beforeEvent: function(chart, event) {
- // If there is no listener registered for this chart, `listened` will be false,
- // meaning we can immediately ignore the incoming event and avoid useless extra
- // computation for users who don't implement label interactions.
- if (chart[EXPANDO_KEY].listened) {
- switch (event.type) {
- case 'mousemove':
- case 'mouseout':
- handleMoveEvents(chart, event);
- break;
- case 'click':
- handleClickEvents(chart, event);
- break;
- default:
- }
- }
- },
- afterEvent: function(chart) {
- var expando = chart[EXPANDO_KEY];
- var previous = expando.actives;
- var actives = expando.actives = chart.lastActive || []; // public API?!
- var updates = utils.arrayDiff(previous, actives);
- var i, ilen, update, label;
- for (i = 0, ilen = updates.length; i < ilen; ++i) {
- update = updates[i];
- if (update[1]) {
- label = update[0][EXPANDO_KEY];
- label.$context.active = (update[1] === 1);
- label.update(label.$context);
- }
- }
- if ((expando.dirty || updates.length) && !chart.animating) {
- chart.render();
- }
- delete expando.dirty;
- }
- });
|