lost-sia 1.3.0 → 2.0.0-alpha1

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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/package.json +60 -47
  4. package/src/AnnoExampleViewer.jsx +57 -0
  5. package/src/AnnoLabelInput.jsx +99 -0
  6. package/src/AnnoToolBar.jsx +132 -0
  7. package/src/Annotation/AnnoBar.jsx +148 -0
  8. package/src/Annotation/Annotation.jsx +358 -0
  9. package/src/Annotation/Annotation.scss +47 -0
  10. package/src/Annotation/BBox.jsx +291 -0
  11. package/src/Annotation/Edge.jsx +82 -0
  12. package/src/Annotation/InfSelectionArea.jsx +71 -0
  13. package/src/Annotation/Line.jsx +60 -0
  14. package/src/Annotation/Node.jsx +278 -0
  15. package/src/Annotation/Point.jsx +196 -0
  16. package/src/Annotation/Polygon.jsx +361 -0
  17. package/src/Canvas.jsx +1843 -0
  18. package/src/ImgBar.jsx +123 -0
  19. package/src/InfoBoxes/AnnoDetails.jsx +166 -0
  20. package/src/InfoBoxes/AnnoStats.jsx +104 -0
  21. package/src/InfoBoxes/InfoBox.jsx +78 -0
  22. package/src/InfoBoxes/InfoBoxArea.jsx +155 -0
  23. package/src/InfoBoxes/LabelInfo.jsx +95 -0
  24. package/src/LabelInput.jsx +224 -0
  25. package/src/Prompt.jsx +46 -0
  26. package/src/SIA.scss +10 -0
  27. package/src/SIAFilterButton.jsx +178 -0
  28. package/src/SIASettingButton.jsx +122 -0
  29. package/src/Sia.jsx +491 -0
  30. package/src/SiaPopup.jsx +10 -0
  31. package/src/ToolBar.jsx +399 -0
  32. package/src/Toolbar.css +13 -0
  33. package/src/ToolbarItem.jsx +25 -0
  34. package/src/filterTools.js +3 -0
  35. package/src/index.js +16 -0
  36. package/src/siaDummyData.js +265 -0
  37. package/src/style.scss +3 -0
  38. package/src/test.js +7 -0
  39. package/src/types/annoStatus.js +4 -0
  40. package/src/types/canvasActions.js +57 -0
  41. package/src/types/cursorstyles.js +3 -0
  42. package/src/types/modes.js +8 -0
  43. package/src/types/notificationType.js +4 -0
  44. package/src/types/toolbarEvents.js +33 -0
  45. package/src/types/tools.js +11 -0
  46. package/src/utils/annoConversion.js +115 -0
  47. package/src/utils/colorlut.js +67 -0
  48. package/src/utils/constraints.js +75 -0
  49. package/src/utils/hist.js +67 -0
  50. package/src/utils/keyActions.js +107 -0
  51. package/src/utils/mouse.js +14 -0
  52. package/src/utils/siaIcons.jsx +95 -0
  53. package/src/utils/transform.js +318 -0
  54. package/src/utils/uiConfig.js +57 -0
  55. package/src/utils/windowViewport.js +35 -0
  56. package/CHANGELOG.md +0 -169
  57. package/dist/index.css +0 -24823
  58. package/dist/index.es.js +0 -10482
  59. package/dist/index.es.js.map +0 -1
  60. package/dist/index.js +0 -10497
  61. package/dist/index.js.map +0 -1
