lost-sia 2.0.1-alpha13 → 2.0.1-alpha15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/Annotation/ui/AnnotationComponent.js +1 -1
  2. package/dist/Annotation/ui/atoms/DaviIcon.d.ts +1 -1
  3. package/dist/Canvas/Canvas.js +1 -1
  4. package/dist/IconButton.d.ts +25 -0
  5. package/dist/IconButton.js +1 -0
  6. package/dist/Sia.js +1 -1
  7. package/dist/Toolbar/ToolbarItems/AccessibilityTools.js +1 -1
  8. package/dist/Toolbar/ToolbarItems/AnnoToolSelector.js +1 -1
  9. package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.js +1 -1
  10. package/dist/Toolbar/ToolbarItems/ImageTools.js +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1 -1
  13. package/dist/models/index.d.ts +1 -1
  14. package/package.json +3 -2
  15. package/src/AnnoExampleViewer.jsx +18 -18
  16. package/src/Annotation/logic/Annotation.ts +24 -24
  17. package/src/Annotation/ui/AnnotationComponent.tsx +89 -96
  18. package/src/Annotation/ui/atoms/DaviIcon.tsx +12 -22
  19. package/src/Annotation/ui/atoms/PolygonArea.tsx +35 -35
  20. package/src/Annotation/ui/tools/Point.tsx +17 -17
  21. package/src/Canvas/Canvas.tsx +48 -17
  22. package/src/Canvas/LabelInput.tsx +27 -34
  23. package/src/IconButton.tsx +119 -0
  24. package/src/InfoBoxes/AnnoDetails.jsx +53 -53
  25. package/src/InfoBoxes/AnnoStats.jsx +41 -41
  26. package/src/InfoBoxes/InfoBox.jsx +16 -16
  27. package/src/InfoBoxes/LabelInfo.jsx +30 -30
  28. package/src/SIASettingButton.jsx +25 -25
  29. package/src/Sia.tsx +43 -15
  30. package/src/Toolbar/Toolbar.tsx +25 -25
  31. package/src/Toolbar/ToolbarItems/AccessibilityTools.tsx +23 -43
  32. package/src/Toolbar/ToolbarItems/AnnoToolSelector.tsx +53 -43
  33. package/src/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.tsx +71 -71
  34. package/src/Toolbar/ToolbarItems/ImageToolItems/TagLabel.tsx +24 -24
  35. package/src/Toolbar/ToolbarItems/ImageTools.tsx +28 -30
  36. package/src/Toolbar/ToolbarItems/Instructions.tsx +47 -50
  37. package/src/Toolbar/ToolbarItems/InstructionsModal.tsx +8 -8
  38. package/src/index.ts +1 -0
  39. package/src/models/AnnotationMode.ts +1 -1
  40. package/src/models/AnnotationStatus.ts +1 -1
  41. package/src/models/AnnotationTool.ts +1 -1
  42. package/src/models/CanvasAction.ts +1 -1
  43. package/src/models/Direction.ts +1 -1
  44. package/src/models/EditorModes.ts +1 -1
  45. package/src/models/KeyAction.ts +1 -1
  46. package/src/models/NotificationType.ts +1 -1
  47. package/src/models/index.ts +6 -12
  48. package/src/siaDummyData.js +71 -71
  49. package/src/stories/Button.jsx +11 -15
  50. package/src/stories/Button.stories.js +17 -17
  51. package/src/stories/Canvas/CanvasWithOffset.stories.tsx +25 -25
  52. package/src/stories/FilterDropdown.stories.ts +10 -10
  53. package/src/stories/Header.jsx +8 -13
  54. package/src/stories/Header.stories.js +9 -9
  55. package/src/stories/Page.jsx +21 -26
  56. package/src/stories/Page.stories.js +14 -14
  57. package/src/stories/SIA2/DemoWrapper.stories.tsx +22 -22
  58. package/src/stories/Toolbar/ImageTools/TagLabel.stories.tsx +11 -11
  59. package/src/stories/Toolbar/Instructions.stories.tsx +11 -11
  60. package/src/stories/Toolbar/Toolbar.stories.tsx +20 -20
  61. package/src/stories/siaDummyData.js +71 -71
  62. package/src/stories/siaDummyData2.ts +72 -72
  63. package/src/types.ts +47 -47
  64. package/src/utils/KeyMapper.ts +56 -61
  65. package/src/utils/TimeUtils.ts +7 -10
  66. package/src/utils/color.ts +25 -25
  67. package/src/utils/hist.js +22 -22
  68. package/src/utils/index.ts +3 -3
  69. package/src/utils/keyActions.js +81 -81
  70. package/src/utils/mouse.ts +9 -9
  71. package/src/utils/transform.ts +66 -72
  72. package/src/utils/uiConfig.js +19 -22
  73. package/src/utils/windowViewport.ts +10 -10
