react-msaview 7.0.0 → 8.1.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/README.md +11 -10
- package/bundle/index.js +122 -111
- package/bundle/index.js.map +4 -4
- package/dist/calculateBlocks.js +5 -3
- package/dist/calculateBlocks.js.map +1 -1
- package/dist/colorSchemes.js +1 -2
- package/dist/colorSchemes.js.map +1 -1
- package/dist/columnStats.d.ts +28 -0
- package/dist/columnStats.js +34 -0
- package/dist/columnStats.js.map +1 -0
- package/dist/components/DragHandle.d.ts +3 -2
- package/dist/components/DragHandle.js +11 -5
- package/dist/components/DragHandle.js.map +1 -1
- package/dist/components/Loading.js +2 -8
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAView.js +17 -6
- package/dist/components/MSAView.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +64 -7
- package/dist/components/MSAViewer.js +128 -23
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/PortalTooltip.d.ts +11 -0
- package/dist/components/PortalTooltip.js +29 -0
- package/dist/components/PortalTooltip.js.map +1 -0
- package/dist/components/ResizeHandles.js +11 -29
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/SequenceTextArea.js +3 -4
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/Track.js +43 -24
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/AboutDialog.js +6 -6
- package/dist/components/dialogs/AboutDialog.js.map +1 -1
- package/dist/components/dialogs/AnnotationFileDialog.js +5 -2
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +6 -4
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +8 -3
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
- package/dist/components/header/GappynessSlider.js +6 -11
- package/dist/components/header/GappynessSlider.js.map +1 -1
- package/dist/components/header/Header.js +4 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/LoadWarnings.js +6 -23
- package/dist/components/header/LoadWarnings.js.map +1 -1
- package/dist/components/header/ScrollZoomIcons.d.ts +6 -0
- package/dist/components/header/ScrollZoomIcons.js +35 -0
- package/dist/components/header/ScrollZoomIcons.js.map +1 -0
- package/dist/components/header/TreeSettingsMenu.js +1 -3
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/UnshareableDataWarning.js +2 -4
- package/dist/components/header/UnshareableDataWarning.js.map +1 -1
- package/dist/components/header/ZoomControls.js +49 -6
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/getAnnotationsMenu.js +3 -6
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +12 -9
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/import/ImportForm.js +1 -1
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/util.js +1 -2
- package/dist/components/import/util.js.map +1 -1
- package/dist/components/minimap/Minimap.js +1 -2
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/msa/AnnotationLegend.js +27 -19
- package/dist/components/msa/AnnotationLegend.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +2 -4
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +13 -15
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +2 -1
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/domainBandCursor.d.ts +7 -11
- package/dist/components/msa/domainBandCursor.js +7 -11
- package/dist/components/msa/domainBandCursor.js.map +1 -1
- package/dist/components/msa/drawFeatureSpans.d.ts +59 -0
- package/dist/components/msa/drawFeatureSpans.js +113 -0
- package/dist/components/msa/drawFeatureSpans.js.map +1 -0
- package/dist/components/msa/legendRows.d.ts +8 -0
- package/dist/components/msa/legendRows.js +16 -0
- package/dist/components/msa/legendRows.js.map +1 -0
- package/dist/components/msa/msaRaster.d.ts +24 -31
- package/dist/components/msa/msaRaster.js +30 -39
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/components/msa/packDomainLanes.d.ts +20 -0
- package/dist/components/msa/packDomainLanes.js +33 -0
- package/dist/components/msa/packDomainLanes.js.map +1 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +3 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +50 -87
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +15 -13
- package/dist/components/msa/renderHighlights.js +43 -20
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +12 -12
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/tileColor.js +4 -7
- package/dist/components/msa/tileColor.js.map +1 -1
- package/dist/components/msa/useMsaBlockMouse.d.ts +5 -3
- package/dist/components/msa/useMsaBlockMouse.js +6 -6
- package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
- package/dist/components/rowpanels/RowPanelBlock.d.ts +9 -0
- package/dist/components/rowpanels/RowPanelBlock.js +86 -0
- package/dist/components/rowpanels/RowPanelBlock.js.map +1 -0
- package/dist/components/rowpanels/RowPanelHeaders.d.ts +12 -0
- package/dist/components/rowpanels/RowPanelHeaders.js +38 -0
- package/dist/components/rowpanels/RowPanelHeaders.js.map +1 -0
- package/dist/components/{msa/ColumnStats.d.ts → rowpanels/RowPanels.d.ts} +2 -2
- package/dist/components/rowpanels/RowPanels.js +32 -0
- package/dist/components/rowpanels/RowPanels.js.map +1 -0
- package/dist/components/rowpanels/headerLayout.d.ts +1 -0
- package/dist/components/rowpanels/headerLayout.js +5 -0
- package/dist/components/rowpanels/headerLayout.js.map +1 -0
- package/dist/components/rowpanels/renderFeaturePanel.d.ts +21 -0
- package/dist/components/rowpanels/renderFeaturePanel.js +37 -0
- package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -0
- package/dist/components/rowpanels/renderRowPanel.d.ts +23 -0
- package/dist/components/rowpanels/renderRowPanel.js +22 -0
- package/dist/components/rowpanels/renderRowPanel.js.map +1 -0
- package/dist/components/rowpanels/renderStrip.d.ts +20 -0
- package/dist/components/rowpanels/renderStrip.js +26 -0
- package/dist/components/rowpanels/renderStrip.js.map +1 -0
- package/dist/components/tracks/TrackBlocks.js +3 -5
- package/dist/components/tracks/TrackBlocks.js.map +1 -1
- package/dist/components/tracks/TrackTooltipContent.d.ts +15 -0
- package/dist/components/tracks/TrackTooltipContent.js +109 -0
- package/dist/components/tracks/TrackTooltipContent.js.map +1 -0
- package/dist/components/tracks/drawTracks.d.ts +4 -4
- package/dist/components/tracks/drawTracks.js +25 -39
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tracks/useTrackHover.d.ts +22 -0
- package/dist/components/tracks/useTrackHover.js +30 -0
- package/dist/components/tracks/useTrackHover.js.map +1 -0
- package/dist/components/tree/CladeLabels.d.ts +12 -0
- package/dist/components/tree/CladeLabels.js +48 -0
- package/dist/components/tree/CladeLabels.js.map +1 -0
- package/dist/components/tree/TreeBranchMenu.js +1 -1
- package/dist/components/tree/TreeBranchMenu.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +5 -4
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +4 -21
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeNodeMenu.js +1 -1
- package/dist/components/tree/TreeNodeMenu.js.map +1 -1
- package/dist/components/tree/TreeOverview.d.ts +14 -0
- package/dist/components/tree/TreeOverview.js +75 -0
- package/dist/components/tree/TreeOverview.js.map +1 -0
- package/dist/components/tree/TreeRuler.js +9 -13
- package/dist/components/tree/TreeRuler.js.map +1 -1
- package/dist/components/tree/cladeBrackets.d.ts +25 -0
- package/dist/components/tree/cladeBrackets.js +51 -0
- package/dist/components/tree/cladeBrackets.js.map +1 -0
- package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +2 -2
- package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +160 -25
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/tree/renderTreeOverview.d.ts +52 -0
- package/dist/components/tree/renderTreeOverview.js +138 -0
- package/dist/components/tree/renderTreeOverview.js.map +1 -0
- package/dist/components/tree/useTreeHover.js +2 -4
- package/dist/components/tree/useTreeHover.js.map +1 -1
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +52 -22
- package/dist/constants.js.map +1 -1
- package/dist/createPaletteMap.d.ts +7 -6
- package/dist/createPaletteMap.js +10 -9
- package/dist/createPaletteMap.js.map +1 -1
- package/dist/element.d.ts +13 -0
- package/dist/element.js +269 -0
- package/dist/element.js.map +1 -0
- package/dist/featureFields.d.ts +11 -0
- package/dist/featureFields.js +17 -0
- package/dist/featureFields.js.map +1 -0
- package/dist/flatToTree.js +1 -2
- package/dist/flatToTree.js.map +1 -1
- package/dist/ggplotPalettes.d.ts +10 -0
- package/dist/ggplotPalettes.js +58 -0
- package/dist/ggplotPalettes.js.map +1 -1
- package/dist/hierarchy.d.ts +27 -0
- package/dist/hierarchy.js +66 -9
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/model/DataModel.js +2 -5
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model/msaModel.d.ts +3 -2
- package/dist/model/msaModel.js +3 -2
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.d.ts +30 -0
- package/dist/model/treeModel.js +37 -1
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +549 -294
- package/dist/model.js +1412 -621
- package/dist/model.js.map +1 -1
- package/dist/mount.d.ts +11 -0
- package/dist/mount.js +22 -0
- package/dist/mount.js.map +1 -0
- package/dist/neighborJoining.js +8 -32
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderToStaticMarkup.d.ts +10 -19
- package/dist/renderToStaticMarkup.js +13 -24
- package/dist/renderToStaticMarkup.js.map +1 -1
- package/dist/renderToSvg.js +180 -56
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +4 -6
- package/dist/rowCoordinateCalculations.js +4 -6
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/scales.d.ts +22 -0
- package/dist/scales.js +30 -0
- package/dist/scales.js.map +1 -0
- package/dist/seqPosToGlobalCol.d.ts +2 -3
- package/dist/seqPosToGlobalCol.js +2 -3
- package/dist/seqPosToGlobalCol.js.map +1 -1
- package/dist/sequenceLogo.d.ts +6 -11
- package/dist/sequenceLogo.js +6 -11
- package/dist/sequenceLogo.js.map +1 -1
- package/dist/stripDefault.d.ts +7 -14
- package/dist/stripDefault.js +7 -14
- package/dist/stripDefault.js.map +1 -1
- package/dist/svgTestUtil.d.ts +186 -70
- package/dist/types.d.ts +221 -21
- package/dist/umd.d.ts +2 -0
- package/dist/umd.js +4 -2
- package/dist/umd.js.map +1 -1
- package/dist/useCanvasAutorun.d.ts +5 -10
- package/dist/useCanvasAutorun.js +5 -10
- package/dist/useCanvasAutorun.js.map +1 -1
- package/dist/useMsaSvgFigure.d.ts +20 -0
- package/dist/useMsaSvgFigure.js +46 -0
- package/dist/useMsaSvgFigure.js.map +1 -0
- package/dist/useWheelScroll.js +0 -4
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/util.d.ts +5 -6
- package/dist/util.js +13 -6
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +19 -12
- package/src/calculateBlocks.ts +5 -3
- package/src/colorSchemes.ts +1 -2
- package/src/columnStats.ts +58 -0
- package/src/components/DragHandle.tsx +18 -7
- package/src/components/Loading.tsx +4 -36
- package/src/components/MSAView.tsx +19 -5
- package/src/components/MSAViewer.tsx +240 -28
- package/src/components/PortalTooltip.tsx +47 -0
- package/src/components/ResizeHandles.tsx +17 -32
- package/src/components/SequenceTextArea.tsx +3 -4
- package/src/components/Track.tsx +57 -19
- package/src/components/dialogs/AboutDialog.tsx +35 -6
- package/src/components/dialogs/AnnotationFileDialog.tsx +10 -1
- package/src/components/dialogs/ExportSVGDialog.tsx +8 -3
- package/src/components/dialogs/FeatureDialog.tsx +11 -3
- package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
- package/src/components/header/GappynessSlider.tsx +6 -11
- package/src/components/header/Header.tsx +7 -1
- package/src/components/header/LoadWarnings.tsx +11 -25
- package/src/components/header/ScrollZoomIcons.tsx +52 -0
- package/src/components/header/TreeSettingsMenu.tsx +1 -3
- package/src/components/header/UnshareableDataWarning.tsx +2 -4
- package/src/components/header/ZoomControls.tsx +74 -14
- package/src/components/header/getAnnotationsMenu.ts +3 -6
- package/src/components/header/settingsMenuItems.ts +13 -8
- package/src/components/import/ImportForm.tsx +3 -5
- package/src/components/import/ImportFormExamples.tsx +2 -2
- package/src/components/import/util.ts +1 -2
- package/src/components/minimap/Minimap.tsx +1 -2
- package/src/components/msa/AnnotationLegend.tsx +48 -38
- package/src/components/msa/MSACanvas.tsx +3 -8
- package/src/components/msa/MSACanvasBlock.tsx +13 -16
- package/src/components/msa/MSAMouseoverCanvas.tsx +2 -1
- package/src/components/msa/domainBandCursor.ts +7 -11
- package/src/components/msa/drawFeatureSpans.ts +205 -0
- package/src/components/msa/legendRows.ts +24 -0
- package/src/components/msa/msaRaster.ts +30 -39
- package/src/components/msa/packDomainLanes.ts +36 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +67 -123
- package/src/components/msa/renderHighlights.ts +53 -20
- package/src/components/msa/renderMSABlock.ts +14 -11
- package/src/components/msa/tileColor.ts +4 -7
- package/src/components/msa/useMsaBlockMouse.ts +6 -6
- package/src/components/rowpanels/RowPanelBlock.tsx +139 -0
- package/src/components/rowpanels/RowPanelHeaders.tsx +57 -0
- package/src/components/rowpanels/RowPanels.tsx +77 -0
- package/src/components/rowpanels/headerLayout.ts +5 -0
- package/src/components/rowpanels/renderFeaturePanel.ts +66 -0
- package/src/components/rowpanels/renderRowPanel.ts +41 -0
- package/src/components/rowpanels/renderStrip.ts +46 -0
- package/src/components/tracks/TrackBlocks.tsx +3 -5
- package/src/components/tracks/TrackTooltipContent.tsx +156 -0
- package/src/components/tracks/drawTracks.ts +25 -50
- package/src/components/tracks/useTrackHover.ts +42 -0
- package/src/components/tree/CladeLabels.tsx +77 -0
- package/src/components/tree/TreeBranchMenu.tsx +1 -2
- package/src/components/tree/TreeCanvas.tsx +2 -3
- package/src/components/tree/TreeCanvasBlock.tsx +8 -32
- package/src/components/tree/TreeNodeMenu.tsx +1 -2
- package/src/components/tree/TreeOverview.tsx +106 -0
- package/src/components/tree/TreeRuler.tsx +11 -22
- package/src/components/tree/cladeBrackets.ts +73 -0
- package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
- package/src/components/tree/renderTreeCanvas.ts +242 -21
- package/src/components/tree/renderTreeOverview.ts +212 -0
- package/src/components/tree/useTreeHover.ts +2 -4
- package/src/constants.ts +61 -22
- package/src/createPaletteMap.ts +10 -9
- package/src/element.ts +321 -0
- package/src/featureFields.ts +19 -0
- package/src/flatToTree.ts +1 -2
- package/src/ggplotPalettes.ts +60 -0
- package/src/hierarchy.ts +77 -9
- package/src/index.ts +31 -7
- package/src/model/DataModel.ts +2 -5
- package/src/model/msaModel.ts +3 -2
- package/src/model/treeModel.ts +42 -0
- package/src/model.ts +1677 -643
- package/src/mount.tsx +32 -0
- package/src/neighborJoining.ts +8 -32
- package/src/renderToStaticMarkup.ts +13 -24
- package/src/renderToSvg.tsx +335 -68
- package/src/rowCoordinateCalculations.ts +4 -6
- package/src/scales.ts +47 -0
- package/src/seqPosToGlobalCol.ts +2 -3
- package/src/sequenceLogo.ts +6 -11
- package/src/stripDefault.ts +7 -14
- package/src/types.ts +260 -45
- package/src/umd.ts +4 -2
- package/src/useCanvasAutorun.ts +5 -10
- package/src/useMsaSvgFigure.ts +72 -0
- package/src/useWheelScroll.ts +0 -4
- package/src/util.ts +15 -6
- package/src/version.ts +1 -1
- package/dist/components/msa/ColumnStats.js +0 -42
- package/dist/components/msa/ColumnStats.js.map +0 -1
- package/src/components/msa/ColumnStats.tsx +0 -54
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, { useCallback, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import { observer } from 'mobx-react'
|
|
4
|
+
|
|
5
|
+
import { useWheelScroll } from '../../useWheelScroll.ts'
|
|
6
|
+
import RowPanelBlock from './RowPanelBlock.tsx'
|
|
7
|
+
|
|
8
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
9
|
+
import type { ResolvedRowPanel } from '../../types.ts'
|
|
10
|
+
|
|
11
|
+
const RowPanel = observer(function ({
|
|
12
|
+
model,
|
|
13
|
+
panel,
|
|
14
|
+
}: {
|
|
15
|
+
model: MsaViewModel
|
|
16
|
+
panel: ResolvedRowPanel
|
|
17
|
+
}) {
|
|
18
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
19
|
+
const { height, blocksY, scrollY } = model
|
|
20
|
+
const onScrollY = useCallback(
|
|
21
|
+
(d: number) => {
|
|
22
|
+
model.doScrollY(d)
|
|
23
|
+
},
|
|
24
|
+
[model],
|
|
25
|
+
)
|
|
26
|
+
const { onMouseDown, onMouseUp } = useWheelScroll({ ref, onScrollY })
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
ref={ref}
|
|
31
|
+
data-testid={`rowpanel_${panel.id}`}
|
|
32
|
+
onMouseDown={onMouseDown}
|
|
33
|
+
onMouseUp={onMouseUp}
|
|
34
|
+
style={{
|
|
35
|
+
height,
|
|
36
|
+
width: panel.width,
|
|
37
|
+
flexShrink: 0,
|
|
38
|
+
overflow: 'hidden',
|
|
39
|
+
position: 'relative',
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
{/* one transform for the whole block set, matching TreeCanvas: the
|
|
43
|
+
strips scroll in lockstep with the tree and the alignment */}
|
|
44
|
+
<div
|
|
45
|
+
style={{
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
top: 0,
|
|
48
|
+
left: 0,
|
|
49
|
+
transform: `translateY(${scrollY}px)`,
|
|
50
|
+
willChange: 'transform',
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
{blocksY.map(block => (
|
|
54
|
+
<RowPanelBlock
|
|
55
|
+
key={block}
|
|
56
|
+
model={model}
|
|
57
|
+
panel={panel}
|
|
58
|
+
offsetY={block}
|
|
59
|
+
/>
|
|
60
|
+
))}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const RowPanels = observer(function ({ model }: { model: MsaViewModel }) {
|
|
67
|
+
const { resolvedRowPanels } = model
|
|
68
|
+
return resolvedRowPanels.length > 0 ? (
|
|
69
|
+
<div style={{ display: 'flex', flexShrink: 0 }}>
|
|
70
|
+
{resolvedRowPanels.map(panel => (
|
|
71
|
+
<RowPanel key={panel.id} model={model} panel={panel} />
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
) : null
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
export default RowPanels
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { contrastTextFn } from '../../util.ts'
|
|
2
|
+
import { getVisibleLeaves } from '../getVisibleLeaves.ts'
|
|
3
|
+
import { drawFeatureSpans, spanRows } from '../msa/drawFeatureSpans.ts'
|
|
4
|
+
|
|
5
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
6
|
+
import type { ResolvedFeaturePanel, RowPanelSpan } from '../../types.ts'
|
|
7
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
8
|
+
import type { Theme } from '@mui/material'
|
|
9
|
+
|
|
10
|
+
// a span keeps a pixel clear above and below it, so stacked lanes and adjacent
|
|
11
|
+
// rows read apart
|
|
12
|
+
const rowPadding = 2
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The spans of a `features` panel, drawn with the mark the alignment overlay
|
|
16
|
+
* draws: an arrow where the feature carries a strand, a box otherwise, labeled
|
|
17
|
+
* where the text fits. The x mapping is the panel's own, resolved in
|
|
18
|
+
* `resolvedRowPanels`; a row needing several lanes divides its height between
|
|
19
|
+
* them.
|
|
20
|
+
*/
|
|
21
|
+
export function renderFeaturePanel({
|
|
22
|
+
ctx,
|
|
23
|
+
model,
|
|
24
|
+
theme,
|
|
25
|
+
panel,
|
|
26
|
+
x,
|
|
27
|
+
offsetY,
|
|
28
|
+
blockSizeYOverride,
|
|
29
|
+
highResScaleFactorOverride,
|
|
30
|
+
}: {
|
|
31
|
+
ctx: RenderCtx
|
|
32
|
+
model: MsaViewModel
|
|
33
|
+
theme: Theme
|
|
34
|
+
panel: ResolvedFeaturePanel
|
|
35
|
+
x: number
|
|
36
|
+
offsetY: number
|
|
37
|
+
blockSizeYOverride?: number
|
|
38
|
+
highResScaleFactorOverride?: number
|
|
39
|
+
}) {
|
|
40
|
+
const { rowHeight, blockSize, highResScaleFactor } = model
|
|
41
|
+
const by = blockSizeYOverride ?? blockSize
|
|
42
|
+
const k = highResScaleFactorOverride ?? highResScaleFactor
|
|
43
|
+
ctx.resetTransform()
|
|
44
|
+
ctx.scale(k, k)
|
|
45
|
+
ctx.translate(x, rowHeight / 2 - offsetY)
|
|
46
|
+
|
|
47
|
+
drawFeatureSpans<RowPanelSpan>({
|
|
48
|
+
ctx,
|
|
49
|
+
rows: spanRows(
|
|
50
|
+
getVisibleLeaves({ model, offsetY, blockSizeY: by }),
|
|
51
|
+
panel.spans,
|
|
52
|
+
),
|
|
53
|
+
layout: {
|
|
54
|
+
height: laneCount => Math.max(1, rowHeight / laneCount - rowPadding),
|
|
55
|
+
top: (y, lane, h) =>
|
|
56
|
+
y - rowHeight + lane * (h + rowPadding) + rowPadding / 2,
|
|
57
|
+
headLength: h => h,
|
|
58
|
+
},
|
|
59
|
+
xOf: span => [span.xStart, span.xEnd],
|
|
60
|
+
colors: panel.colors,
|
|
61
|
+
labelOf: span => panel.labels?.get(span.annotation),
|
|
62
|
+
contrastText: contrastTextFn(theme),
|
|
63
|
+
xMin: 0,
|
|
64
|
+
xMax: panel.width,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { renderFeaturePanel } from './renderFeaturePanel.ts'
|
|
2
|
+
import { renderStrip } from './renderStrip.ts'
|
|
3
|
+
|
|
4
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
5
|
+
import type { ResolvedRowPanel } from '../../types.ts'
|
|
6
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
7
|
+
import type { Theme } from '@mui/material'
|
|
8
|
+
|
|
9
|
+
interface PanelArgs {
|
|
10
|
+
ctx: RenderCtx
|
|
11
|
+
model: MsaViewModel
|
|
12
|
+
theme: Theme
|
|
13
|
+
panel: ResolvedRowPanel
|
|
14
|
+
x: number
|
|
15
|
+
offsetY: number
|
|
16
|
+
blockSizeYOverride?: number
|
|
17
|
+
highResScaleFactorOverride?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* One row panel, by kind. `x` is the panel's left edge in the context: the
|
|
22
|
+
* live view gives each panel a canvas of its own and passes 0, and the export
|
|
23
|
+
* packs every panel into one context and passes the panel's own offset.
|
|
24
|
+
*/
|
|
25
|
+
export function renderRowPanel({ panel, ...rest }: PanelArgs) {
|
|
26
|
+
if (panel.kind === 'features') {
|
|
27
|
+
renderFeaturePanel({ panel, ...rest })
|
|
28
|
+
} else {
|
|
29
|
+
renderStrip({ panel, ...rest })
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** every row panel side by side in one context, for the SVG export */
|
|
34
|
+
export function renderRowPanels({
|
|
35
|
+
model,
|
|
36
|
+
...rest
|
|
37
|
+
}: Omit<PanelArgs, 'panel' | 'x'>) {
|
|
38
|
+
for (const panel of model.resolvedRowPanels) {
|
|
39
|
+
renderRowPanel({ model, panel, x: panel.offsetX, ...rest })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getVisibleLeaves } from '../getVisibleLeaves.ts'
|
|
2
|
+
|
|
3
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
4
|
+
import type { ResolvedStripPanel } from '../../types.ts'
|
|
5
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One cell per row of a `strip` panel, in the color its field takes through
|
|
9
|
+
* the panel's scale. A row the table has no value for leaves the cell empty.
|
|
10
|
+
*
|
|
11
|
+
* `x` is the panel's left edge in the context: the live view gives each panel
|
|
12
|
+
* a canvas of its own and passes 0, and the export packs every panel into one
|
|
13
|
+
* context and passes the panel's own offset.
|
|
14
|
+
*/
|
|
15
|
+
export function renderStrip({
|
|
16
|
+
ctx,
|
|
17
|
+
model,
|
|
18
|
+
panel,
|
|
19
|
+
x,
|
|
20
|
+
offsetY,
|
|
21
|
+
blockSizeYOverride,
|
|
22
|
+
highResScaleFactorOverride,
|
|
23
|
+
}: {
|
|
24
|
+
ctx: RenderCtx
|
|
25
|
+
model: MsaViewModel
|
|
26
|
+
panel: ResolvedStripPanel
|
|
27
|
+
x: number
|
|
28
|
+
offsetY: number
|
|
29
|
+
blockSizeYOverride?: number
|
|
30
|
+
highResScaleFactorOverride?: number
|
|
31
|
+
}) {
|
|
32
|
+
const { rowHeight, blockSize, highResScaleFactor } = model
|
|
33
|
+
const by = blockSizeYOverride ?? blockSize
|
|
34
|
+
ctx.resetTransform()
|
|
35
|
+
const k = highResScaleFactorOverride ?? highResScaleFactor
|
|
36
|
+
ctx.scale(k, k)
|
|
37
|
+
ctx.translate(x, -offsetY)
|
|
38
|
+
for (const leaf of getVisibleLeaves({ model, offsetY, blockSizeY: by })) {
|
|
39
|
+
const color = panel.colors.get(leaf.data.name)
|
|
40
|
+
if (color) {
|
|
41
|
+
ctx.fillStyle = color
|
|
42
|
+
// leaf.x is the row's center, at rowHeight*(index+0.5)
|
|
43
|
+
ctx.fillRect(0, leaf.x! - rowHeight / 2, panel.width, rowHeight)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -64,11 +64,9 @@ const TrackBlocks = observer(function ({
|
|
|
64
64
|
model: MsaViewModel
|
|
65
65
|
track: BasicTrack
|
|
66
66
|
}) {
|
|
67
|
-
// msaCanvasWidth, not msaAreaWidth:
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// last columns appear in the tracks but not in the alignment, and the hovered
|
|
71
|
-
// -column indicator (sized to the canvas) stops short of the track it crosses
|
|
67
|
+
// msaCanvasWidth, not msaAreaWidth: tracks stop where the alignment canvas
|
|
68
|
+
// does, not 20px further under the vertical scrollbar, so their columns and
|
|
69
|
+
// the hover indicator line up with the alignment
|
|
72
70
|
const { blocksX, msaCanvasWidth, scrollX } = model
|
|
73
71
|
const { kind, height, data, arcs } = track.model
|
|
74
72
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { observer } from 'mobx-react'
|
|
4
|
+
|
|
5
|
+
import { columnLogoStack } from '../../sequenceLogo.ts'
|
|
6
|
+
import { barTrackValues } from './drawTracks.ts'
|
|
7
|
+
|
|
8
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
9
|
+
import type { BasicTrack } from '../../types.ts'
|
|
10
|
+
|
|
11
|
+
const pct = (n: number) => `${Math.round(n * 100)}%`
|
|
12
|
+
|
|
13
|
+
const MAX_PARTNERS = 8
|
|
14
|
+
|
|
15
|
+
function ColumnLabel({ model, col }: { model: MsaViewModel; col: number }) {
|
|
16
|
+
const { relativeTo } = model
|
|
17
|
+
const pos = relativeTo
|
|
18
|
+
? model.visibleColToSeqPosOneBased(relativeTo, col)
|
|
19
|
+
: undefined
|
|
20
|
+
return (
|
|
21
|
+
<b>
|
|
22
|
+
Column {col + 1}
|
|
23
|
+
{pos === undefined ? null : ` (${relativeTo}:${pos})`}
|
|
24
|
+
</b>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function BarTooltip({
|
|
29
|
+
model,
|
|
30
|
+
track,
|
|
31
|
+
col,
|
|
32
|
+
}: {
|
|
33
|
+
model: MsaViewModel
|
|
34
|
+
track: BasicTrack
|
|
35
|
+
col: number
|
|
36
|
+
}) {
|
|
37
|
+
const { id, name } = track.model
|
|
38
|
+
const value = barTrackValues(model, id)[col]
|
|
39
|
+
return value === undefined ? null : (
|
|
40
|
+
<div>
|
|
41
|
+
<ColumnLabel model={model} col={col} />
|
|
42
|
+
<br />
|
|
43
|
+
{name}: {value.toFixed(2)}
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function LogoTooltip({ model, col }: { model: MsaViewModel; col: number }) {
|
|
49
|
+
const stats = model.columnStatsAt(col)
|
|
50
|
+
if (!stats) {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
const { total, consensusLetter, consensusFraction, gapFraction } = stats
|
|
54
|
+
const stack = columnLogoStack(model.colStats, col, model.alphabetMaxBits)
|
|
55
|
+
const bits = stack.reduce((a, b) => a + b.bits, 0)
|
|
56
|
+
return (
|
|
57
|
+
<div>
|
|
58
|
+
<ColumnLabel model={model} col={col} /> ({total} seqs)
|
|
59
|
+
<br />
|
|
60
|
+
Information: {bits.toFixed(2)} bits
|
|
61
|
+
<br />
|
|
62
|
+
Consensus: {consensusLetter || '-'} ({pct(consensusFraction)})
|
|
63
|
+
<br />
|
|
64
|
+
Gaps: {pct(gapFraction)}
|
|
65
|
+
<br />
|
|
66
|
+
{stats.distribution.slice(0, 6).map(([letter, count]) => (
|
|
67
|
+
<span key={letter} style={{ marginRight: 8 }}>
|
|
68
|
+
{letter} {pct(count / total)}
|
|
69
|
+
</span>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function TextTooltip({
|
|
76
|
+
model,
|
|
77
|
+
track,
|
|
78
|
+
col,
|
|
79
|
+
}: {
|
|
80
|
+
model: MsaViewModel
|
|
81
|
+
track: BasicTrack
|
|
82
|
+
col: number
|
|
83
|
+
}) {
|
|
84
|
+
const { name, data } = track.model
|
|
85
|
+
const letter = data?.[col]
|
|
86
|
+
return letter === undefined ? null : (
|
|
87
|
+
<div>
|
|
88
|
+
<ColumnLabel model={model} col={col} />
|
|
89
|
+
<br />
|
|
90
|
+
{name}: {letter === ' ' ? 'blank' : letter}
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function ArcTooltip({
|
|
96
|
+
model,
|
|
97
|
+
track,
|
|
98
|
+
col,
|
|
99
|
+
}: {
|
|
100
|
+
model: MsaViewModel
|
|
101
|
+
track: BasicTrack
|
|
102
|
+
col: number
|
|
103
|
+
}) {
|
|
104
|
+
const { name, arcs } = track.model
|
|
105
|
+
const partners = (arcs ?? [])
|
|
106
|
+
.filter(arc => arc.start === col || arc.end === col)
|
|
107
|
+
.map(arc => (arc.start === col ? arc.end : arc.start) + 1)
|
|
108
|
+
return partners.length === 0 ? null : (
|
|
109
|
+
<div>
|
|
110
|
+
<ColumnLabel model={model} col={col} />
|
|
111
|
+
<br />
|
|
112
|
+
{name}: pairs with {partners.slice(0, MAX_PARTNERS).join(', ')}
|
|
113
|
+
{partners.length > MAX_PARTNERS ? '...' : null}
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* One track's reading at the hovered column: a bar track's value, the logo's
|
|
120
|
+
* information content and composition, the columns an arc joins, the letter on a
|
|
121
|
+
* text track. `drawTrackBlock` dispatches on the same `kind`, so a new kind
|
|
122
|
+
* needs a case in both.
|
|
123
|
+
*/
|
|
124
|
+
const TrackTooltipContent = observer(function ({
|
|
125
|
+
model,
|
|
126
|
+
track,
|
|
127
|
+
col,
|
|
128
|
+
}: {
|
|
129
|
+
model: MsaViewModel
|
|
130
|
+
track: BasicTrack
|
|
131
|
+
col: number
|
|
132
|
+
}) {
|
|
133
|
+
switch (track.model.kind) {
|
|
134
|
+
case 'bar': {
|
|
135
|
+
return <BarTooltip model={model} track={track} col={col} />
|
|
136
|
+
}
|
|
137
|
+
case 'logo': {
|
|
138
|
+
return <LogoTooltip model={model} col={col} />
|
|
139
|
+
}
|
|
140
|
+
case 'text': {
|
|
141
|
+
return <TextTooltip model={model} track={track} col={col} />
|
|
142
|
+
}
|
|
143
|
+
case 'arc': {
|
|
144
|
+
return <ArcTooltip model={model} track={track} col={col} />
|
|
145
|
+
}
|
|
146
|
+
case 'ruler': {
|
|
147
|
+
return (
|
|
148
|
+
<div>
|
|
149
|
+
<ColumnLabel model={model} col={col} />
|
|
150
|
+
</div>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
export default TrackTooltipContent
|
|
@@ -41,8 +41,8 @@ export function drawConservationBars({
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
//
|
|
44
|
+
// bar-track values are model getters, off the plain track object, so the canvas
|
|
45
|
+
// autorun stays reactive
|
|
46
46
|
export function barTrackValues(model: MsaViewModel, trackId: string) {
|
|
47
47
|
const supplied = model.columnTrackContent.get(trackId)?.values
|
|
48
48
|
if (supplied) {
|
|
@@ -53,10 +53,8 @@ export function barTrackValues(model: MsaViewModel, trackId: string) {
|
|
|
53
53
|
: model.conservation
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
// An arc's apex scales with
|
|
57
|
-
// and a
|
|
58
|
-
// than linear because a contact map's spans are heavily skewed towards short
|
|
59
|
-
// ones, which linear scaling flattens into a smear along the baseline.
|
|
56
|
+
// An arc's apex scales with the square root of its span. Contact-map spans skew
|
|
57
|
+
// short, and a linear scale flattens them along the baseline.
|
|
60
58
|
export function drawArcs({
|
|
61
59
|
ctx,
|
|
62
60
|
arcs,
|
|
@@ -145,17 +143,13 @@ export function drawTextTrackContent({
|
|
|
145
143
|
ctx.fillRect(x, 0, colWidth, rowHeight)
|
|
146
144
|
}
|
|
147
145
|
if (drawLetters) {
|
|
148
|
-
// a letter on a colored tile needs that tile's contrast color; otherwise
|
|
149
|
-
// it sits on the plain background and takes the theme's text color
|
|
150
146
|
ctx.fillStyle = contrastText(fill)
|
|
151
147
|
ctx.fillText(letter, x + colWidth / 2, rowHeight / 2)
|
|
152
148
|
}
|
|
153
149
|
}
|
|
154
150
|
}
|
|
155
151
|
|
|
156
|
-
//
|
|
157
|
-
// smallest 1/2/5 x 10^n that leaves room for a label, so a zoom changes how many
|
|
158
|
-
// numbers appear rather than crowding them.
|
|
152
|
+
// the smallest 1/2/5 x 10^n column step that leaves room for a label
|
|
159
153
|
export function rulerStep(colWidth: number, minPixels = 55) {
|
|
160
154
|
let step = 1
|
|
161
155
|
const mantissas = [1, 2, 5]
|
|
@@ -215,17 +209,11 @@ export function drawColumnRuler({
|
|
|
215
209
|
}
|
|
216
210
|
}
|
|
217
211
|
|
|
218
|
-
// Cap height as a fraction of the font size
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
// backend, so the ratio is a constant: the cap height of the sans-serif faces
|
|
223
|
-
// both backends use, within a percent or two. It only has to be consistent --
|
|
224
|
-
// the same ratio for every letter is what makes the slices sum to the stack
|
|
225
|
-
// height instead of drifting apart.
|
|
212
|
+
// Cap height as a fraction of the font size, within a percent or two for the
|
|
213
|
+
// sans-serif faces both backends use. `measureText` reports no vertical box in
|
|
214
|
+
// the SVG export backend, so one constant ratio scales every logo letter, and
|
|
215
|
+
// the slices sum to the stack height.
|
|
226
216
|
const CAP_HEIGHT_RATIO = 0.72
|
|
227
|
-
// letters below this many pixels are noise, and a stack that thin reads better
|
|
228
|
-
// as the bar chart it approximates
|
|
229
217
|
const MIN_LETTER_PX = 3
|
|
230
218
|
|
|
231
219
|
export function drawSequenceLogo({
|
|
@@ -256,8 +244,8 @@ export function drawSequenceLogo({
|
|
|
256
244
|
})
|
|
257
245
|
const end = Math.min(xEnd, colStats.numColumns)
|
|
258
246
|
|
|
259
|
-
// one reference size for
|
|
260
|
-
//
|
|
247
|
+
// one reference font size for the block; each letter's transform scales it to
|
|
248
|
+
// a height proportional to its frequency
|
|
261
249
|
const referenceFontSize = trackHeight
|
|
262
250
|
setFontSize(ctx, referenceFontSize)
|
|
263
251
|
ctx.textAlign = 'left'
|
|
@@ -270,9 +258,7 @@ export function drawSequenceLogo({
|
|
|
270
258
|
continue
|
|
271
259
|
}
|
|
272
260
|
const x = col * colWidth
|
|
273
|
-
//
|
|
274
|
-
// lands on top, which is the convention that makes a logo readable at a
|
|
275
|
-
// glance
|
|
261
|
+
// the stack is ascending, so the tallest letter lands on top
|
|
276
262
|
let bottom = trackHeight
|
|
277
263
|
for (const { letter, bits } of stack) {
|
|
278
264
|
const h = (bits / maxBits) * trackHeight
|
|
@@ -300,9 +286,9 @@ export function drawSequenceLogo({
|
|
|
300
286
|
|
|
301
287
|
/**
|
|
302
288
|
* Draw one track's content into a block of the alignment's column space, at
|
|
303
|
-
* `offsetY` within the ctx. Owns the transform, so
|
|
304
|
-
*
|
|
305
|
-
*
|
|
289
|
+
* `offsetY` within the ctx. Owns the transform, so the live canvas blocks and
|
|
290
|
+
* the SVG export draw into the same coordinate space. A new track kind needs a
|
|
291
|
+
* `kind`, a draw function above, and a case here.
|
|
306
292
|
*/
|
|
307
293
|
export function drawTrackBlock({
|
|
308
294
|
model,
|
|
@@ -323,18 +309,9 @@ export function drawTrackBlock({
|
|
|
323
309
|
blockSizeXOverride?: number
|
|
324
310
|
highResScaleFactorOverride?: number
|
|
325
311
|
}) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
colWidth,
|
|
330
|
-
colStats,
|
|
331
|
-
colorScheme: modelColorScheme,
|
|
332
|
-
fontSize,
|
|
333
|
-
rowHeight,
|
|
334
|
-
showMsaLetters,
|
|
335
|
-
alphabetMaxBits,
|
|
336
|
-
highResScaleFactor,
|
|
337
|
-
} = model
|
|
312
|
+
// an autorun tracks every model read here, so each kind reads only what it
|
|
313
|
+
// draws and a vertical zoom leaves the bar and arc canvases alone
|
|
314
|
+
const { blockSize, colWidth, highResScaleFactor } = model
|
|
338
315
|
const {
|
|
339
316
|
id,
|
|
340
317
|
kind,
|
|
@@ -381,9 +358,9 @@ export function drawTrackBlock({
|
|
|
381
358
|
case 'logo': {
|
|
382
359
|
drawSequenceLogo({
|
|
383
360
|
ctx,
|
|
384
|
-
colStats,
|
|
385
|
-
colorScheme:
|
|
386
|
-
maxBits: alphabetMaxBits,
|
|
361
|
+
colStats: model.colStats,
|
|
362
|
+
colorScheme: model.colorScheme,
|
|
363
|
+
maxBits: model.alphabetMaxBits,
|
|
387
364
|
textColor,
|
|
388
365
|
colWidth,
|
|
389
366
|
trackHeight,
|
|
@@ -397,8 +374,7 @@ export function drawTrackBlock({
|
|
|
397
374
|
drawColumnRuler({
|
|
398
375
|
ctx,
|
|
399
376
|
numColumns,
|
|
400
|
-
//
|
|
401
|
-
// drawn relative to one, which is what the rest of the view counts in
|
|
377
|
+
// with relativeTo set, number by the reference row's residues
|
|
402
378
|
label: relativeTo
|
|
403
379
|
? col => {
|
|
404
380
|
const pos = model.visibleColToSeqPosOneBased(relativeTo, col)
|
|
@@ -414,17 +390,16 @@ export function drawTrackBlock({
|
|
|
414
390
|
break
|
|
415
391
|
}
|
|
416
392
|
case 'text': {
|
|
393
|
+
const { bgColor, fontSize, rowHeight, showMsaLetters } = model
|
|
417
394
|
ctx.textAlign = 'center'
|
|
418
395
|
ctx.textBaseline = 'middle'
|
|
419
396
|
setFontSize(ctx, fontSize)
|
|
420
397
|
drawTextTrackContent({
|
|
421
398
|
ctx,
|
|
422
399
|
data,
|
|
423
|
-
colorScheme: customColorScheme ??
|
|
400
|
+
colorScheme: customColorScheme ?? model.colorScheme,
|
|
424
401
|
contrastText: contrastTextFn(theme),
|
|
425
402
|
bgColor,
|
|
426
|
-
// a text track is one alignment row tall, so it shows letters exactly
|
|
427
|
-
// when the alignment does
|
|
428
403
|
drawLetters: showMsaLetters,
|
|
429
404
|
colWidth,
|
|
430
405
|
rowHeight,
|
|
@@ -440,7 +415,7 @@ export function drawTrackBlock({
|
|
|
440
415
|
|
|
441
416
|
/**
|
|
442
417
|
* Every turned-on track stacked top to bottom, for the SVG export. The live
|
|
443
|
-
* view draws the same content one canvas block at a time
|
|
418
|
+
* view draws the same content one canvas block at a time in TrackBlocks.
|
|
444
419
|
*/
|
|
445
420
|
export function renderAllTracks({
|
|
446
421
|
model,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
4
|
+
import type React from 'react'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Translates pointer events over a track's row into the hovered column and a
|
|
8
|
+
* tooltip anchor. The track shares the alignment's columns, so hovering it sets
|
|
9
|
+
* the model's mouse column too and the alignment's hover band follows along.
|
|
10
|
+
*
|
|
11
|
+
* `tooltip` off skips the anchor, so a track whose tooltip is turned off still
|
|
12
|
+
* drives the hover band without re-rendering on every mouse move.
|
|
13
|
+
*/
|
|
14
|
+
export function useTrackHover({
|
|
15
|
+
model,
|
|
16
|
+
tooltip,
|
|
17
|
+
}: {
|
|
18
|
+
model: MsaViewModel
|
|
19
|
+
tooltip: boolean
|
|
20
|
+
}) {
|
|
21
|
+
const [hover, setHover] = useState<{ col: number; x: number; y: number }>()
|
|
22
|
+
|
|
23
|
+
// the blocks are laid out in column space and scrolled by a transform on
|
|
24
|
+
// their container, so the pointer's column comes off the untransformed row
|
|
25
|
+
function onMouseMove(event: React.MouseEvent, el: HTMLElement) {
|
|
26
|
+
const { colWidth, scrollX, numColumns } = model
|
|
27
|
+
const { left } = el.getBoundingClientRect()
|
|
28
|
+
const col = Math.floor((event.clientX - left - scrollX) / colWidth)
|
|
29
|
+
const hit = col >= 0 && col < numColumns
|
|
30
|
+
model.setMousePos(hit ? col : undefined, undefined)
|
|
31
|
+
if (tooltip) {
|
|
32
|
+
setHover(hit ? { col, x: event.clientX, y: event.clientY } : undefined)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function onMouseLeave() {
|
|
37
|
+
setHover(undefined)
|
|
38
|
+
model.setMousePos(undefined, undefined)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { hover: tooltip ? hover : undefined, onMouseMove, onMouseLeave }
|
|
42
|
+
}
|