jbrowse-plugin-mafviewer 1.2.3 → 1.2.4

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 (71) 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/ColorLegend.d.ts +2 -4
  6. package/dist/LinearMafDisplay/components/ColorLegend.js +2 -3
  7. package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
  8. package/dist/LinearMafDisplay/components/{ReactComponent.d.ts → LinearMafDisplayComponent.d.ts} +1 -1
  9. package/dist/LinearMafDisplay/components/{ReactComponent.js → LinearMafDisplayComponent.js} +2 -2
  10. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -0
  11. package/dist/LinearMafDisplay/components/SetRowHeightDialog.js +38 -0
  12. package/dist/LinearMafDisplay/components/SetRowHeightDialog.js.map +1 -0
  13. package/dist/LinearMafDisplay/components/SvgWrapper.d.ts +1 -1
  14. package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -1
  15. package/dist/LinearMafDisplay/components/Tree.d.ts +2 -1
  16. package/dist/LinearMafDisplay/components/Tree.js +2 -0
  17. package/dist/LinearMafDisplay/components/Tree.js.map +1 -1
  18. package/dist/LinearMafDisplay/components/YScaleBars.d.ts +1 -1
  19. package/dist/LinearMafDisplay/components/YScaleBars.js +1 -10
  20. package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
  21. package/dist/LinearMafDisplay/index.js +1 -1
  22. package/dist/LinearMafDisplay/index.js.map +1 -1
  23. package/dist/LinearMafDisplay/stateModel.d.ts +20 -19
  24. package/dist/LinearMafDisplay/stateModel.js +42 -7
  25. package/dist/LinearMafDisplay/stateModel.js.map +1 -1
  26. package/dist/LinearMafDisplay/types.d.ts +5 -3
  27. package/dist/LinearMafDisplay/types.js +1 -15
  28. package/dist/LinearMafDisplay/types.js.map +1 -1
  29. package/dist/LinearMafDisplay/util.d.ts +4 -0
  30. package/dist/LinearMafDisplay/util.js +16 -0
  31. package/dist/LinearMafDisplay/util.js.map +1 -0
  32. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +4 -4
  33. package/dist/LinearMafRenderer/LinearMafRenderer.js +8 -10
  34. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
  35. package/dist/LinearMafRenderer/makeImageData.js +18 -16
  36. package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
  37. package/dist/MafAddTrackWorkflow/index.js +0 -1
  38. package/dist/MafAddTrackWorkflow/index.js.map +1 -1
  39. package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +1 -1
  40. package/dist/MafTabixAdapter/MafTabixAdapter.js +6 -7
  41. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
  42. package/dist/MafTabixAdapter/configSchema.js +29 -1
  43. package/dist/MafTabixAdapter/configSchema.js.map +1 -1
  44. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +7 -20
  45. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
  46. package/dist/util.d.ts +2 -2
  47. package/dist/util.js +5 -1
  48. package/dist/util.js.map +1 -1
  49. package/package.json +10 -10
  50. package/src/BigMafAdapter/BigMafAdapter.ts +52 -49
  51. package/src/LinearMafDisplay/components/ColorLegend.tsx +11 -7
  52. package/src/LinearMafDisplay/components/{ReactComponent.tsx → LinearMafDisplayComponent.tsx} +2 -2
  53. package/src/LinearMafDisplay/components/SetRowHeightDialog.tsx +83 -0
  54. package/src/LinearMafDisplay/components/SvgWrapper.tsx +1 -2
  55. package/src/LinearMafDisplay/components/Tree.tsx +5 -1
  56. package/src/LinearMafDisplay/components/YScaleBars.tsx +3 -21
  57. package/src/LinearMafDisplay/index.ts +1 -1
  58. package/src/LinearMafDisplay/stateModel.ts +49 -18
  59. package/src/LinearMafDisplay/types.ts +4 -24
  60. package/src/LinearMafDisplay/util.ts +27 -0
  61. package/src/LinearMafRenderer/LinearMafRenderer.ts +9 -14
  62. package/src/LinearMafRenderer/makeImageData.ts +18 -17
  63. package/src/MafAddTrackWorkflow/index.ts +0 -1
  64. package/src/MafTabixAdapter/MafTabixAdapter.ts +9 -7
  65. package/src/MafTabixAdapter/configSchema.ts +29 -1
  66. package/src/util.ts +6 -2
  67. package/dist/LinearMafDisplay/components/ReactComponent.js.map +0 -1
  68. package/dist/LinearMafDisplay/components/SetRowHeight.js +0 -36
  69. package/dist/LinearMafDisplay/components/SetRowHeight.js.map +0 -1
  70. package/src/LinearMafDisplay/components/SetRowHeight.tsx +0 -83
  71. /package/dist/LinearMafDisplay/components/{SetRowHeight.d.ts → SetRowHeightDialog.d.ts} +0 -0