@@ -1,35 +1,35 @@
1
- import AnnotationTool from "../../models/AnnotationTool";
2
- import Annotation from "../logic/Annotation";
3
- import * as colorUtils from "../../utils/color";
4
- import PointTool from "./tools/Point";
5
- import Line from "./tools/Line";
6
- import AnnoBar from "./atoms/AnnoBar";
7
- import CanvasAction from "../../models/CanvasAction";
8
- import BBox from "./tools/BBox";
9
- import Polygon from "./tools/Polygon";
10
- import { useEffect, useRef, useState } from "react";
11
- import { AnnotationSettings, Label, Point, SIANotification } from "../../types";
12
- import AnnotationMode from "../../models/AnnotationMode";
13
- import TimeUtils from "../../utils/TimeUtils";
1
+ import AnnotationTool from '../../models/AnnotationTool'
2
+ import Annotation from '../logic/Annotation'
3
+ import * as colorUtils from '../../utils/color'
4
+ import PointTool from './tools/Point'
5
+ import Line from './tools/Line'
6
+ import AnnoBar from './atoms/AnnoBar'
7
+ import CanvasAction from '../../models/CanvasAction'
8
+ import BBox from './tools/BBox'
9
+ import Polygon from './tools/Polygon'
10
+ import { useEffect, useRef, useState } from 'react'
11
+ import { AnnotationSettings, Label, Point, SIANotification } from '../../types'
12
+ import AnnotationMode from '../../models/AnnotationMode'
13
+ import TimeUtils from '../../utils/TimeUtils'
14
14
 
15
15
  type AnnotationComponentProps = {
16
- scaledAnnotation: Annotation;
17
- annotationSettings: AnnotationSettings;
18
- possibleLabels: Label[];
19
- svgScale: number;
20
- svgTranslation: Point;
21
- pageToStageOffset: Point;
22
- strokeWidth: number;
23
- nodeRadius: number;
24
- isSelected: boolean;
25
- isDisabled?: boolean;
26
- onFinishAnnoCreate: (fullyCreatedAnnotation: Annotation) => void;
27
- onLabelIconClicked: (markerPosition: Point) => void;
28
- onAction?: (annotation: Annotation, canvasAction: CanvasAction) => void;
29
- onAnnoChanged?: (annotation: Annotation) => void;
30
- onAnnotationModeChange?: (annotationMode: AnnotationMode) => void;
31
- onNotification?: (notification: SIANotification) => void;
32
- };
16
+ scaledAnnotation: Annotation
17
+ annotationSettings: AnnotationSettings
18
+ possibleLabels: Label[]
19
+ svgScale: number
20
+ svgTranslation: Point
21
+ pageToStageOffset: Point
22
+ strokeWidth: number
23
+ nodeRadius: number
24
+ isSelected: boolean
25
+ isDisabled?: boolean
26
+ onFinishAnnoCreate: (fullyCreatedAnnotation: Annotation) => void
27
+ onLabelIconClicked: (markerPosition: Point) => void
28
+ onAction?: (annotation: Annotation, canvasAction: CanvasAction) => void
29
+ onAnnoChanged?: (annotation: Annotation) => void
30
+ onAnnotationModeChange?: (annotationMode: AnnotationMode) => void
31
+ onNotification?: (notification: SIANotification) => void
32
+ }
33
33
 
