jbrowse-plugin-mafviewer 1.2.1 → 1.2.3
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 +6 -1
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +11 -2
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +123 -94
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -1
- package/dist/LinearMafDisplay/components/ColorLegend.js +1 -1
- package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
- package/dist/LinearMafDisplay/components/RectBg.js +1 -1
- package/dist/LinearMafDisplay/stateModel.d.ts +10 -0
- package/dist/LinearMafDisplay/stateModel.js +30 -10
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.js +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/makeImageData.js +25 -9
- package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +8 -7
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/package.json +2 -1
- package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +135 -99
- package/src/LinearMafDisplay/components/ColorLegend.tsx +2 -2
- package/src/LinearMafDisplay/components/RectBg.tsx +1 -1
- package/src/LinearMafDisplay/stateModel.ts +30 -10
- package/src/LinearMafRenderer/LinearMafRenderer.ts +1 -1
- package/src/LinearMafRenderer/makeImageData.ts +38 -9
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +9 -11
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.2.
|
|
2
|
+
"version": "1.2.3",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "jbrowse-plugin-mafviewer",
|
|
5
5
|
"keywords": [
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@gmod/bgzf-filehandle": "^2.0.4",
|
|
66
|
+
"abortable-promise-cache": "^1.5.0",
|
|
66
67
|
"buffer": "^6.0.3",
|
|
67
68
|
"d3-array": "^3.2.4",
|
|
68
69
|
"d3-hierarchy": "^3.1.2",
|
|
@@ -9,8 +9,10 @@ import {
|
|
|
9
9
|
SimpleFeature,
|
|
10
10
|
updateStatus,
|
|
11
11
|
} from '@jbrowse/core/util'
|
|
12
|
+
import QuickLRU from '@jbrowse/core/util/QuickLRU'
|
|
12
13
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
13
14
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
15
|
+
import AbortablePromiseCache from 'abortable-promise-cache'
|
|
14
16
|
import Long from 'long'
|
|
15
17
|
|
|
16
18
|
import VirtualOffset from './virtualOffset'
|
|
@@ -20,6 +22,7 @@ import { parseRowInstructions } from './rowInstructions'
|
|
|
20
22
|
import { parseLineByLine } from './util'
|
|
21
23
|
|
|
22
24
|
import type { IndexData, OrganismRecord } from './types'
|
|
25
|
+
|
|
23
26
|
interface Entry {
|
|
24
27
|
type: string
|
|
25
28
|
row: number
|
|
@@ -35,11 +38,125 @@ const toP = (s = 0) => +(+s).toFixed(1)
|
|
|
35
38
|
export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
36
39
|
public setupP?: Promise<IndexData>
|
|
37
40
|
|
|
41
|
+
private cache = new AbortablePromiseCache({
|
|
42
|
+
cache: new QuickLRU({ maxSize: 50 }),
|
|
43
|
+
// @ts-expect-error
|
|
44
|
+
fill: async ({ nextEntry, firstEntry }, signal, statusCallback) => {
|
|
45
|
+
const file = openLocation(this.getConf('tafGzLocation'))
|
|
46
|
+
const response = await file.read(
|
|
47
|
+
nextEntry.virtualOffset.blockPosition -
|
|
48
|
+
firstEntry.virtualOffset.blockPosition,
|
|
49
|
+
firstEntry.virtualOffset.blockPosition,
|
|
50
|
+
)
|
|
51
|
+
const buffer = await unzip(response)
|
|
52
|
+
const slice = buffer.slice(firstEntry.virtualOffset.dataPosition)
|
|
53
|
+
return this.getChunk(slice, {
|
|
54
|
+
statusCallback: statusCallback as (arg: string) => void,
|
|
55
|
+
signal,
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
38
60
|
async getRefNames() {
|
|
39
61
|
const data = await this.setup()
|
|
40
62
|
return Object.keys(data)
|
|
41
63
|
}
|
|
42
64
|
|
|
65
|
+
async getChunk(buffer: Uint8Array, opts?: BaseOptions) {
|
|
66
|
+
const { statusCallback = () => {} } = opts || {}
|
|
67
|
+
const alignments = {} as Record<string, OrganismRecord>
|
|
68
|
+
const data = [] as Entry[]
|
|
69
|
+
let a0: any
|
|
70
|
+
let j = 0
|
|
71
|
+
let b = 0
|
|
72
|
+
parseLineByLine(buffer, line => {
|
|
73
|
+
if (j++ % 100 === 0) {
|
|
74
|
+
statusCallback(
|
|
75
|
+
`Processing ${toP(b / 1_000_000)}/${toP(buffer.length / 1_000_000)}Mb`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
b += line.length
|
|
79
|
+
if (line) {
|
|
80
|
+
const [lineData, rowInstructions] = line.split(' ; ')
|
|
81
|
+
if (rowInstructions) {
|
|
82
|
+
for (const ins of parseRowInstructions(rowInstructions)) {
|
|
83
|
+
if (ins.type === 'i') {
|
|
84
|
+
data.splice(ins.row, 0, ins)
|
|
85
|
+
if (!alignments[ins.asm]) {
|
|
86
|
+
alignments[ins.asm] = {
|
|
87
|
+
start: ins.start,
|
|
88
|
+
strand: ins.strand,
|
|
89
|
+
srcSize: ins.length,
|
|
90
|
+
chr: ins.ref,
|
|
91
|
+
data: '',
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const e = alignments[ins.asm]!
|
|
95
|
+
e.data += ' '.repeat(Math.max(0, j - e.data.length - 1)) // catch it up
|
|
96
|
+
} else if (ins.type === 's') {
|
|
97
|
+
if (!alignments[ins.asm]) {
|
|
98
|
+
alignments[ins.asm] = {
|
|
99
|
+
start: ins.start,
|
|
100
|
+
strand: ins.strand,
|
|
101
|
+
srcSize: ins.length,
|
|
102
|
+
chr: ins.ref,
|
|
103
|
+
data: '',
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const e = alignments[ins.asm]!
|
|
107
|
+
e.data += ' '.repeat(Math.max(0, j - e.data.length - 1)) // catch it up
|
|
108
|
+
data[ins.row] = ins
|
|
109
|
+
} else if (ins.type === 'd') {
|
|
110
|
+
data.splice(ins.row, 1)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// no gaps for now(?)
|
|
114
|
+
// else if (ins.type === 'g') {
|
|
115
|
+
// console.log('g??')
|
|
116
|
+
// } else if (ins.type === 'G') {
|
|
117
|
+
// console.log('G??')
|
|
118
|
+
// }
|
|
119
|
+
}
|
|
120
|
+
if (!a0) {
|
|
121
|
+
a0 = data[0]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const lineLen = lineData!.length
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < lineLen; i++) {
|
|
127
|
+
const letter = lineData![i]
|
|
128
|
+
const r = data[i]
|
|
129
|
+
|
|
130
|
+
if (r) {
|
|
131
|
+
alignments[r.asm]!.data += letter
|
|
132
|
+
} else {
|
|
133
|
+
// not sure why but chr22_KI270731v1_random.taf.gz ends up here
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
if (a0) {
|
|
139
|
+
const row0 = alignments[a0.asm]!
|
|
140
|
+
|
|
141
|
+
// see
|
|
142
|
+
// https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
|
|
143
|
+
// for the significance of row[0]:
|
|
144
|
+
//
|
|
145
|
+
// "An anchor line in TAF is a column from which all sequence
|
|
146
|
+
// coordinates can be deduced without scanning backwards to previous
|
|
147
|
+
// lines "
|
|
148
|
+
return {
|
|
149
|
+
uniqueId: `${row0.start}-${row0.data.length}`,
|
|
150
|
+
start: row0.start,
|
|
151
|
+
end: row0.start + row0.data.length,
|
|
152
|
+
strand: row0.strand,
|
|
153
|
+
alignments,
|
|
154
|
+
seq: row0.data,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return undefined
|
|
158
|
+
}
|
|
159
|
+
|
|
43
160
|
setupPre() {
|
|
44
161
|
if (!this.setupP) {
|
|
45
162
|
this.setupP = this.readTaiFile().catch((e: unknown) => {
|
|
@@ -104,103 +221,23 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
104
221
|
return ObservableCreate<Feature>(async observer => {
|
|
105
222
|
try {
|
|
106
223
|
const byteRanges = await this.setup()
|
|
107
|
-
const
|
|
224
|
+
const feat = await updateStatus(
|
|
108
225
|
'Downloading alignments',
|
|
109
226
|
statusCallback,
|
|
110
227
|
() => this.getLines(query, byteRanges),
|
|
111
228
|
)
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
b += line.length
|
|
125
|
-
if (line) {
|
|
126
|
-
const [lineData, rowInstructions] = line.split(' ; ')
|
|
127
|
-
if (rowInstructions) {
|
|
128
|
-
for (const ins of parseRowInstructions(rowInstructions)) {
|
|
129
|
-
if (ins.type === 'i') {
|
|
130
|
-
data.splice(ins.row, 0, ins)
|
|
131
|
-
if (!alignments[ins.asm]) {
|
|
132
|
-
alignments[ins.asm] = {
|
|
133
|
-
start: ins.start,
|
|
134
|
-
strand: ins.strand,
|
|
135
|
-
srcSize: ins.length,
|
|
136
|
-
chr: ins.ref,
|
|
137
|
-
data: '',
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const e = alignments[ins.asm]!
|
|
141
|
-
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
142
|
-
} else if (ins.type === 's') {
|
|
143
|
-
if (!alignments[ins.asm]) {
|
|
144
|
-
alignments[ins.asm] = {
|
|
145
|
-
start: ins.start,
|
|
146
|
-
strand: ins.strand,
|
|
147
|
-
srcSize: ins.length,
|
|
148
|
-
chr: ins.ref,
|
|
149
|
-
data: '',
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const e = alignments[ins.asm]!
|
|
153
|
-
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
154
|
-
data[ins.row] = ins
|
|
155
|
-
} else if (ins.type === 'd') {
|
|
156
|
-
data.splice(ins.row, 1)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// no gaps for now(?)
|
|
160
|
-
// else if (ins.type === 'g') {
|
|
161
|
-
// }
|
|
162
|
-
// else if (ins.type === 'G') {
|
|
163
|
-
// }
|
|
164
|
-
}
|
|
165
|
-
if (!a0) {
|
|
166
|
-
a0 = data[0]
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const lineLen = lineData!.length
|
|
170
|
-
for (let i = 0; i < lineLen; i++) {
|
|
171
|
-
const letter = lineData![i]
|
|
172
|
-
const r = data[i]
|
|
173
|
-
if (r) {
|
|
174
|
-
alignments[r.asm]!.data += letter
|
|
175
|
-
} else {
|
|
176
|
-
// not sure why but chr22_KI270731v1_random.taf.gz ends up here
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
if (a0) {
|
|
182
|
-
const row0 = alignments[a0.asm]!
|
|
183
|
-
|
|
184
|
-
// see
|
|
185
|
-
// https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
|
|
186
|
-
// for the significance of row[0]:
|
|
187
|
-
//
|
|
188
|
-
// "An anchor line in TAF is a column from which all sequence
|
|
189
|
-
// coordinates can be deduced without scanning backwards to previous
|
|
190
|
-
// lines "
|
|
191
|
-
observer.next(
|
|
192
|
-
new SimpleFeature({
|
|
193
|
-
uniqueId: `${row0.start}-${row0.data.length}`,
|
|
194
|
-
refName: query.refName,
|
|
195
|
-
start: row0.start,
|
|
196
|
-
end: row0.start + row0.data.length,
|
|
197
|
-
strand: row0.strand,
|
|
198
|
-
alignments,
|
|
199
|
-
seq: row0.data,
|
|
200
|
-
}),
|
|
201
|
-
)
|
|
202
|
-
}
|
|
229
|
+
if (feat) {
|
|
230
|
+
observer.next(
|
|
231
|
+
// @ts-expect-error
|
|
232
|
+
new SimpleFeature({
|
|
233
|
+
...feat,
|
|
234
|
+
refName: query.refName,
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
237
|
+
} else {
|
|
238
|
+
console.error('no feature found')
|
|
203
239
|
}
|
|
240
|
+
|
|
204
241
|
statusCallback('')
|
|
205
242
|
observer.complete()
|
|
206
243
|
} catch (e) {
|
|
@@ -226,7 +263,6 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
226
263
|
|
|
227
264
|
// TODO: cache processed large chunks
|
|
228
265
|
async getLines(query: Region, byteRanges: IndexData) {
|
|
229
|
-
const file = openLocation(this.getConf('tafGzLocation'))
|
|
230
266
|
const records = byteRanges[query.refName]
|
|
231
267
|
if (records) {
|
|
232
268
|
let firstEntry
|
|
@@ -255,13 +291,13 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
255
291
|
// file whn you request e.g. out of range region (e.g. taf in chr22:1-100
|
|
256
292
|
// and you are at chr22:200-300)
|
|
257
293
|
if (firstEntry && nextEntry) {
|
|
258
|
-
|
|
259
|
-
nextEntry.
|
|
260
|
-
|
|
261
|
-
|
|
294
|
+
return this.cache.get(
|
|
295
|
+
`${JSON.stringify(nextEntry)}_${JSON.stringify(firstEntry)}`,
|
|
296
|
+
{
|
|
297
|
+
nextEntry,
|
|
298
|
+
firstEntry,
|
|
299
|
+
},
|
|
262
300
|
)
|
|
263
|
-
const buffer = await unzip(response)
|
|
264
|
-
return buffer.slice(firstEntry.virtualOffset.dataPosition)
|
|
265
301
|
}
|
|
266
302
|
}
|
|
267
303
|
return undefined
|
|
@@ -43,10 +43,10 @@ const ColorLegend = observer(function ({
|
|
|
43
43
|
? samples.map((sample, idx) => (
|
|
44
44
|
<text
|
|
45
45
|
key={`${sample.id}-${idx}`}
|
|
46
|
-
y={idx * rowHeight + rowHeight / 2}
|
|
47
46
|
dominantBaseline="middle"
|
|
48
|
-
x={2}
|
|
49
47
|
fontSize={svgFontSize}
|
|
48
|
+
x={2}
|
|
49
|
+
y={idx * rowHeight + rowHeight / 2}
|
|
50
50
|
>
|
|
51
51
|
{sample.label}
|
|
52
52
|
</text>
|
|
@@ -244,7 +244,7 @@ export default function stateModelFactory(
|
|
|
244
244
|
return {
|
|
245
245
|
...s,
|
|
246
246
|
notReady:
|
|
247
|
-
!self.volatileSamples
|
|
247
|
+
(!self.volatileSamples && !self.volatileTree) || super.notReady,
|
|
248
248
|
config: rendererConfig,
|
|
249
249
|
samples,
|
|
250
250
|
rowHeight,
|
|
@@ -260,16 +260,36 @@ export default function stateModelFactory(
|
|
|
260
260
|
return [
|
|
261
261
|
...superTrackMenuItems(),
|
|
262
262
|
{
|
|
263
|
-
label: 'Set
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
263
|
+
label: 'Set feature height',
|
|
264
|
+
type: 'subMenu',
|
|
265
|
+
subMenu: [
|
|
266
|
+
{
|
|
267
|
+
label: 'Normal',
|
|
268
|
+
onClick: () => {
|
|
269
|
+
self.setRowHeight(15)
|
|
270
|
+
self.setRowProportion(0.8)
|
|
270
271
|
},
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
label: 'Compact',
|
|
275
|
+
onClick: () => {
|
|
276
|
+
self.setRowHeight(8)
|
|
277
|
+
self.setRowProportion(0.9)
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
label: 'Manually set height',
|
|
282
|
+
onClick: () => {
|
|
283
|
+
getSession(self).queueDialog(handleClose => [
|
|
284
|
+
SetRowHeightDialog,
|
|
285
|
+
{
|
|
286
|
+
model: self,
|
|
287
|
+
handleClose,
|
|
288
|
+
},
|
|
289
|
+
])
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
],
|
|
273
293
|
},
|
|
274
294
|
{
|
|
275
295
|
label: 'Show all letters',
|
|
@@ -42,7 +42,7 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
42
42
|
rowHeight,
|
|
43
43
|
} = renderProps
|
|
44
44
|
const region = regions[0]!
|
|
45
|
-
const height = samples.length *
|
|
45
|
+
const height = samples.length * rowHeight + 100
|
|
46
46
|
const width = (region.end - region.start) / bpPerPx
|
|
47
47
|
const features = await this.getFeatures(renderProps)
|
|
48
48
|
const res = await renderToAbstractCanvas(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
|
|
2
2
|
import { createJBrowseTheme } from '@jbrowse/core/ui'
|
|
3
|
-
import { Feature, featureSpanPx } from '@jbrowse/core/util'
|
|
3
|
+
import { Feature, featureSpanPx, measureText } from '@jbrowse/core/util'
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
fillRect,
|
|
@@ -97,7 +97,7 @@ export function makeImageData({
|
|
|
97
97
|
if (seq[i] !== '-') {
|
|
98
98
|
const c = alignment[i]
|
|
99
99
|
const l = leftPx + scale * o
|
|
100
|
-
if (seq[i] === c && c !== '-') {
|
|
100
|
+
if (seq[i] === c && c !== '-' && c !== ' ') {
|
|
101
101
|
fillRect(ctx, l, offset + t, scale + f, h, canvasWidth)
|
|
102
102
|
}
|
|
103
103
|
o++
|
|
@@ -183,8 +183,6 @@ export function makeImageData({
|
|
|
183
183
|
|
|
184
184
|
const t = rowHeight * row
|
|
185
185
|
|
|
186
|
-
ctx.beginPath()
|
|
187
|
-
ctx.fillStyle = 'purple'
|
|
188
186
|
for (let i = 0, o = 0; i < alignment.length; i++) {
|
|
189
187
|
let ins = ''
|
|
190
188
|
while (seq[i] === '-') {
|
|
@@ -196,15 +194,46 @@ export function makeImageData({
|
|
|
196
194
|
if (ins.length > 0) {
|
|
197
195
|
const l = leftPx + scale * o - 1
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
197
|
+
if (ins.length > 10) {
|
|
198
|
+
const txt = `${ins.length}`
|
|
199
|
+
if (bpPerPx > 10) {
|
|
200
|
+
fillRect(ctx, l - 1, t, 2, h, canvasWidth, 'purple')
|
|
201
|
+
} else if (h > charHeight) {
|
|
202
|
+
const rwidth = measureText(txt)
|
|
203
|
+
const padding = 5
|
|
204
|
+
fillRect(
|
|
205
|
+
ctx,
|
|
206
|
+
l - rwidth / 2 - padding,
|
|
207
|
+
t,
|
|
208
|
+
rwidth + 2 * padding,
|
|
209
|
+
h,
|
|
210
|
+
canvasWidth,
|
|
211
|
+
'purple',
|
|
212
|
+
)
|
|
213
|
+
ctx.fillStyle = 'white'
|
|
214
|
+
ctx.fillText(txt, l - rwidth / 2, t + h)
|
|
215
|
+
} else {
|
|
216
|
+
const padding = 2
|
|
217
|
+
fillRect(
|
|
218
|
+
ctx,
|
|
219
|
+
l - padding,
|
|
220
|
+
t,
|
|
221
|
+
2 * padding,
|
|
222
|
+
h,
|
|
223
|
+
canvasWidth,
|
|
224
|
+
'purple',
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
fillRect(ctx, l, offset + t, 1, h, canvasWidth, 'purple')
|
|
229
|
+
if (bpPerPx < 0.2 && rowHeight > 5) {
|
|
230
|
+
fillRect(ctx, l - 2, offset + t, 5, 1, canvasWidth)
|
|
231
|
+
fillRect(ctx, l - 2, offset + t + h - 1, 5, 1, canvasWidth)
|
|
232
|
+
}
|
|
203
233
|
}
|
|
204
234
|
}
|
|
205
235
|
o++
|
|
206
236
|
}
|
|
207
|
-
ctx.fill()
|
|
208
237
|
}
|
|
209
238
|
}
|
|
210
239
|
}
|
|
@@ -69,17 +69,15 @@ export default function MultiMAFWidget({ model }: { model: AddTrackModel }) {
|
|
|
69
69
|
setFileTypeChoice(event.target.value as AdapterTypeOptions)
|
|
70
70
|
}}
|
|
71
71
|
>
|
|
72
|
-
{['BigMafAdapter', 'MafTabixAdapter'
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
),
|
|
82
|
-
)}
|
|
72
|
+
{['BigMafAdapter', 'MafTabixAdapter'].map(r => (
|
|
73
|
+
<FormControlLabel
|
|
74
|
+
key={r}
|
|
75
|
+
value={r}
|
|
76
|
+
control={<Radio />}
|
|
77
|
+
checked={fileTypeChoice === r}
|
|
78
|
+
label={r}
|
|
79
|
+
/>
|
|
80
|
+
))}
|
|
83
81
|
</RadioGroup>
|
|
84
82
|
</FormControl>
|
|
85
83
|
{fileTypeChoice === 'BigMafAdapter' ? (
|