@apollo-annotation/jbrowse-plugin-apollo 0.3.6 → 0.3.8

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 (84) hide show
  1. package/dist/index.esm.js +4603 -2045
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +4611 -2039
  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 +9387 -4016
  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 +15 -15
  12. package/src/ApolloInternetAccount/model.ts +48 -13
  13. package/src/BackendDrivers/CollaborationServerDriver.ts +23 -2
  14. package/src/ChangeManager.ts +42 -18
  15. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
  16. package/src/FeatureDetailsWidget/Attributes.tsx +8 -3
  17. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -81
  18. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +946 -190
  19. package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +4 -0
  20. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +61 -73
  21. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +55 -211
  22. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +562 -108
  23. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +78 -14
  24. package/src/LinearApolloDisplay/glyphs/Glyph.ts +15 -9
  25. package/src/LinearApolloDisplay/stateModel/base.ts +63 -43
  26. package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
  27. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +79 -292
  28. package/src/LinearApolloDisplay/stateModel/rendering.ts +45 -344
  29. package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
  30. package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
  31. package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
  32. package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
  33. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
  34. package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
  35. package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +481 -0
  36. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +102 -40
  37. package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +12 -20
  38. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +382 -243
  39. package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
  40. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +83 -4
  41. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +23 -11
  42. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +118 -123
  43. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +53 -63
  44. package/src/OntologyManager/index.ts +4 -1
  45. package/src/TabularEditor/HybridGrid/Feature.tsx +20 -14
  46. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
  47. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +108 -16
  48. package/src/components/AddAssembly.tsx +1 -1
  49. package/src/components/AddAssemblyAliases.tsx +114 -0
  50. package/src/components/AddChildFeature.tsx +7 -7
  51. package/src/components/AddFeature.tsx +20 -15
  52. package/src/components/AddRefSeqAliases.tsx +9 -9
  53. package/src/components/CopyFeature.tsx +4 -4
  54. package/src/components/CreateApolloAnnotation.tsx +335 -151
  55. package/src/components/DeleteAssembly.tsx +1 -1
  56. package/src/components/DeleteFeature.tsx +358 -11
  57. package/src/components/DownloadGFF3.tsx +20 -1
  58. package/src/components/EditZoomThresholdDialog.tsx +69 -0
  59. package/src/components/FilterFeatures.tsx +7 -7
  60. package/src/components/FilterTranscripts.tsx +86 -0
  61. package/src/components/ImportFeatures.tsx +1 -1
  62. package/src/components/ManageChecks.tsx +1 -1
  63. package/src/components/MergeExons.tsx +193 -0
  64. package/src/components/MergeTranscripts.tsx +182 -0
  65. package/src/components/OntologyTermMultiSelect.tsx +11 -11
  66. package/src/components/OpenLocalFile.tsx +11 -7
  67. package/src/components/SplitExon.tsx +134 -0
  68. package/src/components/ViewCheckResults.tsx +1 -1
  69. package/src/components/index.ts +4 -0
  70. package/src/config.ts +11 -0
  71. package/src/extensions/annotationFromJBrowseFeature.ts +2 -0
  72. package/src/extensions/annotationFromPileup.ts +99 -89
  73. package/src/index.ts +42 -105
  74. package/src/makeDisplayComponent.tsx +0 -1
  75. package/src/menus/index.ts +1 -0
  76. package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +60 -33
  77. package/src/menus/topLevelMenuAdmin.ts +154 -0
  78. package/src/session/session.ts +163 -104
  79. package/src/util/annotationFeatureUtils.ts +59 -0
  80. package/src/util/copyToClipboard.ts +21 -0
  81. package/src/util/displayUtils.ts +149 -0
  82. package/src/util/glyphUtils.ts +201 -0
  83. package/src/util/index.ts +2 -0
  84. package/src/util/mouseEventsUtils.ts +145 -0
@@ -2,15 +2,15 @@
2
2
  import type PluginManager from '@jbrowse/core/PluginManager'
3
3
  import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
4
4
  import { doesIntersect2 } from '@jbrowse/core/util'
