@abidibo/react-cam-roi 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +279 -16
  2. package/dist/Components/Modal/Modal.module.css +85 -0
  3. package/dist/Components/Modal/index.d.ts +9 -0
  4. package/dist/Components/Modal/index.js +16 -0
  5. package/dist/Components/RoiEditor/Canvas.js +2 -4
  6. package/dist/Components/RoiEditor/Hooks.d.ts +3 -4
  7. package/dist/Components/RoiEditor/Hooks.js +64 -16
  8. package/dist/Components/RoiEditor/ParametersModalForm.d.ts +5 -0
  9. package/dist/Components/RoiEditor/ParametersModalForm.js +8 -0
  10. package/dist/Components/RoiEditor/Polygon.d.ts +3 -2
  11. package/dist/Components/RoiEditor/Polygon.js +18 -0
  12. package/dist/Components/RoiEditor/Polyline.d.ts +12 -1
  13. package/dist/Components/RoiEditor/Polyline.js +18 -0
  14. package/dist/Components/RoiEditor/Rectangle.d.ts +17 -1
  15. package/dist/Components/RoiEditor/Rectangle.js +23 -1
  16. package/dist/Components/RoiEditor/ShapesList.d.ts +2 -0
  17. package/dist/Components/RoiEditor/ShapesList.js +34 -0
  18. package/dist/Components/RoiEditor/ShapesList.module.css +36 -0
  19. package/dist/Components/RoiEditor/Toolbar.js +10 -6
  20. package/dist/Components/RoiEditor/Toolbar.module.css +28 -0
  21. package/dist/Components/RoiEditor/Types.d.ts +47 -1
  22. package/dist/Components/RoiEditor/Types.js +15 -1
  23. package/dist/Components/RoiEditor/Utils.d.ts +5 -0
  24. package/dist/Components/RoiEditor/Utils.js +37 -0
  25. package/dist/Components/RoiEditor/index.d.ts +2 -0
  26. package/dist/Components/RoiEditor/index.js +4 -5
  27. package/dist/Components/Typography/index.d.ts +3 -0
  28. package/dist/Components/Typography/index.js +3 -2
  29. package/dist/Icons/AnnotateIcon.d.ts +6 -0
  30. package/dist/Icons/AnnotateIcon.js +5 -0
  31. package/dist/Icons/CloseIcon.d.ts +6 -0
  32. package/dist/Icons/CloseIcon.js +5 -0
  33. package/dist/Icons/CopyIcon.d.ts +6 -0
  34. package/dist/Icons/CopyIcon.js +5 -0
  35. package/dist/Providers/EditorProvider.d.ts +3 -2
  36. package/dist/Providers/EditorProvider.js +2 -2
  37. package/dist/Providers/UiProvider.d.ts +21 -2
  38. package/dist/Providers/UiProvider.js +26 -2
  39. package/dist/index.d.ts +2 -1
  40. package/dist/index.js +2 -1
  41. package/package.json +7 -3
  42. package/dist/Components/RoiEditor/Metadata.d.ts +0 -2
  43. package/dist/Components/RoiEditor/Metadata.js +0 -31
  44. package/dist/Components/RoiEditor/Metadata.module.css +0 -34
@@ -1,8 +1,11 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
1
+ import { useCallback, useContext, useEffect, useRef, useState } from 'react';
2
+ import { useEditorContext } from '../../Providers/EditorProvider';
3
+ import { UiContext } from '../../Providers/UiProvider';
2
4
  import Dispatcher from '../../Utils/Dispatcher';
