@abidibo/react-cam-roi 0.0.7 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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,3 +1,15 @@
|
|
1
|
+
.toolbar-info {
|
2
|
+
padding: .5rem;
|
3
|
+
}
|
4
|
+
.toolbar-info-light {
|
5
|
+
background-color: #ddd;;
|
6
|
+
color: #000;
|
7
|
+
}
|
8
|
+
.toolbar-info-dark {
|
9
|
+
background-color: #222;
|
10
|
+
color: #fff;
|
11
|
+
}
|
12
|
+
|
1
13
|
.toolbar {
|
2
14
|
align-items: center;
|
3
15
|
display: flex;
|
@@ -11,3 +23,31 @@
|
|
11
23
|
.toolbar-dark {
|
12
24
|
background-color: #333;
|
13
25
|
}
|
26
|
+
|
27
|
+
.toolbar-helper {
|
28
|
+
padding: .5rem;
|
29
|
+
}
|
30
|
+
|
31
|
+
.toolbar-helper-light {
|
32
|
+
background-color: #f7f7f7;;
|
33
|
+
color: #000;
|
34
|
+
}
|
35
|
+
|
36
|
+
.toolbar-helper-dark {
|
37
|
+
background-color: #222;
|
38
|
+
color: #fff;
|
39
|
+
}
|
40
|
+
|
41
|
+
.toolbar-spacer {
|
42
|
+
height: 20px;
|
43
|
+
margin: 0 .5rem;
|
44
|
+
width: 1px;
|
45
|
+
}
|
46
|
+
|
47
|
+
.toolbar-spacer-light {
|
48
|
+
background-color: #ccc;
|
49
|
+
}
|
50
|
+
|
51
|
+
.toolbar-spacer-dark {
|
52
|
+
background-color: #555;
|
53
|
+
}
|
@@ -3,7 +3,7 @@ export declare const enum ToolEnum {
|
|
3
3
|
Pointer = "pointer",
|
4
4
|
Polyline = "polyline",
|
5
5
|
Polygon = "polygon",
|
6
|
-
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,99 @@ 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;
|
42
|
+
label: string;
|
43
|
+
}[];
|
44
|
+
multiple?: boolean;
|
45
|
+
required: boolean;
|
46
|
+
value: number | string | boolean | string[] | number[] | null;
|
47
|
+
};
|
48
|
+
export type RoiConfiguration = {
|
49
|
+
role: string;
|
50
|
+
type: Omit<ShapeType, 'pointer'>;
|
51
|
+
multiplicity: {
|
52
|
+
operator: OperatorEnum;
|
53
|
+
threshold: number;
|
54
|
+
};
|
55
|
+
parameters: ConfigurationParameter[];
|
56
|
+
};
|
57
|
+
export type Configuration = {
|
58
|
+
parameters: ConfigurationParameter[];
|
59
|
+
rois: RoiConfiguration[];
|
60
|
+
options?: {
|
61
|
+
hideForbiddenTools?: boolean;
|
62
|
+
description?: string;
|
63
|
+
};
|
64
|
+
};
|
65
|
+
export interface INotify {
|
66
|
+
info: (message: string) => void;
|
67
|
+
warn: (message: string) => void;
|
68
|
+
error: (message: string) => void;
|
69
|
+
success: (message: string) => void;
|
70
|
+
}
|
71
|
+
export type Metadata = {
|
72
|
+
parameters: OutputParameter[];
|
73
|
+
rois: {
|
74
|
+
id: string;
|
75
|
+
parameters: OutputParameter[];
|
76
|
+
}[];
|
77
|
+
};
|
78
|
+
export type OutputShapeRect = {
|
79
|
+
top: number;
|
80
|
+
left: number;
|
81
|
+
width: number;
|
82
|
+
height: number;
|
83
|
+
color: string;
|
84
|
+
};
|
85
|
+
export type OutputShapePolyline = {
|
86
|
+
points: {
|
87
|
+
x: number;
|
88
|
+
y: number;
|
89
|
+
}[];
|
90
|
+
top: number;
|
91
|
+
left: number;
|
92
|
+
color: string;
|
93
|
+
};
|
94
|
+
export type OutputShapePolygon = {
|
95
|
+
points: {
|
96
|
+
x: number;
|
97
|
+
y: number;
|
98
|
+
}[];
|
99
|
+
top: number;
|
100
|
+
left: number;
|
101
|
+
color: string;
|
102
|
+
};
|
103
|
+
export interface OutputParameter {
|
104
|
+
codename: string;
|
105
|
+
value: number | string | boolean | string[] | number[] | null;
|
106
|
+
}
|
107
|
+
export interface OutputRoi {
|
108
|
+
parameters: OutputParameter[];
|
109
|
+
type: ShapeType;
|
110
|
+
shape: OutputShapeRect | OutputShapePolyline | OutputShapePolygon;
|
111
|
+
}
|
112
|
+
export interface Output {
|
113
|
+
parameters: OutputParameter[];
|
114
|
+
rois: OutputRoi[];
|
115
|
+
}
|
@@ -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,22 @@
|
|
1
|
+
import { Configuration, ConfigurationParameter, INotify, Metadata, Shape, Shapes, ShapeType, 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;
|
6
|
+
export declare const validateParametersForm: (parameters: ConfigurationParameter[], fields: Record<string, unknown>, setErrors: (errors: Record<string, string>) => void) => boolean;
|
7
|
+
export declare const validate: (configuration: Configuration, shapes: Shapes, metadata: Metadata, strings: Record<string, string>) => [boolean, string[]];
|
8
|
+
export declare const fabricShapeToOutputShape: (shape: Shape, type: ShapeType) => {
|
9
|
+
top: number;
|
10
|
+
left: number;
|
11
|
+
width: number;
|
12
|
+
height: number;
|
13
|
+
color: string;
|
14
|
+
points?: undefined;
|
15
|
+
} | {
|
16
|
+
points: any;
|
17
|
+
top: number;
|
18
|
+
left: number;
|
19
|
+
color: string;
|
20
|
+
width?: undefined;
|
21
|
+
height?: undefined;
|
22
|
+
};
|
@@ -0,0 +1,143 @@
|
|
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
|
+
};
|
38
|
+
export const validateParametersForm = (parameters, fields, setErrors) => {
|
39
|
+
const err = {};
|
40
|
+
parameters.forEach((p) => {
|
41
|
+
if (p.required && isEmpty(fields[p.codename])) {
|
42
|
+
err[p.codename] = 'requiredField';
|
43
|
+
}
|
44
|
+
});
|
45
|
+
if (Object.keys(err).length > 0) {
|
46
|
+
setErrors(err);
|
47
|
+
return false;
|
48
|
+
}
|
49
|
+
return true;
|
50
|
+
};
|
51
|
+
const isEmpty = (v) => {
|
52
|
+
if (typeof v === 'string') {
|
53
|
+
return v.length === 0;
|
54
|
+
}
|
55
|
+
if (Array.isArray(v)) {
|
56
|
+
return v.length === 0;
|
57
|
+
}
|
58
|
+
return v === null || v === undefined;
|
59
|
+
};
|
60
|
+
export const validate = (configuration, shapes, metadata, strings) => {
|
61
|
+
const errors = [];
|
62
|
+
// check main parameters
|
63
|
+
if (configuration.parameters.length) {
|
64
|
+
if (metadata.parameters.find((p) => { var _a; return ((_a = configuration.parameters.find((p2) => p2.codename === p.codename)) === null || _a === void 0 ? void 0 : _a.required) && isEmpty(p.value); })) {
|
65
|
+
errors.push(strings.missingRequiredValuesInMainParameters);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
// check rois number
|
69
|
+
configuration.rois.forEach((roi) => {
|
70
|
+
// check multiplicity
|
71
|
+
if (roi.multiplicity) {
|
72
|
+
switch (roi.multiplicity.operator) {
|
73
|
+
case OperatorEnum.Eq:
|
74
|
+
if (Object.values(shapes).filter((s) => s.type === roi.type).length !== roi.multiplicity.threshold) {
|
75
|
+
errors.push(strings.shapesOfTypeShouldBeEqualToThreshold
|
76
|
+
.replace('{type}', String(roi.type))
|
77
|
+
.replace('{threshold}', roi.multiplicity.threshold.toString()));
|
78
|
+
}
|
79
|
+
break;
|
80
|
+
case OperatorEnum.Lt:
|
81
|
+
if (Object.values(shapes).filter((s) => s.type === roi.type).length >= roi.multiplicity.threshold) {
|
82
|
+
errors.push(strings.shapesOfTypeShouldBeLessThanThreshold
|
83
|
+
.replace('{type}', String(roi.type))
|
84
|
+
.replace('{threshold}', roi.multiplicity.threshold.toString()));
|
85
|
+
}
|
86
|
+
break;
|
87
|
+
case OperatorEnum.Lte:
|
88
|
+
if (Object.values(shapes).filter((s) => s.type === roi.type).length > roi.multiplicity.threshold) {
|
89
|
+
errors.push(strings.shapesOfTypeShouldBeLessThanOrEqualToThreshold
|
90
|
+
.replace('{type}', String(roi.type))
|
91
|
+
.replace('{threshold}', roi.multiplicity.threshold.toString()));
|
92
|
+
}
|
93
|
+
break;
|
94
|
+
case OperatorEnum.Gt:
|
95
|
+
if (Object.values(shapes).filter((s) => s.type === roi.type).length <= roi.multiplicity.threshold) {
|
96
|
+
errors.push(strings.shapesOfTypeShouldBeGreaterThanThreshold
|
97
|
+
.replace('{type}', String(roi.type))
|
98
|
+
.replace('{threshold}', roi.multiplicity.threshold.toString()));
|
99
|
+
}
|
100
|
+
break;
|
101
|
+
case OperatorEnum.Gte:
|
102
|
+
if (Object.values(shapes).filter((s) => s.type === roi.type).length < roi.multiplicity.threshold) {
|
103
|
+
errors.push(strings.shapesOfTypeShouldBeGreaterThanOrEqualToThreshold
|
104
|
+
.replace('{type}', String(roi.type))
|
105
|
+
.replace('{threshold}', roi.multiplicity.threshold.toString()));
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
});
|
110
|
+
// check rois metadata
|
111
|
+
Object.keys(shapes).forEach(shapeId => {
|
112
|
+
var _a, _b;
|
113
|
+
const type = shapes[shapeId].type;
|
114
|
+
const confParameters = (_b = (_a = configuration.rois.find((r) => r.type === type)) === null || _a === void 0 ? void 0 : _a.parameters) !== null && _b !== void 0 ? _b : [];
|
115
|
+
confParameters.forEach((p) => {
|
116
|
+
var _a, _b;
|
117
|
+
if (p.required && isEmpty((_b = (_a = metadata.rois.find((r) => r.id === shapeId)) === null || _a === void 0 ? void 0 : _a.parameters.find((p) => p.codename === p.codename)) === null || _b === void 0 ? void 0 : _b.value)) {
|
118
|
+
errors.push(strings.missingRequiredValuesInShapeParameters.replace('{id}', shapeId));
|
119
|
+
}
|
120
|
+
});
|
121
|
+
});
|
122
|
+
return [errors.length === 0, errors];
|
123
|
+
};
|
124
|
+
export const fabricShapeToOutputShape = (shape, type) => {
|
125
|
+
switch (type) {
|
126
|
+
case "rect" /* ToolEnum.Rectangle */:
|
127
|
+
return {
|
128
|
+
top: shape.top,
|
129
|
+
left: shape.left,
|
130
|
+
width: shape.width,
|
131
|
+
height: shape.height,
|
132
|
+
color: shape.stroke
|
133
|
+
};
|
134
|
+
case "polygon" /* ToolEnum.Polygon */:
|
135
|
+
case "polyline" /* ToolEnum.Polyline */:
|
136
|
+
return {
|
137
|
+
points: shape.get('points'),
|
138
|
+
top: shape.top,
|
139
|
+
left: shape.left,
|
140
|
+
color: shape.stroke
|
141
|
+
};
|
142
|
+
}
|
143
|
+
};
|
@@ -1,5 +1,9 @@
|
|
1
|
+
import { Configuration, Output } from './Types';
|
1
2
|
export type RoiEditorProps = {
|
2
3
|
imageUrl: string;
|
4
|
+
configuration: Configuration;
|
5
|
+
onSubmit: (data: Output) => void;
|
6
|
+
initialData?: Output;
|
3
7
|
};
|
4
8
|
declare const RoiEditor: React.FC<RoiEditorProps>;
|
5
9
|
export default RoiEditor;
|
@@ -6,32 +6,69 @@ 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';
|
10
9
|
import styles from './RoiEditor.module.css';
|
10
|
+
import ShapesList from './ShapesList';
|
11
11
|
import Toolbar from './Toolbar';
|
12
|
-
|
12
|
+
import { fabricShapeToOutputShape, validate } from './Utils';
|
13
13
|
// https://github.com/n-mazaheri/image-editor
|
14
|
-
const RoiEditor = ({ imageUrl }) => {
|
15
|
-
|
14
|
+
const RoiEditor = ({ imageUrl, configuration, onSubmit, initialData }) => {
|
15
|
+
var _a, _b, _c;
|
16
|
+
const { themeMode, enableLogs, pickerColors, strings, notify } = useContext(UiContext);
|
16
17
|
const { imageSize, canvasSize, wrapperRef, isReady } = useCanvasSize(imageUrl);
|
17
18
|
const [activeTool, setActiveTool] = useState("pointer" /* ToolEnum.Pointer */);
|
18
19
|
const [activeColor, setActiveColor] = useState(pickerColors[0]);
|
20
|
+
const [metadata, setMetadata] = useState({
|
21
|
+
parameters: [
|
22
|
+
...((_a = configuration.parameters.map((p) => {
|
23
|
+
const initial = initialData === null || initialData === void 0 ? void 0 : initialData.parameters.find((p) => p.codename === p.codename);
|
24
|
+
return {
|
25
|
+
codename: p.codename,
|
26
|
+
value: initial ? initial.value : p.value,
|
27
|
+
};
|
28
|
+
})) !== null && _a !== void 0 ? _a : []),
|
29
|
+
],
|
30
|
+
rois: [],
|
31
|
+
});
|
19
32
|
const [shapes, setShapes] = useState({});
|
20
33
|
const addShape = useCallback((id, type, shape) => setShapes(Object.assign(Object.assign({}, shapes), { [id]: { type, shape } })), [shapes]);
|
34
|
+
const addShapes = useCallback((s) => setShapes(Object.assign(Object.assign({}, shapes), s.reduce((r, s) => (Object.assign(Object.assign({}, r), { [s.id]: s })), {}))), [shapes]);
|
21
35
|
const removeShape = useCallback((id) => {
|
22
36
|
const newShapes = Object.assign({}, shapes);
|
23
37
|
delete newShapes[id];
|
24
38
|
setShapes(newShapes);
|
25
|
-
|
39
|
+
setMetadata(Object.assign(Object.assign({}, metadata), { rois: metadata.rois.filter((r) => r.id !== id) }));
|
40
|
+
}, [shapes, metadata]);
|
41
|
+
const handleSubmit = useCallback(() => {
|
42
|
+
var _a, _b;
|
43
|
+
const [isValid, errors] = validate(configuration, shapes, metadata, strings);
|
44
|
+
if (isValid) {
|
45
|
+
onSubmit({
|
46
|
+
parameters: (_b = (_a = metadata.parameters) === null || _a === void 0 ? void 0 : _a.map((p) => ({ codename: p.codename, value: p.value }))) !== null && _b !== void 0 ? _b : [],
|
47
|
+
rois: Object.keys(shapes).map((shapeId) => {
|
48
|
+
var _a, _b, _c;
|
49
|
+
return ({
|
50
|
+
parameters: (_c = (_b = (_a = metadata.rois
|
51
|
+
.find((r) => r.id === shapeId)) === null || _a === void 0 ? void 0 : _a.parameters) === null || _b === void 0 ? void 0 : _b.map((p) => ({ codename: p.codename, value: p.value }))) !== null && _c !== void 0 ? _c : [],
|
52
|
+
type: shapes[shapeId].type,
|
53
|
+
shape: fabricShapeToOutputShape(shapes[shapeId].shape, shapes[shapeId].shape.type),
|
54
|
+
});
|
55
|
+
}),
|
56
|
+
});
|
57
|
+
}
|
58
|
+
else {
|
59
|
+
notify.error(strings.invalidSubmission + '\n' + errors.map((e) => `- ${e}`).join('\n'));
|
60
|
+
}
|
61
|
+
}, [onSubmit, configuration, shapes, metadata, strings, notify]);
|
26
62
|
log('info', enableLogs, 'react-cam-roi', 'active tool', activeTool);
|
27
63
|
log('info', enableLogs, 'react-cam-roi', 'canvas size', canvasSize);
|
64
|
+
log('info', enableLogs, 'react-cam-roi', 'metadata', metadata);
|
28
65
|
if (!isReady) {
|
29
66
|
return _jsx(Loader, {});
|
30
67
|
}
|
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: {
|
68
|
+
return (_jsx(EditorProvider, { hideForbiddenTools: (_c = (_b = configuration.options) === null || _b === void 0 ? void 0 : _b.hideForbiddenTools) !== null && _c !== void 0 ? _c : false, activeTool: activeTool, setActiveTool: setActiveTool, activeColor: activeColor, setActiveColor: setActiveColor, shapes: shapes, addShape: addShape, addShapes: addShapes, removeShape: removeShape, configuration: configuration, metadata: metadata, setMetadata: setMetadata, onSubmit: handleSubmit, children: _jsxs("div", { style: { maxWidth: '100%', width: `${imageSize.width}px` }, ref: wrapperRef, children: [_jsx(Toolbar, {}), _jsx("div", { className: css('canvasWrapper', styles, themeMode), style: {
|
32
69
|
width: `${canvasSize.width}px`,
|
33
70
|
height: `${canvasSize.height}px`,
|
34
71
|
backgroundImage: `url(${imageUrl})`,
|
35
|
-
}, children: _jsx(Canvas, { canvasSize: canvasSize }) }), _jsx(
|
72
|
+
}, children: _jsx(Canvas, { canvasSize: canvasSize, initialData: initialData }) }), _jsx(ShapesList, {})] }) }));
|
36
73
|
};
|
37
74
|
export default RoiEditor;
|
@@ -0,0 +1,61 @@
|
|
1
|
+
/*
|
2
|
+
.text-field-wrapper {
|
3
|
+
margin-bottom: 2rem;
|
4
|
+
}
|
5
|
+
.text-field-wrapper-light {
|
6
|
+
}
|
7
|
+
.text-field-wrapper-dark {
|
8
|
+
}
|
9
|
+
*/
|
10
|
+
|
11
|
+
.text-field {
|
12
|
+
border-radius: 0.25rem;
|
13
|
+
box-sizing: border-box;
|
14
|
+
padding: 0.5rem;
|
15
|
+
width: 100%;
|
16
|
+
}
|
17
|
+
.text-field:focus-visible {
|
18
|
+
outline: none;
|
19
|
+
border: 1px solid #1976d2;
|
20
|
+
}
|
21
|
+
.text-field-light {
|
22
|
+
background-color: #fff;
|
23
|
+
color: #333;
|
24
|
+
border: 1px solid #ccc;
|
25
|
+
}
|
26
|
+
.text-field-dark {
|
27
|
+
background-color: #333;
|
28
|
+
border: 1px solid #666;
|
29
|
+
color: #fff;
|
30
|
+
}
|
31
|
+
.text-field-error {
|
32
|
+
border: 1px solid #d32f2f;
|
33
|
+
}
|
34
|
+
.text-field-label {
|
35
|
+
font-weight: bold;
|
36
|
+
display: block;
|
37
|
+
margin: 0 0 1rem 0;
|
38
|
+
}
|
39
|
+
/*
|
40
|
+
.text-fiel-label-light {
|
41
|
+
}
|
42
|
+
.text-field-label-dark {
|
43
|
+
}
|
44
|
+
*/
|
45
|
+
.text-field-label-error {
|
46
|
+
color: #d32f2f;
|
47
|
+
}
|
48
|
+
.text-field-helper-text {
|
49
|
+
font-style: italic;
|
50
|
+
font-size: 0.9rem;
|
51
|
+
margin-top: 0.5rem;
|
52
|
+
}
|
53
|
+
/*
|
54
|
+
.text-field-helper-text-light {
|
55
|
+
}
|
56
|
+
.text-field-helper-text-dark {
|
57
|
+
}
|
58
|
+
*/
|
59
|
+
.text-field-helper-text-error {
|
60
|
+
color: #d32f2f;
|
61
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { jsxs as _jsxs, 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 styles from './TextField.module.css';
|
6
|
+
const TextField = ({ onChange, type = 'text', value, label, helperText, error, required = false, readOnly = false, disabled = false, }) => {
|
7
|
+
const { themeMode, Typography } = useContext(UiContext);
|
8
|
+
const handleChange = (e) => {
|
9
|
+
onChange(e.target.value);
|
10
|
+
};
|
11
|
+
return (_jsxs("div", { className: css('text-field-wrapper', styles, themeMode), children: [_jsx("label", { className: `${css('text-field-label', styles, themeMode)} ${error ? css('text-field-label-error', styles, null) : ''}`, children: _jsxs(Typography, { children: [label, required && ' *'] }) }), _jsx("input", { type: type, className: `${css('text-field', styles, themeMode)} ${error ? css('text-field-error', styles, null) : ''}`, onChange: handleChange, value: value, readOnly: readOnly, disabled: disabled }), helperText && (_jsx(Typography, { component: 'div', className: `${css('text-field-helper-text', styles, themeMode)} ${error ? css('text-field-helper-text-error', styles, null) : ''}`, children: helperText }))] }));
|
12
|
+
};
|
13
|
+
export default TextField;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
-
const Typography = ({ children }) => {
|
3
|
-
|
2
|
+
const Typography = ({ children, style = {}, className = '', component = 'span' }) => {
|
3
|
+
const Tag = component;
|
4
|
+
return _jsx(Tag, { className: className, style: style, children: children });
|
4
5
|
};
|
5
6
|
export default Typography;
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
const AnnotateIcon = ({ color = 'black', style = {} }) => {
|
3
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", style: style, children: _jsx("path", { fill: color, d: "M280-280h84l240-238-86-86-238 238v86Zm352-266 42-44q6-6 6-14t-6-14l-56-56q-6-6-14-6t-14 6l-44 42 86 86ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h168q13-36 43.5-58t68.5-22q38 0 68.5 22t43.5 58h168q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm280-590q13 0 21.5-8.5T510-820q0-13-8.5-21.5T480-850q-13 0-21.5 8.5T450-820q0 13 8.5 21.5T480-790ZM200-200v-560 560Z" }) }));
|
4
|
+
};
|
5
|
+
export default AnnotateIcon;
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
const CloseIcon = ({ color = 'black', style = {} }) => {
|
3
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", style: style, children: _jsx("path", { fill: color, d: "m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" }) }));
|
4
|
+
};
|
5
|
+
export default CloseIcon;
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
const CopyIcon = ({ color = 'black', style = {} }) => {
|
3
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", width: "24px", viewBox: "0 -960 960 960", style: style, children: _jsx("path", { fill: color, d: "M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360Zm0-80h360v-480H360v480ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Zm160-240v-480 480Z" }) }));
|
4
|
+
};
|
5
|
+
export default CopyIcon;
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
const SaveIcon = ({ color = 'black', style = {} }) => {
|
3
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", style: style, children: _jsx("path", { fill: color, d: "M840-680v480q0 33-23.5 56.5T760-120H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h480l160 160Zm-80 34L646-760H200v560h560v-446ZM480-240q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35ZM240-560h360v-160H240v160Zm-40-86v446-560 114Z" }) }));
|
4
|
+
};
|
5
|
+
export default SaveIcon;
|
@@ -1,15 +1,25 @@
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
2
|
-
import { Shape, Shapes, ShapeType, ToolEnum } from '../Components/RoiEditor/Types';
|
2
|
+
import { Configuration, Metadata, Shape, Shapes, ShapeType, ToolEnum } from '../Components/RoiEditor/Types';
|
3
3
|
type EditorContextType = {
|
4
|
+
hideForbiddenTools: boolean;
|
4
5
|
activeTool: ToolEnum;
|
5
6
|
setActiveTool: (tool: ToolEnum) => void;
|
6
7
|
activeColor: string;
|
7
8
|
setActiveColor: (color: string) => void;
|
8
9
|
shapes: Shapes;
|
9
10
|
addShape: (id: string, type: ShapeType, shape: Shape) => void;
|
11
|
+
addShapes: (shapes: {
|
12
|
+
id: string;
|
13
|
+
type: ShapeType;
|
14
|
+
shape: Shape;
|
15
|
+
}[]) => void;
|
10
16
|
removeShape: (id: string) => void;
|
17
|
+
configuration: Configuration;
|
18
|
+
metadata: Metadata;
|
19
|
+
setMetadata: (data: Metadata) => void;
|
20
|
+
onSubmit: () => void;
|
11
21
|
};
|
12
22
|
export declare const EditorContext: import("react").Context<EditorContextType | undefined>;
|
13
23
|
export declare function useEditorContext(): EditorContextType;
|
14
|
-
declare const EditorProvider: ({ children, activeTool, setActiveTool, activeColor, setActiveColor, shapes, addShape, removeShape, }: PropsWithChildren<EditorContextType>) => import("react/jsx-runtime").JSX.Element;
|
24
|
+
declare const EditorProvider: ({ children, hideForbiddenTools, activeTool, setActiveTool, activeColor, setActiveColor, shapes, addShape, addShapes, removeShape, configuration, metadata, setMetadata, onSubmit, }: PropsWithChildren<EditorContextType>) => import("react/jsx-runtime").JSX.Element;
|
15
25
|
export default EditorProvider;
|
@@ -8,7 +8,21 @@ export function useEditorContext() {
|
|
8
8
|
}
|
9
9
|
return context;
|
10
10
|
}
|
11
|
-
const EditorProvider = ({ children, activeTool, setActiveTool, activeColor, setActiveColor, shapes, addShape, removeShape, }) => {
|
12
|
-
return (_jsx(EditorContext.Provider, { value: {
|
11
|
+
const EditorProvider = ({ children, hideForbiddenTools, activeTool, setActiveTool, activeColor, setActiveColor, shapes, addShape, addShapes, removeShape, configuration, metadata, setMetadata, onSubmit, }) => {
|
12
|
+
return (_jsx(EditorContext.Provider, { value: {
|
13
|
+
hideForbiddenTools,
|
14
|
+
activeTool,
|
15
|
+
setActiveTool,
|
16
|
+
activeColor,
|
17
|
+
setActiveColor,
|
18
|
+
shapes,
|
19
|
+
addShape,
|
20
|
+
addShapes,
|
21
|
+
removeShape,
|
22
|
+
configuration,
|
23
|
+
metadata,
|
24
|
+
setMetadata,
|
25
|
+
onSubmit,
|
26
|
+
}, children: children }));
|
13
27
|
};
|
14
28
|
export default EditorProvider;
|