]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Add support for floating bar chart ([start, end]) (#6056)
authorgwyneblaidd <derevmyk@ukr.net>
Tue, 21 May 2019 08:54:13 +0000 (11:54 +0300)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Tue, 21 May 2019 08:54:13 +0000 (10:54 +0200)
13 files changed:
docs/charts/bar.md
src/controllers/controller.bar.js
src/core/core.scale.js
src/scales/scale.linear.js
src/scales/scale.logarithmic.js
test/fixtures/controller.bar/floatBar/float-bar-horizontal.json [new file with mode: 0644]
test/fixtures/controller.bar/floatBar/float-bar-horizontal.png [new file with mode: 0644]
test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.json [new file with mode: 0644]
test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.png [new file with mode: 0644]
test/fixtures/controller.bar/floatBar/float-bar-stacked.json [new file with mode: 0644]
test/fixtures/controller.bar/floatBar/float-bar-stacked.png [new file with mode: 0644]
test/fixtures/controller.bar/floatBar/float-bar.json [new file with mode: 0644]
test/fixtures/controller.bar/floatBar/float-bar.png [new file with mode: 0644]

index 02f080614a104b80dd82aeb8a4456c2b92c28e95..328b1df0e1935ab2bfff7411b76f2893ed211c5e 100644 (file)
@@ -213,6 +213,11 @@ You can also specify the dataset as x/y coordinates when using the [time scale](
 data: [{x:'2016-12-25', y:20}, {x:'2016-12-26', y:10}]
 ```
 
+You can also specify the dataset for a bar chart as arrays of two numbers. This will force rendering of bars with gaps between them (floating-bars). First and second numbers in array will correspond the start and the end point of a bar respectively.
+```javascript
+data: [[5,6], [-3,-6]]
+```
+
 ## Stacked Bar Chart
 
 Bar charts can be configured into stacked bar charts by changing the settings on the X and Y axes to enable stacking. Stacked bar charts can be used to show how one data series is made up of a number of smaller pieces.
index 0736cf6e1e08ba5aa53e95c20b33d7f0ca2f2fc8..aa8b06a2cbc073efcaa549f1f6c31056ea3b5841 100644 (file)
@@ -170,6 +170,10 @@ module.exports = DatasetController.extend({
                        label: me.chart.data.labels[index]
                };
 
+               if (helpers.isArray(dataset.data[index])) {
+                       rectangle._model.borderSkipped = null;
+               }
+
                me._updateElementGeometry(rectangle, index, reset);
 
                rectangle.pivot();
@@ -293,12 +297,13 @@ module.exports = DatasetController.extend({
                var scale = me._getValueScale();
                var isHorizontal = scale.isHorizontal();
                var datasets = chart.data.datasets;
-               var value = +scale.getRightValue(datasets[datasetIndex].data[index]);
+               var value = scale._parseValue(datasets[datasetIndex].data[index]);
                var minBarLength = scale.options.minBarLength;
                var stacked = scale.options.stacked;
                var stack = meta.stack;
-               var start = 0;
-               var i, imeta, ivalue, base, head, size;
+               var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max;
+               var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max;
+               var i, imeta, ivalue, base, head, size, stackLength;
 
                if (stacked || (stacked === undefined && stack !== undefined)) {
                        for (i = 0; i < datasetIndex; ++i) {
@@ -309,8 +314,10 @@ module.exports = DatasetController.extend({
                                        imeta.controller._getValueScaleId() === scale.id &&
                                        chart.isDatasetVisible(i)) {
 
-                                       ivalue = +scale.getRightValue(datasets[i].data[index]);
-                                       if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
+                                       stackLength = scale._parseValue(datasets[i].data[index]);
+                                       ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min;
+
+                                       if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) {
                                                start += ivalue;
                                        }
                                }
@@ -318,12 +325,12 @@ module.exports = DatasetController.extend({
                }
 
                base = scale.getPixelForValue(start);
-               head = scale.getPixelForValue(start + value);
+               head = scale.getPixelForValue(start + length);
                size = head - base;
 
                if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
                        size = minBarLength;
-                       if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) {
+                       if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) {
                                head = base - minBarLength;
                        } else {
                                head = base + minBarLength;
@@ -374,7 +381,8 @@ module.exports = DatasetController.extend({
                helpers.canvas.clipArea(chart.ctx, chart.chartArea);
 
                for (; i < ilen; ++i) {
-                       if (!isNaN(scale.getRightValue(dataset.data[i]))) {
+                       var val = scale._parseValue(dataset.data[i]);
+                       if (!isNaN(val.min) && !isNaN(val.max)) {
                                rects[i].draw();
                        }
                }
index cf1fb281e88a218ec27b117e60649c26ca65e6b8..7fccd4d4a1522b1b849df44c93581798d78a27c8 100644 (file)
@@ -586,6 +586,7 @@ var Scale = Element.extend({
                if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) {
                        return NaN;
                }
+
                // If it is in fact an object, dive in one more level
                if (rawValue) {
                        if (this.isHorizontal()) {
@@ -601,6 +602,45 @@ var Scale = Element.extend({
                return rawValue;
        },
 
+       /**
+       * @private
+       */
+       _parseValue: function(value) {
+               var start, end, min, max;
+
+               if (helpers.isArray(value)) {
+                       start = +this.getRightValue(value[0]);
+                       end = +this.getRightValue(value[1]);
+                       min = Math.min(start, end);
+                       max = Math.max(start, end);
+               } else {
+                       value = +this.getRightValue(value);
+                       start = undefined;
+                       end = value;
+                       min = value;
+                       max = value;
+               }
+
+               return {
+                       min: min,
+                       max: max,
+                       start: start,
+                       end: end
+               };
+       },
+
+       /**
+       * @private
+       */
+       _getScaleLabel: function(rawValue) {
+               var v = this._parseValue(rawValue);
+               if (v.start !== undefined) {
+                       return '[' + v.start + ', ' + v.end + ']';
+               }
+
+               return +this.getRightValue(rawValue);
+       },
+
        /**
         * Used to get the value to display in the tooltip for the data at the given index
         * @param index
index a2199b66e78dbba87d950c3bb3e13ce4063ee409..c9ce8709dedea84dc794455e85a60790d1fb947f 100644 (file)
@@ -70,20 +70,25 @@ module.exports = LinearScaleBase.extend({
 
                                if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
                                        helpers.each(dataset.data, function(rawValue, index) {
-                                               var value = +me.getRightValue(rawValue);
-                                               if (isNaN(value) || meta.data[index].hidden) {
+                                               var value = me._parseValue(rawValue);
+
+                                               if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden) {
                                                        return;
                                                }
 
                                                positiveValues[index] = positiveValues[index] || 0;
                                                negativeValues[index] = negativeValues[index] || 0;
 
+                                               if (value.min === 0 && !opts.ticks.beginAtZero) {
+                                                       value.min = value.max;
+                                               }
+
                                                if (opts.relativePoints) {
                                                        positiveValues[index] = 100;
-                                               } else if (value < 0) {
-                                                       negativeValues[index] += value;
+                                               } else if (value.min < 0 || value.max < 0) {
+                                                       negativeValues[index] += value.min;
                                                } else {
-                                                       positiveValues[index] += value;
+                                                       positiveValues[index] += value.max;
                                                }
                                        });
                                }
@@ -102,21 +107,18 @@ module.exports = LinearScaleBase.extend({
                                var meta = chart.getDatasetMeta(datasetIndex);
                                if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
                                        helpers.each(dataset.data, function(rawValue, index) {
-                                               var value = +me.getRightValue(rawValue);
-                                               if (isNaN(value) || meta.data[index].hidden) {
+                                               var value = me._parseValue(rawValue);
+
+                                               if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden) {
                                                        return;
                                                }
 
-                                               if (me.min === null) {
-                                                       me.min = value;
-                                               } else if (value < me.min) {
-                                                       me.min = value;
+                                               if (me.min === null || value.min < me.min) {
+                                                       me.min = value.min;
                                                }
 
-                                               if (me.max === null) {
-                                                       me.max = value;
-                                               } else if (value > me.max) {
-                                                       me.max = value;
+                                               if (me.max === null || me.max < value.max) {
+                                                       me.max = value.max;
                                                }
                                        });
                                }
@@ -151,7 +153,7 @@ module.exports = LinearScaleBase.extend({
        },
 
        getLabelForIndex: function(index, datasetIndex) {
-               return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
+               return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
        },
 
        // Utils
index fd67f0b19a45c8a58390d9b17a0c4af429f9bc9e..3e9b2849f0b7adf075ed8f422aaca13df0ed8dde 100644 (file)
@@ -118,13 +118,13 @@ module.exports = Scale.extend({
 
                                        helpers.each(dataset.data, function(rawValue, index) {
                                                var values = valuesPerStack[key];
-                                               var value = +me.getRightValue(rawValue);
+                                               var value = me._parseValue(rawValue);
                                                // invalid, hidden and negative values are ignored
-                                               if (isNaN(value) || meta.data[index].hidden || value < 0) {
+                                               if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden || value.min < 0 || value.max < 0) {
                                                        return;
                                                }
                                                values[index] = values[index] || 0;
-                                               values[index] += value;
+                                               values[index] += value.max;
                                        });
                                }
                        });
@@ -143,26 +143,22 @@ module.exports = Scale.extend({
                                var meta = chart.getDatasetMeta(datasetIndex);
                                if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
                                        helpers.each(dataset.data, function(rawValue, index) {
-                                               var value = +me.getRightValue(rawValue);
+                                               var value = me._parseValue(rawValue);
                                                // invalid, hidden and negative values are ignored
-                                               if (isNaN(value) || meta.data[index].hidden || value < 0) {
+                                               if (isNaN(value.min) || isNaN(value.max) || meta.data[index].hidden || value.min < 0 || value.max < 0) {
                                                        return;
                                                }
 
-                                               if (me.min === null) {
-                                                       me.min = value;
-                                               } else if (value < me.min) {
-                                                       me.min = value;
+                                               if (me.min === null || value.min < me.min) {
+                                                       me.min = value.min;
                                                }
 
-                                               if (me.max === null) {
-                                                       me.max = value;
-                                               } else if (value > me.max) {
-                                                       me.max = value;
+                                               if (me.max === null || me.max < value.max) {
+                                                       me.max = value.max;
                                                }
 
-                                               if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
-                                                       me.minNotZero = value;
+                                               if (value.min !== 0 && (me.minNotZero === null || value.min < me.minNotZero)) {
+                                                       me.minNotZero = value.min;
                                                }
                                        });
                                }
@@ -247,7 +243,7 @@ module.exports = Scale.extend({
 
        // Get the correct tooltip label
        getLabelForIndex: function(index, datasetIndex) {
-               return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
+               return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
        },
 
        getPixelForTick: function(index) {
diff --git a/test/fixtures/controller.bar/floatBar/float-bar-horizontal.json b/test/fixtures/controller.bar/floatBar/float-bar-horizontal.json
new file mode 100644 (file)
index 0000000..c54dea9
--- /dev/null
@@ -0,0 +1,41 @@
+{
+    "config": {
+        "type": "horizontalBar",
+        "data": {
+            "labels": ["2030", "2034", "2038", "2042"],
+            "datasets": [{
+                "backgroundColor": "#FF6384",
+                "data": [11, [6,2], [-4,-7], -2]
+            }, {
+                "backgroundColor": "#36A2EB",
+                "data": [[1,2], [3,4], [-2,-3], [1,4]]
+            }, {
+                "backgroundColor": "#FFCE56",
+                "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+            }]
+        },
+        "options": {
+            "title": false,
+            "legend": false,
+            "scales": {
+                "xAxes": [{
+                    "display": false,
+                    "ticks": {
+                        "min": -8,
+                        "max": 12
+                    }
+                }],
+                "yAxes": [{
+                    "display": false
+                }]
+            }
+        }
+    },
+    "debug": false,
+    "options": {
+        "canvas": {
+            "height": 256,
+            "width": 512
+        }
+    }
+}
diff --git a/test/fixtures/controller.bar/floatBar/float-bar-horizontal.png b/test/fixtures/controller.bar/floatBar/float-bar-horizontal.png
new file mode 100644 (file)
index 0000000..3293dc8
Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar-horizontal.png differ
diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.json b/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.json
new file mode 100644 (file)
index 0000000..8322ef4
--- /dev/null
@@ -0,0 +1,43 @@
+{
+    "config": {
+        "type": "horizontalBar",
+        "data": {
+            "labels": ["2030", "2034", "2038", "2042"],
+            "datasets": [{
+                "backgroundColor": "#FF6384",
+                "data": [11, [6,2], [-4,-7], -2]
+            }, {
+                "backgroundColor": "#36A2EB",
+                "data": [[1,2], [3,4], [-2,-3], [1,4]]
+            }, {
+                "backgroundColor": "#FFCE56",
+                "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+            }]
+        },
+        "options": {
+            "title": false,
+            "legend": false,
+            "scales": {
+                "xAxes": [{
+                    "display": false,
+                    "stacked": true
+                }],
+                "yAxes": [{
+                    "display": false,
+                    "stacked": true,
+                    "ticks": {
+                        "min": -8,
+                        "max": 12
+                    }
+                }]
+            }
+        }
+    },
+    "debug": false,
+    "options": {
+        "canvas": {
+            "height": 256,
+            "width": 512
+        }
+    }
+}
diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.png b/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.png
new file mode 100644 (file)
index 0000000..5df5731
Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar-stacked-horizontal.png differ
diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked.json b/test/fixtures/controller.bar/floatBar/float-bar-stacked.json
new file mode 100644 (file)
index 0000000..d91b61f
--- /dev/null
@@ -0,0 +1,43 @@
+{
+    "config": {
+        "type": "bar",
+        "data": {
+            "labels": ["2030", "2034", "2038", "2042"],
+            "datasets": [{
+                "backgroundColor": "#FF6384",
+                "data": [11, [6,2], [-4,-7], -2]
+            }, {
+                "backgroundColor": "#36A2EB",
+                "data": [[1,2], [3,4], [-2,-3], [1,4]]
+            }, {
+                "backgroundColor": "#FFCE56",
+                "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+            }]
+        },
+        "options": {
+            "title": false,
+            "legend": false,
+            "scales": {
+                "xAxes": [{
+                    "display": false,
+                    "stacked": true,
+                    "ticks": {
+                        "min": -8,
+                        "max": 12
+                    }
+                }],
+                "yAxes": [{
+                    "display": false,
+                    "stacked": true
+                }]
+            }
+        }
+    },
+    "debug": false,
+    "options": {
+        "canvas": {
+            "height": 256,
+            "width": 512
+        }
+    }
+}
diff --git a/test/fixtures/controller.bar/floatBar/float-bar-stacked.png b/test/fixtures/controller.bar/floatBar/float-bar-stacked.png
new file mode 100644 (file)
index 0000000..c9c02ea
Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar-stacked.png differ
diff --git a/test/fixtures/controller.bar/floatBar/float-bar.json b/test/fixtures/controller.bar/floatBar/float-bar.json
new file mode 100644 (file)
index 0000000..caeaa23
--- /dev/null
@@ -0,0 +1,41 @@
+{
+    "config": {
+        "type": "bar",
+        "data": {
+            "labels": ["2030", "2034", "2038", "2042"],
+            "datasets": [{
+                "backgroundColor": "#FF6384",
+                "data": [11, [6,2], [-4,-7], -2]
+            }, {
+                "backgroundColor": "#36A2EB",
+                "data": [[1,2], [3,4], [-2,-3], [1,4]]
+            }, {
+                "backgroundColor": "#FFCE56",
+                "data": [[0,1], [1,2], [-2,-1], [1,-7]]
+            }]
+        },
+        "options": {
+            "title": false,
+            "legend": false,
+            "scales": {
+                "xAxes": [{
+                    "display": false,
+                    "ticks": {
+                        "min": -8,
+                        "max": 12
+                    }
+                }],
+                "yAxes": [{
+                    "display": false
+                }]
+            }
+        }
+    },
+    "debug": false,
+    "options": {
+        "canvas": {
+            "height": 256,
+            "width": 512
+        }
+    }
+}
diff --git a/test/fixtures/controller.bar/floatBar/float-bar.png b/test/fixtures/controller.bar/floatBar/float-bar.png
new file mode 100644 (file)
index 0000000..eaff55a
Binary files /dev/null and b/test/fixtures/controller.bar/floatBar/float-bar.png differ