jbrowse-plugin-mafviewer 1.2.3 → 1.3.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.
Files changed (147) hide show
  1. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +1 -1
  2. package/dist/BigMafAdapter/BigMafAdapter.d.ts +1 -1
  3. package/dist/BigMafAdapter/BigMafAdapter.js +50 -49
  4. package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
  5. package/dist/LinearMafDisplay/components/Crosshairs.d.ts +10 -0
  6. package/dist/LinearMafDisplay/components/Crosshairs.js +18 -0
  7. package/dist/LinearMafDisplay/components/Crosshairs.js.map +1 -0
  8. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.d.ts +11 -0
  9. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js +97 -0
  10. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js.map +1 -0
  11. package/dist/LinearMafDisplay/components/{ReactComponent.d.ts → LinearMafDisplayComponent.d.ts} +1 -1
  12. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js +168 -0
  13. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -0
  14. package/dist/LinearMafDisplay/components/MAFTooltip.d.ts +12 -0
  15. package/dist/LinearMafDisplay/components/MAFTooltip.js +29 -0
  16. package/dist/LinearMafDisplay/components/MAFTooltip.js.map +1 -0
  17. package/dist/LinearMafDisplay/components/SetRowHeightDialog/SetRowHeightDialog.js +38 -0
  18. package/dist/LinearMafDisplay/components/SetRowHeightDialog/SetRowHeightDialog.js.map +1 -0
  19. package/dist/LinearMafDisplay/components/Sidebar/ColorLegend.d.ts +6 -0
  20. package/dist/LinearMafDisplay/components/{ColorLegend.js → Sidebar/ColorLegend.js} +2 -3
  21. package/dist/LinearMafDisplay/components/Sidebar/ColorLegend.js.map +1 -0
  22. package/dist/LinearMafDisplay/components/Sidebar/RectBg.js.map +1 -0
  23. package/dist/LinearMafDisplay/components/{SvgWrapper.d.ts → Sidebar/SvgWrapper.d.ts} +1 -1
  24. package/dist/LinearMafDisplay/components/{SvgWrapper.js → Sidebar/SvgWrapper.js} +3 -1
  25. package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js.map +1 -0
  26. package/dist/LinearMafDisplay/components/{Tree.d.ts → Sidebar/Tree.d.ts} +2 -1
  27. package/dist/LinearMafDisplay/components/{Tree.js → Sidebar/Tree.js} +2 -0
  28. package/dist/LinearMafDisplay/components/Sidebar/Tree.js.map +1 -0
  29. package/dist/LinearMafDisplay/components/{YScaleBars.d.ts → Sidebar/YScaleBars.d.ts} +1 -1
  30. package/dist/LinearMafDisplay/components/Sidebar/YScaleBars.js +11 -0
  31. package/dist/LinearMafDisplay/components/Sidebar/YScaleBars.js.map +1 -0
  32. package/dist/LinearMafDisplay/index.js +1 -1
  33. package/dist/LinearMafDisplay/index.js.map +1 -1
  34. package/dist/LinearMafDisplay/renderSvg.js +1 -1
  35. package/dist/LinearMafDisplay/renderSvg.js.map +1 -1
  36. package/dist/LinearMafDisplay/stateModel.d.ts +23 -20
  37. package/dist/LinearMafDisplay/stateModel.js +62 -8
  38. package/dist/LinearMafDisplay/stateModel.js.map +1 -1
  39. package/dist/LinearMafDisplay/types.d.ts +5 -3
  40. package/dist/LinearMafDisplay/types.js +1 -15
  41. package/dist/LinearMafDisplay/types.js.map +1 -1
  42. package/dist/LinearMafDisplay/util.d.ts +4 -0
  43. package/dist/LinearMafDisplay/util.js +16 -0
  44. package/dist/LinearMafDisplay/util.js.map +1 -0
  45. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +5 -4
  46. package/dist/LinearMafRenderer/LinearMafRenderer.js +8 -10
  47. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
  48. package/dist/LinearMafRenderer/makeImageData.d.ts +1 -0
  49. package/dist/LinearMafRenderer/makeImageData.js +25 -20
  50. package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
  51. package/dist/MafAddTrackWorkflow/index.js +0 -1
  52. package/dist/MafAddTrackWorkflow/index.js.map +1 -1
  53. package/dist/{MafRPC/index.d.ts → MafGetSamples/MafGetSamples.d.ts} +1 -3
  54. package/dist/{MafRPC/index.js → MafGetSamples/MafGetSamples.js} +2 -7
  55. package/dist/MafGetSamples/MafGetSamples.js.map +1 -0
  56. package/dist/MafGetSamples/index.d.ts +2 -0
  57. package/dist/MafGetSamples/index.js +7 -0
  58. package/dist/MafGetSamples/index.js.map +1 -0
  59. package/dist/MafGetSequences/MafGetSequences.d.ts +16 -0
  60. package/dist/MafGetSequences/MafGetSequences.js +20 -0
  61. package/dist/MafGetSequences/MafGetSequences.js.map +1 -0
  62. package/dist/MafGetSequences/index.d.ts +2 -0
  63. package/dist/MafGetSequences/index.js +7 -0
  64. package/dist/MafGetSequences/index.js.map +1 -0
  65. package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +1 -1
  66. package/dist/MafTabixAdapter/MafTabixAdapter.js +9 -11
  67. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
  68. package/dist/MafTabixAdapter/configSchema.js +29 -1
  69. package/dist/MafTabixAdapter/configSchema.js.map +1 -1
  70. package/dist/index.js +4 -2
  71. package/dist/index.js.map +1 -1
  72. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +9 -20
  73. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
  74. package/dist/util/extractSubsequence.d.ts +12 -0
  75. package/dist/util/extractSubsequence.js +60 -0
  76. package/dist/util/extractSubsequence.js.map +1 -0
  77. package/dist/util/extractSubsequence.test.d.ts +1 -0
  78. package/dist/util/extractSubsequence.test.js +42 -0
  79. package/dist/util/extractSubsequence.test.js.map +1 -0
  80. package/dist/util/fastaUtils.d.ts +16 -0
  81. package/dist/util/fastaUtils.js +84 -0
  82. package/dist/util/fastaUtils.js.map +1 -0
  83. package/dist/util/fastaUtils.test.d.ts +1 -0
  84. package/dist/util/fastaUtils.test.js +95 -0
  85. package/dist/util/fastaUtils.test.js.map +1 -0
  86. package/dist/util/fetchSequences.d.ts +18 -0
  87. package/dist/util/fetchSequences.js +39 -0
  88. package/dist/util/fetchSequences.js.map +1 -0
  89. package/dist/util/useSequences.d.ts +21 -0
  90. package/dist/util/useSequences.js +64 -0
  91. package/dist/util/useSequences.js.map +1 -0
  92. package/dist/util.d.ts +2 -2
  93. package/dist/util.js +5 -1
  94. package/dist/util.js.map +1 -1
  95. package/package.json +13 -13
  96. package/src/BigMafAdapter/BigMafAdapter.ts +52 -49
  97. package/src/LinearMafDisplay/components/Crosshairs.tsx +50 -0
  98. package/src/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.tsx +175 -0
  99. package/src/LinearMafDisplay/components/LinearMafDisplayComponent.tsx +257 -0
  100. package/src/LinearMafDisplay/components/MAFTooltip.tsx +59 -0
  101. package/src/LinearMafDisplay/components/SetRowHeightDialog/SetRowHeightDialog.tsx +83 -0
  102. package/src/LinearMafDisplay/components/{ColorLegend.tsx → Sidebar/ColorLegend.tsx} +11 -7
  103. package/src/LinearMafDisplay/components/{SvgWrapper.tsx → Sidebar/SvgWrapper.tsx} +5 -3
  104. package/src/LinearMafDisplay/components/{Tree.tsx → Sidebar/Tree.tsx} +5 -1
  105. package/src/LinearMafDisplay/components/Sidebar/YScaleBars.tsx +23 -0
  106. package/src/LinearMafDisplay/index.ts +1 -1
  107. package/src/LinearMafDisplay/renderSvg.tsx +1 -1
  108. package/src/LinearMafDisplay/stateModel.ts +71 -18
  109. package/src/LinearMafDisplay/types.ts +4 -24
  110. package/src/LinearMafDisplay/util.ts +27 -0
  111. package/src/LinearMafRenderer/LinearMafRenderer.ts +10 -14
  112. package/src/LinearMafRenderer/makeImageData.ts +33 -22
  113. package/src/MafAddTrackWorkflow/index.ts +0 -1
  114. package/src/{MafRPC/index.ts → MafGetSamples/MafGetSamples.ts} +1 -8
  115. package/src/MafGetSamples/index.ts +9 -0
  116. package/src/MafGetSequences/MafGetSequences.ts +47 -0
  117. package/src/MafGetSequences/index.ts +9 -0
  118. package/src/MafTabixAdapter/MafTabixAdapter.ts +13 -12
  119. package/src/MafTabixAdapter/configSchema.ts +29 -1
  120. package/src/index.ts +4 -2
  121. package/src/util/__snapshots__/fastaUtils.test.ts.snap +22 -0
  122. package/src/util/extractSubsequence.test.ts +54 -0
  123. package/src/util/extractSubsequence.ts +74 -0
  124. package/src/util/fastaUtils.test.ts +99 -0
  125. package/src/util/fastaUtils.ts +102 -0
  126. package/src/util/fetchSequences.ts +57 -0
  127. package/src/util/useSequences.ts +90 -0
  128. package/src/util.ts +6 -2
  129. package/dist/LinearMafDisplay/components/ColorLegend.d.ts +0 -8
  130. package/dist/LinearMafDisplay/components/ColorLegend.js.map +0 -1
  131. package/dist/LinearMafDisplay/components/ReactComponent.js +0 -50
  132. package/dist/LinearMafDisplay/components/ReactComponent.js.map +0 -1
  133. package/dist/LinearMafDisplay/components/RectBg.js.map +0 -1
  134. package/dist/LinearMafDisplay/components/SetRowHeight.js +0 -36
  135. package/dist/LinearMafDisplay/components/SetRowHeight.js.map +0 -1
  136. package/dist/LinearMafDisplay/components/SvgWrapper.js.map +0 -1
  137. package/dist/LinearMafDisplay/components/Tree.js.map +0 -1
  138. package/dist/LinearMafDisplay/components/YScaleBars.js +0 -20
  139. package/dist/LinearMafDisplay/components/YScaleBars.js.map +0 -1
  140. package/dist/MafRPC/index.js.map +0 -1
  141. package/src/LinearMafDisplay/components/ReactComponent.tsx +0 -91
  142. package/src/LinearMafDisplay/components/SetRowHeight.tsx +0 -83
  143. package/src/LinearMafDisplay/components/YScaleBars.tsx +0 -41
  144. /package/dist/LinearMafDisplay/components/{SetRowHeight.d.ts → SetRowHeightDialog/SetRowHeightDialog.d.ts} +0 -0
  145. /package/dist/LinearMafDisplay/components/{RectBg.d.ts → Sidebar/RectBg.d.ts} +0 -0
  146. /package/dist/LinearMafDisplay/components/{RectBg.js → Sidebar/RectBg.js} +0 -0
  147. /package/src/LinearMafDisplay/components/{RectBg.tsx → Sidebar/RectBg.tsx} +0 -0
