Add new scale hooks for plugins (#8112)

* Add new scale hooks for plugins

* Add notes

* cc

* cancelability
This commit is contained in:
Jukka Kurkela 2020-12-02 06:51:47 +02:00 committed by GitHub
parent 6814b79b65
commit c1aeba1e67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 300 deletions

View File

@ -440,7 +440,6 @@ class Chart {
update(mode) {
const me = this;
const args = {mode};
let i, ilen;
each(me.scales, (scale) => {
@ -458,7 +457,7 @@ class Chart {
// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
me._plugins.invalidate();
if (me.notifyPlugins('beforeUpdate', args) === false) {
if (me.notifyPlugins('beforeUpdate', {mode, cancelable: true}) === false) {
return;
}
@ -480,7 +479,7 @@ class Chart {
me._updateDatasets(mode);
// Do this before render so that any plugins that need final scale updates can use it
me.notifyPlugins('afterUpdate', args);
me.notifyPlugins('afterUpdate', {mode});
me._layers.sort(compare2Level('z', '_idx'));
@ -500,7 +499,7 @@ class Chart {
_updateLayout() {
const me = this;
if (me.notifyPlugins('beforeLayout') === false) {
if (me.notifyPlugins('beforeLayout', {cancelable: true}) === false) {
return;
}
@ -531,9 +530,8 @@ class Chart {
_updateDatasets(mode) {
const me = this;
const isFunction = typeof mode === 'function';
const args = {mode};
if (me.notifyPlugins('beforeDatasetsUpdate', args) === false) {
if (me.notifyPlugins('beforeDatasetsUpdate', {mode, cancelable: true}) === false) {
return;
}
@ -541,7 +539,7 @@ class Chart {
me._updateDataset(i, isFunction ? mode({datasetIndex: i}) : mode);
}
me.notifyPlugins('afterDatasetsUpdate', args);
me.notifyPlugins('afterDatasetsUpdate', {mode});
}
/**
@ -552,7 +550,7 @@ class Chart {
_updateDataset(index, mode) {
const me = this;
const meta = me.getDatasetMeta(index);
const args = {meta, index, mode};
const args = {meta, index, mode, cancelable: true};
if (me.notifyPlugins('beforeDatasetUpdate', args) === false) {
return;
@ -560,12 +558,13 @@ class Chart {
meta.controller._update(mode);
args.cancelable = false;
me.notifyPlugins('afterDatasetUpdate', args);
}
render() {
const me = this;
if (me.notifyPlugins('beforeRender') === false) {
if (me.notifyPlugins('beforeRender', {cancelable: true}) === false) {
return;
}
@ -593,7 +592,7 @@ class Chart {
return;
}
if (me.notifyPlugins('beforeDraw') === false) {
if (me.notifyPlugins('beforeDraw', {cancelable: true}) === false) {
return;
}
@ -650,7 +649,7 @@ class Chart {
_drawDatasets() {
const me = this;
if (me.notifyPlugins('beforeDatasetsDraw') === false) {
if (me.notifyPlugins('beforeDatasetsDraw', {cancelable: true}) === false) {
return;
}
@ -675,6 +674,7 @@ class Chart {
const args = {
meta,
index: meta.index,
cancelable: true
};
if (me.notifyPlugins('beforeDatasetDraw', args) === false) {
@ -692,6 +692,7 @@ class Chart {
unclipArea(ctx);
args.cancelable = false;
me.notifyPlugins('afterDatasetDraw', args);
}
@ -1004,7 +1005,7 @@ class Chart {
*/
_eventHandler(e, replay) {
const me = this;
const args = {event: e, replay};
const args = {event: e, replay, cancelable: true};
if (me.notifyPlugins('beforeEvent', args) === false) {
return;
@ -1012,6 +1013,7 @@ class Chart {
const changed = me._handleEvent(e, replay);
args.cancelable = false;
me.notifyPlugins('afterEvent', args);
if (changed) {

View File

@ -49,7 +49,7 @@ export default class PluginService {
const plugin = descriptor.plugin;
const method = plugin[hook];
const params = [chart, args, descriptor.options];
if (callCallback(method, params, plugin) === false) {
if (callCallback(method, params, plugin) === false && args.cancelable) {
return false;
}
}
@ -149,272 +149,3 @@ function createDescriptors(plugins, options, all) {
return result;
}
/**
* Plugin extension hooks.
* @interface Plugin
* @typedef {object} IPlugin
* @since 2.1.0
*/
/**
* @method IPlugin#install
* @desc Called when plugin is installed for this chart instance. This hook is called on disabled plugins (options === false).
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/
/**
* @method IPlugin#start
* @desc Called when a plugin is starting. This happens when chart is created or plugin is enabled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/
/**
* @method IPlugin#stop
* @desc Called when a plugin stopping. This happens when chart is destroyed or plugin is disabled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/
/**
* @method IPlugin#beforeInit
* @desc Called before initializing `chart`.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#afterInit
* @desc Called after `chart` has been initialized and before the first update.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeUpdate
* @desc Called before updating `chart`. If any plugin returns `false`, the update
* is cancelled (and thus subsequent render(s)) until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {string} args.mode - The update mode
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart update.
*/
/**
* @method IPlugin#afterUpdate
* @desc Called after `chart` has been updated and before rendering. Note that this
* hook will not be called if the chart update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {string} args.mode - The update mode
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#reset
* @desc Called during chart reset
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since version 3.0.0
*/
/**
* @method IPlugin#beforeDatasetsUpdate
* @desc Called before updating the `chart` datasets. If any plugin returns `false`,
* the datasets update is cancelled until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {string} args.mode - The update mode
* @param {object} options - The plugin options.
* @returns {boolean} false to cancel the datasets update.
* @since version 2.1.5
*/
/**
* @method IPlugin#afterDatasetsUpdate
* @desc Called after the `chart` datasets have been updated. Note that this hook
* will not be called if the datasets update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {string} args.mode - The update mode
* @param {object} options - The plugin options.
* @since version 2.1.5
*/
/**
* @method IPlugin#beforeDatasetUpdate
* @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
* returns `false`, the datasets update is cancelled until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {string} args.mode - The update mode
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart datasets drawing.
*/
/**
* @method IPlugin#afterDatasetUpdate
* @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
* that this hook will not be called if the datasets update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {string} args.mode - The update mode
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeLayout
* @desc Called before laying out `chart`. If any plugin returns `false`,
* the layout update is cancelled until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart layout.
*/
/**
* @method IPlugin#afterLayout
* @desc Called after the `chart` has been laid out. Note that this hook will not
* be called if the layout update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeRender
* @desc Called before rendering `chart`. If any plugin returns `false`,
* the rendering is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart rendering.
*/
/**
* @method IPlugin#afterRender
* @desc Called after the `chart` has been fully rendered (and animation completed). Note
* that this hook will not be called if the rendering has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeDraw
* @desc Called before drawing `chart` at every animation frame. If any plugin returns `false`,
* the frame drawing is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart drawing.
*/
/**
* @method IPlugin#afterDraw
* @desc Called after the `chart` has been drawn. Note that this hook will not be called
* if the drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeDatasetsDraw
* @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
* the datasets drawing is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart datasets drawing.
*/
/**
* @method IPlugin#afterDatasetsDraw
* @desc Called after the `chart` datasets have been drawn. Note that this hook
* will not be called if the datasets drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeDatasetDraw
* @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
* are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
* is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart datasets drawing.
*/
/**
* @method IPlugin#afterDatasetDraw
* @desc Called after the `chart` datasets at the given `args.index` have been drawn
* (datasets are drawn in the reverse order). Note that this hook will not be called
* if the datasets drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeTooltipDraw
* @desc Called before drawing the `tooltip`. If any plugin returns `false`,
* the tooltip drawing is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Tooltip} args.tooltip - The tooltip.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart tooltip drawing.
*/
/**
* @method IPlugin#afterTooltipDraw
* @desc Called after drawing the `tooltip`. Note that this hook will not
* be called if the tooltip drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Tooltip} args.tooltip - The tooltip.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#beforeEvent
* @desc Called before processing the specified `event`. If any plugin returns `false`,
* the event will be discarded.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {ChartEvent} args.event - The event object.
* @param {boolean} args.replay - True if this event is replayed from `Chart.update`
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#afterEvent
* @desc Called after the `event` has been consumed. Note that this hook
* will not be called if the `event` has been previously discarded.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {ChartEvent} args.event - The event object.
* @param {boolean} args.replay - True if this event is replayed from `Chart.update`
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#resize
* @desc Called after the chart as been resized.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.size - The new canvas display size (eq. canvas.style width & height).
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#destroy
* @desc Called after the chart has been destroyed.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
/**
* @method IPlugin#uninstall
* @desc Called after chart is destroyed on all plugins that were installed for that chart. This hook is called on disabled plugins (options === false).
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/

View File

@ -649,18 +649,24 @@ export default class Scale extends Element {
call(this.options.afterSetDimensions, [this]);
}
_callHooks(name) {
const me = this;
me.chart.notifyPlugins(name, me.getContext());
call(me.options[name], [me]);
}
// Data limits
beforeDataLimits() {
call(this.options.beforeDataLimits, [this]);
this._callHooks('beforeDataLimits');
}
determineDataLimits() {}
afterDataLimits() {
call(this.options.afterDataLimits, [this]);
this._callHooks('afterDataLimits');
}
//
beforeBuildTicks() {
call(this.options.beforeBuildTicks, [this]);
this._callHooks('beforeBuildTicks');
}
/**
* @return {object[]} the ticks
@ -669,7 +675,7 @@ export default class Scale extends Element {
return [];
}
afterBuildTicks() {
call(this.options.afterBuildTicks, [this]);
this._callHooks('afterBuildTicks');
}
beforeTickToLabelConversion() {

View File

@ -1396,6 +1396,14 @@ describe('Chart', function() {
update: [
'beforeUpdate',
'beforeLayout',
'beforeDataLimits',
'afterDataLimits',
'beforeBuildTicks',
'afterBuildTicks',
'beforeDataLimits',
'afterDataLimits',
'beforeBuildTicks',
'afterBuildTicks',
'afterLayout',
'beforeDatasetsUpdate',
'beforeDatasetUpdate',

View File

@ -151,7 +151,7 @@ describe('Chart.plugins', function() {
spyOn(plugin, 'hook').and.callThrough();
});
var ret = chart.notifyPlugins('hook');
var ret = chart.notifyPlugins('hook', {cancelable: true});
expect(ret).toBeFalsy();
expect(plugins[0].hook).toHaveBeenCalled();
expect(plugins[1].hook).toHaveBeenCalled();

58
types/core/index.d.ts vendored
View File

@ -597,14 +597,14 @@ export interface Plugin<O = {}> {
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
beforeInit?(chart: Chart, args: {}, options: O): boolean | void;
beforeInit?(chart: Chart, args: {}, options: O): void;
/**
* @desc Called after `chart` has been initialized and before the first update.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterInit?(chart: Chart, args: {}, options: O): boolean | void;
afterInit?(chart: Chart, args: {}, options: O): void;
/**
* @desc Called before updating `chart`. If any plugin returns `false`, the update
* is cancelled (and thus subsequent render(s)) until another `update` is triggered.
@ -623,7 +623,7 @@ export interface Plugin<O = {}> {
* @param {UpdateMode} args.mode - The update mode
* @param {object} options - The plugin options.
*/
afterUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): boolean | void;
afterUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): void;
/**
* @desc Called during chart reset
* @param {Chart} chart - The chart instance.
@ -652,7 +652,7 @@ export interface Plugin<O = {}> {
* @param {object} options - The plugin options.
* @since version 2.1.5
*/
afterDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): boolean | void;
afterDatasetsUpdate?(chart: Chart, args: { mode: UpdateMode }, options: O): void;
/**
* @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
* returns `false`, the datasets update is cancelled until another `update` is triggered.
@ -675,7 +675,7 @@ export interface Plugin<O = {}> {
* @param {UpdateMode} args.mode - The update mode.
* @param {object} options - The plugin options.
*/
afterDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode }, options: O): boolean | void;
afterDatasetUpdate?(chart: Chart, args: { index: number; meta: ChartMeta, mode: UpdateMode }, options: O): void;
/**
* @desc Called before laying out `chart`. If any plugin returns `false`,
* the layout update is cancelled until another `update` is triggered.
@ -685,6 +685,38 @@ export interface Plugin<O = {}> {
* @returns {boolean} `false` to cancel the chart layout.
*/
beforeLayout?(chart: Chart, args: {}, options: O): boolean | void;
/**
* @desc Called before scale data limits are calculated. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
beforeDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void;
/**
* @desc Called after scale data limits are calculated. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
afterDataLimits?(chart: Chart, args: { scale: Scale }, options: O): void;
/**
* @desc Called before scale bulds its ticks. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
beforeBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void;
/**
* @desc Called after scale has build its ticks. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
afterBuildTicks?(chart: Chart, args: { scale: Scale }, options: O): void;
/**
* @desc Called after the `chart` has been laid out. Note that this hook will not
* be called if the layout update has been previously cancelled.
@ -692,7 +724,7 @@ export interface Plugin<O = {}> {
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterLayout?(chart: Chart, args: {}, options: O): boolean | void;
afterLayout?(chart: Chart, args: {}, options: O): void;
/**
* @desc Called before rendering `chart`. If any plugin returns `false`,
* the rendering is cancelled until another `render` is triggered.
@ -709,7 +741,7 @@ export interface Plugin<O = {}> {
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterRender?(chart: Chart, args: {}, options: O): boolean | void;
afterRender?(chart: Chart, args: {}, options: O): void;
/**
* @desc Called before drawing `chart` at every animation frame. If any plugin returns `false`,
* the frame drawing is cancelled untilanother `render` is triggered.
@ -726,7 +758,7 @@ export interface Plugin<O = {}> {
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterDraw?(chart: Chart, args: {}, options: O): boolean | void;
afterDraw?(chart: Chart, args: {}, options: O): void;
/**
* @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
* the datasets drawing is cancelled until another `render` is triggered.
@ -743,7 +775,7 @@ export interface Plugin<O = {}> {
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterDatasetsDraw?(chart: Chart, args: {}, options: O): boolean | void;
afterDatasetsDraw?(chart: Chart, args: {}, options: O): void;
/**
* @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
* are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
@ -766,7 +798,7 @@ export interface Plugin<O = {}> {
* @param {object} args.meta - The dataset metadata.
* @param {object} options - The plugin options.
*/
afterDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): boolean | void;
afterDatasetDraw?(chart: Chart, args: { index: number; meta: ChartMeta }, options: O): void;
/**
* @desc Called before processing the specified `event`. If any plugin returns `false`,
* the event will be discarded.
@ -786,7 +818,7 @@ export interface Plugin<O = {}> {
* @param {boolean} replay - True if this event is replayed from `Chart.update`
* @param {object} options - The plugin options.
*/
afterEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean }, options: O): boolean | void;
afterEvent?(chart: Chart, args: { event: ChartEvent, replay: boolean }, options: O): void;
/**
* @desc Called after the chart as been resized.
* @param {Chart} chart - The chart instance.
@ -794,14 +826,14 @@ export interface Plugin<O = {}> {
* @param {number} args.size - The new canvas display size (eq. canvas.style width & height).
* @param {object} options - The plugin options.
*/
resize?(chart: Chart, args: { size: { width: number, height: number } }, options: O): boolean | void;
resize?(chart: Chart, args: { size: { width: number, height: number } }, options: O): void;
/**
* Called after the chart has been destroyed.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
destroy?(chart: Chart, args: {}, options: O): boolean | void;
destroy?(chart: Chart, args: {}, options: O): void;
/**
* Called after chart is destroyed on all plugins that were installed for that chart. This hook is also invoked for disabled plugins (options === false).
* @param {Chart} chart - The chart instance.