jbrowse-plugin-mafviewer 1.0.8 → 1.1.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.
Files changed (126) hide show
  1. package/README.md +54 -20
  2. package/dist/{TaffyAdapter/TaffyAdapter.d.ts → BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts} +11 -7
  3. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +204 -0
  4. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -0
  5. package/dist/{TaffyAdapter → BgzipTaffyAdapter}/configSchema.d.ts +14 -1
  6. package/dist/{TaffyAdapter → BgzipTaffyAdapter}/configSchema.js +21 -6
  7. package/dist/BgzipTaffyAdapter/configSchema.js.map +1 -0
  8. package/dist/BgzipTaffyAdapter/index.d.ts +2 -0
  9. package/dist/BgzipTaffyAdapter/index.js +11 -0
  10. package/dist/BgzipTaffyAdapter/index.js.map +1 -0
  11. package/dist/BgzipTaffyAdapter/rowInstructions.d.ts +35 -0
  12. package/dist/BgzipTaffyAdapter/rowInstructions.js +55 -0
  13. package/dist/BgzipTaffyAdapter/rowInstructions.js.map +1 -0
  14. package/dist/BgzipTaffyAdapter/types.d.ts +13 -0
  15. package/dist/BgzipTaffyAdapter/types.js +2 -0
  16. package/dist/BgzipTaffyAdapter/types.js.map +1 -0
  17. package/dist/BgzipTaffyAdapter/virtualOffset.d.ts +8 -0
  18. package/dist/BgzipTaffyAdapter/virtualOffset.js +23 -0
  19. package/dist/BgzipTaffyAdapter/virtualOffset.js.map +1 -0
  20. package/dist/BigMafAdapter/BigMafAdapter.js +1 -1
  21. package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
  22. package/dist/BigMafAdapter/configSchema.d.ts +11 -0
  23. package/dist/BigMafAdapter/configSchema.js +11 -0
  24. package/dist/BigMafAdapter/configSchema.js.map +1 -1
  25. package/dist/BigMafAdapter/index.js +1 -1
  26. package/dist/BigMafAdapter/index.js.map +1 -1
  27. package/dist/LinearMafDisplay/components/ColorLegend.js +10 -6
  28. package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
  29. package/dist/LinearMafDisplay/components/ReactComponent.js +39 -4
  30. package/dist/LinearMafDisplay/components/ReactComponent.js.map +1 -1
  31. package/dist/LinearMafDisplay/components/SetRowHeight.js +7 -7
  32. package/dist/LinearMafDisplay/components/SetRowHeight.js.map +1 -1
  33. package/dist/LinearMafDisplay/components/SvgWrapper.d.ts +8 -0
  34. package/dist/LinearMafDisplay/components/SvgWrapper.js +21 -0
  35. package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -0
  36. package/dist/LinearMafDisplay/components/Tree.d.ts +5 -0
  37. package/dist/LinearMafDisplay/components/Tree.js +22 -0
  38. package/dist/LinearMafDisplay/components/Tree.js.map +1 -0
  39. package/dist/LinearMafDisplay/components/YScaleBars.d.ts +0 -1
  40. package/dist/LinearMafDisplay/components/YScaleBars.js +6 -27
  41. package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
  42. package/dist/LinearMafDisplay/components/util.d.ts +1 -0
  43. package/dist/LinearMafDisplay/components/util.js +8 -0
  44. package/dist/LinearMafDisplay/components/util.js.map +1 -0
  45. package/dist/LinearMafDisplay/index.js +1 -1
  46. package/dist/LinearMafDisplay/index.js.map +1 -1
  47. package/dist/LinearMafDisplay/renderSvg.js +1 -0
  48. package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
  49. package/dist/LinearMafDisplay/stateModel.d.ts +76 -14
  50. package/dist/LinearMafDisplay/stateModel.js +118 -18
  51. package/dist/LinearMafDisplay/stateModel.js.map +1 -1
  52. package/dist/LinearMafDisplay/types.d.ts +17 -0
  53. package/dist/LinearMafDisplay/types.js +16 -0
  54. package/dist/LinearMafDisplay/types.js.map +1 -0
  55. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +3 -3
  56. package/dist/LinearMafRenderer/LinearMafRenderer.js +11 -9
  57. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
  58. package/dist/LinearMafRenderer/components/ReactComponent.js +1 -1
  59. package/dist/LinearMafRenderer/components/ReactComponent.js.map +1 -1
  60. package/dist/LinearMafRenderer/index.js +1 -1
  61. package/dist/LinearMafRenderer/index.js.map +1 -1
  62. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +42 -25
  63. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
  64. package/dist/MafRPC/index.d.ts +16 -0
  65. package/dist/MafRPC/index.js +19 -0
  66. package/dist/MafRPC/index.js.map +1 -0
  67. package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +8 -0
  68. package/dist/MafTabixAdapter/MafTabixAdapter.js +18 -19
  69. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
  70. package/dist/MafTabixAdapter/configSchema.d.ts +17 -0
  71. package/dist/MafTabixAdapter/configSchema.js +17 -1
  72. package/dist/MafTabixAdapter/configSchema.js.map +1 -1
  73. package/dist/MafTabixAdapter/index.js +1 -1
  74. package/dist/MafTabixAdapter/index.js.map +1 -1
  75. package/dist/MafTrack/index.js.map +1 -1
  76. package/dist/index.js +6 -4
  77. package/dist/index.js.map +1 -1
  78. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +65 -4
  79. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
  80. package/dist/parseNewick.d.ts +60 -0
  81. package/dist/parseNewick.js +95 -0
  82. package/dist/parseNewick.js.map +1 -0
  83. package/dist/util.d.ts +9 -0
  84. package/dist/util.js +9 -0
  85. package/dist/util.js.map +1 -0
  86. package/package.json +18 -5
  87. package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +235 -0
  88. package/src/{TaffyAdapter → BgzipTaffyAdapter}/configSchema.ts +21 -6
  89. package/src/{TaffyAdapter → BgzipTaffyAdapter}/index.ts +5 -4
  90. package/src/BgzipTaffyAdapter/rowInstructions.ts +91 -0
  91. package/src/BgzipTaffyAdapter/types.ts +16 -0
  92. package/src/BgzipTaffyAdapter/virtualOffset.ts +29 -0
  93. package/src/BigMafAdapter/BigMafAdapter.ts +11 -11
  94. package/src/BigMafAdapter/configSchema.ts +11 -0
  95. package/src/BigMafAdapter/index.ts +2 -1
  96. package/src/LinearMafDisplay/components/ColorLegend.tsx +36 -25
  97. package/src/LinearMafDisplay/components/ReactComponent.tsx +68 -3
  98. package/src/LinearMafDisplay/components/SetRowHeight.tsx +6 -5
  99. package/src/LinearMafDisplay/components/SvgWrapper.tsx +39 -0
  100. package/src/LinearMafDisplay/components/Tree.tsx +33 -0
  101. package/src/LinearMafDisplay/components/YScaleBars.tsx +8 -43
  102. package/src/LinearMafDisplay/components/util.ts +7 -0
  103. package/src/LinearMafDisplay/index.ts +2 -1
  104. package/src/LinearMafDisplay/renderSvg.tsx +2 -1
  105. package/src/LinearMafDisplay/stateModel.ts +139 -18
  106. package/src/LinearMafDisplay/types.ts +41 -0
  107. package/src/LinearMafRenderer/LinearMafRenderer.ts +13 -10
  108. package/src/LinearMafRenderer/components/ReactComponent.tsx +2 -1
  109. package/src/LinearMafRenderer/index.ts +2 -1
  110. package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +109 -65
  111. package/src/MafRPC/index.ts +39 -0
  112. package/src/MafTabixAdapter/MafTabixAdapter.ts +31 -25
  113. package/src/MafTabixAdapter/configSchema.ts +17 -1
  114. package/src/MafTabixAdapter/index.ts +2 -1
  115. package/src/MafTrack/index.ts +1 -0
  116. package/src/index.ts +6 -4
  117. package/src/parseNewick.ts +94 -0
  118. package/src/util.ts +11 -0
  119. package/LICENSE +0 -201
  120. package/dist/TaffyAdapter/TaffyAdapter.js +0 -89
  121. package/dist/TaffyAdapter/TaffyAdapter.js.map +0 -1
  122. package/dist/TaffyAdapter/configSchema.js.map +0 -1
  123. package/dist/TaffyAdapter/index.d.ts +0 -2
  124. package/dist/TaffyAdapter/index.js +0 -11
  125. package/dist/TaffyAdapter/index.js.map +0 -1
  126. package/src/TaffyAdapter/TaffyAdapter.ts +0 -112
