react-msaview 9.1.0 → 9.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.
- package/bundle/index.js +111 -115
- package/bundle/index.js.map +4 -4
- package/dist/blosum62.d.ts +3 -0
- package/dist/blosum62.js +43 -0
- package/dist/blosum62.js.map +1 -0
- package/dist/colorSchemes.js +1 -0
- package/dist/colorSchemes.js.map +1 -1
- package/dist/components/Loading.d.ts +3 -2
- package/dist/components/Loading.js +2 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.d.ts +3 -2
- package/dist/components/MSAView.js +7 -4
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +64 -12
- package/dist/components/MetadataTable.d.ts +4 -1
- package/dist/components/MetadataTable.js +55 -16
- package/dist/components/MetadataTable.js.map +1 -1
- package/dist/components/Track.js +6 -0
- package/dist/components/Track.js.map +1 -1
- package/dist/components/controls.js +2 -1
- package/dist/components/controls.js.map +1 -1
- package/dist/components/dialogs/AnnotationFileDialog.js +7 -2
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +2 -2
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/{header/MSASettingsMenu.d.ts → dialogs/GapThresholdDialog.d.ts} +3 -2
- package/dist/components/dialogs/GapThresholdDialog.js +15 -0
- package/dist/components/dialogs/GapThresholdDialog.js.map +1 -0
- package/dist/components/{header → dialogs}/GappynessSlider.d.ts +1 -1
- package/dist/components/{header → dialogs}/GappynessSlider.js +3 -10
- package/dist/components/dialogs/GappynessSlider.js.map +1 -0
- package/dist/components/{header → dialogs}/GoToBox.d.ts +4 -2
- package/dist/components/dialogs/GoToBox.js +48 -0
- package/dist/components/dialogs/GoToBox.js.map +1 -0
- package/dist/components/dialogs/GoToDialog.d.ts +6 -0
- package/dist/components/dialogs/GoToDialog.js +10 -0
- package/dist/components/dialogs/GoToDialog.js.map +1 -0
- package/dist/components/dialogs/MetadataDialog.js +11 -3
- package/dist/components/dialogs/MetadataDialog.js.map +1 -1
- package/dist/components/dialogs/goTo.d.ts +5 -0
- package/dist/components/{header → dialogs}/goTo.js +1 -1
- package/dist/components/dialogs/goTo.js.map +1 -0
- package/dist/components/dialogs/metadataLabels.d.ts +4 -0
- package/dist/components/dialogs/metadataLabels.js +52 -0
- package/dist/components/dialogs/metadataLabels.js.map +1 -0
- package/dist/components/dialogs/metadataRecord.d.ts +1 -0
- package/dist/components/dialogs/metadataRecord.js +38 -0
- package/dist/components/dialogs/metadataRecord.js.map +1 -0
- package/dist/components/header/Header.d.ts +6 -2
- package/dist/components/header/Header.js +30 -32
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/{TreeSettingsMenu.d.ts → HiddenColumnsChip.d.ts} +2 -2
- package/dist/components/header/HiddenColumnsChip.js +15 -0
- package/dist/components/header/HiddenColumnsChip.js.map +1 -0
- package/dist/components/header/MenuBar.d.ts +6 -0
- package/dist/components/header/MenuBar.js +33 -0
- package/dist/components/header/MenuBar.js.map +1 -0
- package/dist/components/header/ScrollZoomCheckbox.d.ts +7 -0
- package/dist/components/header/ScrollZoomCheckbox.js +19 -0
- package/dist/components/header/ScrollZoomCheckbox.js.map +1 -0
- package/dist/components/header/ZoomControls.js +10 -30
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/colorSchemeMenuItems.d.ts +4 -0
- package/dist/components/header/colorSchemeMenuItems.js +106 -0
- package/dist/components/header/colorSchemeMenuItems.js.map +1 -0
- package/dist/components/header/getAnnotationsMenu.d.ts +3 -30
- package/dist/components/header/getAnnotationsMenu.js +48 -54
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/header/menuBarItems.d.ts +11 -0
- package/dist/components/header/menuBarItems.js +277 -0
- package/dist/components/header/menuBarItems.js.map +1 -0
- package/dist/components/import/ImportForm.js +12 -6
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/minimap/Minimap.js +7 -3
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +2 -1
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/minimap/minimapLayout.d.ts +12 -0
- package/dist/components/minimap/minimapLayout.js +20 -0
- package/dist/components/minimap/minimapLayout.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +26 -9
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +15 -11
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/WheelZoomHint.d.ts +7 -0
- package/dist/components/msa/WheelZoomHint.js +38 -0
- package/dist/components/msa/WheelZoomHint.js.map +1 -0
- package/dist/components/msa/msaKeys.js +3 -0
- package/dist/components/msa/msaKeys.js.map +1 -1
- package/dist/components/msa/msaRaster.d.ts +4 -5
- package/dist/components/msa/msaRaster.js +5 -5
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +22 -2
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +0 -3
- package/dist/components/msa/renderHighlights.js +3 -3
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +6 -1
- package/dist/components/msa/renderMSABlock.js +44 -17
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/tileColor.js +7 -0
- package/dist/components/msa/tileColor.js.map +1 -1
- package/dist/components/rowpanels/RowPanelBlock.js +2 -2
- package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
- package/dist/components/settings/MenuItemsForm.d.ts +10 -0
- package/dist/components/settings/MenuItemsForm.js +80 -0
- package/dist/components/settings/MenuItemsForm.js.map +1 -0
- package/dist/components/settings/SettingsPanel.d.ts +12 -0
- package/dist/components/settings/SettingsPanel.js +92 -0
- package/dist/components/settings/SettingsPanel.js.map +1 -0
- package/dist/components/{header/FileMenu.d.ts → settings/TrackList.d.ts} +2 -2
- package/dist/components/settings/TrackList.js +58 -0
- package/dist/components/settings/TrackList.js.map +1 -0
- package/dist/components/tracks/TrackBlocks.js +2 -2
- package/dist/components/tracks/TrackBlocks.js.map +1 -1
- package/dist/components/tracks/TrackTooltipContent.js +2 -1
- package/dist/components/tracks/TrackTooltipContent.js.map +1 -1
- package/dist/components/tracks/drawTracks.js +13 -6
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +2 -2
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeMenu.js +4 -6
- package/dist/components/tree/TreeMenu.js.map +1 -1
- package/dist/components/tree/TreeOverview.js +2 -2
- package/dist/components/tree/TreeOverview.js.map +1 -1
- package/dist/constants.d.ts +4 -1
- package/dist/constants.js +7 -1
- package/dist/constants.js.map +1 -1
- package/dist/darkReader.d.ts +7 -0
- package/dist/darkReader.js +42 -0
- package/dist/darkReader.js.map +1 -0
- package/dist/element.js +22 -47
- package/dist/element.js.map +1 -1
- package/dist/featurePanels.d.ts +37 -0
- package/dist/featurePanels.js +187 -0
- package/dist/featurePanels.js.map +1 -0
- package/dist/filehandleLoader.d.ts +41 -0
- package/dist/filehandleLoader.js +99 -0
- package/dist/filehandleLoader.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/model/treeModel.d.ts +9 -0
- package/dist/model/treeModel.js +12 -1
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +148 -32
- package/dist/model.js +291 -569
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +1 -40
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderToSvg.js +1 -13
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rerootTree.d.ts +1 -0
- package/dist/rerootTree.js +7 -6
- package/dist/rerootTree.js.map +1 -1
- package/dist/schemeResidueColor.d.ts +12 -0
- package/dist/schemeResidueColor.js +33 -0
- package/dist/schemeResidueColor.js.map +1 -0
- package/dist/similarityColors.d.ts +11 -0
- package/dist/similarityColors.js +90 -0
- package/dist/similarityColors.js.map +1 -0
- package/dist/svgTestUtil.d.ts +64 -12
- package/dist/treeDisplay.d.ts +37 -0
- package/dist/treeDisplay.js +209 -0
- package/dist/treeDisplay.js.map +1 -0
- package/dist/types.d.ts +7 -3
- package/dist/useWheelScroll.d.ts +3 -1
- package/dist/useWheelScroll.js +36 -17
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +2 -0
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wheelZoom.d.ts +8 -3
- package/dist/wheelZoom.js +44 -32
- package/dist/wheelZoom.js.map +1 -1
- package/dist/zoomSnapshot.d.ts +29 -0
- package/dist/zoomSnapshot.js +47 -0
- package/dist/zoomSnapshot.js.map +1 -0
- package/package.json +11 -11
- package/src/blosum62.ts +45 -0
- package/src/colorSchemes.ts +1 -0
- package/src/components/Loading.tsx +6 -2
- package/src/components/MSAView.tsx +22 -5
- package/src/components/MetadataTable.tsx +83 -26
- package/src/components/Track.tsx +13 -0
- package/src/components/controls.ts +2 -1
- package/src/components/dialogs/AnnotationFileDialog.tsx +12 -1
- package/src/components/dialogs/FeatureDialog.tsx +2 -5
- package/src/components/dialogs/GapThresholdDialog.tsx +36 -0
- package/src/components/{header → dialogs}/GappynessSlider.tsx +3 -10
- package/src/components/{header → dialogs}/GoToBox.tsx +36 -10
- package/src/components/dialogs/GoToDialog.tsx +30 -0
- package/src/components/dialogs/MetadataDialog.tsx +32 -4
- package/src/components/{header → dialogs}/goTo.ts +1 -1
- package/src/components/dialogs/metadataLabels.ts +54 -0
- package/src/components/dialogs/metadataRecord.tsx +61 -0
- package/src/components/header/Header.tsx +75 -42
- package/src/components/header/HiddenColumnsChip.tsx +39 -0
- package/src/components/header/MenuBar.tsx +64 -0
- package/src/components/header/ScrollZoomCheckbox.tsx +46 -0
- package/src/components/header/ZoomControls.tsx +20 -35
- package/src/components/header/colorSchemeMenuItems.ts +121 -0
- package/src/components/header/getAnnotationsMenu.ts +52 -56
- package/src/components/header/menuBarItems.ts +357 -0
- package/src/components/import/ImportForm.tsx +19 -7
- package/src/components/minimap/Minimap.tsx +11 -3
- package/src/components/minimap/MinimapSVG.tsx +17 -1
- package/src/components/minimap/minimapLayout.ts +27 -0
- package/src/components/msa/MSACanvas.tsx +22 -3
- package/src/components/msa/MSACanvasBlock.tsx +18 -11
- package/src/components/msa/MSAMouseoverCanvas.tsx +2 -2
- package/src/components/msa/WheelZoomHint.tsx +56 -0
- package/src/components/msa/msaKeys.ts +3 -0
- package/src/components/msa/msaRaster.ts +6 -8
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +28 -5
- package/src/components/msa/renderHighlights.ts +3 -3
- package/src/components/msa/renderMSABlock.ts +51 -15
- package/src/components/msa/tileColor.ts +7 -0
- package/src/components/rowpanels/RowPanelBlock.tsx +2 -2
- package/src/components/settings/MenuItemsForm.tsx +225 -0
- package/src/components/settings/SettingsPanel.tsx +156 -0
- package/src/components/settings/TrackList.tsx +124 -0
- package/src/components/tracks/TrackBlocks.tsx +2 -2
- package/src/components/tracks/TrackTooltipContent.tsx +2 -1
- package/src/components/tracks/drawTracks.ts +15 -7
- package/src/components/tree/TreeCanvasBlock.tsx +2 -2
- package/src/components/tree/TreeMenu.tsx +4 -6
- package/src/components/tree/TreeOverview.tsx +2 -2
- package/src/constants.ts +9 -1
- package/src/darkReader.ts +57 -0
- package/src/element.ts +34 -57
- package/src/featurePanels.ts +269 -0
- package/src/filehandleLoader.ts +136 -0
- package/src/index.ts +4 -0
- package/src/model/treeModel.ts +13 -0
- package/src/model.ts +370 -748
- package/src/neighborJoining.ts +1 -42
- package/src/renderToSvg.tsx +1 -13
- package/src/rerootTree.ts +7 -6
- package/src/schemeResidueColor.ts +37 -0
- package/src/similarityColors.ts +112 -0
- package/src/treeDisplay.ts +278 -0
- package/src/types.ts +7 -4
- package/src/useWheelScroll.ts +43 -22
- package/src/util.ts +3 -0
- package/src/version.ts +1 -1
- package/src/wheelZoom.ts +55 -33
- package/src/zoomSnapshot.ts +80 -0
- package/dist/components/header/ColorSchemeMenu.d.ts +0 -12
- package/dist/components/header/ColorSchemeMenu.js +0 -37
- package/dist/components/header/ColorSchemeMenu.js.map +0 -1
- package/dist/components/header/FileMenu.js +0 -92
- package/dist/components/header/FileMenu.js.map +0 -1
- package/dist/components/header/GappynessSlider.js.map +0 -1
- package/dist/components/header/GoToBox.js +0 -31
- package/dist/components/header/GoToBox.js.map +0 -1
- package/dist/components/header/MSASettingsMenu.js +0 -11
- package/dist/components/header/MSASettingsMenu.js.map +0 -1
- package/dist/components/header/ScrollZoomIcons.d.ts +0 -6
- package/dist/components/header/ScrollZoomIcons.js +0 -35
- package/dist/components/header/ScrollZoomIcons.js.map +0 -1
- package/dist/components/header/TreeSettingsMenu.js +0 -11
- package/dist/components/header/TreeSettingsMenu.js.map +0 -1
- package/dist/components/header/goTo.d.ts +0 -20
- package/dist/components/header/goTo.js.map +0 -1
- package/dist/components/header/settingsMenuItems.d.ts +0 -5
- package/dist/components/header/settingsMenuItems.js +0 -171
- package/dist/components/header/settingsMenuItems.js.map +0 -1
- package/src/components/header/ColorSchemeMenu.tsx +0 -56
- package/src/components/header/FileMenu.tsx +0 -107
- package/src/components/header/MSASettingsMenu.tsx +0 -26
- package/src/components/header/ScrollZoomIcons.tsx +0 -52
- package/src/components/header/TreeSettingsMenu.tsx +0 -30
- package/src/components/header/settingsMenuItems.ts +0 -216
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useCallback, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Paper, Typography } from '@mui/material'
|
|
4
|
+
|
|
5
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
6
|
+
|
|
7
|
+
const hinted = new WeakSet<MsaViewModel>()
|
|
8
|
+
|
|
9
|
+
const zoomKey =
|
|
10
|
+
typeof navigator !== 'undefined' && navigator.platform.startsWith('Mac')
|
|
11
|
+
? '⌘'
|
|
12
|
+
: 'Ctrl'
|
|
13
|
+
|
|
14
|
+
export function useWheelZoomHint(model: MsaViewModel) {
|
|
15
|
+
const [visible, setVisible] = useState(false)
|
|
16
|
+
const timer = useRef<ReturnType<typeof setTimeout>>(undefined)
|
|
17
|
+
const onWheelScroll = useCallback(
|
|
18
|
+
(event: WheelEvent) => {
|
|
19
|
+
if (
|
|
20
|
+
!model.scrollZoom &&
|
|
21
|
+
Math.abs(event.deltaY) > Math.abs(event.deltaX) &&
|
|
22
|
+
!hinted.has(model)
|
|
23
|
+
) {
|
|
24
|
+
hinted.add(model)
|
|
25
|
+
setVisible(true)
|
|
26
|
+
clearTimeout(timer.current)
|
|
27
|
+
timer.current = setTimeout(() => {
|
|
28
|
+
setVisible(false)
|
|
29
|
+
}, 2500)
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
[model],
|
|
33
|
+
)
|
|
34
|
+
return { visible, onWheelScroll }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function WheelZoomHint() {
|
|
38
|
+
return (
|
|
39
|
+
<Paper
|
|
40
|
+
elevation={3}
|
|
41
|
+
style={{
|
|
42
|
+
position: 'absolute',
|
|
43
|
+
top: 12,
|
|
44
|
+
left: '50%',
|
|
45
|
+
transform: 'translateX(-50%)',
|
|
46
|
+
padding: '4px 12px',
|
|
47
|
+
pointerEvents: 'none',
|
|
48
|
+
zIndex: 2,
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
<Typography variant="body2">
|
|
52
|
+
{zoomKey} + scroll to zoom, or check Zoom on scroll
|
|
53
|
+
</Typography>
|
|
54
|
+
</Paper>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -6,7 +6,7 @@ import type { Theme } from '@mui/material'
|
|
|
6
6
|
// One raster tile holds this many cells on a side. A tile costs ~10ms to build
|
|
7
7
|
// and ~1MB to hold, and is then drawn at any zoom for ~1ms, so the cache only
|
|
8
8
|
// has to be large enough to cover a screen without thrashing.
|
|
9
|
-
|
|
9
|
+
const rasterTileSize = 512
|
|
10
10
|
const maxCachedTiles = 64
|
|
11
11
|
const maxThumbnailColumns = 2000
|
|
12
12
|
const maxColorCacheEntries = 4096
|
|
@@ -286,15 +286,13 @@ function paint(
|
|
|
286
286
|
let supported: boolean | undefined
|
|
287
287
|
|
|
288
288
|
/**
|
|
289
|
-
* Whether the alignment background comes from the raster: true
|
|
290
|
-
*
|
|
291
|
-
*
|
|
289
|
+
* Whether the alignment background comes from the raster: true while the
|
|
290
|
+
* residue scheme fills the cells, with a 2d context that reads back, which
|
|
291
|
+
* jsdom lacks. renderMSABlock paints each cell otherwise.
|
|
292
292
|
*/
|
|
293
|
-
export function rasterApplies(
|
|
294
|
-
model: Pick<MsaViewModel, 'actuallyShowDomains' | 'bgColor'>,
|
|
295
|
-
) {
|
|
293
|
+
export function rasterApplies(model: Pick<MsaViewModel, 'cellFill'>) {
|
|
296
294
|
supported ??= !!makeRasterCanvas(1, 1)
|
|
297
|
-
return
|
|
295
|
+
return model.cellFill === 'residue' && supported
|
|
298
296
|
}
|
|
299
297
|
|
|
300
298
|
interface RasterCache {
|
|
@@ -41,18 +41,41 @@ export function renderBoxFeatureCanvasBlock({
|
|
|
41
41
|
ctx.scale(k, k)
|
|
42
42
|
ctx.translate(-offsetX, rowHeight / 2 - offsetY)
|
|
43
43
|
|
|
44
|
+
const source = domainOverlaySource(
|
|
45
|
+
model,
|
|
46
|
+
getVisibleLeaves({ model, offsetY, blockSizeY: by }),
|
|
47
|
+
)
|
|
48
|
+
const contrastText = contrastTextFn(theme)
|
|
49
|
+
if (model.cellFill === 'residue' && model.domainUnderline) {
|
|
50
|
+
// over residue colors a bar in a categorical color blends into the cells
|
|
51
|
+
// beside it, so a strip of the page background parts it from them
|
|
52
|
+
const { height } = source.layout
|
|
53
|
+
const halo = theme.palette.background.default
|
|
54
|
+
drawFeatureSpans<DomainBand>({
|
|
55
|
+
ctx,
|
|
56
|
+
...source,
|
|
57
|
+
layout: { ...source.layout, height: n => height(n) + barHalo },
|
|
58
|
+
colors: new Map(
|
|
59
|
+
[...source.colors.keys()].map(a => [a, { fill: halo, stroke: halo }]),
|
|
60
|
+
),
|
|
61
|
+
labelOf: undefined,
|
|
62
|
+
labelDrawn: undefined,
|
|
63
|
+
contrastText,
|
|
64
|
+
xMin: offsetX,
|
|
65
|
+
xMax: offsetX + bx,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
44
68
|
drawFeatureSpans<DomainBand>({
|
|
45
69
|
ctx,
|
|
46
|
-
...
|
|
47
|
-
|
|
48
|
-
getVisibleLeaves({ model, offsetY, blockSizeY: by }),
|
|
49
|
-
),
|
|
50
|
-
contrastText: contrastTextFn(theme),
|
|
70
|
+
...source,
|
|
71
|
+
contrastText,
|
|
51
72
|
xMin: offsetX,
|
|
52
73
|
xMax: offsetX + bx,
|
|
53
74
|
})
|
|
54
75
|
}
|
|
55
76
|
|
|
77
|
+
const barHalo = 1.5
|
|
78
|
+
|
|
56
79
|
/**
|
|
57
80
|
* The domain overlay's spans over `leaves`, in alignment pixels: filled rows,
|
|
58
81
|
* an underline bar, or thin sub-rows, as `domainUnderline` and
|
|
@@ -11,11 +11,11 @@ import type { Theme } from '@mui/material'
|
|
|
11
11
|
|
|
12
12
|
// a fill plus a solid border; the hover wash alone is invisible over clustalx
|
|
13
13
|
// coloring
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const highlightFill = 'rgba(255,140,0,0.28)'
|
|
15
|
+
const highlightBorder = 'rgba(210,90,0,0.95)'
|
|
16
16
|
export const highlightRowFill = 'rgba(255,140,0,0.18)'
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const labelFontSize = 11
|
|
19
19
|
const labelPad = 3
|
|
20
20
|
// the label box above a highlight band; tree blocks also check rows this far
|
|
21
21
|
// outside themselves
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { zoomFrameMaxLetters } from '../../constants.ts'
|
|
1
2
|
import { setFontSize } from '../../setFontSize.ts'
|
|
2
3
|
import { adjustColorForContrast, contrastTextFn } from '../../util.ts'
|
|
3
4
|
import { getVisibleLeaves } from '../getVisibleLeaves.ts'
|
|
4
5
|
import { domainBandCursor } from './domainBandCursor.ts'
|
|
6
|
+
import { renderBoxFeatureCanvasBlock } from './renderBoxFeatureCanvasBlock.ts'
|
|
5
7
|
import { tileColorFn } from './tileColor.ts'
|
|
6
8
|
import { visibleColRange } from './visibleColRange.ts'
|
|
7
9
|
|
|
@@ -21,6 +23,7 @@ export function renderMSABlock({
|
|
|
21
23
|
blockSizeXOverride,
|
|
22
24
|
blockSizeYOverride,
|
|
23
25
|
rasterTiles,
|
|
26
|
+
domains,
|
|
24
27
|
}: {
|
|
25
28
|
offsetX: number
|
|
26
29
|
offsetY: number
|
|
@@ -31,6 +34,11 @@ export function renderMSABlock({
|
|
|
31
34
|
blockSizeXOverride?: number
|
|
32
35
|
blockSizeYOverride?: number
|
|
33
36
|
rasterTiles?: boolean
|
|
37
|
+
/**
|
|
38
|
+
* draw the domain overlay too, over the cell colors and under the letters,
|
|
39
|
+
* which is the one order every cell fill reads right in
|
|
40
|
+
*/
|
|
41
|
+
domains?: boolean
|
|
34
42
|
}) {
|
|
35
43
|
const {
|
|
36
44
|
colWidth,
|
|
@@ -38,18 +46,21 @@ export function renderMSABlock({
|
|
|
38
46
|
rowHeight,
|
|
39
47
|
fontSize,
|
|
40
48
|
highResScaleFactor,
|
|
41
|
-
|
|
49
|
+
cellFill,
|
|
42
50
|
bgColor,
|
|
43
51
|
} = model
|
|
44
52
|
const k = highResScaleFactorOverride ?? highResScaleFactor
|
|
45
53
|
const bx = blockSizeXOverride ?? blockSize
|
|
46
54
|
const by = blockSizeYOverride ?? blockSize
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
const setup = () => {
|
|
56
|
+
ctx.resetTransform()
|
|
57
|
+
ctx.scale(k, k)
|
|
58
|
+
ctx.translate(-offsetX, rowHeight / 2 - offsetY)
|
|
59
|
+
ctx.textAlign = 'center'
|
|
60
|
+
ctx.textBaseline = 'alphabetic'
|
|
61
|
+
setFontSize(ctx, fontSize, !bgColor)
|
|
62
|
+
}
|
|
63
|
+
setup()
|
|
53
64
|
|
|
54
65
|
const { xStart, xEnd } = visibleColRange({
|
|
55
66
|
offsetX,
|
|
@@ -62,7 +73,7 @@ export function renderMSABlock({
|
|
|
62
73
|
? columns.get(relativeTo)?.slice(xStart, xEnd)
|
|
63
74
|
: null
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
const cells = {
|
|
66
77
|
model,
|
|
67
78
|
ctx,
|
|
68
79
|
theme,
|
|
@@ -70,13 +81,34 @@ export function renderMSABlock({
|
|
|
70
81
|
xEnd,
|
|
71
82
|
visibleLeaves,
|
|
72
83
|
referenceSeq,
|
|
73
|
-
// when domains
|
|
84
|
+
// when the domains fill the cells the background comes from
|
|
74
85
|
// renderBoxFeatureCanvasBlock, so only the letters are drawn here
|
|
75
|
-
drawTiles:
|
|
86
|
+
drawTiles: cellFill !== 'domain',
|
|
76
87
|
// ...and when the caller has already laid the raster down, the tiles are
|
|
77
88
|
// there but nothing here has to paint them
|
|
78
89
|
rasterTiles,
|
|
79
|
-
|
|
90
|
+
letters:
|
|
91
|
+
model.showMsaLetters &&
|
|
92
|
+
(!model.wheelZooming ||
|
|
93
|
+
(bx * by) / (colWidth * rowHeight) <= zoomFrameMaxLetters),
|
|
94
|
+
}
|
|
95
|
+
if (domains) {
|
|
96
|
+
drawTilesAndText({ ...cells, pass: 'tiles' })
|
|
97
|
+
renderBoxFeatureCanvasBlock({
|
|
98
|
+
model,
|
|
99
|
+
theme,
|
|
100
|
+
ctx,
|
|
101
|
+
offsetX,
|
|
102
|
+
offsetY,
|
|
103
|
+
highResScaleFactorOverride,
|
|
104
|
+
blockSizeXOverride,
|
|
105
|
+
blockSizeYOverride,
|
|
106
|
+
})
|
|
107
|
+
setup()
|
|
108
|
+
drawTilesAndText({ ...cells, pass: 'letters' })
|
|
109
|
+
} else {
|
|
110
|
+
drawTilesAndText({ ...cells, pass: 'both' })
|
|
111
|
+
}
|
|
80
112
|
drawInsertionIndicators({
|
|
81
113
|
model,
|
|
82
114
|
ctx,
|
|
@@ -97,6 +129,8 @@ function drawTilesAndText({
|
|
|
97
129
|
referenceSeq,
|
|
98
130
|
drawTiles,
|
|
99
131
|
rasterTiles,
|
|
132
|
+
letters: lettersOn,
|
|
133
|
+
pass,
|
|
100
134
|
}: {
|
|
101
135
|
model: MsaViewModel
|
|
102
136
|
theme: Theme
|
|
@@ -107,6 +141,8 @@ function drawTilesAndText({
|
|
|
107
141
|
referenceSeq: string | null | undefined
|
|
108
142
|
drawTiles: boolean
|
|
109
143
|
rasterTiles?: boolean
|
|
144
|
+
letters: boolean
|
|
145
|
+
pass: 'tiles' | 'letters' | 'both'
|
|
110
146
|
}) {
|
|
111
147
|
const {
|
|
112
148
|
bgColor,
|
|
@@ -115,7 +151,6 @@ function drawTilesAndText({
|
|
|
115
151
|
rowHeight,
|
|
116
152
|
fontSize,
|
|
117
153
|
relativeTo,
|
|
118
|
-
showMsaLetters,
|
|
119
154
|
subFeatureRows,
|
|
120
155
|
domainUnderline,
|
|
121
156
|
domainBandsByStart,
|
|
@@ -123,8 +158,9 @@ function drawTilesAndText({
|
|
|
123
158
|
} = model
|
|
124
159
|
|
|
125
160
|
const tiles = drawTiles && bgColor
|
|
126
|
-
const paintTiles = tiles && !rasterTiles
|
|
127
|
-
|
|
161
|
+
const paintTiles = tiles && !rasterTiles && pass !== 'letters'
|
|
162
|
+
const letters = lettersOn && pass !== 'tiles'
|
|
163
|
+
if (paintTiles || letters) {
|
|
128
164
|
const tileColor = tileColorFn(model)
|
|
129
165
|
// Domain fills come from a categorical palette with no fixed lightness, and
|
|
130
166
|
// a dynamic scheme colors a cell from its column, so a readable letter color
|
|
@@ -169,7 +205,7 @@ function drawTilesAndText({
|
|
|
169
205
|
ctx.fillRect(x, tileY, colWidth, rowHeight)
|
|
170
206
|
}
|
|
171
207
|
|
|
172
|
-
if (
|
|
208
|
+
if (letters) {
|
|
173
209
|
const covering = bandAt(col)
|
|
174
210
|
ctx.fillStyle = covering
|
|
175
211
|
? // on top of a domain box: contrast against the box fill
|
|
@@ -9,6 +9,10 @@ export function tileColorFn(model: MsaViewModel) {
|
|
|
9
9
|
const { colClustalX } = model
|
|
10
10
|
return (col: number, letter: string) => colClustalX[col]![letter]
|
|
11
11
|
}
|
|
12
|
+
if (dynamicColorSchemeName === 'blosum62_dynamic') {
|
|
13
|
+
const { colSimilarity } = model
|
|
14
|
+
return (col: number, letter: string) => colSimilarity[col]![letter]
|
|
15
|
+
}
|
|
12
16
|
if (dynamicColorSchemeName === 'percent_identity_dynamic') {
|
|
13
17
|
const { colConsensus } = model
|
|
14
18
|
return (col: number, letter: string) => {
|
|
@@ -28,6 +32,9 @@ export function tileColorTable(model: MsaViewModel) {
|
|
|
28
32
|
if (dynamicColorSchemeName === 'clustalx_protein_dynamic') {
|
|
29
33
|
return model.colClustalX
|
|
30
34
|
}
|
|
35
|
+
if (dynamicColorSchemeName === 'blosum62_dynamic') {
|
|
36
|
+
return model.colSimilarity
|
|
37
|
+
}
|
|
31
38
|
if (dynamicColorSchemeName === 'percent_identity_dynamic') {
|
|
32
39
|
return model.colConsensus
|
|
33
40
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
|
|
4
|
-
import { useTheme } from '@mui/material'
|
|
5
4
|
import { observer } from 'mobx-react'
|
|
6
5
|
|
|
6
|
+
import { useCanvasTheme } from '../../darkReader.ts'
|
|
7
7
|
import { featureField, featureName } from '../../featureFields.ts'
|
|
8
8
|
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
9
9
|
import { useHoverAnchor } from '../useHoverAnchor.ts'
|
|
@@ -64,7 +64,7 @@ const RowPanelBlock = observer(function ({
|
|
|
64
64
|
name: string
|
|
65
65
|
lines: string[]
|
|
66
66
|
}>()
|
|
67
|
-
const theme =
|
|
67
|
+
const theme = useCanvasTheme()
|
|
68
68
|
const { blockSize, highResScaleFactor, rowHeight } = model
|
|
69
69
|
const width = panel.width
|
|
70
70
|
const height = blockSize
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import ArrowDropDown from '@mui/icons-material/ArrowDropDown'
|
|
4
|
+
import ArrowRight from '@mui/icons-material/ArrowRight'
|
|
5
|
+
import {
|
|
6
|
+
Box,
|
|
7
|
+
Button,
|
|
8
|
+
Checkbox,
|
|
9
|
+
FormControlLabel,
|
|
10
|
+
MenuItem as MuiMenuItem,
|
|
11
|
+
Radio,
|
|
12
|
+
TextField,
|
|
13
|
+
ToggleButton,
|
|
14
|
+
ToggleButtonGroup,
|
|
15
|
+
Typography,
|
|
16
|
+
} from '@mui/material'
|
|
17
|
+
|
|
18
|
+
import type { MenuItem } from '@jbrowse/core/ui/Menu'
|
|
19
|
+
|
|
20
|
+
type RadioMenuItem = Extract<MenuItem, { type: 'radio' }>
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A submenu of radios may name a compact control to render as: `segmented`
|
|
24
|
+
* for a few short choices, `select` for a longer list
|
|
25
|
+
*/
|
|
26
|
+
export type ChoiceDisplay = 'segmented' | 'select'
|
|
27
|
+
|
|
28
|
+
function radios(items: MenuItem[]) {
|
|
29
|
+
return items.filter(
|
|
30
|
+
(i): i is RadioMenuItem => 'type' in i && i.type === 'radio',
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function Caption({ children }: { children: React.ReactNode }) {
|
|
35
|
+
return (
|
|
36
|
+
<Typography
|
|
37
|
+
variant="caption"
|
|
38
|
+
color="text.secondary"
|
|
39
|
+
sx={{ mt: 1, display: 'block' }}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
</Typography>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function Segmented({
|
|
47
|
+
label,
|
|
48
|
+
options,
|
|
49
|
+
}: {
|
|
50
|
+
label: string
|
|
51
|
+
options: RadioMenuItem[]
|
|
52
|
+
}) {
|
|
53
|
+
const value = options.findIndex(o => o.checked)
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<Caption>{label}</Caption>
|
|
57
|
+
<ToggleButtonGroup
|
|
58
|
+
exclusive
|
|
59
|
+
fullWidth
|
|
60
|
+
size="small"
|
|
61
|
+
value={value}
|
|
62
|
+
aria-label={label}
|
|
63
|
+
onChange={(_event, i: number | null) => {
|
|
64
|
+
if (i !== null) {
|
|
65
|
+
options[i]?.onClick()
|
|
66
|
+
}
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
{options.map((o, i) => (
|
|
70
|
+
<ToggleButton
|
|
71
|
+
key={String(o.label)}
|
|
72
|
+
value={i}
|
|
73
|
+
disabled={o.disabled}
|
|
74
|
+
title={o.helpText}
|
|
75
|
+
sx={{ textTransform: 'none', py: 0.25 }}
|
|
76
|
+
>
|
|
77
|
+
{o.label}
|
|
78
|
+
</ToggleButton>
|
|
79
|
+
))}
|
|
80
|
+
</ToggleButtonGroup>
|
|
81
|
+
</>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// each option fires its own onClick, so picking the checked one again still
|
|
86
|
+
// runs, which reopens a dialog like the custom gap threshold
|
|
87
|
+
function Select({
|
|
88
|
+
label,
|
|
89
|
+
options,
|
|
90
|
+
}: {
|
|
91
|
+
label: string
|
|
92
|
+
options: RadioMenuItem[]
|
|
93
|
+
}) {
|
|
94
|
+
const value = options.findIndex(o => o.checked)
|
|
95
|
+
return (
|
|
96
|
+
<TextField
|
|
97
|
+
select
|
|
98
|
+
size="small"
|
|
99
|
+
fullWidth
|
|
100
|
+
label={label}
|
|
101
|
+
value={value === -1 ? '' : value}
|
|
102
|
+
sx={{ mt: 1.5, '& .MuiInputBase-root': { typography: 'body2' } }}
|
|
103
|
+
>
|
|
104
|
+
{options.map((o, i) => (
|
|
105
|
+
<MuiMenuItem
|
|
106
|
+
key={String(o.label)}
|
|
107
|
+
value={i}
|
|
108
|
+
disabled={o.disabled}
|
|
109
|
+
dense
|
|
110
|
+
onClick={() => {
|
|
111
|
+
o.onClick()
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
{o.label}
|
|
115
|
+
</MuiMenuItem>
|
|
116
|
+
))}
|
|
117
|
+
</TextField>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// a short submenu opens in place under a caption, where every choice shows at
|
|
122
|
+
// once; a long one, like the extra color schemes, folds behind a toggle
|
|
123
|
+
const inlineUpTo = 8
|
|
124
|
+
|
|
125
|
+
function SubSection({ item }: { item: MenuItem }) {
|
|
126
|
+
const [open, setOpen] = useState(false)
|
|
127
|
+
if (!('subMenu' in item) || !Array.isArray(item.subMenu)) {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
const label = String(item.label)
|
|
131
|
+
const display =
|
|
132
|
+
'display' in item ? (item.display as ChoiceDisplay) : undefined
|
|
133
|
+
if (display) {
|
|
134
|
+
const options = radios(item.subMenu)
|
|
135
|
+
const rest = item.subMenu.filter(i => !options.includes(i as RadioMenuItem))
|
|
136
|
+
const Control = display === 'segmented' ? Segmented : Select
|
|
137
|
+
return (
|
|
138
|
+
<>
|
|
139
|
+
<Control label={label} options={options} />
|
|
140
|
+
<MenuItemsForm items={rest} />
|
|
141
|
+
</>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
if (item.subMenu.length <= inlineUpTo) {
|
|
145
|
+
return (
|
|
146
|
+
<>
|
|
147
|
+
<Caption>{label}</Caption>
|
|
148
|
+
<MenuItemsForm items={item.subMenu} />
|
|
149
|
+
</>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
return (
|
|
153
|
+
<>
|
|
154
|
+
<Button
|
|
155
|
+
size="small"
|
|
156
|
+
color="inherit"
|
|
157
|
+
startIcon={open ? <ArrowDropDown /> : <ArrowRight />}
|
|
158
|
+
onClick={() => {
|
|
159
|
+
setOpen(!open)
|
|
160
|
+
}}
|
|
161
|
+
sx={{ textTransform: 'none', justifyContent: 'flex-start', px: 0.5 }}
|
|
162
|
+
>
|
|
163
|
+
{label}
|
|
164
|
+
</Button>
|
|
165
|
+
{open ? (
|
|
166
|
+
<Box sx={{ pl: 2 }}>
|
|
167
|
+
<MenuItemsForm items={item.subMenu} />
|
|
168
|
+
</Box>
|
|
169
|
+
) : null}
|
|
170
|
+
</>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export default function MenuItemsForm({ items }: { items: MenuItem[] }) {
|
|
175
|
+
return (
|
|
176
|
+
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
|
177
|
+
{items.map((item, i) => {
|
|
178
|
+
const key = 'label' in item ? String(item.label) : String(i)
|
|
179
|
+
if (!('label' in item)) {
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
if ('type' in item && item.type === 'subHeader') {
|
|
183
|
+
return <Caption key={key}>{item.label}</Caption>
|
|
184
|
+
}
|
|
185
|
+
if ('subMenu' in item) {
|
|
186
|
+
return <SubSection key={key} item={item} />
|
|
187
|
+
}
|
|
188
|
+
const onClick = 'onClick' in item ? item.onClick : undefined
|
|
189
|
+
if (
|
|
190
|
+
'type' in item &&
|
|
191
|
+
(item.type === 'checkbox' || item.type === 'radio')
|
|
192
|
+
) {
|
|
193
|
+
const Control = item.type === 'checkbox' ? Checkbox : Radio
|
|
194
|
+
return (
|
|
195
|
+
<FormControlLabel
|
|
196
|
+
key={key}
|
|
197
|
+
disabled={item.disabled}
|
|
198
|
+
control={
|
|
199
|
+
<Control
|
|
200
|
+
size="small"
|
|
201
|
+
checked={item.checked}
|
|
202
|
+
onChange={() => onClick?.()}
|
|
203
|
+
sx={{ py: 0.25 }}
|
|
204
|
+
/>
|
|
205
|
+
}
|
|
206
|
+
label={item.label}
|
|
207
|
+
slotProps={{ typography: { variant: 'body2' } }}
|
|
208
|
+
/>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
return (
|
|
212
|
+
<Button
|
|
213
|
+
key={key}
|
|
214
|
+
size="small"
|
|
215
|
+
disabled={item.disabled}
|
|
216
|
+
onClick={() => onClick?.()}
|
|
217
|
+
sx={{ alignSelf: 'flex-start', textTransform: 'none' }}
|
|
218
|
+
>
|
|
219
|
+
{item.label}
|
|
220
|
+
</Button>
|
|
221
|
+
)
|
|
222
|
+
})}
|
|
223
|
+
</Box>
|
|
224
|
+
)
|
|
225
|
+
}
|