react-msaview 8.0.0 → 8.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 (94) hide show
  1. package/bundle/index.js +99 -99
  2. package/bundle/index.js.map +3 -3
  3. package/dist/calculateBlocks.js +5 -3
  4. package/dist/calculateBlocks.js.map +1 -1
  5. package/dist/components/Loading.js +2 -8
  6. package/dist/components/Loading.js.map +1 -1
  7. package/dist/components/SequenceTextArea.js +0 -1
  8. package/dist/components/SequenceTextArea.js.map +1 -1
  9. package/dist/components/Track.js +2 -2
  10. package/dist/components/Track.js.map +1 -1
  11. package/dist/components/dialogs/AboutDialog.js +6 -6
  12. package/dist/components/dialogs/AboutDialog.js.map +1 -1
  13. package/dist/components/dialogs/AnnotationFileDialog.js +5 -2
  14. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
  15. package/dist/components/dialogs/ExportSVGDialog.js +1 -1
  16. package/dist/components/dialogs/FeatureDialog.js +8 -3
  17. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  18. package/dist/components/header/Header.js +1 -1
  19. package/dist/components/header/Header.js.map +1 -1
  20. package/dist/components/header/LoadWarnings.js +3 -19
  21. package/dist/components/header/LoadWarnings.js.map +1 -1
  22. package/dist/components/header/settingsMenuItems.js +3 -3
  23. package/dist/components/header/settingsMenuItems.js.map +1 -1
  24. package/dist/components/import/ImportForm.js +1 -1
  25. package/dist/components/import/ImportForm.js.map +1 -1
  26. package/dist/components/msa/MSACanvas.js +1 -3
  27. package/dist/components/msa/MSACanvas.js.map +1 -1
  28. package/dist/components/msa/drawFeatureSpans.d.ts +7 -1
  29. package/dist/components/msa/drawFeatureSpans.js +58 -23
  30. package/dist/components/msa/drawFeatureSpans.js.map +1 -1
  31. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +7 -8
  32. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  33. package/dist/components/msa/renderMSABlock.js +8 -9
  34. package/dist/components/msa/renderMSABlock.js.map +1 -1
  35. package/dist/components/tracks/drawTracks.js +8 -5
  36. package/dist/components/tracks/drawTracks.js.map +1 -1
  37. package/dist/components/tree/TreeBranchMenu.js +1 -1
  38. package/dist/components/tree/TreeBranchMenu.js.map +1 -1
  39. package/dist/components/tree/TreeCanvas.js +1 -3
  40. package/dist/components/tree/TreeCanvas.js.map +1 -1
  41. package/dist/components/tree/TreeCanvasBlock.js +2 -2
  42. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  43. package/dist/components/tree/TreeNodeMenu.js +1 -1
  44. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  45. package/dist/components/tree/renderTreeCanvas.js +2 -1
  46. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  47. package/dist/constants.d.ts +1 -0
  48. package/dist/constants.js +4 -0
  49. package/dist/constants.js.map +1 -1
  50. package/dist/expandSpec.d.ts +46 -0
  51. package/dist/expandSpec.js +127 -0
  52. package/dist/expandSpec.js.map +1 -0
  53. package/dist/index.d.ts +2 -0
  54. package/dist/index.js +2 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/model.d.ts +52 -33
  57. package/dist/model.js +232 -142
  58. package/dist/model.js.map +1 -1
  59. package/dist/svgTestUtil.d.ts +28 -24
  60. package/dist/types.d.ts +2 -0
  61. package/dist/useWheelScroll.js +0 -4
  62. package/dist/useWheelScroll.js.map +1 -1
  63. package/dist/version.d.ts +1 -1
  64. package/dist/version.js +1 -1
  65. package/package.json +3 -3
  66. package/src/calculateBlocks.ts +5 -3
  67. package/src/components/Loading.tsx +2 -30
  68. package/src/components/SequenceTextArea.tsx +0 -1
  69. package/src/components/Track.tsx +2 -1
  70. package/src/components/dialogs/AboutDialog.tsx +35 -6
  71. package/src/components/dialogs/AnnotationFileDialog.tsx +10 -1
  72. package/src/components/dialogs/ExportSVGDialog.tsx +1 -1
  73. package/src/components/dialogs/FeatureDialog.tsx +11 -3
  74. package/src/components/header/Header.tsx +1 -1
  75. package/src/components/header/LoadWarnings.tsx +8 -21
  76. package/src/components/header/settingsMenuItems.ts +3 -3
  77. package/src/components/import/ImportForm.tsx +3 -5
  78. package/src/components/msa/MSACanvas.tsx +0 -3
  79. package/src/components/msa/drawFeatureSpans.ts +80 -25
  80. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +7 -9
  81. package/src/components/msa/renderMSABlock.ts +9 -8
  82. package/src/components/tracks/drawTracks.ts +8 -16
  83. package/src/components/tree/TreeBranchMenu.tsx +1 -2
  84. package/src/components/tree/TreeCanvas.tsx +0 -3
  85. package/src/components/tree/TreeCanvasBlock.tsx +2 -2
  86. package/src/components/tree/TreeNodeMenu.tsx +1 -2
  87. package/src/components/tree/renderTreeCanvas.ts +2 -1
  88. package/src/constants.ts +5 -0
  89. package/src/expandSpec.ts +196 -0
  90. package/src/index.ts +8 -0
  91. package/src/model.ts +289 -162
  92. package/src/types.ts +2 -0
  93. package/src/useWheelScroll.ts +0 -4
  94. package/src/version.ts +1 -1