5
- import { type Theme } from '@mui/material'
5
+ import { type Theme, createTheme } from '@mui/material'
6
6
  import { autorun } from 'mobx'
7
- import { type Instance, addDisposer } from 'mobx-state-tree'
7
+ import { type Instance, addDisposer, types } from 'mobx-state-tree'
8
8
 
9
9
  import { type ApolloSessionModel } from '../../session'
10
10
 
11
11
  import { layoutsModelFactory } from './layouts'
12
12
 
13
- export function renderingModelIntermediateFactory(
13
+ export function renderingModelFactory(
14
14
  pluginManager: PluginManager,
15
15
  configSchema: AnyConfigurationSchemaType,
16
16
  ) {
@@ -23,23 +23,24 @@ export function renderingModelIntermediateFactory(
23
23
  'LinearApolloSixFrameDisplayRendering',
24
24
  )
25
25
  .props({
26
- sequenceRowHeight: 15,
27
26
  apolloRowHeight: 20,
28
27
  detailsMinHeight: 200,
29
28
  detailsHeight: 200,
30
- lastRowTooltipBufferHeight: 80,
29
+ lastRowTooltipBufferHeight: 120,
31
30
  isShown: true,
31
+ filteredTranscripts: types.array(types.string),
32
32
  })
33
33
  .volatile(() => ({
34
34
  canvas: null as HTMLCanvasElement | null,
35
35
  overlayCanvas: null as HTMLCanvasElement | null,
36
36
  collaboratorCanvas: null as HTMLCanvasElement | null,
37
- theme: undefined as Theme | undefined,
37
+ theme: createTheme(),
38
38
  }))
39
39
  .views((self) => ({
40
40
  get featuresHeight() {
41
+ const featureLabelSpacer = self.showFeatureLabels ? 2 : 1
41
42
  return (
42
- (self.highestRow + 1) * self.apolloRowHeight +
43
+ featureLabelSpacer * ((self.highestRow + 1) * self.apolloRowHeight) +
43
44
  self.lastRowTooltipBufferHeight
44
45
  )
45
46
  },
@@ -68,6 +69,8 @@ export function renderingModelIntermediateFactory(
68
69
  setTheme(theme: Theme) {
69
70
  self.theme = theme
70
71
  },
72
+ }))
73
+ .actions((self) => ({
71
74
  afterAttach() {
72
75
  addDisposer(
73
76
  self,
@@ -128,69 +131,56 @@ export function renderingModelIntermediateFactory(
128
131
  { name: 'LinearApolloSixFrameDisplayRenderCollaborators' },
129
132
  ),
130
133
  )
131
- },
132
- }))
133
- }
134
-
135
- export function renderingModelFactory(
136
- pluginManager: PluginManager,
137
- configSchema: AnyConfigurationSchemaType,
138
- ) {
139
- const LinearApolloSixFrameDisplayRendering =
140
- renderingModelIntermediateFactory(pluginManager, configSchema)
141
-
142
- return LinearApolloSixFrameDisplayRendering.actions((self) => ({
143
- afterAttach() {
144
- addDisposer(
145
- self,
146
- autorun(
147
- () => {
148
- const { canvas, featureLayouts, featuresHeight, lgv } = self
149
- if (!lgv.initialized || self.regionCannotBeRendered()) {
150
- return
151
- }
152
- const { displayedRegions, dynamicBlocks } = lgv
134
+ addDisposer(
135
+ self,
136
+ autorun(
137
+ () => {
138
+ const { canvas, featureLayouts, featuresHeight, lgv } = self
139
+ if (!lgv.initialized || self.regionCannotBeRendered()) {
140
+ return
141
+ }
142
+ const { displayedRegions, dynamicBlocks } = lgv
153
143
 
154
- const ctx = canvas?.getContext('2d')
155
- if (!ctx) {
156
- return
157
- }
158
- ctx.clearRect(0, 0, dynamicBlocks.totalWidthPx, featuresHeight)
159
- for (const [idx, featureLayout] of featureLayouts.entries()) {
160
- const displayedRegion = displayedRegions[idx]
161
- for (const [row, featureLayoutRow] of featureLayout.entries()) {
162
- for (const { feature } of featureLayoutRow) {
163
- if (!feature.looksLikeGene) {
164
- continue
165
- }
166
- if (
167
- !doesIntersect2(
168
- displayedRegion.start,
169
- displayedRegion.end,
170
- feature.min,
171
- feature.max,
172
- )
173
- ) {
174
- continue
175
- }
176
- const { topLevelFeature } = feature
177
- const glyph = self.getGlyph(topLevelFeature)
178
- if (glyph !== undefined) {
179
- glyph.draw(ctx, topLevelFeature, row, self, idx)
144
+ const ctx = canvas?.getContext('2d')
145
+ if (!ctx) {
146
+ return
147
+ }
148
+ ctx.clearRect(0, 0, dynamicBlocks.totalWidthPx, featuresHeight)
149
+ for (const [idx, featureLayout] of featureLayouts.entries()) {
150
+ const displayedRegion = displayedRegions[idx]
151
+ for (const [row, featureLayoutRow] of featureLayout.entries()) {
152
+ for (const { feature } of featureLayoutRow) {
153
+ if (!feature.looksLikeGene) {
154
+ continue
155
+ }
156
+ if (
157
+ !doesIntersect2(
158
+ displayedRegion.start,
159
+ displayedRegion.end,
160
+ feature.min,
161
+ feature.max,
162
+ )
163
+ ) {
164
+ continue
165
+ }
166
+ const { topLevelFeature } = feature
167
+ const glyph = self.getGlyph(topLevelFeature)
168
+ if (glyph !== undefined) {
169
+ glyph.draw(ctx, topLevelFeature, row, self, idx)
170
+ }
180
171
  }
181
172
  }
182
173
  }
183
- }
184
- },
185
- { name: 'LinearApolloSixFrameDisplayRenderFeatures' },
186
- ),
187
- )
188
- },
189
- }))
174
+ },
175
+ { name: 'LinearApolloSixFrameDisplayRenderFeatures' },
176
+ ),
177
+ )
178
+ },
179
+ }))
190
180
  }
191
181
 
192
182
  export type LinearApolloSixFrameDisplayRenderingModel = ReturnType<
193
- typeof renderingModelIntermediateFactory
183
+ typeof renderingModelFactory
194
184
  >
195
185
  // eslint disable because of
196
186
  // https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
@@ -15,6 +15,7 @@ import {
15
15
  flow,
16
16
  getRoot,
17
17
  getSnapshot,
18
+ isAlive,
18
19
  types,
19
20
  } from 'mobx-state-tree'
20
21
 
@@ -79,7 +80,9 @@ export const OntologyRecordType = types
79
80
  const equivalents: string[] = terms
80
81
  .map((term) => term.lbl)
81
82
  .filter((term) => term != undefined)
82
- self.setEquivalentTypes(type, equivalents)
83
+ if (isAlive(self)) {
84
+ self.setEquivalentTypes(type, equivalents)
85
+ }
83
86
  }),