@@ -0,0 +1,83 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import { Dialog } from '@jbrowse/core/ui'
4
+ import {
5
+ Button,
6
+ DialogActions,
7
+ DialogContent,
8
+ TextField,
9
+ Typography,
10
+ } from '@mui/material'
11
+ import { observer } from 'mobx-react'
12
+ import { makeStyles } from 'tss-react/mui'
13
+
14
+ const useStyles = makeStyles()({
15
+ root: {
16
+ width: 500,
17
+ },
18
+ })
19
+
20
+ const SetRowHeightDialog = observer(function (props: {
21
+ model: {
22
+ rowHeight?: number
23
+ rowProportion?: number
24
+ setRowHeight: (arg: number) => void
25
+ setRowProportion: (arg: number) => void
26
+ }
27
+ handleClose: () => void
28
+ }) {
29
+ const { model, handleClose } = props
30
+ const { classes } = useStyles()
31
+ const [rowHeight, setRowHeight] = useState(`${model.rowHeight}`)
32
+ const [rowProportion, setRowProportion] = useState(`${model.rowProportion}`)
33
+
34
+ return (
35
+ <Dialog open onClose={handleClose} title="Set row height">
36
+ <form
37
+ onSubmit={event => {
38
+ event.preventDefault()
39
+ model.setRowProportion(+rowProportion)
40
+ model.setRowHeight(+rowHeight)
41
+ handleClose()
42
+ }}
43
+ >
44
+ <DialogContent className={classes.root}>
45
+ <Typography>
46
+ Set row height and the proportion of the row height to use for
47
+ drawing each row
48
+ </Typography>
49
+ <TextField
50
+ value={rowHeight}
51
+ helperText="Enter row height"
52
+ autoFocus
53
+ onChange={event => {
54
+ setRowHeight(event.target.value)
55
+ }}
56
+ />
57
+ <TextField
58
+ value={rowProportion}
59
+ helperText="Enter row proportion"
60
+ onChange={event => {
61
+ setRowProportion(event.target.value)
62
+ }}
63
+ />
64
+ <DialogActions>
65
+ <Button variant="contained" color="primary" type="submit">
66
+ Submit
67
+ </Button>
68
+ <Button
69
+ variant="contained"
70
+ color="secondary"
71
+ onClick={() => {
72
+ handleClose()
73
+ }}
74
+ >
75
+ Cancel
76
+ </Button>
77
+ </DialogActions>
78
+ </DialogContent>
79
+ </form>
80
+ </Dialog>
81
+ )
82
+ })
83
+ export default SetRowHeightDialog
@@ -2,21 +2,25 @@ import React from 'react'
2
2
 
