@jbrowse/plugin-linear-genome-view 2.4.1 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseLinearDisplay/components/LinearBlocks.d.ts +1 -3
- package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +8 -43
- package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
- package/dist/BaseLinearDisplay/components/TooLargeMessage.d.ts +2 -2
- package/dist/BaseLinearDisplay/components/TooLargeMessage.js +4 -8
- package/dist/BaseLinearDisplay/components/TooLargeMessage.js.map +1 -1
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +13 -28
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +93 -137
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
- package/dist/BaseLinearDisplay/models/util.d.ts +4 -0
- package/dist/BaseLinearDisplay/models/util.js +33 -1
- package/dist/BaseLinearDisplay/models/util.js.map +1 -1
- package/dist/LinearBareDisplay/index.js +1 -0
- package/dist/LinearBareDisplay/index.js.map +1 -1
- package/dist/LinearBareDisplay/model.d.ts +11 -19
- package/dist/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -1
- package/dist/LinearBasicDisplay/components/SetMaxHeight.js +2 -5
- package/dist/LinearBasicDisplay/components/SetMaxHeight.js.map +1 -1
- package/dist/LinearBasicDisplay/index.js +1 -1
- package/dist/LinearBasicDisplay/model.d.ts +14 -35
- package/dist/LinearBasicDisplay/model.js.map +1 -1
- package/dist/LinearGenomeView/components/RefNameAutocomplete.js +11 -17
- package/dist/LinearGenomeView/components/RefNameAutocomplete.js.map +1 -1
- package/dist/index.d.ts +33 -57
- package/esm/BaseLinearDisplay/components/LinearBlocks.d.ts +1 -3
- package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +8 -20
- package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
- package/esm/BaseLinearDisplay/components/TooLargeMessage.d.ts +2 -2
- package/esm/BaseLinearDisplay/components/TooLargeMessage.js +4 -8
- package/esm/BaseLinearDisplay/components/TooLargeMessage.js.map +1 -1
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +13 -28
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +92 -136
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
- package/esm/BaseLinearDisplay/models/util.d.ts +4 -0
- package/esm/BaseLinearDisplay/models/util.js +30 -0
- package/esm/BaseLinearDisplay/models/util.js.map +1 -1
- package/esm/LinearBareDisplay/index.js +1 -0
- package/esm/LinearBareDisplay/index.js.map +1 -1
- package/esm/LinearBareDisplay/model.d.ts +11 -19
- package/esm/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -1
- package/esm/LinearBasicDisplay/components/SetMaxHeight.js +2 -5
- package/esm/LinearBasicDisplay/components/SetMaxHeight.js.map +1 -1
- package/esm/LinearBasicDisplay/index.js +1 -1
- package/esm/LinearBasicDisplay/model.d.ts +14 -35
- package/esm/LinearBasicDisplay/model.js.map +1 -1
- package/esm/LinearGenomeView/components/RefNameAutocomplete.js +11 -17
- package/esm/LinearGenomeView/components/RefNameAutocomplete.js.map +1 -1
- package/esm/index.d.ts +33 -57
- package/package.json +2 -2
- package/src/BaseLinearDisplay/components/ServerSideRenderedBlockContent.tsx +8 -28
- package/src/BaseLinearDisplay/components/TooLargeMessage.tsx +8 -10
- package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +130 -189
- package/src/BaseLinearDisplay/models/util.ts +37 -0
- package/src/LinearBareDisplay/index.ts +1 -0
- package/src/LinearBasicDisplay/components/SetMaxHeight.tsx +3 -7
- package/src/LinearBasicDisplay/index.ts +1 -1
- package/src/LinearBasicDisplay/model.ts +1 -1
- package/src/LinearGenomeView/components/RefNameAutocomplete.tsx +16 -18
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import React from 'react'
|
|
3
|
+
import { ThemeOptions } from '@mui/material'
|
|
3
4
|
import { BaseDisplay } from '@jbrowse/core/pluggableElementTypes/models'
|
|
4
5
|
import { getConf } from '@jbrowse/core/configuration'
|
|
5
6
|
import { MenuItem } from '@jbrowse/core/ui'
|
|
@@ -19,10 +20,7 @@ import { Stats } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
|
19
20
|
import { BaseBlock } from '@jbrowse/core/util/blockTypes'
|
|
20
21
|
import { Region } from '@jbrowse/core/util/types'
|
|
21
22
|
import CompositeMap from '@jbrowse/core/util/compositeMap'
|
|
22
|
-
import {
|
|
23
|
-
getParentRenderProps,
|
|
24
|
-
getRpcSessionId,
|
|
25
|
-
} from '@jbrowse/core/util/tracks'
|
|
23
|
+
import { getParentRenderProps } from '@jbrowse/core/util/tracks'
|
|
26
24
|
import { autorun } from 'mobx'
|
|
27
25
|
import { addDisposer, isAlive, types, Instance } from 'mobx-state-tree'
|
|
28
26
|
|
|
@@ -34,7 +32,7 @@ import { LinearGenomeViewModel, ExportSvgOptions } from '../../LinearGenomeView'
|
|
|
34
32
|
import { Tooltip } from '../components/BaseLinearDisplay'
|
|
35
33
|
import TooLargeMessage from '../components/TooLargeMessage'
|
|
36
34
|
import BlockState, { renderBlockData } from './serverSideRenderedBlock'
|
|
37
|
-
import {
|
|
35
|
+
import { getId, getDisplayStr, estimateRegionsStatsPre } from './util'
|
|
38
36
|
|
|
39
37
|
type LGV = LinearGenomeViewModel
|
|
40
38
|
|
|
@@ -46,26 +44,8 @@ export interface Layout {
|
|
|
46
44
|
name: string
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
// stabilize clipid under test for snapshot
|
|
50
|
-
function getId(id: string, index: number) {
|
|
51
|
-
const isJest = typeof jest === 'undefined'
|
|
52
|
-
return `clip-${isJest ? id : 'jest'}-${index}`
|
|
53
|
-
}
|
|
54
|
-
|
|
55
47
|
type LayoutRecord = [number, number, number, number]
|
|
56
48
|
|
|
57
|
-
function getDisplayStr(totalBytes: number) {
|
|
58
|
-
let displayBp
|
|
59
|
-
if (Math.floor(totalBytes / 1000000) > 0) {
|
|
60
|
-
displayBp = `${Number.parseFloat((totalBytes / 1000000).toPrecision(3))} Mb`
|
|
61
|
-
} else if (Math.floor(totalBytes / 1000) > 0) {
|
|
62
|
-
displayBp = `${Number.parseFloat((totalBytes / 1000).toPrecision(3))} Kb`
|
|
63
|
-
} else {
|
|
64
|
-
displayBp = `${Math.floor(totalBytes)} bytes`
|
|
65
|
-
}
|
|
66
|
-
return displayBp
|
|
67
|
-
}
|
|
68
|
-
|
|
69
49
|
const minDisplayHeight = 20
|
|
70
50
|
|
|
71
51
|
/**
|
|
@@ -109,8 +89,8 @@ function stateModelFactory() {
|
|
|
109
89
|
message: '',
|
|
110
90
|
featureIdUnderMouse: undefined as undefined | string,
|
|
111
91
|
contextMenuFeature: undefined as undefined | Feature,
|
|
112
|
-
|
|
113
|
-
|
|
92
|
+
estimatedRegionsStatsP: undefined as undefined | Promise<Stats>,
|
|
93
|
+
estimatedRegionsStats: undefined as undefined | Stats,
|
|
114
94
|
}))
|
|
115
95
|
.views(self => ({
|
|
116
96
|
get height() {
|
|
@@ -184,7 +164,7 @@ function stateModelFactory() {
|
|
|
184
164
|
get features() {
|
|
185
165
|
const featureMaps = []
|
|
186
166
|
for (const block of self.blockState.values()) {
|
|
187
|
-
if (block
|
|
167
|
+
if (block?.features) {
|
|
188
168
|
featureMaps.push(block.features)
|
|
189
169
|
}
|
|
190
170
|
}
|
|
@@ -202,7 +182,11 @@ function stateModelFactory() {
|
|
|
202
182
|
/**
|
|
203
183
|
* #getter
|
|
204
184
|
*/
|
|
205
|
-
getFeatureOverlapping(
|
|
185
|
+
getFeatureOverlapping(
|
|
186
|
+
blockKey: string,
|
|
187
|
+
x: number,
|
|
188
|
+
y: number,
|
|
189
|
+
): string | undefined {
|
|
206
190
|
return self.blockState.get(blockKey)?.layout?.getByCoord(x, y)
|
|
207
191
|
},
|
|
208
192
|
|
|
@@ -231,7 +215,7 @@ function stateModelFactory() {
|
|
|
231
215
|
* #getter
|
|
232
216
|
*/
|
|
233
217
|
get currentBytesRequested() {
|
|
234
|
-
return self.
|
|
218
|
+
return self.estimatedRegionsStats?.bytes || 0
|
|
235
219
|
},
|
|
236
220
|
|
|
237
221
|
/**
|
|
@@ -239,7 +223,7 @@ function stateModelFactory() {
|
|
|
239
223
|
*/
|
|
240
224
|
get currentFeatureScreenDensity() {
|
|
241
225
|
const view = getContainingView(self) as LGV
|
|
242
|
-
return (self.
|
|
226
|
+
return (self.estimatedRegionsStats?.featureDensity || 0) * view.bpPerPx
|
|
243
227
|
},
|
|
244
228
|
|
|
245
229
|
/**
|
|
@@ -252,7 +236,7 @@ function stateModelFactory() {
|
|
|
252
236
|
* #getter
|
|
253
237
|
*/
|
|
254
238
|
get estimatedStatsReady() {
|
|
255
|
-
return !!self.
|
|
239
|
+
return !!self.estimatedRegionsStats || !!self.userBpPerPxLimit
|
|
256
240
|
},
|
|
257
241
|
|
|
258
242
|
/**
|
|
@@ -261,7 +245,7 @@ function stateModelFactory() {
|
|
|
261
245
|
get maxAllowableBytes() {
|
|
262
246
|
return (
|
|
263
247
|
self.userByteSizeLimit ||
|
|
264
|
-
self.
|
|
248
|
+
self.estimatedRegionsStats?.fetchSizeLimit ||
|
|
265
249
|
(getConf(self, 'fetchSizeLimit') as number)
|
|
266
250
|
)
|
|
267
251
|
},
|
|
@@ -273,95 +257,68 @@ function stateModelFactory() {
|
|
|
273
257
|
setMessage(message: string) {
|
|
274
258
|
self.message = message
|
|
275
259
|
},
|
|
276
|
-
|
|
260
|
+
}))
|
|
261
|
+
.actions(self => ({
|
|
277
262
|
afterAttach() {
|
|
278
263
|
// watch the parent's blocks to update our block state when they change,
|
|
279
264
|
// then we recreate the blocks on our own model (creating and deleting to
|
|
280
265
|
// match the parent blocks)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
self
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
266
|
+
addDisposer(
|
|
267
|
+
self,
|
|
268
|
+
autorun(() => {
|
|
269
|
+
const blocksPresent: { [key: string]: boolean } = {}
|
|
270
|
+
const view = getContainingView(self) as LGV
|
|
271
|
+
if (view.initialized) {
|
|
272
|
+
self.blockDefinitions.contentBlocks.forEach(block => {
|
|
273
|
+
blocksPresent[block.key] = true
|
|
274
|
+
if (!self.blockState.has(block.key)) {
|
|
275
|
+
this.addBlock(block.key, block)
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
self.blockState.forEach((_, key) => {
|
|
279
|
+
if (!blocksPresent[key]) {
|
|
280
|
+
this.deleteBlock(key)
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
}),
|
|
285
|
+
)
|
|
300
286
|
},
|
|
301
287
|
|
|
302
288
|
/**
|
|
303
289
|
* #action
|
|
304
290
|
*/
|
|
305
|
-
estimateRegionsStats(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (self.estimatedRegionStatsP) {
|
|
314
|
-
return self.estimatedRegionStatsP
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const { rpcManager } = getSession(self)
|
|
318
|
-
const { adapterConfig } = self
|
|
319
|
-
if (!adapterConfig) {
|
|
320
|
-
// A track extending the base track might not have an adapter config
|
|
321
|
-
// e.g. Apollo tracks don't use adapters
|
|
322
|
-
return Promise.resolve({})
|
|
323
|
-
}
|
|
324
|
-
const sessionId = getRpcSessionId(self)
|
|
325
|
-
|
|
326
|
-
const params = {
|
|
327
|
-
sessionId,
|
|
328
|
-
regions,
|
|
329
|
-
adapterConfig,
|
|
330
|
-
statusCallback: (message: string) => {
|
|
331
|
-
if (isAlive(self)) {
|
|
332
|
-
this.setMessage(message)
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
...opts,
|
|
291
|
+
async estimateRegionsStats() {
|
|
292
|
+
if (!self.estimatedRegionsStatsP) {
|
|
293
|
+
self.estimatedRegionsStatsP = estimateRegionsStatsPre(self).catch(
|
|
294
|
+
e => {
|
|
295
|
+
this.setRegionsStatsP(undefined)
|
|
296
|
+
throw e
|
|
297
|
+
},
|
|
298
|
+
)
|
|
336
299
|
}
|
|
337
|
-
|
|
338
|
-
self.estimatedRegionStatsP = rpcManager
|
|
339
|
-
.call(sessionId, 'CoreEstimateRegionStats', params)
|
|
340
|
-
.catch(e => {
|
|
341
|
-
this.setRegionStatsP(undefined)
|
|
342
|
-
throw e
|
|
343
|
-
}) as Promise<Stats>
|
|
344
|
-
|
|
345
|
-
return self.estimatedRegionStatsP
|
|
300
|
+
return self.estimatedRegionsStatsP
|
|
346
301
|
},
|
|
302
|
+
|
|
347
303
|
/**
|
|
348
304
|
* #action
|
|
349
305
|
*/
|
|
350
|
-
|
|
351
|
-
self.
|
|
306
|
+
setRegionsStatsP(arg: any) {
|
|
307
|
+
self.estimatedRegionsStatsP = arg
|
|
352
308
|
},
|
|
309
|
+
|
|
353
310
|
/**
|
|
354
311
|
* #action
|
|
355
312
|
*/
|
|
356
|
-
|
|
357
|
-
self.
|
|
313
|
+
setRegionsStats(estimatedRegionsStats?: Stats) {
|
|
314
|
+
self.estimatedRegionsStats = estimatedRegionsStats
|
|
358
315
|
},
|
|
359
316
|
/**
|
|
360
317
|
* #action
|
|
361
318
|
*/
|
|
362
|
-
|
|
363
|
-
self.
|
|
364
|
-
self.
|
|
319
|
+
clearRegionsStats() {
|
|
320
|
+
self.estimatedRegionsStatsP = undefined
|
|
321
|
+
self.estimatedRegionsStats = undefined
|
|
365
322
|
},
|
|
366
323
|
/**
|
|
367
324
|
* #action
|
|
@@ -390,9 +347,9 @@ function stateModelFactory() {
|
|
|
390
347
|
/**
|
|
391
348
|
* #action
|
|
392
349
|
*/
|
|
393
|
-
updateStatsLimit(stats
|
|
350
|
+
updateStatsLimit(stats?: Stats) {
|
|
394
351
|
const view = getContainingView(self) as LGV
|
|
395
|
-
if (stats
|
|
352
|
+
if (stats?.bytes) {
|
|
396
353
|
self.userByteSizeLimit = stats.bytes
|
|
397
354
|
} else {
|
|
398
355
|
self.userBpPerPxLimit = view.bpPerPx
|
|
@@ -484,13 +441,11 @@ function stateModelFactory() {
|
|
|
484
441
|
if (!self.estimatedStatsReady || view.dynamicBlocks.totalBp < 20_000) {
|
|
485
442
|
return false
|
|
486
443
|
}
|
|
487
|
-
const bpLimitOrDensity = self.userBpPerPxLimit
|
|
488
|
-
? view.bpPerPx > self.userBpPerPxLimit
|
|
489
|
-
: self.currentFeatureScreenDensity > self.maxFeatureScreenDensity
|
|
490
|
-
|
|
491
444
|
return (
|
|
492
445
|
self.currentBytesRequested > self.maxAllowableBytes ||
|
|
493
|
-
|
|
446
|
+
(self.userBpPerPxLimit
|
|
447
|
+
? view.bpPerPx > self.userBpPerPxLimit
|
|
448
|
+
: self.currentFeatureScreenDensity > self.maxFeatureScreenDensity)
|
|
494
449
|
)
|
|
495
450
|
},
|
|
496
451
|
|
|
@@ -517,7 +472,6 @@ function stateModelFactory() {
|
|
|
517
472
|
*/
|
|
518
473
|
async reload() {
|
|
519
474
|
self.setError()
|
|
520
|
-
const aborter = new AbortController()
|
|
521
475
|
const view = getContainingView(self) as LGV
|
|
522
476
|
|
|
523
477
|
// extra check for contentBlocks.length
|
|
@@ -527,84 +481,70 @@ function stateModelFactory() {
|
|
|
527
481
|
}
|
|
528
482
|
|
|
529
483
|
try {
|
|
530
|
-
|
|
531
|
-
view.staticBlocks.contentBlocks,
|
|
532
|
-
{ signal: aborter.signal },
|
|
533
|
-
)
|
|
534
|
-
const estimatedRegionStats = await self.estimatedRegionStatsP
|
|
484
|
+
const estimatedRegionsStats = await self.estimateRegionsStats()
|
|
535
485
|
|
|
536
486
|
if (isAlive(self)) {
|
|
537
|
-
self.
|
|
487
|
+
self.setRegionsStats(estimatedRegionsStats)
|
|
538
488
|
superReload()
|
|
539
|
-
} else {
|
|
540
|
-
return
|
|
541
489
|
}
|
|
542
490
|
} catch (e) {
|
|
543
491
|
console.error(e)
|
|
544
492
|
self.setError(e)
|
|
545
493
|
}
|
|
546
494
|
},
|
|
547
|
-
afterAttach() {
|
|
548
|
-
// this autorun performs stats estimation
|
|
549
|
-
//
|
|
550
|
-
// the chain of events calls estimateRegionsStats against the data
|
|
551
|
-
// adapter which by default uses featureDensity, but can also respond
|
|
552
|
-
// with a byte size estimate and fetch size limit (data adapter can
|
|
553
|
-
// define what is too much data)
|
|
554
|
-
addDisposer(
|
|
555
|
-
self,
|
|
556
|
-
autorun(
|
|
557
|
-
async () => {
|
|
558
|
-
try {
|
|
559
|
-
const aborter = new AbortController()
|
|
560
|
-
const view = getContainingView(self) as LGV
|
|
561
|
-
|
|
562
|
-
// extra check for contentBlocks.length
|
|
563
|
-
// https://github.com/GMOD/jbrowse-components/issues/2694
|
|
564
|
-
if (
|
|
565
|
-
!view.initialized ||
|
|
566
|
-
!view.staticBlocks.contentBlocks.length
|
|
567
|
-
) {
|
|
568
|
-
return
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// don't re-estimate featureDensity even if zoom level changes,
|
|
572
|
-
// jbrowse1-style assume it's sort of representative
|
|
573
|
-
if (self.estimatedRegionStats?.featureDensity !== undefined) {
|
|
574
|
-
self.setCurrBpPerPx(view.bpPerPx)
|
|
575
|
-
return
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// we estimate stats once at a given zoom level
|
|
579
|
-
if (view.bpPerPx === self.currBpPerPx) {
|
|
580
|
-
return
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
self.clearRegionStats()
|
|
584
|
-
self.setCurrBpPerPx(view.bpPerPx)
|
|
585
|
-
const statsP = self.estimateRegionsStats(
|
|
586
|
-
view.staticBlocks.contentBlocks,
|
|
587
|
-
{ signal: aborter.signal },
|
|
588
|
-
)
|
|
589
|
-
self.setRegionStatsP(statsP)
|
|
590
|
-
const estimatedRegionStats = await statsP
|
|
591
|
-
|
|
592
|
-
if (isAlive(self)) {
|
|
593
|
-
self.setRegionStats(estimatedRegionStats)
|
|
594
|
-
}
|
|
595
|
-
} catch (e) {
|
|
596
|
-
if (!isAbortException(e) && isAlive(self)) {
|
|
597
|
-
console.error(e)
|
|
598
|
-
self.setError(e)
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
},
|
|
602
|
-
{ delay: 500 },
|
|
603
|
-
),
|
|
604
|
-
)
|
|
605
|
-
},
|
|
606
495
|
}
|
|
607
496
|
})
|
|
497
|
+
.actions(self => ({
|
|
498
|
+
afterAttach() {
|
|
499
|
+
// this autorun performs stats estimation
|
|
500
|
+
//
|
|
501
|
+
// the chain of events calls estimateRegionsStats against the data
|
|
502
|
+
// adapter which by default uses featureDensity, but can also respond
|
|
503
|
+
// with a byte size estimate and fetch size limit (data adapter can
|
|
504
|
+
// define what is too much data)
|
|
505
|
+
addDisposer(
|
|
506
|
+
self,
|
|
507
|
+
autorun(async () => {
|
|
508
|
+
try {
|
|
509
|
+
const view = getContainingView(self) as LGV
|
|
510
|
+
|
|
511
|
+
// extra check for contentBlocks.length
|
|
512
|
+
// https://github.com/GMOD/jbrowse-components/issues/2694
|
|
513
|
+
if (
|
|
514
|
+
!view.initialized ||
|
|
515
|
+
!view.staticBlocks.contentBlocks.length
|
|
516
|
+
) {
|
|
517
|
+
return
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// don't re-estimate featureDensity even if zoom level changes,
|
|
521
|
+
// jbrowse1-style assume it's sort of representative
|
|
522
|
+
if (self.estimatedRegionsStats?.featureDensity !== undefined) {
|
|
523
|
+
self.setCurrBpPerPx(view.bpPerPx)
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// we estimate stats once at a given zoom level
|
|
528
|
+
if (view.bpPerPx === self.currBpPerPx) {
|
|
529
|
+
return
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
self.clearRegionsStats()
|
|
533
|
+
self.setCurrBpPerPx(view.bpPerPx)
|
|
534
|
+
const estimatedRegionsStats = await self.estimateRegionsStats()
|
|
535
|
+
if (isAlive(self)) {
|
|
536
|
+
self.setRegionsStats(estimatedRegionsStats)
|
|
537
|
+
}
|
|
538
|
+
} catch (e) {
|
|
539
|
+
if (!isAbortException(e) && isAlive(self)) {
|
|
540
|
+
console.error(e)
|
|
541
|
+
self.setError(e)
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}),
|
|
545
|
+
)
|
|
546
|
+
},
|
|
547
|
+
}))
|
|
608
548
|
.views(self => ({
|
|
609
549
|
/**
|
|
610
550
|
* #method
|
|
@@ -622,8 +562,7 @@ function stateModelFactory() {
|
|
|
622
562
|
* react node allows user to force load at current setting
|
|
623
563
|
*/
|
|
624
564
|
regionCannotBeRendered(_region: Region) {
|
|
625
|
-
|
|
626
|
-
return regionTooLarge ? <TooLargeMessage model={self} /> : null
|
|
565
|
+
return self.regionTooLarge ? <TooLargeMessage model={self} /> : null
|
|
627
566
|
},
|
|
628
567
|
|
|
629
568
|
/**
|
|
@@ -636,20 +575,22 @@ function stateModelFactory() {
|
|
|
636
575
|
/**
|
|
637
576
|
* #method
|
|
638
577
|
*/
|
|
639
|
-
contextMenuItems() {
|
|
640
|
-
return
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
578
|
+
contextMenuItems(): MenuItem[] {
|
|
579
|
+
return [
|
|
580
|
+
...(self.contextMenuFeature
|
|
581
|
+
? [
|
|
582
|
+
{
|
|
583
|
+
label: 'Open feature details',
|
|
584
|
+
icon: MenuOpenIcon,
|
|
585
|
+
onClick: () => {
|
|
586
|
+
if (self.contextMenuFeature) {
|
|
587
|
+
self.selectFeature(self.contextMenuFeature)
|
|
588
|
+
}
|
|
589
|
+
},
|
|
649
590
|
},
|
|
650
|
-
|
|
651
|
-
]
|
|
652
|
-
|
|
591
|
+
]
|
|
592
|
+
: []),
|
|
593
|
+
]
|
|
653
594
|
},
|
|
654
595
|
/**
|
|
655
596
|
* #method
|
|
@@ -659,7 +600,7 @@ function stateModelFactory() {
|
|
|
659
600
|
return {
|
|
660
601
|
...getParentRenderProps(self),
|
|
661
602
|
notReady:
|
|
662
|
-
self.currBpPerPx !== view.bpPerPx || !self.
|
|
603
|
+
self.currBpPerPx !== view.bpPerPx || !self.estimatedRegionsStats,
|
|
663
604
|
rpcDriverName: self.rpcDriverName,
|
|
664
605
|
displayModel: self,
|
|
665
606
|
onFeatureClick(_: unknown, featureId?: string) {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { Stats } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
2
|
+
import { getContainingView, getSession } from '@jbrowse/core/util'
|
|
3
|
+
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
|
|
4
|
+
import { IAnyStateTreeNode, isAlive } from 'mobx-state-tree'
|
|
5
|
+
import { LinearGenomeViewModel } from '../../LinearGenomeView'
|
|
6
|
+
|
|
1
7
|
export interface RenderProps {
|
|
2
8
|
rendererType: any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
3
9
|
renderArgs: { [key: string]: any } // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -22,3 +28,34 @@ export function getDisplayStr(totalBytes: number) {
|
|
|
22
28
|
}
|
|
23
29
|
return displayBp
|
|
24
30
|
}
|
|
31
|
+
|
|
32
|
+
// stabilize clipid under test for snapshot
|
|
33
|
+
export function getId(id: string, index: number) {
|
|
34
|
+
const isJest = typeof jest === 'undefined'
|
|
35
|
+
return `clip-${isJest ? id : 'jest'}-${index}`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function estimateRegionsStatsPre(self: IAnyStateTreeNode) {
|
|
39
|
+
const view = getContainingView(self) as LinearGenomeViewModel
|
|
40
|
+
const regions = view.staticBlocks.contentBlocks
|
|
41
|
+
|
|
42
|
+
const { rpcManager } = getSession(self)
|
|
43
|
+
const { adapterConfig } = self
|
|
44
|
+
if (!adapterConfig) {
|
|
45
|
+
// A track extending the base track might not have an adapter config
|
|
46
|
+
// e.g. Apollo tracks don't use adapters
|
|
47
|
+
return {}
|
|
48
|
+
}
|
|
49
|
+
const sessionId = getRpcSessionId(self)
|
|
50
|
+
|
|
51
|
+
return rpcManager.call(sessionId, 'CoreEstimateRegionStats', {
|
|
52
|
+
sessionId,
|
|
53
|
+
regions,
|
|
54
|
+
adapterConfig,
|
|
55
|
+
statusCallback: (message: string) => {
|
|
56
|
+
if (isAlive(self)) {
|
|
57
|
+
self.setMessage(message)
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
}) as Promise<Stats>
|
|
61
|
+
}
|
|
@@ -12,6 +12,7 @@ export default (pluginManager: PluginManager) => {
|
|
|
12
12
|
return new DisplayType({
|
|
13
13
|
name: 'LinearBareDisplay',
|
|
14
14
|
configSchema,
|
|
15
|
+
displayName: 'Bare feature display',
|
|
15
16
|
stateModel: stateModelFactory(configSchema),
|
|
16
17
|
trackType: 'BasicTrack',
|
|
17
18
|
viewType: 'LinearGenomeView',
|
|
@@ -10,15 +10,11 @@ import {
|
|
|
10
10
|
} from '@mui/material'
|
|
11
11
|
import { makeStyles } from 'tss-react/mui'
|
|
12
12
|
|
|
13
|
-
const useStyles = makeStyles()(
|
|
13
|
+
const useStyles = makeStyles()({
|
|
14
14
|
root: {
|
|
15
15
|
width: 500,
|
|
16
16
|
},
|
|
17
|
-
|
|
18
|
-
field: {
|
|
19
|
-
margin: theme.spacing(2),
|
|
20
|
-
},
|
|
21
|
-
}))
|
|
17
|
+
})
|
|
22
18
|
|
|
23
19
|
function SetMaxHeightDlg({
|
|
24
20
|
model,
|
|
@@ -26,7 +22,7 @@ function SetMaxHeightDlg({
|
|
|
26
22
|
}: {
|
|
27
23
|
model: {
|
|
28
24
|
maxHeight?: number
|
|
29
|
-
setMaxHeight:
|
|
25
|
+
setMaxHeight: (arg?: number) => void
|
|
30
26
|
}
|
|
31
27
|
handleClose: () => void
|
|
32
28
|
}) {
|
|
@@ -11,7 +11,7 @@ export default (pluginManager: PluginManager) => {
|
|
|
11
11
|
const config = configSchema(pluginManager)
|
|
12
12
|
return new DisplayType({
|
|
13
13
|
name: 'LinearBasicDisplay',
|
|
14
|
-
displayName: 'Basic
|
|
14
|
+
displayName: 'Basic feature display',
|
|
15
15
|
configSchema: config,
|
|
16
16
|
stateModel: modelFactory(config),
|
|
17
17
|
trackType: 'FeatureTrack',
|
|
@@ -30,13 +30,12 @@ export interface Option {
|
|
|
30
30
|
function aggregateResults(results: BaseResult[]) {
|
|
31
31
|
const m: { [key: string]: BaseResult[] } = {}
|
|
32
32
|
|
|
33
|
-
for (
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
m[d] = []
|
|
33
|
+
for (const result of results) {
|
|
34
|
+
const displayString = result.getDisplayString()
|
|
35
|
+
if (!m[displayString]) {
|
|
36
|
+
m[displayString] = []
|
|
38
37
|
}
|
|
39
|
-
m[
|
|
38
|
+
m[displayString].push(result)
|
|
40
39
|
}
|
|
41
40
|
return m
|
|
42
41
|
}
|
|
@@ -63,13 +62,11 @@ function getDeduplicatedResult(results: BaseResult[]) {
|
|
|
63
62
|
// because they do not have a matchedObject. the trix search results already
|
|
64
63
|
// filter so don't need re-filtering
|
|
65
64
|
function filterOptions(options: Option[], searchQuery: string) {
|
|
66
|
-
return options.filter(
|
|
67
|
-
|
|
68
|
-
return (
|
|
65
|
+
return options.filter(
|
|
66
|
+
({ result }) =>
|
|
69
67
|
result.getLabel().toLowerCase().includes(searchQuery) ||
|
|
70
|
-
result.matchedObject
|
|
71
|
-
|
|
72
|
-
})
|
|
68
|
+
result.matchedObject,
|
|
69
|
+
)
|
|
73
70
|
}
|
|
74
71
|
|
|
75
72
|
function RefNameAutocomplete({
|
|
@@ -109,18 +106,17 @@ function RefNameAutocomplete({
|
|
|
109
106
|
const assembly = assemblyName ? assemblyManager.get(assemblyName) : undefined
|
|
110
107
|
const { coarseVisibleLocStrings, hasDisplayedRegions } = model
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
const regions = assembly?.regions || []
|
|
109
|
+
const regions = assembly?.regions
|
|
114
110
|
|
|
115
111
|
const options = useMemo(
|
|
116
112
|
() =>
|
|
117
|
-
regions
|
|
113
|
+
regions?.map(option => ({
|
|
118
114
|
result: new RefSequenceResult({
|
|
119
115
|
refName: option.refName,
|
|
120
116
|
label: option.refName,
|
|
121
117
|
matchedAttribute: 'refName',
|
|
122
118
|
}),
|
|
123
|
-
})),
|
|
119
|
+
})) || [],
|
|
124
120
|
[regions],
|
|
125
121
|
)
|
|
126
122
|
|
|
@@ -157,8 +153,10 @@ function RefNameAutocomplete({
|
|
|
157
153
|
|
|
158
154
|
// heuristic, text width + icon width + 50 accommodates help icon and search
|
|
159
155
|
// icon
|
|
160
|
-
const
|
|
161
|
-
|
|
156
|
+
const width = Math.min(
|
|
157
|
+
Math.max(measureText(inputBoxVal, 16) + 50, minWidth),
|
|
158
|
+
maxWidth,
|
|
159
|
+
)
|
|
162
160
|
|
|
163
161
|
// notes on implementation:
|
|
164
162
|
// The selectOnFocus setting helps highlight the field when clicked
|