3
- import { handleDoubleClickPolygon, handleMouseDownPolygon, handleMouseMovePolygon } from './Polygon';
4
- import { handleDoubleClickPolyline, handleMouseDownPolyline, handleMouseMovePolyline } from './Polyline';
5
- import { handleMouseDownRect, handleMouseMoveRect, handleMouseUpRect } from './Rectangle';
5
+ import { copyPolygon, handleDoubleClickPolygon, handleMouseDownPolygon, handleMouseMovePolygon } from './Polygon';
6
+ import { copyPolyline, handleDoubleClickPolyline, handleMouseDownPolyline, handleMouseMovePolyline } from './Polyline';
7
+ import { copyRectangle, handleMouseDownRect, handleMouseMoveRect, handleMouseUpRect } from './Rectangle';
8
+ import { canDrawShape } from './Utils';
6
9
  export const useImageSize = (imageUrl) => {
7
10
  const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
8
11
  useEffect(() => {
@@ -47,7 +50,9 @@ export const useCanvasSize = (imageUrl) => {
47
50
  }, [imageSize, wrapperRef.current]); // eslint-disable-line
48
51
  return { imageSize, canvasSize, wrapperRef, isReady };
49
52
  };
50
- export const useTool = (tool, activeColor, addShape, canvas) => {
53
+ export const useTool = (canvas) => {
54
+ const { configuration, activeTool, activeColor, shapes, addShape } = useEditorContext();
55
+ const { notify, strings } = useContext(UiContext);
51
56
  const [isDrawing, setIsDrawing] = useState(false);
52
57
  const [shape, setShape] = useState(null);
53
58
  const [originX, setOriginX] = useState(0);
@@ -68,7 +73,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
68
73
  if (!canvas) {
69
74
  return;
70
75
  }
71
- if (tool === "pointer" /* ToolEnum.Pointer */) {
76
+ if (activeTool === "pointer" /* ToolEnum.Pointer */) {
72
77
  // enable selection
73
78
  canvas.selection = true;
74
79
  canvas.getObjects().forEach((object) => {
@@ -84,14 +89,20 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
84
89
  canvas.renderAll();
85
90
  }
86
91
  const handleMouseDown = (event) => {
87
- switch (tool) {
88
- case "rectangle" /* ToolEnum.Rectangle */:
92
+ switch (activeTool) {
93
+ case "rect" /* ToolEnum.Rectangle */:
94
+ if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
95
+ return;
89
96
  handleMouseDownRect(event, canvas, activeColor, setOriginX, setOriginY, setShape, setIsDrawing);
90
97
  break;
91
98
  case "polygon" /* ToolEnum.Polygon */:
99
+ if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
100
+ return;
92
101
  handleMouseDownPolygon(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
93
102
  break;
94
103
  case "polyline" /* ToolEnum.Polyline */:
104
+ if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
105
+ return;
95
106
  handleMouseDownPolyline(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
96
107
  break;
97
108
  default:
@@ -99,8 +110,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
99
110
  }
100
111
  };
101
112
  const handleMouseMove = (event) => {
102
- switch (tool) {
103
- case "rectangle" /* ToolEnum.Rectangle */:
113
+ switch (activeTool) {
114
+ case "rect" /* ToolEnum.Rectangle */:
104
115
  handleMouseMoveRect(event, canvas, originX, originY, shape, isDrawing);
105
116
  break;
106
117
  case "polygon" /* ToolEnum.Polygon */:
@@ -114,8 +125,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
114
125
  }
115
126
  };
116
127
  const handleMouseUp = () => {
117
- switch (tool) {
118
- case "rectangle" /* ToolEnum.Rectangle */:
128
+ switch (activeTool) {
129
+ case "rect" /* ToolEnum.Rectangle */:
119
130
  handleMouseUpRect(canvas, setIsDrawing, shape, setShape, addShape);
120
131
  break;
121
132
  default:
@@ -123,7 +134,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
123
134
  }
124
135
  };
125
136
  const handleDoubleClick = () => {
126
- switch (tool) {
137
+ switch (activeTool) {
127
138
  case "polygon" /* ToolEnum.Polygon */:
128
139
  handleDoubleClickPolygon(canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines, addShape);
129
140
  break;
@@ -151,7 +162,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
151
162
  canvas.off('selection:cleared', handleSelectionCleared);
152
163
  };
153
164
  }, [
154
- tool,
165
+ activeTool,
155
166
  activeColor,
156
167
  isDrawing,
157
168
  shape,
@@ -164,9 +175,15 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
164
175
  addShape,
165
176
  handleObjectSelected,
166
177
  handleSelectionCleared,
178
+ configuration,
179
+ notify,
180
+ strings,
181
+ shapes,
167
182
  ]);
168
183
  };
169
- export const useDispatcherEvents = (canvas, setActiveTool) => {
184
+ export const useDispatcherEvents = (canvas) => {
185
+ const { configuration, shapes, addShape, setActiveTool } = useEditorContext();
186
+ const { notify, strings } = useContext(UiContext);
170
187
  useEffect(() => {
171
188
  const removeShape = (_, id) => {
172
189
  const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
@@ -174,6 +191,35 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
174
191
  canvas === null || canvas === void 0 ? void 0 : canvas.remove(obj);
175
192
  }
176
193
  };
194
+ const copyShape = (_, id) => {
195
+ const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
196
+ let copy;
197
+ switch (obj === null || obj === void 0 ? void 0 : obj.type) {
198
+ case "polygon" /* ToolEnum.Polygon */:
199
+ if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
200
+ return;
201
+ copy = copyPolygon(canvas, obj, addShape);
202
+ // @ts-expect-error id exists but his stupid ts does not know
203
+ Dispatcher.emit('canvas:selectShape', copy.id);
204
+ break;
205
+ case "polyline" /* ToolEnum.Polyline */:
206
+ if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
207
+ return;
208
+ copy = copyPolyline(canvas, obj, addShape);
209
+ // @ts-expect-error id exists but his stupid ts does not know
210
+ Dispatcher.emit('canvas:selectShape', copy.id);
211
+ break;
212
+ case "rect" /* ToolEnum.Rectangle */:
213
+ if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
214
+ return;
215
+ copy = copyRectangle(canvas, obj, addShape);
216
+ // @ts-expect-error id exists but his stupid ts does not know
217
+ Dispatcher.emit('canvas:selectShape', copy.id);
218
+ break;
219
+ default:
220
+ break;
221
+ }
222
+ };
177
223
  const selectShape = (_, id) => {
178
224
  const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
179
225
  if (obj) {
@@ -184,10 +230,12 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
184
230
  }
185
231
  };
186
232
  Dispatcher.register('canvas:removeShape', removeShape);
233
+ Dispatcher.register('canvas:copyShape', copyShape);
187
234
  Dispatcher.register('canvas:selectShape', selectShape);
188
235
  return () => {
189
236
  Dispatcher.unregister('canvas:removeShape', removeShape);
237
+ Dispatcher.unregister('canvas:copyShape', copyShape);
190
238
  Dispatcher.unregister('canvas:selectShape', selectShape);
191
239
  };
192
- }, [setActiveTool, canvas]);
240
+ }, [setActiveTool, canvas, addShape, configuration, shapes, notify, strings]);
193
241
  };
@@ -0,0 +1,5 @@
1
+ export type ParametersModalFormProps = {
2
+ onClose: () => void;
3
+ };
4
+ declare const ParametersModalForm: React.FC<ParametersModalFormProps>;
5
+ export default ParametersModalForm;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext } from 'react';
3
+ import { UiContext } from '../../Providers/UiProvider';
4
+ const ParametersModalForm = ({ onClose }) => {
5
+ const { Modal, strings, Typography } = useContext(UiContext);
6
+ return (_jsx(Modal, { onClose: onClose, title: strings.mainParametersMetadata, isOpen: true, size: "lg", children: _jsx(Typography, { children: "Here comes the dynamic parameters form" }) }));
7
+ };
8
+ export default ParametersModalForm;
@@ -1,5 +1,5 @@
1
1
  import * as fabric from 'fabric';
2
- import { FabricEvent, ShapeType } from './Types';
2
+ import { FabricEvent, IAddShape } from './Types';
3
3
  export declare const handleMouseDownPolygon: (event: FabricEvent, canvas: fabric.Canvas, activeColor: string, setIsDrawing: (v: boolean) => void, points: {
4
4
  x: number;
5
5
  y: number;
@@ -14,4 +14,5 @@ export declare const handleDoubleClickPolygon: (canvas: fabric.Canvas, activeCol
14
14
  }[], setPoints: (v: {
15
15
  x: number;
16
16
  y: number;
17
- }[]) => void, lines: fabric.Line[], setLines: (v: fabric.Line[]) => void, addShape: (id: string, type: ShapeType, shape: fabric.Polygon) => void) => void;
17
+ }[]) => void, lines: fabric.Line[], setLines: (v: fabric.Line[]) => void, addShape: IAddShape) => void;
18
+ export declare const copyPolygon: (canvas: fabric.Canvas, polygon: fabric.Polygon, addShape: IAddShape) => fabric.Polygon;
@@ -56,3 +56,21 @@ export const handleDoubleClickPolygon = (canvas, activeColor, setIsDrawing, poin
56
56
  setIsDrawing(false);
57
57
  }
58
58
  };
59
+ export const copyPolygon = (canvas, polygon, addShape) => {
60
+ const id = uuidv4();
61
+ const copy = new fabric.Polygon(polygon.points, {
62
+ top: polygon.top + 10,
63
+ left: polygon.left + 10,
64
+ fill: 'transparent',
65
+ stroke: polygon.stroke,
66
+ strokeWidth: polygon.strokeWidth,
67
+ selectable: false,
68
+ hasControls: true,
69
+ hoverCursor: 'default',
70
+ // @ts-expect-error id is not included in types but the property is added and it works
71
+ id,
72
+ });
73
+ canvas.add(copy);
74
+ addShape(id, "polygon" /* ToolEnum.Polygon */, copy);
75
+ return copy;
76
+ };
@@ -1,5 +1,5 @@
1
1
  import * as fabric from 'fabric';
2
- import { FabricEvent, ShapeType } from './Types';
2
+ import { FabricEvent, IAddShape, ShapeType } from './Types';
3
3
  export declare const handleMouseDownPolyline: (event: FabricEvent, canvas: fabric.Canvas, activeColor: string, setIsDrawing: (v: boolean) => void, points: {
4
4
  x: number;
5
5
  y: number;
@@ -15,3 +15,14 @@ export declare const handleDoubleClickPolyline: (canvas: fabric.Canvas, activeCo
15
15
  x: number;
16
16
  y: number;
17
17
  }[]) => void, lines: fabric.Line[], setLines: (v: fabric.Line[]) => void, addShape: (id: string, type: ShapeType, shape: fabric.Polyline) => void) => void;
18
+ export declare const copyPolyline: (canvas: fabric.Canvas, polyline: fabric.Polyline, addShape: IAddShape) => fabric.Polyline<{
19
+ top: number;
20
+ left: number;
21
+ fill: string;
22
+ stroke: string | fabric.TFiller | null;
23
+ strokeWidth: number;
24
+ selectable: false;
25
+ hasControls: true;
26
+ hoverCursor: string;
27
+ id: string;
28
+ }, fabric.SerializedPolylineProps, fabric.ObjectEvents>;
@@ -37,6 +37,7 @@ export const handleDoubleClickPolyline = (canvas, activeColor, setIsDrawing, poi
37
37
  if (points.length > 2) {
38
38
  const id = uuidv4();
39
39
  const polyline = new fabric.Polyline(points, {
40
+ fill: 'transparent',
40
41
  stroke: activeColor,
41
42
  strokeWidth: 2,
42
43
  selectable: false,
@@ -54,3 +55,20 @@ export const handleDoubleClickPolyline = (canvas, activeColor, setIsDrawing, poi
54
55
  setIsDrawing(false);
55
56
  }
56
57
  };
58
+ export const copyPolyline = (canvas, polyline, addShape) => {
59
+ const id = uuidv4();
60
+ const copy = new fabric.Polyline(polyline.points, {
61
+ top: polyline.top + 10,
62
+ left: polyline.left + 10,
63
+ fill: 'transparent',
64
+ stroke: polyline.stroke,
65
+ strokeWidth: polyline.strokeWidth,
66
+ selectable: false,
67
+ hasControls: true,
68
+ hoverCursor: 'default',
69
+ id,
70
+ });
71
+ canvas.add(copy);
72
+ addShape(id, "polyline" /* ToolEnum.Polyline */, copy);
73
+ return copy;
74
+ };
@@ -1,5 +1,21 @@
1
1
  import * as fabric from 'fabric';
2
- import { FabricEvent, Shape, ShapeType } from './Types';
2
+ import { FabricEvent, IAddShape, Shape, ShapeType } from './Types';
3
3
  export declare const handleMouseDownRect: (event: FabricEvent, canvas: fabric.Canvas, activeColor: string, setOriginX: (v: number) => void, setOriginY: (v: number) => void, setShape: (v: Shape) => void, setIsDrawing: (v: boolean) => void) => void;
4
4
  export declare const handleMouseMoveRect: (event: FabricEvent, canvas: fabric.Canvas, originX: number, originY: number, shape: Shape, isDrawing: boolean) => void;
5
5
  export declare const handleMouseUpRect: (canvas: fabric.Canvas, setIsDrawing: (v: boolean) => void, shape: Shape, setShape: (v: Shape | null) => void, addShape: (id: string, type: ShapeType, shape: Shape) => void) => void;
6
+ export declare const copyRectangle: (canvas: fabric.Canvas, rectangle: fabric.Rect, addShape: IAddShape) => fabric.Rect<{
7
+ left: number;
8
+ top: number;
9
+ originX: "left";
10
+ originY: "top";
11
+ width: number;
12
+ height: number;
13
+ fill: string;
14
+ stroke: string | fabric.TFiller | null;
15
+ strokeWidth: number;
16
+ strokeUniform: true;
17
+ selectable: false;
18
+ hasControls: true;
19
+ hoverCursor: string;
20
+ id: string;
21
+ }, fabric.SerializedRectProps, fabric.ObjectEvents>;
@@ -44,7 +44,29 @@ export const handleMouseMoveRect = (event, canvas, originX, originY, shape, isDr
44
44
  export const handleMouseUpRect = (canvas, setIsDrawing, shape, setShape, addShape) => {
45
45
  setIsDrawing(false);
46
46
  shape.setCoords();
47
- addShape(shape.id, "rectangle" /* ToolEnum.Rectangle */, shape);
47
+ addShape(shape.id, "rect" /* ToolEnum.Rectangle */, shape);
48
48
  setShape(null);
49
49
  canvas.defaultCursor = 'default';
50
50
  };
51
+ export const copyRectangle = (canvas, rectangle, addShape) => {
52
+ const id = uuidv4();
53
+ const copy = new fabric.Rect({
54
+ left: rectangle.left + 10,
55
+ top: rectangle.top + 10,
56
+ originX: 'left',
57
+ originY: 'top',
58
+ width: rectangle.width,
59
+ height: rectangle.height,
60
+ fill: 'transparent',
61
+ stroke: rectangle.stroke,
62
+ strokeWidth: rectangle.strokeWidth,
63
+ strokeUniform: true,
64
+ selectable: false,
65
+ hasControls: true,
66
+ hoverCursor: 'default',
67
+ id,
68
+ });
69
+ canvas.add(copy);
70
+ addShape(id, "rect" /* ToolEnum.Rectangle */, copy);
71
+ return copy;
72
+ };
@@ -0,0 +1,2 @@
1
+ declare const ShapesList: React.FC;
2
+ export default ShapesList;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useContext, useEffect, useState } from 'react';
3
+ import { useEditorContext } from '../../Providers/EditorProvider';
4
+ import { UiContext } from '../../Providers/UiProvider';
5
+ import { css } from '../../Utils';
6
+ import Dispatcher from '../../Utils/Dispatcher';
7
+ import styles from './ShapesList.module.css';
8
+ const ShapesList = () => {
9
+ const { strings, Typography, IconButton, DeleteIcon, EditIcon, SelectIcon, CopyIcon, themeMode } = useContext(UiContext);
10
+ const { shapes, removeShape } = useEditorContext();
11
+ const [selected, setSelected] = useState([]);
12
+ useEffect(() => {
13
+ const setSelectedShapes = (_, event) => { var _a; return setSelected((_a = event === null || event === void 0 ? void 0 : event.map((s) => s.id)) !== null && _a !== void 0 ? _a : []); };
14
+ Dispatcher.register('canvas:shapeSelected', setSelectedShapes);
15
+ return () => {
16
+ Dispatcher.unregister('canvas:shapeSelected', setSelectedShapes);
17
+ };
18
+ }, [shapes]);
19
+ const handleCopyShape = (id) => () => {
20
+ Dispatcher.emit('canvas:copyShape', id);
21
+ };
22
+ const handleRemoveShape = (id) => () => {
23
+ Dispatcher.emit('canvas:removeShape', id);
24
+ removeShape(id);
25
+ };
26
+ const handleSelectShape = (id) => () => {
27
+ Dispatcher.emit('canvas:selectShape', id);
28
+ };
29
+ const iconColor = themeMode === 'light' ? 'black' : 'white';
30
+ return (_jsxs("table", { className: css('shapes-table', styles, themeMode), children: [Object.keys(shapes).length > 0 && (_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: _jsx(Typography, { children: strings.id }) }), _jsx("th", { children: _jsx(Typography, { children: strings.type }) }), _jsx("th", {})] }) })), _jsx("tbody", { children: Object.keys(shapes).map((id) => {
31
+ return (_jsxs("tr", { className: selected.indexOf(id) > -1 ? css('shapes-row-selected', styles, themeMode) : '', children: [_jsx("td", { children: _jsx("div", { children: _jsx(Typography, { children: id.substring(0, 6) }) }) }), _jsx("td", { children: _jsx(Typography, { children: strings[shapes[id].type] }) }), _jsxs("td", { children: [_jsx(IconButton, { onClick: handleSelectShape(id), children: _jsx(SelectIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleCopyShape(id), children: _jsx(CopyIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleRemoveShape(id), children: _jsx(EditIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleRemoveShape(id), children: _jsx(DeleteIcon, { color: iconColor }) })] })] }, id));
32
+ }) })] }));
33
+ };
34
+ export default ShapesList;
@@ -0,0 +1,36 @@
1
+ .shapes-table {
2
+ border-collapse: collapse;
3
+ width: 100%;
4
+ }
5
+
6
+ .shapes-table th {
7
+ padding: .8rem .3rem .3rem;
8
+ text-align: left;
9
+ }
10
+
11
+ .shapes-table td {
12
+ padding: 0 .3rem;
13
+ }
14
+
15
+ .shapes-table tr td:last-child,
16
+ .shapes-table tr th:last-child {
17
+ text-align: right;
18
+ }
19
+
20
+ .shapes-table-light {
21
+ background-color: #eee;
22
+ color: #000;
23
+ }
24
+
25
+ .shapes-table-dark {
26
+ background-color: #333;
27
+ color: #fff;
28
+ }
29
+
30
+ .shapes-row-selected-light {
31
+ background-color: #ccc;
32
+ }
33
+
34
+ .shapes-row-selected-dark {
35
+ background-color: #666;
36
+ }
@@ -1,5 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useContext } from 'react';
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useContext, useState } from 'react';
3
+ import AnnotateIcon from '../../Icons/AnnotateIcon';
3
4
  import PointerIcon from '../../Icons/PointerIcon';
4
5
  import PolygonIcon from '../../Icons/PolygonIcon';
5
6
  import PolylineIcon from '../../Icons/PolylineIcon';
@@ -7,13 +8,16 @@ import RectangleIcon from '../../Icons/RectangleIcon';
7
8
  import { useEditorContext } from '../../Providers/EditorProvider';
8
9
  import { UiContext } from '../../Providers/UiProvider';
9
10
  import { css } from '../../Utils';
10
- import styles from './Toolbar.module.css';
11
11
  import ColorPicker from './ColorPicker';
12
+ import ParametersModalForm from './ParametersModalForm';
13
+ import styles from './Toolbar.module.css';
14
+ import { enableMainMetadata, enableRois } from './Utils';
12
15
  const Toolbar = () => {
13
- const { IconButton, themeMode, primaryColor } = useContext(UiContext);
14
- const { activeTool, setActiveTool } = useEditorContext();
16
+ const { IconButton, themeMode, primaryColor, Typography, strings } = useContext(UiContext);
17
+ const { activeTool, setActiveTool, configuration } = useEditorContext();
18
+ const [form, setForm] = useState({ isOpen: false, data: [] });
15
19
  const iconColor = (tool) => (tool === activeTool ? primaryColor : themeMode === 'light' ? 'black' : 'white');
16
20
  const setTool = (tool) => () => setActiveTool(tool);
17
- return (_jsxs("div", { className: css('toolbar', styles, themeMode), children: [_jsx(IconButton, { onClick: setTool("pointer" /* ToolEnum.Pointer */), children: _jsx(PointerIcon, { color: iconColor("pointer" /* ToolEnum.Pointer */) }) }), _jsx(IconButton, { onClick: setTool("polyline" /* ToolEnum.Polyline */), children: _jsx(PolylineIcon, { color: iconColor("polyline" /* ToolEnum.Polyline */) }) }), _jsx(IconButton, { onClick: setTool("polygon" /* ToolEnum.Polygon */), children: _jsx(PolygonIcon, { color: iconColor("polygon" /* ToolEnum.Polygon */) }) }), _jsx(IconButton, { onClick: setTool("rectangle" /* ToolEnum.Rectangle */), children: _jsx(RectangleIcon, { color: iconColor("rectangle" /* ToolEnum.Rectangle */) }) }), _jsx(ColorPicker, { style: { marginLeft: 'auto' } })] }));
21
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css('toolbar', styles, themeMode), children: [enableRois(configuration) && (_jsxs(_Fragment, { children: [_jsx(IconButton, { onClick: setTool("pointer" /* ToolEnum.Pointer */), children: _jsx(PointerIcon, { color: iconColor("pointer" /* ToolEnum.Pointer */) }) }), _jsx(IconButton, { onClick: setTool("polyline" /* ToolEnum.Polyline */), children: _jsx(PolylineIcon, { color: iconColor("polyline" /* ToolEnum.Polyline */) }) }), _jsx(IconButton, { onClick: setTool("polygon" /* ToolEnum.Polygon */), children: _jsx(PolygonIcon, { color: iconColor("polygon" /* ToolEnum.Polygon */) }) }), _jsx(IconButton, { onClick: setTool("rect" /* ToolEnum.Rectangle */), children: _jsx(RectangleIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) }), _jsx(ColorPicker, { style: { marginLeft: 'auto' } })] })), enableRois(configuration) && enableMainMetadata(configuration) && (_jsx("div", { className: css('toolbar-spacer', styles, themeMode) })), enableMainMetadata(configuration) && (_jsx(IconButton, { onClick: () => setForm({ isOpen: true, data: configuration.parameters }), children: _jsx(AnnotateIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) }))] }), _jsx("div", { className: css('toolbar-helper', styles, themeMode), children: _jsxs(Typography, { children: [strings[activeTool], ": ", strings[`${activeTool}HelpText`]] }) }), form.isOpen && _jsx(ParametersModalForm, { onClose: () => setForm({ isOpen: false, data: [] }) })] }));
18
22
  };