@@ -1,4 +1,4 @@
1
- import { Instance, types } from 'mobx-state-tree'
1
+ import PluginManager from '@jbrowse/core/PluginManager'
2
2
  import {
3
3
  AnyConfigurationModel,
4
4
  AnyConfigurationSchemaType,
@@ -6,12 +6,26 @@ import {
6
6
  getConf,
7
7
  } from '@jbrowse/core/configuration'
8
8
  import { getEnv, getSession } from '@jbrowse/core/util'
9
- import PluginManager from '@jbrowse/core/PluginManager'
9
+ import { getRpcSessionId } from '@jbrowse/core/util/tracks'
10
10
  import { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
11
+ import { ascending } from 'd3-array'
12
+ import { type HierarchyNode, cluster, hierarchy } from 'd3-hierarchy'
13
+ import { autorun } from 'mobx'
14
+ import { Instance, addDisposer, isAlive, types } from 'mobx-state-tree'
15
+
11
16
  import SetRowHeightDialog from './components/SetRowHeight'
17
+ import {
18
+ NodeWithIds,
19
+ NodeWithIdsAndLength,
20
+ maxLength,
21
+ setBrLength,
22
+ } from './types'
23
+ import { normalize } from '../util'
12
24
 
13
- function isStrs(array: unknown[]): array is string[] {
14
- return typeof array[0] === 'string'
25
+ interface Sample {
26
+ id: string
27
+ label: string
28
+ color?: string
15
29
  }
16
30
 
17
31
  /**
@@ -56,6 +70,16 @@ export default function stateModelFactory(
56
70
  * #property
57
71
  */
58
72
  mismatchRendering: true,
73
+
74
+ /**
75
+ * #property
76
+ */
77
+ showBranchLen: false,
78
+
79
+ /**
80
+ * #property
81
+ */
82
+ treeAreaWidth: 80,
59
83
  }),
60
84
  )
