react-msaview 7.0.0 → 8.0.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/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.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 -3
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/Track.js +41 -22
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +5 -3
- package/dist/components/dialogs/ExportSVGDialog.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 +3 -0
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/LoadWarnings.js +3 -4
- 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 +10 -7
- package/dist/components/header/settingsMenuItems.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 +1 -1
- 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 +53 -0
- package/dist/components/msa/drawFeatureSpans.js +78 -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 +51 -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 +5 -4
- 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 +17 -34
- 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/TreeCanvas.js +4 -1
- 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/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 +158 -24
- 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 +15 -0
- package/dist/constants.js +48 -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 +531 -275
- package/dist/model.js +1295 -547
- 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 +172 -52
- package/dist/types.d.ts +219 -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/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/colorSchemes.ts +1 -2
- package/src/columnStats.ts +58 -0
- package/src/components/DragHandle.tsx +18 -7
- package/src/components/Loading.tsx +2 -6
- 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 -3
- package/src/components/Track.tsx +55 -18
- package/src/components/dialogs/ExportSVGDialog.tsx +7 -2
- package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
- package/src/components/header/GappynessSlider.tsx +6 -11
- package/src/components/header/Header.tsx +6 -0
- package/src/components/header/LoadWarnings.tsx +3 -4
- 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 +11 -6
- 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 -5
- 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 +150 -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 +69 -123
- package/src/components/msa/renderHighlights.ts +53 -20
- package/src/components/msa/renderMSABlock.ts +5 -3
- 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 +17 -34
- package/src/components/tracks/useTrackHover.ts +42 -0
- package/src/components/tree/CladeLabels.tsx +77 -0
- package/src/components/tree/TreeCanvas.tsx +2 -0
- package/src/components/tree/TreeCanvasBlock.tsx +8 -32
- 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 +240 -20
- package/src/components/tree/renderTreeOverview.ts +212 -0
- package/src/components/tree/useTreeHover.ts +2 -4
- package/src/constants.ts +56 -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 +1523 -560
- 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 +258 -45
- package/src/umd.ts +4 -2
- package/src/useCanvasAutorun.ts +5 -10
- package/src/useMsaSvgFigure.ts +72 -0
- 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
|
@@ -2,27 +2,13 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
3
|
import { useTheme } from '@mui/material';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
|
-
import { createPortal } from 'react-dom';
|
|
6
5
|
import { useCanvasAutorun } from '../../useCanvasAutorun.js';
|
|
6
|
+
import PortalTooltip from '../PortalTooltip.js';
|
|
7
7
|
import TreeBranchMenu from './TreeBranchMenu.js';
|
|
8
8
|
import TreeNodeMenu from './TreeNodeMenu.js';
|
|
9
9
|
import { renderTreeCanvas } from './renderTreeCanvas.js';
|
|
10
10
|
import { useTreeHover } from './useTreeHover.js';
|
|
11
|
-
const useStyles = makeStyles()(
|
|
12
|
-
tooltip: {
|
|
13
|
-
position: 'fixed',
|
|
14
|
-
pointerEvents: 'none',
|
|
15
|
-
zIndex: 10000,
|
|
16
|
-
backgroundColor: theme.palette.grey[700],
|
|
17
|
-
color: theme.palette.common.white,
|
|
18
|
-
padding: '4px 8px',
|
|
19
|
-
borderRadius: 4,
|
|
20
|
-
fontSize: 12,
|
|
21
|
-
whiteSpace: 'nowrap',
|
|
22
|
-
maxWidth: 300,
|
|
23
|
-
overflow: 'hidden',
|
|
24
|
-
textOverflow: 'ellipsis',
|
|
25
|
-
},
|
|
11
|
+
const useStyles = makeStyles()(() => ({
|
|
26
12
|
hover: {
|
|
27
13
|
position: 'absolute',
|
|
28
14
|
pointerEvents: 'none',
|
|
@@ -37,8 +23,7 @@ const TreeCanvasBlock = observer(function ({ model, offsetY, }) {
|
|
|
37
23
|
const [nodeMenu, setNodeMenu] = useState();
|
|
38
24
|
const { clickMap, hovered, hitTest, onMouseMove, onMouseLeave } = useTreeHover({ model, offsetY });
|
|
39
25
|
const { treeAreaWidth, blockSize, highResScaleFactor } = model;
|
|
40
|
-
// TreePanel clips to treeAreaWidth, so
|
|
41
|
-
// viewer never sees -- it was 600px of it per block, at dpr squared
|
|
26
|
+
// TreePanel clips to treeAreaWidth, so a wider canvas is wasted backing store
|
|
42
27
|
const width = treeAreaWidth;
|
|
43
28
|
const height = blockSize;
|
|
44
29
|
const canvasWidth = width * highResScaleFactor;
|
|
@@ -100,9 +85,7 @@ const TreeCanvasBlock = observer(function ({ model, offsetY, }) {
|
|
|
100
85
|
width: hovered.maxX - hovered.minX,
|
|
101
86
|
height: hovered.maxY - hovered.minY,
|
|
102
87
|
} })) : null,
|
|
103
|
-
hovered
|
|
104
|
-
? createPortal(React.createElement("div", { className: classes.tooltip, style: { left: hovered.clientX + 12, top: hovered.clientY + 12 } }, hovered.name), document.body)
|
|
105
|
-
: null));
|
|
88
|
+
hovered ? (React.createElement(PortalTooltip, { clientX: hovered.clientX, clientY: hovered.clientY }, hovered.name)) : null));
|
|
106
89
|
});
|
|
107
90
|
export default TreeCanvasBlock;
|
|
108
91
|
//# sourceMappingURL=TreeCanvasBlock.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeCanvasBlock.js","sourceRoot":"","sources":["../../../src/components/tree/TreeCanvasBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"TreeCanvasBlock.js","sourceRoot":"","sources":["../../../src/components/tree/TreeCanvasBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,aAAa,MAAM,qBAAsB,CAAA;AAChD,OAAO,cAAc,MAAM,qBAAsB,CAAA;AACjD,OAAO,YAAY,MAAM,mBAAoB,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IACpC,KAAK,EAAE;QACL,QAAQ,EAAE,UAAU;QACpB,aAAa,EAAE,MAAM;QACrB,MAAM,EAAE,GAAG;QACX,UAAU,EAAE,iBAAiB;KAC9B;CACF,CAAC,CAAC,CAAA;AASH,MAAM,eAAe,GAAG,QAAQ,CAAC,UAAU,EACzC,KAAK,EACL,OAAO,GAIR;IACC,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,CAAA;IAC/B,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;IACxB,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,EAAY,CAAA;IACxD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,EAAY,CAAA;IACpD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,GAC7D,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IAElC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,kBAAkB,EAAE,GAAG,KAAK,CAAA;IAC9D,8EAA8E;IAC9E,MAAM,KAAK,GAAG,aAAa,CAAA;IAC3B,MAAM,MAAM,GAAG,SAAS,CAAA;IAExB,MAAM,WAAW,GAAG,KAAK,GAAG,kBAAkB,CAAA;IAC9C,MAAM,YAAY,GAAG,MAAM,GAAG,kBAAkB,CAAA;IAChD,MAAM,GAAG,GAAG,gBAAgB,CAAC;QAC3B,IAAI,EAAE,GAAG,CAAC,EAAE;YACV,GAAG,CAAC,cAAc,EAAE,CAAA;YACpB,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,CAAA;YAC9C,gBAAgB,CAAC;gBACf,GAAG;gBACH,KAAK;gBACL,OAAO;gBACP,QAAQ;gBACR,KAAK;aACN,CAAC,CAAA;QACJ,CAAC;QACD,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,YAAY;QACpB,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;KACxC,CAAC,CAAA;IAEF,MAAM,KAAK,GAAG;QACZ,KAAK;QACL,MAAM;QACN,GAAG,EAAE,OAAO;QACZ,IAAI,EAAE,CAAC;QACP,QAAQ,EAAE,UAAU;KACZ,CAAA;IAEV,OAAO,CACL;QACG,UAAU,CAAC,CAAC,CAAC,CACZ,oBAAC,cAAc,IACb,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,GAAG,EAAE;gBACZ,aAAa,CAAC,SAAS,CAAC,CAAA;YAC1B,CAAC,GACD,CACH,CAAC,CAAC,CAAC,IAAI;QAEP,QAAQ,CAAC,CAAC,CAAC,CACV,oBAAC,YAAY,IACX,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,GAAG,EAAE;gBACZ,WAAW,CAAC,SAAS,CAAC,CAAA;YACxB,CAAC,GACD,CACH,CAAC,CAAC,CAAC,IAAI;QAER,gCACE,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,EAC5D,WAAW,EAAE,KAAK,CAAC,EAAE;gBACnB,WAAW,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC,EACD,YAAY,EAAE,GAAG,EAAE;gBACjB,YAAY,EAAE,CAAA;YAChB,CAAC,EACD,OAAO,EAAE,KAAK,CAAC,EAAE;gBACf,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;gBAC5B,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,IAAI,GAAG;wBACX,CAAC,EAAE,KAAK,CAAC,OAAO;wBAChB,CAAC,EAAE,KAAK,CAAC,OAAO;wBAChB,EAAE,EAAE,KAAK,CAAC,EAAE;wBACZ,IAAI,EAAE,KAAK,CAAC,IAAI;qBACjB,CAAA;oBACD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;wBACjB,aAAa,CAAC,IAAI,CAAC,CAAA;oBACrB,CAAC;yBAAM,CAAC;wBACN,WAAW,CAAC,IAAI,CAAC,CAAA;oBACnB,CAAC;gBACH,CAAC;YACH,CAAC,GACD;QAGD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAC5B,6BACE,SAAS,EAAE,OAAO,CAAC,KAAK,EACxB,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,GAAG,EAAE,OAAO,CAAC,IAAI;gBACjB,KAAK,EAAE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI;gBAClC,MAAM,EAAE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI;aACpC,GACD,CACH,CAAC,CAAC,CAAC,IAAI;QAEP,OAAO,CAAC,CAAC,CAAC,CACT,oBAAC,aAAa,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAC9D,OAAO,CAAC,IAAI,CACC,CACjB,CAAC,CAAC,CAAC,IAAI,CACP,CACJ,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,eAAe,eAAe,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { MsaViewModel } from '../../model.ts';
|
|
3
|
+
/**
|
|
4
|
+
* The brush on the row scale: the whole tree drawn small above the tree panel,
|
|
5
|
+
* with the focused subtree boxed. A click focuses the subtree under the
|
|
6
|
+
* pointer, and a click inside the box clears the focus.
|
|
7
|
+
*
|
|
8
|
+
* The tree itself comes off a cached offscreen canvas, so the frames a pointer
|
|
9
|
+
* moving across it costs are a blit and two boxes.
|
|
10
|
+
*/
|
|
11
|
+
declare const TreeOverview: ({ model }: {
|
|
12
|
+
model: MsaViewModel;
|
|
13
|
+
}) => React.JSX.Element;
|
|
14
|
+
export default TreeOverview;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useTheme } from '@mui/material';
|
|
3
|
+
import { observer } from 'mobx-react';
|
|
4
|
+
import { useCanvasAutorun } from '../../useCanvasAutorun.js';
|
|
5
|
+
import { drawTreeOverview, drawTreeOverviewBox, treeOverviewImage, } from './renderTreeOverview.js';
|
|
6
|
+
/**
|
|
7
|
+
* The brush on the row scale: the whole tree drawn small above the tree panel,
|
|
8
|
+
* with the focused subtree boxed. A click focuses the subtree under the
|
|
9
|
+
* pointer, and a click inside the box clears the focus.
|
|
10
|
+
*
|
|
11
|
+
* The tree itself comes off a cached offscreen canvas, so the frames a pointer
|
|
12
|
+
* moving across it costs are a blit and two boxes.
|
|
13
|
+
*/
|
|
14
|
+
const TreeOverview = observer(function ({ model }) {
|
|
15
|
+
const { treeAreaWidth, overviewHeight, highResScaleFactor } = model;
|
|
16
|
+
const theme = useTheme();
|
|
17
|
+
const [hover, setHover] = useState();
|
|
18
|
+
const width = Math.round(treeAreaWidth * highResScaleFactor);
|
|
19
|
+
const height = Math.round(overviewHeight * highResScaleFactor);
|
|
20
|
+
const ref = useCanvasAutorun({
|
|
21
|
+
draw: ctx => {
|
|
22
|
+
ctx.resetTransform();
|
|
23
|
+
ctx.clearRect(0, 0, width, height);
|
|
24
|
+
const image = treeOverviewImage({
|
|
25
|
+
model,
|
|
26
|
+
theme,
|
|
27
|
+
width: treeAreaWidth,
|
|
28
|
+
height: overviewHeight,
|
|
29
|
+
scale: highResScaleFactor,
|
|
30
|
+
});
|
|
31
|
+
ctx.scale(highResScaleFactor, highResScaleFactor);
|
|
32
|
+
const box = { model, ctx, width: treeAreaWidth, height: overviewHeight };
|
|
33
|
+
if (image) {
|
|
34
|
+
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, box.width, box.height);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
drawTreeOverview({ ...box, theme });
|
|
38
|
+
}
|
|
39
|
+
if (hover) {
|
|
40
|
+
drawTreeOverviewBox({
|
|
41
|
+
...box,
|
|
42
|
+
rows: hover.rows,
|
|
43
|
+
color: theme.palette.text.secondary,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const focus = model.treeOverviewFocusRows;
|
|
47
|
+
if (focus) {
|
|
48
|
+
drawTreeOverviewBox({
|
|
49
|
+
...box,
|
|
50
|
+
rows: focus,
|
|
51
|
+
color: theme.palette.text.primary,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
width,
|
|
56
|
+
height,
|
|
57
|
+
deps: [model, theme, hover],
|
|
58
|
+
});
|
|
59
|
+
return (React.createElement("canvas", { "data-testid": "tree_overview", ref: ref, width: width, height: height, style: {
|
|
60
|
+
display: 'block',
|
|
61
|
+
flexShrink: 0,
|
|
62
|
+
cursor: 'pointer',
|
|
63
|
+
width: treeAreaWidth,
|
|
64
|
+
height: overviewHeight,
|
|
65
|
+
}, onMouseMove: event => {
|
|
66
|
+
const hit = model.treeOverviewHit(event.nativeEvent.offsetY);
|
|
67
|
+
setHover(prev => (prev?.id === hit?.id ? prev : hit));
|
|
68
|
+
}, onMouseLeave: () => {
|
|
69
|
+
setHover(undefined);
|
|
70
|
+
}, onClick: event => {
|
|
71
|
+
model.treeOverviewClick(event.nativeEvent.offsetY);
|
|
72
|
+
} }));
|
|
73
|
+
});
|
|
74
|
+
export default TreeOverview;
|
|
75
|
+
//# sourceMappingURL=TreeOverview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeOverview.js","sourceRoot":"","sources":["../../../src/components/tree/TreeOverview.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useTheme } from '@mui/material';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// phylogram fills it with a scale bar: without one the branch lengths are drawn
|
|
10
|
-
// to a scale nothing states.
|
|
4
|
+
import { treeScaleBarHeight as h } from '../../constants.js';
|
|
5
|
+
// The gutter above the tree panel, the width of the tree area, so the strip
|
|
6
|
+
// headers beside it start where the strips do. A phylogram fills it with a
|
|
7
|
+
// scale bar: without one the branch lengths are drawn to a scale nothing
|
|
8
|
+
// states.
|
|
11
9
|
const TreeRuler = observer(({ model }) => {
|
|
12
|
-
const { treeAreaWidth,
|
|
10
|
+
const { treeAreaWidth, marginLeft, treeScaleBar: bar } = model;
|
|
13
11
|
const theme = useTheme();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
React.createElement("text", { x: marginLeft, y: BAR_HEIGHT - 9, fontSize: 10, fill: "currentColor" }, bar.label),
|
|
18
|
-
React.createElement("path", { d: `M${marginLeft} ${BAR_HEIGHT - 7} v6 h${bar.px} v-6`, fill: "none", stroke: "currentColor" }))) : null));
|
|
12
|
+
return (React.createElement("div", { style: { flexShrink: 0, width: treeAreaWidth } }, bar ? (React.createElement("svg", { width: treeAreaWidth, height: h, style: { display: 'block' }, color: theme.palette.text.primary },
|
|
13
|
+
React.createElement("text", { x: marginLeft, y: h - 9, fontSize: 10, fill: "currentColor" }, bar.label),
|
|
14
|
+
React.createElement("path", { d: `M${marginLeft} ${h - 7} v6 h${bar.px} v-6`, fill: "none", stroke: "currentColor" }))) : null));
|
|
19
15
|
});
|
|
20
16
|
export default TreeRuler;
|
|
21
17
|
//# sourceMappingURL=TreeRuler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeRuler.js","sourceRoot":"","sources":["../../../src/components/tree/TreeRuler.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"TreeRuler.js","sourceRoot":"","sources":["../../../src/components/tree/TreeRuler.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,OAAO,EAAE,kBAAkB,IAAI,CAAC,EAAE,MAAM,oBAAoB,CAAA;AAI5D,4EAA4E;AAC5E,2EAA2E;AAC3E,yEAAyE;AACzE,UAAU;AACV,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,EAAE,KAAK,EAA2B,EAAE,EAAE;IAChE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,KAAK,CAAA;IAC9D,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;IAExB,OAAO,CACL,6BAAK,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,IAChD,GAAG,CAAC,CAAC,CAAC,CACL,6BACE,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAC3B,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO;QAEjC,8BAAM,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAC,cAAc,IAC7D,GAAG,CAAC,KAAK,CACL;QACP,8BACE,CAAC,EAAE,IAAI,UAAU,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,MAAM,EAC9C,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,GACrB,CACE,CACP,CAAC,CAAC,CAAC,IAAI,CACJ,CACP,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,eAAe,SAAS,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ResolvedClade } from '../../types.ts';
|
|
2
|
+
export declare const bracketBarWidth = 3;
|
|
3
|
+
export declare const bracketGap = 4;
|
|
4
|
+
export declare const maxCladeGutterWidth = 140;
|
|
5
|
+
/** whether a clade draws in the gutter: a bracket, or a highlight with a label */
|
|
6
|
+
export declare function cladeInGutter(clade: ResolvedClade): boolean;
|
|
7
|
+
/** the pixel height of the rows a clade covers */
|
|
8
|
+
export declare function cladeHeight(clade: ResolvedClade, rowHeight: number): number;
|
|
9
|
+
/**
|
|
10
|
+
* Whether a label reads across the rows. It does where they are taller than the
|
|
11
|
+
* font, and runs up the bar where they are not.
|
|
12
|
+
*/
|
|
13
|
+
export declare function cladeLabelHorizontal(clade: ResolvedClade, rowHeight: number, fontSize: number): boolean;
|
|
14
|
+
/** the px between the left edge of the gutter and the label's own column */
|
|
15
|
+
export declare function cladeLabelOffset(clade: ResolvedClade): number;
|
|
16
|
+
/**
|
|
17
|
+
* The gutter the bracket mark needs at the right of the tree area: the bar plus
|
|
18
|
+
* the widest label at the tree font, zero where no clade draws there, and at
|
|
19
|
+
* most maxCladeGutterWidth.
|
|
20
|
+
*/
|
|
21
|
+
export declare function cladeGutterWidth({ clades, rowHeight, fontSize, }: {
|
|
22
|
+
clades: ResolvedClade[];
|
|
23
|
+
rowHeight: number;
|
|
24
|
+
fontSize: number;
|
|
25
|
+
}): number;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { measureTextCanvas } from '../../measureTextCanvas.js';
|
|
2
|
+
// The bar of the bracket mark and the space around it, in px.
|
|
3
|
+
export const bracketBarWidth = 3;
|
|
4
|
+
export const bracketGap = 4;
|
|
5
|
+
// The widest gutter the bracket mark takes. A longer label is clipped, so one
|
|
6
|
+
// long name cannot squeeze the tree it annotates.
|
|
7
|
+
export const maxCladeGutterWidth = 140;
|
|
8
|
+
/** whether a clade draws in the gutter: a bracket, or a highlight with a label */
|
|
9
|
+
export function cladeInGutter(clade) {
|
|
10
|
+
return (clade.mark === 'bracket' || (clade.mark === 'highlight' && !!clade.label));
|
|
11
|
+
}
|
|
12
|
+
/** the pixel height of the rows a clade covers */
|
|
13
|
+
export function cladeHeight(clade, rowHeight) {
|
|
14
|
+
return (clade.rows[1] - clade.rows[0] + 1) * rowHeight;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Whether a label reads across the rows. It does where they are taller than the
|
|
18
|
+
* font, and runs up the bar where they are not.
|
|
19
|
+
*/
|
|
20
|
+
export function cladeLabelHorizontal(clade, rowHeight, fontSize) {
|
|
21
|
+
return cladeHeight(clade, rowHeight) >= fontSize;
|
|
22
|
+
}
|
|
23
|
+
/** the px between the left edge of the gutter and the label's own column */
|
|
24
|
+
export function cladeLabelOffset(clade) {
|
|
25
|
+
return clade.mark === 'bracket'
|
|
26
|
+
? bracketGap + bracketBarWidth + bracketGap
|
|
27
|
+
: bracketGap;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The gutter the bracket mark needs at the right of the tree area: the bar plus
|
|
31
|
+
* the widest label at the tree font, zero where no clade draws there, and at
|
|
32
|
+
* most maxCladeGutterWidth.
|
|
33
|
+
*/
|
|
34
|
+
export function cladeGutterWidth({ clades, rowHeight, fontSize, }) {
|
|
35
|
+
let widest = 0;
|
|
36
|
+
for (const clade of clades) {
|
|
37
|
+
if (!cladeInGutter(clade)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const text = clade.label
|
|
41
|
+
? cladeLabelHorizontal(clade, rowHeight, fontSize)
|
|
42
|
+
? measureTextCanvas(clade.label, fontSize)
|
|
43
|
+
: fontSize
|
|
44
|
+
: 0;
|
|
45
|
+
widest = Math.max(widest, cladeLabelOffset(clade) + text + bracketGap);
|
|
46
|
+
}
|
|
47
|
+
// a whole pixel, so the room the renderers measure against covers the label
|
|
48
|
+
// the measurement here sized the gutter for
|
|
49
|
+
return Math.min(maxCladeGutterWidth, Math.ceil(widest));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=cladeBrackets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cladeBrackets.js","sourceRoot":"","sources":["../../../src/components/tree/cladeBrackets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAI9D,8DAA8D;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAA;AAChC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAA;AAE3B,8EAA8E;AAC9E,kDAAkD;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAEtC,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,KAAoB;IAChD,OAAO,CACL,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAC1E,CAAA;AACH,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,WAAW,CAAC,KAAoB,EAAE,SAAiB;IACjE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAA;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAoB,EACpB,SAAiB,EACjB,QAAgB;IAEhB,OAAO,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,QAAQ,CAAA;AAClD,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,KAAoB;IACnD,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS;QAC7B,CAAC,CAAC,UAAU,GAAG,eAAe,GAAG,UAAU;QAC3C,CAAC,CAAC,UAAU,CAAA;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,SAAS,EACT,QAAQ,GAKT;IACC,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,SAAQ;QACV,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK;YACtB,CAAC,CAAC,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;gBAChD,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC;gBAC1C,CAAC,CAAC,QAAQ;YACZ,CAAC,CAAC,CAAC,CAAA;QACL,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,UAAU,CAAC,CAAA;IACxE,CAAC;IACD,4EAA4E;IAC5E,4CAA4C;IAC5C,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;AACzD,CAAC"}
|
|
@@ -6,8 +6,8 @@ import { DialogContent } from '@mui/material';
|
|
|
6
6
|
import { observer } from 'mobx-react';
|
|
7
7
|
import SequenceTextArea from '../../SequenceTextArea.js';
|
|
8
8
|
const TreeNodeInfoDialog = observer(function ({ info, model, nodeName, onClose, }) {
|
|
9
|
-
const {
|
|
10
|
-
const metadata =
|
|
9
|
+
const { rows } = model;
|
|
10
|
+
const metadata = model.rowDataOf(nodeName);
|
|
11
11
|
const res = rows.find(f => f[0] === nodeName);
|
|
12
12
|
return (React.createElement(Dialog, { onClose: () => {
|
|
13
13
|
onClose();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeNodeInfoDialog.js","sourceRoot":"","sources":["../../../../src/components/tree/dialogs/TreeNodeInfoDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,UAAU,MAAM,8DAA8D,CAAA;AACrF,OAAO,QAAQ,MAAM,4DAA4D,CAAA;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,OAAO,gBAAgB,MAAM,2BAA4B,CAAA;AAIzD,MAAM,kBAAkB,GAAG,QAAQ,CAAC,UAAU,EAC5C,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,OAAO,GAMR;IACC,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"TreeNodeInfoDialog.js","sourceRoot":"","sources":["../../../../src/components/tree/dialogs/TreeNodeInfoDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,UAAU,MAAM,8DAA8D,CAAA;AACrF,OAAO,QAAQ,MAAM,4DAA4D,CAAA;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,OAAO,gBAAgB,MAAM,2BAA4B,CAAA;AAIzD,MAAM,kBAAkB,GAAG,QAAQ,CAAC,UAAU,EAC5C,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,OAAO,GAMR;IACC,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAA;IACtB,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAA;IAC7C,OAAO,CACL,oBAAC,MAAM,IACL,OAAO,EAAE,GAAG,EAAE;YACZ,OAAO,EAAE,CAAA;QACX,CAAC,EACD,IAAI,QACJ,KAAK,EAAC,gBAAgB,EACtB,QAAQ,EAAC,IAAI;QAEb,oBAAC,aAAa;YACZ,oBAAC,QAAQ,IAAC,KAAK,EAAC,YAAY;gBAC1B,oBAAC,UAAU,IAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAI,CACxC;YACX,oBAAC,QAAQ,IAAC,KAAK,EAAC,UAAU,IACvB,GAAG,CAAC,CAAC,CAAC,CACL,oBAAC,gBAAgB,IAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAI,CACjC,CAAC,CAAC,CAAC,CACF,sDAA6B,CAC9B,CACQ;YACV,QAAQ,CAAC,CAAC,CAAC,CACV,oBAAC,QAAQ,IAAC,KAAK,EAAC,gBAAgB;gBAC9B,oBAAC,UAAU,IAAC,UAAU,EAAE,QAAQ,GAAI,CAC3B,CACZ,CAAC,CAAC,CAAC,IAAI,CACM,CACT,CACV,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,eAAe,kBAAkB,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { calcDepthToLeaf } from '../../hierarchy.js';
|
|
1
|
+
import { calcDepthToLeaf, nodeCoveringRows } from '../../hierarchy.js';
|
|
2
2
|
import { setFontSize } from '../../setFontSize.js';
|
|
3
3
|
import { getVisibleLeaves } from '../getVisibleLeaves.js';
|
|
4
4
|
import { drawHighlightLabel, highlightLabelHeight, highlightRowFill, } from '../msa/renderHighlights.js';
|
|
5
|
+
import { bracketBarWidth, bracketGap } from './cladeBrackets.js';
|
|
5
6
|
const radius = 2.5;
|
|
6
7
|
const d = radius * 2;
|
|
7
8
|
// How far outside a block a node's row-center can sit and still draw into it: a
|
|
@@ -17,12 +18,12 @@ function inYBlock(y, offsetY, by, pad) {
|
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* Every node whose subtree reaches into the block, skipping the subtrees that
|
|
20
|
-
* do not. A subtree's leaves are one contiguous run of rows
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* do not. A subtree's leaves are one contiguous run of rows, recorded by
|
|
22
|
+
* clusterLayout as xMin/xMax, so a block of a 200k-tip tree visits a few dozen
|
|
23
|
+
* nodes.
|
|
23
24
|
*
|
|
24
|
-
* A link out of a visited node is
|
|
25
|
-
*
|
|
25
|
+
* A link out of a visited node is drawn even when its child is pruned, so a
|
|
26
|
+
* branch crossing the block stays visible.
|
|
26
27
|
*/
|
|
27
28
|
function forEachNodeInBlock(root, offsetY, by, pad, cb) {
|
|
28
29
|
const top = offsetY - pad;
|
|
@@ -57,9 +58,10 @@ export function getNodeX(node, showBranchLen, tipX, maxDepthToLeaf) {
|
|
|
57
58
|
return ((maxDepthToLeaf - depthToLeaf) / maxDepthToLeaf) * tipX;
|
|
58
59
|
}
|
|
59
60
|
function renderTree({ offsetY, ctx, model, theme, tipX, maxDepthToLeaf, blockSizeYOverride, }) {
|
|
60
|
-
const { hierarchy, showBranchLenEffective: showBranchLen, blockSize } = model;
|
|
61
|
+
const { hierarchy, showBranchLenEffective: showBranchLen, blockSize, branchColors, } = model;
|
|
61
62
|
const by = blockSizeYOverride ?? blockSize;
|
|
62
|
-
|
|
63
|
+
const defaultStroke = theme.palette.text.primary;
|
|
64
|
+
ctx.strokeStyle = defaultStroke;
|
|
63
65
|
forEachNodeInBlock(hierarchy, offsetY, by, blockPad(model), source => {
|
|
64
66
|
const sy = source.x;
|
|
65
67
|
const sx = getNodeX(source, showBranchLen, tipX, maxDepthToLeaf);
|
|
@@ -78,6 +80,10 @@ function renderTree({ offsetY, ctx, model, theme, tipX, maxDepthToLeaf, blockSiz
|
|
|
78
80
|
// optimization that allows us to skip drawing most tree links outside the
|
|
79
81
|
// block
|
|
80
82
|
if (offsetY + by >= y1 && y2 >= offsetY) {
|
|
83
|
+
// the edge belongs to the child, whose id the branch channel colors
|
|
84
|
+
if (branchColors) {
|
|
85
|
+
ctx.strokeStyle = branchColors.get(target.data.id) ?? defaultStroke;
|
|
86
|
+
}
|
|
81
87
|
ctx.beginPath();
|
|
82
88
|
ctx.moveTo(sx, sy);
|
|
83
89
|
ctx.lineTo(sx, ty);
|
|
@@ -86,9 +92,10 @@ function renderTree({ offsetY, ctx, model, theme, tipX, maxDepthToLeaf, blockSiz
|
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
94
|
});
|
|
95
|
+
ctx.strokeStyle = defaultStroke;
|
|
89
96
|
}
|
|
90
97
|
function renderCollapsedTriangles({ ctx, clickMap, offsetY, model, theme, tipX, maxDepthToLeaf, blockSizeYOverride, collapsedSet, }) {
|
|
91
|
-
const { hierarchy, showBranchLenEffective: showBranchLen, blockSize, rowHeight, fontSize, marginLeft: ml, } = model;
|
|
98
|
+
const { hierarchy, showBranchLenEffective: showBranchLen, blockSize, rowHeight, fontSize, marginLeft: ml, branchColors, } = model;
|
|
92
99
|
// nothing collapsed is the common case, and the traversal below visits every
|
|
93
100
|
// node in the tree on every block of every redraw
|
|
94
101
|
if (collapsedSet.size === 0) {
|
|
@@ -111,7 +118,7 @@ function renderCollapsedTriangles({ ctx, clickMap, offsetY, model, theme, tipX,
|
|
|
111
118
|
ctx.lineTo(baseX, y - halfHeight);
|
|
112
119
|
ctx.lineTo(baseX, y + halfHeight);
|
|
113
120
|
ctx.closePath();
|
|
114
|
-
ctx.fillStyle = theme.palette.action.disabled;
|
|
121
|
+
ctx.fillStyle = branchColors?.get(id) ?? theme.palette.action.disabled;
|
|
115
122
|
ctx.fill();
|
|
116
123
|
ctx.strokeStyle = theme.palette.text.primary;
|
|
117
124
|
ctx.stroke();
|
|
@@ -141,7 +148,7 @@ function renderCollapsedTriangles({ ctx, clickMap, offsetY, model, theme, tipX,
|
|
|
141
148
|
}
|
|
142
149
|
// the bubbles are click targets whether or not they are painted, so this pass
|
|
143
150
|
// always runs and `draw` only decides whether it also strokes the circles
|
|
144
|
-
function renderNodeBubbles({ ctx, clickMap, offsetY, model, tipX, maxDepthToLeaf, blockSizeYOverride, collapsedSet, draw, }) {
|
|
151
|
+
function renderNodeBubbles({ ctx, theme, clickMap, offsetY, model, tipX, maxDepthToLeaf, blockSizeYOverride, collapsedSet, draw, }) {
|
|
145
152
|
const { hierarchy, showBranchLenEffective: showBranchLen, blockSize, marginLeft: ml, } = model;
|
|
146
153
|
const by = blockSizeYOverride ?? blockSize;
|
|
147
154
|
forEachNodeInBlock(hierarchy, offsetY, by, blockPad(model), node => {
|
|
@@ -155,8 +162,10 @@ function renderNodeBubbles({ ctx, clickMap, offsetY, model, tipX, maxDepthToLeaf
|
|
|
155
162
|
if (node.height >= 1 && inYBlock(y, offsetY, by, blockPad(model))) {
|
|
156
163
|
const isCollapsed = collapsedSet.has(id);
|
|
157
164
|
if (draw) {
|
|
158
|
-
ctx.strokeStyle =
|
|
159
|
-
ctx.fillStyle = isCollapsed
|
|
165
|
+
ctx.strokeStyle = theme.palette.text.primary;
|
|
166
|
+
ctx.fillStyle = isCollapsed
|
|
167
|
+
? theme.palette.text.primary
|
|
168
|
+
: theme.palette.background.default;
|
|
160
169
|
ctx.beginPath();
|
|
161
170
|
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
|
162
171
|
ctx.fill();
|
|
@@ -178,8 +187,33 @@ function renderNodeBubbles({ ctx, clickMap, offsetY, model, tipX, maxDepthToLeaf
|
|
|
178
187
|
}
|
|
179
188
|
});
|
|
180
189
|
}
|
|
190
|
+
// The label a newick file puts on an internal node, which is where a bootstrap
|
|
191
|
+
// or posterior support value lands: ((A,B)95,(C,D)80);. withId falls back to the
|
|
192
|
+
// path-derived id for an unnamed node, so a node whose name is its id has no
|
|
193
|
+
// label of its own and an ungated pass prints node-0-1-1 across the tree.
|
|
194
|
+
function renderNodeLabels({ ctx, theme, offsetY, model, tipX, maxDepthToLeaf, blockSizeYOverride, }) {
|
|
195
|
+
const { hierarchy, showBranchLenEffective: showBranchLen, blockSize, fontSize, } = model;
|
|
196
|
+
const by = blockSizeYOverride ?? blockSize;
|
|
197
|
+
const pad = blockPad(model);
|
|
198
|
+
setFontSize(ctx, Math.max(6, fontSize - 3));
|
|
199
|
+
ctx.fillStyle = theme.palette.text.primary;
|
|
200
|
+
ctx.textAlign = 'right';
|
|
201
|
+
forEachNodeInBlock(hierarchy, offsetY, by, pad, node => {
|
|
202
|
+
const { id, name } = node.data;
|
|
203
|
+
if (node.height < 1 || name === id) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const x = getNodeX(node, showBranchLen, tipX, maxDepthToLeaf);
|
|
207
|
+
const y = node.x;
|
|
208
|
+
if (x !== undefined && inYBlock(y, offsetY, by, pad)) {
|
|
209
|
+
ctx.fillText(name, x - radius - 1, y - radius - 1);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
ctx.textAlign = 'start';
|
|
213
|
+
setFontSize(ctx, fontSize);
|
|
214
|
+
}
|
|
181
215
|
function renderTreeLabels({ theme, model, offsetY, ctx, clickMap, tipX, maxDepthToLeaf, blockSizeYOverride, collapsedSet, }) {
|
|
182
|
-
const { fontSize, showBranchLenEffective: showBranchLen,
|
|
216
|
+
const { fontSize, showBranchLenEffective: showBranchLen, tipLabelColors, blockSize, labelsAlignRight, drawTree, treeAreaWidthMinusMargin, marginLeft, noTree, labelWidthMap, labelWidthScale, } = model;
|
|
183
217
|
const by = blockSizeYOverride ?? blockSize;
|
|
184
218
|
// labels only exist for leaves, which are laid out top to bottom, so take the
|
|
185
219
|
// same slice the MSA renderer uses instead of walking every tip per block
|
|
@@ -197,7 +231,7 @@ function renderTreeLabels({ theme, model, offsetY, ctx, clickMap, tipX, maxDepth
|
|
|
197
231
|
for (const node of visibleLeaves) {
|
|
198
232
|
const { data: { name, id }, } = node;
|
|
199
233
|
const y = node.x;
|
|
200
|
-
const displayName =
|
|
234
|
+
const displayName = model.rowDataOf(name)?.genome || name;
|
|
201
235
|
// a collapsed clade is drawn as a triangle + tip count; suppress its leaf
|
|
202
236
|
// label when the "name" is just the auto-generated internal-node id
|
|
203
237
|
const isAnonymousCollapsed = collapsedSet.has(id) && name === id;
|
|
@@ -212,7 +246,7 @@ function renderTreeLabels({ theme, model, offsetY, ctx, clickMap, tipX, maxDepth
|
|
|
212
246
|
const width = measured === undefined
|
|
213
247
|
? ctx.measureText(displayName).width
|
|
214
248
|
: measured * labelWidthScale;
|
|
215
|
-
ctx.fillStyle = theme.palette.text.primary;
|
|
249
|
+
ctx.fillStyle = tipLabelColors?.get(name) ?? theme.palette.text.primary;
|
|
216
250
|
if (labelsAlignRight) {
|
|
217
251
|
const smallPadding = 2;
|
|
218
252
|
const offset = treeAreaWidthMinusMargin - smallPadding;
|
|
@@ -254,22 +288,101 @@ function renderTreeLabels({ theme, model, offsetY, ctx, clickMap, tipX, maxDepth
|
|
|
254
288
|
}
|
|
255
289
|
ctx.setLineDash([]);
|
|
256
290
|
}
|
|
257
|
-
// the row
|
|
258
|
-
//
|
|
291
|
+
// the first row of a set, as a loop: spreading into Math.min passes one
|
|
292
|
+
// argument per row, and the ~125k argument limit throws RangeError on a set
|
|
293
|
+
// covering the 230k-tip COVID tree
|
|
294
|
+
function firstRow(rowIndices) {
|
|
295
|
+
let min = Infinity;
|
|
296
|
+
for (const index of rowIndices) {
|
|
297
|
+
if (index < min) {
|
|
298
|
+
min = index;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return min;
|
|
302
|
+
}
|
|
303
|
+
// the highlight mark of the `clades` layer: a translucent rectangle from the
|
|
304
|
+
// clade's common ancestor to the right edge of the tree area, under the
|
|
305
|
+
// branches. A clade spanning hundreds of rows crosses several blocks, so each
|
|
306
|
+
// block draws the whole rectangle and the canvas takes the part that is its.
|
|
307
|
+
function renderCladeHighlights({ ctx, model, offsetY, tipX, maxDepthToLeaf, blockSizeYOverride, }) {
|
|
308
|
+
const { resolvedClades, hierarchy, showBranchLenEffective: showBranchLen, rowHeight, treeAreaWidth, marginLeft, blockSize, } = model;
|
|
309
|
+
const by = blockSizeYOverride ?? blockSize;
|
|
310
|
+
const right = treeAreaWidth - marginLeft;
|
|
311
|
+
for (const { rows, color, mark } of resolvedClades) {
|
|
312
|
+
if (mark !== 'highlight') {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const top = rows[0] * rowHeight;
|
|
316
|
+
const bottom = (rows[1] + 1) * rowHeight;
|
|
317
|
+
if (bottom < offsetY || top > offsetY + by) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const node = nodeCoveringRows(hierarchy, top + rowHeight / 2, bottom - rowHeight / 2);
|
|
321
|
+
const x = getNodeX(node, showBranchLen, tipX, maxDepthToLeaf) ?? 0;
|
|
322
|
+
ctx.fillStyle = color;
|
|
323
|
+
ctx.fillRect(x, top, right - x, bottom - top);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// the bar of the bracket mark, in the gutter `cladeGutterWidth` reserves at the
|
|
327
|
+
// right of the tree area. The label beside it is DOM text on screen and a
|
|
328
|
+
// <text> in the export, since the canvas layer has no rotation.
|
|
329
|
+
function renderCladeBrackets({ ctx, model, theme, offsetY, blockSizeYOverride, }) {
|
|
330
|
+
const { resolvedClades, cladeGutterWidth, rowHeight, treeAreaWidth, marginLeft, blockSize, } = model;
|
|
331
|
+
if (cladeGutterWidth === 0) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const by = blockSizeYOverride ?? blockSize;
|
|
335
|
+
const x = treeAreaWidth -
|
|
336
|
+
marginLeft -
|
|
337
|
+
cladeGutterWidth +
|
|
338
|
+
bracketGap +
|
|
339
|
+
bracketBarWidth / 2;
|
|
340
|
+
ctx.lineWidth = bracketBarWidth;
|
|
341
|
+
for (const { rows, mark, markColor } of resolvedClades) {
|
|
342
|
+
const top = rows[0] * rowHeight + 1;
|
|
343
|
+
const bottom = (rows[1] + 1) * rowHeight - 1;
|
|
344
|
+
if (mark !== 'bracket' || bottom < offsetY || top > offsetY + by) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
ctx.strokeStyle = markColor ?? theme.palette.text.primary;
|
|
348
|
+
ctx.beginPath();
|
|
349
|
+
ctx.moveTo(x, top);
|
|
350
|
+
ctx.lineTo(x, bottom);
|
|
351
|
+
ctx.stroke();
|
|
352
|
+
}
|
|
353
|
+
ctx.lineWidth = 1;
|
|
354
|
+
ctx.strokeStyle = theme.palette.text.primary;
|
|
355
|
+
}
|
|
356
|
+
// the `rowTint` encoding and the row sets of `highlights`, washed across the
|
|
357
|
+
// tree area under the labels, with each set's label in the gutter at its first
|
|
358
|
+
// row
|
|
259
359
|
function renderRowHighlights({ ctx, model, theme, offsetY, blockSizeYOverride, }) {
|
|
260
|
-
const { resolvedHighlights, rowHeight, treeAreaWidth, marginLeft, blockSize, } = model;
|
|
360
|
+
const { resolvedHighlights, rowTints, rowHeight, treeAreaWidth, marginLeft, blockSize, } = model;
|
|
261
361
|
const by = blockSizeYOverride ?? blockSize;
|
|
262
362
|
const pad = blockPad(model);
|
|
263
363
|
const placed = [];
|
|
364
|
+
const fillRow = (index) => {
|
|
365
|
+
ctx.fillRect(-marginLeft, index * rowHeight, treeAreaWidth, rowHeight);
|
|
366
|
+
};
|
|
367
|
+
if (rowTints) {
|
|
368
|
+
const top = Math.max(0, Math.floor((offsetY - pad) / rowHeight));
|
|
369
|
+
const bottom = Math.min(rowTints.length - 1, Math.ceil((offsetY + by + pad) / rowHeight));
|
|
370
|
+
for (let index = top; index <= bottom; index++) {
|
|
371
|
+
const color = rowTints[index];
|
|
372
|
+
if (color) {
|
|
373
|
+
ctx.fillStyle = color;
|
|
374
|
+
fillRow(index);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
264
378
|
for (const { rowIndices, label, color } of resolvedHighlights) {
|
|
265
379
|
ctx.fillStyle = color ?? highlightRowFill;
|
|
266
380
|
for (const index of rowIndices) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
ctx.fillRect(-marginLeft, y, treeAreaWidth, rowHeight);
|
|
381
|
+
if (inYBlock(index * rowHeight + rowHeight / 2, offsetY, by, pad)) {
|
|
382
|
+
fillRow(index);
|
|
270
383
|
}
|
|
271
384
|
}
|
|
272
|
-
const first =
|
|
385
|
+
const first = firstRow(rowIndices);
|
|
273
386
|
if (label &&
|
|
274
387
|
rowIndices.length &&
|
|
275
388
|
inYBlock(first * rowHeight, offsetY, by, pad)) {
|
|
@@ -289,7 +402,7 @@ function renderRowHighlights({ ctx, model, theme, offsetY, blockSizeYOverride, }
|
|
|
289
402
|
export function renderTreeCanvas({ model, clickMap, ctx, offsetY, theme, highResScaleFactorOverride, blockSizeYOverride, }) {
|
|
290
403
|
clickMap?.clear();
|
|
291
404
|
// Defer the finish call until after all inserts are done
|
|
292
|
-
const { noTree, drawTree, drawNodeBubbles, highResScaleFactor, fontSize, showTreeText, marginLeft,
|
|
405
|
+
const { noTree, drawTree, drawNodeBubbles, drawNodeLabels, highResScaleFactor, fontSize, showTreeText, marginLeft,
|
|
293
406
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
294
407
|
rowHeight: _rowHeight, // read so the draw autorun re-fires after a zoom change
|
|
295
408
|
} = model;
|
|
@@ -310,6 +423,14 @@ export function renderTreeCanvas({ model, clickMap, ctx, offsetY, theme, highRes
|
|
|
310
423
|
// built once for all three passes: each of them asks about a different node on
|
|
311
424
|
// every block of every redraw, and `collapsed` is a list
|
|
312
425
|
const collapsedSet = new Set(model.collapsed);
|
|
426
|
+
renderCladeHighlights({
|
|
427
|
+
ctx,
|
|
428
|
+
model,
|
|
429
|
+
offsetY,
|
|
430
|
+
tipX,
|
|
431
|
+
maxDepthToLeaf,
|
|
432
|
+
blockSizeYOverride,
|
|
433
|
+
});
|
|
313
434
|
renderRowHighlights({ ctx, model, theme, offsetY, blockSizeYOverride });
|
|
314
435
|
setFontSize(ctx, fontSize);
|
|
315
436
|
if (!noTree && drawTree) {
|
|
@@ -335,6 +456,7 @@ export function renderTreeCanvas({ model, clickMap, ctx, offsetY, theme, highRes
|
|
|
335
456
|
});
|
|
336
457
|
renderNodeBubbles({
|
|
337
458
|
ctx,
|
|
459
|
+
theme,
|
|
338
460
|
offsetY,
|
|
339
461
|
clickMap,
|
|
340
462
|
model,
|
|
@@ -344,6 +466,17 @@ export function renderTreeCanvas({ model, clickMap, ctx, offsetY, theme, highRes
|
|
|
344
466
|
collapsedSet,
|
|
345
467
|
draw: drawNodeBubbles,
|
|
346
468
|
});
|
|
469
|
+
if (drawNodeLabels) {
|
|
470
|
+
renderNodeLabels({
|
|
471
|
+
ctx,
|
|
472
|
+
theme,
|
|
473
|
+
offsetY,
|
|
474
|
+
model,
|
|
475
|
+
tipX,
|
|
476
|
+
maxDepthToLeaf,
|
|
477
|
+
blockSizeYOverride,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
347
480
|
}
|
|
348
481
|
if (showTreeText) {
|
|
349
482
|
renderTreeLabels({
|
|
@@ -358,6 +491,7 @@ export function renderTreeCanvas({ model, clickMap, ctx, offsetY, theme, highRes
|
|
|
358
491
|
collapsedSet,
|
|
359
492
|
});
|
|
360
493
|
}
|
|
494
|
+
renderCladeBrackets({ ctx, model, theme, offsetY, blockSizeYOverride });
|
|
361
495
|
// Finish the index so it's ready for queries
|
|
362
496
|
clickMap?.finish();
|
|
363
497
|
}
|