@@ -116,10 +116,6 @@ export function useWheelScroll({ ref, onScrollX, onScrollY, onZoom, scrollZoom,
116
116
  };
117
117
  }, [mouseDragging, onScrollX, onScrollY]);
118
118
  function onMouseDown(event) {
119
- const target = event.target;
120
- if (target.draggable || target.dataset.resizer) {
121
- return;
122
- }
123
119
  if (event.button === 0) {
124
120
  prevX.current = event.clientX;
125
121
  prevY.current = event.clientY;
@@ -1 +1 @@
1
- {"version":3,"file":"useWheelScroll.js","sourceRoot":"","sources":["../src/useWheelScroll.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useWheelScroll.js","sourceRoot":"","sources":["../src/useWheelScroll.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "8.0.0";
1
+ export declare const version = "8.2.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '8.0.0';
1
+ export const version = '8.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": "8.0.0",
4
+ "version": "8.2.0",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
@@ -50,10 +50,10 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@gmod/newick": "^1.0.3",
53
- "@jbrowse/svgcanvas": "8.0.0",
53
+ "@jbrowse/svgcanvas": "8.2.0",
54
54
  "colord": "^2.10.0",
55
55
  "flatbush": "^4.6.2",
56
- "msa-parsers": "8.0.0"
56
+ "msa-parsers": "8.2.0"
57
57
  },
58
58
  "scripts": {
59
59
  "clean": "rimraf dist bundle",
@@ -1,7 +1,8 @@
1
1
  // Returns the block offsets (in pixels) needed to cover the viewport along one
2
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.
3
+ // block set to the content edge, so an over-scroll past the end still fills
4
+ // the viewport, and content smaller than the viewport mounts only the blocks
5
+ // it covers: none for a tree-only figure with zero columns.
5
6
  export function calculateBlocks({
6
7
  mapSize,
7
8
  blockSize,
@@ -14,8 +15,9 @@ export function calculateBlocks({
14
15
  viewportSize: number
15
16
  }) {
16
17
  const clamped = Math.max(0, Math.min(viewportPos, mapSize - viewportSize))
18
+ const end = Math.min(clamped + viewportSize, mapSize)
17
19
  const minTile = Math.floor(clamped / blockSize)
18
- const maxTile = Math.floor((clamped + viewportSize - 1) / blockSize)
20
+ const maxTile = Math.ceil(end / blockSize) - 1
19
21
 
20
22
  const blocks = []
21
23
  for (let tile = minTile; tile <= maxTile; tile++) {
@@ -2,7 +2,7 @@ import React from 'react'
2
2
 
3
3
  import { ErrorMessage } from '@jbrowse/core/ui'
4
4
  import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary'
5
- import { Button, Typography } from '@mui/material'
5
+ import { Button, CircularProgress, Typography } from '@mui/material'
6
6
  import { observer } from 'mobx-react'
7
7
 
8
8
  import MSAView from './MSAView.tsx'
@@ -16,35 +16,7 @@ const LoadingSpinner = observer(function ({ model }: { model: MsaViewModel }) {
16
16
  <div
17
17
  style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 20 }}
18
18
  >
19
- <svg
20
- width="24"
21
- height="24"
22
- viewBox="0 0 24 24"
23
- xmlns="http://www.w3.org/2000/svg"
24
- >
25
- <style>
26
- {`@keyframes spinner { to { transform: rotate(360deg); } }`}
27
- </style>
28
- <circle
29
- cx="12"
30
- cy="12"
31
- r="10"
32
- stroke="#ccc"
33
- strokeWidth="3"
34
- fill="none"
35
- />
36
- <path
37
- d="M12 2a10 10 0 0 1 10 10"
38
- stroke="#1976d2"
39
- strokeWidth="3"
40
- fill="none"
41
- strokeLinecap="round"
42
- style={{
43
- animation: 'spinner 1s linear infinite',
44
- transformOrigin: 'center',
45
- }}
46
- />
47
- </svg>
19
+ <CircularProgress size={24} />
48
20
  <Typography variant="h6">{status?.msg ?? 'Loading...'}</Typography>
49
21
  {status?.onCancel ? (
50
22
  <Button
@@ -15,7 +15,6 @@ const useStyles = makeStyles()(theme => ({
15
15
  dialogContent: {
16
16
  background: theme.palette.action.selected,
17
17
  margin: 4,
18
- minWidth: '80em',
19
18
  },
20
19
  }))
21
20
 
@@ -78,6 +78,7 @@ const TrackLabel = observer(function TrackLabel({
78
78
  </span>
79
79
  <IconButton
80
80
  className={classes.button}
81
+ aria-label={`${name} track menu`}
81
82
  style={{
82
83
  width: trackLabelHeight,
83
84
  height: trackLabelHeight,
@@ -105,7 +106,7 @@ const TrackLabel = observer(function TrackLabel({
105
106
  closeMenu()
106
107
  }}
107
108
  >
108
- Close
109
+ Hide track
109
110
  </MenuItem>
110
111
  <MenuItem
111
112
  dense
@@ -17,16 +17,33 @@ export default function AboutDialog({ onClose }: { onClose: () => void }) {
17
17
  <DialogContent>
18
18
  <Typography>
19
19
  MSAView {version} (
20
- <Link href="https://github.com/gmod/react-msaview">Github</Link>)
20
+ <Link
21
+ target="_blank"
22
+ rel="noopener"
23
+ href="https://github.com/gmod/react-msaview"
24
+ >
25
+ Github
26
+ </Link>
27
+ )
21
28
  </Typography>
22
29
 
23
30
  <ul>
24
31
  <li>
25
32
  <Typography>
26
33
  We use some color schemes from the{' '}
27
- <Link href="https://github.com/biotite-dev/biotite">biotite</Link>{' '}
34
+ <Link
35
+ target="_blank"
36
+ rel="noopener"
37
+ href="https://github.com/biotite-dev/biotite"
38
+ >
39
+ biotite
40
+ </Link>{' '}
28
41
  project, and their license is reproduced{' '}
29
- <Link href="https://github.com/biotite-dev/biotite/blob/master/LICENSE.rst">
42
+ <Link
43
+ target="_blank"
44
+ rel="noopener"
45
+ href="https://github.com/biotite-dev/biotite/blob/master/LICENSE.rst"
46
+ >
30
47
  here
31
48
  </Link>
32
49
  </Typography>
@@ -34,7 +51,11 @@ export default function AboutDialog({ onClose }: { onClose: () => void }) {
34
51
  <li>
35
52
  <Typography>
36
53
  See this page for some information on jalview colorings{' '}
37
- <Link href="https://www.jalview.org/help/html/colourSchemes/">
54
+ <Link
55
+ target="_blank"
56
+ rel="noopener"
57
+ href="https://www.jalview.org/help/html/colourSchemes/"
58
+ >
38
59
  here
39
60
  </Link>
40
61
  </Typography>
@@ -43,7 +64,11 @@ export default function AboutDialog({ onClose }: { onClose: () => void }) {
43
64
  <Typography>
44
65
  See this page for some info on the clustal, cinema, maeditor, and
45
66
  lesk color schemes{' '}
46
- <Link href="http://www.bioinformatics.nl/~berndb/aacolour.html">
67
+ <Link
68
+ target="_blank"
69
+ rel="noopener"
70
+ href="http://www.bioinformatics.nl/~berndb/aacolour.html"
71
+ >
47
72
  here
48
73
  </Link>
49
74
  </Typography>
@@ -51,7 +76,11 @@ export default function AboutDialog({ onClose }: { onClose: () => void }) {
51
76
  <li>
52
77
  <Typography>
53
78
  See this paper about the flower color scheme{' '}
54
- <Link href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7245768/">
79
+ <Link
80
+ target="_blank"
81
+ rel="noopener"
82
+ href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7245768/"
83
+ >
55
84
  here
56
85
  </Link>
57
86
  </Typography>
@@ -126,7 +126,7 @@ const AnnotationFileDialog = observer(function ({
126
126
  <Button
127
127
  variant="contained"
128
128
  color="primary"
129
- disabled={loading}
129
+ disabled={loading || (choice === 'file' ? !file : !url.trim())}
130
130
  onClick={() => {
131
131
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
132
132
  ;(async () => {
@@ -154,6 +154,15 @@ const AnnotationFileDialog = observer(function ({
154
154
  >
155
155
  Open annotations
156
156
  </Button>
157
+ <Button
158
+ variant="contained"
159
+ color="secondary"
160
+ onClick={() => {
161
+ handleClose()
162
+ }}
163
+ >
164
+ Cancel
165
+ </Button>
157
166
  </DialogActions>
158
167
  </Dialog>
159
168
  )
@@ -167,7 +167,7 @@ export default function ExportSVGDialog({
167
167
  })
168
168
  }}
169
169
  >
170
- {exporting ? 'Exporting…' : 'Submit'}
170
+ {exporting ? 'Exporting…' : 'Export'}
171
171
  </Button>
172
172
  <Button
173
173
  variant="contained"
@@ -47,10 +47,18 @@ const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
47
47
  })
48
48
 
49
49
  const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
50
- const { annotationTypes, annotations, fillPalette } = model
50
+ const { annotationTypes, annotations, featureColors, fillPalette } = model
51
51
  const counts = new Map<string, number>()
52
+ const swatches = new Map<string, string>()
52
53
  for (const annot of annotations) {
53
- counts.set(annot.accession, (counts.get(annot.accession) ?? 0) + 1)
54
+ const { accession } = annot
55
+ counts.set(accession, (counts.get(accession) ?? 0) + 1)
56
+ if (!swatches.has(accession)) {
57
+ swatches.set(
58
+ accession,
59
+ featureColors.get(annot)?.fill ?? fillPalette[accession]!,
60
+ )
61
+ }
54
62
  }
55
63
  return (
56
64
  <>
@@ -86,7 +94,7 @@ const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
86
94
  style={{
87
95
  width: 20,
88
96
  height: 20,
89
- background: fillPalette[accession],
97
+ background: swatches.get(accession),
90
98
  }}
91
99
  />
92
100
  </TableCell>
@@ -34,7 +34,7 @@ const Header = observer(function ({ model }: { model: MsaViewModel }) {
34
34
  [model],
35
35
  )
36
36
  return (
37
- <div ref={ref} style={{ display: 'flex' }}>
37
+ <div ref={ref} style={{ display: 'flex', flexWrap: 'wrap' }}>
38
38
  <FileMenu model={model} />
39
39
  <ColorSchemeMenu model={model} />
40
40
  <TreeSettingsMenu model={model} />
@@ -1,46 +1,33 @@
1
1
  import React from 'react'
2
2
 
3
- import { makeStyles } from '@jbrowse/core/util/tss-react'
4
3
  import Warning from '@mui/icons-material/Warning'
5
- import { Tooltip, Typography } from '@mui/material'
4
+ import { Button, Tooltip } from '@mui/material'
6
5
  import { observer } from 'mobx-react'
7
6
 
8
7
  import type { MsaViewModel } from '../../model.ts'
9
8
 
10
- const useStyles = makeStyles()(theme => ({
11
- warning: {
12
- display: 'flex',
13
- alignItems: 'center',
14
- gap: 4,
15
- margin: 'auto',
16
- marginLeft: 10,
17
- whiteSpace: 'nowrap',
18
- color: theme.palette.warning.main,
19
- cursor: 'pointer',
20
- },
21
- }))
22
-
23
9
  // A failed optional layer, such as a 404 on the annotations or row metadata
24
10
  // that is not JSON, shows here as a dismissible warning. Setting `error` would
25
11
  // clear dataInitialized and hide the loaded alignment.
26
12
  const LoadWarnings = observer(function ({ model }: { model: MsaViewModel }) {
27
13
  const { warnings } = model
28
- const { classes } = useStyles()
29
14
  if (warnings.length === 0) {
30
15
  return null
31
16
  }
32
17
  return (
33
18
  <Tooltip title={`${warnings.join(' ')} (click to dismiss)`}>
34
- <Typography
35
- component="div"
36
- className={classes.warning}
19
+ <Button
20
+ aria-label="Dismiss warnings"
21
+ color="warning"
22
+ size="small"
23
+ startIcon={<Warning fontSize="small" />}
24
+ style={{ margin: 'auto', marginLeft: 10, whiteSpace: 'nowrap' }}
37
25
  onClick={() => {
38
26
  model.clearWarnings()
39
27
  }}
40
28
  >
41
- <Warning fontSize="small" />
42
29
  {warnings.length > 1 ? `${warnings.length} warnings` : 'Warning'}
43
- </Typography>
30
+ </Button>
44
31
  </Tooltip>
45
32
  )
46
33
  })
@@ -12,7 +12,7 @@ function toggle(label: string, checked: boolean, set: (arg: boolean) => void) {
12
12
  }
13
13
  }
14
14
 
15
- // the only place in the UI to turn a closed track back on
15
+ // the only place in the UI to turn a hidden track back on
16
16
  function tracksSubMenu(model: MsaViewModel): MenuItem[] {
17
17
  const shown = new Set(model.turnedOnTracks.map(t => t.model.id))
18
18
  return model.tracks.map(({ model: { id, name } }) =>
@@ -38,10 +38,10 @@ export function msaSettingsMenuItems(model: MsaViewModel): MenuItem[] {
38
38
  model.setDrawMsaLetters(arg)
39
39
  }),
40
40
  // inverted: bgColor colors the tile, and off colors the letter
41
- toggle('Color letters instead of background of tiles', !bgColor, arg => {
41
+ toggle('Color letters, not cells', !bgColor, arg => {
42
42
  model.setBgColor(!arg)
43
43
  }),
44
- toggle('Enable hiding gappy columns?', hideGaps, arg => {
44
+ toggle('Hide gappy columns', hideGaps, arg => {
45
45
  model.setHideGaps(arg)
46
46
  }),
47
47
  ]
@@ -24,11 +24,9 @@ const ImportForm = observer(function ({ model }: { model: MsaViewModel }) {
24
24
  Open an MSA file (FASTA, Stockholm, Clustal, A3M or EMF format) and/or
25
25
  a tree file (Newick format).
26
26
  </Typography>
27
- <Typography color="error">
28
- Note: you can open up just an MSA or just a tree, both are not
29
- required. Some MSA files e.g. stockholm format have an embedded tree
30
- also and this is fine, and opening a separate tree file is not
31
- required.
27
+ <Typography color="text.secondary">
28
+ An MSA alone or a tree alone is enough, and a Stockholm file with an
29
+ embedded tree needs no separate tree file.
32
30
  </Typography>
33
31
  </div>
34
32
 
@@ -54,9 +54,6 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
54
54
  data-testid="msa_canvas"
55
55
  onMouseDown={onMouseDown}
56
56
  onMouseUp={onMouseUp}
57
- onMouseLeave={event => {
58
- event.preventDefault()
59
- }}
60
57
  style={{
61
58
  position: 'relative',
62
59
  height,
@@ -1,3 +1,5 @@
1
+ import { minFeatureLabelHeight } from '../../constants.ts'
2
+
1
3
  import type { HierarchyNode } from '../../hierarchy.ts'
2
4
  import type { Annotation, NodeWithIdsAndLength } from '../../types.ts'
3
5
  import type { RenderCtx } from '../renderCtx.ts'
@@ -25,8 +27,14 @@ export interface SpanLayout {
25
27
  height: (laneCount: number) => number
26
28
  /** the band's top edge, from its row baseline, its lane and its height */
27
29
  top: (y: number, lane: number, height: number) => number
28
- /** how far a strand head reaches past the body */
30
+ /** how far back from the feature's end the head starts */
29
31
  headLength: (height: number) => number
32
+ /**
33
+ * how far the head rises above the band. A bar too thin to taper reads its
34
+ * direction off a head taller than itself, and the bar sits on the row's
35
+ * bottom edge, so the room is above.
36
+ */
37
+ headRise?: (height: number) => number
30
38
  }
31
39
 
32
40
  /** the rows of `bandsByRow` that `leaves` covers, in display order */
@@ -76,9 +84,26 @@ export function drawFeatureSpans<T extends SpanBand>({
76
84
  xMin: number
77
85
  xMax: number
78
86
  }) {
87
+ // one row's spans are drawn before any of its labels. Bands are ordered
88
+ // longest-first so a nested one lands on top, and drawing each label with its
89
+ // own span put the child's box over the parent's text: `S` covered `RBD`, and
90
+ // `RBD` read as `RB`. Reused across rows rather than allocated per row.
91
+ const placed: {
92
+ band: T
93
+ xStart: number
94
+ w: number
95
+ t: number
96
+ head: number
97
+ fill: string
98
+ }[] = []
99
+
79
100
  for (const { y, bands } of rows) {
80
101
  const h = layout.height(bands[0]!.laneCount)
81
102
  const headLen = layout.headLength(h)
103
+ const rise = layout.headRise?.(h) ?? 0
104
+ const labelled = labelOf !== undefined && h >= minFeatureLabelHeight
105
+ placed.length = 0
106
+
82
107
  for (const band of bands) {
83
108
  const [xStart, xEnd] = xOf(band)
84
109
  const w = xEnd - xStart
@@ -90,21 +115,33 @@ export function drawFeatureSpans<T extends SpanBand>({
90
115
  ctx.fillStyle = fill
91
116
  ctx.strokeStyle = stroke
92
117
  const { strand } = band.annotation
118
+ const head = strand === undefined ? 0 : Math.min(headLen, w)
93
119
  if (strand === undefined) {
94
120
  ctx.fillRect(xStart, t, w, h)
95
121
  ctx.strokeRect(xStart, t, w, h)
96
122
  } else {
97
- drawGeneArrow({ ctx, x: xStart, t, w, h, headLen, strand })
123
+ drawGeneArrow({ ctx, x: xStart, t, w, h, head, rise, strand })
98
124
  }
99
- const label = labelOf?.(band)
100
- if (label !== undefined) {
101
- const fontSize = Math.min(h - 2, 11)
102
- ctx.font = `${fontSize}px sans-serif`
103
- if (ctx.measureText(label).width + 2 <= w) {
125
+ if (labelled) {
126
+ placed.push({ band, xStart, w, t, head, fill })
127
+ }
128
+ }
129
+
130
+ if (placed.length > 0) {
131
+ ctx.font = `${Math.min(h - 2, 11)}px sans-serif`
132
+ ctx.textAlign = 'center'
133
+ ctx.textBaseline = 'middle'
134
+ for (const { band, xStart, w, t, head, fill } of placed) {
135
+ // asking for the label here rather than in the pass above keeps the
136
+ // once-per-block segment numbering in drawing order
137
+ const label = labelOf!(band)
138
+ // the head narrows to a point, so only about half of it holds text
139
+ if (
140
+ label !== undefined &&
141
+ ctx.measureText(label).width + 2 <= w - head / 2
142
+ ) {
104
143
  labelDrawn?.(band)
105
144
  ctx.fillStyle = contrastText(fill)
106
- ctx.textAlign = 'center'
107
- ctx.textBaseline = 'middle'
108
145
  ctx.fillText(label, xStart + w / 2, t + h / 2)
109
146
  }
110
147
  }
@@ -112,19 +149,24 @@ export function drawFeatureSpans<T extends SpanBand>({
112
149
  }
113
150
  }
114
151
 
115
- // A gene arrow: a full-width rectangular body spanning the feature's
116
- // start..end columns, plus a triangular head that points *beyond* that end in
117
- // the strand direction (right for +, left for -). Keeping the body aligned to
118
- // the exact start/end columns means + and - strand features read as having the
119
- // same boundaries; the arrow lives outside them purely to show transcription
120
- // direction, preserving the column-by-column homology down the rows.
152
+ /**
153
+ * A gene arrow, drawn the way gggenes and gggenomes draw one: the head is the
154
+ * last `head` pixels *of* the feature, tapering to a point at its end, and a
155
+ * feature shorter than the head is all head. The glyph therefore covers its
156
+ * start..end span and nothing else, so adjacent genes butt together instead of
157
+ * biting triangles out of each other, a neighbour never draws over a label, and
158
+ * a gene 20 columns wide reads as 20 columns wide.
159
+ *
160
+ * `rise` lifts the head above the band, for a bar too thin to taper.
161
+ */
121
162
  function drawGeneArrow({
122
163
  ctx,
123
164
  x,
124
165
  t,
125
166
  w,
126
167
  h,
127
- headLen,
168
+ head,
169
+ rise,
128
170
  strand,
129
171
  }: {
130
172
  ctx: RenderCtx
@@ -132,18 +174,31 @@ function drawGeneArrow({
132
174
  t: number
133
175
  w: number
134
176
  h: number
135
- headLen: number
177
+ head: number
178
+ rise: number
136
179
  strand: number
137
180
  }) {
138
- const dir = strand > 0 ? 1 : -1
139
- const bodyStart = strand > 0 ? x : x + w
140
- const bodyEnd = strand > 0 ? x + w : x
181
+ const tip = strand > 0 ? x + w : x
182
+ const tail = strand > 0 ? x : x + w
183
+ const flange = strand > 0 ? tip - head : tip + head
184
+ const bottom = t + h
185
+ const crown = t - rise
186
+ const point = (crown + bottom) / 2
141
187
  ctx.beginPath()
142
- ctx.moveTo(bodyStart, t)
143
- ctx.lineTo(bodyEnd, t)
144
- ctx.lineTo(bodyEnd + dir * headLen, t + h / 2)
145
- ctx.lineTo(bodyEnd, t + h)
146
- ctx.lineTo(bodyStart, t + h)
188
+ if (head >= w) {
189
+ ctx.moveTo(tail, crown)
190
+ ctx.lineTo(tip, point)
191
+ ctx.lineTo(tail, bottom)
192
+ } else {
193
+ ctx.moveTo(tail, t)
194
+ ctx.lineTo(flange, t)
195
+ if (rise > 0) {
196
+ ctx.lineTo(flange, crown)
197
+ }
198
+ ctx.lineTo(tip, point)
199
+ ctx.lineTo(flange, bottom)
200
+ ctx.lineTo(tail, bottom)
201
+ }
147
202
  ctx.closePath()
148
203
  ctx.fill()
149
204
  ctx.stroke()
@@ -60,16 +60,12 @@ export function renderBoxFeatureCanvasBlock({
60
60
  // out); when letters show, the alternating shades alone mark the boundaries
61
61
  // and a number would collide with the sequence. A featureLabel encoding names
62
62
  // what every span carries instead
63
- const drawSegmentLabels =
64
- !featureLabels && !showMsaLetters && !subFeatureRows && barHeight >= 9
63
+ const drawSegmentLabels = !featureLabels && !showMsaLetters && !subFeatureRows
65
64
  // a segment (exon) number labels its band once per block, on the topmost
66
65
  // visible row carrying that segment, so it reads as a column header for the
67
66
  // whole band. Keyed by accession rather than drawn on row 0, because the rows
68
67
  // carrying the gene model are often not the first ones in the alignment
69
68
  const labelled = drawSegmentLabels ? new Set<string>() : undefined
70
- // gene arrow heads stick out up to a row height past the band, so pad the
71
- // cull window enough that a band just outside the block still draws its head
72
- const cull = rowHeight + colWidth
73
69
 
74
70
  drawFeatureSpans<DomainBand>({
75
71
  ctx,
@@ -88,9 +84,11 @@ export function renderBoxFeatureCanvasBlock({
88
84
  y -
89
85
  rowHeight +
90
86
  (subFeatureRows ? lane * h : domainUnderline ? rowHeight - h : 0),
91
- // the head keeps its full size on an underline bar, since a head as short
92
- // as the bar reads as a nub rather than as a direction
93
87
  headLength: h => (domainUnderline ? rowHeight / 2 : h),
88
+ // a head only as tall as an underline bar tapers over ten pixels of
89
+ // three, which reads as nothing; it takes the lower two fifths of the
90
+ // row instead, leaving the letters above it
91
+ headRise: h => (domainUnderline ? Math.max(0, rowHeight * 0.4 - h) : 0),
94
92
  },
95
93
  xOf: band => [band.startCol * colWidth, band.endCol * colWidth],
96
94
  colors: featureColors,
@@ -101,7 +99,7 @@ export function renderBoxFeatureCanvasBlock({
101
99
  : undefined),
102
100
  labelDrawn: band => labelled?.add(band.annotation.accession),
103
101
  contrastText: contrastTextFn(theme),
104
- xMin: offsetX - cull,
105
- xMax: offsetX + bx + cull,
102
+ xMin: offsetX,
103
+ xMax: offsetX + bx,
106
104
  })
107
105
  }