19
23
  export default Toolbar;
@@ -11,3 +11,31 @@
11
11
  .toolbar-dark {
12
12
  background-color: #333;
13
13
  }
14
+
15
+ .toolbar-helper {
16
+ padding: .5rem;
17
+ }
18
+
19
+ .toolbar-helper-light {
20
+ background-color: #f7f7f7;;
21
+ color: #000;
22
+ }
23
+
24
+ .toolbar-helper-dark {
25
+ background-color: #222;
26
+ color: #fff;
27
+ }
28
+
29
+ .toolbar-spacer {
30
+ height: 20px;
31
+ margin: 0 .5rem 0 1rem;
32
+ width: 1px;
33
+ }
34
+
35
+ .toolbar-spacer-light {
36
+ background-color: #ccc;
37
+ }
38
+
39
+ .toolbar-spacer-dark {
40
+ background-color: #555;
41
+ }
@@ -3,7 +3,7 @@ export declare const enum ToolEnum {
3
3
  Pointer = "pointer",
4
4
  Polyline = "polyline",
5
5
  Polygon = "polygon",
6
- Rectangle = "rectangle"
6
+ Rectangle = "rect"
7
7
  }
8
8
  export type ShapeType = ToolEnum.Polyline | ToolEnum.Polygon | ToolEnum.Rectangle;
