jbrowse-plugin-graphgenomeviewer 7.4.4 → 7.5.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/dist/chunks/{GbzBaseSyntenyAdapter-A5MNPCKX.js → GbzBaseSyntenyAdapter-4KVWVUN6.js} +2 -2
- package/dist/chunks/{GbzBaseSyntenyAdapter-A5MNPCKX.js.map → GbzBaseSyntenyAdapter-4KVWVUN6.js.map} +1 -1
- package/dist/chunks/{GraphGenomeView-SEAPDRXS.js → GraphGenomeView-KI4FQ5A7.js} +2 -2
- package/dist/chunks/GraphTrackSettingsDialog-UQNMDXTE.js +4 -0
- package/dist/chunks/GraphTrackSettingsDialog-UQNMDXTE.js.map +7 -0
- package/dist/chunks/{HighlightColorDialog-QAYWLO5I.js → HighlightColorDialog-D4RCUCUF.js} +2 -2
- package/dist/chunks/LinearGraphDisplay-RPZUZSM2.js +2 -0
- package/dist/chunks/{LinearGraphDisplay-ZC4BQ27J.js.map → LinearGraphDisplay-RPZUZSM2.js.map} +2 -2
- package/dist/chunks/{MinigraphBubbleAdapter-3QM4XV47.js → MinigraphBubbleAdapter-TJYI7KEN.js} +2 -2
- package/dist/chunks/RgfaTabixAdapter-5ZMJLFZP.js +2 -0
- package/dist/chunks/RgfaTabixAdapter-5ZMJLFZP.js.map +7 -0
- package/dist/chunks/{WalkFormsAdapter-FT2SW4H6.js → WalkFormsAdapter-CAJOWOEX.js} +2 -2
- package/dist/chunks/WalkTabixSyntenyAdapter-VI3KA2GX.js +2 -0
- package/dist/chunks/{WalkTabixSyntenyAdapter-3WRVNDLB.js.map → WalkTabixSyntenyAdapter-VI3KA2GX.js.map} +2 -2
- package/dist/chunks/{chunk-6WUWQX2P.js → chunk-22ANI2KL.js} +2 -2
- package/dist/chunks/{chunk-6WUWQX2P.js.map → chunk-22ANI2KL.js.map} +1 -1
- package/dist/chunks/chunk-42KD4AVY.js +2 -0
- package/dist/chunks/{chunk-HCDKP3IP.js.map → chunk-42KD4AVY.js.map} +4 -4
- package/dist/chunks/chunk-4NIWMRKH.js +2 -0
- package/dist/chunks/{chunk-S333HOG5.js.map → chunk-4NIWMRKH.js.map} +4 -4
- package/dist/chunks/chunk-BCOXRUNQ.js +3 -0
- package/dist/chunks/chunk-BCOXRUNQ.js.map +7 -0
- package/dist/chunks/chunk-DAPI7M4M.js +4 -0
- package/dist/chunks/chunk-DAPI7M4M.js.map +7 -0
- package/dist/chunks/{chunk-G53BI7VK.js → chunk-DEWZOJSC.js} +2 -2
- package/dist/chunks/{chunk-G53BI7VK.js.map → chunk-DEWZOJSC.js.map} +1 -1
- package/dist/chunks/{chunk-6SWXMRAV.js → chunk-FCMXEFJA.js} +1 -1
- package/dist/chunks/{chunk-6SWXMRAV.js.map → chunk-FCMXEFJA.js.map} +1 -1
- package/dist/chunks/{chunk-P3FBI5ML.js → chunk-GYY6LF5L.js} +1 -1
- package/dist/chunks/{chunk-P3FBI5ML.js.map → chunk-GYY6LF5L.js.map} +1 -1
- package/dist/chunks/{chunk-HF4H6LQI.js → chunk-HHMJNAA3.js} +2 -2
- package/dist/chunks/{chunk-XXBE4IFZ.js → chunk-HTTONSO4.js} +2 -2
- package/dist/chunks/{chunk-XXBE4IFZ.js.map → chunk-HTTONSO4.js.map} +1 -1
- package/dist/chunks/{chunk-MUONTMYZ.js → chunk-K4JL57NR.js} +2 -2
- package/dist/chunks/chunk-KOC23YNN.js +4 -0
- package/dist/chunks/chunk-KOC23YNN.js.map +7 -0
- package/dist/chunks/{chunk-OJWBLPPH.js → chunk-LTNFVL2F.js} +2 -2
- package/dist/chunks/chunk-OZEKEYT3.js +2 -0
- package/dist/chunks/{chunk-6VNU7SD4.js.map → chunk-OZEKEYT3.js.map} +4 -4
- package/dist/chunks/{chunk-KIYRMZWP.js → chunk-POUV7OPY.js} +2 -2
- package/dist/chunks/{chunk-KIYRMZWP.js.map → chunk-POUV7OPY.js.map} +1 -1
- package/dist/chunks/{chunk-LE7FZM6Y.js → chunk-SPL3UO2D.js} +2 -2
- package/dist/chunks/{chunk-LE7FZM6Y.js.map → chunk-SPL3UO2D.js.map} +1 -1
- package/dist/chunks/chunk-UR2MJJUP.js +2 -0
- package/dist/chunks/chunk-VTMJQFN5.js +2 -0
- package/dist/chunks/chunk-VTMJQFN5.js.map +7 -0
- package/dist/chunks/{chunk-2KVE7YAS.js → chunk-XZGYJ3JC.js} +1 -1
- package/dist/chunks/{chunk-2KVE7YAS.js.map → chunk-XZGYJ3JC.js.map} +1 -1
- package/dist/chunks/{chunk-7RJMG6I2.js → chunk-Z3IKZAYC.js} +1 -1
- package/dist/chunks/{chunk-7RJMG6I2.js.map → chunk-Z3IKZAYC.js.map} +1 -1
- package/dist/chunks/{chunk-FTXDTU27.js → chunk-ZV5FNOHS.js} +3 -3
- package/dist/chunks/{chunk-FTXDTU27.js.map → chunk-ZV5FNOHS.js.map} +2 -2
- package/dist/chunks/{renderSvg-ZIUOPWJQ.js → renderSvg-3PPAEMBT.js} +2 -2
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +4 -3
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +4 -4
- package/package.json +2 -2
- package/src/GraphComputeLayout.test.ts +24 -0
- package/src/GraphComputeLayout.ts +4 -2
- package/src/GraphGenomeView/components/GraphSettingsDialog.tsx +16 -0
- package/src/GraphGenomeView/components/WalkRowsOverlay.tsx +83 -0
- package/src/GraphGenomeView/components/usePaneGestures.test.tsx +82 -5
- package/src/GraphGenomeView/components/usePaneGestures.ts +10 -4
- package/src/GraphGenomeView/graphLayers.ts +14 -1
- package/src/GraphGenomeView/legendSections.ts +32 -0
- package/src/GraphGenomeView/model.test.ts +292 -4
- package/src/GraphGenomeView/model.ts +3 -10
- package/src/GraphGenomeView/nodeSize.test.ts +21 -5
- package/src/GraphGenomeView/nodeSize.ts +24 -8
- package/src/GraphGenomeView/pane/fitViews.ts +5 -1
- package/src/GraphGenomeView/pane/graphViews.ts +96 -16
- package/src/GraphGenomeView/pane/launchActions.ts +55 -11
- package/src/GraphGenomeView/pane/launchViews.ts +46 -0
- package/src/GraphGenomeView/pane/loadActions.ts +85 -25
- package/src/GraphGenomeView/pane/morph.test.ts +25 -1
- package/src/GraphGenomeView/pane/morph.ts +3 -1
- package/src/GraphGenomeView/pane/paneBase.ts +5 -0
- package/src/GraphGenomeView/pane/renderingActions.ts +61 -2
- package/src/GraphGenomeView/pane/settingActions.ts +62 -4
- package/src/GraphGenomeView/pane/walkRowViews.ts +17 -6
- package/src/GraphGenomeView/tubeMapPanels.ts +2 -1
- package/src/GraphGenomeView/viewModel.ts +4 -6
- package/src/GraphGenomeView/walkRowBlocks.ts +50 -0
- package/src/LinearGraphDisplay/LinearGraphDisplay.test.ts +35 -5
- package/src/LinearGraphDisplay/components/GraphStatusChrome.test.tsx +18 -0
- package/src/LinearGraphDisplay/components/GraphStatusChrome.tsx +4 -2
- package/src/LinearGraphDisplay/components/SubgraphHaplotypesField.test.tsx +41 -1
- package/src/LinearGraphDisplay/components/SubgraphHaplotypesField.tsx +33 -19
- package/src/LinearGraphDisplay/configSchema.ts +4 -3
- package/src/RgfaTabixAdapter/RgfaTabixAdapter.ts +2 -9
- package/src/WalkTabixSyntenyAdapter/WalkTabixSyntenyAdapter.ts +3 -6
- package/src/hoverSync/graphViewHighlights.test.ts +53 -0
- package/src/hoverSync/graphViewHighlights.ts +25 -0
- package/src/hoverSync/lgvHover.test.ts +100 -1
- package/src/hoverSync/lgvHover.ts +77 -6
- package/src/launchFromGraph/contributors.test.ts +52 -0
- package/src/launchFromGraph/contributors.ts +105 -1
- package/src/launchFromGraph/launchFromGraph.ts +2 -2
- package/src/panSNTabix.test.ts +28 -0
- package/src/panSNTabix.ts +26 -8
- package/src/version.ts +1 -1
- package/dist/chunks/GraphTrackSettingsDialog-EVIIGC5G.js +0 -6
- package/dist/chunks/GraphTrackSettingsDialog-EVIIGC5G.js.map +0 -7
- package/dist/chunks/LinearGraphDisplay-ZC4BQ27J.js +0 -2
- package/dist/chunks/RgfaTabixAdapter-XSOIEWBK.js +0 -2
- package/dist/chunks/RgfaTabixAdapter-XSOIEWBK.js.map +0 -7
- package/dist/chunks/WalkTabixSyntenyAdapter-3WRVNDLB.js +0 -2
- package/dist/chunks/chunk-6VNU7SD4.js +0 -2
- package/dist/chunks/chunk-GRTN25B6.js +0 -3
- package/dist/chunks/chunk-GRTN25B6.js.map +0 -7
- package/dist/chunks/chunk-HCDKP3IP.js +0 -2
- package/dist/chunks/chunk-LE43I4PT.js +0 -4
- package/dist/chunks/chunk-LE43I4PT.js.map +0 -7
- package/dist/chunks/chunk-QV7SSOMJ.js +0 -2
- package/dist/chunks/chunk-S333HOG5.js +0 -2
- package/dist/chunks/chunk-VZQ6VOUP.js +0 -2
- package/dist/chunks/chunk-VZQ6VOUP.js.map +0 -7
- package/dist/chunks/chunk-WLRRI35A.js +0 -3
- package/dist/chunks/chunk-WLRRI35A.js.map +0 -7
- package/src/launchFromGraph/launchTracks.test.ts +0 -95
- package/src/launchFromGraph/launchTracks.ts +0 -49
- /package/dist/chunks/{GraphGenomeView-SEAPDRXS.js.map → GraphGenomeView-KI4FQ5A7.js.map} +0 -0
- /package/dist/chunks/{HighlightColorDialog-QAYWLO5I.js.map → HighlightColorDialog-D4RCUCUF.js.map} +0 -0
- /package/dist/chunks/{MinigraphBubbleAdapter-3QM4XV47.js.map → MinigraphBubbleAdapter-TJYI7KEN.js.map} +0 -0
- /package/dist/chunks/{WalkFormsAdapter-FT2SW4H6.js.map → WalkFormsAdapter-CAJOWOEX.js.map} +0 -0
- /package/dist/chunks/{chunk-HF4H6LQI.js.map → chunk-HHMJNAA3.js.map} +0 -0
- /package/dist/chunks/{chunk-MUONTMYZ.js.map → chunk-K4JL57NR.js.map} +0 -0
- /package/dist/chunks/{chunk-OJWBLPPH.js.map → chunk-LTNFVL2F.js.map} +0 -0
- /package/dist/chunks/{chunk-QV7SSOMJ.js.map → chunk-UR2MJJUP.js.map} +0 -0
- /package/dist/chunks/{renderSvg-ZIUOPWJQ.js.map → renderSvg-3PPAEMBT.js.map} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sameBubble } from '@jbrowse/bandage-core/bubbles/bubbleLine'
|
|
1
2
|
import { contains } from '@jbrowse/bandage-core/viewport'
|
|
2
3
|
import { getSession } from '@jbrowse/core/util'
|
|
3
4
|
import { addDisposer } from '@jbrowse/mobx-state-tree'
|
|
@@ -11,11 +12,16 @@ import {
|
|
|
11
12
|
paneViewportOf,
|
|
12
13
|
} from './paneBase'
|
|
13
14
|
import {
|
|
15
|
+
bubbleForLgvHover,
|
|
16
|
+
hoverFromPair,
|
|
14
17
|
hoverInRegion,
|
|
15
18
|
nodeForLgvHover,
|
|
16
19
|
readLgvHover,
|
|
20
|
+
readSelectedFeature,
|
|
17
21
|
} from '../../hoverSync/lgvHover'
|
|
18
22
|
import { walksForRow } from '../../hoverSync/rowWalks'
|
|
23
|
+
import { isRecord } from '../../isRecord'
|
|
24
|
+
import { withRows } from '../../launchFromGraph/linearViewTarget'
|
|
19
25
|
import { trackedDpr } from '../trackedDpr'
|
|
20
26
|
|
|
21
27
|
import type { LayoutEngineKind } from '@jbrowse/bandage-core/layoutEngines'
|
|
@@ -88,6 +94,23 @@ export const withRenderingActions = withLoadActions
|
|
|
88
94
|
},
|
|
89
95
|
),
|
|
90
96
|
)
|
|
97
|
+
// and the haplotypes' own genes on their nodes off the reference
|
|
98
|
+
addDisposer(
|
|
99
|
+
self,
|
|
100
|
+
reaction(
|
|
101
|
+
() => ({
|
|
102
|
+
graph: self.graph,
|
|
103
|
+
reads: JSON.stringify(self.ownGeneReads),
|
|
104
|
+
}),
|
|
105
|
+
() => {
|
|
106
|
+
void self.loadOwnGenes()
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
fireImmediately: true,
|
|
110
|
+
equals: (a, b) => a.graph === b.graph && a.reads === b.reads,
|
|
111
|
+
},
|
|
112
|
+
),
|
|
113
|
+
)
|
|
91
114
|
// Walk rows and tube map panels read the source track's samples
|
|
92
115
|
// TSV, to group by its columns, again for a graph after a dropped one
|
|
93
116
|
addDisposer(
|
|
@@ -145,8 +168,16 @@ export const withRenderingActions = withLoadActions
|
|
|
145
168
|
untracked(() => {
|
|
146
169
|
const region = self.graphRegion
|
|
147
170
|
const graph = self.graph
|
|
171
|
+
const liveViewIds = new Set(
|
|
172
|
+
withRows([...getSession(self).views]).flatMap(v =>
|
|
173
|
+
isRecord(v) && typeof v.id === 'string' ? [v.id] : [],
|
|
174
|
+
),
|
|
175
|
+
)
|
|
148
176
|
const inRegion =
|
|
149
|
-
hover &&
|
|
177
|
+
hover &&
|
|
178
|
+
region &&
|
|
179
|
+
hoverInRegion(hover, region) &&
|
|
180
|
+
hoverFromPair(hover, self.pairedViewId, liveViewIds)
|
|
150
181
|
? hover
|
|
151
182
|
: undefined
|
|
152
183
|
// a row naming a walk is the hover, not the node at its bp,
|
|
@@ -155,9 +186,14 @@ export const withRenderingActions = withLoadActions
|
|
|
155
186
|
inRegion?.featureName && graph?.paths
|
|
156
187
|
? walksForRow(inRegion.featureName, graph.paths)
|
|
157
188
|
: []
|
|
189
|
+
// a bubble track's feature, or a graph-derived VCF record,
|
|
190
|
+
// names a whole bubble, which lights whatever the setting
|
|
191
|
+
const bubble =
|
|
192
|
+
inRegion && bubbleForLgvHover(inRegion, self.bubbles)
|
|
158
193
|
if (region && graph && !self.pointerInPane) {
|
|
194
|
+
self.setHoveredBubble(bubble ?? null)
|
|
159
195
|
self.setHoveredNode(
|
|
160
|
-
inRegion && lightsNode && rowWalks.length === 0
|
|
196
|
+
inRegion && lightsNode && rowWalks.length === 0 && !bubble
|
|
161
197
|
? nodeForLgvHover({ hover: inRegion, nodes: graph.nodes })
|
|
162
198
|
: null,
|
|
163
199
|
)
|
|
@@ -167,6 +203,29 @@ export const withRenderingActions = withLoadActions
|
|
|
167
203
|
}),
|
|
168
204
|
)
|
|
169
205
|
|
|
206
|
+
// A click on a bubble feature or graph-derived variant in a linear
|
|
207
|
+
// view opens its bubble here, as clicking its label would
|
|
208
|
+
addDisposer(
|
|
209
|
+
self,
|
|
210
|
+
reaction(
|
|
211
|
+
() => readSelectedFeature(getSession(self).selection),
|
|
212
|
+
selected => {
|
|
213
|
+
const region = self.graphRegion
|
|
214
|
+
const bubble =
|
|
215
|
+
selected &&
|
|
216
|
+
region &&
|
|
217
|
+
hoverInRegion(selected, region) &&
|
|
218
|
+
bubbleForLgvHover(selected, self.bubbles)
|
|
219
|
+
if (
|
|
220
|
+
bubble &&
|
|
221
|
+
!self.openBubbles.some(open => sameBubble(open, bubble))
|
|
222
|
+
) {
|
|
223
|
+
self.toggleBubble(bubble)
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
),
|
|
227
|
+
)
|
|
228
|
+
|
|
170
229
|
// Reaction: drop a hover the pointer cannot still be over.
|
|
171
230
|
//
|
|
172
231
|
// A hover is set by a mousemove on the canvas, and here the drawing
|
|
@@ -20,7 +20,7 @@ import { nodeOwnLocation } from '../../launchFromGraph/contributors'
|
|
|
20
20
|
import { withRows } from '../../launchFromGraph/linearViewTarget'
|
|
21
21
|
import { withLayer } from '../graphLayers'
|
|
22
22
|
import { colorOfScheme } from '../nodeColor'
|
|
23
|
-
import {
|
|
23
|
+
import { sizeOf } from '../nodeSize'
|
|
24
24
|
|
|
25
25
|
import type { GraphGrammar } from './graphViews'
|
|
26
26
|
import type { ScreenTransform } from './keepInView'
|
|
@@ -32,6 +32,7 @@ import type { WalkRowGroupBy } from '../walkRowGroups'
|
|
|
32
32
|
import type { BubbleSpread } from '@jbrowse/bandage-core/bubbleSpreads'
|
|
33
33
|
import type { MinigraphBubble } from '@jbrowse/bandage-core/bubbles/bubbleLine'
|
|
34
34
|
import type { ColorScheme } from '@jbrowse/bandage-core/colorSchemes'
|
|
35
|
+
import type { EdgeWidth } from '@jbrowse/bandage-core/edgeWidths'
|
|
35
36
|
import type { FacetSetting } from '@jbrowse/bandage-core/facetGrid'
|
|
36
37
|
import type { LayoutEngineKind } from '@jbrowse/bandage-core/layoutEngines'
|
|
37
38
|
import type { LayoutModeValue } from '@jbrowse/bandage-core/layoutModes'
|
|
@@ -44,6 +45,8 @@ import type {
|
|
|
44
45
|
WalkLayer,
|
|
45
46
|
} from '@jbrowse/bandage-core/walkEncoding'
|
|
46
47
|
|
|
48
|
+
const ROW_HOVER_SETTLE_MS = 80
|
|
49
|
+
|
|
47
50
|
export const withSettingActions = withTubeMapViews
|
|
48
51
|
.actions(() => ({
|
|
49
52
|
// each host overrides: a view assigns its props, a track display writes
|
|
@@ -166,7 +169,20 @@ export const withSettingActions = withTubeMapViews
|
|
|
166
169
|
},
|
|
167
170
|
setNodeWidth(width: NodeWidth) {
|
|
168
171
|
self.writeGrammar({
|
|
169
|
-
size:
|
|
172
|
+
size: sizeOf({
|
|
173
|
+
width,
|
|
174
|
+
px: self.contigThickness,
|
|
175
|
+
links: self.linkWidth,
|
|
176
|
+
}),
|
|
177
|
+
})
|
|
178
|
+
},
|
|
179
|
+
setLinkWidth(links: EdgeWidth) {
|
|
180
|
+
self.writeGrammar({
|
|
181
|
+
size: sizeOf({
|
|
182
|
+
width: self.nodeWidth,
|
|
183
|
+
px: self.contigThickness,
|
|
184
|
+
links,
|
|
185
|
+
}),
|
|
170
186
|
})
|
|
171
187
|
},
|
|
172
188
|
setTubeMapFold(bp: number) {
|
|
@@ -237,7 +253,19 @@ export const withSettingActions = withTubeMapViews
|
|
|
237
253
|
)
|
|
238
254
|
},
|
|
239
255
|
setWalkRowSamples(samples: string[] | undefined) {
|
|
240
|
-
|
|
256
|
+
const order = self.walkRowOrder
|
|
257
|
+
self.writeGrammar({
|
|
258
|
+
rows: {
|
|
259
|
+
...(samples ? { kept: samples } : {}),
|
|
260
|
+
...(order ? { order } : {}),
|
|
261
|
+
},
|
|
262
|
+
})
|
|
263
|
+
},
|
|
264
|
+
setWalkRowOrder(order: 'shared' | undefined) {
|
|
265
|
+
const kept = self.walkRowSamples
|
|
266
|
+
self.writeGrammar({
|
|
267
|
+
rows: { ...(kept ? { kept } : {}), ...(order ? { order } : {}) },
|
|
268
|
+
})
|
|
241
269
|
},
|
|
242
270
|
setWalkRowGroupBy(groupBy: WalkRowGroupBy | undefined) {
|
|
243
271
|
if (groupBy || self.walkRowGroupBy) {
|
|
@@ -276,7 +304,7 @@ export const withSettingActions = withTubeMapViews
|
|
|
276
304
|
self.hoveredTube = tube
|
|
277
305
|
}
|
|
278
306
|
},
|
|
279
|
-
|
|
307
|
+
applyHoveredRowWalks(walks: string[]) {
|
|
280
308
|
if (walks.join('\n') !== self.hoveredRowWalks.join('\n')) {
|
|
281
309
|
self.hoveredRowWalks = walks
|
|
282
310
|
}
|
|
@@ -469,6 +497,36 @@ export const withSettingActions = withTubeMapViews
|
|
|
469
497
|
self.builtViewport = undefined
|
|
470
498
|
},
|
|
471
499
|
}))
|
|
500
|
+
.actions(self => {
|
|
501
|
+
let settle: ReturnType<typeof setTimeout> | undefined
|
|
502
|
+
let pending: string | undefined
|
|
503
|
+
return {
|
|
504
|
+
// A lane row's lift rebuilds the drawing (43 ms at 10k nodes), and a
|
|
505
|
+
// sweep down a lane crosses a row every few px, so a move from one row
|
|
506
|
+
// to the next lifts once the pointer has stayed on a row a moment.
|
|
507
|
+
// Entering and leaving the rows apply at once.
|
|
508
|
+
setHoveredRowWalks(walks: string[]) {
|
|
509
|
+
const key = walks.join('\n')
|
|
510
|
+
if (key === pending) {
|
|
511
|
+
return
|
|
512
|
+
}
|
|
513
|
+
clearTimeout(settle)
|
|
514
|
+
pending = undefined
|
|
515
|
+
if (walks.length > 0 && self.hoveredRowWalks.length > 0) {
|
|
516
|
+
pending = key
|
|
517
|
+
settle = setTimeout(() => {
|
|
518
|
+
pending = undefined
|
|
519
|
+
// a hover cleared meanwhile, by a pan, stays cleared
|
|
520
|
+
if (isAlive(self) && self.hoveredRowWalks.length > 0) {
|
|
521
|
+
self.applyHoveredRowWalks(walks)
|
|
522
|
+
}
|
|
523
|
+
}, ROW_HOVER_SETTLE_MS)
|
|
524
|
+
} else {
|
|
525
|
+
self.applyHoveredRowWalks(walks)
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
}
|
|
529
|
+
})
|
|
472
530
|
.actions(self => ({
|
|
473
531
|
// The frame clock. Unclamped, since clampZoom's floor is there to keep a
|
|
474
532
|
// scale finite and 1 / bpPerPx already is; clamped, a whole-chromosome
|
|
@@ -18,6 +18,7 @@ import { resolveLocationAssembly } from '../../launchFromGraph/contributors'
|
|
|
18
18
|
|
|
19
19
|
import type { SubgraphRegion } from '../../GetSubgraph'
|
|
20
20
|
import type { WalkAxis, WalkRow } from '@jbrowse/bandage-core/layout/walkRows'
|
|
21
|
+
import type { LayoutModeValue } from '@jbrowse/bandage-core/layoutModes'
|
|
21
22
|
|
|
22
23
|
export const withWalkRowViews = withLaunchViews
|
|
23
24
|
.views(self => ({
|
|
@@ -47,13 +48,23 @@ export const withWalkRowViews = withLaunchViews
|
|
|
47
48
|
}
|
|
48
49
|
return walkStripFrame(bars, { width: self.paneWidth })
|
|
49
50
|
},
|
|
50
|
-
// Whether a cut must follow every snarl a walk leaves the
|
|
51
|
-
// walks come back whole: walk rows measure them, and so does
|
|
51
|
+
// Whether a cut under `mode` must follow every snarl a walk leaves the
|
|
52
|
+
// window by, so walks come back whole: walk rows measure them, and so does
|
|
53
|
+
// the strip under a layout that draws nodes. Only a GBZ cut follows
|
|
54
|
+
// snarls; a walk file cuts the same either way, so asking would re-fetch
|
|
55
|
+
// an identical graph.
|
|
56
|
+
cutsWholeWalksIn(mode: LayoutModeValue, walkStrip = self.walkStrip) {
|
|
57
|
+
const m = layoutModeByValue(mode)
|
|
58
|
+
return (
|
|
59
|
+
self.sourceAdapter?.type === 'GbzBaseSyntenyAdapter' &&
|
|
60
|
+
wholeWalksRule(m, {
|
|
61
|
+
walkStrip: walkStrip && m.drawsNodes,
|
|
62
|
+
host: !!self.host,
|
|
63
|
+
})
|
|
64
|
+
)
|
|
65
|
+
},
|
|
52
66
|
get cutsWholeWalks() {
|
|
53
|
-
return
|
|
54
|
-
walkStrip: self.walkStrip,
|
|
55
|
-
host: !!self.host,
|
|
56
|
-
})
|
|
67
|
+
return this.cutsWholeWalksIn(self.chosenLayoutMode)
|
|
57
68
|
},
|
|
58
69
|
// Where each walk passes the hovered node, or the selected one
|
|
59
70
|
get walkStripMarks() {
|
|
@@ -215,9 +215,10 @@ export function walksThrough(graph: Graph, box: string) {
|
|
|
215
215
|
for (const p of graph.paths ?? []) {
|
|
216
216
|
const walk = walkOf(p.name)
|
|
217
217
|
if (walk !== graph.referencePath) {
|
|
218
|
+
const through = p.nodeIds.includes(box)
|
|
218
219
|
for (const member of p.members ?? [walk]) {
|
|
219
220
|
all.add(member)
|
|
220
|
-
if (
|
|
221
|
+
if (through) {
|
|
221
222
|
here.add(member)
|
|
222
223
|
}
|
|
223
224
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LAYOUT_MODE_VALUES,
|
|
3
|
-
layoutModeByValue,
|
|
4
|
-
} from '@jbrowse/bandage-core/layoutModes'
|
|
1
|
+
import { LAYOUT_MODE_VALUES } from '@jbrowse/bandage-core/layoutModes'
|
|
5
2
|
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'
|
|
6
3
|
import { getSession } from '@jbrowse/core/util'
|
|
7
4
|
import { addDisposer, isAlive, types } from '@jbrowse/mobx-state-tree'
|
|
@@ -222,10 +219,11 @@ export default function stateModelFactory() {
|
|
|
222
219
|
self,
|
|
223
220
|
reaction(
|
|
224
221
|
() => self.walkStrip,
|
|
225
|
-
|
|
222
|
+
on => {
|
|
226
223
|
if (
|
|
227
224
|
self.loadedTrackId &&
|
|
228
|
-
|
|
225
|
+
self.cutsWholeWalks !==
|
|
226
|
+
self.cutsWholeWalksIn(self.chosenLayoutMode, !on)
|
|
229
227
|
) {
|
|
230
228
|
void self.load()
|
|
231
229
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { WalkRowBlock } from '@jbrowse/bandage-core/layout/walkRowLinks'
|
|
2
|
+
|
|
3
|
+
// ggbandage's: the three biggest blocks in colour, the rest a pale grey that
|
|
4
|
+
// still carries its letter
|
|
5
|
+
const BLOCK_COLORS = ['#d4735e', '#d8a94a', '#c98aa4']
|
|
6
|
+
const OTHER_BLOCK = '#e6e6e6'
|
|
7
|
+
const BLOCK_EDGE = '#555'
|
|
8
|
+
const LETTER_FONT = 'bold 11px sans-serif'
|
|
9
|
+
const MIN_LETTER_PX = 12
|
|
10
|
+
const MIN_LETTER_BAR_PX = 9
|
|
11
|
+
|
|
12
|
+
export const blockFill = (block: number) => BLOCK_COLORS[block] ?? OTHER_BLOCK
|
|
13
|
+
|
|
14
|
+
// A, B, ... Z, then AA, AB, ...
|
|
15
|
+
export function blockLetter(block: number): string {
|
|
16
|
+
const letter = String.fromCharCode(65 + (block % 26))
|
|
17
|
+
return block < 26 ? letter : blockLetter(Math.floor(block / 26) - 1) + letter
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// How many blocks get a colour of their own, for the key
|
|
21
|
+
export const COLORED_BLOCKS = BLOCK_COLORS.length
|
|
22
|
+
|
|
23
|
+
// Each stretch as a box over its row's bar, lettered where it is wide enough
|
|
24
|
+
export function paintWalkRowBlocks(
|
|
25
|
+
ctx: CanvasRenderingContext2D,
|
|
26
|
+
blocks: WalkRowBlock[],
|
|
27
|
+
f: {
|
|
28
|
+
x: (offset: number) => number
|
|
29
|
+
rowCentre: (row: number) => number
|
|
30
|
+
barPx: number
|
|
31
|
+
},
|
|
32
|
+
) {
|
|
33
|
+
ctx.font = LETTER_FONT
|
|
34
|
+
ctx.textAlign = 'center'
|
|
35
|
+
ctx.textBaseline = 'middle'
|
|
36
|
+
for (const { row, start, bp, block } of blocks) {
|
|
37
|
+
const left = f.x(start)
|
|
38
|
+
const width = f.x(start + bp) - left
|
|
39
|
+
const top = f.rowCentre(row) - f.barPx / 2
|
|
40
|
+
ctx.fillStyle = blockFill(block)
|
|
41
|
+
ctx.fillRect(left, top, width, f.barPx)
|
|
42
|
+
ctx.strokeStyle = BLOCK_EDGE
|
|
43
|
+
ctx.lineWidth = 1
|
|
44
|
+
ctx.strokeRect(left + 0.5, top + 0.5, width - 1, f.barPx - 1)
|
|
45
|
+
if (width >= MIN_LETTER_PX && f.barPx >= MIN_LETTER_BAR_PX) {
|
|
46
|
+
ctx.fillStyle = '#111'
|
|
47
|
+
ctx.fillText(blockLetter(block), left + width / 2, f.rowCentre(row))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -868,8 +868,14 @@ test('walk rows fit their own bars in the track, since a walk can be longer than
|
|
|
868
868
|
expect(pane.viewportOwner).toBe('fit')
|
|
869
869
|
})
|
|
870
870
|
|
|
871
|
-
test('walk rows cut every snarl a walk takes past the window, so a walk comes whole', async () => {
|
|
872
|
-
const {
|
|
871
|
+
test('walk rows cut every snarl a GBZ walk takes past the window, so a walk comes whole', async () => {
|
|
872
|
+
const { view, cuts } = createEnvironment({ paths: true })
|
|
873
|
+
view.zoomTo(60_000 / WIDTH_PX)
|
|
874
|
+
view.scrollTo(1_000_000 / view.bpPerPx)
|
|
875
|
+
view.showTrack('walks')
|
|
876
|
+
const pane = view.tracks[0]!.displays[0] as LinearGraphDisplayModel
|
|
877
|
+
pane.startRenderingBackend(fakeRenderer())
|
|
878
|
+
await wait(SETTLE_MS)
|
|
873
879
|
await pane.switchLayout('force')
|
|
874
880
|
await wait(SETTLE_MS)
|
|
875
881
|
const before = cuts.length
|
|
@@ -884,6 +890,18 @@ test('walk rows cut every snarl a walk takes past the window, so a walk comes wh
|
|
|
884
890
|
expect(cuts.at(-1)!.snarls).toBeUndefined()
|
|
885
891
|
})
|
|
886
892
|
|
|
893
|
+
// A walk file cuts the same whatever the snarl option says
|
|
894
|
+
test('walk rows on an rGFA track cut for the step budget, not for snarls', async () => {
|
|
895
|
+
const { pane, cuts } = await shownGraph({ paths: true })
|
|
896
|
+
await pane.switchLayout('force')
|
|
897
|
+
await wait(SETTLE_MS)
|
|
898
|
+
const before = cuts.length
|
|
899
|
+
await pane.switchLayout('walkrows')
|
|
900
|
+
await wait(SETTLE_MS)
|
|
901
|
+
expect(cuts).toHaveLength(before + 1)
|
|
902
|
+
expect(cuts.at(-1)!.snarls).toBeUndefined()
|
|
903
|
+
})
|
|
904
|
+
|
|
887
905
|
test('a launch in the force layout cuts the window alone', async () => {
|
|
888
906
|
const { view, cuts } = createEnvironment()
|
|
889
907
|
view.zoomTo(60_000 / WIDTH_PX)
|
|
@@ -1018,9 +1036,11 @@ test('each grammar setter writes the track config, and the pane reads it back',
|
|
|
1018
1036
|
display.setLayoutMode('ordered')
|
|
1019
1037
|
display.setColorScheme('depth')
|
|
1020
1038
|
display.setNodeWidth('uniform')
|
|
1039
|
+
display.setLinkWidth('walks')
|
|
1021
1040
|
display.setLayer('bubbles', true)
|
|
1022
1041
|
display.setFacet('walk')
|
|
1023
1042
|
display.setWalkRowSamples(['HG1'])
|
|
1043
|
+
display.setWalkRowOrder('shared')
|
|
1024
1044
|
display.setHover('off')
|
|
1025
1045
|
const slots = [
|
|
1026
1046
|
'layoutMode',
|
|
@@ -1040,21 +1060,31 @@ test('each grammar setter writes the track config, and the pane reads it back',
|
|
|
1040
1060
|
).toEqual({
|
|
1041
1061
|
layoutMode: 'ordered',
|
|
1042
1062
|
color: { field: 'depth' },
|
|
1043
|
-
size: 6,
|
|
1063
|
+
size: { value: 6, links: 'walks' },
|
|
1044
1064
|
layers: { bubbles: true },
|
|
1045
1065
|
facet: { field: 'walk', domain: [] },
|
|
1046
|
-
rows: { kept: ['HG1'] },
|
|
1066
|
+
rows: { kept: ['HG1'], order: 'shared' },
|
|
1047
1067
|
hover: 'off',
|
|
1048
1068
|
})
|
|
1049
1069
|
expect([
|
|
1050
1070
|
display.chosenLayoutMode,
|
|
1051
1071
|
display.chosenColorScheme,
|
|
1052
1072
|
display.nodeWidth,
|
|
1073
|
+
display.linkWidth,
|
|
1053
1074
|
display.showBubbles,
|
|
1054
1075
|
display.facetSetting.field,
|
|
1055
1076
|
display.walkRowSamples,
|
|
1056
1077
|
display.chosenHover,
|
|
1057
|
-
]).toEqual([
|
|
1078
|
+
]).toEqual([
|
|
1079
|
+
'ordered',
|
|
1080
|
+
'depth',
|
|
1081
|
+
'uniform',
|
|
1082
|
+
'walks',
|
|
1083
|
+
true,
|
|
1084
|
+
'walk',
|
|
1085
|
+
['HG1'],
|
|
1086
|
+
'off',
|
|
1087
|
+
])
|
|
1058
1088
|
})
|
|
1059
1089
|
|
|
1060
1090
|
// Reset track settings, undo and the config editor write the slot without
|
|
@@ -39,6 +39,24 @@ test('a drawn graph sits in the chrome with its readiness attributes', () => {
|
|
|
39
39
|
expect(screen.getByTestId('graph-canvas')).toBeTruthy()
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
+
// GraphCanvas shows it, with the retry that rebuilds the backend
|
|
43
|
+
test('a render error alone is left to the canvas', () => {
|
|
44
|
+
render(
|
|
45
|
+
<GraphStatusChrome
|
|
46
|
+
model={model({ renderError: new Error('context lost') })}
|
|
47
|
+
phase="error"
|
|
48
|
+
drawn={false}
|
|
49
|
+
testid="linear-graph-display"
|
|
50
|
+
>
|
|
51
|
+
<canvas />
|
|
52
|
+
</GraphStatusChrome>,
|
|
53
|
+
)
|
|
54
|
+
expect(screen.queryByText(/context lost/)).toBeNull()
|
|
55
|
+
expect(screen.getByTestId('linear-graph-display').dataset.displayPhase).toBe(
|
|
56
|
+
'error',
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
|
|
42
60
|
test('a refused cut shows its notice over the graph', () => {
|
|
43
61
|
const error = Object.assign(
|
|
44
62
|
new Error('Zoom in to about 54Kbp to see the graph'),
|
|
@@ -31,7 +31,7 @@ const FORCE_LOAD_ADVICE =
|
|
|
31
31
|
// read `model` and `visible`; later hosts read values and callbacks. Each
|
|
32
32
|
// overlay gets both, so one build runs on either.
|
|
33
33
|
function overlayProps(model: LinearGraphDisplayModel) {
|
|
34
|
-
const error = model
|
|
34
|
+
const { error } = model
|
|
35
35
|
const regionTooLarge =
|
|
36
36
|
error instanceof Error &&
|
|
37
37
|
(error as { regionTooLarge?: unknown }).regionTooLarge === true
|
|
@@ -128,7 +128,9 @@ export default observer(function GraphStatusChrome({
|
|
|
128
128
|
{children}
|
|
129
129
|
</BottomRightCornerContext>
|
|
130
130
|
<TrackOverlayPortal>
|
|
131
|
-
{
|
|
131
|
+
{/* a backend's render error is the canvas's banner, whose retry
|
|
132
|
+
rebuilds the backend */}
|
|
133
|
+
{phase === 'error' && model.error !== undefined ? (
|
|
132
134
|
<ErrorBar model={model} visible {...props.error} />
|
|
133
135
|
) : null}
|
|
134
136
|
<Loading
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { fireEvent, render, screen } from '@testing-library/react'
|
|
1
|
+
import { act, fireEvent, render, screen } from '@testing-library/react'
|
|
2
|
+
import { observable, runInAction } from 'mobx'
|
|
2
3
|
|
|
3
4
|
import SubgraphHaplotypesField from './SubgraphHaplotypesField'
|
|
4
5
|
|
|
@@ -58,3 +59,42 @@ test('without names the field takes typed ones', () => {
|
|
|
58
59
|
fireEvent.keyDown(field, { key: 'Enter' })
|
|
59
60
|
expect(m.setSubgraphHaplotypes).toHaveBeenCalledWith(['HG002', 'NA19240#1'])
|
|
60
61
|
})
|
|
62
|
+
|
|
63
|
+
// A model whose set re-renders the field, as the display's does
|
|
64
|
+
function liveModel(haplotypeNames?: string[]) {
|
|
65
|
+
const m = observable({
|
|
66
|
+
sourceAdapter: { type: 'RgfaTabixAdapter', walksUri: 'chr22' },
|
|
67
|
+
cutHaplotypes: ['HG002'],
|
|
68
|
+
haplotypeNames,
|
|
69
|
+
setSubgraphHaplotypes(haplotypes: string[]) {
|
|
70
|
+
m.cutHaplotypes = haplotypes
|
|
71
|
+
},
|
|
72
|
+
recut: vi.fn(),
|
|
73
|
+
})
|
|
74
|
+
return m as unknown as LinearGraphCutModel & { cutHaplotypes: string[] }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
test('applying from the field keeps its focus', () => {
|
|
78
|
+
const m = liveModel()
|
|
79
|
+
render(<SubgraphHaplotypesField model={m} />)
|
|
80
|
+
const field = screen.getByTestId<HTMLInputElement>('graph-haplotypes-field')
|
|
81
|
+
field.focus()
|
|
82
|
+
fireEvent.change(field, { target: { value: 'HG002, NA19240#1' } })
|
|
83
|
+
fireEvent.keyDown(field, { key: 'Enter' })
|
|
84
|
+
expect(m.cutHaplotypes).toEqual(['HG002', 'NA19240#1'])
|
|
85
|
+
expect(screen.getByTestId('graph-haplotypes-field')).toBe(field)
|
|
86
|
+
expect(document.activeElement).toBe(field)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('a set changed elsewhere resets the draft', () => {
|
|
90
|
+
const m = liveModel()
|
|
91
|
+
render(<SubgraphHaplotypesField model={m} />)
|
|
92
|
+
act(() => {
|
|
93
|
+
runInAction(() => {
|
|
94
|
+
m.cutHaplotypes = ['HG00097#1']
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
expect(
|
|
98
|
+
screen.getByTestId<HTMLInputElement>('graph-haplotypes-field').value,
|
|
99
|
+
).toBe('HG00097#1')
|
|
100
|
+
})
|
|
@@ -18,20 +18,39 @@ function parseHaplotypeList(text: string) {
|
|
|
18
18
|
return text.split(/[\s,]+/).filter(name => name !== '')
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
const sameList = (a: readonly string[], b: readonly string[]) =>
|
|
22
|
+
a.join('\n') === b.join('\n')
|
|
23
|
+
|
|
24
|
+
// The cut's set, and the last one this field saw: an outside change (a
|
|
25
|
+
// restored session, a launch) resets the draft, while the field's own apply
|
|
26
|
+
// records what it set first, so it keeps its focus and draft
|
|
27
|
+
function useCutHaplotypes(model: LinearGraphCutModel, reset: () => void) {
|
|
28
|
+
const current = model.cutHaplotypes ?? []
|
|
29
|
+
const [seen, setSeen] = useState(current)
|
|
30
|
+
if (!sameList(current, seen)) {
|
|
31
|
+
setSeen(current)
|
|
32
|
+
reset()
|
|
33
|
+
}
|
|
34
|
+
return (haplotypes: string[]) => {
|
|
35
|
+
if (!sameList(haplotypes, current)) {
|
|
36
|
+
setSeen(haplotypes)
|
|
37
|
+
model.setSubgraphHaplotypes(haplotypes)
|
|
38
|
+
model.recut()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
21
43
|
const HaplotypeListField = observer(function HaplotypeListField({
|
|
22
44
|
model,
|
|
23
45
|
}: {
|
|
24
46
|
model: LinearGraphCutModel
|
|
25
47
|
}) {
|
|
26
48
|
const [draft, setDraft] = useState(model.cutHaplotypes?.join(', ') ?? '')
|
|
49
|
+
const setCut = useCutHaplotypes(model, () => {
|
|
50
|
+
setDraft(model.cutHaplotypes?.join(', ') ?? '')
|
|
51
|
+
})
|
|
27
52
|
const apply = () => {
|
|
28
|
-
|
|
29
|
-
const unchanged =
|
|
30
|
-
parsed.join('\n') === (model.cutHaplotypes ?? []).join('\n')
|
|
31
|
-
if (!unchanged) {
|
|
32
|
-
model.setSubgraphHaplotypes(parsed)
|
|
33
|
-
model.recut()
|
|
34
|
-
}
|
|
53
|
+
setCut(parseHaplotypeList(draft))
|
|
35
54
|
}
|
|
36
55
|
return (
|
|
37
56
|
<TextField
|
|
@@ -72,12 +91,10 @@ const HaplotypePickField = observer(function HaplotypePickField({
|
|
|
72
91
|
const draftRef = useRef(draft)
|
|
73
92
|
const typed = useRef('')
|
|
74
93
|
const [open, setOpen] = useState(false)
|
|
75
|
-
const apply = (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
}
|
|
94
|
+
const apply = useCutHaplotypes(model, () => {
|
|
95
|
+
draftRef.current = model.cutHaplotypes ?? []
|
|
96
|
+
setDraft(draftRef.current)
|
|
97
|
+
})
|
|
81
98
|
return (
|
|
82
99
|
<Autocomplete
|
|
83
100
|
multiple
|
|
@@ -126,9 +143,7 @@ const HaplotypePickField = observer(function HaplotypePickField({
|
|
|
126
143
|
)
|
|
127
144
|
})
|
|
128
145
|
|
|
129
|
-
// Only a GBZ cut and a walk-indexed rGFA cut read the set
|
|
130
|
-
// keyed on it so an outside change (a restored session, a launch) resets the
|
|
131
|
-
// draft rather than fighting it.
|
|
146
|
+
// Only a GBZ cut and a walk-indexed rGFA cut read the set
|
|
132
147
|
const SubgraphHaplotypesField = observer(function SubgraphHaplotypesField({
|
|
133
148
|
model,
|
|
134
149
|
}: {
|
|
@@ -136,14 +151,13 @@ const SubgraphHaplotypesField = observer(function SubgraphHaplotypesField({
|
|
|
136
151
|
}) {
|
|
137
152
|
const { classes } = useStyles()
|
|
138
153
|
const names = model.haplotypeNames
|
|
139
|
-
const key = model.cutHaplotypes?.join(',') ?? ''
|
|
140
154
|
const adapter = model.sourceAdapter
|
|
141
155
|
return adapter && cutsByHaplotype(adapter) ? (
|
|
142
156
|
<div className={classes.section}>
|
|
143
157
|
{names ? (
|
|
144
|
-
<HaplotypePickField
|
|
158
|
+
<HaplotypePickField model={model} names={names} />
|
|
145
159
|
) : (
|
|
146
|
-
<HaplotypeListField
|
|
160
|
+
<HaplotypeListField model={model} />
|
|
147
161
|
)}
|
|
148
162
|
<Typography variant="caption" component="div" color="text.secondary">
|
|
149
163
|
{names
|
|
@@ -44,7 +44,8 @@ export function configSchemaFactory() {
|
|
|
44
44
|
* #slot
|
|
45
45
|
* the node thickness: a px number draws every node that thick;
|
|
46
46
|
* `{ field: 'depth', value }` thickens a node the more paths carry it,
|
|
47
|
-
* around `value` px
|
|
47
|
+
* around `value` px; `links: 'walks'` thickens a link the more walks
|
|
48
|
+
* take it
|
|
48
49
|
*/
|
|
49
50
|
size: {
|
|
50
51
|
type: 'frozen',
|
|
@@ -54,8 +55,8 @@ export function configSchemaFactory() {
|
|
|
54
55
|
/**
|
|
55
56
|
* #slot
|
|
56
57
|
* what the drawing adds over its nodes and edges, stated as changes to
|
|
57
|
-
* the defaults: `paths`, `bubbles` and `
|
|
58
|
-
* `genes` and `referenceStrip` on
|
|
58
|
+
* the defaults: `paths`, `bubbles`, `walkStrip` and `walkRibbons` are
|
|
59
|
+
* off, `deletions`, `genes` and `referenceStrip` on
|
|
59
60
|
* ```js
|
|
60
61
|
* { layers: { bubbles: true, genes: false } }
|
|
61
62
|
* ```
|
|
@@ -4,11 +4,7 @@ import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
|
4
4
|
import { parseSegmentRow, rgfaCut, segmentSamples } from '@jbrowse/graph-walks'
|
|
5
5
|
|
|
6
6
|
import { locationIsSet } from '../locationName.ts'
|
|
7
|
-
import {
|
|
8
|
-
PanSNRefNames,
|
|
9
|
-
openTabixSlot,
|
|
10
|
-
unmappedRegionMessage,
|
|
11
|
-
} from '../panSNTabix.ts'
|
|
7
|
+
import { PanSNRefNames, openTabixSlot } from '../panSNTabix.ts'
|
|
12
8
|
import { getLines } from '../tabixRange.ts'
|
|
13
9
|
import { WalkIndex } from './walkReader.ts'
|
|
14
10
|
|
|
@@ -194,10 +190,7 @@ export default class RgfaTabixAdapter extends BaseFeatureDataAdapter<RgfaTabixAd
|
|
|
194
190
|
): Promise<string | WalkCut> {
|
|
195
191
|
const { hops, signal, tier } = opts
|
|
196
192
|
const index = this.index(tier)
|
|
197
|
-
const tabixRefName = await index.refNames.
|
|
198
|
-
if (tabixRefName === undefined) {
|
|
199
|
-
throw new Error(unmappedRegionMessage(region))
|
|
200
|
-
}
|
|
193
|
+
const tabixRefName = await index.refNames.resolveForCut(region, { signal })
|
|
201
194
|
if (index.walks) {
|
|
202
195
|
return index.walks.subgraph(tabixRefName, region, opts)
|
|
203
196
|
}
|