gramene-search 2.39.0 → 2.40.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 +294 -495
- 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,6 +8362,18 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
|
|
|
8356
8362
|
studies,
|
|
8357
8363
|
geneStudyIds
|
|
8358
8364
|
]);
|
|
8365
|
+
// Without a baseline study the EBI Studies heatmap has nothing to draw (its
|
|
8366
|
+
// request fails rather than coming back empty), so the tab says so and
|
|
8367
|
+
// points at the differential EBI studies the gene does appear in -- e.g.
|
|
8368
|
+
// Medicago truncatula, whose studies are all differential.
|
|
8369
|
+
const ebiDifferential = (0, $gXNCa$react.useMemo)(()=>{
|
|
8370
|
+
if (!geneStudyIds || !studies) return [];
|
|
8371
|
+
const mine = new Set(geneStudyIds);
|
|
8372
|
+
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);
|
|
8373
|
+
}, [
|
|
8374
|
+
studies,
|
|
8375
|
+
geneStudyIds
|
|
8376
|
+
]);
|
|
8359
8377
|
const allJgiKey = JSON.stringify(allJgiStudies.map((e)=>[
|
|
8360
8378
|
e._id,
|
|
8361
8379
|
e.name
|
|
@@ -8454,7 +8472,43 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
|
|
|
8454
8472
|
role: "status",
|
|
8455
8473
|
children: "Loading expression studies\u2026"
|
|
8456
8474
|
}),
|
|
8457
|
-
activeTab === "gene" && studiesSettled && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
8475
|
+
activeTab === "gene" && studiesSettled && !hasEbiBaseline && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
8476
|
+
className: "text-muted small",
|
|
8477
|
+
role: "status",
|
|
8478
|
+
children: [
|
|
8479
|
+
gene.name || gene._id,
|
|
8480
|
+
" is not in any baseline expression study, so there is no heatmap here.",
|
|
8481
|
+
ebiDifferential.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
8482
|
+
children: [
|
|
8483
|
+
' ',
|
|
8484
|
+
"It has results in ",
|
|
8485
|
+
ebiDifferential.length === 1 ? 'a differential study' : `${ebiDifferential.length} differential studies`,
|
|
8486
|
+
" at Expression Atlas:",
|
|
8487
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("ul", {
|
|
8488
|
+
className: "mt-1 mb-0",
|
|
8489
|
+
children: ebiDifferential.map((e)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
|
|
8490
|
+
children: [
|
|
8491
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("a", {
|
|
8492
|
+
href: `${$9e29a4f60318db7a$var$EBI_GXA}experiments/${encodeURIComponent(e._id)}/Results?geneQuery=${encodeURIComponent(JSON.stringify([
|
|
8493
|
+
{
|
|
8494
|
+
value: geneQuery
|
|
8495
|
+
}
|
|
8496
|
+
]))}`,
|
|
8497
|
+
target: "_blank",
|
|
8498
|
+
rel: "noopener noreferrer",
|
|
8499
|
+
children: e.description || e._id
|
|
8500
|
+
}),
|
|
8501
|
+
" (",
|
|
8502
|
+
e._id,
|
|
8503
|
+
")"
|
|
8504
|
+
]
|
|
8505
|
+
}, e._id))
|
|
8506
|
+
})
|
|
8507
|
+
]
|
|
8508
|
+
})
|
|
8509
|
+
]
|
|
8510
|
+
}),
|
|
8511
|
+
activeTab === "gene" && studiesSettled && hasEbiBaseline && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$grameneatlasheatmap.ExpressionAtlasHeatmap), {
|
|
8458
8512
|
...$9e29a4f60318db7a$var$HEATMAP_OPTIONS,
|
|
8459
8513
|
resolveUrl: resolveUrl,
|
|
8460
8514
|
atlasUrl: atlasUrl,
|
|
@@ -8545,6 +8599,7 @@ var $9e29a4f60318db7a$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
8545
8599
|
|
|
8546
8600
|
|
|
8547
8601
|
|
|
8602
|
+
|
|
8548
8603
|
const $5c2c79352d3d7b81$export$ec6b54b688be1708 = ({ fullscreen: fullscreen, onExitFullscreen: onExitFullscreen, title: title, className: className, children: children })=>{
|
|
8549
8604
|
const [stableNode] = (0, $gXNCa$react.useState)(()=>typeof document !== 'undefined' ? document.createElement('div') : null);
|
|
8550
8605
|
const inlineRef = (0, $gXNCa$react.useRef)(null);
|
|
@@ -8728,33 +8783,28 @@ const $5c2c79352d3d7b81$export$b2e089eb3692b073 = (props)=>/*#__PURE__*/ (0, $gX
|
|
|
8728
8783
|
|
|
8729
8784
|
|
|
8730
8785
|
|
|
8786
|
+
|
|
8731
8787
|
const $cd8bc494277e92a4$var$ORGAN_CELL_W = 16;
|
|
8732
8788
|
const $cd8bc494277e92a4$var$MAXTPM_W = 46;
|
|
8733
8789
|
const $cd8bc494277e92a4$var$STRESS_MIN = 120;
|
|
8734
8790
|
function $cd8bc494277e92a4$export$965a6b1d7408d496(docs, tree) {
|
|
8735
|
-
const
|
|
8736
|
-
const nodeGene = {}; // leaf nodeId -> gene id
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
nodeGene[n.id] = n.geneId;
|
|
8741
|
-
}
|
|
8791
|
+
const hasTree = Boolean(tree && tree.nodes);
|
|
8792
|
+
const nodeGene = hasTree ? (0, $gXNCa$tbrowsecore.geneIdByLeafNodeId)(tree) : {}; // leaf nodeId -> gene id (hover readout)
|
|
8793
|
+
const byGeneId = {};
|
|
8794
|
+
(docs || []).forEach((d)=>{
|
|
8795
|
+
if (d && d.id) byGeneId[d.id] = (0, $4b8473139e48e77e$export$69fb5d19b5db1cfa)(d);
|
|
8742
8796
|
});
|
|
8797
|
+
// Docs whose gene is not a leaf of this tree are dropped (joinByLeaf's `unmatched`).
|
|
8798
|
+
const byNode = hasTree ? (0, $gXNCa$tbrowsecore.joinByLeaf)(tree, byGeneId).byNode : {};
|
|
8743
8799
|
const organSet = new Set();
|
|
8744
|
-
const byNode = {};
|
|
8745
8800
|
let tpmMin = Infinity;
|
|
8746
8801
|
let tpmMax = -Infinity;
|
|
8747
|
-
(
|
|
8748
|
-
if (!d || !d.id) return;
|
|
8749
|
-
const nodeId = nodeOf[d.id];
|
|
8750
|
-
if (!nodeId) return;
|
|
8751
|
-
const attrs = (0, $4b8473139e48e77e$export$69fb5d19b5db1cfa)(d);
|
|
8802
|
+
Object.values(byNode).forEach((attrs)=>{
|
|
8752
8803
|
Object.keys(attrs.organLevels).forEach((o)=>organSet.add(o));
|
|
8753
8804
|
if (attrs.maxTpm !== null) {
|
|
8754
8805
|
if (attrs.maxTpm < tpmMin) tpmMin = attrs.maxTpm;
|
|
8755
8806
|
if (attrs.maxTpm > tpmMax) tpmMax = attrs.maxTpm;
|
|
8756
8807
|
}
|
|
8757
|
-
byNode[nodeId] = attrs;
|
|
8758
8808
|
});
|
|
8759
8809
|
return {
|
|
8760
8810
|
organs: (0, $4b8473139e48e77e$export$65382efdcdd96f8c)(organSet),
|
|
@@ -8766,14 +8816,6 @@ function $cd8bc494277e92a4$export$965a6b1d7408d496(docs, tree) {
|
|
|
8766
8816
|
}
|
|
8767
8817
|
};
|
|
8768
8818
|
}
|
|
8769
|
-
// Shared row-highlight background, matching the other zones so hovering any zone
|
|
8770
|
-
// lights up the same row across all of them.
|
|
8771
|
-
function $cd8bc494277e92a4$var$rowHighlight(isSelected, isExactHover, isInHoveredSubtree) {
|
|
8772
|
-
if (isSelected) return 'var(--tbrowse-row-select-bg)';
|
|
8773
|
-
if (isExactHover) return 'var(--tbrowse-row-hover-bg)';
|
|
8774
|
-
if (isInHoveredSubtree) return 'var(--tbrowse-row-subtree-bg)';
|
|
8775
|
-
return 'transparent';
|
|
8776
|
-
}
|
|
8777
8819
|
const $cd8bc494277e92a4$var$gridBorder = '1px solid var(--tbrowse-grid-line, rgba(0,0,0,0.06))';
|
|
8778
8820
|
const $cd8bc494277e92a4$var$stressWidth = (width, organCount)=>Math.max($cd8bc494277e92a4$var$STRESS_MIN, width - organCount * $cd8bc494277e92a4$var$ORGAN_CELL_W - $cd8bc494277e92a4$var$MAXTPM_W);
|
|
8779
8821
|
const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState: setZoneState, width: width, data: data, hoveredNodeId: hoveredNodeId })=>{
|
|
@@ -8968,8 +9010,12 @@ const $cd8bc494277e92a4$var$ExprBody = ({ visibleRows: visibleRows, rowRange: ro
|
|
|
8968
9010
|
max: 0
|
|
8969
9011
|
};
|
|
8970
9012
|
const sW = $cd8bc494277e92a4$var$stressWidth(width, organs.length);
|
|
8971
|
-
const totalHeight =
|
|
8972
|
-
const rows =
|
|
9013
|
+
const totalHeight = (0, $gXNCa$tbrowsecore.rowsTotalHeight)(visibleRows);
|
|
9014
|
+
const rows = (0, $gXNCa$tbrowse.rowsWithFlags)(visibleRows, rowRange, {
|
|
9015
|
+
hoveredNodeId: hoveredNodeId,
|
|
9016
|
+
hoveredSubtreeIds: hoveredSubtreeIds,
|
|
9017
|
+
selectedNodeId: selectedNodeId
|
|
9018
|
+
});
|
|
8973
9019
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
8974
9020
|
style: {
|
|
8975
9021
|
position: 'relative',
|
|
@@ -8977,9 +9023,8 @@ const $cd8bc494277e92a4$var$ExprBody = ({ visibleRows: visibleRows, rowRange: ro
|
|
|
8977
9023
|
height: totalHeight,
|
|
8978
9024
|
overflow: 'hidden'
|
|
8979
9025
|
},
|
|
8980
|
-
children: rows.map((r)=>{
|
|
9026
|
+
children: rows.map(({ row: r, background: background })=>{
|
|
8981
9027
|
const gene = byNode[r.nodeId];
|
|
8982
|
-
const background = $cd8bc494277e92a4$var$rowHighlight(selectedNodeId === r.nodeId, hoveredNodeId === r.nodeId, !!(hoveredSubtreeIds && hoveredSubtreeIds.has(r.nodeId)));
|
|
8983
9028
|
const tpmFrac = gene ? (0, $4b8473139e48e77e$export$fc40d061e6c16645)(gene.maxTpm, tpmRange) : null;
|
|
8984
9029
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
8985
9030
|
onMouseEnter: ()=>onHoverNode(r.nodeId),
|
|
@@ -9083,17 +9128,6 @@ const $047461923b1badda$export$964d88edb00bbcaa = (suggestion)=>{
|
|
|
9083
9128
|
|
|
9084
9129
|
|
|
9085
9130
|
|
|
9086
|
-
const $64fad37f770d2bfe$var$genomeZone = (0, $gXNCa$tbrowse.createGenomeZone)({
|
|
9087
|
-
id: 'genome'
|
|
9088
|
-
});
|
|
9089
|
-
// The genome (gene-structure) zone is gated behind the site-config flag
|
|
9090
|
-
// `enable_tbrowse_genome_zone` (defaults to false) — see getTbrowseZones().
|
|
9091
|
-
const $64fad37f770d2bfe$var$TBROWSE_BASE_ZONES = [
|
|
9092
|
-
(0, $gXNCa$tbrowse.treeZone),
|
|
9093
|
-
(0, $gXNCa$tbrowse.labelsZone),
|
|
9094
|
-
(0, $gXNCa$tbrowse.msaZone),
|
|
9095
|
-
(0, $gXNCa$tbrowse.neighborhoodZone)
|
|
9096
|
-
];
|
|
9097
9131
|
// A gene's trees: the main Compara tree (homology.gene_tree) and/or trees in
|
|
9098
9132
|
// clade-specific tree sets, homology.<tree_set> — e.g. a wheat gene may carry
|
|
9099
9133
|
// gene_tree, homology.wheat = {id: 'EplWheatGT_…'} and homology.barley. Solr
|
|
@@ -9126,27 +9160,72 @@ const $64fad37f770d2bfe$var$treeLabel = (ref)=>{
|
|
|
9126
9160
|
const set = ref.field === 'gene_tree' ? 'Compara' : ref.field.charAt(0).toUpperCase() + ref.field.slice(1);
|
|
9127
9161
|
return ref.rootTaxonName ? `${set} \xb7 ${ref.rootTaxonName}` : set;
|
|
9128
9162
|
};
|
|
9163
|
+
const $64fad37f770d2bfe$var$genomeZone = (0, $gXNCa$tbrowse.createGenomeZone)({
|
|
9164
|
+
id: 'genome'
|
|
9165
|
+
});
|
|
9166
|
+
// The genome (gene-structure) zone is gated behind the site-config flag
|
|
9167
|
+
// `enable_tbrowse_genome_zone` (defaults to false) — see getTbrowseZones().
|
|
9168
|
+
const $64fad37f770d2bfe$var$TBROWSE_BASE_ZONES = [
|
|
9169
|
+
(0, $gXNCa$tbrowse.treeZone),
|
|
9170
|
+
(0, $gXNCa$tbrowse.labelsZone),
|
|
9171
|
+
(0, $gXNCa$tbrowse.msaZone),
|
|
9172
|
+
(0, $gXNCa$tbrowse.neighborhoodZone)
|
|
9173
|
+
];
|
|
9174
|
+
// The expression-attributes zone's data, as a slot on tbrowse-core's Gramene
|
|
9175
|
+
// gene-tree source: one tree-scoped /search for the expr_*__attr_* fields
|
|
9176
|
+
// (fetchGrameneExprAttrDocs), shaped by buildExprData and delivered through
|
|
9177
|
+
// hostData.exprAttrs — the same lifecycle as the built-in neighborhood and
|
|
9178
|
+
// gene-structure slots. Enabled per site via the ctx flag the component
|
|
9179
|
+
// derives from `enable_tbrowse_expression_zone`. Scoped by root.meta, which
|
|
9180
|
+
// carries the Solr field holding the tree id: gene_tree, or a clade tree set
|
|
9181
|
+
// such as wheat.
|
|
9182
|
+
const $64fad37f770d2bfe$var$exprAttrsSlot = (0, $gXNCa$tbrowsecore.defineSlot)({
|
|
9183
|
+
id: 'exprAttrs',
|
|
9184
|
+
enabled: (ctx)=>ctx.expression === true,
|
|
9185
|
+
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, {
|
|
9186
|
+
treeField: root.meta.treeField,
|
|
9187
|
+
fetch: fetch,
|
|
9188
|
+
signal: signal
|
|
9189
|
+
}), tree),
|
|
9190
|
+
toHostData: (v)=>({
|
|
9191
|
+
hostData: {
|
|
9192
|
+
exprAttrs: v
|
|
9193
|
+
}
|
|
9194
|
+
})
|
|
9195
|
+
});
|
|
9196
|
+
const $64fad37f770d2bfe$var$homologySource = (0, $gXNCa$tbrowsecore.extendSource)((0, $gXNCa$tbrowsecore.grameneGeneTreeSource), {
|
|
9197
|
+
id: 'gramene-homology',
|
|
9198
|
+
slots: [
|
|
9199
|
+
$64fad37f770d2bfe$var$exprAttrsSlot
|
|
9200
|
+
]
|
|
9201
|
+
});
|
|
9202
|
+
// Host zone id → the slot(s) feeding it, so the TBrowse toolbar shows the
|
|
9203
|
+
// per-zone loading / error affordances (TBrowseProps.zoneStatus).
|
|
9204
|
+
const $64fad37f770d2bfe$var$ZONE_SLOTS = {
|
|
9205
|
+
neighborhood: [
|
|
9206
|
+
'neighborhood'
|
|
9207
|
+
],
|
|
9208
|
+
genome: [
|
|
9209
|
+
'geneStructures'
|
|
9210
|
+
],
|
|
9211
|
+
expression: [
|
|
9212
|
+
'exprAttrs'
|
|
9213
|
+
]
|
|
9214
|
+
};
|
|
9129
9215
|
class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$react))).Component {
|
|
9130
9216
|
constructor(props){
|
|
9131
9217
|
super(props);
|
|
9132
|
-
//
|
|
9218
|
+
// The tbrowse data — tree, MSA, domains and junctions adapted from the
|
|
9219
|
+
// stored genetree doc, then the neighborhood / gene-structure / expression
|
|
9220
|
+
// slots — is loaded by a tbrowse-core loader kept on the instance (one per
|
|
9221
|
+
// mount); its snapshot is mirrored into state so renders follow it. The
|
|
9133
9222
|
// user-controlled view state (viewer toggle, resize height, tbrowse
|
|
9134
9223
|
// ViewState) is lifted into the uiViewState bundle, keyed by geneId.
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
// prop. They start `undefined` (no signal); flipped to 'loading'
|
|
9139
|
-
// when the fetch kicks off and 'ready' / 'error' on settle.
|
|
9224
|
+
this.loader = (0, $gXNCa$tbrowsecore.createLoader)($64fad37f770d2bfe$var$homologySource, {
|
|
9225
|
+
zoneSlots: $64fad37f770d2bfe$var$ZONE_SLOTS
|
|
9226
|
+
});
|
|
9140
9227
|
this.state = {
|
|
9141
|
-
|
|
9142
|
-
neighborhoodTreeId: null,
|
|
9143
|
-
neighborhoodStatus: undefined,
|
|
9144
|
-
geneStructures: null,
|
|
9145
|
-
geneStructuresTreeId: null,
|
|
9146
|
-
geneStructuresStatus: undefined,
|
|
9147
|
-
exprAttrs: null,
|
|
9148
|
-
exprAttrsTreeId: null,
|
|
9149
|
-
exprAttrsStatus: undefined
|
|
9228
|
+
snap: this.loader.getSnapshot()
|
|
9150
9229
|
};
|
|
9151
9230
|
if (!props.geneDocs.hasOwnProperty(props.searchResult.id)) props.requestGene(props.searchResult.id);
|
|
9152
9231
|
this.taxonomy = (0, ($parcel$interopDefault($gXNCa$gramenetreesclientsrctaxonomy))).tree(Object.values(props.grameneTaxonomy));
|
|
@@ -9216,248 +9295,95 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9216
9295
|
// When the user has limited the search to a subset of genomes
|
|
9217
9296
|
// (grameneGenomes.active), prune the gene tree to genes from those genomes
|
|
9218
9297
|
// — mirroring the taxon_id filter api.js applies to the search itself, and
|
|
9219
|
-
// the genomesOfInterest TreeVis already honours.
|
|
9220
|
-
//
|
|
9221
|
-
//
|
|
9222
|
-
//
|
|
9223
|
-
//
|
|
9224
|
-
//
|
|
9298
|
+
// the genomesOfInterest TreeVis already honours. tbrowse-core's
|
|
9299
|
+
// maximalExcludedClades returns the ids of the highest nodes whose entire
|
|
9300
|
+
// subtree lies outside the active set (maximal excluded clades), rather
|
|
9301
|
+
// than individual leaves: tbrowse then sheds one stub per clade instead of
|
|
9302
|
+
// one per leaf, and its single-marker count is meaningful. The gene of
|
|
9303
|
+
// interest is always kept so the tree never collapses to nothing. Empty
|
|
9304
|
+
// active set means "no limit" → full tree.
|
|
9225
9305
|
getGenomePrunedIds() {
|
|
9226
9306
|
const active = this.props.grameneGenomes && this.props.grameneGenomes.active || {};
|
|
9227
9307
|
const activeIds = Object.keys(active);
|
|
9228
9308
|
if (activeIds.length === 0) return [];
|
|
9229
|
-
|
|
9309
|
+
const snap = this.state.snap;
|
|
9310
|
+
if (!snap.tree) return [];
|
|
9230
9311
|
const activeSet = new Set(activeIds.map(String));
|
|
9231
9312
|
const keepGeneId = this.gene && this.gene._id;
|
|
9232
|
-
|
|
9233
|
-
const children = {};
|
|
9234
|
-
Object.values(nodes).forEach((n)=>{
|
|
9235
|
-
if (n.parentId != null) (children[n.parentId] = children[n.parentId] || []).push(n.id);
|
|
9236
|
-
});
|
|
9237
|
-
// hasKept(node): does the subtree contain a leaf we must keep (its taxon
|
|
9238
|
-
// is active, or it's the gene of interest)? Memoised post-order walk.
|
|
9239
|
-
const memo = {};
|
|
9240
|
-
const hasKept = (id)=>{
|
|
9241
|
-
if (id in memo) return memo[id];
|
|
9242
|
-
const n = nodes[id];
|
|
9243
|
-
const v = n.isLeaf ? n.taxonomyId != null && activeSet.has(String(n.taxonomyId)) || n.geneId === keepGeneId : (children[id] || []).some(hasKept);
|
|
9244
|
-
memo[id] = v;
|
|
9245
|
-
return v;
|
|
9246
|
-
};
|
|
9247
|
-
// A node is a maximal excluded clade when it keeps nothing but its parent
|
|
9248
|
-
// does (or it's the root) — i.e. the highest fully-excluded node on its
|
|
9249
|
-
// path. The root always keeps the gene of interest, so it is never pruned.
|
|
9250
|
-
return Object.values(nodes).filter((n)=>!hasKept(n.id) && (n.parentId == null || hasKept(n.parentId))).map((n)=>n.id);
|
|
9313
|
+
return (0, $gXNCa$tbrowsecore.maximalExcludedClades)(snap.tree, (leaf)=>leaf.taxonomyId != null && activeSet.has(String(leaf.taxonomyId)) || leaf.geneId === keepGeneId);
|
|
9251
9314
|
}
|
|
9252
9315
|
// Seed the bundle with a pivot-computed initial tbrowse view state once the
|
|
9253
|
-
//
|
|
9254
|
-
// viewer
|
|
9316
|
+
// loader has the tree and the user is actually looking at the tbrowse
|
|
9317
|
+
// viewer, from lifecycle (not render) to avoid dispatching mid-render. The
|
|
9318
|
+
// dispatch lands synchronously in the per-gene slice, so the slice check is
|
|
9319
|
+
// the dedupe. buildInitialViewState is the same routine the chassis runs in
|
|
9320
|
+
// uncontrolled mode (each zone's fr-share, initial visibility, default
|
|
9321
|
+
// per-zone state); the pivot is computed for THIS gene — the loader is
|
|
9322
|
+
// shared by every gene of the tree, so its own pivot may be another gene's.
|
|
9255
9323
|
maybeSeedTbrowseViewState() {
|
|
9256
9324
|
if (this.getViewer() !== 'tbrowse') return;
|
|
9257
9325
|
if (this.getHomologySlice().tbrowse) return;
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
const
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
if (!raw || !raw.taxon_id) return;
|
|
9266
|
-
const adapted = (0, $gXNCa$tbrowse.fromGrameneGenetree)([
|
|
9267
|
-
raw
|
|
9268
|
-
]);
|
|
9269
|
-
const pivot = (0, $gXNCa$tbrowse.computePivotState)(adapted.tree, gene._id);
|
|
9270
|
-
this.setTbrowseViewState({
|
|
9271
|
-
selectedNodeId: null,
|
|
9272
|
-
collapsedNodeIds: pivot ? pivot.collapsedNodeIds : [],
|
|
9273
|
-
prunedNodeIds: [],
|
|
9274
|
-
swappedNodeIds: pivot ? pivot.swappedNodeIds : [],
|
|
9275
|
-
compressedNodeIds: [],
|
|
9276
|
-
nodeOfInterestId: pivot ? pivot.targetId : null,
|
|
9277
|
-
// Use each zone's intended fr-share + initial visibility from its
|
|
9278
|
-
// definition (tree:30 / labels:20 / msa:50 / neighborhood:30 /
|
|
9279
|
-
// genome:32) instead of uniform 25 across the board. Gives the MSA
|
|
9280
|
-
// a wider initial pane and the tree a narrower one.
|
|
9281
|
-
//
|
|
9282
|
-
// `defaultVisible ?? true` matches tbrowse's own buildInitialViewState
|
|
9283
|
-
// logic — zones default to visible unless their definition opts out
|
|
9284
|
-
// (e.g. neighborhood and genome opt out so they only appear once
|
|
9285
|
-
// their async data lands; tbrowse's Layout auto-flips them on then).
|
|
9286
|
-
zones: this.getTbrowseZones().map((z)=>({
|
|
9287
|
-
id: z.id,
|
|
9288
|
-
width: z.defaultWidth,
|
|
9289
|
-
visible: z.defaultVisible ?? true
|
|
9290
|
-
})),
|
|
9291
|
-
zoneStates: {},
|
|
9292
|
-
search: null
|
|
9293
|
-
});
|
|
9326
|
+
if (!this.gene) return;
|
|
9327
|
+
// The live snapshot, not the mirrored one: right after a tree switch the
|
|
9328
|
+
// loader has already restarted but state.snap still holds the old tree.
|
|
9329
|
+
const snap = this.loader.getSnapshot();
|
|
9330
|
+
const ref = this.selectedTreeRef(this.gene);
|
|
9331
|
+
if (!ref || snap.status !== 'ready' || !snap.tree || !snap.root || snap.root.meta.treeId !== ref.id) return;
|
|
9332
|
+
this.setTbrowseViewState((0, $gXNCa$tbrowsecore.applyPivot)((0, $gXNCa$tbrowsecore.buildInitialViewState)(this.getTbrowseZones()), (0, $gXNCa$tbrowsecore.computePivotState)(snap.tree, this.gene._id)));
|
|
9294
9333
|
}
|
|
9295
9334
|
componentDidMount() {
|
|
9335
|
+
this._unsubscribe = this.loader.subscribe(()=>this.setState({
|
|
9336
|
+
snap: this.loader.getSnapshot()
|
|
9337
|
+
}));
|
|
9338
|
+
this.maybeStartLoader();
|
|
9296
9339
|
this.maybeSeedTbrowseViewState();
|
|
9297
|
-
this.maybeFetchTbrowseData();
|
|
9298
9340
|
}
|
|
9299
9341
|
componentDidUpdate() {
|
|
9342
|
+
this.maybeStartLoader();
|
|
9300
9343
|
this.maybeSeedTbrowseViewState();
|
|
9301
|
-
this.maybeFetchTbrowseData();
|
|
9302
9344
|
}
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
//
|
|
9308
|
-
|
|
9345
|
+
componentWillUnmount() {
|
|
9346
|
+
if (this._unsubscribe) this._unsubscribe();
|
|
9347
|
+
this.loader.dispose();
|
|
9348
|
+
}
|
|
9349
|
+
// Start the loader (or restart it when the gene's tree changes) from
|
|
9350
|
+
// lifecycle, not render. Gated on the user actually being on the tbrowse
|
|
9351
|
+
// viewer + the tree doc being loaded — otherwise we'd pay network cost for
|
|
9352
|
+
// genes the user never looks at. The stored genetree doc is handed over as
|
|
9353
|
+
// `rawGenetree`, so the source makes no /genes or /genetrees request of its
|
|
9354
|
+
// own (docs.js's taxon-name rewrite stays upstream); the neighborhood,
|
|
9355
|
+
// gene-structure and expression slots are the only fetches, and the loader
|
|
9356
|
+
// retries their transient failures with backoff.
|
|
9357
|
+
maybeStartLoader() {
|
|
9309
9358
|
if (this.getViewer() !== 'tbrowse') return;
|
|
9310
9359
|
const id = this.getGeneId();
|
|
9311
9360
|
if (!this.props.geneDocs.hasOwnProperty(id)) return;
|
|
9312
9361
|
const gene = this.props.geneDocs[id];
|
|
9362
|
+
// The selected tree: gene_tree, or a clade tree set (homology.wheat,
|
|
9363
|
+
// homology.oat, …); none for genes with homologues but no tree. A switch
|
|
9364
|
+
// restarts the loader (new generation, the old slots are aborted).
|
|
9313
9365
|
const ref = this.selectedTreeRef(gene);
|
|
9314
9366
|
if (!ref) return;
|
|
9315
9367
|
const treeId = ref.id;
|
|
9316
9368
|
const raw = this.props.grameneTrees[treeId];
|
|
9317
9369
|
if (!raw || !raw.taxon_id) return;
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
this.
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
this._neighborhoodFetchedFor = treeId;
|
|
9336
|
-
this.setState({
|
|
9337
|
-
neighborhoodStatus: 'loading'
|
|
9338
|
-
});
|
|
9339
|
-
const api = this.props.grameneAPI;
|
|
9340
|
-
const url = new URL(`${api}/search`);
|
|
9341
|
-
const fl = 'id,name,gene_tree,gene_idx,region,start,end,strand,biotype,system_name,description';
|
|
9342
|
-
url.searchParams.set('fl', field === 'gene_tree' ? fl : `${fl},${field}`);
|
|
9343
|
-
// cache=false: as a cached fq the graph query caches its seed inside its own
|
|
9344
|
-
// filterCache computeIfAbsent, and Solr's FastLRUCache throws "Recursive
|
|
9345
|
-
// update" (HTTP 500) whenever the two keys collide — for some trees, until
|
|
9346
|
-
// the seed happens to be cached by another request.
|
|
9347
|
-
url.searchParams.set('fq', `{!graph from=compara_neighbors_10 to=compara_idx_multi maxDepth=1 cache=false}${field}:${treeId}`);
|
|
9348
|
-
url.searchParams.set('rows', '100000');
|
|
9349
|
-
url.searchParams.set('start', '0');
|
|
9350
|
-
fetch(url.toString(), {
|
|
9351
|
-
headers: {
|
|
9352
|
-
Accept: 'application/json'
|
|
9353
|
-
}
|
|
9354
|
-
}).then((res)=>{
|
|
9355
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9356
|
-
return res.json();
|
|
9357
|
-
}).then((json)=>{
|
|
9358
|
-
if (this._neighborhoodFetchedFor !== treeId) return;
|
|
9359
|
-
// The neighborhood zone colours genes by family from doc.gene_tree; in
|
|
9360
|
-
// a clade tree set the family id lives under the set's own field.
|
|
9361
|
-
if (field !== 'gene_tree' && json && json.response && Array.isArray(json.response.docs)) json.response.docs.forEach((d)=>{
|
|
9362
|
-
if (d[field] && !d.gene_tree) d.gene_tree = d[field];
|
|
9363
|
-
});
|
|
9364
|
-
this.setState({
|
|
9365
|
-
neighborhood: (0, $gXNCa$tbrowse.fromGrameneNeighborhood)(json),
|
|
9366
|
-
neighborhoodTreeId: treeId,
|
|
9367
|
-
neighborhoodStatus: 'ready'
|
|
9368
|
-
});
|
|
9369
|
-
}).catch((err)=>{
|
|
9370
|
-
console.warn('tbrowse neighborhood fetch failed:', err);
|
|
9371
|
-
// Surface the failure via the zoneStatus prop. Reset the
|
|
9372
|
-
// dedupe key so a re-mount or tree-change can retry.
|
|
9373
|
-
if (this._neighborhoodFetchedFor === treeId) this.setState({
|
|
9374
|
-
neighborhoodStatus: 'error'
|
|
9375
|
-
});
|
|
9376
|
-
this._neighborhoodFetchedFor = null;
|
|
9377
|
-
});
|
|
9378
|
-
}
|
|
9379
|
-
fetchGeneStructures(treeId, tree) {
|
|
9380
|
-
if (this._geneStructuresFetchedFor === treeId) return;
|
|
9381
|
-
this._geneStructuresFetchedFor = treeId;
|
|
9382
|
-
const ids = Object.values(tree.nodes).filter((n)=>n.isLeaf && n.geneId).map((n)=>n.geneId);
|
|
9383
|
-
if (ids.length === 0) return;
|
|
9384
|
-
this.setState({
|
|
9385
|
-
geneStructuresStatus: 'loading'
|
|
9386
|
-
});
|
|
9387
|
-
const api = this.props.grameneAPI;
|
|
9388
|
-
const BATCH_SIZE = 50;
|
|
9389
|
-
const batches = [];
|
|
9390
|
-
for(let i = 0; i < ids.length; i += BATCH_SIZE)batches.push(ids.slice(i, i + BATCH_SIZE));
|
|
9391
|
-
const fetchBatch = (batch)=>{
|
|
9392
|
-
const url = new URL(`${api}/genes`);
|
|
9393
|
-
url.searchParams.set('idList', batch.join(','));
|
|
9394
|
-
url.searchParams.set('rows', '-1');
|
|
9395
|
-
return fetch(url.toString(), {
|
|
9396
|
-
headers: {
|
|
9397
|
-
Accept: 'application/json'
|
|
9398
|
-
}
|
|
9399
|
-
}).then((res)=>{
|
|
9400
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9401
|
-
return res.json();
|
|
9402
|
-
});
|
|
9403
|
-
};
|
|
9404
|
-
Promise.all(batches.map(fetchBatch)).then((results)=>{
|
|
9405
|
-
if (this._geneStructuresFetchedFor !== treeId) return;
|
|
9406
|
-
const combined = [].concat(...results.map((r)=>Array.isArray(r) ? r : []));
|
|
9407
|
-
this.setState({
|
|
9408
|
-
geneStructures: (0, $gXNCa$tbrowse.fromGrameneGeneStructures)(combined),
|
|
9409
|
-
geneStructuresTreeId: treeId,
|
|
9410
|
-
geneStructuresStatus: 'ready'
|
|
9411
|
-
});
|
|
9412
|
-
}).catch((err)=>{
|
|
9413
|
-
console.warn('tbrowse gene-structures fetch failed:', err);
|
|
9414
|
-
if (this._geneStructuresFetchedFor === treeId) this.setState({
|
|
9415
|
-
geneStructuresStatus: 'error'
|
|
9416
|
-
});
|
|
9417
|
-
this._geneStructuresFetchedFor = null;
|
|
9418
|
-
});
|
|
9419
|
-
}
|
|
9420
|
-
// Fetch the expression attributes (expr_*__attr_*) for this tree's genes in a
|
|
9421
|
-
// single query: scope by the tree's field and the `expression_attributes` capability
|
|
9422
|
-
// so only genes that actually have the data come back (rows high enough to
|
|
9423
|
-
// cover the whole tree). Uses /search (not /genes, whose trimmed docs omit the
|
|
9424
|
-
// attr fields) with an fl glob so the columns are discovered from the data
|
|
9425
|
-
// rather than hard-coded. `tree` is still needed to map gene ids to leaf node
|
|
9426
|
-
// ids (see buildExprByNode).
|
|
9427
|
-
fetchExprAttrs(treeId, tree, field = 'gene_tree') {
|
|
9428
|
-
if (this._exprAttrsFetchedFor === treeId) return;
|
|
9429
|
-
this._exprAttrsFetchedFor = treeId;
|
|
9430
|
-
this.setState({
|
|
9431
|
-
exprAttrsStatus: 'loading'
|
|
9432
|
-
});
|
|
9433
|
-
const api = this.props.grameneAPI;
|
|
9434
|
-
const url = new URL(`${api}/search`);
|
|
9435
|
-
url.searchParams.set('q', '*:*');
|
|
9436
|
-
url.searchParams.append('fq', `${field}:${treeId}`);
|
|
9437
|
-
url.searchParams.append('fq', 'capabilities:expression_attributes');
|
|
9438
|
-
url.searchParams.set('fl', 'id,expr_*__attr_*');
|
|
9439
|
-
url.searchParams.set('rows', '10000');
|
|
9440
|
-
fetch(url.toString(), {
|
|
9441
|
-
headers: {
|
|
9442
|
-
Accept: 'application/json'
|
|
9443
|
-
}
|
|
9444
|
-
}).then((res)=>{
|
|
9445
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
9446
|
-
return res.json();
|
|
9447
|
-
}).then((json)=>{
|
|
9448
|
-
if (this._exprAttrsFetchedFor !== treeId) return;
|
|
9449
|
-
const docs = json && json.response && Array.isArray(json.response.docs) ? json.response.docs : [];
|
|
9450
|
-
this.setState({
|
|
9451
|
-
exprAttrs: (0, $cd8bc494277e92a4$export$965a6b1d7408d496)(docs, tree),
|
|
9452
|
-
exprAttrsTreeId: treeId,
|
|
9453
|
-
exprAttrsStatus: 'ready'
|
|
9454
|
-
});
|
|
9455
|
-
}).catch((err)=>{
|
|
9456
|
-
console.warn('tbrowse expression-attrs fetch failed:', err);
|
|
9457
|
-
if (this._exprAttrsFetchedFor === treeId) this.setState({
|
|
9458
|
-
exprAttrsStatus: 'error'
|
|
9459
|
-
});
|
|
9460
|
-
this._exprAttrsFetchedFor = null;
|
|
9370
|
+
const key = `${ref.field}:${treeId}`;
|
|
9371
|
+
if (this._loaderStartedFor === key) return;
|
|
9372
|
+
this._loaderStartedFor = key;
|
|
9373
|
+
this.loader.start({
|
|
9374
|
+
base: this.props.grameneAPI,
|
|
9375
|
+
treeId: treeId,
|
|
9376
|
+
treeField: ref.field,
|
|
9377
|
+
geneId: gene._id,
|
|
9378
|
+
rawGenetree: raw,
|
|
9379
|
+
// Only pay for gene-structure data when the genome zone is enabled; the
|
|
9380
|
+
// request shape (50 ids per /genes call, no fl) is what this tab has
|
|
9381
|
+
// always sent.
|
|
9382
|
+
geneStructures: this.isGenomeZoneEnabled(),
|
|
9383
|
+
geneStructureChunkSize: 50,
|
|
9384
|
+
geneStructureFl: null,
|
|
9385
|
+
// Same for the per-gene expression attributes zone.
|
|
9386
|
+
expression: this.isExpressionZoneEnabled()
|
|
9461
9387
|
});
|
|
9462
9388
|
}
|
|
9463
9389
|
startResize(e) {
|
|
@@ -9510,49 +9436,30 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9510
9436
|
});
|
|
9511
9437
|
}
|
|
9512
9438
|
renderTBrowse() {
|
|
9513
|
-
const
|
|
9514
|
-
|
|
9515
|
-
//
|
|
9516
|
-
// the
|
|
9517
|
-
//
|
|
9518
|
-
if (this._tbrowseTreeId !== treeId) {
|
|
9519
|
-
this._tbrowseTreeId = treeId;
|
|
9520
|
-
this._tbrowseData = (0, $gXNCa$tbrowse.fromGrameneGenetree)([
|
|
9521
|
-
this.props.grameneTrees[treeId]
|
|
9522
|
-
]);
|
|
9523
|
-
}
|
|
9524
|
-
const neighborhood = this.state.neighborhoodTreeId === treeId ? this.state.neighborhood : undefined;
|
|
9525
|
-
const geneStructures = this.state.geneStructuresTreeId === treeId ? this.state.geneStructures : undefined;
|
|
9526
|
-
const exprAttrs = this.state.exprAttrsTreeId === treeId ? this.state.exprAttrs : undefined;
|
|
9527
|
-
// Per-zone status for the TBrowse toolbar — pulses while a fetch
|
|
9528
|
-
// is in flight, turns red on failure. Tracked per-tree so a
|
|
9529
|
-
// tree-change resets any stale 'ready'/'error' from the prior gene.
|
|
9530
|
-
const zoneStatus = {
|
|
9531
|
-
neighborhood: this.state.neighborhoodTreeId === treeId ? this.state.neighborhoodStatus : this.state.neighborhoodStatus === 'loading' ? 'loading' : undefined,
|
|
9532
|
-
genome: this.state.geneStructuresTreeId === treeId ? this.state.geneStructuresStatus : this.state.geneStructuresStatus === 'loading' ? 'loading' : undefined,
|
|
9533
|
-
expression: this.state.exprAttrsTreeId === treeId ? this.state.exprAttrsStatus : this.state.exprAttrsStatus === 'loading' ? 'loading' : undefined
|
|
9534
|
-
};
|
|
9535
|
-
// Bundle-driven (controlled) view state. If we're rendering tbrowse before
|
|
9536
|
-
// componentDidMount/Update has seeded the bundle slice, skip this turn and
|
|
9537
|
-
// let the re-render with the seeded state do the work.
|
|
9439
|
+
const snap = this.state.snap;
|
|
9440
|
+
const hostProps = (0, $gXNCa$tbrowsecore.hostPropsFrom)(snap);
|
|
9441
|
+
// Nothing to draw until the loader has adapted the tree (one tick after
|
|
9442
|
+
// start(), since the stored doc needs no fetch) and the bundle slice has
|
|
9443
|
+
// been seeded by maybeSeedTbrowseViewState (lifecycle, not render).
|
|
9538
9444
|
const tbrowseVS = this.getHomologySlice().tbrowse;
|
|
9539
|
-
|
|
9445
|
+
// Also wait until the snapshot holds the SELECTED tree, so a switch never
|
|
9446
|
+
// draws the previous tree under the new selection.
|
|
9447
|
+
const isSelected = snap.root && this.treeRef && snap.root.meta.treeId === this.treeRef.id;
|
|
9448
|
+
if (!hostProps || !tbrowseVS || !isSelected) // Reserve the pane's height so the card doesn't jump when the tree lands.
|
|
9449
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
9450
|
+
className: "gene-genetree",
|
|
9451
|
+
style: {
|
|
9452
|
+
height: this.getHeight(),
|
|
9453
|
+
width: '100%'
|
|
9454
|
+
},
|
|
9455
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Spinner), {})
|
|
9456
|
+
});
|
|
9540
9457
|
// Layer the genome-limit prune set on top of the user's own prunes for the
|
|
9541
|
-
// controlled view state, but persist only the user's prunes (
|
|
9542
|
-
// genome-derived ones in onViewStateChange) so the bundle stays
|
|
9543
|
-
// filter-derived state and re-applies cleanly when the filter
|
|
9544
|
-
|
|
9545
|
-
const
|
|
9546
|
-
...tbrowseVS,
|
|
9547
|
-
prunedNodeIds: (0, ($parcel$interopDefault($gXNCa$lodash))).union(tbrowseVS.prunedNodeIds || [], genomePrunedIds)
|
|
9548
|
-
} : tbrowseVS;
|
|
9549
|
-
const onViewStateChange = genomePrunedIds.length ? (next)=>{
|
|
9550
|
-
const genomeSet = new Set(genomePrunedIds);
|
|
9551
|
-
this.setTbrowseViewState({
|
|
9552
|
-
...next,
|
|
9553
|
-
prunedNodeIds: (next.prunedNodeIds || []).filter((id)=>!genomeSet.has(id))
|
|
9554
|
-
});
|
|
9555
|
-
} : (next)=>this.setTbrowseViewState(next);
|
|
9458
|
+
// controlled view state, but persist only the user's prunes (the layer
|
|
9459
|
+
// strips the genome-derived ones in onViewStateChange) so the bundle stays
|
|
9460
|
+
// free of filter-derived state and re-applies cleanly when the filter
|
|
9461
|
+
// changes.
|
|
9462
|
+
const pruneLayer = (0, $gXNCa$tbrowsecore.createDerivedPruneLayer)(this.getGenomePrunedIds());
|
|
9556
9463
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
9557
9464
|
children: [
|
|
9558
9465
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
@@ -9562,27 +9469,16 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9562
9469
|
width: '100%'
|
|
9563
9470
|
},
|
|
9564
9471
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$tbrowse.TBrowse), {
|
|
9565
|
-
|
|
9566
|
-
taxonomy: this._tbrowseData.taxonomy,
|
|
9567
|
-
msa: this._tbrowseData.msa,
|
|
9568
|
-
geneMetadata: this._tbrowseData.geneMetadata,
|
|
9569
|
-
proteinDomains: this._tbrowseData.proteinDomains,
|
|
9570
|
-
exonJunctions: this._tbrowseData.exonJunctions,
|
|
9571
|
-
neighborhood: neighborhood,
|
|
9572
|
-
geneStructures: geneStructures,
|
|
9573
|
-
hostData: exprAttrs ? {
|
|
9574
|
-
exprAttrs: exprAttrs
|
|
9575
|
-
} : undefined,
|
|
9472
|
+
...hostProps,
|
|
9576
9473
|
zones: this.getTbrowseZones(),
|
|
9577
9474
|
nodeOfInterest: this.gene._id,
|
|
9578
|
-
viewState:
|
|
9579
|
-
onViewStateChange:
|
|
9475
|
+
viewState: pruneLayer.effective(tbrowseVS),
|
|
9476
|
+
onViewStateChange: (next)=>this.setTbrowseViewState(pruneLayer.onChange(next)),
|
|
9580
9477
|
densityControls: true,
|
|
9581
9478
|
defaultOpenSections: {
|
|
9582
9479
|
zones: true,
|
|
9583
9480
|
search: true
|
|
9584
|
-
}
|
|
9585
|
-
zoneStatus: zoneStatus
|
|
9481
|
+
}
|
|
9586
9482
|
})
|
|
9587
9483
|
}),
|
|
9588
9484
|
this.renderResizeHandle()
|
|
@@ -9814,7 +9710,7 @@ class $64fad37f770d2bfe$var$Homology extends (0, ($parcel$interopDefault($gXNCa$
|
|
|
9814
9710
|
});
|
|
9815
9711
|
}
|
|
9816
9712
|
}
|
|
9817
|
-
var $64fad37f770d2bfe$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneTaxonomy', 'selectGrameneTrees', 'selectGrameneGenomes', 'selectGrameneAPI', 'selectConfiguration', 'selectCuration', 'selectUiViewState', 'doRequestGrameneTree', 'doSetHomologyViewer', '
|
|
9713
|
+
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);
|
|
9818
9714
|
|
|
9819
9715
|
|
|
9820
9716
|
|
|
@@ -20474,69 +20370,21 @@ var $597fe213417ee6ca$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
20474
20370
|
|
|
20475
20371
|
|
|
20476
20372
|
|
|
20373
|
+
|
|
20477
20374
|
// Adapter: gramene taxonomy-with-genomes tree → tbrowse `Tree` + `Taxonomy`.
|
|
20478
20375
|
//
|
|
20479
20376
|
// `grameneTaxDist` (from the grameneTaxDist bundle's selectGrameneTaxDist) is
|
|
20480
|
-
// the root node of a gramene-taxonomy-with-genomes tree.
|
|
20481
|
-
//
|
|
20482
|
-
//
|
|
20483
|
-
//
|
|
20484
|
-
// tbrowse
|
|
20485
|
-
|
|
20486
|
-
|
|
20487
|
-
// NodeIds off the (unique) NCBI taxon id and set `taxonomyId` to the same id.
|
|
20488
|
-
//
|
|
20489
|
-
// Branch lengths are uniform (taxonomy carries none); the tree zone is driven
|
|
20490
|
-
// in 'cladogram' mode for this view, which ignores `distance` and aligns the
|
|
20491
|
-
// genome rows at the right edge regardless.
|
|
20492
|
-
/**
|
|
20493
|
-
* @param {object} taxDist - root node of the gramene taxDist tree
|
|
20494
|
-
* (the object returned by selectGrameneTaxDist).
|
|
20495
|
-
* @returns {{ tree: {rootId: string, nodes: object}, taxonomy: object,
|
|
20496
|
-
* leafTaxonIds: string[] } | null}
|
|
20497
|
-
*/ function $dc4f9c952681c07f$export$5fb520c9ccc6bdef(taxDist) {
|
|
20377
|
+
// the root node of a gramene-taxonomy-with-genomes (tree-model) tree. The
|
|
20378
|
+
// conversion itself — node ids = String(NCBI taxon id), uniform branch lengths
|
|
20379
|
+
// (the tree zone runs in 'cladogram' mode for this view), `model.name` exposed
|
|
20380
|
+
// as both scientific and common name, duplicate-id guard — is
|
|
20381
|
+
// tbrowse-core's `fromTreeModel`, which was lifted from this file.
|
|
20382
|
+
|
|
20383
|
+
function $dc4f9c952681c07f$export$5fb520c9ccc6bdef(taxDist) {
|
|
20498
20384
|
if (!taxDist || !taxDist.model) return null;
|
|
20499
|
-
|
|
20500
|
-
|
|
20501
|
-
|
|
20502
|
-
const rootId = String(taxDist.model.id);
|
|
20503
|
-
const walk = (node, parentId)=>{
|
|
20504
|
-
if (!node || !node.model) return;
|
|
20505
|
-
const id = String(node.model.id);
|
|
20506
|
-
// Guard against the (shouldn't-happen) cyclic / duplicate id case so a
|
|
20507
|
-
// malformed taxonomy can't spin the recursion.
|
|
20508
|
-
if (nodes[id]) return;
|
|
20509
|
-
const kids = node.children || [];
|
|
20510
|
-
const isLeaf = kids.length === 0;
|
|
20511
|
-
nodes[id] = {
|
|
20512
|
-
id: id,
|
|
20513
|
-
parentId: parentId,
|
|
20514
|
-
distance: parentId === null ? 0 : 1,
|
|
20515
|
-
isLeaf: isLeaf,
|
|
20516
|
-
taxonomyId: id
|
|
20517
|
-
};
|
|
20518
|
-
// `model.name` is the NCBI scientific name for clades and is overwritten
|
|
20519
|
-
// with the genome's display_name for genome leaves upstream in
|
|
20520
|
-
// selectGrameneTaxDist. Expose it as both names so the labels zone shows
|
|
20521
|
-
// something whichever field the user has active.
|
|
20522
|
-
const name = node.model.name;
|
|
20523
|
-
taxonomy[id] = {
|
|
20524
|
-
scientificName: name,
|
|
20525
|
-
commonName: name,
|
|
20526
|
-
rank: node.model.rank
|
|
20527
|
-
};
|
|
20528
|
-
if (isLeaf) leafTaxonIds.push(id);
|
|
20529
|
-
for (const k of kids)walk(k, id);
|
|
20530
|
-
};
|
|
20531
|
-
walk(taxDist, null);
|
|
20532
|
-
return {
|
|
20533
|
-
tree: {
|
|
20534
|
-
rootId: rootId,
|
|
20535
|
-
nodes: nodes
|
|
20536
|
-
},
|
|
20537
|
-
taxonomy: taxonomy,
|
|
20538
|
-
leafTaxonIds: leafTaxonIds
|
|
20539
|
-
};
|
|
20385
|
+
return (0, $gXNCa$tbrowsecore.fromTreeModel)(taxDist, {
|
|
20386
|
+
taxonomyIdAs: 'string'
|
|
20387
|
+
});
|
|
20540
20388
|
}
|
|
20541
20389
|
|
|
20542
20390
|
|
|
@@ -20563,6 +20411,7 @@ var $597fe213417ee6ca$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
20563
20411
|
|
|
20564
20412
|
|
|
20565
20413
|
|
|
20414
|
+
|
|
20566
20415
|
// ── colour model (ported from gramene-search-vis util/colors.js) ──────────
|
|
20567
20416
|
// schemeCategory10, kept as RGB triples so we need no d3 dependency here.
|
|
20568
20417
|
const $bfd29d54a0f0e853$var$REGION_RGB = [
|
|
@@ -21090,12 +20939,6 @@ const $bfd29d54a0f0e853$var$BinsHeader = ({ data: data, zoneState: zoneState, se
|
|
|
21090
20939
|
// lets the row-highlight background (rendered in the layer beneath the canvas)
|
|
21091
20940
|
// show as bands above/below the strip on hover/select.
|
|
21092
20941
|
const $bfd29d54a0f0e853$var$ROW_PAD_Y = 2;
|
|
21093
|
-
function $bfd29d54a0f0e853$var$rowHighlight(isSelected, isExactHover, isInHoveredSubtree) {
|
|
21094
|
-
if (isSelected) return 'var(--tbrowse-row-select-bg)';
|
|
21095
|
-
if (isExactHover) return 'var(--tbrowse-row-hover-bg)';
|
|
21096
|
-
if (isInHoveredSubtree) return 'var(--tbrowse-row-subtree-bg)';
|
|
21097
|
-
return 'transparent';
|
|
21098
|
-
}
|
|
21099
20942
|
const $bfd29d54a0f0e853$var$BinsBody = (props)=>{
|
|
21100
20943
|
const { visibleRows: visibleRows, rowRange: rowRange, width: width, data: data, zoneState: zoneState, hoveredNodeId: hoveredNodeId, hoveredSubtreeIds: hoveredSubtreeIds, selectedNodeId: selectedNodeId, onHoverNode: onHoverNode, onSelectNode: onSelectNode } = props;
|
|
21101
20944
|
const canvasRef = (0, $gXNCa$react.useRef)(null);
|
|
@@ -21109,7 +20952,7 @@ const $bfd29d54a0f0e853$var$BinsBody = (props)=>{
|
|
|
21109
20952
|
const snap = (0, $gXNCa$react.useSyncExternalStore)(store ? store.subscribe : $bfd29d54a0f0e853$var$NOOP_SUB, store ? store.getState : $bfd29d54a0f0e853$var$NOOP_GET);
|
|
21110
20953
|
const transform = snap.transform || $bfd29d54a0f0e853$var$DEFAULT_TRANSFORM;
|
|
21111
20954
|
const mode = zoneState && zoneState.mode || 'select';
|
|
21112
|
-
const totalHeight =
|
|
20955
|
+
const totalHeight = (0, $gXNCa$tbrowsecore.rowsTotalHeight)(visibleRows);
|
|
21113
20956
|
const bins = data.hostData && data.hostData.bins;
|
|
21114
20957
|
const rows = visibleRows.slice(rowRange.startIndex, rowRange.endIndex);
|
|
21115
20958
|
const w = Math.max(1, Math.floor(width));
|
|
@@ -21362,7 +21205,11 @@ const $bfd29d54a0f0e853$var$BinsBody = (props)=>{
|
|
|
21362
21205
|
left: 0,
|
|
21363
21206
|
right: 0,
|
|
21364
21207
|
height: r.height,
|
|
21365
|
-
background:
|
|
21208
|
+
background: (0, $gXNCa$tbrowse.rowHighlightBackground)((0, $gXNCa$tbrowsecore.rowFlags)(r, {
|
|
21209
|
+
hoveredNodeId: hoveredNodeId,
|
|
21210
|
+
hoveredSubtreeIds: hoveredSubtreeIds,
|
|
21211
|
+
selectedNodeId: selectedNodeId
|
|
21212
|
+
})),
|
|
21366
21213
|
cursor: rowCursor,
|
|
21367
21214
|
opacity: r.opacity ?? 1
|
|
21368
21215
|
}
|
|
@@ -21402,37 +21249,12 @@ const $bfd29d54a0f0e853$export$d85d069f2280460c = {
|
|
|
21402
21249
|
// Host-defined tbrowse zone: a narrow, right-justified text column showing the
|
|
21403
21250
|
// number of matching genes per leaf (genome) — or, for a collapsed clade, the
|
|
21404
21251
|
// summed count over its subtree. Counts come from the taxon_id facet; the
|
|
21405
|
-
// per-node sums are
|
|
21406
|
-
// hostData.geneCounts = { [nodeId]: number }.
|
|
21252
|
+
// per-node sums are computed by tbrowse-core's `countsFromFacet` in the
|
|
21253
|
+
// container and handed in via hostData.geneCounts = { [nodeId]: number }.
|
|
21254
|
+
|
|
21407
21255
|
|
|
21408
21256
|
|
|
21409
21257
|
|
|
21410
|
-
function $15275c4f5939e11d$export$ce87f9041549c2e9(tree, taxonIdFacet) {
|
|
21411
|
-
if (!tree || !tree.nodes) return {};
|
|
21412
|
-
const leafCount = {};
|
|
21413
|
-
if (Array.isArray(taxonIdFacet)) for(let i = 0; i < taxonIdFacet.length; i += 2)leafCount[String(taxonIdFacet[i])] = +taxonIdFacet[i + 1];
|
|
21414
|
-
const childrenOf = {};
|
|
21415
|
-
for (const id of Object.keys(tree.nodes)){
|
|
21416
|
-
const p = tree.nodes[id].parentId;
|
|
21417
|
-
if (p !== null && p !== undefined) (childrenOf[p] = childrenOf[p] || []).push(id);
|
|
21418
|
-
}
|
|
21419
|
-
const out = {};
|
|
21420
|
-
const visit = (id)=>{
|
|
21421
|
-
const node = tree.nodes[id];
|
|
21422
|
-
if (!node) return 0;
|
|
21423
|
-
if (node.isLeaf) {
|
|
21424
|
-
const c = leafCount[id] || 0;
|
|
21425
|
-
out[id] = c;
|
|
21426
|
-
return c;
|
|
21427
|
-
}
|
|
21428
|
-
let sum = 0;
|
|
21429
|
-
for (const k of childrenOf[id] || [])sum += visit(k);
|
|
21430
|
-
out[id] = sum;
|
|
21431
|
-
return sum;
|
|
21432
|
-
};
|
|
21433
|
-
visit(tree.rootId);
|
|
21434
|
-
return out;
|
|
21435
|
-
}
|
|
21436
21258
|
const $15275c4f5939e11d$var$CountsHeader = ({ zoneState: zoneState, setZoneState: setZoneState })=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21437
21259
|
style: {
|
|
21438
21260
|
padding: '0 10px',
|
|
@@ -21450,28 +21272,24 @@ const $15275c4f5939e11d$var$CountsHeader = ({ zoneState: zoneState, setZoneState
|
|
|
21450
21272
|
}))
|
|
21451
21273
|
})
|
|
21452
21274
|
});
|
|
21453
|
-
// Shared row-highlight background, matching the labels zone so hovering any
|
|
21454
|
-
// zone lights up the same row across all of them (state flows through the
|
|
21455
|
-
// tbrowse store's hoveredNodeId).
|
|
21456
|
-
function $15275c4f5939e11d$var$rowHighlight(isSelected, isExactHover, isInHoveredSubtree) {
|
|
21457
|
-
if (isSelected) return 'var(--tbrowse-row-select-bg)';
|
|
21458
|
-
if (isExactHover) return 'var(--tbrowse-row-hover-bg)';
|
|
21459
|
-
if (isInHoveredSubtree) return 'var(--tbrowse-row-subtree-bg)';
|
|
21460
|
-
return 'transparent';
|
|
21461
|
-
}
|
|
21462
21275
|
const $15275c4f5939e11d$var$CountsBody = ({ visibleRows: visibleRows, rowRange: rowRange, width: width, data: data, hoveredNodeId: hoveredNodeId, hoveredSubtreeIds: hoveredSubtreeIds, selectedNodeId: selectedNodeId, onHoverNode: onHoverNode, onSelectNode: onSelectNode })=>{
|
|
21463
|
-
const totalHeight =
|
|
21276
|
+
const totalHeight = (0, $gXNCa$tbrowsecore.rowsTotalHeight)(visibleRows);
|
|
21464
21277
|
const counts = data.hostData && data.hostData.geneCounts || {};
|
|
21465
|
-
|
|
21278
|
+
// Rows in the virtualised range with the shared hover/select background, so
|
|
21279
|
+
// hovering any zone lights up the same row across all of them.
|
|
21280
|
+
const rows = (0, $gXNCa$tbrowse.rowsWithFlags)(visibleRows, rowRange, {
|
|
21281
|
+
hoveredNodeId: hoveredNodeId,
|
|
21282
|
+
hoveredSubtreeIds: hoveredSubtreeIds,
|
|
21283
|
+
selectedNodeId: selectedNodeId
|
|
21284
|
+
});
|
|
21466
21285
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21467
21286
|
style: {
|
|
21468
21287
|
position: 'relative',
|
|
21469
21288
|
width: width,
|
|
21470
21289
|
height: totalHeight
|
|
21471
21290
|
},
|
|
21472
|
-
children: rows.map((r)=>{
|
|
21291
|
+
children: rows.map(({ row: r, background: background })=>{
|
|
21473
21292
|
const n = counts[r.nodeId];
|
|
21474
|
-
const background = $15275c4f5939e11d$var$rowHighlight(selectedNodeId === r.nodeId, hoveredNodeId === r.nodeId, !!(hoveredSubtreeIds && hoveredSubtreeIds.has(r.nodeId)));
|
|
21475
21293
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21476
21294
|
onMouseEnter: ()=>onHoverNode(r.nodeId),
|
|
21477
21295
|
onMouseLeave: ()=>onHoverNode(null),
|
|
@@ -21541,56 +21359,37 @@ function $15504f5eba8e73bd$var$binIdxsToSolr(idxs) {
|
|
|
21541
21359
|
}
|
|
21542
21360
|
return parts.join(' OR ');
|
|
21543
21361
|
}
|
|
21362
|
+
// The initial controlled ViewState: tbrowse-core's buildInitialViewState over
|
|
21363
|
+
// this view's zone list with the view's own fr-shares, and — passed through
|
|
21364
|
+
// `overrides` so they replace the seeded defaults verbatim — the per-zone states
|
|
21365
|
+
// exactly as this view has always persisted them. The tree zone tolerates a
|
|
21366
|
+
// partial state (reads with `?? default`); the labels zone reads `visibleFields`
|
|
21367
|
+
// directly, so it must be present. Drive the tree as a cladogram (taxonomy has
|
|
21368
|
+
// no branch lengths) and show the genome/species name in the labels column.
|
|
21544
21369
|
function $15504f5eba8e73bd$var$baseViewState() {
|
|
21545
|
-
return {
|
|
21546
|
-
|
|
21547
|
-
|
|
21548
|
-
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
nodeOfInterestId: null,
|
|
21552
|
-
zones: [
|
|
21553
|
-
{
|
|
21554
|
-
id: 'tree',
|
|
21555
|
-
width: 30,
|
|
21556
|
-
visible: true
|
|
21557
|
-
},
|
|
21558
|
-
{
|
|
21559
|
-
id: 'labels',
|
|
21560
|
-
width: 22,
|
|
21561
|
-
visible: true
|
|
21562
|
-
},
|
|
21563
|
-
{
|
|
21564
|
-
id: 'counts',
|
|
21565
|
-
width: 10,
|
|
21566
|
-
visible: true
|
|
21567
|
-
},
|
|
21568
|
-
{
|
|
21569
|
-
id: 'bins',
|
|
21570
|
-
width: 60,
|
|
21571
|
-
visible: true
|
|
21572
|
-
}
|
|
21573
|
-
],
|
|
21574
|
-
// Controlled mode supplies zoneStates wholesale, so each must carry the
|
|
21575
|
-
// fields its zone reads. The tree zone tolerates a partial state (reads
|
|
21576
|
-
// with `?? default`); the labels zone reads `visibleFields` directly, so
|
|
21577
|
-
// it must be present. Drive the tree as a cladogram (taxonomy has no
|
|
21578
|
-
// branch lengths) and show the genome/species name in the labels column.
|
|
21579
|
-
zoneStates: {
|
|
21580
|
-
tree: {
|
|
21581
|
-
layoutMode: 'cladogram',
|
|
21582
|
-
showLeafExtensions: false
|
|
21583
|
-
},
|
|
21584
|
-
labels: {
|
|
21585
|
-
visibleFields: [
|
|
21586
|
-
'taxonomy.commonName'
|
|
21587
|
-
]
|
|
21588
|
-
},
|
|
21589
|
-
counts: {},
|
|
21590
|
-
bins: {}
|
|
21370
|
+
return (0, $gXNCa$tbrowsecore.buildInitialViewState)($15504f5eba8e73bd$var$ZONES, {
|
|
21371
|
+
widths: {
|
|
21372
|
+
tree: 30,
|
|
21373
|
+
labels: 22,
|
|
21374
|
+
counts: 10,
|
|
21375
|
+
bins: 60
|
|
21591
21376
|
},
|
|
21592
|
-
|
|
21593
|
-
|
|
21377
|
+
overrides: {
|
|
21378
|
+
zoneStates: {
|
|
21379
|
+
tree: {
|
|
21380
|
+
layoutMode: 'cladogram',
|
|
21381
|
+
showLeafExtensions: false
|
|
21382
|
+
},
|
|
21383
|
+
labels: {
|
|
21384
|
+
visibleFields: [
|
|
21385
|
+
'taxonomy.commonName'
|
|
21386
|
+
]
|
|
21387
|
+
},
|
|
21388
|
+
counts: {},
|
|
21389
|
+
bins: {}
|
|
21390
|
+
}
|
|
21391
|
+
}
|
|
21392
|
+
});
|
|
21594
21393
|
}
|
|
21595
21394
|
// The set of taxa to SHOW, mirroring the legacy TaxDist.js logic.
|
|
21596
21395
|
function $15504f5eba8e73bd$var$computeSelectedTaxa({ grameneSearch: grameneSearch, grameneGenomes: grameneGenomes, grameneMaps: grameneMaps }, treeNodes, collapseEmpties, comparaOnly, showCompara) {
|
|
@@ -21654,7 +21453,7 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21654
21453
|
const facet = props.grameneSearch && props.grameneSearch.facet_counts && props.grameneSearch.facet_counts.facet_fields && props.grameneSearch.facet_counts.facet_fields.taxon_id;
|
|
21655
21454
|
return {
|
|
21656
21455
|
bins: (0, $bfd29d54a0f0e853$export$390e10a52782f018)(grameneTaxDist),
|
|
21657
|
-
geneCounts: adapter ? (0, $
|
|
21456
|
+
geneCounts: adapter ? (0, $gXNCa$tbrowsecore.countsFromFacet)(adapter.tree, facet, 'nodeId') : null,
|
|
21658
21457
|
binsUI: binsUiRef.current,
|
|
21659
21458
|
onApplyBinsFilter: onApplyBinsFilter
|
|
21660
21459
|
};
|
|
@@ -21680,7 +21479,7 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21680
21479
|
const prunedNodeIds = (0, $gXNCa$react.useMemo)(()=>{
|
|
21681
21480
|
if (!adapter) return [];
|
|
21682
21481
|
const selected = $15504f5eba8e73bd$var$computeSelectedTaxa(props, adapter.tree.nodes, collapseEmpties, comparaOnly, showCompara);
|
|
21683
|
-
return adapter.
|
|
21482
|
+
return (0, $gXNCa$tbrowsecore.leavesFailing)(adapter.tree, (leaf)=>Boolean(selected[String(leaf.taxonomyId)]));
|
|
21684
21483
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
21685
21484
|
}, [
|
|
21686
21485
|
adapter,
|
|
@@ -21692,7 +21491,9 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21692
21491
|
props.grameneMaps
|
|
21693
21492
|
]);
|
|
21694
21493
|
// Effective (controlled) view state = user interactions + control-driven
|
|
21695
|
-
// pruning.
|
|
21494
|
+
// pruning. The derived set REPLACES prunedNodeIds (this view has no user
|
|
21495
|
+
// prunes of its own; the snapshot strips the field for the same reason).
|
|
21496
|
+
// Memoised so the reference is stable across unrelated renders.
|
|
21696
21497
|
const effectiveViewState = (0, $gXNCa$react.useMemo)(()=>({
|
|
21697
21498
|
...viewState,
|
|
21698
21499
|
prunedNodeIds: prunedNodeIds
|
|
@@ -21713,20 +21514,18 @@ const $15504f5eba8e73bd$var$TaxDistTbrowse = (props)=>{
|
|
|
21713
21514
|
const el = wrapperRef.current;
|
|
21714
21515
|
if (!el || el.clientWidth <= 0 || window.innerWidth < 800) return;
|
|
21715
21516
|
sizedRef.current = true;
|
|
21716
|
-
const
|
|
21517
|
+
const widths = (0, $gXNCa$tbrowsecore.pixelWidthsToFr)({
|
|
21717
21518
|
tree: 270,
|
|
21718
21519
|
labels: 200,
|
|
21719
21520
|
counts: 64
|
|
21720
|
-
};
|
|
21721
|
-
const used = TARGET.tree + TARGET.labels + TARGET.counts;
|
|
21722
|
-
const binsW = Math.max(160, el.clientWidth - used);
|
|
21521
|
+
}, el.clientWidth, 'bins', 160);
|
|
21723
21522
|
const base = $15504f5eba8e73bd$var$baseViewState();
|
|
21724
21523
|
setTaxTreeView({
|
|
21725
21524
|
viewState: {
|
|
21726
21525
|
...base,
|
|
21727
21526
|
zones: base.zones.map((z)=>({
|
|
21728
21527
|
...z,
|
|
21729
|
-
width:
|
|
21528
|
+
width: widths[z.id] ?? z.width
|
|
21730
21529
|
}))
|
|
21731
21530
|
}
|
|
21732
21531
|
});
|