9
9
  export type Shape = (fabric.Rect | fabric.Polygon | fabric.Polyline) & {
@@ -17,3 +17,49 @@ export type FabricEvent = fabric.TPointerEventInfo<fabric.TPointerEvent>;
17
17
  export type FabricSelectionEvent = Partial<fabric.TEvent> & {
18
18
  selected: fabric.Object[];
19
19
  };
20
+ export type IAddShape = (id: string, type: ShapeType, shape: Shape) => void;
21
+ export declare enum DataTypeEnum {
22
+ Integer = "int",
23
+ Float = "float",
24
+ String = "string",
25
+ Boolean = "bool"
26
+ }
27
+ export declare enum OperatorEnum {
28
+ Lt = "lt",
29
+ Lte = "lte",
30
+ Gt = "gt",
31
+ Gte = "gte",
32
+ Eq = "eq"
33
+ }
34
+ export type ConfigurationParameter = {
35
+ codename: string;
36
+ label: string;
37
+ description: string;
38
+ unit: string;
39
+ type: DataTypeEnum;
40
+ options: {
41
+ value: number | string | boolean;
42
+ label: string;
43
+ }[];
44
+ required: boolean;
45
+ value: number | string | boolean | null;
46
+ };
47
+ export type RoiConfiguration = {
48
+ role: string;
49
+ type: Omit<ShapeType, 'pointer'>;
50
+ multiplicity: {
51
+ operator: OperatorEnum;
52
+ threshold: number;
53
+ };
54
+ parameters: ConfigurationParameter[];
55
+ };
56
+ export type Configuration = {
57
+ parameters: ConfigurationParameter[];
58
+ rois: RoiConfiguration[];
59
+ };
60
+ export interface INotify {
61
+ info: (message: string) => void;
62
+ warn: (message: string) => void;
63
+ error: (message: string) => void;
64
+ success: (message: string) => void;
65
+ }
@@ -1 +1,15 @@
1
- export {};
1
+ export var DataTypeEnum;
2
+ (function (DataTypeEnum) {
3
+ DataTypeEnum["Integer"] = "int";
4
+ DataTypeEnum["Float"] = "float";
5
+ DataTypeEnum["String"] = "string";
6
+ DataTypeEnum["Boolean"] = "bool";
7
+ })(DataTypeEnum || (DataTypeEnum = {}));
8
+ export var OperatorEnum;
9
+ (function (OperatorEnum) {
10
+ OperatorEnum["Lt"] = "lt";
11
+ OperatorEnum["Lte"] = "lte";
12
+ OperatorEnum["Gt"] = "gt";
13
+ OperatorEnum["Gte"] = "gte";
14
+ OperatorEnum["Eq"] = "eq";
15
+ })(OperatorEnum || (OperatorEnum = {}));
@@ -0,0 +1,5 @@
1
+ import { Configuration, INotify, Shapes, ToolEnum } from './Types';
2
+ export declare const notify: INotify;
3
+ export declare const enableRois: (configuration: Configuration) => boolean;
4
+ export declare const enableMainMetadata: (configuration: Configuration) => boolean;
5
+ export declare const canDrawShape: (configuration: Configuration, shapeType: Omit<ToolEnum, ToolEnum.Pointer>, shapes: Shapes, notify: INotify, message: string) => boolean;
@@ -0,0 +1,37 @@
1
+ import { OperatorEnum } from './Types';
2
+ export const notify = {
3
+ info: (message) => alert(`Info: ${message}`),
4
+ warn: (message) => alert(`Warning: ${message}`),
5
+ error: (message) => alert(`Error: ${message}`),
6
+ success: (message) => alert(`Success: ${message}`),
7
+ };
8
+ export const enableRois = (configuration) => {
9
+ return configuration.rois.length > 0;
10
+ };
11
+ export const enableMainMetadata = (configuration) => {
12
+ return configuration.parameters.length > 0;
13
+ };
14
+ export const canDrawShape = (configuration, shapeType, shapes, notify, message) => {
15
+ const rule = configuration.rois.find((roi) => roi.type === shapeType);
16
+ // no rule
17
+ if (!rule || !rule.multiplicity || Object.keys(rule.multiplicity).length === 0) {
18
+ return true;
19
+ }
20
+ const currentShapeCount = Object.values(shapes).filter((s) => s.type === shapeType).length;
21
+ let res = true;
22
+ switch (rule.multiplicity.operator) {
23
+ case OperatorEnum.Eq:
24
+ case OperatorEnum.Lte:
25
+ res = currentShapeCount + 1 <= rule.multiplicity.threshold;
26
+ break;
27
+ case OperatorEnum.Lt:
28
+ res = currentShapeCount + 1 < rule.multiplicity.threshold;
29
+ break;
30
+ default:
31
+ return true;
32
+ }
33
+ if (!res && notify) {
34
+ notify.warn(message);
35
+ }
36
+ return res;
37
+ };
@@ -1,5 +1,7 @@
1
+ import { Configuration } from './Types';
1
2
  export type RoiEditorProps = {
2
3
  imageUrl: string;
4
+ configuration: Configuration;
3
5
  };
4
6
  declare const RoiEditor: React.FC<RoiEditorProps>;
5
7
  export default RoiEditor;
@@ -6,12 +6,11 @@ import { css, log } from '../../Utils';
6
6
  import { Loader } from '../Loader';
7
7
  import Canvas from './Canvas';
8
8
  import { useCanvasSize } from './Hooks';
9
- import Metadata from './Metadata';
9
+ import ShapesList from './ShapesList';
10
10
  import styles from './RoiEditor.module.css';
11
11
  import Toolbar from './Toolbar';
12
- // https://medium.com/@na.mazaheri/dynamically-drawing-shapes-on-canvas-with-fabric-js-in-react-js-8b9c42791903
13
12
  // https://github.com/n-mazaheri/image-editor
14
- const RoiEditor = ({ imageUrl }) => {
13
+ const RoiEditor = ({ imageUrl, configuration }) => {
15
14
  const { themeMode, enableLogs, pickerColors } = useContext(UiContext);
16
15
  const { imageSize, canvasSize, wrapperRef, isReady } = useCanvasSize(imageUrl);
17
16
  const [activeTool, setActiveTool] = useState("pointer" /* ToolEnum.Pointer */);
@@ -28,10 +27,10 @@ const RoiEditor = ({ imageUrl }) => {
28
27
  if (!isReady) {
29
28
  return _jsx(Loader, {});
30
29
  }
31
- return (_jsx(EditorProvider, { activeTool: activeTool, setActiveTool: setActiveTool, activeColor: activeColor, setActiveColor: setActiveColor, shapes: shapes, addShape: addShape, removeShape: removeShape, children: _jsxs("div", { style: { maxWidth: '100%', width: `${imageSize.width}px` }, ref: wrapperRef, children: [_jsx(Toolbar, {}), _jsx("div", { className: css('canvasWrapper', styles, themeMode), style: {
30
+ return (_jsx(EditorProvider, { activeTool: activeTool, setActiveTool: setActiveTool, activeColor: activeColor, setActiveColor: setActiveColor, shapes: shapes, addShape: addShape, removeShape: removeShape, configuration: configuration, children: _jsxs("div", { style: { maxWidth: '100%', width: `${imageSize.width}px` }, ref: wrapperRef, children: [_jsx(Toolbar, {}), _jsx("div", { className: css('canvasWrapper', styles, themeMode), style: {
32
31
  width: `${canvasSize.width}px`,
33
32
  height: `${canvasSize.height}px`,
34
33
  backgroundImage: `url(${imageUrl})`,
35
- }, children: _jsx(Canvas, { canvasSize: canvasSize }) }), _jsx(Metadata, {})] }) }));
34
+ }, children: _jsx(Canvas, { canvasSize: canvasSize }) }), _jsx(ShapesList, {})] }) }));
36
35
  };
37
36
  export default RoiEditor;
@@ -1,5 +1,8 @@
1
1
  export type TypographyProps = {
2
2
  children?: React.ReactNode;
3
+ className?: string;
4
+ variant?: any;
5
+ component?: any;
3
6
  };
4
7
  declare const Typography: React.FC<TypographyProps>;
5
8
  export default Typography;
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- const Typography = ({ children }) => {
3
- return _jsx("span", { children: children });
2
+ const Typography = ({ children, className = '', component = 'span' }) => {
3
+ const Tag = component;
4
+ return _jsx(Tag, { className: className, children: children });
4
5
  };
5
6
  export default Typography;
@@ -0,0 +1,6 @@
1
+ type AnnotateIconProps = {
2
+ color?: string;
3
+ style?: React.CSSProperties;
4
+ };
5
+ declare const AnnotateIcon: React.FC<AnnotateIconProps>;
6
+ export default AnnotateIcon;