@abidibo/react-cam-roi 0.0.7 → 0.0.10
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.
- package/README.md +588 -29
- package/dist/Components/BoolField/BoolField.module.css +60 -0
- package/dist/Components/BoolField/index.d.ts +5 -0
- package/dist/Components/BoolField/index.js +13 -0
- package/dist/Components/Button/Button.module.css +27 -0
- package/dist/Components/Button/index.d.ts +8 -0
- package/dist/Components/Button/index.js +15 -0
- package/dist/Components/EnumField/EnumField.module.css +61 -0
- package/dist/Components/EnumField/index.d.ts +10 -0
- package/dist/Components/EnumField/index.js +16 -0
- package/dist/Components/IconButton/IconButton.module.css +8 -3
- package/dist/Components/IconButton/index.d.ts +1 -0
- package/dist/Components/IconButton/index.js +3 -3
- package/dist/Components/Modal/Modal.module.css +92 -0
- package/dist/Components/Modal/index.d.ts +10 -0
- package/dist/Components/Modal/index.js +16 -0
- package/dist/Components/NumberField/NumberField.module.css +60 -0
- package/dist/Components/NumberField/index.d.ts +3 -0
- package/dist/Components/NumberField/index.js +13 -0
- package/dist/Components/RoiEditor/Canvas.d.ts +2 -0
- package/dist/Components/RoiEditor/Canvas.js +21 -11
- package/dist/Components/RoiEditor/Hooks.d.ts +16 -4
- package/dist/Components/RoiEditor/Hooks.js +148 -21
- package/dist/Components/RoiEditor/ParameterField.d.ts +9 -0
- package/dist/Components/RoiEditor/ParameterField.js +27 -0
- package/dist/Components/RoiEditor/ParametersModalForm/ParametersModalForm.module.css +5 -0
- package/dist/Components/RoiEditor/ParametersModalForm/index.d.ts +10 -0
- package/dist/Components/RoiEditor/ParametersModalForm/index.js +31 -0
- package/dist/Components/RoiEditor/Polygon.d.ts +3 -2
- package/dist/Components/RoiEditor/Polygon.js +18 -0
- package/dist/Components/RoiEditor/Polyline.d.ts +12 -1
- package/dist/Components/RoiEditor/Polyline.js +18 -0
- package/dist/Components/RoiEditor/Rectangle.d.ts +17 -1
- package/dist/Components/RoiEditor/Rectangle.js +23 -1
- package/dist/Components/RoiEditor/ShapesList.d.ts +2 -0
- package/dist/Components/RoiEditor/ShapesList.js +47 -0
- package/dist/Components/RoiEditor/ShapesList.module.css +36 -0
- package/dist/Components/RoiEditor/Toolbar.js +20 -6
- package/dist/Components/RoiEditor/Toolbar.module.css +40 -0
- package/dist/Components/RoiEditor/Types.d.ts +97 -1
- package/dist/Components/RoiEditor/Types.js +15 -1
- package/dist/Components/RoiEditor/Utils.d.ts +22 -0
- package/dist/Components/RoiEditor/Utils.js +143 -0
- package/dist/Components/RoiEditor/index.d.ts +4 -0
- package/dist/Components/RoiEditor/index.js +44 -7
- package/dist/Components/TextField/TextField.module.css +61 -0
- package/dist/Components/TextField/index.d.ts +6 -0
- package/dist/Components/TextField/index.js +13 -0
- package/dist/Components/Typography/index.d.ts +4 -0
- package/dist/Components/Typography/index.js +3 -2
- package/dist/Icons/AnnotateIcon.d.ts +6 -0
- package/dist/Icons/AnnotateIcon.js +5 -0
- package/dist/Icons/CloseIcon.d.ts +6 -0
- package/dist/Icons/CloseIcon.js +5 -0
- package/dist/Icons/CopyIcon.d.ts +6 -0
- package/dist/Icons/CopyIcon.js +5 -0
- package/dist/Icons/SaveIcon.d.ts +6 -0
- package/dist/Icons/SaveIcon.js +5 -0
- package/dist/Providers/EditorProvider.d.ts +12 -2
- package/dist/Providers/EditorProvider.js +16 -2
- package/dist/Providers/UiProvider.d.ts +44 -2
- package/dist/Providers/UiProvider.js +55 -2
- package/dist/Types.d.ts +10 -0
- package/dist/Types.js +1 -0
- package/dist/Utils/index.d.ts +1 -1
- package/dist/Utils/index.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +13 -4
- package/dist/Components/RoiEditor/Metadata.d.ts +0 -2
- package/dist/Components/RoiEditor/Metadata.js +0 -31
- package/dist/Components/RoiEditor/Metadata.module.css +0 -34
@@ -1,8 +1,14 @@
|
|
1
|
-
import
|
1
|
+
import * as fabric from 'fabric';
|
2
|
+
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
4
|
+
import { useEditorContext } from '../../Providers/EditorProvider';
|
5
|
+
import { UiContext } from '../../Providers/UiProvider';
|
6
|
+
import { log } from '../../Utils';
|
2
7
|
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';
|
8
|
+
import { copyPolygon, handleDoubleClickPolygon, handleMouseDownPolygon, handleMouseMovePolygon } from './Polygon';
|
9
|
+
import { copyPolyline, handleDoubleClickPolyline, handleMouseDownPolyline, handleMouseMovePolyline } from './Polyline';
|
10
|
+
import { copyRectangle, handleMouseDownRect, handleMouseMoveRect, handleMouseUpRect } from './Rectangle';
|
11
|
+
import { canDrawShape } from './Utils';
|
6
12
|
export const useImageSize = (imageUrl) => {
|
7
13
|
const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
|
8
14
|
useEffect(() => {
|
@@ -37,17 +43,81 @@ export const useCanvasSize = (imageUrl) => {
|
|
37
43
|
};
|
38
44
|
if (imageSize.width > 0 && wrapperRef.current) {
|
39
45
|
update();
|
40
|
-
// observe ref for resizing event and in case update canvas dimensions
|
41
|
-
const resizeObserver = new ResizeObserver(() => {
|
42
|
-
update();
|
43
|
-
});
|
44
|
-
resizeObserver.observe(wrapperRef.current);
|
45
46
|
}
|
46
47
|
}
|
47
48
|
}, [imageSize, wrapperRef.current]); // eslint-disable-line
|
48
49
|
return { imageSize, canvasSize, wrapperRef, isReady };
|
49
50
|
};
|
50
|
-
export const
|
51
|
+
export const initCanvasData = (canvasRef, addShapes, metadata, setMetadata, initialData, enableLogs) => {
|
52
|
+
log('info', enableLogs !== null && enableLogs !== void 0 ? enableLogs : false, 'Loading initial shapes data', initialData, canvasRef.current);
|
53
|
+
if (initialData === null || initialData === void 0 ? void 0 : initialData.rois) {
|
54
|
+
const m = [];
|
55
|
+
const s = [];
|
56
|
+
initialData.rois.forEach((r) => {
|
57
|
+
var _a, _b, _c;
|
58
|
+
log('info', enableLogs !== null && enableLogs !== void 0 ? enableLogs : false, 'Loading initial shape', r);
|
59
|
+
const id = uuidv4();
|
60
|
+
let shape;
|
61
|
+
switch (r.type) {
|
62
|
+
case "rect" /* ToolEnum.Rectangle */:
|
63
|
+
shape = new fabric.Rect({
|
64
|
+
left: r.shape.left,
|
65
|
+
top: r.shape.top,
|
66
|
+
originX: 'left',
|
67
|
+
originY: 'top',
|
68
|
+
width: r.shape.width,
|
69
|
+
height: r.shape.height,
|
70
|
+
fill: 'transparent',
|
71
|
+
stroke: r.shape.color,
|
72
|
+
strokeWidth: 2,
|
73
|
+
strokeUniform: true,
|
74
|
+
selectable: false,
|
75
|
+
hasControls: true,
|
76
|
+
hoverCursor: 'default',
|
77
|
+
id,
|
78
|
+
});
|
79
|
+
(_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.add(shape);
|
80
|
+
break;
|
81
|
+
case "polygon" /* ToolEnum.Polygon */:
|
82
|
+
shape = new fabric.Polygon(r.shape.points, {
|
83
|
+
top: r.shape.top + 10,
|
84
|
+
left: r.shape.left + 10,
|
85
|
+
fill: 'transparent',
|
86
|
+
stroke: r.shape.color,
|
87
|
+
strokeWidth: 2,
|
88
|
+
selectable: false,
|
89
|
+
hasControls: true,
|
90
|
+
hoverCursor: 'default',
|
91
|
+
// @ts-expect-error id is not included in types but the property is added and it works
|
92
|
+
id,
|
93
|
+
});
|
94
|
+
(_b = canvasRef.current) === null || _b === void 0 ? void 0 : _b.add(shape);
|
95
|
+
break;
|
96
|
+
case "polyline" /* ToolEnum.Polyline */:
|
97
|
+
shape = new fabric.Polyline(r.shape.points, {
|
98
|
+
top: r.shape.top + 10,
|
99
|
+
left: r.shape.left + 10,
|
100
|
+
fill: 'transparent',
|
101
|
+
stroke: r.shape.color,
|
102
|
+
strokeWidth: 2,
|
103
|
+
selectable: false,
|
104
|
+
hasControls: true,
|
105
|
+
hoverCursor: 'default',
|
106
|
+
id,
|
107
|
+
});
|
108
|
+
(_c = canvasRef.current) === null || _c === void 0 ? void 0 : _c.add(shape);
|
109
|
+
break;
|
110
|
+
}
|
111
|
+
m.push({ id, parameters: r.parameters });
|
112
|
+
s.push({ id, type: r.type, shape });
|
113
|
+
});
|
114
|
+
addShapes(s);
|
115
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { rois: m }));
|
116
|
+
}
|
117
|
+
};
|
118
|
+
export const useTool = (canvas) => {
|
119
|
+
const { configuration, activeTool, activeColor, shapes, addShape } = useEditorContext();
|
120
|
+
const { notify, strings } = useContext(UiContext);
|
51
121
|
const [isDrawing, setIsDrawing] = useState(false);
|
52
122
|
const [shape, setShape] = useState(null);
|
53
123
|
const [originX, setOriginX] = useState(0);
|
@@ -68,7 +138,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
68
138
|
if (!canvas) {
|
69
139
|
return;
|
70
140
|
}
|
71
|
-
if (
|
141
|
+
if (activeTool === "pointer" /* ToolEnum.Pointer */) {
|
72
142
|
// enable selection
|
73
143
|
canvas.selection = true;
|
74
144
|
canvas.getObjects().forEach((object) => {
|
@@ -84,14 +154,20 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
84
154
|
canvas.renderAll();
|
85
155
|
}
|
86
156
|
const handleMouseDown = (event) => {
|
87
|
-
switch (
|
88
|
-
case "
|
157
|
+
switch (activeTool) {
|
158
|
+
case "rect" /* ToolEnum.Rectangle */:
|
159
|
+
if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
|
160
|
+
return;
|
89
161
|
handleMouseDownRect(event, canvas, activeColor, setOriginX, setOriginY, setShape, setIsDrawing);
|
90
162
|
break;
|
91
163
|
case "polygon" /* ToolEnum.Polygon */:
|
164
|
+
if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
|
165
|
+
return;
|
92
166
|
handleMouseDownPolygon(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
|
93
167
|
break;
|
94
168
|
case "polyline" /* ToolEnum.Polyline */:
|
169
|
+
if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
|
170
|
+
return;
|
95
171
|
handleMouseDownPolyline(event, canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines);
|
96
172
|
break;
|
97
173
|
default:
|
@@ -99,8 +175,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
99
175
|
}
|
100
176
|
};
|
101
177
|
const handleMouseMove = (event) => {
|
102
|
-
switch (
|
103
|
-
case "
|
178
|
+
switch (activeTool) {
|
179
|
+
case "rect" /* ToolEnum.Rectangle */:
|
104
180
|
handleMouseMoveRect(event, canvas, originX, originY, shape, isDrawing);
|
105
181
|
break;
|
106
182
|
case "polygon" /* ToolEnum.Polygon */:
|
@@ -114,8 +190,8 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
114
190
|
}
|
115
191
|
};
|
116
192
|
const handleMouseUp = () => {
|
117
|
-
switch (
|
118
|
-
case "
|
193
|
+
switch (activeTool) {
|
194
|
+
case "rect" /* ToolEnum.Rectangle */:
|
119
195
|
handleMouseUpRect(canvas, setIsDrawing, shape, setShape, addShape);
|
120
196
|
break;
|
121
197
|
default:
|
@@ -123,7 +199,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
123
199
|
}
|
124
200
|
};
|
125
201
|
const handleDoubleClick = () => {
|
126
|
-
switch (
|
202
|
+
switch (activeTool) {
|
127
203
|
case "polygon" /* ToolEnum.Polygon */:
|
128
204
|
handleDoubleClickPolygon(canvas, activeColor, setIsDrawing, points, setPoints, lines, setLines, addShape);
|
129
205
|
break;
|
@@ -151,7 +227,7 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
151
227
|
canvas.off('selection:cleared', handleSelectionCleared);
|
152
228
|
};
|
153
229
|
}, [
|
154
|
-
|
230
|
+
activeTool,
|
155
231
|
activeColor,
|
156
232
|
isDrawing,
|
157
233
|
shape,
|
@@ -164,9 +240,15 @@ export const useTool = (tool, activeColor, addShape, canvas) => {
|
|
164
240
|
addShape,
|
165
241
|
handleObjectSelected,
|
166
242
|
handleSelectionCleared,
|
243
|
+
configuration,
|
244
|
+
notify,
|
245
|
+
strings,
|
246
|
+
shapes,
|
167
247
|
]);
|
168
248
|
};
|
169
|
-
export const useDispatcherEvents = (canvas
|
249
|
+
export const useDispatcherEvents = (canvas) => {
|
250
|
+
const { configuration, shapes, addShape, setActiveTool } = useEditorContext();
|
251
|
+
const { notify, strings } = useContext(UiContext);
|
170
252
|
useEffect(() => {
|
171
253
|
const removeShape = (_, id) => {
|
172
254
|
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
@@ -174,6 +256,35 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
|
|
174
256
|
canvas === null || canvas === void 0 ? void 0 : canvas.remove(obj);
|
175
257
|
}
|
176
258
|
};
|
259
|
+
const copyShape = (_, id) => {
|
260
|
+
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
261
|
+
let copy;
|
262
|
+
switch (obj === null || obj === void 0 ? void 0 : obj.type) {
|
263
|
+
case "polygon" /* ToolEnum.Polygon */:
|
264
|
+
if (!canDrawShape(configuration, "polygon" /* ToolEnum.Polygon */, shapes, notify, strings.cannotDrawMorePolygons))
|
265
|
+
return;
|
266
|
+
copy = copyPolygon(canvas, obj, addShape);
|
267
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
268
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
269
|
+
break;
|
270
|
+
case "polyline" /* ToolEnum.Polyline */:
|
271
|
+
if (!canDrawShape(configuration, "polyline" /* ToolEnum.Polyline */, shapes, notify, strings.cannotDrawMorePolylines))
|
272
|
+
return;
|
273
|
+
copy = copyPolyline(canvas, obj, addShape);
|
274
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
275
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
276
|
+
break;
|
277
|
+
case "rect" /* ToolEnum.Rectangle */:
|
278
|
+
if (!canDrawShape(configuration, "rect" /* ToolEnum.Rectangle */, shapes, notify, strings.cannotDrawMoreRectangles))
|
279
|
+
return;
|
280
|
+
copy = copyRectangle(canvas, obj, addShape);
|
281
|
+
// @ts-expect-error id exists but his stupid ts does not know
|
282
|
+
Dispatcher.emit('canvas:selectShape', copy.id);
|
283
|
+
break;
|
284
|
+
default:
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
};
|
177
288
|
const selectShape = (_, id) => {
|
178
289
|
const obj = canvas === null || canvas === void 0 ? void 0 : canvas.getObjects().find((s) => s.id === id);
|
179
290
|
if (obj) {
|
@@ -184,10 +295,26 @@ export const useDispatcherEvents = (canvas, setActiveTool) => {
|
|
184
295
|
}
|
185
296
|
};
|
186
297
|
Dispatcher.register('canvas:removeShape', removeShape);
|
298
|
+
Dispatcher.register('canvas:copyShape', copyShape);
|
187
299
|
Dispatcher.register('canvas:selectShape', selectShape);
|
188
300
|
return () => {
|
189
301
|
Dispatcher.unregister('canvas:removeShape', removeShape);
|
302
|
+
Dispatcher.unregister('canvas:copyShape', copyShape);
|
190
303
|
Dispatcher.unregister('canvas:selectShape', selectShape);
|
191
304
|
};
|
192
|
-
}, [setActiveTool, canvas]);
|
305
|
+
}, [setActiveTool, canvas, addShape, configuration, shapes, notify, strings]);
|
306
|
+
};
|
307
|
+
export const useParametersForm = (parameters) => {
|
308
|
+
const [errors, setErrors] = useState({});
|
309
|
+
const [fields, setFields] = useState(parameters.reduce((acc, p) => (Object.assign(Object.assign({}, acc), { [p.codename]: p.value })), {}));
|
310
|
+
const setField = (key) => (value) => {
|
311
|
+
setFields(Object.assign(Object.assign({}, fields), { [key]: value }));
|
312
|
+
};
|
313
|
+
return {
|
314
|
+
fields,
|
315
|
+
setField,
|
316
|
+
setFields,
|
317
|
+
errors,
|
318
|
+
setErrors,
|
319
|
+
};
|
193
320
|
};
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { ConfigurationParameter } from './Types';
|
2
|
+
export type ParameterFieldProps<T> = {
|
3
|
+
value: T;
|
4
|
+
onChange: (value: T) => void;
|
5
|
+
parameter: ConfigurationParameter;
|
6
|
+
errors: Record<string, string>;
|
7
|
+
};
|
8
|
+
declare const ParameterField: <T>({ value, onChange, parameter, errors }: ParameterFieldProps<T>) => import("react/jsx-runtime").JSX.Element | null;
|
9
|
+
export default ParameterField;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useContext } from 'react';
|
3
|
+
import { UiContext } from '../../Providers/UiProvider';
|
4
|
+
const ParameterField = ({ value, onChange, parameter, errors }) => {
|
5
|
+
var _a, _b;
|
6
|
+
const { TextField, NumberField, BoolField, EnumField, strings } = useContext(UiContext);
|
7
|
+
const props = {
|
8
|
+
required: parameter.required,
|
9
|
+
label: `${parameter.label}${parameter.unit ? ` (${parameter.unit})` : ''}`,
|
10
|
+
error: !!errors[parameter.codename],
|
11
|
+
helperText: errors[parameter.codename]
|
12
|
+
? strings[errors[parameter.codename]]
|
13
|
+
: parameter.description,
|
14
|
+
};
|
15
|
+
switch (parameter.type) {
|
16
|
+
case 'string':
|
17
|
+
return ((_a = parameter.options) === null || _a === void 0 ? void 0 : _a.length) ? (_jsx(EnumField, Object.assign({ value: value, onChange: (v) => onChange(v), options: parameter.options, multiple: parameter.multiple }, props))) : (_jsx(TextField, Object.assign({ type: "text", value: value, onChange: (v) => onChange(v) }, props)));
|
18
|
+
case 'int':
|
19
|
+
case 'float':
|
20
|
+
return ((_b = parameter.options) === null || _b === void 0 ? void 0 : _b.length) ? (_jsx(EnumField, Object.assign({ value: value, onChange: (v) => onChange(v), options: parameter.options, multiple: parameter.multiple }, props))) : (_jsx(NumberField, Object.assign({ value: value, onChange: (v) => onChange(v) }, props)));
|
21
|
+
case 'bool':
|
22
|
+
return _jsx(BoolField, Object.assign({ value: value, onChange: (v) => onChange(v) }, props));
|
23
|
+
default:
|
24
|
+
return null;
|
25
|
+
}
|
26
|
+
};
|
27
|
+
export default ParameterField;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { ConfigurationParameter, OutputParameter } from '../Types';
|
2
|
+
export type ParametersModalFormProps = {
|
3
|
+
onClose: () => void;
|
4
|
+
title: string;
|
5
|
+
parameters: ConfigurationParameter[];
|
6
|
+
data: OutputParameter[];
|
7
|
+
onSubmit: (data: OutputParameter[]) => void;
|
8
|
+
};
|
9
|
+
declare const ParametersModalForm: React.FC<ParametersModalFormProps>;
|
10
|
+
export default ParametersModalForm;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useContext } from 'react';
|
3
|
+
import { UiContext } from '../../../Providers/UiProvider';
|
4
|
+
import { css } from '../../../Utils';
|
5
|
+
import { useParametersForm } from '../Hooks';
|
6
|
+
import ParameterField from '../ParameterField';
|
7
|
+
import styles from './ParametersModalForm.module.css';
|
8
|
+
import { validateParametersForm } from '../Utils';
|
9
|
+
const ParametersModalForm = ({ title, onClose, parameters, data, onSubmit }) => {
|
10
|
+
const { Modal } = useContext(UiContext);
|
11
|
+
const { fields, setField, errors, setErrors } = useParametersForm(data);
|
12
|
+
const handleSubmit = () => {
|
13
|
+
if (validateParametersForm(parameters, fields, setErrors)) {
|
14
|
+
onSubmit([...parameters.map((p) => ({ codename: p.codename, value: fields[p.codename] }))]);
|
15
|
+
}
|
16
|
+
};
|
17
|
+
return (_jsx(Modal, { onClose: onClose, title: title, isOpen: true, maxWidth: "sm", onSubmit: handleSubmit, children: _jsx("div", { className: css('form', styles, null), children: parameters.map((parameter) => {
|
18
|
+
switch (parameter.type) {
|
19
|
+
case 'string':
|
20
|
+
return (_jsx(ParameterField, { value: String(fields[parameter.codename]), onChange: setField(parameter.codename), parameter: parameter, errors: errors }, parameter.codename));
|
21
|
+
case 'int':
|
22
|
+
case 'float':
|
23
|
+
return (_jsx(ParameterField, { value: fields[parameter.codename], onChange: setField(parameter.codename), parameter: parameter, errors: errors }, parameter.codename));
|
24
|
+
case 'bool':
|
25
|
+
return (_jsx(ParameterField, { value: fields[parameter.codename], onChange: setField(parameter.codename), parameter: parameter, errors: errors }, parameter.codename));
|
26
|
+
default:
|
27
|
+
return null;
|
28
|
+
}
|
29
|
+
}) }) }));
|
30
|
+
};
|
31
|
+
export default ParametersModalForm;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as fabric from 'fabric';
|
2
|
-
import { FabricEvent,
|
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:
|
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, "
|
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,47 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 ParametersModalForm from './ParametersModalForm';
|
8
|
+
import styles from './ShapesList.module.css';
|
9
|
+
const ShapesList = () => {
|
10
|
+
var _a, _b, _c, _d, _e, _f;
|
11
|
+
const { strings, Typography, IconButton, DeleteIcon, AnnotateIcon, SelectIcon, CopyIcon, themeMode } = useContext(UiContext);
|
12
|
+
const { shapes, removeShape, configuration, metadata, setMetadata } = useEditorContext();
|
13
|
+
const [selected, setSelected] = useState([]);
|
14
|
+
const [form, setForm] = useState({ isOpen: false, shapeId: '' });
|
15
|
+
useEffect(() => {
|
16
|
+
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 : []); };
|
17
|
+
Dispatcher.register('canvas:shapeSelected', setSelectedShapes);
|
18
|
+
return () => {
|
19
|
+
Dispatcher.unregister('canvas:shapeSelected', setSelectedShapes);
|
20
|
+
};
|
21
|
+
}, [shapes]);
|
22
|
+
const handleCopyShape = (id) => () => {
|
23
|
+
Dispatcher.emit('canvas:copyShape', id);
|
24
|
+
};
|
25
|
+
const handleRemoveShape = (id) => () => {
|
26
|
+
Dispatcher.emit('canvas:removeShape', id);
|
27
|
+
removeShape(id);
|
28
|
+
};
|
29
|
+
const handleSelectShape = (id) => () => {
|
30
|
+
Dispatcher.emit('canvas:selectShape', id);
|
31
|
+
};
|
32
|
+
const handleEditShapeMetadata = (id) => () => {
|
33
|
+
setForm({ isOpen: true, shapeId: id });
|
34
|
+
};
|
35
|
+
const handleSubmitMetadata = (shapeId) => (data) => {
|
36
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { rois: [
|
37
|
+
...metadata.rois.filter((r) => r.id !== shapeId),
|
38
|
+
{ id: shapeId, parameters: data },
|
39
|
+
] }));
|
40
|
+
setForm({ isOpen: false, shapeId: '' });
|
41
|
+
};
|
42
|
+
const iconColor = themeMode === 'light' ? 'black' : 'white';
|
43
|
+
return (_jsxs(_Fragment, { children: [_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, { style: { fontWeight: 'bold' }, children: strings.id }) }), _jsx("th", { children: _jsx(Typography, { style: { fontWeight: 'bold' }, children: strings.type }) }), _jsx("th", {})] }) })), _jsx("tbody", { children: Object.keys(shapes).map((id) => {
|
44
|
+
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: handleEditShapeMetadata(id), disabled: !configuration.rois.find((roi) => roi.type === shapes[id].type), children: _jsx(AnnotateIcon, { color: iconColor }) }), _jsx(IconButton, { onClick: handleRemoveShape(id), children: _jsx(DeleteIcon, { color: iconColor }) })] })] }, id));
|
45
|
+
}) })] }), form.isOpen && (_jsx(ParametersModalForm, { parameters: (_b = (_a = configuration.rois.find((roi) => roi.type === shapes[form.shapeId].type)) === null || _a === void 0 ? void 0 : _a.parameters) !== null && _b !== void 0 ? _b : [], data: (_f = (_d = (_c = metadata.rois.find((roi) => roi.id === form.shapeId)) === null || _c === void 0 ? void 0 : _c.parameters) !== null && _d !== void 0 ? _d : (_e = configuration.rois.find((roi) => roi.type === shapes[form.shapeId].type)) === null || _e === void 0 ? void 0 : _e.parameters) !== null && _f !== void 0 ? _f : [], title: strings.mainParametersMetadata, onClose: () => setForm({ isOpen: false, shapeId: '' }), onSubmit: handleSubmitMetadata(form.shapeId) }))] }));
|
46
|
+
};
|
47
|
+
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,19 +1,33 @@
|
|
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';
|
6
7
|
import RectangleIcon from '../../Icons/RectangleIcon';
|
8
|
+
import SaveIcon from '../../Icons/SaveIcon';
|
7
9
|
import { useEditorContext } from '../../Providers/EditorProvider';
|
8
10
|
import { UiContext } from '../../Providers/UiProvider';
|
9
11
|
import { css } from '../../Utils';
|
10
|
-
import styles from './Toolbar.module.css';
|
11
12
|
import ColorPicker from './ColorPicker';
|
13
|
+
import ParametersModalForm from './ParametersModalForm';
|
14
|
+
import styles from './Toolbar.module.css';
|
15
|
+
import { enableMainMetadata, enableRois } from './Utils';
|
12
16
|
const Toolbar = () => {
|
13
|
-
|
14
|
-
const {
|
17
|
+
var _a, _b;
|
18
|
+
const { IconButton, themeMode, primaryColor, Typography, strings } = useContext(UiContext);
|
19
|
+
const { activeTool, setActiveTool, configuration, metadata, setMetadata, onSubmit } = useEditorContext();
|
20
|
+
const [form, setForm] = useState({ isOpen: false });
|
15
21
|
const iconColor = (tool) => (tool === activeTool ? primaryColor : themeMode === 'light' ? 'black' : 'white');
|
16
22
|
const setTool = (tool) => () => setActiveTool(tool);
|
17
|
-
|
23
|
+
const handleSubmitMetadata = (data) => {
|
24
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { parameters: data }));
|
25
|
+
setForm({ isOpen: false });
|
26
|
+
};
|
27
|
+
const hideForbiddenTools = (_a = configuration.options) === null || _a === void 0 ? void 0 : _a.hideForbiddenTools;
|
28
|
+
const polylineEnabled = configuration.rois.find((r) => r.type === "polyline" /* ToolEnum.Polyline */);
|
29
|
+
const polygonEnabled = configuration.rois.find((r) => r.type === "polygon" /* ToolEnum.Polygon */);
|
30
|
+
const rectangleEnabled = configuration.rois.find((r) => r.type === "rect" /* ToolEnum.Rectangle */);
|
31
|
+
return (_jsxs(_Fragment, { children: [((_b = configuration.options) === null || _b === void 0 ? void 0 : _b.description) && (_jsx("div", { className: css('toolbar-info', styles, themeMode), children: _jsx(Typography, { children: configuration.options.description }) })), _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 */) }) }), (!hideForbiddenTools || polylineEnabled) && (_jsx(IconButton, { onClick: setTool("polyline" /* ToolEnum.Polyline */), disabled: !polylineEnabled, children: _jsx(PolylineIcon, { color: iconColor("polyline" /* ToolEnum.Polyline */) }) })), (!hideForbiddenTools || polygonEnabled) && (_jsx(IconButton, { onClick: setTool("polygon" /* ToolEnum.Polygon */), disabled: !polygonEnabled, children: _jsx(PolygonIcon, { color: iconColor("polygon" /* ToolEnum.Polygon */) }) })), (!hideForbiddenTools || rectangleEnabled) && (_jsx(IconButton, { onClick: setTool("rect" /* ToolEnum.Rectangle */), disabled: !rectangleEnabled, children: _jsx(RectangleIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) })), _jsx(ColorPicker, { style: { marginLeft: 'auto', marginRight: '.5rem' } })] })), enableRois(configuration) && enableMainMetadata(configuration) && (_jsx("div", { className: css('toolbar-spacer', styles, themeMode) })), enableMainMetadata(configuration) && (_jsx(IconButton, { onClick: () => setForm({ isOpen: true }), children: _jsx(AnnotateIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) })), _jsx("div", { className: css('toolbar-spacer', styles, themeMode) }), _jsx(IconButton, { onClick: onSubmit, children: _jsx(SaveIcon, { color: iconColor("rect" /* ToolEnum.Rectangle */) }) })] }), enableRois(configuration) && (_jsx("div", { className: css('toolbar-helper', styles, themeMode), children: _jsxs(Typography, { children: [strings[activeTool], ": ", strings[`${activeTool}HelpText`]] }) })), form.isOpen && (_jsx(ParametersModalForm, { parameters: configuration.parameters, data: metadata.parameters, title: strings.mainParametersMetadata, onClose: () => setForm({ isOpen: false }), onSubmit: handleSubmitMetadata }))] }));
|
18
32
|
};
|
19
33
|
export default Toolbar;
|