@@ -0,0 +1,95 @@
1
+ import React, {useEffect, useState} from 'react'
2
+ import { Divider, Image, Header } from 'semantic-ui-react'
3
+ import InfoBox from './InfoBox'
4
+ import SiaPopup from '../SiaPopup'
5
+ import AnnoExampleViewer from '../AnnoExampleViewer'
6
+ const LabelInfo = (props) => {
7
+
8
+ const [showExampleViewer, setShowExampleViewer] = useState(false)
9
+ const [myLbl, setMyLbl] = useState(undefined)
10
+ // const { data: exampleImg, mutate: getAnnoExample } = exampleApi.useGetAnnoExampleImg({})
11
+ useEffect(() => {
12
+ if (props.selectedAnno){
13
+ const selectedLabelIds = props.selectedAnno.labelIds
14
+ if (selectedLabelIds) {
15
+ const lbl = props.possibleLabels.find( e => {
16
+ return selectedLabelIds[0] === e.id
17
+ })
18
+ if (lbl){
19
+ if (lbl !== myLbl){
20
+ setMyLbl(lbl)
21
+ if (props.visible)
22
+ // getAnnoExample({llId:lbl.id, type:'annoBased', drawAnno: true, addContext:0.05})
23
+ requestImg(lbl, props.selectedAnno)
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }, [props.selectedAnno])
29
+ const onDismiss = () => {
30
+ if (props.onDismiss){
31
+ props.onDismiss()
32
+ }
33
+ }
34
+
35
+ const requestImg = (lbl, anno) => {
36
+ if (props.onGetAnnoExample){
37
+ props.onGetAnnoExample({lbl:lbl, anno:anno})
38
+ }
39
+ }
40
+
41
+ const handleImgClick = () => {
42
+ console.log('clicked img')
43
+ // setShowExampleViewer(true)
44
+ // requestImg(myLbl, props.selectedAnno)
45
+
46
+ setShowExampleViewer(true)
47
+ // getAnnoExample({llId:myLbl.id, type:'annoBased', drawAnno: true, addContext:0.05})
48
+ }
49
+
50
+ const renderExampleImg = () => {
51
+ if (!props.exampleImg) return null
52
+ return <div>
53
+ <Divider onClick={() => handleImgClick()} horizontal> Example </Divider>
54
+ <SiaPopup trigger={<Image src={props.exampleImg.img} rounded centered size='medium'
55
+ onClick={() => handleImgClick()}
56
+ />}
57
+ content={'Click on image to view more examples'} />
58
+ {/* <Image src='https://www.gstatic.com/webp/gallery3/1.png'/> */}
59
+ </div>
60
+ }
61
+
62
+ const renderDescription = () => {
63
+ // if (props.selectedAnno){
64
+ // if (myLbl){
65
+ // const selectedLabelIds = props.selectedAnno.labelIds
66
+ // if (!selectedLabelIds) return 'No Label'
67
+ // const lbl = props.possibleLabels.find( e => {
68
+ // return selectedLabelIds[0] === e.id
69
+ // })
70
+ if (!myLbl) return "No Label"
71
+ return <div>
72
+ <Header>{
73
+ myLbl.label
74
+ }</Header>
75
+ <div dangerouslySetInnerHTML={{__html: myLbl.description}} />
76
+ {renderExampleImg()}
77
+ <AnnoExampleViewer onRequestExample={() => requestImg(myLbl, props.selectedAnno)} onClose={() => {setShowExampleViewer(false)}} active={showExampleViewer} lbl={myLbl} exampleImg={props.exampleImg} />
78
+ </div>
79
+ // } else {
80
+ // return 'No Label'
81
+ // }
82
+ }
83
+
84
+
85
+ return <InfoBox
86
+ header="Label Info"
87
+ content={renderDescription()}
88
+ visible={props.visible}
89
+ defaultPos={props.defaultPos}
90
+ onDismiss={() => onDismiss()}
91
+ />
92
+ }
93
+
94
+
95
+ export default LabelInfo
@@ -0,0 +1,224 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { Dropdown, Ref, Popup, Header } from 'semantic-ui-react';
3
+
4
+ const LabelInput = ({ defaultLabel, disabled, focusOnRender, initLabelIds, multilabels, possibleLabelsProp, relatedId, renderPopup, visible, open, onClose, onLabelConfirmed, onLabelUpdate }) => {
5
+ const [label, setLabel] = useState([]);
6
+ const [possibleLabels, setPossibleLabels] = useState([]);
7
+ const [performInit, setPerformInit] = useState(true);
8
+ const [confirmLabel, setConfirmLabel] = useState(0);
9
+
10
+ const inputRef = useRef(null);
11
+
12
+ useEffect(() => {
13
+ updatePossibleLabels();
14
+ }, []);
15
+
16
+ useEffect(() => {
17
+ if (initLabelIds) {
18
+ setPerformInit(true);
19
+ }
20
+ }, [initLabelIds]);
21
+
22
+ useEffect(() => {
23
+ if (visible) {
24
+ if (focusOnRender) {
25
+ if (inputRef.current) {
26
+ inputRef.current.click();
27
+ }
28
+ }
29
+ }
30
+
31
+ if (confirmLabel !== 0) {
32
+ annoLabelUpdate(label);
33
+ closeLabelInput();
34
+ }
35
+
36
+ if (initLabelIds) {
37
+ if (performInit) {
38
+ setPerformInit(false);
39
+
40
+ if (initLabelIds.length > 0) {
41
+ setLabel(initLabelIds);
42
+ } else {
43
+ setLabel([]);
44
+ }
45
+ }
46
+ }
47
+ }, [visible, focusOnRender, label, confirmLabel, possibleLabelsProp, initLabelIds, relatedId]);
48
+
49
+ const onKeyDown = (e) => {
50
+ e.stopPropagation();
51
+ performKeyAction(e.key);
52
+ }
53
+
54
+ const onChange = (e, item) => {
55
+ let lbl;
56
+
57
+ if (multilabels) {
58
+ lbl = item.value !== -1 ? item.value : [];
59
+ } else {
60
+ lbl = item.value !== -1 ? [item.value] : [];
61
+ }
62
+
63
+ setLabel(lbl);
64
+ annoLabelUpdate(lbl);
65
+ }
66
+
67
+ const onItemClick = (e, item) => {
68
+ incrementConfirmLabel();
69
+ }
70
+
71
+ const updatePossibleLabels = () => {
72
+ let _possibleLabels = [];
73
+ let _defaultLabel;
74
+
75
+ if (defaultLabel) {
76
+ if (Number.isInteger(defaultLabel)) {
77
+ _defaultLabel = undefined;
78
+ } else {
79
+ _defaultLabel = defaultLabel;
80
+ }
81
+ } else {
82
+ _defaultLabel = 'no label';
83
+ }
84
+
85
+ if (possibleLabelsProp.length > 0) {
86
+ _possibleLabels = possibleLabelsProp.map(e => {
87
+ return {
88
+ key: e.id,
89
+ value: e.id,
90
+ text: e.label,
91
+ content: (
92
+ <div onClick={(event) => onItemClick(event, e.id)}>
93
+ {e.label}
94
+ </div>
95
+ )
96
+ };
97
+ });
98
+ }
99
+
100
+ if (_defaultLabel) {
101
+ _possibleLabels.unshift({
102
+ key: -1,
103
+ value: -1,
104
+ text: _defaultLabel,
105
+ content: (
106
+ <div onClick={(event) => onItemClick(event, -1)}>
107
+ {_defaultLabel}
108
+ </div>
109
+ )
110
+ });
111
+ }
112
+
113
+ setPossibleLabels(_possibleLabels);
114
+ }
115
+
116
+ const performKeyAction = (key) => {
117
+ switch (key) {
118
+ case 'Enter':
119
+ if (!multilabels) {
120
+ if (visible) incrementConfirmLabel();
121
+ }
122
+ break;
123
+ case 'Escape':
124
+ closeLabelInput();
125
+ break;
126
+ default:
127
+ break;
128
+ }
129
+ }
130
+
131
+ const annoLabelUpdate = (label) => {
132
+ console.log('LabelInput -> annoLabelUpdate ', label);
133
+
134
+ if (onLabelUpdate) {
135
+ onLabelUpdate(label.filter(val => val !== -1));
136
+ }
137
+ }
138
+
139
+ const incrementConfirmLabel = () => {
140
+ setConfirmLabel(confirmLabel + 1);
141
+ }
142
+
143
+ const closeLabelInput = () => {
144
+ console.log('LabelInput -> closeLabelInput');
145
+
146
+ if (onLabelConfirmed) onLabelConfirmed(label.filter(val => val !== -1));
147
+ if (onClose) onClose();
148
+ }
149
+
150
+ const renderLabelInput = () => {
151
+ let lbl;
152
+
153
+ if (multilabels) lbl = label;
154
+ else {
155
+ lbl = (label.length > 0 ? label[0] : -1)
156
+ }
157
+
158
+ return (
159
+ <Ref innerRef={inputRef}>
160
+ <Dropdown
161
+ multiple={multilabels}
162
+ search
163
+ selection
164
+ closeOnChange
165
+ icon="search"
166
+ options={possibleLabels}
167
+ placeholder='Enter label'
168
+ tabIndex={0}
169
+ onKeyDown={e => onKeyDown(e)}
170
+ value={lbl}
171
+ onChange={(e, item) => onChange(e, item)}
172
+ style={{ opacity: 0.8 }}
173
+ disabled={disabled}
174
+ open={open}
175
+ />
176
+ </Ref>
177
+ );
178
+ }
179
+
180
+ const renderLabelInfo = () => {
181
+ if (!label) return null;
182
+
183
+ let lbl = undefined;
184
+
185
+ if (label.length > 0) {
186
+ lbl = possibleLabels.find(e => label[label.length - 1] === e.id);
187
+ }
188
+
189
+ if (!lbl) return "No label";
190
+
191
+ return (
192
+ <div>
193
+ <Header>{lbl.label}</Header>
194
+ <div dangerouslySetInnerHTML={{ __html: lbl.description }} />
195
+ </div>
196
+ );
197
+ }
198
+
199
+ const renderPopupContent = () => {
200
+ return (
201
+ <div>
202
+ {renderLabelInfo()}
203
+ </div>
204
+ );
205
+ }
206
+
207
+ if (!visible) return null;
208
+
209
+ if (renderPopup) {
210
+ return (
211
+ <Popup
212
+ trigger={renderLabelInput()}
213
+ content={renderPopupContent()}
214
+ open
215
+ position="right center"
216
+ style={{ opacity: 0.9 }}
217
+ />
218
+ );
219
+ } else {
220
+ return renderLabelInput();
221
+ }
222
+ }
223
+
224
+ export default LabelInput;
package/src/Prompt.jsx ADDED
@@ -0,0 +1,46 @@
1
+ import React, { Component } from 'react'
2
+ import { Dimmer, Header } from 'semantic-ui-react'
3
+
4
+ class Prompt extends Component {
5
+
6
+ constructor(props) {
7
+ super(props)
8
+ this.state = {
9
+ active: false
10
+ }
11
+ }
12
+
13
+ handleClick(e) {
14
+ if (this.props.onClick) {
15
+ this.props.onClick(e)
16
+ }
17
+ }
18
+
19
+ componentDidMount() {
20
+ this.setState({ active: this.props.active })
21
+ }
22
+
23
+ componentDidUpdate(prevProps) {
24
+ if (this.props.active !== prevProps.active) {
25
+ this.setState({ active: this.props.active })
26
+ }
27
+ }
28
+
29
+
30
+ render() {
31
+ return (
32
+ <Dimmer page
33
+ active={this.state.active}
34
+ style={{ zIndex: 7000 }}
35
+ onClick={e => this.handleClick(e)}
36
+ >
37
+ <Header as="h3" inverted style={{ background: 'rgba(0,0,0,0)' }}>
38
+ {this.props.header}
39
+ </Header>
40
+ {this.props.content}
41
+ </Dimmer>
42
+ )
43
+ }
44
+ }
45
+
46
+ export default Prompt
package/src/SIA.scss ADDED
@@ -0,0 +1,10 @@
1
+ .sia-fullscreen{
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ z-index: 6000;
6
+ width: 100%;
7
+ height: 100%;
8
+ background-color: #FFFF;
9
+ }
10
+
@@ -0,0 +1,178 @@
1
+ import React, { Component } from 'react'
2
+ import { Popup, Icon, Menu, Divider, Checkbox } from 'semantic-ui-react'
3
+ import * as filterTools from './filterTools'
4
+ import * as tbe from './types/toolbarEvents'
5
+ import { faFilter } from '@fortawesome/free-solid-svg-icons'
6
+ import { CPopover } from '@coreui/react'
7
+ import ToolbarItem from './ToolbarItem'
8
+ class SIAFilterButton extends Component {
9
+
10
+ constructor(props) {
11
+ super(props)
12
+ this.state = {
13
+ clipLimit: 3,
14
+ active: false,
15
+ color: undefined
16
+ }
17
+ }
18
+
19
+ componentDidUpdate(prevProps) {
20
+ if (prevProps.filter.clahe.clipLimit !== this.props.filter.clahe.clipLimit) {
21
+ this.setState({ clipLimit: this.props.filter.clahe.clipLimit })
22
+ }
23
+ if (this.props.filter !== prevProps.filter) {
24
+ if (filterTools.active(this.props.filter)) {
25
+ this.setState({ color: 'red', active: true })
26
+ } else {
27
+ this.setState({ color: 'white', active: false })
28
+ }
29
+ }
30
+ }
31
+
32
+ triggerEvent(e, data) {
33
+ if (this.props.onFilterEvent) {
34
+ this.props.onFilterEvent(e, data)
35
+ }
36
+ }
37
+
38
+ handleClipLimitChange(e) {
39
+ const cl = parseInt(e.target.value)
40
+ this.setState({ clipLimit: cl })
41
+ // this.claheFilter(cl)
42
+ }
43
+
44
+ rotateImg(angle) {
45
+ const active = !(this.props.filter.rotate.active && this.props.filter.rotate.angle === angle)
46
+ const myAngle = active ? angle : 0
47
+ this.triggerEvent(tbe.APPLY_FILTER,
48
+ {
49
+ ...this.props.filter,
50
+ rotate: { angle: myAngle, active: active }
51
+ })
52
+
53
+ // this.props.siaApplyFilter({
54
+ // ...this.props.filter,
55
+ // rotate: {angle:myAngle, active:active}
56
+ // })
57
+ }
58
+
59
+ claheFilter(clipLimit) {
60
+ const filter = {
61
+ 'clahe': {
62
+ 'clipLimit': clipLimit,
63
+ active: !this.props.filter.clahe.active
64
+ }
65
+ }
66
+ this.triggerEvent(tbe.APPLY_FILTER,
67
+ {
68
+ ...this.props.filter,
69
+ clahe: filter.clahe
70
+ })
71
+ // this.props.siaApplyFilter({
72
+ // ...this.props.filter,
73
+ // clahe: filter.clahe
74
+ // })
75
+ }
76
+
77
+ releaseCLAHESlider(e) {
78
+ const filter = {
79
+ 'clahe': {
80
+ 'clipLimit': parseInt(e.target.value),
81
+ active: true
82
+ }
83
+ }
84
+ this.triggerEvent(tbe.APPLY_FILTER,
85
+ {
86
+ ...this.props.filter,
87
+ clahe: filter.clahe
88
+ })
89
+ // this.props.siaApplyFilter({
90
+ // ...this.props.filter,
91
+ // clahe: filter.clahe
92
+ // })
93
+ }
94
+
95
+ renderRotateContent() {
96
+ const filter = this.props.filter
97
+ return <div>
98
+ <Divider horizontal>Rotate</Divider>
99
+ <Checkbox
100
+ checked={filter.rotate.active && filter.rotate.angle === 90}
101
+ label="Rotate 90" toggle
102
+ onClick={() => this.rotateImg(90)}
103
+ />
104
+ <Checkbox
105
+ checked={filter.rotate.active && filter.rotate.angle === -90}
106
+ label="Rotate -90" toggle
107
+ onClick={() => this.rotateImg(-90)}
108
+ />
109
+ <Checkbox
110
+ checked={filter.rotate.active && filter.rotate.angle === 180}
111
+ label="Rotate 180" toggle
112
+ onClick={() => this.rotateImg(180)}
113
+ />
114
+ </div>
115
+
116
+ }
117
+
118
+ renderRotate() {
119
+ if (!this.props.enabled) return null
120
+ if (this.props.enabled === true) {
121
+ return this.renderRotateContent()
122
+ } else {
123
+ if (this.props.enabled.rotate) {
124
+ return this.renderRotateContent()
125
+ }
126
+ }
127
+ }
128
+
129
+ renderClaheContent() {
130
+ const filter = this.props.filter
131
+ return <div>
132
+ <Divider horizontal>Histogram equalization</Divider>
133
+ <Checkbox
134
+ checked={filter.clahe.active}
135
+ label="Histogram equalization" toggle
136
+ onClick={() => this.claheFilter(this.state.clipLimit)}
137
+ />
138
+ <div>Cliplimit: {this.state.clipLimit}</div>
139
+ <input
140
+ type='range'
141
+ min={0}
142
+ max={40}
143
+ value={this.state.clipLimit}
144
+ onChange={e => this.handleClipLimitChange(e)}
145
+ onMouseUp={e => this.releaseCLAHESlider(e)}
146
+ />
147
+ </div>
148
+
149
+ }
150
+
151
+ renderClahe() {
152
+ if (!this.props.enabled) return null
153
+ if (this.props.enabled === true) {
154
+ return this.renderClaheContent()
155
+ } else {
156
+ if (this.props.enabled.clahe) {
157
+ return this.renderClaheContent()
158
+ }
159
+ }
160
+ }
161
+
162
+ render() {
163
+ if (!this.props.imageMeta) return null
164
+ const popupContent = <div >
165
+ {this.renderRotate()}
166
+ {this.renderClahe()}
167
+ </div>
168
+ return (
169
+ <CPopover content={popupContent} placement='right'>
170
+ <span>
171
+ <ToolbarItem faIcon={faFilter} />
172
+ </span>
173
+ </CPopover>
174
+ )
175
+ }
176
+ }
177
+
178
+ export default SIAFilterButton
@@ -0,0 +1,122 @@
1
+ import React, { Component } from 'react'
2
+
3
+ import { Popup, Icon, Menu, Divider, Checkbox } from 'semantic-ui-react'
4
+ import * as tbe from './types/toolbarEvents'
5
+ import { faCog } from '@fortawesome/free-solid-svg-icons'
6
+ import { CPopover } from '@coreui/react'
7
+ import ToolbarItem from './ToolbarItem'
8
+ class SIASettingButton extends Component {
9
+
10
+ constructor(props) {
11
+ super(props)
12
+ this.state = {
13
+
14
+ }
15
+ }
16
+
17
+ triggerEvent(e, data) {
18
+ if (this.props.onSettingEvent) {
19
+ this.props.onSettingEvent(e, data)
20
+ }
21
+ }
22
+ toggleAnnoDetails() {
23
+ this.triggerEvent(tbe.SHOW_ANNO_DETAILS)
24
+ }
25
+
26
+ toggleLabelInfo() {
27
+ this.triggerEvent(tbe.SHOW_LABEL_INFO)
28
+ }
29
+
30
+ toggleAnnoStats() {
31
+ this.triggerEvent(tbe.SHOW_ANNO_STATS)
32
+ }
33
+
34
+ handleStrokeWidthChange(e) {
35
+ this.triggerEvent(tbe.EDIT_STROKE_WIDTH, parseInt(e.target.value))
36
+ }
37
+
38
+ handleNodeRadiusChange(e) {
39
+ this.triggerEvent(tbe.EDIT_NODE_RADIUS, parseInt(e.target.value))
40
+ }
41
+
42
+ renderInfoBoxContent() {
43
+ return <div>
44
+ <Divider horizontal>Info Boxes</Divider>
45
+ <Checkbox
46
+ checked={this.props.uiConfig.annoDetails.visible}
47
+ label="Annotation Details" toggle
48
+ onClick={() => this.toggleAnnoDetails()}
49
+ />
50
+ <Checkbox
51
+ checked={this.props.uiConfig.labelInfo.visible}
52
+ label="Label Info" toggle
53
+ onClick={() => this.toggleLabelInfo()}
54
+ />
55
+ <Checkbox
56
+ checked={this.props.uiConfig.annoStats.visible}
57
+ label="Anno Stats" toggle
58
+ onClick={() => this.toggleAnnoStats()}
59
+ />
60
+ </div>
61
+ }
62
+ renderInfoBoxes() {
63
+ if (!this.props.enabled) return null
64
+ if (this.props.enabled === true) {
65
+ return this.renderInfoBoxContent()
66
+ } else {
67
+ if (this.props.enabled.infoBoxes) {
68
+ return this.renderInfoBoxContent()
69
+ }
70
+ }
71
+ }
72
+
73
+ renderAnnoStyle() {
74
+ if (!this.props.enabled) return null
75
+ if (this.props.enabled === true) {
76
+ return this.renderAnnoStyleContent()
77
+ } else {
78
+ if (this.props.enabled.annoStyle) {
79
+ return this.renderAnnoStyleContent()
80
+ }
81
+ }
82
+ }
83
+ renderAnnoStyleContent() {
84
+ return <div>
85
+ <Divider horizontal>Anno Appearance</Divider>
86
+ <div>Stroke width: {this.props.uiConfig.strokeWidth}</div>
87
+ <input
88
+ type='range'
89
+ min={1}
90
+ max={10}
91
+ value={this.props.uiConfig.strokeWidth}
92
+ onChange={e => this.handleStrokeWidthChange(e)}
93
+ />
94
+ <div>Node radius: {this.props.uiConfig.nodeRadius}</div>
95
+ <input
96
+ type='range'
97
+ min={1}
98
+ max={10}
99
+ value={this.props.uiConfig.nodeRadius}
100
+ onChange={e => this.handleNodeRadiusChange(e)}
101
+ />
102
+ </div>
103
+ }
104
+
105
+ render() {
106
+
107
+ if (!this.props.uiConfig) return null
108
+ const popupContent = <div >
109
+ {this.renderInfoBoxes()}
110
+ {this.renderAnnoStyle()}
111
+ </div>
112
+ return (
113
+ <CPopover content={popupContent} placement='right'>
114
+ <span>
115
+ <ToolbarItem faIcon={faCog} />
116
+ </span>
117
+ </CPopover>
118
+ )
119
+ }
120
+ }
121
+
122
+ export default SIASettingButton