3
3
  import { observer } from 'mobx-react'
4
4
 
5
- import { LinearMafDisplayModel } from '../stateModel'
6
5
  import RectBg from './RectBg'
7
6
  import Tree from './Tree'
8
7
 
8
+ import type { LinearMafDisplayModel } from '../../stateModel'
9
+
9
10
  const ColorLegend = observer(function ({
10
11
  model,
11
- labelWidth,
12
- svgFontSize,
13
12
  }: {
14
13
  model: LinearMafDisplayModel
15
- svgFontSize: number
16
- labelWidth: number
17
14
  }) {
18
- const { totalHeight, treeWidth, samples = [], rowHeight } = model
19
- const canDisplayLabel = rowHeight >= 8
15
+ const {
16
+ labelWidth,
17
+ canDisplayLabel,
18
+ totalHeight,
19
+ treeWidth,
20
+ samples = [],
21
+ rowHeight,
22
+ svgFontSize,
23
+ } = model
20
24
  const boxHeight = Math.min(20, rowHeight)
21
25
 
22
26
  return (
@@ -3,8 +3,8 @@ import React from 'react'
3
3
  import { getContainingView } from '@jbrowse/core/util'
4
4
  import { observer } from 'mobx-react'
5
5
 
6
- // locals
7
- import { LinearMafDisplayModel } from '../stateModel'
6
+ import type { LinearMafDisplayModel } from '../../stateModel'
7
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
8
8
 
9
9
  const SvgWrapper = observer(function ({
10
10
  children,
@@ -19,15 +19,17 @@ const SvgWrapper = observer(function ({
19
19
  return <>{children}</>
20
20
  } else {
21
21
  const { totalHeight } = model
22
+ const { width } = getContainingView(model) as LinearGenomeViewModel
22
23
  return (
23
24
  <svg
24
25
  style={{
25
26
  position: 'absolute',
27
+ userSelect: 'none',
26
28
  top: 0,
27
29
  left: 0,
28
30
  pointerEvents: 'none',
29
31
  height: totalHeight,
30
- width: getContainingView(model).width,
32
+ width,
31
33
  }}
32
34
  >
33
35
  {children}
@@ -2,7 +2,9 @@ import React from 'react'
2
2
 
3
3
  import { observer } from 'mobx-react'
4
4
 
5
- const Tree = observer(function ({ model }: { model: any }) {
5
+ import type { LinearMafDisplayModel } from '../../stateModel'
6
+
7
+ const Tree = observer(function ({ model }: { model: LinearMafDisplayModel }) {
6
8
  const {
7
9
  // this is needed for redrawing after zoom change, similar to react-msaview
8
10
  // renderTreeCanvas
@@ -20,7 +22,9 @@ const Tree = observer(function ({ model }: { model: any }) {
20
22
  const { source, target } = link
21
23
  const sy = source.x!
22
24
  const ty = target.x!
25
+ // @ts-expect-error
23
26
  const tx = showBranchLen ? target.len : target.y
27
+ // @ts-expect-error
24
28
  const sx = showBranchLen ? source.len : source.y
25
29
 
26
30
  // 1d line intersection to check if line crosses block at all, this is
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+
3
+ import { observer } from 'mobx-react'
4
+
5
+ import ColorLegend from './ColorLegend'
6
+ import SvgWrapper from './SvgWrapper'
7
+
8
+ import type { LinearMafDisplayModel } from '../../stateModel'
9
+
10
+ export const YScaleBars = observer(function (props: {
11
+ model: LinearMafDisplayModel
12
+ orientation?: string
13
+ exportSVG?: boolean
14
+ }) {
15
+ const { model } = props
16
+ return (
17
+ <SvgWrapper {...props}>
18
+ <ColorLegend model={model} />
19
+ </SvgWrapper>
20
+ )
21
+ })
22
+
23
+ export default YScaleBars
@@ -1,7 +1,7 @@
1
1
  import PluginManager from '@jbrowse/core/PluginManager'
2
2
  import { DisplayType } from '@jbrowse/core/pluggableElementTypes'
3
3
 
4
- import ReactComponent from './components/ReactComponent'
4
+ import ReactComponent from './components/LinearMafDisplayComponent'
5
5
  import configSchemaF from './configSchema'
6
6
  import stateModelFactory from './stateModel'
7
7
 
@@ -2,7 +2,7 @@ import React from 'react'
2
2
 
3
3
  import { getContainingView } from '@jbrowse/core/util'
4
4
 
5
- import YScaleBars from './components/YScaleBars'
5
+ import YScaleBars from './components/Sidebar/YScaleBars'
6
6
 
7
7
  import type { LinearMafDisplayModel } from './stateModel'
8
8
  import type {
@@ -1,5 +1,7 @@
1
+ import { lazy } from 'react'
2
+
1
3
  import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
2
- import { getEnv, getSession } from '@jbrowse/core/util'
4
+ import { getEnv, getSession, max, measureText } from '@jbrowse/core/util'
3
5
  import { getRpcSessionId } from '@jbrowse/core/util/tracks'
4
6
  import { ascending } from 'd3-array'
5
7
  import { cluster, hierarchy } from 'd3-hierarchy'
@@ -7,11 +9,10 @@ import deepEqual from 'fast-deep-equal'
7
9
  import { autorun } from 'mobx'
8
10
  import { addDisposer, isAlive, types } from 'mobx-state-tree'
9
11
 
10
- import SetRowHeightDialog from './components/SetRowHeight'
11
- import { maxLength, setBrLength } from './types'
12
+ import { maxLength, setBrLength } from './util'
12
13
  import { normalize } from '../util'
13
14
 
14
- import type { NodeWithIds, NodeWithIdsAndLength } from './types'
15
+ import type { NodeWithIds, NodeWithIdsAndLength, Sample } from './types'
15
16
  import type PluginManager from '@jbrowse/core/PluginManager'
16
17
  import type {
17
18
  AnyConfigurationModel,
@@ -21,11 +22,9 @@ import type { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view
21
22
  import type { HierarchyNode } from 'd3-hierarchy'
22
23
  import type { Instance } from 'mobx-state-tree'
23
24
 
24
- interface Sample {
25
- id: string
26
- label: string
27
- color?: string
28
- }
25
+ const SetRowHeightDialog = lazy(
26
+ () => import('./components/SetRowHeightDialog/SetRowHeightDialog'),
27
+ )
29
28
 
30
29
  /**
31
30
  * #stateModel LinearMafDisplay
@@ -79,6 +78,10 @@ export default function stateModelFactory(
79
78
  * #property
80
79
  */
81
80
  treeAreaWidth: 80,
81
+ /**
82
+ * #property
83
+ */
84
+ showAsUpperCase: true,
82
85
  }),
83
86
  )
84
87
  .volatile(() => ({
@@ -131,6 +134,12 @@ export default function stateModelFactory(
131
134
  self.volatileTree = tree
132
135
  }
133
136
  },
137
+ /**
138
+ * #action
139
+ */
140
+ setShowAsUpperCase(arg: boolean) {
141
+ self.showAsUpperCase = arg
142
+ },
134
143
  }))
135
144
  .views(self => ({
136
145
  /**
@@ -193,8 +202,20 @@ export default function stateModelFactory(
193
202
  * #getter
194
203
  */
195
204
  get samples() {
196
- return this.rowNames ? normalize(this.rowNames) : self.volatileSamples
205
+ if (this.rowNames) {
206
+ const volatileSamplesMap = self.volatileSamples
207
+ ? Object.fromEntries(self.volatileSamples.map(e => [e.id, e]))
208
+ : undefined
209
+ return normalize(this.rowNames).map(r => ({
210
+ ...r,
211
+ label: volatileSamplesMap?.[r.id]?.label || r.label,
212
+ color: volatileSamplesMap?.[r.id]?.color || r.color,
213
+ }))
214
+ } else {
215
+ return self.volatileSamples
216
+ }
197
217
  },
218
+
198
219
  /**
199
220
  * #getter
200
221
  */
@@ -239,6 +260,7 @@ export default function stateModelFactory(
239
260
  rowHeight,
240
261
  rowProportion,
241
262
  mismatchRendering,
263
+ showAsUpperCase,
242
264
  } = self
243
265
  const s = superRenderProps()
244
266
  return {
@@ -251,6 +273,7 @@ export default function stateModelFactory(
251
273
  rowProportion,
252
274
  showAllLetters,
253
275
  mismatchRendering,
276
+ showAsUpperCase,
254
277
  }
255
278
  },
256
279
  /**
@@ -291,6 +314,14 @@ export default function stateModelFactory(
291
314
  },
292
315
  ],
293
316
  },
317
+ {
318
+ label: 'Use upper-case',
319
+ type: 'checkbox',
320
+ checked: self.showAsUpperCase,
321
+ onClick: () => {
322
+ self.setShowAsUpperCase(!self.showAsUpperCase)
323
+ },
324
+ },
294
325
  {
295
326
  label: 'Show all letters',
296
327
  type: 'checkbox',
@@ -311,6 +342,32 @@ export default function stateModelFactory(
311
342
  },
312
343
  }
313
344
  })
345
+ .views(self => ({
346
+ /**
347
+ * #getter
348
+ */
349
+ get svgFontSize() {
350
+ return Math.min(Math.max(self.rowHeight, 8), 14)
351
+ },
352
+ /**
353
+ * #getter
354
+ */
355
+ get canDisplayLabel() {
356
+ return self.rowHeight >= 7
357
+ },
358
+ /**
359
+ * #getter
360
+ */
361
+ get labelWidth() {
362
+ const minWidth = 20
363
+ return max(
364
+ self.samples
365
+ ?.map(s => measureText(s.label, this.svgFontSize))
366
+ .map(width => (this.canDisplayLabel ? width : minWidth)) || [],
367
+ 0,
368
+ )
369
+ },
370
+ }))
314
371
  .actions(self => ({
315
372
  afterCreate() {
316
373
  addDisposer(
@@ -319,11 +376,8 @@ export default function stateModelFactory(
319
376
  try {
320
377
  const { rpcManager } = getSession(self)
321
378
  const sessionId = getRpcSessionId(self)
322
-
323
- const results = (await rpcManager.call(
324
- sessionId,
325
- 'MafGetSamples',
326
- {
379
+ self.setSamples(
380
+ (await rpcManager.call(sessionId, 'MafGetSamples', {
327
381
  sessionId,
328
382
  adapterConfig: self.adapterConfig,
329
383
  statusCallback: (message: string) => {
@@ -331,9 +385,8 @@ export default function stateModelFactory(
331
385
  self.setMessage(message)
332
386
  }
333
387
  },
334
- },
335
- )) as any
336
- self.setSamples(results)
388
+ })) as { samples: Sample[]; tree: unknown },
389
+ )
337
390
  } catch (e) {
338
391
  console.error(e)
339
392
  getSession(self).notifyError(`${e}`, e)
@@ -1,7 +1,3 @@
1
- import { max } from 'd3-array'
2
-
3
- import type { HierarchyNode } from 'd3-hierarchy'
4
-
5
1
  export interface NodeWithIds {
6
2
  id: string
7
3
  name: string
@@ -18,24 +14,8 @@ export interface NodeWithIdsAndLength {
18
14
  length: number
19
15
  }
20
16
 
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
- }
17
+ export interface Sample {
18
+ id: string
19
+ label?: string
20
+ color?: string
41
21
  }
@@ -0,0 +1,27 @@
1
+ import { max } from 'd3-array'
2
+
3
+ import type { NodeWithIds } from './types'
4
+ import type { HierarchyNode } from 'd3-hierarchy'
5
+
6
+ // basically same as maxLength from https://observablehq.com/@d3/tree-of-life
7
+ export function maxLength(d: HierarchyNode<NodeWithIds>): number {
8
+ return (
9
+ (d.data.length || 0) + (d.children ? max(d.children, maxLength) || 0 : 0)
10
+ )
11
+ }
12
+
13
+ // basically same as setRadius from https://observablehq.com/@d3/tree-of-life
14
+ export function setBrLength(
15
+ d: HierarchyNode<NodeWithIds>,
16
+ y0: number,
17
+ k: number,
18
+ ) {
19
+ // @ts-expect-error
20
+ d.len = (y0 += Math.max(d.data.length || 0, 0)) * k
21
+
22
+ if (d.children) {
23
+ d.children.forEach(d => {
24
+ setBrLength(d, y0, k)
25
+ })
26
+ }
27
+ }
@@ -19,6 +19,7 @@ interface RenderArgs extends RenderArgsDeserialized {
19
19
  showAllLetters: boolean
20
20
  mismatchRendering: boolean
21
21
  statusCallback?: (arg: string) => void
22
+ showAsUpperCase: boolean
22
23
  }
23
24
 
24
25
  export default class LinearMafRenderer extends FeatureRendererType {
@@ -45,22 +46,17 @@ export default class LinearMafRenderer extends FeatureRendererType {
45
46
  const height = samples.length * rowHeight + 100
46
47
  const width = (region.end - region.start) / bpPerPx
47
48
  const features = await this.getFeatures(renderProps)
48
- const res = await renderToAbstractCanvas(
49
- width,
50
- height,
51
- renderProps,
52
- async ctx => {
53
- await updateStatus('Rendering alignment', statusCallback, () => {
54
- makeImageData({
55
- ctx,
56
- renderArgs: {
57
- ...renderProps,
58
- features,
59
- },
60
- })
49
+ const res = await updateStatus('Rendering alignment', statusCallback, () =>
50
+ renderToAbstractCanvas(width, height, renderProps, ctx => {
51
+ makeImageData({
52
+ ctx,
53
+ renderArgs: {
54
+ ...renderProps,
55
+ features,
56
+ },
61
57
  })
62
58
  return undefined
63
- },
59
+ }),
64
60
  )
65
61
  const results = await super.render({
66
62
  ...renderProps,
@@ -21,6 +21,11 @@ interface RenderArgs extends RenderArgsDeserialized {
21
21
  mismatchRendering: boolean
22
22
  features: Map<string, Feature>
23
23
  statusCallback?: (arg: string) => void
24
+ showAsUpperCase: boolean
25
+ }
26
+
27
+ function getLetter(a: string, showAsUpperCase: boolean) {
28
+ return showAsUpperCase ? a.toUpperCase() : a
24
29
  }
25
30
 
26
31
  export function makeImageData({
@@ -40,6 +45,7 @@ export function makeImageData({
40
45
  samples,
41
46
  rowProportion,
42
47
  features,
48
+ showAsUpperCase,
43
49
  } = renderArgs
44
50
  const region = regions[0]!
45
51
  const canvasWidth = (region.end - region.start) / bpPerPx
@@ -61,11 +67,11 @@ export function makeImageData({
61
67
 
62
68
  for (const feature of features.values()) {
63
69
  const [leftPx] = featureSpanPx(feature, region, bpPerPx)
64
- const vals = feature.get('alignments') as Record<string, { data: string }>
70
+ const vals = feature.get('alignments') as Record<string, { seq: string }>
65
71
  const seq = feature.get('seq').toLowerCase()
66
72
  const r = Object.entries(vals)
67
73
  for (const [sample, val] of r) {
68
- const origAlignment = val.data
74
+ const origAlignment = val.seq
69
75
  const alignment = origAlignment.toLowerCase()
70
76
 
71
77
  const row = sampleToRowMap.get(sample)
@@ -74,11 +80,12 @@ export function makeImageData({
74
80
  }
75
81
 
76
82
  const t = rowHeight * row
83
+ const t2 = offset + t
77
84
 
78
85
  // gaps
79
86
  ctx.beginPath()
80
87
  ctx.fillStyle = 'black'
81
- for (let i = 0, o = 0; i < alignment.length; i++) {
88
+ for (let i = 0, o = 0, l = alignment.length; i < l; i++) {
82
89
  if (seq[i] !== '-') {
83
90
  if (alignment[i] === '-') {
84
91
  const l = leftPx + scale * o
@@ -93,12 +100,12 @@ export function makeImageData({
93
100
  if (!showAllLetters) {
94
101
  // matches
95
102
  ctx.fillStyle = 'lightgrey'
96
- for (let i = 0, o = 0; i < alignment.length; i++) {
103
+ for (let i = 0, o = 0, l = alignment.length; i < l; i++) {
97
104
  if (seq[i] !== '-') {
98
105
  const c = alignment[i]
99
106
  const l = leftPx + scale * o
100
107
  if (seq[i] === c && c !== '-' && c !== ' ') {
101
- fillRect(ctx, l, offset + t, scale + f, h, canvasWidth)
108
+ fillRect(ctx, l, t2, scale + f, h, canvasWidth)
102
109
  }
103
110
  o++
104
111
  }
@@ -106,7 +113,7 @@ export function makeImageData({
106
113
  }
107
114
 
108
115
  // mismatches
109
- for (let i = 0, o = 0; i < alignment.length; i++) {
116
+ for (let i = 0, o = 0, l = alignment.length; i < l; i++) {
110
117
  const c = alignment[i]
111
118
  if (seq[i] !== '-') {
112
119
  if (c !== '-') {
@@ -115,7 +122,7 @@ export function makeImageData({
115
122
  fillRect(
116
123
  ctx,
117
124
  l,
118
- offset + t,
125
+ t2,
119
126
  scale + f,
120
127
  h,
121
128
  canvasWidth,
@@ -128,7 +135,7 @@ export function makeImageData({
128
135
  fillRect(
129
136
  ctx,
130
137
  l,
131
- offset + t,
138
+ t2,
132
139
  scale + f,
133
140
  h,
134
141
  canvasWidth,
@@ -146,7 +153,7 @@ export function makeImageData({
146
153
  // font
147
154
  const charSizeW = 10
148
155
  if (scale >= charSizeW) {
149
- for (let i = 0, o = 0; i < alignment.length; i++) {
156
+ for (let i = 0, o = 0, l = alignment.length; i < l; i++) {
150
157
  if (seq[i] !== '-') {
151
158
  const l = leftPx + scale * o
152
159
  const offset = (scale - charSizeW) / 2 + 1
@@ -156,7 +163,11 @@ export function makeImageData({
156
163
  ? (contrastForBase[c] ?? 'white')
157
164
  : 'black'
158
165
  if (rowHeight > charHeight) {
159
- ctx.fillText(origAlignment[i] || '', l + offset, hp2 + t + 3)
166
+ ctx.fillText(
167
+ getLetter(origAlignment[i] || '', showAsUpperCase),
168
+ l + offset,
169
+ hp2 + t + 3,
170
+ )
160
171
  }
161
172
  }
162
173
  o++
@@ -170,11 +181,11 @@ export function makeImageData({
170
181
  // insertions are always 'on top' of the other features
171
182
  for (const feature of features.values()) {
172
183
  const [leftPx] = featureSpanPx(feature, region, bpPerPx)
173
- const vals = feature.get('alignments') as Record<string, { data: string }>
184
+ const vals = feature.get('alignments') as Record<string, { seq: string }>
174
185
  const seq = feature.get('seq').toLowerCase()
175
186
 
176
187
  for (const [sample, val] of Object.entries(vals)) {
177
- const origAlignment = val.data
188
+ const origAlignment = val.seq
178
189
  const alignment = origAlignment.toLowerCase()
179
190
  const row = sampleToRowMap.get(sample)
180
191
  if (row === undefined) {
@@ -182,8 +193,8 @@ export function makeImageData({
182
193
  }
183
194
 
184
195
  const t = rowHeight * row
185
-
186
- for (let i = 0, o = 0; i < alignment.length; i++) {
196
+ const t2 = offset + t
197
+ for (let i = 0, o = 0, l = alignment.length; i < l; i++) {
187
198
  let ins = ''
188
199
  while (seq[i] === '-') {
189
200
  if (alignment[i] !== '-' && alignment[i] !== ' ') {
@@ -197,14 +208,14 @@ export function makeImageData({
197
208
  if (ins.length > 10) {
198
209
  const txt = `${ins.length}`
199
210
  if (bpPerPx > 10) {
200
- fillRect(ctx, l - 1, t, 2, h, canvasWidth, 'purple')
211
+ fillRect(ctx, l - 1, t2, 2, h, canvasWidth, 'purple')
201
212
  } else if (h > charHeight) {
202
- const rwidth = measureText(txt)
203
- const padding = 5
213
+ const rwidth = measureText(txt, 10)
214
+ const padding = 2
204
215
  fillRect(
205
216
  ctx,
206
217
  l - rwidth / 2 - padding,
207
- t,
218
+ t2,
208
219
  rwidth + 2 * padding,
209
220
  h,
210
221
  canvasWidth,
@@ -217,7 +228,7 @@ export function makeImageData({
217
228
  fillRect(
218
229
  ctx,
219
230
  l - padding,
220
- t,
231
+ t2,
221
232
  2 * padding,
222
233
  h,
223
234
  canvasWidth,
@@ -225,10 +236,10 @@ export function makeImageData({
225
236
  )
226
237
  }
227
238
  } else {
228
- fillRect(ctx, l, offset + t, 1, h, canvasWidth, 'purple')
239
+ fillRect(ctx, l, t2, 1, h, canvasWidth, 'purple')
229
240
  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)
241
+ fillRect(ctx, l - 2, t2, 5, 1, canvasWidth)
242
+ fillRect(ctx, l - 2, t2 + h - 1, 5, 1, canvasWidth)
232
243
  }
233
244
  }
234
245
  }
@@ -2,7 +2,6 @@ import PluginManager from '@jbrowse/core/PluginManager'
2
2
  import { AddTrackWorkflowType } from '@jbrowse/core/pluggableElementTypes'
3
3
  import { types } from 'mobx-state-tree'
4
4
 
5
- // locals
6
5
  import MultiMAFWidget from './AddTrackWorkflow'
7
6
 
8
7
  export default function MafAddTrackWorkflowF(pluginManager: PluginManager) {