]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Preserve scriptable context (#7981)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Sun, 1 Nov 2020 12:39:08 +0000 (14:39 +0200)
committerGitHub <noreply@github.com>
Sun, 1 Nov 2020 12:39:08 +0000 (07:39 -0500)
* Preserve scriptable context
* CC, utilize `index` in tests
* Update example to utilize context

docs/docs/general/options.md
samples/animations/delay.html
src/core/core.controller.js
src/core/core.datasetController.js
src/core/core.scale.js
test/fixtures/controller.line/backgroundColor/scriptable.js
test/fixtures/controller.line/borderColor/scriptable.js
test/fixtures/controller.line/borderWidth/scriptable.js
test/fixtures/controller.radar/backgroundColor/scriptable.js
test/fixtures/controller.radar/borderColor/scriptable.js
test/fixtures/controller.radar/borderWidth/scriptable.js

index f7074356651fe95062952b0de574a6a392bc0f7d..399c1d43cfac947d1f5fd12af20f5c990bfd6902 100644 (file)
@@ -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
+- `index`: getter for `datasetIndex`
+
+### 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)
 
-**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.
+- `tick`: the associated tick object
+- `index`: tick index
index 5c9d3fc8a8f9ee93bdc64554fa33ae199a953b8b..ea38a50d1f82c0fade104df01c827ad9e48c635e 100644 (file)
@@ -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',
index 20e097f3e6e4e378362a0145c8145eabf0567558..13d3f8d0fbbbe24f83204a12ba8c39aceb2f14f3 100644 (file)
@@ -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;
        }
index 6c9dc259c1eea08a0bb2d71600fd596eb22cd624..d9c2bd5c7429cb1ec96c2e8665fcb0ce80ca337f 100644 (file)
@@ -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;
        }
 
        /**
index 3e04ddfc4e2d22891b48ed09300bca3453febc78..84cd4e3d57e7b09158d6d630678e5f2c4870cc31 100644 (file)
@@ -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));
        }
 
        /**
index ec4c653fbcc7944f857676801da5908664fc8713..a1844973a5bf25f1194a50f86ce281c1fa7518ab 100644 (file)
@@ -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';
index 15137ea0258d315a46a985d98cf9afd2e8c5db23..33941b9f547648ba1f6e4ee13f4f221e87a82a06 100644 (file)
@@ -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';
index 244c7d9cd654fd2535e0cdf985768046ab207aaf..f4167d7c01345353a23f7362172150454146fe4f 100644 (file)
@@ -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,
index 0f5d313dd3ac19f28a5d2adca61457c24550dd8a..2ff729099a3ba16b1142702feb308f0e980279e5 100644 (file)
@@ -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';
index c1cdbb6f84666fa2ba6bf2d8bc2db1144ec1bfd7..2767e9786fefd155ea412f7a4bb0c3187741ee7c 100644 (file)
@@ -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';
index adf8338b8fd12184194bf5b0b4a1ccc786a75331..64fcffad9889226009286e68218a241e8fdd0811 100644 (file)
@@ -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