diff --git a/src/AnnotationCreation.js b/src/AnnotationCreation.js index 8949be53c528267c44f711c086d870152865b11a..5d4b5b0bbf0ca8aa468ef17f768c362588b6b673 100644 --- a/src/AnnotationCreation.js +++ b/src/AnnotationCreation.js @@ -8,7 +8,7 @@ import { v4 as uuid } from 'uuid'; import { exportStageSVG } from 'react-konva-to-svg'; import CompanionWindow from 'mirador/dist/es/src/containers/CompanionWindow'; import { OSDReferences } from 'mirador/dist/es/src/plugins/OSDReferences'; -import { VideosReferences } from "mirador/dist/es/src/plugins/VideosReferences"; +import { VideosReferences } from 'mirador/dist/es/src/plugins/VideosReferences'; import Tab from '@mui/material/Tab'; import HighlightAltIcon from '@mui/icons-material/HighlightAlt'; import LayersIcon from '@mui/icons-material/Layers'; @@ -20,30 +20,24 @@ import WebAnnotation from './WebAnnotation'; import { secondsToHMS } from './utils'; import AnnotationFormContent from './annotationForm/AnnotationFormContent'; import AnnotationFormTime from './annotationForm/AnnotationFormTime'; -import AnnotationFormDrawing from './annotationForm/AnnotationFormDrawing'; import { geomFromAnnoTarget, timeFromAnnoTarget } from './AnnotationCreationUtils'; +import AnnotationFormOverlay from './annotationForm/AnnotationFormOverlay/AnnotationFormOverlay.js'; const TARGET_VIEW = 'target'; const OVERLAY_VIEW = 'layer'; const TAG_VIEW = 'tag'; const MANIFEST_LINK_VIEW = 'link'; - /** Component for creating annotations. * Display in companion window when a manifest is open and an annoation created or edited */ function AnnotationCreation(props) { const [toolState, setToolState] = useState({ - activeTool: 'cursor', + activeTool: 'edit', closedMode: 'closed', - colorPopoverOpen: false, - currentColorType: false, fillColor: 'rgba(255, 0, 0, 0.5)', image: { id: null }, imageEvent: null, - lineWeightPopoverOpen: false, - popoverAnchorEl: null, - popoverLineWeightAnchorEl: null, - strokeColor: 'green', + strokeColor: 'rgba(255, 0, 0, 0.5)', strokeWidth: 3, }); @@ -147,8 +141,6 @@ function AnnotationCreation(props) { }; }, []); - - useEffect(() => { }, [toolState.fillColor, toolState.strokeColor, toolState.strokeWidth]); @@ -248,7 +240,6 @@ function AnnotationCreation(props) { /** */ const setShapeProperties = (options) => new Promise(() => { - if (options.fill) { state.fillColor = options.fill; } @@ -282,30 +273,26 @@ function AnnotationCreation(props) { return svg; }; - /** Set color tool from current shape */ const setColorToolFromCurrentShape = (colorState) => { setToolState((prevState) => ({ ...prevState, ...colorState, })); - } - + }; /** update shapes with shapes from annotationDrawing */ const updateShapes = (newShapes) => { - setShapes(newShapes); - } + }; /** delete shape */ const deleteShape = (shapeId) => { - const newShapes = shapes.filter((shape) => shape.id !== shapeId); setShapes(newShapes); - } + }; /** * Validate form and save annotation @@ -527,12 +514,12 @@ function AnnotationCreation(props) { <StyledTabPanel value={OVERLAY_VIEW} > - <AnnotationFormDrawing + <AnnotationFormOverlay toolState={toolState} updateToolState={setToolState} handleImgChange={handleImgChange} shapes={shapes} - deleteShape = {deleteShape} + deleteShape={deleteShape} /> </StyledTabPanel> <StyledTabPanel diff --git a/src/AnnotationCreationUtils.js b/src/AnnotationCreationUtils.js index 2007051bbc3eec06508876f2255c9891dab7bbde..387fbeab8ad3493ac6500962376324791237e69b 100644 --- a/src/AnnotationCreationUtils.js +++ b/src/AnnotationCreationUtils.js @@ -18,3 +18,26 @@ export function geomFromAnnoTarget(annotarget) { } return r[1]; } + +export const OVERLAY_TOOL = { + CURSOR: 'cursor', + DELETE: 'delete', + EDIT: 'edit', + IMAGE: 'image', + SHAPE: 'shapes', + TEXT: 'text', +}; + +export const SHAPES_TOOL = { + ARROW: 'arrow', + ELLIPSE: 'ellipse', + FREEHAND: 'freehand', + POLYGON: 'polygon', + RECTANGLE: 'rectangle', + SHAPES: 'shapes', +}; + +export function isShapesTool(activeTool) { + // Find if active tool in the list of overlay tools. I want a boolean in return + return Object.values(SHAPES_TOOL).find((tool) => tool === activeTool) ; +} diff --git a/src/annotationForm/AnnotationDrawing.js b/src/annotationForm/AnnotationDrawing.js index c146bbc9310b4676814628d41b3e7fdc09f7c82c..cadceacfef87c979cbcdbbb7b066500bca9975ab 100644 --- a/src/annotationForm/AnnotationDrawing.js +++ b/src/annotationForm/AnnotationDrawing.js @@ -11,11 +11,8 @@ import { v4 as uuidv4 } from 'uuid'; import { OSDReferences } from 'mirador/dist/es/src/plugins/OSDReferences'; // eslint-disable-next-line import/no-extraneous-dependencies import { VideosReferences } from 'mirador/dist/es/src/plugins/VideosReferences'; -import ParentComponent from './KonvaDrawing/shapes/ParentComponent'; -import Surface from './KonvaDrawing/Surface'; -import { act } from '@psychobolt/react-paperjs/dist/index.dev'; -import { set } from 'lodash'; - +import ParentComponent from './AnnotationFormOverlay/KonvaDrawing/shapes/ParentComponent'; +import { SHAPES_TOOL } from '../AnnotationCreationUtils'; /** All the stuff to draw on the canvas */ function AnnotationDrawing(props) { const [shapes, setShapes] = useState([]); @@ -76,18 +73,6 @@ function AnnotationDrawing(props) { const { fillColor, strokeColor, strokeWidth } = props; - /** Debug function facility */ - const debug = (command) => { - console.debug('***************************'); - console.debug(command); - console.debug('shapes', shapes); - console.debug('shapes taille', shapes.length); - console.debug('currentShape', currentShape); - console.debug('isDrawing', isDrawing); - console.debug('props.activeTool', props.activeTool); - console.debug('-----------------------------'); - }; - /** */ useEffect(() => { if (!isDrawing) { @@ -123,7 +108,8 @@ function AnnotationDrawing(props) { fillColor: currentShape.fill, strokeColor: currentShape.stroke, strokeWidth: currentShape.strokeWidth, - }) + }, + ); return () => { window.removeEventListener('keydown', handleKeyPress); @@ -131,25 +117,18 @@ function AnnotationDrawing(props) { } }, [currentShape]); - useEffect(() => { -//compare shapes and props.shapes p, if different, update shapes + // compare shapes and props.shapes p, if different, update shapes - if (props.shapes.length !== shapes.length) {/// nul a revoir + if (props.shapes.length !== shapes.length) { /// nul a revoir setShapes(props.shapes); } - - - }, [props.shapes]); - - /** */ const onShapeClick = async (shp) => { const shape = shapes.find((s) => s.id === shp.id); if (props.activeTool === 'delete') { - const newShapes = shapes.filter((s) => s.id !== shape.id); setShapes(newShapes); return; @@ -162,25 +141,22 @@ function AnnotationDrawing(props) { fillColor: shape.fill, strokeColor: shape.stroke, strokeWidth: shape.strokeWidth, - }) + }, + ); }; const onTransform = (evt) => { - - const modifiedshape = evt.target.attrs; const shape = shapes.find((s) => s.id === modifiedshape.id); - Object.assign(shape, modifiedshape); setCurrentShape({ ...shape }); updateCurrentShapeInShapes(); }; const handleDragEnd = (evt) => { - const modifiedshape = evt.currentTarget.attrs; const shape = shapes.find((s) => s.id === modifiedshape.id); shape.x = modifiedshape.x; @@ -244,7 +220,6 @@ function AnnotationDrawing(props) { } }; - /** */ const updateCurrentShapeInShapes = () => { const index = shapes.findIndex((s) => s.id === currentShape.id); @@ -265,7 +240,7 @@ function AnnotationDrawing(props) { pos.y /= props.scale; let shape = null; switch (props.activeTool) { - case 'rectangle': + case SHAPES_TOOL.RECTANGLE: shape = { fill: props.fillColor, height: 1, @@ -283,18 +258,19 @@ function AnnotationDrawing(props) { setIsDrawing(true); setShapes([...shapes, shape]); setCurrentShape(shape); - case 'ellipse': + break; + case SHAPES_TOOL.ELLIPSE: shape = { fill: props.fillColor, height: 1, id: uuidv4(), + radiusX: 1, + radiusY: 1, rotation: 0, scaleX: 1, scaleY: 1, stroke: props.strokeColor, strokeWidth: props.strokeWidth, - radiusX: 1, - radiusY: 1, type: props.activeTool, width: 1, x: pos.x, @@ -305,7 +281,7 @@ function AnnotationDrawing(props) { setCurrentShape(shape); break; - case 'text': + case SHAPES_TOOL.TEXT: shape = { fill: props.fillColor, fontSize: 50, @@ -314,7 +290,7 @@ function AnnotationDrawing(props) { scaleX: 1, scaleY: 1, text: 'text', - type: 'text', + type: SHAPES_TOOL.TEXT, x: pos.x, y: pos.y, }; @@ -322,7 +298,7 @@ function AnnotationDrawing(props) { setShapes([...shapes, shape]); setCurrentShape(shape); break; - case 'freehand': + case SHAPES_TOOL.FREEHAND: // Not totally functionnal setIsDrawing(true); shape = { @@ -342,14 +318,14 @@ function AnnotationDrawing(props) { scaleY: 1, stroke: props.strokeColor, strokeWidth: props.strokeWidth, - type: 'freehand', + type: SHAPES_TOOL.FREEHAND, x: 0, y: 0, }; setShapes([...shapes, shape]); setCurrentShape(shape); break; - case 'polygon': + case SHAPES_TOOL.POLYGON: setIsDrawing(true); shape = { fill: props.fillColor, @@ -360,14 +336,14 @@ function AnnotationDrawing(props) { scaleY: 1, stroke: props.strokeColor, strokeWidth: props.strokeWidth, - type: 'polygon', + type: SHAPES_TOOL.POLYGON, x: 0, y: 0, }; setShapes([...shapes, shape]); setCurrentShape(shape); break; - case 'arrow': + case SHAPES_TOOL.ARROW: setIsDrawing(true); shape = { fill: props.fillColor, @@ -379,12 +355,10 @@ function AnnotationDrawing(props) { scaleX: 1, scaleY: 1, stroke: props.strokeColor, - type: 'arrow', + type: SHAPES_TOOL.ARROW, }; setShapes([...shapes, shape]); setCurrentShape(shape); - case 'debug': - debug('debug'); break; default: // Handle other cases if any @@ -409,7 +383,7 @@ function AnnotationDrawing(props) { pos.y /= props.scale; switch (props.activeTool) { - case 'rectangle': + case SHAPES_TOOL.RECTANGLE: setCurrentShape({ ...currentShape, @@ -417,7 +391,8 @@ function AnnotationDrawing(props) { width: pos.x - currentShape.x, }); updateCurrentShapeInShapes(); - case 'ellipse': + break; + case SHAPES_TOOL.ELLIPSE: // prevent negative radius for ellipse if (pos.x < currentShape.x) { @@ -427,7 +402,6 @@ function AnnotationDrawing(props) { pos.y = currentShape.y; } - setCurrentShape({ ...currentShape, height: pos.y - currentShape.y, @@ -438,7 +412,7 @@ function AnnotationDrawing(props) { updateCurrentShapeInShapes(); break; - case 'freehand': + case SHAPES_TOOL.FREEHAND: const shape = { ...currentShape }; shape.lines.push({ points: [pos.x, pos.y, pos.x, pos.y], @@ -448,14 +422,14 @@ function AnnotationDrawing(props) { setCurrentShape(shape); updateCurrentShapeInShapes(); break; - case 'polygon': + case SHAPES_TOOL.POLYGON: const polygonShape = { ...currentShape }; polygonShape.points[2] = pos.x; polygonShape.points[3] = pos.y; setCurrentShape(polygonShape); updateCurrentShapeInShapes(); break; - case 'arrow': + case SHAPES_TOOL.ARROW: // TODO improve const arrowShape = {}; // update points diff --git a/src/annotationForm/AnnotationFormDrawing.js b/src/annotationForm/AnnotationFormDrawing.js deleted file mode 100644 index c912235cef82c3881fd94adc215431d5e95137c3..0000000000000000000000000000000000000000 --- a/src/annotationForm/AnnotationFormDrawing.js +++ /dev/null @@ -1,414 +0,0 @@ -import { - Button, ClickAwayListener, Divider, Grid, MenuItem, MenuList, Paper, Popover, -} from '@mui/material'; -import Typography from '@mui/material/Typography'; -import ToggleButton from '@mui/material/ToggleButton'; -import TitleIcon from '@mui/icons-material/Title'; -import ImageIcon from '@mui/icons-material/Image'; -import DeleteIcon from '@mui/icons-material/Delete'; -import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; -import RectangleIcon from '@mui/icons-material/CheckBoxOutlineBlank'; -import CircleIcon from '@mui/icons-material/RadioButtonUnchecked'; -import PolygonIcon from '@mui/icons-material/Timeline'; -import GestureIcon from '@mui/icons-material/Gesture'; -import React, { useEffect } from 'react'; -import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; -import StrokeColorIcon from '@mui/icons-material/BorderColor'; -import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -import LineWeightIcon from '@mui/icons-material/LineWeight'; -import FormatColorFillIcon from '@mui/icons-material/FormatColorFill'; -import ClosedPolygonIcon from '@mui/icons-material/ChangeHistory'; -import OpenPolygonIcon from '@mui/icons-material/ShowChart'; -import AddPhotoAlternateIcon from '@mui/icons-material/AddPhotoAlternate'; -import PropTypes from 'prop-types'; -import { styled } from '@mui/material/styles'; -import { SketchPicker } from 'react-color'; -import { v4 as uuidv4 } from 'uuid'; -import CategoryIcon from '@mui/icons-material/Category'; -import CursorIcon from '../icons/Cursor'; -import ImageFormField from './ImageFormField'; - -const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ - '&:first-of-type': { - borderRadius: theme.shape.borderRadius, - }, - '&:not(:first-of-type)': { - borderRadius: theme.shape.borderRadius, - }, - border: 'none', - margin: theme.spacing(0.5), -})); - -const StyledDivider = styled(Divider)(({ theme }) => ({ - margin: theme.spacing(1, 0.5), -})); - -const StyledLi = styled('li')(({ theme }) => ({ - display:'flex', - wordBreak: 'break-word', -})); - -const StyledUl = styled('ul')(({ theme }) => ({ - display:'flex', - flexDirection: 'column', - gap: '5px', - listStyle: 'none', - paddingLeft: '0', - -})); - -const StyledPaper = styled(Paper)(({ theme }) => ({ - padding: '5px', -})); - -const StyledDivButtonImage = styled('div')(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', - marginTop: '5px', -})); - -const rgbaToObj = (rgba = 'rgba(255,255,255,0.5)') => { - const rgbaArray = rgba.split(','); - const r = Number(rgbaArray[0].split('(')[1]); - const g = Number(rgbaArray[1]); - const b = Number(rgbaArray[2]); - const a = Number(rgbaArray[3].split(')')[0]); - return { - // eslint-disable-next-line sort-keys - r, g, b, a, - }; -}; - -const objToRgba = (obj = { - // eslint-disable-next-line sort-keys - r: 255, g: 255, b: 255, a: 0.5, -}) => `rgba(${obj.r},${obj.g},${obj.b},${obj.a})`; - -/** Check if we are using an overlay tool or selecting the overlay view */ -function isShapeTool(activeTool) { - switch (activeTool) { - case 'rectangle': - case 'ellipse': - case 'arrow': - case 'polygon': - case 'freehand': - case 'shapes': - return true; - break; - default: - return false; - } -} - -/** All the stuff to manage to choose the drawing tool */ -function AnnotationFormDrawing({ updateToolState, toolState, handleImgChange, shapes,deleteShape }) { - useEffect(() => { - - }, [toolState.fillColor, toolState.strokeColor, toolState.strokeWidth]); - - /** */ - const openChooseLineWeight = (e) => { - updateToolState({ - ...toolState, - lineWeightPopoverOpen: true, - popoverLineWeightAnchorEl: e.currentTarget, - }); - }; - - /** Close color popover window */ - const closeChooseColor = (e) => { - updateToolState({ - ...toolState, - colorPopoverOpen: false, - currentColorType: null, - popoverAnchorEl: null, - }); - }; - - /** Update color : fillColor or strokeColor */ - const updateStrokeColor = (color) => { - updateToolState({ - ...toolState, - [toolState.currentColorType]: objToRgba(color.rgb), - }); - }; - /** */ - const openChooseColor = (e) => { - updateToolState({ - ...toolState, - colorPopoverOpen: true, - currentColorType: e.currentTarget.value, - popoverAnchorEl: e.currentTarget, - }); - }; - - /** */ - const handleCloseLineWeight = (e) => { - updateToolState({ - ...toolState, - lineWeightPopoverOpen: false, - popoverLineWeightAnchorEl: null, - }); - }; - - /** */ - const handleLineWeightSelect = (e) => { - updateToolState({ - ...toolState, - lineWeightPopoverOpen: false, - popoverLineWeightAnchorEl: null, - strokeWidth: e.currentTarget.value, - }); - }; - - const changeTool = (e, tool) => { - updateToolState({ - ...toolState, - activeTool: tool, - }); - }; - - const changeClosedMode = (e) => { - updateToolState({ - ...toolState, - closedMode: e.currentTarget.value, - }); - }; - - /** - * - */ - const addImage = () => { - const data = { - id: image?.id, - uuid: uuidv4(), - }; - - updateToolState({ - ...toolState, - image: { id: null }, - imageEvent: data, - }); - }; - - const { - activeTool, - closedMode, - image, - lineWeightPopoverOpen, - popoverLineWeightAnchorEl, - fillColor, - strokeColor, - strokeWidth, - colorPopoverOpen, - popoverAnchorEl, - currentColorType, - } = toolState; - - return ( - <StyledPaper> - <div> - <Grid container> - <Grid item xs={12}> - <Typography variant="overline"> - Overlay - </Typography> - </Grid> - <Grid item xs={12}> - <StyledToggleButtonGroup - value={activeTool} // State or props ? - exclusive - onChange={changeTool} - aria-label="tool selection" - size="small" - > - <ToggleButton value="edit" aria-label="select cursor"> - <CursorIcon /> - </ToggleButton> - <ToggleButton value="shapes" aria-label="select cursor"> - <CategoryIcon /> - </ToggleButton> - <ToggleButton value="images" aria-label="select cursor"> - <ImageIcon /> - </ToggleButton> - <ToggleButton value="text" aria-label="select text"> - <TitleIcon /> - </ToggleButton> - <ToggleButton value="delete" aria-label="select cursor"> - <DeleteIcon /> - </ToggleButton> - </StyledToggleButtonGroup> - { - activeTool === 'edit' && ( - <StyledUl> - {shapes && shapes.map((shape) => ( - <StyledLi key={shape.id}> - {shape.id} - <Button onClick={() => deleteShape(shape.id)}> - <DeleteIcon /> - </Button> - </StyledLi> - ))} - </StyledUl> - ) - } - { - isShapeTool(activeTool) && ( - <> - <StyledToggleButtonGroup - value={activeTool} // State or props ? - exclusive - onChange={changeTool} - aria-label="tool selection" - size="small" - > - <ToggleButton value="rectangle" aria-label="add a rectangle"> - <RectangleIcon /> - </ToggleButton> - <ToggleButton value="ellipse" aria-label="add a circle"> - <CircleIcon /> - </ToggleButton> - <ToggleButton value="arrow" aria-label="add an arrow"> - <ArrowOutwardIcon /> - </ToggleButton> - <ToggleButton value="polygon" aria-label="add a polygon"> - <PolygonIcon /> - </ToggleButton> - <ToggleButton value="freehand" aria-label="free hand polygon"> - <GestureIcon /> - </ToggleButton> - </StyledToggleButtonGroup> - <div> - <Grid container> - <Grid item xs={12}> - <Typography variant="overline"> - Style - </Typography> - </Grid> - <Grid item xs={12}> - <ToggleButtonGroup - aria-label="style selection" - size="small" - > - <ToggleButton - value="strokeColor" - aria-label="select color" - onClick={openChooseColor} - > - <StrokeColorIcon style={{ fill: strokeColor }} /> - <ArrowDropDownIcon /> - </ToggleButton> - <ToggleButton - value="strokeColor" - aria-label="select line weight" - onClick={openChooseLineWeight} - > - <LineWeightIcon /> - <ArrowDropDownIcon /> - </ToggleButton> - <ToggleButton - value="fillColor" - aria-label="select color" - onClick={openChooseColor} - > - <FormatColorFillIcon style={{ fill: fillColor }} /> - <ArrowDropDownIcon /> - </ToggleButton> - </ToggleButtonGroup> - - <StyledDivider flexItem orientation="vertical" /> - { /* close / open polygon mode only for freehand drawing mode. */ - activeTool === 'freehand' - ? ( - <ToggleButtonGroup - size="small" - value={closedMode} - onChange={changeClosedMode} - > - <ToggleButton value="closed"> - <ClosedPolygonIcon /> - </ToggleButton> - <ToggleButton value="open"> - <OpenPolygonIcon /> - </ToggleButton> - </ToggleButtonGroup> - ) - : null - } - </Grid> - </Grid> - </div> - </> - ) - } - { - activeTool === 'images' ? ( - <> - <Grid container> - <ImageFormField xs={8} value={image} onChange={handleImgChange} /> - </Grid> - <StyledDivButtonImage> - <Button variant="contained" onClick={addImage}> - <AddPhotoAlternateIcon /> - </Button> - </StyledDivButtonImage> - </> - ) : (<></>) - } - { - activeTool === 'text' && ( - <Typography> - Ajouter un input text - </Typography> - ) - } - </Grid> - </Grid> - </div> - <Popover - open={lineWeightPopoverOpen} - anchorEl={popoverLineWeightAnchorEl} - > - <Paper> - <ClickAwayListener onClickAway={handleCloseLineWeight}> - <MenuList autoFocus role="listbox"> - {[1, 3, 5, 10, 50].map((option, index) => ( - <MenuItem - key={option} - onClick={handleLineWeightSelect} - value={option} - selected={option == strokeWidth} - role="option" - aria-selected={option == strokeWidth} - > - {option} - </MenuItem> - ))} - </MenuList> - </ClickAwayListener> - </Paper> - </Popover> - <Popover - open={colorPopoverOpen} - anchorEl={popoverAnchorEl} - onClose={closeChooseColor} - > - <SketchPicker - disableAlpha={false} - color={rgbaToObj(toolState[currentColorType])} - onChangeComplete={updateStrokeColor} - /> - </Popover> - </StyledPaper> - ); -} - -AnnotationFormDrawing.propTypes = { - handleImgChange: PropTypes.func, - toolState: PropTypes.object, - updateToolState: PropTypes.func, - shapes: PropTypes.object, - deleteShape: PropTypes.func, -}; - -export default AnnotationFormDrawing; diff --git a/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlay.js b/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlay.js new file mode 100644 index 0000000000000000000000000000000000000000..4c03fe52ace15b571fbcf49363e7f7ca1eba87cc --- /dev/null +++ b/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlay.js @@ -0,0 +1,116 @@ +import { + Button, Grid, Paper, +} from '@mui/material'; +import Typography from '@mui/material/Typography'; +import ToggleButton from '@mui/material/ToggleButton'; +import TitleIcon from '@mui/icons-material/Title'; +import ImageIcon from '@mui/icons-material/Image'; +import DeleteIcon from '@mui/icons-material/Delete'; +import React, { useEffect } from 'react'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; +import CategoryIcon from '@mui/icons-material/Category'; +import CursorIcon from '../../icons/Cursor'; +import AnnotationFormOverlayTool from './AnnotationFormOverlayTool'; +import { OVERLAY_TOOL } from '../../AnnotationCreationUtils'; + +const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ + '&:first-of-type': { + borderRadius: theme.shape.borderRadius, + }, + '&:not(:first-of-type)': { + borderRadius: theme.shape.borderRadius, + }, + border: 'none', + margin: theme.spacing(0.5), +})); + + +const StyledPaper = styled(Paper)(({ theme }) => ({ + padding: '5px', +})); + +/** All the stuff to manage to choose the drawing tool */ +function AnnotationFormOverlay({ + updateToolState, toolState, shapes, deleteShape, +}) { + useEffect(() => { + + }, [toolState.fillColor, toolState.strokeColor, toolState.strokeWidth]); + + const changeTool = (e, tool) => { + updateToolState({ + ...toolState, + activeTool: tool, + }); + }; + + const { + activeTool, + } = toolState; + + return ( + <StyledPaper> + <div> + <Grid container> + <Grid item xs={12}> + <Typography variant="overline"> + Overlay + </Typography> + </Grid> + <Grid item xs={12}> + <StyledToggleButtonGroup + value={activeTool} // State or props ? + exclusive + onChange={changeTool} + aria-label="tool selection" + size="small" + > + <ToggleButton value={OVERLAY_TOOL.EDIT} aria-label="select cursor"> + <CursorIcon /> + </ToggleButton> + <ToggleButton value={OVERLAY_TOOL.SHAPE} aria-label="select cursor"> + <CategoryIcon /> + </ToggleButton> + <ToggleButton value={OVERLAY_TOOL.IMAGE} aria-label="select cursor"> + <ImageIcon /> + </ToggleButton> + <ToggleButton value={OVERLAY_TOOL.TEXT} aria-label="select text"> + <TitleIcon /> + </ToggleButton> + <ToggleButton value={OVERLAY_TOOL.DELETE} aria-label="select cursor"> + <DeleteIcon /> + </ToggleButton> + </StyledToggleButtonGroup> + <AnnotationFormOverlayTool + toolState={toolState} + updateToolState={updateToolState} + shapes={shapes} + deleteShape={deleteShape} + /> + </Grid> + </Grid> + </div> + </StyledPaper> + ); +} + +AnnotationFormOverlay.propTypes = { + deleteShape: PropTypes.func.isRequired, + shapes: PropTypes.array.isRequired, + toolState: PropTypes.shape({ + activeTool: PropTypes.string.isRequired, + closedMode: PropTypes.bool.isRequired, + fillColor: PropTypes.string.isRequired, + image: PropTypes.shape({ + id: PropTypes.string, + }).isRequired, + strokeColor: PropTypes.string.isRequired, + strokeWidth: PropTypes.number.isRequired, + updateColor: PropTypes.func.isRequired, + }).isRequired, + updateToolState: PropTypes.func.isRequired, +}; + +export default AnnotationFormOverlay; diff --git a/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayTool.js b/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayTool.js new file mode 100644 index 0000000000000000000000000000000000000000..de9bb87b2610eb72a4f95d5e90b5e6fb27ec3075 --- /dev/null +++ b/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayTool.js @@ -0,0 +1,121 @@ +import ToggleButton from '@mui/material/ToggleButton'; +import RectangleIcon from '@mui/icons-material/CheckBoxOutlineBlank'; +import CircleIcon from '@mui/icons-material/RadioButtonUnchecked'; +import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; +import PolygonIcon from '@mui/icons-material/Timeline'; +import GestureIcon from '@mui/icons-material/Gesture'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { styled } from '@mui/material/styles'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import { Button } from '@mui/material'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AnnotationFormOverlayToolOptions from './AnnotationFormOverlayToolOptions'; +import { isShapesTool, OVERLAY_TOOL, SHAPES_TOOL } from '../../AnnotationCreationUtils'; + +const StyledLi = styled('li')(({ theme }) => ({ + display: 'flex', + wordBreak: 'break-word', +})); + +const StyledUl = styled('ul')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: '5px', + listStyle: 'none', + paddingLeft: '0', +})); + +// TODO WIP code duplicated +const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ + '&:first-of-type': { + borderRadius: theme.shape.borderRadius, + }, + '&:not(:first-of-type)': { + borderRadius: theme.shape.borderRadius, + }, + border: 'none', + margin: theme.spacing(0.5), +})); + +/** All the form part for the overlay view */ +function AnnotationFormOverlayTool({ + toolState, updateToolState, shapes, deleteShape, +}) { + /** Change the active overlay tool */ + const changeTool = (e, tool) => { + updateToolState({ + ...toolState, + activeTool: tool, + }); + }; + + return ( + <> + {toolState.activeTool} + { + toolState.activeTool === OVERLAY_TOOL.EDIT && ( + <StyledUl> + {shapes && shapes.map((shape) => ( + <StyledLi key={shape.id}> + {shape.id} + <Button onClick={() => deleteShape(shape.id)}> + <DeleteIcon /> + </Button> + </StyledLi> + ))} + </StyledUl> + ) + } + { + isShapesTool(toolState.activeTool) && ( + <StyledToggleButtonGroup + value={toolState.activeTool} // State or props ? + exclusive + onChange={changeTool} + aria-label="tool selection" + size="small" + > + <ToggleButton value={SHAPES_TOOL.RECTANGLE} aria-label="add a rectangle"> + <RectangleIcon /> + </ToggleButton> + <ToggleButton value={SHAPES_TOOL.ELLIPSE} aria-label="add a circle"> + <CircleIcon /> + </ToggleButton> + <ToggleButton value={SHAPES_TOOL.ARROW} aria-label="add an arrow"> + <ArrowOutwardIcon /> + </ToggleButton> + <ToggleButton value={SHAPES_TOOL.POLYGON} aria-label="add a polygon"> + <PolygonIcon /> + </ToggleButton> + <ToggleButton value={SHAPES_TOOL.FREEHAND} aria-label="free hand polygon"> + <GestureIcon /> + </ToggleButton> + </StyledToggleButtonGroup> + ) + } + <AnnotationFormOverlayToolOptions + toolState={toolState} + updateToolState={updateToolState} + /> + </> + ); +} + +AnnotationFormOverlayTool.propTypes = { + toolState: PropTypes.shape({ + activeTool: PropTypes.string.isRequired, + closedMode: PropTypes.bool.isRequired, + fillColor: PropTypes.string.isRequired, + image: PropTypes.shape({ + id: PropTypes.string, + }).isRequired, + strokeColor: PropTypes.string.isRequired, + strokeWidth: PropTypes.number.isRequired, + updateColor: PropTypes.func.isRequired, + }).isRequired, + updateToolState: PropTypes.func.isRequired, + +}; + +export default AnnotationFormOverlayTool; diff --git a/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayToolOptions.js b/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayToolOptions.js new file mode 100644 index 0000000000000000000000000000000000000000..eb8cc45fc057b9d59ea3c78c69a962d80c991622 --- /dev/null +++ b/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayToolOptions.js @@ -0,0 +1,302 @@ +import { + Button, + ClickAwayListener, Divider, Grid, MenuItem, MenuList, Paper, Popover, +} from '@mui/material'; +import Typography from '@mui/material/Typography'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import ToggleButton from '@mui/material/ToggleButton'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import StrokeColorIcon from '@mui/icons-material/BorderColor'; +import LineWeightIcon from '@mui/icons-material/LineWeight'; +import FormatColorFillIcon from '@mui/icons-material/FormatColorFill'; +import React, { useState } from 'react'; +import ClosedPolygonIcon from '@mui/icons-material/ChangeHistory'; +import OpenPolygonIcon from '@mui/icons-material/ShowChart'; +import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; +import { SketchPicker } from 'react-color'; +import AddPhotoAlternateIcon from '@mui/icons-material/AddPhotoAlternate'; +import { v4 as uuidv4 } from 'uuid'; +import ImageFormField from './ImageFormField'; +import { isShapesTool, OVERLAY_TOOL } from '../../AnnotationCreationUtils'; + +const StyledDivider = styled(Divider)(({ theme }) => ({ + margin: theme.spacing(1, 0.5), +})); + +const StyledDivButtonImage = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + marginTop: '5px', +})); + +/** Utils functions to convert string to object */ +const rgbaToObj = (rgba = 'rgba(255,255,255,0.5)') => { + const rgbaArray = rgba.split(','); + return { + // eslint-disable-next-line sort-keys + r: Number(rgbaArray[0].split('(')[1]), + // eslint-disable-next-line sort-keys + g: Number(rgbaArray[1]), + // eslint-disable-next-line sort-keys + b: Number(rgbaArray[2]), + // eslint-disable-next-line sort-keys + a: Number(rgbaArray[3].split(')')[0]), + }; +}; + +/** Convert color object to rgba string */ +const objToRgba = (obj = { + // eslint-disable-next-line sort-keys + r: 255, g: 255, b: 255, a: 0.5, +}) => `rgba(${obj.r},${obj.g},${obj.b},${obj.a})`; + +/** All the tools options for the overlay options */ +function AnnotationFormOverlayToolOptions({ updateToolState, toolState }) { + // set toolOptionsValue + const [toolOptions, setToolOptions] = useState({ + colorPopoverOpen: false, + currentColorType: null, + lineWeightPopoverOpen: false, + popoverAnchorEl: null, + popoverLineWeightAnchorEl: null, + }); + + // Set unused default color to avoid error on render + const currentColor = toolOptions.currentColorType ? rgbaToObj(toolState[toolOptions.currentColorType]) : 'rgba(255, 0, 0, 0.5)'; + + // Fonction to manage option displaying + /** */ + const openChooseLineWeight = (e) => { + setToolOptions({ + ...toolOptions, + lineWeightPopoverOpen: true, + popoverLineWeightAnchorEl: e.currentTarget, + }); + }; + + /** */ + const handleLineWeightSelect = (e) => { + setToolOptions({ + ...toolOptions, + lineWeightPopoverOpen: false, + popoverLineWeightAnchorEl: null, + }); + updateToolState({ + ...toolState, + strokeWidth: e.currentTarget.value, + }); + }; + + /** Close color popover window */ + const closeChooseColor = (e) => { + setToolOptions({ + ...toolOptions, + colorPopoverOpen: false, + currentColorType: null, + popoverAnchorEl: null, + }); + }; + + /** */ + const openChooseColor = (e) => { + console.log('openChooseColor', e.currentTarget.value); + setToolOptions({ + ...toolOptions, + colorPopoverOpen: true, + currentColorType: e.currentTarget.value, + popoverAnchorEl: e.currentTarget, + }); + }; + + /** */ + const handleCloseLineWeight = (e) => { + setToolOptions({ + ...toolOptions, + lineWeightPopoverOpen: false, + popoverLineWeightAnchorEl: null, + }); + }; + + /** closed mode change */ + const changeClosedMode = (e) => { + updateToolState({ + ...toolState, + closedMode: e.currentTarget.value, + }); + }; + + /** Update color : fillColor or strokeColor */ + const updateColor = (color) => { + updateToolState({ + ...toolState, + [toolOptions.currentColorType]: objToRgba(color.rgb), + }); + }; + + const addImage = () => { + const data = { + id: toolState?.image?.id, + uuid: uuidv4(), + }; + + updateToolState({ + ...toolState, + image: { id: null }, + imageEvent: data, + }); + }; + + const handleImgChange = (newUrl, imgRef) => { + updateToolState({ + ...toolState, + image: { ...toolState.image, id: newUrl }, + }); + }; + + return ( + <div> + { + isShapesTool(toolState.activeTool) && ( + <Grid container> + <Grid item xs={12}> + <Typography variant="overline"> + Style + </Typography> + </Grid> + <Grid item xs={12}> + <ToggleButtonGroup + aria-label="style selection" + size="small" + > + <ToggleButton + value="strokeColor" + aria-label="select color" + onClick={openChooseColor} + > + <StrokeColorIcon style={{ fill: toolState.strokeColor }} /> + <ArrowDropDownIcon /> + </ToggleButton> + <ToggleButton + value="strokeColor" + aria-label="select line weight" + onClick={openChooseLineWeight} + > + <LineWeightIcon /> + <ArrowDropDownIcon /> + </ToggleButton> + <ToggleButton + value="fillColor" + aria-label="select color" + onClick={openChooseColor} + > + <FormatColorFillIcon style={{ fill: toolState.fillColor }} /> + <ArrowDropDownIcon /> + </ToggleButton> + </ToggleButtonGroup> + + <StyledDivider flexItem orientation="vertical" /> + { /* close / open polygon mode only for freehand drawing mode. */ + toolState.activeTool === 'freehand' + && ( + <ToggleButtonGroup + size="small" + value={toolState.closedMode} + onChange={changeClosedMode} + > + <ToggleButton value="closed"> + <ClosedPolygonIcon /> + </ToggleButton> + <ToggleButton value="open"> + <OpenPolygonIcon /> + </ToggleButton> + </ToggleButtonGroup> + ) + } + </Grid> + <Popover + open={toolOptions.lineWeightPopoverOpen} + anchorEl={toolOptions.popoverLineWeightAnchorEl} + > + <Paper> + <ClickAwayListener onClickAway={handleCloseLineWeight}> + <MenuList autoFocus role="listbox"> + {[1, 3, 5, 10, 50].map((option, index) => ( + <MenuItem + key={option} + onClick={handleLineWeightSelect} + value={option} + selected={option === toolState.strokeWidth} + role="option" + aria-selected={option === toolState.strokeWidth} + > + {option} + </MenuItem> + ))} + </MenuList> + </ClickAwayListener> + </Paper> + </Popover> + <Popover + open={toolOptions.colorPopoverOpen} + anchorEl={toolOptions.popoverAnchorEl} + onClose={closeChooseColor} + > + <SketchPicker + disableAlpha={false} + color={currentColor} + onChangeComplete={updateColor} + /> + </Popover> + </Grid> + ) + } + { + toolState.activeTool === OVERLAY_TOOL.IMAGE && ( + <> + <Grid container> + <ImageFormField xs={8} value={toolState.image} onChange={handleImgChange} /> + </Grid> + <StyledDivButtonImage> + <Button variant="contained" onClick={addImage}> + <AddPhotoAlternateIcon /> + </Button> + </StyledDivButtonImage> + </> + ) + } + { + toolState.activeTool === 'text' && ( + // <TextField + // value={toolState.text + // onChange={(ev) => onChange(ev.target.value)} + // error={imgUrl !== '' && !imgIsValid} + // margin="dense" + // label="Image URL" + // type="url" + // fullWidth + // inputRef={inputRef} + // /> + ' TODO add input' + ) + } + </div> + ); +} + +AnnotationFormOverlayToolOptions.propTypes = { + toolState: PropTypes.shape({ + activeTool: PropTypes.string.isRequired, + closedMode: PropTypes.bool.isRequired, + fillColor: PropTypes.string.isRequired, + image: PropTypes.shape({ + id: PropTypes.string, + }).isRequired, + strokeColor: PropTypes.string.isRequired, + strokeWidth: PropTypes.number.isRequired, + updateColor: PropTypes.func.isRequired, + }).isRequired, + updateToolState: PropTypes.func.isRequired, +}; + +export default AnnotationFormOverlayToolOptions; diff --git a/src/annotationForm/ImageFormField.js b/src/annotationForm/AnnotationFormOverlay/ImageFormField.js similarity index 92% rename from src/annotationForm/ImageFormField.js rename to src/annotationForm/AnnotationFormOverlay/ImageFormField.js index 0606ffe9ef8f965a287f148076a37a7b779fc927..1c2b77f0fa5aca0cb3ee1e2486b4cb0266aab4f6 100644 --- a/src/annotationForm/ImageFormField.js +++ b/src/annotationForm/AnnotationFormOverlay/ImageFormField.js @@ -10,10 +10,11 @@ const StyledRoot = styled('div')(({ theme }) => ({ })); const StyledTextField = styled(TextField)(({ theme }) => ({ - marginTop:"0", - marginBottom:"0", + marginBottom: '0', + marginTop: '0', })); +/** Image input field for the annotation form */ function ImageFormField({ value: image, onChange }) { const inputRef = useRef(null); const [imgIsValid, setImgIsValid] = useState(false); @@ -24,7 +25,7 @@ function ImageFormField({ value: image, onChange }) { } else { setImgIsValid(!!image.id); } -}, [image]); + }, [image]); const imgUrl = image.id === null ? '' : image.id; diff --git a/src/annotationForm/KonvaDrawing/shapes/ArrowNode.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/ArrowNode.js similarity index 100% rename from src/annotationForm/KonvaDrawing/shapes/ArrowNode.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/ArrowNode.js diff --git a/src/annotationForm/KonvaDrawing/shapes/EllipseNode.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/EllipseNode.js similarity index 90% rename from src/annotationForm/KonvaDrawing/shapes/EllipseNode.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/EllipseNode.js index 5f3f813a0fa30cb70d5f9977f9cda178db55c11b..0600dc182b772333c2fc4bc529e76ed7b539ceb5 100644 --- a/src/annotationForm/KonvaDrawing/shapes/EllipseNode.js +++ b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/EllipseNode.js @@ -5,7 +5,7 @@ import { Ellipse, Transformer } from 'react-konva'; function EllipseNode({ onShapeClick, shape, activeTool, isSelected, - onTransform, handleDragEnd + onTransform, handleDragEnd, }) { const shapeRef = useRef(); const trRef = useRef(); @@ -52,14 +52,14 @@ function EllipseNode({ } EllipseNode.propTypes = { - - fill: PropTypes.string, + + fill: PropTypes.string.isRequired, height: PropTypes.number, onShapeClick: PropTypes.func.isRequired, selectedShapeId: PropTypes.string, shape: PropTypes.object.isRequired, - stroke: PropTypes.string, - strokeWidth: PropTypes.number, + stroke: PropTypes.string.isRequired, + strokeWidth: PropTypes.number.isRequired, width: PropTypes.number, x: PropTypes.number, y: PropTypes.number, @@ -71,9 +71,6 @@ EllipseNode.defaultProps = { y: 100, width: 100, height: 100, - fill: 'red', - stroke: 'black', - strokeWidth: 1, }; export default EllipseNode; diff --git a/src/annotationForm/KonvaDrawing/shapes/Freehand.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Freehand.js similarity index 100% rename from src/annotationForm/KonvaDrawing/shapes/Freehand.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Freehand.js diff --git a/src/annotationForm/KonvaDrawing/shapes/Image.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Image.js similarity index 100% rename from src/annotationForm/KonvaDrawing/shapes/Image.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Image.js diff --git a/src/annotationForm/KonvaDrawing/shapes/LineNode.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/LineNode.js similarity index 100% rename from src/annotationForm/KonvaDrawing/shapes/LineNode.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/LineNode.js diff --git a/src/annotationForm/KonvaDrawing/shapes/ParentComponent.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/ParentComponent.js similarity index 91% rename from src/annotationForm/KonvaDrawing/shapes/ParentComponent.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/ParentComponent.js index 3e2f71d72bfc326110ca6bb4c8130befc25bbf32..d4ad210c4d8e11a2250d7a3bd59dcc3207e17ddb 100644 --- a/src/annotationForm/KonvaDrawing/shapes/ParentComponent.js +++ b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/ParentComponent.js @@ -1,22 +1,19 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { Layer } from 'react-konva'; - import Rectangle from './Rectangle'; import EllipseNode from './EllipseNode'; import TextNode from './TextNode'; -import LineNode from './LineNode'; import ArrowNode from './ArrowNode'; import Polygon from './Polygon'; import Freehand from './Freehand'; import ImageShape from './Image'; /** Loads Konva and display in function of their type */ - function ParentComponent({ - shapes, onShapeClick, selectedShapeId, activeTool, + isMouseOverSave, scale, width, height, onTransform, handleDragEnd, - isMouseOverSave,trview + shapes, onShapeClick, selectedShapeId, activeTool, }) { // TODO Simplify these state const [selectedShape, setSelectedShape] = useState(null); @@ -54,11 +51,11 @@ function ParentComponent({ <Rectangle {...{ activeTool, + handleDragEnd, isSelected, onShapeClick: handleShapeClick, - shape, onTransform, - handleDragEnd, + shape, }} key={i} /> @@ -68,11 +65,11 @@ function ParentComponent({ <TextNode {...{ activeTool, + handleDragEnd, isSelected, onShapeClick: handleShapeClick, - shape, onTransform, - handleDragEnd, + shape, }} key={i} /> @@ -82,11 +79,11 @@ function ParentComponent({ <EllipseNode {...{ activeTool, + handleDragEnd, isSelected, onShapeClick: handleShapeClick, - shape, onTransform, - handleDragEnd, + shape, }} key={i} /> @@ -96,11 +93,11 @@ function ParentComponent({ <Freehand {...{ activeTool, + handleDragEnd, isSelected, onShapeClick: handleShapeClick, - shape, onTransform, - handleDragEnd, + shape, }} key={i} /> @@ -110,11 +107,11 @@ function ParentComponent({ <Polygon {...{ activeTool, + handleDragEnd, isSelected, onShapeClick: handleShapeClick, - shape, onTransform, - handleDragEnd, + shape, }} key={i} /> @@ -124,11 +121,11 @@ function ParentComponent({ <ArrowNode {...{ activeTool, + handleDragEnd, isSelected, onShapeClick: handleShapeClick, - shape, onTransform, - handleDragEnd, + shape, }} key={i} /> @@ -138,18 +135,16 @@ function ParentComponent({ <ImageShape {...{ activeTool, + handleDragEnd, isSelected, onShapeClick: handleShapeClick, - shape, onTransform, - handleDragEnd, + shape, src: shape.src, }} key={i} /> ); - default: - return null; } })} </Layer> @@ -157,19 +152,15 @@ function ParentComponent({ } ParentComponent.propTypes = { - shapes: PropTypes.arrayOf(PropTypes.object).isRequired, + activeTool: PropTypes.string.isRequired, + handleDragEnd: PropTypes.func.isRequired, + height: PropTypes.number.isRequired, + isMouseOverSave: PropTypes.bool.isRequired, onShapeClick: PropTypes.func.isRequired, - selectedShapeIdProp: PropTypes.string, - activeTool: PropTypes.string, - scale: PropTypes.number, - width: PropTypes.number, - height: PropTypes.number, - onTransform: PropTypes.func, - handleDragEnd: PropTypes.func, -}; - -ParentComponent.defaultProps = { - selectedShapeIdProp: null, + onTransform: PropTypes.func.isRequired, + scale: PropTypes.number.isRequired, + selectedShapeId: PropTypes.string.isRequired, + shapes: PropTypes.arrayOf(PropTypes.object).isRequired, + width: PropTypes.number.isRequired, }; - export default ParentComponent; diff --git a/src/annotationForm/KonvaDrawing/shapes/Polygon.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Polygon.js similarity index 100% rename from src/annotationForm/KonvaDrawing/shapes/Polygon.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Polygon.js diff --git a/src/annotationForm/KonvaDrawing/shapes/Rectangle.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Rectangle.js similarity index 100% rename from src/annotationForm/KonvaDrawing/shapes/Rectangle.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Rectangle.js diff --git a/src/annotationForm/KonvaDrawing/shapes/TextNode.js b/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/TextNode.js similarity index 100% rename from src/annotationForm/KonvaDrawing/shapes/TextNode.js rename to src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/TextNode.js