Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Add more intelligent xAlign for tooltip #5728

Closed
AlexDel opened this issue Sep 13, 2018 · 2 comments · Fixed by #8646
Closed

[FEATURE] Add more intelligent xAlign for tooltip #5728

AlexDel opened this issue Sep 13, 2018 · 2 comments · Fixed by #8646

Comments

@AlexDel
Copy link

AlexDel commented Sep 13, 2018

Currently xAlign property for tooltip has three different variables: left, right and center. But such realisation is not very flexible and gives little control on tooltip allignment.

Current Behavior

Currently with this config I can make tooltip to align automatically:

tooltips: { mode: 'index', intersect: false, yAlign: 'center', ... }
But when tooltip as xAlign-ed to center it covers the current data points beneath it so they are not visible. It's not very handy, because I want to see the points position that I have selected.
vmnclq 1

Current options for xAlign - left and right stick the position of the tooltip, but it becomes cropped if hovered on left- or right-most parts of the canvas.

Expected Behavior

It would be better if tooltip had more adjustable alignment facility so that selected points were not hidden by the tooltip and were positioned whether to right or to left from the selected data points if area of chart allows it.

Possible Solution

The more or less working solution I've found so far is this on SO . But those guys suggest total copy-paste from chart.js tooltip source and monkey-patch it through plugin system, which needs a lot code duplication - only to alter the determineAlignment function I need to fetch all tooltip's code.

Possible solutions can be

  1. Allow to modify determineAlignment function with chart.js options API
  2. Adding additional xAlign variants like floatLeft and floatRight to suggest the tooltip on how to align oneself, and if it's not possible to align left or rigth without cropping it - than alignment goes on auto mode (as it's now)

Any comments are valueable, thank you!

@AlexDel AlexDel changed the title Add more intelligent xAlign for tooltip [FEATURE] Add more intelligent xAlign for tooltip Sep 18, 2018
@AlexDel
Copy link
Author

AlexDel commented Oct 4, 2018

Please relate if this feature could be useful - I can contribute and make a PR, but I want to be sure I am not the only guy who finds this feature reasonable or at least not a fiddle-faddle :)

@mysuf
Copy link

mysuf commented Apr 25, 2019

You are not the only guy. The way how modules are written is not flexible at all. There is number of scoped functions that cannot be used or overriden from outside. This is fast (not bullet-proof) monkey-patch to override two-line function fillLineOfText . :(

function getAlignedX(vm, align) {
	return align === 'center'
		? vm.x + vm.width / 2
		: align === 'right'
			? vm.x + vm.width - vm.xPadding
			: vm.x + vm.xPadding;
}

Chart.Tooltip.prototype.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 = bodyAlign;
  ctx.textBaseline = 'top';
  ctx.font = Chart.helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);

  pt.x = getAlignedX(vm, bodyAlign);

  // Before Body
  var fillLineOfText = function(line) {
    let cols = line.split(':');
    if (bodyAlign == 'left' && cols.length > 1) {
      line = cols.slice(0, cols.length-1).join(':') + ':';
      let rightText = cols.slice(-1);
      ctx.textAlign = 'right';
      let x = vm.x + vm.width - vm.xPadding;
      ctx.fillText(rightText, x, pt.y);
      ctx.textAlign = 'left';
    }
    ctx.fillText(line, pt.x + xLinePadding, pt.y);
    pt.y += bodyFontSize + bodySpacing;
  };

  // Before body lines
  ctx.fillStyle = vm.bodyFontColor;
  Chart.helpers.each(vm.beforeBody, fillLineOfText);

  xLinePadding = drawColorBoxes && bodyAlign !== 'right'
    ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2)
  : 0;

  // Draw body lines now
  Chart.helpers.each(body, function(bodyItem, i) {
    textColor = vm.labelTextColors[i];
    ctx.fillStyle = textColor;
    Chart.helpers.each(bodyItem.before, fillLineOfText);

    Chart.helpers.each(bodyItem.lines, function(line) {
      // Draw Legend-like boxes if needed
      if (drawColorBoxes) {
        // Fill a white rect so that colours merge nicely if the opacity is < 1
        ctx.fillStyle = vm.legendColorBackground;
        ctx.fillRect(colorX, pt.y, bodyFontSize, bodyFontSize);

        // Border
        ctx.lineWidth = 1;
        ctx.strokeStyle = labelColors[i].borderColor;
        ctx.strokeRect(colorX, pt.y, bodyFontSize, bodyFontSize);

        // Inner square
        ctx.fillStyle = labelColors[i].backgroundColor;
        ctx.fillRect(colorX + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
        ctx.fillStyle = textColor;
      }

      fillLineOfText(line);
    });

    Chart.helpers.each(bodyItem.after, fillLineOfText);
  });

  // Reset back to 0 for after body
  xLinePadding = 0;

  // After body lines
  Chart.helpers.each(vm.afterBody, fillLineOfText);
  pt.y -= bodySpacing; // Remove last body spacing
}

Fiddle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants