jbrowse-plugin-mafviewer 1.1.3 → 1.2.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/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +5 -4
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +103 -82
- package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -1
- package/dist/BgzipTaffyAdapter/util.d.ts +1 -0
- package/dist/BgzipTaffyAdapter/util.js +22 -0
- package/dist/BgzipTaffyAdapter/util.js.map +1 -0
- package/dist/BigMafAdapter/BigMafAdapter.d.ts +3 -2
- package/dist/BigMafAdapter/BigMafAdapter.js +4 -3
- package/dist/BigMafAdapter/BigMafAdapter.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/ReactComponent.js +1 -1
- package/dist/LinearMafDisplay/components/ReactComponent.js.map +1 -1
- package/dist/LinearMafDisplay/components/SvgWrapper.js +2 -2
- package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -1
- package/dist/LinearMafDisplay/components/YScaleBars.js +2 -2
- package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
- package/dist/LinearMafDisplay/renderSvg.d.ts +2 -2
- package/dist/LinearMafDisplay/renderSvg.js +0 -1
- package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
- package/dist/LinearMafDisplay/stateModel.d.ts +19 -17
- package/dist/LinearMafDisplay/stateModel.js +17 -10
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +8 -4
- package/dist/LinearMafRenderer/LinearMafRenderer.js +14 -148
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/configSchema.d.ts +6 -1
- package/dist/LinearMafRenderer/configSchema.js +6 -1
- package/dist/LinearMafRenderer/configSchema.js.map +1 -1
- package/dist/LinearMafRenderer/makeImageData.d.ts +20 -0
- package/dist/LinearMafRenderer/makeImageData.js +144 -0
- package/dist/LinearMafRenderer/makeImageData.js.map +1 -0
- package/dist/LinearMafRenderer/util.d.ts +6 -1
- package/dist/LinearMafRenderer/util.js +17 -0
- package/dist/LinearMafRenderer/util.js.map +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.d.ts +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +9 -6
- package/dist/MafTabixAdapter/MafTabixAdapter.js +74 -43
- package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
- package/dist/MafTabixAdapter/configSchema.d.ts +7 -0
- package/dist/MafTabixAdapter/configSchema.js +7 -0
- package/dist/MafTabixAdapter/configSchema.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +7 -8
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/package.json +2 -2
- package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +123 -86
- package/src/BgzipTaffyAdapter/util.ts +25 -0
- package/src/BigMafAdapter/BigMafAdapter.ts +10 -4
- package/src/LinearMafDisplay/components/ColorLegend.tsx +1 -2
- package/src/LinearMafDisplay/components/ReactComponent.tsx +1 -1
- package/src/LinearMafDisplay/components/SvgWrapper.tsx +2 -2
- package/src/LinearMafDisplay/components/YScaleBars.tsx +3 -2
- package/src/LinearMafDisplay/renderSvg.tsx +5 -5
- package/src/LinearMafDisplay/stateModel.ts +30 -24
- package/src/LinearMafRenderer/LinearMafRenderer.ts +21 -176
- package/src/LinearMafRenderer/configSchema.ts +6 -1
- package/src/LinearMafRenderer/makeImageData.ts +211 -0
- package/src/LinearMafRenderer/util.ts +28 -1
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +2 -1
- package/src/MafTabixAdapter/MafTabixAdapter.ts +92 -44
- package/src/MafTabixAdapter/configSchema.ts +7 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.2.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "jbrowse-plugin-mafviewer",
|
|
5
5
|
"keywords": [
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
|
42
42
|
"@typescript-eslint/parser": "^8.18.0",
|
|
43
43
|
"chalk": "^5.3.0",
|
|
44
|
-
"esbuild": "^0.
|
|
44
|
+
"esbuild": "^0.25.0",
|
|
45
45
|
"eslint": "^9.17.0",
|
|
46
46
|
"eslint-plugin-import": "^2.31.0",
|
|
47
47
|
"eslint-plugin-react": "^7.20.3",
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { unzip } from '@gmod/bgzf-filehandle'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
BaseFeatureDataAdapter,
|
|
4
|
+
BaseOptions,
|
|
5
|
+
} from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
6
|
+
import {
|
|
7
|
+
Feature,
|
|
8
|
+
Region,
|
|
9
|
+
SimpleFeature,
|
|
10
|
+
updateStatus,
|
|
11
|
+
} from '@jbrowse/core/util'
|
|
4
12
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
5
13
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
6
14
|
import Long from 'long'
|
|
@@ -11,6 +19,7 @@ import { normalize } from '../util'
|
|
|
11
19
|
import { parseRowInstructions } from './rowInstructions'
|
|
12
20
|
|
|
13
21
|
import type { IndexData, OrganismRecord } from './types'
|
|
22
|
+
import { parseLineByLine } from './util'
|
|
14
23
|
interface Entry {
|
|
15
24
|
type: string
|
|
16
25
|
row: number
|
|
@@ -20,6 +29,9 @@ interface Entry {
|
|
|
20
29
|
strand: number
|
|
21
30
|
length: number
|
|
22
31
|
}
|
|
32
|
+
|
|
33
|
+
const toP = (s = 0) => +(+s).toFixed(1)
|
|
34
|
+
|
|
23
35
|
export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
24
36
|
public setupP?: Promise<IndexData>
|
|
25
37
|
|
|
@@ -28,7 +40,7 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
28
40
|
return Object.keys(data)
|
|
29
41
|
}
|
|
30
42
|
|
|
31
|
-
|
|
43
|
+
setupPre() {
|
|
32
44
|
if (!this.setupP) {
|
|
33
45
|
this.setupP = this.readTaiFile().catch((e: unknown) => {
|
|
34
46
|
this.setupP = undefined
|
|
@@ -37,6 +49,12 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
37
49
|
}
|
|
38
50
|
return this.setupP
|
|
39
51
|
}
|
|
52
|
+
setup(opts?: BaseOptions) {
|
|
53
|
+
const { statusCallback = () => {} } = opts || {}
|
|
54
|
+
return updateStatus('Downloading index', statusCallback, () =>
|
|
55
|
+
this.setupPre(),
|
|
56
|
+
)
|
|
57
|
+
}
|
|
40
58
|
|
|
41
59
|
async readTaiFile() {
|
|
42
60
|
const text = await openLocation(this.getConf('taiLocation')).readFile(
|
|
@@ -81,91 +99,110 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
81
99
|
return entries
|
|
82
100
|
}
|
|
83
101
|
|
|
84
|
-
getFeatures(query: Region) {
|
|
102
|
+
getFeatures(query: Region, opts?: BaseOptions) {
|
|
103
|
+
const { statusCallback = () => {} } = opts || {}
|
|
104
|
+
console.log(query.start, query.end, 'wtf')
|
|
85
105
|
return ObservableCreate<Feature>(async observer => {
|
|
86
106
|
try {
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
const byteRanges = await this.setup()
|
|
108
|
+
const buffer = await updateStatus(
|
|
109
|
+
'Downloading alignments',
|
|
110
|
+
statusCallback,
|
|
111
|
+
() => this.getLines(query, byteRanges),
|
|
112
|
+
)
|
|
113
|
+
if (buffer) {
|
|
114
|
+
const alignments = {} as Record<string, OrganismRecord>
|
|
115
|
+
const data = [] as Entry[]
|
|
116
|
+
let a0: any
|
|
117
|
+
let j = 0
|
|
118
|
+
let b = 0
|
|
119
|
+
parseLineByLine(buffer, line => {
|
|
120
|
+
if (j++ % 100 === 0) {
|
|
121
|
+
statusCallback(
|
|
122
|
+
`Processing ${toP(b / 1_000_000)}/${toP(buffer.length / 1_000_000)}Mb`,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
b += line.length
|
|
126
|
+
if (line) {
|
|
127
|
+
const [lineData, rowInstructions] = line.split(' ; ')
|
|
128
|
+
if (rowInstructions) {
|
|
129
|
+
for (const ins of parseRowInstructions(rowInstructions)) {
|
|
130
|
+
if (ins.type === 'i') {
|
|
131
|
+
data.splice(ins.row, 0, ins)
|
|
132
|
+
if (!alignments[ins.asm]) {
|
|
133
|
+
alignments[ins.asm] = {
|
|
134
|
+
start: ins.start,
|
|
135
|
+
strand: ins.strand,
|
|
136
|
+
srcSize: ins.length,
|
|
137
|
+
chr: ins.ref,
|
|
138
|
+
data: '',
|
|
139
|
+
}
|
|
108
140
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
141
|
+
const e = alignments[ins.asm]!
|
|
142
|
+
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
143
|
+
} else if (ins.type === 's') {
|
|
144
|
+
if (!alignments[ins.asm]) {
|
|
145
|
+
alignments[ins.asm] = {
|
|
146
|
+
start: ins.start,
|
|
147
|
+
strand: ins.strand,
|
|
148
|
+
srcSize: ins.length,
|
|
149
|
+
chr: ins.ref,
|
|
150
|
+
data: '',
|
|
151
|
+
}
|
|
120
152
|
}
|
|
153
|
+
const e = alignments[ins.asm]!
|
|
154
|
+
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
155
|
+
data[ins.row] = ins
|
|
156
|
+
} else if (ins.type === 'd') {
|
|
157
|
+
data.splice(ins.row, 1)
|
|
121
158
|
}
|
|
122
|
-
const e = alignments[ins.asm]!
|
|
123
|
-
e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
|
|
124
|
-
data[ins.row] = ins
|
|
125
|
-
} else if (ins.type === 'd') {
|
|
126
|
-
data.splice(ins.row, 1)
|
|
127
|
-
}
|
|
128
159
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
160
|
+
// no gaps for now(?)
|
|
161
|
+
// else if (ins.type === 'g') {
|
|
162
|
+
// }
|
|
163
|
+
// else if (ins.type === 'G') {
|
|
164
|
+
// }
|
|
165
|
+
}
|
|
166
|
+
if (!a0) {
|
|
167
|
+
a0 = data[0]
|
|
168
|
+
}
|
|
134
169
|
}
|
|
135
|
-
|
|
136
|
-
|
|
170
|
+
const lineLen = lineData!.length
|
|
171
|
+
for (let i = 0; i < lineLen; i++) {
|
|
172
|
+
const letter = lineData![i]
|
|
173
|
+
const r = data[i]
|
|
174
|
+
if (r) {
|
|
175
|
+
alignments[r.asm]!.data += letter
|
|
176
|
+
} else {
|
|
177
|
+
// not sure why but chr22_KI270731v1_random.taf.gz ends up here
|
|
178
|
+
}
|
|
137
179
|
}
|
|
138
180
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
181
|
+
})
|
|
182
|
+
if (a0) {
|
|
183
|
+
const row0 = alignments[a0.asm]!
|
|
184
|
+
|
|
185
|
+
// see
|
|
186
|
+
// https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
|
|
187
|
+
// for the significance of row[0]:
|
|
188
|
+
//
|
|
189
|
+
// "An anchor line in TAF is a column from which all sequence
|
|
190
|
+
// coordinates can be deduced without scanning backwards to previous
|
|
191
|
+
// lines "
|
|
192
|
+
observer.next(
|
|
193
|
+
new SimpleFeature({
|
|
194
|
+
uniqueId: `${row0.start}-${row0.data.length}`,
|
|
195
|
+
refName: query.refName,
|
|
196
|
+
start: row0.start,
|
|
197
|
+
end: row0.start + row0.data.length,
|
|
198
|
+
strand: row0.strand,
|
|
199
|
+
alignments,
|
|
200
|
+
seq: row0.data,
|
|
201
|
+
}),
|
|
202
|
+
)
|
|
145
203
|
}
|
|
146
204
|
}
|
|
147
|
-
|
|
148
|
-
const row0 = alignments[a0.asm]!
|
|
149
|
-
|
|
150
|
-
// see
|
|
151
|
-
// https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
|
|
152
|
-
// for the significance of row[0]:
|
|
153
|
-
//
|
|
154
|
-
// "An anchor line in TAF is a column from which all sequence
|
|
155
|
-
// coordinates can be deduced without scanning backwards to previous
|
|
156
|
-
// lines "
|
|
157
|
-
observer.next(
|
|
158
|
-
new SimpleFeature({
|
|
159
|
-
uniqueId: `${row0.start}-${row0.data.length}`,
|
|
160
|
-
refName: query.refName,
|
|
161
|
-
start: row0.start,
|
|
162
|
-
end: row0.start + row0.data.length,
|
|
163
|
-
strand: row0.strand,
|
|
164
|
-
alignments,
|
|
165
|
-
seq: row0.data,
|
|
166
|
-
}),
|
|
167
|
-
)
|
|
168
|
-
}
|
|
205
|
+
statusCallback('')
|
|
169
206
|
observer.complete()
|
|
170
207
|
} catch (e) {
|
|
171
208
|
observer.error(e)
|
|
@@ -188,15 +225,14 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
188
225
|
}
|
|
189
226
|
}
|
|
190
227
|
|
|
191
|
-
|
|
192
|
-
|
|
228
|
+
// TODO: cache processed large chunks
|
|
229
|
+
async getLines(query: Region, byteRanges: IndexData) {
|
|
193
230
|
const file = openLocation(this.getConf('tafGzLocation'))
|
|
194
|
-
|
|
195
|
-
const decoder = new TextDecoder('utf8')
|
|
196
231
|
const records = byteRanges[query.refName]
|
|
197
232
|
if (records) {
|
|
198
|
-
let firstEntry
|
|
233
|
+
let firstEntry
|
|
199
234
|
let nextEntry
|
|
235
|
+
|
|
200
236
|
// two pass:
|
|
201
237
|
// first pass: find first block greater than query start, then -1 from
|
|
202
238
|
// that
|
|
@@ -216,6 +252,9 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
216
252
|
}
|
|
217
253
|
|
|
218
254
|
nextEntry = nextEntry ?? records.at(-1)
|
|
255
|
+
// we NEED at least a firstEntry (validate behavior?) because othrwise it fetches whole
|
|
256
|
+
// file whn you request e.g. out of range region (e.g. taf in chr22:1-100
|
|
257
|
+
// and you are at chr22:200-300)
|
|
219
258
|
if (firstEntry && nextEntry) {
|
|
220
259
|
const response = await file.read(
|
|
221
260
|
nextEntry.virtualOffset.blockPosition -
|
|
@@ -223,12 +262,10 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
|
|
|
223
262
|
firstEntry.virtualOffset.blockPosition,
|
|
224
263
|
)
|
|
225
264
|
const buffer = await unzip(response)
|
|
226
|
-
return
|
|
227
|
-
.decode(buffer.slice(firstEntry.virtualOffset.dataPosition))
|
|
228
|
-
.split('\n')
|
|
265
|
+
return buffer.slice(firstEntry.virtualOffset.dataPosition)
|
|
229
266
|
}
|
|
230
267
|
}
|
|
231
|
-
return
|
|
268
|
+
return undefined
|
|
232
269
|
}
|
|
233
270
|
|
|
234
271
|
freeResources(): void {}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function parseLineByLine<T>(
|
|
2
|
+
buffer: Uint8Array,
|
|
3
|
+
cb: (line: string) => T | undefined,
|
|
4
|
+
): T[] {
|
|
5
|
+
let blockStart = 0
|
|
6
|
+
const entries: T[] = []
|
|
7
|
+
const decoder = new TextDecoder('utf8')
|
|
8
|
+
while (blockStart < buffer.length) {
|
|
9
|
+
const n = buffer.indexOf(10, blockStart)
|
|
10
|
+
if (n === -1) {
|
|
11
|
+
break
|
|
12
|
+
}
|
|
13
|
+
const b = buffer.subarray(blockStart, n)
|
|
14
|
+
const line = decoder.decode(b).trim()
|
|
15
|
+
if (line) {
|
|
16
|
+
const entry = cb(line)
|
|
17
|
+
if (entry) {
|
|
18
|
+
entries.push(entry)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
blockStart = n + 1
|
|
23
|
+
}
|
|
24
|
+
return entries
|
|
25
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
2
|
-
import {
|
|
2
|
+
import { SimpleFeature, updateStatus } from '@jbrowse/core/util'
|
|
3
3
|
import { openLocation } from '@jbrowse/core/util/io'
|
|
4
4
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
5
5
|
import { getSnapshot } from 'mobx-state-tree'
|
|
@@ -8,6 +8,9 @@ import { firstValueFrom, toArray } from 'rxjs'
|
|
|
8
8
|
import parseNewick from '../parseNewick'
|
|
9
9
|
import { normalize } from '../util'
|
|
10
10
|
|
|
11
|
+
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
12
|
+
import type { Feature, Region } from '@jbrowse/core/util'
|
|
13
|
+
|
|
11
14
|
interface OrganismRecord {
|
|
12
15
|
chr: string
|
|
13
16
|
start: number
|
|
@@ -51,11 +54,14 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
51
54
|
return adapter.getHeader()
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
getFeatures(query: Region) {
|
|
57
|
+
getFeatures(query: Region, opts?: BaseOptions) {
|
|
58
|
+
const { statusCallback = () => {} } = opts || {}
|
|
55
59
|
return ObservableCreate<Feature>(async observer => {
|
|
56
60
|
const { adapter } = await this.setup()
|
|
57
|
-
const features = await
|
|
58
|
-
|
|
61
|
+
const features = await updateStatus(
|
|
62
|
+
'Downloading alignment',
|
|
63
|
+
statusCallback,
|
|
64
|
+
() => firstValueFrom(adapter.getFeatures(query).pipe(toArray())),
|
|
59
65
|
)
|
|
60
66
|
for (const feature of features) {
|
|
61
67
|
const maf = feature.get('mafBlock') as string
|
|
@@ -2,7 +2,6 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
|
|
5
|
-
// locals
|
|
6
5
|
import { LinearMafDisplayModel } from '../stateModel'
|
|
7
6
|
import RectBg from './RectBg'
|
|
8
7
|
import Tree from './Tree'
|
|
@@ -16,7 +15,7 @@ const ColorLegend = observer(function ({
|
|
|
16
15
|
svgFontSize: number
|
|
17
16
|
labelWidth: number
|
|
18
17
|
}) {
|
|
19
|
-
const { totalHeight, treeWidth, samples, rowHeight } = model
|
|
18
|
+
const { totalHeight, treeWidth, samples = [], rowHeight } = model
|
|
20
19
|
const canDisplayLabel = rowHeight >= 8
|
|
21
20
|
const boxHeight = Math.min(20, rowHeight)
|
|
22
21
|
|
|
@@ -18,7 +18,7 @@ const SvgWrapper = observer(function ({
|
|
|
18
18
|
if (exportSVG) {
|
|
19
19
|
return <>{children}</>
|
|
20
20
|
} else {
|
|
21
|
-
const {
|
|
21
|
+
const { totalHeight } = model
|
|
22
22
|
return (
|
|
23
23
|
<svg
|
|
24
24
|
style={{
|
|
@@ -26,7 +26,7 @@ const SvgWrapper = observer(function ({
|
|
|
26
26
|
top: 0,
|
|
27
27
|
left: 0,
|
|
28
28
|
pointerEvents: 'none',
|
|
29
|
-
height:
|
|
29
|
+
height: totalHeight,
|
|
30
30
|
width: getContainingView(model).width,
|
|
31
31
|
}}
|
|
32
32
|
>
|
|
@@ -22,8 +22,9 @@ export const YScaleBars = observer(function (props: {
|
|
|
22
22
|
|
|
23
23
|
const labelWidth = max(
|
|
24
24
|
samples
|
|
25
|
-
|
|
26
|
-
.map(width => (canDisplayLabel ? width : minWidth)),
|
|
25
|
+
?.map(s => measureText(s.label, svgFontSize))
|
|
26
|
+
.map(width => (canDisplayLabel ? width : minWidth)) || [],
|
|
27
|
+
0,
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
return (
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import { getContainingView } from '@jbrowse/core/util'
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import YScaleBars from './components/YScaleBars'
|
|
6
|
+
|
|
7
|
+
import type { LinearMafDisplayModel } from './stateModel'
|
|
8
|
+
import type {
|
|
5
9
|
ExportSvgDisplayOptions,
|
|
6
10
|
LinearGenomeViewModel,
|
|
7
11
|
} from '@jbrowse/plugin-linear-genome-view'
|
|
8
12
|
|
|
9
|
-
// locals
|
|
10
|
-
import YScaleBars from './components/YScaleBars'
|
|
11
|
-
import { LinearMafDisplayModel } from './stateModel'
|
|
12
|
-
|
|
13
13
|
export async function renderSvg(
|
|
14
14
|
self: LinearMafDisplayModel,
|
|
15
15
|
opts: ExportSvgDisplayOptions,
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
AnyConfigurationModel,
|
|
4
|
-
AnyConfigurationSchemaType,
|
|
5
|
-
ConfigurationReference,
|
|
6
|
-
getConf,
|
|
7
|
-
} from '@jbrowse/core/configuration'
|
|
1
|
+
import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
|
|
8
2
|
import { getEnv, getSession } from '@jbrowse/core/util'
|
|
9
3
|
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
|
|
10
|
-
import { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
|
|
11
4
|
import { ascending } from 'd3-array'
|
|
12
|
-
import {
|
|
5
|
+
import { cluster, hierarchy } from 'd3-hierarchy'
|
|
13
6
|
import { autorun } from 'mobx'
|
|
14
|
-
import {
|
|
7
|
+
import { addDisposer, isAlive, types } from 'mobx-state-tree'
|
|
8
|
+
import deepEqual from 'fast-deep-equal'
|
|
15
9
|
|
|
16
10
|
import SetRowHeightDialog from './components/SetRowHeight'
|
|
17
|
-
import {
|
|
18
|
-
NodeWithIds,
|
|
19
|
-
NodeWithIdsAndLength,
|
|
20
|
-
maxLength,
|
|
21
|
-
setBrLength,
|
|
22
|
-
} from './types'
|
|
11
|
+
import { maxLength, setBrLength } from './types'
|
|
23
12
|
import { normalize } from '../util'
|
|
24
13
|
|
|
14
|
+
import type { NodeWithIds, NodeWithIdsAndLength } from './types'
|
|
15
|
+
import type PluginManager from '@jbrowse/core/PluginManager'
|
|
16
|
+
import type {
|
|
17
|
+
AnyConfigurationModel,
|
|
18
|
+
AnyConfigurationSchemaType,
|
|
19
|
+
} from '@jbrowse/core/configuration'
|
|
20
|
+
import type { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
|
|
21
|
+
import type { HierarchyNode } from 'd3-hierarchy'
|
|
22
|
+
import type { Instance } from 'mobx-state-tree'
|
|
23
|
+
|
|
25
24
|
interface Sample {
|
|
26
25
|
id: string
|
|
27
26
|
label: string
|
|
@@ -90,11 +89,11 @@ export default function stateModelFactory(
|
|
|
90
89
|
/**
|
|
91
90
|
* #volatile
|
|
92
91
|
*/
|
|
93
|
-
volatileSamples:
|
|
92
|
+
volatileSamples: undefined as Sample[] | undefined,
|
|
94
93
|
/**
|
|
95
94
|
* #volatile
|
|
96
95
|
*/
|
|
97
|
-
|
|
96
|
+
volatileTree: undefined as any,
|
|
98
97
|
}))
|
|
99
98
|
.actions(self => ({
|
|
100
99
|
/**
|
|
@@ -125,8 +124,12 @@ export default function stateModelFactory(
|
|
|
125
124
|
* #action
|
|
126
125
|
*/
|
|
127
126
|
setSamples({ samples, tree }: { samples: Sample[]; tree: unknown }) {
|
|
128
|
-
self.volatileSamples
|
|
129
|
-
|
|
127
|
+
if (!deepEqual(samples, self.volatileSamples)) {
|
|
128
|
+
self.volatileSamples = samples
|
|
129
|
+
}
|
|
130
|
+
if (!deepEqual(tree, self.volatileTree)) {
|
|
131
|
+
self.volatileTree = tree
|
|
132
|
+
}
|
|
130
133
|
},
|
|
131
134
|
}))
|
|
132
135
|
.views(self => ({
|
|
@@ -159,8 +162,8 @@ export default function stateModelFactory(
|
|
|
159
162
|
* #getter
|
|
160
163
|
*/
|
|
161
164
|
get root() {
|
|
162
|
-
return self.
|
|
163
|
-
? hierarchy(self.
|
|
165
|
+
return self.volatileTree
|
|
166
|
+
? hierarchy(self.volatileTree, d => d.children)
|
|
164
167
|
// todo: investigate whether needed, typescript says children always true
|
|
165
168
|
.sum(d => (d.children ? 0 : 1))
|
|
166
169
|
.sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
|
|
@@ -196,7 +199,7 @@ export default function stateModelFactory(
|
|
|
196
199
|
* #getter
|
|
197
200
|
*/
|
|
198
201
|
get totalHeight() {
|
|
199
|
-
return this.samples.length * self.rowHeight
|
|
202
|
+
return this.samples ? this.samples.length * self.rowHeight : 1
|
|
200
203
|
},
|
|
201
204
|
/**
|
|
202
205
|
* #getter
|
|
@@ -237,8 +240,11 @@ export default function stateModelFactory(
|
|
|
237
240
|
rowProportion,
|
|
238
241
|
mismatchRendering,
|
|
239
242
|
} = self
|
|
243
|
+
const s = superRenderProps()
|
|
240
244
|
return {
|
|
241
|
-
...
|
|
245
|
+
...s,
|
|
246
|
+
notReady:
|
|
247
|
+
!self.volatileSamples || !self.volatileTree || super.notReady,
|
|
242
248
|
config: rendererConfig,
|
|
243
249
|
samples,
|
|
244
250
|
rowHeight,
|