@apollo-annotation/jbrowse-plugin-apollo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +76 -0
  2. package/dist/index.esm.js +10248 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +7 -0
  5. package/dist/jbrowse-plugin-apollo.cjs.development.js +10298 -0
  6. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -0
  7. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +2 -0
  8. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -0
  9. package/dist/jbrowse-plugin-apollo.umd.development.js +46957 -0
  10. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -0
  11. package/dist/jbrowse-plugin-apollo.umd.production.min.js +2 -0
  12. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -0
  13. package/package.json +130 -0
  14. package/src/ApolloInternetAccount/addMenuItems.ts +94 -0
  15. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +121 -0
  16. package/src/ApolloInternetAccount/components/LoginButtons.tsx +62 -0
  17. package/src/ApolloInternetAccount/components/LoginIcons.tsx +74 -0
  18. package/src/ApolloInternetAccount/configSchema.ts +26 -0
  19. package/src/ApolloInternetAccount/index.ts +2 -0
  20. package/src/ApolloInternetAccount/model.ts +448 -0
  21. package/src/ApolloJobModel.ts +117 -0
  22. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +186 -0
  23. package/src/ApolloSequenceAdapter/configSchema.ts +12 -0
  24. package/src/ApolloSequenceAdapter/index.ts +21 -0
  25. package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +12 -0
  26. package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +692 -0
  27. package/src/ApolloSixFrameRenderer/configSchema.ts +7 -0
  28. package/src/ApolloSixFrameRenderer/index.ts +3 -0
  29. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +64 -0
  30. package/src/ApolloTextSearchAdapter/configSchema.ts +24 -0
  31. package/src/ApolloTextSearchAdapter/index.ts +18 -0
  32. package/src/BackendDrivers/BackendDriver.ts +31 -0
  33. package/src/BackendDrivers/CollaborationServerDriver.ts +318 -0
  34. package/src/BackendDrivers/DesktopFileDriver.ts +170 -0
  35. package/src/BackendDrivers/InMemoryFileDriver.ts +76 -0
  36. package/src/BackendDrivers/index.ts +4 -0
  37. package/src/ChangeManager.ts +148 -0
  38. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +248 -0
  39. package/src/LinearApolloDisplay/components/index.ts +1 -0
  40. package/src/LinearApolloDisplay/configSchema.ts +16 -0
  41. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +422 -0
  42. package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +1191 -0
  43. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +151 -0
  44. package/src/LinearApolloDisplay/glyphs/Glyph.ts +382 -0
  45. package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +697 -0
  46. package/src/LinearApolloDisplay/glyphs/index.ts +4 -0
  47. package/src/LinearApolloDisplay/index.ts +2 -0
  48. package/src/LinearApolloDisplay/stateModel/base.ts +146 -0
  49. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +39 -0
  50. package/src/LinearApolloDisplay/stateModel/glyphs.ts +45 -0
  51. package/src/LinearApolloDisplay/stateModel/index.ts +20 -0
  52. package/src/LinearApolloDisplay/stateModel/layouts.ts +230 -0
  53. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +513 -0
  54. package/src/LinearApolloDisplay/stateModel/rendering.ts +441 -0
  55. package/src/LinearApolloDisplay/stateModel/trackHeightMixin.ts +43 -0
  56. package/src/LinearApolloDisplay/types.ts +1 -0
  57. package/src/OntologyManager/OntologyStore/__snapshots__/fulltext.test.ts.snap +208 -0
  58. package/src/OntologyManager/OntologyStore/__snapshots__/index.test.ts.snap +18846 -0
  59. package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +137 -0
  60. package/src/OntologyManager/OntologyStore/fulltext.test.ts +94 -0
  61. package/src/OntologyManager/OntologyStore/fulltext.ts +264 -0
  62. package/src/OntologyManager/OntologyStore/index.test.ts +130 -0
  63. package/src/OntologyManager/OntologyStore/index.ts +526 -0
  64. package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +89 -0
  65. package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +180 -0
  66. package/src/OntologyManager/OntologyStore/obo-graph-json-schema.ts +110 -0
  67. package/src/OntologyManager/OntologyStore/prefixes.ts +35 -0
  68. package/src/OntologyManager/index.ts +173 -0
  69. package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +19 -0
  70. package/src/SixFrameFeatureDisplay/components/index.ts +1 -0
  71. package/src/SixFrameFeatureDisplay/configSchema.ts +21 -0
  72. package/src/SixFrameFeatureDisplay/index.ts +2 -0
  73. package/src/SixFrameFeatureDisplay/stateModel.ts +413 -0
  74. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +88 -0
  75. package/src/TabularEditor/HybridGrid/Feature.tsx +346 -0
  76. package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +34 -0
  77. package/src/TabularEditor/HybridGrid/Highlight.tsx +40 -0
  78. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +138 -0
  79. package/src/TabularEditor/HybridGrid/NumberCell.tsx +77 -0
  80. package/src/TabularEditor/HybridGrid/ToolBar.tsx +59 -0
  81. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +119 -0
  82. package/src/TabularEditor/HybridGrid/index.ts +1 -0
  83. package/src/TabularEditor/TabularEditorPane.tsx +34 -0
  84. package/src/TabularEditor/index.ts +3 -0
  85. package/src/TabularEditor/model.ts +44 -0
  86. package/src/TabularEditor/types.ts +3 -0
  87. package/src/components/AddAssembly.tsx +464 -0
  88. package/src/components/AddChildFeature.tsx +247 -0
  89. package/src/components/AddFeature.tsx +252 -0
  90. package/src/components/CopyFeature.tsx +328 -0
  91. package/src/components/DeleteAssembly.tsx +185 -0
  92. package/src/components/DeleteFeature.tsx +90 -0
  93. package/src/components/Dialog.tsx +47 -0
  94. package/src/components/DownloadGFF3.tsx +213 -0
  95. package/src/components/ImportFeatures.tsx +295 -0
  96. package/src/components/ManageChecks.tsx +280 -0
  97. package/src/components/ManageUsers.tsx +218 -0
  98. package/src/components/ModifyFeatureAttribute.tsx +457 -0
  99. package/src/components/OntologyTermAutocomplete.tsx +240 -0
  100. package/src/components/OntologyTermMultiSelect.tsx +349 -0
  101. package/src/components/OpenLocalFile.tsx +178 -0
  102. package/src/components/ViewChangeLog.tsx +208 -0
  103. package/src/components/ViewCheckResults.tsx +151 -0
  104. package/src/components/index.ts +12 -0
  105. package/src/config.ts +10 -0
  106. package/src/declare.d.ts +3 -0
  107. package/src/extensions/annotationFromPileup.ts +208 -0
  108. package/src/extensions/index.ts +1 -0
  109. package/src/index.ts +394 -0
  110. package/src/makeDisplayComponent.tsx +244 -0
  111. package/src/session/ClientDataStore.ts +282 -0
  112. package/src/session/index.ts +1 -0
  113. package/src/session/session.ts +373 -0
  114. package/src/types.ts +10 -0
  115. package/src/util/index.ts +31 -0
  116. package/src/util/loadAssemblyIntoClient.ts +291 -0
