Skip to content

Commit

Permalink
Implement dataset.order (chartjs#6268)
Browse files Browse the repository at this point in the history
Allow sorting datasets based on the `order` property
  • Loading branch information
kurkle authored and etimberg committed Oct 22, 2019
1 parent 535b9ba commit 53a3eb2
Show file tree
Hide file tree
Showing 30 changed files with 748 additions and 156 deletions.
2 changes: 2 additions & 0 deletions docs/charts/bar.md
Expand Up @@ -77,6 +77,7 @@ the color of the bars is generally set this way.
| [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined`
| [`hoverBorderWidth`](#interactions) | `number` | - | Yes | `1`
| [`label`](#general) | `string` | - | - | `''`
| [`order`](#general) | `number` | - | - | `0`
| [`xAxisID`](#general) | `string` | - | - | first x axis
| [`yAxisID`](#general) | `string` | - | - | first y axis

Expand All @@ -85,6 +86,7 @@ the color of the bars is generally set this way.
| Name | Description
| ---- | ----
| `label` | The label for the dataset which appears in the legend and tooltips.
| `order` | The drawing order of dataset. Also affects order for stacking, tooltip, and legend.
| `xAxisID` | The ID of the x axis to plot this dataset on.
| `yAxisID` | The ID of the y axis to plot this dataset on.

Expand Down
10 changes: 7 additions & 3 deletions docs/charts/bubble.md
Expand Up @@ -49,14 +49,18 @@ The bubble chart allows a number of properties to be specified for each dataset.
| [`hoverBorderWidth`](#interactions) | `number` | Yes | Yes | `1`
| [`hoverRadius`](#interactions) | `number` | Yes | Yes | `4`
| [`hitRadius`](#interactions) | `number` | Yes | Yes | `1`
| [`label`](#labeling) | `string` | - | - | `undefined`
| [`label`](#general) | `string` | - | - | `undefined`
| [`order`](#general) | `number` | - | - | `0`
| [`pointStyle`](#styling) | `string` | Yes | Yes | `'circle'`
| [`rotation`](#styling) | `number` | Yes | Yes | `0`
| [`radius`](#styling) | `number` | Yes | Yes | `3`

### Labeling
### General

`label` defines the text associated to the dataset and which appears in the legend and tooltips.
| Name | Description
| ---- | ----
| `label` | The label for the dataset which appears in the legend and tooltips.
| `order` | The drawing order of dataset.

### Styling

Expand Down
2 changes: 2 additions & 0 deletions docs/charts/line.md
Expand Up @@ -54,6 +54,7 @@ The line chart allows a number of properties to be specified for each dataset. T
| [`fill`](#line-styling) | <code>boolean&#124;string</code> | Yes | - | `true`
| [`label`](#general) | `string` | - | - | `''`
| [`lineTension`](#line-styling) | `number` | - | - | `0.4`
| [`order`](#general) | `number` | - | - | `0`
| [`pointBackgroundColor`](#point-styling) | `Color` | Yes | Yes | `'rgba(0, 0, 0, 0.1)'`
| [`pointBorderColor`](#point-styling) | `Color` | Yes | Yes | `'rgba(0, 0, 0, 0.1)'`
| [`pointBorderWidth`](#point-styling) | `number` | Yes | Yes | `1`
Expand All @@ -76,6 +77,7 @@ The line chart allows a number of properties to be specified for each dataset. T
| Name | Description
| ---- | ----
| `label` | The label for the dataset which appears in the legend and tooltips.
| `order` | The drawing order of dataset. Also affects order for stacking, tooltip, and legend.
| `xAxisID` | The ID of the x axis to plot this dataset on.
| `yAxisID` | The ID of the y axis to plot this dataset on.

Expand Down
26 changes: 26 additions & 0 deletions docs/charts/mixed.md
Expand Up @@ -70,3 +70,29 @@ At this point we have a chart rendering how we'd like. It's important to note th
}
}
{% endchartjs %}

## Drawing order

By default, datasets are drawn so that first one is top-most. This can be altered by specifying `order` option to datasets. `order` defaults to `0`.

```javascript
var mixedChart = new Chart(ctx, {
type: 'bar',
data: {
datasets: [{
label: 'Bar Dataset',
data: [10, 20, 30, 40],
// this dataset is drawn below
order: 1
}, {
label: 'Line Dataset',
data: [10, 10, 10, 10],
type: 'line',
// this dataset is drawn on top
order: 2
}],
labels: ['January', 'February', 'March', 'April']
},
options: options
});
```
1 change: 1 addition & 0 deletions docs/charts/polar.md
Expand Up @@ -70,6 +70,7 @@ All these values, if `undefined`, fallback to the associated [`elements.arc.*`](
### Border Alignment

The following values are supported for `borderAlign`.

* `'center'` (default)
* `'inner'`

Expand Down
3 changes: 3 additions & 0 deletions docs/charts/radar.md
Expand Up @@ -52,6 +52,7 @@ They are often useful for comparing the points of two or more different data set
{% endchartjs %}

## Example Usage

```javascript
var myRadarChart = new Chart(ctx, {
type: 'radar',
Expand All @@ -75,6 +76,7 @@ The radar chart allows a number of properties to be specified for each dataset.
| [`borderWidth`](#line-styling) | `number` | Yes | - | `3`
| [`fill`](#line-styling) | <code>boolean&#124;string</code> | Yes | - | `true`
| [`label`](#general) | `string` | - | - | `''`
| [`order`](#general) | `number` | - | - | `0`
| [`lineTension`](#line-styling) | `number` | - | - | `0`
| [`pointBackgroundColor`](#point-styling) | `Color` | Yes | Yes | `'rgba(0, 0, 0, 0.1)'`
| [`pointBorderColor`](#point-styling) | `Color` | Yes | Yes | `'rgba(0, 0, 0, 0.1)'`
Expand All @@ -94,6 +96,7 @@ The radar chart allows a number of properties to be specified for each dataset.
| Name | Description
| ---- | ----
| `label` | The label for the dataset which appears in the legend and tooltips.
| `order` | The drawing order of dataset.

### Point Styling

Expand Down
1 change: 1 addition & 0 deletions docs/charts/scatter.md
Expand Up @@ -32,6 +32,7 @@ var scatterChart = new Chart(ctx, {
```

## Dataset Properties

The scatter chart supports all of the same properties as the [line chart](./line.md#dataset-properties).

## Data Structure
Expand Down
36 changes: 22 additions & 14 deletions src/controllers/controller.bar.js
Expand Up @@ -207,21 +207,27 @@ module.exports = DatasetController.extend({
*/
_getStacks: function(last) {
var me = this;
var chart = me.chart;
var scale = me._getIndexScale();
var metasets = scale._getMatchingVisibleMetas(me._type);
var stacked = scale.options.stacked;
var ilen = last === undefined ? chart.data.datasets.length : last + 1;
var ilen = metasets.length;
var stacks = [];
var i, meta;

for (i = 0; i < ilen; ++i) {
meta = chart.getDatasetMeta(i);
if (meta.bar && chart.isDatasetVisible(i) &&
(stacked === false ||
(stacked === true && stacks.indexOf(meta.stack) === -1) ||
(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
meta = metasets[i];
// stacked | meta.stack
// | found | not found | undefined
// false | x | x | x
// true | | x |
// undefined | | x | x
if (stacked === false || stacks.indexOf(meta.stack) === -1 ||
(stacked === undefined && meta.stack === undefined)) {
stacks.push(meta.stack);
}
if (meta.index === last) {
break;
}
}

return stacks;
Expand Down Expand Up @@ -290,24 +296,26 @@ module.exports = DatasetController.extend({
var scale = me._getValueScale();
var isHorizontal = scale.isHorizontal();
var datasets = chart.data.datasets;
var metasets = scale._getMatchingVisibleMetas(me._type);
var value = scale._parseValue(datasets[datasetIndex].data[index]);
var minBarLength = scale.options.minBarLength;
var stacked = scale.options.stacked;
var stack = me.getMeta().stack;
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 ilen = metasets.length;
var i, imeta, ivalue, base, head, size, stackLength;

if (stacked || (stacked === undefined && stack !== undefined)) {
for (i = 0; i < datasetIndex; ++i) {
imeta = chart.getDatasetMeta(i);
for (i = 0; i < ilen; ++i) {
imeta = metasets[i];

if (imeta.bar &&
imeta.stack === stack &&
imeta.controller._getValueScaleId() === scale.id &&
chart.isDatasetVisible(i)) {
if (imeta.index === datasetIndex) {
break;
}

stackLength = scale._parseValue(datasets[i].data[index]);
if (imeta.stack === stack) {
stackLength = scale._parseValue(datasets[imeta.index].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)) {
Expand Down
24 changes: 14 additions & 10 deletions src/controllers/controller.line.js
Expand Up @@ -183,14 +183,21 @@ module.exports = DatasetController.extend({
var yScale = me._yScale;
var sumPos = 0;
var sumNeg = 0;
var i, ds, dsMeta;
var rightValue = +yScale.getRightValue(value);
var metasets = chart._getSortedVisibleDatasetMetas();
var ilen = metasets.length;
var i, ds, dsMeta, stackedRightValue;

if (yScale.options.stacked) {
for (i = 0; i < datasetIndex; i++) {
ds = chart.data.datasets[i];
dsMeta = chart.getDatasetMeta(i);
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
for (i = 0; i < ilen; ++i) {
dsMeta = metasets[i];
if (dsMeta.index === datasetIndex) {
break;
}

ds = chart.data.datasets[dsMeta.index];
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) {
stackedRightValue = +yScale.getRightValue(ds.data[index]);
if (stackedRightValue < 0) {
sumNeg += stackedRightValue || 0;
} else {
Expand All @@ -199,14 +206,11 @@ module.exports = DatasetController.extend({
}
}

var rightValue = Number(yScale.getRightValue(value));
if (rightValue < 0) {
return yScale.getPixelForValue(sumNeg + rightValue);
}
return yScale.getPixelForValue(sumPos + rightValue);
}

return yScale.getPixelForValue(value);
return yScale.getPixelForValue(sumPos + rightValue);
},

updateBezierControlPoints: function() {
Expand Down
61 changes: 47 additions & 14 deletions src/core/core.controller.js
Expand Up @@ -154,6 +154,14 @@ function positionIsHorizontal(position) {
return position === 'top' || position === 'bottom';
}

function compare2Level(l1, l2) {
return function(a, b) {
return a[l1] === b[l1]
? a[l2] - b[l2]
: a[l1] - b[l1];
};
}

var Chart = function(item, config) {
this.construct(item, config);
return this;
Expand Down Expand Up @@ -422,6 +430,8 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
meta = me.getDatasetMeta(datasetIndex);
}
meta.type = type;
meta.order = dataset.order || 0;
meta.index = datasetIndex;

if (meta.controller) {
meta.controller.updateIndex(datasetIndex);
Expand Down Expand Up @@ -513,11 +523,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
// Do this before render so that any plugins that need final scale updates can use it
plugins.notify(me, 'afterUpdate');

me._layers.sort(function(a, b) {
return a.z === b.z
? a._idx - b._idx
: a.z - b.z;
});
me._layers.sort(compare2Level('z', '_idx'));

if (me._bufferedRender) {
me._bufferedRequest = {
Expand Down Expand Up @@ -717,23 +723,49 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
me.tooltip.transition(easingValue);
},

/**
* @private
*/
_getSortedDatasetMetas: function(filterVisible) {
var me = this;
var datasets = me.data.datasets || [];
var result = [];
var i, ilen;

for (i = 0, ilen = datasets.length; i < ilen; ++i) {
if (!filterVisible || me.isDatasetVisible(i)) {
result.push(me.getDatasetMeta(i));
}
}

result.sort(compare2Level('order', 'index'));

return result;
},

/**
* @private
*/
_getSortedVisibleDatasetMetas: function() {
return this._getSortedDatasetMetas(true);
},

/**
* Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
* hook, in which case, plugins will not be called on `afterDatasetsDraw`.
* @private
*/
drawDatasets: function(easingValue) {
var me = this;
var metasets, i;

if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
return;
}

// Draw datasets reversed to support proper line stacking
for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
if (me.isDatasetVisible(i)) {
me.drawDataset(i, easingValue);
}
metasets = me._getSortedVisibleDatasetMetas();
for (i = metasets.length - 1; i >= 0; --i) {
me.drawDataset(metasets[i], easingValue);
}

plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
Expand All @@ -744,12 +776,11 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
* hook, in which case, plugins will not be called on `afterDatasetDraw`.
* @private
*/
drawDataset: function(index, easingValue) {
drawDataset: function(meta, easingValue) {
var me = this;
var meta = me.getDatasetMeta(index);
var args = {
meta: meta,
index: index,
index: meta.index,
easingValue: easingValue
};

Expand Down Expand Up @@ -829,7 +860,9 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
controller: null,
hidden: null, // See isDatasetVisible() comment
xAxisID: null,
yAxisID: null
yAxisID: null,
order: dataset.order || 0,
index: datasetIndex
};
}

Expand Down
29 changes: 11 additions & 18 deletions src/core/core.interaction.js
Expand Up @@ -25,17 +25,13 @@ function getRelativePosition(e, chart) {
* @param {function} handler - the callback to execute for each visible item
*/
function parseVisibleItems(chart, handler) {
var datasets = chart.data.datasets;
var meta, i, j, ilen, jlen;
var metasets = chart._getSortedVisibleDatasetMetas();
var metadata, i, j, ilen, jlen, element;

for (i = 0, ilen = datasets.length; i < ilen; ++i) {
if (!chart.isDatasetVisible(i)) {
continue;
}

meta = chart.getDatasetMeta(i);
for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
var element = meta.data[j];
for (i = 0, ilen = metasets.length; i < ilen; ++i) {
metadata = metasets[i].data;
for (j = 0, jlen = metadata.length; j < jlen; ++j) {
element = metadata[j];
if (!element._view.skip) {
handler(element);
}
Expand Down Expand Up @@ -120,15 +116,12 @@ function indexMode(chart, e, options) {
return [];
}

chart.data.datasets.forEach(function(dataset, datasetIndex) {
if (chart.isDatasetVisible(datasetIndex)) {
var meta = chart.getDatasetMeta(datasetIndex);
var element = meta.data[items[0]._index];
chart._getSortedVisibleDatasetMetas().forEach(function(meta) {
var element = meta.data[items[0]._index];

// don't count items that are skipped (null data)
if (element && !element._view.skip) {
elements.push(element);
}
// don't count items that are skipped (null data)
if (element && !element._view.skip) {
elements.push(element);
}
});

Expand Down

0 comments on commit 53a3eb2

Please sign in to comment.