diff --git a/src/AnnotationCreation.js b/src/AnnotationCreation.js index 049f8877d5068c3914c5fea8a88085a0d92eb421..04a6bc738af381b8a4faa79cc6905e2a44c1c147 100644 --- a/src/AnnotationCreation.js +++ b/src/AnnotationCreation.js @@ -620,6 +620,11 @@ class AnnotationCreation extends Component { aria-label="tool selection" size="small" > + + <ToggleButton value="text" aria-label="select text"> + + <CursorIcon /> + </ToggleButton> <ToggleButton value="cursor" aria-label="select cursor"> <CursorIcon /> </ToggleButton> diff --git a/src/AnnotationDrawing.js b/src/AnnotationDrawing.js index b2b305e685e298e3352e378d41a52002c398c8cc..678b0892ea87aece62c64183abcf583ca468cd91 100644 --- a/src/AnnotationDrawing.js +++ b/src/AnnotationDrawing.js @@ -1,12 +1,12 @@ -import React, { Component } from 'react'; +import React, { Component, useState } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import ResizeObserver from 'react-resize-observer'; import { OSDReferences } from '../mirador/dist/es/src/plugins/OSDReferences'; import { VideosReferences } from '../mirador/dist/es/src/plugins/VideosReferences'; import { renderWithPaperScope, PaperContainer } from '@psychobolt/react-paperjs'; -import -{ +import { v4 as uuidv4 } from 'uuid'; +import { EllipseTool, PolygonTool, RectangleTool, @@ -14,7 +14,11 @@ import } from '@psychobolt/react-paperjs-editor'; - import { Stage, Layer, Star, Text } from 'react-konva'; +import { + Stage, Layer, Star, Text, Circle, Rect + , Ellipse + +} from 'react-konva'; import { Point } from 'paper'; @@ -28,12 +32,18 @@ class AnnotationDrawing extends Component { constructor(props) { super(props); + this.paper = null; this.konvas = null; this.getDisplayProps = this.getDisplayProps.bind(this); this.onPaperResize = this.onPaperResize.bind(this); this.paperDidMount = this.paperDidMount.bind(this); this.addPath = this.addPath.bind(this); + this.state = { + shapes: [], + newShape: null, + currentShape: null, + }; } /** Sync drawing canvas on componentDidMount */ @@ -135,6 +145,381 @@ class AnnotationDrawing extends Component { paperDidMount(paper) { this.paper = paper; } + handleMouseDown = (e) => { + console.log('mouse down', this.props.activeTool); + + const pos = e.target.getStage().getPointerPosition(); + console.log('pos', pos); + let shape = null; + switch (this.props.activeTool) { + case 'rectangle': + + shape = { + type: 'rectangle', x: pos.x, y: pos.y, width: 0, height: 0, + strokeColor: this.props.strokeColor, + strokeWidth: this.props.strokeWidth, + fill: this.props.fillColor, + id: uuidv4(), + }; + + + break; + case "ellipse": + shape = { + type: 'ellipse', x: pos.x, y: pos.y, width: 0, height: 0, + stroke: this.props.strokeColor, + strokeWidth: this.props.strokeWidth, + fill: this.props.fillColor, + id: uuidv4(), + }; + this.setState({ + newShape: shape, + currentShape: shape, + }); + break; + // case "polygon": + // this.setState({ newShape: { type: 'circle', x: pos.x, y: pos.y, width: 0, height: 0 } }); + // break; + // case "freehand": + // this.setState({ newShape: { type: 'freehand', x: pos.x, y: pos.y, width: 0, height: 0 } }); + // break; + case "text": + + shape = { + type: 'text', + x: pos.x, + y: pos.y, + fontSize: 20, + fill: this.props.fillColor, + + + text: 'text', + id: uuidv4(), + }; + + this.setState({ newShape: shape }, () => { + // Add global key press event listener + window.addEventListener('keypress', this.handleKeyPress); + }); + this.setState({ + newShape: shape, + currentShape: shape, + }); + + break; + + // Add cases for other shapes here + default: + break; + } + + + + + + }; + + handleMouseUp = () => { + + const { newShape, shapes, currentShape } = this.state; + console.log('mouse up', newShape); + if (newShape) { + this.setState({ + shapes: [...shapes, newShape], + newShape: null, + + }); + } + + }; + + handleMouseMove = (e) => { + + const { newShape, currentShape, currentColorType } = this.state; + console.log('mouse move', newShape); + if (newShape) { + + + switch (newShape.type) { + case 'rectangle': + + const pos = e.target.getStage().getPointerPosition(); + console.log('pos', pos); + let width = pos.x - newShape.x; + let height = pos.y - newShape.y; + + this.setState({ + newShape: { + ...newShape, + width, + height, + }, + }) + + + case 'ellipse': + const pos3 = e.target.getStage().getPointerPosition(); + console.log('pos', pos); + let width3 = pos3.x - newShape.x; + let height3 = pos3.y - newShape.y; + // Negative radius is not allowed + + if (width3 < 0) { + width3 = Math.abs(width3); + newShape.x = pos3.x; + } + if (height3 < 0) { + height3 = Math.abs(height3); + newShape.y = pos3.y; + } + + + + + break; + + case 'text': + const pos2 = e.target.getStage().getPointerPosition(); + console.log('pos', pos2); + + this.setState({ + newShape: { + ...newShape, + + x: pos2.x, + y: pos2.y, + + } + }) + + break; + default: + break; + + } + + + + } + // do we need to do something here ? + if (currentShape) { + + } + + + }; + + handleKeyPress = (e) => { + const { currentShape,shapes } = this.state; + console.log('key press',currentShape, e.key); + if (currentShape) { + if (e.key === 'Backspace') { + // Remove the last character + + + + this.setState({ + + currentShape: { + ...currentShape, + text: currentShape.text.slice(0, -1), + }, + }); + + const index = shapes.findIndex((shape) => shape.id === currentShape.id); + console.log('index', index); + const newShapes = [...shapes]; + newShapes[index] = currentShape; + this.setState({ + shapes: newShapes, + }); + + + + //replace in array + + } else { + // Add the character to the text + this.setState({ + currentShape: { + ...currentShape, + text: currentShape.text + e.key, + }, + }); + + console.log('current shape', currentShape); + const index = shapes.findIndex((shape) => shape.id === currentShape.id); + console.log('index', index); + const newShapes = [...shapes]; + newShapes[index] = currentShape; + this.setState({ + shapes: newShapes, + }); + } + } + + + + + }; + + // ... + + componentDidUpdate(prevProps) { + if (prevProps.activeTool === 'text' && this.props.activeTool !== 'text') { + // Remove global key press event listener + window.removeEventListener('keypress', this.handleKeyPress); + } + } + drawKonvas() { + console.log('rendering konva'); + console.log(this.props) + + const { activeTool, toolState } = this.props; + console.log('active tool', activeTool); + + console.log('tool state', toolState); + + const { shapes, newShape, currentShape } = this.state; + console.log('shapes', shapes); + + + + return ( + <Stage width={1920} height={1080} + + style={{ + height: '100%', left: 0, position: 'absolute', top: 0, width: '100%', + + }} + onMouseDown={this.handleMouseDown} + onMouseUp={this.handleMouseUp} + onMouseMove={this.handleMouseMove} + + > + <Layer> + + {shapes.map((shape, i) => { + console.log('shape', shape); + switch (shape.type) { + + case 'rectangle': + console.log('drawing rectangle'); + return ( + <Rect + key={i} + x={shape.x} + y={shape.y} + width={shape.width} + height={shape.height} + fill={shape.fill} + stroke={shape.strokeColor} + strokeWidth={shape.strokeWidth} + draggable={this.props.activeTool === 'cursor'} + onClick={this.props.activeTool === 'cursor' ? () => this.setState({ currentShape: shape }) : null} + onDragEnd={this.handleDragEnd(shape.id)} // Add this line + /> + ); + + case 'ellipse': + return ( + <Ellipse + key={i} + x={shape.x} + y={shape.y} + radiusX={shape.width} + radiusY={shape.height} + stroke={shape.strokeColor} + fill={shape.fill} + strokeWidth={shape.strokeWidth} + draggable={this.props.activeTool === 'cursor'} + onClick={this.props.activeTool === 'cursor' ? () => this.setState({ currentShape: shape }) : null} + onDragEnd={this.handleDragEnd(shape.id)} // Add this line + + /> + ); + case 'text': + + return ( + <Text + //color$ + + fill={shape.fill} + fontFamily="Calibri" + key={i} + x={shape.x} + y={shape.y} + + text={shape.text} + fontSize={shape.fontSize} + draggable={this.props.activeTool === 'cursor'} + onClick={this.props.activeTool === 'cursor' ? () => this.setState({ currentShape: shape }) : null} + onDragEnd={this.handleDragEnd(shape.id)} // Add this line + /> + ); + + + + // Add cases for other shapes here + default: + return null; + } + })} + {newShape && newShape.type === 'rectangle' && ( + <Rect + x={newShape.x} + y={newShape.y} + width={newShape.width} + height={newShape.height} + stroke={newShape.strokeColor} + fill={newShape.fill} + strokeWidth={newShape.strokeWidth} + draggable={this.props.activeTool === 'cursor'} + /> + )} + {newShape && newShape.type === 'ellipse' && ( + <Ellipse + x={newShape.x} + y={newShape.y} + radiusX={newShape.width} + radiusY={newShape.height} + stroke={newShape.strokeColor} + fill={newShape.fill} + strokeWidth={newShape.strokeWidth} + draggable={this.props.activeTool === 'cursor'} + /> + )} + {newShape && newShape.type === 'text' && ( + <Text + x={newShape.x} + y={newShape.y} + text={newShape.text} + fill={newShape.fill} + fontSize={newShape.fontSize} + draggable={this.props.activeTool === 'cursor'} + /> + )} + + + + + + + </Layer> + </Stage> + ); + + } + + + handleDragEnd = (id) => (e) => { + const { shapes } = this.state; + const shapeNode = e.target; + const updatedShapes = shapes.map(shape => + shape.id === id ? { ...shape, x: shapeNode.x(), y: shapeNode.y() } : shape + ); + this.setState({ shapes: updatedShapes }); + }; /** */ paperThing() { @@ -170,58 +555,62 @@ class AnnotationDrawing extends Component { return ( <Stage width={1920} height={1080} - - style={{ - height: '100%', left: 0, position: 'absolute', top: 0, width: '100%', - - }} + + style={{ + height: '100%', left: 0, position: 'absolute', top: 0, width: '100%', + + }} + + onMouseDown={handleMouseDown} + onMouseUp={handleMouseUp} + onMouseMove={handleMouseMove} > <Layer> - - + + </Layer> </Stage> ); - // return ( - - - - // <div - // style={{ - // height: '100%', left: 0, position: 'absolute', top: 0, width: '100%', - // }} - // > - // <PaperContainer - // canvasProps={canvasProps} - // viewProps={viewProps} - // onMount={this.paperDidMount} - // > - // {renderWithPaperScope((paper) => { - // const paths = flatten(paper.project.layers.map((layer) => ( - // flatten(mapChildren(layer)).map((aPath) => aPath) - // ))); - // if (svg && paths.length === 0) { - // paper.project.importSVG(svg); - // } - // paper.settings.handleSize = 10; // eslint-disable-line no-param-reassign - // paper.settings.hitTolerance = 10; // eslint-disable-line no-param-reassign - // return ( - // <ActiveTool - // onPathAdd={this.addPath} - // pathProps={{ - // fillColor, - // strokeColor, - // strokeWidth: strokeWidth / paper.view.zoom, - // }} - // paper={paper} - // /> - // ); - // })} - // </PaperContainer> - // <ResizeObserver onResize={this.onPaperResize} /> - //</div> + // return ( + + + + // <div + // style={{ + // height: '100%', left: 0, position: 'absolute', top: 0, width: '100%', + // }} + // > + // <PaperContainer + // canvasProps={canvasProps} + // viewProps={viewProps} + // onMount={this.paperDidMount} + // > + // {renderWithPaperScope((paper) => { + // const paths = flatten(paper.project.layers.map((layer) => ( + // flatten(mapChildren(layer)).map((aPath) => aPath) + // ))); + // if (svg && paths.length === 0) { + // paper.project.importSVG(svg); + // } + // paper.settings.handleSize = 10; // eslint-disable-line no-param-reassign + // paper.settings.hitTolerance = 10; // eslint-disable-line no-param-reassign + // return ( + // <ActiveTool + // onPathAdd={this.addPath} + // pathProps={{ + // fillColor, + // strokeColor, + // strokeWidth: strokeWidth / paper.view.zoom, + // }} + // paper={paper} + // /> + // ); + // })} + // </PaperContainer> + // <ResizeObserver onResize={this.onPaperResize} /> + //</div> //); } @@ -240,7 +629,7 @@ class AnnotationDrawing extends Component { ? osdref.current.element : videoref.ref.current.parentElement; return ( - ReactDOM.createPortal(this.paperThing(), container) + ReactDOM.createPortal(this.drawKonvas(), container) ); } }