1👍
Note: This answer implementation doesn’t implement strikethrough. Strikethrough could be implemented by putting unicode character
\u0366
between each character of the label string. Here’s an example how do this with Javascript. The reason I’m not showcasing this here, is because it didn’t really look that great when I tested it on codesandbox.
In a newer version of chart.js
radial scale point label positions were exposed. In the example below I’m using chart.js
version 3.2.0
and react-chartjs-2
version 3.0.3
.
We can use the bounding box of each label and the clicked position to determine if we’ve clicked on a label.
I’ve used a ref
on the chart to get access to the label data.
I’ve chosen to set the data value corresponding to a label to 0
. I do this, because if you were to remove an element corresponding to a label, the label would disappear along with it. My choice probably makes more sense if you see it in action in the demo below.
const swapPreviousCurrent = (data) => {
const temp = data.currentValue;
data.currentValue = data.previousValue;
data.previousValue = temp;
};
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: props.chartData
};
this.radarRef = {};
this.labelsStrikeThrough = props.chartData.datasets.map((dataset) => {
return dataset.data.map((d, dataIndex) => {
return {
data: {
previousValue: 0,
currentValue: d
},
label: {
previousValue: `${props.chartData.labels[dataIndex]} (x)`,
currentValue: props.chartData.labels[dataIndex]
}
};
});
});
}
render() {
return (
<div className="chart">
<Radar
ref={(radarRef) => (this.radarRef = radarRef)}
data={this.state.chartData}
options={{
title: {
display: true,
text: "Testing",
fontSize: 25
},
legend: {
display: true,
position: "right"
}
}}
getElementAtEvent={(element, event) => {
const clickX = event.nativeEvent.offsetX;
const clickY = event.nativeEvent.offsetY;
const scale = this.radarRef.scales.r;
const pointLabelItems = scale._pointLabelItems;
pointLabelItems.forEach((pointLabelItem, index) => {
if (
clickX >= pointLabelItem.left &&
clickX <= pointLabelItem.right &&
clickY >= pointLabelItem.top &&
clickY <= pointLabelItem.bottom
) {
// We've clicked inside the bounding box, swap labels and label data for each dataset
this.radarRef.data.datasets.forEach((dataset, datasetIndex) => {
swapPreviousCurrent(
this.labelsStrikeThrough[datasetIndex][index].data
);
swapPreviousCurrent(
this.labelsStrikeThrough[datasetIndex][index].label
);
this.radarRef.data.datasets[datasetIndex].data[
index
] = this.labelsStrikeThrough[datasetIndex][
index
].data.previousValue;
this.radarRef.data.labels[index] = this.labelsStrikeThrough[
datasetIndex
][index].label.previousValue;
});
// labels and data have been changed, update the graph
this.radarRef.update();
}
});
}}
/>
</div>
);
}
}
So I use the ref
on the chart to get acces to the label positions and I use the event
of getElementAtEvent
to get the clicked x
and y
positions using event.nativeEvent.offsetX
and event.nativeEvent.offsetY
.
When we’ve clicked on the label I’ve chosen to update the value of the ref
and swap the label data value between 0
and its actual value. I swap the label itself between itself and itself concatenated with '(x)'
.
The reason I’m not using state here is because I don’t want to rerender the chart when the label data updates.
0👍
You could run a function that modifies your dataset:
You would create the function where you have your data set
chartClick(index) {
console.log(index);
//this.setState({}) Modify your datasets properties
}
Pass the function as props
<Chart chartClick={this.chartClick} chartData={this.state.chartData} />
Execute the function when clicked
onClick: (e, element) => {
if (element.length) {
this.props.chartClick(element[0]._datasetIndex);
}
}