package/dist/util.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  export declare function normalize(r: string[] | {
2
2
  id: string;
3
- label: string;
3
+ label?: string;
4
4
  color?: string;
5
5
  }[]): {
6
6
  id: string;
7
- label: string;
7
+ label?: string;
8
8
  color?: string;
9
9
  }[];
package/dist/util.js CHANGED
@@ -3,7 +3,11 @@ function isStrs(array) {
3
3
  }
4
4
  export function normalize(r) {
5
5
  return isStrs(r)
6
- ? r.map(elt => ({ id: elt, label: elt, color: undefined }))
6
+ ? r.map(elt => ({
7
+ id: elt,
8
+ label: elt,
9
+ color: undefined,
10
+ }))
7
11
  : r;
8
12
  }
9
13
  //# sourceMappingURL=util.js.map
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,KAAgB;IAC9B,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,CAA6D;IAE7D,OAAO,MAAM,CAAC,CAAC,CAAC;QACd,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC,CAAA;AACP,CAAC"}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,KAAgB;IAC9B,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,CAA8D;IAE9D,OAAO,MAAM,CAAC,CAAC,CAAC;QACd,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACZ,EAAE,EAAE,GAAG;YACP,KAAK,EAAE,GAAG;YACV,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC,CAAA;AACP,CAAC"}
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.3",
2
+ "version": "1.2.4",
3
3
  "license": "MIT",
4
4
  "name": "jbrowse-plugin-mafviewer",
5
5
  "keywords": [
@@ -31,12 +31,12 @@
31
31
  "@jbrowse/core": "^3.0.1",
32
32
  "@jbrowse/plugin-data-management": "^3.0.1",
33
33
  "@jbrowse/plugin-linear-genome-view": "^3.0.1",
34
- "@mui/material": "^6.2.0",
35
- "@mui/system": "^6.2.0",
36
- "@mui/x-data-grid": "^7.2.0",
34
+ "@mui/material": "^7.0.1",
35
+ "@mui/system": "^7.0.1",
36
+ "@mui/x-data-grid": "^8.2.0",
37
37
  "@types/d3-array": "^3.2.1",
38
38
  "@types/d3-hierarchy": "^3.1.7",
39
- "@types/node": "^22.10.2",
39
+ "@types/node": "^22.15.16",
40
40
  "@types/react": "^19.0.1",
41
41
  "@typescript-eslint/eslint-plugin": "^8.18.0",
42
42
  "@typescript-eslint/parser": "^8.18.0",
@@ -46,29 +46,29 @@
46
46
  "eslint-plugin-import": "^2.31.0",
47
47
  "eslint-plugin-react": "^7.20.3",
48
48
  "eslint-plugin-react-hooks": "^5.1.0",
49
- "eslint-plugin-unicorn": "^56.0.1",
49
+ "eslint-plugin-unicorn": "^59.0.1",
50
50
  "mobx": "^6.0.0",
51
51
  "mobx-react": "^9.0.1",
52
52
  "mobx-state-tree": "^5.4.1",
53
53
  "prettier": "^3.4.2",
54
- "pretty-bytes": "^6.1.1",
54
+ "pretty-bytes": "^7.0.0",
55
55
  "react": "^19.0.0",
56
56
  "react-dom": "^19.0.0",
57
57
  "rimraf": "^6.0.1",
58
58
  "rxjs": "^7.8.1",
59
59
  "serve": "^14.2.0",
60
- "tss-react": "^4.8.6",
60
+ "tss-react": "^4.9.18",
61
61
  "typescript": "^5.1.6",
62
62
  "typescript-eslint": "^8.18.0"
63
63
  },
