-
Notifications
You must be signed in to change notification settings - Fork 12k
Description
I want to enable interaction with the axis labels. Clicking the axis label would enable a contextual menu with the ability to change the sorting order, aggregation type on measures, or change the way we present dates (e.g. original, by week, by month, by quarter). It is a very nice way of changing the presentation in a nice approachable way.
ChartJS does not support events on ticks and axis labels out of the box (it does on data points) so I have been looking on the best way to accomplish this. The chart object exposes a number of elements that can help. The chart.scales property provides access to labels, ticks, measurements and coordinates.
I want to separate the axis label, from the axis ticks. This enables me to position a regular DOM element on top of the rendered axis label that can also provide hover effects to emphasize to users that they can interact with the label to show a menu.
I have the rough basis ready, it's not rocket science but before I move on I wanted to validate with the community if someone else already build something similar that I can reuse, perhaps ChartJS has already hidden endpoints/extension points?
Either way, for others to use or learn, the rough basis I stitched together. I haven't yet checked what happens if the position was not explicitly defined on the axis etc. so the code is full of assumptions and possible unhandled weaknesses.
chartWrapper.addEventListener('click', () => {
let parentElementStyle = window.getComputedStyle(chartWrapper.firstElementChild);
let paddingTop = parseInt(parentElementStyle.paddingTop);
let paddingLeft = parseInt(parentElementStyle.paddingLeft);
let marginTop = parseInt(parentElementStyle.marginTop);
let marginLeft = parseInt(parentElementStyle.marginLeft);
var allScales = visComp.visualization.chart.config.options.scales.yAxes.concat(visComp.visualization.chart.config.options.scales.xAxes);
for (var i = 0, j = allScales.length; i < j; i++) {
var scale = allScales[i];
var axisInfo = visComp.visualization.chart.scales[scale.id];
var tickWidth = axisInfo._labelSizes['widest'].width;
var ticksX;
var rect = document.createElement('div');
rect.style.position = 'absolute';
switch (scale.position) {
case 'left':
ticksX = axisInfo._labelItems[0].x - tickWidth;
rect.style.top = axisInfo.top + paddingTop + marginTop + 'px';
rect.style.left = axisInfo.left + paddingLeft + marginLeft + 'px';
rect.style.width = axisInfo.right - ticksX + 'px';
rect.style.height = axisInfo.height + 'px';
break;
case 'right':
ticksX = axisInfo._labelItems[0].x + tickWidth;
rect.style.top = axisInfo.top + paddingTop + marginTop + 'px';
rect.style.left = axisInfo.right - tickWidth + paddingLeft + marginLeft + 'px';
rect.style.width = axisInfo.right - ticksX + 'px';
rect.style.height = axisInfo.height + 'px';
break;
case 'bottom':
console.log(axisInfo);
tickHeight = axisInfo._labelSizes['highest'].height;
rect.style.top = parseInt(axisInfo.top + axisInfo.paddingTop + paddingTop + marginTop + tickHeight) + 'px';
rect.style.left = (axisInfo.left - axisInfo.paddingLeft) + paddingLeft + marginLeft + 'px';
rect.style.width = axisInfo.maxWidth + 'px';
rect.style.height = parseInt(axisInfo.height - tickHeight) + 'px';
break;
case 'top':
tickYCoord = axisInfo._labelItems[0].y;
rect.style.top = axisInfo.top + paddingTop + marginTop + 'px';
rect.style.left = (axisInfo.left - axisInfo.paddingLeft) + paddingLeft + marginLeft + 'px';
rect.style.width = axisInfo.maxWidth + 'px';
rect.style.height = tickYCoord + 'px' ;
break;
}
rect.style.border = '1px solid red';
rect.style.zIndex = '99999';
//rect.style.transformOrigin = "top left";
//rect.style.transform = 'rotate(-270deg)';
//rect.innerHTML = scale.id;
rect.addEventListener('contextmenu', ()=>{alert('show the menu')});
chartWrapper.appendChild(rect);
}
});
And it will look like this in my pet project.
