@jbrowse/plugin-alignments 2.3.1 → 2.3.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/LinearReadArcsDisplay/configSchema.js +8 -0
- package/dist/LinearReadArcsDisplay/configSchema.js.map +1 -1
- package/dist/LinearReadArcsDisplay/drawFeats.d.ts +1 -0
- package/dist/LinearReadArcsDisplay/drawFeats.js +45 -37
- package/dist/LinearReadArcsDisplay/drawFeats.js.map +1 -1
- package/dist/LinearReadArcsDisplay/model.d.ts +46 -15
- package/dist/LinearReadArcsDisplay/model.js +42 -0
- package/dist/LinearReadArcsDisplay/model.js.map +1 -1
- package/dist/LinearReadCloudDisplay/drawFeats.js +18 -32
- package/dist/LinearReadCloudDisplay/drawFeats.js.map +1 -1
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +1 -1
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.js.map +1 -1
- package/esm/LinearReadArcsDisplay/configSchema.js +8 -0
- package/esm/LinearReadArcsDisplay/configSchema.js.map +1 -1
- package/esm/LinearReadArcsDisplay/drawFeats.d.ts +1 -0
- package/esm/LinearReadArcsDisplay/drawFeats.js +45 -37
- package/esm/LinearReadArcsDisplay/drawFeats.js.map +1 -1
- package/esm/LinearReadArcsDisplay/model.d.ts +46 -15
- package/esm/LinearReadArcsDisplay/model.js +42 -0
- package/esm/LinearReadArcsDisplay/model.js.map +1 -1
- package/esm/LinearReadCloudDisplay/drawFeats.js +19 -33
- package/esm/LinearReadCloudDisplay/drawFeats.js.map +1 -1
- package/esm/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
- package/esm/LinearSNPCoverageDisplay/components/Tooltip.js +1 -1
- package/esm/LinearSNPCoverageDisplay/components/Tooltip.js.map +1 -1
- package/package.json +2 -2
- package/src/LinearReadArcsDisplay/configSchema.ts +10 -0
- package/src/LinearReadArcsDisplay/drawFeats.ts +73 -54
- package/src/LinearReadArcsDisplay/model.tsx +45 -0
- package/src/LinearReadCloudDisplay/drawFeats.ts +21 -33
- package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +76 -73
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from '../shared/color'
|
|
10
10
|
import { ChainData } from '../shared/fetchChains'
|
|
11
11
|
import { featurizeSA } from '../MismatchParser'
|
|
12
|
+
import { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
12
13
|
|
|
13
14
|
export function hasPairedReads(features: ChainData) {
|
|
14
15
|
for (const f of features.chains.values()) {
|
|
@@ -21,6 +22,17 @@ export function hasPairedReads(features: ChainData) {
|
|
|
21
22
|
|
|
22
23
|
type LGV = LinearGenomeViewModel
|
|
23
24
|
|
|
25
|
+
function jitter(n: number) {
|
|
26
|
+
return Math.random() * 2 * n - n
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface CoreFeat {
|
|
30
|
+
strand: number
|
|
31
|
+
refName: string
|
|
32
|
+
start: number
|
|
33
|
+
end: number
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export default async function drawFeats(
|
|
25
37
|
self: {
|
|
26
38
|
setLastDrawnOffsetPx: (n: number) => void
|
|
@@ -31,34 +43,48 @@ export default async function drawFeats(
|
|
|
31
43
|
height: number
|
|
32
44
|
chainData?: ChainData
|
|
33
45
|
lineWidthSetting: number
|
|
46
|
+
jitterVal: number
|
|
34
47
|
},
|
|
35
48
|
ctx: CanvasRenderingContext2D,
|
|
36
49
|
) {
|
|
37
|
-
const {
|
|
50
|
+
const {
|
|
51
|
+
chainData,
|
|
52
|
+
height,
|
|
53
|
+
colorBy,
|
|
54
|
+
drawInter,
|
|
55
|
+
drawLongRange,
|
|
56
|
+
lineWidthSetting,
|
|
57
|
+
jitterVal,
|
|
58
|
+
} = self
|
|
38
59
|
if (!chainData) {
|
|
39
60
|
return
|
|
40
61
|
}
|
|
41
|
-
const displayHeight = self.height
|
|
42
62
|
const view = getContainingView(self) as LGV
|
|
43
63
|
const { assemblyManager } = getSession(self)
|
|
44
64
|
self.setLastDrawnOffsetPx(view.offsetPx)
|
|
45
|
-
ctx.lineWidth =
|
|
65
|
+
ctx.lineWidth = lineWidthSetting
|
|
46
66
|
const { chains, stats } = chainData
|
|
47
67
|
const hasPaired = hasPairedReads(chainData)
|
|
48
68
|
const assemblyName = view.assemblyNames[0]
|
|
49
69
|
const asm = assemblyManager.get(assemblyName)
|
|
50
|
-
const type =
|
|
70
|
+
const type = colorBy?.type || 'insertSizeAndOrientation'
|
|
71
|
+
if (!asm) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function drawLineAtOffset(p: number, c: string) {
|
|
76
|
+
// draws a vertical line off to middle of nowhere if the second end not found
|
|
77
|
+
ctx.strokeStyle = c
|
|
78
|
+
ctx.beginPath()
|
|
79
|
+
ctx.moveTo(p, 0)
|
|
80
|
+
ctx.lineTo(p, height)
|
|
81
|
+
ctx.stroke()
|
|
82
|
+
}
|
|
51
83
|
|
|
52
84
|
function draw(
|
|
53
|
-
k1: {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
start: number
|
|
57
|
-
end: number
|
|
58
|
-
tlen?: number
|
|
59
|
-
pair_orientation?: string
|
|
60
|
-
},
|
|
61
|
-
k2: { strand: number; refName: string; start: number; end: number },
|
|
85
|
+
k1: CoreFeat & { tlen?: number; pair_orientation?: string },
|
|
86
|
+
k2: CoreFeat,
|
|
87
|
+
assembly: Assembly,
|
|
62
88
|
longRange?: boolean,
|
|
63
89
|
) {
|
|
64
90
|
const s1 = k1.strand
|
|
@@ -68,14 +94,16 @@ export default async function drawFeats(
|
|
|
68
94
|
|
|
69
95
|
const p1 = f1 ? k1.start : k1.end
|
|
70
96
|
const p2 = hasPaired ? (f2 ? k2.start : k2.end) : f2 ? k2.end : k2.start
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const
|
|
97
|
+
const ra1 = assembly.getCanonicalRefName(k1.refName)
|
|
98
|
+
const ra2 = assembly.getCanonicalRefName(k2.refName)
|
|
99
|
+
const r1 = view.bpToPx({ refName: ra1, coord: p1 })
|
|
100
|
+
const r2 = view.bpToPx({ refName: ra2, coord: p2 })
|
|
74
101
|
|
|
75
102
|
if (r1 && r2) {
|
|
76
103
|
const radius = (r2.offsetPx - r1.offsetPx) / 2
|
|
77
104
|
const absrad = Math.abs(radius)
|
|
78
105
|
const p = r1.offsetPx - view.offsetPx
|
|
106
|
+
const p2 = r2.offsetPx - view.offsetPx
|
|
79
107
|
|
|
80
108
|
// bezier (used for non-long-range arcs) requires moveTo before beginPath
|
|
81
109
|
// arc (used for long-range) requires moveTo after beginPath (or else a
|
|
@@ -99,9 +127,7 @@ export default async function drawFeats(
|
|
|
99
127
|
} else if (type === 'insertSize') {
|
|
100
128
|
ctx.strokeStyle = getInsertSizeColor(k1, k2, stats) || 'grey'
|
|
101
129
|
} else if (type === 'gradient') {
|
|
102
|
-
ctx.strokeStyle = `hsl(${
|
|
103
|
-
Math.log10(Math.abs(p1 - p2)) * 10
|
|
104
|
-
},50%,50%)`
|
|
130
|
+
ctx.strokeStyle = `hsl(${Math.log10(absrad) * 10},50%,50%)`
|
|
105
131
|
}
|
|
106
132
|
} else {
|
|
107
133
|
if (type === 'orientation' || type === 'insertSizeAndOrientation') {
|
|
@@ -113,53 +139,55 @@ export default async function drawFeats(
|
|
|
113
139
|
ctx.strokeStyle = 'grey'
|
|
114
140
|
}
|
|
115
141
|
} else if (type === 'gradient') {
|
|
116
|
-
ctx.strokeStyle = `hsl(${
|
|
117
|
-
Math.log10(Math.abs(p1 - p2)) * 10
|
|
118
|
-
},50%,50%)`
|
|
142
|
+
ctx.strokeStyle = `hsl(${Math.log10(absrad) * 10},50%,50%)`
|
|
119
143
|
}
|
|
120
144
|
}
|
|
121
145
|
}
|
|
122
146
|
|
|
123
147
|
const destX = p + radius * 2
|
|
124
|
-
const destY = Math.min(
|
|
148
|
+
const destY = Math.min(height + jitter(jitterVal), absrad)
|
|
125
149
|
if (longRange) {
|
|
126
|
-
|
|
150
|
+
// avoid drawing gigantic circles that glitch out the rendering,
|
|
151
|
+
// instead draw vertical lines
|
|
152
|
+
if (absrad > 100_000) {
|
|
153
|
+
drawLineAtOffset(p + jitter(jitterVal), 'red')
|
|
154
|
+
drawLineAtOffset(p2 + jitter(jitterVal), 'red')
|
|
155
|
+
} else {
|
|
156
|
+
ctx.arc(p + radius + jitter(jitterVal), 0, absrad, 0, Math.PI)
|
|
157
|
+
ctx.stroke()
|
|
158
|
+
}
|
|
127
159
|
} else {
|
|
128
|
-
ctx.bezierCurveTo(
|
|
160
|
+
ctx.bezierCurveTo(
|
|
161
|
+
p + jitter(jitterVal),
|
|
162
|
+
destY,
|
|
163
|
+
destX,
|
|
164
|
+
destY,
|
|
165
|
+
destX + jitter(jitterVal),
|
|
166
|
+
0,
|
|
167
|
+
)
|
|
168
|
+
ctx.stroke()
|
|
129
169
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// draws a vertical line off to middle of nowhere if the second end not found
|
|
133
|
-
const p = r1.offsetPx - view.offsetPx
|
|
134
|
-
ctx.strokeStyle = 'purple'
|
|
135
|
-
ctx.beginPath()
|
|
136
|
-
ctx.moveTo(p, 0)
|
|
137
|
-
ctx.lineTo(p, displayHeight)
|
|
138
|
-
ctx.stroke()
|
|
170
|
+
} else if (r1 && drawInter) {
|
|
171
|
+
drawLineAtOffset(r1.offsetPx - view.offsetPx, 'purple')
|
|
139
172
|
}
|
|
140
173
|
}
|
|
141
174
|
|
|
142
175
|
for (let i = 0; i < chains.length; i++) {
|
|
143
176
|
let chain = chains[i]
|
|
144
|
-
|
|
145
|
-
if (chain.length === 1 && self.drawLongRange) {
|
|
177
|
+
if (chain.length === 1 && drawLongRange) {
|
|
146
178
|
// singleton feature
|
|
147
179
|
const f = chain[0]
|
|
148
180
|
|
|
149
181
|
// special case where we look at RPOS/RNEXT
|
|
150
182
|
if (hasPaired) {
|
|
151
183
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
152
|
-
const refName =
|
|
184
|
+
const refName = f.next_ref!
|
|
153
185
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
154
186
|
const coord = f.next_pos!
|
|
155
187
|
draw(
|
|
156
188
|
f,
|
|
157
|
-
{
|
|
158
|
-
|
|
159
|
-
start: coord,
|
|
160
|
-
end: coord,
|
|
161
|
-
strand: f.strand,
|
|
162
|
-
},
|
|
189
|
+
{ refName, start: coord, end: coord, strand: f.strand },
|
|
190
|
+
asm,
|
|
163
191
|
true,
|
|
164
192
|
)
|
|
165
193
|
}
|
|
@@ -171,16 +199,7 @@ export default async function drawFeats(
|
|
|
171
199
|
for (let i = 0; i < features.length - 1; i++) {
|
|
172
200
|
const f = features[i]
|
|
173
201
|
const v1 = features[i + 1]
|
|
174
|
-
draw(
|
|
175
|
-
f,
|
|
176
|
-
{
|
|
177
|
-
refName: asm?.getCanonicalRefName(v1.refName) || v1.refName,
|
|
178
|
-
start: v1.start,
|
|
179
|
-
end: v1.end,
|
|
180
|
-
strand: v1.strand,
|
|
181
|
-
},
|
|
182
|
-
true,
|
|
183
|
-
)
|
|
202
|
+
draw(f, v1, asm, true)
|
|
184
203
|
}
|
|
185
204
|
}
|
|
186
205
|
} else {
|
|
@@ -192,7 +211,7 @@ export default async function drawFeats(
|
|
|
192
211
|
chain = chain.filter(f => !(f.flags & 2048))
|
|
193
212
|
}
|
|
194
213
|
for (let i = 0; i < chain.length - 1; i++) {
|
|
195
|
-
draw(chain[i], chain[i + 1], false)
|
|
214
|
+
draw(chain[i], chain[i + 1], asm, false)
|
|
196
215
|
}
|
|
197
216
|
}
|
|
198
217
|
}
|
|
@@ -65,6 +65,11 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) {
|
|
|
65
65
|
*/
|
|
66
66
|
lineWidth: types.maybe(types.number),
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* #property
|
|
70
|
+
*/
|
|
71
|
+
jitter: types.maybe(types.number),
|
|
72
|
+
|
|
68
73
|
/**
|
|
69
74
|
* #property
|
|
70
75
|
*/
|
|
@@ -175,6 +180,15 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) {
|
|
|
175
180
|
setLineWidth(n: number) {
|
|
176
181
|
self.lineWidth = n
|
|
177
182
|
},
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* #action
|
|
186
|
+
* jitter val, helpful to jitter the x direction so you see better evidence when e.g. 100
|
|
187
|
+
* long reads map to same x position
|
|
188
|
+
*/
|
|
189
|
+
setJitter(n: number) {
|
|
190
|
+
self.jitter = n
|
|
191
|
+
},
|
|
178
192
|
}))
|
|
179
193
|
|
|
180
194
|
.views(self => {
|
|
@@ -190,6 +204,13 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) {
|
|
|
190
204
|
get lineWidthSetting() {
|
|
191
205
|
return self.lineWidth ?? getConf(self, 'lineWidth')
|
|
192
206
|
},
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* #getter
|
|
210
|
+
*/
|
|
211
|
+
get jitterVal(): number {
|
|
212
|
+
return self.jitter ?? getConf(self, 'jitter')
|
|
213
|
+
},
|
|
193
214
|
/**
|
|
194
215
|
* #getter
|
|
195
216
|
*/
|
|
@@ -245,6 +266,30 @@ function stateModelFactory(configSchema: AnyConfigurationSchemaType) {
|
|
|
245
266
|
},
|
|
246
267
|
],
|
|
247
268
|
},
|
|
269
|
+
{
|
|
270
|
+
label: 'Jitter x-positions',
|
|
271
|
+
subMenu: [
|
|
272
|
+
{
|
|
273
|
+
type: 'checkbox',
|
|
274
|
+
checked: this.jitterVal === 0,
|
|
275
|
+
label: 'None',
|
|
276
|
+
onClick: () => self.setJitter(0),
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
type: 'checkbox',
|
|
280
|
+
checked: this.jitterVal === 2,
|
|
281
|
+
label: 'Small',
|
|
282
|
+
onClick: () => self.setJitter(2),
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
type: 'checkbox',
|
|
286
|
+
checked: this.jitterVal === 10,
|
|
287
|
+
label: 'Large',
|
|
288
|
+
onClick: () => self.setJitter(10),
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
},
|
|
292
|
+
|
|
248
293
|
{
|
|
249
294
|
label: 'Draw inter-region vertical lines',
|
|
250
295
|
type: 'checkbox',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getConf } from '@jbrowse/core/configuration'
|
|
2
|
-
import { getContainingView } from '@jbrowse/core/util'
|
|
2
|
+
import { getContainingView, getSession } from '@jbrowse/core/util'
|
|
3
3
|
|
|
4
4
|
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
5
5
|
// locals
|
|
@@ -60,9 +60,15 @@ export default async function drawFeats(
|
|
|
60
60
|
if (!chainData) {
|
|
61
61
|
return
|
|
62
62
|
}
|
|
63
|
+
const { assemblyManager } = getSession(self)
|
|
63
64
|
const featureHeight = getConf(self, 'featureHeight')
|
|
64
65
|
const displayHeight = self.height
|
|
65
66
|
const view = getContainingView(self) as LGV
|
|
67
|
+
const assemblyName = view.assemblyNames[0]
|
|
68
|
+
const asm = assemblyManager.get(assemblyName)
|
|
69
|
+
if (!asm) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
66
72
|
|
|
67
73
|
self.setLastDrawnOffsetPx(view.offsetPx)
|
|
68
74
|
|
|
@@ -76,22 +82,13 @@ export default async function drawFeats(
|
|
|
76
82
|
if (chain[0].flags & 1 && chain.length > 1) {
|
|
77
83
|
const v0 = chain[0]
|
|
78
84
|
const v1 = chain[1]
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
})
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})
|
|
87
|
-
const r2s = view.bpToPx({
|
|
88
|
-
refName: v1.refName,
|
|
89
|
-
coord: v1.start,
|
|
90
|
-
})
|
|
91
|
-
const r2e = view.bpToPx({
|
|
92
|
-
refName: v1.refName,
|
|
93
|
-
coord: v1.end,
|
|
94
|
-
})
|
|
85
|
+
const ra1 = asm.getCanonicalRefName(v0.refName)
|
|
86
|
+
const ra2 = asm.getCanonicalRefName(v1.refName)
|
|
87
|
+
const r1s = view.bpToPx({ refName: ra1, coord: v0.start })
|
|
88
|
+
const r1e = view.bpToPx({ refName: ra1, coord: v0.end })
|
|
89
|
+
const r2s = view.bpToPx({ refName: ra2, coord: v1.start })
|
|
90
|
+
const r2e = view.bpToPx({ refName: ra2, coord: v1.end })
|
|
91
|
+
|
|
95
92
|
let distance = 0
|
|
96
93
|
|
|
97
94
|
if (
|
|
@@ -121,22 +118,13 @@ export default async function drawFeats(
|
|
|
121
118
|
for (let i = 1; i < chain.length; i++) {
|
|
122
119
|
const v0 = chain[i - 1]
|
|
123
120
|
const v1 = chain[i]
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
})
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
})
|
|
132
|
-
const r2s = view.bpToPx({
|
|
133
|
-
refName: v1.refName,
|
|
134
|
-
coord: v1.start,
|
|
135
|
-
})
|
|
136
|
-
const r2e = view.bpToPx({
|
|
137
|
-
refName: v1.refName,
|
|
138
|
-
coord: v1.end,
|
|
139
|
-
})
|
|
121
|
+
const ra1 = asm.getCanonicalRefName(v0.refName)
|
|
122
|
+
const ra2 = asm.getCanonicalRefName(v1.refName)
|
|
123
|
+
const r1s = view.bpToPx({ refName: ra1, coord: v0.start })
|
|
124
|
+
const r1e = view.bpToPx({ refName: ra1, coord: v0.end })
|
|
125
|
+
const r2s = view.bpToPx({ refName: ra2, coord: v1.start })
|
|
126
|
+
const r2e = view.bpToPx({ refName: ra2, coord: v1.end })
|
|
127
|
+
|
|
140
128
|
let distance = 0
|
|
141
129
|
|
|
142
130
|
if (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
|
-
import { Feature } from '@jbrowse/core/util
|
|
3
|
+
import { Feature } from '@jbrowse/core/util'
|
|
4
4
|
import { Tooltip } from '@jbrowse/plugin-wiggle'
|
|
5
5
|
|
|
6
6
|
type Count = {
|
|
@@ -29,80 +29,83 @@ type SNPInfo = {
|
|
|
29
29
|
const en = (n: number) => n.toLocaleString('en-US')
|
|
30
30
|
const toP = (s = 0) => +(+s).toFixed(1)
|
|
31
31
|
const pct = (n: number, total: number) => `${toP((n / (total || 1)) * 100)}%`
|
|
32
|
+
interface Props {
|
|
33
|
+
feature: Feature
|
|
34
|
+
}
|
|
35
|
+
const TooltipContents = React.forwardRef<HTMLDivElement, Props>(function (
|
|
36
|
+
{ feature },
|
|
37
|
+
reactRef,
|
|
38
|
+
) {
|
|
39
|
+
const start = feature.get('start')
|
|
40
|
+
const end = feature.get('end')
|
|
41
|
+
const name = feature.get('refName')
|
|
42
|
+
const {
|
|
43
|
+
refbase,
|
|
44
|
+
all,
|
|
45
|
+
total,
|
|
46
|
+
ref,
|
|
47
|
+
'-1': rn1,
|
|
48
|
+
'1': r1,
|
|
49
|
+
'0': r0,
|
|
50
|
+
...info
|
|
51
|
+
} = feature.get('snpinfo') as SNPInfo
|
|
52
|
+
const loc = [name, start === end ? en(start) : `${en(start)}..${en(end)}`]
|
|
53
|
+
.filter(f => !!f)
|
|
54
|
+
.join(':')
|
|
32
55
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<th>Strands</th>
|
|
62
|
-
<th>Source</th>
|
|
63
|
-
</tr>
|
|
64
|
-
</thead>
|
|
65
|
-
<tbody>
|
|
66
|
-
<tr>
|
|
67
|
-
<td>Total</td>
|
|
68
|
-
<td>{all}</td>
|
|
69
|
-
</tr>
|
|
70
|
-
<tr>
|
|
71
|
-
<td>REF {refbase ? `(${refbase.toUpperCase()})` : ''}</td>
|
|
72
|
-
<td>{ref}</td>
|
|
73
|
-
<td>{pct(ref, all)}</td>
|
|
74
|
-
<td>
|
|
75
|
-
{rn1 ? `${rn1}(-)` : ''}
|
|
76
|
-
{r1 ? `${r1}(+)` : ''}
|
|
77
|
-
</td>
|
|
78
|
-
<td />
|
|
79
|
-
</tr>
|
|
56
|
+
return (
|
|
57
|
+
<div ref={reactRef}>
|
|
58
|
+
<table>
|
|
59
|
+
<caption>{loc}</caption>
|
|
60
|
+
<thead>
|
|
61
|
+
<tr>
|
|
62
|
+
<th>Base</th>
|
|
63
|
+
<th>Count</th>
|
|
64
|
+
<th>% of Total</th>
|
|
65
|
+
<th>Strands</th>
|
|
66
|
+
<th>Source</th>
|
|
67
|
+
</tr>
|
|
68
|
+
</thead>
|
|
69
|
+
<tbody>
|
|
70
|
+
<tr>
|
|
71
|
+
<td>Total</td>
|
|
72
|
+
<td>{all}</td>
|
|
73
|
+
</tr>
|
|
74
|
+
<tr>
|
|
75
|
+
<td>REF {refbase ? `(${refbase.toUpperCase()})` : ''}</td>
|
|
76
|
+
<td>{ref}</td>
|
|
77
|
+
<td>{pct(ref, all)}</td>
|
|
78
|
+
<td>
|
|
79
|
+
{rn1 ? `${rn1}(-)` : ''}
|
|
80
|
+
{r1 ? `${r1}(+)` : ''}
|
|
81
|
+
</td>
|
|
82
|
+
<td />
|
|
83
|
+
</tr>
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
)
|
|
85
|
+
{Object.entries(info as unknown as Record<string, Count>).map(
|
|
86
|
+
([key, entry]) =>
|
|
87
|
+
Object.entries(entry).map(([base, score]) => (
|
|
88
|
+
<tr key={base}>
|
|
89
|
+
<td>{base.toUpperCase()}</td>
|
|
90
|
+
<td>{score.total}</td>
|
|
91
|
+
<td>
|
|
92
|
+
{base === 'total' || base === 'skip'
|
|
93
|
+
? '---'
|
|
94
|
+
: pct(score.total, all)}
|
|
95
|
+
</td>
|
|
96
|
+
<td>
|
|
97
|
+
{score['-1'] ? `${score['-1']}(-)` : ''}
|
|
98
|
+
{score['1'] ? `${score['1']}(+)` : ''}
|
|
99
|
+
</td>
|
|
100
|
+
<td>{key}</td>
|
|
101
|
+
</tr>
|
|
102
|
+
)),
|
|
103
|
+
)}
|
|
104
|
+
</tbody>
|
|
105
|
+
</table>
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
})
|
|
106
109
|
|
|
107
110
|
type Coord = [number, number]
|
|
108
111
|
|