34
34
  const AnnotationComponent = ({
35
35
  scaledAnnotation,
@@ -49,21 +49,19 @@ const AnnotationComponent = ({
49
49
  onAnnotationModeChange = (_) => {},
50
50
  onNotification = (_) => {},
51
51
  }: AnnotationComponentProps) => {
52
- const [coordinates, setCoordinates] = useState<Point[]>(
53
- scaledAnnotation.coordinates,
54
- );
52
+ const [coordinates, setCoordinates] = useState<Point[]>(scaledAnnotation.coordinates)
55
53
 
56
54
  const [annotationMode, setAnnotationMode] = useState<AnnotationMode>(
57
55
  scaledAnnotation.mode,
58
- );
59
- const [isDragging, setIsDragging] = useState<boolean>(false);
56
+ )
57
+ const [isDragging, setIsDragging] = useState<boolean>(false)
60
58
 
61
- const annoTimestampRef = useRef<number | undefined>(undefined);
62
- const [annoTimestamp, setAnnoTimestamp] = useState<number | undefined>();
59
+ const annoTimestampRef = useRef<number | undefined>(undefined)
60
+ const [annoTimestamp, setAnnoTimestamp] = useState<number | undefined>()
63
61
 
64
62
  useEffect(() => {
65
- annoTimestampRef.current = annoTimestamp;
66
- }, [annoTimestamp]);
63
+ annoTimestampRef.current = annoTimestamp
64
+ }, [annoTimestamp])
67
65
 
68
66
  /**
69
67
  * during user editing of the annotation, multiple events are fired by the children
@@ -73,120 +71,115 @@ const AnnotationComponent = ({
73
71
  * since both events are fired during the same render, onMoved would give away old coorinates
74
72
  * use this reference as a workaround to get the up-to-date coordinates even in this edge-case
75
73
  */
76
- const coordinatesRef = useRef<Point[]>(coordinates);
74
+ const coordinatesRef = useRef<Point[]>(coordinates)
77
75
 
78
76
  useEffect(() => {
79
- coordinatesRef.current = coordinates;
80
- }, [coordinates]);
77
+ coordinatesRef.current = coordinates
78
+ }, [coordinates])
81
79
 
82
80
  const finishAnnoCreate = () => {
83
- setAnnotationMode(AnnotationMode.VIEW);
81
+ setAnnotationMode(AnnotationMode.VIEW)
84
82
 
85
83
  const newAnnotation = {
86
84
  ...scaledAnnotation,
87
85
  coordinates: coordinatesRef.current,
88
- };
86
+ }
89
87
 
90
- onFinishAnnoCreate(newAnnotation);
91
- };
88
+ onFinishAnnoCreate(newAnnotation)
89
+ }
92
90
 
93
91
  const getLabel = (labelId: number): Label | undefined => {
94
92
  return possibleLabels.find((label: Label) => {
95
- return label.id === labelId;
96
- });
97
- };
93
+ return label.id === labelId
94
+ })
95
+ }
98
96
 
99
97
  const getColor = () => {
100
98
  if (!scaledAnnotation.labelIds || scaledAnnotation.labelIds.length == 0)
101
- return colorUtils.getDefaultColor();
99
+ return colorUtils.getDefaultColor()
102
100
 
103
- const label = getLabel(scaledAnnotation.labelIds[0]);
101
+ const label = getLabel(scaledAnnotation.labelIds[0])
104
102
 
105
- if (label === undefined || label.color === undefined)
106
- return colorUtils.getDefaultColor();
103
+ if (label === undefined || label.color === undefined || label.color === null)
104
+ return colorUtils.getDefaultColor()
107
105
 
108
- return label.color;
109
- };
106
+ return label.color
107
+ }
110
108
 
