gramene-search 2.39.1 → 2.41.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/dist/index.js +322 -532
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ var $gXNCa$gramenetreesclientsrctaxonomy = require("gramene-trees-client/src/tax
|
|
|
12
12
|
var $gXNCa$gramenetaxonomywithgenomes = require("gramene-taxonomy-with-genomes");
|
|
13
13
|
var $gXNCa$reactga4 = require("react-ga4");
|
|
14
14
|
var $gXNCa$moneyclip = require("money-clip");
|
|
15
|
+
var $gXNCa$tbrowsecore = require("tbrowse-core");
|
|
15
16
|
var $gXNCa$reactbootstrap = require("react-bootstrap");
|
|
16
17
|
var $gXNCa$reactswitch = require("react-switch");
|
|
17
18
|
var $gXNCa$reacticonsio5 = require("react-icons/io5");
|
|
@@ -6627,6 +6628,7 @@ var $7f865ea0feda21af$export$2e2bcd8739ae039 = $7f865ea0feda21af$var$uiViewState
|
|
|
6627
6628
|
// `viewState` is null until the view has been interacted with (or restored);
|
|
6628
6629
|
// the container falls back to its built-in default and computes initial pixel
|
|
6629
6630
|
// widths in that case.
|
|
6631
|
+
|
|
6630
6632
|
const $6048209b532f201d$var$taxTreeView = {
|
|
6631
6633
|
name: 'taxTreeView',
|
|
6632
6634
|
getReducer: ()=>{
|
|
@@ -6651,12 +6653,18 @@ const $6048209b532f201d$var$taxTreeView = {
|
|
|
6651
6653
|
type: 'TAXTREE_VIEW_SET',
|
|
6652
6654
|
payload: patch
|
|
6653
6655
|
}),
|
|
6654
|
-
// Re-apply persisted state from a saved-view snapshot.
|
|
6656
|
+
// Re-apply persisted state from a saved-view snapshot. The tbrowse ViewState
|
|
6657
|
+
// is run through tbrowse-core's tolerant deserialiser so a blob saved by an
|
|
6658
|
+
// older viewer (missing arrays / fields added since) still restores.
|
|
6655
6659
|
doApplyTaxTreeSnapshot: (snap)=>({ dispatch: dispatch })=>{
|
|
6656
6660
|
if (!snap || typeof snap !== 'object') return;
|
|
6661
|
+
const payload = snap.viewState && typeof snap.viewState === 'object' ? {
|
|
6662
|
+
...snap,
|
|
6663
|
+
viewState: (0, $gXNCa$tbrowsecore.normalizeViewState)(snap.viewState)
|
|
6664
|
+
} : snap;
|
|
6657
6665
|
dispatch({
|
|
6658
6666
|
type: 'TAXTREE_VIEW_SET',
|
|
6659
|
-
payload:
|
|
6667
|
+
payload: payload
|
|
6660
6668
|
});
|
|
6661
6669
|
},
|
|
6662
6670
|
selectTaxTreeView: (state)=>state.taxTreeView
|
|
@@ -6664,6 +6672,7 @@ const $6048209b532f201d$var$taxTreeView = {
|
|
|
6664
6672
|
var $6048209b532f201d$export$2e2bcd8739ae039 = $6048209b532f201d$var$taxTreeView;
|
|
6665
6673
|
|
|
6666
6674
|
|
|
6675
|
+
|
|
6667
6676
|
// Serializer/deserializer for shareable gene-search views.
|
|
6668
6677
|
//
|
|
6669
6678
|
// `selectViewSnapshot` produces a versioned JSON-safe blob from the current
|
|
@@ -6987,10 +6996,7 @@ function $472fe3745b238881$var$buildSnapshot(state) {
|
|
|
6987
6996
|
collapseEmpties: tv.collapseEmpties !== false,
|
|
6988
6997
|
comparaOnly: tv.comparaOnly !== false,
|
|
6989
6998
|
...hasVS ? {
|
|
6990
|
-
viewState:
|
|
6991
|
-
...tv.viewState,
|
|
6992
|
-
prunedNodeIds: []
|
|
6993
|
-
}
|
|
6999
|
+
viewState: (0, $gXNCa$tbrowsecore.stripPrunesForSnapshot)(tv.viewState)
|
|
6994
7000
|
} : {}
|
|
6995
7001
|
};
|
|
6996
7002
|
}
|
|
@@ -8356,18 +8362,6 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
|
|
|
8356
8362
|
studies,
|
|
8357
8363
|
geneStudyIds
|
|
8358
8364
|
]);
|
|
8359
|
-
// Without a baseline study the EBI Studies heatmap has nothing to draw (its
|
|
8360
|
-
// request fails rather than coming back empty), so the tab says so and
|
|
8361
|
-
// points at the differential EBI studies the gene does appear in -- e.g.
|
|
8362
|
-
// Medicago truncatula, whose studies are all differential.
|
|
8363
|
-
const ebiDifferential = (0, $gXNCa$react.useMemo)(()=>{
|
|
8364
|
-
if (!geneStudyIds || !studies) return [];
|
|
8365
|
-
const mine = new Set(geneStudyIds);
|
|
8366
|
-
return studies.filter((e)=>mine.has(e._id) && $9e29a4f60318db7a$var$isDifferential(e) && !$9e29a4f60318db7a$var$isJgiStudy(e)).sort((a, b)=>$9e29a4f60318db7a$var$studyLabel(a) < $9e29a4f60318db7a$var$studyLabel(b) ? -1 : 1);
|
|
8367
|
-
}, [
|
|
8368
|
-
studies,
|
|
8369
|
-
geneStudyIds
|
|
8370
|
-
]);
|
|
8371
8365
|
const allJgiKey = JSON.stringify(allJgiStudies.map((e)=>[
|
|
8372
8366
|
e._id,
|
|
8373
8367
|
e.name
|
|
@@ -8472,32 +8466,21 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
|
|
|
8472
8466
|
children: [
|
|
8473
8467
|
gene.name || gene._id,
|
|
8474
8468
|
" is not in any baseline expression study, so there is no heatmap here.",
|
|
8475
|
-
|
|
8469
|
+
paralogQuery && atlasExperiment && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
8476
8470
|
children: [
|
|
8477
8471
|
' ',
|
|
8478
|
-
"
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
]))}`,
|
|
8491
|
-
target: "_blank",
|
|
8492
|
-
rel: "noopener noreferrer",
|
|
8493
|
-
children: e.description || e._id
|
|
8494
|
-
}),
|
|
8495
|
-
" (",
|
|
8496
|
-
e._id,
|
|
8497
|
-
")"
|
|
8498
|
-
]
|
|
8499
|
-
}, e._id))
|
|
8500
|
-
})
|
|
8472
|
+
"To explore the differential expression of this gene and its paralogs, use the",
|
|
8473
|
+
' ',
|
|
8474
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
8475
|
+
variant: "link",
|
|
8476
|
+
size: "sm",
|
|
8477
|
+
className: "p-0 align-baseline",
|
|
8478
|
+
onClick: ()=>setExpression({
|
|
8479
|
+
activeTab: 'paralogs'
|
|
8480
|
+
}),
|
|
8481
|
+
children: "Paralogs tab"
|
|
8482
|
+
}),
|
|
8483
|
+
"."
|
|
8501
8484
|
]
|
|
8502
8485
|
})
|
|
8503
8486
|
]
|
|
@@ -8593,6 +8576,7 @@ var $9e29a4f60318db7a$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
8593
8576
|
|
|
8594
8577
|
|
|
8595
8578
|
|
|
8579
|
+
|
|
8596
8580
|
const $5c2c79352d3d7b81$export$ec6b54b688be1708 = ({ fullscreen: fullscreen, onExitFullscreen: onExitFullscreen, title: title, className: className, children: children })=>{
|
|
8597
8581
|
const [stableNode] = (0, $gXNCa$react.useState)(()=>typeof document !== 'undefined' ? document.createElement('div') : null);
|
|
8598
8582
|
const inlineRef = (0, $gXNCa$react.useRef)(null);
|
|
@@ -8776,33 +8760,28 @@ const $5c2c79352d3d7b81$export$b2e089eb3692b073 = (props)=>/*#__PURE__*/ (0, $gX
|
|
|
8776
8760
|
|
|
8777
8761
|
|
|
8778
8762
|
|
|
8763
|
+
|
|
8779
8764
|
const $cd8bc494277e92a4$var$ORGAN_CELL_W = 16;
|
|
8780
8765
|
const $cd8bc494277e92a4$var$MAXTPM_W = 46;
|
|
8781
8766
|
const $cd8bc494277e92a4$var$STRESS_MIN = 120;
|
|
8782
8767
|
function $cd8bc494277e92a4$export$965a6b1d7408d496(docs, tree) {
|
|
8783
|
-
const
|
|
8784
|
-
const nodeGene = {}; // leaf nodeId -> gene id
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
nodeGene[n.id] = n.geneId;
|
|
8789
|
-
}
|
|
8768
|
+
const hasTree = Boolean(tree && tree.nodes);
|
|
8769
|
+
const nodeGene = hasTree ? (0, $gXNCa$tbrowsecore.geneIdByLeafNodeId)(tree) : {}; // leaf nodeId -> gene id (hover readout)
|
|
8770
|
+
const byGeneId = {};
|
|
8771
|
+
(docs || []).forEach((d)=>{
|
|
8772
|
+
if (d && d.id) byGeneId[d.id] = (0, $4b8473139e48e77e$export$69fb5d19b5db1cfa)(d);
|
|
8790
8773
|
});
|
|
8774
|
+
// Docs whose gene is not a leaf of this tree are dropped (joinByLeaf's `unmatched`).
|
|
8775
|
+
const byNode = hasTree ? (0, $gXNCa$tbrowsecore.joinByLeaf)(tree, byGeneId).byNode : {};
|
|
8791
8776
|
const organSet = new Set();
|
|
8792
|
-
const byNode = {};
|
|
8793
8777
|
let tpmMin = Infinity;
|
|
8794
8778
|
let tpmMax = -Infinity;
|
|
8795
|
-
(
|
|
8796
|
-
if (!d || !d.id) return;
|
|
8797
|
-
const nodeId = nodeOf[d.id];
|
|
8798
|
-
if (!nodeId) return;
|
|
8799
|
-
const attrs = (0, $4b8473139e48e77e$export$69fb5d19b5db1cfa)(d);
|
|
8779
|
+
Object.values(byNode).forEach((attrs)=>{
|
|
8800
8780
|
Object.keys(attrs.organLevels).forEach((o)=>organSet.add(o));
|
|
8801
8781
|
if (attrs.maxTpm !== null) {
|
|
8802
8782
|
if (attrs.maxTpm < tpmMin) tpmMin = attrs.maxTpm;
|
|
8803
8783
|
if (attrs.maxTpm > tpmMax) tpmMax = attrs.maxTpm;
|
|
8804
8784
|
}
|
|
8805
|
-
byNode[nodeId] = attrs;
|
|
8806
8785
|
});
|
|
8807
8786
|
return {
|
|
8808
8787
|
organs: (0, $4b8473139e48e77e$export$65382efdcdd96f8c)(organSet),
|
|
@@ -8814,16 +8793,34 @@ function $cd8bc494277e92a4$export$965a6b1d7408d496(docs, tree) {
|
|
|
8814
8793
|
}
|
|
8815
8794
|
};
|
|
8816
8795
|
}
|
|
8817
|
-
// Shared row-highlight background, matching the other zones so hovering any zone
|
|
8818
|
-
// lights up the same row across all of them.
|
|
8819
|
-
function $cd8bc494277e92a4$var$rowHighlight(isSelected, isExactHover, isInHoveredSubtree) {
|
|
8820
|
-
if (isSelected) return 'var(--tbrowse-row-select-bg)';
|
|
8821
|
-
if (isExactHover) return 'var(--tbrowse-row-hover-bg)';
|
|
8822
|
-
if (isInHoveredSubtree) return 'var(--tbrowse-row-subtree-bg)';
|
|
8823
|
-
return 'transparent';
|
|
8824
|
-
}
|
|
8825
8796
|
const $cd8bc494277e92a4$var$gridBorder = '1px solid var(--tbrowse-grid-line, rgba(0,0,0,0.06))';
|
|
8826
8797
|
const $cd8bc494277e92a4$var$stressWidth = (width, organCount)=>Math.max($cd8bc494277e92a4$var$STRESS_MIN, width - organCount * $cd8bc494277e92a4$var$ORGAN_CELL_W - $cd8bc494277e92a4$var$MAXTPM_W);
|
|
8798
|
+
// "Expressed only" toggle. Homology applies it: while on (and the zone is
|
|
8799
|
+
// visible) it prunes every branch with no gene that has expression
|
|
8800
|
+
// attributes, and turning it on expands the branches that remain.
|
|
8801
|
+
const $cd8bc494277e92a4$var$ExpressedOnlyToggle = ({ on: on, onChange: onChange })=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("label", {
|
|
8802
|
+
title: "Prune branches without expression data and expand the rest",
|
|
8803
|
+
style: {
|
|
8804
|
+
display: 'inline-flex',
|
|
8805
|
+
alignItems: 'center',
|
|
8806
|
+
gap: 3,
|
|
8807
|
+
fontSize: 10,
|
|
8808
|
+
cursor: 'pointer',
|
|
8809
|
+
whiteSpace: 'nowrap',
|
|
8810
|
+
margin: 0
|
|
8811
|
+
},
|
|
8812
|
+
children: [
|
|
8813
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
|
|
8814
|
+
type: "checkbox",
|
|
8815
|
+
checked: on,
|
|
8816
|
+
onChange: (e)=>onChange(e.target.checked),
|
|
8817
|
+
style: {
|
|
8818
|
+
margin: 0
|
|
8819
|
+
}
|
|
8820
|
+
}),
|
|
8821
|
+
"expressed only"
|
|
8822
|
+
]
|
|
8823
|
+
});
|
|
8827
8824
|
const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState: setZoneState, width: width, data: data, hoveredNodeId: hoveredNodeId })=>{
|
|
8828
8825
|
const ea = data.hostData && data.hostData.exprAttrs || {};
|
|
8829
8826
|
const organs = ea.organs || [];
|
|
@@ -8856,6 +8853,13 @@ const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState:
|
|
|
8856
8853
|
name: next
|
|
8857
8854
|
}))
|
|
8858
8855
|
}),
|
|
8856
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($cd8bc494277e92a4$var$ExpressedOnlyToggle, {
|
|
8857
|
+
on: !!zoneState?.expressedOnly,
|
|
8858
|
+
onChange: (on)=>setZoneState((s)=>({
|
|
8859
|
+
...s ?? {},
|
|
8860
|
+
expressedOnly: on
|
|
8861
|
+
}))
|
|
8862
|
+
}),
|
|
8859
8863
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
8860
8864
|
style: {
|
|
8861
8865
|
display: 'flex',
|
|
@@ -9016,8 +9020,12 @@ const $cd8bc494277e92a4$var$ExprBody = ({ visibleRows: visibleRows, rowRange: ro
|
|
|
9016
9020
|
max: 0
|
|
9017
9021
|
};
|
|
9018
9022
|
const sW = $cd8bc494277e92a4$var$stressWidth(width, organs.length);
|
|
9019
|
-
const totalHeight =
|
|
9020
|
-
const rows =
|
|
9023
|
+
const totalHeight = (0, $gXNCa$tbrowsecore.rowsTotalHeight)(visibleRows);
|
|
9024
|
+
const rows = (0, $gXNCa$tbrowse.rowsWithFlags)(visibleRows, rowRange, {
|
|
9025
|
+
hoveredNodeId: hoveredNodeId,
|
|
9026
|
+
hoveredSubtreeIds: hoveredSubtreeIds,
|
|
9027
|
+
selectedNodeId: selectedNodeId
|
|
9028
|
+
});
|
|
9021
9029
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
9022
9030
|
style: {
|
|
9023
9031
|
position: 'relative',
|
|
@@ -9025,9 +9033,8 @@ const $cd8bc494277e92a4$var$ExprBody = ({ visibleRows: visibleRows, rowRange: ro
|
|
|
9025
9033
|
height: totalHeight,
|
|
9026
9034
|
overflow: 'hidden'
|
|
9027
9035
|
},
|
|
9028
|
-
children: rows.map((r)=>{
|
|
9036
|
+
children: rows.map(({ row: r, background: background })=>{
|
|
9029
9037
|
const gene = byNode[r.nodeId];
|
|
9030
|
-
const background = $cd8bc494277e92a4$var$rowHighlight(selectedNodeId === r.nodeId, hoveredNodeId === r.nodeId, !!(hoveredSubtreeIds && hoveredSubtreeIds.has(r.nodeId)));
|
|
9031
9038
|
const tpmFrac = gene ? (0, $4b8473139e48e77e$export$fc40d061e6c16645)(gene.maxTpm, tpmRange) : null;
|
|
9032
9039
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
9033
9040
|
onMouseEnter: ()=>onHoverNode(r.nodeId),
|
|
@@ -9131,17 +9138,6 @@ const $047461923b1badda$export$964d88edb00bbcaa = (suggestion)=>{
|
|
|
9131
9138
|
|
|
9132
9139
|
|
|
9133
9140
|
|
|
9134
|
-
const $64fad37f770d2bfe$var$genomeZone = (0, $gXNCa$tbrowse.createGenomeZone)({
|
|
9135
|
-
id: 'genome'
|
|
9136
|
-
});
|
|
9137
|
-
// The genome (gene-structure) zone is gated behind the site-config flag
|
|
9138
|
-
// `enable_tbrowse_genome_zone` (defaults to false) — see getTbrowseZones().
|
|
9139
|
-
const $64fad37f770d2bfe$var$TBROWSE_BASE_ZONES = [
|
|
9140
|
-
(0, $gXNCa$tbrowse.treeZone),
|
|
9141
|
-
(0, $gXNCa$tbrowse.labelsZone),
|
|
9142
|
-
(0, $gXNCa$tbrowse.msaZone),
|
|
9143
|
-
(0, $gXNCa$tbrowse.neighborhoodZone)
|
|
9144
|
-
];
|
|
9145
9141
|
// A gene's trees: the main Compara tree (homology.gene_tree) and/or trees in
|
|
9146
9142
|
// clade-specific tree sets, homology.<tree_set> — e.g. a wheat gene may carry
|
|
9147
9143
|
// gene_tree, homology.wheat = {id: 'EplWheatGT_…'} and homology.barley. Solr
|
|
@@ -9174,27 +9170,72 @@ const $64fad37f770d2bfe$var$treeLabel = (ref)=>{
|
|
|
9174
9170
|
const set = ref.field === 'gene_tree' ? 'Compara' : ref.field.charAt(0).toUpperCase() + ref.field.slice(1);
|
|
9175
9171
|
return ref.rootTaxonName ? `${set} \xb7 ${ref.rootTaxonName}` : set;
|
|
9176
9172
|
};
|
|
9173
|
+
const $64fad37f770d2bfe$var$genomeZone = (0, $gXNCa$tbrowse.createGenomeZone)({
|
|
9174
|
+
id: 'genome'
|
|
9175
|
+
});
|
|
9176
|
+
// The genome (gene-structure) zone is gated behind the site-config flag
|
|
9177
|
+
// `enable_tbrowse_genome_zone` (defaults to false) — see getTbrowseZones().
|
|
9178
|
+
const $64fad37f770d2bfe$var$TBROWSE_BASE_ZONES = [
|
|
9179
|
+
(0, $gXNCa$tbrowse.treeZone),
|
|
9180
|
+
(0, $gXNCa$tbrowse.labelsZone),
|
|
9181
|
+
(0, $gXNCa$tbrowse.msaZone),
|
|
9182
|
+
(0, $gXNCa$tbrowse.neighborhoodZone)
|
|
9183
|
+
];
|
|
9184
|
+
// The expression-attributes zone's data, as a slot on tbrowse-core's Gramene
|
|
9185
|
+
// gene-tree source: one tree-scoped /search for the expr_*__attr_* fields
|
|
9186
|
+
// (fetchGrameneExprAttrDocs), shaped by buildExprData and delivered through
|
|
9187
|
+
// hostData.exprAttrs — the same lifecycle as the built-in neighborhood and
|
|
9188
|
+
// gene-structure slots. Enabled per site via the ctx flag the component
|
|
9189
|
+
// derives from `enable_tbrowse_expression_zone`. Scoped by root.meta, which
|
|
9190
|
+
// carries the Solr field holding the tree id: gene_tree, or a clade tree set
|
|
9191
|
+
// such as wheat.
|
|
9192
|
+
const $64fad37f770d2bfe$var$exprAttrsSlot = (0, $gXNCa$tbrowsecore.defineSlot)({
|
|
9193
|
+
id: 'exprAttrs',
|
|
9194
|
+
enabled: (ctx)=>ctx.expression === true,
|
|
9195
|
+
run: async ({ ctx: ctx, fetch: fetch, signal: signal, tree: tree, root: root })=>(0, $cd8bc494277e92a4$export$965a6b1d7408d496)(await (0, $gXNCa$tbrowsecore.fetchGrameneExprAttrDocs)(ctx.base, root.meta.treeId, {
|
|
9196
|
+
treeField: root.meta.treeField,
|
|
9197
|
+
fetch: fetch,
|
|
9198
|
+
signal: signal
|
|
9199
|
+
}), tree),
|
|
9200
|
+
toHostData: (v)=>({
|
|
9201
|
+
hostData: {
|
|
9202
|
+
exprAttrs: v
|
|
9203
|
+
}
|
|
9204
|
+
})
|
|
9205
|
+
});
|
|
9206
|
+
const $64fad37f770d2bfe$var$homologySource = (0, $gXNCa$tbrowsecore.extendSource)((0, $gXNCa$tbrowsecore.grameneGeneTreeSource), {
|
|
9207
|
+
id: 'gramene-homology',
|
|
9208
|
+
slots: [
|
|
9209
|
+
$64fad37f770d2bfe$var$exprAttrsSlot
|
|
9210
|
+
]
|
|
9211
|
+
});
|
|
9212
|
+
// Host zone id → the slot(s) feeding it, so the TBrowse toolbar shows the
|
|
9213
|
+
// per-zone loading / error affordances (TBrowseProps.zoneStatus).
|
|
9214
|
+
const $64fad37f770d2bfe$var$ZONE_SLOTS = {
|
|
9215
|
+
neighborhood: [
|
|
9216
|
+
'neighborhood'
|
|
9217
|
+
],
|
|
9218
|
+
genome: [
|
|
9219
|
+
'geneStructures'
|
|
9220
|
+
],
|
|
9221
|
+
expression: [
|
|
9222
|
+
'exprAttrs'
|
|
9223
|
+
]
|
|
9224
|
+
};
|
|
9177
9225
|
class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$react))).Component {
|
|
9178
9226
|
constructor(props){
|
|
9179
9227
|
super(props);
|
|
9180
|
-
//
|
|
9228
|
+
// The tbrowse data — tree, MSA, domains and junctions adapted from the
|
|
9229
|
+
// stored genetree doc, then the neighborhood / gene-structure / expression
|
|
9230
|
+
// slots — is loaded by a tbrowse-core loader kept on the instance (one per
|
|
9231
|
+
// mount); its snapshot is mirrored into state so renders follow it. The
|
|
9181
9232
|
// user-controlled view state (viewer toggle, resize height, tbrowse
|
|
9182
9233
|
// ViewState) is lifted into the uiViewState bundle, keyed by geneId.
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
// prop. They start `undefined` (no signal); flipped to 'loading'
|
|
9187
|
-
// when the fetch kicks off and 'ready' / 'error' on settle.
|
|
9234
|
+
this.loader = (0, $gXNCa$tbrowsecore.createLoader)($64fad37f770d2bfe$var$homologySource, {
|
|
9235
|
+
zoneSlots: $64fad37f770d2bfe$var$ZONE_SLOTS
|
|
9236
|
+
});
|
|
9188
9237
|
this.state = {
|
|
9189
|
-
|
|
9190
|
-
neighborhoodTreeId: null,
|
|
9191
|
-
neighborhoodStatus: undefined,
|
|
9192
|
-
geneStructures: null,
|
|
9193
|
-
geneStructuresTreeId: null,
|
|
9194
|
-
geneStructuresStatus: undefined,
|
|
9195
|
-
exprAttrs: null,
|
|
9196
|
-
exprAttrsTreeId: null,
|
|
9197
|
-
exprAttrsStatus: undefined
|
|
9238
|
+
snap: this.loader.getSnapshot()
|
|
9198
9239
|
};
|
|
9199
9240
|
if (!props.geneDocs.hasOwnProperty(props.searchResult.id)) props.requestGene(props.searchResult.id);
|
|
9200
9241
|
this.taxonomy = (0, ($parcel$interopDefault($gXNCa$gramenetreesclientsrctaxonomy))).tree(Object.values(props.grameneTaxonomy));
|
|
@@ -9264,248 +9305,119 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9264
9305
|
// When the user has limited the search to a subset of genomes
|
|
9265
9306
|
// (grameneGenomes.active), prune the gene tree to genes from those genomes
|
|
9266
9307
|
// — mirroring the taxon_id filter api.js applies to the search itself, and
|
|
9267
|
-
// the genomesOfInterest TreeVis already honours.
|
|
9268
|
-
//
|
|
9269
|
-
//
|
|
9270
|
-
//
|
|
9271
|
-
//
|
|
9272
|
-
//
|
|
9308
|
+
// the genomesOfInterest TreeVis already honours. tbrowse-core's
|
|
9309
|
+
// maximalExcludedClades returns the ids of the highest nodes whose entire
|
|
9310
|
+
// subtree lies outside the active set (maximal excluded clades), rather
|
|
9311
|
+
// than individual leaves: tbrowse then sheds one stub per clade instead of
|
|
9312
|
+
// one per leaf, and its single-marker count is meaningful. The gene of
|
|
9313
|
+
// interest is always kept so the tree never collapses to nothing. Empty
|
|
9314
|
+
// active set means "no limit" → full tree.
|
|
9273
9315
|
getGenomePrunedIds() {
|
|
9274
9316
|
const active = this.props.grameneGenomes && this.props.grameneGenomes.active || {};
|
|
9275
9317
|
const activeIds = Object.keys(active);
|
|
9276
9318
|
if (activeIds.length === 0) return [];
|
|
9277
|
-
|
|
9319
|
+
const snap = this.state.snap;
|
|
9320
|
+
if (!snap.tree) return [];
|
|
9278
9321
|
const activeSet = new Set(activeIds.map(String));
|
|
9279
9322
|
const keepGeneId = this.gene && this.gene._id;
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
const
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9323
|
+
return (0, $gXNCa$tbrowsecore.maximalExcludedClades)(snap.tree, (leaf)=>leaf.taxonomyId != null && activeSet.has(String(leaf.taxonomyId)) || leaf.geneId === keepGeneId);
|
|
9324
|
+
}
|
|
9325
|
+
// The Expression zone's "expressed only" toggle (zoneStates.expression
|
|
9326
|
+
// .expressedOnly): while it is on and the zone is visible, prune the largest
|
|
9327
|
+
// branches with no leaf carrying expression attributes. The gene of interest
|
|
9328
|
+
// stays, as with the genome prunes, so the root is never pruned.
|
|
9329
|
+
getExpressionPrunedIds(vs, hostProps) {
|
|
9330
|
+
const zs = vs && vs.zoneStates && vs.zoneStates.expression;
|
|
9331
|
+
if (!zs || !zs.expressedOnly) return [];
|
|
9332
|
+
if (!(vs.zones || []).some((z)=>z.id === 'expression' && z.visible)) return [];
|
|
9333
|
+
const ea = hostProps && hostProps.hostData && hostProps.hostData.exprAttrs;
|
|
9334
|
+
if (!ea || !ea.byNode || !hostProps.tree) return [];
|
|
9335
|
+
const keepGeneId = this.gene && this.gene._id;
|
|
9336
|
+
return (0, $gXNCa$tbrowsecore.maximalExcludedClades)(hostProps.tree, (leaf)=>ea.byNode.hasOwnProperty(leaf.id) || leaf.geneId === keepGeneId);
|
|
9337
|
+
}
|
|
9338
|
+
// Turning "expressed only" on also expands every branch, so the expressed
|
|
9339
|
+
// genes left after pruning are all in view. Done here, in the same update
|
|
9340
|
+
// as the toggle, rather than by a second viewer action that would race it.
|
|
9341
|
+
expandOnExpressedOnly(prev, next) {
|
|
9342
|
+
const was = !!(prev && prev.zoneStates && prev.zoneStates.expression && prev.zoneStates.expression.expressedOnly);
|
|
9343
|
+
const is = !!(next && next.zoneStates && next.zoneStates.expression && next.zoneStates.expression.expressedOnly);
|
|
9344
|
+
return !was && is ? {
|
|
9345
|
+
...next,
|
|
9346
|
+
collapsedNodeIds: []
|
|
9347
|
+
} : next;
|
|
9299
9348
|
}
|
|
9300
9349
|
// Seed the bundle with a pivot-computed initial tbrowse view state once the
|
|
9301
|
-
//
|
|
9302
|
-
// viewer
|
|
9350
|
+
// loader has the tree and the user is actually looking at the tbrowse
|
|
9351
|
+
// viewer, from lifecycle (not render) to avoid dispatching mid-render. The
|
|
9352
|
+
// dispatch lands synchronously in the per-gene slice, so the slice check is
|
|
9353
|
+
// the dedupe. buildInitialViewState is the same routine the chassis runs in
|
|
9354
|
+
// uncontrolled mode (each zone's fr-share, initial visibility, default
|
|
9355
|
+
// per-zone state); the pivot is computed for THIS gene — the loader is
|
|
9356
|
+
// shared by every gene of the tree, so its own pivot may be another gene's.
|
|
9303
9357
|
maybeSeedTbrowseViewState() {
|
|
9304
9358
|
if (this.getViewer() !== 'tbrowse') return;
|
|
9305
9359
|
if (this.getHomologySlice().tbrowse) return;
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
const
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
if (!raw || !raw.taxon_id) return;
|
|
9314
|
-
const adapted = (0, $gXNCa$tbrowse.fromGrameneGenetree)([
|
|
9315
|
-
raw
|
|
9316
|
-
]);
|
|
9317
|
-
const pivot = (0, $gXNCa$tbrowse.computePivotState)(adapted.tree, gene._id);
|
|
9318
|
-
this.setTbrowseViewState({
|
|
9319
|
-
selectedNodeId: null,
|
|
9320
|
-
collapsedNodeIds: pivot ? pivot.collapsedNodeIds : [],
|
|
9321
|
-
prunedNodeIds: [],
|
|
9322
|
-
swappedNodeIds: pivot ? pivot.swappedNodeIds : [],
|
|
9323
|
-
compressedNodeIds: [],
|
|
9324
|
-
nodeOfInterestId: pivot ? pivot.targetId : null,
|
|
9325
|
-
// Use each zone's intended fr-share + initial visibility from its
|
|
9326
|
-
// definition (tree:30 / labels:20 / msa:50 / neighborhood:30 /
|
|
9327
|
-
// genome:32) instead of uniform 25 across the board. Gives the MSA
|
|
9328
|
-
// a wider initial pane and the tree a narrower one.
|
|
9329
|
-
//
|
|
9330
|
-
// `defaultVisible ?? true` matches tbrowse's own buildInitialViewState
|
|
9331
|
-
// logic — zones default to visible unless their definition opts out
|
|
9332
|
-
// (e.g. neighborhood and genome opt out so they only appear once
|
|
9333
|
-
// their async data lands; tbrowse's Layout auto-flips them on then).
|
|
9334
|
-
zones: this.getTbrowseZones().map((z)=>({
|
|
9335
|
-
id: z.id,
|
|
9336
|
-
width: z.defaultWidth,
|
|
9337
|
-
visible: z.defaultVisible ?? true
|
|
9338
|
-
})),
|
|
9339
|
-
zoneStates: {},
|
|
9340
|
-
search: null
|
|
9341
|
-
});
|
|
9360
|
+
if (!this.gene) return;
|
|
9361
|
+
// The live snapshot, not the mirrored one: right after a tree switch the
|
|
9362
|
+
// loader has already restarted but state.snap still holds the old tree.
|
|
9363
|
+
const snap = this.loader.getSnapshot();
|
|
9364
|
+
const ref = this.selectedTreeRef(this.gene);
|
|
9365
|
+
if (!ref || snap.status !== 'ready' || !snap.tree || !snap.root || snap.root.meta.treeId !== ref.id) return;
|
|
9366
|
+
this.setTbrowseViewState((0, $gXNCa$tbrowsecore.applyPivot)((0, $gXNCa$tbrowsecore.buildInitialViewState)(this.getTbrowseZones()), (0, $gXNCa$tbrowsecore.computePivotState)(snap.tree, this.gene._id)));
|
|
9342
9367
|
}
|
|
9343
9368
|
componentDidMount() {
|
|
9369
|
+
this._unsubscribe = this.loader.subscribe(()=>this.setState({
|
|
9370
|
+
snap: this.loader.getSnapshot()
|
|
9371
|
+
}));
|
|
9372
|
+
this.maybeStartLoader();
|
|
9344
9373
|
this.maybeSeedTbrowseViewState();
|
|
9345
|
-
this.maybeFetchTbrowseData();
|
|
9346
9374
|
}
|
|
9347
9375
|
componentDidUpdate() {
|
|
9376
|
+
this.maybeStartLoader();
|
|
9348
9377
|
this.maybeSeedTbrowseViewState();
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
//
|
|
9355
|
-
//
|
|
9356
|
-
|
|
9378
|
+
}
|
|
9379
|
+
componentWillUnmount() {
|
|
9380
|
+
if (this._unsubscribe) this._unsubscribe();
|
|
9381
|
+
this.loader.dispose();
|
|
9382
|
+
}
|
|
9383
|
+
// Start the loader (or restart it when the gene's tree changes) from
|
|
9384
|
+
// lifecycle, not render. Gated on the user actually being on the tbrowse
|
|
9385
|
+
// viewer + the tree doc being loaded — otherwise we'd pay network cost for
|
|
9386
|
+
// genes the user never looks at. The stored genetree doc is handed over as
|
|
9387
|
+
// `rawGenetree`, so the source makes no /genes or /genetrees request of its
|
|
9388
|
+
// own (docs.js's taxon-name rewrite stays upstream); the neighborhood,
|
|
9389
|
+
// gene-structure and expression slots are the only fetches, and the loader
|
|
9390
|
+
// retries their transient failures with backoff.
|
|
9391
|
+
maybeStartLoader() {
|
|
9357
9392
|
if (this.getViewer() !== 'tbrowse') return;
|
|
9358
9393
|
const id = this.getGeneId();
|
|
9359
9394
|
if (!this.props.geneDocs.hasOwnProperty(id)) return;
|
|
9360
9395
|
const gene = this.props.geneDocs[id];
|
|
9396
|
+
// The selected tree: gene_tree, or a clade tree set (homology.wheat,
|
|
9397
|
+
// homology.oat, …); none for genes with homologues but no tree. A switch
|
|
9398
|
+
// restarts the loader (new generation, the old slots are aborted).
|
|
9361
9399
|
const ref = this.selectedTreeRef(gene);
|
|
9362
9400
|
if (!ref) return;
|
|
9363
9401
|
const treeId = ref.id;
|
|
9364
9402
|
const raw = this.props.grameneTrees[treeId];
|
|
9365
9403
|
if (!raw || !raw.taxon_id) return;
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
this.
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
this._neighborhoodFetchedFor = treeId;
|
|
9384
|
-
this.setState({
|
|
9385
|
-
neighborhoodStatus: 'loading'
|
|
9386
|
-
});
|
|
9387
|
-
const api = this.props.grameneAPI;
|
|
9388
|
-
const url = new URL(`${api}/search`);
|
|
9389
|
-
const fl = 'id,name,gene_tree,gene_idx,region,start,end,strand,biotype,system_name,description';
|
|
9390
|
-
url.searchParams.set('fl', field === 'gene_tree' ? fl : `${fl},${field}`);
|
|
9391
|
-
// cache=false: as a cached fq the graph query caches its seed inside its own
|
|
9392
|
-
// filterCache computeIfAbsent, and Solr's FastLRUCache throws "Recursive
|
|
9393
|
-
// update" (HTTP 500) whenever the two keys collide — for some trees, until
|
|
9394
|
-
// the seed happens to be cached by another request.
|
|
9395
|
-
url.searchParams.set('fq', `{!graph from=compara_neighbors_10 to=compara_idx_multi maxDepth=1 cache=false}${field}:${treeId}`);
|
|
9396
|
-
url.searchParams.set('rows', '100000');
|
|
9397
|
-
url.searchParams.set('start', '0');
|
|
9398
|
-
fetch(url.toString(), {
|
|
9399
|
-
headers: {
|
|
9400
|
-
Accept: 'application/json'
|
|
9401
|
-
}
|
|
9402
|
-
}).then((res)=>{
|
|
9403
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9404
|
-
return res.json();
|
|
9405
|
-
}).then((json)=>{
|
|
9406
|
-
if (this._neighborhoodFetchedFor !== treeId) return;
|
|
9407
|
-
// The neighborhood zone colours genes by family from doc.gene_tree; in
|
|
9408
|
-
// a clade tree set the family id lives under the set's own field.
|
|
9409
|
-
if (field !== 'gene_tree' && json && json.response && Array.isArray(json.response.docs)) json.response.docs.forEach((d)=>{
|
|
9410
|
-
if (d[field] && !d.gene_tree) d.gene_tree = d[field];
|
|
9411
|
-
});
|
|
9412
|
-
this.setState({
|
|
9413
|
-
neighborhood: (0, $gXNCa$tbrowse.fromGrameneNeighborhood)(json),
|
|
9414
|
-
neighborhoodTreeId: treeId,
|
|
9415
|
-
neighborhoodStatus: 'ready'
|
|
9416
|
-
});
|
|
9417
|
-
}).catch((err)=>{
|
|
9418
|
-
console.warn('tbrowse neighborhood fetch failed:', err);
|
|
9419
|
-
// Surface the failure via the zoneStatus prop. Reset the
|
|
9420
|
-
// dedupe key so a re-mount or tree-change can retry.
|
|
9421
|
-
if (this._neighborhoodFetchedFor === treeId) this.setState({
|
|
9422
|
-
neighborhoodStatus: 'error'
|
|
9423
|
-
});
|
|
9424
|
-
this._neighborhoodFetchedFor = null;
|
|
9425
|
-
});
|
|
9426
|
-
}
|
|
9427
|
-
fetchGeneStructures(treeId, tree) {
|
|
9428
|
-
if (this._geneStructuresFetchedFor === treeId) return;
|
|
9429
|
-
this._geneStructuresFetchedFor = treeId;
|
|
9430
|
-
const ids = Object.values(tree.nodes).filter((n)=>n.isLeaf && n.geneId).map((n)=>n.geneId);
|
|
9431
|
-
if (ids.length === 0) return;
|
|
9432
|
-
this.setState({
|
|
9433
|
-
geneStructuresStatus: 'loading'
|
|
9434
|
-
});
|
|
9435
|
-
const api = this.props.grameneAPI;
|
|
9436
|
-
const BATCH_SIZE = 50;
|
|
9437
|
-
const batches = [];
|
|
9438
|
-
for(let i = 0; i < ids.length; i += BATCH_SIZE)batches.push(ids.slice(i, i + BATCH_SIZE));
|
|
9439
|
-
const fetchBatch = (batch)=>{
|
|
9440
|
-
const url = new URL(`${api}/genes`);
|
|
9441
|
-
url.searchParams.set('idList', batch.join(','));
|
|
9442
|
-
url.searchParams.set('rows', '-1');
|
|
9443
|
-
return fetch(url.toString(), {
|
|
9444
|
-
headers: {
|
|
9445
|
-
Accept: 'application/json'
|
|
9446
|
-
}
|
|
9447
|
-
}).then((res)=>{
|
|
9448
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9449
|
-
return res.json();
|
|
9450
|
-
});
|
|
9451
|
-
};
|
|
9452
|
-
Promise.all(batches.map(fetchBatch)).then((results)=>{
|
|
9453
|
-
if (this._geneStructuresFetchedFor !== treeId) return;
|
|
9454
|
-
const combined = [].concat(...results.map((r)=>Array.isArray(r) ? r : []));
|
|
9455
|
-
this.setState({
|
|
9456
|
-
geneStructures: (0, $gXNCa$tbrowse.fromGrameneGeneStructures)(combined),
|
|
9457
|
-
geneStructuresTreeId: treeId,
|
|
9458
|
-
geneStructuresStatus: 'ready'
|
|
9459
|
-
});
|
|
9460
|
-
}).catch((err)=>{
|
|
9461
|
-
console.warn('tbrowse gene-structures fetch failed:', err);
|
|
9462
|
-
if (this._geneStructuresFetchedFor === treeId) this.setState({
|
|
9463
|
-
geneStructuresStatus: 'error'
|
|
9464
|
-
});
|
|
9465
|
-
this._geneStructuresFetchedFor = null;
|
|
9466
|
-
});
|
|
9467
|
-
}
|
|
9468
|
-
// Fetch the expression attributes (expr_*__attr_*) for this tree's genes in a
|
|
9469
|
-
// single query: scope by the tree's field and the `expression_attributes` capability
|
|
9470
|
-
// so only genes that actually have the data come back (rows high enough to
|
|
9471
|
-
// cover the whole tree). Uses /search (not /genes, whose trimmed docs omit the
|
|
9472
|
-
// attr fields) with an fl glob so the columns are discovered from the data
|
|
9473
|
-
// rather than hard-coded. `tree` is still needed to map gene ids to leaf node
|
|
9474
|
-
// ids (see buildExprByNode).
|
|
9475
|
-
fetchExprAttrs(treeId, tree, field = 'gene_tree') {
|
|
9476
|
-
if (this._exprAttrsFetchedFor === treeId) return;
|
|
9477
|
-
this._exprAttrsFetchedFor = treeId;
|
|
9478
|
-
this.setState({
|
|
9479
|
-
exprAttrsStatus: 'loading'
|
|
9480
|
-
});
|
|
9481
|
-
const api = this.props.grameneAPI;
|
|
9482
|
-
const url = new URL(`${api}/search`);
|
|
9483
|
-
url.searchParams.set('q', '*:*');
|
|
9484
|
-
url.searchParams.append('fq', `${field}:${treeId}`);
|
|
9485
|
-
url.searchParams.append('fq', 'capabilities:expression_attributes');
|
|
9486
|
-
url.searchParams.set('fl', 'id,expr_*__attr_*');
|
|
9487
|
-
url.searchParams.set('rows', '10000');
|
|
9488
|
-
fetch(url.toString(), {
|
|
9489
|
-
headers: {
|
|
9490
|
-
Accept: 'application/json'
|
|
9491
|
-
}
|
|
9492
|
-
}).then((res)=>{
|
|
9493
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9494
|
-
return res.json();
|
|
9495
|
-
}).then((json)=>{
|
|
9496
|
-
if (this._exprAttrsFetchedFor !== treeId) return;
|
|
9497
|
-
const docs = json && json.response && Array.isArray(json.response.docs) ? json.response.docs : [];
|
|
9498
|
-
this.setState({
|
|
9499
|
-
exprAttrs: (0, $cd8bc494277e92a4$export$965a6b1d7408d496)(docs, tree),
|
|
9500
|
-
exprAttrsTreeId: treeId,
|
|
9501
|
-
exprAttrsStatus: 'ready'
|
|
9502
|
-
});
|
|
9503
|
-
}).catch((err)=>{
|
|
9504
|
-
console.warn('tbrowse expression-attrs fetch failed:', err);
|
|
9505
|
-
if (this._exprAttrsFetchedFor === treeId) this.setState({
|
|
9506
|
-
exprAttrsStatus: 'error'
|
|
9507
|
-
});
|
|
9508
|
-
this._exprAttrsFetchedFor = null;
|
|
9404
|
+
const key = `${ref.field}:${treeId}`;
|
|
9405
|
+
if (this._loaderStartedFor === key) return;
|
|
9406
|
+
this._loaderStartedFor = key;
|
|
9407
|
+
this.loader.start({
|
|
9408
|
+
base: this.props.grameneAPI,
|
|
9409
|
+
treeId: treeId,
|
|
9410
|
+
treeField: ref.field,
|
|
9411
|
+
geneId: gene._id,
|
|
9412
|
+
rawGenetree: raw,
|
|
9413
|
+
// Only pay for gene-structure data when the genome zone is enabled; the
|
|
9414
|
+
// request shape (50 ids per /genes call, no fl) is what this tab has
|
|
9415
|
+
// always sent.
|
|
9416
|
+
geneStructures: this.isGenomeZoneEnabled(),
|
|
9417
|
+
geneStructureChunkSize: 50,
|
|
9418
|
+
geneStructureFl: null,
|
|
9419
|
+
// Same for the per-gene expression attributes zone.
|
|
9420
|
+
expression: this.isExpressionZoneEnabled()
|
|
9509
9421
|
});
|
|
9510
9422
|
}
|
|
9511
9423
|
startResize(e) {
|
|
@@ -9558,49 +9470,35 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9558
9470
|
});
|
|
9559
9471
|
}
|
|
9560
9472
|
renderTBrowse() {
|
|
9561
|
-
const
|
|
9562
|
-
|
|
9563
|
-
//
|
|
9564
|
-
// the
|
|
9565
|
-
//
|
|
9566
|
-
if (this._tbrowseTreeId !== treeId) {
|
|
9567
|
-
this._tbrowseTreeId = treeId;
|
|
9568
|
-
this._tbrowseData = (0, $gXNCa$tbrowse.fromGrameneGenetree)([
|
|
9569
|
-
this.props.grameneTrees[treeId]
|
|
9570
|
-
]);
|
|
9571
|
-
}
|
|
9572
|
-
const neighborhood = this.state.neighborhoodTreeId === treeId ? this.state.neighborhood : undefined;
|
|
9573
|
-
const geneStructures = this.state.geneStructuresTreeId === treeId ? this.state.geneStructures : undefined;
|
|
9574
|
-
const exprAttrs = this.state.exprAttrsTreeId === treeId ? this.state.exprAttrs : undefined;
|
|
9575
|
-
// Per-zone status for the TBrowse toolbar — pulses while a fetch
|
|
9576
|
-
// is in flight, turns red on failure. Tracked per-tree so a
|
|
9577
|
-
// tree-change resets any stale 'ready'/'error' from the prior gene.
|
|
9578
|
-
const zoneStatus = {
|
|
9579
|
-
neighborhood: this.state.neighborhoodTreeId === treeId ? this.state.neighborhoodStatus : this.state.neighborhoodStatus === 'loading' ? 'loading' : undefined,
|
|
9580
|
-
genome: this.state.geneStructuresTreeId === treeId ? this.state.geneStructuresStatus : this.state.geneStructuresStatus === 'loading' ? 'loading' : undefined,
|
|
9581
|
-
expression: this.state.exprAttrsTreeId === treeId ? this.state.exprAttrsStatus : this.state.exprAttrsStatus === 'loading' ? 'loading' : undefined
|
|
9582
|
-
};
|
|
9583
|
-
// Bundle-driven (controlled) view state. If we're rendering tbrowse before
|
|
9584
|
-
// componentDidMount/Update has seeded the bundle slice, skip this turn and
|
|
9585
|
-
// let the re-render with the seeded state do the work.
|
|
9473
|
+
const snap = this.state.snap;
|
|
9474
|
+
const hostProps = (0, $gXNCa$tbrowsecore.hostPropsFrom)(snap);
|
|
9475
|
+
// Nothing to draw until the loader has adapted the tree (one tick after
|
|
9476
|
+
// start(), since the stored doc needs no fetch) and the bundle slice has
|
|
9477
|
+
// been seeded by maybeSeedTbrowseViewState (lifecycle, not render).
|
|
9586
9478
|
const tbrowseVS = this.getHomologySlice().tbrowse;
|
|
9587
|
-
|
|
9588
|
-
//
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9479
|
+
// Also wait until the snapshot holds the SELECTED tree, so a switch never
|
|
9480
|
+
// draws the previous tree under the new selection.
|
|
9481
|
+
const isSelected = snap.root && this.treeRef && snap.root.meta.treeId === this.treeRef.id;
|
|
9482
|
+
if (!hostProps || !tbrowseVS || !isSelected) // Reserve the pane's height so the card doesn't jump when the tree lands.
|
|
9483
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
9484
|
+
className: "gene-genetree",
|
|
9485
|
+
style: {
|
|
9486
|
+
height: this.getHeight(),
|
|
9487
|
+
width: '100%'
|
|
9488
|
+
},
|
|
9489
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Spinner), {})
|
|
9490
|
+
});
|
|
9491
|
+
// Layer the genome-limit and "expressed only" prune sets on top of the
|
|
9492
|
+
// user's own prunes for the controlled view state, but persist only the
|
|
9493
|
+
// user's prunes (the layer strips the derived ones in onViewStateChange) so
|
|
9494
|
+
// the bundle stays free of derived state and re-applies cleanly when the
|
|
9495
|
+
// filter or toggle changes.
|
|
9496
|
+
const pruneLayer = (0, $gXNCa$tbrowsecore.createDerivedPruneLayer)([
|
|
9497
|
+
...new Set([
|
|
9498
|
+
...this.getGenomePrunedIds(),
|
|
9499
|
+
...this.getExpressionPrunedIds(tbrowseVS, hostProps)
|
|
9500
|
+
])
|
|
9501
|
+
]);
|
|
9604
9502
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
9605
9503
|
children: [
|
|
9606
9504
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
@@ -9610,27 +9508,16 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9610
9508
|
width: '100%'
|
|
9611
9509
|
},
|
|
9612
9510
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$tbrowse.TBrowse), {
|
|
9613
|
-
|
|
9614
|
-
taxonomy: this._tbrowseData.taxonomy,
|
|
9615
|
-
msa: this._tbrowseData.msa,
|
|
9616
|
-
geneMetadata: this._tbrowseData.geneMetadata,
|
|
9617
|
-
proteinDomains: this._tbrowseData.proteinDomains,
|
|
9618
|
-
exonJunctions: this._tbrowseData.exonJunctions,
|
|
9619
|
-
neighborhood: neighborhood,
|
|
9620
|
-
geneStructures: geneStructures,
|
|
9621
|
-
hostData: exprAttrs ? {
|
|
9622
|
-
exprAttrs: exprAttrs
|
|
9623
|
-
} : undefined,
|
|
9511
|
+
...hostProps,
|
|
9624
9512
|
zones: this.getTbrowseZones(),
|
|
9625
9513
|
nodeOfInterest: this.gene._id,
|
|
9626
|
-
viewState:
|
|
9627
|
-
onViewStateChange:
|
|
9514
|
+
viewState: pruneLayer.effective(tbrowseVS),
|
|
9515
|
+
onViewStateChange: (next)=>this.setTbrowseViewState(this.expandOnExpressedOnly(tbrowseVS, pruneLayer.onChange(next))),
|
|
9628
9516
|
densityControls: true,
|
|
9629
9517
|
defaultOpenSections: {
|
|
9630
9518
|
zones: true,
|
|
9631
9519
|
search: true
|
|
9632
|
-
}
|
|
9633
|
-
zoneStatus: zoneStatus
|
|
9520
|
+
}
|
|
9634
9521
|
})
|
|
9635
9522
|
}),
|
|
9636
9523
|
this.renderResizeHandle()
|
|
@@ -9862,7 +9749,7 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9862
9749
|
});
|
|
9863
9750
|
}
|
|
9864
9751
|
}
|
|
9865
|
-
var $64fad37f770d2bfe$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneTaxonomy', 'selectGrameneTrees', 'selectGrameneGenomes', 'selectGrameneAPI', 'selectConfiguration', 'selectCuration', 'selectUiViewState', 'doRequestGrameneTree', 'doSetHomologyViewer', '
|
|
9752
|
+
var $64fad37f770d2bfe$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneTaxonomy', 'selectGrameneTrees', 'selectGrameneGenomes', 'selectGrameneAPI', 'selectConfiguration', 'selectCuration', 'selectUiViewState', 'doRequestGrameneTree', 'doSetHomologyViewer', 'doSetHomologyHeight', 'doSetHomologyTbrowseViewState', 'doSetHomologyTree', 'doAcceptGrameneSuggestion', 'doReplaceGrameneFilters', $64fad37f770d2bfe$var$Homology);
|
|
9866
9753
|
|
|
9867
9754
|
|
|
9868
9755
|
|
|
@@ -20522,69 +20409,21 @@ var $597fe213417ee6ca$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
20522
20409
|
|
|
20523
20410
|
|
|
20524
20411
|
|
|
20412
|
+
|
|
20525
20413
|
// Adapter: gramene taxonomy-with-genomes tree → tbrowse `Tree` + `Taxonomy`.
|
|
20526
20414
|
//
|
|
20527
20415
|
// `grameneTaxDist` (from the grameneTaxDist bundle's selectGrameneTaxDist) is
|
|
20528
|
-
// the root node of a gramene-taxonomy-with-genomes tree.
|
|
20529
|
-
//
|
|
20530
|
-
//
|
|
20531
|
-
//
|
|
20532
|
-
// tbrowse
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
// NodeIds off the (unique) NCBI taxon id and set `taxonomyId` to the same id.
|
|
20536
|
-
//
|
|
20537
|
-
// Branch lengths are uniform (taxonomy carries none); the tree zone is driven
|
|
20538
|
-
// in 'cladogram' mode for this view, which ignores `distance` and aligns the
|
|
20539
|
-
// genome rows at the right edge regardless.
|
|
20540
|
-
/**
|
|
20541
|
-
* @param {object} taxDist - root node of the gramene taxDist tree
|
|
20542
|
-
* (the object returned by selectGrameneTaxDist).
|
|
20543
|
-
* @returns {{ tree: {rootId: string, nodes: object}, taxonomy: object,
|
|
20544
|
-
* leafTaxonIds: string[] } | null}
|
|
20545
|
-
*/ function $dc4f9c952681c07f$export$5fb520c9ccc6bdef(taxDist) {
|
|
20416
|
+
// the root node of a gramene-taxonomy-with-genomes (tree-model) tree. The
|
|
20417
|
+
// conversion itself — node ids = String(NCBI taxon id), uniform branch lengths
|
|
20418
|
+
// (the tree zone runs in 'cladogram' mode for this view), `model.name` exposed
|
|
20419
|
+
// as both scientific and common name, duplicate-id guard — is
|
|
20420
|
+
// tbrowse-core's `fromTreeModel`, which was lifted from this file.
|
|
20421
|
+
|
|
20422
|
+
function $dc4f9c952681c07f$export$5fb520c9ccc6bdef(taxDist) {
|
|
20546
20423
|
if (!taxDist || !taxDist.model) return null;
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
const rootId = String(taxDist.model.id);
|
|
20551
|
-
const walk = (node, parentId)=>{
|
|
20552
|
-
if (!node || !node.model) return;
|
|
20553
|
-
const id = String(node.model.id);
|
|
20554
|
-
// Guard against the (shouldn't-happen) cyclic / duplicate id case so a
|
|
20555
|
-
// malformed taxonomy can't spin the recursion.
|
|
20556
|
-
if (nodes[id]) return;
|
|
20557
|
-
const kids = node.children || [];
|
|
20558
|
-
const isLeaf = kids.length === 0;
|
|
20559
|
-
nodes[id] = {
|
|
20560
|
-
id: id,
|
|
20561
|
-
parentId: parentId,
|
|
20562
|
-
distance: parentId === null ? 0 : 1,
|
|
20563
|
-
isLeaf: isLeaf,
|
|
20564
|
-
taxonomyId: id
|
|
20565
|
-
};
|
|
20566
|
-
// `model.name` is the NCBI scientific name for clades and is overwritten
|
|
20567
|
-
// with the genome's display_name for genome leaves upstream in
|
|
20568
|
-
// selectGrameneTaxDist. Expose it as both names so the labels zone shows
|
|
20569
|
-
// something whichever field the user has active.
|
|
20570
|
-
const name = node.model.name;
|
|
20571
|
-
taxonomy[id] = {
|
|
20572
|
-
scientificName: name,
|
|
20573
|
-
commonName: name,
|
|
20574
|
-
rank: node.model.rank
|
|
20575
|
-
};
|
|
20576
|
-
if (isLeaf) leafTaxonIds.push(id);
|
|
20577
|
-
for (const k of kids)walk(k, id);
|
|
20578
|
-
};
|
|
20579
|
-
walk(taxDist, null);
|
|
20580
|
-
return {
|
|
20581
|
-
tree: {
|
|
20582
|
-
rootId: rootId,
|
|
20583
|
-
nodes: nodes
|
|
20584
|
-
},
|
|
20585
|
-
taxonomy: taxonomy,
|
|
20586
|
-
leafTaxonIds: leafTaxonIds
|
|
20587
|
-
};
|
|
20424
|
+
return (0, $gXNCa$tbrowsecore.fromTreeModel)(taxDist, {
|
|
20425
|
+
taxonomyIdAs: 'string'
|
|
20426
|
+
});
|
|
20588
20427
|
}
|
|
20589
20428
|
|
|
20590
20429
|
|
|
@@ -20611,6 +20450,7 @@ var $597fe213417ee6ca$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
20611
20450
|
|
|
20612
20451
|
|
|
20613
20452
|
|
|
20453
|
+
|
|
20614
20454
|
// ── colour model (ported from gramene-search-vis util/colors.js) ──────────
|
|
20615
20455
|
// schemeCategory10, kept as RGB triples so we need no d3 dependency here.
|
|
20616
20456
|
const $bfd29d54a0f0e853$var$REGION_RGB = [
|
|
@@ -21138,12 +20978,6 @@ const $bfd29d54a0f0e853$var$BinsHeader = ({ data: data, zoneState: zoneState, se
|
|
|
21138
20978
|
// lets the row-highlight background (rendered in the layer beneath the canvas)
|
|
21139
20979
|
// show as bands above/below the strip on hover/select.
|
|
21140
20980
|
const $bfd29d54a0f0e853$var$ROW_PAD_Y = 2;
|
|
21141
|
-
function $bfd29d54a0f0e853$var$rowHighlight(isSelected, isExactHover, isInHoveredSubtree) {
|
|
21142
|
-
if (isSelected) return 'var(--tbrowse-row-select-bg)';
|
|
21143
|
-
if (isExactHover) return 'var(--tbrowse-row-hover-bg)';
|
|
21144
|
-
if (isInHoveredSubtree) return 'var(--tbrowse-row-subtree-bg)';
|
|
21145
|
-
return 'transparent';
|
|
21146
|
-
}
|
|
21147
20981
|
const $bfd29d54a0f0e853$var$BinsBody = (props)=>{
|
|
21148
20982
|
const { visibleRows: visibleRows, rowRange: rowRange, width: width, data: data, zoneState: zoneState, hoveredNodeId: hoveredNodeId, hoveredSubtreeIds: hoveredSubtreeIds, selectedNodeId: selectedNodeId, onHoverNode: onHoverNode, onSelectNode: onSelectNode } = props;
|
|
21149
20983
|
const canvasRef = (0, $gXNCa$react.useRef)(null);
|
|
@@ -21157,7 +20991,7 @@ const $bfd29d54a0f0e853$var$BinsBody = (props)=>{
|
|
|
21157
20991
|
const snap = (0, $gXNCa$react.useSyncExternalStore)(store ? store.subscribe : $bfd29d54a0f0e853$var$NOOP_SUB, store ? store.getState : $bfd29d54a0f0e853$var$NOOP_GET);
|
|
21158
20992
|
const transform = snap.transform || $bfd29d54a0f0e853$var$DEFAULT_TRANSFORM;
|
|
21159
20993
|
const mode = zoneState && zoneState.mode || 'select';
|
|
21160
|
-
const totalHeight =
|
|
20994
|
+
const totalHeight = (0, $gXNCa$tbrowsecore.rowsTotalHeight)(visibleRows);
|
|
21161
20995
|
const bins = data.hostData && data.hostData.bins;
|
|
21162
20996
|
const rows = visibleRows.slice(rowRange.startIndex, rowRange.endIndex);
|
|
21163
20997
|
const w = Math.max(1, Math.floor(width));
|
|
@@ -21410,7 +21244,11 @@ const $bfd29d54a0f0e853$var$BinsBody = (props)=>{
|
|
|
21410
21244
|
left: 0,
|
|
21411
21245
|
right: 0,
|
|
21412
21246
|
height: r.height,
|
|
21413
|
-
background:
|
|
21247
|
+
background: (0, $gXNCa$tbrowse.rowHighlightBackground)((0, $gXNCa$tbrowsecore.rowFlags)(r, {
|
|
21248
|
+
hoveredNodeId: hoveredNodeId,
|
|
21249
|
+
hoveredSubtreeIds: hoveredSubtreeIds,
|
|
21250
|
+
selectedNodeId: selectedNodeId
|
|
21251
|
+
})),
|
|
21414
21252
|
cursor: rowCursor,
|
|
21415
21253
|
opacity: r.opacity ?? 1
|
|
21416
21254
|
}
|
|
@@ -21450,37 +21288,12 @@ const $bfd29d54a0f0e853$export$d85d069f2280460c = {
|
|
|
21450
21288
|
// Host-defined tbrowse zone: a narrow, right-justified text column showing the
|
|
21451
21289
|
// number of matching genes per leaf (genome) — or, for a collapsed clade, the
|
|
21452
21290
|
// summed count over its subtree. Counts come from the taxon_id facet; the
|
|
21453
|
-
// per-node sums are
|
|
21454
|
-
// hostData.geneCounts = { [nodeId]: number }.
|
|
21291
|
+
// per-node sums are computed by tbrowse-core's `countsFromFacet` in the
|
|
21292
|
+
// container and handed in via hostData.geneCounts = { [nodeId]: number }.
|
|
21293
|
+
|
|
21455
21294
|
|
|
21456
21295
|
|
|
21457
21296
|
|
|
21458
|
-
function $15275c4f5939e11d$export$ce87f9041549c2e9(tree, taxonIdFacet) {
|
|
21459
|
-
if (!tree || !tree.nodes) return {};
|
|
21460
|
-
const leafCount = {};
|
|
21461
|
-
if (Array.isArray(taxonIdFacet)) for(let i = 0; i < taxonIdFacet.length; i += 2)leafCount[String(taxonIdFacet[i])] = +taxonIdFacet[i + 1];
|
|
21462
|
-
const childrenOf = {};
|
|
21463
|
-
for (const id of Object.keys(tree.nodes)){
|
|
21464
|
-
const p = tree.nodes[id].parentId;
|
|
21465
|
-
if (p !== null && p !== undefined) (childrenOf[p] = childrenOf[p] || []).push(id);
|
|
21466
|
-
}
|
|
21467
|
-
const out = {};
|
|
21468
|
-
const visit = (id)=>{
|
|
21469
|
-
const node = tree.nodes[id];
|
|
21470
|
-
if (!node) return 0;
|
|
21471
|
-
if (node.isLeaf) {
|
|
21472
|
-
const c = leafCount[id] || 0;
|
|
21473
|
-
out[id] = c;
|
|
21474
|
-
return c;
|
|
21475
|
-
}
|
|
21476
|
-
let sum = 0;
|
|
21477
|
-
for (const k of childrenOf[id] || [])sum += visit(k);
|
|
21478
|
-
out[id] = sum;
|
|
21479
|
-
return sum;
|
|
21480
|
-
};
|
|
21481
|
-
visit(tree.rootId);
|
|
21482
|
-
return out;
|
|
21483
|
-
}
|
|
21484
21297
|
const $15275c4f5939e11d$var$CountsHeader = ({ zoneState: zoneState, setZoneState: setZoneState })=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21485
21298
|
style: {
|
|
21486
21299
|
padding: '0 10px',
|
|
@@ -21498,28 +21311,24 @@ const $15275c4f5939e11d$var$CountsHeader = ({ zoneState: zoneState, setZoneState
|
|
|
21498
21311
|
}))
|
|
21499
21312
|
})
|
|
21500
21313
|
});
|
|
21501
|
-
// Shared row-highlight background, matching the labels zone so hovering any
|
|
21502
|
-
// zone lights up the same row across all of them (state flows through the
|
|
21503
|
-
// tbrowse store's hoveredNodeId).
|
|
21504
|
-
function $15275c4f5939e11d$var$rowHighlight(isSelected, isExactHover, isInHoveredSubtree) {
|
|
21505
|
-
if (isSelected) return 'var(--tbrowse-row-select-bg)';
|
|
21506
|
-
if (isExactHover) return 'var(--tbrowse-row-hover-bg)';
|
|
21507
|
-
if (isInHoveredSubtree) return 'var(--tbrowse-row-subtree-bg)';
|
|
21508
|
-
return 'transparent';
|
|
21509
|
-
}
|
|
21510
21314
|
const $15275c4f5939e11d$var$CountsBody = ({ visibleRows: visibleRows, rowRange: rowRange, width: width, data: data, hoveredNodeId: hoveredNodeId, hoveredSubtreeIds: hoveredSubtreeIds, selectedNodeId: selectedNodeId, onHoverNode: onHoverNode, onSelectNode: onSelectNode })=>{
|
|
21511
|
-
const totalHeight =
|
|
21315
|
+
const totalHeight = (0, $gXNCa$tbrowsecore.rowsTotalHeight)(visibleRows);
|
|
21512
21316
|
const counts = data.hostData && data.hostData.geneCounts || {};
|
|
21513
|
-
|
|
21317
|
+
// Rows in the virtualised range with the shared hover/select background, so
|
|
21318
|
+
// hovering any zone lights up the same row across all of them.
|
|
21319
|
+
const rows = (0, $gXNCa$tbrowse.rowsWithFlags)(visibleRows, rowRange, {
|
|
21320
|
+
hoveredNodeId: hoveredNodeId,
|
|
21321
|
+
hoveredSubtreeIds: hoveredSubtreeIds,
|
|
21322
|
+
selectedNodeId: selectedNodeId
|
|
21323
|
+
});
|
|
21514
21324
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21515
21325
|
style: {
|
|
21516
21326
|
position: 'relative',
|
|
21517
21327
|
width: width,
|
|
21518
21328
|
height: totalHeight
|
|
21519
21329
|
},
|
|
21520
|
-
children: rows.map((r)=>{
|
|
21330
|
+
children: rows.map(({ row: r, background: background })=>{
|
|
21521
21331
|
const n = counts[r.nodeId];
|
|
21522
|
-
const background = $15275c4f5939e11d$var$rowHighlight(selectedNodeId === r.nodeId, hoveredNodeId === r.nodeId, !!(hoveredSubtreeIds && hoveredSubtreeIds.has(r.nodeId)));
|
|
21523
21332
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21524
21333
|
onMouseEnter: ()=>onHoverNode(r.nodeId),
|
|
21525
21334
|
onMouseLeave: ()=>onHoverNode(null),
|
|
@@ -21589,56 +21398,37 @@ function $15504f5eba8e73bd$var$binIdxsToSolr(idxs) {
|
|
|
21589
21398
|
}
|
|
21590
21399
|
return parts.join(' OR ');
|
|
21591
21400
|
}
|
|
21401
|
+
// The initial controlled ViewState: tbrowse-core's buildInitialViewState over
|
|
21402
|
+
// this view's zone list with the view's own fr-shares, and — passed through
|
|
21403
|
+
// `overrides` so they replace the seeded defaults verbatim — the per-zone states
|
|
21404
|
+
// exactly as this view has always persisted them. The tree zone tolerates a
|
|
21405
|
+
// partial state (reads with `?? default`); the labels zone reads `visibleFields`
|
|
21406
|
+
// directly, so it must be present. Drive the tree as a cladogram (taxonomy has
|
|
21407
|
+
// no branch lengths) and show the genome/species name in the labels column.
|
|
21592
21408
|
function $15504f5eba8e73bd$var$baseViewState() {
|
|
21593
|
-
return {
|
|
21594
|
-
|
|
21595
|
-
|
|
21596
|
-
|
|
21597
|
-
|
|
21598
|
-
|
|
21599
|
-
nodeOfInterestId: null,
|
|
21600
|
-
zones: [
|
|
21601
|
-
{
|
|
21602
|
-
id: 'tree',
|
|
21603
|
-
width: 30,
|
|
21604
|
-
visible: true
|
|
21605
|
-
},
|
|
21606
|
-
{
|
|
21607
|
-
id: 'labels',
|
|
21608
|
-
width: 22,
|
|
21609
|
-
visible: true
|
|
21610
|
-
},
|
|
21611
|
-
{
|
|
21612
|
-
id: 'counts',
|
|
21613
|
-
width: 10,
|
|
21614
|
-
visible: true
|
|
21615
|
-
},
|
|
21616
|
-
{
|
|
21617
|
-
id: 'bins',
|
|
21618
|
-
width: 60,
|
|
21619
|
-
visible: true
|
|
21620
|
-
}
|
|
21621
|
-
],
|
|
21622
|
-
// Controlled mode supplies zoneStates wholesale, so each must carry the
|
|
21623
|
-
// fields its zone reads. The tree zone tolerates a partial state (reads
|
|
21624
|
-
// with `?? default`); the labels zone reads `visibleFields` directly, so
|
|
21625
|
-
// it must be present. Drive the tree as a cladogram (taxonomy has no
|
|
21626
|
-
// branch lengths) and show the genome/species name in the labels column.
|
|
21627
|
-
zoneStates: {
|
|
21628
|
-
tree: {
|
|
21629
|
-
layoutMode: 'cladogram',
|
|
21630
|
-
showLeafExtensions: false
|
|
21631
|
-
},
|
|
21632
|
-
labels: {
|
|
21633
|
-
visibleFields: [
|
|
21634
|
-
'taxonomy.commonName'
|
|
21635
|
-
]
|
|
21636
|
-
},
|
|
21637
|
-
counts: {},
|
|
21638
|
-
bins: {}
|
|
21409
|
+
return (0, $gXNCa$tbrowsecore.buildInitialViewState)($15504f5eba8e73bd$var$ZONES, {
|
|
21410
|
+
widths: {
|
|
21411
|
+
tree: 30,
|
|
21412
|
+
labels: 22,
|
|
21413
|
+
counts: 10,
|
|
21414
|
+
bins: 60
|
|
21639
21415
|
},
|
|
21640
|
-
|
|
21641
|
-
|
|
21416
|
+
overrides: {
|
|
21417
|
+
zoneStates: {
|
|
21418
|
+
tree: {
|
|
21419
|
+
layoutMode: 'cladogram',
|
|
21420
|
+
showLeafExtensions: false
|
|
21421
|
+
},
|
|
21422
|
+
labels: {
|
|
21423
|
+
visibleFields: [
|
|
21424
|
+
'taxonomy.commonName'
|
|
21425
|
+
]
|
|
21426
|
+
},
|
|
21427
|
+
counts: {},
|
|
21428
|
+
bins: {}
|
|
21429
|
+
}
|
|
21430
|
+
}
|
|
21431
|
+
});
|
|
21642
21432
|
}
|
|
21643
21433
|
// The set of taxa to SHOW, mirroring the legacy TaxDist.js logic.
|
|
21644
21434
|
function $15504f5eba8e73bd$var$computeSelectedTaxa({ grameneSearch: grameneSearch, grameneGenomes: grameneGenomes, grameneMaps: grameneMaps }, treeNodes, collapseEmpties, comparaOnly, showCompara) {
|
|
@@ -21702,7 +21492,7 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21702
21492
|
const facet = props.grameneSearch && props.grameneSearch.facet_counts && props.grameneSearch.facet_counts.facet_fields && props.grameneSearch.facet_counts.facet_fields.taxon_id;
|
|
21703
21493
|
return {
|
|
21704
21494
|
bins: (0, $bfd29d54a0f0e853$export$390e10a52782f018)(grameneTaxDist),
|
|
21705
|
-
geneCounts: adapter ? (0, $
|
|
21495
|
+
geneCounts: adapter ? (0, $gXNCa$tbrowsecore.countsFromFacet)(adapter.tree, facet, 'nodeId') : null,
|
|
21706
21496
|
binsUI: binsUiRef.current,
|
|
21707
21497
|
onApplyBinsFilter: onApplyBinsFilter
|
|
21708
21498
|
};
|
|
@@ -21728,7 +21518,7 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21728
21518
|
const prunedNodeIds = (0, $gXNCa$react.useMemo)(()=>{
|
|
21729
21519
|
if (!adapter) return [];
|
|
21730
21520
|
const selected = $15504f5eba8e73bd$var$computeSelectedTaxa(props, adapter.tree.nodes, collapseEmpties, comparaOnly, showCompara);
|
|
21731
|
-
return adapter.
|
|
21521
|
+
return (0, $gXNCa$tbrowsecore.leavesFailing)(adapter.tree, (leaf)=>Boolean(selected[String(leaf.taxonomyId)]));
|
|
21732
21522
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
21733
21523
|
}, [
|
|
21734
21524
|
adapter,
|
|
@@ -21740,7 +21530,9 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21740
21530
|
props.grameneMaps
|
|
21741
21531
|
]);
|
|
21742
21532
|
// Effective (controlled) view state = user interactions + control-driven
|
|
21743
|
-
// pruning.
|
|
21533
|
+
// pruning. The derived set REPLACES prunedNodeIds (this view has no user
|
|
21534
|
+
// prunes of its own; the snapshot strips the field for the same reason).
|
|
21535
|
+
// Memoised so the reference is stable across unrelated renders.
|
|
21744
21536
|
const effectiveViewState = (0, $gXNCa$react.useMemo)(()=>({
|
|
21745
21537
|
...viewState,
|
|
21746
21538
|
prunedNodeIds: prunedNodeIds
|
|
@@ -21761,20 +21553,18 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21761
21553
|
const el = wrapperRef.current;
|
|
21762
21554
|
if (!el || el.clientWidth <= 0 || window.innerWidth < 800) return;
|
|
21763
21555
|
sizedRef.current = true;
|
|
21764
|
-
const
|
|
21556
|
+
const widths = (0, $gXNCa$tbrowsecore.pixelWidthsToFr)({
|
|
21765
21557
|
tree: 270,
|
|
21766
21558
|
labels: 200,
|
|
21767
21559
|
counts: 64
|
|
21768
|
-
};
|
|
21769
|
-
const used = TARGET.tree + TARGET.labels + TARGET.counts;
|
|
21770
|
-
const binsW = Math.max(160, el.clientWidth - used);
|
|
21560
|
+
}, el.clientWidth, 'bins', 160);
|
|
21771
21561
|
const base = $15504f5eba8e73bd$var$baseViewState();
|
|
21772
21562
|
setTaxTreeView({
|
|
21773
21563
|
viewState: {
|
|
21774
21564
|
...base,
|
|
21775
21565
|
zones: base.zones.map((z)=>({
|
|
21776
21566
|
...z,
|
|
21777
|
-
width:
|
|
21567
|
+
width: widths[z.id] ?? z.width
|
|
21778
21568
|
}))
|
|
21779
21569
|
}
|
|
21780
21570
|
});
|