@@ -0,0 +1,151 @@
1
+ import { AnnotationFeatureI } from 'apollo-mst'
2
+
3
+ import { LinearApolloDisplay } from '../stateModel'
4
+ import { MousePosition } from '../stateModel/mouseEvents'
5
+ import { CanvasMouseEvent } from '../types'
6
+ import { BoxGlyph } from './BoxGlyph'
7
+
8
+ export class GenericChildGlyph extends BoxGlyph {
9
+ featuresForRow(feature: AnnotationFeatureI): AnnotationFeatureI[][] {
10
+ const features = [[feature]]
11
+ if (feature.children) {
12
+ for (const [, child] of feature.children ?? new Map()) {
13
+ features.push(...this.featuresForRow(child))
14
+ }
15
+ }
16
+ return features
17
+ }
18
+
19
+ getRowCount(feature: AnnotationFeatureI) {
20
+ return this.featuresForRow(feature).length
21
+ }
22
+
23
+ draw(
24
+ stateModel: LinearApolloDisplay,
25
+ ctx: CanvasRenderingContext2D,
26
+ feature: AnnotationFeatureI,
27
+ xOffset: number,
28
+ row: number,
29
+ reversed: boolean,
30
+ ) {
31
+ for (let i = 0; i < this.getRowCount(feature); i++) {
32
+ this.drawRow(stateModel, ctx, feature, xOffset, row + i, row, reversed)
33
+ }
34
+ }
35
+
36
+ drawRow(
37
+ stateModel: LinearApolloDisplay,
38
+ ctx: CanvasRenderingContext2D,
39
+ topLevelFeature: AnnotationFeatureI,
40
+ xOffset: number,
41
+ row: number,
42
+ topRow: number,
43
+ reversed: boolean,
44
+ ) {
45
+ const features = this.featuresForRow(topLevelFeature)[row - topRow]
46
+ for (const feature of features) {
47
+ this.drawFeature(
48
+ stateModel,
49
+ ctx,
50
+ topLevelFeature,
51
+ feature,
52
+ xOffset,
53
+ row,
54
+ reversed,
55
+ )
56
+ }
57
+ }
58
+
59
+ private drawFeature(
60
+ stateModel: LinearApolloDisplay,
61
+ ctx: CanvasRenderingContext2D,
62
+ topLevelFeature: AnnotationFeatureI,
63
+ feature: AnnotationFeatureI,
64
+ xOffset: number,
65
+ row: number,
66
+ reversed: boolean,
67
+ ) {
68
+ const { apolloRowHeight: heightPx, lgv, session } = stateModel
69
+ const { bpPerPx } = lgv
70
+ const { apolloSelectedFeature } = session
71
+ const offsetPx = (feature.start - topLevelFeature.min) / bpPerPx
72
+ const widthPx = feature.length / bpPerPx
73
+ const startPx = reversed ? xOffset - offsetPx - widthPx : xOffset + offsetPx
74
+ const top = row * heightPx
75
+ const rowCount = this.getRowCount(feature)
76
+ const isSelected = this.getIsSelectedFeature(feature, apolloSelectedFeature)
77
+ const groupingColor = isSelected
78
+ ? 'rgba(130,0,0,0.45)'
79
+ : 'rgba(255,0,0,0.25)'
80
+ if (rowCount > 1) {
81
+ // draw background that encapsulates all child features
82
+ const featureHeight = rowCount * heightPx
83
+ this.drawBox(ctx, startPx, top, widthPx, featureHeight, groupingColor)
84
+ }
85
+ super.draw(stateModel, ctx, feature, startPx, row, reversed)
86
+ }
87
+
88
+ drawHover(stateModel: LinearApolloDisplay, ctx: CanvasRenderingContext2D) {
89
+ const { apolloHover, apolloRowHeight, displayedRegions, lgv } = stateModel
90
+ if (!apolloHover) {
91
+ return
92
+ }
93
+ const { feature, mousePosition } = apolloHover
94
+ if (!(feature && mousePosition)) {
95
+ return
96
+ }
97
+ const { regionNumber, y } = mousePosition
98
+ const displayedRegion = displayedRegions[regionNumber]
99
+ const { refName, reversed } = displayedRegion
100
+ const { bpPerPx, bpToPx, offsetPx } = lgv
101
+ const { end, length, start } = feature
102
+ const startPx =
103
+ (bpToPx({ refName, coord: reversed ? end : start, regionNumber })
104
+ ?.offsetPx ?? 0) - offsetPx
105
+ const row = Math.floor(y / apolloRowHeight)
106
+ const top = row * apolloRowHeight
107
+ const widthPx = length / bpPerPx
108
+ ctx.fillStyle = 'rgba(0,0,0,0.2)'
109
+ ctx.fillRect(
110
+ startPx,
111
+ top,
112
+ widthPx,
113
+ apolloRowHeight * this.getRowCount(feature),
114
+ )
115
+ }
116
+
117
+ onMouseUp(stateModel: LinearApolloDisplay, event: CanvasMouseEvent) {
118
+ if (stateModel.apolloDragging ?? event.button !== 0) {
119
+ return
120
+ }
121
+ const { feature } = stateModel.getFeatureAndGlyphUnderMouse(event)
122
+ if (feature) {
123
+ stateModel.setSelectedFeature(feature)
124
+ }
125
+ }
126
+
127
+ continueDrag(
128
+ _display: LinearApolloDisplay,
129
+ _currentMousePosition: MousePosition,
130
+ ): void {
131
+ // pass
132
+ }
133
+
134
+ getFeatureFromLayout(feature: AnnotationFeatureI, bp: number, row: number) {
135
+ const layoutRow = this.featuresForRow(feature)[row]
136
+ return layoutRow?.find((f) => bp >= f.start && bp <= f.end)
137
+ }
138
+
139
+ getRowForFeature(
140
+ feature: AnnotationFeatureI,
141
+ childFeature: AnnotationFeatureI,
142
+ ) {
143
+ const rows = this.featuresForRow(feature)
144
+ for (const [idx, row] of rows.entries()) {
145
+ if (row.some((feature) => feature._id === childFeature._id)) {
146
+ return idx
147
+ }
148
+ }
149
+ return
150
+ }
151
+ }
@@ -0,0 +1,382 @@
1
+ import { MenuItem } from '@jbrowse/core/ui'
2
+ import { AbstractSessionModel } from '@jbrowse/core/util'
3
+ import { alpha } from '@mui/material'
4
+ import { AnnotationFeatureI } from 'apollo-mst'
5
+
6
+ import {
7
+ AddChildFeature,
8
+ CopyFeature,
9
+ DeleteFeature,
10
+ ModifyFeatureAttribute,
11
+ } from '../../components'
12
+ import {
13
+ LinearApolloDisplayMouseEvents,
14
+ MousePosition,
15
+ } from '../stateModel/mouseEvents'
16
+ import { LinearApolloDisplayRendering } from '../stateModel/rendering'
17
+ import { CanvasMouseEvent } from '../types'
18
+
19
+ export abstract class Glyph {
20
+ /** @returns number of layout rows used by this glyph with this feature and zoom level */
21
+ abstract getRowCount(feature: AnnotationFeatureI, bpPerPx: number): number
22
+
23
+ /** draw the feature's primary rendering on the canvas */
24
+ abstract draw(
25
+ display: LinearApolloDisplayRendering,
26
+ ctx: CanvasRenderingContext2D,
27
+ feature: AnnotationFeatureI,
28
+ xOffset: number,
29
+ row: number,
30
+ reversed: boolean,
31
+ ): void
32
+
33
+ /** @returns the feature or subfeature at the given bp and row number in this glyph's layout */
34
+ abstract getFeatureFromLayout(
35
+ feature: AnnotationFeatureI,
36
+ bp: number,
37
+ row: number,
38
+ ): AnnotationFeatureI | undefined
39
+
40
+ abstract getRowForFeature(
41
+ feature: AnnotationFeatureI,
42
+ childFeature: AnnotationFeatureI,
43
+ ): number | undefined
44
+
45
+ abstract continueDrag(
46
+ display: LinearApolloDisplayRendering,
47
+ currentMousePosition: MousePosition,
48
+ ): void
49
+
50
+ drawHover(
51
+ _display: LinearApolloDisplayMouseEvents,
52
+ _overlayCtx: CanvasRenderingContext2D,
53
+ _rowNum?: number,
54
+ _xOffset?: number,
55
+ _reversed?: boolean,
56
+ ) {
57
+ return
58
+ }
59
+
60
+ drawDragPreview(
61
+ _display: LinearApolloDisplayMouseEvents,
62
+ _ctx: CanvasRenderingContext2D,
63
+ ) {
64
+ return
65
+ }
66
+
67
+ /** @returns true if the current drag that is starting is valid */
68
+ startDrag(
69
+ _display: LinearApolloDisplayMouseEvents,
70
+ _event: CanvasMouseEvent,
71
+ ): boolean {
72
+ return false
73
+ }
74
+
75
+ executeDrag(
76
+ _display: LinearApolloDisplayMouseEvents,
77
+ _event: CanvasMouseEvent,
78
+ ): void {
79
+ return
80
+ }
81
+
82
+ onMouseDown(
83
+ _display: LinearApolloDisplayMouseEvents,
84
+ _event: CanvasMouseEvent,
85
+ ): void {
86
+ return
87
+ }
88
+
89
+ onMouseMove(
90
+ _display: LinearApolloDisplayMouseEvents,
91
+ _event: CanvasMouseEvent,
92
+ ): void {
93
+ return
94
+ }
95
+
96
+ onMouseLeave(
97
+ _display: LinearApolloDisplayMouseEvents,
98
+ _event: CanvasMouseEvent,
99
+ ): void {
100
+ return
101
+ }
102
+
103
+ onMouseUp(
104
+ _display: LinearApolloDisplayMouseEvents,
105
+ _event: CanvasMouseEvent,
106
+ ): void {
107
+ return
108
+ }
109
+
110
+ onContextMenu(
111
+ _display: LinearApolloDisplayMouseEvents,
112
+ _event: CanvasMouseEvent,
113
+ ): void {
114
+ return
115
+ }
116
+
117
+ drawTooltip(
118
+ display: LinearApolloDisplayMouseEvents,
119
+ context: CanvasRenderingContext2D,
120
+ ): void {
121
+ const { apolloHover, apolloRowHeight, displayedRegions, lgv, theme } =
122
+ display
123
+ if (!apolloHover) {
124
+ return
125
+ }
126
+ const { feature, mousePosition } = apolloHover
127
+ if (!(feature && mousePosition)) {
128
+ return
129
+ }
130
+ const { regionNumber, y } = mousePosition
131
+ const displayedRegion = displayedRegions[regionNumber]
132
+ const { refName, reversed } = displayedRegion
133
+ const { bpPerPx, bpToPx, offsetPx } = lgv
134
+
135
+ const { discontinuousLocations } = feature
136
+ let start: number, end: number, length: number
137
+ let location = 'Loc: '
138
+ if (discontinuousLocations && discontinuousLocations.length > 0) {
139
+ const lastLoc = discontinuousLocations.at(-1)
140
+ if (!lastLoc) {
141
+ return
142
+ }
143
+ ;({ start } = lastLoc)
144
+ ;({ end } = lastLoc)
145
+ length = lastLoc.end - lastLoc.start
146
+
147
+ if (discontinuousLocations.length <= 2) {
148
+ for (const [i, loc] of discontinuousLocations.entries()) {
149
+ location += `${loc.start + 1}–${loc.end}`
150
+ if (i !== discontinuousLocations.length - 1) {
151
+ location += ','
152
+ }
153
+ }
154
+ } else {
155
+ const [firstLoc] = discontinuousLocations
156
+ location += `${firstLoc.start + 1}–${firstLoc.end},…,${
157
+ lastLoc.start + 1
158
+ }–${lastLoc.end}`
159
+ }
160
+ } else {
161
+ ;({ end, length, start } = feature)
162
+ location += `${start + 1}–${end}`
163
+ }
164
+
165
+ let startPx =
166
+ (bpToPx({ refName, coord: reversed ? end : start, regionNumber })
167
+ ?.offsetPx ?? 0) - offsetPx
168
+ const row = Math.floor(y / apolloRowHeight)
169
+ const top = row * apolloRowHeight
170
+ const widthPx = length / bpPerPx
171
+
172
+ const featureType = `Type: ${feature.type}`
173
+ const { attributes } = feature
174
+ const featureName = attributes.get('gff_name')?.find((name) => name !== '')
175
+ const textWidth = [
176
+ context.measureText(featureType).width,
177
+ context.measureText(location).width,
178
+ ]
179
+ if (featureName) {
180
+ textWidth.push(context.measureText(`Name: ${featureName}`).width)
181
+ }
182
+ const maxWidth = Math.max(...textWidth)
183
+
184
+ startPx = startPx + widthPx + 5
185
+ context.fillStyle = alpha(
186
+ theme?.palette.text.primary ?? 'rgb(1, 1, 1)',
187
+ 0.7,
188
+ )
189
+ context.fillRect(
190
+ startPx,
191
+ top,
192
+ maxWidth + 4,
193
+ textWidth.length === 3 ? 45 : 35,
194
+ )
195
+ context.beginPath()
196
+ context.moveTo(startPx, top)
197
+ context.lineTo(startPx - 5, top + 5)
198
+ context.lineTo(startPx, top + 10)
199
+ context.fill()
200
+ context.fillStyle =
201
+ theme?.palette.background.default ?? 'rgba(255, 255, 255)'
202
+ let textTop = top + 12
203
+ context.fillText(featureType, startPx + 2, textTop)
204
+ if (featureName) {
205
+ textTop = textTop + 12
206
+ context.fillText(`Name: ${featureName}`, startPx + 2, textTop)
207
+ }
208
+ textTop = textTop + 12
209
+ context.fillText(location, startPx + 2, textTop)
210
+ }
211
+
212
+ getAdjacentFeatures(
213
+ feature?: AnnotationFeatureI,
214
+ parentFeature?: AnnotationFeatureI,
215
+ ): {
216
+ prevFeature?: AnnotationFeatureI
217
+ nextFeature?: AnnotationFeatureI
218
+ } {
219
+ let prevFeature: AnnotationFeatureI | undefined
220
+ let nextFeature: AnnotationFeatureI | undefined
221
+ let i = 0
222
+ if (!feature || !(parentFeature && parentFeature.children)) {
223
+ return { prevFeature, nextFeature }
224
+ }
225
+ for (const [, f] of parentFeature.children) {
226
+ if (f._id === feature._id) {
227
+ break
228
+ }
229
+ i++
230
+ }
231
+ const keys = [...parentFeature.children.keys()]
232
+ if (i > 0) {
233
+ const key = keys[i - 1]
234
+ prevFeature = parentFeature.children.get(key)
235
+ }
236
+ if (i < keys.length - 1) {
237
+ const key = keys[i + 1]
238
+ nextFeature = parentFeature.children.get(key)
239
+ }
240
+ return { prevFeature, nextFeature }
241
+ }
242
+
243
+ getParentFeature(
244
+ feature?: AnnotationFeatureI,
245
+ topLevelFeature?: AnnotationFeatureI,
246
+ ) {
247
+ let parentFeature
248
+
249
+ if (!feature || !(topLevelFeature && topLevelFeature.children)) {
250
+ return parentFeature
251
+ }
252
+
253
+ for (const [, f] of topLevelFeature.children) {
254
+ if (f._id === feature._id) {
255
+ parentFeature = topLevelFeature
256
+ break
257
+ }
258
+ if (!f?.children) {
259
+ continue
260
+ }
261
+ for (const [, cf] of f.children) {
262
+ if (cf._id === feature._id) {
263
+ parentFeature = f
264
+ break
265
+ }
266
+ }
267
+ if (parentFeature) {
268
+ break
269
+ }
270
+ }
271
+ return parentFeature
272
+ }
273
+
274
+ getContextMenuItems(display: LinearApolloDisplayMouseEvents): MenuItem[] {
275
+ const {
276
+ apolloHover,
277
+ apolloInternetAccount: internetAccount,
278
+ changeManager,
279
+ getAssemblyId,
280
+ regions,
281
+ selectedFeature,
282
+ session,
283
+ setSelectedFeature,
284
+ } = display
285
+ const { feature: sourceFeature } = apolloHover ?? {}
286
+ const role = internetAccount ? internetAccount.role : 'admin'
287
+ const admin = role === 'admin'
288
+ const readOnly = !(role && ['admin', 'user'].includes(role))
289
+ const menuItems: MenuItem[] = []
290
+ if (sourceFeature) {
291
+ const [region] = regions
292
+ const sourceAssemblyId = getAssemblyId(region.assemblyName)
293
+ const currentAssemblyId = getAssemblyId(region.assemblyName)
294
+ menuItems.push(
295
+ {
296
+ label: 'Add child feature',
297
+ disabled: readOnly,
298
+ onClick: () => {
299
+ ;(session as unknown as AbstractSessionModel).queueDialog(
300
+ (doneCallback) => [
301
+ AddChildFeature,
302
+ {
303
+ session,
304
+ handleClose: () => {
305
+ doneCallback()
306
+ },
307
+ changeManager,
308
+ sourceFeature,
309
+ sourceAssemblyId,
310
+ internetAccount,
311
+ },
312
+ ],
313
+ )
314
+ },
315
+ },
316
+ {
317
+ label: 'Copy features and annotations',
318
+ disabled: readOnly,
319
+ onClick: () => {
320
+ ;(session as unknown as AbstractSessionModel).queueDialog(
321
+ (doneCallback) => [
322
+ CopyFeature,
323
+ {
324
+ session,
325
+ handleClose: () => {
326
+ doneCallback()
327
+ },
328
+ changeManager,
329
+ sourceFeature,
330
+ sourceAssemblyId: currentAssemblyId,
331
+ },
332
+ ],
333
+ )
334
+ },
335
+ },
336
+ {
337
+ label: 'Delete feature',
338
+ disabled: !admin,
339
+ onClick: () => {
340
+ ;(session as unknown as AbstractSessionModel).queueDialog(
341
+ (doneCallback) => [
342
+ DeleteFeature,
343
+ {
344
+ session,
345
+ handleClose: () => {
346
+ doneCallback()
347
+ },
348
+ changeManager,
349
+ sourceFeature,
350
+ sourceAssemblyId: currentAssemblyId,
351
+ selectedFeature,
352
+ setSelectedFeature,
353
+ },
354
+ ],
355
+ )
356
+ },
357
+ },
358
+ {
359
+ label: 'Modify feature attribute',
360
+ disabled: readOnly,
361
+ onClick: () => {
362
+ ;(session as unknown as AbstractSessionModel).queueDialog(
363
+ (doneCallback) => [
364
+ ModifyFeatureAttribute,
365
+ {
366
+ session,
367
+ handleClose: () => {
368
+ doneCallback()
369
+ },
370
+ changeManager,
371
+ sourceFeature,
372
+ sourceAssemblyId: currentAssemblyId,
373
+ },
374
+ ],
375
+ )
376
+ },
377
+ },
378
+ )
379
+ }
380
+ return menuItems
381
+ }
382
+ }