111
- const color = getColor();
109
+ const color = getColor()
112
110
  const annotationStyle = {
113
111
  stroke: color,
114
112
  fill: color,
115
113
  strokeWidth: strokeWidth / svgScale,
116
114
  r: nodeRadius / svgScale,
117
- };
115
+ }
118
116
 
119
117
  const changeAnnoCoords = (newCoordinates: Point[]) => {
120
- setCoordinates(newCoordinates);
118
+ setCoordinates(newCoordinates)
121
119
 
122
- let newCoordinatesWithoutMouse = newCoordinates;
120
+ let newCoordinatesWithoutMouse = newCoordinates
123
121
 
124
122
  // while adding/moving the annotation, the mouse cursor is the last point of the coordinates
125
123
  // remove it before saving the annotation
126
124
  if ([AnnotationMode.ADD, AnnotationMode.MOVE].includes(annotationMode)) {
127
125
  // last point is mouse - remove it before export
128
- newCoordinatesWithoutMouse = newCoordinates.slice(0, -1);
126
+ newCoordinatesWithoutMouse = newCoordinates.slice(0, -1)
129
127
  }
130
128
 
131
129
  onAnnoChanged({
132
130
  ...scaledAnnotation,
133
131
  coordinates: newCoordinatesWithoutMouse,
134
- });
135
- };
132
+ })
133
+ }
136
134
 
137
135
  const onMoving = (newCoords: Point[]) => {
138
136
  if (
139
- ![
140
- AnnotationMode.ADD,
141
- AnnotationMode.CREATE,
142
- AnnotationMode.MOVE,
143
- ].includes(annotationMode)
137
+ ![AnnotationMode.ADD, AnnotationMode.CREATE, AnnotationMode.MOVE].includes(
138
+ annotationMode,
139
+ )
144
140
  ) {
145
- setAnnotationMode(AnnotationMode.MOVE);
141
+ setAnnotationMode(AnnotationMode.MOVE)
146
142
 
147
- setAnnoTimestamp(performance.now());
143
+ setAnnoTimestamp(performance.now())
148
144
  }
149
145
 
150
- setCoordinates(newCoords);
151
- };
146
+ setCoordinates(newCoords)
147
+ }
152
148
 
153
149
  const onMoved = () => {
154
- setAnnotationMode(AnnotationMode.VIEW);
150
+ setAnnotationMode(AnnotationMode.VIEW)
155
151
 
156
152
  const annoEditDuration: number = TimeUtils.getRoundedDuration(
157
153
  annoTimestampRef.current,
158
154
  performance.now(),
159
- );
155
+ )
160
156
 
161
157
  // add annotation time (or set it if there was no time before)
162
158
  // null seems to be a number in the JS world
163
159
  const newAnnoTime: number =
164
160
  isNaN(scaledAnnotation.annoTime) || scaledAnnotation.annoTime === null
165
161
  ? annoEditDuration
166
- : scaledAnnotation.annoTime + annoEditDuration;
162
+ : scaledAnnotation.annoTime + annoEditDuration
167
163
 
168
164
  // moving finished - send event to canvas
169
165
  onAnnoChanged({
170
166
  ...scaledAnnotation,
171
167
  coordinates: coordinatesRef.current,
172
168
  annoTime: newAnnoTime,
173
- });
174
- };
169
+ })
170
+ }
175
171
 
176
172
  useEffect(() => {
177
- onAnnotationModeChange(annotationMode);
178
- }, [annotationMode]);
173
+ onAnnotationModeChange(annotationMode)
174
+ }, [annotationMode])
179
175
 
180
176
  // apply coordinate changes from sia (e.g. out of image fixes)
181
177
  // ignore outside changes while creating annotation
182
178
  useEffect(() => {
183
- if (
184
- annotationMode === AnnotationMode.CREATE ||
185
- annotationMode === AnnotationMode.ADD
186
- )
187
- return;
188
- setCoordinates(scaledAnnotation.coordinates);
189
- }, [scaledAnnotation]);
179
+ if (annotationMode === AnnotationMode.CREATE || annotationMode === AnnotationMode.ADD)
180
+ return
181
+ setCoordinates(scaledAnnotation.coordinates)
182
+ }, [scaledAnnotation])
190
183
 
