react-msaview 5.1.0 → 5.2.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 (100) hide show
  1. package/bundle/index.js +35 -135
  2. package/bundle/index.js.map +1 -1
  3. package/dist/calculateBlocks.d.ts +4 -10
  4. package/dist/calculateBlocks.js +12 -20
  5. package/dist/calculateBlocks.js.map +1 -1
  6. package/dist/components/ConservationTrack.js +18 -24
  7. package/dist/components/ConservationTrack.js.map +1 -1
  8. package/dist/components/TextTrack.js +24 -34
  9. package/dist/components/TextTrack.js.map +1 -1
  10. package/dist/components/dialogs/ExportSVGDialog.js +20 -20
  11. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  12. package/dist/components/dialogs/InterProScanDialog.js +6 -1
  13. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  14. package/dist/components/header/FileMenu.js +2 -1
  15. package/dist/components/header/FileMenu.js.map +1 -1
  16. package/dist/components/header/Header.js +1 -1
  17. package/dist/components/header/settingsMenuItems.js +24 -8
  18. package/dist/components/header/settingsMenuItems.js.map +1 -1
  19. package/dist/components/minimap/Minimap.js +1 -6
  20. package/dist/components/minimap/Minimap.js.map +1 -1
  21. package/dist/components/minimap/MinimapSVG.js +4 -8
  22. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  23. package/dist/components/msa/getVisibleLeaves.d.ts +6 -0
  24. package/dist/components/msa/getVisibleLeaves.js +9 -0
  25. package/dist/components/msa/getVisibleLeaves.js.map +1 -0
  26. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +2 -1
  27. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +5 -6
  28. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  29. package/dist/components/msa/renderMSABlock.d.ts +2 -1
  30. package/dist/components/msa/renderMSABlock.js +98 -84
  31. package/dist/components/msa/renderMSABlock.js.map +1 -1
  32. package/dist/components/msa/renderMSAMouseover.js +3 -3
  33. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  34. package/dist/components/msa/visibleColRange.d.ts +8 -0
  35. package/dist/components/msa/visibleColRange.js +10 -0
  36. package/dist/components/msa/visibleColRange.js.map +1 -0
  37. package/dist/components/renderCtx.d.ts +7 -0
  38. package/dist/components/renderCtx.js +2 -0
  39. package/dist/components/renderCtx.js.map +1 -0
  40. package/dist/components/tracks/renderTracksSvg.d.ts +6 -5
  41. package/dist/components/tracks/renderTracksSvg.js +11 -4
  42. package/dist/components/tracks/renderTracksSvg.js.map +1 -1
  43. package/dist/components/tree/TreeCanvas.js.map +1 -1
  44. package/dist/components/tree/TreeCanvasBlock.js +2 -10
  45. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  46. package/dist/components/tree/renderTreeCanvas.d.ts +5 -4
  47. package/dist/components/tree/renderTreeCanvas.js +16 -24
  48. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  49. package/dist/components/util.d.ts +0 -14
  50. package/dist/components/util.js +0 -58
  51. package/dist/components/util.js.map +1 -1
  52. package/dist/hierarchy.d.ts +2 -0
  53. package/dist/hierarchy.js +19 -0
  54. package/dist/hierarchy.js.map +1 -1
  55. package/dist/index.js +7 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/model.d.ts +26 -30
  58. package/dist/model.js +74 -82
  59. package/dist/model.js.map +1 -1
  60. package/dist/renderToSvg.d.ts +1 -1
  61. package/dist/renderToSvg.js +20 -26
  62. package/dist/renderToSvg.js.map +1 -1
  63. package/dist/rowCoordinateCalculations.d.ts +0 -9
  64. package/dist/rowCoordinateCalculations.js +0 -20
  65. package/dist/rowCoordinateCalculations.js.map +1 -1
  66. package/dist/util.d.ts +1 -0
  67. package/dist/util.js +23 -1
  68. package/dist/util.js.map +1 -1
  69. package/dist/version.d.ts +1 -1
  70. package/dist/version.js +1 -1
  71. package/package.json +5 -5
  72. package/src/calculateBlocks.ts +18 -51
  73. package/src/components/ConservationTrack.tsx +18 -27
  74. package/src/components/TextTrack.tsx +30 -38
  75. package/src/components/dialogs/ExportSVGDialog.tsx +27 -17
  76. package/src/components/dialogs/InterProScanDialog.tsx +6 -1
  77. package/src/components/header/FileMenu.tsx +2 -1
  78. package/src/components/header/Header.tsx +1 -1
  79. package/src/components/header/settingsMenuItems.ts +24 -8
  80. package/src/components/minimap/Minimap.tsx +1 -6
  81. package/src/components/minimap/MinimapSVG.tsx +4 -8
  82. package/src/components/msa/getVisibleLeaves.ts +21 -0
  83. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +9 -9
  84. package/src/components/msa/renderMSABlock.ts +119 -118
  85. package/src/components/msa/renderMSAMouseover.ts +2 -3
  86. package/src/components/msa/visibleColRange.ts +17 -0
  87. package/src/components/renderCtx.ts +29 -0
  88. package/src/components/tracks/renderTracksSvg.ts +18 -9
  89. package/src/components/tree/TreeCanvas.tsx +3 -1
  90. package/src/components/tree/TreeCanvasBlock.tsx +2 -12
  91. package/src/components/tree/renderTreeCanvas.ts +25 -28
  92. package/src/components/util.ts +0 -81
  93. package/src/hierarchy.ts +27 -0
  94. package/src/index.ts +7 -0
  95. package/src/model.ts +81 -98
  96. package/src/renderToSvg.tsx +23 -31
  97. package/src/rowCoordinateCalculations.test.ts +0 -51
  98. package/src/rowCoordinateCalculations.ts +0 -23
  99. package/src/util.ts +25 -1
  100. package/src/version.ts +1 -1
