lost-sia 3.2.0-alpha0 → 3.2.0

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 (54) hide show
  1. package/dist/Canvas/Canvas.js +1 -1
  2. package/dist/Canvas/LabelInput.js +1 -1
  3. package/dist/Sia.js +1 -1
  4. package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.d.ts +2 -1
  5. package/dist/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.js +1 -1
  6. package/dist/Toolbar/ToolbarItems/ImageToolItems/TagLabel.d.ts +2 -1
  7. package/dist/Toolbar/ToolbarItems/ImageToolItems/TagLabel.js +1 -1
  8. package/dist/Toolbar/ToolbarItems/ImageTools.js +1 -1
  9. package/dist/assets/SIA-CoOqcT1a.css +1 -0
  10. package/dist/assets/brand-icons-Cu_C0hZ4.svg +1008 -0
  11. package/dist/assets/brand-icons-F3SPCeH1.woff +0 -0
  12. package/dist/assets/brand-icons-XL9sxUpA.woff2 +0 -0
  13. package/dist/assets/brand-icons-sqJ2Pg7a.eot +0 -0
  14. package/dist/assets/brand-icons-ubhWoxly.ttf +0 -0
  15. package/dist/assets/flags-DOLqOU7Y.png +0 -0
  16. package/dist/assets/icons-BOCtAERH.woff +0 -0
  17. package/dist/assets/icons-CHzK1VD9.eot +0 -0
  18. package/dist/assets/icons-D29ZQHHw.ttf +0 -0
  19. package/dist/assets/icons-Du6TOHnR.woff2 +0 -0
  20. package/dist/assets/icons-RwhydX30.svg +1518 -0
  21. package/dist/assets/node_modules/semantic-ui-css/semantic.min-09YPtVE6.css +1 -0
  22. package/dist/assets/outline-icons-BfdLr8tr.svg +366 -0
  23. package/dist/assets/outline-icons-DD8jm0uy.ttf +0 -0
  24. package/dist/assets/outline-icons-DInHoiqI.woff2 +0 -0
  25. package/dist/assets/outline-icons-LX8adJ4n.eot +0 -0
  26. package/dist/assets/outline-icons-aQ88nltS.woff +0 -0
  27. package/dist/index.d.ts +0 -1
  28. package/dist/index.js +1 -1
  29. package/dist/stories/Canvas/Canvas.stories.d.ts +3 -3
  30. package/dist/stories/Canvas/CanvasWithOffset.stories.d.ts +6 -6
  31. package/dist/stories/Toolbar/ImageTools/TagLabel.stories.d.ts +2 -1
  32. package/package.json +4 -3
  33. package/src/AnnoExampleViewer.jsx +69 -0
  34. package/src/Canvas/Canvas.tsx +45 -17
  35. package/src/Canvas/LabelInput.tsx +11 -9
  36. package/src/InfoBoxes/AnnoDetails.jsx +165 -0
  37. package/src/InfoBoxes/AnnoStats.jsx +106 -0
  38. package/src/InfoBoxes/InfoBox.jsx +76 -0
  39. package/src/InfoBoxes/InfoBoxArea.jsx +152 -0
  40. package/src/InfoBoxes/LabelInfo.jsx +107 -0
  41. package/src/SIA.scss +13 -0
  42. package/src/SIASettingButton.jsx +126 -0
  43. package/src/Sia.tsx +70 -54
  44. package/src/Toolbar/ToolbarItems/ImageToolItems/ImageLabelInput.tsx +74 -24
  45. package/src/Toolbar/ToolbarItems/ImageToolItems/TagLabel.tsx +33 -0
  46. package/src/Toolbar/ToolbarItems/ImageTools.tsx +1 -0
  47. package/src/index.ts +1 -1
  48. package/src/stories/exampleData/exampleExternalAnnotations.ts +22 -22
  49. package/dist/SiaViewer.d.ts +0 -19
  50. package/dist/SiaViewer.js +0 -1
  51. package/dist/assets/SIA-BV1tYu3P.css +0 -1
  52. package/dist/stories/SIA/SiaViewer.stories.d.ts +0 -52
  53. package/src/SiaViewer.tsx +0 -125
  54. package/src/stories/SIA/SiaViewer.stories.tsx +0 -105