191
184
  const renderAnno = () => {
192
185
  switch (scaledAnnotation.type) {
@@ -204,7 +197,7 @@ const AnnotationComponent = ({
204
197
  onMoved={onMoved}
205
198
  onIsDraggingStateChanged={setIsDragging}
206
199
  />
207
- );
200
+ )
208
201
  case AnnotationTool.Line:
209
202
  return (
210
203
  <Line
@@ -224,7 +217,7 @@ const AnnotationComponent = ({
224
217
  onIsDraggingStateChanged={setIsDragging}
225
218
  onFinishAnnoCreate={finishAnnoCreate}
226
219
  />
227
- );
220
+ )
228
221
  case AnnotationTool.BBox:
229
222
  return (
230
223
  <BBox
@@ -238,14 +231,14 @@ const AnnotationComponent = ({
238
231
  svgScale={svgScale}
239
232
  svgTranslation={svgTranslation}
240
233
  onDeleteNode={() => {
241
- console.log("TODO");
234
+ console.log('TODO')
242
235
  }}
243
236
  onIsDraggingStateChanged={setIsDragging}
244
237
  onFinishAnnoCreate={finishAnnoCreate}
245
238
  onMoving={onMoving}
246
239
  onMoved={onMoved}
247
240
  />
248
- );
241
+ )
249
242
  case AnnotationTool.Polygon:
250
243
  return (
251
244
  <Polygon
@@ -267,9 +260,9 @@ const AnnotationComponent = ({
267
260
  onIsDraggingStateChanged={setIsDragging}
268
261
  onFinishAnnoCreate={finishAnnoCreate}
269
262
  />
270
- );
263
+ )
271
264
  }
272
- };
265
+ }
273
266
 
274
267
  return (
275
268
  <g
@@ -278,8 +271,8 @@ const AnnotationComponent = ({
278
271
  // onMouseDown={(e) => this.onMouseDown(e)}
279
272
  // onContextMenu={(e) => this.onContextMenu(e)}
280
273
  onClick={(e) => {
281
- e.stopPropagation();
282
- onAction(scaledAnnotation, CanvasAction.ANNO_SELECTED);
274
+ e.stopPropagation()
275
+ onAction(scaledAnnotation, CanvasAction.ANNO_SELECTED)
283
276
  }}
284
277
  >
285
278
  {!isDragging && annotationMode !== AnnotationMode.CREATE && (
@@ -297,7 +290,7 @@ const AnnotationComponent = ({
297
290
  )}
298
291
  {renderAnno()}
299
292
  </g>
300
- );
301
- };
293
+ )
294
+ }
302
295
 
303
- export default AnnotationComponent;
296
+ export default AnnotationComponent
@@ -1,26 +1,16 @@
1
1
  type DaviIconProps = {
2
- x: number;
3
- y: number;
4
- color: string;
5
- size: number;
6
- onClick?: () => void;
7
- };
2
+ x: number
3
+ y: number
4
+ color: string
5
+ size: number
6
+ onClick?: () => void
7
+ }
8
8
 
9
- const DaviIcon = ({
10
- x,
11
- y,
12
- color,
13
- size = 60,
14
- onClick = () => {},
15
- }: DaviIconProps) => {
16
- const scale: number = size / 1400;
9
+ const DaviIcon = ({ x, y, color, size = 60, onClick = () => {} }: DaviIconProps) => {
10
+ const scale: number = size / 1400
17
11
 
18
12
  return (
19
- <g
20
- transform={`translate(${x} ${y}) scale(${scale})`}
21
- fill={color}
22
- onClick={onClick}
23
- >
13
+ <g transform={`translate(${x} ${y}) scale(${scale})`} fill={color} onClick={onClick}>
24
14
  <path
25
15
  id="Maps"
26
16
  d="M620.561,817.217c-1.568-3.62-3.771-7.101-4.611-10.885
@@ -51,7 +41,7 @@ const DaviIcon = ({
51
41
  c-0.074,8.051-6.938,12.819-13.703,12.847C731.482,263.519,724.956,259.059,724.709,250.898z"
52
42
  />
53
43
  </g>
54
- );
55
- };
44
+ )
45
+ }
56
46
 
57
- export default DaviIcon;
47
+ export default DaviIcon
@@ -1,23 +1,23 @@
1
- import { CSSProperties, useEffect, useState } from "react";
2
- import { Point } from "../../../types";
3
- import AnnotationMode from "../../../models/AnnotationMode";
4
- import { AnnotationSettings } from "../../../types";
1
+ import { CSSProperties, useEffect, useState } from 'react'
2
+ import { Point } from '../../../types'
3
+ import AnnotationMode from '../../../models/AnnotationMode'
4
+ import { AnnotationSettings } from '../../../types'
5
5
 
6
6
  type PolygonAreaProps = {
7
- coordinates: Point[];
8
- isSelected: boolean;
9
- isDisabled?: boolean;
10
- annotationMode: AnnotationMode;
11
- annotationSettings: AnnotationSettings;
12
- pageToStageOffset: Point;
13
- svgScale: number;
14
- style: CSSProperties;
15
- onFinishAnnoCreate?: () => void;
16
- onMouseDown: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void;
17
- onMouseUp?: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void;
18
- onMouseMove: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void;
19
- onIsDraggingStateChanged: (bool) => void;
20
- };
7
+ coordinates: Point[]
8
+ isSelected: boolean
9
+ isDisabled?: boolean
10
+ annotationMode: AnnotationMode
11
+ annotationSettings: AnnotationSettings
12
+ pageToStageOffset: Point
13
+ svgScale: number
14
+ style: CSSProperties
15
+ onFinishAnnoCreate?: () => void
16
+ onMouseDown: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
17
+ onMouseUp?: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
18
+ onMouseMove: (e: React.MouseEvent<SVGPolygonElement, MouseEvent>) => void
19
+ onIsDraggingStateChanged: (bool) => void
20
+ }
21
21
 
22
22
  const PolygonArea = ({
23
23
  coordinates,
@@ -33,35 +33,35 @@ const PolygonArea = ({
33
33
  // draw line between nodes
34
34
  const svgLineCoords: string = coordinates
35
35
  .map((point: Point) => `${point.x},${point.y}`)
36
- .join(" ");
36
+ .join(' ')
37
37
 
38
- const [cursorStyle, setCursorStyle] = useState<string>("pointer");
38
+ const [cursorStyle, setCursorStyle] = useState<string>('pointer')
39
39
 
40
40
  useEffect(() => {
41
- if (isDisabled) return setCursorStyle("not-allowed");
42
- if (isSelected) setCursorStyle("grab");
43
- else setCursorStyle("pointer");
44
- }, [isSelected, isDisabled]);
41
+ if (isDisabled) return setCursorStyle('not-allowed')
42
+ if (isSelected) setCursorStyle('grab')
43
+ else setCursorStyle('pointer')
44
+ }, [isSelected, isDisabled])
45
45
 
46
46
  // adjust style for polyline
47
- const polyLineStyle = { ...style };
48
- polyLineStyle.cursor = cursorStyle;
49
- polyLineStyle.fillOpacity = isSelected ? 0 : 0.3;
47
+ const polyLineStyle = { ...style }
48
+ polyLineStyle.cursor = cursorStyle
49
+ polyLineStyle.fillOpacity = isSelected ? 0 : 0.3
50
50
 
51
51
  // dont show the polygon edges (the line does what as a stripe if enabled)
52
- if (isSelected && isDisabled) polyLineStyle.stroke = "none";
52
+ if (isSelected && isDisabled) polyLineStyle.stroke = 'none'
53
53
 
54
54
  return (
55
55
  <polygon
56
56
  points={svgLineCoords}
57
57
  style={polyLineStyle}
58
58
  onMouseDown={(e) => {
59
- if (isSelected) setCursorStyle("grabbing");
60
- onMouseDown(e);
59
+ if (isSelected) setCursorStyle('grabbing')
60
+ onMouseDown(e)
61
61
  }}
62
62
  onMouseUp={(e) => {
63
- setCursorStyle("grab");
64
- onMouseUp(e);
63
+ setCursorStyle('grab')
64
+ onMouseUp(e)
65
65
  }}
66
66
  onDoubleClick={() =>
67
67
  annotationMode === AnnotationMode.CREATE && onFinishAnnoCreate()
@@ -69,7 +69,7 @@ const PolygonArea = ({
69
69
  onMouseMove={onMouseMove}
70
70
  onContextMenu={(e) => e.preventDefault()}
71
71
  />
72
- );
73
- };
72
+ )
73
+ }
74
74
 
75
- export default PolygonArea;
75
+ export default PolygonArea
@@ -1,21 +1,21 @@
1
- import { CSSProperties } from "react";
1
+ import { CSSProperties } from 'react'
2
2
 
3
3
  // rename type to avoid naming conflict
4
- import { AnnotationSettings, Point as TPoint } from "../../../types";
5
- import Node from "../atoms/Node";
4
+ import { AnnotationSettings, Point as TPoint } from '../../../types'
5
+ import Node from '../atoms/Node'
6
6
 
7
7
  type PointProps = {
8
- annotationSettings: AnnotationSettings;
9
- coordinates: TPoint;
10
- isSelected: boolean;
11
- pageToStageOffset: TPoint;
12
- svgScale: number;
13
- svgTranslation: TPoint;
14
- style: CSSProperties;
15
- onIsDraggingStateChanged: (bool) => void;
16
- onMoving: (coordinates: TPoint) => void; // during moving - update coordinates in parent
17
- onMoved: (coordinates: TPoint[]) => void; // moving finished - send annotation changed event
18
- };
8
+ annotationSettings: AnnotationSettings
9
+ coordinates: TPoint
10
+ isSelected: boolean
11
+ pageToStageOffset: TPoint
12
+ svgScale: number
13
+ svgTranslation: TPoint
14
+ style: CSSProperties
15
+ onIsDraggingStateChanged: (bool) => void
16
+ onMoving: (coordinates: TPoint) => void // during moving - update coordinates in parent
17
+ onMoved: (coordinates: TPoint[]) => void // moving finished - send annotation changed event
18
+ }
19
19
 
20
20
  const Point = ({
21
21
  annotationSettings,
@@ -46,7 +46,7 @@ const Point = ({
46
46
  onMoved={() => onMoved([coordinates])}
47
47
  onIsDraggingStateChanged={onIsDraggingStateChanged}
48
48
  />
49
- );
50
- };
49
+ )
50
+ }
51
51
 
52
- export default Point;
52
+ export default Point
@@ -133,6 +133,9 @@ const Canvas = ({
133
133
  const [labelInputPosition, setLabelInputPosition] = useState<Point>()
134
134
  const [isLabelInputVisible, setIsLabelInputVisible] = useState<boolean>(false)
135
135
 
136
+ // svg element - used to trap focus
137
+ const svgRef = useRef(null)
138
+
136
139
  // available canvas area - all possible space for creating a canvas
137
140
  const canvasRef = useRef(null)
138
141
 
@@ -234,7 +237,6 @@ const Canvas = ({
234
237
  }
235
238
  }
236
239
 
237
- // @TODO
238
240
  const editSelectedAnnotation = () => {
239
241
  if (
240
242
  selectedAnnotation &&
@@ -242,8 +244,6 @@ const Canvas = ({
242
244
  )
243
245
  return
244
246
 
245
- // selectedAnnotation!.mode = AnnotationMode.CREATE;
246
-
247
247
  const newSelectedAnnotation: Annotation | undefined = annotations.find(
248
248
  (annotation: Annotation) =>
249
249
  annotation.internalId === selectedAnnotation?.internalId,
@@ -251,18 +251,20 @@ const Canvas = ({
251
251
 
252
252
  if (newSelectedAnnotation === undefined) return
253
253
 
254
- newSelectedAnnotation.mode = AnnotationMode.CREATE
255
- newSelectedAnnotation.status = AnnotationStatus.CREATING
256
- newSelectedAnnotation.selectedNode = newSelectedAnnotation.coordinates.length - 1
257
- setEditorMode(EditorModes.ADD)
254
+ setEditorMode(EditorModes.CREATE)
258
255
  onSetSelectedTool(newSelectedAnnotation.type)
259
256
 
260
- // replace annotation in list
261
- // const selectedAnnotationId: number = annotations.findIndex((annotation: Annotation) => annotation.internalId === selectedAnnotation?.internalId)
262
- // const newAnnotations = [...annotations]
263
- // newAnnotations[selectedAnnotationId] = newSelectedAnnotation
264
- onAnnoEditing(newSelectedAnnotation)
265
- // onShouldDeleteAnno(selectedAnnotation!.internalId);
257
+ // recreate annotation with new internal id
258
+ const _newAnnotation: Annotation = {
259
+ ...newSelectedAnnotation,
260
+ mode: AnnotationMode.CREATE,
261
+ status: AnnotationStatus.CREATING,
262
+ internalId: onRequestNewAnnoId(),
263
+ selectedNode: newSelectedAnnotation.coordinates.length - 1,
264
+ }
265
+
266
+ // "copy" annotation by recreating it with a new internal it and deleting the old one
267
+ onAnnoEditing(_newAnnotation)
266
268
  }
267
269
 
268
270
  const traverseAnnos = () => {
@@ -473,6 +475,11 @@ const Canvas = ({
473
475
  setIsLabelInputVisible(false)
474
476
  }
475
477
 
478
+ useEffect(() => {
479
+ // set initial focus to make keybinds work
480
+ svgRef.current?.focus()
481
+ }, [])
482
+
476
483
  // image changed after init -> reset everything
477
484
  useEffect(() => {
478
485
  if (canvasRef?.current !== undefined) {
@@ -832,6 +839,21 @@ const Canvas = ({
832
839
  },
833
840
  )
834
841
 
842
+ // move the selected annotation to the last spot
843
+ // this way, it's always on top of the others so that its nodes can be reached
844
+ if (selectedAnnotation) {
845
+ const _selectedAnnotation: Annotation = scaledAnnotations.find(
846
+ (a) => a.internalId === selectedAnnotation?.internalId,
847
+ )
848
+
849
+ // since annos was generated by iterating through scaledAnnotations, the index is the same
850
+ const selectedAnnotationIndex: number =
851
+ scaledAnnotations.indexOf(_selectedAnnotation)
852
+
853
+ // move the selected annotation to the last spot
854
+ annos.push(annos.splice(selectedAnnotationIndex, 1)[0])
855
+ }
856
+
835
857
  return <g>{annos}</g>
836
858
  }
837
859
 
@@ -862,8 +884,13 @@ const Canvas = ({
862
884
  <div
863
885
  ref={canvasRef}
864
886
  style={{
865
- width: '100%',
866
- height: '100%',
887
+ // use the max available height as a flex child
888
+ flex: '1 1 auto',
889
+ minHeight: 0,
890
+
891
+ // give the max available height to the next child
892
+ display: 'flex',
893
+ flexDirection: 'column',
867
894
  }}
868
895
  >
869
896
  <div
@@ -941,8 +968,12 @@ const Canvas = ({
941
968
  )}
942
969
 
943
970
  <svg
944
- width="100%"
945
- height="100%"
971
+ ref={svgRef}
972
+ style={{
973
+ // use the max available height as a flex child
974
+ flex: '1 1 auto',
975
+ minHeight: 0,
976
+ }}
946
977
  onKeyDown={onKeyDown}
947
978
  onKeyUp={onKeyUp}
948
979
  onMouseMove={(e) => onMouseMove(e.movementX, e.movementY)}