61
85
  .volatile(() => ({
@@ -63,6 +87,14 @@ export default function stateModelFactory(
63
87
  * #volatile
64
88
  */
65
89
  prefersOffset: true,
90
+ /**
91
+ * #volatile
92
+ */
93
+ volatileSamples: [] as Sample[],
94
+ /**
95
+ * #volatile
96
+ */
97
+ tree: undefined as any,
66
98
  }))
67
99
  .actions(self => ({
68
100
  /**
@@ -89,26 +121,22 @@ export default function stateModelFactory(
89
121
  setMismatchRendering(f: boolean) {
90
122
  self.mismatchRendering = f
91
123
  },
92
- }))
93
- .views(self => ({
94
124
  /**
95
- * #getter
125
+ * #action
96
126
  */
97
- get samples() {
98
- const r = self.adapterConfig.samples as
99
- | string[]
100
- | { id: string; label: string; color?: string }[]
101
- return isStrs(r)
102
- ? r.map(elt => ({ id: elt, label: elt, color: undefined }))
103
- : r
127
+ setSamples({ samples, tree }: { samples: Sample[]; tree: unknown }) {
128
+ self.volatileSamples = samples
129
+ self.tree = tree
104
130
  },
105
-
131
+ }))
132
+ .views(self => ({
106
133
  /**
107
134
  * #getter
108
135
  */
109
136
  get rendererTypeName() {
110
137
  return 'LinearMafRenderer'
111
138
  },
139
+
112
140
  /**
113
141
  * #getter
114
142
  */
@@ -125,14 +153,77 @@ export default function stateModelFactory(
125
153
  )
126
154
  },
127
155
  }))
156
+
157
+ .views(self => ({
158
+ /**
159
+ * #getter
160
+ */
161
+ get root() {
162
+ return self.tree
163
+ ? hierarchy(self.tree, d => d.children)
164
+ // todo: investigate whether needed, typescript says children always true
165
+ .sum(d => (d.children ? 0 : 1))
166
+ .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
167
+ : undefined
168
+ },
169
+ }))
170
+ .views(self => ({
171
+ /**
172
+ * #getter
173
+ * generates a new tree that is clustered with x,y positions
174
+ */
175
+ get hierarchy(): HierarchyNode<NodeWithIdsAndLength> | undefined {
176
+ const r = self.root
177
+ if (r) {
178
+ const width = self.treeAreaWidth
179
+ const clust = cluster<NodeWithIds>()
180
+ .size([this.totalHeight, width])
181
+ .separation(() => 1)
182
+ clust(r)
183
+ setBrLength(r, (r.data.length = 0), width / maxLength(r))
184
+ return r as HierarchyNode<NodeWithIdsAndLength>
185
+ } else {
186
+ return undefined
187
+ }
188
+ },
189
+ /**
190
+ * #getter
191
+ */
192
+ get samples() {
193
+ return this.rowNames ? normalize(this.rowNames) : self.volatileSamples
194
+ },
195
+ /**
196
+ * #getter
197
+ */
198
+ get totalHeight() {
199
+ return this.samples.length * self.rowHeight
200
+ },
201
+ /**
202
+ * #getter
203
+ */
204
+ get leaves() {
205
+ return self.root?.leaves()
206
+ },
207
+ /**
208
+ * #getter
209
+ */
210
+ get rowNames(): string[] | undefined {
211
+ return this.leaves?.map(n => n.data.name)
212
+ },
213
+ }))
128
214
  .views(self => {
129
215
  const {
130
- // eslint-disable-next-line @typescript-eslint/unbound-method
131
216
  trackMenuItems: superTrackMenuItems,
132
- // eslint-disable-next-line @typescript-eslint/unbound-method
217
+
133
218
  renderProps: superRenderProps,
134
219
  } = self
135
220
  return {
221
+ /**
222
+ * #getter
223
+ */
224
+ get treeWidth() {
225
+ return self.hierarchy ? self.treeAreaWidth : 0
226
+ },
136
227
  /**
137
228
  * #method
138
229
  */
@@ -193,8 +284,38 @@ export default function stateModelFactory(
193
284
  },
194
285
  }
195
286
  })
287
+ .actions(self => ({
288
+ afterCreate() {
289
+ addDisposer(
290
+ self,
291
+ autorun(async () => {
292
+ try {
293
+ const { rpcManager } = getSession(self)
294
+ const sessionId = getRpcSessionId(self)
295
+
296
+ const results = (await rpcManager.call(
297
+ sessionId,
298
+ 'MafGetSamples',
299
+ {
300
+ sessionId,
301
+ adapterConfig: self.adapterConfig,
302
+ statusCallback: (message: string) => {
303
+ if (isAlive(self)) {
304
+ self.setMessage(message)
305
+ }
306
+ },
307
+ },
308
+ )) as any
309
+ self.setSamples(results)
310
+ } catch (e) {
311
+ console.error(e)
312
+ getSession(self).notifyError(`${e}`, e)
313
+ }
314
+ }),
315
+ )
316
+ },
317
+ }))
196
318
  .actions(self => {
197
- // eslint-disable-next-line @typescript-eslint/unbound-method
198
319
  const { renderSvg: superRenderSvg } = self
199
320
  return {
200
321
  /**
@@ -0,0 +1,41 @@
1
+ import { max } from 'd3-array'
2
+
3
+ import type { HierarchyNode } from 'd3-hierarchy'
4
+
5
+ export interface NodeWithIds {
6
+ id: string
7
+ name: string
8
+ children: NodeWithIds[]
9
+ length?: number
10
+ noTree?: boolean
11
+ }
12
+
13
+ export interface NodeWithIdsAndLength {
14
+ id: string
15
+ name: string
16
+ children: NodeWithIdsAndLength[]
17
+ noTree?: boolean
18
+ length: number
19
+ }
20
+
21
+ // basically same as maxLength from https://observablehq.com/@d3/tree-of-life
22
+ export function maxLength(d: HierarchyNode<NodeWithIds>): number {
23
+ return (
24
+ (d.data.length || 0) + (d.children ? max(d.children, maxLength) || 0 : 0)
25
+ )
26
+ }
27
+ // basically same as setRadius from https://observablehq.com/@d3/tree-of-life
28
+ export function setBrLength(
29
+ d: HierarchyNode<NodeWithIds>,
30
+ y0: number,
31
+ k: number,
32
+ ) {
33
+ // @ts-expect-error
34
+ d.len = (y0 += Math.max(d.data.length || 0, 0)) * k
35
+
36
+ if (d.children) {
37
+ d.children.forEach(d => {
38
+ setBrLength(d, y0, k)
39
+ })
40
+ }
41
+ }
@@ -7,6 +7,7 @@ import {
7
7
  featureSpanPx,
8
8
  renderToAbstractCanvas,
9
9
  } from '@jbrowse/core/util'
10
+
10
11
  import { getColorBaseMap, getContrastBaseMap } from './util'
11
12
 
12
13
  interface Sample {
@@ -39,7 +40,7 @@ function makeImageData({
39
40
  rowProportion,
40
41
  features,
41
42
  } = renderArgs
42
- const [region] = regions
43
+ const region = regions[0]!
43
44
  const h = rowHeight * rowProportion
44
45
  const theme = createJBrowseTheme(configTheme)
45
46
  const colorForBase = getColorBaseMap(theme)
@@ -64,7 +65,7 @@ function makeImageData({
64
65
 
65
66
  const row = sampleToRowMap.get(sample)
66
67
  if (row === undefined) {
67
- throw new Error(`unknown sample encountered: ${sample}`)
68
+ continue
68
69
  }
69
70
 
70
71
  const t = rowHeight * row
@@ -107,7 +108,7 @@ function makeImageData({
107
108
  if (seq[i] !== '-') {
108
109
  if (c !== '-') {
109
110
  const l = leftPx + scale * o
110
- if (seq[i] !== c) {
111
+ if (seq[i] !== c && c !== ' ') {
111
112
  ctx.fillStyle = mismatchRendering
112
113
  ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
113
114
  (colorForBase[c as keyof typeof colorForBase] ?? 'black')
@@ -132,10 +133,12 @@ function makeImageData({
132
133
  if (seq[i] !== '-') {
133
134
  const l = leftPx + scale * o
134
135
  const offset = (scale - charSizeW) / 2 + 1
135
- const c = alignment[i]
136
+ const c = alignment[i]!
136
137
  if ((showAllLetters || seq[i] !== c) && c !== '-') {
137
- ctx.fillStyle = contrastForBase[c] ?? 'white'
138
- ctx.fillText(origAlignment[i], l + offset, hp2 + t + 3)
138
+ ctx.fillStyle = mismatchRendering
139
+ ? (contrastForBase[c] ?? 'white')
140
+ : 'black'
141
+ ctx.fillText(origAlignment[i] || '', l + offset, hp2 + t + 3)
139
142
  }
140
143
  o++
141
144
  }
@@ -156,7 +159,7 @@ function makeImageData({
156
159
  const alignment = origAlignment.toLowerCase()
157
160
  const row = sampleToRowMap.get(sample)
158
161
  if (row === undefined) {
159
- throw new Error(`unknown sample encountered: ${sample}`)
162
+ continue
160
163
  }
161
164
 
162
165
  const t = rowHeight * row
@@ -166,7 +169,7 @@ function makeImageData({
166
169
  for (let i = 0, o = 0; i < alignment.length; i++) {
167
170
  let ins = ''
168
171
  while (seq[i] === '-') {
169
- if (alignment[i] !== '-') {
172
+ if (alignment[i] !== '-' && alignment[i] !== ' ') {
170
173
  ins += alignment[i]
171
174
  }
172
175
  i++
@@ -197,8 +200,8 @@ export default class LinearMafRenderer extends FeatureRendererType {
197
200
  }
198
201
  async render(renderProps: RenderArgs) {
199
202
  const { regions, bpPerPx, samples, rowHeight } = renderProps
200
- const [region] = regions
201
- const height = samples.length * rowHeight + 100
203
+ const region = regions[0]!
204
+ const height = samples.length * (rowHeight + 1) + 100
202
205
  const width = (region.end - region.start) / bpPerPx
203
206
  const features = await this.getFeatures(renderProps)
204
207
  const res = await renderToAbstractCanvas(
@@ -1,6 +1,7 @@
1
+ import React from 'react'
2
+
1
3
  import { PrerenderedCanvas } from '@jbrowse/core/ui'
2
4
  import { observer } from 'mobx-react'
3
- import React from 'react'
4
5
 
5
6
  const LinearMafRendering = observer(function (props: {
6
7
  width: number
@@ -1,7 +1,8 @@
1
1
  import PluginManager from '@jbrowse/core/PluginManager'
2
- import configSchema from './configSchema'
2
+
3
3
  import LinearMafRenderer from './LinearMafRenderer'
4
4
  import ReactComponent from './components/ReactComponent'
5
+ import configSchema from './configSchema'
5
6
 
6
7
  export default function LinearMafRendererF(pluginManager: PluginManager) {
7
8
  pluginManager.addRendererType(
@@ -1,4 +1,13 @@
1
1
  import React, { useState } from 'react'
2
+
3
+ import { ErrorMessage, FileSelector } from '@jbrowse/core/ui'
4
+ import {
5
+ FileLocation,
6
+ getSession,
7
+ isSessionModelWithWidgets,
8
+ isSessionWithAddTracks,
9
+ } from '@jbrowse/core/util'
10
+ import { AddTrackModel } from '@jbrowse/plugin-data-management'
2
11
  import {
3
12
  Button,
4
13
  FormControl,
@@ -9,16 +18,8 @@ import {
9
18
  RadioGroup,
10
19
  TextField,
11
20
  } from '@mui/material'
12
- import { makeStyles } from 'tss-react/mui'
13
- import {
14
- FileLocation,
15
- getSession,
16
- isSessionModelWithWidgets,
17
- isSessionWithAddTracks,
18
- } from '@jbrowse/core/util'
19
- import { AddTrackModel } from '@jbrowse/plugin-data-management'
20
- import { ErrorMessage, FileSelector } from '@jbrowse/core/ui'
21
21
  import { getRoot } from 'mobx-state-tree'
22
+ import { makeStyles } from 'tss-react/mui'
22
23
 
23
24
  const useStyles = makeStyles()(theme => ({
24
25
  textbox: {
@@ -35,15 +36,24 @@ const useStyles = makeStyles()(theme => ({
35
36
  },
36
37
  }))
37
38
 
39
+ type AdapterTypeOptions =
40
+ | 'BigMafAdapter'
41
+ | 'MafTabixAdapter'
42
+ | 'BgzipTaffyAdapter'
43
+ type IndexTypeOptions = 'TBI' | 'CSI'
44
+
38
45
  export default function MultiMAFWidget({ model }: { model: AddTrackModel }) {
39
46
  const { classes } = useStyles()
40
47
  const [samples, setSamples] = useState('')
41
48
  const [loc, setLoc] = useState<FileLocation>()
42
49
  const [indexLoc, setIndexLoc] = useState<FileLocation>()
50
+ const [nhLoc, setNhLoc] = useState<FileLocation>()
43
51
  const [error, setError] = useState<unknown>()
44
52
  const [trackName, setTrackName] = useState('MAF track')
45
- const [fileTypeChoice, setFileTypeChoice] = useState('BigMafAdapter')
46
- const [indexTypeChoice, setIndexTypeChoice] = useState('TBI')
53
+ const [fileTypeChoice, setFileTypeChoice] =
54
+ useState<AdapterTypeOptions>('BigMafAdapter')
55
+ const [indexTypeChoice, setIndexTypeChoice] =
56
+ useState<IndexTypeOptions>('TBI')
47
57
 
48
58
  const rootModel = getRoot<any>(model)
49
59
  return (
@@ -55,21 +65,19 @@ export default function MultiMAFWidget({ model }: { model: AddTrackModel }) {
55
65
  <RadioGroup
56
66
  value={fileTypeChoice}
57
67
  onChange={event => {
58
- setFileTypeChoice(event.target.value)
68
+ setFileTypeChoice(event.target.value as AdapterTypeOptions)
59
69
  }}
60
70
  >
61
- <FormControlLabel
62
- value="BigMafAdapter"
63
- control={<Radio />}
64
- checked={fileTypeChoice === 'BigMafAdapter'}
65
- label="bigMaf"
66
- />
67
- <FormControlLabel
68
- value="MafTabixAdapter"
69
- control={<Radio />}
70
- checked={fileTypeChoice === 'MafTabixAdapter'}
71
- label="mafTabix"
72
- />
71
+ {['BigMafAdapter', 'MafTabixAdapter', 'BgzipTaffyAdapter'].map(
72
+ r => (
73
+ <FormControlLabel
74
+ value={r}
75
+ control={<Radio />}
76
+ checked={fileTypeChoice === r}
77
+ label={r}
78
+ />
79
+ ),
80
+ )}
73
81
  </RadioGroup>
74
82
  </FormControl>
75
83
  {fileTypeChoice === 'BigMafAdapter' ? (
@@ -81,62 +89,88 @@ export default function MultiMAFWidget({ model }: { model: AddTrackModel }) {
81
89
  setLoc(arg)
82
90
  }}
83
91
  />
84
- ) : (
92
+ ) : fileTypeChoice === 'MafTabixAdapter' ? (
85
93
  <>
86
94
  <FormControl>
87
95
  <FormLabel>Index type</FormLabel>
88
96
  <RadioGroup
89
97
  value={fileTypeChoice}
90
98
  onChange={event => {
91
- setIndexTypeChoice(event.target.value)
99
+ setIndexTypeChoice(event.target.value as IndexTypeOptions)
92
100
  }}
93
101
  >
94
- <FormControlLabel
95
- value="TBI"
96
- control={<Radio />}
97
- checked={indexTypeChoice === 'TBI'}
98
- label="TBI"
99
- />
100
- <FormControlLabel
101
- value="CSI"
102
- control={<Radio />}
103
- checked={indexTypeChoice === 'CSI'}
104
- label="CSI"
105
- />
102
+ {['TBI', 'CSI'].map(r => (
103
+ <FormControlLabel
104
+ value={r}
105
+ control={<Radio />}
106
+ checked={fileTypeChoice === r}
107
+ label={r}
108
+ />
109
+ ))}
106
110
  </RadioGroup>
107
111
  </FormControl>
108
112
  <FileSelector
109
113
  location={loc}
110
114
  name="Path to MAF tabix"
115
+ rootModel={rootModel}
111
116
  setLocation={arg => {
112
117
  setLoc(arg)
113
118
  }}
114
- rootModel={rootModel}
115
119
  />
116
120
  <FileSelector
117
121
  location={indexLoc}
118
122
  name="Path to MAF tabix index"
123
+ rootModel={rootModel}
119
124
  setLocation={arg => {
120
125
  setIndexLoc(arg)
121
126
  }}
127
+ />
128
+ </>
129
+ ) : (
130
+ <>
131
+ <FileSelector
132
+ location={loc}
133
+ name="Path to TAF.gz (Bgzipped TAF)"
134
+ rootModel={rootModel}
135
+ setLocation={arg => {
136
+ setLoc(arg)
137
+ }}
138
+ />
139
+ <FileSelector
140
+ location={indexLoc}
141
+ name="Path to TAF.gz.tai (TAF index)"
122
142
  rootModel={rootModel}
143
+ setLocation={arg => {
144
+ setIndexLoc(arg)
145
+ }}
123
146
  />
124
147
  </>
125
148
  )}
126
149
  </Paper>
127
- <TextField
128
- multiline
129
- rows={10}
130
- value={samples}
131
- onChange={event => {
132
- setSamples(event.target.value)
133
- }}
134
- placeholder={
135
- 'Enter sample names from the MAF file, one per line, or JSON formatted array of samples'
136
- }
137
- variant="outlined"
138
- fullWidth
139
- />
150
+ <div>
151
+ <FileSelector
152
+ location={nhLoc}
153
+ name="Path to newick tree (.nh)"
154
+ rootModel={rootModel}
155
+ setLocation={arg => {
156
+ setNhLoc(arg)
157
+ }}
158
+ />
159
+ <TextField
160
+ multiline
161
+ rows={10}
162
+ value={samples}
163
+ onChange={event => {
164
+ setSamples(event.target.value)
165
+ }}
166
+ helperText="Sample names (optional if .nh supplied, required if not)"
167
+ placeholder={
168
+ 'Enter sample names from the MAF file, one per line, or JSON formatted array of samples'
169
+ }
170
+ variant="outlined"
171
+ fullWidth
172
+ />
173
+ </div>
140
174
 
141
175
  <TextField
142
176
  value={trackName}
@@ -172,19 +206,29 @@ export default function MultiMAFWidget({ model }: { model: AddTrackModel }) {
172
206
  adapter:
173
207
  fileTypeChoice === 'BigMafAdapter'
174
208
  ? {
175
- type: fileTypeChoice,
176
- bigBedLocation: loc,
177
- samples: sampleNames,
178
- }
179
- : {
180
- type: fileTypeChoice,
181
- bedGzLocation: loc,
182
- index: {
183
- indexType: indexTypeChoice,
184
- location: indexLoc,
185
- },
186
- samples: sampleNames,
187
- },
209
+ type: fileTypeChoice,
210
+ bigBedLocation: loc,
211
+ samples: sampleNames,
212
+ nhLocation: nhLoc,
213
+ }
214
+ : fileTypeChoice === 'MafTabixAdapter'
215
+ ? {
216
+ type: fileTypeChoice,
217
+ bedGzLocation: loc,
218
+ nhLocation: nhLoc,
219
+ index: {
220
+ indexType: indexTypeChoice,
221
+ location: indexLoc,
222
+ },
223
+ samples: sampleNames,
224
+ }
225
+ : {
226
+ type: fileTypeChoice,
227
+ tafGzLocation: loc,
228
+ taiLocation: indexLoc,
229
+ nhLocation: nhLoc,
230
+ samples: sampleNames,
231
+ },
188
232
  })
189
233
 
190
234
  model.view?.showTrack(trackId)
@@ -0,0 +1,39 @@
1
+ import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'
2
+ import RpcMethodTypeWithFiltersAndRenameRegions from '@jbrowse/core/pluggableElementTypes/RpcMethodTypeWithFiltersAndRenameRegions'
3
+
4
+ import type PluginManager from '@jbrowse/core/PluginManager'
5
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration'
6
+ import type { Region } from '@jbrowse/core/util'
7
+
8
+ export class MafGetSamples extends RpcMethodTypeWithFiltersAndRenameRegions {
9
+ name = 'MafGetSamples'
10
+
11
+ async execute(
12
+ args: {
13
+ adapterConfig: AnyConfigurationModel
14
+ stopToken?: string
15
+ sessionId: string
16
+ headers?: Record<string, string>
17
+ regions: Region[]
18
+ bpPerPx: number
19
+ },
20
+ rpcDriverClassName: string,
21
+ ) {
22
+ const pm = this.pluginManager
23
+ const deserializedArgs = await this.deserializeArguments(
24
+ args,
25
+ rpcDriverClassName,
26
+ )
27
+ const { regions, adapterConfig, sessionId } = deserializedArgs
28
+ const { dataAdapter } = await getAdapter(pm, sessionId, adapterConfig)
29
+
30
+ // @ts-expect-error
31
+ return dataAdapter.getSamples(regions, deserializedArgs)
32
+ }
33
+ }
34
+
35
+ export default function MafRPCF(pluginManager: PluginManager) {
36
+ pluginManager.addRpcMethod(() => {
37
+ return new MafGetSamples(pluginManager)
38
+ })
39
+ }