64
64
  "dependencies": {
65
- "@gmod/bgzf-filehandle": "^2.0.4",
65
+ "@gmod/bgzf-filehandle": "^3.0.2",
66
66
  "abortable-promise-cache": "^1.5.0",
67
67
  "buffer": "^6.0.3",
68
68
  "d3-array": "^3.2.4",
69
69
  "d3-hierarchy": "^3.1.2",
70
70
  "fast-deep-equal": "^3.1.3",
71
- "generic-filehandle2": "^1.0.0",
71
+ "generic-filehandle2": "^2.0.1",
72
72
  "long": "^5.2.3"
73
73
  }
74
74
  }
@@ -26,12 +26,13 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
26
26
  if (!this.getSubAdapter) {
27
27
  throw new Error('no getSubAdapter available')
28
28
  }
29
- const adapter = await this.getSubAdapter({
30
- ...getSnapshot(this.config),
31
- type: 'BigBedAdapter',
32
- })
33
29
  return {
34
- adapter: adapter.dataAdapter as BaseFeatureDataAdapter,
30
+ adapter: (
31
+ await this.getSubAdapter({
32
+ ...getSnapshot(this.config),
33
+ type: 'BigBedAdapter',
34
+ })
35
+ ).dataAdapter as BaseFeatureDataAdapter,
35
36
  }
36
37
  }
