@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.
- package/README.md +76 -0
- package/dist/index.esm.js +10248 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +7 -0
- package/dist/jbrowse-plugin-apollo.cjs.development.js +10298 -0
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +2 -0
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.umd.development.js +46957 -0
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +2 -0
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -0
- package/package.json +130 -0
- package/src/ApolloInternetAccount/addMenuItems.ts +94 -0
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +121 -0
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +62 -0
- package/src/ApolloInternetAccount/components/LoginIcons.tsx +74 -0
- package/src/ApolloInternetAccount/configSchema.ts +26 -0
- package/src/ApolloInternetAccount/index.ts +2 -0
- package/src/ApolloInternetAccount/model.ts +448 -0
- package/src/ApolloJobModel.ts +117 -0
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +186 -0
- package/src/ApolloSequenceAdapter/configSchema.ts +12 -0
- package/src/ApolloSequenceAdapter/index.ts +21 -0
- package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +12 -0
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +692 -0
- package/src/ApolloSixFrameRenderer/configSchema.ts +7 -0
- package/src/ApolloSixFrameRenderer/index.ts +3 -0
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +64 -0
- package/src/ApolloTextSearchAdapter/configSchema.ts +24 -0
- package/src/ApolloTextSearchAdapter/index.ts +18 -0
- package/src/BackendDrivers/BackendDriver.ts +31 -0
- package/src/BackendDrivers/CollaborationServerDriver.ts +318 -0
- package/src/BackendDrivers/DesktopFileDriver.ts +170 -0
- package/src/BackendDrivers/InMemoryFileDriver.ts +76 -0
- package/src/BackendDrivers/index.ts +4 -0
- package/src/ChangeManager.ts +148 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +248 -0
- package/src/LinearApolloDisplay/components/index.ts +1 -0
- package/src/LinearApolloDisplay/configSchema.ts +16 -0
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +422 -0
- package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +1191 -0
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +151 -0
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +382 -0
- package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +697 -0
- package/src/LinearApolloDisplay/glyphs/index.ts +4 -0
- package/src/LinearApolloDisplay/index.ts +2 -0
- package/src/LinearApolloDisplay/stateModel/base.ts +146 -0
- package/src/LinearApolloDisplay/stateModel/getGlyph.ts +39 -0
- package/src/LinearApolloDisplay/stateModel/glyphs.ts +45 -0
- package/src/LinearApolloDisplay/stateModel/index.ts +20 -0
- package/src/LinearApolloDisplay/stateModel/layouts.ts +230 -0
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +513 -0
- package/src/LinearApolloDisplay/stateModel/rendering.ts +441 -0
- package/src/LinearApolloDisplay/stateModel/trackHeightMixin.ts +43 -0
- package/src/LinearApolloDisplay/types.ts +1 -0
- package/src/OntologyManager/OntologyStore/__snapshots__/fulltext.test.ts.snap +208 -0
- package/src/OntologyManager/OntologyStore/__snapshots__/index.test.ts.snap +18846 -0
- package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +137 -0
- package/src/OntologyManager/OntologyStore/fulltext.test.ts +94 -0
- package/src/OntologyManager/OntologyStore/fulltext.ts +264 -0
- package/src/OntologyManager/OntologyStore/index.test.ts +130 -0
- package/src/OntologyManager/OntologyStore/index.ts +526 -0
- package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +89 -0
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +180 -0
- package/src/OntologyManager/OntologyStore/obo-graph-json-schema.ts +110 -0
- package/src/OntologyManager/OntologyStore/prefixes.ts +35 -0
- package/src/OntologyManager/index.ts +173 -0
- package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +19 -0
- package/src/SixFrameFeatureDisplay/components/index.ts +1 -0
- package/src/SixFrameFeatureDisplay/configSchema.ts +21 -0
- package/src/SixFrameFeatureDisplay/index.ts +2 -0
- package/src/SixFrameFeatureDisplay/stateModel.ts +413 -0
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +88 -0
- package/src/TabularEditor/HybridGrid/Feature.tsx +346 -0
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +34 -0
- package/src/TabularEditor/HybridGrid/Highlight.tsx +40 -0
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +138 -0
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +77 -0
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +59 -0
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +119 -0
- package/src/TabularEditor/HybridGrid/index.ts +1 -0
- package/src/TabularEditor/TabularEditorPane.tsx +34 -0
- package/src/TabularEditor/index.ts +3 -0
- package/src/TabularEditor/model.ts +44 -0
- package/src/TabularEditor/types.ts +3 -0
- package/src/components/AddAssembly.tsx +464 -0
- package/src/components/AddChildFeature.tsx +247 -0
- package/src/components/AddFeature.tsx +252 -0
- package/src/components/CopyFeature.tsx +328 -0
- package/src/components/DeleteAssembly.tsx +185 -0
- package/src/components/DeleteFeature.tsx +90 -0
- package/src/components/Dialog.tsx +47 -0
- package/src/components/DownloadGFF3.tsx +213 -0
- package/src/components/ImportFeatures.tsx +295 -0
- package/src/components/ManageChecks.tsx +280 -0
- package/src/components/ManageUsers.tsx +218 -0
- package/src/components/ModifyFeatureAttribute.tsx +457 -0
- package/src/components/OntologyTermAutocomplete.tsx +240 -0
- package/src/components/OntologyTermMultiSelect.tsx +349 -0
- package/src/components/OpenLocalFile.tsx +178 -0
- package/src/components/ViewChangeLog.tsx +208 -0
- package/src/components/ViewCheckResults.tsx +151 -0
- package/src/components/index.ts +12 -0
- package/src/config.ts +10 -0
- package/src/declare.d.ts +3 -0
- package/src/extensions/annotationFromPileup.ts +208 -0
- package/src/extensions/index.ts +1 -0
- package/src/index.ts +394 -0
- package/src/makeDisplayComponent.tsx +244 -0
- package/src/session/ClientDataStore.ts +282 -0
- package/src/session/index.ts +1 -0
- package/src/session/session.ts +373 -0
- package/src/types.ts +10 -0
- package/src/util/index.ts +31 -0
- package/src/util/loadAssemblyIntoClient.ts +291 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { ConfigurationSchema } from '@jbrowse/core/configuration'
|
|
2
|
+
import {
|
|
3
|
+
DisplayType,
|
|
4
|
+
InternetAccountType,
|
|
5
|
+
PluggableElementType,
|
|
6
|
+
TrackType,
|
|
7
|
+
ViewType,
|
|
8
|
+
createBaseTrackConfig,
|
|
9
|
+
createBaseTrackModel,
|
|
10
|
+
} from '@jbrowse/core/pluggableElementTypes'
|
|
11
|
+
import Plugin from '@jbrowse/core/Plugin'
|
|
12
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
13
|
+
import {
|
|
14
|
+
AbstractSessionModel,
|
|
15
|
+
Region,
|
|
16
|
+
getSession,
|
|
17
|
+
isAbstractMenuManager,
|
|
18
|
+
} from '@jbrowse/core/util'
|
|
19
|
+
import { LinearGenomeViewStateModel } from '@jbrowse/plugin-linear-genome-view'
|
|
20
|
+
import AddIcon from '@mui/icons-material/Add'
|
|
21
|
+
import { changeRegistry, checkRegistry } from 'apollo-common'
|
|
22
|
+
import {
|
|
23
|
+
CDSCheck,
|
|
24
|
+
CoreValidation,
|
|
25
|
+
ParentChildValidation,
|
|
26
|
+
changes,
|
|
27
|
+
validationRegistry,
|
|
28
|
+
} from 'apollo-shared'
|
|
29
|
+
|
|
30
|
+
import { version } from '../package.json'
|
|
31
|
+
import {
|
|
32
|
+
configSchema as apolloInternetAccountConfigSchema,
|
|
33
|
+
modelFactory as apolloInternetAccountModelFactory,
|
|
34
|
+
} from './ApolloInternetAccount'
|
|
35
|
+
import { installApolloSequenceAdapter } from './ApolloSequenceAdapter'
|
|
36
|
+
import {
|
|
37
|
+
ApolloSixFrameRenderer,
|
|
38
|
+
ReactComponent as ApolloSixFrameRendererReactComponent,
|
|
39
|
+
configSchema as apolloSixFrameRendererConfigSchema,
|
|
40
|
+
} from './ApolloSixFrameRenderer'
|
|
41
|
+
import { installApolloTextSearchAdapter } from './ApolloTextSearchAdapter'
|
|
42
|
+
import { BackendDriver } from './BackendDrivers'
|
|
43
|
+
import {
|
|
44
|
+
DownloadGFF3,
|
|
45
|
+
ManageChecks,
|
|
46
|
+
OpenLocalFile,
|
|
47
|
+
ViewChangeLog,
|
|
48
|
+
} from './components'
|
|
49
|
+
import { AddFeature } from './components/AddFeature'
|
|
50
|
+
import { ViewCheckResults } from './components/ViewCheckResults'
|
|
51
|
+
import ApolloPluginConfigurationSchema from './config'
|
|
52
|
+
import { annotationFromPileup } from './extensions'
|
|
53
|
+
import {
|
|
54
|
+
stateModelFactory as LinearApolloDisplayStateModelFactory,
|
|
55
|
+
configSchemaFactory as linearApolloDisplayConfigSchemaFactory,
|
|
56
|
+
} from './LinearApolloDisplay'
|
|
57
|
+
import {
|
|
58
|
+
DisplayComponent,
|
|
59
|
+
makeSixFrameDisplayComponent,
|
|
60
|
+
} from './makeDisplayComponent'
|
|
61
|
+
import { ApolloSessionModel, extendSession } from './session'
|
|
62
|
+
import {
|
|
63
|
+
stateModelFactory as SixFrameFeatureDisplayStateModelFactory,
|
|
64
|
+
configSchemaFactory as sixFrameFeatureDisplayConfigSchemaFactory,
|
|
65
|
+
} from './SixFrameFeatureDisplay'
|
|
66
|
+
|
|
67
|
+
interface RpcHandle {
|
|
68
|
+
on(event: string, listener: (event: MessageEvent) => void): this
|
|
69
|
+
workers: Worker[]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface ApolloMessageData {
|
|
73
|
+
apollo: true
|
|
74
|
+
messageId: string
|
|
75
|
+
method: string
|
|
76
|
+
region: Region
|
|
77
|
+
sequence: string
|
|
78
|
+
assembly: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isApolloMessageData(data?: unknown): data is ApolloMessageData {
|
|
82
|
+
return (
|
|
83
|
+
typeof data === 'object' &&
|
|
84
|
+
data !== null &&
|
|
85
|
+
'apollo' in data &&
|
|
86
|
+
data.apollo === true
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const inWebWorker = 'WorkerGlobalScope' in globalThis
|
|
91
|
+
|
|
92
|
+
for (const [changeName, change] of Object.entries(changes)) {
|
|
93
|
+
changeRegistry.registerChange(changeName, change)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const cdsCheck = new CDSCheck()
|
|
97
|
+
checkRegistry.registerCheck(cdsCheck.name, cdsCheck)
|
|
98
|
+
validationRegistry.registerValidation(new CoreValidation())
|
|
99
|
+
validationRegistry.registerValidation(new ParentChildValidation())
|
|
100
|
+
|
|
101
|
+
export default class ApolloPlugin extends Plugin {
|
|
102
|
+
name = 'ApolloPlugin'
|
|
103
|
+
version = version
|
|
104
|
+
configurationSchema = ApolloPluginConfigurationSchema
|
|
105
|
+
|
|
106
|
+
install(pluginManager: PluginManager) {
|
|
107
|
+
installApolloSequenceAdapter(pluginManager)
|
|
108
|
+
installApolloTextSearchAdapter(pluginManager)
|
|
109
|
+
pluginManager.addTrackType(() => {
|
|
110
|
+
const configSchema = ConfigurationSchema(
|
|
111
|
+
'ApolloTrack',
|
|
112
|
+
{ adapter: '' },
|
|
113
|
+
{
|
|
114
|
+
baseConfiguration: createBaseTrackConfig(pluginManager),
|
|
115
|
+
explicitIdentifier: 'trackId',
|
|
116
|
+
},
|
|
117
|
+
)
|
|
118
|
+
return new TrackType({
|
|
119
|
+
name: 'ApolloTrack',
|
|
120
|
+
configSchema,
|
|
121
|
+
stateModel: createBaseTrackModel(
|
|
122
|
+
pluginManager,
|
|
123
|
+
'ApolloTrack',
|
|
124
|
+
configSchema,
|
|
125
|
+
),
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
pluginManager.addInternetAccountType(() => {
|
|
130
|
+
return new InternetAccountType({
|
|
131
|
+
name: 'ApolloInternetAccount',
|
|
132
|
+
configSchema: apolloInternetAccountConfigSchema,
|
|
133
|
+
stateModel: apolloInternetAccountModelFactory(
|
|
134
|
+
apolloInternetAccountConfigSchema,
|
|
135
|
+
),
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
pluginManager.addDisplayType(() => {
|
|
140
|
+
const configSchema = linearApolloDisplayConfigSchemaFactory(pluginManager)
|
|
141
|
+
return new DisplayType({
|
|
142
|
+
name: 'LinearApolloDisplay',
|
|
143
|
+
configSchema,
|
|
144
|
+
stateModel: LinearApolloDisplayStateModelFactory(
|
|
145
|
+
pluginManager,
|
|
146
|
+
configSchema,
|
|
147
|
+
),
|
|
148
|
+
trackType: 'ApolloTrack',
|
|
149
|
+
viewType: 'LinearGenomeView',
|
|
150
|
+
ReactComponent: DisplayComponent,
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
pluginManager.addDisplayType(() => {
|
|
155
|
+
const configSchema =
|
|
156
|
+
sixFrameFeatureDisplayConfigSchemaFactory(pluginManager)
|
|
157
|
+
const SixFrameDisplayComponent =
|
|
158
|
+
makeSixFrameDisplayComponent(pluginManager)
|
|
159
|
+
return new DisplayType({
|
|
160
|
+
name: 'SixFrameFeatureDisplay',
|
|
161
|
+
configSchema,
|
|
162
|
+
stateModel: SixFrameFeatureDisplayStateModelFactory(
|
|
163
|
+
pluginManager,
|
|
164
|
+
configSchema,
|
|
165
|
+
),
|
|
166
|
+
trackType: 'ApolloTrack',
|
|
167
|
+
viewType: 'LinearGenomeView',
|
|
168
|
+
ReactComponent: SixFrameDisplayComponent,
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
pluginManager.addRendererType(
|
|
173
|
+
() =>
|
|
174
|
+
new ApolloSixFrameRenderer({
|
|
175
|
+
name: 'ApolloSixFrameRenderer',
|
|
176
|
+
ReactComponent: ApolloSixFrameRendererReactComponent,
|
|
177
|
+
configSchema: apolloSixFrameRendererConfigSchema,
|
|
178
|
+
pluginManager,
|
|
179
|
+
}),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
pluginManager.addToExtensionPoint(
|
|
183
|
+
'Core-extendSession',
|
|
184
|
+
extendSession.bind(this, pluginManager),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
pluginManager.addToExtensionPoint(
|
|
188
|
+
'Core-extendPluggableElement',
|
|
189
|
+
(pluggableElement: PluggableElementType) => {
|
|
190
|
+
if (pluggableElement.name === 'LinearGenomeView') {
|
|
191
|
+
const { stateModel } = pluggableElement as ViewType
|
|
192
|
+
const lgv = stateModel as LinearGenomeViewStateModel
|
|
193
|
+
const newStateModel = lgv.views((self) => {
|
|
194
|
+
const superRubberBandMenuItems = self.rubberBandMenuItems
|
|
195
|
+
return {
|
|
196
|
+
rubberBandMenuItems() {
|
|
197
|
+
return [
|
|
198
|
+
...superRubberBandMenuItems(),
|
|
199
|
+
{
|
|
200
|
+
label: 'Add new feature',
|
|
201
|
+
icon: AddIcon,
|
|
202
|
+
onClick: () => {
|
|
203
|
+
const session = getSession(
|
|
204
|
+
self,
|
|
205
|
+
) as unknown as ApolloSessionModel
|
|
206
|
+
const { leftOffset, rightOffset } = self
|
|
207
|
+
const selectedRegions = self.getSelectedRegions(
|
|
208
|
+
leftOffset,
|
|
209
|
+
rightOffset,
|
|
210
|
+
)
|
|
211
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
212
|
+
(doneCallback) => [
|
|
213
|
+
AddFeature,
|
|
214
|
+
{
|
|
215
|
+
session,
|
|
216
|
+
handleClose: () => {
|
|
217
|
+
doneCallback()
|
|
218
|
+
},
|
|
219
|
+
region: selectedRegions[0],
|
|
220
|
+
changeManager:
|
|
221
|
+
session.apolloDataStore.changeManager,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
)
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
;(pluggableElement as ViewType).stateModel = newStateModel
|
|
232
|
+
}
|
|
233
|
+
return pluggableElement
|
|
234
|
+
},
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
pluginManager.addToExtensionPoint(
|
|
238
|
+
'Core-extendPluggableElement',
|
|
239
|
+
annotationFromPileup,
|
|
240
|
+
)
|
|
241
|
+
if (!inWebWorker) {
|
|
242
|
+
pluginManager.addToExtensionPoint(
|
|
243
|
+
'Core-extendWorker',
|
|
244
|
+
(handle: RpcHandle) => {
|
|
245
|
+
if (!('on' in handle && handle.on)) {
|
|
246
|
+
return handle
|
|
247
|
+
}
|
|
248
|
+
handle.on('apollo', async (event: MessageEvent) => {
|
|
249
|
+
if (!isApolloMessageData(event)) {
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
const { apollo, messageId, method } = event
|
|
253
|
+
switch (method) {
|
|
254
|
+
case 'getSequence': {
|
|
255
|
+
const { region } = event
|
|
256
|
+
const { assemblyName } = region
|
|
257
|
+
const dataStore = (
|
|
258
|
+
pluginManager.rootModel?.session as
|
|
259
|
+
| ApolloSessionModel
|
|
260
|
+
| undefined
|
|
261
|
+
)?.apolloDataStore
|
|
262
|
+
if (!dataStore) {
|
|
263
|
+
break
|
|
264
|
+
}
|
|
265
|
+
const backendDriver = dataStore.getBackendDriver(
|
|
266
|
+
assemblyName,
|
|
267
|
+
) as BackendDriver
|
|
268
|
+
const { seq: sequence } =
|
|
269
|
+
await backendDriver.getSequence(region)
|
|
270
|
+
handle.workers[0].postMessage({
|
|
271
|
+
apollo,
|
|
272
|
+
messageId,
|
|
273
|
+
sequence,
|
|
274
|
+
})
|
|
275
|
+
break
|
|
276
|
+
}
|
|
277
|
+
case 'getRegions': {
|
|
278
|
+
const { assembly } = event
|
|
279
|
+
const dataStore = (
|
|
280
|
+
pluginManager.rootModel?.session as
|
|
281
|
+
| ApolloSessionModel
|
|
282
|
+
| undefined
|
|
283
|
+
)?.apolloDataStore
|
|
284
|
+
if (!dataStore) {
|
|
285
|
+
break
|
|
286
|
+
}
|
|
287
|
+
const backendDriver = dataStore.getBackendDriver(
|
|
288
|
+
assembly,
|
|
289
|
+
) as BackendDriver
|
|
290
|
+
const regions = await backendDriver.getRegions(assembly)
|
|
291
|
+
handle.workers[0].postMessage({
|
|
292
|
+
apollo,
|
|
293
|
+
messageId,
|
|
294
|
+
regions,
|
|
295
|
+
})
|
|
296
|
+
break
|
|
297
|
+
}
|
|
298
|
+
default: {
|
|
299
|
+
break
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
return handle
|
|
304
|
+
},
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
configure(pluginManager: PluginManager) {
|
|
310
|
+
if (isAbstractMenuManager(pluginManager.rootModel)) {
|
|
311
|
+
pluginManager.rootModel.appendToMenu('Apollo', {
|
|
312
|
+
label: 'Download GFF3',
|
|
313
|
+
onClick: (session: ApolloSessionModel) => {
|
|
314
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
315
|
+
(doneCallback) => [
|
|
316
|
+
DownloadGFF3,
|
|
317
|
+
{
|
|
318
|
+
session,
|
|
319
|
+
handleClose: () => {
|
|
320
|
+
doneCallback()
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
)
|
|
325
|
+
},
|
|
326
|
+
})
|
|
327
|
+
pluginManager.rootModel.appendToMenu('Apollo', {
|
|
328
|
+
label: 'Manage Checks',
|
|
329
|
+
onClick: (session: ApolloSessionModel) => {
|
|
330
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
331
|
+
(doneCallback) => [
|
|
332
|
+
ManageChecks,
|
|
333
|
+
{
|
|
334
|
+
session,
|
|
335
|
+
handleClose: () => {
|
|
336
|
+
doneCallback()
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
)
|
|
341
|
+
},
|
|
342
|
+
})
|
|
343
|
+
pluginManager.rootModel.appendToMenu('Apollo', {
|
|
344
|
+
label: 'View Change Log',
|
|
345
|
+
onClick: (session: ApolloSessionModel) => {
|
|
346
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
347
|
+
(doneCallback) => [
|
|
348
|
+
ViewChangeLog,
|
|
349
|
+
{
|
|
350
|
+
session,
|
|
351
|
+
handleClose: () => {
|
|
352
|
+
doneCallback()
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
)
|
|
357
|
+
},
|
|
358
|
+
})
|
|
359
|
+
pluginManager.rootModel.appendToMenu('Apollo', {
|
|
360
|
+
label: 'Open local GFF3 file',
|
|
361
|
+
onClick: (session: ApolloSessionModel) => {
|
|
362
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
363
|
+
(doneCallback) => [
|
|
364
|
+
OpenLocalFile,
|
|
365
|
+
{
|
|
366
|
+
session,
|
|
367
|
+
handleClose: () => {
|
|
368
|
+
doneCallback()
|
|
369
|
+
},
|
|
370
|
+
inMemoryFileDriver: session.apolloDataStore.inMemoryFileDriver,
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
)
|
|
374
|
+
},
|
|
375
|
+
})
|
|
376
|
+
pluginManager.rootModel.appendToMenu('Apollo', {
|
|
377
|
+
label: 'View check results',
|
|
378
|
+
onClick: (session: ApolloSessionModel) => {
|
|
379
|
+
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
380
|
+
(doneCallback) => [
|
|
381
|
+
ViewCheckResults,
|
|
382
|
+
{
|
|
383
|
+
session,
|
|
384
|
+
handleClose: () => {
|
|
385
|
+
doneCallback()
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
)
|
|
390
|
+
},
|
|
391
|
+
})
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
|
+
import type LinearGenomeViewPlugin from '@jbrowse/plugin-linear-genome-view'
|
|
3
|
+
import ExpandLessIcon from '@mui/icons-material/ExpandLess'
|
|
4
|
+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
|
5
|
+
import { Typography } from '@mui/material'
|
|
6
|
+
import { alpha } from '@mui/material/styles'
|
|
7
|
+
import { observer } from 'mobx-react'
|
|
8
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
9
|
+
import { makeStyles } from 'tss-react/mui'
|
|
10
|
+
|
|
11
|
+
import { LinearApolloDisplay } from './LinearApolloDisplay/components'
|
|
12
|
+
import { LinearApolloDisplay as LinearApolloDisplayI } from './LinearApolloDisplay/stateModel'
|
|
13
|
+
import { TrackLines } from './SixFrameFeatureDisplay/components'
|
|
14
|
+
import { SixFrameFeatureDisplay } from './SixFrameFeatureDisplay/stateModel'
|
|
15
|
+
import { TabularEditorPane } from './TabularEditor'
|
|
16
|
+
|
|
17
|
+
const accordionControlHeight = 12
|
|
18
|
+
|
|
19
|
+
const useStyles = makeStyles()((theme) => ({
|
|
20
|
+
shading: {
|
|
21
|
+
background: alpha(theme.palette.primary.main, 0.2),
|
|
22
|
+
overflowY: 'scroll',
|
|
23
|
+
overflowX: 'hidden',
|
|
24
|
+
},
|
|
25
|
+
details: {
|
|
26
|
+
background: theme.palette.background.paper,
|
|
27
|
+
},
|
|
28
|
+
accordionControl: {
|
|
29
|
+
height: accordionControlHeight,
|
|
30
|
+
width: '100%',
|
|
31
|
+
'&:hover': {
|
|
32
|
+
background: theme.palette.action.hover,
|
|
33
|
+
},
|
|
34
|
+
display: 'flex',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
justifyContent: 'center',
|
|
37
|
+
},
|
|
38
|
+
accordionRoot: {
|
|
39
|
+
background: theme.palette.divider,
|
|
40
|
+
},
|
|
41
|
+
resizeHandle: {
|
|
42
|
+
width: '100%',
|
|
43
|
+
height: 4,
|
|
44
|
+
position: 'absolute',
|
|
45
|
+
cursor: 'row-resize',
|
|
46
|
+
zIndex: 100,
|
|
47
|
+
},
|
|
48
|
+
expandIcon: {
|
|
49
|
+
// position: 'relative',
|
|
50
|
+
},
|
|
51
|
+
title: {
|
|
52
|
+
// position: 'relative',
|
|
53
|
+
userSelect: 'none',
|
|
54
|
+
},
|
|
55
|
+
}))
|
|
56
|
+
|
|
57
|
+
function scrollSelectedFeatureIntoView(
|
|
58
|
+
model: LinearApolloDisplayI,
|
|
59
|
+
scrollContainerRef: React.RefObject<HTMLDivElement>,
|
|
60
|
+
) {
|
|
61
|
+
const { apolloRowHeight, selectedFeature } = model
|
|
62
|
+
if (scrollContainerRef.current && selectedFeature) {
|
|
63
|
+
const position = model.getFeatureLayoutPosition(selectedFeature)
|
|
64
|
+
if (position) {
|
|
65
|
+
const row = position.layoutRow + position.featureRow
|
|
66
|
+
const top = row * apolloRowHeight
|
|
67
|
+
scrollContainerRef.current.scroll({ top, behavior: 'smooth' })
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ResizeHandle = ({
|
|
73
|
+
onResize,
|
|
74
|
+
}: {
|
|
75
|
+
onResize: (sizeDelta: number) => void
|
|
76
|
+
}) => {
|
|
77
|
+
const { classes } = useStyles()
|
|
78
|
+
const mouseMove = useCallback(
|
|
79
|
+
(event: MouseEvent) => {
|
|
80
|
+
event.stopPropagation()
|
|
81
|
+
event.preventDefault()
|
|
82
|
+
onResize(event.movementY)
|
|
83
|
+
},
|
|
84
|
+
[onResize],
|
|
85
|
+
)
|
|
86
|
+
const cancelDrag: (event: MouseEvent) => void = useCallback(
|
|
87
|
+
(event: MouseEvent) => {
|
|
88
|
+
event.stopPropagation()
|
|
89
|
+
event.preventDefault()
|
|
90
|
+
window.removeEventListener('mousemove', mouseMove)
|
|
91
|
+
window.removeEventListener('mouseup', cancelDrag)
|
|
92
|
+
window.removeEventListener('mouseleave', cancelDrag)
|
|
93
|
+
},
|
|
94
|
+
[mouseMove],
|
|
95
|
+
)
|
|
96
|
+
return (
|
|
97
|
+
// TODO: a11y
|
|
98
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
|
99
|
+
<div
|
|
100
|
+
onMouseDown={(event: React.MouseEvent) => {
|
|
101
|
+
event.stopPropagation()
|
|
102
|
+
window.addEventListener('mousemove', mouseMove)
|
|
103
|
+
window.addEventListener('mouseup', cancelDrag)
|
|
104
|
+
window.addEventListener('mouseleave', cancelDrag)
|
|
105
|
+
}}
|
|
106
|
+
onClick={(e) => {
|
|
107
|
+
e.stopPropagation()
|
|
108
|
+
e.preventDefault()
|
|
109
|
+
}}
|
|
110
|
+
className={classes.resizeHandle}
|
|
111
|
+
/>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const AccordionControl = observer(function AccordionControl({
|
|
116
|
+
onClick,
|
|
117
|
+
onResize,
|
|
118
|
+
open,
|
|
119
|
+
title,
|
|
120
|
+
}: {
|
|
121
|
+
open: boolean
|
|
122
|
+
onClick: (e: React.MouseEvent) => void
|
|
123
|
+
onResize?: (sizeDelta: number) => void
|
|
124
|
+
title?: string
|
|
125
|
+
}) {
|
|
126
|
+
const { classes } = useStyles()
|
|
127
|
+
return (
|
|
128
|
+
<div className={classes.accordionRoot}>
|
|
129
|
+
{open && onResize ? <ResizeHandle onResize={onResize} /> : null}
|
|
130
|
+
{/* TODO: a11y */}
|
|
131
|
+
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
|
|
132
|
+
<div className={classes.accordionControl} onClick={onClick}>
|
|
133
|
+
{open ? (
|
|
134
|
+
<ExpandLessIcon className={classes.expandIcon} />
|
|
135
|
+
) : (
|
|
136
|
+
<ExpandMoreIcon className={classes.expandIcon} />
|
|
137
|
+
)}
|
|
138
|
+
{title ? (
|
|
139
|
+
<Typography
|
|
140
|
+
className={classes.title}
|
|
141
|
+
variant="caption"
|
|
142
|
+
component="span"
|
|
143
|
+
>
|
|
144
|
+
{title}
|
|
145
|
+
</Typography>
|
|
146
|
+
) : null}
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
export const DisplayComponent = observer(function DisplayComponent({
|
|
153
|
+
model,
|
|
154
|
+
...other
|
|
155
|
+
}: {
|
|
156
|
+
model: LinearApolloDisplayI
|
|
157
|
+
}) {
|
|
158
|
+
const { classes } = useStyles()
|
|
159
|
+
|
|
160
|
+
const {
|
|
161
|
+
height: overallHeight,
|
|
162
|
+
isShown,
|
|
163
|
+
selectedFeature,
|
|
164
|
+
tabularEditor,
|
|
165
|
+
toggleShown,
|
|
166
|
+
} = model
|
|
167
|
+
const detailsHeight = tabularEditor.isShown ? model.detailsHeight : 0
|
|
168
|
+
const featureAreaHeight = isShown
|
|
169
|
+
? overallHeight - detailsHeight - accordionControlHeight * 2
|
|
170
|
+
: 0
|
|
171
|
+
|
|
172
|
+
const onDetailsResize = (delta: number) => {
|
|
173
|
+
model.setDetailsHeight(model.detailsHeight - delta)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const canvasScrollContainerRef = useRef<HTMLDivElement>(null)
|
|
177
|
+
useEffect(
|
|
178
|
+
() => scrollSelectedFeatureIntoView(model, canvasScrollContainerRef),
|
|
179
|
+
[model, selectedFeature],
|
|
180
|
+
)
|
|
181
|
+
return (
|
|
182
|
+
<div className={classes.details} style={{ height: overallHeight }}>
|
|
183
|
+
<AccordionControl
|
|
184
|
+
open={isShown}
|
|
185
|
+
title="Graphical"
|
|
186
|
+
onClick={toggleShown}
|
|
187
|
+
/>
|
|
188
|
+
<div
|
|
189
|
+
className={classes.shading}
|
|
190
|
+
ref={canvasScrollContainerRef}
|
|
191
|
+
style={{ height: featureAreaHeight }}
|
|
192
|
+
>
|
|
193
|
+
<LinearApolloDisplay model={model} {...other} />
|
|
194
|
+
</div>
|
|
195
|
+
<AccordionControl
|
|
196
|
+
title="Table"
|
|
197
|
+
open={tabularEditor.isShown}
|
|
198
|
+
onClick={tabularEditor.togglePane}
|
|
199
|
+
onResize={onDetailsResize}
|
|
200
|
+
/>
|
|
201
|
+
<div style={{ height: detailsHeight }}>
|
|
202
|
+
<TabularEditorPane model={model} />
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
export function makeSixFrameDisplayComponent(pluginManager: PluginManager) {
|
|
209
|
+
const LGVPlugin = pluginManager.getPlugin('LinearGenomeViewPlugin') as
|
|
210
|
+
| LinearGenomeViewPlugin
|
|
211
|
+
| undefined
|
|
212
|
+
if (!LGVPlugin) {
|
|
213
|
+
throw new Error('LinearGenomeView plugin not found')
|
|
214
|
+
}
|
|
215
|
+
const { BaseLinearDisplayComponent } = LGVPlugin.exports
|
|
216
|
+
function ApolloDisplayComponent({
|
|
217
|
+
model,
|
|
218
|
+
...other
|
|
219
|
+
}: {
|
|
220
|
+
model: SixFrameFeatureDisplay
|
|
221
|
+
}) {
|
|
222
|
+
const { classes } = useStyles()
|
|
223
|
+
const { height, selectedFeature } = model
|
|
224
|
+
let { detailsHeight } = model
|
|
225
|
+
if (!selectedFeature) {
|
|
226
|
+
detailsHeight = 0
|
|
227
|
+
}
|
|
228
|
+
const featureAreaHeight = height - detailsHeight
|
|
229
|
+
return (
|
|
230
|
+
<div style={{ height: model.height }}>
|
|
231
|
+
<div className={classes.shading} style={{ height: featureAreaHeight }}>
|
|
232
|
+
<BaseLinearDisplayComponent model={model} {...other} />
|
|
233
|
+
</div>
|
|
234
|
+
{/* <div className={classes.details} style={{ height: detailsHeight }}>
|
|
235
|
+
<ApolloDetails model={model} />
|
|
236
|
+
</div> */}
|
|
237
|
+
<div className="testTrackLines">
|
|
238
|
+
<TrackLines model={model} />
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
return observer(ApolloDisplayComponent)
|
|
244
|
+
}
|