@@ -33,15 +33,6 @@ export declare function visibleColToGlobalCol(blanks: number[], visibleCol: numb
33
33
  * @returns The visible column index, or undefined if the column is hidden
34
34
  */
35
35
  export declare function globalColToVisibleCol(blanks: number[], globalCol: number): number | undefined;
36
- /**
37
- * Convert a global column index to a row-specific sequence position.
38
- * This counts non-gap characters up to the given global column.
39
- *
40
- * @param seq - The row's sequence string (including gaps)
41
- * @param globalCol - The global column index
42
- * @returns The sequence position (count of non-gap characters before this column)
43
- */
44
- export declare function globalColToSeqPos(seq: string, globalCol: number): number;
45
36
  /**
46
37
  * Convert a visible column to a row-specific sequence position.
47
38
  * Returns undefined if the position is a gap in the sequence.
@@ -75,26 +75,6 @@ export function globalColToVisibleCol(blanks, globalCol) {
75
75
  const blanksBefore = left;
76
76
  return globalCol - blanksBefore;
77
77
  }
78
- /**
79
- * Convert a global column index to a row-specific sequence position.
80
- * This counts non-gap characters up to the given global column.
81
- *
82
- * @param seq - The row's sequence string (including gaps)
83
- * @param globalCol - The global column index
84
- * @returns The sequence position (count of non-gap characters before this column)
85
- */
86
- export function globalColToSeqPos(seq, globalCol) {
87
- let seqPos = 0;
88
- let currentCol = 0;
89
- const seqLen = seq.length;
90
- while (currentCol < globalCol && currentCol < seqLen) {
91
- if (!isBlank(seq[currentCol])) {
92
- seqPos++;
93
- }
94
- currentCol++;
95
- }
96
- return seqPos;
97
- }
98
78
  /**
99
79
  * Convert a visible column to a row-specific sequence position.
100
80
  * Returns undefined if the position is a gap in the sequence.
@@ -1 +1 @@
1
- {"version":3,"file":"rowCoordinateCalculations.js","sourceRoot":"","sources":["../src/rowCoordinateCalculations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rowCoordinateCalculations.js","sourceRoot":"","sources":["../src/rowCoordinateCalculations.ts"],"names":[],"mappings":"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"}
package/dist/util.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Theme } from '@mui/material';
2
+ export declare function adjustColorForContrast(color: string, bg: string): string;
2
3
  export declare function transform<T>(obj: Record<string, T>, cb: (arg0: [string, T]) => [string, T]): {
3
4
  [k: string]: T;
4
5
  };
package/dist/util.js CHANGED
@@ -1,6 +1,28 @@
1
1
  import { colord, extend } from 'colord';
2
+ import a11yPlugin from 'colord/plugins/a11y';
2
3
  import namesPlugin from 'colord/plugins/names';
3
- extend([namesPlugin]);
4
+ extend([namesPlugin, a11yPlugin]);
5
+ const contrastAdjustCache = new Map();
6
+ // Adjusts a background-tile color until it meets WCAG AA (4.5:1) contrast
7
+ // against `bg`, so it can be used as letter text in letter-color mode.
8
+ // Darkens on light backgrounds, lightens on dark backgrounds.
9
+ export function adjustColorForContrast(color, bg) {
10
+ const cacheKey = `${color}|${bg}`;
11
+ const cached = contrastAdjustCache.get(cacheKey);
12
+ if (cached !== undefined) {
13
+ return cached;
14
+ }
15
+ let c = colord(color);
16
+ if (!c.isReadable(bg)) {
17
+ const bgIsDark = colord(bg).luminance() < 0.5;
18
+ for (let i = 0; i < 40 && !c.isReadable(bg); i++) {
19
+ c = bgIsDark ? c.lighten(0.05) : c.darken(0.05);
20
+ }
21
+ }
22
+ const result = c.toHex();
23
+ contrastAdjustCache.set(cacheKey, result);
24
+ return result;
25
+ }
4
26
  export function transform(obj, cb) {
5
27
  return Object.fromEntries(Object.entries(obj).map(cb));
6
28
  }
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,WAAW,MAAM,sBAAsB,CAAA;AAI9C,MAAM,CAAC,CAAC,WAAW,CAAC,CAAC,CAAA;AAErB,MAAM,UAAU,SAAS,CACvB,GAAsB,EACtB,EAAsC;IAEtC,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,WAAmC,EACnC,KAAY;IAEZ,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM;QACN,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;KACrD,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAgB,EAAE,GAAW;IACtD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,MAAM,MAAM,GAAG,EAAE,CAAA;IACjB,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;QAC9B,IAAI,QAAQ,GAAG,OAAO,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC3C,CAAC;QACD,OAAO,GAAG,QAAQ,GAAG,CAAC,CAAA;IACxB,CAAC;IACD,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA;IACjC,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,CAAiC;IACnD,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAA;AACxB,CAAC;AAED,8CAA8C;AAC9C,oCAAoC;AACpC,MAAM,UAAU,OAAO,CAAC,CAAU;IAChC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAA;AAC/B,CAAC"}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.1.0";
1
+ export declare const version = "5.2.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '5.1.0';
1
+ export const version = '5.2.0';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-msaview",
3
3
  "author": "Colin",
4
- "version": "5.1.0",
4
+ "version": "5.2.0",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
@@ -38,12 +38,12 @@
38
38
  "@types/node": "^25.9.1"
39
39
  },
40
40
  "dependencies": {
41
- "@mui/icons-material": "^9.0.1",
42
- "@mui/material": "^9.0.1",
41
+ "@mui/icons-material": "^7.3.11",
42
+ "@mui/material": "^7.3.11",
43
43
  "colord": "^2.9.3",
44
44
  "flatbush": "^4.5.1",
45
- "@jbrowse/svgcanvas": "5.1.0",
46
- "msa-parsers": "5.1.0"
45
+ "msa-parsers": "5.2.0",
46
+ "@jbrowse/svgcanvas": "5.2.0"
47
47
  },
48
48
  "scripts": {
49
49
  "clean": "rimraf dist bundle",
@@ -1,58 +1,25 @@
1
- export function blocksY({
2
- mapHeight,
1
+ // Returns the block offsets (in pixels) needed to cover the viewport along one
2
+ // axis. mapSize/viewportPos/viewportSize are all in pixels. The clamp pins the
3
+ // block set to the content edge so an over-scroll past the end still fills the
4
+ // viewport rather than leaving a gap.
5
+ export function calculateBlocks({
6
+ mapSize,
3
7
  blockSize,
4
- viewportY,
5
- viewportHeight,
8
+ viewportPos,
9
+ viewportSize,
6
10
  }: {
7
- mapHeight: number
11
+ mapSize: number
8
12
  blockSize: number
9
- viewportY: number
10
- viewportHeight: number
13
+ viewportPos: number
14
+ viewportSize: number
11
15
  }) {
12
- const clampedViewportY = Math.max(
13
- 0,
14
- Math.min(viewportY, mapHeight * blockSize - viewportHeight),
15
- )
16
+ const clamped = Math.max(0, Math.min(viewportPos, mapSize - viewportSize))
17
+ const minTile = Math.floor(clamped / blockSize)
18
+ const maxTile = Math.floor((clamped + viewportSize - 1) / blockSize)
16
19
 
17
- // Calculate visible tile ranges
18
- const minTileY = Math.floor(clampedViewportY / blockSize)
19
- const maxTileY = Math.floor(
20
- (clampedViewportY + viewportHeight - 1) / blockSize,
21
- )
22
-
23
- // Generate list of visible tiles
24
- const blocksY = []
25
- for (let tileY = minTileY; tileY <= maxTileY; tileY++) {
26
- blocksY.push(tileY * blockSize)
20
+ const blocks = []
21
+ for (let tile = minTile; tile <= maxTile; tile++) {
22
+ blocks.push(tile * blockSize)
27
23
  }
28
-
29
- return blocksY
30
- }
31
-
32
- export function blocksX({
33
- mapWidth,
34
- blockSize,
35
- viewportX,
36
- viewportWidth,
37
- }: {
38
- mapWidth: number
39
- blockSize: number
40
- viewportX: number
41
- viewportWidth: number
42
- }) {
43
- const clampedViewportX = Math.max(
44
- 0,
45
- Math.min(viewportX, mapWidth - viewportWidth),
46
- )
47
-
48
- const minTileX = Math.floor(clampedViewportX / blockSize)
49
- const maxTileX = Math.floor(
50
- (clampedViewportX + viewportWidth - 1) / blockSize,
51
- )
52
- const blocksX = []
53
- for (let tileX = minTileX; tileX <= maxTileX; tileX++) {
54
- blocksX.push(tileX * blockSize)
55
- }
56
-
57
- return blocksX
24
+ return blocks
58
25
  }
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useRef } from 'react'
2
2
 
3
3
  import { ResizeHandle } from '@jbrowse/core/ui'
4
+ import { autorun } from 'mobx'
4
5
  import { observer } from 'mobx-react'
5
6
 
6
7
  import { drawConservationBars } from './tracks/renderTracksSvg.ts'
@@ -17,42 +18,32 @@ const ConservationBlock = observer(function ({
17
18
  offsetX: number
18
19
  trackHeight: number
19
20
  }) {
20
- const { blockSize, scrollX, colWidth, highResScaleFactor, conservation } =
21
- model
21
+ const { blockSize, scrollX, highResScaleFactor } = model
22
22
 
23
23
  const ref = useRef<HTMLCanvasElement>(null)
24
24
 
25
25
  useEffect(() => {
26
- if (!ref.current) {
27
- return
28
- }
29
-
30
- const ctx = ref.current.getContext('2d')
26
+ const ctx = ref.current?.getContext('2d')
31
27
  if (!ctx) {
32
28
  return
33
29
  }
30
+ return autorun(() => {
31
+ const { blockSize, colWidth, highResScaleFactor, conservation } = model
32
+ ctx.resetTransform()
33
+ ctx.scale(highResScaleFactor, highResScaleFactor)
34
+ ctx.clearRect(0, 0, blockSize, trackHeight)
35
+ ctx.translate(-offsetX, 0)
34
36
 
35
- ctx.resetTransform()
36
- ctx.scale(highResScaleFactor, highResScaleFactor)
37
- ctx.clearRect(0, 0, blockSize, trackHeight)
38
- ctx.translate(-offsetX, 0)
39
-
40
- drawConservationBars({
41
- ctx,
42
- conservation,
43
- colWidth,
44
- trackHeight,
45
- offsetX,
46
- blockSize,
37
+ drawConservationBars({
38
+ ctx,
39
+ conservation,
40
+ colWidth,
41
+ trackHeight,
42
+ offsetX,
43
+ blockSize,
44
+ })
47
45
  })
48
- }, [
49
- blockSize,
50
- colWidth,
51
- trackHeight,
52
- offsetX,
53
- highResScaleFactor,
54
- conservation,
55
- ])
46
+ }, [model, offsetX, trackHeight])
56
47
 
57
48
  return (
58
49
  <canvas
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useRef } from 'react'
2
2
 
3
+ import { autorun } from 'mobx'
3
4
  import { observer } from 'mobx-react'
4
5
 
5
6
  import { useColorContrast } from '../useColorContrast.ts'
@@ -20,10 +21,7 @@ const AnnotationBlock = observer(function ({
20
21
  const {
21
22
  blockSize,
22
23
  scrollX,
23
- bgColor,
24
24
  colorScheme: modelColorScheme,
25
- colWidth,
26
- fontSize,
27
25
  rowHeight,
28
26
  highResScaleFactor,
29
27
  } = model
@@ -31,49 +29,43 @@ const AnnotationBlock = observer(function ({
31
29
  model: { customColorScheme, data },
32
30
  } = track
33
31
 
34
- const colorScheme = customColorScheme || modelColorScheme
32
+ const colorScheme = customColorScheme ?? modelColorScheme
35
33
  const ref = useRef<HTMLCanvasElement>(null)
36
34
  const { contrastScheme } = useColorContrast(colorScheme)
37
35
  useEffect(() => {
38
- if (!ref.current) {
39
- return
40
- }
41
-
42
- const ctx = ref.current.getContext('2d')
36
+ const ctx = ref.current?.getContext('2d')
43
37
  if (!ctx) {
44
38
  return
45
39
  }
40
+ return autorun(() => {
41
+ const {
42
+ blockSize,
43
+ bgColor,
44
+ colWidth,
45
+ fontSize,
46
+ rowHeight,
47
+ highResScaleFactor,
48
+ } = model
49
+ ctx.resetTransform()
50
+ ctx.scale(highResScaleFactor, highResScaleFactor)
51
+ ctx.clearRect(0, 0, blockSize, rowHeight)
52
+ ctx.translate(-offsetX, 0)
53
+ ctx.textAlign = 'center'
54
+ ctx.font = ctx.font.replace(/\d+px/, `${fontSize}px`)
46
55
 
47
- ctx.resetTransform()
48
- ctx.scale(highResScaleFactor, highResScaleFactor)
49
- ctx.clearRect(0, 0, blockSize, rowHeight)
50
- ctx.translate(-offsetX, 0)
51
- ctx.textAlign = 'center'
52
- ctx.font = ctx.font.replace(/\d+px/, `${fontSize}px`)
53
-
54
- drawTextTrackContent({
55
- ctx,
56
- data,
57
- colorScheme,
58
- contrastScheme,
59
- bgColor,
60
- colWidth,
61
- rowHeight,
62
- offsetX,
63
- blockSize,
56
+ drawTextTrackContent({
57
+ ctx,
58
+ data,
59
+ colorScheme,
60
+ contrastScheme,
61
+ bgColor,
62
+ colWidth,
63
+ rowHeight,
64
+ offsetX,
65
+ blockSize,
66
+ })
64
67
  })
65
- }, [
66
- fontSize,
67
- bgColor,
68
- blockSize,
69
- colWidth,
70
- rowHeight,
71
- offsetX,
72
- contrastScheme,
73
- colorScheme,
74
- highResScaleFactor,
75
- data,
76
- ])
68
+ }, [model, offsetX, contrastScheme, colorScheme, data])
77
69
  return (
78
70
  <canvas
79
71
  ref={ref}
@@ -28,13 +28,21 @@ export default function ExportSVGDialog({
28
28
  }) {
29
29
  const [includeMinimap, setIncludeMinimap] = useState(true)
30
30
  const [includeTracks, setIncludeTracks] = useState(true)
31
- const [exportType, setExportType] = useState('viewport')
31
+ const [exportType, setExportType] = useState<'entire' | 'viewport'>(
32
+ 'viewport',
33
+ )
32
34
  const [error, setError] = useState<unknown>()
33
35
  const theme = useTheme()
34
- const { totalWidth, totalHeight, treeAreaWidth, turnedOnTracks } = model
36
+ const {
37
+ totalWidth,
38
+ totalHeight,
39
+ treeAreaWidth,
40
+ turnedOnTracks,
41
+ totalTrackAreaHeight,
42
+ } = model
35
43
  const hasTracks = turnedOnTracks.length > 0
36
44
  const entireWidth = totalWidth + treeAreaWidth
37
- const entireHeight = totalHeight
45
+ const entireHeight = totalHeight + (includeTracks ? totalTrackAreaHeight : 0)
38
46
  const isLargeExport =
39
47
  exportType === 'entire' && (entireWidth > 10000 || entireHeight > 10000)
40
48
  return (
@@ -71,7 +79,10 @@ export default function ExportSVGDialog({
71
79
  <RadioGroup
72
80
  value={exportType}
73
81
  onChange={event => {
74
- setExportType(event.target.value)
82
+ const { value } = event.target
83
+ if (value === 'entire' || value === 'viewport') {
84
+ setExportType(value)
85
+ }
75
86
  }}
76
87
  >
77
88
  <FormControlLabel
@@ -99,22 +110,21 @@ export default function ExportSVGDialog({
99
110
  variant="contained"
100
111
  color="primary"
101
112
  onClick={() => {
102
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
103
- ;(async () => {
104
- try {
105
- await model.exportSVG({
106
- theme,
107
- includeMinimap:
108
- exportType === 'entire' ? false : includeMinimap,
109
- includeTracks: hasTracks && includeTracks,
110
- exportType,
111
- })
113
+ void model
114
+ .exportSVG({
115
+ theme,
116
+ includeMinimap:
117
+ exportType === 'entire' ? false : includeMinimap,
118
+ includeTracks: hasTracks && includeTracks,
119
+ exportType,
120
+ })
121
+ .then(() => {
112
122
  onClose()
113
- } catch (e) {
123
+ })
124
+ .catch((e: unknown) => {
114
125
  console.error(e)
115
126
  setError(e)
116
- }
117
- })()
127
+ })
118
128
  }}
119
129
  >
120
130
  Submit
@@ -258,7 +258,12 @@ const InterProScanDialog = observer(function ({
258
258
  onProgress: arg => {
259
259
  model.setStatus(
260
260
  arg
261
- ? { ...arg, onCancel: () => { controller.abort() } }
261
+ ? {
262
+ ...arg,
263
+ onCancel: () => {
264
+ controller.abort()
265
+ },
266
+ }
262
267
  : undefined,
263
268
  )
264
269
  },
@@ -3,6 +3,7 @@ import React, { lazy } from 'react'
3
3
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
4
4
  import Assignment from '@mui/icons-material/Assignment'
5
5
  import FolderOpen from '@mui/icons-material/FolderOpen'
6
+ import Menu from '@mui/icons-material/Menu'
6
7
  import PhotoCamera from '@mui/icons-material/PhotoCamera'
7
8
  import Settings from '@mui/icons-material/Settings'
8
9
  import { observer } from 'mobx-react'
@@ -76,7 +77,7 @@ const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
76
77
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
77
78
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
78
79
  >
79
- <FolderOpen />
80
+ <Menu />
80
81
  </CascadingMenuButton>
81
82
  )
82
83
  })
@@ -23,7 +23,7 @@ const AboutDialog = lazy(() => import('../dialogs/AboutDialog.tsx'))
23
23
  const Header = observer(function ({ model }: { model: MsaViewModel }) {
24
24
  const [ref, { height }] = useMeasure()
25
25
  useEffect(() => {
26
- model.setHeaderHeight(height || 0)
26
+ model.setHeaderHeight(height ?? 0)
27
27
  }, [model, height])
28
28
  return (
29
29
  <div ref={ref} style={{ display: 'flex' }}>
@@ -8,19 +8,25 @@ export function msaSettingsMenuItems(model: MsaViewModel): MenuItem[] {
8
8
  label: 'Draw letters',
9
9
  type: 'checkbox',
10
10
  checked: drawMsaLetters,
11
- onClick: () => { model.setDrawMsaLetters(!drawMsaLetters) },
11
+ onClick: () => {
12
+ model.setDrawMsaLetters(!drawMsaLetters)
13
+ },
12
14
  },
13
15
  {
14
16
  label: 'Color letters instead of background of tiles',
15
17
  type: 'checkbox',
16
18
  checked: !bgColor,
17
- onClick: () => { model.setBgColor(!bgColor) },
19
+ onClick: () => {
20
+ model.setBgColor(!bgColor)
21
+ },
18
22
  },
19
23
  {
20
24
  label: 'Enable hiding gappy columns?',
21
25
  type: 'checkbox',
22
26
  checked: hideGaps,
23
- onClick: () => { model.setHideGaps(!hideGaps) },
27
+ onClick: () => {
28
+ model.setHideGaps(!hideGaps)
29
+ },
24
30
  },
25
31
  ]
26
32
  }
@@ -38,31 +44,41 @@ export function treeSettingsMenuItems(model: MsaViewModel): MenuItem[] {
38
44
  label: 'Show branch length',
39
45
  type: 'checkbox',
40
46
  checked: showBranchLen,
41
- onClick: () => { model.setShowBranchLen(!showBranchLen) },
47
+ onClick: () => {
48
+ model.setShowBranchLen(!showBranchLen)
49
+ },
42
50
  },
43
51
  {
44
52
  label: 'Show tree',
45
53
  type: 'checkbox',
46
54
  checked: drawTree,
47
- onClick: () => { model.setDrawTree(!drawTree) },
55
+ onClick: () => {
56
+ model.setDrawTree(!drawTree)
57
+ },
48
58
  },
49
59
  {
50
60
  label: 'Draw clickable bubbles on tree branches',
51
61
  type: 'checkbox',
52
62
  checked: drawNodeBubbles,
53
- onClick: () => { model.setDrawNodeBubbles(!drawNodeBubbles) },
63
+ onClick: () => {
64
+ model.setDrawNodeBubbles(!drawNodeBubbles)
65
+ },
54
66
  },
55
67
  {
56
68
  label: 'Tree labels align right',
57
69
  type: 'checkbox',
58
70
  checked: labelsAlignRight,
59
- onClick: () => { model.setLabelsAlignRight(!labelsAlignRight) },
71
+ onClick: () => {
72
+ model.setLabelsAlignRight(!labelsAlignRight)
73
+ },
60
74
  },
61
75
  {
62
76
  label: 'Draw labels',
63
77
  type: 'checkbox',
64
78
  checked: drawLabels,
65
- onClick: () => { model.setDrawLabels(!drawLabels) },
79
+ onClick: () => {
80
+ model.setDrawLabels(!drawLabels)
81
+ },
66
82
  },
67
83
  ]
68
84
  }
@@ -69,12 +69,7 @@ const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
69
69
  <svg height={polygonHeight} style={{ width: '100%' }}>
70
70
  <polygon
71
71
  fill={fill}
72
- points={[
73
- [s + w, 0],
74
- [s, 0],
75
- [0, polygonHeight],
76
- [msaAreaWidth, polygonHeight],
77
- ].toString()}
72
+ points={`${s + w},0 ${s},0 0,${polygonHeight} ${msaAreaWidth},${polygonHeight}`}
78
73
  />
79
74
  </svg>
80
75
  </div>
@@ -14,13 +14,14 @@ const MinimapSVG = observer(({ model }: { model: MsaViewModel }) => {
14
14
  } = model
15
15
 
16
16
  const BAR_HEIGHT = 12
17
- const H2 = H - 12
17
+ const H2 = H - BAR_HEIGHT
18
18
 
19
19
  const unit = W / numColumns / colWidth
20
20
  const left = -scrollX
21
21
  const right = left + W
22
22
  const s = left * unit
23
23
  const e = right * unit
24
+ const w = Math.max(e - s, 20)
24
25
  const fillColor = 'rgb(66, 119, 127)'
25
26
  const fillOpacity = 0.3
26
27
 
@@ -37,7 +38,7 @@ const MinimapSVG = observer(({ model }: { model: MsaViewModel }) => {
37
38
  <rect
38
39
  x={Math.max(0, s)}
39
40
  y={0}
40
- width={e - s}
41
+ width={w}
41
42
  height={BAR_HEIGHT}
42
43
  fill={fillColor}
43
44
  fillOpacity={fillOpacity}
@@ -47,12 +48,7 @@ const MinimapSVG = observer(({ model }: { model: MsaViewModel }) => {
47
48
  <polygon
48
49
  fill={fillColor}
49
50
  fillOpacity={fillOpacity}
50
- points={[
51
- [e, 0],
52
- [s, 0],
53
- [0, H2],
54
- [W, H2],
55
- ].toString()}
51
+ points={`${s + w},0 ${s},0 0,${H2} ${W},${H2}`}
56
52
  />
57
53
  </g>
58
54
  </>
@@ -0,0 +1,21 @@
1
+ import type { MsaViewModel } from '../../model.ts'
2
+
3
+ // The slice of leaves whose rows intersect a block of height blockSizeY at
4
+ // vertical offset offsetY, padded by one row on each side.
5
+ export function getVisibleLeaves({
6
+ model,
7
+ offsetY,
8
+ blockSizeY,
9
+ }: {
10
+ model: MsaViewModel
11
+ offsetY: number
12
+ blockSizeY: number
13
+ }) {
14
+ const { leaves, rowHeight } = model
15
+ const yStart = Math.max(0, Math.floor((offsetY - rowHeight) / rowHeight))
16
+ const yEnd = Math.max(
17
+ 0,
18
+ Math.ceil((offsetY + blockSizeY + rowHeight) / rowHeight),
19
+ )
20
+ return leaves.slice(yStart, yEnd)
21
+ }