mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
parent
69c8cc7e7b
commit
7024aad38f
@ -6,7 +6,7 @@ The chart title defines text to draw at the top of the chart.
|
|||||||
|
|
||||||
## Title Configuration
|
## Title Configuration
|
||||||
|
|
||||||
The title configuration is passed into the `options.title` namespace. The global options for the chart title is defined in `Chart.defaults.title`.
|
The title configuration is passed into the `options.title` namespace. The global options for the chart title is defined in `Chart.defaults.plugins.title`.
|
||||||
|
|
||||||
| Name | Type | Default | Description
|
| Name | Type | Default | Description
|
||||||
| ---- | ---- | ------- | -----------
|
| ---- | ---- | ------- | -----------
|
||||||
|
|||||||
@ -4,7 +4,7 @@ title: Tooltip
|
|||||||
|
|
||||||
## Tooltip Configuration
|
## Tooltip Configuration
|
||||||
|
|
||||||
The tooltip configuration is passed into the `options.tooltips` namespace. The global options for the chart tooltips is defined in `Chart.defaults.tooltips`.
|
The tooltip configuration is passed into the `options.tooltips` namespace. The global options for the chart tooltips is defined in `Chart.defaults.plugins.tooltip`.
|
||||||
|
|
||||||
| Name | Type | Default | Description
|
| Name | Type | Default | Description
|
||||||
| ---- | ---- | ------- | -----------
|
| ---- | ---- | ------- | -----------
|
||||||
@ -63,7 +63,7 @@ Example:
|
|||||||
* @param eventPosition {Point} the position of the event in canvas coordinates
|
* @param eventPosition {Point} the position of the event in canvas coordinates
|
||||||
* @returns {Point} the tooltip position
|
* @returns {Point} the tooltip position
|
||||||
*/
|
*/
|
||||||
const tooltipPlugin = Chart.plugins.getAll().find(p => p.id === 'tooltip');
|
const tooltipPlugin = Chart.registry.getPlugin('tooltip');
|
||||||
tooltipPlugin.positioners.custom = function(elements, eventPosition) {
|
tooltipPlugin.positioners.custom = function(elements, eventPosition) {
|
||||||
/** @type {Tooltip} */
|
/** @type {Tooltip} */
|
||||||
var tooltip = this;
|
var tooltip = this;
|
||||||
|
|||||||
@ -5,12 +5,27 @@ title: New Axes
|
|||||||
Axes in Chart.js can be individually extended. Axes should always derive from `Chart.Scale` but this is not a mandatory requirement.
|
Axes in Chart.js can be individually extended. Axes should always derive from `Chart.Scale` but this is not a mandatory requirement.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
class MyScale extends Chart.Scale{
|
class MyScale extends Chart.Scale {
|
||||||
/* extensions ... */
|
/* extensions ... */
|
||||||
}
|
}
|
||||||
MyScale.id = 'myScale';
|
MyScale.id = 'myScale';
|
||||||
MyScale.defaults = defaultConfigObject;
|
MyScale.defaults = defaultConfigObject;
|
||||||
|
|
||||||
|
// Or in classic style
|
||||||
|
/*
|
||||||
|
function MyScale() {
|
||||||
|
Chart.Scale.call(this, arguments);
|
||||||
|
// constructor stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
MyScale.prototype.draw = function(ctx) {
|
||||||
|
Chart.Scale.prototype.draw.call(this, arguments);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
MyScale.id = 'myScale';
|
||||||
|
MyScale.defaults = defaultConfigObject;
|
||||||
|
*/
|
||||||
|
|
||||||
// MyScale is now derived from Chart.Scale
|
// MyScale is now derived from Chart.Scale
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -18,6 +33,11 @@ Once you have created your scale class, you need to register it with the global
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
Chart.register(MyScale);
|
Chart.register(MyScale);
|
||||||
|
|
||||||
|
// If the scale is created in classical way, the prototype can not be used to detect what
|
||||||
|
// you are trying to register - so you need to be explicit:
|
||||||
|
|
||||||
|
// Chart.registry.addScales(MyScale);
|
||||||
```
|
```
|
||||||
|
|
||||||
To use the new scale, simply pass in the string key to the config when creating a chart.
|
To use the new scale, simply pass in the string key to the config when creating a chart.
|
||||||
@ -66,6 +86,7 @@ Scale instances are given the following properties during the fitting process.
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Scale Interface
|
## Scale Interface
|
||||||
|
|
||||||
To work with Chart.js, custom scale types must implement the following interface.
|
To work with Chart.js, custom scale types must implement the following interface.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -120,6 +141,7 @@ Optionally, the following methods may also be overwritten, but an implementation
|
|||||||
```
|
```
|
||||||
|
|
||||||
The Core.Scale base class also has some utility functions that you may find useful.
|
The Core.Scale base class also has some utility functions that you may find useful.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
// Returns true if the scale instance is horizontal
|
// Returns true if the scale instance is horizontal
|
||||||
|
|||||||
@ -111,3 +111,41 @@ new Chart(ctx, {
|
|||||||
options: options
|
options: options
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Same example in classic style
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function Custom() {
|
||||||
|
Chart.controllers.bubble.call(this, arguments);
|
||||||
|
// constructor stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
Custom.prototype.draw = function(ctx) {
|
||||||
|
Chart.controllers.bubble.prototype.draw.call(this, arguments);
|
||||||
|
|
||||||
|
var meta = this.getMeta();
|
||||||
|
var pt0 = meta.data[0];
|
||||||
|
var radius = pt0.radius;
|
||||||
|
|
||||||
|
var ctx = this.chart.chart.ctx;
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = 'red';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeRect(pt0.x - radius, pt0.y - radius, 2 * radius, 2 * radius);
|
||||||
|
ctx.restore();}
|
||||||
|
}
|
||||||
|
|
||||||
|
Custom.id = 'derivedBubble';
|
||||||
|
Custom.defaults = Chart.defaults.bubble;
|
||||||
|
|
||||||
|
// Prototype chain can not be used to detect we are trying to register a controller, so we need
|
||||||
|
// to be explicit
|
||||||
|
Chart.registry.addControllers(Custom);
|
||||||
|
|
||||||
|
// Now we can create and use our new chart type
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'derivedBubble',
|
||||||
|
data: data,
|
||||||
|
options: options
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|||||||
@ -22,8 +22,13 @@ var myChart = new Chart(ctx, {...});
|
|||||||
|
|
||||||
## Bundlers (Webpack, Rollup, etc.)
|
## Bundlers (Webpack, Rollup, etc.)
|
||||||
|
|
||||||
|
Chart.js 3 is tree-shakeable, so it is necessary to import and register the controllers, elements, scales and plugins you are going to use.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import Chart from 'chart.js';
|
import Chart, LineController, Line, Point, LinearScale, CategoryScale, Title, Tooltip, Filler, Legend from 'chart.js';
|
||||||
|
|
||||||
|
Chart.register(LineController, Line, Point, LinearScale, CategoryScale, Title, Tooltip, Filler, Legend);
|
||||||
|
|
||||||
var myChart = new Chart(ctx, {...});
|
var myChart = new Chart(ctx, {...});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ Chart.js 3.0 introduces a number of breaking changes. Chart.js 2.0 was released
|
|||||||
* API documentation generated and verified by TypeDoc
|
* API documentation generated and verified by TypeDoc
|
||||||
* No more CSS injection
|
* No more CSS injection
|
||||||
* Tons of bug fixes
|
* Tons of bug fixes
|
||||||
|
* Tree shaking
|
||||||
|
|
||||||
## End user migration
|
## End user migration
|
||||||
|
|
||||||
@ -200,6 +201,32 @@ Some of the biggest things that have changed:
|
|||||||
* `Element._model` and `Element._view` are no longer used and properties are now set directly on the elements. You will have to use the method `getProps` to access these properties inside most methods such as `inXRange`/`inYRange` and `getCenterPoint`. Please take a look at [the Chart.js-provided elements](https://github.com/chartjs/Chart.js/tree/master/src/elements) for examples.
|
* `Element._model` and `Element._view` are no longer used and properties are now set directly on the elements. You will have to use the method `getProps` to access these properties inside most methods such as `inXRange`/`inYRange` and `getCenterPoint`. Please take a look at [the Chart.js-provided elements](https://github.com/chartjs/Chart.js/tree/master/src/elements) for examples.
|
||||||
* When building the elements in a controller, it's now suggested to call `updateElement` to provide the element properties. There are also methods such as `getSharedOptions` and `includeOptions` that have been added to skip redundant computation. Please take a look at [the Chart.js-provided controllers](https://github.com/chartjs/Chart.js/tree/master/src/controllers) for examples.
|
* When building the elements in a controller, it's now suggested to call `updateElement` to provide the element properties. There are also methods such as `getSharedOptions` and `includeOptions` that have been added to skip redundant computation. Please take a look at [the Chart.js-provided controllers](https://github.com/chartjs/Chart.js/tree/master/src/controllers) for examples.
|
||||||
* Scales introduced a new parsing API. This API takes user data and converts it into a more standard format. E.g. it allows users to provide numeric data as a `string` and converts it to a `number` where necessary. Previously this was done on the fly as charts were rendered. Now it's done up front with the ability to skip it for better performance if users provide data in the correct format. If you're using standard data format like `x`/`y` you may not need to do anything. If you're using a custom data format you will have to override some of the parse methods in `core.datasetController.js`. An example can be found in [chartjs-chart-financial](https://github.com/chartjs/chartjs-chart-financial), which uses an `{o, h, l, c}` data format.
|
* Scales introduced a new parsing API. This API takes user data and converts it into a more standard format. E.g. it allows users to provide numeric data as a `string` and converts it to a `number` where necessary. Previously this was done on the fly as charts were rendered. Now it's done up front with the ability to skip it for better performance if users provide data in the correct format. If you're using standard data format like `x`/`y` you may not need to do anything. If you're using a custom data format you will have to override some of the parse methods in `core.datasetController.js`. An example can be found in [chartjs-chart-financial](https://github.com/chartjs/chartjs-chart-financial), which uses an `{o, h, l, c}` data format.
|
||||||
|
* Chart.js 3 is tree-shakeable. So when you use it as a module in a project, you need to import and register the controllers, elements, scales and plugins you want to use:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import Chart, LineController, Line, Point, LinearScale, Title from `chart.js`
|
||||||
|
|
||||||
|
Chart.register(LineController, Line, Point, LinearScale, Title);
|
||||||
|
|
||||||
|
const chart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
// data: ...
|
||||||
|
options: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Chart Title'
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'linear'
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: 'linear'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
A few changes were made to controllers that are more straight-forward, but will affect all controllers:
|
A few changes were made to controllers that are more straight-forward, but will affect all controllers:
|
||||||
|
|
||||||
@ -231,12 +258,14 @@ The following properties and methods were removed:
|
|||||||
* `Chart.offsetX`
|
* `Chart.offsetX`
|
||||||
* `Chart.offsetY`
|
* `Chart.offsetY`
|
||||||
* `Chart.outerRadius` now lives on doughnut, pie, and polarArea controllers
|
* `Chart.outerRadius` now lives on doughnut, pie, and polarArea controllers
|
||||||
|
* `Chart.plugins` was replaced with `Chart.registry`. Plugin defaults are now in `Chart.defaults.plugins[id]`.
|
||||||
* `Chart.PolarArea`. New charts are created via `new Chart` and providing the appropriate `type` parameter
|
* `Chart.PolarArea`. New charts are created via `new Chart` and providing the appropriate `type` parameter
|
||||||
* `Chart.prototype.generateLegend`
|
* `Chart.prototype.generateLegend`
|
||||||
* `Chart.platform`. It only contained `disableCSSInjection`. CSS is never injected in v3.
|
* `Chart.platform`. It only contained `disableCSSInjection`. CSS is never injected in v3.
|
||||||
* `Chart.PluginBase`
|
* `Chart.PluginBase`
|
||||||
* `Chart.Radar`. New charts are created via `new Chart` and providing the appropriate `type` parameter
|
* `Chart.Radar`. New charts are created via `new Chart` and providing the appropriate `type` parameter
|
||||||
* `Chart.radiusLength`
|
* `Chart.radiusLength`
|
||||||
|
* `Chart.scaleService` was replaced with `Chart.registry`. Scale defaults are now in `Chart.defaults.scales[type]`.
|
||||||
* `Chart.Scatter`. New charts are created via `new Chart` and providing the appropriate `type` parameter
|
* `Chart.Scatter`. New charts are created via `new Chart` and providing the appropriate `type` parameter
|
||||||
* `Chart.types`
|
* `Chart.types`
|
||||||
* `Chart.Title` was moved to `Chart.plugins.title._element` and made private
|
* `Chart.Title` was moved to `Chart.plugins.title._element` and made private
|
||||||
@ -405,7 +434,6 @@ The APIs listed in this section have changed in signature or behaviour from vers
|
|||||||
|
|
||||||
* `Scale.getLabelForIndex` was replaced by `scale.getLabelForValue`
|
* `Scale.getLabelForIndex` was replaced by `scale.getLabelForValue`
|
||||||
* `Scale.getPixelForValue` now has only one parameter. For the `TimeScale` that parameter must be millis since the epoch
|
* `Scale.getPixelForValue` now has only one parameter. For the `TimeScale` that parameter must be millis since the epoch
|
||||||
* `ScaleService.registerScaleType` was renamed to `ScaleService.registerScale` and now takes a scale constructors which is expected to have `id` and `defaults` properties.
|
|
||||||
|
|
||||||
##### Ticks
|
##### Ticks
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ You can download the latest version of Chart.js from the [GitHub releases](https
|
|||||||
It's easy to get started with Chart.js. All that's required is the script included in your page along with a single `<canvas>` node to render the chart.
|
It's easy to get started with Chart.js. All that's required is the script included in your page along with a single `<canvas>` node to render the chart.
|
||||||
|
|
||||||
In this example, we create a bar chart for a single dataset and render that in our page. You can see all the ways to use Chart.js in the [usage documentation](./getting-started/usage.md).
|
In this example, we create a bar chart for a single dataset and render that in our page. You can see all the ways to use Chart.js in the [usage documentation](./getting-started/usage.md).
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<canvas id="myChart" width="400" height="400"></canvas>
|
<canvas id="myChart" width="400" height="400"></canvas>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"autobuild": "rollup -c -w",
|
"autobuild": "rollup -c -w",
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"dev": "cross-env NODE_ENV=test karma start ---auto-watch --no-single-run --browsers chrome --grep",
|
"dev": "karma start ---auto-watch --no-single-run --browsers chrome --grep",
|
||||||
"docs": "cd docs && npm install && npm run build && mkdir -p ../dist && cp -r build ../dist/docs",
|
"docs": "cd docs && npm install && npm run build && mkdir -p ../dist && cp -r build ../dist/docs",
|
||||||
"lint-js": "eslint samples/**/*.html samples/**/*.js src/**/*.js test/**/*.js",
|
"lint-js": "eslint samples/**/*.html samples/**/*.js src/**/*.js test/**/*.js",
|
||||||
"lint-tsc": "tsc",
|
"lint-tsc": "tsc",
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
Chart.defaults.tooltips.custom = function(tooltip) {
|
Chart.defaults.plugins.tooltip.custom = function(tooltip) {
|
||||||
// Tooltip Element
|
// Tooltip Element
|
||||||
var tooltipEl = document.getElementById('chartjs-tooltip');
|
var tooltipEl = document.getElementById('chartjs-tooltip');
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import defaults from './core.defaults';
|
|||||||
import Interaction from './core.interaction';
|
import Interaction from './core.interaction';
|
||||||
import layouts from './core.layouts';
|
import layouts from './core.layouts';
|
||||||
import {BasicPlatform, DomPlatform} from '../platform';
|
import {BasicPlatform, DomPlatform} from '../platform';
|
||||||
import plugins from './core.plugins';
|
import PluginService from './core.plugins';
|
||||||
import registry from './core.registry';
|
import registry from './core.registry';
|
||||||
import {getMaximumWidth, getMaximumHeight, retinaScale} from '../helpers/helpers.dom';
|
import {getMaximumWidth, getMaximumHeight, retinaScale} from '../helpers/helpers.dom';
|
||||||
import {mergeIf, merge, _merger, each, callback as callCallback, uid, valueOrDefault, _elementsEqual} from '../helpers/helpers.core';
|
import {mergeIf, merge, _merger, each, callback as callCallback, uid, valueOrDefault, _elementsEqual} from '../helpers/helpers.core';
|
||||||
@ -111,12 +111,15 @@ function initConfig(config) {
|
|||||||
|
|
||||||
const scaleConfig = mergeScaleConfig(config, config.options);
|
const scaleConfig = mergeScaleConfig(config, config.options);
|
||||||
|
|
||||||
config.options = mergeConfig(
|
const options = config.options = mergeConfig(
|
||||||
defaults,
|
defaults,
|
||||||
defaults[config.type],
|
defaults[config.type],
|
||||||
config.options || {});
|
config.options || {});
|
||||||
|
|
||||||
config.options.scales = scaleConfig;
|
options.scales = scaleConfig;
|
||||||
|
|
||||||
|
options.title = (options.title !== false) && merge({}, [defaults.plugins.title, options.title]);
|
||||||
|
options.tooltips = (options.tooltips !== false) && merge({}, [defaults.plugins.tooltip, options.tooltips]);
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@ -178,7 +181,7 @@ function onAnimationsComplete(ctx) {
|
|||||||
const chart = ctx.chart;
|
const chart = ctx.chart;
|
||||||
const animationOptions = chart.options.animation;
|
const animationOptions = chart.options.animation;
|
||||||
|
|
||||||
plugins.notify(chart, 'afterRender');
|
chart._plugins.notify(chart, 'afterRender');
|
||||||
callCallback(animationOptions && animationOptions.onComplete, [ctx], chart);
|
callCallback(animationOptions && animationOptions.onComplete, [ctx], chart);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +268,7 @@ class Chart {
|
|||||||
this._updating = false;
|
this._updating = false;
|
||||||
this.scales = {};
|
this.scales = {};
|
||||||
this.scale = undefined;
|
this.scale = undefined;
|
||||||
this.$plugins = undefined;
|
this._plugins = new PluginService();
|
||||||
this.$proxies = {};
|
this.$proxies = {};
|
||||||
this._hiddenIndices = {};
|
this._hiddenIndices = {};
|
||||||
this.attached = false;
|
this.attached = false;
|
||||||
@ -308,7 +311,7 @@ class Chart {
|
|||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
// Before init plugin notification
|
// Before init plugin notification
|
||||||
plugins.notify(me, 'beforeInit');
|
me._plugins.notify(me, 'beforeInit');
|
||||||
|
|
||||||
if (me.options.responsive) {
|
if (me.options.responsive) {
|
||||||
// Initial resize before chart draws (must be silent to preserve initial animations).
|
// Initial resize before chart draws (must be silent to preserve initial animations).
|
||||||
@ -320,7 +323,7 @@ class Chart {
|
|||||||
me.bindEvents();
|
me.bindEvents();
|
||||||
|
|
||||||
// After init plugin notification
|
// After init plugin notification
|
||||||
plugins.notify(me, 'afterInit');
|
me._plugins.notify(me, 'afterInit');
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
}
|
}
|
||||||
@ -372,7 +375,7 @@ class Chart {
|
|||||||
retinaScale(me, newRatio);
|
retinaScale(me, newRatio);
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
plugins.notify(me, 'resize', [newSize]);
|
me._plugins.notify(me, 'resize', [newSize]);
|
||||||
|
|
||||||
callCallback(options.onResize, [newSize], me);
|
callCallback(options.onResize, [newSize], me);
|
||||||
|
|
||||||
@ -572,7 +575,7 @@ class Chart {
|
|||||||
*/
|
*/
|
||||||
reset() {
|
reset() {
|
||||||
this._resetElements();
|
this._resetElements();
|
||||||
plugins.notify(this, 'reset');
|
this._plugins.notify(this, 'reset');
|
||||||
}
|
}
|
||||||
|
|
||||||
update(mode) {
|
update(mode) {
|
||||||
@ -588,9 +591,9 @@ class Chart {
|
|||||||
|
|
||||||
// plugins options references might have change, let's invalidate the cache
|
// plugins options references might have change, let's invalidate the cache
|
||||||
// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
||||||
plugins.invalidate(me);
|
me._plugins.invalidate();
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeUpdate') === false) {
|
if (me._plugins.notify(me, 'beforeUpdate') === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +617,7 @@ class Chart {
|
|||||||
me._updateDatasets(mode);
|
me._updateDatasets(mode);
|
||||||
|
|
||||||
// Do this before render so that any plugins that need final scale updates can use it
|
// Do this before render so that any plugins that need final scale updates can use it
|
||||||
plugins.notify(me, 'afterUpdate');
|
me._plugins.notify(me, 'afterUpdate');
|
||||||
|
|
||||||
me._layers.sort(compare2Level('z', '_idx'));
|
me._layers.sort(compare2Level('z', '_idx'));
|
||||||
|
|
||||||
@ -636,7 +639,7 @@ class Chart {
|
|||||||
_updateLayout() {
|
_updateLayout() {
|
||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeLayout') === false) {
|
if (me._plugins.notify(me, 'beforeLayout') === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,7 +659,7 @@ class Chart {
|
|||||||
item._idx = index;
|
item._idx = index;
|
||||||
});
|
});
|
||||||
|
|
||||||
plugins.notify(me, 'afterLayout');
|
me._plugins.notify(me, 'afterLayout');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -668,7 +671,7 @@ class Chart {
|
|||||||
const me = this;
|
const me = this;
|
||||||
const isFunction = typeof mode === 'function';
|
const isFunction = typeof mode === 'function';
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
|
if (me._plugins.notify(me, 'beforeDatasetsUpdate') === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,7 +679,7 @@ class Chart {
|
|||||||
me._updateDataset(i, isFunction ? mode({datasetIndex: i}) : mode);
|
me._updateDataset(i, isFunction ? mode({datasetIndex: i}) : mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.notify(me, 'afterDatasetsUpdate');
|
me._plugins.notify(me, 'afterDatasetsUpdate');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -689,23 +692,23 @@ class Chart {
|
|||||||
const meta = me.getDatasetMeta(index);
|
const meta = me.getDatasetMeta(index);
|
||||||
const args = {meta, index, mode};
|
const args = {meta, index, mode};
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
|
if (me._plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.controller._update(mode);
|
meta.controller._update(mode);
|
||||||
|
|
||||||
plugins.notify(me, 'afterDatasetUpdate', [args]);
|
me._plugins.notify(me, 'afterDatasetUpdate', [args]);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const me = this;
|
const me = this;
|
||||||
const animationOptions = me.options.animation;
|
const animationOptions = me.options.animation;
|
||||||
if (plugins.notify(me, 'beforeRender') === false) {
|
if (me._plugins.notify(me, 'beforeRender') === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const onComplete = function() {
|
const onComplete = function() {
|
||||||
plugins.notify(me, 'afterRender');
|
me._plugins.notify(me, 'afterRender');
|
||||||
callCallback(animationOptions && animationOptions.onComplete, [], me);
|
callCallback(animationOptions && animationOptions.onComplete, [], me);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -729,7 +732,7 @@ class Chart {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeDraw') === false) {
|
if (me._plugins.notify(me, 'beforeDraw') === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,7 +751,7 @@ class Chart {
|
|||||||
layers[i].draw(me.chartArea);
|
layers[i].draw(me.chartArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.notify(me, 'afterDraw');
|
me._plugins.notify(me, 'afterDraw');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -786,7 +789,7 @@ class Chart {
|
|||||||
_drawDatasets() {
|
_drawDatasets() {
|
||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeDatasetsDraw') === false) {
|
if (me._plugins.notify(me, 'beforeDatasetsDraw') === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,7 +798,7 @@ class Chart {
|
|||||||
me._drawDataset(metasets[i]);
|
me._drawDataset(metasets[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.notify(me, 'afterDatasetsDraw');
|
me._plugins.notify(me, 'afterDatasetsDraw');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -813,7 +816,7 @@ class Chart {
|
|||||||
index: meta.index,
|
index: meta.index,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
|
if (me._plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,7 +831,7 @@ class Chart {
|
|||||||
|
|
||||||
unclipArea(ctx);
|
unclipArea(ctx);
|
||||||
|
|
||||||
plugins.notify(me, 'afterDatasetDraw', [args]);
|
me._plugins.notify(me, 'afterDatasetDraw', [args]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -969,7 +972,7 @@ class Chart {
|
|||||||
me.ctx = null;
|
me.ctx = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.notify(me, 'destroy');
|
me._plugins.notify(me, 'destroy');
|
||||||
|
|
||||||
delete Chart.instances[me.id];
|
delete Chart.instances[me.id];
|
||||||
}
|
}
|
||||||
@ -1096,13 +1099,13 @@ class Chart {
|
|||||||
_eventHandler(e, replay) {
|
_eventHandler(e, replay) {
|
||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
if (plugins.notify(me, 'beforeEvent', [e, replay]) === false) {
|
if (me._plugins.notify(me, 'beforeEvent', [e, replay]) === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
me._handleEvent(e, replay);
|
me._handleEvent(e, replay);
|
||||||
|
|
||||||
plugins.notify(me, 'afterEvent', [e, replay]);
|
me._plugins.notify(me, 'afterEvent', [e, replay]);
|
||||||
|
|
||||||
me.render();
|
me.render();
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import Animations from './core.animations';
|
import Animations from './core.animations';
|
||||||
import {isObject, merge, _merger, isArray, valueOrDefault, mergeIf, resolveObjectKey} from '../helpers/helpers.core';
|
import {isObject, merge, _merger, isArray, valueOrDefault, mergeIf, resolveObjectKey, _capitalize} from '../helpers/helpers.core';
|
||||||
import {listenArrayEvents, unlistenArrayEvents} from '../helpers/helpers.collection';
|
import {listenArrayEvents, unlistenArrayEvents} from '../helpers/helpers.collection';
|
||||||
import {resolve} from '../helpers/helpers.options';
|
import {resolve} from '../helpers/helpers.options';
|
||||||
import {getHoverColor} from '../helpers/helpers.color';
|
import {getHoverColor} from '../helpers/helpers.color';
|
||||||
@ -152,7 +152,7 @@ function optionKeys(optionNames) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function optionKey(key, active) {
|
function optionKey(key, active) {
|
||||||
return active ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;
|
return active ? 'hover' + _capitalize(key) : key;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DatasetController {
|
export default class DatasetController {
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export class Defaults {
|
|||||||
this.onClick = null;
|
this.onClick = null;
|
||||||
this.responsive = true;
|
this.responsive = true;
|
||||||
this.showLines = true;
|
this.showLines = true;
|
||||||
this.plugins = undefined;
|
this.plugins = {};
|
||||||
this.scale = undefined;
|
this.scale = undefined;
|
||||||
this.legend = undefined;
|
this.legend = undefined;
|
||||||
this.title = undefined;
|
this.title = undefined;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import defaults from './core.defaults';
|
import defaults from './core.defaults';
|
||||||
import {clone} from '../helpers/helpers.core';
|
import registry from './core.registry';
|
||||||
|
import {mergeIf} from '../helpers/helpers.core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef { import("./core.controller").default } Chart
|
* @typedef { import("./core.controller").default } Chart
|
||||||
@ -7,88 +8,7 @@ import {clone} from '../helpers/helpers.core';
|
|||||||
* @typedef { import("../plugins/plugin.tooltip").default } Tooltip
|
* @typedef { import("../plugins/plugin.tooltip").default } Tooltip
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defaults.set('plugins', {});
|
export default class PluginService {
|
||||||
|
|
||||||
/**
|
|
||||||
* The plugin service singleton
|
|
||||||
* @namespace Chart.plugins
|
|
||||||
* @since 2.1.0
|
|
||||||
*/
|
|
||||||
export class PluginService {
|
|
||||||
constructor() {
|
|
||||||
/**
|
|
||||||
* Globally registered plugins.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this._plugins = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This identifier is used to invalidate the descriptors cache attached to each chart
|
|
||||||
* when a global plugin is registered or unregistered. In this case, the cache ID is
|
|
||||||
* incremented and descriptors are regenerated during following API calls.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this._cacheId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the given plugin(s) if not already registered.
|
|
||||||
* @param {IPlugin[]|IPlugin} plugins plugin instance(s).
|
|
||||||
*/
|
|
||||||
register(plugins) {
|
|
||||||
const p = this._plugins;
|
|
||||||
([]).concat(plugins).forEach((plugin) => {
|
|
||||||
if (p.indexOf(plugin) === -1) {
|
|
||||||
p.push(plugin);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._cacheId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters the given plugin(s) only if registered.
|
|
||||||
* @param {IPlugin[]|IPlugin} plugins plugin instance(s).
|
|
||||||
*/
|
|
||||||
unregister(plugins) {
|
|
||||||
const p = this._plugins;
|
|
||||||
([]).concat(plugins).forEach((plugin) => {
|
|
||||||
const idx = p.indexOf(plugin);
|
|
||||||
if (idx !== -1) {
|
|
||||||
p.splice(idx, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._cacheId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all registered plugins.
|
|
||||||
* @since 2.1.5
|
|
||||||
*/
|
|
||||||
clear() {
|
|
||||||
this._plugins = [];
|
|
||||||
this._cacheId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of registered plugins?
|
|
||||||
* @returns {number}
|
|
||||||
* @since 2.1.5
|
|
||||||
*/
|
|
||||||
count() {
|
|
||||||
return this._plugins.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all registered plugin instances.
|
|
||||||
* @returns {IPlugin[]} array of plugin objects.
|
|
||||||
* @since 2.1.5
|
|
||||||
*/
|
|
||||||
getAll() {
|
|
||||||
return this._plugins;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls enabled plugins for `chart` on the specified hook and with the given args.
|
* Calls enabled plugins for `chart` on the specified hook and with the given args.
|
||||||
* This method immediately returns as soon as a plugin explicitly returns false. The
|
* This method immediately returns as soon as a plugin explicitly returns false. The
|
||||||
@ -100,15 +20,13 @@ export class PluginService {
|
|||||||
*/
|
*/
|
||||||
notify(chart, hook, args) {
|
notify(chart, hook, args) {
|
||||||
const descriptors = this._descriptors(chart);
|
const descriptors = this._descriptors(chart);
|
||||||
const ilen = descriptors.length;
|
|
||||||
let i, descriptor, plugin, params, method;
|
|
||||||
|
|
||||||
for (i = 0; i < ilen; ++i) {
|
for (let i = 0; i < descriptors.length; ++i) {
|
||||||
descriptor = descriptors[i];
|
const descriptor = descriptors[i];
|
||||||
plugin = descriptor.plugin;
|
const plugin = descriptor.plugin;
|
||||||
method = plugin[hook];
|
const method = plugin[hook];
|
||||||
if (typeof method === 'function') {
|
if (typeof method === 'function') {
|
||||||
params = [chart].concat(args || []);
|
const params = [chart].concat(args || []);
|
||||||
params.push(descriptor.options);
|
params.push(descriptor.options);
|
||||||
if (method.apply(plugin, params) === false) {
|
if (method.apply(plugin, params) === false) {
|
||||||
return false;
|
return false;
|
||||||
@ -119,64 +37,71 @@ export class PluginService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidate() {
|
||||||
|
this._cache = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns descriptors of enabled plugins for the given chart.
|
|
||||||
* @param {Chart} chart
|
* @param {Chart} chart
|
||||||
* @returns {object[]} [{ plugin, options }]
|
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_descriptors(chart) {
|
_descriptors(chart) {
|
||||||
const cache = chart.$plugins || (chart.$plugins = {});
|
if (this._cache) {
|
||||||
if (cache.id === this._cacheId) {
|
return this._cache;
|
||||||
return cache.descriptors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugins = [];
|
|
||||||
const descriptors = [];
|
|
||||||
const config = (chart && chart.config) || {};
|
const config = (chart && chart.config) || {};
|
||||||
const options = (config.options && config.options.plugins) || {};
|
const options = (config.options && config.options.plugins) || {};
|
||||||
|
const plugins = allPlugins(config);
|
||||||
|
const descriptors = createDescriptors(plugins, options);
|
||||||
|
|
||||||
this._plugins.concat(config.plugins || []).forEach((plugin) => {
|
this._cache = descriptors;
|
||||||
const idx = plugins.indexOf(plugin);
|
|
||||||
if (idx !== -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = plugin.id;
|
|
||||||
let opts = options[id];
|
|
||||||
if (opts === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts === true) {
|
|
||||||
opts = clone(defaults.plugins[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins.push(plugin);
|
|
||||||
descriptors.push({
|
|
||||||
plugin,
|
|
||||||
options: opts || {}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
cache.descriptors = descriptors;
|
|
||||||
cache.id = this._cacheId;
|
|
||||||
return descriptors;
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates cache for the given chart: descriptors hold a reference on plugin option,
|
|
||||||
* but in some cases, this reference can be changed by the user when updating options.
|
|
||||||
* https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
|
||||||
* @param {Chart} chart
|
|
||||||
*/
|
|
||||||
invalidate(chart) {
|
|
||||||
delete chart.$plugins;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// singleton instance
|
function allPlugins(config) {
|
||||||
export default new PluginService();
|
const plugins = [];
|
||||||
|
const keys = Object.keys(registry.plugins.items);
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
plugins.push(registry.getPlugin(keys[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
const local = config.plugins || [];
|
||||||
|
for (let i = 0; i < local.length; i++) {
|
||||||
|
const plugin = local[i];
|
||||||
|
|
||||||
|
if (plugins.indexOf(plugin) === -1) {
|
||||||
|
plugins.push(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDescriptors(plugins, options) {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < plugins.length; i++) {
|
||||||
|
const plugin = plugins[i];
|
||||||
|
const id = plugin.id;
|
||||||
|
|
||||||
|
let opts = options[id];
|
||||||
|
if (opts === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (opts === true) {
|
||||||
|
opts = {};
|
||||||
|
}
|
||||||
|
result.push({
|
||||||
|
plugin,
|
||||||
|
options: mergeIf({}, [opts, defaults.plugins[id]])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin extension hooks.
|
* Plugin extension hooks.
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import DatasetController from './core.datasetController';
|
|||||||
import Element from './core.element';
|
import Element from './core.element';
|
||||||
import Scale from './core.scale';
|
import Scale from './core.scale';
|
||||||
import TypedRegistry from './core.typedRegistry';
|
import TypedRegistry from './core.typedRegistry';
|
||||||
import {each, callback as call} from '../helpers/helpers.core';
|
import {each, callback as call, _capitalize} from '../helpers/helpers.core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Please use the module's default export which provides a singleton instance
|
* Please use the module's default export which provides a singleton instance
|
||||||
@ -23,35 +23,39 @@ export class Registry {
|
|||||||
* @param {...any} args
|
* @param {...any} args
|
||||||
*/
|
*/
|
||||||
add(...args) {
|
add(...args) {
|
||||||
this._registerEach(args);
|
this._each('register', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(...args) {
|
||||||
|
this._each('unregister', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {...typeof DatasetController} args
|
* @param {...typeof DatasetController} args
|
||||||
*/
|
*/
|
||||||
addControllers(...args) {
|
addControllers(...args) {
|
||||||
this._registerEach(args, this.controllers);
|
this._each('register', args, this.controllers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {...typeof Element} args
|
* @param {...typeof Element} args
|
||||||
*/
|
*/
|
||||||
addElements(...args) {
|
addElements(...args) {
|
||||||
this._registerEach(args, this.elements);
|
this._each('register', args, this.elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {...any} args
|
* @param {...any} args
|
||||||
*/
|
*/
|
||||||
addPlugins(...args) {
|
addPlugins(...args) {
|
||||||
this._registerEach(args, this.plugins);
|
this._each('register', args, this.plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {...typeof Scale} args
|
* @param {...typeof Scale} args
|
||||||
*/
|
*/
|
||||||
addScales(...args) {
|
addScales(...args) {
|
||||||
this._registerEach(args, this.scales);
|
this._each('register', args, this.scales);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,12 +93,12 @@ export class Registry {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_registerEach(args, typedRegistry) {
|
_each(method, args, typedRegistry) {
|
||||||
const me = this;
|
const me = this;
|
||||||
[...args].forEach(arg => {
|
[...args].forEach(arg => {
|
||||||
const reg = typedRegistry || me._getRegistryForType(arg);
|
const reg = typedRegistry || me._getRegistryForType(arg);
|
||||||
if (reg.isForType(arg)) {
|
if (reg.isForType(arg) || (reg === me.plugins && arg.id)) {
|
||||||
me._registerComponent(reg, arg);
|
me._exec(method, reg, arg);
|
||||||
} else {
|
} else {
|
||||||
// Handle loopable args
|
// Handle loopable args
|
||||||
// Use case:
|
// Use case:
|
||||||
@ -108,7 +112,7 @@ export class Registry {
|
|||||||
// Chart.register(treemap);
|
// Chart.register(treemap);
|
||||||
|
|
||||||
const itemReg = typedRegistry || me._getRegistryForType(item);
|
const itemReg = typedRegistry || me._getRegistryForType(item);
|
||||||
me._registerComponent(itemReg, item);
|
me._exec(method, itemReg, item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -117,10 +121,11 @@ export class Registry {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_registerComponent(registry, component) {
|
_exec(method, registry, component) {
|
||||||
call(component.beforeRegister, [], component);
|
const camelMethod = _capitalize(method);
|
||||||
registry.register(component);
|
call(component['before' + camelMethod], [], component);
|
||||||
call(component.afterRegister, [], component);
|
registry[method](component);
|
||||||
|
call(component['after' + camelMethod], [], component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import {_capitalize} from './helpers.core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binary search
|
* Binary search
|
||||||
* @param {array} table - the table search. must be sorted!
|
* @param {array} table - the table search. must be sorted!
|
||||||
@ -114,7 +116,7 @@ export function listenArrayEvents(array, listener) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
arrayEvents.forEach((key) => {
|
arrayEvents.forEach((key) => {
|
||||||
const method = '_onData' + key.charAt(0).toUpperCase() + key.slice(1);
|
const method = '_onData' + _capitalize(key);
|
||||||
const base = array[key];
|
const base = array[key];
|
||||||
|
|
||||||
Object.defineProperty(array, key, {
|
Object.defineProperty(array, key, {
|
||||||
|
|||||||
@ -276,3 +276,10 @@ export function resolveObjectKey(obj, key) {
|
|||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export function _capitalize(str) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
|
|||||||
21
src/index.js
21
src/index.js
@ -19,13 +19,23 @@ import Interaction from './core/core.interaction';
|
|||||||
import layouts from './core/core.layouts';
|
import layouts from './core/core.layouts';
|
||||||
import * as platforms from './platform/index';
|
import * as platforms from './platform/index';
|
||||||
import * as plugins from './plugins';
|
import * as plugins from './plugins';
|
||||||
import pluginsCore from './core/core.plugins';
|
|
||||||
import registry from './core/core.registry';
|
import registry from './core/core.registry';
|
||||||
import Scale from './core/core.scale';
|
import Scale from './core/core.scale';
|
||||||
import * as scales from './scales';
|
import * as scales from './scales';
|
||||||
import Ticks from './core/core.ticks';
|
import Ticks from './core/core.ticks';
|
||||||
|
import {each} from './helpers/helpers.core';
|
||||||
|
|
||||||
Chart.register = (...items) => registry.add(...items);
|
// @ts-ignore
|
||||||
|
const invalidatePlugins = () => each(Chart.instances, (chart) => chart._plugins.invalidate());
|
||||||
|
|
||||||
|
Chart.register = (...items) => {
|
||||||
|
registry.add(...items);
|
||||||
|
invalidatePlugins();
|
||||||
|
};
|
||||||
|
Chart.unregister = (...items) => {
|
||||||
|
registry.remove(...items);
|
||||||
|
invalidatePlugins();
|
||||||
|
};
|
||||||
|
|
||||||
// Register built-ins
|
// Register built-ins
|
||||||
Chart.register(controllers, scales, elements, plugins);
|
Chart.register(controllers, scales, elements, plugins);
|
||||||
@ -43,17 +53,10 @@ Chart.elements = elements;
|
|||||||
Chart.Interaction = Interaction;
|
Chart.Interaction = Interaction;
|
||||||
Chart.layouts = layouts;
|
Chart.layouts = layouts;
|
||||||
Chart.platforms = platforms;
|
Chart.platforms = platforms;
|
||||||
Chart.plugins = pluginsCore;
|
|
||||||
Chart.registry = registry;
|
Chart.registry = registry;
|
||||||
Chart.Scale = Scale;
|
Chart.Scale = Scale;
|
||||||
Chart.Ticks = Ticks;
|
Chart.Ticks = Ticks;
|
||||||
|
|
||||||
for (const k in plugins) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(plugins, k)) {
|
|
||||||
Chart.plugins.register(plugins[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.Chart = Chart;
|
window.Chart = Chart;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export {default as filler} from './plugin.filler';
|
export {default as Filler} from './plugin.filler';
|
||||||
export {default as legend} from './plugin.legend';
|
export {default as Legend} from './plugin.legend';
|
||||||
export {default as title} from './plugin.title';
|
export {default as Title} from './plugin.title';
|
||||||
export {default as tooltip} from './plugin.tooltip';
|
export {default as Tooltip} from './plugin.tooltip';
|
||||||
|
|||||||
@ -4,19 +4,6 @@ import layouts from '../core/core.layouts';
|
|||||||
import {isArray, mergeIf} from '../helpers/helpers.core';
|
import {isArray, mergeIf} from '../helpers/helpers.core';
|
||||||
import {toPadding, toFont} from '../helpers/helpers.options';
|
import {toPadding, toFont} from '../helpers/helpers.options';
|
||||||
|
|
||||||
defaults.set('title', {
|
|
||||||
align: 'center',
|
|
||||||
display: false,
|
|
||||||
font: {
|
|
||||||
style: 'bold',
|
|
||||||
},
|
|
||||||
fullWidth: true,
|
|
||||||
padding: 10,
|
|
||||||
position: 'top',
|
|
||||||
text: '',
|
|
||||||
weight: 2000 // by default greater than legend (1000) to be above
|
|
||||||
});
|
|
||||||
|
|
||||||
export class Title extends Element {
|
export class Title extends Element {
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
super();
|
super();
|
||||||
@ -257,7 +244,7 @@ export default {
|
|||||||
const titleBlock = chart.titleBlock;
|
const titleBlock = chart.titleBlock;
|
||||||
|
|
||||||
if (titleOpts) {
|
if (titleOpts) {
|
||||||
mergeIf(titleOpts, defaults.title);
|
mergeIf(titleOpts, defaults.plugins.title);
|
||||||
|
|
||||||
if (titleBlock) {
|
if (titleBlock) {
|
||||||
layouts.configure(chart, titleBlock, titleOpts);
|
layouts.configure(chart, titleBlock, titleOpts);
|
||||||
@ -269,5 +256,18 @@ export default {
|
|||||||
layouts.removeBox(chart, titleBlock);
|
layouts.removeBox(chart, titleBlock);
|
||||||
delete chart.titleBlock;
|
delete chart.titleBlock;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
defaults: {
|
||||||
|
align: 'center',
|
||||||
|
display: false,
|
||||||
|
font: {
|
||||||
|
style: 'bold',
|
||||||
|
},
|
||||||
|
fullWidth: true,
|
||||||
|
padding: 10,
|
||||||
|
position: 'top',
|
||||||
|
text: '',
|
||||||
|
weight: 2000 // by default greater than legend (1000) to be above
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import Animations from '../core/core.animations';
|
import Animations from '../core/core.animations';
|
||||||
import defaults from '../core/core.defaults';
|
import defaults from '../core/core.defaults';
|
||||||
import Element from '../core/core.element';
|
import Element from '../core/core.element';
|
||||||
import plugins from '../core/core.plugins';
|
import {valueOrDefault, each, noop, isNullOrUndef, isArray, _elementsEqual, merge} from '../helpers/helpers.core';
|
||||||
import {valueOrDefault, each, noop, isNullOrUndef, isArray, _elementsEqual} from '../helpers/helpers.core';
|
|
||||||
import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl';
|
import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl';
|
||||||
import {distanceBetweenPoints} from '../helpers/helpers.math';
|
import {distanceBetweenPoints} from '../helpers/helpers.math';
|
||||||
import {toFont} from '../helpers/helpers.options';
|
import {toFont} from '../helpers/helpers.options';
|
||||||
@ -11,114 +10,6 @@ import {toFont} from '../helpers/helpers.options';
|
|||||||
* @typedef { import("../platform/platform.base").IEvent } IEvent
|
* @typedef { import("../platform/platform.base").IEvent } IEvent
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defaults.set('tooltips', {
|
|
||||||
enabled: true,
|
|
||||||
custom: null,
|
|
||||||
mode: 'nearest',
|
|
||||||
position: 'average',
|
|
||||||
intersect: true,
|
|
||||||
backgroundColor: 'rgba(0,0,0,0.8)',
|
|
||||||
titleFont: {
|
|
||||||
style: 'bold',
|
|
||||||
color: '#fff',
|
|
||||||
},
|
|
||||||
titleSpacing: 2,
|
|
||||||
titleMarginBottom: 6,
|
|
||||||
titleAlign: 'left',
|
|
||||||
bodySpacing: 2,
|
|
||||||
bodyFont: {
|
|
||||||
color: '#fff',
|
|
||||||
},
|
|
||||||
bodyAlign: 'left',
|
|
||||||
footerSpacing: 2,
|
|
||||||
footerMarginTop: 6,
|
|
||||||
footerFont: {
|
|
||||||
color: '#fff',
|
|
||||||
style: 'bold',
|
|
||||||
},
|
|
||||||
footerAlign: 'left',
|
|
||||||
yPadding: 6,
|
|
||||||
xPadding: 6,
|
|
||||||
caretPadding: 2,
|
|
||||||
caretSize: 5,
|
|
||||||
cornerRadius: 6,
|
|
||||||
multiKeyBackground: '#fff',
|
|
||||||
displayColors: true,
|
|
||||||
borderColor: 'rgba(0,0,0,0)',
|
|
||||||
borderWidth: 0,
|
|
||||||
animation: {
|
|
||||||
duration: 400,
|
|
||||||
easing: 'easeOutQuart',
|
|
||||||
numbers: {
|
|
||||||
type: 'number',
|
|
||||||
properties: ['x', 'y', 'width', 'height', 'caretX', 'caretY'],
|
|
||||||
},
|
|
||||||
opacity: {
|
|
||||||
easing: 'linear',
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
callbacks: {
|
|
||||||
// Args are: (tooltipItems, data)
|
|
||||||
beforeTitle: noop,
|
|
||||||
title(tooltipItems, data) {
|
|
||||||
let title = '';
|
|
||||||
const labels = data.labels;
|
|
||||||
const labelCount = labels ? labels.length : 0;
|
|
||||||
|
|
||||||
if (tooltipItems.length > 0) {
|
|
||||||
const item = tooltipItems[0];
|
|
||||||
if (item.label) {
|
|
||||||
title = item.label;
|
|
||||||
} else if (labelCount > 0 && item.index < labelCount) {
|
|
||||||
title = labels[item.index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return title;
|
|
||||||
},
|
|
||||||
afterTitle: noop,
|
|
||||||
|
|
||||||
// Args are: (tooltipItems, data)
|
|
||||||
beforeBody: noop,
|
|
||||||
|
|
||||||
// Args are: (tooltipItem, data)
|
|
||||||
beforeLabel: noop,
|
|
||||||
label(tooltipItem, data) {
|
|
||||||
let label = data.datasets[tooltipItem.datasetIndex].label || '';
|
|
||||||
|
|
||||||
if (label) {
|
|
||||||
label += ': ';
|
|
||||||
}
|
|
||||||
const value = tooltipItem.value;
|
|
||||||
if (!isNullOrUndef(value)) {
|
|
||||||
label += value;
|
|
||||||
}
|
|
||||||
return label;
|
|
||||||
},
|
|
||||||
labelColor(tooltipItem, chart) {
|
|
||||||
const meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
|
|
||||||
const options = meta.controller.getStyle(tooltipItem.index);
|
|
||||||
return {
|
|
||||||
borderColor: options.borderColor,
|
|
||||||
backgroundColor: options.backgroundColor
|
|
||||||
};
|
|
||||||
},
|
|
||||||
labelTextColor() {
|
|
||||||
return this.options.bodyFont.color;
|
|
||||||
},
|
|
||||||
afterLabel: noop,
|
|
||||||
|
|
||||||
// Args are: (tooltipItems, data)
|
|
||||||
afterBody: noop,
|
|
||||||
|
|
||||||
// Args are: (tooltipItems, data)
|
|
||||||
beforeFooter: noop,
|
|
||||||
footer: noop,
|
|
||||||
afterFooter: noop
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const positioners = {
|
const positioners = {
|
||||||
/**
|
/**
|
||||||
* Average mode places the tooltip at the average position of the elements shown
|
* Average mode places the tooltip at the average position of the elements shown
|
||||||
@ -242,7 +133,7 @@ function createTooltipItem(chart, item) {
|
|||||||
*/
|
*/
|
||||||
function resolveOptions(options) {
|
function resolveOptions(options) {
|
||||||
|
|
||||||
options = Object.assign({}, defaults.tooltips, options);
|
options = merge({}, [defaults.plugins.tooltip, options]);
|
||||||
|
|
||||||
options.bodyFont = toFont(options.bodyFont);
|
options.bodyFont = toFont(options.bodyFont);
|
||||||
options.titleFont = toFont(options.titleFont);
|
options.titleFont = toFont(options.titleFont);
|
||||||
@ -1101,7 +992,7 @@ export default {
|
|||||||
tooltip
|
tooltip
|
||||||
};
|
};
|
||||||
|
|
||||||
if (plugins.notify(chart, 'beforeTooltipDraw', [args]) === false) {
|
if (chart._plugins.notify(chart, 'beforeTooltipDraw', [args]) === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1109,7 +1000,7 @@ export default {
|
|||||||
tooltip.draw(chart.ctx);
|
tooltip.draw(chart.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.notify(chart, 'afterTooltipDraw', [args]);
|
chart._plugins.notify(chart, 'afterTooltipDraw', [args]);
|
||||||
},
|
},
|
||||||
|
|
||||||
afterEvent(chart, e, replay) {
|
afterEvent(chart, e, replay) {
|
||||||
@ -1118,5 +1009,113 @@ export default {
|
|||||||
const useFinalPosition = replay;
|
const useFinalPosition = replay;
|
||||||
chart.tooltip.handleEvent(e, useFinalPosition);
|
chart.tooltip.handleEvent(e, useFinalPosition);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
defaults: {
|
||||||
|
enabled: true,
|
||||||
|
custom: null,
|
||||||
|
mode: 'nearest',
|
||||||
|
position: 'average',
|
||||||
|
intersect: true,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.8)',
|
||||||
|
titleFont: {
|
||||||
|
style: 'bold',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
titleSpacing: 2,
|
||||||
|
titleMarginBottom: 6,
|
||||||
|
titleAlign: 'left',
|
||||||
|
bodySpacing: 2,
|
||||||
|
bodyFont: {
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
bodyAlign: 'left',
|
||||||
|
footerSpacing: 2,
|
||||||
|
footerMarginTop: 6,
|
||||||
|
footerFont: {
|
||||||
|
color: '#fff',
|
||||||
|
style: 'bold',
|
||||||
|
},
|
||||||
|
footerAlign: 'left',
|
||||||
|
yPadding: 6,
|
||||||
|
xPadding: 6,
|
||||||
|
caretPadding: 2,
|
||||||
|
caretSize: 5,
|
||||||
|
cornerRadius: 6,
|
||||||
|
multiKeyBackground: '#fff',
|
||||||
|
displayColors: true,
|
||||||
|
borderColor: 'rgba(0,0,0,0)',
|
||||||
|
borderWidth: 0,
|
||||||
|
animation: {
|
||||||
|
duration: 400,
|
||||||
|
easing: 'easeOutQuart',
|
||||||
|
numbers: {
|
||||||
|
type: 'number',
|
||||||
|
properties: ['x', 'y', 'width', 'height', 'caretX', 'caretY'],
|
||||||
|
},
|
||||||
|
opacity: {
|
||||||
|
easing: 'linear',
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callbacks: {
|
||||||
|
// Args are: (tooltipItems, data)
|
||||||
|
beforeTitle: noop,
|
||||||
|
title(tooltipItems, data) {
|
||||||
|
let title = '';
|
||||||
|
const labels = data.labels;
|
||||||
|
const labelCount = labels ? labels.length : 0;
|
||||||
|
|
||||||
|
if (tooltipItems.length > 0) {
|
||||||
|
const item = tooltipItems[0];
|
||||||
|
if (item.label) {
|
||||||
|
title = item.label;
|
||||||
|
} else if (labelCount > 0 && item.index < labelCount) {
|
||||||
|
title = labels[item.index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
},
|
||||||
|
afterTitle: noop,
|
||||||
|
|
||||||
|
// Args are: (tooltipItems, data)
|
||||||
|
beforeBody: noop,
|
||||||
|
|
||||||
|
// Args are: (tooltipItem, data)
|
||||||
|
beforeLabel: noop,
|
||||||
|
label(tooltipItem, data) {
|
||||||
|
let label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
label += ': ';
|
||||||
|
}
|
||||||
|
const value = tooltipItem.value;
|
||||||
|
if (!isNullOrUndef(value)) {
|
||||||
|
label += value;
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
},
|
||||||
|
labelColor(tooltipItem, chart) {
|
||||||
|
const meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
|
||||||
|
const options = meta.controller.getStyle(tooltipItem.index);
|
||||||
|
return {
|
||||||
|
borderColor: options.borderColor,
|
||||||
|
backgroundColor: options.backgroundColor
|
||||||
|
};
|
||||||
|
},
|
||||||
|
labelTextColor() {
|
||||||
|
return this.options.bodyFont.color;
|
||||||
|
},
|
||||||
|
afterLabel: noop,
|
||||||
|
|
||||||
|
// Args are: (tooltipItems, data)
|
||||||
|
afterBody: noop,
|
||||||
|
|
||||||
|
// Args are: (tooltipItems, data)
|
||||||
|
beforeFooter: noop,
|
||||||
|
footer: noop,
|
||||||
|
afterFooter: noop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,67 +1,4 @@
|
|||||||
describe('Chart.plugins', function() {
|
describe('Chart.plugins', function() {
|
||||||
beforeEach(function() {
|
|
||||||
this._plugins = Chart.plugins.getAll();
|
|
||||||
Chart.plugins.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
Chart.plugins.clear();
|
|
||||||
Chart.plugins.register(this._plugins);
|
|
||||||
delete this._plugins;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Chart.plugins.register', function() {
|
|
||||||
it('should register a plugin', function() {
|
|
||||||
Chart.plugins.register({});
|
|
||||||
expect(Chart.plugins.count()).toBe(1);
|
|
||||||
Chart.plugins.register({});
|
|
||||||
expect(Chart.plugins.count()).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should register an array of plugins', function() {
|
|
||||||
Chart.plugins.register([{}, {}, {}]);
|
|
||||||
expect(Chart.plugins.count()).toBe(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should succeed to register an already registered plugin', function() {
|
|
||||||
var plugin = {};
|
|
||||||
Chart.plugins.register(plugin);
|
|
||||||
expect(Chart.plugins.count()).toBe(1);
|
|
||||||
Chart.plugins.register(plugin);
|
|
||||||
expect(Chart.plugins.count()).toBe(1);
|
|
||||||
Chart.plugins.register([{}, plugin, plugin]);
|
|
||||||
expect(Chart.plugins.count()).toBe(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Chart.plugins.unregister', function() {
|
|
||||||
it('should unregister a plugin', function() {
|
|
||||||
var plugin = {};
|
|
||||||
Chart.plugins.register(plugin);
|
|
||||||
expect(Chart.plugins.count()).toBe(1);
|
|
||||||
Chart.plugins.unregister(plugin);
|
|
||||||
expect(Chart.plugins.count()).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should unregister an array of plugins', function() {
|
|
||||||
var plugins = [{}, {}, {}];
|
|
||||||
Chart.plugins.register(plugins);
|
|
||||||
expect(Chart.plugins.count()).toBe(3);
|
|
||||||
Chart.plugins.unregister(plugins.slice(0, 2));
|
|
||||||
expect(Chart.plugins.count()).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should succeed to unregister a plugin not registered', function() {
|
|
||||||
var plugin = {};
|
|
||||||
Chart.plugins.register(plugin);
|
|
||||||
expect(Chart.plugins.count()).toBe(1);
|
|
||||||
Chart.plugins.unregister({});
|
|
||||||
expect(Chart.plugins.count()).toBe(1);
|
|
||||||
Chart.plugins.unregister([{}, plugin]);
|
|
||||||
expect(Chart.plugins.count()).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Chart.plugins.notify', function() {
|
describe('Chart.plugins.notify', function() {
|
||||||
it('should call inline plugins with arguments', function() {
|
it('should call inline plugins with arguments', function() {
|
||||||
var plugin = {hook: function() {}};
|
var plugin = {hook: function() {}};
|
||||||
@ -71,7 +8,7 @@ describe('Chart.plugins', function() {
|
|||||||
|
|
||||||
spyOn(plugin, 'hook');
|
spyOn(plugin, 'hook');
|
||||||
|
|
||||||
Chart.plugins.notify(chart, 'hook', 42);
|
chart._plugins.notify(chart, 'hook', 42);
|
||||||
expect(plugin.hook.calls.count()).toBe(1);
|
expect(plugin.hook.calls.count()).toBe(1);
|
||||||
expect(plugin.hook.calls.first().args[0]).toBe(chart);
|
expect(plugin.hook.calls.first().args[0]).toBe(chart);
|
||||||
expect(plugin.hook.calls.first().args[1]).toBe(42);
|
expect(plugin.hook.calls.first().args[1]).toBe(42);
|
||||||
@ -79,30 +16,32 @@ describe('Chart.plugins', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should call global plugins with arguments', function() {
|
it('should call global plugins with arguments', function() {
|
||||||
var plugin = {hook: function() {}};
|
var plugin = {id: 'a', hook: function() {}};
|
||||||
var chart = window.acquireChart({});
|
var chart = window.acquireChart({});
|
||||||
|
|
||||||
spyOn(plugin, 'hook');
|
spyOn(plugin, 'hook');
|
||||||
|
|
||||||
Chart.plugins.register(plugin);
|
Chart.register(plugin);
|
||||||
Chart.plugins.notify(chart, 'hook', 42);
|
chart._plugins.notify(chart, 'hook', 42);
|
||||||
expect(plugin.hook.calls.count()).toBe(1);
|
expect(plugin.hook.calls.count()).toBe(1);
|
||||||
expect(plugin.hook.calls.first().args[0]).toBe(chart);
|
expect(plugin.hook.calls.first().args[0]).toBe(chart);
|
||||||
expect(plugin.hook.calls.first().args[1]).toBe(42);
|
expect(plugin.hook.calls.first().args[1]).toBe(42);
|
||||||
expect(plugin.hook.calls.first().args[2]).toEqual({});
|
expect(plugin.hook.calls.first().args[2]).toEqual({});
|
||||||
|
Chart.unregister(plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call plugin only once even if registered multiple times', function() {
|
it('should call plugin only once even if registered multiple times', function() {
|
||||||
var plugin = {hook: function() {}};
|
var plugin = {id: 'test', hook: function() {}};
|
||||||
var chart = window.acquireChart({
|
var chart = window.acquireChart({
|
||||||
plugins: [plugin, plugin]
|
plugins: [plugin, plugin]
|
||||||
});
|
});
|
||||||
|
|
||||||
spyOn(plugin, 'hook');
|
spyOn(plugin, 'hook');
|
||||||
|
|
||||||
Chart.plugins.register([plugin, plugin]);
|
Chart.register([plugin, plugin]);
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
expect(plugin.hook.calls.count()).toBe(1);
|
expect(plugin.hook.calls.count()).toBe(1);
|
||||||
|
Chart.unregister(plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call plugins in the correct order (global first)', function() {
|
it('should call plugins in the correct order (global first)', function() {
|
||||||
@ -123,23 +62,28 @@ describe('Chart.plugins', function() {
|
|||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
Chart.plugins.register([{
|
var plugins = [{
|
||||||
|
id: 'a',
|
||||||
hook: function() {
|
hook: function() {
|
||||||
results.push(4);
|
results.push(4);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
id: 'b',
|
||||||
hook: function() {
|
hook: function() {
|
||||||
results.push(5);
|
results.push(5);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
id: 'c',
|
||||||
hook: function() {
|
hook: function() {
|
||||||
results.push(6);
|
results.push(6);
|
||||||
}
|
}
|
||||||
}]);
|
}];
|
||||||
|
Chart.register(plugins);
|
||||||
|
|
||||||
var ret = Chart.plugins.notify(chart, 'hook');
|
var ret = chart._plugins.notify(chart, 'hook');
|
||||||
expect(ret).toBeTruthy();
|
expect(ret).toBeTruthy();
|
||||||
expect(results).toEqual([4, 5, 6, 1, 2, 3]);
|
expect(results).toEqual([4, 5, 6, 1, 2, 3]);
|
||||||
|
Chart.unregister(plugins);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return TRUE if no plugin explicitly returns FALSE', function() {
|
it('should return TRUE if no plugin explicitly returns FALSE', function() {
|
||||||
@ -170,7 +114,7 @@ describe('Chart.plugins', function() {
|
|||||||
spyOn(plugin, 'hook').and.callThrough();
|
spyOn(plugin, 'hook').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
var ret = Chart.plugins.notify(chart, 'hook');
|
var ret = chart._plugins.notify(chart, 'hook');
|
||||||
expect(ret).toBeTruthy();
|
expect(ret).toBeTruthy();
|
||||||
plugins.forEach(function(plugin) {
|
plugins.forEach(function(plugin) {
|
||||||
expect(plugin.hook).toHaveBeenCalled();
|
expect(plugin.hook).toHaveBeenCalled();
|
||||||
@ -205,7 +149,7 @@ describe('Chart.plugins', function() {
|
|||||||
spyOn(plugin, 'hook').and.callThrough();
|
spyOn(plugin, 'hook').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
var ret = Chart.plugins.notify(chart, 'hook');
|
var ret = chart._plugins.notify(chart, 'hook');
|
||||||
expect(ret).toBeFalsy();
|
expect(ret).toBeFalsy();
|
||||||
expect(plugins[0].hook).toHaveBeenCalled();
|
expect(plugins[0].hook).toHaveBeenCalled();
|
||||||
expect(plugins[1].hook).toHaveBeenCalled();
|
expect(plugins[1].hook).toHaveBeenCalled();
|
||||||
@ -218,6 +162,7 @@ describe('Chart.plugins', function() {
|
|||||||
describe('config.options.plugins', function() {
|
describe('config.options.plugins', function() {
|
||||||
it('should call plugins with options at last argument', function() {
|
it('should call plugins with options at last argument', function() {
|
||||||
var plugin = {id: 'foo', hook: function() {}};
|
var plugin = {id: 'foo', hook: function() {}};
|
||||||
|
|
||||||
var chart = window.acquireChart({
|
var chart = window.acquireChart({
|
||||||
options: {
|
options: {
|
||||||
plugins: {
|
plugins: {
|
||||||
@ -228,15 +173,17 @@ describe('Chart.plugins', function() {
|
|||||||
|
|
||||||
spyOn(plugin, 'hook');
|
spyOn(plugin, 'hook');
|
||||||
|
|
||||||
Chart.plugins.register(plugin);
|
Chart.register(plugin);
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
Chart.plugins.notify(chart, 'hook', ['bla']);
|
chart._plugins.notify(chart, 'hook', ['bla']);
|
||||||
Chart.plugins.notify(chart, 'hook', ['bla', 42]);
|
chart._plugins.notify(chart, 'hook', ['bla', 42]);
|
||||||
|
|
||||||
expect(plugin.hook.calls.count()).toBe(3);
|
expect(plugin.hook.calls.count()).toBe(3);
|
||||||
expect(plugin.hook.calls.argsFor(0)[1]).toEqual({a: '123'});
|
expect(plugin.hook.calls.argsFor(0)[1]).toEqual({a: '123'});
|
||||||
expect(plugin.hook.calls.argsFor(1)[2]).toEqual({a: '123'});
|
expect(plugin.hook.calls.argsFor(1)[2]).toEqual({a: '123'});
|
||||||
expect(plugin.hook.calls.argsFor(2)[3]).toEqual({a: '123'});
|
expect(plugin.hook.calls.argsFor(2)[3]).toEqual({a: '123'});
|
||||||
|
|
||||||
|
Chart.unregister(plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call plugins with options associated to their identifier', function() {
|
it('should call plugins with options associated to their identifier', function() {
|
||||||
@ -246,7 +193,7 @@ describe('Chart.plugins', function() {
|
|||||||
c: {id: 'c', hook: function() {}}
|
c: {id: 'c', hook: function() {}}
|
||||||
};
|
};
|
||||||
|
|
||||||
Chart.plugins.register(plugins.a);
|
Chart.register(plugins.a);
|
||||||
|
|
||||||
var chart = window.acquireChart({
|
var chart = window.acquireChart({
|
||||||
plugins: [plugins.b, plugins.c],
|
plugins: [plugins.b, plugins.c],
|
||||||
@ -263,7 +210,7 @@ describe('Chart.plugins', function() {
|
|||||||
spyOn(plugins.b, 'hook');
|
spyOn(plugins.b, 'hook');
|
||||||
spyOn(plugins.c, 'hook');
|
spyOn(plugins.c, 'hook');
|
||||||
|
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
|
|
||||||
expect(plugins.a.hook).toHaveBeenCalled();
|
expect(plugins.a.hook).toHaveBeenCalled();
|
||||||
expect(plugins.b.hook).toHaveBeenCalled();
|
expect(plugins.b.hook).toHaveBeenCalled();
|
||||||
@ -271,6 +218,8 @@ describe('Chart.plugins', function() {
|
|||||||
expect(plugins.a.hook.calls.first().args[1]).toEqual({a: '123'});
|
expect(plugins.a.hook.calls.first().args[1]).toEqual({a: '123'});
|
||||||
expect(plugins.b.hook.calls.first().args[1]).toEqual({b: '456'});
|
expect(plugins.b.hook.calls.first().args[1]).toEqual({b: '456'});
|
||||||
expect(plugins.c.hook.calls.first().args[1]).toEqual({c: '789'});
|
expect(plugins.c.hook.calls.first().args[1]).toEqual({c: '789'});
|
||||||
|
|
||||||
|
Chart.unregister(plugins.a);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not called plugins when config.options.plugins.{id} is FALSE', function() {
|
it('should not called plugins when config.options.plugins.{id} is FALSE', function() {
|
||||||
@ -280,7 +229,7 @@ describe('Chart.plugins', function() {
|
|||||||
c: {id: 'c', hook: function() {}}
|
c: {id: 'c', hook: function() {}}
|
||||||
};
|
};
|
||||||
|
|
||||||
Chart.plugins.register(plugins.a);
|
Chart.register(plugins.a);
|
||||||
|
|
||||||
var chart = window.acquireChart({
|
var chart = window.acquireChart({
|
||||||
plugins: [plugins.b, plugins.c],
|
plugins: [plugins.b, plugins.c],
|
||||||
@ -296,18 +245,19 @@ describe('Chart.plugins', function() {
|
|||||||
spyOn(plugins.b, 'hook');
|
spyOn(plugins.b, 'hook');
|
||||||
spyOn(plugins.c, 'hook');
|
spyOn(plugins.c, 'hook');
|
||||||
|
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
|
|
||||||
expect(plugins.a.hook).not.toHaveBeenCalled();
|
expect(plugins.a.hook).not.toHaveBeenCalled();
|
||||||
expect(plugins.b.hook).not.toHaveBeenCalled();
|
expect(plugins.b.hook).not.toHaveBeenCalled();
|
||||||
expect(plugins.c.hook).toHaveBeenCalled();
|
expect(plugins.c.hook).toHaveBeenCalled();
|
||||||
|
|
||||||
|
Chart.unregister(plugins.a);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call plugins with default options when plugin options is TRUE', function() {
|
it('should call plugins with default options when plugin options is TRUE', function() {
|
||||||
var plugin = {id: 'a', hook: function() {}};
|
var plugin = {id: 'a', hook: function() {}, defaults: {a: 42}};
|
||||||
|
|
||||||
Chart.defaults.plugins.a = {a: 42};
|
Chart.register(plugin);
|
||||||
Chart.plugins.register(plugin);
|
|
||||||
|
|
||||||
var chart = window.acquireChart({
|
var chart = window.acquireChart({
|
||||||
options: {
|
options: {
|
||||||
@ -319,34 +269,33 @@ describe('Chart.plugins', function() {
|
|||||||
|
|
||||||
spyOn(plugin, 'hook');
|
spyOn(plugin, 'hook');
|
||||||
|
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
|
|
||||||
expect(plugin.hook).toHaveBeenCalled();
|
expect(plugin.hook).toHaveBeenCalled();
|
||||||
expect(plugin.hook.calls.first().args[1]).toEqual({a: 42});
|
expect(plugin.hook.calls.first().args[1]).toEqual({a: 42});
|
||||||
|
|
||||||
delete Chart.defaults.plugins.a;
|
Chart.unregister(plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call plugins with default options if plugin config options is undefined', function() {
|
it('should call plugins with default options if plugin config options is undefined', function() {
|
||||||
var plugin = {id: 'a', hook: function() {}};
|
var plugin = {id: 'a', hook: function() {}, defaults: {a: 'foobar'}};
|
||||||
|
|
||||||
Chart.defaults.plugins.a = {a: 'foobar'};
|
Chart.register(plugin);
|
||||||
Chart.plugins.register(plugin);
|
|
||||||
spyOn(plugin, 'hook');
|
spyOn(plugin, 'hook');
|
||||||
|
|
||||||
var chart = window.acquireChart();
|
var chart = window.acquireChart();
|
||||||
|
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
|
|
||||||
expect(plugin.hook).toHaveBeenCalled();
|
expect(plugin.hook).toHaveBeenCalled();
|
||||||
expect(plugin.hook.calls.first().args[1]).toEqual({a: 'foobar'});
|
expect(plugin.hook.calls.first().args[1]).toEqual({a: 'foobar'});
|
||||||
|
|
||||||
delete Chart.defaults.plugins.a;
|
Chart.unregister(plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
||||||
it('should invalidate cache when update plugin options', function() {
|
it('should update plugin options', function() {
|
||||||
var plugin = {id: 'a', hook: function() {}};
|
var plugin = {id: 'a', hook: function() {}};
|
||||||
var chart = window.acquireChart({
|
var chart = window.acquireChart({
|
||||||
plugins: [plugin],
|
plugins: [plugin],
|
||||||
@ -361,7 +310,7 @@ describe('Chart.plugins', function() {
|
|||||||
|
|
||||||
spyOn(plugin, 'hook');
|
spyOn(plugin, 'hook');
|
||||||
|
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
|
|
||||||
expect(plugin.hook).toHaveBeenCalled();
|
expect(plugin.hook).toHaveBeenCalled();
|
||||||
expect(plugin.hook.calls.first().args[1]).toEqual({foo: 'foo'});
|
expect(plugin.hook.calls.first().args[1]).toEqual({foo: 'foo'});
|
||||||
@ -370,7 +319,7 @@ describe('Chart.plugins', function() {
|
|||||||
chart.update();
|
chart.update();
|
||||||
|
|
||||||
plugin.hook.calls.reset();
|
plugin.hook.calls.reset();
|
||||||
Chart.plugins.notify(chart, 'hook');
|
chart._plugins.notify(chart, 'hook');
|
||||||
|
|
||||||
expect(plugin.hook).toHaveBeenCalled();
|
expect(plugin.hook).toHaveBeenCalled();
|
||||||
expect(plugin.hook.calls.first().args[1]).toEqual({bar: 'bar'});
|
expect(plugin.hook.calls.first().args[1]).toEqual({bar: 'bar'});
|
||||||
|
|||||||
@ -11,7 +11,6 @@ describe('Chart namespace', function() {
|
|||||||
expect(Chart.Element instanceof Object).toBeTruthy();
|
expect(Chart.Element instanceof Object).toBeTruthy();
|
||||||
expect(Chart.Interaction instanceof Object).toBeTruthy();
|
expect(Chart.Interaction instanceof Object).toBeTruthy();
|
||||||
expect(Chart.layouts instanceof Object).toBeTruthy();
|
expect(Chart.layouts instanceof Object).toBeTruthy();
|
||||||
expect(Chart.plugins instanceof Object).toBeTruthy();
|
|
||||||
|
|
||||||
expect(Chart.platforms.BasePlatform instanceof Function).toBeTruthy();
|
expect(Chart.platforms.BasePlatform instanceof Function).toBeTruthy();
|
||||||
expect(Chart.platforms.BasicPlatform instanceof Function).toBeTruthy();
|
expect(Chart.platforms.BasicPlatform instanceof Function).toBeTruthy();
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
// Test the rectangle element
|
// Test the rectangle element
|
||||||
|
|
||||||
var Title = Chart.plugins.getAll().find(p => p.id === 'title')._element;
|
var Title = Chart.registry.getPlugin('title')._element;
|
||||||
|
|
||||||
describe('Title block tests', function() {
|
describe('Title block tests', function() {
|
||||||
it('Should have the correct default config', function() {
|
it('Should have the correct default config', function() {
|
||||||
expect(Chart.defaults.title).toEqual({
|
expect(Chart.defaults.plugins.title).toEqual({
|
||||||
align: 'center',
|
align: 'center',
|
||||||
display: false,
|
display: false,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
@ -21,7 +21,7 @@ describe('Title block tests', function() {
|
|||||||
it('should update correctly', function() {
|
it('should update correctly', function() {
|
||||||
var chart = {};
|
var chart = {};
|
||||||
|
|
||||||
var options = Chart.helpers.clone(Chart.defaults.title);
|
var options = Chart.helpers.clone(Chart.defaults.plugins.title);
|
||||||
options.text = 'My title';
|
options.text = 'My title';
|
||||||
|
|
||||||
var title = new Title({
|
var title = new Title({
|
||||||
@ -46,7 +46,7 @@ describe('Title block tests', function() {
|
|||||||
it('should update correctly when vertical', function() {
|
it('should update correctly when vertical', function() {
|
||||||
var chart = {};
|
var chart = {};
|
||||||
|
|
||||||
var options = Chart.helpers.clone(Chart.defaults.title);
|
var options = Chart.helpers.clone(Chart.defaults.plugins.title);
|
||||||
options.text = 'My title';
|
options.text = 'My title';
|
||||||
options.position = 'left';
|
options.position = 'left';
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ describe('Title block tests', function() {
|
|||||||
it('should have the correct size when there are multiple lines of text', function() {
|
it('should have the correct size when there are multiple lines of text', function() {
|
||||||
var chart = {};
|
var chart = {};
|
||||||
|
|
||||||
var options = Chart.helpers.clone(Chart.defaults.title);
|
var options = Chart.helpers.clone(Chart.defaults.plugins.title);
|
||||||
options.text = ['line1', 'line2'];
|
options.text = ['line1', 'line2'];
|
||||||
options.position = 'left';
|
options.position = 'left';
|
||||||
options.display = true;
|
options.display = true;
|
||||||
@ -93,7 +93,7 @@ describe('Title block tests', function() {
|
|||||||
var chart = {};
|
var chart = {};
|
||||||
var context = window.createMockContext();
|
var context = window.createMockContext();
|
||||||
|
|
||||||
var options = Chart.helpers.clone(Chart.defaults.title);
|
var options = Chart.helpers.clone(Chart.defaults.plugins.title);
|
||||||
options.text = 'My title';
|
options.text = 'My title';
|
||||||
|
|
||||||
var title = new Title({
|
var title = new Title({
|
||||||
@ -145,7 +145,7 @@ describe('Title block tests', function() {
|
|||||||
var chart = {};
|
var chart = {};
|
||||||
var context = window.createMockContext();
|
var context = window.createMockContext();
|
||||||
|
|
||||||
var options = Chart.helpers.clone(Chart.defaults.title);
|
var options = Chart.helpers.clone(Chart.defaults.plugins.title);
|
||||||
options.text = 'My title';
|
options.text = 'My title';
|
||||||
options.position = 'left';
|
options.position = 'left';
|
||||||
|
|
||||||
@ -315,7 +315,7 @@ describe('Title block tests', function() {
|
|||||||
chart.options.title = {};
|
chart.options.title = {};
|
||||||
chart.update();
|
chart.update();
|
||||||
expect(chart.titleBlock).not.toBe(undefined);
|
expect(chart.titleBlock).not.toBe(undefined);
|
||||||
expect(chart.titleBlock.options).toEqual(jasmine.objectContaining(Chart.defaults.title));
|
expect(chart.titleBlock.options).toEqual(jasmine.objectContaining(Chart.defaults.plugins.title));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// Test the rectangle element
|
// Test the rectangle element
|
||||||
const tooltipPlugin = Chart.plugins.getAll().find(p => p.id === 'tooltip');
|
const tooltipPlugin = Chart.registry.getPlugin('tooltip');
|
||||||
const Tooltip = tooltipPlugin._element;
|
const Tooltip = tooltipPlugin._element;
|
||||||
|
|
||||||
describe('Plugin.Tooltip', function() {
|
describe('Plugin.Tooltip', function() {
|
||||||
@ -22,11 +22,11 @@ describe('Plugin.Tooltip', function() {
|
|||||||
value: '20'
|
value: '20'
|
||||||
};
|
};
|
||||||
|
|
||||||
var label = Chart.defaults.tooltips.callbacks.label(tooltipItem, data);
|
var label = Chart.defaults.plugins.tooltip.callbacks.label(tooltipItem, data);
|
||||||
expect(label).toBe('20');
|
expect(label).toBe('20');
|
||||||
|
|
||||||
data.datasets[0].label = 'My dataset';
|
data.datasets[0].label = 'My dataset';
|
||||||
label = Chart.defaults.tooltips.callbacks.label(tooltipItem, data);
|
label = Chart.defaults.plugins.tooltip.callbacks.label(tooltipItem, data);
|
||||||
expect(label).toBe('My dataset: 20');
|
expect(label).toBe('My dataset: 20');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user