react-msaview 6.5.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 +13 -11
- package/bundle/index.js +122 -112
- 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 +2 -2
- 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 +69 -4
- package/dist/components/MSAViewer.js +201 -8
- 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.d.ts +3 -3
- package/dist/components/ResizeHandles.js +13 -24
- 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/{InterProScanDialog.d.ts → AnnotationFileDialog.d.ts} +2 -2
- package/dist/components/dialogs/AnnotationFileDialog.js +92 -0
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -0
- package/dist/components/dialogs/ExportSVGDialog.js +5 -3
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +13 -9
- 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 +7 -0
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderStatusArea.js +0 -2
- package/dist/components/header/HeaderStatusArea.js.map +1 -1
- package/dist/components/header/LoadWarnings.d.ts +6 -0
- package/dist/components/header/LoadWarnings.js +35 -0
- package/dist/components/header/LoadWarnings.js.map +1 -0
- package/dist/components/header/MultiAlignmentSelector.js +0 -0
- package/dist/components/header/MultiAlignmentSelector.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 +7 -1
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/UnshareableDataWarning.d.ts +6 -0
- package/dist/components/header/UnshareableDataWarning.js +40 -0
- package/dist/components/header/UnshareableDataWarning.js.map +1 -0
- package/dist/components/header/ZoomControls.js +49 -6
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/getAnnotationsMenu.js +11 -14
- 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/ImportForm.js +1 -1
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/ImportFormExamples.js +2 -6
- package/dist/components/import/ImportFormExamples.js.map +1 -1
- package/dist/components/import/util.js +2 -0
- 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 +17 -20
- 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 +30 -27
- package/dist/components/msa/msaRaster.js +193 -90
- 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 +44 -4
- package/dist/components/msa/renderHighlights.js +99 -11
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +1 -2
- package/dist/components/msa/renderMSABlock.js +28 -31
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +9 -30
- package/dist/components/msa/renderMSAMouseover.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/renderCtx.d.ts +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 +13 -16
- 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 +30 -12
- package/dist/components/tracks/drawTracks.js +127 -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 +6 -4
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +6 -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 +11 -5
- 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.d.ts +0 -1
- package/dist/components/tree/renderTreeCanvas.js +221 -47
- 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/scaleBar.d.ts +10 -0
- package/dist/components/tree/scaleBar.js +24 -0
- package/dist/components/tree/scaleBar.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 +18 -1
- package/dist/constants.js +51 -8
- 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/fetchUtils.d.ts +7 -7
- package/dist/fetchUtils.js +4 -32
- package/dist/fetchUtils.js.map +1 -1
- 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 +29 -1
- package/dist/hierarchy.js +82 -19
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/model/DataModel.js +4 -4
- 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 +792 -259
- package/dist/model.js +2092 -675
- 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 +15 -13
- package/dist/renderToStaticMarkup.js +37 -25
- package/dist/renderToStaticMarkup.js.map +1 -1
- package/dist/renderToSvg.js +198 -68
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +26 -28
- package/dist/rowCoordinateCalculations.js +13 -39
- 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 +5 -4
- 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 +306 -124
- package/dist/types.d.ts +295 -3
- 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 +10 -4
- package/dist/util.js +37 -10
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wuss.d.ts +14 -0
- package/dist/wuss.js +55 -0
- package/dist/wuss.js.map +1 -0
- package/package.json +23 -14
- package/src/colorSchemes.ts +1 -2
- package/src/columnStats.ts +58 -0
- package/src/components/DragHandle.tsx +18 -7
- package/src/components/Loading.tsx +14 -8
- package/src/components/MSAView.tsx +19 -5
- package/src/components/MSAViewer.tsx +326 -10
- package/src/components/PortalTooltip.tsx +47 -0
- package/src/components/ResizeHandles.tsx +21 -36
- package/src/components/SequenceTextArea.tsx +3 -3
- package/src/components/Track.tsx +55 -18
- package/src/components/dialogs/AnnotationFileDialog.tsx +162 -0
- package/src/components/dialogs/ExportSVGDialog.tsx +7 -2
- package/src/components/dialogs/FeatureDialog.tsx +13 -9
- package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
- package/src/components/header/GappynessSlider.tsx +6 -11
- package/src/components/header/Header.tsx +10 -0
- package/src/components/header/HeaderStatusArea.tsx +0 -5
- package/src/components/header/LoadWarnings.tsx +48 -0
- package/src/components/header/MultiAlignmentSelector.tsx +0 -0
- package/src/components/header/ScrollZoomIcons.tsx +52 -0
- package/src/components/header/TreeSettingsMenu.tsx +7 -1
- package/src/components/header/UnshareableDataWarning.tsx +56 -0
- package/src/components/header/ZoomControls.tsx +74 -14
- package/src/components/header/getAnnotationsMenu.ts +13 -17
- package/src/components/header/settingsMenuItems.ts +11 -6
- package/src/components/import/ImportForm.tsx +2 -2
- package/src/components/import/ImportFormExamples.tsx +4 -8
- package/src/components/import/util.ts +2 -0
- 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 +17 -21
- 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 +236 -88
- package/src/components/msa/packDomainLanes.ts +36 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +69 -123
- package/src/components/msa/renderHighlights.ts +141 -10
- package/src/components/msa/renderMSABlock.ts +29 -38
- package/src/components/msa/renderMSAMouseover.ts +9 -39
- package/src/components/msa/tileColor.ts +4 -7
- package/src/components/msa/useMsaBlockMouse.ts +6 -6
- package/src/components/renderCtx.ts +1 -0
- 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 +13 -18
- package/src/components/tracks/TrackTooltipContent.tsx +156 -0
- package/src/components/tracks/drawTracks.ts +169 -38
- package/src/components/tracks/useTrackHover.ts +42 -0
- package/src/components/tree/CladeLabels.tsx +77 -0
- package/src/components/tree/TreeCanvas.tsx +4 -3
- package/src/components/tree/TreeCanvasBlock.tsx +10 -32
- package/src/components/tree/TreeOverview.tsx +106 -0
- package/src/components/tree/TreeRuler.tsx +29 -5
- package/src/components/tree/cladeBrackets.ts +73 -0
- package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
- package/src/components/tree/renderTreeCanvas.ts +312 -48
- package/src/components/tree/renderTreeOverview.ts +212 -0
- package/src/components/tree/scaleBar.ts +23 -0
- package/src/components/tree/useTreeHover.ts +2 -4
- package/src/constants.ts +61 -8
- package/src/createPaletteMap.ts +10 -9
- package/src/element.ts +321 -0
- package/src/featureFields.ts +19 -0
- package/src/fetchUtils.ts +7 -40
- package/src/flatToTree.ts +1 -2
- package/src/ggplotPalettes.ts +60 -0
- package/src/hierarchy.ts +97 -20
- package/src/index.ts +46 -6
- package/src/model/DataModel.ts +5 -4
- package/src/model/msaModel.ts +3 -2
- package/src/model/treeModel.ts +42 -0
- package/src/model.ts +2492 -793
- package/src/mount.tsx +32 -0
- package/src/neighborJoining.ts +8 -32
- package/src/rawImports.d.ts +9 -0
- package/src/renderToStaticMarkup.ts +41 -25
- package/src/renderToSvg.tsx +365 -92
- package/src/rowCoordinateCalculations.ts +16 -62
- package/src/scales.ts +47 -0
- package/src/seqPosToGlobalCol.ts +5 -4
- package/src/sequenceLogo.ts +6 -11
- package/src/stripDefault.ts +7 -14
- package/src/types.ts +352 -12
- package/src/umd.ts +4 -2
- package/src/useCanvasAutorun.ts +5 -10
- package/src/useMsaSvgFigure.ts +72 -0
- package/src/util.ts +40 -14
- package/src/version.ts +1 -1
- package/src/wuss.ts +62 -0
- package/dist/components/dialogs/InterProScanDialog.js +0 -137
- package/dist/components/dialogs/InterProScanDialog.js.map +0 -1
- package/dist/components/dialogs/InterProScanFileDialog.d.ts +0 -7
- package/dist/components/dialogs/InterProScanFileDialog.js +0 -64
- package/dist/components/dialogs/InterProScanFileDialog.js.map +0 -1
- package/dist/components/msa/ColumnStats.js +0 -42
- package/dist/components/msa/ColumnStats.js.map +0 -1
- package/dist/launchInterProScan.d.ts +0 -13
- package/dist/launchInterProScan.js +0 -110
- package/dist/launchInterProScan.js.map +0 -1
- package/dist/useColorContrast.d.ts +0 -10
- package/dist/useColorContrast.js +0 -13
- package/dist/useColorContrast.js.map +0 -1
- package/src/clustalX.test.ts +0 -74
- package/src/colSpace.test.ts +0 -67
- package/src/collapseLogic.test.ts +0 -115
- package/src/colorSchemes.docs.test.ts +0 -57
- package/src/columnCounts.test.ts +0 -73
- package/src/columnStats.test.ts +0 -94
- package/src/columnTracks.test.ts +0 -97
- package/src/components/dialogs/InterProScanDialog.tsx +0 -214
- package/src/components/dialogs/InterProScanFileDialog.tsx +0 -138
- package/src/components/minimap/minimapLayout.test.ts +0 -71
- package/src/components/msa/ColumnStats.tsx +0 -54
- package/src/components/msa/msaRaster.test.ts +0 -145
- package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +0 -51
- package/src/components/tree/renderTreeCanvas.test.ts +0 -296
- package/src/dataInitialized.test.ts +0 -42
- package/src/domainBands.test.ts +0 -122
- package/src/domainLetterColor.test.ts +0 -80
- package/src/exportFileName.test.ts +0 -40
- package/src/fetchUtils.test.ts +0 -83
- package/src/flatToTree.test.ts +0 -51
- package/src/hierarchy.test.ts +0 -228
- package/src/highlights.test.ts +0 -70
- package/src/impgRender.test.tsx +0 -68
- package/src/largeTree.test.ts +0 -67
- package/src/launchInterProScan.ts +0 -167
- package/src/loadErrorDisplay.test.tsx +0 -102
- package/src/modelFilehandleLoaders.test.ts +0 -137
- package/src/modelReset.test.ts +0 -120
- package/src/neighborJoining.test.ts +0 -164
- package/src/parseAsn1.test.ts +0 -36
- package/src/propertyConservation.test.ts +0 -36
- package/src/renderColumnTracksSvg.test.tsx +0 -108
- package/src/renderDomainsSvg.test.tsx +0 -118
- package/src/renderHighlightsSvg.test.tsx +0 -85
- package/src/renderMinimapSvg.test.tsx +0 -62
- package/src/renderRasterSvg.test.tsx +0 -144
- package/src/renderSequenceLogoSvg.test.tsx +0 -165
- package/src/renderToSvg.test.tsx +0 -65
- package/src/reset.test.ts +0 -61
- package/src/rowCoordinateCalculations.test.ts +0 -211
- package/src/rowSpace.test.ts +0 -46
- package/src/segmentOverlay.test.ts +0 -70
- package/src/seqPosToGlobalCol.test.ts +0 -59
- package/src/sequenceLogo.test.ts +0 -88
- package/src/setFontSize.test.ts +0 -33
- package/src/stripDefault.test.ts +0 -48
- package/src/stripDefaultSnapshot.test.ts +0 -87
- package/src/treeMetadata.test.ts +0 -39
- package/src/useCanvasAutorun.test.tsx +0 -98
- package/src/useColorContrast.ts +0 -18
- package/src/viewChrome.test.tsx +0 -134
- package/src/wheelZoom.test.ts +0 -46
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { descendants } from '../../hierarchy.ts'
|
|
2
|
+
import { getNodeX } from './renderTreeCanvas.ts'
|
|
3
|
+
|
|
4
|
+
import type { HierarchyNode } from '../../hierarchy.ts'
|
|
5
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
6
|
+
import type { RasterCanvas } from '../msa/msaRaster.ts'
|
|
7
|
+
import type { RenderCtx } from '../renderCtx.ts'
|
|
8
|
+
import type { Theme } from '@mui/material'
|
|
9
|
+
|
|
10
|
+
// the tree draws this far inside the band, so a box around the first or the
|
|
11
|
+
// last row keeps both of its edges
|
|
12
|
+
const pad = 1
|
|
13
|
+
|
|
14
|
+
interface OverviewArgs {
|
|
15
|
+
model: MsaViewModel
|
|
16
|
+
ctx: RenderCtx
|
|
17
|
+
theme: Theme
|
|
18
|
+
width: number
|
|
19
|
+
height: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function rowScale(numTips: number, height: number) {
|
|
23
|
+
const inner = height - pad * 2
|
|
24
|
+
return (row: number) => pad + (row / numTips) * inner
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The whole tree at the size of the overview band, with the clade highlights
|
|
29
|
+
* under it.
|
|
30
|
+
*
|
|
31
|
+
* A branch that moves less than a pixel on both axes is dropped. The 230k-tip
|
|
32
|
+
* COVID tree has 460k of them over a 120px band, and the ones landing on the
|
|
33
|
+
* pixel their parent already covers cost a path segment each and change no
|
|
34
|
+
* pixel; the test in renderTreeOverview.test.ts measures a 200k-tip tree.
|
|
35
|
+
*/
|
|
36
|
+
export function drawTreeOverview({
|
|
37
|
+
model,
|
|
38
|
+
ctx,
|
|
39
|
+
theme,
|
|
40
|
+
width,
|
|
41
|
+
height,
|
|
42
|
+
}: OverviewArgs) {
|
|
43
|
+
const layout = model.treeOverviewLayout
|
|
44
|
+
if (!layout) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
const { root, numTips, maxDepthToLeaf, showBranchLen } = layout
|
|
48
|
+
const innerWidth = width - pad * 2
|
|
49
|
+
const rowY = rowScale(numTips, height)
|
|
50
|
+
const nodeX = (node: HierarchyNode) =>
|
|
51
|
+
pad + (getNodeX(node, showBranchLen, 1, maxDepthToLeaf) ?? 0) * innerWidth
|
|
52
|
+
|
|
53
|
+
for (const { rows, color } of model.treeOverviewClades) {
|
|
54
|
+
ctx.fillStyle = color
|
|
55
|
+
ctx.fillRect(0, rowY(rows[0]), width, rowY(rows[1] + 1) - rowY(rows[0]))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ctx.strokeStyle = theme.palette.text.primary
|
|
59
|
+
ctx.beginPath()
|
|
60
|
+
for (const source of descendants(root)) {
|
|
61
|
+
if (!source.children) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
const sx = nodeX(source)
|
|
65
|
+
const sy = rowY(source.x!)
|
|
66
|
+
for (const target of source.children) {
|
|
67
|
+
const tx = nodeX(target)
|
|
68
|
+
const ty = rowY(target.x!)
|
|
69
|
+
if (Math.abs(tx - sx) < 1 && Math.abs(ty - sy) < 1) {
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
ctx.moveTo(sx, sy)
|
|
73
|
+
ctx.lineTo(sx, ty)
|
|
74
|
+
ctx.lineTo(tx, ty)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
ctx.stroke()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A box across the overview around a run of tip rows: the focused subtree's
|
|
82
|
+
* extent, or the one a hovered pixel would focus.
|
|
83
|
+
*/
|
|
84
|
+
export function drawTreeOverviewBox({
|
|
85
|
+
model,
|
|
86
|
+
ctx,
|
|
87
|
+
color,
|
|
88
|
+
rows,
|
|
89
|
+
width,
|
|
90
|
+
height,
|
|
91
|
+
}: Omit<OverviewArgs, 'theme'> & {
|
|
92
|
+
color: string
|
|
93
|
+
rows: [number, number]
|
|
94
|
+
}) {
|
|
95
|
+
const layout = model.treeOverviewLayout
|
|
96
|
+
if (!layout) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
const rowY = rowScale(layout.numTips, height)
|
|
100
|
+
const top = rowY(rows[0])
|
|
101
|
+
ctx.strokeStyle = color
|
|
102
|
+
ctx.strokeRect(0.5, top, width - 1, Math.max(2, rowY(rows[1] + 1) - top))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The overview as the figure carries it: the tree, the clade highlights and
|
|
107
|
+
* the focus box. The SVG export draws it straight onto a svgcanvas Context,
|
|
108
|
+
* and the live canvas blits `treeOverviewImage` in place of the first two.
|
|
109
|
+
*/
|
|
110
|
+
export function renderTreeOverview(args: OverviewArgs) {
|
|
111
|
+
drawTreeOverview(args)
|
|
112
|
+
const rows = args.model.treeOverviewFocusRows
|
|
113
|
+
if (rows) {
|
|
114
|
+
drawTreeOverviewBox({
|
|
115
|
+
...args,
|
|
116
|
+
rows,
|
|
117
|
+
color: args.theme.palette.text.primary,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface OverviewCache {
|
|
123
|
+
keys: unknown[]
|
|
124
|
+
canvas: RasterCanvas | undefined
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const caches = new WeakMap<object, OverviewCache>()
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Everything the drawn tree depends on. `treeOverviewLayout` and
|
|
131
|
+
* `treeOverviewClades` are computed views, which recompute on every read
|
|
132
|
+
* outside a reaction, so the key holds the values behind them instead.
|
|
133
|
+
*/
|
|
134
|
+
function overviewKeys({
|
|
135
|
+
model,
|
|
136
|
+
theme,
|
|
137
|
+
width,
|
|
138
|
+
height,
|
|
139
|
+
scale,
|
|
140
|
+
}: {
|
|
141
|
+
model: MsaViewModel
|
|
142
|
+
theme: Theme
|
|
143
|
+
width: number
|
|
144
|
+
height: number
|
|
145
|
+
scale: number
|
|
146
|
+
}) {
|
|
147
|
+
return [
|
|
148
|
+
model.data.tree,
|
|
149
|
+
model.data.msa,
|
|
150
|
+
model.currentAlignment,
|
|
151
|
+
model.collapsed.join(','),
|
|
152
|
+
JSON.stringify(model.clades),
|
|
153
|
+
model.showBranchLen,
|
|
154
|
+
width,
|
|
155
|
+
height,
|
|
156
|
+
scale,
|
|
157
|
+
theme.palette.text.primary,
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function makeCanvas(width: number, height: number) {
|
|
162
|
+
const canvas =
|
|
163
|
+
typeof OffscreenCanvas !== 'undefined'
|
|
164
|
+
? new OffscreenCanvas(width, height)
|
|
165
|
+
: typeof document !== 'undefined'
|
|
166
|
+
? Object.assign(document.createElement('canvas'), { width, height })
|
|
167
|
+
: undefined
|
|
168
|
+
const ctx = canvas?.getContext('2d')
|
|
169
|
+
return ctx && typeof (ctx as CanvasRenderingContext2D).stroke === 'function'
|
|
170
|
+
? { canvas: canvas as RasterCanvas, ctx: ctx as CanvasRenderingContext2D }
|
|
171
|
+
: undefined
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The tree and its clade highlights on an offscreen canvas, drawn once per
|
|
176
|
+
* change of anything they depend on and blitted every frame after that. A
|
|
177
|
+
* 230k-branch tree takes hundreds of milliseconds to walk and cannot be redrawn
|
|
178
|
+
* as the focus box follows the pointer.
|
|
179
|
+
*
|
|
180
|
+
* Returns undefined where a canvas has no 2d context (jsdom), and the caller
|
|
181
|
+
* then draws the tree itself.
|
|
182
|
+
*/
|
|
183
|
+
export function treeOverviewImage({
|
|
184
|
+
model,
|
|
185
|
+
theme,
|
|
186
|
+
width,
|
|
187
|
+
height,
|
|
188
|
+
scale,
|
|
189
|
+
}: {
|
|
190
|
+
model: MsaViewModel
|
|
191
|
+
theme: Theme
|
|
192
|
+
width: number
|
|
193
|
+
height: number
|
|
194
|
+
scale: number
|
|
195
|
+
}) {
|
|
196
|
+
const keys = overviewKeys({ model, theme, width, height, scale })
|
|
197
|
+
const prev = caches.get(model)
|
|
198
|
+
if (prev && keys.every((key, i) => key === prev.keys[i])) {
|
|
199
|
+
return prev.canvas
|
|
200
|
+
}
|
|
201
|
+
const made = makeCanvas(
|
|
202
|
+
Math.max(1, Math.round(width * scale)),
|
|
203
|
+
Math.max(1, Math.round(height * scale)),
|
|
204
|
+
)
|
|
205
|
+
if (made) {
|
|
206
|
+
made.ctx.scale(scale, scale)
|
|
207
|
+
drawTreeOverview({ model, ctx: made.ctx, theme, width, height })
|
|
208
|
+
}
|
|
209
|
+
const next = { keys, canvas: made?.canvas }
|
|
210
|
+
caches.set(model, next)
|
|
211
|
+
return next.canvas
|
|
212
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A round branch-length step and its width in pixels: the largest 1/2/5 x 10^n
|
|
3
|
+
* substitutions-per-site that fits in `maxPx`, aiming at about half of it.
|
|
4
|
+
* Returns undefined in cladogram mode, where the x-positions carry no length.
|
|
5
|
+
*/
|
|
6
|
+
export function scaleBarLength(pxPerUnit: number, maxPx: number) {
|
|
7
|
+
if (pxPerUnit <= 0 || maxPx < 20) {
|
|
8
|
+
return undefined
|
|
9
|
+
}
|
|
10
|
+
const target = maxPx / 2 / pxPerUnit
|
|
11
|
+
const magnitude = 10 ** Math.floor(Math.log10(target))
|
|
12
|
+
const mantissa = target / magnitude
|
|
13
|
+
const step = (mantissa >= 5 ? 5 : mantissa >= 2 ? 2 : 1) * magnitude
|
|
14
|
+
const px = step * pxPerUnit
|
|
15
|
+
return px > maxPx
|
|
16
|
+
? undefined
|
|
17
|
+
: {
|
|
18
|
+
step,
|
|
19
|
+
px,
|
|
20
|
+
// a length of 0.02 reads as 0.02, not 0.020000000000000004
|
|
21
|
+
label: `${Number(step.toPrecision(2))}`,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import { ClickMapIndex } from './clickMap.ts'
|
|
4
4
|
|
|
@@ -24,9 +24,7 @@ export function useTreeHover({
|
|
|
24
24
|
model: MsaViewModel
|
|
25
25
|
offsetY: number
|
|
26
26
|
}) {
|
|
27
|
-
const
|
|
28
|
-
clickMapRef.current ??= new ClickMapIndex()
|
|
29
|
-
const clickMap = clickMapRef.current
|
|
27
|
+
const [clickMap] = useState(() => new ClickMapIndex())
|
|
30
28
|
const [hovered, setHovered] = useState<TreeHoverTarget>()
|
|
31
29
|
|
|
32
30
|
// leaf labels win over the branch/bubble targets they overlap, so a click on a
|
package/src/constants.ts
CHANGED
|
@@ -5,18 +5,22 @@ export const defaultHeight = 550
|
|
|
5
5
|
export const defaultScrollX = 0
|
|
6
6
|
export const defaultScrollY = 0
|
|
7
7
|
export const defaultCurrentAlignment = 0
|
|
8
|
-
|
|
8
|
+
// Only a hidden overlay is stored, so a link that never mentions it opens
|
|
9
|
+
// showing its annotations.
|
|
10
|
+
export const defaultShowDomains = true
|
|
9
11
|
export const defaultShowDomainLegend = true
|
|
10
12
|
export const defaultHideGaps = true
|
|
11
13
|
export const defaultAllowedGappyness = 100
|
|
12
14
|
export const defaultSubFeatureRows = false
|
|
13
|
-
//
|
|
15
|
+
// height of one sub-row when subFeatureRows is on, unless the row has more
|
|
16
|
+
// lanes than fit at this height (see renderBoxFeatureCanvasBlock)
|
|
14
17
|
export const subFeatureRowHeight = 4
|
|
18
|
+
// height of the bar the overlay draws under a row in letter-color mode
|
|
19
|
+
export const domainUnderlineHeight = 3
|
|
15
20
|
|
|
16
|
-
// Feature types that are ordinal
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// number, instead of assigning every one a distinct hue + legend row.
|
|
21
|
+
// Feature types that are ordinal segments of one transcript. The overlay
|
|
22
|
+
// alternates two shades across them and labels them by number, where a domain
|
|
23
|
+
// type gets its own hue and legend row.
|
|
20
24
|
export const segmentFeatureTypes = new Set([
|
|
21
25
|
'exon',
|
|
22
26
|
'CDS',
|
|
@@ -31,9 +35,16 @@ export const segmentShades = ['#9fb6d4', '#d4dcea']
|
|
|
31
35
|
export const defaultDrawMsaLetters = true
|
|
32
36
|
export const defaultScrollZoom = false
|
|
33
37
|
|
|
38
|
+
// Which cell dimensions a wheel zoom scales. 'horizontal' holds the row height,
|
|
39
|
+
// which fixes the label and letter font size while the columns compress.
|
|
40
|
+
// 'vertical' does the reverse.
|
|
41
|
+
export const scrollZoomAxes = ['both', 'horizontal', 'vertical'] as const
|
|
42
|
+
export type ScrollZoomAxis = (typeof scrollZoomAxes)[number]
|
|
43
|
+
export const defaultScrollZoomAxis: ScrollZoomAxis = 'both'
|
|
44
|
+
|
|
34
45
|
// Cell size floors for drawing residue letters and tree labels. Below these a
|
|
35
|
-
// 500px block holds thousands of glyphs, and fillText
|
|
36
|
-
//
|
|
46
|
+
// 500px block holds thousands of glyphs, and fillText dominates every zoom
|
|
47
|
+
// frame. A sprite atlas measured slower.
|
|
37
48
|
export const minLetterRowHeight = 8
|
|
38
49
|
export const minLetterColWidth = 5
|
|
39
50
|
|
|
@@ -61,3 +72,45 @@ export const defaultTreeWidth = 300
|
|
|
61
72
|
export const defaultShowBranchLen = true
|
|
62
73
|
export const defaultDrawTree = true
|
|
63
74
|
export const defaultDrawNodeBubbles = true
|
|
75
|
+
export const defaultDrawNodeLabels = false
|
|
76
|
+
export const defaultShowTreeOverview = false
|
|
77
|
+
export const defaultOverviewHeight = 120
|
|
78
|
+
|
|
79
|
+
// Neighbor joining does not run above this many rows. The distance matrix is
|
|
80
|
+
// O(n^2*L) and the join loop O(n^3), both on the main thread with no progress
|
|
81
|
+
// and no cancel: 400 rows measured 1.5s, 800 rows 10s, and 1600 would be about
|
|
82
|
+
// seventy. See agent-docs/ideas/neighbor-joining-scaling.md.
|
|
83
|
+
export const maxNeighborJoiningRows = 500
|
|
84
|
+
|
|
85
|
+
// The opacity a `rowTint` encoding draws its scale's color at, so the residues
|
|
86
|
+
// under the wash stay readable. A color the producer already gave an alpha
|
|
87
|
+
// keeps it.
|
|
88
|
+
export const rowTintAlpha = 0.25
|
|
89
|
+
|
|
90
|
+
// The fill a `clades` record with the `highlight` mark draws behind its rows,
|
|
91
|
+
// and the opacity a producer's own color takes when it carries none.
|
|
92
|
+
export const cladeHighlightColor = '#fff3c4'
|
|
93
|
+
export const cladeHighlightAlpha = 0.6
|
|
94
|
+
|
|
95
|
+
// The largest inline document the snapshot carries. A larger pasted or
|
|
96
|
+
// locally-opened file stays in the live model and out of the snapshot, keeping
|
|
97
|
+
// shared URLs sendable. `unshareableData` warns using the same number.
|
|
98
|
+
export const maxInlineSnapshotBytes = 50_000
|
|
99
|
+
|
|
100
|
+
// Height of the band the `rowPanels` headers draw in, above the strips in the
|
|
101
|
+
// top area beside the tree ruler and the minimap. A panel wider than the band
|
|
102
|
+
// is tall writes its header across, where it has more room.
|
|
103
|
+
export const rowPanelHeaderHeight = 56
|
|
104
|
+
|
|
105
|
+
// Height of the branch-length scale bar above the tree.
|
|
106
|
+
export const treeScaleBarHeight = 22
|
|
107
|
+
|
|
108
|
+
// Width of a `features` row panel that names none, wide enough for a gene
|
|
109
|
+
// neighborhood of a few arrows to read.
|
|
110
|
+
export const defaultFeaturePanelWidth = 200
|
|
111
|
+
|
|
112
|
+
// The alignment's overlay canvas draws the mouseover, the persisted highlights
|
|
113
|
+
// and the row tints over the residues, and the legend key floats above that,
|
|
114
|
+
// so a tint reaching the top right corner leaves the key readable.
|
|
115
|
+
export const msaOverlayZIndex = 1000
|
|
116
|
+
export const legendZIndex = msaOverlayZIndex + 1
|
package/src/createPaletteMap.ts
CHANGED
|
@@ -3,17 +3,18 @@ import { colord } from 'colord'
|
|
|
3
3
|
import palettes from './ggplotPalettes.ts'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Creates a map from keys to colors.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Creates a map from keys to colors. `palette` names the colors to take in
|
|
7
|
+
* order, such as the list a scale asked for by name; without it the keys take
|
|
8
|
+
* the ggplot palette of their own size. Beyond the colors available — a
|
|
9
|
+
* gene-structure overlay with one feature per exon, or nine keys on an
|
|
10
|
+
* eight-color palette — every key gets an evenly spaced HSL hue instead, so no
|
|
11
|
+
* two keys share a color.
|
|
11
12
|
*/
|
|
12
|
-
export function createPaletteMap(keys: string[]) {
|
|
13
|
+
export function createPaletteMap(keys: string[], palette?: readonly string[]) {
|
|
13
14
|
const n = keys.length
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return Object.fromEntries(keys.map((key, i) => [key,
|
|
15
|
+
const colors = palette ?? palettes[n - 1]
|
|
16
|
+
if (colors && n <= colors.length) {
|
|
17
|
+
return Object.fromEntries(keys.map((key, i) => [key, colors[i]!]))
|
|
17
18
|
}
|
|
18
19
|
return Object.fromEntries(
|
|
19
20
|
keys.map((key, i) => [
|
package/src/element.ts
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { when } from 'mobx'
|
|
2
|
+
|
|
3
|
+
import { mount } from './mount.tsx'
|
|
4
|
+
|
|
5
|
+
import type { MSAViewerProps } from './components/MSAViewer.tsx'
|
|
6
|
+
import type { MsaViewModel } from './model.ts'
|
|
7
|
+
import type { MountedViewer } from './mount.tsx'
|
|
8
|
+
import type {
|
|
9
|
+
Cell,
|
|
10
|
+
ColumnTrackSpec,
|
|
11
|
+
Highlight,
|
|
12
|
+
ResidueMapping,
|
|
13
|
+
Viewport,
|
|
14
|
+
} from './types.ts'
|
|
15
|
+
|
|
16
|
+
type ElementData = Pick<
|
|
17
|
+
MSAViewerProps,
|
|
18
|
+
'msa' | 'tree' | 'gff' | 'highlights' | 'columnTracks' | 'residueMappings'
|
|
19
|
+
>
|
|
20
|
+
|
|
21
|
+
interface NightingaleManager extends HTMLElement {
|
|
22
|
+
register: (element: HTMLElement) => void
|
|
23
|
+
unregister: (element: HTMLElement) => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ownAttributes = [
|
|
27
|
+
'msa-url',
|
|
28
|
+
'tree-url',
|
|
29
|
+
'gff-url',
|
|
30
|
+
'color-scheme',
|
|
31
|
+
'height',
|
|
32
|
+
'hide-header',
|
|
33
|
+
'theme',
|
|
34
|
+
'reference-row',
|
|
35
|
+
'tree-area-width',
|
|
36
|
+
] as const
|
|
37
|
+
|
|
38
|
+
// the attributes a <nightingale-manager> writes on the elements it holds
|
|
39
|
+
const nightingaleAttributes = ['display-start', 'display-end', 'highlight']
|
|
40
|
+
|
|
41
|
+
function location(uri: string | null) {
|
|
42
|
+
return uri ? { uri, locationType: 'UriLocation' as const } : undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function numberAttribute(value: string | null | undefined) {
|
|
46
|
+
const n = value ? Number(value) : Number.NaN
|
|
47
|
+
return Number.isFinite(n) ? n : undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Nightingale writes a highlight as "start:end,start:end", 1-based inclusive
|
|
51
|
+
function parseHighlight(value: string | null, row?: string): Highlight[] {
|
|
52
|
+
return (value ?? '').split(',').flatMap(span => {
|
|
53
|
+
const [start, end] = span.split(':').map(numberAttribute)
|
|
54
|
+
return start === undefined ? [] : [{ row, start, end: end ?? start }]
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function residueAtColumn(model: MsaViewModel, row: string, column: number) {
|
|
59
|
+
const visible = model.globalColToVisibleCol(column - 1)
|
|
60
|
+
return visible === undefined
|
|
61
|
+
? undefined
|
|
62
|
+
: model.visibleColToSeqPosOneBased(row, visible)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// the first and last residues of `row` inside the viewport's columns
|
|
66
|
+
function residueRange(model: MsaViewModel, row: string, viewport: Viewport) {
|
|
67
|
+
let start: number | undefined
|
|
68
|
+
let end: number | undefined
|
|
69
|
+
for (let c = viewport.startColumn; c <= viewport.endColumn && !start; c++) {
|
|
70
|
+
start = residueAtColumn(model, row, c)
|
|
71
|
+
}
|
|
72
|
+
for (let c = viewport.endColumn; c >= viewport.startColumn && !end; c--) {
|
|
73
|
+
end = residueAtColumn(model, row, c)
|
|
74
|
+
}
|
|
75
|
+
return start && end ? { start, end } : undefined
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Registers a custom element that renders MSAViewer, for pages with no React.
|
|
80
|
+
* Attributes: msa-url, tree-url, gff-url, color-scheme, height, hide-header,
|
|
81
|
+
* theme, tree-area-width and reference-row. Properties: msa, tree and gff text, highlights,
|
|
82
|
+
* columnTracks and residueMappings. Events: cell-hover, cell-click and
|
|
83
|
+
* viewport-change, each with the MSAViewer callback's value as detail.
|
|
84
|
+
*
|
|
85
|
+
* Inside a <nightingale-manager>, the element registers with it and speaks its
|
|
86
|
+
* protocol: it follows the display-start, display-end and highlight attributes
|
|
87
|
+
* the manager writes, and reports its own range and hovered residue as change
|
|
88
|
+
* events. Positions are residues of reference-row, or columns without one.
|
|
89
|
+
*/
|
|
90
|
+
export function defineMsaElement(tagName = 'jbrowse-msa') {
|
|
91
|
+
const existing = customElements.get(tagName)
|
|
92
|
+
if (existing) {
|
|
93
|
+
return existing
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class MsaElement extends HTMLElement {
|
|
97
|
+
static observedAttributes = [...ownAttributes, ...nightingaleAttributes]
|
|
98
|
+
|
|
99
|
+
#viewer?: MountedViewer
|
|
100
|
+
#model?: MsaViewModel
|
|
101
|
+
#manager?: NightingaleManager
|
|
102
|
+
#cancelRange?: () => void
|
|
103
|
+
#rangePending = false
|
|
104
|
+
#reported?: { start: number; end: number }
|
|
105
|
+
#data: ElementData = {}
|
|
106
|
+
|
|
107
|
+
get msa() {
|
|
108
|
+
return this.#data.msa
|
|
109
|
+
}
|
|
110
|
+
set msa(value: string | undefined) {
|
|
111
|
+
this.#setData({ msa: value })
|
|
112
|
+
}
|
|
113
|
+
get tree() {
|
|
114
|
+
return this.#data.tree
|
|
115
|
+
}
|
|
116
|
+
set tree(value: string | undefined) {
|
|
117
|
+
this.#setData({ tree: value })
|
|
118
|
+
}
|
|
119
|
+
get gff() {
|
|
120
|
+
return this.#data.gff
|
|
121
|
+
}
|
|
122
|
+
set gff(value: string | undefined) {
|
|
123
|
+
this.#setData({ gff: value })
|
|
124
|
+
}
|
|
125
|
+
get highlights() {
|
|
126
|
+
return this.#data.highlights
|
|
127
|
+
}
|
|
128
|
+
set highlights(value: Highlight[] | undefined) {
|
|
129
|
+
this.#setData({ highlights: value })
|
|
130
|
+
}
|
|
131
|
+
get columnTracks() {
|
|
132
|
+
return this.#data.columnTracks
|
|
133
|
+
}
|
|
134
|
+
set columnTracks(value: ColumnTrackSpec[] | undefined) {
|
|
135
|
+
this.#setData({ columnTracks: value })
|
|
136
|
+
}
|
|
137
|
+
get residueMappings() {
|
|
138
|
+
return this.#data.residueMappings
|
|
139
|
+
}
|
|
140
|
+
set residueMappings(value: ResidueMapping[] | undefined) {
|
|
141
|
+
this.#setData({ residueMappings: value })
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
get #referenceRow() {
|
|
145
|
+
return this.getAttribute('reference-row') ?? undefined
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#setData(data: ElementData) {
|
|
149
|
+
this.#data = { ...this.#data, ...data }
|
|
150
|
+
this.#viewer?.update(this.#props())
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#props(): MSAViewerProps {
|
|
154
|
+
const theme = this.getAttribute('theme')
|
|
155
|
+
return {
|
|
156
|
+
...this.#data,
|
|
157
|
+
msaFilehandle: location(this.getAttribute('msa-url')),
|
|
158
|
+
treeFilehandle: location(this.getAttribute('tree-url')),
|
|
159
|
+
gffFilehandle: location(this.getAttribute('gff-url')),
|
|
160
|
+
colorScheme: this.getAttribute('color-scheme') ?? undefined,
|
|
161
|
+
height: numberAttribute(this.getAttribute('height')),
|
|
162
|
+
treeAreaWidth: numberAttribute(this.getAttribute('tree-area-width')),
|
|
163
|
+
hideHeader: this.hasAttribute('hide-header'),
|
|
164
|
+
theme: theme === 'dark' ? 'dark' : 'light',
|
|
165
|
+
onCellHover: cell => {
|
|
166
|
+
this.#emit('cell-hover', cell)
|
|
167
|
+
this.#reportHover(cell)
|
|
168
|
+
},
|
|
169
|
+
onCellClick: cell => {
|
|
170
|
+
this.#emit('cell-click', cell)
|
|
171
|
+
},
|
|
172
|
+
onViewportChange: viewport => {
|
|
173
|
+
this.#emit('viewport-change', viewport)
|
|
174
|
+
this.#reportRange(viewport)
|
|
175
|
+
},
|
|
176
|
+
onModel: model => {
|
|
177
|
+
this.#model = model
|
|
178
|
+
this.#followRange()
|
|
179
|
+
this.#followHighlight()
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#emit(type: string, detail: unknown) {
|
|
185
|
+
this.dispatchEvent(new CustomEvent(type, { detail }))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
#nightingaleChange(detail: Record<string, unknown>) {
|
|
189
|
+
if (this.#manager) {
|
|
190
|
+
this.dispatchEvent(
|
|
191
|
+
new CustomEvent('change', {
|
|
192
|
+
detail,
|
|
193
|
+
bubbles: true,
|
|
194
|
+
cancelable: true,
|
|
195
|
+
}),
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#reportHover(cell: Cell | undefined) {
|
|
201
|
+
const model = this.#model
|
|
202
|
+
const row = this.#referenceRow
|
|
203
|
+
const position =
|
|
204
|
+
cell && model && row
|
|
205
|
+
? residueAtColumn(model, row, cell.column)
|
|
206
|
+
: cell?.column
|
|
207
|
+
this.#nightingaleChange({
|
|
208
|
+
type: 'highlight',
|
|
209
|
+
value: position === undefined ? null : `${position}:${position}`,
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#reportRange(viewport: Viewport | undefined) {
|
|
214
|
+
const model = this.#model
|
|
215
|
+
const row = this.#referenceRow
|
|
216
|
+
if (!viewport || !model) {
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
const range = row
|
|
220
|
+
? residueRange(model, row, viewport)
|
|
221
|
+
: { start: viewport.startColumn, end: viewport.endColumn }
|
|
222
|
+
const shownStart = numberAttribute(this.getAttribute('display-start'))
|
|
223
|
+
const shownEnd = numberAttribute(this.getAttribute('display-end'))
|
|
224
|
+
// a range the manager just wrote comes back within a column of itself,
|
|
225
|
+
// since the viewer shows whole columns
|
|
226
|
+
const echo =
|
|
227
|
+
range &&
|
|
228
|
+
shownStart !== undefined &&
|
|
229
|
+
shownEnd !== undefined &&
|
|
230
|
+
Math.abs(range.start - shownStart) <= 1 &&
|
|
231
|
+
Math.abs(range.end - shownEnd) <= 1
|
|
232
|
+
if (range && !echo) {
|
|
233
|
+
this.#reported = range
|
|
234
|
+
this.#nightingaleChange({
|
|
235
|
+
'display-start': range.start,
|
|
236
|
+
'display-end': range.end,
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
#followRange() {
|
|
242
|
+
const model = this.#model
|
|
243
|
+
const start = numberAttribute(this.getAttribute('display-start'))
|
|
244
|
+
const end = numberAttribute(this.getAttribute('display-end'))
|
|
245
|
+
const reported = this.#reported
|
|
246
|
+
// the manager writes the range this element reported back onto it, and
|
|
247
|
+
// zooming to it again would snap the reader's own zoom to whole residues
|
|
248
|
+
if (reported && reported.start === start && reported.end === end) {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
this.#cancelRange?.()
|
|
252
|
+
if (model && start !== undefined && end !== undefined) {
|
|
253
|
+
const row = this.#referenceRow
|
|
254
|
+
this.#cancelRange = when(
|
|
255
|
+
() => model.viewInitialized && model.numColumns > 0,
|
|
256
|
+
() => {
|
|
257
|
+
model.zoomToRegion({ row, start, end })
|
|
258
|
+
},
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#followHighlight() {
|
|
264
|
+
const spans = parseHighlight(
|
|
265
|
+
this.getAttribute('highlight'),
|
|
266
|
+
this.#referenceRow,
|
|
267
|
+
)
|
|
268
|
+
if (spans.length) {
|
|
269
|
+
this.#model?.applyHighlight('nightingale', spans)
|
|
270
|
+
} else {
|
|
271
|
+
this.#model?.clearHighlight('nightingale')
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
connectedCallback() {
|
|
276
|
+
if (!this.style.display) {
|
|
277
|
+
this.style.display = 'block'
|
|
278
|
+
}
|
|
279
|
+
this.#viewer = mount(this, this.#props())
|
|
280
|
+
const manager = this.closest<NightingaleManager>('nightingale-manager')
|
|
281
|
+
if (manager) {
|
|
282
|
+
void customElements.whenDefined('nightingale-manager').then(() => {
|
|
283
|
+
if (this.isConnected) {
|
|
284
|
+
this.#manager = manager
|
|
285
|
+
manager.register(this)
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
disconnectedCallback() {
|
|
292
|
+
this.#manager?.unregister(this)
|
|
293
|
+
this.#manager = undefined
|
|
294
|
+
this.#cancelRange?.()
|
|
295
|
+
this.#viewer?.destroy()
|
|
296
|
+
this.#viewer = undefined
|
|
297
|
+
this.#model = undefined
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
attributeChangedCallback(name: string) {
|
|
301
|
+
if (name === 'display-start' || name === 'display-end') {
|
|
302
|
+
// the manager writes the two attributes one after the other, and a
|
|
303
|
+
// zoom between them reports a range that overwrites the second
|
|
304
|
+
if (!this.#rangePending) {
|
|
305
|
+
this.#rangePending = true
|
|
306
|
+
queueMicrotask(() => {
|
|
307
|
+
this.#rangePending = false
|
|
308
|
+
this.#followRange()
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
} else if (name === 'highlight') {
|
|
312
|
+
this.#followHighlight()
|
|
313
|
+
} else {
|
|
314
|
+
this.#viewer?.update(this.#props())
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
customElements.define(tagName, MsaElement)
|
|
320
|
+
return MsaElement
|
|
321
|
+
}
|