Skip to content

Commit

Permalink
Implement legend.align: 'start', 'center', 'end' (chartjs#6141)
Browse files Browse the repository at this point in the history
New `options.legend.align`config option for controlling alignment of legend blocks in horizontal/vertical legends.
  • Loading branch information
dkichler authored and simonbrunel committed Mar 23, 2019
1 parent d078119 commit 2533cc0
Show file tree
Hide file tree
Showing 39 changed files with 491 additions and 367 deletions.
9 changes: 9 additions & 0 deletions docs/configuration/legend.md
Expand Up @@ -9,6 +9,7 @@ The legend configuration is passed into the `options.legend` namespace. The glob
| ---- | ---- | ------- | -----------
| `display` | `boolean` | `true` | Is the legend shown?
| `position` | `string` | `'top'` | Position of the legend. [more...](#position)
| `align` | `string` | `'center'` | Alignment of the legend. [more...](#align)
| `fullWidth` | `boolean` | `true` | Marks that this box should take the full width of the canvas (pushing down other boxes). This is unlikely to need to be changed in day-to-day use.
| `onClick` | `function` | | A callback that is called when a click event is registered on a label item.
| `onHover` | `function` | | A callback that is called when a 'mousemove' event is registered on top of a label item.
Expand All @@ -23,6 +24,14 @@ Position of the legend. Options are:
* `'bottom'`
* `'right'`

## Align
Alignment of the legend. Options are:
* `'start'`
* `'center'`
* `'end'`

Defaults to `'center'` for unrecognized values.

## Legend Label Configuration

The legend label configuration is nested below the legend configuration using the `labels` key.
Expand Down
45 changes: 30 additions & 15 deletions src/plugins/plugin.legend.js
Expand Up @@ -12,6 +12,7 @@ defaults._set('global', {
legend: {
display: true,
position: 'top',
align: 'center',
fullWidth: true,
reverse: false,
weight: 1000,
Expand Down Expand Up @@ -102,18 +103,19 @@ function getBoxWidth(labelOpts, fontSize) {
var Legend = Element.extend({

initialize: function(config) {
helpers.extend(this, config);
var me = this;
helpers.extend(me, config);

// Contains hit boxes for each dataset (in dataset order)
this.legendHitBoxes = [];
me.legendHitBoxes = [];

/**
* @private
*/
this._hoveredItem = null;
me._hoveredItem = null;

// Are we in doughnut mode which has a different data type
this.doughnutMode = false;
me.doughnutMode = false;
},

// These methods are ordered by lifecycle. Utilities then follow.
Expand Down Expand Up @@ -253,9 +255,9 @@ var Legend = Element.extend({
var boxWidth = getBoxWidth(labelOpts, fontSize);
var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

if (i === 0 || lineWidths[lineWidths.length - 1] + width + labelOpts.padding > minSize.width) {
if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) {
totalHeight += fontSize + labelOpts.padding;
lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = labelOpts.padding;
lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
}

// Store the hitbox width and height here. Final position will be updated in `draw`
Expand All @@ -274,27 +276,27 @@ var Legend = Element.extend({
} else {
var vPadding = labelOpts.padding;
var columnWidths = me.columnWidths = [];
var columnHeights = me.columnHeights = [];
var totalWidth = labelOpts.padding;
var currentColWidth = 0;
var currentColHeight = 0;
var itemHeight = fontSize + vPadding;

helpers.each(me.legendItems, function(legendItem, i) {
var boxWidth = getBoxWidth(labelOpts, fontSize);
var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

// If too tall, go to new column
if (i > 0 && currentColHeight + itemHeight > minSize.height - vPadding) {
if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) {
totalWidth += currentColWidth + labelOpts.padding;
columnWidths.push(currentColWidth); // previous column width

columnHeights.push(currentColHeight);
currentColWidth = 0;
currentColHeight = 0;
}

// Get max width
currentColWidth = Math.max(currentColWidth, itemWidth);
currentColHeight += itemHeight;
currentColHeight += fontSize + vPadding;

// Store the hitbox width and height here. Final position will be updated in `draw`
hitboxes[i] = {
Expand All @@ -307,6 +309,7 @@ var Legend = Element.extend({

totalWidth += currentColWidth;
columnWidths.push(currentColWidth);
columnHeights.push(currentColHeight);
minSize.width += totalWidth;
}
}
Expand All @@ -329,6 +332,8 @@ var Legend = Element.extend({
var globalDefaults = defaults.global;
var defaultColor = globalDefaults.defaultColor;
var lineDefault = globalDefaults.elements.line;
var legendHeight = me.height;
var columnHeights = me.columnHeights;
var legendWidth = me.width;
var lineWidths = me.lineWidths;

Expand Down Expand Up @@ -408,18 +413,29 @@ var Legend = Element.extend({
}
};

var alignmentOffset = function(dimension, blockSize) {
switch (opts.align) {
case 'start':
return labelOpts.padding;
case 'end':
return dimension - blockSize;
default: // center
return (dimension - blockSize + labelOpts.padding) / 2;
}
};

// Horizontal
var isHorizontal = me.isHorizontal();
if (isHorizontal) {
cursor = {
x: me.left + ((legendWidth - lineWidths[0]) / 2) + labelOpts.padding,
x: me.left + alignmentOffset(legendWidth, lineWidths[0]),
y: me.top + labelOpts.padding,
line: 0
};
} else {
cursor = {
x: me.left + labelOpts.padding,
y: me.top + labelOpts.padding,
y: me.top + alignmentOffset(legendHeight, columnHeights[0]),
line: 0
};
}
Expand All @@ -438,12 +454,12 @@ var Legend = Element.extend({
if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) {
y = cursor.y += itemHeight;
cursor.line++;
x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2) + labelOpts.padding;
x = cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]);
}
} else if (i > 0 && y + itemHeight > me.top + me.minSize.height) {
x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
y = cursor.y = me.top + labelOpts.padding;
cursor.line++;
y = cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]);
}

drawLegendBox(x, y, legendItem);
Expand All @@ -459,7 +475,6 @@ var Legend = Element.extend({
} else {
cursor.y += itemHeight;
}

});
}
},
Expand Down
@@ -0,0 +1,25 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"datasets": [{
"data": [10, 20, 30, 40, 50, 60, 70, 10, 20, 30, 40, 50, 60, 70, 10, 20, 30, 20, 10],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "bottom",
"align": "center"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,25 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": [""],
"datasets": [{
"data": [10],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "bottom",
"align": "center"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,25 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"datasets": [{
"data": [10, 20, 30, 40, 50, 60, 70, 10, 20, 30, 40, 50, 60, 70, 10],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "bottom",
"align": "end"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,25 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"datasets": [{
"data": [10, 20, 30, 40, 50, 60, 70, 10, 20, 30, 40, 50, 60, 70, 10],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "bottom",
"align": "start"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,25 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"datasets": [{
"data": [10, 20, 30, 40, 50, 60, 70, 10, 20, 30, 40, 50, 60, 70, 10, 20, 30],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "left",
"align": "center"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,25 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": [""],
"datasets": [{
"data": [10],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "left",
"align": "center"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,24 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["", "", "", "", "", ""],
"datasets": [{
"data": [10, 20, 30, 40, 50],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "left"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,25 @@
{
"config": {
"type": "doughnut",
"data": {
"labels": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"datasets": [{
"data": [10, 20, 30, 40, 50, 60, 70, 10, 20, 30, 40, 50, 60, 70, 10, 20, 30],
"backgroundColor": "#00ff00",
"borderWidth": 0
}]
},
"options": {
"legend": {
"position": "left",
"align": "end"
}
}
},
"options": {
"canvas": {
"height": 256,
"width": 512
}
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2533cc0

Please sign in to comment.