Draggable Annotations don't work in React
PaulSender opened this issue · comments
Paul Sender commented
I've set up my chart to mimic the configuration on this page.
element
never gets updated even if I put it in a state variable
Here's the code for reference.
const dragger = {
id: 'dragger',
beforeEvent(chart, args, options) {
console.log(element, _ele); // logs undefined always
if (handleDrag(args.event)) {
args.changed = true;
return;
}
},
};
const [element, setElement] = useState<any>();
const [lastEvent, setLastEvent] = useState<any>();
let _ele;
const drag = function (moveX, moveY) {
element.x += moveX;
element.y += moveY;
element.x2 += moveX;
element.y2 += moveY;
element.centerX += moveX;
element.centerY += moveY;
if (element.elements && element.elements.length) {
for (const subEl of element.elements) {
subEl.x += moveX;
subEl.y += moveY;
subEl.x2 += moveX;
subEl.y2 += moveY;
subEl.centerX += moveX;
subEl.centerY += moveY;
subEl.bX += moveX;
subEl.bY += moveY;
}
}
};
const handleElementDragging = function (event) {
console.log('dragging'); // never logs
if (!lastEvent || !element) {
return;
}
const moveX = event.x - lastEvent.x;
const moveY = event.y - lastEvent.y;
drag(moveX, moveY);
setLastEvent(event);
return true;
};
const handleDrag = function (event) {
console.log(element, _ele); // logs undefined always
if (element) {
switch (event.type) {
case 'mousemove':
return handleElementDragging(event);
case 'mouseout':
case 'mouseup':
setLastEvent(undefined);
break;
case 'mousedown':
setLastEvent(event);
break;
default:
}
}
};
<_Chart
type="bar"
data={chartData}
plugins={[dragger]}
options={{
events: ['mousedown', 'mouseup', 'mousemove', 'mouseout'],
scales: {
y: {
beginAtZero: true,
min: 0,
max: 100,
},
},
plugins: {
annotation: {
enter(ctx) {
setElement(ctx.element);
_ele = ctx.element;
console.log(_ele); // logs correctly here
},
leave() {
setElement(undefined);
setLastEvent(undefined);
},
annotations: {
annotation1: {
type: 'label',
backgroundColor: 'rgba(255, 99, 132, 0.25)',
borderWidth: 3,
borderColor: 'black',
content: ['Label annotation', 'to drag'],
callout: {
display: true,
borderColor: 'black',
},
xValue: 1,
yValue: 40,
},
},
},
},
}}
/>
stockiNail commented
@PaulSender thank you for the issue. I'm not so expert on React. Can you prepare a codesandbox with the sample in order to reproduce the issue?
Tristan Duck commented
Did you ever fix this?
Gregorha commented
You can use Ref to fix this problems with external mutations
To setup the variable you can do with use ref const elementRef = useRef();
Updates to values can be done with elementRef.current =
.
The entire code will look like this:
const elementRef = useRef<AnnotationElement | undefined>();
const lastEventRef = useRef<AnnotationEvents | undefined>();
const drag = function (moveX: number, moveY: number) {
elementRef.current.x += moveX;
elementRef.current.y += moveY;
elementRef.current.x2 += moveX;
elementRef.current.y2 += moveY;
elementRef.current.centerX += moveX;
elementRef.current.centerY += moveY;
if (elementRef.current.elements && elementRef.current.elements.length) {
for (const subEl of elementRef.current.elements) {
subEl.x += moveX;
subEl.y += moveY;
subEl.x2 += moveX;
subEl.y2 += moveY;
subEl.centerX += moveX;
subEl.centerY += moveY;
subEl.bX += moveX;
subEl.bY += moveY;
}
}
};
const handleElementDragging = function (event) {
if (!lastEventRef.current || !elementRef.current) {
return;
}
const moveX = event.x - lastEventRef.current.x;
const moveY = event.y - lastEventRef.current.y;
drag(moveX, moveY);
lastEventRef.current = event;
return true;
};
const handleDrag = function (event) {
if (elementRef.current && elementRef.current.options.id === "draggablebox") {
switch (event.type) {
case "mousemove":
return handleElementDragging(event);
case "mouseout":
case "mouseup":
lastEventRef.current = undefined;
break;
case "mousedown":
lastEventRef.current = event;
break;
default:
}
}
};
const handleEnter = (ctx) => {
elementRef.current = ctx.element;
console.log(elementRef.current)
};
const handleLeave = () => {
elementRef.current = undefined;
lastEventRef.current = undefined;
};
const dragger = {
id: "dragger",
beforeEvent(chart, args, options) {
if (handleDrag(args.event)) {
args.changed = true;
return;
}
},
};
const options: ChartOptions<"line"> = {
responsive: true,
interaction: {
mode: "index" as const,
intersect: false,
},
plugins: {
annotation: {
enter(ctx) {
handleEnter(ctx)
},
leave() {
handleLeave()
},
annotations: {
draggablebox: {
type: "box",
backgroundColor: "rgba(165, 214, 167, 0.2)",
borderColor: "rgb(165, 214, 167)",
borderWidth: 2,
label: {
display: true,
content: ["Box annotation", "to drag"],
textAlign: "center",
},
xMax: labels[2],
xMin: labels[7],
xScaleID: "x",
yMax: 5,
yMin: 0,
yScaleID: "y",
},
},
},
},
events: ["mousedown", "mouseup", "mousemove", "mouseout"],
};
// const labels = dates && getUniqueDays(dates)
const datasets = dataGraph.registros && [
{
label: "data",
data: dataGraph.registros.map((registro) => registro.aceleracaoXEmMs2),
borderColor: "#ffc210",
backgroundColor: colorLib("#ffc210").alpha(0.5).rgbString(),
},
];
const data: ChartData<"line"> = {
datasets,
labels,
};
return (
<>
<div id="chart-area">
{datasets && labels && <Line options={options} data={data} plugins={[dragger]} />}
</div>
</>
);