mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-04 07:24:02 +01:00
Fix title, body and footer alignment inside tooltip (#5925)
This commit is contained in:
committed by
Simon Brunel
parent
934ce241ae
commit
dd3564aee5
@@ -463,6 +463,14 @@ function getBackgroundPoint(vm, size, alignment, chart) {
|
||||
};
|
||||
}
|
||||
|
||||
function getAlignedX(vm, align) {
|
||||
return align === 'center'
|
||||
? vm.x + vm.width / 2
|
||||
: align === 'right'
|
||||
? vm.x + vm.width - vm.xPadding
|
||||
: vm.x + vm.xPadding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to build before and after body lines
|
||||
*/
|
||||
@@ -730,6 +738,8 @@ var exports = Element.extend({
|
||||
var title = vm.title;
|
||||
|
||||
if (title.length) {
|
||||
pt.x = getAlignedX(vm, vm._titleAlign);
|
||||
|
||||
ctx.textAlign = vm._titleAlign;
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
@@ -754,14 +764,21 @@ var exports = Element.extend({
|
||||
drawBody: function(pt, vm, ctx) {
|
||||
var bodyFontSize = vm.bodyFontSize;
|
||||
var bodySpacing = vm.bodySpacing;
|
||||
var bodyAlign = vm._bodyAlign;
|
||||
var body = vm.body;
|
||||
var drawColorBoxes = vm.displayColors;
|
||||
var labelColors = vm.labelColors;
|
||||
var xLinePadding = 0;
|
||||
var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0;
|
||||
var textColor;
|
||||
|
||||
ctx.textAlign = vm._bodyAlign;
|
||||
ctx.textAlign = bodyAlign;
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
|
||||
|
||||
pt.x = getAlignedX(vm, bodyAlign);
|
||||
|
||||
// Before Body
|
||||
var xLinePadding = 0;
|
||||
var fillLineOfText = function(line) {
|
||||
ctx.fillText(line, pt.x + xLinePadding, pt.y);
|
||||
pt.y += bodyFontSize + bodySpacing;
|
||||
@@ -771,12 +788,13 @@ var exports = Element.extend({
|
||||
ctx.fillStyle = vm.bodyFontColor;
|
||||
helpers.each(vm.beforeBody, fillLineOfText);
|
||||
|
||||
var drawColorBoxes = vm.displayColors;
|
||||
xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
|
||||
xLinePadding = drawColorBoxes && bodyAlign !== 'right'
|
||||
? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2)
|
||||
: 0;
|
||||
|
||||
// Draw body lines now
|
||||
helpers.each(body, function(bodyItem, i) {
|
||||
var textColor = vm.labelTextColors[i];
|
||||
textColor = vm.labelTextColors[i];
|
||||
ctx.fillStyle = textColor;
|
||||
helpers.each(bodyItem.before, fillLineOfText);
|
||||
|
||||
@@ -785,16 +803,16 @@ var exports = Element.extend({
|
||||
if (drawColorBoxes) {
|
||||
// Fill a white rect so that colours merge nicely if the opacity is < 1
|
||||
ctx.fillStyle = vm.legendColorBackground;
|
||||
ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
|
||||
ctx.fillRect(colorX, pt.y, bodyFontSize, bodyFontSize);
|
||||
|
||||
// Border
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = vm.labelColors[i].borderColor;
|
||||
ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
|
||||
ctx.strokeStyle = labelColors[i].borderColor;
|
||||
ctx.strokeRect(colorX, pt.y, bodyFontSize, bodyFontSize);
|
||||
|
||||
// Inner square
|
||||
ctx.fillStyle = vm.labelColors[i].backgroundColor;
|
||||
ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
|
||||
ctx.fillStyle = labelColors[i].backgroundColor;
|
||||
ctx.fillRect(colorX + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
|
||||
ctx.fillStyle = textColor;
|
||||
}
|
||||
|
||||
@@ -816,6 +834,7 @@ var exports = Element.extend({
|
||||
var footer = vm.footer;
|
||||
|
||||
if (footer.length) {
|
||||
pt.x = getAlignedX(vm, vm._footerAlign);
|
||||
pt.y += vm.footerMarginTop;
|
||||
|
||||
ctx.textAlign = vm._footerAlign;
|
||||
@@ -905,7 +924,6 @@ var exports = Element.extend({
|
||||
this.drawBackground(pt, vm, ctx, tooltipSize);
|
||||
|
||||
// Draw Title, Body, and Footer
|
||||
pt.x += vm.xPadding;
|
||||
pt.y += vm.yPadding;
|
||||
|
||||
// Titles
|
||||
|
||||
@@ -1099,4 +1099,174 @@ describe('Core.Tooltip', function() {
|
||||
}]
|
||||
}));
|
||||
});
|
||||
|
||||
describe('text align', function() {
|
||||
var globalDefaults = Chart.defaults.global;
|
||||
var makeView = function(title, body, footer) {
|
||||
return {
|
||||
// Positioning
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
xPadding: 5,
|
||||
yPadding: 5,
|
||||
xAlign: 'left',
|
||||
yAlign: 'top',
|
||||
|
||||
// Body
|
||||
bodyFontColor: '#fff',
|
||||
_bodyFontFamily: globalDefaults.defaultFontFamily,
|
||||
_bodyFontStyle: globalDefaults.defaultFontStyle,
|
||||
_bodyAlign: body,
|
||||
bodyFontSize: globalDefaults.defaultFontSize,
|
||||
bodySpacing: 2,
|
||||
|
||||
// Title
|
||||
titleFontColor: '#fff',
|
||||
_titleFontFamily: globalDefaults.defaultFontFamily,
|
||||
_titleFontStyle: 'bold',
|
||||
titleFontSize: globalDefaults.defaultFontSize,
|
||||
_titleAlign: title,
|
||||
titleSpacing: 2,
|
||||
titleMarginBottom: 6,
|
||||
|
||||
// Footer
|
||||
footerFontColor: '#fff',
|
||||
_footerFontFamily: globalDefaults.defaultFontFamily,
|
||||
_footerFontStyle: 'bold',
|
||||
footerFontSize: globalDefaults.defaultFontSize,
|
||||
_footerAlign: footer,
|
||||
footerSpacing: 2,
|
||||
footerMarginTop: 6,
|
||||
|
||||
// Appearance
|
||||
caretSize: 5,
|
||||
cornerRadius: 6,
|
||||
borderColor: '#aaa',
|
||||
borderWidth: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.8)',
|
||||
opacity: 1,
|
||||
legendColorBackground: '#fff',
|
||||
|
||||
// Text
|
||||
title: ['title'],
|
||||
beforeBody: [],
|
||||
body: [{
|
||||
before: [],
|
||||
lines: ['label'],
|
||||
after: []
|
||||
}],
|
||||
afterBody: [],
|
||||
footer: ['footer'],
|
||||
caretPadding: 2,
|
||||
labelTextColors: ['#fff'],
|
||||
labelColors: [{
|
||||
borderColor: 'rgb(255, 0, 0)',
|
||||
backgroundColor: 'rgb(0, 255, 0)'
|
||||
}, {
|
||||
borderColor: 'rgb(0, 0, 255)',
|
||||
backgroundColor: 'rgb(0, 255, 255)'
|
||||
}]
|
||||
};
|
||||
};
|
||||
var drawBody = [
|
||||
{name: 'save', args: []},
|
||||
{name: 'setFillStyle', args: ['rgba(0,0,0,0.8)']},
|
||||
{name: 'setStrokeStyle', args: ['#aaa']},
|
||||
{name: 'setLineWidth', args: [1]},
|
||||
{name: 'beginPath', args: []},
|
||||
{name: 'moveTo', args: [106, 100]},
|
||||
{name: 'lineTo', args: [106, 100]},
|
||||
{name: 'lineTo', args: [111, 95]},
|
||||
{name: 'lineTo', args: [116, 100]},
|
||||
{name: 'lineTo', args: [194, 100]},
|
||||
{name: 'quadraticCurveTo', args: [200, 100, 200, 106]},
|
||||
{name: 'lineTo', args: [200, 194]},
|
||||
{name: 'quadraticCurveTo', args: [200, 200, 194, 200]},
|
||||
{name: 'lineTo', args: [106, 200]},
|
||||
{name: 'quadraticCurveTo', args: [100, 200, 100, 194]},
|
||||
{name: 'lineTo', args: [100, 106]},
|
||||
{name: 'quadraticCurveTo', args: [100, 100, 106, 100]},
|
||||
{name: 'closePath', args: []},
|
||||
{name: 'fill', args: []},
|
||||
{name: 'stroke', args: []}
|
||||
];
|
||||
|
||||
var mockContext = window.createMockContext();
|
||||
var tooltip = new Chart.Tooltip({
|
||||
_options: globalDefaults.tooltips,
|
||||
_chart: {
|
||||
ctx: mockContext,
|
||||
}
|
||||
});
|
||||
|
||||
it('Should go left', function() {
|
||||
mockContext.resetCalls();
|
||||
tooltip._view = makeView('left', 'left', 'left');
|
||||
tooltip.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['title', 105, 105]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['label', 105, 123]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['footer', 105, 141]},
|
||||
{name: 'restore', args: []}
|
||||
]));
|
||||
});
|
||||
|
||||
it('Should go right', function() {
|
||||
mockContext.resetCalls();
|
||||
tooltip._view = makeView('right', 'right', 'right');
|
||||
tooltip.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['title', 195, 105]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['label', 195, 123]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['footer', 195, 141]},
|
||||
{name: 'restore', args: []}
|
||||
]));
|
||||
});
|
||||
|
||||
it('Should center', function() {
|
||||
mockContext.resetCalls();
|
||||
tooltip._view = makeView('center', 'center', 'center');
|
||||
tooltip.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['title', 150, 105]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['label', 150, 123]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['footer', 150, 141]},
|
||||
{name: 'restore', args: []}
|
||||
]));
|
||||
});
|
||||
|
||||
it('Should allow mixed', function() {
|
||||
mockContext.resetCalls();
|
||||
tooltip._view = makeView('right', 'center', 'left');
|
||||
tooltip.draw();
|
||||
|
||||
expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['title', 195, 105]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['label', 150, 123]},
|
||||
{name: 'setFillStyle', args: ['#fff']},
|
||||
{name: 'fillText', args: ['footer', 105, 141]},
|
||||
{name: 'restore', args: []}
|
||||
]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user