Closed pedromoto4 closed 1 month ago
Did you find a solution to this? I'm having the same issue.
@pedromoto4 @CSorel-Catalyte this issue originates from a lack of proper typings - the global chart.js's typings need to be augmented to accept the plugin's configuration. Migration to TS (which is going to happen in the near future) will solve this.
is this issue resolved?
The issue is not sorted as the TypeScript version is still not online. My way around this issue is like so:
import { ChartOptions } from "chart.js";
type RadarOptionsType = ChartOptions<"radar"> & {
plugins?: {
dragData?: {
round?: number;
showTooltip?: boolean;
onDragStart?: (e: MouseEvent, element: any) => boolean | void;
onDrag?: (
e: MouseEvent,
datasetIndex: number,
index: number,
value: number
) => boolean | void;
onDragEnd?: (e: MouseEvent) => void;
};
};
};
const options: RadarOptionsType = {
layout: {
// layout options here
},
scales: {
// scale options here
},
plugins: {
// other plugins options here
dragData: {
round: 0,
showTooltip: false,
onDragStart: (e: MouseEvent, element: any) => {
// onDragStart body here
},
onDrag: (e: MouseEvent, datasetIndex: number, index: number, value: number) => {
// onDrag body here
},
onDragEnd: (e: MouseEvent) {
// onDragEnd body here
},
},
},
// other options here
};
and then
import { Radar } from "react-chartjs-2";
import { Chart } from "chart.js";
import ChartDragDataPlugin from "@/app/plugins/ChartDragDataPlugin.js"; // let's call this a polyfill shall we?
Chart.register(
ChartDragDataPlugin
);
// you can add all the props that you use within your chart/project
type ChartDataType = {
labels: string[];
datasets: {
label: string;
data: number[];
backgroundColor?: string;
borderColor?: string;
pointBackgroundColor?: string;
pointBorderColor?: string;
pointRadius?: number;
borderWidth?: number;
pointBorderWidth?: number;
pointHoverRadius?: number;
fill?: boolean;
zIndex?: number;
datalabels?: {
display: boolean;
};
}[];
};
interface RadarProps {
data: ChartDataType;
options: RadarOptionsType;
}
<Radar data={data} options={options} />;
[!IMPORTANT] Mark that we import the JavaScript file here
and the polyfile file, which is just a copy of dragdata plugin source (but in my case limited only to radar, you can copy the rest of the code from here)
import { drag } from "d3-drag";
import { select } from "d3-selection";
let element, rAxisID, type, eventSettings;
let isDragging = false;
function getSafe(func) {
try {
return func();
} catch (e) {
return "";
}
}
const getElement = (e, chartInstance, callback) => {
element = chartInstance.getElementsAtEventForMode(
e,
"nearest",
{ intersect: true },
false
)[0];
type = chartInstance.config.type;
if (element && type === "radar") {
let datasetIndex = element.datasetIndex;
let index = element.index;
eventSettings = getSafe(
() => chartInstance.config.options.plugins.tooltip.animation
);
const datasetMeta = chartInstance.getDatasetMeta(datasetIndex);
rAxisID = datasetMeta.rAxisID;
if (
chartInstance.config.options.scales[rAxisID] &&
chartInstance.config.options.scales[rAxisID].dragData === false
) {
element = null;
return;
}
if (
chartInstance.config.options.plugins.dragData.showTooltip ===
undefined ||
chartInstance.config.options.plugins.dragData.showTooltip
) {
if (!chartInstance.config.options.plugins.tooltip)
chartInstance.config.options.plugins.tooltip = {};
chartInstance.config.options.plugins.tooltip.animation = false;
}
if (typeof callback === "function" && element) {
if (callback(e, datasetIndex, index) === false) {
element = null;
}
}
}
};
function roundValue(value, pos) {
if (!isNaN(pos) && pos >= 0) {
return Math.round(value * Math.pow(10, pos)) / Math.pow(10, pos);
}
return value;
}
function calcRadar(e, chartInstance) {
let x, y, v;
if (e.touches) {
x =
e.touches[0].clientX -
chartInstance.canvas.getBoundingClientRect().left;
y =
e.touches[0].clientY -
chartInstance.canvas.getBoundingClientRect().top;
} else {
x = e.clientX - chartInstance.canvas.getBoundingClientRect().left;
y = e.clientY - chartInstance.canvas.getBoundingClientRect().top;
}
let rScale = chartInstance.scales[rAxisID];
let d = Math.sqrt(
Math.pow(x - rScale.xCenter, 2) + Math.pow(y - rScale.yCenter, 2)
);
let scalingFactor = rScale.drawingArea / (rScale.max - rScale.min);
if (rScale.options.ticks.reverse) {
v = rScale.max - d / scalingFactor;
} else {
v = rScale.min + d / scalingFactor;
}
v = roundValue(v, chartInstance.config.options.plugins.dragData.round);
v = v > rScale.max ? rScale.max : v;
v = v < rScale.min ? rScale.min : v;
return v;
}
const updateData = (e, chartInstance, pluginOptions, callback) => {
if (element) {
let datasetIndex = element.datasetIndex;
let index = element.index;
isDragging = true;
let dataPoint = calcRadar(e, chartInstance);
if (
!callback ||
(typeof callback === "function" &&
callback(e, datasetIndex, index, dataPoint) !== false)
) {
chartInstance.data.datasets[datasetIndex].data[index] = dataPoint;
chartInstance.update("none");
}
}
};
const dragEndCallback = (e, chartInstance, callback) => {
isDragging = false;
if (chartInstance.config.options.plugins.tooltip) {
chartInstance.config.options.plugins.tooltip.animation = eventSettings;
chartInstance.update("none");
}
if (typeof callback === "function" && element) {
const datasetIndex = element.datasetIndex;
const index = element.index;
let value = chartInstance.data.datasets[datasetIndex].data[index];
return callback(e, datasetIndex, index, value);
}
};
const ChartDragDataPlugin = {
id: "dragdata",
afterInit: function (chartInstance) {
if (
chartInstance.config.options.plugins &&
chartInstance.config.options.plugins.dragData
) {
const pluginOptions = chartInstance.config.options.plugins.dragData;
select(chartInstance.canvas).call(
drag()
.container(chartInstance.canvas)
.on("start", (e) =>
getElement(
e.sourceEvent,
chartInstance,
pluginOptions.onDragStart
)
)
.on("drag", (e) =>
updateData(
e.sourceEvent,
chartInstance,
pluginOptions,
pluginOptions.onDrag
)
)
.on("end", (e) =>
dragEndCallback(
e.sourceEvent,
chartInstance,
pluginOptions.onDragEnd
)
)
);
}
},
beforeEvent: function (chart) {
if (isDragging) {
if (chart.tooltip) chart.tooltip.update();
return false;
}
},
};
export default ChartDragDataPlugin;
Mark that you also need to update tsconfig.json
file
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"app/plugins/ChartDragDataPlugin.js"
],
The profit? Well you actually don't need to use this plugin at all, we just created a new one with types The downside? It's ugly and it stinks...
Describe the bug I cant add this plugin data in the ChartOptions structure I receive this error. I'm not sure if im doing this the right way but I hope anyone can help me, the true is that to angular 15 with this plugin I cant find any example :/
this is my package "@angular/animations": "^15.2.0", "@angular/common": "^15.2.0", "@angular/compiler": "^15.2.0", "@angular/core": "^15.2.0", "@angular/forms": "^15.2.0", "@angular/platform-browser": "^15.2.0", "@angular/platform-browser-dynamic": "^15.2.0", "@angular/router": "^15.2.0", "chart.js": "^3.9.1", "chartjs-plugin-dragdata": "^2.2.5", "ng2-charts": "^4.1.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.12.0" the html <canvas baseChart width="400" height="400" [type]="'line'" [data]="lineChartData" [options]="lineChartOptions" [legend]="lineChartLegend">
xyz.component.ts import { Component } from '@angular/core'; import { ChartConfiguration, ChartOptions, ChartType } from "chart.js"; import 'chartjs-plugin-dragdata' ;
@Component({ selector: 'app-xyz', templateUrl: './xyz.component.html', styleUrls: ['./xyz.component.scss'] }) export class XyzComponent {
title = 'ng2-charts-demo';
public lineChartData: ChartConfiguration<'line'>['data'] = { labels: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July' ], datasets: [ { data: [ 65, 59, 80, 81, 56, 55, 40 ], label: 'Series A', fill: true, tension: 0.5, borderColor: 'black', backgroundColor: 'rgba(255,0,0,0.3)', } ] };
public lineChartOptions: ChartOptions<'line'> = { responsive: false, plugins:{ dragData: { round: 1, showTooltip: true, onDragStart: function(e) { // console.log(e) }, onDrag: function(e, datasetIndex, index, value) {
e.target.style.cursor = 'grabbing' if (value < 0) return false // console.log(e, datasetIndex, index, value) }, onDragEnd: function(e, datasetIndex, index, value) { e.target.style.cursor = 'default' // console.log(datasetIndex, index, value) }, } } }; public lineChartLegend = true;
constructor( ){ } }