chartjs / chartjs-plugin-annotation

Annotation plugin for Chart.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Draggable Annotations don't work in React

PaulSender opened this issue · comments

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,
                    },
                  },
                },
              },
            }}
          />

@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?

Did you ever fix this?

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>
    </>
  );