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

Non-focused dataset state #10779

Open
alex-statsig opened this issue Oct 10, 2022 · 2 comments
Open

Non-focused dataset state #10779

alex-statsig opened this issue Oct 10, 2022 · 2 comments

Comments

@alex-statsig
Copy link

Feature Proposal

It would be nice if hover dataset mode could support an "unfocused" state for the non-hovered items (rather than just a "hovered" state for the hovered one). In particular, it's much easier to dim every other dataset than to somehow draw extra attention to the hovered one. See the example screenshot below.

Screen Shot 2022-10-10 at 11 57 27 AM

It would also be nice if this could work in conjunction with hover.mode="index". In my case, I'd like to highlight the data points for the hovered index, and also dim out other datasets. Currently, using mode="dataset" will cause every point to be hovered. But using mode="index" allows no customization of the hovered dataset (only point).

Possible Implementation

I hacked together a plugin which uses getElementsAtEventForMode to find the active dataset, and modifies the background / border colors to dim out non-focused ones. However, it's hard to make this generic (supporting arrays, functions, values for borderColor/ backgroundColor, etc.). It also feels very hacky. I store the original background color, but I don't think it would work well if they change the color of the datasets themselves.

Essentially in afterEvent, I do something like this:

const items = chart.getElementsAtEventForMode(
            args.event.native,
            'dataset',
            { intersect: true },
            true,
          );
          let hoveredIndex = -1;
          if (items.length > 0) {
            hoveredIndex = items[0].datasetIndex;
          }

          if (hoveredIndex !== this.hoveredIndex) {
            console.log('Change to ', hoveredIndex);
            this.hoveredIndex = hoveredIndex;

            let idx = 0;
            for (const dataset of chart.data.datasets) {
              if (dataset._backgroundColor == null) {
                dataset._backgroundColor = dataset.backgroundColor;
                dataset._borderColor = dataset.borderColor;
              }
              if (idx === hoveredIndex || hoveredIndex === -1) {
                dataset.backgroundColor = dataset._backgroundColor;
                dataset.borderColor = dataset._borderColor;
              } else {
                if (dataset._backgroundColor != null) {
                  if (Array.isArray(dataset._backgroundColor)) {
                    dataset.backgroundColor = dataset._backgroundColor.map(
                      (c) => alpha(c, 0.2),
                    );
                  } else {
                    dataset.backgroundColor = alpha(
                      dataset._backgroundColor,
                      0.2,
                    );
                  }
                }
                if (dataset._borderColor != null) {
                  if (Array.isArray(dataset._borderColor)) {
                    dataset.borderColor = dataset._borderColor.map((c) =>
                      alpha(c, 0.2),
                    );
                  } else {
                    dataset.borderColor = alpha(dataset._borderColor, 0.2);
                  }
                }
              }

              idx++;
            }
            args.changed = true;
            this.chart.update();
            this.chart.draw();
          }
@indhug19
Copy link

indhug19 commented Nov 25, 2022

for doughnut chart with single dataset

  afterDatasetsDraw(chart, args, pluginOptions){
    const {ctx, data, options, _active, chartArea: { top, bottom, left, right, width, height}, scales: {x, y}} = chart
    ctx.save()
    const dataset  = data.datasets[0]
    if (dataset._backgroundColor == null) {
      dataset._backgroundColor = dataset.backgroundColor
    }
  if(!_active.length){
    dataset.backgroundColor = dataset?._backgroundColor
  }

  if(_active.length) {
    const datasetIndexValue = _active[0].datasetIndex
    const dataIndexValue = _active[0].index
    const dataPoint = data.datasets[datasetIndexValue].data[dataIndexValue]
    dataset.backgroundColor = dataset?._backgroundColor.map((c, index) => 
      (index === dataIndexValue) ? c : alpha(c, 0.2)
    )
  }
  args.changed = true;
  chart.update();
},`

@alex-statsig
Copy link
Author

One other way this could be supported is using scriptable properties, i.e. borderColor: (ctx) => ctx.hovered ? regularColor : dimmedColor

There is something which almost works to support this today, which is ctx.active. There are two issues though:

  1. You cannot detect if something else is active (in my case, I want to dim the non-active ones only if something else is active, ex. ctx.isActive || !ctx.hasActive ? regularColor : dimmedColor
  2. It requires configuring the hover mode accordingly (i.e. mode: 'dataset'), which is an annoying coupling for me (I am using index mode currently for other purposes, such as tooltip)

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

No branches or pull requests

2 participants