diff --git a/lib/apidocs/MsaView.md b/lib/apidocs/MsaView.md index 896f9c5b..63a711df 100644 --- a/lib/apidocs/MsaView.md +++ b/lib/apidocs/MsaView.md @@ -53,6 +53,15 @@ number colWidth: 16 ``` +#### property: contrastLettering + +```js +// type signature +true +// code +contrastLettering: true +``` + #### property: currentAlignment ```js @@ -243,13 +252,6 @@ type: types.literal('MsaView') ### MsaView - Getters -#### getter: \_tree - -```js -// type -NodeWithIds -``` - #### getter: adapterTrackModels ```js @@ -406,6 +408,15 @@ any ;ClustalMSA | StockholmMSA | FastaMSA ``` +#### getter: msaAreaHeight + +widget width minus the tree area gives the space for the MSA + +```js +// type +number +``` + #### getter: msaAreaWidth widget width minus the tree area gives the space for the MSA @@ -415,7 +426,7 @@ widget width minus the tree area gives the space for the MSA number ``` -#### getter: noAnnotations +#### getter: noDomains ```js // type @@ -557,6 +568,13 @@ number ITextTrack[] ``` +#### getter: tree + +```js +// type +NodeWithIds +``` + #### getter: treeAreaWidthMinusMargin ```js @@ -692,6 +710,13 @@ set col width (px) setColWidth: (n: number) => void ``` +#### action: setContrastLettering + +```js +// type signature +setContrastLettering: (arg: boolean) => void +``` + #### action: setCurrentAlignment ```js @@ -729,6 +754,13 @@ setError: (error?: unknown) => void setFilter: (arg: string, flag: boolean) => void ``` +#### action: setHeaderHeight + +```js +// type signature +setHeaderHeight: (arg: number) => void +``` + #### action: setHeight set the height of the view in px diff --git a/lib/src/components/dialogs/InterProScanPanel.tsx b/lib/src/components/dialogs/InterProScanDialog.tsx similarity index 95% rename from lib/src/components/dialogs/InterProScanPanel.tsx rename to lib/src/components/dialogs/InterProScanDialog.tsx index c9dd9faf..3b550ddd 100644 --- a/lib/src/components/dialogs/InterProScanPanel.tsx +++ b/lib/src/components/dialogs/InterProScanDialog.tsx @@ -6,6 +6,7 @@ import { Button, DialogActions, DialogContent, Typography } from '@mui/material' import type { MsaViewModel } from '../../model' import { getSession } from '@jbrowse/core/util' import { launchInterProScan } from '../../launchInterProScan' +import { Dialog } from '@jbrowse/core/ui' const InterProScanDialog = observer(function ({ handleClose, @@ -147,10 +148,16 @@ const InterProScanDialog = observer(function ({ const [show, setShow] = useState(false) return ( - <> + handleClose()} + open + > - This will run InterProScan on all rows of the current MSA + This will run InterProScan via the InterProScan API on all rows of the + current MSA - + ) }) diff --git a/lib/src/components/dialogs/UserProvidedResultPanel.tsx b/lib/src/components/dialogs/UserProvidedDomainsDialog.tsx similarity index 92% rename from lib/src/components/dialogs/UserProvidedResultPanel.tsx rename to lib/src/components/dialogs/UserProvidedDomainsDialog.tsx index b81fbc08..23f04ea9 100644 --- a/lib/src/components/dialogs/UserProvidedResultPanel.tsx +++ b/lib/src/components/dialogs/UserProvidedDomainsDialog.tsx @@ -17,8 +17,9 @@ import { getSession } from '@jbrowse/core/util' import type { MsaViewModel } from '../../model' import { jsonfetch } from '../../fetchUtils' import type { InterProScanResponse } from '../../launchInterProScan' +import { Dialog } from '@jbrowse/core/ui' -const UserProvidedResultsDialog = observer(function ({ +const UserProvidedDomainsDialog = observer(function ({ handleClose, model, }: { @@ -30,7 +31,12 @@ const UserProvidedResultsDialog = observer(function ({ const [interProURL, setInterProURL] = useState('') return ( - <> + handleClose()} + open + >
@@ -112,8 +118,8 @@ const UserProvidedResultsDialog = observer(function ({ Open results - +
) }) -export default UserProvidedResultsDialog +export default UserProvidedDomainsDialog diff --git a/lib/src/components/header/HeaderMenuExtra.tsx b/lib/src/components/header/HeaderMenuExtra.tsx index 778e0593..01b6cf16 100644 --- a/lib/src/components/header/HeaderMenuExtra.tsx +++ b/lib/src/components/header/HeaderMenuExtra.tsx @@ -14,6 +14,7 @@ import FolderOpen from '@mui/icons-material/FolderOpen' import Settings from '@mui/icons-material/Settings' import Assignment from '@mui/icons-material/Assignment' import List from '@mui/icons-material/List' +import FolderOpen from '@mui/icons-material/FolderOpen' // locals import type { MsaViewModel } from '../../model' @@ -24,10 +25,13 @@ const MetadataDialog = lazy(() => import('../dialogs/MetadataDialog')) const TracklistDialog = lazy(() => import('../dialogs/TracklistDialog')) const ExportSVGDialog = lazy(() => import('../dialogs/ExportSVGDialog')) const FeatureFilterDialog = lazy(() => import('../dialogs/FeatureDialog')) -const DomainDialog = lazy(() => import('../dialogs/DomainDialog')) +const UserProvidedDomainsDialog = lazy( + () => import('../dialogs/UserProvidedDomainsDialog'), +) +const InterProScanDialog = lazy(() => import('../dialogs/InterProScanDialog')) const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => { - const { showDomains, subFeatureRows, noAnnotations } = model + const { showDomains, subFeatureRows, noDomains } = model return ( { type: 'subMenu', subMenu: [ { - label: `Show domains${noAnnotations ? ' (no domains loaded)' : ''}`, + label: 'Open domains...', + icon: FolderOpen, + onClick: () => + model.queueDialog(handleClose => [ + UserProvidedDomainsDialog, + { handleClose, model }, + ]), + }, + { + label: 'Query InterProScan for domains...', + icon: Search, + onClick: () => + model.queueDialog(handleClose => [ + InterProScanDialog, + { handleClose, model }, + ]), + }, + { + label: `Show domains${noDomains ? ' (no domains loaded)' : ''}`, + disabled: noDomains, icon: Visibility, checked: showDomains, type: 'checkbox', onClick: () => model.setShowDomains(!showDomains), }, { - label: 'Use sub-row layout', + label: `Use sub-row layout${noDomains ? ' (no domains loaded)' : ''}`, + disabled: noDomains, checked: subFeatureRows, icon: Sort, type: 'checkbox', onClick: () => model.setSubFeatureRows(!subFeatureRows), }, { - label: 'Filter domains', + label: `Filter domains${noDomains ? ' (no domains loaded)' : ''}`, icon: FilterAlt, + disabled: noDomains, onClick: () => { model.queueDialog(onClose => [ FeatureFilterDialog, @@ -96,15 +121,6 @@ const HeaderMenuExtra = observer(({ model }: { model: MsaViewModel }) => { ]) }, }, - { - label: 'View domains', - icon: Search, - onClick: () => - model.queueDialog(handleClose => [ - DomainDialog, - { handleClose, model }, - ]), - }, ], }, ...(model.extraViewMenuItems?.() || []), diff --git a/lib/src/components/import/ImportFormExamples.tsx b/lib/src/components/import/ImportFormExamples.tsx index 37e59ba7..c50ffb3e 100644 --- a/lib/src/components/import/ImportFormExamples.tsx +++ b/lib/src/components/import/ImportFormExamples.tsx @@ -43,7 +43,7 @@ const ImportFormExamples = observer(function ({ model={model} onClick={() => load(model, undefined, { - uri: 'https://jbrowse.org/genomes/newick_trees/sarscov2phylo.pub.ft.nh', + uri: 'https://jbrowse.org/genomes/newicktrees/sarscov2phylo.pub.ft.nh', locationType: 'UriLocation', }) } diff --git a/lib/src/components/tree/renderTreeCanvas.ts b/lib/src/components/tree/renderTreeCanvas.ts index a317a7cd..aa5f9f21 100644 --- a/lib/src/components/tree/renderTreeCanvas.ts +++ b/lib/src/components/tree/renderTreeCanvas.ts @@ -38,7 +38,7 @@ export function renderTree({ ctx.strokeStyle = theme.palette.text.primary for (const link of hierarchy.links()) { const { source, target } = link - if (target.height === 0) { + if (target.height === 0 && !showBranchLen) { continue } const sy = source.x! @@ -137,6 +137,7 @@ export function renderTreeLabels({ treeMetadata, hierarchy, collapsed, + collapsedLeaves, blockSize, labelsAlignRight, drawTree, @@ -169,8 +170,16 @@ export function renderTreeLabels({ // note: +rowHeight/4 matches with -rowHeight/4 in msa const yp = y + fontSize / 4 let xp = (showBranchLen ? len : x) || 0 - if (!collapsed.includes(id)) { - xp -= treeWidth / hierarchy.height // this subtraction is a hack to compensate for the leafnode (issue #71) + if ( + !showBranchLen && + !collapsed.includes(id) && + !collapsedLeaves.includes(id) + ) { + // this subtraction is a hack to compensate for the leafnode rendering + // glitch (issue #71). the context is that an extra leaf node is added + // so that 'collapsing/hiding leaf nodes is possible' but this causes + // weird workarounds + xp -= treeWidth / hierarchy.height } const { width } = ctx.measureText(displayName) diff --git a/lib/src/model.ts b/lib/src/model.ts index be170a76..bacb162b 100644 --- a/lib/src/model.ts +++ b/lib/src/model.ts @@ -551,12 +551,12 @@ function stateModelFactory() { * #getter */ get noTree() { - return !!this._tree.noTree + return !!this.tree.noTree }, /** * #getter */ - get noAnnotations() { + get noDomains() { return !self.interProAnnotations }, /** @@ -597,7 +597,7 @@ function stateModelFactory() { /** * #getter */ - get _tree(): NodeWithIds { + get tree(): NodeWithIds { const ret = self.data.tree ? generateNodeIds(parseNewick(self.data.tree)) : this.MSA?.getTree() || { @@ -626,7 +626,7 @@ function stateModelFactory() { * #getter */ get root() { - let hier = hierarchy(this._tree, d => d.branchset) + let hier = hierarchy(this.tree, d => d.branchset) .sum(d => (d.branchset ? 0 : 1)) .sort((a, b) => ascending(a.data.length || 1, b.data.length || 1)) diff --git a/package.json b/package.json index 81ac9959..aac62ca4 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@rollup/plugin-typescript": "^11.1.0", "@types/d3": "^7.4.0", "@types/file-saver": "^2.0.7", + "@types/node": "^22.1.0", "@types/normalize-wheel": "^1.0.2", "@types/pako": "^2.0.3", "@types/rbush": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 78229789..a2c706e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1133,6 +1133,13 @@ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.14.tgz#319b63ad6df705ee2a65a73ef042c8271e696613" integrity sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg== +"@types/node@^22.1.0": + version "22.1.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b" + integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw== + dependencies: + undici-types "~6.13.0" + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" @@ -4040,6 +4047,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" + integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg== + universalify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"