TypeScript throws errors for Konva ReactJS project

435 Views Asked by At

I'm trying out TypeScript (TS) with Konva.js in a ReacJS project. Without TS it would work fine. But with TS it throws an error:

Module build failed (from ./node_modules/babel-loader/lib/index.js): SyntaxError: /.../testProject/src/App.ts: Unexpected token, expected "," (25:10)

At React.Fragment.

I have no idea how to fix it. It seems like he doesn't recognize React or any exported module from Konva.js. I wonder why.

Here is the project https://github.com/kodingreview/konva

I tried to use <> instead of React.Fragement. I uninstalled and re-installed TS again. Because without TS it would work fine. With TS it doesn't. I expect TS to recognize React and the konva.js modules.

The error is in the App.ts:

import React, { useState, useRef, useEffect } from 'react';
import { Stage, Layer, Rect, Circle, Group, Transformer } from 'react-konva';

export interface RectangleProps {
  shapeProps: any, 
  isSelected: boolean; 
  onSelect: () => void;
  onChange: (newAttrs: any) => void; 
  onHoveringRect: (id: string | null) => void;
}
const Rectangle = (props: RectangleProps) => {
  const { shapeProps, isSelected, onSelect, onChange, onHoveringRect } = props;
  const shapeRef = React.useRef<any>();
  const trRef = React.useRef<any>();

  React.useEffect(() => {
    if (isSelected) {
      // we need to attach transformer manually
      trRef.current.nodes([shapeRef.current]);
      trRef.current.getLayer().batchDraw();
    }
  }, [isSelected]);

  return (
    <React.Fragment>
      <Rect
        onClick={onSelect}
        onTap={onSelect}
        ref={shapeRef}
        {...shapeProps}
        draggable
        onMouseOver={() => onHoveringRect(shapeProps.id)}
        onMouseOut={() => onHoveringRect(null)}
        onDragEnd={(e) => {
          onChange({
            ...shapeProps,
            x: e.target.x(),
            y: e.target.y(),
          });
        }}
        onTransformEnd={(e) => {
          // transformer is changing scale of the node
          // and NOT its width or height
          // but in the store we have only width and height
          // to match the data better we will reset scale on transform end
          const node = shapeRef.current;
          const scaleX = node.scaleX();
          const scaleY = node.scaleY();

          // we will reset it back
          node.scaleX(1);
          node.scaleY(1);
          onChange({
            ...shapeProps,
            x: node.x(),
            y: node.y(),
            // set minimal value
            width: Math.max(5, node.width() * scaleX),
            height: Math.max(node.height() * scaleY),
          });
        }}
      />
      {isSelected && (
        <Transformer
          ref={trRef}
          boundBoxFunc={(oldBox, newBox) => {
            // limit resize
            if (newBox.width < 5 || newBox.height < 5) {
              return oldBox;
            }
            return newBox;
          }}
        />
      )}
    </React.Fragment>
  );
};

const initialRectangles = [
  {
    x: 10,
    y: 10,
    width: 100,
    height: 100,
    fill: 'red',
    id: 'rect1',
  },
  {
    x: 150,
    y: 150,
    width: 100,
    height: 100,
    fill: 'green',
    id: 'rect2',
  },
];

const App = () => {
  const [selectedRectangle, setSelectedRectangle] = useState(null);
  const [rectangles, setRectangles] = useState(initialRectangles);
  const [hoveringRect, setHoveringRect] = useState(null);
  const [drawing, setDrawing] = useState(false);

  const handleMouseMove = (e) => {
    if (!drawing) {
      return;
    }
    const { x, y } = e.target.getStage().getPointerPosition();

    selectedRectangle.width = x - selectedRectangle.x;
    selectedRectangle.height = y - selectedRectangle.y;
    
    const index = rectangles.findIndex(rect => rect.id === selectedRectangle.id);
    const beforeRecs = rectangles.slice(0, index);
    const afterRecs = rectangles.slice(index + 1);
    setRectangles([...beforeRecs, selectedRectangle, ...afterRecs]);
  };
  const handleMouseUp = (e) => {
    setSelectedRectangle(null);
    setDrawing(false);
  };

  const handleMouseDown = (e) => {    
    if (selectedRectangle || hoveringRect !== null) {
      return;
    }
    setDrawing(true);
    const { x, y } = e.target.getStage().getPointerPosition();
    const newRectangle = {
      id: (Date.now() + Math.random()).toString(),
      x: x,
      y: y,
      fill: "rgba(0,0,0,0.5)",
      width: 0,
      height: 0
    };
    setSelectedRectangle(newRectangle);
    setRectangles([...rectangles, newRectangle]);
  };
  
  const checkDeselect = (e) => {
    // deselect when clicked on empty area
    const clickedOnEmpty = e.target === e.target.getStage();
    if (clickedOnEmpty) {
      setSelectedRectangle(null);
    }
    handleMouseDown(e);
  };

  return (
    <Stage
      width={window.innerWidth}
      height={window.innerHeight}
      onMouseDown={checkDeselect}
      onMouseUp={handleMouseUp}
      onTouchStart={checkDeselect}
      onMouseMove={handleMouseMove}
    >
      <Layer>
        {rectangles.map((rect, i) => {
          return (
            <Rectangle
              key={rect.id}
              shapeProps={rect}
              isSelected={selectedRectangle && rect.id === selectedRectangle.id}
              onSelect={() => {
                setSelectedRectangle(rect);
              }}
              onChange={(newAttrs) => {
                const rects = rectangles.slice();
                rects[i] = newAttrs;
                setRectangles(rects);
              }}
              onHoveringRect={setHoveringRect}
            />
          );
        })}
      </Layer>
    </Stage>
  );
};


export default App;
0

There are 0 best solutions below