@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,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;
|