kurkle / chartjs-chart-treemap

Chart.js module for creating treemap charts
MIT License
140 stars 34 forks source link

How to modify data labels? #92

Closed AlvinSartorTrityum closed 2 years ago

AlvinSartorTrityum commented 2 years ago

I have played with the treemap and got to this result: image

But what I aim at is this: image

Is is possible to modify the labels and set different sizes (maybe via a plugin?) Also, labels currently do not wrap (unless we wrap them manually by using arrays). You think this is something we have to stick with or it would be possible to also automatically wrap them somehow?

nullpaper commented 2 years ago

@kurkle would it be possible as OP requests to override implementations using some kind of plugin system? Writing up a custom label renderer would help solve this.

Re: https://github.com/kurkle/chartjs-chart-treemap/blob/next/src/controller.js#L136

stockiNail commented 2 years ago

@nullpaper I think, instead of referring to a plugin, the controller could expose the hooks which can be implemented.

  data: {
    datasets: [{
      ...
      captions: {
        display: true,
        draw(context) { 
          ....  // <-- custom drawing  
        } 
      },
      labels: {
        display: true,
        draw(context) {
          ....  // <-- custom drawing
        }
      }
    }]
  },

This kind of enhancement could open the door to many issues even if it could be the most flexible solution.

Maybe you could evaluate to invoke the scriptable options for color and font for each line of the label, and draw them accordingly with the result of the callback. This must be checked if doable (due to proxies) and could increase complexity on size calculation of the elements as well.

stockiNail commented 2 years ago

@AlvinSartorTrityum @nullpaper I had a look a bit to the implementation and I think this could be doable changing how the font and color are managed now for the labels, using a scriptable option. Now, activating a scriptable option, the font and color options must return a single instance. But we could change that, allowing to return an array of objects and the controller could apply the font and color at index of the provided array using the index of the label in the labels array.

 ....
  formatter: () => ['label1', 'label2', 'label3'],
  font: () => [{size: 24}, {size:14}],
  color: {} => ['white', 'grey'],
 ....

With above sample, we could have:

I have played a bit locally and sounds working. The calculation of the label point, where starting drawing, should be rewritten and it could be a breaking change.

@kurkle @LeeLenaleee what do you think? This could be also applicable to annotation plugin to address the enhancement https://github.com/chartjs/chartjs-plugin-annotation/issues/739

AlvinSartorTrityum commented 2 years ago

@stockiNail thanks for your answer! Having the possibility to use different formatting for the labels would be great, but it would not solve 100% of our problem.

The fact is that labels are often cut and that looks quite bad. Ideally, the labels should be removed altogether if the square is too small for them. If the user wanted to get more info, they could still mouse-hover and see the tooltip.

image

My company is asking to stop using this plugin because of the cut labels, while I am rooting to keep it, but I need this feature to allow that 🤓

stockiNail commented 2 years ago

Got it! I agree. Similar to #67. As soon as I have time, I'll have a look. Maybe that last one issue, could be addressable as the first one if you agree

stockiNail commented 2 years ago

And if you agree, I'd like to complete the PR #102 where the drawing is moved to the element to be more consistent. Some functions to draw the label are changed and maybe you can wait for a while. Furthermore I think it could helpful to add an additional option, clip, where the user can decide if he/she wants to cut the label or to show anyway overriding the other elements or automatically decide to omit the label is bigger then the box.

 ... clip: true ! false | 'auto'

That's my thought, thinking loud, but the community will decide if the feature could be added or not. ;)

stockiNail commented 2 years ago

@AlvinSartorTrityum I have created another issue about the labels visibility if the rectangle is too small.

EDIT: created PR #106

stockiNail commented 2 years ago

@AlvinSartorTrityum as you can see the issue has been closed. In next version of this controller, 2 features, related to this issue, will be released and they are:

  1. with new overflow option in labels configuration set to 'hidden', you can hide the label if the square is too small for it. Default is 'cut'.
  2. when the label to draw has multiple lines, you can use different font and color for each row of the label. This is enabled configuring an array of fonts or colors for those options. When the lines are more than the configured fonts or colors, the last configuration of those options is used for all remaining lines.
lrdj commented 2 years ago

Hello all, late to this and not a JS developer.

I'm having both issues identified by @AlvinSartorTrityum that is:

1) Labels are rendered in boxes too small to do so legibly 2) Labels do not accept line breaks (<br> or \n are removed in the standard code?) and do not wrap natively, always being rendered as one line that can be cropped by the edge of the box

My question is could the script hand the rendering back to the browser?

It seems like being able to handle this with CSS box specifications and font behaviours (which would also include right-to-left an unicode fonts etc.) would allow the tool to be used by nonscripters and would ultimately made the the rendering more robust.

Is that an idea?

All the best:-)

stockiNail commented 2 years ago
  1. Labels are rendered in boxes too small to do so legibly

@lrdj new overflow option in labels configuration set to 'hidden', you can hide the label if the square is too small for it. Default is 'cut'.

2. Labels do not accept line breaks (
or \n are removed in the standard code?) and do not wrap natively, always being rendered as one line that can be cropped by the edge of the box

@lrdj the controller doesn't split any label/string (with
or \n char) but you can provide an array of strings which will be drawn as multiline label.

It seems like being able to handle this with CSS box specifications and font behaviours (which would also include right-to-left an unicode fonts etc.)

We can have a look! Thanks!

lrdj commented 2 years ago
  1. Labels are rendered in boxes too small to do so legibly

@lrdj new overflow option in labels configuration set to 'hidden', you can hide the label if the square is too small for it. Default is 'cut'.

2. Labels do not accept line breaks (
or \n are removed in the standard code?) and do not wrap natively, always being rendered as one line that can be cropped by the edge of the box

@lrdj the controller doesn't split any label/string (with
or \n char) but you can provide an array of strings which will be drawn as multiline label.

It seems like being able to handle this with CSS box specifications and font behaviours (which would also include right-to-left an unicode fonts etc.)

We can have a look! Thanks!

Thanks for the response!

Could you point me to a code sample of using arrays to create a multi-line label?

Thanks again for all the work you do!

stockiNail commented 2 years ago

Have a look to the sample section: https://chartjs-chart-treemap.pages.dev/samples/labelsFontsAndColors.html

To have a multiline label, the formatter scriptable option must return an array.

          formatter(ctx) {
            if (ctx.type !== 'data') {
              return;
            }
            return [ctx.raw._data.what, 'Value is ' + ctx.raw.v];
          },