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