37
38
  async setupPre() {
@@ -59,59 +60,61 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
59
60
  return ObservableCreate<Feature>(async observer => {
60
61
  const { adapter } = await this.setup()
61
62
  const features = await updateStatus(
62
- 'Downloading alignment',
63
+ 'Downloading alignments',
63
64
  statusCallback,
64
65
  () => firstValueFrom(adapter.getFeatures(query).pipe(toArray())),
65
66
  )
66
- for (const feature of features) {
67
- const maf = feature.get('mafBlock') as string
68
- const blocks = maf.split(';')
69
- let aln: string | undefined
70
- const alns = [] as string[]
71
- const alignments = {} as Record<string, OrganismRecord>
72
- const blocks2 = [] as string[]
73
- for (const block of blocks) {
74
- if (block.startsWith('s')) {
75
- if (aln) {
76
- alns.push(block.split(/ +/)[6]!)
77
- blocks2.push(block)
78
- } else {
79
- aln = block.split(/ +/)[6]
80
- alns.push(aln!)
81
- blocks2.push(block)
67
+ await updateStatus('Processing alignments', statusCallback, () => {
68
+ for (const feature of features) {
69
+ const maf = feature.get('mafBlock') as string
70
+ const blocks = maf.split(';')
71
+ let aln: string | undefined
72
+ const alns = [] as string[]
73
+ const alignments = {} as Record<string, OrganismRecord>
74
+ const blocks2 = [] as string[]
75
+ for (const block of blocks) {
76
+ if (block.startsWith('s')) {
77
+ if (aln) {
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
+ }
82
85
  }
83
86
  }
84
- }
85
87
 
86
- for (let i = 0; i < blocks2.length; i++) {
87
- const elt = blocks2[i]!
88
- const ad = elt.split(/ +/)
89
- const y = ad[1]!.split('.')
90
- const org = y[0]!
91
- const chr = y[1]!
88
+ for (let i = 0; i < blocks2.length; i++) {
89
+ const elt = blocks2[i]!
90
+ const ad = elt.split(/ +/)
91
+ const y = ad[1]!.split('.')
92
+ const org = y[0]!
93
+ const chr = y[1]!
92
94
 
93
- alignments[org] = {
94
- chr: chr,
95
- start: +ad[1]!,
96
- srcSize: +ad[2]!,
97
- strand: ad[3] === '+' ? 1 : -1,
98
- unknown: +ad[4]!,
99
- data: alns[i]!,
95
+ alignments[org] = {
96
+ chr: chr,
97
+ start: +ad[1]!,
98
+ srcSize: +ad[2]!,
99
+ strand: ad[3] === '+' ? 1 : -1,
100
+ unknown: +ad[4]!,
101
+ data: alns[i]!,
102
+ }
100
103
  }
104
+ observer.next(
105
+ new SimpleFeature({
106
+ id: feature.id(),
107
+ data: {
108
+ start: feature.get('start'),
109
+ end: feature.get('end'),
110
+ refName: feature.get('refName'),
111
+ seq: alns[0],
112
+ alignments: alignments,
113
+ },
114
+ }),
115
+ )
101
116
  }
102
- observer.next(
103
- new SimpleFeature({
104
- id: feature.id(),
105
- data: {
106
- start: feature.get('start'),
107
- end: feature.get('end'),
108
- refName: feature.get('refName'),
109
- seq: alns[0],
110
- alignments: alignments,
111
- },
112
- }),
113
- )
114
- }
117
+ })
115
118
  observer.complete()
116
119
  })
117
120
  }
@@ -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 (
@@ -1,14 +1,14 @@
1
1
  import React, { useRef, useState } from 'react'
2
2
 
3
+ import { SanitizedHTML } from '@jbrowse/core/ui'
3
4
  import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
4
- import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML'
5
5
  import { getContainingView, getEnv } from '@jbrowse/core/util'
6
6
  import { observer } from 'mobx-react'
7
7
  import { makeStyles } from 'tss-react/mui'
8
8
 
9
9
  import YScaleBars from './YScaleBars'
10
- import { LinearMafDisplayModel } from '../stateModel'
11
10
 
11
+ import type { LinearMafDisplayModel } from '../stateModel'
12
12
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
13
13
 
14
14
  const useStyles = makeStyles()({
@@ -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
@@ -3,8 +3,7 @@ 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'
8
7
 
9
8
  const SvgWrapper = observer(function ({
10
9
  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
@@ -1,13 +1,11 @@
1
1
  import React from 'react'
2
2
 
3
- import { measureText } from '@jbrowse/core/util'
4
3
  import { observer } from 'mobx-react'
5
4
 
6
- // locals
7
- import { LinearMafDisplayModel } from '../stateModel'
8
5
  import ColorLegend from './ColorLegend'
9
6
  import SvgWrapper from './SvgWrapper'
10
- import { max } from './util'
7
+
8
+ import type { LinearMafDisplayModel } from '../stateModel'
11
9
 
12
10
  export const YScaleBars = observer(function (props: {
13
11
  model: LinearMafDisplayModel
@@ -15,25 +13,9 @@ export const YScaleBars = observer(function (props: {
15
13
  exportSVG?: boolean
16
14
  }) {
17
15
  const { model } = props
18
- const { rowHeight, samples } = model
19
- const svgFontSize = Math.min(Math.max(rowHeight, 8), 14)
20
- const canDisplayLabel = rowHeight >= 8
21
- const minWidth = 20
22
-
23
- const labelWidth = max(
24
- samples
25
- ?.map(s => measureText(s.label, svgFontSize))
26
- .map(width => (canDisplayLabel ? width : minWidth)) || [],
27
- 0,
28
- )
29
-
30
16
  return (
31
17
  <SvgWrapper {...props}>
32
- <ColorLegend
33
- model={model}
34
- labelWidth={labelWidth}
35
- svgFontSize={svgFontSize}
36
- />
18
+ <ColorLegend model={model} />
37
19
  </SvgWrapper>
38
20
  )
39
21
  })
@@ -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
 
@@ -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,7 @@ 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(() => import('./components/SetRowHeightDialog'))
29
26
 
30
27
  /**
31
28
  * #stateModel LinearMafDisplay
@@ -193,8 +190,20 @@ export default function stateModelFactory(
193
190
  * #getter
194
191
  */
195
192
  get samples() {
196
- return this.rowNames ? normalize(this.rowNames) : self.volatileSamples
193
+ if (this.rowNames) {
194
+ const volatileSamplesMap = self.volatileSamples
195
+ ? Object.fromEntries(self.volatileSamples.map(e => [e.id, e]))
196
+ : undefined
197
+ return normalize(this.rowNames).map(r => ({
198
+ ...r,
199
+ label: volatileSamplesMap?.[r.id]?.label || r.label,
200
+ color: volatileSamplesMap?.[r.id]?.color || r.color,
201
+ }))
202
+ } else {
203
+ return self.volatileSamples
204
+ }
197
205
  },
206
+
198
207
  /**
199
208
  * #getter
200
209
  */
@@ -311,6 +320,32 @@ export default function stateModelFactory(
311
320
  },
312
321
  }
313
322
  })
323
+ .views(self => ({
324
+ /**
325
+ * #getter
326
+ */
327
+ get svgFontSize() {
328
+ return Math.min(Math.max(self.rowHeight, 8), 14)
329
+ },
330
+ /**
331
+ * #getter
332
+ */
333
+ get canDisplayLabel() {
334
+ return self.rowHeight >= 7
335
+ },
336
+ /**
337
+ * #getter
338
+ */
339
+ get labelWidth() {
340
+ const minWidth = 20
341
+ return max(
342
+ self.samples
343
+ ?.map(s => measureText(s.label, this.svgFontSize))
344
+ .map(width => (this.canDisplayLabel ? width : minWidth)) || [],
345
+ 0,
346
+ )
347
+ },
348
+ }))
314
349
  .actions(self => ({
315
350
  afterCreate() {
316
351
  addDisposer(
@@ -319,11 +354,8 @@ export default function stateModelFactory(
319
354
  try {
320
355
  const { rpcManager } = getSession(self)
321
356
  const sessionId = getRpcSessionId(self)
322
-
323
- const results = (await rpcManager.call(
324
- sessionId,
325
- 'MafGetSamples',
326
- {
357
+ self.setSamples(
358
+ (await rpcManager.call(sessionId, 'MafGetSamples', {
327
359
  sessionId,
328
360
  adapterConfig: self.adapterConfig,
329
361
  statusCallback: (message: string) => {
@@ -331,9 +363,8 @@ export default function stateModelFactory(
331
363
  self.setMessage(message)
332
364
  }
333
365
  },
334
- },
335
- )) as any
336
- self.setSamples(results)
366
+ })) as { samples: Sample[]; tree: unknown },
367
+ )
337
368
  } catch (e) {
338
369
  console.error(e)
339
370
  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
+ }
@@ -45,22 +45,17 @@ export default class LinearMafRenderer extends FeatureRendererType {
45
45
  const height = samples.length * rowHeight + 100
46
46
  const width = (region.end - region.start) / bpPerPx
47
47
  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
- })
48
+ const res = await updateStatus('Rendering alignment', statusCallback, () =>
49
+ renderToAbstractCanvas(width, height, renderProps, ctx => {
50
+ makeImageData({
51
+ ctx,
52
+ renderArgs: {
53
+ ...renderProps,
54
+ features,
55
+ },
61
56
  })
62
57
  return undefined
63
- },
58
+ }),
64
59
  )
65
60
  const results = await super.render({
66
61
  ...renderProps,