84
87
  }))
85
88
  .actions((self) => ({
@@ -10,6 +10,7 @@ import { makeStyles } from 'tss-react/mui'
10
10
  import { isOntologyClass } from '../../OntologyManager'
11
11
  import type OntologyStore from '../../OntologyManager/OntologyStore'
12
12
  import { OntologyTermAutocomplete } from '../../components/OntologyTermAutocomplete'
13
+ import { navToFeatureCenter } from '../../util'
13
14
  import { type DisplayStateModel } from '../types'
14
15
 
15
16
  import {
@@ -68,6 +69,8 @@ function makeContextMenuItems(
68
69
  selectedFeature,
69
70
  session,
70
71
  setSelectedFeature,
72
+ filteredTranscripts,
73
+ updateFilteredTranscripts,
71
74
  } = display
72
75
  return featureContextMenuItems(
73
76
  feature,
@@ -77,15 +80,18 @@ function makeContextMenuItems(
77
80
  setSelectedFeature,
78
81
  session,
79
82
  changeManager,
83
+ filteredTranscripts,
84
+ updateFilteredTranscripts,
80
85
  )
81
86
  }
82
87
 
83
- function getTopLevelFeature(feature: AnnotationFeature): AnnotationFeature {
84
- let cur = feature
85
- while (cur.parent) {
86
- cur = cur.parent
87
- }
88
- return cur
88
+ function navigateHere(
89
+ displayState: DisplayStateModel,
90
+ feature: AnnotationFeature,
91
+ ) {
92
+ displayState.lgv.navTo(
93
+ navToFeatureCenter(feature, 0.1, displayState.lgv.totalBp),
94
+ )
89
95
  }
90
96
 
91
97
  export const Feature = observer(function Feature({
@@ -107,8 +113,8 @@ export const Feature = observer(function Feature({
107
113
  }) {
108
114
  const { classes } = useStyles()
109
115
  const {
110
- apolloHover,
111
116
  changeManager,
117
+ hoveredFeature,
112
118
  selectedFeature,
113
119
  session,
114
120
  tabularEditor: tabularEditorState,
@@ -130,12 +136,7 @@ export const Feature = observer(function Feature({
130
136
  <>
131
137
  <tr
132
138
  onMouseEnter={(_e) => {
133
- displayState.setApolloHover({
134
- feature,
135
- topLevelFeature: getTopLevelFeature(feature),
136
- // @ts-expect-error TODO fix in future when moving hover logic to session.
137
- glyph: displayState.getGlyph(getTopLevelFeature(feature)),
138
- })
139
+ displayState.setHoveredFeature({ feature, bp: min })
139
140
  }}
140
141
  className={
141
142
  classes.feature +
@@ -149,6 +150,10 @@ export const Feature = observer(function Feature({
149
150
  e.stopPropagation()
150
151
  displayState.setSelectedFeature(feature)
151
152
  }}
153
+ onDoubleClick={() => {
154
+ displayState.setSelectedFeature(feature)
155
+ navigateHere(displayState, feature)
156
+ }}
152
157
  onContextMenu={(e) => {
153
158
  e.preventDefault()
154
159
  setContextMenu({
@@ -254,7 +259,8 @@ export const Feature = observer(function Feature({
254
259
  return text.includes(filterText)
255
260
  })
256
261
  .map(([featureId, childFeature]) => {
257
- const childHovered = apolloHover?.feature._id === childFeature._id
262
+ const childHovered =
263
+ hoveredFeature?.feature._id === childFeature._id
258
264
  const childSelected = selectedFeature?._id === childFeature._id
259
265
  return (
260
266
  <Feature
@@ -37,7 +37,7 @@ const HybridGrid = observer(function HybridGrid({
37
37
  }: {
38
38
  model: DisplayStateModel
39
39
  }) {
40
- const { apolloHover, seenFeatures, selectedFeature, tabularEditor } = model
40
+ const { hoveredFeature, seenFeatures, selectedFeature, tabularEditor } = model
41
41
  const theme = useTheme()
42
42
  const { classes } = useStyles()
43
43
  const scrollContainerRef = useRef<HTMLDivElement>(null)
@@ -96,7 +96,7 @@ const HybridGrid = observer(function HybridGrid({
96
96
  })
97
97
  .map(([featureId, feature]) => {
98
98
  const isSelected = selectedFeature?._id === featureId
99
- const isHovered = apolloHover?.feature._id === featureId
99
+ const isHovered = hoveredFeature?.feature._id === featureId
100
100
  return (
101
101
  <Feature
102
102
  key={featureId}
@@ -121,9 +121,11 @@ const HybridGrid = observer(function HybridGrid({
121
121
  onClose={() => {
122
122
  setContextMenu(null)
123
123
  }}
124
- TransitionProps={{
125
- onExit: () => {
126
- setContextMenu(null)
124
+ slotProps={{
125
+ transition: {
126
+ onExit: () => {
127
+ setContextMenu(null)
128
+ },
127
129
  },
128
130
  }}
129
131
  style={{ zIndex: theme.zIndex.tooltip }}
@@ -7,7 +7,14 @@ import {
7
7
  } from '@jbrowse/core/util'
8
8
 
9
9
  import { type ChangeManager } from '../../ChangeManager'
10
- import { AddChildFeature, CopyFeature, DeleteFeature } from '../../components'
10
+ import {
11
+ AddChildFeature,
12
+ CopyFeature,
13
+ DeleteFeature,
14
+ MergeExons,
15
+ MergeTranscripts,
16
+ SplitExon,
17
+ } from '../../components'
11
18
  import { type ApolloSessionModel } from '../../session'
12
19
  import { getApolloInternetAccount } from '../../util'
13
20
 
@@ -19,6 +26,8 @@ export function featureContextMenuItems(
19
26
  setSelectedFeature: (f: AnnotationFeature | undefined) => void,
20
27
  session: ApolloSessionModel,
21
28
  changeManager: ChangeManager,
29
+ filteredTranscripts: string[],
30
+ updateFilteredTranscripts: (forms: string[]) => void,
22
31
  ) {
23
32
  const internetAccount = getApolloInternetAccount(session)
24
33
  const role = internetAccount ? internetAccount.role : 'admin'
@@ -26,6 +35,7 @@ export function featureContextMenuItems(
26
35
  const readOnly = !(role && ['admin', 'user'].includes(role))
27
36
  const menuItems: MenuItem[] = []
28
37
  if (feature) {
38
+ const featureID = feature.attributes.get('gff_id')?.toString()
29
39
  const sourceAssemblyId = getAssemblyId(region.assemblyName)
30
40
  const currentAssemblyId = getAssemblyId(region.assemblyName)
31
41
  menuItems.push(
@@ -111,6 +121,72 @@ export function featureContextMenuItems(
111
121
  )
112
122
  },
113
123
  },
124
+ {
125
+ label: 'Merge transcripts',
126
+ disabled: !admin,
127
+ onClick: () => {
128
+ ;(session as unknown as AbstractSessionModel).queueDialog(
129
+ (doneCallback) => [
130
+ MergeTranscripts,
131
+ {
132
+ session,
133
+ handleClose: () => {
134
+ doneCallback()
135
+ },
136
+ changeManager,
137
+ sourceFeature: feature,
138
+ sourceAssemblyId: currentAssemblyId,
139
+ selectedFeature,
140
+ setSelectedFeature,
141
+ },
142
+ ],
143
+ )
144
+ },
145
+ },
146
+ {
147
+ label: 'Merge exons',
148
+ disabled: !admin,
149
+ onClick: () => {
150
+ ;(session as unknown as AbstractSessionModel).queueDialog(
151
+ (doneCallback) => [
152
+ MergeExons,
153
+ {
154
+ session,
155
+ handleClose: () => {
156
+ doneCallback()
157
+ },
158
+ changeManager,
159
+ sourceFeature: feature,
160
+ sourceAssemblyId: currentAssemblyId,
161
+ selectedFeature,
162
+ setSelectedFeature,
163
+ },
164
+ ],
165
+ )
166
+ },
167
+ },
168
+ {
169
+ label: 'Split exon',
170
+ disabled: !admin,
171
+ onClick: () => {
172
+ ;(session as unknown as AbstractSessionModel).queueDialog(
173
+ (doneCallback) => [
174
+ SplitExon,
175
+ {
176
+ session,
177
+ handleClose: () => {
178
+ doneCallback()
179
+ },
180
+ changeManager,
181
+ sourceFeature: feature,
182
+ sourceAssemblyId: currentAssemblyId,
183
+ selectedFeature,
184
+ setSelectedFeature,
185
+ },
186
+ ],
187
+ )
188
+ },
189
+ },
114
190
  )
115
191
  const { featureTypeOntology } = session.apolloDataStore.ontologyManager
116
192
  if (!featureTypeOntology) {
@@ -121,22 +197,38 @@ export function featureContextMenuItems(
121
197
  featureTypeOntology.isTypeOf(feature.type, 'pseudogenic_transcript')) &&
122
198
  isSessionModelWithWidgets(session)
123
199
  ) {
124
- menuItems.push({
125
- label: 'Edit transcript details',
126
- onClick: () => {
127
- const apolloTranscriptWidget = session.addWidget(
128
- 'ApolloTranscriptDetails',
129
- 'apolloTranscriptDetails',
130
- {
131
- feature,
132
- assembly: currentAssemblyId,
133
- changeManager,
134
- refName: region.refName,
135
- },
136
- )
137
- session.showWidget(apolloTranscriptWidget)
200
+ menuItems.push(
201
+ {
202
+ label: 'Edit transcript details',
203
+ onClick: () => {
204
+ const apolloTranscriptWidget = session.addWidget(
205
+ 'ApolloTranscriptDetails',
206
+ 'apolloTranscriptDetails',
207
+ {
208
+ feature,
209
+ assembly: currentAssemblyId,
210
+ changeManager,
211
+ refName: region.refName,
212
+ },
213
+ )
214
+ session.showWidget(apolloTranscriptWidget)
215
+ },
216
+ },
217
+ {
218
+ label: 'Visible',
219
+ type: 'checkbox',
220
+ checked:
221
+ featureID && filteredTranscripts.includes(featureID) ? false : true,
222
+ onClick: () => {
223
+ if (featureID) {
224
+ const newForms = filteredTranscripts.includes(featureID)
225
+ ? filteredTranscripts.filter((form) => form !== featureID)
226
+ : [...filteredTranscripts, featureID]
227
+ updateFilteredTranscripts(newForms)
228
+ }
229
+ },
138
230
  },
139
- })
231
+ )
140
232
  }
141
233
  }
142
234
  return menuItems
@@ -80,7 +80,7 @@ const useStyles = makeStyles()((theme) => ({
80
80
  borderTop: '1px solid rgba(0, 0, 0, .125)',
81
81
  },
82
82
  radioIcon: {
83
- color: theme?.palette?.tertiary?.contrastText,
83
+ color: theme.palette.tertiary.contrastText,
84
84
  },
85
85
  dialog: {
86
86
  // minHeight: 500,
@@ -0,0 +1,114 @@
1
+ import { AddAssemblyAliasesChange } from '@apollo-annotation/shared'
2
+ import { Box, DialogContent, DialogContentText } from '@mui/material'
3
+ import { DataGrid, type GridColDef, type GridRowModel } from '@mui/x-data-grid'
4
+ import React from 'react'
5
+
6
+ import {
7
+ type ApolloInternetAccount,
8
+ type CollaborationServerDriver,
9
+ } from '../BackendDrivers'
10
+ import { type ChangeManager } from '../ChangeManager'
11
+ import { type ApolloSessionModel } from '../session'
12
+
13
+ import { Dialog } from './Dialog'
14
+
15
+ interface AddAssemblyAliasProps {
16
+ session: ApolloSessionModel
17
+ handleClose: () => void
18
+ changeManager: ChangeManager
19
+ }
20
+
21
+ const columns: GridColDef[] = [
22
+ {
23
+ field: 'name',
24
+ headerName: 'Assembly Name',
25
+ width: 150,
26
+ editable: false,
27
+ },
28
+ {
29
+ field: 'aliases',
30
+ headerName: 'Aliases',
31
+ width: 300,
32
+ editable: true,
33
+ },
34
+ ]
35
+
36
+ interface AssemblyAlias {
37
+ id: string
38
+ name: string
39
+ aliases: string
40
+ }
41
+
42
+ export function AddAssemblyAliases({
43
+ changeManager,
44
+ handleClose,
45
+ session,
46
+ }: AddAssemblyAliasProps) {
47
+ const { apolloDataStore } = session
48
+ const { collaborationServerDriver } = apolloDataStore as {
49
+ collaborationServerDriver: CollaborationServerDriver
50
+ getInternetAccount(
51
+ assemblyName?: string,
52
+ internetAccountId?: string,
53
+ ): ApolloInternetAccount
54
+ }
55
+ const assemblies = collaborationServerDriver.getAssemblies()
56
+
57
+ const rows: AssemblyAlias[] = assemblies.map((assembly) => {
58
+ return {
59
+ id: assembly.name,
60
+ name: assembly.displayName,
61
+ aliases: assembly.aliases.join(', '),
62
+ } as AssemblyAlias
63
+ })
64
+
65
+ const [errorMessage, setErrorMessage] = React.useState('')
66
+
67
+ const processRowUpdate = (newRow: GridRowModel, _oldRow: GridRowModel) => {
68
+ const change = new AddAssemblyAliasesChange({
69
+ typeName: 'AddAssemblyAliasesChange',
70
+ assembly: newRow.id as string,
71
+ aliases: (newRow.aliases as string).split(','),
72
+ })
73
+ void changeManager.submit(change).catch(() => {
74
+ setErrorMessage('Error submitting change')
75
+ })
76
+ handleClose()
77
+ return newRow
78
+ }
79
+
80
+ return (
81
+ <Dialog
82
+ open
83
+ title="Add assembly aliases"
84
+ handleClose={handleClose}
85
+ maxWidth={'sm'}
86
+ data-testid="add-assembly-alias"
87
+ fullWidth
88
+ >
89
+ <DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
90
+ <Box sx={{ height: 400, width: '100%' }}>
91
+ <DataGrid
92
+ rows={rows}
93
+ columns={columns}
94
+ initialState={{
95
+ pagination: {
96
+ paginationModel: {
97
+ pageSize: 5,
98
+ },
99
+ },
100
+ }}
101
+ pageSizeOptions={[5]}
102
+ processRowUpdate={processRowUpdate}
103
+ disableRowSelectionOnClick
104
+ />
105
+ </Box>
106
+ </DialogContent>
107
+ {errorMessage ? (
108
+ <DialogContent>
109
+ <DialogContentText color="error">{errorMessage}</DialogContentText>
110
+ </DialogContent>
111
+ ) : null}
112
+ </Dialog>
113
+ )
114
+ }
@@ -1,8 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/unbound-method */
2
- /* eslint-disable @typescript-eslint/no-misused-promises */
2
+
3
3
  import { type AnnotationFeature } from '@apollo-annotation/mst'
4
4
  import { AddFeatureChange } from '@apollo-annotation/shared'
5
- import { type AbstractSessionModel } from '@jbrowse/core/util'
6
5
  import {
7
6
  Button,
8
7
  DialogActions,
@@ -37,7 +36,6 @@ export function AddChildFeature({
37
36
  sourceAssemblyId,
38
37
  sourceFeature,
39
38
  }: AddChildFeatureProps) {
40
- const { notify } = session as unknown as AbstractSessionModel
41
39
  const [end, setEnd] = useState(String(sourceFeature.max))
42
40
  const [start, setStart] = useState(String(sourceFeature.min + 1))
43
41
  const [type, setType] = useState('')
@@ -63,15 +61,16 @@ export function AddChildFeature({
63
61
  return terms
64
62
  }
65
63
 
66
- async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
64
+ function onSubmit(event: React.FormEvent<HTMLFormElement>) {
67
65
  event.preventDefault()
68
66
  setErrorMessage('')
67
+ const _id = new ObjectID().toHexString()
69
68
  const change = new AddFeatureChange({
70
69
  changedIds: [sourceFeature._id],
71
70
  typeName: 'AddFeatureChange',
72
71
  assembly: sourceAssemblyId,
73
72
  addedFeature: {
74
- _id: new ObjectID().toHexString(),
73
+ _id,
75
74
  refSeq: sourceFeature.refSeq,
76
75
  min: Number(start) - 1,
77
76
  max: Number(end),
@@ -79,8 +78,9 @@ export function AddChildFeature({
79
78
  },
80
79
  parentFeatureId: sourceFeature._id,
81
80
  })
82
- await changeManager.submit(change)
83
- notify('Feature added successfully', 'success')
81
+ void changeManager.submit(change).then(() => {
82
+ session.apolloSetSelectedFeature(_id)
83
+ })
84
84
  handleClose()
85
85
  event.preventDefault()
86
86
  }