@apollo-annotation/jbrowse-plugin-apollo 0.3.4 → 0.3.6
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/dist/index.esm.js +5466 -4490
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +5283 -4318
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +6806 -4088
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +4 -4
- package/src/ApolloInternetAccount/addMenuItems.ts +5 -2
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -0
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +1 -1
- package/src/ApolloInternetAccount/components/LoginIcons.tsx +1 -1
- package/src/ApolloInternetAccount/configSchema.ts +1 -1
- package/src/ApolloInternetAccount/model.ts +11 -10
- package/src/ApolloJobModel.ts +1 -1
- package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +8 -6
- package/src/ApolloRefNameAliasAdapter/index.ts +2 -2
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +17 -4
- package/src/ApolloSequenceAdapter/index.ts +1 -1
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +8 -7
- package/src/ApolloTextSearchAdapter/index.ts +1 -1
- package/src/BackendDrivers/BackendDriver.ts +7 -7
- package/src/BackendDrivers/CollaborationServerDriver.ts +14 -10
- package/src/BackendDrivers/DesktopFileDriver.ts +11 -10
- package/src/BackendDrivers/InMemoryFileDriver.ts +10 -6
- package/src/ChangeManager.ts +5 -5
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +92 -20
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +170 -27
- package/src/FeatureDetailsWidget/AttributeKey.tsx +50 -0
- package/src/FeatureDetailsWidget/AttributeKeySelector.tsx +104 -0
- package/src/FeatureDetailsWidget/Attributes.tsx +213 -320
- package/src/FeatureDetailsWidget/BasicInformation.tsx +8 -9
- package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +104 -0
- package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +22 -0
- package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +10 -8
- package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/Sequence.tsx +18 -35
- package/src/FeatureDetailsWidget/StringTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +140 -95
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +600 -0
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +54 -0
- package/src/FeatureDetailsWidget/model.ts +8 -3
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +19 -12
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +19 -41
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +44 -22
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +6 -5
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -7
- package/src/LinearApolloDisplay/stateModel/base.ts +52 -10
- package/src/LinearApolloDisplay/stateModel/index.ts +4 -3
- package/src/LinearApolloDisplay/stateModel/layouts.ts +8 -34
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +13 -12
- package/src/LinearApolloDisplay/stateModel/rendering.ts +63 -31
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +221 -0
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +40 -0
- package/src/LinearApolloSixFrameDisplay/components/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/configSchema.ts +7 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +821 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +63 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/index.ts +1 -0
- package/src/LinearApolloSixFrameDisplay/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +261 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/index.ts +27 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +236 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +349 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +199 -0
- package/src/LinearApolloSixFrameDisplay/types.ts +1 -0
- package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +10 -1
- package/src/OntologyManager/OntologyStore/fulltext.test.ts +1 -1
- package/src/OntologyManager/OntologyStore/fulltext.ts +8 -3
- package/src/OntologyManager/OntologyStore/index.test.ts +4 -1
- package/src/OntologyManager/OntologyStore/index.ts +19 -14
- package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +6 -5
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +11 -5
- package/src/OntologyManager/index.ts +10 -6
- package/src/OntologyManager/util.ts +3 -2
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
- package/src/TabularEditor/HybridGrid/Feature.tsx +9 -8
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +1 -1
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +3 -2
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +8 -1
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +15 -13
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +9 -33
- package/src/TabularEditor/TabularEditorPane.tsx +1 -1
- package/src/TabularEditor/model.ts +2 -2
- package/src/TabularEditor/types.ts +5 -2
- package/src/components/AddAssembly.tsx +611 -291
- package/src/components/AddChildFeature.tsx +6 -5
- package/src/components/AddFeature.tsx +211 -38
- package/src/components/AddRefSeqAliases.tsx +14 -12
- package/src/components/CopyFeature.tsx +8 -7
- package/src/components/CreateApolloAnnotation.tsx +154 -46
- package/src/components/DeleteAssembly.tsx +9 -8
- package/src/components/DeleteFeature.tsx +5 -4
- package/src/components/Dialog.tsx +1 -1
- package/src/components/DownloadGFF3.tsx +11 -10
- package/src/components/FilterFeatures.tsx +6 -4
- package/src/components/ImportFeatures.tsx +7 -6
- package/src/components/LogOut.tsx +5 -4
- package/src/components/ManageChecks.tsx +9 -8
- package/src/components/ManageUsers.tsx +11 -10
- package/src/components/OntologyTermAutocomplete.tsx +5 -5
- package/src/components/OntologyTermMultiSelect.tsx +9 -6
- package/src/components/OpenLocalFile.tsx +4 -3
- package/src/components/ViewChangeLog.tsx +7 -6
- package/src/components/ViewCheckResults.tsx +8 -7
- package/src/components/index.ts +0 -1
- package/src/extensions/annotationFromJBrowseFeature.test.ts +1 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +14 -12
- package/src/extensions/annotationFromPileup.ts +6 -6
- package/src/index.ts +33 -50
- package/src/makeDisplayComponent.tsx +93 -41
- package/src/session/ClientDataStore.ts +21 -17
- package/src/session/session.ts +20 -26
- package/src/types.ts +4 -4
- package/src/util/annotationFeatureUtils.ts +53 -0
- package/src/util/index.ts +4 -3
- package/src/util/loadAssemblyIntoClient.ts +10 -3
- package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +0 -13
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +0 -707
- package/src/ApolloSixFrameRenderer/configSchema.ts +0 -7
- package/src/ApolloSixFrameRenderer/index.ts +0 -3
- package/src/FeatureDetailsWidget/TranscriptBasic.tsx +0 -200
- package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +0 -19
- package/src/SixFrameFeatureDisplay/components/index.ts +0 -1
- package/src/SixFrameFeatureDisplay/configSchema.ts +0 -21
- package/src/SixFrameFeatureDisplay/index.ts +0 -2
- package/src/SixFrameFeatureDisplay/stateModel.ts +0 -439
- package/src/components/ModifyFeatureAttribute.tsx +0 -460
|
@@ -1,90 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
3
|
-
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
4
|
-
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
1
|
+
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
5
2
|
import { FeatureAttributeChange } from '@apollo-annotation/shared'
|
|
6
|
-
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
3
|
+
import { type AbstractSessionModel, getEnv } from '@jbrowse/core/util'
|
|
7
4
|
import DeleteIcon from '@mui/icons-material/Delete'
|
|
5
|
+
import EditIcon from '@mui/icons-material/Edit'
|
|
6
|
+
import MoreHorizIcon from '@mui/icons-material/MoreHoriz'
|
|
8
7
|
import {
|
|
9
8
|
Button,
|
|
10
|
-
DialogActions,
|
|
11
|
-
FormControl,
|
|
12
|
-
FormControlLabel,
|
|
13
|
-
FormLabel,
|
|
14
|
-
Grid2,
|
|
15
9
|
IconButton,
|
|
10
|
+
List,
|
|
11
|
+
ListItem,
|
|
12
|
+
ListItemIcon,
|
|
13
|
+
ListItemText,
|
|
14
|
+
Menu,
|
|
15
|
+
MenuItem,
|
|
16
16
|
Paper,
|
|
17
|
-
Radio,
|
|
18
|
-
RadioGroup,
|
|
19
|
-
TextField,
|
|
20
17
|
Typography,
|
|
21
18
|
} from '@mui/material'
|
|
19
|
+
import { entries } from 'mobx'
|
|
22
20
|
import { observer } from 'mobx-react'
|
|
23
21
|
import { getSnapshot } from 'mobx-state-tree'
|
|
24
22
|
import React, { useState } from 'react'
|
|
25
23
|
import { makeStyles } from 'tss-react/mui'
|
|
26
24
|
|
|
27
|
-
import {
|
|
28
|
-
import { OntologyTermMultiSelect } from '../components/OntologyTermMultiSelect'
|
|
29
|
-
import { ApolloSessionModel } from '../session'
|
|
30
|
-
import { StringTextField } from './StringTextField'
|
|
25
|
+
import { type ApolloSessionModel } from '../session'
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<OntologyTermMultiSelect {...props} ontologyName="Sequence Ontology" />
|
|
44
|
-
)
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
])
|
|
48
|
-
|
|
49
|
-
const reservedTerms = [
|
|
50
|
-
'ID',
|
|
51
|
-
'Name',
|
|
52
|
-
'Alias',
|
|
53
|
-
'Target',
|
|
54
|
-
'Gap',
|
|
55
|
-
'Derives_from',
|
|
56
|
-
'Note',
|
|
57
|
-
'Dbxref',
|
|
58
|
-
'Ontology',
|
|
59
|
-
'Is_Circular',
|
|
60
|
-
]
|
|
27
|
+
import { AttributeKey } from './AttributeKey'
|
|
28
|
+
import { AttributeKeySelector } from './AttributeKeySelector'
|
|
29
|
+
import {
|
|
30
|
+
type AttributeEditorProps,
|
|
31
|
+
DefaultAttributeEditor,
|
|
32
|
+
} from './DefaultAttributeEditor'
|
|
33
|
+
import {
|
|
34
|
+
type AttributeViewerProps,
|
|
35
|
+
DefaultAttributeViewer,
|
|
36
|
+
} from './DefaultAttributeViewer'
|
|
61
37
|
|
|
62
38
|
const useStyles = makeStyles()((theme) => ({
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
39
|
+
list: {
|
|
40
|
+
'li:nth-of-type(odd)': {
|
|
41
|
+
backgroundColor: theme.palette.action.focus,
|
|
42
|
+
},
|
|
43
|
+
'li:nth-of-type(even)': {
|
|
44
|
+
backgroundColor: theme.palette.action.hover,
|
|
45
|
+
},
|
|
70
46
|
},
|
|
71
47
|
}))
|
|
72
48
|
|
|
73
|
-
function CustomAttributeValueEditor(props: AttributeValueEditorProps) {
|
|
74
|
-
const { onChange, value } = props
|
|
75
|
-
return (
|
|
76
|
-
<StringTextField
|
|
77
|
-
value={value}
|
|
78
|
-
onChangeCommitted={(newValue) => {
|
|
79
|
-
onChange(newValue.split(','))
|
|
80
|
-
}}
|
|
81
|
-
variant="outlined"
|
|
82
|
-
fullWidth
|
|
83
|
-
helperText="Separate multiple values for the attribute with commas"
|
|
84
|
-
/>
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
49
|
export const Attributes = observer(function Attributes({
|
|
89
50
|
assembly,
|
|
90
51
|
editable,
|
|
@@ -96,285 +57,217 @@ export const Attributes = observer(function Attributes({
|
|
|
96
57
|
assembly: string
|
|
97
58
|
editable: boolean
|
|
98
59
|
}) {
|
|
99
|
-
const
|
|
100
|
-
const [showAddNewForm, setShowAddNewForm] = useState(false)
|
|
60
|
+
const { pluginManager } = getEnv(session)
|
|
101
61
|
const { classes } = useStyles()
|
|
102
|
-
const [
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const capitalizedKey = newKey.charAt(0).toUpperCase() + newKey.slice(1)
|
|
108
|
-
return [capitalizedKey, getSnapshot(value)]
|
|
109
|
-
}
|
|
110
|
-
if (key === '_id') {
|
|
111
|
-
return ['ID', getSnapshot(value)]
|
|
112
|
-
}
|
|
113
|
-
return [key, getSnapshot(value)]
|
|
114
|
-
}),
|
|
115
|
-
)
|
|
116
|
-
const { notify } = session as unknown as AbstractSessionModel
|
|
62
|
+
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
63
|
+
const [selectedKey, setSelectedKey] = useState<null | string>(null)
|
|
64
|
+
const [editingKey, setEditingKey] = useState<null | string>(null)
|
|
65
|
+
const [showAddNewForm, setShowAddNewForm] = useState(false)
|
|
66
|
+
const [newKey, setNewKey] = useState<string | undefined>()
|
|
117
67
|
|
|
118
|
-
const
|
|
68
|
+
const open = Boolean(anchorEl)
|
|
119
69
|
|
|
120
|
-
|
|
121
|
-
|
|
70
|
+
const { changeManager } = session.apolloDataStore
|
|
71
|
+
const { notify } = session as unknown as AbstractSessionModel
|
|
122
72
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
attrs._id = val
|
|
144
|
-
break
|
|
145
|
-
}
|
|
146
|
-
case 'Name': {
|
|
147
|
-
attrs.gff_name = val
|
|
148
|
-
break
|
|
149
|
-
}
|
|
150
|
-
case 'Alias': {
|
|
151
|
-
attrs.gff_alias = val
|
|
152
|
-
break
|
|
153
|
-
}
|
|
154
|
-
case 'Target': {
|
|
155
|
-
attrs.gff_target = val
|
|
156
|
-
break
|
|
157
|
-
}
|
|
158
|
-
case 'Gap': {
|
|
159
|
-
attrs.gff_gap = val
|
|
160
|
-
break
|
|
161
|
-
}
|
|
162
|
-
case 'Derives_from': {
|
|
163
|
-
attrs.gff_derives_from = val
|
|
164
|
-
break
|
|
165
|
-
}
|
|
166
|
-
case 'Note': {
|
|
167
|
-
attrs.gff_note = val
|
|
168
|
-
break
|
|
169
|
-
}
|
|
170
|
-
case 'Dbxref': {
|
|
171
|
-
attrs.gff_dbxref = val
|
|
172
|
-
break
|
|
173
|
-
}
|
|
174
|
-
case 'Ontology_term': {
|
|
175
|
-
attrs.gff_ontology_term = val
|
|
176
|
-
break
|
|
177
|
-
}
|
|
178
|
-
case 'Is_circular': {
|
|
179
|
-
attrs.gff_is_circular = val
|
|
180
|
-
break
|
|
181
|
-
}
|
|
182
|
-
default: {
|
|
183
|
-
attrs[key.toLowerCase()] = val
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
73
|
+
function handleListMenuClick(
|
|
74
|
+
event: React.MouseEvent<HTMLButtonElement>,
|
|
75
|
+
key: string,
|
|
76
|
+
) {
|
|
77
|
+
setAnchorEl(event.currentTarget)
|
|
78
|
+
setSelectedKey(key)
|
|
79
|
+
}
|
|
80
|
+
function handleClose() {
|
|
81
|
+
setAnchorEl(null)
|
|
82
|
+
setSelectedKey(null)
|
|
83
|
+
}
|
|
84
|
+
function handleDelete() {
|
|
85
|
+
if (selectedKey) {
|
|
86
|
+
deleteFeatureAttribute(selectedKey)
|
|
87
|
+
}
|
|
88
|
+
handleClose()
|
|
89
|
+
}
|
|
90
|
+
function handleEdit() {
|
|
91
|
+
if (selectedKey) {
|
|
92
|
+
setEditingKey(selectedKey)
|
|
187
93
|
}
|
|
94
|
+
handleClose()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const { _id, attributes } = feature
|
|
188
98
|
|
|
99
|
+
function deleteFeatureAttribute(key: string) {
|
|
100
|
+
const attributesSerialized = getSnapshot(attributes)
|
|
101
|
+
const { [key]: deletedAttribute, ...remainingAttributes } =
|
|
102
|
+
attributesSerialized
|
|
189
103
|
const change = new FeatureAttributeChange({
|
|
190
|
-
changedIds: [
|
|
104
|
+
changedIds: [_id],
|
|
191
105
|
typeName: 'FeatureAttributeChange',
|
|
192
106
|
assembly,
|
|
193
|
-
featureId:
|
|
194
|
-
attributes:
|
|
107
|
+
featureId: _id,
|
|
108
|
+
attributes: remainingAttributes,
|
|
195
109
|
})
|
|
196
|
-
|
|
197
|
-
notify('Feature attributes modified successfully', 'success')
|
|
110
|
+
void changeManager.submit(change)
|
|
198
111
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
if (newAttributeKey === 'Parent') {
|
|
206
|
-
setErrorMessage(
|
|
207
|
-
'"Parent" -key is handled internally and it cannot be modified manually',
|
|
208
|
-
)
|
|
209
|
-
return
|
|
210
|
-
}
|
|
211
|
-
if (newAttributeKey in attributes) {
|
|
212
|
-
setErrorMessage(`Attribute "${newAttributeKey}" already exists`)
|
|
112
|
+
|
|
113
|
+
function modifyFeatureAttribute(key: string, attribute: string[]) {
|
|
114
|
+
const serializedAttributes = { ...getSnapshot(attributes) }
|
|
115
|
+
if (!(key in serializedAttributes)) {
|
|
116
|
+
notify(`"${key}" not found in feature attributes`, 'error')
|
|
213
117
|
return
|
|
214
118
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
!reservedTerms.includes(newAttributeKey) &&
|
|
218
|
-
![...reservedKeys.keys()].includes(newAttributeKey)
|
|
219
|
-
) {
|
|
220
|
-
setErrorMessage(
|
|
221
|
-
`Key cannot starts with uppercase letter unless key is one of these: ${reservedTerms.join(
|
|
222
|
-
', ',
|
|
223
|
-
)}`,
|
|
224
|
-
)
|
|
119
|
+
const oldAttribute = serializedAttributes[key]
|
|
120
|
+
if (oldAttribute.toString() === attribute.toString()) {
|
|
225
121
|
return
|
|
226
122
|
}
|
|
227
|
-
|
|
123
|
+
serializedAttributes[key] = attribute
|
|
124
|
+
|
|
125
|
+
const change = new FeatureAttributeChange({
|
|
126
|
+
changedIds: [feature._id],
|
|
127
|
+
typeName: 'FeatureAttributeChange',
|
|
128
|
+
assembly,
|
|
129
|
+
featureId: feature._id,
|
|
130
|
+
attributes: serializedAttributes,
|
|
131
|
+
})
|
|
132
|
+
void changeManager.submit(change)
|
|
228
133
|
}
|
|
229
134
|
|
|
230
|
-
function
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
setNewAttributeKey('')
|
|
236
|
-
} else if (reservedKeys.has(value)) {
|
|
237
|
-
setNewAttributeKey(value)
|
|
238
|
-
} else {
|
|
239
|
-
setErrorMessage('Unknown attribute type')
|
|
135
|
+
function addFeatureAttribute(key: string, attribute: string[]) {
|
|
136
|
+
const serializedAttributes = { ...getSnapshot(attributes) }
|
|
137
|
+
if (key in serializedAttributes) {
|
|
138
|
+
notify(`Feature already has attribute "${key}"`, 'error')
|
|
139
|
+
return
|
|
240
140
|
}
|
|
141
|
+
serializedAttributes[key] = attribute
|
|
142
|
+
|
|
143
|
+
const change = new FeatureAttributeChange({
|
|
144
|
+
changedIds: [feature._id],
|
|
145
|
+
typeName: 'FeatureAttributeChange',
|
|
146
|
+
assembly,
|
|
147
|
+
featureId: feature._id,
|
|
148
|
+
attributes: serializedAttributes,
|
|
149
|
+
})
|
|
150
|
+
void changeManager.submit(change)
|
|
241
151
|
}
|
|
242
152
|
|
|
153
|
+
const NewKeyAttributeEditor = pluginManager.evaluateExtensionPoint(
|
|
154
|
+
'Apollo-AttributeEditorComponent',
|
|
155
|
+
DefaultAttributeEditor,
|
|
156
|
+
{ key: newKey },
|
|
157
|
+
) as React.ElementType<AttributeEditorProps>
|
|
158
|
+
|
|
243
159
|
return (
|
|
244
160
|
<>
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
161
|
+
<List className={classes.list}>
|
|
162
|
+
{entries(attributes).map(([key, values]) => {
|
|
163
|
+
const AttributeEditor = pluginManager.evaluateExtensionPoint(
|
|
164
|
+
'Apollo-AttributeEditorComponent',
|
|
165
|
+
DefaultAttributeEditor,
|
|
166
|
+
{ key },
|
|
167
|
+
) as React.ElementType<AttributeEditorProps>
|
|
168
|
+
const AttributeViewer = pluginManager.evaluateExtensionPoint(
|
|
169
|
+
'Apollo-AttributeViewerComponent',
|
|
170
|
+
DefaultAttributeViewer,
|
|
171
|
+
{ key },
|
|
172
|
+
) as React.ElementType<AttributeViewerProps>
|
|
253
173
|
return (
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
<Button
|
|
282
|
-
color="primary"
|
|
283
|
-
variant="contained"
|
|
284
|
-
disabled={showAddNewForm || !editable}
|
|
285
|
-
onClick={() => {
|
|
286
|
-
setShowAddNewForm(true)
|
|
287
|
-
}}
|
|
288
|
-
>
|
|
289
|
-
Add new
|
|
290
|
-
</Button>
|
|
291
|
-
</Grid2>
|
|
292
|
-
{showAddNewForm ? (
|
|
293
|
-
<Grid2>
|
|
294
|
-
<Paper elevation={8} className={classes.newAttributePaper}>
|
|
295
|
-
<Grid2 container direction="column">
|
|
296
|
-
<Grid2>
|
|
297
|
-
<FormControl>
|
|
298
|
-
<FormLabel id="attribute-radio-button-group">
|
|
299
|
-
Select attribute type
|
|
300
|
-
</FormLabel>
|
|
301
|
-
<RadioGroup
|
|
302
|
-
aria-labelledby="demo-radio-buttons-group-label"
|
|
303
|
-
defaultValue="custom"
|
|
304
|
-
name="radio-buttons-group"
|
|
305
|
-
onChange={handleRadioButtonChange}
|
|
306
|
-
>
|
|
307
|
-
<FormControlLabel
|
|
308
|
-
value="custom"
|
|
309
|
-
control={<Radio />}
|
|
310
|
-
disableTypography
|
|
311
|
-
label={
|
|
312
|
-
<Grid2 container spacing={1} alignItems="center">
|
|
313
|
-
<Grid2>
|
|
314
|
-
<Typography>Custom</Typography>
|
|
315
|
-
</Grid2>
|
|
316
|
-
<Grid2>
|
|
317
|
-
<TextField
|
|
318
|
-
label="Custom attribute key"
|
|
319
|
-
variant="outlined"
|
|
320
|
-
value={
|
|
321
|
-
reservedKeys.has(newAttributeKey)
|
|
322
|
-
? ''
|
|
323
|
-
: newAttributeKey
|
|
324
|
-
}
|
|
325
|
-
disabled={reservedKeys.has(newAttributeKey)}
|
|
326
|
-
onChange={(event) => {
|
|
327
|
-
setNewAttributeKey(event.target.value)
|
|
328
|
-
}}
|
|
329
|
-
/>
|
|
330
|
-
</Grid2>
|
|
331
|
-
</Grid2>
|
|
174
|
+
<ListItem
|
|
175
|
+
key={key}
|
|
176
|
+
secondaryAction={
|
|
177
|
+
editable && !editingKey ? (
|
|
178
|
+
<IconButton
|
|
179
|
+
edge="end"
|
|
180
|
+
onClick={(event) => {
|
|
181
|
+
handleListMenuClick(event, key)
|
|
182
|
+
}}
|
|
183
|
+
>
|
|
184
|
+
<MoreHorizIcon />
|
|
185
|
+
</IconButton>
|
|
186
|
+
) : null
|
|
187
|
+
}
|
|
188
|
+
>
|
|
189
|
+
<ListItemText
|
|
190
|
+
disableTypography
|
|
191
|
+
primary={<AttributeKey attributeKey={key} />}
|
|
192
|
+
secondary={
|
|
193
|
+
editingKey === key ? (
|
|
194
|
+
<AttributeEditor
|
|
195
|
+
session={session}
|
|
196
|
+
attributeValues={values as string[] | undefined}
|
|
197
|
+
setAttribute={(newValues) => {
|
|
198
|
+
setEditingKey(null)
|
|
199
|
+
if (newValues) {
|
|
200
|
+
modifyFeatureAttribute(key, newValues)
|
|
332
201
|
}
|
|
333
|
-
/>
|
|
334
|
-
{[...reservedKeys.keys()].map((key) => (
|
|
335
|
-
<FormControlLabel
|
|
336
|
-
key={key}
|
|
337
|
-
value={key}
|
|
338
|
-
control={<Radio />}
|
|
339
|
-
label={key}
|
|
340
|
-
/>
|
|
341
|
-
))}
|
|
342
|
-
</RadioGroup>
|
|
343
|
-
</FormControl>
|
|
344
|
-
</Grid2>
|
|
345
|
-
<Grid2>
|
|
346
|
-
<DialogActions>
|
|
347
|
-
<Button
|
|
348
|
-
key="addButton"
|
|
349
|
-
color="primary"
|
|
350
|
-
variant="contained"
|
|
351
|
-
onClick={handleAddNewAttributeChange}
|
|
352
|
-
disabled={!newAttributeKey}
|
|
353
|
-
>
|
|
354
|
-
Add
|
|
355
|
-
</Button>
|
|
356
|
-
<Button
|
|
357
|
-
key="cancelAddButton"
|
|
358
|
-
variant="outlined"
|
|
359
|
-
type="submit"
|
|
360
|
-
onClick={() => {
|
|
361
|
-
setShowAddNewForm(false)
|
|
362
|
-
setNewAttributeKey('')
|
|
363
|
-
setErrorMessage('')
|
|
364
202
|
}}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
</
|
|
372
|
-
|
|
203
|
+
/>
|
|
204
|
+
) : (
|
|
205
|
+
<AttributeViewer values={values as string[] | undefined} />
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
/>
|
|
209
|
+
</ListItem>
|
|
210
|
+
)
|
|
211
|
+
})}
|
|
212
|
+
{newKey ? (
|
|
213
|
+
<ListItem>
|
|
214
|
+
<ListItemText
|
|
215
|
+
disableTypography
|
|
216
|
+
primary={<AttributeKey attributeKey={newKey} />}
|
|
217
|
+
secondary={
|
|
218
|
+
<NewKeyAttributeEditor
|
|
219
|
+
session={session}
|
|
220
|
+
attributeValues={[]}
|
|
221
|
+
setAttribute={(newValues) => {
|
|
222
|
+
if (newValues) {
|
|
223
|
+
addFeatureAttribute(newKey, newValues)
|
|
224
|
+
}
|
|
225
|
+
setNewKey(undefined)
|
|
226
|
+
}}
|
|
227
|
+
isNew
|
|
228
|
+
/>
|
|
229
|
+
}
|
|
230
|
+
/>
|
|
231
|
+
</ListItem>
|
|
373
232
|
) : null}
|
|
374
|
-
</
|
|
375
|
-
{
|
|
376
|
-
<
|
|
233
|
+
</List>
|
|
234
|
+
{editable ? (
|
|
235
|
+
<Button
|
|
236
|
+
color="primary"
|
|
237
|
+
variant="contained"
|
|
238
|
+
disabled={showAddNewForm || Boolean(newKey)}
|
|
239
|
+
onClick={() => {
|
|
240
|
+
setShowAddNewForm(true)
|
|
241
|
+
}}
|
|
242
|
+
>
|
|
243
|
+
Add new
|
|
244
|
+
</Button>
|
|
245
|
+
) : null}
|
|
246
|
+
{showAddNewForm ? (
|
|
247
|
+
<Paper variant="outlined" style={{ marginTop: 8 }}>
|
|
248
|
+
<AttributeKeySelector
|
|
249
|
+
session={session}
|
|
250
|
+
setKey={(newKey) => {
|
|
251
|
+
setNewKey(newKey)
|
|
252
|
+
setShowAddNewForm(false)
|
|
253
|
+
}}
|
|
254
|
+
/>
|
|
255
|
+
</Paper>
|
|
377
256
|
) : null}
|
|
257
|
+
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
|
258
|
+
<MenuItem onClick={handleDelete}>
|
|
259
|
+
<ListItemIcon>
|
|
260
|
+
<DeleteIcon fontSize="small" />
|
|
261
|
+
</ListItemIcon>
|
|
262
|
+
<Typography variant="inherit">Delete</Typography>
|
|
263
|
+
</MenuItem>
|
|
264
|
+
<MenuItem onClick={handleEdit}>
|
|
265
|
+
<ListItemIcon>
|
|
266
|
+
<EditIcon fontSize="small" />
|
|
267
|
+
</ListItemIcon>
|
|
268
|
+
<Typography variant="inherit">Edit</Typography>
|
|
269
|
+
</MenuItem>
|
|
270
|
+
</Menu>
|
|
378
271
|
</>
|
|
379
272
|
)
|
|
380
273
|
})
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/use-unknown-in-catch-callback-variable */
|
|
2
1
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
3
|
-
import { AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
|
+
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
4
3
|
import {
|
|
5
4
|
LocationEndChange,
|
|
6
5
|
LocationStartChange,
|
|
7
6
|
StrandChange,
|
|
8
7
|
TypeChange,
|
|
9
8
|
} from '@apollo-annotation/shared'
|
|
10
|
-
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
9
|
+
import { type AbstractSessionModel } from '@jbrowse/core/util'
|
|
11
10
|
import { TextField, Typography } from '@mui/material'
|
|
12
11
|
import { observer } from 'mobx-react'
|
|
13
12
|
import React, { useState } from 'react'
|
|
14
13
|
|
|
15
|
-
import { OntologyTermAutocomplete } from '../components/OntologyTermAutocomplete'
|
|
16
14
|
import { isOntologyClass } from '../OntologyManager'
|
|
17
|
-
import OntologyStore from '../OntologyManager/OntologyStore'
|
|
15
|
+
import type OntologyStore from '../OntologyManager/OntologyStore'
|
|
18
16
|
import { fetchValidDescendantTerms } from '../OntologyManager/util'
|
|
19
|
-
import {
|
|
17
|
+
import { OntologyTermAutocomplete } from '../components/OntologyTermAutocomplete'
|
|
18
|
+
import { type ApolloSessionModel } from '../session'
|
|
19
|
+
|
|
20
20
|
import { NumberTextField } from './NumberTextField'
|
|
21
21
|
|
|
22
22
|
export const BasicInformation = observer(function BasicInformation({
|
|
@@ -111,8 +111,7 @@ export const BasicInformation = observer(function BasicInformation({
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
return (
|
|
114
|
-
|
|
115
|
-
<Typography variant="h5">Basic information</Typography>
|
|
114
|
+
<div data-testid="basic_information">
|
|
116
115
|
<NumberTextField
|
|
117
116
|
margin="dense"
|
|
118
117
|
id="start"
|
|
@@ -183,6 +182,6 @@ export const BasicInformation = observer(function BasicInformation({
|
|
|
183
182
|
{errorMessage ? (
|
|
184
183
|
<Typography color="error">{errorMessage}</Typography>
|
|
185
184
|
) : null}
|
|
186
|
-
|
|
185
|
+
</div>
|
|
187
186
|
)
|
|
188
187
|
})
|