mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Preserve scriptable context (#7981)
* Preserve scriptable context * CC, utilize `index` in tests * Update example to utilize context
This commit is contained in:
parent
72dc37581c
commit
23bf7c0c89
@ -37,14 +37,52 @@ color: [
|
||||
## Option Context
|
||||
|
||||
The option context is used to give contextual information when resolving options and currently only applies to [scriptable options](#scriptable-options).
|
||||
The object is preserved, so it can be used to store and pass information between calls.
|
||||
|
||||
There are multiple levels of context objects:
|
||||
|
||||
- `chart`
|
||||
- `dataset`
|
||||
- `data`
|
||||
- `scale`
|
||||
- `tick`
|
||||
|
||||
Each level inherits its parent(s) and any contextual information stored in the parent is available through the child.
|
||||
|
||||
The context object contains the following properties:
|
||||
|
||||
### chart
|
||||
|
||||
- `chart`: the associated chart
|
||||
- `dataPoint`: the parsed data values for the given `dataIndex` and `datasetIndex`
|
||||
- `dataIndex`: index of the current data
|
||||
|
||||
### dataset
|
||||
|
||||
In addition to [chart](#chart)
|
||||
|
||||
- `active`: true if element is active (hovered)
|
||||
- `dataset`: dataset at index `datasetIndex`
|
||||
- `datasetIndex`: index of the current dataset
|
||||
- `active`: true if element is active (hovered)
|
||||
- `index`: getter for `datasetIndex`
|
||||
|
||||
**Important**: since the context can represent different types of entities (dataset, data, ticks, etc.), some properties may be `undefined` so be sure to test any context property before using it.
|
||||
### data
|
||||
|
||||
In addition to [dataset](#dataset)
|
||||
|
||||
- `active`: true if element is active (hovered)
|
||||
- `dataIndex`: index of the current data
|
||||
- `dataPoint`: the parsed data values for the given `dataIndex` and `datasetIndex`
|
||||
- `element`: the element (point, arc, bar, etc.) for this data
|
||||
- `index`: getter for `dataIndex`
|
||||
|
||||
### scale
|
||||
|
||||
In addition to [chart](#chart)
|
||||
|
||||
- `scale`: the associated scale
|
||||
|
||||
### tick
|
||||
|
||||
In addition to [scale](#scale)
|
||||
|
||||
- `tick`: the associated tick object
|
||||
- `index`: tick index
|
||||
|
||||
@ -63,7 +63,6 @@
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById('canvas').getContext('2d');
|
||||
var started = {};
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
@ -77,9 +76,9 @@
|
||||
var delay = 0;
|
||||
var dsIndex = context.datasetIndex;
|
||||
var index = context.dataIndex;
|
||||
if (!started[index + dsIndex * 1000]) {
|
||||
if (context.dataPoint && !context.delayed) {
|
||||
delay = index * 300 + dsIndex * 100;
|
||||
started[index + dsIndex * 1000] = true;
|
||||
context.delayed = true;
|
||||
}
|
||||
return {
|
||||
easing: 'linear',
|
||||
|
||||
@ -116,6 +116,7 @@ class Chart {
|
||||
this._hiddenIndices = {};
|
||||
this.attached = false;
|
||||
this._animationsDisabled = undefined;
|
||||
this.$context = undefined;
|
||||
|
||||
// Add the chart instance to the global namespace
|
||||
Chart.instances[me.id] = me;
|
||||
@ -712,6 +713,14 @@ class Chart {
|
||||
return meta;
|
||||
}
|
||||
|
||||
getContext() {
|
||||
return this.$context || (this.$context = Object.create(null, {
|
||||
chart: {
|
||||
value: this
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
getVisibleDatasetCount() {
|
||||
return this.getSortedVisibleDatasetMetas().length;
|
||||
}
|
||||
|
||||
@ -148,6 +148,49 @@ function getFirstScaleId(chart, axis) {
|
||||
return Object.keys(scales).filter(key => scales[key].axis === axis).shift();
|
||||
}
|
||||
|
||||
function createDatasetContext(parent, index, dataset) {
|
||||
return Object.create(parent, {
|
||||
active: {
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
dataset: {
|
||||
value: dataset
|
||||
},
|
||||
datasetIndex: {
|
||||
value: index
|
||||
},
|
||||
index: {
|
||||
get() {
|
||||
return this.datasetIndex;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createDataContext(parent, index, point, element) {
|
||||
return Object.create(parent, {
|
||||
active: {
|
||||
writable: true,
|
||||
value: false
|
||||
},
|
||||
dataIndex: {
|
||||
value: index
|
||||
},
|
||||
dataPoint: {
|
||||
value: point
|
||||
},
|
||||
element: {
|
||||
value: element
|
||||
},
|
||||
index: {
|
||||
get() {
|
||||
return this.dataIndex;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const optionKeys = (optionNames) => isArray(optionNames) ? optionNames : Object.keys(optionNames);
|
||||
const optionKey = (key, active) => active ? 'hover' + _capitalize(key) : key;
|
||||
const isDirectUpdateMode = (mode) => mode === 'reset' || mode === 'none';
|
||||
@ -177,6 +220,7 @@ export default class DatasetController {
|
||||
this._drawStart = undefined;
|
||||
this._drawCount = undefined;
|
||||
this.enableOptionSharing = false;
|
||||
this.$context = undefined;
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
@ -491,6 +535,13 @@ export default class DatasetController {
|
||||
return this._cachedMeta._parsed[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
getDataElement(index) {
|
||||
return this._cachedMeta.data[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*/
|
||||
@ -698,14 +749,18 @@ export default class DatasetController {
|
||||
* @protected
|
||||
*/
|
||||
getContext(index, active) {
|
||||
return {
|
||||
chart: this.chart,
|
||||
dataPoint: this.getParsed(index),
|
||||
dataIndex: index,
|
||||
dataset: this.getDataset(),
|
||||
datasetIndex: this.index,
|
||||
active
|
||||
};
|
||||
const me = this;
|
||||
let context;
|
||||
if (index >= 0 && index < me._cachedMeta.data.length) {
|
||||
const element = me._cachedMeta.data[index];
|
||||
context = element.$context ||
|
||||
(element.$context = createDataContext(me.getContext(), index, me.getParsed(index), element));
|
||||
} else {
|
||||
context = me.$context || (me.$context = createDatasetContext(me.chart.getContext(), me.index, me.getDataset()));
|
||||
}
|
||||
|
||||
context.active = !!active;
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@ import Ticks from './core.ticks';
|
||||
|
||||
/**
|
||||
* @typedef { import("./core.controller").default } Chart
|
||||
* @typedef {{value:any, label?:string, major?:boolean}} Tick
|
||||
* @typedef {{value:any, label?:string, major?:boolean, $context?:any}} Tick
|
||||
*/
|
||||
|
||||
defaults.set('scale', {
|
||||
@ -269,6 +269,25 @@ function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
|
||||
}
|
||||
}
|
||||
|
||||
function createScaleContext(parent, scale) {
|
||||
return Object.create(parent, {
|
||||
scale: {
|
||||
value: scale
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createTickContext(parent, index, tick) {
|
||||
return Object.create(parent, {
|
||||
tick: {
|
||||
value: tick
|
||||
},
|
||||
index: {
|
||||
value: index
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default class Scale extends Element {
|
||||
|
||||
// eslint-disable-next-line max-statements
|
||||
@ -345,6 +364,7 @@ export default class Scale extends Element {
|
||||
this._ticksLength = 0;
|
||||
this._borderValue = 0;
|
||||
this._cache = {};
|
||||
this.$context = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1043,13 +1063,16 @@ export default class Scale extends Element {
|
||||
* @protected
|
||||
*/
|
||||
getContext(index) {
|
||||
const ticks = this.ticks || [];
|
||||
return {
|
||||
chart: this.chart,
|
||||
scale: this,
|
||||
tick: ticks[index],
|
||||
index
|
||||
};
|
||||
const me = this;
|
||||
const ticks = me.ticks || [];
|
||||
|
||||
if (index >= 0 && index < ticks.length) {
|
||||
const tick = ticks[index];
|
||||
return tick.$context ||
|
||||
(tick.$context = createTickContext(me.getContext(), index, tick));
|
||||
}
|
||||
return me.$context ||
|
||||
(me.$context = createScaleContext(me.chart.getContext(), me));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@ module.exports = {
|
||||
// option in dataset
|
||||
data: [4, 5, 10, null, -10, -5],
|
||||
backgroundColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#ff00ff';
|
||||
@ -26,7 +26,7 @@ module.exports = {
|
||||
elements: {
|
||||
line: {
|
||||
backgroundColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#ff00ff';
|
||||
|
||||
@ -8,7 +8,7 @@ module.exports = {
|
||||
// option in dataset
|
||||
data: [4, 5, 10, null, -10, -5],
|
||||
borderColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#0000ff';
|
||||
@ -26,7 +26,7 @@ module.exports = {
|
||||
elements: {
|
||||
line: {
|
||||
borderColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#0000ff';
|
||||
|
||||
@ -9,7 +9,7 @@ module.exports = {
|
||||
data: [4, 5, 10, null, -10, -5],
|
||||
borderColor: '#0000ff',
|
||||
borderWidth: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index % 2 ? 10 : 20;
|
||||
},
|
||||
pointBorderColor: '#00ff00'
|
||||
@ -27,7 +27,7 @@ module.exports = {
|
||||
line: {
|
||||
borderColor: '#ff0000',
|
||||
borderWidth: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index % 2 ? 10 : 20;
|
||||
},
|
||||
fill: false,
|
||||
|
||||
@ -8,7 +8,7 @@ module.exports = {
|
||||
// option in dataset
|
||||
data: [0, 5, 10, null, -10, -5],
|
||||
backgroundColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#ff00ff';
|
||||
@ -26,7 +26,7 @@ module.exports = {
|
||||
elements: {
|
||||
line: {
|
||||
backgroundColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#ff00ff';
|
||||
|
||||
@ -8,7 +8,7 @@ module.exports = {
|
||||
// option in dataset
|
||||
data: [0, 5, 10, null, -10, -5],
|
||||
borderColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#0000ff';
|
||||
@ -26,7 +26,7 @@ module.exports = {
|
||||
elements: {
|
||||
line: {
|
||||
borderColor: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index === 0 ? '#ff0000'
|
||||
: index === 1 ? '#00ff00'
|
||||
: '#0000ff';
|
||||
|
||||
@ -9,7 +9,7 @@ module.exports = {
|
||||
data: [0, 5, 10, null, -10, -5],
|
||||
borderColor: '#0000ff',
|
||||
borderWidth: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index % 2 ? 10 : 20;
|
||||
},
|
||||
pointBorderColor: '#00ff00'
|
||||
@ -27,7 +27,7 @@ module.exports = {
|
||||
line: {
|
||||
borderColor: '#ff0000',
|
||||
borderWidth: function(ctx) {
|
||||
var index = (ctx.dataIndex === undefined ? ctx.datasetIndex : ctx.dataIndex);
|
||||
var index = ctx.index;
|
||||
return index % 2 ? 10 : 20;
|
||||
},
|
||||
fill: false
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user