jbrowse-plugin-mafviewer 1.3.1 → 1.4.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/README.md +1 -48
- package/dist/BigMafAdapter/BigMafAdapter.js +39 -28
- package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
- package/dist/LinearMafDisplay/components/Crosshairs.js +1 -1
- package/dist/LinearMafDisplay/components/Crosshairs.js.map +1 -1
- package/dist/LinearMafDisplay/components/MAFTooltip.d.ts +2 -3
- package/dist/LinearMafDisplay/components/MAFTooltip.js +6 -19
- package/dist/LinearMafDisplay/components/MAFTooltip.js.map +1 -1
- package/dist/LinearMafDisplay/stateModel.d.ts +8 -0
- package/dist/LinearMafDisplay/stateModel.js +10 -0
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafDisplay/util.d.ts +20 -0
- package/dist/LinearMafDisplay/util.js +29 -0
- package/dist/LinearMafDisplay/util.js.map +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +9 -0
- package/dist/LinearMafRenderer/LinearMafRenderer.js +1 -2
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/components/LinearMafRendering.d.ts +13 -0
- package/dist/LinearMafRenderer/components/LinearMafRendering.js +46 -0
- package/dist/LinearMafRenderer/components/LinearMafRendering.js.map +1 -0
- package/dist/LinearMafRenderer/components/ReactComponent.d.ts +3 -0
- package/dist/LinearMafRenderer/components/ReactComponent.js +41 -2
- package/dist/LinearMafRenderer/components/ReactComponent.js.map +1 -1
- package/dist/LinearMafRenderer/components/util.d.ts +1 -0
- package/dist/LinearMafRenderer/components/util.js +13 -0
- package/dist/LinearMafRenderer/components/util.js.map +1 -0
- package/dist/LinearMafRenderer/index.js +1 -1
- package/dist/LinearMafRenderer/index.js.map +1 -1
- package/dist/LinearMafRenderer/makeImageData.d.ts +6 -5
- package/dist/LinearMafRenderer/makeImageData.js +35 -146
- package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/features.d.ts +4 -0
- package/dist/LinearMafRenderer/rendering/features.js +41 -0
- package/dist/LinearMafRenderer/rendering/features.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/gaps.d.ts +2 -0
- package/dist/LinearMafRenderer/rendering/gaps.js +19 -0
- package/dist/LinearMafRenderer/rendering/gaps.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/index.d.ts +8 -0
- package/dist/LinearMafRenderer/rendering/index.js +10 -0
- package/dist/LinearMafRenderer/rendering/index.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/insertions.d.ts +2 -0
- package/dist/LinearMafRenderer/rendering/insertions.js +78 -0
- package/dist/LinearMafRenderer/rendering/insertions.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/matches.d.ts +2 -0
- package/dist/LinearMafRenderer/rendering/matches.js +34 -0
- package/dist/LinearMafRenderer/rendering/matches.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/mismatches.d.ts +13 -0
- package/dist/LinearMafRenderer/rendering/mismatches.js +57 -0
- package/dist/LinearMafRenderer/rendering/mismatches.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/spatialIndex.d.ts +9 -0
- package/dist/LinearMafRenderer/rendering/spatialIndex.js +19 -0
- package/dist/LinearMafRenderer/rendering/spatialIndex.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/text.d.ts +12 -0
- package/dist/LinearMafRenderer/rendering/text.js +42 -0
- package/dist/LinearMafRenderer/rendering/text.js.map +1 -0
- package/dist/LinearMafRenderer/rendering/types.d.ts +55 -0
- package/dist/LinearMafRenderer/rendering/types.js +15 -0
- package/dist/LinearMafRenderer/rendering/types.js.map +1 -0
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +37 -35
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/MafTabixAdapter/MafTabixAdapter.js +48 -22
- package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +5 -29
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/dist/out.js +32310 -0
- package/dist/out.js.map +7 -0
- package/dist/util/fastaUtils.js.map +1 -1
- package/package.json +3 -2
- package/src/BigMafAdapter/BigMafAdapter.ts +49 -28
- package/src/LinearMafDisplay/components/Crosshairs.tsx +1 -7
- package/src/LinearMafDisplay/components/MAFTooltip.tsx +14 -33
- package/src/LinearMafDisplay/stateModel.ts +10 -0
- package/src/LinearMafDisplay/util.ts +57 -0
- package/src/LinearMafRenderer/LinearMafRenderer.ts +1 -2
- package/src/LinearMafRenderer/components/LinearMafRendering.tsx +76 -0
- package/src/LinearMafRenderer/components/util.ts +13 -0
- package/src/LinearMafRenderer/index.ts +1 -1
- package/src/LinearMafRenderer/makeImageData.ts +64 -196
- package/src/LinearMafRenderer/rendering/features.ts +111 -0
- package/src/LinearMafRenderer/rendering/gaps.ts +33 -0
- package/src/LinearMafRenderer/rendering/index.ts +9 -0
- package/src/LinearMafRenderer/rendering/insertions.ts +154 -0
- package/src/LinearMafRenderer/rendering/matches.ts +62 -0
- package/src/LinearMafRenderer/rendering/mismatches.ts +113 -0
- package/src/LinearMafRenderer/rendering/spatialIndex.ts +40 -0
- package/src/LinearMafRenderer/rendering/text.ts +72 -0
- package/src/LinearMafRenderer/rendering/types.ts +65 -0
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +5 -6
- package/src/MafTabixAdapter/MafTabixAdapter.ts +77 -22
- package/src/index.ts +0 -2
- package/src/util/fastaUtils.ts +2 -1
- package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +0 -307
- package/src/BgzipTaffyAdapter/configSchema.ts +0 -59
- package/src/BgzipTaffyAdapter/index.ts +0 -16
- package/src/BgzipTaffyAdapter/rowInstructions.ts +0 -91
- package/src/BgzipTaffyAdapter/types.ts +0 -16
- package/src/BgzipTaffyAdapter/util.ts +0 -25
- package/src/BgzipTaffyAdapter/virtualOffset.ts +0 -29
- package/src/LinearMafRenderer/components/ReactComponent.tsx +0 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastaUtils.js","sourceRoot":"","sources":["../../src/util/fastaUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fastaUtils.js","sourceRoot":"","sources":["../../src/util/fastaUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.4.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "jbrowse-plugin-mafviewer",
|
|
5
5
|
"keywords": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"eslint-plugin-import": "^2.31.0",
|
|
46
46
|
"eslint-plugin-react": "^7.20.3",
|
|
47
47
|
"eslint-plugin-react-hooks": "^5.1.0",
|
|
48
|
-
"eslint-plugin-unicorn": "^
|
|
48
|
+
"eslint-plugin-unicorn": "^60.0.0",
|
|
49
49
|
"mobx": "^6.0.0",
|
|
50
50
|
"mobx-react": "^9.0.1",
|
|
51
51
|
"mobx-state-tree": "^5.4.1",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"d3-array": "^3.2.4",
|
|
69
69
|
"d3-hierarchy": "^3.1.2",
|
|
70
70
|
"fast-deep-equal": "^3.1.3",
|
|
71
|
+
"flatbush": "^4.4.1",
|
|
71
72
|
"generic-filehandle2": "^2.0.1",
|
|
72
73
|
"long": "^5.2.3"
|
|
73
74
|
}
|
|
@@ -57,6 +57,9 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
57
57
|
|
|
58
58
|
getFeatures(query: Region, opts?: BaseOptions) {
|
|
59
59
|
const { statusCallback = () => {} } = opts || {}
|
|
60
|
+
// Pre-compile regex for better performance
|
|
61
|
+
const WHITESPACE_REGEX = / +/
|
|
62
|
+
|
|
60
63
|
return ObservableCreate<Feature>(async observer => {
|
|
61
64
|
const { adapter } = await this.setup()
|
|
62
65
|
const features = await updateStatus(
|
|
@@ -68,39 +71,57 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
68
71
|
for (const feature of features) {
|
|
69
72
|
const maf = feature.get('mafBlock') as string
|
|
70
73
|
const blocks = maf.split(';')
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const blocks2 = [] as string[]
|
|
74
|
+
|
|
75
|
+
// Count sequence blocks first to pre-size arrays
|
|
76
|
+
let sequenceBlockCount = 0
|
|
75
77
|
for (const block of blocks) {
|
|
76
78
|
if (block.startsWith('s')) {
|
|
77
|
-
|
|
78
|
-
alns.push(block.split(/ +/)[6]!)
|
|
79
|
-
blocks2.push(block)
|
|
80
|
-
} else {
|
|
81
|
-
aln = block.split(/ +/)[6]
|
|
82
|
-
alns.push(aln!)
|
|
83
|
-
blocks2.push(block)
|
|
84
|
-
}
|
|
79
|
+
sequenceBlockCount++
|
|
85
80
|
}
|
|
86
81
|
}
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
83
|
+
// Pre-size arrays based on actual sequence block count
|
|
84
|
+
const alns = new Array<string>(sequenceBlockCount)
|
|
85
|
+
const alignments = {} as Record<string, OrganismRecord>
|
|
86
|
+
|
|
87
|
+
let sequenceIndex = 0
|
|
88
|
+
let referenceSeq: string | undefined
|
|
89
|
+
|
|
90
|
+
// Single-pass processing: combine both loops
|
|
91
|
+
for (const block of blocks) {
|
|
92
|
+
if (block.startsWith('s')) {
|
|
93
|
+
// Split once and cache the result
|
|
94
|
+
const parts = block.split(WHITESPACE_REGEX)
|
|
95
|
+
const sequence = parts[6]!
|
|
96
|
+
const organismChr = parts[1]!
|
|
97
|
+
|
|
98
|
+
// Store sequence in pre-sized array
|
|
99
|
+
alns[sequenceIndex] = sequence
|
|
100
|
+
|
|
101
|
+
// Set reference sequence from first block
|
|
102
|
+
if (referenceSeq === undefined) {
|
|
103
|
+
referenceSeq = sequence
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Parse organism and chromosome once
|
|
107
|
+
const dotIndex = organismChr.indexOf('.')
|
|
108
|
+
const org = organismChr.slice(0, Math.max(0, dotIndex))
|
|
109
|
+
const chr = organismChr.slice(Math.max(0, dotIndex + 1))
|
|
110
|
+
|
|
111
|
+
// Create alignment record directly
|
|
112
|
+
alignments[org] = {
|
|
113
|
+
chr,
|
|
114
|
+
start: +parts[2]!,
|
|
115
|
+
srcSize: +parts[3]!,
|
|
116
|
+
strand: parts[4] === '+' ? 1 : -1,
|
|
117
|
+
unknown: +parts[5]!,
|
|
118
|
+
seq: sequence,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
sequenceIndex++
|
|
102
122
|
}
|
|
103
123
|
}
|
|
124
|
+
|
|
104
125
|
observer.next(
|
|
105
126
|
new SimpleFeature({
|
|
106
127
|
id: feature.id(),
|
|
@@ -108,8 +129,8 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
|
|
|
108
129
|
start: feature.get('start'),
|
|
109
130
|
end: feature.get('end'),
|
|
110
131
|
refName: feature.get('refName'),
|
|
111
|
-
seq:
|
|
112
|
-
alignments
|
|
132
|
+
seq: referenceSeq,
|
|
133
|
+
alignments,
|
|
113
134
|
},
|
|
114
135
|
}),
|
|
115
136
|
)
|
|
@@ -35,13 +35,7 @@ const Crosshairs = ({
|
|
|
35
35
|
top: scrollTop,
|
|
36
36
|
}}
|
|
37
37
|
>
|
|
38
|
-
<line
|
|
39
|
-
x1={0}
|
|
40
|
-
x2={width}
|
|
41
|
-
y1={mouseY - scrollTop}
|
|
42
|
-
y2={mouseY - scrollTop}
|
|
43
|
-
stroke="black"
|
|
44
|
-
/>
|
|
38
|
+
<line x1={0} x2={width} y1={mouseY} y2={mouseY} stroke="black" />
|
|
45
39
|
<line x1={mouseX} x2={mouseX} y1={0} y2={height} stroke="black" />
|
|
46
40
|
</svg>
|
|
47
41
|
)
|
|
@@ -2,55 +2,36 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { SanitizedHTML } from '@jbrowse/core/ui'
|
|
4
4
|
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
|
|
5
|
-
import {
|
|
6
|
-
getBpDisplayStr,
|
|
7
|
-
getContainingView,
|
|
8
|
-
toLocale,
|
|
9
|
-
} from '@jbrowse/core/util'
|
|
5
|
+
import { getContainingView } from '@jbrowse/core/util'
|
|
10
6
|
import { observer } from 'mobx-react'
|
|
11
7
|
|
|
8
|
+
import { generateTooltipContent } from '../util'
|
|
9
|
+
|
|
12
10
|
import type { LinearMafDisplayModel } from '../stateModel'
|
|
11
|
+
import type { HoveredInfo } from '../util'
|
|
13
12
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
const MAFTooltip = observer(function ({
|
|
15
|
+
model,
|
|
16
|
+
mouseX,
|
|
17
|
+
origMouseX,
|
|
18
|
+
}: {
|
|
16
19
|
mouseY: number
|
|
17
20
|
mouseX: number
|
|
18
21
|
rowHeight: number
|
|
19
22
|
sources: Record<string, any>[]
|
|
20
23
|
model: LinearMafDisplayModel
|
|
21
24
|
origMouseX?: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const MAFTooltip = observer(function ({
|
|
25
|
-
model,
|
|
26
|
-
mouseY,
|
|
27
|
-
mouseX,
|
|
28
|
-
origMouseX,
|
|
29
|
-
rowHeight,
|
|
30
|
-
sources,
|
|
31
|
-
}: MAFTooltipProps) {
|
|
25
|
+
}) {
|
|
26
|
+
const { hoveredInfo } = model
|
|
32
27
|
const view = getContainingView(model) as LinearGenomeViewModel
|
|
33
|
-
const ret = Object.entries(sources[Math.floor(mouseY / rowHeight)] || {})
|
|
34
|
-
.filter(([key]) => key !== 'color' && key !== 'id')
|
|
35
|
-
.map(([key, value]) => `${key}:${value}`)
|
|
36
|
-
.join('\n')
|
|
37
28
|
const p1 = origMouseX ? view.pxToBp(origMouseX) : undefined
|
|
38
29
|
const p2 = view.pxToBp(mouseX)
|
|
39
|
-
|
|
30
|
+
|
|
31
|
+
return hoveredInfo ? (
|
|
40
32
|
<BaseTooltip>
|
|
41
33
|
<SanitizedHTML
|
|
42
|
-
html={
|
|
43
|
-
ret,
|
|
44
|
-
...(p1
|
|
45
|
-
? [
|
|
46
|
-
`Start: ${p1.refName}:${toLocale(p1.coord)}`,
|
|
47
|
-
`End: ${p2.refName}:${toLocale(p2.coord)}`,
|
|
48
|
-
`Length: ${getBpDisplayStr(Math.abs(p1.coord - p2.coord))}`,
|
|
49
|
-
]
|
|
50
|
-
: [`${p2.refName}:${toLocale(p2.coord)}`]),
|
|
51
|
-
]
|
|
52
|
-
.filter(f => !!f)
|
|
53
|
-
.join('<br/>')}
|
|
34
|
+
html={generateTooltipContent(hoveredInfo as HoveredInfo, p1, p2)}
|
|
54
35
|
/>
|
|
55
36
|
</BaseTooltip>
|
|
56
37
|
) : null
|
|
@@ -85,6 +85,10 @@ export default function stateModelFactory(
|
|
|
85
85
|
}),
|
|
86
86
|
)
|
|
87
87
|
.volatile(() => ({
|
|
88
|
+
/**
|
|
89
|
+
* #volatile
|
|
90
|
+
*/
|
|
91
|
+
hoveredInfo: undefined as Record<string, unknown> | undefined,
|
|
88
92
|
/**
|
|
89
93
|
* #volatile
|
|
90
94
|
*/
|
|
@@ -99,6 +103,12 @@ export default function stateModelFactory(
|
|
|
99
103
|
volatileTree: undefined as any,
|
|
100
104
|
}))
|
|
101
105
|
.actions(self => ({
|
|
106
|
+
/**
|
|
107
|
+
* #action
|
|
108
|
+
*/
|
|
109
|
+
setHoveredInfo(arg?: Record<string, unknown>) {
|
|
110
|
+
self.hoveredInfo = arg
|
|
111
|
+
},
|
|
102
112
|
/**
|
|
103
113
|
* #action
|
|
104
114
|
*/
|
|
@@ -1,8 +1,65 @@
|
|
|
1
|
+
import { getBpDisplayStr, toLocale } from '@jbrowse/core/util'
|
|
1
2
|
import { max } from 'd3-array'
|
|
2
3
|
|
|
3
4
|
import type { NodeWithIds } from './types'
|
|
4
5
|
import type { HierarchyNode } from 'd3-hierarchy'
|
|
5
6
|
|
|
7
|
+
export interface HoveredInfo {
|
|
8
|
+
sampleId: string
|
|
9
|
+
pos: number
|
|
10
|
+
base: string
|
|
11
|
+
chr: string
|
|
12
|
+
[key: string]: unknown // Allow additional properties for compatibility
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface GenomicPosition {
|
|
16
|
+
refName: string
|
|
17
|
+
coord: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Generates tooltip HTML content for MAF alignments
|
|
22
|
+
* Truncates long sequences to 50 characters, with ellipses added if display exceeds 20 characters
|
|
23
|
+
* @param hoveredInfo - Information about the hovered base/position
|
|
24
|
+
* @param p1 - Start position (for range selections)
|
|
25
|
+
* @param p2 - End position (current mouse position)
|
|
26
|
+
* @returns HTML string for tooltip content
|
|
27
|
+
*/
|
|
28
|
+
export function generateTooltipContent(
|
|
29
|
+
hoveredInfo: HoveredInfo | undefined,
|
|
30
|
+
p1: GenomicPosition | undefined,
|
|
31
|
+
p2: GenomicPosition,
|
|
32
|
+
): string {
|
|
33
|
+
const contentLines: string[] = []
|
|
34
|
+
|
|
35
|
+
if (p1) {
|
|
36
|
+
// Range selection mode
|
|
37
|
+
contentLines.push(
|
|
38
|
+
`Start: ${p1.refName}:${toLocale(p1.coord)}`,
|
|
39
|
+
`End: ${p2.refName}:${toLocale(p2.coord)}`,
|
|
40
|
+
`Length: ${getBpDisplayStr(Math.abs(p1.coord - p2.coord))}`,
|
|
41
|
+
)
|
|
42
|
+
} else {
|
|
43
|
+
// Single position mode
|
|
44
|
+
contentLines.push(`Ref: ${p2.refName}:${toLocale(p2.coord)}`)
|
|
45
|
+
|
|
46
|
+
if (hoveredInfo) {
|
|
47
|
+
const { base, sampleId, pos, chr } = hoveredInfo
|
|
48
|
+
const thresh = 20
|
|
49
|
+
const len = base.length
|
|
50
|
+
const lengthSuffix = len > 1 ? ` ${len}bp` : ''
|
|
51
|
+
const baseDisplay =
|
|
52
|
+
base.length > thresh ? base.slice(0, thresh) + '...' : base
|
|
53
|
+
|
|
54
|
+
contentLines.push(
|
|
55
|
+
`Alt ${sampleId}: ${chr}:${pos.toLocaleString('en-US')} (${baseDisplay}${lengthSuffix})`,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return contentLines.filter(line => !!line).join('<br/>')
|
|
61
|
+
}
|
|
62
|
+
|
|
6
63
|
// basically same as maxLength from https://observablehq.com/@d3/tree-of-life
|
|
7
64
|
export function maxLength(d: HierarchyNode<NodeWithIds>): number {
|
|
8
65
|
return (
|
|
@@ -48,14 +48,13 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
48
48
|
const features = await this.getFeatures(renderProps)
|
|
49
49
|
const res = await updateStatus('Rendering alignment', statusCallback, () =>
|
|
50
50
|
renderToAbstractCanvas(width, height, renderProps, ctx => {
|
|
51
|
-
makeImageData({
|
|
51
|
+
return makeImageData({
|
|
52
52
|
ctx,
|
|
53
53
|
renderArgs: {
|
|
54
54
|
...renderProps,
|
|
55
55
|
features,
|
|
56
56
|
},
|
|
57
57
|
})
|
|
58
|
-
return undefined
|
|
59
58
|
}),
|
|
60
59
|
)
|
|
61
60
|
const results = await super.render({
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { useMemo, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import { PrerenderedCanvas } from '@jbrowse/core/ui'
|
|
4
|
+
import Flatbush from 'flatbush'
|
|
5
|
+
import { observer } from 'mobx-react'
|
|
6
|
+
import { RenderedBase } from '../rendering'
|
|
7
|
+
import { Sample } from '../../LinearMafDisplay/types'
|
|
8
|
+
|
|
9
|
+
type SerializedRBush = any
|
|
10
|
+
|
|
11
|
+
const LinearMafRendering = observer(function (props: {
|
|
12
|
+
width: number
|
|
13
|
+
height: number
|
|
14
|
+
displayModel: any
|
|
15
|
+
flatbush: SerializedRBush
|
|
16
|
+
items: RenderedBase[]
|
|
17
|
+
samples: Sample[]
|
|
18
|
+
}) {
|
|
19
|
+
const { items, displayModel, height, samples, flatbush } = props
|
|
20
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
21
|
+
const rbush2 = useMemo(() => Flatbush.from(flatbush), [flatbush])
|
|
22
|
+
|
|
23
|
+
function getFeatureUnderMouse(eventClientX: number, eventClientY: number) {
|
|
24
|
+
let offsetX = 0
|
|
25
|
+
let offsetY = 0
|
|
26
|
+
if (ref.current) {
|
|
27
|
+
const r = ref.current.getBoundingClientRect()
|
|
28
|
+
offsetX = eventClientX - r.left
|
|
29
|
+
offsetY = eventClientY - r.top
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const x = rbush2.search(offsetX, offsetY, offsetX + 1, offsetY + 1)
|
|
33
|
+
if (x.length) {
|
|
34
|
+
const elt = x.findIndex(idx => items[idx]?.isInsertion)
|
|
35
|
+
const r = elt !== -1 ? items[elt]! : items[x[0]!]!
|
|
36
|
+
const s = samples[r.sampleId]
|
|
37
|
+
return {
|
|
38
|
+
...r,
|
|
39
|
+
sampleId: s?.label || s?.id || 'unknown',
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
ref={ref}
|
|
48
|
+
onMouseMove={e =>
|
|
49
|
+
displayModel.setHoveredInfo?.(
|
|
50
|
+
getFeatureUnderMouse(e.clientX, e.clientY),
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
onMouseLeave={() => {
|
|
54
|
+
displayModel.setHoveredInfo?.(undefined)
|
|
55
|
+
}}
|
|
56
|
+
onMouseOut={() => {
|
|
57
|
+
displayModel.setHoveredInfo?.(undefined)
|
|
58
|
+
}}
|
|
59
|
+
style={{
|
|
60
|
+
overflow: 'visible',
|
|
61
|
+
position: 'relative',
|
|
62
|
+
height,
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<PrerenderedCanvas
|
|
66
|
+
{...props}
|
|
67
|
+
style={{
|
|
68
|
+
position: 'absolute',
|
|
69
|
+
left: 0,
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export default LinearMafRendering
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function minElt<T>(arr: Iterable<T>, cb: (arg: T) => number) {
|
|
2
|
+
let min = Infinity
|
|
3
|
+
let minElement: T | undefined
|
|
4
|
+
for (const entry of arr) {
|
|
5
|
+
const val = cb(entry)
|
|
6
|
+
|
|
7
|
+
if (val < min) {
|
|
8
|
+
min = val
|
|
9
|
+
minElement = entry
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return minElement
|
|
13
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
2
|
|
|
3
3
|
import LinearMafRenderer from './LinearMafRenderer'
|
|
4
|
-
import ReactComponent from './components/
|
|
4
|
+
import ReactComponent from './components/LinearMafRendering'
|
|
5
5
|
import configSchema from './configSchema'
|
|
6
6
|
|
|
7
7
|
export default function LinearMafRendererF(pluginManager: PluginManager) {
|