@@ -0,0 +1,152 @@
1
+ import { Component } from 'react'
2
+ import AnnoDetails from './AnnoDetails'
3
+ import AnnoStats from './AnnoStats'
4
+ import LabelInfo from './LabelInfo'
5
+
6
+ class InfoBoxes extends Component {
7
+ constructor(props) {
8
+ super(props)
9
+ this.state = {
10
+ position: {
11
+ top: 0,
12
+ left: 0,
13
+ },
14
+ }
15
+ }
16
+
17
+ componentDidMount() {
18
+ this.updateLayout()
19
+ }
20
+
21
+ componentDidUpdate(prevProps) {
22
+ if (this.props.layoutUpdate !== prevProps.layoutUpdate) {
23
+ this.updateLayout()
24
+ }
25
+ if (this.props.commentInputTrigger !== prevProps.commentInputTrigger) {
26
+ if (!this.props.uiConfig.annoDetails.visible) {
27
+ this.showAnnoDetails(true)
28
+ }
29
+ }
30
+ }
31
+
32
+ updateLayout() {
33
+ if (this.props.container.current) {
34
+ const container = this.props.container.current.getBoundingClientRect()
35
+ this.setState({
36
+ position: {
37
+ ...this.state.position,
38
+ left: container.right - 250,
39
+ top: container.top,
40
+ },
41
+ })
42
+ }
43
+ }
44
+
45
+ showAnnoDetails(show = true) {
46
+ this.props.onUiConfigUpdate({
47
+ ...this.props.uiConfig,
48
+ annoDetails: {
49
+ ...this.props.uiConfig.annoDetails,
50
+ visible: show,
51
+ },
52
+ })
53
+ }
54
+
55
+ onDismiss(type) {
56
+ if (this.props.onUiConfigUpdate) {
57
+ switch (type) {
58
+ case 'AnnoDetails':
59
+ this.showAnnoDetails(false)
60
+ break
61
+ case 'LabelInfo':
62
+ this.props.onUiConfigUpdate({
63
+ ...this.props.uiConfig,
64
+ labelInfo: {
65
+ ...this.props.uiConfig.labelInfo,
66
+ visible: false,
67
+ },
68
+ })
69
+ break
70
+ case 'AnnoStats':
71
+ this.props.onUiConfigUpdate({
72
+ ...this.props.uiConfig,
73
+ annoStats: {
74
+ ...this.props.uiConfig.annoStats,
75
+ visible: false,
76
+ },
77
+ })
78
+ break
79
+ default:
80
+ break
81
+ }
82
+ }
83
+ }
84
+
85
+ onCommentUpdate(comment) {
86
+ if (this.props.onCommentUpdate) {
87
+ this.props.onCommentUpdate(comment)
88
+ }
89
+ }
90
+
91
+ onMarkExample(anno) {
92
+ if (this.props.onMarkExample) {
93
+ this.props.onMarkExample(anno)
94
+ }
95
+ }
96
+
97
+ onHideLbl(lbl, hide) {
98
+ if (this.props.onHideLbl) {
99
+ this.props.onHideLbl(lbl, hide)
100
+ }
101
+ }
102
+
103
+ render() {
104
+ if (!this.props.annos) return null
105
+ // if (!this.props.selectedAnno) return null
106
+ return (
107
+ <div>
108
+ <LabelInfo
109
+ selectedAnno={this.props.selectedAnno}
110
+ possibleLabels={this.props.possibleLabels}
111
+ defaultPos={this.state.position}
112
+ onDismiss={() => this.onDismiss('LabelInfo')}
113
+ visible={this.props.uiConfig.labelInfo.visible}
114
+ onGetAnnoExample={(exampleArgs) =>
115
+ this.props.onGetAnnoExample ? this.props.onGetAnnoExample(exampleArgs) : {}
116
+ }
117
+ exampleImg={this.props.exampleImg}
118
+ />
119
+ <AnnoDetails
120
+ anno={this.props.selectedAnno}
121
+ possibleLabels={this.props.possibleLabels}
122
+ defaultPos={{
123
+ left: this.state.position.left - 300,
124
+ top: this.state.position.top,
125
+ }}
126
+ onDismiss={() => this.onDismiss('AnnoDetails')}
127
+ onCommentUpdate={(comment) => this.onCommentUpdate(comment)}
128
+ onMarkExample={(anno) => this.onMarkExample(anno)}
129
+ allowedToMarkExample={this.props.allowedToMarkExample}
130
+ commentInputTrigger={this.props.commentInputTrigger}
131
+ visible={this.props.uiConfig.annoDetails.visible}
132
+ />
133
+ <AnnoStats
134
+ selectedAnno={this.props.selectedAnno}
135
+ annos={this.props.annos}
136
+ possibleLabels={this.props.possibleLabels}
137
+ defaultPos={{
138
+ left: this.state.position.left,
139
+ top: this.state.position.top + 400,
140
+ }}
141
+ // defaultPos={this.state.position}
142
+ onDismiss={() => this.onDismiss('AnnoStats')}
143
+ onHideLbl={(lbl, hide) => this.onHideLbl(lbl, hide)}
144
+ visible={this.props.uiConfig.annoStats.visible}
145
+ imgLoadCount={this.props.imgLoadCount}
146
+ />
147
+ </div>
148
+ )
149
+ }
150
+ }
151
+
152
+ export default InfoBoxes
@@ -0,0 +1,107 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import InfoBox from './InfoBox'
3
+ import SiaPopup from '../SiaPopup'
4
+ import AnnoExampleViewer from '../AnnoExampleViewer'
5
+ const LabelInfo = (props) => {
6
+ const [showExampleViewer, setShowExampleViewer] = useState(false)
7
+ const [myLbl, setMyLbl] = useState(undefined)
8
+ // const { data: exampleImg, mutate: getAnnoExample } = exampleApi.useGetAnnoExampleImg({})
9
+ useEffect(() => {
10
+ if (props.selectedAnno) {
11
+ const selectedLabelIds = props.selectedAnno.labelIds
12
+ if (selectedLabelIds) {
13
+ const lbl = props.possibleLabels.find((e) => {
14
+ return selectedLabelIds[0] === e.id
15
+ })
16
+ if (lbl) {
17
+ if (lbl !== myLbl) {
18
+ setMyLbl(lbl)
19
+ if (props.visible)
20
+ // getAnnoExample({llId:lbl.id, type:'annoBased', drawAnno: true, addContext:0.05})
21
+ requestImg(lbl, props.selectedAnno)
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }, [props.selectedAnno])
27
+ const onDismiss = () => {
28
+ if (props.onDismiss) {
29
+ props.onDismiss()
30
+ }
31
+ }
32
+
33
+ const requestImg = (lbl, anno) => {
34
+ if (props.onGetAnnoExample) {
35
+ props.onGetAnnoExample({ lbl: lbl, anno: anno })
36
+ }
37
+ }
38
+
39
+ const handleImgClick = () => {
40
+ // setShowExampleViewer(true)
41
+ // requestImg(myLbl, props.selectedAnno)
42
+
43
+ setShowExampleViewer(true)
44
+ // getAnnoExample({llId:myLbl.id, type:'annoBased', drawAnno: true, addContext:0.05})
45
+ }
46
+
47
+ const renderExampleImg = () => {
48
+ if (!props.exampleImg) return null
49
+ return (
50
+ <div>
51
+ <h4 onClick={() => handleImgClick()}>Example:</h4>
52
+ <SiaPopup
53
+ trigger={
54
+ <img
55
+ src={props.exampleImg.img}
56
+ onClick={() => handleImgClick()}
57
+ style={{ borderRadius: 25, width: '100%' }}
58
+ />
59
+ }
60
+ content={'Click on image to view more examples'}
61
+ />
62
+ </div>
63
+ )
64
+ }
65
+
66
+ const renderDescription = () => {
67
+ // if (props.selectedAnno){
68
+ // if (myLbl){
69
+ // const selectedLabelIds = props.selectedAnno.labelIds
70
+ // if (!selectedLabelIds) return 'No Label'
71
+ // const lbl = props.possibleLabels.find( e => {
72
+ // return selectedLabelIds[0] === e.id
73
+ // })
74
+ if (!myLbl) return 'No Label'
75
+ return (
76
+ <div>
77
+ <h4>{myLbl.label}</h4>
78
+ <div dangerouslySetInnerHTML={{ __html: myLbl.description }} />
79
+ {renderExampleImg()}
80
+ <AnnoExampleViewer
81
+ onRequestExample={() => requestImg(myLbl, props.selectedAnno)}
82
+ onClose={() => {
83
+ setShowExampleViewer(false)
84
+ }}
85
+ active={showExampleViewer}
86
+ lbl={myLbl}
87
+ exampleImg={props.exampleImg}
88
+ />
89
+ </div>
90
+ )
91
+ // } else {
92
+ // return 'No Label'
93
+ // }
94
+ }
95
+
96
+ return (
97
+ <InfoBox
98
+ header="Label Info"
99
+ content={renderDescription()}
100
+ visible={props.visible}
101
+ defaultPos={props.defaultPos}
102
+ onDismiss={() => onDismiss()}
103
+ />
104
+ )
105
+ }
106
+
107
+ export default LabelInfo
package/src/SIA.scss CHANGED
@@ -7,3 +7,16 @@
7
7
  height: 100%;
8
8
  background-color: #ffff;
9
9
  }
10
+
11
+ .tag-scroll::-webkit-scrollbar {
12
+ height: 4px;
13
+ }
14
+
15
+ .tag-scroll::-webkit-scrollbar-track {
16
+ background: transparent;
17
+ }
18
+
19
+ .tag-scroll::-webkit-scrollbar-thumb {
20
+ background-color: rgba(0, 0, 0, 0.25);
21
+ border-radius: 2px;
22
+ }
@@ -0,0 +1,126 @@
1
+ import React, { Component } from 'react'
2
+
3
+ import * as tbe from './types/toolbarEvents'
4
+ import { faCog } from '@fortawesome/free-solid-svg-icons'
5
+ import { CFormSwitch, CPopover } from '@coreui/react'
6
+ import ToolbarItem from './ToolbarItem'
7
+ class SIASettingButton extends Component {
8
+ constructor(props) {
9
+ super(props)
10
+ this.state = {}
11
+ }
12
+
13
+ triggerEvent(e, data) {
14
+ if (this.props.onSettingEvent) {
15
+ this.props.onSettingEvent(e, data)
16
+ }
17
+ }
18
+ toggleAnnoDetails() {
19
+ this.triggerEvent(tbe.SHOW_ANNO_DETAILS)
20
+ }
21
+
22
+ toggleLabelInfo() {
23
+ this.triggerEvent(tbe.SHOW_LABEL_INFO)
24
+ }
25
+
26
+ toggleAnnoStats() {
27
+ this.triggerEvent(tbe.SHOW_ANNO_STATS)
28
+ }
29
+
30
+ handleStrokeWidthChange(e) {
31
+ this.triggerEvent(tbe.EDIT_STROKE_WIDTH, parseInt(e.target.value))
32
+ }
33
+
34
+ handleNodeRadiusChange(e) {
35
+ this.triggerEvent(tbe.EDIT_NODE_RADIUS, parseInt(e.target.value))
36
+ }
37
+
38
+ renderInfoBoxContent() {
39
+ return (
40
+ <div>
41
+ <h4>Info Boxes</h4>
42
+ <CFormSwitch
43
+ checked={this.props.uiConfig.annoDetails.visible}
44
+ label="Annotation Details"
45
+ size="xl"
46
+ onClick={() => this.toggleAnnoDetails()}
47
+ />
48
+ <CFormSwitch
49
+ checked={this.props.uiConfig.labelInfo.visible}
50
+ label="Label Info"
51
+ size="xl"
52
+ onClick={() => this.toggleLabelInfo()}
53
+ />
54
+ <CFormSwitch
55
+ checked={this.props.uiConfig.annoStats.visible}
56
+ label="Anno Stats"
57
+ size="xl"
58
+ onClick={() => this.toggleAnnoStats()}
59
+ />
60
+ </div>
61
+ )
62
+ }
63
+ renderInfoBoxes() {
64
+ if (!this.props.enabled) return null
65
+ if (this.props.enabled === true) {
66
+ return this.renderInfoBoxContent()
67
+ } else {
68
+ if (this.props.enabled.infoBoxes) {
69
+ return this.renderInfoBoxContent()
70
+ }
71
+ }
72
+ }
73
+
74
+ renderAnnoStyle() {
75
+ if (!this.props.enabled) return null
76
+ if (this.props.enabled === true) {
77
+ return this.renderAnnoStyleContent()
78
+ } else {
79
+ if (this.props.enabled.annoStyle) {
80
+ return this.renderAnnoStyleContent()
81
+ }
82
+ }
83
+ }
84
+ renderAnnoStyleContent() {
85
+ return (
86
+ <div>
87
+ <h4 style={{ marginTop: 25 }}>Anno Appearance</h4>
88
+ <div>Stroke width: {this.props.uiConfig.strokeWidth}</div>
89
+ <input
90
+ type="range"
91
+ min={1}
92
+ max={10}
93
+ value={this.props.uiConfig.strokeWidth}
94
+ onChange={(e) => this.handleStrokeWidthChange(e)}
95
+ />
96
+ <div>Node radius: {this.props.uiConfig.nodeRadius}</div>
97
+ <input
98
+ type="range"
99
+ min={1}
100
+ max={10}
101
+ value={this.props.uiConfig.nodeRadius}
102
+ onChange={(e) => this.handleNodeRadiusChange(e)}
103
+ />
104
+ </div>
105
+ )
106
+ }
107
+
108
+ render() {
109
+ if (!this.props.uiConfig) return null
110
+ const popupContent = (
111
+ <div>
112
+ {this.renderInfoBoxes()}
113
+ {this.renderAnnoStyle()}
114
+ </div>
115
+ )
116
+ return (
117
+ <CPopover content={popupContent} placement="right">
118
+ <span>
119
+ <ToolbarItem faIcon={faCog} />
120
+ </span>
121
+ </CPopover>
122
+ )
123
+ }
124
+ }
125
+
126
+ export default SIASettingButton
package/src/Sia.tsx CHANGED
@@ -95,24 +95,38 @@ const Sia = ({
95
95
  defaultAnnotationTool ?? AnnotationTool.Point,
96
96
  )
97
97
 
98
+ // refs to always have the latest state without stale closure issues
99
+ const annotationsRef = useRef<Annotation[]>(annotations)
100
+ useEffect(() => {
101
+ annotationsRef.current = annotations
102
+ }, [annotations])
103
+
104
+ const annotationHistoryIndexRef = useRef<number | undefined>(annotationHistoryIndex)
105
+ useEffect(() => {
106
+ annotationHistoryIndexRef.current = annotationHistoryIndex
107
+ }, [annotationHistoryIndex])
108
+
98
109
  const updateAnnotationHistory = (annotations: Annotation[]) => {
99
110
  const _annotations = [...annotations]
100
- const _annotationHistory = [...annotationHistory]
101
111
 
102
- // user did some changes from within the past
103
- // time to create an alternative timeline and delete the original one
104
- if (annotationHistoryIndex !== undefined) {
105
- // remove everything after the state the user is
106
- _annotationHistory.splice(annotationHistoryIndex + 1)
107
- }
112
+ setAnnotationHistory((prevHistory) => {
113
+ const _annotationHistory = [...prevHistory]
108
114
 
109
- // update the list with out latest change (it is always living in the present)
110
- _annotationHistory.push(_annotations)
115
+ // user did some changes from within the past
116
+ // time to create an alternative timeline and delete the original one
117
+ if (annotationHistoryIndexRef.current !== undefined) {
118
+ // remove everything after the state the user is
119
+ _annotationHistory.splice(annotationHistoryIndexRef.current + 1)
120
+ }
121
+
122
+ // update the list with out latest change (it is always living in the present)
123
+ _annotationHistory.push(_annotations)
124
+ return _annotationHistory
125
+ })
111
126
 
112
127
  // keep history index marker in the present
128
+ annotationHistoryIndexRef.current = undefined
113
129
  setAnnotationHistoryIndex(undefined)
114
-
115
- setAnnotationHistory(_annotationHistory)
116
130
  }
117
131
 
118
132
  // for adjusting the container/canvas size
@@ -137,17 +151,19 @@ const Sia = ({
137
151
  const [usedInternalIds, setUsedInternalIds] = useState<number[]>([])
138
152
 
139
153
  const deleteAnnotationByInternalId = (internalId: number) => {
140
- // get index of selected annotation
141
- const annoListIndex: number = annotations.findIndex(
154
+ const currentAnnotations = annotationsRef.current
155
+ const annoListIndex: number = currentAnnotations.findIndex(
142
156
  (anno) => anno.internalId === internalId,
143
157
  )
144
158
 
145
- // dereference list to force state update
146
- const _annotations: Annotation[] = [...annotations]
159
+ if (annoListIndex === -1) return
160
+
161
+ const _annotations: Annotation[] = [...currentAnnotations]
147
162
 
148
163
  // remove annotation
149
164
  const removedAnno: Annotation = _annotations.splice(annoListIndex, 1)[0]
150
165
 
166
+ annotationsRef.current = _annotations
151
167
  setAnnotations(_annotations)
152
168
  setSelectedAnnotation(undefined)
153
169
  updateAnnotationHistory(_annotations)
@@ -206,7 +222,7 @@ const Sia = ({
206
222
  }
207
223
 
208
224
  const handleAnnoEditing = (annotation: Annotation) => {
209
- const _annotations: Annotation[] = [...annotations]
225
+ const _annotations: Annotation[] = [...annotationsRef.current]
210
226
 
211
227
  // annotation is being edited - remove it from current annotations for this time
212
228
  const selectedAnnotationIndex: number = _annotations.findIndex(
@@ -498,34 +514,29 @@ const Sia = ({
498
514
  possibleLabels={possibleLabels}
499
515
  uiConfig={uiConfig}
500
516
  onAnnoCreated={(annotation: Annotation) => {
501
- setAnnotations((prev) => {
502
- const _annotations = [...prev, annotation]
503
- onAnnoCreated(annotation, _annotations)
504
- return _annotations
505
- })
517
+ const _annotations = [...annotationsRef.current, annotation]
518
+ annotationsRef.current = _annotations
519
+ setAnnotations(_annotations)
520
+ onAnnoCreated(annotation, _annotations)
506
521
  setSelectedAnnotation(annotation)
507
522
  // dont update history here - we dont have a finished anno at this point
508
523
  }}
509
524
  onAnnoChanged={(changedAnno: Annotation) => {
510
- setAnnotations((prev) => {
511
- // update annotation list
512
- const annoListIndex: number = prev.findIndex(
513
- (anno) => anno.internalId === changedAnno.internalId,
514
- )
515
-
516
- // only fire event if item found
517
- if (annoListIndex === -1) return prev
525
+ const annoListIndex: number = annotationsRef.current.findIndex(
526
+ (anno) => anno.internalId === changedAnno.internalId,
527
+ )
518
528
 
519
- const _annotations: Annotation[] = [...prev]
529
+ if (annoListIndex !== -1) {
530
+ const _annotations: Annotation[] = [...annotationsRef.current]
520
531
  _annotations[annoListIndex] = changedAnno
532
+ annotationsRef.current = _annotations
533
+ setAnnotations(_annotations)
521
534
 
522
535
  // only update history for full/finished annotations
523
536
  if (changedAnno.status !== AnnotationStatus.CREATING) {
524
537
  updateAnnotationHistory(_annotations)
525
538
  }
526
-
527
- return _annotations
528
- })
539
+ }
529
540
 
530
541
  // inform the outside world about our change
531
542
  // (kept outside the updater — side effects must not run inside setState)
@@ -551,30 +562,35 @@ const Sia = ({
551
562
  ]
552
563
  : []
553
564
 
554
- setAnnotations((prev) => {
555
- // update annotation list
556
- const _annotations: Annotation[] = [...prev]
557
-
558
- // remove the source annotations that were consumed by the polygon operation
559
- for (const annotation of annosToDelete) {
560
- const annoListIndex: number = _annotations.findIndex(
561
- (anno) => anno.internalId === annotation.internalId,
562
- )
563
- if (annoListIndex !== -1) _annotations.splice(annoListIndex, 1)
564
- }
565
+ const _annotations: Annotation[] = [...annotationsRef.current]
565
566
 
566
- if (hasAnnoJustBeenCreated) _annotations.push(finishedAnno)
567
- else {
568
- const annoListIndex: number = prev.findIndex(
569
- (anno) => anno.internalId === finishedAnno.internalId,
570
- )
571
- _annotations[annoListIndex] = finishedAnno
572
- }
567
+ // remove annotations consumed by polygon operation
568
+ for (const annotation of annosToDelete) {
569
+ const annoListIndex: number = _annotations.findIndex(
570
+ (anno) => anno.internalId === annotation.internalId,
571
+ )
572
+ if (annoListIndex !== -1) _annotations.splice(annoListIndex, 1)
573
+ }
573
574
 
574
- updateAnnotationHistory(_annotations)
575
+ if (hasAnnoJustBeenCreated) {
576
+ // replace the CREATING entry with the finished CREATED version
577
+ const existingIdx = _annotations.findIndex(
578
+ (a) => a.internalId === finishedAnno.internalId,
579
+ )
580
+ if (existingIdx !== -1) _annotations.splice(existingIdx, 1)
581
+ _annotations.push(finishedAnno)
582
+ } else {
583
+ const annoListIndex: number = _annotations.findIndex(
584
+ (anno) => anno.internalId === finishedAnno.internalId,
585
+ )
586
+ if (annoListIndex !== -1) _annotations[annoListIndex] = finishedAnno
587
+ }
575
588
 
576
- return _annotations
577
- })
589
+ annotationsRef.current = _annotations
590
+ setAnnotations(_annotations)
591
+ updateAnnotationHistory(_annotations)
592
+ setSelectedAnnotation(finishedAnno)
593
+ onSelectAnnotation(finishedAnno)
578
594
 
579
595
  // Notify the server about the deletions (outside of the updater/setState!!!)
580
596
  for (const annotation of annosToDelete) {