gramene-search 2.0.6 → 2.0.7
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/.claude/settings.local.json +1 -1
- package/.parcel-cache/83e7562660f7cc15-BundleGraph +0 -0
- package/.parcel-cache/d3a1b9507cb44047-AssetGraph +0 -0
- package/.parcel-cache/data.mdb +0 -0
- package/.parcel-cache/dc1da35000e13623-RequestGraph +0 -0
- package/.parcel-cache/lock.mdb +0 -0
- package/.parcel-cache/snapshot-dc1da35000e13623.txt +2 -2
- package/dist/index.css +176 -0
- package/dist/index.css.map +1 -1
- package/dist/index.js +2715 -1209
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/bundles/docs.js +42 -15
- package/src/bundles/gsea.js +568 -0
- package/src/bundles/index.js +2 -1
- package/src/bundles/ontologies.js +52 -60
- package/src/bundles/views.js +7 -1
- package/src/components/geneSearchUI.js +2 -0
- package/src/components/results/GSEA.js +618 -0
- package/src/components/results/HelpDemo.js +30 -0
- package/src/components/results/gsea.css +177 -0
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ var $gXNCa$gramenebinsclient = require("gramene-bins-client");
|
|
|
11
11
|
var $gXNCa$gramenetreesclient = require("gramene-trees-client");
|
|
12
12
|
var $gXNCa$gramenetaxonomywithgenomes = require("gramene-taxonomy-with-genomes");
|
|
13
13
|
var $gXNCa$reactga4 = require("react-ga4");
|
|
14
|
+
var $gXNCa$moneyclip = require("money-clip");
|
|
14
15
|
var $gXNCa$reactbootstrap = require("react-bootstrap");
|
|
15
16
|
var $gXNCa$reactswitch = require("react-switch");
|
|
16
17
|
var $gXNCa$reacticonsio5 = require("react-icons/io5");
|
|
@@ -1456,6 +1457,12 @@ const $24971af0a229e0e3$var$grameneViews = {
|
|
|
1456
1457
|
show: 'off',
|
|
1457
1458
|
shouldScroll: false
|
|
1458
1459
|
},
|
|
1460
|
+
{
|
|
1461
|
+
id: 'gsea',
|
|
1462
|
+
name: 'Gene set enrichment',
|
|
1463
|
+
show: 'off',
|
|
1464
|
+
shouldScroll: false
|
|
1465
|
+
},
|
|
1459
1466
|
{
|
|
1460
1467
|
id: 'userLists',
|
|
1461
1468
|
name: 'User Gene Lists',
|
|
@@ -1551,7 +1558,9 @@ const $24971af0a229e0e3$var$grameneViews = {
|
|
|
1551
1558
|
const resultDependentIds = new Set([
|
|
1552
1559
|
'taxonomy',
|
|
1553
1560
|
'list',
|
|
1554
|
-
'export'
|
|
1561
|
+
'export',
|
|
1562
|
+
'exprViz',
|
|
1563
|
+
'gsea'
|
|
1555
1564
|
]);
|
|
1556
1565
|
const autoDisable = numFound === 0 || !hasFilters;
|
|
1557
1566
|
const hasFirebase = !!(config && config.firebaseConfig);
|
|
@@ -1599,6 +1608,16 @@ const $24971af0a229e0e3$var$grameneViews = {
|
|
|
1599
1608
|
var $24971af0a229e0e3$export$2e2bcd8739ae039 = $24971af0a229e0e3$var$grameneViews;
|
|
1600
1609
|
|
|
1601
1610
|
|
|
1611
|
+
|
|
1612
|
+
// Dedicated IndexedDB store for the full pathway corpus, persisted
|
|
1613
|
+
// indefinitely (default maxAge of `Infinity`). The pathway set is small
|
|
1614
|
+
// and stable enough to keep around across sessions, so we bulk-load it
|
|
1615
|
+
// once with `?rows=-1` and reuse it instead of issuing per-id requests.
|
|
1616
|
+
const $671312b287158a8a$var$pathwayCache = (0, $gXNCa$moneyclip.getConfiguredCache)({
|
|
1617
|
+
version: 1,
|
|
1618
|
+
name: 'gramene_pathways'
|
|
1619
|
+
});
|
|
1620
|
+
let $671312b287158a8a$var$pathwaysBulkPromise = null;
|
|
1602
1621
|
const $671312b287158a8a$var$grameneDocs = {
|
|
1603
1622
|
name: 'grameneDocs',
|
|
1604
1623
|
getReducer: ()=>{
|
|
@@ -1803,26 +1822,37 @@ const $671312b287158a8a$var$grameneDocs = {
|
|
|
1803
1822
|
});
|
|
1804
1823
|
}
|
|
1805
1824
|
},
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
if (
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1825
|
+
// Signature kept for backward compatibility; the `ids` argument is
|
|
1826
|
+
// ignored. On first call we bulk-load every pathway record from the
|
|
1827
|
+
// dedicated IndexedDB cache (or `${api}/pathways?rows=-1` on miss),
|
|
1828
|
+
// dispatch a single GRAMENE_PATHWAYS_RECEIVED, and short-circuit all
|
|
1829
|
+
// subsequent calls.
|
|
1830
|
+
doRequestGramenePathways: (_ids)=>({ dispatch: dispatch, store: store })=>{
|
|
1831
|
+
if ($671312b287158a8a$var$pathwaysBulkPromise) return $671312b287158a8a$var$pathwaysBulkPromise;
|
|
1832
|
+
$671312b287158a8a$var$pathwaysBulkPromise = $671312b287158a8a$var$pathwayCache.get('all').then((cached)=>{
|
|
1833
|
+
if (cached) {
|
|
1834
|
+
dispatch({
|
|
1835
|
+
type: 'GRAMENE_PATHWAYS_RECEIVED',
|
|
1836
|
+
payload: cached
|
|
1837
|
+
});
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
return fetch(`${store.selectGrameneAPI()}/pathways?rows=-1`).then((res)=>res.json()).then((res)=>{
|
|
1841
|
+
const pathways = {};
|
|
1842
|
+
(res || []).forEach((p)=>{
|
|
1843
|
+
if (p && p._id != null) pathways[p._id] = p;
|
|
1819
1844
|
});
|
|
1820
1845
|
dispatch({
|
|
1821
1846
|
type: 'GRAMENE_PATHWAYS_RECEIVED',
|
|
1822
1847
|
payload: pathways
|
|
1823
1848
|
});
|
|
1849
|
+
$671312b287158a8a$var$pathwayCache.set('all', pathways).catch((e)=>console.warn('Failed to cache pathways', e));
|
|
1824
1850
|
});
|
|
1825
|
-
}
|
|
1851
|
+
}).catch((err)=>{
|
|
1852
|
+
console.error('Failed to load pathways', err);
|
|
1853
|
+
$671312b287158a8a$var$pathwaysBulkPromise = null; // allow retry on next call
|
|
1854
|
+
});
|
|
1855
|
+
return $671312b287158a8a$var$pathwaysBulkPromise;
|
|
1826
1856
|
},
|
|
1827
1857
|
doRequestExpressionStudies: (id)=>({ dispatch: dispatch, store: store })=>{
|
|
1828
1858
|
fetch(`${store.selectGrameneAPI()}/experiments?rows=-1`).then((res)=>res.json()).then((res)=>{
|
|
@@ -3765,13 +3795,21 @@ const $1508f5a42be6e7b5$var$exporter = {
|
|
|
3765
3795
|
var $1508f5a42be6e7b5$export$2e2bcd8739ae039 = $1508f5a42be6e7b5$var$exporter;
|
|
3766
3796
|
|
|
3767
3797
|
|
|
3798
|
+
|
|
3768
3799
|
const $c921a0d2b34aadb6$var$ONT_KEYS = [
|
|
3769
3800
|
'GO',
|
|
3770
3801
|
'PO',
|
|
3771
3802
|
'TO',
|
|
3772
3803
|
'domains'
|
|
3773
3804
|
];
|
|
3774
|
-
|
|
3805
|
+
// Dedicated IndexedDB store for ontology records. The full term set per
|
|
3806
|
+
// ontology is stable enough to keep around indefinitely (default maxAge of
|
|
3807
|
+
// `Infinity`), and we don't want it to share the short TTL of the app-wide
|
|
3808
|
+
// cache configured in demo.js.
|
|
3809
|
+
const $c921a0d2b34aadb6$var$cache = (0, $gXNCa$moneyclip.getConfiguredCache)({
|
|
3810
|
+
version: 1,
|
|
3811
|
+
name: 'gramene_ontologies'
|
|
3812
|
+
});
|
|
3775
3813
|
const $c921a0d2b34aadb6$var$inflight = {};
|
|
3776
3814
|
const $c921a0d2b34aadb6$var$ontologies = {
|
|
3777
3815
|
name: 'ontologies',
|
|
@@ -3780,95 +3818,64 @@ const $c921a0d2b34aadb6$var$ontologies = {
|
|
|
3780
3818
|
GO: {},
|
|
3781
3819
|
PO: {},
|
|
3782
3820
|
TO: {},
|
|
3783
|
-
domains: {}
|
|
3821
|
+
domains: {},
|
|
3822
|
+
loaded: {}
|
|
3784
3823
|
};
|
|
3785
3824
|
return (state = initialState, { type: type, payload: payload })=>{
|
|
3786
3825
|
switch(type){
|
|
3787
|
-
case '
|
|
3788
|
-
{
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
[key]: bucket
|
|
3797
|
-
};
|
|
3798
|
-
}
|
|
3799
|
-
case 'ONTOLOGY_RECORDS_RECEIVED':
|
|
3800
|
-
{
|
|
3801
|
-
const { key: key, records: records } = payload;
|
|
3802
|
-
return {
|
|
3803
|
-
...state,
|
|
3804
|
-
[key]: {
|
|
3805
|
-
...state[key],
|
|
3806
|
-
...records
|
|
3807
|
-
}
|
|
3808
|
-
};
|
|
3809
|
-
}
|
|
3826
|
+
case 'ONTOLOGY_BULK_LOADED':
|
|
3827
|
+
return {
|
|
3828
|
+
...state,
|
|
3829
|
+
[payload.key]: payload.records,
|
|
3830
|
+
loaded: {
|
|
3831
|
+
...state.loaded,
|
|
3832
|
+
[payload.key]: true
|
|
3833
|
+
}
|
|
3834
|
+
};
|
|
3810
3835
|
default:
|
|
3811
3836
|
return state;
|
|
3812
3837
|
}
|
|
3813
3838
|
};
|
|
3814
3839
|
},
|
|
3815
|
-
|
|
3840
|
+
// Signature kept for backward compatibility — the `ids` argument is
|
|
3841
|
+
// ignored. On first call per ontology key we load the full term set
|
|
3842
|
+
// (cache hit if available, otherwise `${api}/${key}?rows=-1`) and
|
|
3843
|
+
// dispatch a single bulk load. Subsequent calls are no-ops.
|
|
3844
|
+
doEnsureOntologyRecords: (key, _ids)=>({ dispatch: dispatch, store: store })=>{
|
|
3816
3845
|
if (!$c921a0d2b34aadb6$var$ONT_KEYS.includes(key)) return Promise.resolve();
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
if (
|
|
3822
|
-
const idNum = +id;
|
|
3823
|
-
if (!existing.hasOwnProperty(idNum) && !($c921a0d2b34aadb6$var$inflight[key] && $c921a0d2b34aadb6$var$inflight[key].has(idNum))) missing.push(idNum);
|
|
3824
|
-
}
|
|
3825
|
-
if (missing.length === 0) return Promise.resolve();
|
|
3826
|
-
if (!$c921a0d2b34aadb6$var$inflight[key]) $c921a0d2b34aadb6$var$inflight[key] = new Set();
|
|
3827
|
-
for (const id of missing)$c921a0d2b34aadb6$var$inflight[key].add(id);
|
|
3828
|
-
dispatch({
|
|
3829
|
-
type: 'ONTOLOGY_RECORDS_REQUESTED',
|
|
3830
|
-
payload: {
|
|
3831
|
-
key: key,
|
|
3832
|
-
ids: missing
|
|
3833
|
-
}
|
|
3834
|
-
});
|
|
3835
|
-
const api = store.selectGrameneAPI();
|
|
3836
|
-
const batches = [];
|
|
3837
|
-
for(let i = 0; i < missing.length; i += $c921a0d2b34aadb6$var$BATCH_SIZE)batches.push(missing.slice(i, i + $c921a0d2b34aadb6$var$BATCH_SIZE));
|
|
3838
|
-
const fetchBatch = (batch)=>{
|
|
3839
|
-
const idList = batch.length === 1 ? `${batch[0]},0` : batch.join(',');
|
|
3840
|
-
return fetch(`${api}/${key}?idList=${idList}&rows=${batch.length + 1}`).then((r)=>r.json()).then((docs)=>{
|
|
3841
|
-
const records = {};
|
|
3842
|
-
for (const d of docs || [])if (d && d._id != null) records[d._id] = d;
|
|
3843
|
-
for (const id of batch)if (!records.hasOwnProperty(id)) records[id] = {
|
|
3844
|
-
_id: id,
|
|
3845
|
-
missing: true
|
|
3846
|
-
};
|
|
3846
|
+
const state = store.selectOntologies();
|
|
3847
|
+
if (state.loaded && state.loaded[key]) return Promise.resolve();
|
|
3848
|
+
if ($c921a0d2b34aadb6$var$inflight[key]) return $c921a0d2b34aadb6$var$inflight[key];
|
|
3849
|
+
$c921a0d2b34aadb6$var$inflight[key] = $c921a0d2b34aadb6$var$cache.get(key).then((cached)=>{
|
|
3850
|
+
if (cached) {
|
|
3847
3851
|
dispatch({
|
|
3848
|
-
type: '
|
|
3852
|
+
type: 'ONTOLOGY_BULK_LOADED',
|
|
3849
3853
|
payload: {
|
|
3850
3854
|
key: key,
|
|
3851
|
-
records:
|
|
3855
|
+
records: cached
|
|
3852
3856
|
}
|
|
3853
3857
|
});
|
|
3854
|
-
|
|
3858
|
+
return;
|
|
3859
|
+
}
|
|
3860
|
+
const api = store.selectGrameneAPI();
|
|
3861
|
+
return fetch(`${api}/${key}?rows=-1`).then((r)=>r.json()).then((docs)=>{
|
|
3855
3862
|
const records = {};
|
|
3856
|
-
for (const
|
|
3857
|
-
_id: id,
|
|
3858
|
-
missing: true
|
|
3859
|
-
};
|
|
3863
|
+
for (const d of docs || [])if (d && d._id != null) records[d._id] = d;
|
|
3860
3864
|
dispatch({
|
|
3861
|
-
type: '
|
|
3865
|
+
type: 'ONTOLOGY_BULK_LOADED',
|
|
3862
3866
|
payload: {
|
|
3863
3867
|
key: key,
|
|
3864
3868
|
records: records
|
|
3865
3869
|
}
|
|
3866
3870
|
});
|
|
3867
|
-
|
|
3868
|
-
for (const id of batch)$c921a0d2b34aadb6$var$inflight[key].delete(id);
|
|
3871
|
+
$c921a0d2b34aadb6$var$cache.set(key, records).catch((e)=>console.warn('Failed to cache ontology', key, e));
|
|
3869
3872
|
});
|
|
3870
|
-
}
|
|
3871
|
-
|
|
3873
|
+
}).catch((err)=>{
|
|
3874
|
+
console.error(`Failed to load ontology ${key}`, err);
|
|
3875
|
+
}).finally(()=>{
|
|
3876
|
+
delete $c921a0d2b34aadb6$var$inflight[key];
|
|
3877
|
+
});
|
|
3878
|
+
return $c921a0d2b34aadb6$var$inflight[key];
|
|
3872
3879
|
},
|
|
3873
3880
|
selectOntologies: (state)=>state.ontologies
|
|
3874
3881
|
};
|
|
@@ -4443,185 +4450,863 @@ const $4f15cd8a7d970b18$var$exprViz = {
|
|
|
4443
4450
|
var $4f15cd8a7d970b18$export$2e2bcd8739ae039 = $4f15cd8a7d970b18$var$exprViz;
|
|
4444
4451
|
|
|
4445
4452
|
|
|
4446
|
-
var $5df6c55c1bef3469$export$2e2bcd8739ae039 = [
|
|
4447
|
-
...(0, $9d9aeaf9299e61a1$export$2e2bcd8739ae039),
|
|
4448
|
-
(0, $671312b287158a8a$export$2e2bcd8739ae039),
|
|
4449
|
-
(0, $af4441dd29af05df$export$2e2bcd8739ae039),
|
|
4450
|
-
(0, $24971af0a229e0e3$export$2e2bcd8739ae039),
|
|
4451
|
-
(0, $0d54502f6cafe273$export$2e2bcd8739ae039),
|
|
4452
|
-
(0, $0f839422d0d8c772$export$2e2bcd8739ae039),
|
|
4453
|
-
(0, $1508f5a42be6e7b5$export$2e2bcd8739ae039),
|
|
4454
|
-
(0, $c921a0d2b34aadb6$export$2e2bcd8739ae039),
|
|
4455
|
-
(0, $4f15cd8a7d970b18$export$2e2bcd8739ae039)
|
|
4456
|
-
];
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
4453
|
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4454
|
+
// Gene Set Enrichment Analysis bundle.
|
|
4455
|
+
//
|
|
4456
|
+
// For each species tab we run:
|
|
4457
|
+
// foreground: q=<filters>&fq=taxon_id:<tid>&rows=0 + facet on six __ancestors fields
|
|
4458
|
+
// background: q=taxon_id:<tid>&rows=0 + same facets (cached forever per tid)
|
|
4459
|
+
//
|
|
4460
|
+
// Per-ontology denominators (n_ont, N_ont) come from the root term's facet
|
|
4461
|
+
// count, since every annotated gene carries the root in __ancestors. Roots
|
|
4462
|
+
// are identified once ontology records are loaded by picking the ancestor
|
|
4463
|
+
// (or self) with the highest background facet count — this works for
|
|
4464
|
+
// multi-rooted GO (BP/MF/CC) and for the rice-rooted pathway tree.
|
|
4465
|
+
//
|
|
4466
|
+
// Enrichment uses the upper-tail Fisher exact (hypergeometric); p-values
|
|
4467
|
+
// are corrected per ontology with Benjamini–Hochberg. The "most-specific"
|
|
4468
|
+
// collapse is applied AFTER BH so a parent term that's significant on its
|
|
4469
|
+
// own is preserved even when none of its children clear the cutoff.
|
|
4470
|
+
const $0736cb5a41609896$var$ONTOLOGIES = [
|
|
4471
|
+
{
|
|
4472
|
+
key: 'GO',
|
|
4473
|
+
field: 'GO__ancestors',
|
|
4474
|
+
label: 'Gene Ontology',
|
|
4475
|
+
bucket: 'GO'
|
|
4476
|
+
},
|
|
4477
|
+
{
|
|
4478
|
+
key: 'PO',
|
|
4479
|
+
field: 'PO__ancestors',
|
|
4480
|
+
label: 'Plant Ontology',
|
|
4481
|
+
bucket: 'PO'
|
|
4482
|
+
},
|
|
4483
|
+
{
|
|
4484
|
+
key: 'TO',
|
|
4485
|
+
field: 'TO__ancestors',
|
|
4486
|
+
label: 'Trait Ontology',
|
|
4487
|
+
bucket: 'TO'
|
|
4488
|
+
},
|
|
4489
|
+
{
|
|
4490
|
+
key: 'QTL_TO',
|
|
4491
|
+
field: 'QTL_TO__ancestors',
|
|
4492
|
+
label: 'QTL Traits (TO)',
|
|
4493
|
+
bucket: 'TO'
|
|
4494
|
+
},
|
|
4495
|
+
{
|
|
4496
|
+
key: 'domains',
|
|
4497
|
+
field: 'domains__ancestors',
|
|
4498
|
+
label: 'InterPro Domains',
|
|
4499
|
+
bucket: 'domains'
|
|
4500
|
+
},
|
|
4501
|
+
{
|
|
4502
|
+
key: 'pathways',
|
|
4503
|
+
field: 'pathways__ancestors',
|
|
4504
|
+
label: 'Pathways',
|
|
4505
|
+
bucket: null
|
|
4496
4506
|
}
|
|
4497
|
-
|
|
4498
|
-
const $
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
}
|
|
4503
|
-
}
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
}
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
className: "container mb40 anchor",
|
|
4554
|
-
children: [
|
|
4555
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
4556
|
-
className: "fancy-title mb40",
|
|
4557
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
|
|
4558
|
-
children: "Species"
|
|
4559
|
-
})
|
|
4560
|
-
}),
|
|
4561
|
-
results.taxonomy.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Taxon, {
|
|
4562
|
-
taxon: doc
|
|
4563
|
-
}, idx))
|
|
4564
|
-
]
|
|
4565
|
-
});
|
|
4566
|
-
};
|
|
4567
|
-
const $2fec4872fbf7ebd2$var$ResultList = ({ grameneGenes: grameneGenes, grameneDomains: grameneDomains, gramenePathways: gramenePathways, grameneTaxonomy: grameneTaxonomy, searchUI: searchUI, searchUpdated: searchUpdated, doChangeQuantity: doChangeQuantity })=>{
|
|
4568
|
-
if (searchUI.Gramene) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
4569
|
-
id: "gramene",
|
|
4570
|
-
className: "row",
|
|
4571
|
-
children: [
|
|
4572
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
4573
|
-
className: "fancy-title pt50",
|
|
4574
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h3", {
|
|
4575
|
-
children: "Gramene search results"
|
|
4576
|
-
})
|
|
4577
|
-
}),
|
|
4578
|
-
searchUI.Genes && $2fec4872fbf7ebd2$var$Genes(grameneGenes, searchUI.rows.Genes, doChangeQuantity),
|
|
4579
|
-
searchUI.Domains && $2fec4872fbf7ebd2$var$Domains(grameneDomains),
|
|
4580
|
-
searchUI.Pathways && $2fec4872fbf7ebd2$var$Pathways(gramenePathways),
|
|
4581
|
-
searchUI.Species && $2fec4872fbf7ebd2$var$Species(grameneTaxonomy)
|
|
4582
|
-
]
|
|
4583
|
-
});
|
|
4584
|
-
else return null;
|
|
4585
|
-
};
|
|
4586
|
-
var $2fec4872fbf7ebd2$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneGenes', 'selectGrameneDomains', 'selectGramenePathways', 'selectGrameneTaxonomy', 'selectSearchUI', 'selectSearchUpdated', 'doChangeQuantity', $2fec4872fbf7ebd2$var$ResultList);
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
const $27617dbc24e7faf0$var$getStatus = (cat, results, isChecked, toggle)=>{
|
|
4593
|
-
const tally = results ? results.numFound : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("img", {
|
|
4594
|
-
src: "/static/images/dna_spinner.svg"
|
|
4595
|
-
});
|
|
4596
|
-
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
|
|
4597
|
-
className: "category-leaf",
|
|
4598
|
-
children: [
|
|
4599
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
|
|
4600
|
-
type: "checkbox",
|
|
4601
|
-
checked: isChecked,
|
|
4602
|
-
onChange: (e)=>toggle(cat)
|
|
4603
|
-
}),
|
|
4604
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
|
|
4605
|
-
"data-scroll": true,
|
|
4606
|
-
href: `#${cat}`,
|
|
4607
|
-
className: "nav-link active",
|
|
4608
|
-
children: [
|
|
4609
|
-
cat,
|
|
4610
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
4611
|
-
style: {
|
|
4612
|
-
float: "right"
|
|
4507
|
+
];
|
|
4508
|
+
const $0736cb5a41609896$var$FACET_PARAMS = $0736cb5a41609896$var$ONTOLOGIES.map((o)=>`facet.field=${encodeURIComponent(`{!facet.limit=10000 facet.mincount=1 key=${o.key}}${o.field}`)}`).join('&');
|
|
4509
|
+
const $0736cb5a41609896$var$fgPending = {};
|
|
4510
|
+
const $0736cb5a41609896$var$bgPending = {};
|
|
4511
|
+
function $0736cb5a41609896$var$fgSig(q, taxon) {
|
|
4512
|
+
return `${q}|${taxon}`;
|
|
4513
|
+
}
|
|
4514
|
+
function $0736cb5a41609896$var$bgSig(taxon) {
|
|
4515
|
+
return `bg|${taxon}`;
|
|
4516
|
+
}
|
|
4517
|
+
function $0736cb5a41609896$var$parseFacets(json) {
|
|
4518
|
+
const out = {};
|
|
4519
|
+
const ff = json && json.facet_counts && json.facet_counts.facet_fields || {};
|
|
4520
|
+
for (const o of $0736cb5a41609896$var$ONTOLOGIES){
|
|
4521
|
+
const arr = ff[o.key] || [];
|
|
4522
|
+
const map = {};
|
|
4523
|
+
for(let i = 0; i < arr.length; i += 2)map[+arr[i]] = +arr[i + 1];
|
|
4524
|
+
out[o.key] = map;
|
|
4525
|
+
}
|
|
4526
|
+
return out;
|
|
4527
|
+
}
|
|
4528
|
+
const $0736cb5a41609896$var$gsea = {
|
|
4529
|
+
name: 'gsea',
|
|
4530
|
+
// Background facet counts depend only on the species — they're invariant
|
|
4531
|
+
// across filter changes and across sessions, so we persist whenever a bg
|
|
4532
|
+
// fetch completes. Foreground state piggybacks on the same write but is
|
|
4533
|
+
// self-invalidated by the signature check in the reactor.
|
|
4534
|
+
persistActions: [
|
|
4535
|
+
'GSEA_BG_SUCCEEDED'
|
|
4536
|
+
],
|
|
4537
|
+
getReducer: ()=>{
|
|
4538
|
+
const initialState = {
|
|
4539
|
+
activeTaxon: null,
|
|
4540
|
+
byTaxon: {},
|
|
4541
|
+
ui: {
|
|
4542
|
+
pAdjCutoff: 0.05,
|
|
4543
|
+
minK: 2,
|
|
4544
|
+
mostSpecific: true,
|
|
4545
|
+
ontology: 'all',
|
|
4546
|
+
search: ''
|
|
4547
|
+
}
|
|
4548
|
+
};
|
|
4549
|
+
function ensureTaxon(state, tid) {
|
|
4550
|
+
if (state.byTaxon[tid]) return state;
|
|
4551
|
+
return {
|
|
4552
|
+
...state,
|
|
4553
|
+
byTaxon: {
|
|
4554
|
+
...state.byTaxon,
|
|
4555
|
+
[tid]: {
|
|
4556
|
+
fg: {
|
|
4557
|
+
status: 'idle',
|
|
4558
|
+
signature: null,
|
|
4559
|
+
requestId: 0,
|
|
4560
|
+
terms: null,
|
|
4561
|
+
numFound: 0,
|
|
4562
|
+
error: null
|
|
4613
4563
|
},
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4564
|
+
bg: {
|
|
4565
|
+
status: 'idle',
|
|
4566
|
+
signature: null,
|
|
4567
|
+
requestId: 0,
|
|
4568
|
+
terms: null,
|
|
4569
|
+
numFound: 0,
|
|
4570
|
+
error: null
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
return (state = initialState, { type: type, payload: payload })=>{
|
|
4577
|
+
switch(type){
|
|
4578
|
+
case 'GSEA_ACTIVE_TAXON_SET':
|
|
4579
|
+
return {
|
|
4580
|
+
...ensureTaxon(state, payload),
|
|
4581
|
+
activeTaxon: payload
|
|
4582
|
+
};
|
|
4583
|
+
case 'GSEA_UI_SET':
|
|
4584
|
+
return {
|
|
4585
|
+
...state,
|
|
4586
|
+
ui: {
|
|
4587
|
+
...state.ui,
|
|
4588
|
+
...payload || {}
|
|
4589
|
+
}
|
|
4590
|
+
};
|
|
4591
|
+
case 'GSEA_FG_STARTED':
|
|
4592
|
+
{
|
|
4593
|
+
const next = ensureTaxon(state, payload.taxon);
|
|
4594
|
+
const t = next.byTaxon[payload.taxon];
|
|
4595
|
+
return {
|
|
4596
|
+
...next,
|
|
4597
|
+
byTaxon: {
|
|
4598
|
+
...next.byTaxon,
|
|
4599
|
+
[payload.taxon]: {
|
|
4600
|
+
...t,
|
|
4601
|
+
fg: {
|
|
4602
|
+
status: 'loading',
|
|
4603
|
+
signature: payload.signature,
|
|
4604
|
+
requestId: payload.requestId,
|
|
4605
|
+
terms: t.fg.terms,
|
|
4606
|
+
numFound: t.fg.numFound,
|
|
4607
|
+
error: null
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
};
|
|
4612
|
+
}
|
|
4613
|
+
case 'GSEA_FG_SUCCEEDED':
|
|
4614
|
+
{
|
|
4615
|
+
const t = state.byTaxon[payload.taxon];
|
|
4616
|
+
if (!t || payload.requestId !== t.fg.requestId) return state;
|
|
4617
|
+
return {
|
|
4618
|
+
...state,
|
|
4619
|
+
byTaxon: {
|
|
4620
|
+
...state.byTaxon,
|
|
4621
|
+
[payload.taxon]: {
|
|
4622
|
+
...t,
|
|
4623
|
+
fg: {
|
|
4624
|
+
...t.fg,
|
|
4625
|
+
status: 'ready',
|
|
4626
|
+
terms: payload.terms,
|
|
4627
|
+
numFound: payload.numFound
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
};
|
|
4632
|
+
}
|
|
4633
|
+
case 'GSEA_FG_FAILED':
|
|
4634
|
+
{
|
|
4635
|
+
const t = state.byTaxon[payload.taxon];
|
|
4636
|
+
if (!t || payload.requestId !== t.fg.requestId) return state;
|
|
4637
|
+
return {
|
|
4638
|
+
...state,
|
|
4639
|
+
byTaxon: {
|
|
4640
|
+
...state.byTaxon,
|
|
4641
|
+
[payload.taxon]: {
|
|
4642
|
+
...t,
|
|
4643
|
+
fg: {
|
|
4644
|
+
...t.fg,
|
|
4645
|
+
status: 'error',
|
|
4646
|
+
error: payload.error
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
};
|
|
4651
|
+
}
|
|
4652
|
+
case 'GSEA_BG_STARTED':
|
|
4653
|
+
{
|
|
4654
|
+
const next = ensureTaxon(state, payload.taxon);
|
|
4655
|
+
const t = next.byTaxon[payload.taxon];
|
|
4656
|
+
return {
|
|
4657
|
+
...next,
|
|
4658
|
+
byTaxon: {
|
|
4659
|
+
...next.byTaxon,
|
|
4660
|
+
[payload.taxon]: {
|
|
4661
|
+
...t,
|
|
4662
|
+
bg: {
|
|
4663
|
+
status: 'loading',
|
|
4664
|
+
signature: payload.signature,
|
|
4665
|
+
requestId: payload.requestId,
|
|
4666
|
+
terms: t.bg.terms,
|
|
4667
|
+
numFound: t.bg.numFound,
|
|
4668
|
+
error: null
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
};
|
|
4673
|
+
}
|
|
4674
|
+
case 'GSEA_BG_SUCCEEDED':
|
|
4675
|
+
{
|
|
4676
|
+
const t = state.byTaxon[payload.taxon];
|
|
4677
|
+
if (!t || payload.requestId !== t.bg.requestId) return state;
|
|
4678
|
+
return {
|
|
4679
|
+
...state,
|
|
4680
|
+
byTaxon: {
|
|
4681
|
+
...state.byTaxon,
|
|
4682
|
+
[payload.taxon]: {
|
|
4683
|
+
...t,
|
|
4684
|
+
bg: {
|
|
4685
|
+
...t.bg,
|
|
4686
|
+
status: 'ready',
|
|
4687
|
+
terms: payload.terms,
|
|
4688
|
+
numFound: payload.numFound
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
case 'GSEA_BG_FAILED':
|
|
4695
|
+
{
|
|
4696
|
+
const t = state.byTaxon[payload.taxon];
|
|
4697
|
+
if (!t || payload.requestId !== t.bg.requestId) return state;
|
|
4698
|
+
return {
|
|
4699
|
+
...state,
|
|
4700
|
+
byTaxon: {
|
|
4701
|
+
...state.byTaxon,
|
|
4702
|
+
[payload.taxon]: {
|
|
4703
|
+
...t,
|
|
4704
|
+
bg: {
|
|
4705
|
+
...t.bg,
|
|
4706
|
+
status: 'error',
|
|
4707
|
+
error: payload.error
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
case 'GRAMENE_SEARCH_CLEARED':
|
|
4714
|
+
{
|
|
4715
|
+
// Filters changed — invalidate foreground but keep background
|
|
4716
|
+
// (it depends only on taxon).
|
|
4717
|
+
const newByTaxon = {};
|
|
4718
|
+
for (const tid of Object.keys(state.byTaxon)){
|
|
4719
|
+
const t = state.byTaxon[tid];
|
|
4720
|
+
newByTaxon[tid] = {
|
|
4721
|
+
...t,
|
|
4722
|
+
fg: {
|
|
4723
|
+
status: 'idle',
|
|
4724
|
+
signature: null,
|
|
4725
|
+
requestId: 0,
|
|
4726
|
+
terms: null,
|
|
4727
|
+
numFound: 0,
|
|
4728
|
+
error: null
|
|
4729
|
+
}
|
|
4730
|
+
};
|
|
4731
|
+
}
|
|
4732
|
+
return {
|
|
4733
|
+
...state,
|
|
4734
|
+
byTaxon: newByTaxon
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
default:
|
|
4738
|
+
return state;
|
|
4739
|
+
}
|
|
4740
|
+
};
|
|
4741
|
+
},
|
|
4742
|
+
doSetGseaActiveTaxon: (tid)=>({ dispatch: dispatch })=>dispatch({
|
|
4743
|
+
type: 'GSEA_ACTIVE_TAXON_SET',
|
|
4744
|
+
payload: tid
|
|
4745
|
+
}),
|
|
4746
|
+
doSetGseaUI: (patch)=>({ dispatch: dispatch })=>dispatch({
|
|
4747
|
+
type: 'GSEA_UI_SET',
|
|
4748
|
+
payload: patch
|
|
4749
|
+
}),
|
|
4750
|
+
doFetchGseaForeground: (taxon)=>({ dispatch: dispatch, store: store })=>{
|
|
4751
|
+
const q = store.selectGrameneFiltersQueryString();
|
|
4752
|
+
const signature = $0736cb5a41609896$var$fgSig(q, taxon);
|
|
4753
|
+
const state = store.selectGsea();
|
|
4754
|
+
const t = state.byTaxon[taxon];
|
|
4755
|
+
if (t && t.fg.signature === signature && (t.fg.status === 'loading' || t.fg.status === 'ready')) return;
|
|
4756
|
+
const requestId = $0736cb5a41609896$var$fgPending[taxon] = ($0736cb5a41609896$var$fgPending[taxon] || 0) + 1;
|
|
4757
|
+
dispatch({
|
|
4758
|
+
type: 'GSEA_FG_STARTED',
|
|
4759
|
+
payload: {
|
|
4760
|
+
taxon: taxon,
|
|
4761
|
+
signature: signature,
|
|
4762
|
+
requestId: requestId
|
|
4763
|
+
}
|
|
4764
|
+
});
|
|
4765
|
+
const api = store.selectGrameneAPI();
|
|
4766
|
+
const url = `${api}/search?q=${q}&fq=taxon_id:${taxon}&rows=0&facet=true&${$0736cb5a41609896$var$FACET_PARAMS}`;
|
|
4767
|
+
fetch(url).then((r)=>r.json()).then((json)=>{
|
|
4768
|
+
if (requestId !== $0736cb5a41609896$var$fgPending[taxon]) return;
|
|
4769
|
+
const terms = $0736cb5a41609896$var$parseFacets(json);
|
|
4770
|
+
const numFound = json && json.response && json.response.numFound || 0;
|
|
4771
|
+
dispatch({
|
|
4772
|
+
type: 'GSEA_FG_SUCCEEDED',
|
|
4773
|
+
payload: {
|
|
4774
|
+
taxon: taxon,
|
|
4775
|
+
requestId: requestId,
|
|
4776
|
+
terms: terms,
|
|
4777
|
+
numFound: numFound
|
|
4778
|
+
}
|
|
4779
|
+
});
|
|
4780
|
+
store.doEnsureGseaTermRecords(taxon);
|
|
4781
|
+
}).catch((err)=>{
|
|
4782
|
+
if (requestId !== $0736cb5a41609896$var$fgPending[taxon]) return;
|
|
4783
|
+
dispatch({
|
|
4784
|
+
type: 'GSEA_FG_FAILED',
|
|
4785
|
+
payload: {
|
|
4786
|
+
taxon: taxon,
|
|
4787
|
+
requestId: requestId,
|
|
4788
|
+
error: String(err)
|
|
4789
|
+
}
|
|
4790
|
+
});
|
|
4791
|
+
});
|
|
4792
|
+
},
|
|
4793
|
+
doFetchGseaBackground: (taxon)=>({ dispatch: dispatch, store: store })=>{
|
|
4794
|
+
const signature = $0736cb5a41609896$var$bgSig(taxon);
|
|
4795
|
+
const state = store.selectGsea();
|
|
4796
|
+
const t = state.byTaxon[taxon];
|
|
4797
|
+
if (t && t.bg.signature === signature && (t.bg.status === 'loading' || t.bg.status === 'ready')) return;
|
|
4798
|
+
const requestId = $0736cb5a41609896$var$bgPending[taxon] = ($0736cb5a41609896$var$bgPending[taxon] || 0) + 1;
|
|
4799
|
+
dispatch({
|
|
4800
|
+
type: 'GSEA_BG_STARTED',
|
|
4801
|
+
payload: {
|
|
4802
|
+
taxon: taxon,
|
|
4803
|
+
signature: signature,
|
|
4804
|
+
requestId: requestId
|
|
4805
|
+
}
|
|
4806
|
+
});
|
|
4807
|
+
const api = store.selectGrameneAPI();
|
|
4808
|
+
const url = `${api}/search?q=taxon_id:${taxon}&rows=0&facet=true&${$0736cb5a41609896$var$FACET_PARAMS}`;
|
|
4809
|
+
fetch(url).then((r)=>r.json()).then((json)=>{
|
|
4810
|
+
if (requestId !== $0736cb5a41609896$var$bgPending[taxon]) return;
|
|
4811
|
+
const terms = $0736cb5a41609896$var$parseFacets(json);
|
|
4812
|
+
const numFound = json && json.response && json.response.numFound || 0;
|
|
4813
|
+
dispatch({
|
|
4814
|
+
type: 'GSEA_BG_SUCCEEDED',
|
|
4815
|
+
payload: {
|
|
4816
|
+
taxon: taxon,
|
|
4817
|
+
requestId: requestId,
|
|
4818
|
+
terms: terms,
|
|
4819
|
+
numFound: numFound
|
|
4820
|
+
}
|
|
4821
|
+
});
|
|
4822
|
+
store.doEnsureGseaTermRecords(taxon);
|
|
4823
|
+
}).catch((err)=>{
|
|
4824
|
+
if (requestId !== $0736cb5a41609896$var$bgPending[taxon]) return;
|
|
4825
|
+
dispatch({
|
|
4826
|
+
type: 'GSEA_BG_FAILED',
|
|
4827
|
+
payload: {
|
|
4828
|
+
taxon: taxon,
|
|
4829
|
+
requestId: requestId,
|
|
4830
|
+
error: String(err)
|
|
4831
|
+
}
|
|
4832
|
+
});
|
|
4833
|
+
});
|
|
4834
|
+
},
|
|
4835
|
+
doEnsureGseaTermRecords: (taxon)=>({ store: store })=>{
|
|
4836
|
+
const state = store.selectGsea();
|
|
4837
|
+
if (!state.byTaxon[taxon]) return;
|
|
4838
|
+
// The ontologies and pathways bundles bulk-load + persist on first
|
|
4839
|
+
// request; we just need to nudge each one once.
|
|
4840
|
+
const buckets = new Set();
|
|
4841
|
+
for (const o of $0736cb5a41609896$var$ONTOLOGIES)if (o.bucket) buckets.add(o.bucket);
|
|
4842
|
+
for (const k of buckets)store.doEnsureOntologyRecords(k);
|
|
4843
|
+
if (store.doRequestGramenePathways) store.doRequestGramenePathways();
|
|
4844
|
+
},
|
|
4845
|
+
reactGseaFetch: (0, $gXNCa$reduxbundler.createSelector)('selectGsea', 'selectGrameneFiltersStatus', 'selectGrameneFiltersQueryString', 'selectGrameneViewsOn', (gs, fStatus, q, viewsOn)=>{
|
|
4846
|
+
if (!viewsOn || !viewsOn.has('gsea')) return;
|
|
4847
|
+
if (fStatus === 'init') return;
|
|
4848
|
+
const tid = gs.activeTaxon;
|
|
4849
|
+
if (!tid) return;
|
|
4850
|
+
const t = gs.byTaxon[tid];
|
|
4851
|
+
if (!t) return;
|
|
4852
|
+
const sig = $0736cb5a41609896$var$fgSig(q, tid);
|
|
4853
|
+
// A 'loading' status from a rehydrated state with no live request is
|
|
4854
|
+
// treated as idle — otherwise we'd deadlock waiting on a fetch that
|
|
4855
|
+
// ended when the previous tab closed.
|
|
4856
|
+
const fgInFlight = t.fg.status === 'loading' && ($0736cb5a41609896$var$fgPending[tid] || 0) === t.fg.requestId && t.fg.requestId > 0;
|
|
4857
|
+
if (t.fg.signature !== sig && !fgInFlight) return {
|
|
4858
|
+
actionCreator: 'doFetchGseaForeground',
|
|
4859
|
+
args: [
|
|
4860
|
+
tid
|
|
4861
|
+
]
|
|
4862
|
+
};
|
|
4863
|
+
const bgInFlight = t.bg.status === 'loading' && ($0736cb5a41609896$var$bgPending[tid] || 0) === t.bg.requestId && t.bg.requestId > 0;
|
|
4864
|
+
if (t.bg.status !== 'ready' && !bgInFlight) return {
|
|
4865
|
+
actionCreator: 'doFetchGseaBackground',
|
|
4866
|
+
args: [
|
|
4867
|
+
tid
|
|
4868
|
+
]
|
|
4869
|
+
};
|
|
4870
|
+
}),
|
|
4871
|
+
selectGsea: (state)=>state.gsea,
|
|
4872
|
+
selectGseaUI: (state)=>state.gsea.ui,
|
|
4873
|
+
selectGseaOntologyDefs: ()=>$0736cb5a41609896$var$ONTOLOGIES,
|
|
4874
|
+
selectGseaResults: (0, $gXNCa$reduxbundler.createSelector)('selectGsea', 'selectOntologies', 'selectGramenePathways', (gs, ontoBuckets, pathwayDocs)=>{
|
|
4875
|
+
const tid = gs.activeTaxon;
|
|
4876
|
+
if (!tid) return null;
|
|
4877
|
+
const t = gs.byTaxon[tid];
|
|
4878
|
+
if (!t || !t.fg.terms || !t.bg.terms) return null;
|
|
4879
|
+
const ui = gs.ui;
|
|
4880
|
+
const out = {};
|
|
4881
|
+
for (const o of $0736cb5a41609896$var$ONTOLOGIES){
|
|
4882
|
+
const fg = t.fg.terms[o.key] || {};
|
|
4883
|
+
const bg = t.bg.terms[o.key] || {};
|
|
4884
|
+
const recs = o.key === 'pathways' ? pathwayDocs || {} : ontoBuckets && ontoBuckets[o.bucket] || {};
|
|
4885
|
+
// Forest-fallback denominators: when a term is itself a forest root
|
|
4886
|
+
// (no parents in `recs` — common for InterPro), root finding returns
|
|
4887
|
+
// the term and we'd get fold=1 by construction. Use the maximum
|
|
4888
|
+
// counts across the ontology as a proxy for "annotated in this
|
|
4889
|
+
// ontology" instead. For ontologies with a true synthetic root,
|
|
4890
|
+
// these maxima equal the root counts and the answer is unchanged.
|
|
4891
|
+
let maxFg = 0, maxBg = 0;
|
|
4892
|
+
for (const idStr of Object.keys(bg)){
|
|
4893
|
+
const v = bg[idStr];
|
|
4894
|
+
if (v > maxBg) maxBg = v;
|
|
4895
|
+
}
|
|
4896
|
+
for (const idStr of Object.keys(fg)){
|
|
4897
|
+
const v = fg[idStr];
|
|
4898
|
+
if (v > maxFg) maxFg = v;
|
|
4899
|
+
}
|
|
4900
|
+
const rootCache = {};
|
|
4901
|
+
const rootOf = (id)=>{
|
|
4902
|
+
if (rootCache.hasOwnProperty(id)) return rootCache[id];
|
|
4903
|
+
const r = $0736cb5a41609896$var$findRoot(o.key, id, recs, bg);
|
|
4904
|
+
rootCache[id] = r;
|
|
4905
|
+
return r;
|
|
4906
|
+
};
|
|
4907
|
+
const rows = [];
|
|
4908
|
+
for (const idStr of Object.keys(fg)){
|
|
4909
|
+
const id = +idStr;
|
|
4910
|
+
const k = fg[id];
|
|
4911
|
+
const K = bg[id] || 0;
|
|
4912
|
+
if (K < k) continue; // bg should always be >= fg
|
|
4913
|
+
if (k < ui.minK) continue;
|
|
4914
|
+
const termRec = recs && recs[id];
|
|
4915
|
+
if (termRec && (termRec.is_obsolete || termRec.obsolete)) continue;
|
|
4916
|
+
const root = rootOf(id);
|
|
4917
|
+
// If the "root" is the term itself, the term is a forest root in
|
|
4918
|
+
// this ontology — fall back to ontology-wide maxima.
|
|
4919
|
+
const fellBack = +root === id;
|
|
4920
|
+
const n = fellBack ? maxFg : root != null && fg[root] ? fg[root] : k;
|
|
4921
|
+
const N = fellBack ? maxBg : root != null && bg[root] ? bg[root] : K;
|
|
4922
|
+
if (n <= 0 || N <= 0) continue;
|
|
4923
|
+
if (k > n || K > N) continue;
|
|
4924
|
+
const fold = k / n / (K / N);
|
|
4925
|
+
const p = $0736cb5a41609896$var$fisherUpperTail(k, n, K, N);
|
|
4926
|
+
rows.push({
|
|
4927
|
+
ontology: o.key,
|
|
4928
|
+
ontology_label: o.label,
|
|
4929
|
+
term_id: id,
|
|
4930
|
+
field: o.field,
|
|
4931
|
+
k: k,
|
|
4932
|
+
n: n,
|
|
4933
|
+
K: K,
|
|
4934
|
+
N: N,
|
|
4935
|
+
fold: fold,
|
|
4936
|
+
p: p,
|
|
4937
|
+
pAdj: 1,
|
|
4938
|
+
root: root,
|
|
4939
|
+
denomFallback: fellBack
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
// GO is split into its three top-level namespaces (BP / MF / CC)
|
|
4943
|
+
// and each is tested as its own ontology — both BH correction and
|
|
4944
|
+
// most-specific collapse run within a namespace. We only split once
|
|
4945
|
+
// ontology records have arrived and root finding has produced
|
|
4946
|
+
// canonical roots; otherwise we'd see a swarm of singleton groups
|
|
4947
|
+
// during the brief loading window between bg landing and records
|
|
4948
|
+
// being fetched.
|
|
4949
|
+
if (o.key === 'GO' && Object.keys(recs).length > 0) {
|
|
4950
|
+
const byRoot = {};
|
|
4951
|
+
for (const r of rows){
|
|
4952
|
+
const k = String(r.root);
|
|
4953
|
+
if (!byRoot[k]) byRoot[k] = [];
|
|
4954
|
+
byRoot[k].push(r);
|
|
4955
|
+
}
|
|
4956
|
+
const rootKeys = Object.keys(byRoot).sort((a, b)=>{
|
|
4957
|
+
const na = $0736cb5a41609896$var$goRootName(recs[+a]) || a;
|
|
4958
|
+
const nb = $0736cb5a41609896$var$goRootName(recs[+b]) || b;
|
|
4959
|
+
return na.localeCompare(nb);
|
|
4960
|
+
});
|
|
4961
|
+
for (const rootKey of rootKeys){
|
|
4962
|
+
const rootRec = recs[+rootKey];
|
|
4963
|
+
const rootName = $0736cb5a41609896$var$goRootName(rootRec);
|
|
4964
|
+
const sectionKey = `GO:${rootKey}`;
|
|
4965
|
+
const sectionLabel = rootName ? `GO: ${$0736cb5a41609896$var$titleCase(rootName)}` : `GO: ${rootKey}`;
|
|
4966
|
+
out[sectionKey] = $0736cb5a41609896$var$finalizeBlock(byRoot[rootKey], o.key, o.field, recs, ui, sectionKey, sectionLabel);
|
|
4967
|
+
}
|
|
4968
|
+
} else out[o.key] = $0736cb5a41609896$var$finalizeBlock(rows, o.key, o.field, recs, ui, o.key, o.label);
|
|
4969
|
+
}
|
|
4970
|
+
return out;
|
|
4971
|
+
})
|
|
4972
|
+
};
|
|
4973
|
+
function $0736cb5a41609896$var$goRootName(rec) {
|
|
4974
|
+
if (!rec) return '';
|
|
4975
|
+
return rec.name || rec.display_name || rec.namespace || '';
|
|
4976
|
+
}
|
|
4977
|
+
function $0736cb5a41609896$var$titleCase(s) {
|
|
4978
|
+
return String(s).split('_').map((w)=>w ? w[0].toUpperCase() + w.slice(1) : '').join(' ');
|
|
4979
|
+
}
|
|
4980
|
+
// BH correction + most-specific collapse + metadata + final sort, returning
|
|
4981
|
+
// the block descriptor consumed by the view layer.
|
|
4982
|
+
function $0736cb5a41609896$var$finalizeBlock(rows, ontKey, ontField, recs, ui, sectionKey, sectionLabel) {
|
|
4983
|
+
rows.sort((a, b)=>a.p - b.p);
|
|
4984
|
+
const m = rows.length;
|
|
4985
|
+
let prev = 1;
|
|
4986
|
+
for(let i = m - 1; i >= 0; i--){
|
|
4987
|
+
const adj = Math.min(prev, rows[i].p * m / (i + 1));
|
|
4988
|
+
rows[i].pAdj = adj;
|
|
4989
|
+
prev = adj;
|
|
4990
|
+
}
|
|
4991
|
+
const passing = rows.filter((r)=>r.pAdj <= ui.pAdjCutoff);
|
|
4992
|
+
let display = passing;
|
|
4993
|
+
if (ui.mostSpecific) display = $0736cb5a41609896$var$collapseToMostSpecific(ontKey, passing, recs);
|
|
4994
|
+
for (const r of display){
|
|
4995
|
+
const rec = recs && recs[r.term_id];
|
|
4996
|
+
if (rec) {
|
|
4997
|
+
r.term_name = rec.name || rec.display_name || '';
|
|
4998
|
+
r.term_namespace = rec.namespace || rec.type || '';
|
|
4999
|
+
r.term_display_id = rec.id != null ? String(rec.id) : String(r.term_id);
|
|
5000
|
+
} else {
|
|
5001
|
+
r.term_name = '';
|
|
5002
|
+
r.term_namespace = '';
|
|
5003
|
+
r.term_display_id = String(r.term_id);
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
display.sort((a, b)=>a.pAdj - b.pAdj || b.fold - a.fold);
|
|
5007
|
+
return {
|
|
5008
|
+
ontology: sectionKey,
|
|
5009
|
+
label: sectionLabel,
|
|
5010
|
+
field: ontField,
|
|
5011
|
+
tested: rows.length,
|
|
5012
|
+
passing: passing.length,
|
|
5013
|
+
rows: display
|
|
5014
|
+
};
|
|
5015
|
+
}
|
|
5016
|
+
// ---------- math helpers ----------
|
|
5017
|
+
const $0736cb5a41609896$var$LF_CACHE = [
|
|
5018
|
+
0,
|
|
5019
|
+
0
|
|
5020
|
+
];
|
|
5021
|
+
function $0736cb5a41609896$var$logFactorial(n) {
|
|
5022
|
+
if (n < $0736cb5a41609896$var$LF_CACHE.length) return $0736cb5a41609896$var$LF_CACHE[n];
|
|
5023
|
+
let lf = $0736cb5a41609896$var$LF_CACHE[$0736cb5a41609896$var$LF_CACHE.length - 1];
|
|
5024
|
+
for(let i = $0736cb5a41609896$var$LF_CACHE.length; i <= n; i++){
|
|
5025
|
+
lf += Math.log(i);
|
|
5026
|
+
$0736cb5a41609896$var$LF_CACHE.push(lf);
|
|
5027
|
+
}
|
|
5028
|
+
return $0736cb5a41609896$var$LF_CACHE[n];
|
|
5029
|
+
}
|
|
5030
|
+
function $0736cb5a41609896$var$logChoose(n, k) {
|
|
5031
|
+
if (k < 0 || k > n) return -Infinity;
|
|
5032
|
+
return $0736cb5a41609896$var$logFactorial(n) - $0736cb5a41609896$var$logFactorial(k) - $0736cb5a41609896$var$logFactorial(n - k);
|
|
5033
|
+
}
|
|
5034
|
+
function $0736cb5a41609896$var$logHypergeom(x, n, K, N) {
|
|
5035
|
+
return $0736cb5a41609896$var$logChoose(K, x) + $0736cb5a41609896$var$logChoose(N - K, n - x) - $0736cb5a41609896$var$logChoose(N, n);
|
|
5036
|
+
}
|
|
5037
|
+
function $0736cb5a41609896$var$logSumExp(a, b) {
|
|
5038
|
+
if (a === -Infinity) return b;
|
|
5039
|
+
if (b === -Infinity) return a;
|
|
5040
|
+
const m = Math.max(a, b);
|
|
5041
|
+
return m + Math.log(Math.exp(a - m) + Math.exp(b - m));
|
|
5042
|
+
}
|
|
5043
|
+
function $0736cb5a41609896$var$fisherUpperTail(k, n, K, N) {
|
|
5044
|
+
const upper = Math.min(n, K);
|
|
5045
|
+
let logP = -Infinity;
|
|
5046
|
+
for(let x = k; x <= upper; x++)logP = $0736cb5a41609896$var$logSumExp(logP, $0736cb5a41609896$var$logHypergeom(x, n, K, N));
|
|
5047
|
+
const p = Math.exp(logP);
|
|
5048
|
+
if (!isFinite(p)) return 1;
|
|
5049
|
+
return Math.min(1, Math.max(0, p));
|
|
5050
|
+
}
|
|
5051
|
+
// ---------- ontology graph helpers ----------
|
|
5052
|
+
// Pick the "root" for a term as the ancestor (or self) with the highest
|
|
5053
|
+
// background facet count. The true root has every annotated gene under it,
|
|
5054
|
+
// so this selects BP/MF/CC for GO terms automatically and the rice root
|
|
5055
|
+
// for pathways without needing per-ontology hardcoded ids.
|
|
5056
|
+
function $0736cb5a41609896$var$findRoot(ontKey, id, recs, bgCounts) {
|
|
5057
|
+
const rec = recs && recs[id];
|
|
5058
|
+
const candidates = new Set([
|
|
5059
|
+
+id
|
|
5060
|
+
]);
|
|
5061
|
+
if (rec) {
|
|
5062
|
+
if (ontKey === 'pathways') for (const k of Object.keys(rec)){
|
|
5063
|
+
if (k.startsWith('ancestors_') && Array.isArray(rec[k])) for (const a of rec[k])candidates.add(+a);
|
|
5064
|
+
}
|
|
5065
|
+
else if (Array.isArray(rec.ancestors)) for (const a of rec.ancestors)candidates.add(+a);
|
|
5066
|
+
else if (Array.isArray(rec.is_a)) {
|
|
5067
|
+
const stack = rec.is_a.slice();
|
|
5068
|
+
while(stack.length){
|
|
5069
|
+
const cur = +stack.pop();
|
|
5070
|
+
if (candidates.has(cur)) continue;
|
|
5071
|
+
candidates.add(cur);
|
|
5072
|
+
const r = recs[cur];
|
|
5073
|
+
if (r && Array.isArray(r.is_a)) for (const p of r.is_a)stack.push(p);
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
let best = +id;
|
|
5078
|
+
let bestCount = bgCounts[+id] || 0;
|
|
5079
|
+
for (const c of candidates){
|
|
5080
|
+
const cnt = bgCounts[c] || 0;
|
|
5081
|
+
if (cnt > bestCount) {
|
|
5082
|
+
bestCount = cnt;
|
|
5083
|
+
best = c;
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
return best;
|
|
5087
|
+
}
|
|
5088
|
+
function $0736cb5a41609896$var$ancestorsOf(ontKey, id, recs) {
|
|
5089
|
+
const out = new Set();
|
|
5090
|
+
const rec = recs && recs[id];
|
|
5091
|
+
if (!rec) return out;
|
|
5092
|
+
if (ontKey === 'pathways') {
|
|
5093
|
+
for (const k of Object.keys(rec))if (k.startsWith('ancestors_') && Array.isArray(rec[k])) {
|
|
5094
|
+
for (const a of rec[k])if (+a !== +id) out.add(+a);
|
|
5095
|
+
}
|
|
5096
|
+
return out;
|
|
5097
|
+
}
|
|
5098
|
+
if (Array.isArray(rec.ancestors)) {
|
|
5099
|
+
for (const a of rec.ancestors)if (+a !== +id) out.add(+a);
|
|
5100
|
+
return out;
|
|
5101
|
+
}
|
|
5102
|
+
if (Array.isArray(rec.is_a)) {
|
|
5103
|
+
const stack = rec.is_a.slice();
|
|
5104
|
+
while(stack.length){
|
|
5105
|
+
const cur = +stack.pop();
|
|
5106
|
+
if (out.has(cur)) continue;
|
|
5107
|
+
out.add(cur);
|
|
5108
|
+
const r = recs[cur];
|
|
5109
|
+
if (r && Array.isArray(r.is_a)) for (const p of r.is_a)stack.push(p);
|
|
5110
|
+
}
|
|
5111
|
+
}
|
|
5112
|
+
return out;
|
|
5113
|
+
}
|
|
5114
|
+
// Drop any term that is an ancestor of another term in the same passing set.
|
|
5115
|
+
// Applied AFTER BH so a parent term can survive when none of its children
|
|
5116
|
+
// pass the p_adj cutoff.
|
|
5117
|
+
function $0736cb5a41609896$var$collapseToMostSpecific(ontKey, rows, recs) {
|
|
5118
|
+
if (!rows || rows.length <= 1) return rows;
|
|
5119
|
+
const inSet = new Set(rows.map((r)=>+r.term_id));
|
|
5120
|
+
const covered = new Set();
|
|
5121
|
+
for (const r of rows){
|
|
5122
|
+
const ancs = $0736cb5a41609896$var$ancestorsOf(ontKey, +r.term_id, recs);
|
|
5123
|
+
for (const a of ancs)if (inSet.has(a)) covered.add(a);
|
|
5124
|
+
}
|
|
5125
|
+
return rows.filter((r)=>!covered.has(+r.term_id));
|
|
5126
|
+
}
|
|
5127
|
+
var $0736cb5a41609896$export$2e2bcd8739ae039 = $0736cb5a41609896$var$gsea;
|
|
5128
|
+
|
|
5129
|
+
|
|
5130
|
+
var $5df6c55c1bef3469$export$2e2bcd8739ae039 = [
|
|
5131
|
+
...(0, $9d9aeaf9299e61a1$export$2e2bcd8739ae039),
|
|
5132
|
+
(0, $671312b287158a8a$export$2e2bcd8739ae039),
|
|
5133
|
+
(0, $af4441dd29af05df$export$2e2bcd8739ae039),
|
|
5134
|
+
(0, $24971af0a229e0e3$export$2e2bcd8739ae039),
|
|
5135
|
+
(0, $0d54502f6cafe273$export$2e2bcd8739ae039),
|
|
5136
|
+
(0, $0f839422d0d8c772$export$2e2bcd8739ae039),
|
|
5137
|
+
(0, $1508f5a42be6e7b5$export$2e2bcd8739ae039),
|
|
5138
|
+
(0, $c921a0d2b34aadb6$export$2e2bcd8739ae039),
|
|
5139
|
+
(0, $4f15cd8a7d970b18$export$2e2bcd8739ae039),
|
|
5140
|
+
(0, $0736cb5a41609896$export$2e2bcd8739ae039)
|
|
5141
|
+
];
|
|
5142
|
+
|
|
5143
|
+
|
|
5144
|
+
|
|
5145
|
+
|
|
5146
|
+
|
|
5147
|
+
const $2fec4872fbf7ebd2$var$Gene = ({ gene: gene })=>{
|
|
5148
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5149
|
+
className: "row",
|
|
5150
|
+
children: gene.id
|
|
5151
|
+
});
|
|
5152
|
+
};
|
|
5153
|
+
const $2fec4872fbf7ebd2$var$Genes = (results, rows, doChangeQuantity)=>{
|
|
5154
|
+
if (results && results.numFound > 0) {
|
|
5155
|
+
const moreButton = results.numFound > rows ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("button", {
|
|
5156
|
+
onClick: (e)=>doChangeQuantity('Genes', 20),
|
|
5157
|
+
children: "more"
|
|
5158
|
+
}) : '';
|
|
5159
|
+
const fewerButton = rows > 20 ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("button", {
|
|
5160
|
+
onClick: (e)=>doChangeQuantity('Genes', -20),
|
|
5161
|
+
children: "fewer"
|
|
5162
|
+
}) : '';
|
|
5163
|
+
const docsToShow = results.response.docs.slice(0, rows);
|
|
5164
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
5165
|
+
id: "Genes",
|
|
5166
|
+
className: "container mb40 anchor",
|
|
5167
|
+
children: [
|
|
5168
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5169
|
+
className: "fancy-title mb40",
|
|
5170
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
|
|
5171
|
+
children: "Genes"
|
|
5172
|
+
})
|
|
5173
|
+
}),
|
|
5174
|
+
docsToShow.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Gene, {
|
|
5175
|
+
gene: doc
|
|
5176
|
+
}, idx)),
|
|
5177
|
+
fewerButton,
|
|
5178
|
+
moreButton
|
|
5179
|
+
]
|
|
5180
|
+
});
|
|
5181
|
+
}
|
|
5182
|
+
};
|
|
5183
|
+
const $2fec4872fbf7ebd2$var$Pathway = ({ pathway: pathway })=>{
|
|
5184
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5185
|
+
className: "row",
|
|
5186
|
+
children: pathway.name
|
|
5187
|
+
});
|
|
5188
|
+
};
|
|
5189
|
+
const $2fec4872fbf7ebd2$var$Pathways = (results)=>{
|
|
5190
|
+
if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
5191
|
+
id: "Pathways",
|
|
5192
|
+
className: "container mb40 anchor",
|
|
5193
|
+
children: [
|
|
5194
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5195
|
+
className: "fancy-title",
|
|
5196
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
|
|
5197
|
+
children: "Pathways"
|
|
5198
|
+
})
|
|
5199
|
+
}),
|
|
5200
|
+
results.pathways.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Pathway, {
|
|
5201
|
+
pathway: doc
|
|
5202
|
+
}, idx))
|
|
5203
|
+
]
|
|
5204
|
+
});
|
|
5205
|
+
};
|
|
5206
|
+
const $2fec4872fbf7ebd2$var$Domain = ({ domain: domain })=>{
|
|
5207
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5208
|
+
className: "row",
|
|
5209
|
+
children: domain.id
|
|
5210
|
+
});
|
|
5211
|
+
};
|
|
5212
|
+
const $2fec4872fbf7ebd2$var$Domains = (results)=>{
|
|
5213
|
+
if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
5214
|
+
id: "Domains",
|
|
5215
|
+
className: "container mb40 anchor",
|
|
5216
|
+
children: [
|
|
5217
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5218
|
+
className: "fancy-title mb40",
|
|
5219
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
|
|
5220
|
+
children: "Domains"
|
|
5221
|
+
})
|
|
5222
|
+
}),
|
|
5223
|
+
results.domains.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Domain, {
|
|
5224
|
+
domain: doc
|
|
5225
|
+
}, idx))
|
|
5226
|
+
]
|
|
5227
|
+
});
|
|
5228
|
+
};
|
|
5229
|
+
const $2fec4872fbf7ebd2$var$Taxon = ({ taxon: taxon })=>{
|
|
5230
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5231
|
+
className: "row",
|
|
5232
|
+
children: taxon.id
|
|
5233
|
+
});
|
|
5234
|
+
};
|
|
5235
|
+
const $2fec4872fbf7ebd2$var$Species = (results)=>{
|
|
5236
|
+
if (results && results.numFound > 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
5237
|
+
id: "Species",
|
|
5238
|
+
className: "container mb40 anchor",
|
|
5239
|
+
children: [
|
|
5240
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5241
|
+
className: "fancy-title mb40",
|
|
5242
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h4", {
|
|
5243
|
+
children: "Species"
|
|
5244
|
+
})
|
|
5245
|
+
}),
|
|
5246
|
+
results.taxonomy.map((doc, idx)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($2fec4872fbf7ebd2$var$Taxon, {
|
|
5247
|
+
taxon: doc
|
|
5248
|
+
}, idx))
|
|
5249
|
+
]
|
|
5250
|
+
});
|
|
5251
|
+
};
|
|
5252
|
+
const $2fec4872fbf7ebd2$var$ResultList = ({ grameneGenes: grameneGenes, grameneDomains: grameneDomains, gramenePathways: gramenePathways, grameneTaxonomy: grameneTaxonomy, searchUI: searchUI, searchUpdated: searchUpdated, doChangeQuantity: doChangeQuantity })=>{
|
|
5253
|
+
if (searchUI.Gramene) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
5254
|
+
id: "gramene",
|
|
5255
|
+
className: "row",
|
|
5256
|
+
children: [
|
|
5257
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
5258
|
+
className: "fancy-title pt50",
|
|
5259
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("h3", {
|
|
5260
|
+
children: "Gramene search results"
|
|
5261
|
+
})
|
|
5262
|
+
}),
|
|
5263
|
+
searchUI.Genes && $2fec4872fbf7ebd2$var$Genes(grameneGenes, searchUI.rows.Genes, doChangeQuantity),
|
|
5264
|
+
searchUI.Domains && $2fec4872fbf7ebd2$var$Domains(grameneDomains),
|
|
5265
|
+
searchUI.Pathways && $2fec4872fbf7ebd2$var$Pathways(gramenePathways),
|
|
5266
|
+
searchUI.Species && $2fec4872fbf7ebd2$var$Species(grameneTaxonomy)
|
|
5267
|
+
]
|
|
5268
|
+
});
|
|
5269
|
+
else return null;
|
|
5270
|
+
};
|
|
5271
|
+
var $2fec4872fbf7ebd2$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneGenes', 'selectGrameneDomains', 'selectGramenePathways', 'selectGrameneTaxonomy', 'selectSearchUI', 'selectSearchUpdated', 'doChangeQuantity', $2fec4872fbf7ebd2$var$ResultList);
|
|
5272
|
+
|
|
5273
|
+
|
|
5274
|
+
|
|
5275
|
+
|
|
5276
|
+
|
|
5277
|
+
const $27617dbc24e7faf0$var$getStatus = (cat, results, isChecked, toggle)=>{
|
|
5278
|
+
const tally = results ? results.numFound : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("img", {
|
|
5279
|
+
src: "/static/images/dna_spinner.svg"
|
|
5280
|
+
});
|
|
5281
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
|
|
5282
|
+
className: "category-leaf",
|
|
5283
|
+
children: [
|
|
5284
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
|
|
5285
|
+
type: "checkbox",
|
|
5286
|
+
checked: isChecked,
|
|
5287
|
+
onChange: (e)=>toggle(cat)
|
|
5288
|
+
}),
|
|
5289
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("a", {
|
|
5290
|
+
"data-scroll": true,
|
|
5291
|
+
href: `#${cat}`,
|
|
5292
|
+
className: "nav-link active",
|
|
5293
|
+
children: [
|
|
5294
|
+
cat,
|
|
5295
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
5296
|
+
style: {
|
|
5297
|
+
float: "right"
|
|
5298
|
+
},
|
|
5299
|
+
children: tally
|
|
5300
|
+
})
|
|
5301
|
+
]
|
|
5302
|
+
})
|
|
5303
|
+
]
|
|
5304
|
+
});
|
|
5305
|
+
};
|
|
5306
|
+
const $27617dbc24e7faf0$var$ResultSummary = ({ grameneGenes: grameneGenes, gramenePathways: gramenePathways, grameneDomains: grameneDomains, grameneTaxonomy: grameneTaxonomy, searchUI: searchUI, searchUpdated: searchUpdated, doToggleCategory: doToggleCategory })=>{
|
|
5307
|
+
const status = grameneGenes ? grameneGenes.numFound : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("img", {
|
|
5308
|
+
src: "/static/images/dna_spinner.svg"
|
|
5309
|
+
});
|
|
4625
5310
|
if (searchUI.Gramene) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
|
|
4626
5311
|
className: "active category-expanded",
|
|
4627
5312
|
children: [
|
|
@@ -7916,6 +8601,36 @@ const $b36244140732570a$var$examples = [
|
|
|
7916
8601
|
]
|
|
7917
8602
|
}
|
|
7918
8603
|
},
|
|
8604
|
+
{
|
|
8605
|
+
subsite: {
|
|
8606
|
+
maize: 1,
|
|
8607
|
+
sorghum: 1,
|
|
8608
|
+
grapevine: 1,
|
|
8609
|
+
rice: 1,
|
|
8610
|
+
main: 1
|
|
8611
|
+
},
|
|
8612
|
+
text: "Which genes are curated in the scientific literature?",
|
|
8613
|
+
filters: {
|
|
8614
|
+
status: 'init',
|
|
8615
|
+
rows: 20,
|
|
8616
|
+
operation: 'AND',
|
|
8617
|
+
negate: false,
|
|
8618
|
+
leftIdx: 0,
|
|
8619
|
+
rightIdx: 3,
|
|
8620
|
+
children: [
|
|
8621
|
+
{
|
|
8622
|
+
fq_field: 'capabilities',
|
|
8623
|
+
fq_value: 'pubs',
|
|
8624
|
+
name: 'publication',
|
|
8625
|
+
category: 'Curated',
|
|
8626
|
+
leftIdx: 1,
|
|
8627
|
+
rightIdx: 2,
|
|
8628
|
+
negate: false,
|
|
8629
|
+
marked: false
|
|
8630
|
+
}
|
|
8631
|
+
]
|
|
8632
|
+
}
|
|
8633
|
+
},
|
|
7919
8634
|
{
|
|
7920
8635
|
subsite: {
|
|
7921
8636
|
grapevine: 1
|
|
@@ -11609,340 +12324,729 @@ function $4ab64e76c1caef59$var$buildColumnDefs(fields, fieldInfo, expanded, togg
|
|
|
11609
12324
|
{
|
|
11610
12325
|
headerName: 'Study'
|
|
11611
12326
|
}
|
|
11612
|
-
];
|
|
11613
|
-
const titleLabels = [
|
|
11614
|
-
{
|
|
11615
|
-
headerName: 'Title'
|
|
12327
|
+
];
|
|
12328
|
+
const titleLabels = [
|
|
12329
|
+
{
|
|
12330
|
+
headerName: 'Title'
|
|
12331
|
+
}
|
|
12332
|
+
];
|
|
12333
|
+
if (expanded.factors) factorTypes.forEach((t, i)=>{
|
|
12334
|
+
studyLabels.push({
|
|
12335
|
+
headerName: 'Factor',
|
|
12336
|
+
headerClass: 'exprviz-hg-factors',
|
|
12337
|
+
...i === 0 ? {
|
|
12338
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
12339
|
+
headerGroupComponentParams: {
|
|
12340
|
+
onToggle: toggles.toggleFactors,
|
|
12341
|
+
expanded: true
|
|
12342
|
+
}
|
|
12343
|
+
} : {}
|
|
12344
|
+
});
|
|
12345
|
+
titleLabels.push({
|
|
12346
|
+
headerName: t,
|
|
12347
|
+
headerClass: 'exprviz-hg-factors'
|
|
12348
|
+
});
|
|
12349
|
+
});
|
|
12350
|
+
else if (factorTypes.length > 0) {
|
|
12351
|
+
studyLabels.push({
|
|
12352
|
+
headerName: 'Factors',
|
|
12353
|
+
headerClass: 'exprviz-hg-factors exprviz-hg-collapsed',
|
|
12354
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
12355
|
+
headerGroupComponentParams: {
|
|
12356
|
+
onToggle: toggles.toggleFactors,
|
|
12357
|
+
expanded: false,
|
|
12358
|
+
suffix: `(${factorTypes.length})`
|
|
12359
|
+
}
|
|
12360
|
+
});
|
|
12361
|
+
titleLabels.push({
|
|
12362
|
+
headerName: '',
|
|
12363
|
+
headerClass: 'exprviz-hg-factors exprviz-hg-collapsed'
|
|
12364
|
+
});
|
|
12365
|
+
}
|
|
12366
|
+
if (expanded.chars) charTypes.forEach((t, i)=>{
|
|
12367
|
+
studyLabels.push({
|
|
12368
|
+
headerName: 'Characteristic',
|
|
12369
|
+
headerClass: 'exprviz-hg-chars',
|
|
12370
|
+
...i === 0 ? {
|
|
12371
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
12372
|
+
headerGroupComponentParams: {
|
|
12373
|
+
onToggle: toggles.toggleChars,
|
|
12374
|
+
expanded: true
|
|
12375
|
+
}
|
|
12376
|
+
} : {}
|
|
12377
|
+
});
|
|
12378
|
+
titleLabels.push({
|
|
12379
|
+
headerName: t,
|
|
12380
|
+
headerClass: 'exprviz-hg-chars'
|
|
12381
|
+
});
|
|
12382
|
+
});
|
|
12383
|
+
else if (charTypes.length > 0) {
|
|
12384
|
+
studyLabels.push({
|
|
12385
|
+
headerName: 'Characteristics',
|
|
12386
|
+
headerClass: 'exprviz-hg-chars exprviz-hg-collapsed',
|
|
12387
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
12388
|
+
headerGroupComponentParams: {
|
|
12389
|
+
onToggle: toggles.toggleChars,
|
|
12390
|
+
expanded: false,
|
|
12391
|
+
suffix: `(${charTypes.length})`
|
|
12392
|
+
}
|
|
12393
|
+
});
|
|
12394
|
+
titleLabels.push({
|
|
12395
|
+
headerName: '',
|
|
12396
|
+
headerClass: 'exprviz-hg-chars exprviz-hg-collapsed'
|
|
12397
|
+
});
|
|
12398
|
+
}
|
|
12399
|
+
const idCol = $4ab64e76c1caef59$var$wrapLeafWithLabels($4ab64e76c1caef59$var$baseColDefs[0], studyLabels);
|
|
12400
|
+
const nameCol = $4ab64e76c1caef59$var$wrapLeafWithLabels($4ab64e76c1caef59$var$baseColDefs[1], titleLabels);
|
|
12401
|
+
return [
|
|
12402
|
+
idCol,
|
|
12403
|
+
nameCol,
|
|
12404
|
+
...exprTopGroups
|
|
12405
|
+
];
|
|
12406
|
+
}
|
|
12407
|
+
const $4ab64e76c1caef59$var$ExprTable = ({ rows: rows, fields: fields, onReorder: onReorder, studies: studies, expressionSamples: expressionSamples, onHoverRow: onHoverRow })=>{
|
|
12408
|
+
const fieldInfo = (0, $gXNCa$react.useMemo)(()=>$4ab64e76c1caef59$export$7b242440eb2c300d(fields, studies, expressionSamples), [
|
|
12409
|
+
fields,
|
|
12410
|
+
studies,
|
|
12411
|
+
expressionSamples
|
|
12412
|
+
]);
|
|
12413
|
+
// Default: factor rows expanded, characteristic rows collapsed (per spec).
|
|
12414
|
+
const [expanded, setExpanded] = (0, $gXNCa$react.useState)({
|
|
12415
|
+
factors: true,
|
|
12416
|
+
chars: false
|
|
12417
|
+
});
|
|
12418
|
+
const toggleFactors = (0, $gXNCa$react.useCallback)(()=>setExpanded((e)=>({
|
|
12419
|
+
...e,
|
|
12420
|
+
factors: !e.factors
|
|
12421
|
+
})), []);
|
|
12422
|
+
const toggleChars = (0, $gXNCa$react.useCallback)(()=>setExpanded((e)=>({
|
|
12423
|
+
...e,
|
|
12424
|
+
chars: !e.chars
|
|
12425
|
+
})), []);
|
|
12426
|
+
const columnDefs = (0, $gXNCa$react.useMemo)(()=>$4ab64e76c1caef59$var$buildColumnDefs(fields, fieldInfo, expanded, {
|
|
12427
|
+
toggleFactors: toggleFactors,
|
|
12428
|
+
toggleChars: toggleChars
|
|
12429
|
+
}), [
|
|
12430
|
+
fields,
|
|
12431
|
+
fieldInfo,
|
|
12432
|
+
expanded,
|
|
12433
|
+
toggleFactors,
|
|
12434
|
+
toggleChars
|
|
12435
|
+
]);
|
|
12436
|
+
const onColumnMoved = (e)=>{
|
|
12437
|
+
if (!onReorder || !e.finished) return;
|
|
12438
|
+
const allCols = e.api.getAllGridColumns ? e.api.getAllGridColumns() : e.columnApi && e.columnApi.getAllGridColumns && e.columnApi.getAllGridColumns();
|
|
12439
|
+
if (!allCols) return;
|
|
12440
|
+
const next = allCols.map((c)=>c.getColId()).filter((id)=>fields.includes(id));
|
|
12441
|
+
if (!$4ab64e76c1caef59$var$arraysEqual(next, fields)) onReorder(next);
|
|
12442
|
+
};
|
|
12443
|
+
if (!rows || rows.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12444
|
+
className: "exprviz-table-empty",
|
|
12445
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
12446
|
+
children: "No data loaded."
|
|
12447
|
+
})
|
|
12448
|
+
});
|
|
12449
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12450
|
+
className: "ag-theme-quartz exprviz-aggrid",
|
|
12451
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$aggridreact.AgGridReact), {
|
|
12452
|
+
rowData: rows,
|
|
12453
|
+
columnDefs: columnDefs,
|
|
12454
|
+
defaultColDef: $4ab64e76c1caef59$var$DEFAULT_COL_DEF,
|
|
12455
|
+
animateRows: false,
|
|
12456
|
+
suppressFieldDotNotation: true,
|
|
12457
|
+
suppressDragLeaveHidesColumns: true,
|
|
12458
|
+
suppressColumnVirtualisation: true,
|
|
12459
|
+
groupHeaderHeight: 24,
|
|
12460
|
+
onColumnMoved: onColumnMoved,
|
|
12461
|
+
onCellMouseOver: (e)=>onHoverRow && onHoverRow(e.data && e.data.id),
|
|
12462
|
+
onCellMouseOut: ()=>onHoverRow && onHoverRow(null)
|
|
12463
|
+
})
|
|
12464
|
+
});
|
|
12465
|
+
};
|
|
12466
|
+
var $4ab64e76c1caef59$export$2e2bcd8739ae039 = $4ab64e76c1caef59$var$ExprTable;
|
|
12467
|
+
|
|
12468
|
+
|
|
12469
|
+
|
|
12470
|
+
|
|
12471
|
+
|
|
12472
|
+
// Parallel-coordinates plot with per-axis brushing and drag-to-reorder axes.
|
|
12473
|
+
// - Axis labels are draggable horizontally; on drop, onReorder(newOrder) fires.
|
|
12474
|
+
// - Brushes on axes intersect (AND): a row is "in" only when every active brush contains it.
|
|
12475
|
+
// - Numeric fields use a linear or symlog scale; non-numeric values are skipped.
|
|
12476
|
+
const $caf32827df861c4e$var$MARGIN = {
|
|
12477
|
+
top: 100,
|
|
12478
|
+
right: 24,
|
|
12479
|
+
bottom: 24,
|
|
12480
|
+
left: 32
|
|
12481
|
+
};
|
|
12482
|
+
const $caf32827df861c4e$var$LABEL_ROTATION = -40;
|
|
12483
|
+
const $caf32827df861c4e$var$BRUSH_WIDTH = 16;
|
|
12484
|
+
function $caf32827df861c4e$var$isNumeric(v) {
|
|
12485
|
+
if (v == null) return false;
|
|
12486
|
+
if (Array.isArray(v)) return false;
|
|
12487
|
+
const n = +v;
|
|
12488
|
+
return Number.isFinite(n);
|
|
12489
|
+
}
|
|
12490
|
+
function $caf32827df861c4e$var$arraysEqual(a, b) {
|
|
12491
|
+
if (a.length !== b.length) return false;
|
|
12492
|
+
for(let i = 0; i < a.length; i++)if (a[i] !== b[i]) return false;
|
|
12493
|
+
return true;
|
|
12494
|
+
}
|
|
12495
|
+
// Powers-of-10 tick values spanning [lo, hi]; includes 0 if the range crosses zero.
|
|
12496
|
+
// Clamped to |v| >= 0.1 to keep low-magnitude tick labels from overlapping near 0.
|
|
12497
|
+
const $caf32827df861c4e$var$MIN_LOG_TICK = 0.1;
|
|
12498
|
+
function $caf32827df861c4e$var$logTickValues([lo, hi]) {
|
|
12499
|
+
const ticks = new Set();
|
|
12500
|
+
if (lo <= 0 && hi >= 0) ticks.add(0);
|
|
12501
|
+
if (hi >= $caf32827df861c4e$var$MIN_LOG_TICK) {
|
|
12502
|
+
const start = Math.max(-1, Math.floor(Math.log10(lo > 0 ? lo : $caf32827df861c4e$var$MIN_LOG_TICK)));
|
|
12503
|
+
const end = Math.ceil(Math.log10(hi));
|
|
12504
|
+
for(let p = start; p <= end; p++){
|
|
12505
|
+
const v = Math.pow(10, p);
|
|
12506
|
+
if (v >= $caf32827df861c4e$var$MIN_LOG_TICK && v <= hi) ticks.add(v);
|
|
12507
|
+
}
|
|
12508
|
+
}
|
|
12509
|
+
if (lo <= -$caf32827df861c4e$var$MIN_LOG_TICK) {
|
|
12510
|
+
const start = Math.max(-1, Math.floor(Math.log10(hi < 0 ? -hi : $caf32827df861c4e$var$MIN_LOG_TICK)));
|
|
12511
|
+
const end = Math.ceil(Math.log10(-lo));
|
|
12512
|
+
for(let p = start; p <= end; p++){
|
|
12513
|
+
const v = -Math.pow(10, p);
|
|
12514
|
+
if (-v >= $caf32827df861c4e$var$MIN_LOG_TICK && v >= lo) ticks.add(v);
|
|
11616
12515
|
}
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11631
|
-
|
|
11632
|
-
|
|
11633
|
-
|
|
12516
|
+
}
|
|
12517
|
+
return Array.from(ticks).sort((a, b)=>a - b);
|
|
12518
|
+
}
|
|
12519
|
+
function $caf32827df861c4e$var$logTickFormat(v) {
|
|
12520
|
+
if (v === 0) return '0';
|
|
12521
|
+
const a = Math.abs(v);
|
|
12522
|
+
if (a >= 0.01 && a < 10000) return $gXNCa$d3.format('~g')(v);
|
|
12523
|
+
return $gXNCa$d3.format('.0e')(v);
|
|
12524
|
+
}
|
|
12525
|
+
const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields, scale: scale = 'linear', onBrushChange: onBrushChange, onReorder: onReorder, clearVersion: clearVersion = 0, hoveredId: hoveredId = null, axisLabels: axisLabels = null })=>{
|
|
12526
|
+
const svgRef = (0, $gXNCa$react.useRef)(null);
|
|
12527
|
+
const containerRef = (0, $gXNCa$react.useRef)(null);
|
|
12528
|
+
// selections in data domain: { [field]: [lo, hi] }
|
|
12529
|
+
const selectionsRef = (0, $gXNCa$react.useRef)({});
|
|
12530
|
+
const lastClearRef = (0, $gXNCa$react.useRef)(0);
|
|
12531
|
+
// Track container size so the d3 render reruns when the user drags the
|
|
12532
|
+
// pane resizer (or when the window is resized). The values themselves
|
|
12533
|
+
// aren't read inside the effect — the effect always reads clientWidth/
|
|
12534
|
+
// clientHeight — but listing them in the deps array is what triggers it.
|
|
12535
|
+
const [size, setSize] = (0, $gXNCa$react.useState)({
|
|
12536
|
+
w: 0,
|
|
12537
|
+
h: 0
|
|
11634
12538
|
});
|
|
11635
|
-
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
12539
|
+
// Custom HTML tooltip for axis labels — gives us bold labels and structured
|
|
12540
|
+
// sections, which the native SVG <title> can't do.
|
|
12541
|
+
const [tooltip, setTooltip] = (0, $gXNCa$react.useState)(null);
|
|
12542
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
12543
|
+
const el = containerRef.current;
|
|
12544
|
+
if (!el || typeof ResizeObserver === 'undefined') return;
|
|
12545
|
+
const ro = new ResizeObserver((entries)=>{
|
|
12546
|
+
for (const entry of entries){
|
|
12547
|
+
const { width: width, height: height } = entry.contentRect;
|
|
12548
|
+
setSize((prev)=>{
|
|
12549
|
+
if (Math.abs(prev.w - width) < 1 && Math.abs(prev.h - height) < 1) return prev;
|
|
12550
|
+
return {
|
|
12551
|
+
w: width,
|
|
12552
|
+
h: height
|
|
12553
|
+
};
|
|
12554
|
+
});
|
|
11644
12555
|
}
|
|
11645
12556
|
});
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
headerGroupComponentParams: {
|
|
11658
|
-
onToggle: toggles.toggleChars,
|
|
11659
|
-
expanded: true
|
|
11660
|
-
}
|
|
11661
|
-
} : {}
|
|
12557
|
+
ro.observe(el);
|
|
12558
|
+
return ()=>ro.disconnect();
|
|
12559
|
+
}, []);
|
|
12560
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
12561
|
+
if (clearVersion !== lastClearRef.current) {
|
|
12562
|
+
selectionsRef.current = {};
|
|
12563
|
+
lastClearRef.current = clearVersion;
|
|
12564
|
+
if (onBrushChange) onBrushChange({});
|
|
12565
|
+
}
|
|
12566
|
+
Object.keys(selectionsRef.current).forEach((f)=>{
|
|
12567
|
+
if (!fields || !fields.includes(f)) delete selectionsRef.current[f];
|
|
11662
12568
|
});
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
12569
|
+
const svg = $gXNCa$d3.select(svgRef.current);
|
|
12570
|
+
svg.selectAll('*').remove();
|
|
12571
|
+
if (!fields || fields.length === 0 || !rows || rows.length === 0) return;
|
|
12572
|
+
const el = containerRef.current;
|
|
12573
|
+
const width = el && el.clientWidth || 600;
|
|
12574
|
+
const height = el && el.clientHeight || 300;
|
|
12575
|
+
const innerW = width - $caf32827df861c4e$var$MARGIN.left - $caf32827df861c4e$var$MARGIN.right;
|
|
12576
|
+
const innerH = height - $caf32827df861c4e$var$MARGIN.top - $caf32827df861c4e$var$MARGIN.bottom;
|
|
12577
|
+
svg.attr('viewBox', `0 0 ${width} ${height}`);
|
|
12578
|
+
const g = svg.append('g').attr('transform', `translate(${$caf32827df861c4e$var$MARGIN.left},${$caf32827df861c4e$var$MARGIN.top})`);
|
|
12579
|
+
// Mutable order during drag — starts as a copy of fields.
|
|
12580
|
+
let order = fields.slice();
|
|
12581
|
+
const x = $gXNCa$d3.scalePoint().range([
|
|
12582
|
+
0,
|
|
12583
|
+
innerW
|
|
12584
|
+
]).padding(0.5).domain(order);
|
|
12585
|
+
const yByField = {};
|
|
12586
|
+
let globalExt = null;
|
|
12587
|
+
if (scale === 'log') {
|
|
12588
|
+
const all = [];
|
|
12589
|
+
fields.forEach((f)=>{
|
|
12590
|
+
rows.forEach((r)=>{
|
|
12591
|
+
const v = r[f];
|
|
12592
|
+
if ($caf32827df861c4e$var$isNumeric(v)) all.push(+v);
|
|
12593
|
+
});
|
|
12594
|
+
});
|
|
12595
|
+
globalExt = all.length ? $gXNCa$d3.extent(all) : [
|
|
12596
|
+
0,
|
|
12597
|
+
1
|
|
12598
|
+
];
|
|
12599
|
+
}
|
|
12600
|
+
fields.forEach((f)=>{
|
|
12601
|
+
if (scale === 'log') yByField[f] = $gXNCa$d3.scaleSymlog().domain(globalExt).range([
|
|
12602
|
+
innerH,
|
|
12603
|
+
0
|
|
12604
|
+
]).nice();
|
|
12605
|
+
else {
|
|
12606
|
+
const vals = rows.map((r)=>r[f]).filter($caf32827df861c4e$var$isNumeric).map(Number);
|
|
12607
|
+
const ext = vals.length ? $gXNCa$d3.extent(vals) : [
|
|
12608
|
+
0,
|
|
12609
|
+
1
|
|
12610
|
+
];
|
|
12611
|
+
yByField[f] = $gXNCa$d3.scaleLinear().domain(ext).range([
|
|
12612
|
+
innerH,
|
|
12613
|
+
0
|
|
12614
|
+
]).nice();
|
|
12615
|
+
}
|
|
11666
12616
|
});
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11670
|
-
|
|
11671
|
-
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
12617
|
+
function pathForRow(row, posOf) {
|
|
12618
|
+
const pts = order.map((f)=>{
|
|
12619
|
+
const v = row[f];
|
|
12620
|
+
if (!$caf32827df861c4e$var$isNumeric(v)) return null;
|
|
12621
|
+
return [
|
|
12622
|
+
posOf(f),
|
|
12623
|
+
yByField[f](Number(v))
|
|
12624
|
+
];
|
|
12625
|
+
});
|
|
12626
|
+
return line(pts);
|
|
12627
|
+
}
|
|
12628
|
+
const line = $gXNCa$d3.line().defined((d)=>d != null && Number.isFinite(d[1])).x((d)=>d[0]).y((d)=>d[1]);
|
|
12629
|
+
const linesG = g.append('g').attr('class', 'exprviz-pc-lines');
|
|
12630
|
+
const paths = linesG.selectAll('path').data(rows).enter().append('path').attr('fill', 'none').attr('stroke', 'steelblue').attr('stroke-width', 1).attr('data-id', (d)=>d && d.id != null ? String(d.id) : null).attr('d', (row)=>pathForRow(row, (f)=>x(f)));
|
|
12631
|
+
function isBrushedIn(row) {
|
|
12632
|
+
for (const f of order){
|
|
12633
|
+
const sel = selectionsRef.current[f];
|
|
12634
|
+
if (!sel) continue;
|
|
12635
|
+
const v = row[f];
|
|
12636
|
+
if (!$caf32827df861c4e$var$isNumeric(v)) return false;
|
|
12637
|
+
const n = Number(v);
|
|
12638
|
+
const [lo, hi] = sel;
|
|
12639
|
+
if (n < lo || n > hi) return false;
|
|
12640
|
+
}
|
|
12641
|
+
return true;
|
|
12642
|
+
}
|
|
12643
|
+
function applyBrushStyles() {
|
|
12644
|
+
const anyActive = Object.keys(selectionsRef.current).length > 0;
|
|
12645
|
+
paths.classed('exprviz-pc-line-in', (d)=>!anyActive || isBrushedIn(d)).classed('exprviz-pc-line-out', (d)=>anyActive && !isBrushedIn(d));
|
|
12646
|
+
}
|
|
12647
|
+
applyBrushStyles();
|
|
12648
|
+
// axis groups, keyed by field name so D3 can match them across reorders
|
|
12649
|
+
const axisG = g.selectAll('.exprviz-pc-axis').data(order, (d)=>d).enter().append('g').attr('class', 'exprviz-pc-axis').attr('transform', (d)=>`translate(${x(d)},0)`);
|
|
12650
|
+
axisG.each(function(f) {
|
|
12651
|
+
const ax = $gXNCa$d3.select(this);
|
|
12652
|
+
const axisGen = $gXNCa$d3.axisLeft(yByField[f]);
|
|
12653
|
+
if (scale === 'log') axisGen.tickValues($caf32827df861c4e$var$logTickValues(yByField[f].domain())).tickFormat($caf32827df861c4e$var$logTickFormat);
|
|
12654
|
+
else axisGen.ticks(5);
|
|
12655
|
+
ax.call(axisGen);
|
|
12656
|
+
// Compact axis label. Hovering the label or its drag-handle rect shows
|
|
12657
|
+
// a custom HTML tooltip (rendered outside the SVG by React) that can
|
|
12658
|
+
// include bold labels and section headings.
|
|
12659
|
+
const labelInfo = axisLabels && axisLabels[f] || {
|
|
12660
|
+
short: f.replace(/__expr$/, ''),
|
|
12661
|
+
structured: {
|
|
12662
|
+
studyTitle: f,
|
|
12663
|
+
group: '',
|
|
12664
|
+
factors: [],
|
|
12665
|
+
characteristics: []
|
|
12666
|
+
}
|
|
12667
|
+
};
|
|
12668
|
+
const showTip = (event)=>setTooltip({
|
|
12669
|
+
x: event.clientX,
|
|
12670
|
+
y: event.clientY,
|
|
12671
|
+
info: labelInfo.structured
|
|
12672
|
+
});
|
|
12673
|
+
const moveTip = (event)=>setTooltip((t)=>t ? {
|
|
12674
|
+
...t,
|
|
12675
|
+
x: event.clientX,
|
|
12676
|
+
y: event.clientY
|
|
12677
|
+
} : null);
|
|
12678
|
+
const hideTip = ()=>setTooltip(null);
|
|
12679
|
+
ax.append('text').attr('class', 'exprviz-pc-axis-label').attr('x', 4).attr('y', -4).attr('text-anchor', 'start').attr('transform', `rotate(${$caf32827df861c4e$var$LABEL_ROTATION}, 0, -4)`).attr('fill', '#333').style('font-size', '10px').style('cursor', 'grab').text(labelInfo.short).on('mouseenter', showTip).on('mousemove', moveTip).on('mouseleave', hideTip);
|
|
12680
|
+
// hit area for grabbing — sits along the rotated label
|
|
12681
|
+
ax.append('rect').attr('class', 'exprviz-pc-axis-handle').attr('x', 0).attr('y', -11).attr('width', 140).attr('height', 14).attr('transform', `rotate(${$caf32827df861c4e$var$LABEL_ROTATION}, 0, -4)`).attr('fill', 'transparent').style('cursor', 'grab').on('mouseenter', showTip).on('mousemove', moveTip).on('mouseleave', hideTip);
|
|
12682
|
+
const brush = $gXNCa$d3.brushY().extent([
|
|
12683
|
+
[
|
|
12684
|
+
-$caf32827df861c4e$var$BRUSH_WIDTH / 2,
|
|
12685
|
+
0
|
|
12686
|
+
],
|
|
12687
|
+
[
|
|
12688
|
+
$caf32827df861c4e$var$BRUSH_WIDTH / 2,
|
|
12689
|
+
innerH
|
|
12690
|
+
]
|
|
12691
|
+
]).on('brush end', (event)=>{
|
|
12692
|
+
const s = event.selection;
|
|
12693
|
+
if (!s) delete selectionsRef.current[f];
|
|
12694
|
+
else {
|
|
12695
|
+
const y = yByField[f];
|
|
12696
|
+
const a = y.invert(s[0]);
|
|
12697
|
+
const b = y.invert(s[1]);
|
|
12698
|
+
selectionsRef.current[f] = [
|
|
12699
|
+
Math.min(a, b),
|
|
12700
|
+
Math.max(a, b)
|
|
12701
|
+
];
|
|
12702
|
+
}
|
|
12703
|
+
applyBrushStyles();
|
|
12704
|
+
// event.sourceEvent is null when brush.move is called programmatically
|
|
12705
|
+
// (e.g. when this effect re-runs and we restore prior selections).
|
|
12706
|
+
// Skipping that case avoids a re-render loop with the parent.
|
|
12707
|
+
if (event.type === 'end' && event.sourceEvent && onBrushChange) onBrushChange({
|
|
12708
|
+
...selectionsRef.current
|
|
12709
|
+
});
|
|
12710
|
+
});
|
|
12711
|
+
const brushG = ax.append('g').attr('class', 'exprviz-pc-brush').call(brush);
|
|
12712
|
+
const prior = selectionsRef.current[f];
|
|
12713
|
+
if (prior) {
|
|
12714
|
+
const y = yByField[f];
|
|
12715
|
+
const py0 = y(prior[1]);
|
|
12716
|
+
const py1 = y(prior[0]);
|
|
12717
|
+
if (Number.isFinite(py0) && Number.isFinite(py1)) brushG.call(brush.move, [
|
|
12718
|
+
py0,
|
|
12719
|
+
py1
|
|
12720
|
+
]);
|
|
11677
12721
|
}
|
|
11678
12722
|
});
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
12723
|
+
// Drag-to-reorder: while dragging, only the dragged axis moves and the
|
|
12724
|
+
// line segments connecting to it are recomputed. Other axes stay put.
|
|
12725
|
+
// The new order is computed once at drag end and emitted via onReorder.
|
|
12726
|
+
const drag = $gXNCa$d3.drag().container(function() {
|
|
12727
|
+
return g.node();
|
|
12728
|
+
}).subject(function(event, d) {
|
|
12729
|
+
return {
|
|
12730
|
+
x: x(d),
|
|
12731
|
+
y: 0
|
|
12732
|
+
};
|
|
12733
|
+
}).on('start', function(event, d) {
|
|
12734
|
+
const axNode = this.parentNode;
|
|
12735
|
+
$gXNCa$d3.select(axNode).raise().classed('exprviz-pc-axis-dragging', true);
|
|
12736
|
+
$gXNCa$d3.select(axNode).select('.exprviz-pc-axis-label').style('cursor', 'grabbing');
|
|
12737
|
+
linesG.classed('exprviz-pc-lines-dragging', true);
|
|
12738
|
+
}).on('drag', function(event, d) {
|
|
12739
|
+
const axNode = this.parentNode;
|
|
12740
|
+
const newX = Math.max(0, Math.min(innerW, event.x));
|
|
12741
|
+
$gXNCa$d3.select(axNode).attr('transform', `translate(${newX},0)`);
|
|
12742
|
+
paths.attr('d', (row)=>pathForRow(row, (f)=>f === d ? newX : x(f)));
|
|
12743
|
+
}).on('end', function(event, d) {
|
|
12744
|
+
const axNode = this.parentNode;
|
|
12745
|
+
const newX = Math.max(0, Math.min(innerW, event.x));
|
|
12746
|
+
$gXNCa$d3.select(axNode).classed('exprviz-pc-axis-dragging', false);
|
|
12747
|
+
$gXNCa$d3.select(axNode).select('.exprviz-pc-axis-label').style('cursor', 'grab');
|
|
12748
|
+
linesG.classed('exprviz-pc-lines-dragging', false);
|
|
12749
|
+
const newOrder = order.slice().sort((a, b)=>{
|
|
12750
|
+
const xa = a === d ? newX : x(a);
|
|
12751
|
+
const xb = b === d ? newX : x(b);
|
|
12752
|
+
return xa - xb;
|
|
12753
|
+
});
|
|
12754
|
+
if (onReorder && !$caf32827df861c4e$var$arraysEqual(newOrder, fields)) {
|
|
12755
|
+
// Snap the dragged axis to its target slot for the brief moment
|
|
12756
|
+
// before the parent re-renders with the new order.
|
|
12757
|
+
x.domain(newOrder);
|
|
12758
|
+
$gXNCa$d3.select(axNode).attr('transform', `translate(${x(d)},0)`);
|
|
12759
|
+
paths.attr('d', (row)=>pathForRow(row, (f)=>x(f)));
|
|
12760
|
+
onReorder(newOrder);
|
|
12761
|
+
} else {
|
|
12762
|
+
// No order change — restore the dragged axis to its original slot.
|
|
12763
|
+
$gXNCa$d3.select(axNode).attr('transform', `translate(${x(d)},0)`);
|
|
12764
|
+
paths.attr('d', (row)=>pathForRow(row, (f)=>x(f)));
|
|
12765
|
+
}
|
|
11682
12766
|
});
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
11686
|
-
return [
|
|
11687
|
-
idCol,
|
|
11688
|
-
nameCol,
|
|
11689
|
-
...exprTopGroups
|
|
11690
|
-
];
|
|
11691
|
-
}
|
|
11692
|
-
const $4ab64e76c1caef59$var$ExprTable = ({ rows: rows, fields: fields, onReorder: onReorder, studies: studies, expressionSamples: expressionSamples, onHoverRow: onHoverRow })=>{
|
|
11693
|
-
const fieldInfo = (0, $gXNCa$react.useMemo)(()=>$4ab64e76c1caef59$export$7b242440eb2c300d(fields, studies, expressionSamples), [
|
|
12767
|
+
axisG.selectAll('.exprviz-pc-axis-label, .exprviz-pc-axis-handle').call(drag);
|
|
12768
|
+
}, [
|
|
12769
|
+
rows,
|
|
11694
12770
|
fields,
|
|
11695
|
-
|
|
11696
|
-
|
|
12771
|
+
scale,
|
|
12772
|
+
onBrushChange,
|
|
12773
|
+
onReorder,
|
|
12774
|
+
clearVersion,
|
|
12775
|
+
axisLabels,
|
|
12776
|
+
size.w,
|
|
12777
|
+
size.h
|
|
11697
12778
|
]);
|
|
11698
|
-
//
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
toggleChars: toggleChars
|
|
11714
|
-
}), [
|
|
12779
|
+
// Highlight the polyline matching the hovered row id without rebuilding the
|
|
12780
|
+
// SVG. Raises the highlighted path so it draws above its neighbors.
|
|
12781
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
12782
|
+
const svg = $gXNCa$d3.select(svgRef.current);
|
|
12783
|
+
if (svg.empty()) return;
|
|
12784
|
+
const paths = svg.selectAll('.exprviz-pc-lines path');
|
|
12785
|
+
paths.classed('exprviz-pc-line-hover', false);
|
|
12786
|
+
if (hoveredId == null) return;
|
|
12787
|
+
const target = paths.filter(function() {
|
|
12788
|
+
return this.getAttribute('data-id') === String(hoveredId);
|
|
12789
|
+
});
|
|
12790
|
+
target.classed('exprviz-pc-line-hover', true).raise();
|
|
12791
|
+
}, [
|
|
12792
|
+
hoveredId,
|
|
12793
|
+
rows,
|
|
11715
12794
|
fields,
|
|
11716
|
-
|
|
11717
|
-
expanded,
|
|
11718
|
-
toggleFactors,
|
|
11719
|
-
toggleChars
|
|
12795
|
+
scale
|
|
11720
12796
|
]);
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
const allCols = e.api.getAllGridColumns ? e.api.getAllGridColumns() : e.columnApi && e.columnApi.getAllGridColumns && e.columnApi.getAllGridColumns();
|
|
11724
|
-
if (!allCols) return;
|
|
11725
|
-
const next = allCols.map((c)=>c.getColId()).filter((id)=>fields.includes(id));
|
|
11726
|
-
if (!$4ab64e76c1caef59$var$arraysEqual(next, fields)) onReorder(next);
|
|
11727
|
-
};
|
|
11728
|
-
if (!rows || rows.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
11729
|
-
className: "exprviz-table-empty",
|
|
12797
|
+
if (!fields || fields.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12798
|
+
className: "exprviz-plot-empty",
|
|
11730
12799
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
11731
|
-
children: "
|
|
12800
|
+
children: "Select fields to plot."
|
|
11732
12801
|
})
|
|
11733
12802
|
});
|
|
11734
|
-
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
|
|
12803
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12804
|
+
ref: containerRef,
|
|
12805
|
+
className: "exprviz-pc-container",
|
|
12806
|
+
children: [
|
|
12807
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("svg", {
|
|
12808
|
+
ref: svgRef,
|
|
12809
|
+
width: "100%",
|
|
12810
|
+
height: "100%",
|
|
12811
|
+
preserveAspectRatio: "none"
|
|
12812
|
+
}),
|
|
12813
|
+
tooltip && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($caf32827df861c4e$var$AxisTooltip, {
|
|
12814
|
+
x: tooltip.x,
|
|
12815
|
+
y: tooltip.y,
|
|
12816
|
+
info: tooltip.info
|
|
12817
|
+
})
|
|
12818
|
+
]
|
|
11749
12819
|
});
|
|
11750
12820
|
};
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
|
|
12821
|
+
// Position-fixed so it can escape the plot pane's clipping. Offset slightly
|
|
12822
|
+
// from the cursor and clamped to the viewport so it never spills off-screen.
|
|
12823
|
+
const $caf32827df861c4e$var$AxisTooltip = ({ x: x, y: y, info: info })=>{
|
|
12824
|
+
const ref = (0, $gXNCa$react.useRef)(null);
|
|
12825
|
+
const [pos, setPos] = (0, $gXNCa$react.useState)({
|
|
12826
|
+
left: x + 12,
|
|
12827
|
+
top: y + 12
|
|
12828
|
+
});
|
|
12829
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
12830
|
+
const el = ref.current;
|
|
12831
|
+
if (!el) return;
|
|
12832
|
+
const w = el.offsetWidth;
|
|
12833
|
+
const h = el.offsetHeight;
|
|
12834
|
+
const vw = window.innerWidth;
|
|
12835
|
+
const vh = window.innerHeight;
|
|
12836
|
+
let left = x + 12;
|
|
12837
|
+
let top = y + 12;
|
|
12838
|
+
if (left + w > vw - 4) left = Math.max(4, x - 12 - w);
|
|
12839
|
+
if (top + h > vh - 4) top = Math.max(4, y - 12 - h);
|
|
12840
|
+
setPos({
|
|
12841
|
+
left: left,
|
|
12842
|
+
top: top
|
|
12843
|
+
});
|
|
12844
|
+
}, [
|
|
12845
|
+
x,
|
|
12846
|
+
y,
|
|
12847
|
+
info
|
|
12848
|
+
]);
|
|
12849
|
+
const { studyTitle: studyTitle, group: group, factors: factors, characteristics: characteristics } = info;
|
|
12850
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12851
|
+
ref: ref,
|
|
12852
|
+
className: "exprviz-pc-tooltip",
|
|
12853
|
+
style: pos,
|
|
12854
|
+
children: [
|
|
12855
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12856
|
+
children: [
|
|
12857
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
12858
|
+
className: "exprviz-pc-tip-key",
|
|
12859
|
+
children: "Study:"
|
|
12860
|
+
}),
|
|
12861
|
+
" ",
|
|
12862
|
+
studyTitle,
|
|
12863
|
+
group ? ` (${group})` : ''
|
|
12864
|
+
]
|
|
12865
|
+
}),
|
|
12866
|
+
factors.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
12867
|
+
children: [
|
|
12868
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12869
|
+
className: "exprviz-pc-tip-section",
|
|
12870
|
+
children: "Factors"
|
|
12871
|
+
}),
|
|
12872
|
+
factors.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12873
|
+
className: "exprviz-pc-tip-row",
|
|
12874
|
+
children: [
|
|
12875
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
|
|
12876
|
+
className: "exprviz-pc-tip-key",
|
|
12877
|
+
children: [
|
|
12878
|
+
p.name,
|
|
12879
|
+
":"
|
|
12880
|
+
]
|
|
12881
|
+
}),
|
|
12882
|
+
" ",
|
|
12883
|
+
p.value
|
|
12884
|
+
]
|
|
12885
|
+
}, `f-${i}`))
|
|
12886
|
+
]
|
|
12887
|
+
}),
|
|
12888
|
+
characteristics.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
12889
|
+
children: [
|
|
12890
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12891
|
+
className: "exprviz-pc-tip-section",
|
|
12892
|
+
children: "Characteristics"
|
|
12893
|
+
}),
|
|
12894
|
+
characteristics.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12895
|
+
className: "exprviz-pc-tip-row",
|
|
12896
|
+
children: [
|
|
12897
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
|
|
12898
|
+
className: "exprviz-pc-tip-key",
|
|
12899
|
+
children: [
|
|
12900
|
+
p.name,
|
|
12901
|
+
":"
|
|
12902
|
+
]
|
|
12903
|
+
}),
|
|
12904
|
+
" ",
|
|
12905
|
+
p.value
|
|
12906
|
+
]
|
|
12907
|
+
}, `c-${i}`))
|
|
12908
|
+
]
|
|
12909
|
+
})
|
|
12910
|
+
]
|
|
12911
|
+
});
|
|
12912
|
+
};
|
|
12913
|
+
var $caf32827df861c4e$export$2e2bcd8739ae039 = $caf32827df861c4e$var$ParallelCoordsPlot;
|
|
11754
12914
|
|
|
11755
12915
|
|
|
11756
12916
|
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
// - Numeric fields use a linear or symlog scale; non-numeric values are skipped.
|
|
11761
|
-
const $caf32827df861c4e$var$MARGIN = {
|
|
11762
|
-
top: 100,
|
|
11763
|
-
right: 24,
|
|
11764
|
-
bottom: 24,
|
|
11765
|
-
left: 32
|
|
11766
|
-
};
|
|
11767
|
-
const $caf32827df861c4e$var$LABEL_ROTATION = -40;
|
|
11768
|
-
const $caf32827df861c4e$var$BRUSH_WIDTH = 16;
|
|
11769
|
-
function $caf32827df861c4e$var$isNumeric(v) {
|
|
11770
|
-
if (v == null) return false;
|
|
11771
|
-
if (Array.isArray(v)) return false;
|
|
11772
|
-
const n = +v;
|
|
11773
|
-
return Number.isFinite(n);
|
|
11774
|
-
}
|
|
11775
|
-
function $caf32827df861c4e$var$arraysEqual(a, b) {
|
|
11776
|
-
if (a.length !== b.length) return false;
|
|
11777
|
-
for(let i = 0; i < a.length; i++)if (a[i] !== b[i]) return false;
|
|
11778
|
-
return true;
|
|
11779
|
-
}
|
|
11780
|
-
// Powers-of-10 tick values spanning [lo, hi]; includes 0 if the range crosses zero.
|
|
11781
|
-
// Clamped to |v| >= 0.1 to keep low-magnitude tick labels from overlapping near 0.
|
|
11782
|
-
const $caf32827df861c4e$var$MIN_LOG_TICK = 0.1;
|
|
11783
|
-
function $caf32827df861c4e$var$logTickValues([lo, hi]) {
|
|
11784
|
-
const ticks = new Set();
|
|
11785
|
-
if (lo <= 0 && hi >= 0) ticks.add(0);
|
|
11786
|
-
if (hi >= $caf32827df861c4e$var$MIN_LOG_TICK) {
|
|
11787
|
-
const start = Math.max(-1, Math.floor(Math.log10(lo > 0 ? lo : $caf32827df861c4e$var$MIN_LOG_TICK)));
|
|
11788
|
-
const end = Math.ceil(Math.log10(hi));
|
|
11789
|
-
for(let p = start; p <= end; p++){
|
|
11790
|
-
const v = Math.pow(10, p);
|
|
11791
|
-
if (v >= $caf32827df861c4e$var$MIN_LOG_TICK && v <= hi) ticks.add(v);
|
|
11792
|
-
}
|
|
11793
|
-
}
|
|
11794
|
-
if (lo <= -$caf32827df861c4e$var$MIN_LOG_TICK) {
|
|
11795
|
-
const start = Math.max(-1, Math.floor(Math.log10(hi < 0 ? -hi : $caf32827df861c4e$var$MIN_LOG_TICK)));
|
|
11796
|
-
const end = Math.ceil(Math.log10(-lo));
|
|
11797
|
-
for(let p = start; p <= end; p++){
|
|
11798
|
-
const v = -Math.pow(10, p);
|
|
11799
|
-
if (-v >= $caf32827df861c4e$var$MIN_LOG_TICK && v >= lo) ticks.add(v);
|
|
11800
|
-
}
|
|
11801
|
-
}
|
|
11802
|
-
return Array.from(ticks).sort((a, b)=>a - b);
|
|
12917
|
+
function $1fd2507769d5bd00$var$speciesTaxonId(tid) {
|
|
12918
|
+
const n = +tid;
|
|
12919
|
+
return n > 1000000 ? Math.floor(n / 1000) : n;
|
|
11803
12920
|
}
|
|
11804
|
-
function $
|
|
11805
|
-
if (
|
|
11806
|
-
const
|
|
11807
|
-
if (
|
|
11808
|
-
|
|
12921
|
+
function $1fd2507769d5bd00$var$genomeName(grameneMaps, tid) {
|
|
12922
|
+
if (!grameneMaps) return tid;
|
|
12923
|
+
const direct = grameneMaps[tid];
|
|
12924
|
+
if (direct && direct.display_name) return direct.display_name;
|
|
12925
|
+
const sp = grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(tid)];
|
|
12926
|
+
if (sp && sp.display_name) return sp.display_name;
|
|
12927
|
+
return tid;
|
|
11809
12928
|
}
|
|
11810
|
-
const $
|
|
11811
|
-
const
|
|
11812
|
-
const
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
});
|
|
11824
|
-
// Custom HTML tooltip for axis labels — gives us bold labels and structured
|
|
11825
|
-
// sections, which the native SVG <title> can't do.
|
|
11826
|
-
const [tooltip, setTooltip] = (0, $gXNCa$react.useState)(null);
|
|
11827
|
-
(0, $gXNCa$react.useEffect)(()=>{
|
|
11828
|
-
const el = containerRef.current;
|
|
11829
|
-
if (!el || typeof ResizeObserver === 'undefined') return;
|
|
11830
|
-
const ro = new ResizeObserver((entries)=>{
|
|
11831
|
-
for (const entry of entries){
|
|
11832
|
-
const { width: width, height: height } = entry.contentRect;
|
|
11833
|
-
setSize((prev)=>{
|
|
11834
|
-
if (Math.abs(prev.w - width) < 1 && Math.abs(prev.h - height) < 1) return prev;
|
|
11835
|
-
return {
|
|
11836
|
-
w: width,
|
|
11837
|
-
h: height
|
|
11838
|
-
};
|
|
11839
|
-
});
|
|
11840
|
-
}
|
|
12929
|
+
const $1fd2507769d5bd00$var$ExprVizViewCmp = (props)=>{
|
|
12930
|
+
const { exprVizPivot: pivot, exprViz: exprViz, exprVizActiveTaxon: activeTaxon, grameneMaps: grameneMaps, expressionStudies: expressionStudies, expressionSamples: expressionSamples, doSetExprVizActiveTaxon: doSetExprVizActiveTaxon, doToggleExprVizFieldsModal: doToggleExprVizFieldsModal, doFetchExprVizData: doFetchExprVizData } = props;
|
|
12931
|
+
const studiesFor = (tid)=>{
|
|
12932
|
+
if (!expressionStudies) return [];
|
|
12933
|
+
return expressionStudies[tid] || expressionStudies[$1fd2507769d5bd00$var$speciesTaxonId(tid)] || [];
|
|
12934
|
+
};
|
|
12935
|
+
const taxa = (0, $gXNCa$react.useMemo)(()=>{
|
|
12936
|
+
const ids = Object.keys(pivot.data || {});
|
|
12937
|
+
if (!grameneMaps) return ids;
|
|
12938
|
+
return ids.sort((a, b)=>{
|
|
12939
|
+
const ma = grameneMaps[a] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(a)];
|
|
12940
|
+
const mb = grameneMaps[b] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(b)];
|
|
12941
|
+
return (ma && ma.left_index || 0) - (mb && mb.left_index || 0);
|
|
11841
12942
|
});
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
12943
|
+
}, [
|
|
12944
|
+
pivot.data,
|
|
12945
|
+
grameneMaps
|
|
12946
|
+
]);
|
|
11845
12947
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
11846
|
-
if (
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
11874
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
}
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
12948
|
+
if (taxa.length === 0) return;
|
|
12949
|
+
if (!activeTaxon || !taxa.includes(String(activeTaxon))) doSetExprVizActiveTaxon(taxa[0]);
|
|
12950
|
+
}, [
|
|
12951
|
+
taxa,
|
|
12952
|
+
activeTaxon,
|
|
12953
|
+
doSetExprVizActiveTaxon
|
|
12954
|
+
]);
|
|
12955
|
+
if (pivot.status === 'loading') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12956
|
+
className: "exprviz-view",
|
|
12957
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
12958
|
+
children: "Loading studies\u2026"
|
|
12959
|
+
})
|
|
12960
|
+
});
|
|
12961
|
+
if (pivot.status === 'error') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12962
|
+
className: "exprviz-view",
|
|
12963
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
|
|
12964
|
+
children: [
|
|
12965
|
+
"Error: ",
|
|
12966
|
+
pivot.error
|
|
12967
|
+
]
|
|
12968
|
+
})
|
|
12969
|
+
});
|
|
12970
|
+
if (taxa.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12971
|
+
className: "exprviz-view",
|
|
12972
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
12973
|
+
children: "No expression studies for current results."
|
|
12974
|
+
})
|
|
12975
|
+
});
|
|
12976
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12977
|
+
className: "exprviz-view",
|
|
12978
|
+
children: [
|
|
12979
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tabs), {
|
|
12980
|
+
activeKey: activeTaxon || taxa[0],
|
|
12981
|
+
onSelect: (k)=>doSetExprVizActiveTaxon(k),
|
|
12982
|
+
className: "exprviz-tabs",
|
|
12983
|
+
children: taxa.map((tid)=>{
|
|
12984
|
+
const studies = studiesFor(tid);
|
|
12985
|
+
const taxName = $1fd2507769d5bd00$var$genomeName(grameneMaps, tid);
|
|
12986
|
+
const geneCount = pivot.data[tid] || 0;
|
|
12987
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
|
|
12988
|
+
eventKey: tid,
|
|
12989
|
+
title: `${taxName} (${studies.length} studies \xb7 ${geneCount} genes)`,
|
|
12990
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($1fd2507769d5bd00$var$TaxonPanel, {
|
|
12991
|
+
taxon: tid,
|
|
12992
|
+
studies: studies,
|
|
12993
|
+
expressionSamples: expressionSamples,
|
|
12994
|
+
tabState: exprViz.byTaxon[tid],
|
|
12995
|
+
onOpenFields: ()=>doToggleExprVizFieldsModal(tid, true),
|
|
12996
|
+
onLoad: ()=>doFetchExprVizData(tid),
|
|
12997
|
+
onReorder: (next)=>props.doReorderExprVizFields(tid, next),
|
|
12998
|
+
onAddRangeQuery: props.doAddGrameneRangeQuery
|
|
12999
|
+
})
|
|
13000
|
+
}, tid);
|
|
13001
|
+
})
|
|
13002
|
+
}),
|
|
13003
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4d0c2e01f58b53b1$export$2e2bcd8739ae039), {})
|
|
13004
|
+
]
|
|
13005
|
+
});
|
|
13006
|
+
};
|
|
13007
|
+
// Compact axis labels for the parallel-coords plot. The raw Solr field name
|
|
13008
|
+
// (e.g. "E_CURD_148_g5__expr") is uninformative; we prefer the assay's
|
|
13009
|
+
// factor labels, falling back to "organism part" then to the group id.
|
|
13010
|
+
// `full` is exposed via an SVG <title> so the user can hover to see study,
|
|
13011
|
+
// group, and every factor/characteristic.
|
|
13012
|
+
const $1fd2507769d5bd00$var$AXIS_LABEL_MAX = 22;
|
|
13013
|
+
function $1fd2507769d5bd00$var$truncateLabel(s, n) {
|
|
13014
|
+
if (!s) return '';
|
|
13015
|
+
return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
13016
|
+
}
|
|
13017
|
+
function $1fd2507769d5bd00$var$compactAssayLabel(assay, group) {
|
|
13018
|
+
if (!assay) return group || '';
|
|
13019
|
+
const factorVals = (assay.factor || []).map((f)=>f && f.label).filter(Boolean);
|
|
13020
|
+
if (factorVals.length) return factorVals.join('; ');
|
|
13021
|
+
const chars = assay.characteristic || [];
|
|
13022
|
+
const organ = chars.find((c)=>c && c.type === 'organism part');
|
|
13023
|
+
if (organ && organ.label) return organ.label;
|
|
13024
|
+
const firstChar = chars.find((c)=>c && c.label);
|
|
13025
|
+
if (firstChar) return firstChar.label;
|
|
13026
|
+
return group || '';
|
|
13027
|
+
}
|
|
13028
|
+
function $1fd2507769d5bd00$var$assayPairs(list) {
|
|
13029
|
+
return (list || []).filter((x)=>x && x.label).map((x)=>({
|
|
13030
|
+
name: x.type || '',
|
|
13031
|
+
value: x.label
|
|
13032
|
+
}));
|
|
13033
|
+
}
|
|
13034
|
+
function $1fd2507769d5bd00$var$buildAxisLabels(fields, studies, expressionSamples) {
|
|
13035
|
+
const labels = {};
|
|
13036
|
+
if (!fields) return labels;
|
|
13037
|
+
const studyById = {};
|
|
13038
|
+
(studies || []).forEach((s)=>{
|
|
13039
|
+
if (s && s._id) studyById[s._id] = s;
|
|
13040
|
+
});
|
|
13041
|
+
const findAssay = (studyId, group)=>{
|
|
13042
|
+
const arr = expressionSamples && expressionSamples[studyId];
|
|
13043
|
+
return arr ? arr.find((a)=>a.group === group) : null;
|
|
13044
|
+
};
|
|
13045
|
+
for (const f of fields){
|
|
13046
|
+
const m = f.match(/^(.+?)_g(\d+)__expr$/);
|
|
13047
|
+
if (!m) {
|
|
13048
|
+
labels[f] = {
|
|
13049
|
+
short: $1fd2507769d5bd00$var$truncateLabel(f.replace(/__expr$/, ''), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
|
|
11946
13050
|
structured: {
|
|
11947
13051
|
studyTitle: f,
|
|
11948
13052
|
group: '',
|
|
@@ -11950,716 +13054,1117 @@ const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields,
|
|
|
11950
13054
|
characteristics: []
|
|
11951
13055
|
}
|
|
11952
13056
|
};
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
const brush = $gXNCa$d3.brushY().extent([
|
|
11968
|
-
[
|
|
11969
|
-
-$caf32827df861c4e$var$BRUSH_WIDTH / 2,
|
|
11970
|
-
0
|
|
11971
|
-
],
|
|
11972
|
-
[
|
|
11973
|
-
$caf32827df861c4e$var$BRUSH_WIDTH / 2,
|
|
11974
|
-
innerH
|
|
11975
|
-
]
|
|
11976
|
-
]).on('brush end', (event)=>{
|
|
11977
|
-
const s = event.selection;
|
|
11978
|
-
if (!s) delete selectionsRef.current[f];
|
|
11979
|
-
else {
|
|
11980
|
-
const y = yByField[f];
|
|
11981
|
-
const a = y.invert(s[0]);
|
|
11982
|
-
const b = y.invert(s[1]);
|
|
11983
|
-
selectionsRef.current[f] = [
|
|
11984
|
-
Math.min(a, b),
|
|
11985
|
-
Math.max(a, b)
|
|
11986
|
-
];
|
|
11987
|
-
}
|
|
11988
|
-
applyBrushStyles();
|
|
11989
|
-
// event.sourceEvent is null when brush.move is called programmatically
|
|
11990
|
-
// (e.g. when this effect re-runs and we restore prior selections).
|
|
11991
|
-
// Skipping that case avoids a re-render loop with the parent.
|
|
11992
|
-
if (event.type === 'end' && event.sourceEvent && onBrushChange) onBrushChange({
|
|
11993
|
-
...selectionsRef.current
|
|
11994
|
-
});
|
|
11995
|
-
});
|
|
11996
|
-
const brushG = ax.append('g').attr('class', 'exprviz-pc-brush').call(brush);
|
|
11997
|
-
const prior = selectionsRef.current[f];
|
|
11998
|
-
if (prior) {
|
|
11999
|
-
const y = yByField[f];
|
|
12000
|
-
const py0 = y(prior[1]);
|
|
12001
|
-
const py1 = y(prior[0]);
|
|
12002
|
-
if (Number.isFinite(py0) && Number.isFinite(py1)) brushG.call(brush.move, [
|
|
12003
|
-
py0,
|
|
12004
|
-
py1
|
|
12005
|
-
]);
|
|
12006
|
-
}
|
|
12007
|
-
});
|
|
12008
|
-
// Drag-to-reorder: while dragging, only the dragged axis moves and the
|
|
12009
|
-
// line segments connecting to it are recomputed. Other axes stay put.
|
|
12010
|
-
// The new order is computed once at drag end and emitted via onReorder.
|
|
12011
|
-
const drag = $gXNCa$d3.drag().container(function() {
|
|
12012
|
-
return g.node();
|
|
12013
|
-
}).subject(function(event, d) {
|
|
12014
|
-
return {
|
|
12015
|
-
x: x(d),
|
|
12016
|
-
y: 0
|
|
12017
|
-
};
|
|
12018
|
-
}).on('start', function(event, d) {
|
|
12019
|
-
const axNode = this.parentNode;
|
|
12020
|
-
$gXNCa$d3.select(axNode).raise().classed('exprviz-pc-axis-dragging', true);
|
|
12021
|
-
$gXNCa$d3.select(axNode).select('.exprviz-pc-axis-label').style('cursor', 'grabbing');
|
|
12022
|
-
linesG.classed('exprviz-pc-lines-dragging', true);
|
|
12023
|
-
}).on('drag', function(event, d) {
|
|
12024
|
-
const axNode = this.parentNode;
|
|
12025
|
-
const newX = Math.max(0, Math.min(innerW, event.x));
|
|
12026
|
-
$gXNCa$d3.select(axNode).attr('transform', `translate(${newX},0)`);
|
|
12027
|
-
paths.attr('d', (row)=>pathForRow(row, (f)=>f === d ? newX : x(f)));
|
|
12028
|
-
}).on('end', function(event, d) {
|
|
12029
|
-
const axNode = this.parentNode;
|
|
12030
|
-
const newX = Math.max(0, Math.min(innerW, event.x));
|
|
12031
|
-
$gXNCa$d3.select(axNode).classed('exprviz-pc-axis-dragging', false);
|
|
12032
|
-
$gXNCa$d3.select(axNode).select('.exprviz-pc-axis-label').style('cursor', 'grab');
|
|
12033
|
-
linesG.classed('exprviz-pc-lines-dragging', false);
|
|
12034
|
-
const newOrder = order.slice().sort((a, b)=>{
|
|
12035
|
-
const xa = a === d ? newX : x(a);
|
|
12036
|
-
const xb = b === d ? newX : x(b);
|
|
12037
|
-
return xa - xb;
|
|
12038
|
-
});
|
|
12039
|
-
if (onReorder && !$caf32827df861c4e$var$arraysEqual(newOrder, fields)) {
|
|
12040
|
-
// Snap the dragged axis to its target slot for the brief moment
|
|
12041
|
-
// before the parent re-renders with the new order.
|
|
12042
|
-
x.domain(newOrder);
|
|
12043
|
-
$gXNCa$d3.select(axNode).attr('transform', `translate(${x(d)},0)`);
|
|
12044
|
-
paths.attr('d', (row)=>pathForRow(row, (f)=>x(f)));
|
|
12045
|
-
onReorder(newOrder);
|
|
12046
|
-
} else {
|
|
12047
|
-
// No order change — restore the dragged axis to its original slot.
|
|
12048
|
-
$gXNCa$d3.select(axNode).attr('transform', `translate(${x(d)},0)`);
|
|
12049
|
-
paths.attr('d', (row)=>pathForRow(row, (f)=>x(f)));
|
|
13057
|
+
continue;
|
|
13058
|
+
}
|
|
13059
|
+
const expId = m[1].replace(/_/g, '-');
|
|
13060
|
+
const group = 'g' + m[2];
|
|
13061
|
+
const assay = findAssay(expId, group);
|
|
13062
|
+
const study = studyById[expId];
|
|
13063
|
+
const studyName = study && study.description || expId;
|
|
13064
|
+
labels[f] = {
|
|
13065
|
+
short: $1fd2507769d5bd00$var$truncateLabel($1fd2507769d5bd00$var$compactAssayLabel(assay, group), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
|
|
13066
|
+
structured: {
|
|
13067
|
+
studyTitle: studyName,
|
|
13068
|
+
group: group,
|
|
13069
|
+
factors: $1fd2507769d5bd00$var$assayPairs(assay && assay.factor),
|
|
13070
|
+
characteristics: $1fd2507769d5bd00$var$assayPairs(assay && assay.characteristic)
|
|
12050
13071
|
}
|
|
12051
|
-
}
|
|
12052
|
-
|
|
13072
|
+
};
|
|
13073
|
+
}
|
|
13074
|
+
return labels;
|
|
13075
|
+
}
|
|
13076
|
+
function $1fd2507769d5bd00$var$rowMatchesSelections(row, selections) {
|
|
13077
|
+
for (const f of Object.keys(selections)){
|
|
13078
|
+
const v = row[f];
|
|
13079
|
+
if (v == null || Array.isArray(v)) return false;
|
|
13080
|
+
const n = +v;
|
|
13081
|
+
if (!Number.isFinite(n)) return false;
|
|
13082
|
+
const [lo, hi] = selections[f];
|
|
13083
|
+
if (n < lo || n > hi) return false;
|
|
13084
|
+
}
|
|
13085
|
+
return true;
|
|
13086
|
+
}
|
|
13087
|
+
function $1fd2507769d5bd00$var$fmt(n) {
|
|
13088
|
+
if (!Number.isFinite(n)) return String(n);
|
|
13089
|
+
const a = Math.abs(n);
|
|
13090
|
+
if (a !== 0 && (a < 0.001 || a >= 1e6)) return n.toExponential(3);
|
|
13091
|
+
return Number(n.toFixed(4)).toString();
|
|
13092
|
+
}
|
|
13093
|
+
function $1fd2507769d5bd00$var$tsvCell(v) {
|
|
13094
|
+
if (v == null) return '';
|
|
13095
|
+
if (Array.isArray(v)) return v.join(',');
|
|
13096
|
+
const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
|
|
13097
|
+
return s.replace(/[\t\r\n]+/g, ' ');
|
|
13098
|
+
}
|
|
13099
|
+
// Mirror the on-screen table header in the TSV: one row per metadata level
|
|
13100
|
+
// the table is showing (Study, then one row per distinct factor type, then
|
|
13101
|
+
// one row per distinct characteristic type), followed by the leaf header
|
|
13102
|
+
// (Gene ID / Name / per-sample group). The first two columns are repurposed
|
|
13103
|
+
// to carry the row category and the row's specific name, matching the
|
|
13104
|
+
// pinned-column labels in ExprTable.
|
|
13105
|
+
function $1fd2507769d5bd00$var$downloadTsv(filename, rows, fields, studies, expressionSamples) {
|
|
13106
|
+
const cols = [
|
|
13107
|
+
'id',
|
|
13108
|
+
'name',
|
|
13109
|
+
...fields
|
|
13110
|
+
];
|
|
13111
|
+
const fieldInfo = (0, $4ab64e76c1caef59$export$7b242440eb2c300d)(fields, studies, expressionSamples);
|
|
13112
|
+
const factorTypes = new Set();
|
|
13113
|
+
const charTypes = new Set();
|
|
13114
|
+
for (const f of fields){
|
|
13115
|
+
const info = fieldInfo[f];
|
|
13116
|
+
if (!info) continue;
|
|
13117
|
+
Object.keys(info.factors || {}).forEach((t)=>factorTypes.add(t));
|
|
13118
|
+
Object.keys(info.characteristics || {}).forEach((t)=>charTypes.add(t));
|
|
13119
|
+
}
|
|
13120
|
+
const factorTypeList = Array.from(factorTypes).sort();
|
|
13121
|
+
const charTypeList = Array.from(charTypes).sort();
|
|
13122
|
+
const metaRow = (cat, label, getValue)=>{
|
|
13123
|
+
const cells = [
|
|
13124
|
+
cat,
|
|
13125
|
+
label
|
|
13126
|
+
];
|
|
13127
|
+
for (const f of fields)cells.push($1fd2507769d5bd00$var$tsvCell(getValue(fieldInfo[f] || {})));
|
|
13128
|
+
return cells.join('\t');
|
|
13129
|
+
};
|
|
13130
|
+
const lines = [];
|
|
13131
|
+
lines.push(metaRow('Study', 'Title', (info)=>info.studyDescription || ''));
|
|
13132
|
+
for (const t of factorTypeList)lines.push(metaRow('Factor', t, (info)=>info.factors && info.factors[t] || ''));
|
|
13133
|
+
for (const t of charTypeList)lines.push(metaRow('Characteristic', t, (info)=>info.characteristics && info.characteristics[t] || ''));
|
|
13134
|
+
// Leaf header — column ids for the data rows.
|
|
13135
|
+
lines.push([
|
|
13136
|
+
'Gene ID',
|
|
13137
|
+
'Name',
|
|
13138
|
+
...fields.map((f)=>{
|
|
13139
|
+
const info = fieldInfo[f];
|
|
13140
|
+
return info && info.group || f.replace(/__expr$/, '');
|
|
13141
|
+
})
|
|
13142
|
+
].join('\t'));
|
|
13143
|
+
for (const r of rows)lines.push(cols.map((c)=>$1fd2507769d5bd00$var$tsvCell(r[c])).join('\t'));
|
|
13144
|
+
const blob = new Blob([
|
|
13145
|
+
lines.join('\n') + '\n'
|
|
13146
|
+
], {
|
|
13147
|
+
type: 'text/tab-separated-values'
|
|
13148
|
+
});
|
|
13149
|
+
const url = URL.createObjectURL(blob);
|
|
13150
|
+
const a = document.createElement('a');
|
|
13151
|
+
a.href = url;
|
|
13152
|
+
a.download = filename;
|
|
13153
|
+
document.body.appendChild(a);
|
|
13154
|
+
a.click();
|
|
13155
|
+
document.body.removeChild(a);
|
|
13156
|
+
URL.revokeObjectURL(url);
|
|
13157
|
+
}
|
|
13158
|
+
const $1fd2507769d5bd00$var$TaxonPanel = ({ taxon: taxon, studies: studies, expressionSamples: expressionSamples, tabState: tabState, onOpenFields: onOpenFields, onLoad: onLoad, onReorder: onReorder, onAddRangeQuery: onAddRangeQuery })=>{
|
|
13159
|
+
const selected = tabState && tabState.selectedFields || [];
|
|
13160
|
+
const rows = tabState && tabState.rows || [];
|
|
13161
|
+
const fetchInfo = tabState && tabState.fetch || {
|
|
13162
|
+
status: 'idle',
|
|
13163
|
+
total: 0
|
|
13164
|
+
};
|
|
13165
|
+
const [scale, setScale] = (0, $gXNCa$react.useState)('linear');
|
|
13166
|
+
const [selections, setSelections] = (0, $gXNCa$react.useState)({});
|
|
13167
|
+
const [clearVersion, setClearVersion] = (0, $gXNCa$react.useState)(0);
|
|
13168
|
+
const [hoveredId, setHoveredId] = (0, $gXNCa$react.useState)(null);
|
|
13169
|
+
const [plotHeight, setPlotHeight] = (0, $gXNCa$react.useState)(320);
|
|
13170
|
+
const resizeStateRef = (0, $gXNCa$react.useRef)(null);
|
|
13171
|
+
// Drag the horizontal separator between the plot and the table to retune
|
|
13172
|
+
// their relative sizes. Bounded so neither pane disappears entirely.
|
|
13173
|
+
const startResize = (e)=>{
|
|
13174
|
+
e.preventDefault();
|
|
13175
|
+
resizeStateRef.current = {
|
|
13176
|
+
startY: e.clientY,
|
|
13177
|
+
startHeight: plotHeight
|
|
13178
|
+
};
|
|
13179
|
+
const onMove = (ev)=>{
|
|
13180
|
+
const s = resizeStateRef.current;
|
|
13181
|
+
if (!s) return;
|
|
13182
|
+
const next = Math.max(120, Math.min(1200, s.startHeight + (ev.clientY - s.startY)));
|
|
13183
|
+
setPlotHeight(next);
|
|
13184
|
+
};
|
|
13185
|
+
const onUp = ()=>{
|
|
13186
|
+
resizeStateRef.current = null;
|
|
13187
|
+
document.removeEventListener('mousemove', onMove);
|
|
13188
|
+
document.removeEventListener('mouseup', onUp);
|
|
13189
|
+
document.body.style.cursor = '';
|
|
13190
|
+
document.body.style.userSelect = '';
|
|
13191
|
+
};
|
|
13192
|
+
document.body.style.cursor = 'row-resize';
|
|
13193
|
+
document.body.style.userSelect = 'none';
|
|
13194
|
+
document.addEventListener('mousemove', onMove);
|
|
13195
|
+
document.addEventListener('mouseup', onUp);
|
|
13196
|
+
};
|
|
13197
|
+
const hasBrush = Object.keys(selections).length > 0;
|
|
13198
|
+
const filteredRows = (0, $gXNCa$react.useMemo)(()=>{
|
|
13199
|
+
if (!hasBrush) return rows;
|
|
13200
|
+
return rows.filter((r)=>$1fd2507769d5bd00$var$rowMatchesSelections(r, selections));
|
|
12053
13201
|
}, [
|
|
12054
13202
|
rows,
|
|
12055
|
-
|
|
12056
|
-
|
|
12057
|
-
onBrushChange,
|
|
12058
|
-
onReorder,
|
|
12059
|
-
clearVersion,
|
|
12060
|
-
axisLabels,
|
|
12061
|
-
size.w,
|
|
12062
|
-
size.h
|
|
13203
|
+
selections,
|
|
13204
|
+
hasBrush
|
|
12063
13205
|
]);
|
|
12064
|
-
//
|
|
12065
|
-
//
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
if (
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
return this.getAttribute('data-id') === String(hoveredId);
|
|
12074
|
-
});
|
|
12075
|
-
target.classed('exprviz-pc-line-hover', true).raise();
|
|
13206
|
+
// Drop fields with no numeric data in the loaded rows so empty axes/columns
|
|
13207
|
+
// don't clutter the visualization. Selected-but-empty fields stay in the
|
|
13208
|
+
// underlying selection so a future load can repopulate them.
|
|
13209
|
+
const visibleFields = (0, $gXNCa$react.useMemo)(()=>{
|
|
13210
|
+
if (rows.length === 0 || selected.length === 0) return selected;
|
|
13211
|
+
return selected.filter((f)=>rows.some((r)=>{
|
|
13212
|
+
const v = r[f];
|
|
13213
|
+
return v != null && !Array.isArray(v) && Number.isFinite(+v);
|
|
13214
|
+
}));
|
|
12076
13215
|
}, [
|
|
12077
|
-
hoveredId,
|
|
12078
13216
|
rows,
|
|
12079
|
-
|
|
12080
|
-
scale
|
|
13217
|
+
selected
|
|
12081
13218
|
]);
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
13219
|
+
const handleReorder = onReorder ? (newVisibleOrder)=>{
|
|
13220
|
+
const visibleSet = new Set(newVisibleOrder);
|
|
13221
|
+
const hidden = selected.filter((f)=>!visibleSet.has(f));
|
|
13222
|
+
onReorder([
|
|
13223
|
+
...newVisibleOrder,
|
|
13224
|
+
...hidden
|
|
13225
|
+
]);
|
|
13226
|
+
} : undefined;
|
|
13227
|
+
const axisLabels = (0, $gXNCa$react.useMemo)(()=>$1fd2507769d5bd00$var$buildAxisLabels(visibleFields, studies, expressionSamples), [
|
|
13228
|
+
visibleFields,
|
|
13229
|
+
studies,
|
|
13230
|
+
expressionSamples
|
|
13231
|
+
]);
|
|
13232
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
13233
|
+
if (rows.length === 0 && hasBrush) {
|
|
13234
|
+
setSelections({});
|
|
13235
|
+
setClearVersion((v)=>v + 1);
|
|
13236
|
+
}
|
|
13237
|
+
}, [
|
|
13238
|
+
rows.length,
|
|
13239
|
+
hasBrush
|
|
13240
|
+
]);
|
|
13241
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
13242
|
+
className: "exprviz-tab-panel",
|
|
13243
|
+
children: [
|
|
13244
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
13245
|
+
className: "exprviz-toolbar",
|
|
13246
|
+
children: [
|
|
13247
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Button), {
|
|
13248
|
+
size: "sm",
|
|
13249
|
+
onClick: onOpenFields,
|
|
13250
|
+
children: [
|
|
13251
|
+
"Select fields (",
|
|
13252
|
+
selected.length,
|
|
13253
|
+
" selected, ",
|
|
13254
|
+
studies.length,
|
|
13255
|
+
" studies)"
|
|
13256
|
+
]
|
|
13257
|
+
}),
|
|
13258
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
13259
|
+
size: "sm",
|
|
13260
|
+
variant: "primary",
|
|
13261
|
+
disabled: selected.length === 0 || fetchInfo.status === 'loading',
|
|
13262
|
+
onClick: onLoad,
|
|
13263
|
+
children: fetchInfo.status === 'loading' ? "Loading\u2026" : 'Load data'
|
|
13264
|
+
}),
|
|
13265
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.ToggleButtonGroup), {
|
|
13266
|
+
type: "radio",
|
|
13267
|
+
name: `exprviz-scale-${taxon}`,
|
|
13268
|
+
size: "sm",
|
|
13269
|
+
value: scale,
|
|
13270
|
+
onChange: setScale,
|
|
13271
|
+
children: [
|
|
13272
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
|
|
13273
|
+
id: `exprviz-scale-${taxon}-lin`,
|
|
13274
|
+
value: "linear",
|
|
13275
|
+
variant: "outline-secondary",
|
|
13276
|
+
children: "Linear"
|
|
13277
|
+
}),
|
|
13278
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
|
|
13279
|
+
id: `exprviz-scale-${taxon}-log`,
|
|
13280
|
+
value: "log",
|
|
13281
|
+
variant: "outline-secondary",
|
|
13282
|
+
children: "Log"
|
|
13283
|
+
})
|
|
13284
|
+
]
|
|
13285
|
+
}),
|
|
13286
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
13287
|
+
size: "sm",
|
|
13288
|
+
variant: "outline-secondary",
|
|
13289
|
+
disabled: !hasBrush,
|
|
13290
|
+
onClick: ()=>{
|
|
13291
|
+
setClearVersion((v)=>v + 1);
|
|
13292
|
+
setSelections({});
|
|
13293
|
+
},
|
|
13294
|
+
children: "Clear brushes"
|
|
13295
|
+
}),
|
|
13296
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
13297
|
+
size: "sm",
|
|
13298
|
+
variant: "outline-secondary",
|
|
13299
|
+
disabled: filteredRows.length === 0 || visibleFields.length === 0,
|
|
13300
|
+
onClick: ()=>$1fd2507769d5bd00$var$downloadTsv(`expression_${taxon}.tsv`, filteredRows, visibleFields, studies, expressionSamples),
|
|
13301
|
+
title: "Download the visible rows and columns as tab-delimited text",
|
|
13302
|
+
children: "Download TSV"
|
|
13303
|
+
}),
|
|
13304
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
13305
|
+
size: "sm",
|
|
13306
|
+
variant: "success",
|
|
13307
|
+
disabled: !hasBrush || !onAddRangeQuery,
|
|
13308
|
+
onClick: ()=>{
|
|
13309
|
+
const terms = Object.keys(selections).map((field)=>{
|
|
13310
|
+
const [lo, hi] = selections[field];
|
|
13311
|
+
return {
|
|
13312
|
+
category: 'Expression',
|
|
13313
|
+
name: `${field}: ${$1fd2507769d5bd00$var$fmt(lo)}\u{2013}${$1fd2507769d5bd00$var$fmt(hi)}`,
|
|
13314
|
+
fq_field: field,
|
|
13315
|
+
fq_value: `[${lo} TO ${hi}]`
|
|
13316
|
+
};
|
|
13317
|
+
});
|
|
13318
|
+
onAddRangeQuery(terms);
|
|
13319
|
+
},
|
|
13320
|
+
title: "Add brush ranges as an AND-conjunction filter on the search",
|
|
13321
|
+
children: "Apply as filter"
|
|
13322
|
+
}),
|
|
13323
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
|
|
13324
|
+
className: "exprviz-status",
|
|
13325
|
+
children: [
|
|
13326
|
+
hasBrush ? `${filteredRows.length} of ${rows.length}` : rows.length,
|
|
13327
|
+
fetchInfo.total ? ` / ${fetchInfo.total}` : '',
|
|
13328
|
+
" genes",
|
|
13329
|
+
hasBrush ? ' (brushed)' : ' loaded'
|
|
13330
|
+
]
|
|
13331
|
+
})
|
|
13332
|
+
]
|
|
13333
|
+
}),
|
|
13334
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
13335
|
+
className: "exprviz-body",
|
|
13336
|
+
children: [
|
|
13337
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13338
|
+
className: "exprviz-plot",
|
|
13339
|
+
style: {
|
|
13340
|
+
height: plotHeight
|
|
13341
|
+
},
|
|
13342
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $caf32827df861c4e$export$2e2bcd8739ae039), {
|
|
13343
|
+
rows: rows,
|
|
13344
|
+
fields: visibleFields,
|
|
13345
|
+
scale: scale,
|
|
13346
|
+
onBrushChange: setSelections,
|
|
13347
|
+
onReorder: handleReorder,
|
|
13348
|
+
clearVersion: clearVersion,
|
|
13349
|
+
hoveredId: hoveredId,
|
|
13350
|
+
axisLabels: axisLabels
|
|
13351
|
+
})
|
|
13352
|
+
}),
|
|
13353
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13354
|
+
className: "exprviz-resizer",
|
|
13355
|
+
role: "separator",
|
|
13356
|
+
"aria-orientation": "horizontal",
|
|
13357
|
+
"aria-label": "Resize plot",
|
|
13358
|
+
onMouseDown: startResize,
|
|
13359
|
+
title: "Drag to resize"
|
|
13360
|
+
}),
|
|
13361
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13362
|
+
className: "exprviz-table",
|
|
13363
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4ab64e76c1caef59$export$2e2bcd8739ae039), {
|
|
13364
|
+
rows: filteredRows,
|
|
13365
|
+
fields: visibleFields,
|
|
13366
|
+
onReorder: handleReorder,
|
|
13367
|
+
studies: studies,
|
|
13368
|
+
expressionSamples: expressionSamples,
|
|
13369
|
+
onHoverRow: setHoveredId
|
|
13370
|
+
})
|
|
13371
|
+
})
|
|
13372
|
+
]
|
|
13373
|
+
})
|
|
13374
|
+
]
|
|
13375
|
+
});
|
|
13376
|
+
};
|
|
13377
|
+
var $1fd2507769d5bd00$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectExprViz', 'selectExprVizPivot', 'selectExprVizActiveTaxon', 'selectGrameneMaps', 'selectExpressionStudies', 'selectExpressionSamples', 'doSetExprVizActiveTaxon', 'doToggleExprVizFieldsModal', 'doFetchExprVizData', 'doReorderExprVizFields', 'doAddGrameneRangeQuery', $1fd2507769d5bd00$var$ExprVizViewCmp);
|
|
13378
|
+
|
|
13379
|
+
|
|
13380
|
+
|
|
13381
|
+
|
|
13382
|
+
|
|
13383
|
+
|
|
13384
|
+
|
|
13385
|
+
|
|
13386
|
+
function $f511198465122338$var$speciesTaxonId(tid) {
|
|
13387
|
+
const n = +tid;
|
|
13388
|
+
return n > 1000000 ? Math.floor(n / 1000) : n;
|
|
13389
|
+
}
|
|
13390
|
+
function $f511198465122338$var$genomeName(grameneMaps, tid) {
|
|
13391
|
+
if (!grameneMaps) return String(tid);
|
|
13392
|
+
const direct = grameneMaps[tid];
|
|
13393
|
+
if (direct && direct.display_name) return direct.display_name;
|
|
13394
|
+
const sp = grameneMaps[$f511198465122338$var$speciesTaxonId(tid)];
|
|
13395
|
+
if (sp && sp.display_name) return sp.display_name;
|
|
13396
|
+
return String(tid);
|
|
13397
|
+
}
|
|
13398
|
+
function $f511198465122338$var$fmtP(p) {
|
|
13399
|
+
if (p == null) return '';
|
|
13400
|
+
if (p === 0) return '0';
|
|
13401
|
+
if (p < 1e-4) return p.toExponential(2);
|
|
13402
|
+
return p.toPrecision(3);
|
|
13403
|
+
}
|
|
13404
|
+
function $f511198465122338$var$fmtFold(f) {
|
|
13405
|
+
if (!isFinite(f)) return '';
|
|
13406
|
+
if (f >= 100) return f.toFixed(0);
|
|
13407
|
+
return f.toFixed(2);
|
|
13408
|
+
}
|
|
13409
|
+
// ---------- species tree helpers ----------
|
|
13410
|
+
function $f511198465122338$var$findRoots(tax) {
|
|
13411
|
+
return Object.values(tax).filter((n)=>!n.is_a || n.is_a.length === 0 || !n.is_a.some((p)=>tax[p]));
|
|
13412
|
+
}
|
|
13413
|
+
// Place each facet tid onto a taxonomy node id (fall back to species level
|
|
13414
|
+
// when the exact subspecies tid isn't in grameneTaxonomy).
|
|
13415
|
+
function $f511198465122338$var$placeTaxa(taxonomy, taxa) {
|
|
13416
|
+
const placement = {};
|
|
13417
|
+
const byPlace = {};
|
|
13418
|
+
for (const { tid: tid, count: count } of taxa){
|
|
13419
|
+
const place = taxonomy[tid] ? +tid : $f511198465122338$var$speciesTaxonId(tid);
|
|
13420
|
+
placement[tid] = place;
|
|
13421
|
+
if (!byPlace[place]) byPlace[place] = [];
|
|
13422
|
+
byPlace[place].push({
|
|
13423
|
+
tid: tid,
|
|
13424
|
+
count: count
|
|
13425
|
+
});
|
|
13426
|
+
}
|
|
13427
|
+
return {
|
|
13428
|
+
placement: placement,
|
|
13429
|
+
byPlace: byPlace
|
|
13430
|
+
};
|
|
13431
|
+
}
|
|
13432
|
+
// Set of taxonomy node ids on the path from any placed leaf back to its root.
|
|
13433
|
+
function $f511198465122338$var$relevantNodeIds(taxonomy, byPlace) {
|
|
13434
|
+
const out = new Set();
|
|
13435
|
+
for (const placeId of Object.keys(byPlace)){
|
|
13436
|
+
let cur = taxonomy[placeId];
|
|
13437
|
+
const seen = new Set();
|
|
13438
|
+
while(cur && !seen.has(+cur._id)){
|
|
13439
|
+
seen.add(+cur._id);
|
|
13440
|
+
out.add(+cur._id);
|
|
13441
|
+
const pid = cur.is_a && cur.is_a[0];
|
|
13442
|
+
cur = pid ? taxonomy[pid] : null;
|
|
13443
|
+
}
|
|
13444
|
+
}
|
|
13445
|
+
return out;
|
|
13446
|
+
}
|
|
13447
|
+
function $f511198465122338$var$relevantChildren(node, taxonomy, relevant) {
|
|
13448
|
+
return (node.children || []).map((cid)=>taxonomy[cid]).filter((c)=>c && relevant.has(+c._id));
|
|
13449
|
+
}
|
|
13450
|
+
// Walk down through single-relevant-child internal nodes to a branch / leaf.
|
|
13451
|
+
function $f511198465122338$var$compressChain(node, taxonomy, relevant, byPlace) {
|
|
13452
|
+
const chain = [
|
|
13453
|
+
node
|
|
13454
|
+
];
|
|
13455
|
+
let cur = node;
|
|
13456
|
+
while(true){
|
|
13457
|
+
if (byPlace[cur._id]) break; // node itself is a placed leaf
|
|
13458
|
+
const kids = $f511198465122338$var$relevantChildren(cur, taxonomy, relevant);
|
|
13459
|
+
if (kids.length !== 1) break;
|
|
13460
|
+
cur = kids[0];
|
|
13461
|
+
chain.push(cur);
|
|
13462
|
+
}
|
|
13463
|
+
return {
|
|
13464
|
+
chain: chain,
|
|
13465
|
+
terminal: cur
|
|
13466
|
+
};
|
|
13467
|
+
}
|
|
13468
|
+
const $f511198465122338$var$CHAIN_DISPLAY_MAX = 3;
|
|
13469
|
+
function $f511198465122338$var$renderChain(chain) {
|
|
13470
|
+
const nameOf = (n)=>n.short_name || n.name || String(n._id);
|
|
13471
|
+
if (chain.length <= $f511198465122338$var$CHAIN_DISPLAY_MAX) return chain.map((n, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, ($parcel$interopDefault($gXNCa$react))).Fragment, {
|
|
13472
|
+
children: [
|
|
13473
|
+
i > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13474
|
+
className: "tax-sep",
|
|
13475
|
+
children: " \u203A "
|
|
13476
|
+
}),
|
|
13477
|
+
nameOf(n)
|
|
13478
|
+
]
|
|
13479
|
+
}, n._id));
|
|
13480
|
+
const first = chain[0];
|
|
13481
|
+
const last = chain[chain.length - 1];
|
|
13482
|
+
const middle = chain.slice(1, -1);
|
|
13483
|
+
const fullTitle = chain.map(nameOf).join(" \u203A ");
|
|
13484
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
13485
|
+
children: [
|
|
13486
|
+
nameOf(first),
|
|
13487
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13488
|
+
className: "tax-sep",
|
|
13489
|
+
children: " \u203A "
|
|
13490
|
+
}),
|
|
13491
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
|
|
13492
|
+
className: "tax-ellipsis",
|
|
13493
|
+
title: fullTitle,
|
|
13494
|
+
children: [
|
|
13495
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13496
|
+
className: "tax-ellipsis-short",
|
|
13497
|
+
children: "\u2026"
|
|
13498
|
+
}),
|
|
13499
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13500
|
+
className: "tax-ellipsis-full",
|
|
13501
|
+
children: middle.map((n)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, ($parcel$interopDefault($gXNCa$react))).Fragment, {
|
|
13502
|
+
children: [
|
|
13503
|
+
nameOf(n),
|
|
13504
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13505
|
+
className: "tax-sep",
|
|
13506
|
+
children: " \u203A "
|
|
13507
|
+
})
|
|
13508
|
+
]
|
|
13509
|
+
}, n._id))
|
|
13510
|
+
})
|
|
13511
|
+
]
|
|
13512
|
+
}),
|
|
13513
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13514
|
+
className: "tax-sep",
|
|
13515
|
+
children: " \u203A "
|
|
13516
|
+
}),
|
|
13517
|
+
nameOf(last)
|
|
13518
|
+
]
|
|
12087
13519
|
});
|
|
13520
|
+
}
|
|
13521
|
+
function $f511198465122338$var$dedupAdjacent(chain) {
|
|
13522
|
+
const out = [];
|
|
13523
|
+
for (const n of chain)if (out.length && out[out.length - 1].name === n.name) out[out.length - 1] = n;
|
|
13524
|
+
else out.push(n);
|
|
13525
|
+
return out;
|
|
13526
|
+
}
|
|
13527
|
+
const $f511198465122338$var$SpeciesTreeNode = ({ node: node, depth: depth, taxonomy: taxonomy, relevant: relevant, byPlace: byPlace, grameneMaps: grameneMaps, expanded: expanded, onToggleExpand: onToggleExpand, activeTaxon: activeTaxon, onSelect: onSelect })=>{
|
|
13528
|
+
const { chain: rawChain, terminal: terminal } = $f511198465122338$var$compressChain(node, taxonomy, relevant, byPlace);
|
|
13529
|
+
const chain = $f511198465122338$var$dedupAdjacent(rawChain);
|
|
13530
|
+
const placed = byPlace[terminal._id] || [];
|
|
13531
|
+
const kids = $f511198465122338$var$relevantChildren(terminal, taxonomy, relevant);
|
|
13532
|
+
const hasKids = kids.length > 0;
|
|
13533
|
+
const isOpen = expanded.has(+terminal._id);
|
|
12088
13534
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12089
|
-
|
|
12090
|
-
className: "exprviz-pc-container",
|
|
13535
|
+
className: "tax-node gsea-tree-node",
|
|
12091
13536
|
children: [
|
|
12092
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
13537
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
13538
|
+
className: "tax-row",
|
|
13539
|
+
style: {
|
|
13540
|
+
paddingLeft: depth * 7
|
|
13541
|
+
},
|
|
13542
|
+
children: [
|
|
13543
|
+
hasKids ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13544
|
+
className: "tax-chevron",
|
|
13545
|
+
onClick: ()=>onToggleExpand(+terminal._id),
|
|
13546
|
+
children: isOpen ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsChevronDown), {}) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsChevronRight), {})
|
|
13547
|
+
}) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13548
|
+
className: "tax-chevron-spacer"
|
|
13549
|
+
}),
|
|
13550
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13551
|
+
className: hasKids ? 'tax-label tax-label-internal' : 'tax-label',
|
|
13552
|
+
children: $f511198465122338$var$renderChain(chain)
|
|
13553
|
+
})
|
|
13554
|
+
]
|
|
12097
13555
|
}),
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
13556
|
+
placed.map(({ tid: tid, count: count })=>{
|
|
13557
|
+
const isActive = String(activeTaxon) === String(tid);
|
|
13558
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
13559
|
+
className: 'gsea-tree-leaf' + (isActive ? ' gsea-tree-leaf-active' : ''),
|
|
13560
|
+
style: {
|
|
13561
|
+
paddingLeft: (depth + 1) * 7 + 18
|
|
13562
|
+
},
|
|
13563
|
+
onClick: ()=>onSelect(tid),
|
|
13564
|
+
children: [
|
|
13565
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
|
|
13566
|
+
type: "radio",
|
|
13567
|
+
readOnly: true,
|
|
13568
|
+
checked: isActive,
|
|
13569
|
+
onChange: ()=>onSelect(tid)
|
|
13570
|
+
}),
|
|
13571
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13572
|
+
className: "gsea-tree-leaf-name",
|
|
13573
|
+
children: $f511198465122338$var$genomeName(grameneMaps, tid)
|
|
13574
|
+
}),
|
|
13575
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13576
|
+
className: "gsea-tree-leaf-count",
|
|
13577
|
+
children: count.toLocaleString()
|
|
13578
|
+
})
|
|
13579
|
+
]
|
|
13580
|
+
}, tid);
|
|
13581
|
+
}),
|
|
13582
|
+
hasKids && isOpen && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13583
|
+
className: "tax-children",
|
|
13584
|
+
children: kids.map((c)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SpeciesTreeNode, {
|
|
13585
|
+
node: c,
|
|
13586
|
+
depth: depth + 1,
|
|
13587
|
+
taxonomy: taxonomy,
|
|
13588
|
+
relevant: relevant,
|
|
13589
|
+
byPlace: byPlace,
|
|
13590
|
+
grameneMaps: grameneMaps,
|
|
13591
|
+
expanded: expanded,
|
|
13592
|
+
onToggleExpand: onToggleExpand,
|
|
13593
|
+
activeTaxon: activeTaxon,
|
|
13594
|
+
onSelect: onSelect
|
|
13595
|
+
}, c._id))
|
|
12102
13596
|
})
|
|
12103
13597
|
]
|
|
12104
13598
|
});
|
|
12105
13599
|
};
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
13600
|
+
const $f511198465122338$var$SpeciesTree = ({ taxonomy: taxonomy, grameneMaps: grameneMaps, taxa: taxa, activeTaxon: activeTaxon, onSelect: onSelect })=>{
|
|
13601
|
+
const { placement: placement, byPlace: byPlace } = (0, $gXNCa$react.useMemo)(()=>$f511198465122338$var$placeTaxa(taxonomy, taxa), [
|
|
13602
|
+
taxonomy,
|
|
13603
|
+
taxa
|
|
13604
|
+
]);
|
|
13605
|
+
const relevant = (0, $gXNCa$react.useMemo)(()=>$f511198465122338$var$relevantNodeIds(taxonomy, byPlace), [
|
|
13606
|
+
taxonomy,
|
|
13607
|
+
byPlace
|
|
13608
|
+
]);
|
|
13609
|
+
const roots = (0, $gXNCa$react.useMemo)(()=>$f511198465122338$var$findRoots(taxonomy).filter((r)=>relevant.has(+r._id)), [
|
|
13610
|
+
taxonomy,
|
|
13611
|
+
relevant
|
|
13612
|
+
]);
|
|
13613
|
+
// Default: every relevant internal node expanded so the user sees the
|
|
13614
|
+
// full pruned tree on first paint.
|
|
13615
|
+
const [expanded, setExpanded] = (0, $gXNCa$react.useState)(()=>new Set(relevant));
|
|
12114
13616
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
12115
|
-
|
|
12116
|
-
if (!el) return;
|
|
12117
|
-
const w = el.offsetWidth;
|
|
12118
|
-
const h = el.offsetHeight;
|
|
12119
|
-
const vw = window.innerWidth;
|
|
12120
|
-
const vh = window.innerHeight;
|
|
12121
|
-
let left = x + 12;
|
|
12122
|
-
let top = y + 12;
|
|
12123
|
-
if (left + w > vw - 4) left = Math.max(4, x - 12 - w);
|
|
12124
|
-
if (top + h > vh - 4) top = Math.max(4, y - 12 - h);
|
|
12125
|
-
setPos({
|
|
12126
|
-
left: left,
|
|
12127
|
-
top: top
|
|
12128
|
-
});
|
|
13617
|
+
setExpanded(new Set(relevant));
|
|
12129
13618
|
}, [
|
|
12130
|
-
|
|
12131
|
-
y,
|
|
12132
|
-
info
|
|
13619
|
+
relevant
|
|
12133
13620
|
]);
|
|
12134
|
-
const
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
13621
|
+
const handleToggle = (id)=>{
|
|
13622
|
+
setExpanded((prev)=>{
|
|
13623
|
+
const next = new Set(prev);
|
|
13624
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
13625
|
+
return next;
|
|
13626
|
+
});
|
|
13627
|
+
};
|
|
13628
|
+
// Skip the root spine: if a root has only one relevant child and isn't
|
|
13629
|
+
// itself a placed leaf, render from its child instead.
|
|
13630
|
+
const topLevel = [];
|
|
13631
|
+
for (const r of roots){
|
|
13632
|
+
const { terminal: terminal } = $f511198465122338$var$compressChain(r, taxonomy, relevant, byPlace);
|
|
13633
|
+
if (byPlace[terminal._id]) topLevel.push(r);
|
|
13634
|
+
else {
|
|
13635
|
+
const kids = $f511198465122338$var$relevantChildren(terminal, taxonomy, relevant);
|
|
13636
|
+
kids.forEach((k)=>topLevel.push(k));
|
|
13637
|
+
}
|
|
13638
|
+
}
|
|
13639
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13640
|
+
className: "gsea-tree",
|
|
13641
|
+
children: topLevel.map((n)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SpeciesTreeNode, {
|
|
13642
|
+
node: n,
|
|
13643
|
+
depth: 0,
|
|
13644
|
+
taxonomy: taxonomy,
|
|
13645
|
+
relevant: relevant,
|
|
13646
|
+
byPlace: byPlace,
|
|
13647
|
+
grameneMaps: grameneMaps,
|
|
13648
|
+
expanded: expanded,
|
|
13649
|
+
onToggleExpand: handleToggle,
|
|
13650
|
+
activeTaxon: activeTaxon,
|
|
13651
|
+
onSelect: onSelect
|
|
13652
|
+
}, n._id))
|
|
13653
|
+
});
|
|
13654
|
+
};
|
|
13655
|
+
// ---------- enrichment panel ----------
|
|
13656
|
+
const $f511198465122338$var$ControlsBar = ({ ui: ui, onChange: onChange })=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
13657
|
+
className: "gsea-controls",
|
|
12139
13658
|
children: [
|
|
12140
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)(
|
|
13659
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Group, {
|
|
13660
|
+
className: "gsea-control",
|
|
12141
13661
|
children: [
|
|
12142
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)(
|
|
12143
|
-
|
|
12144
|
-
children: "Study:"
|
|
13662
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Label, {
|
|
13663
|
+
children: "p_adj \u2264"
|
|
12145
13664
|
}),
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
13665
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
13666
|
+
type: "number",
|
|
13667
|
+
step: "0.01",
|
|
13668
|
+
min: "0",
|
|
13669
|
+
max: "1",
|
|
13670
|
+
value: ui.pAdjCutoff,
|
|
13671
|
+
onChange: (e)=>onChange({
|
|
13672
|
+
pAdjCutoff: +e.target.value
|
|
13673
|
+
})
|
|
13674
|
+
})
|
|
12149
13675
|
]
|
|
12150
13676
|
}),
|
|
12151
|
-
|
|
13677
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Group, {
|
|
13678
|
+
className: "gsea-control",
|
|
12152
13679
|
children: [
|
|
12153
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)(
|
|
12154
|
-
|
|
12155
|
-
children: "Factors"
|
|
13680
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Label, {
|
|
13681
|
+
children: "min k"
|
|
12156
13682
|
}),
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
}),
|
|
12167
|
-
" ",
|
|
12168
|
-
p.value
|
|
12169
|
-
]
|
|
12170
|
-
}, `f-${i}`))
|
|
13683
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
13684
|
+
type: "number",
|
|
13685
|
+
step: "1",
|
|
13686
|
+
min: "1",
|
|
13687
|
+
value: ui.minK,
|
|
13688
|
+
onChange: (e)=>onChange({
|
|
13689
|
+
minK: Math.max(1, +e.target.value)
|
|
13690
|
+
})
|
|
13691
|
+
})
|
|
12171
13692
|
]
|
|
12172
13693
|
}),
|
|
12173
|
-
|
|
13694
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Check, {
|
|
13695
|
+
className: "gsea-control",
|
|
13696
|
+
type: "switch",
|
|
13697
|
+
id: "gsea-most-specific",
|
|
13698
|
+
label: "Most-specific only",
|
|
13699
|
+
checked: !!ui.mostSpecific,
|
|
13700
|
+
onChange: (e)=>onChange({
|
|
13701
|
+
mostSpecific: e.target.checked
|
|
13702
|
+
})
|
|
13703
|
+
}),
|
|
13704
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Group, {
|
|
13705
|
+
className: "gsea-control gsea-control-grow",
|
|
12174
13706
|
children: [
|
|
12175
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)(
|
|
12176
|
-
|
|
12177
|
-
children: "Characteristics"
|
|
13707
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Label, {
|
|
13708
|
+
children: "Filter terms"
|
|
12178
13709
|
}),
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
13710
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
13711
|
+
type: "text",
|
|
13712
|
+
placeholder: "term id or name\u2026",
|
|
13713
|
+
value: ui.search || '',
|
|
13714
|
+
onChange: (e)=>onChange({
|
|
13715
|
+
search: e.target.value
|
|
13716
|
+
})
|
|
13717
|
+
})
|
|
13718
|
+
]
|
|
13719
|
+
})
|
|
13720
|
+
]
|
|
13721
|
+
});
|
|
13722
|
+
const $f511198465122338$var$TermRow = ({ row: row, showType: showType, onAddFilter: onAddFilter })=>{
|
|
13723
|
+
const handleClick = ()=>onAddFilter && onAddFilter(row);
|
|
13724
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("tr", {
|
|
13725
|
+
onClick: handleClick,
|
|
13726
|
+
title: "Click to add as a filter",
|
|
13727
|
+
children: [
|
|
13728
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
13729
|
+
className: "gsea-term-id",
|
|
13730
|
+
children: row.term_display_id
|
|
13731
|
+
}),
|
|
13732
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
13733
|
+
className: "gsea-term-name",
|
|
13734
|
+
children: row.term_name || /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
13735
|
+
children: "(loading\u2026)"
|
|
13736
|
+
})
|
|
13737
|
+
}),
|
|
13738
|
+
showType && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
13739
|
+
className: "gsea-term-type",
|
|
13740
|
+
children: row.term_namespace || ''
|
|
13741
|
+
}),
|
|
13742
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("td", {
|
|
13743
|
+
className: "gsea-num",
|
|
13744
|
+
children: [
|
|
13745
|
+
row.k,
|
|
13746
|
+
" / ",
|
|
13747
|
+
row.n
|
|
13748
|
+
]
|
|
13749
|
+
}),
|
|
13750
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("td", {
|
|
13751
|
+
className: "gsea-num",
|
|
13752
|
+
children: [
|
|
13753
|
+
row.K,
|
|
13754
|
+
" / ",
|
|
13755
|
+
row.N
|
|
13756
|
+
]
|
|
13757
|
+
}),
|
|
13758
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
13759
|
+
className: "gsea-num",
|
|
13760
|
+
children: $f511198465122338$var$fmtFold(row.fold)
|
|
13761
|
+
}),
|
|
13762
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
13763
|
+
className: "gsea-num",
|
|
13764
|
+
children: $f511198465122338$var$fmtP(row.p)
|
|
13765
|
+
}),
|
|
13766
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
13767
|
+
className: "gsea-num",
|
|
13768
|
+
children: $f511198465122338$var$fmtP(row.pAdj)
|
|
13769
|
+
})
|
|
13770
|
+
]
|
|
13771
|
+
});
|
|
13772
|
+
};
|
|
13773
|
+
// InterPro entry types and pathway types are useful per-row context
|
|
13774
|
+
// (e.g. Domain vs Family vs Repeat for InterPro), and aren't reflected in
|
|
13775
|
+
// the section title the way GO namespaces are. Show a Type column for
|
|
13776
|
+
// those two ontologies only.
|
|
13777
|
+
const $f511198465122338$var$ONTS_WITH_TYPE_COLUMN = new Set([
|
|
13778
|
+
'domains',
|
|
13779
|
+
'pathways'
|
|
13780
|
+
]);
|
|
13781
|
+
const $f511198465122338$var$SORT_ACCESSORS = {
|
|
13782
|
+
term: (r)=>r.term_display_id || '',
|
|
13783
|
+
name: (r)=>(r.term_name || '').toLowerCase(),
|
|
13784
|
+
type: (r)=>(r.term_namespace || '').toLowerCase(),
|
|
13785
|
+
k: (r)=>r.k,
|
|
13786
|
+
K: (r)=>r.K,
|
|
13787
|
+
fold: (r)=>r.fold,
|
|
13788
|
+
p: (r)=>r.p,
|
|
13789
|
+
pAdj: (r)=>r.pAdj
|
|
13790
|
+
};
|
|
13791
|
+
// Defaults chosen so a single click does what the user usually wants:
|
|
13792
|
+
// numeric "more interesting" columns (k, K, fold) descend; p-values and
|
|
13793
|
+
// text columns ascend.
|
|
13794
|
+
const $f511198465122338$var$SORT_DEFAULT_DIR = {
|
|
13795
|
+
term: 'asc',
|
|
13796
|
+
name: 'asc',
|
|
13797
|
+
type: 'asc',
|
|
13798
|
+
k: 'desc',
|
|
13799
|
+
K: 'desc',
|
|
13800
|
+
fold: 'desc',
|
|
13801
|
+
p: 'asc',
|
|
13802
|
+
pAdj: 'asc'
|
|
13803
|
+
};
|
|
13804
|
+
const $f511198465122338$var$SortableTh = ({ label: label, sortKey: sortKey, activeKey: activeKey, activeDir: activeDir, onSort: onSort, numeric: numeric })=>{
|
|
13805
|
+
const active = activeKey === sortKey;
|
|
13806
|
+
const arrow = active ? activeDir === 'asc' ? " \u25B2" : " \u25BC" : '';
|
|
13807
|
+
const cls = 'gsea-sort-th' + (active ? ' gsea-sort-th-active' : '') + (numeric ? ' gsea-num' : '');
|
|
13808
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("th", {
|
|
13809
|
+
className: cls,
|
|
13810
|
+
onClick: ()=>onSort(sortKey),
|
|
13811
|
+
children: [
|
|
13812
|
+
label,
|
|
13813
|
+
arrow
|
|
13814
|
+
]
|
|
13815
|
+
});
|
|
13816
|
+
};
|
|
13817
|
+
const $f511198465122338$var$OntologySection = ({ block: block, search: search, onAddFilter: onAddFilter })=>{
|
|
13818
|
+
const filtered = (0, $gXNCa$react.useMemo)(()=>{
|
|
13819
|
+
if (!search) return block.rows;
|
|
13820
|
+
const needle = search.toLowerCase();
|
|
13821
|
+
return block.rows.filter((r)=>r.term_display_id && r.term_display_id.toLowerCase().includes(needle) || r.term_name && r.term_name.toLowerCase().includes(needle));
|
|
13822
|
+
}, [
|
|
13823
|
+
block.rows,
|
|
13824
|
+
search
|
|
13825
|
+
]);
|
|
13826
|
+
const showType = $f511198465122338$var$ONTS_WITH_TYPE_COLUMN.has(block.ontology);
|
|
13827
|
+
const [sortKey, setSortKey] = (0, $gXNCa$react.useState)('pAdj');
|
|
13828
|
+
const [sortDir, setSortDir] = (0, $gXNCa$react.useState)('asc');
|
|
13829
|
+
const handleSort = (key)=>{
|
|
13830
|
+
if (key === sortKey) setSortDir((d)=>d === 'asc' ? 'desc' : 'asc');
|
|
13831
|
+
else {
|
|
13832
|
+
setSortKey(key);
|
|
13833
|
+
setSortDir($f511198465122338$var$SORT_DEFAULT_DIR[key] || 'asc');
|
|
13834
|
+
}
|
|
13835
|
+
};
|
|
13836
|
+
const sorted = (0, $gXNCa$react.useMemo)(()=>{
|
|
13837
|
+
const accessor = $f511198465122338$var$SORT_ACCESSORS[sortKey];
|
|
13838
|
+
if (!accessor) return filtered;
|
|
13839
|
+
const sign = sortDir === 'desc' ? -1 : 1;
|
|
13840
|
+
const arr = filtered.slice();
|
|
13841
|
+
arr.sort((a, b)=>{
|
|
13842
|
+
const va = accessor(a);
|
|
13843
|
+
const vb = accessor(b);
|
|
13844
|
+
if (typeof va === 'number' && typeof vb === 'number') {
|
|
13845
|
+
if (va === vb) return 0;
|
|
13846
|
+
return (va - vb) * sign;
|
|
13847
|
+
}
|
|
13848
|
+
return String(va).localeCompare(String(vb)) * sign;
|
|
13849
|
+
});
|
|
13850
|
+
return arr;
|
|
13851
|
+
}, [
|
|
13852
|
+
filtered,
|
|
13853
|
+
sortKey,
|
|
13854
|
+
sortDir
|
|
13855
|
+
]);
|
|
13856
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Accordion).Item, {
|
|
13857
|
+
eventKey: block.ontology,
|
|
13858
|
+
children: [
|
|
13859
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Accordion).Header, {
|
|
13860
|
+
children: [
|
|
13861
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
13862
|
+
className: "gsea-ont-title",
|
|
13863
|
+
children: block.label
|
|
13864
|
+
}),
|
|
13865
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Badge), {
|
|
13866
|
+
bg: "secondary",
|
|
13867
|
+
className: "gsea-ont-badge",
|
|
13868
|
+
children: [
|
|
13869
|
+
block.passing,
|
|
13870
|
+
" significant / ",
|
|
13871
|
+
block.tested,
|
|
13872
|
+
" tested"
|
|
13873
|
+
]
|
|
13874
|
+
})
|
|
12193
13875
|
]
|
|
13876
|
+
}),
|
|
13877
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Accordion).Body, {
|
|
13878
|
+
children: sorted.length === 0 ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
13879
|
+
children: "No terms pass the current cutoffs."
|
|
13880
|
+
}) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("table", {
|
|
13881
|
+
className: "gsea-table",
|
|
13882
|
+
children: [
|
|
13883
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("thead", {
|
|
13884
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("tr", {
|
|
13885
|
+
children: [
|
|
13886
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13887
|
+
label: "Term",
|
|
13888
|
+
sortKey: "term",
|
|
13889
|
+
activeKey: sortKey,
|
|
13890
|
+
activeDir: sortDir,
|
|
13891
|
+
onSort: handleSort
|
|
13892
|
+
}),
|
|
13893
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13894
|
+
label: "Name",
|
|
13895
|
+
sortKey: "name",
|
|
13896
|
+
activeKey: sortKey,
|
|
13897
|
+
activeDir: sortDir,
|
|
13898
|
+
onSort: handleSort
|
|
13899
|
+
}),
|
|
13900
|
+
showType && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13901
|
+
label: "Type",
|
|
13902
|
+
sortKey: "type",
|
|
13903
|
+
activeKey: sortKey,
|
|
13904
|
+
activeDir: sortDir,
|
|
13905
|
+
onSort: handleSort
|
|
13906
|
+
}),
|
|
13907
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13908
|
+
label: "k / n",
|
|
13909
|
+
sortKey: "k",
|
|
13910
|
+
activeKey: sortKey,
|
|
13911
|
+
activeDir: sortDir,
|
|
13912
|
+
onSort: handleSort,
|
|
13913
|
+
numeric: true
|
|
13914
|
+
}),
|
|
13915
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13916
|
+
label: "K / N",
|
|
13917
|
+
sortKey: "K",
|
|
13918
|
+
activeKey: sortKey,
|
|
13919
|
+
activeDir: sortDir,
|
|
13920
|
+
onSort: handleSort,
|
|
13921
|
+
numeric: true
|
|
13922
|
+
}),
|
|
13923
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13924
|
+
label: "Fold",
|
|
13925
|
+
sortKey: "fold",
|
|
13926
|
+
activeKey: sortKey,
|
|
13927
|
+
activeDir: sortDir,
|
|
13928
|
+
onSort: handleSort,
|
|
13929
|
+
numeric: true
|
|
13930
|
+
}),
|
|
13931
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13932
|
+
label: "p",
|
|
13933
|
+
sortKey: "p",
|
|
13934
|
+
activeKey: sortKey,
|
|
13935
|
+
activeDir: sortDir,
|
|
13936
|
+
onSort: handleSort,
|
|
13937
|
+
numeric: true
|
|
13938
|
+
}),
|
|
13939
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SortableTh, {
|
|
13940
|
+
label: "p_adj",
|
|
13941
|
+
sortKey: "pAdj",
|
|
13942
|
+
activeKey: sortKey,
|
|
13943
|
+
activeDir: sortDir,
|
|
13944
|
+
onSort: handleSort,
|
|
13945
|
+
numeric: true
|
|
13946
|
+
})
|
|
13947
|
+
]
|
|
13948
|
+
})
|
|
13949
|
+
}),
|
|
13950
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("tbody", {
|
|
13951
|
+
children: sorted.map((r)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$TermRow, {
|
|
13952
|
+
row: r,
|
|
13953
|
+
showType: showType,
|
|
13954
|
+
onAddFilter: onAddFilter
|
|
13955
|
+
}, `${r.ontology}:${r.term_id}`))
|
|
13956
|
+
})
|
|
13957
|
+
]
|
|
13958
|
+
})
|
|
12194
13959
|
})
|
|
12195
13960
|
]
|
|
12196
13961
|
});
|
|
12197
13962
|
};
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
function $1fd2507769d5bd00$var$speciesTaxonId(tid) {
|
|
12203
|
-
const n = +tid;
|
|
12204
|
-
return n > 1000000 ? Math.floor(n / 1000) : n;
|
|
12205
|
-
}
|
|
12206
|
-
function $1fd2507769d5bd00$var$genomeName(grameneMaps, tid) {
|
|
12207
|
-
if (!grameneMaps) return tid;
|
|
12208
|
-
const direct = grameneMaps[tid];
|
|
12209
|
-
if (direct && direct.display_name) return direct.display_name;
|
|
12210
|
-
const sp = grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(tid)];
|
|
12211
|
-
if (sp && sp.display_name) return sp.display_name;
|
|
12212
|
-
return tid;
|
|
12213
|
-
}
|
|
12214
|
-
const $1fd2507769d5bd00$var$ExprVizViewCmp = (props)=>{
|
|
12215
|
-
const { exprVizPivot: pivot, exprViz: exprViz, exprVizActiveTaxon: activeTaxon, grameneMaps: grameneMaps, expressionStudies: expressionStudies, expressionSamples: expressionSamples, doSetExprVizActiveTaxon: doSetExprVizActiveTaxon, doToggleExprVizFieldsModal: doToggleExprVizFieldsModal, doFetchExprVizData: doFetchExprVizData } = props;
|
|
12216
|
-
const studiesFor = (tid)=>{
|
|
12217
|
-
if (!expressionStudies) return [];
|
|
12218
|
-
return expressionStudies[tid] || expressionStudies[$1fd2507769d5bd00$var$speciesTaxonId(tid)] || [];
|
|
12219
|
-
};
|
|
12220
|
-
const taxa = (0, $gXNCa$react.useMemo)(()=>{
|
|
12221
|
-
const ids = Object.keys(pivot.data || {});
|
|
12222
|
-
if (!grameneMaps) return ids;
|
|
12223
|
-
return ids.sort((a, b)=>{
|
|
12224
|
-
const ma = grameneMaps[a] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(a)];
|
|
12225
|
-
const mb = grameneMaps[b] || grameneMaps[$1fd2507769d5bd00$var$speciesTaxonId(b)];
|
|
12226
|
-
return (ma && ma.left_index || 0) - (mb && mb.left_index || 0);
|
|
12227
|
-
});
|
|
12228
|
-
}, [
|
|
12229
|
-
pivot.data,
|
|
12230
|
-
grameneMaps
|
|
12231
|
-
]);
|
|
12232
|
-
(0, $gXNCa$react.useEffect)(()=>{
|
|
12233
|
-
if (taxa.length === 0) return;
|
|
12234
|
-
if (!activeTaxon || !taxa.includes(String(activeTaxon))) doSetExprVizActiveTaxon(taxa[0]);
|
|
12235
|
-
}, [
|
|
12236
|
-
taxa,
|
|
12237
|
-
activeTaxon,
|
|
12238
|
-
doSetExprVizActiveTaxon
|
|
12239
|
-
]);
|
|
12240
|
-
if (pivot.status === 'loading') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12241
|
-
className: "exprviz-view",
|
|
13963
|
+
const $f511198465122338$var$TaxonPanel = ({ taxon: taxon, gsea: gsea, results: results, ui: ui, onUiChange: onUiChange, onAddFilter: onAddFilter })=>{
|
|
13964
|
+
if (!taxon) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13965
|
+
className: "gsea-panel",
|
|
12242
13966
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
12243
|
-
children: "
|
|
13967
|
+
children: "Select a species on the left."
|
|
12244
13968
|
})
|
|
12245
13969
|
});
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
pivot.error
|
|
12252
|
-
]
|
|
13970
|
+
const t = gsea.byTaxon[taxon];
|
|
13971
|
+
if (!t) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13972
|
+
className: "gsea-panel",
|
|
13973
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
13974
|
+
children: "Initializing\u2026"
|
|
12253
13975
|
})
|
|
12254
13976
|
});
|
|
12255
|
-
if (
|
|
12256
|
-
className: "
|
|
13977
|
+
if (t.fg.status === 'loading' || t.bg.status === 'loading' || t.fg.status === 'idle' || t.bg.status === 'idle') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13978
|
+
className: "gsea-panel gsea-loading",
|
|
12257
13979
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
12258
|
-
children: "
|
|
13980
|
+
children: "Loading enrichment\u2026"
|
|
12259
13981
|
})
|
|
12260
13982
|
});
|
|
12261
|
-
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
12262
|
-
className: "
|
|
12263
|
-
children:
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
const studies = studiesFor(tid);
|
|
12270
|
-
const taxName = $1fd2507769d5bd00$var$genomeName(grameneMaps, tid);
|
|
12271
|
-
const geneCount = pivot.data[tid] || 0;
|
|
12272
|
-
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
|
|
12273
|
-
eventKey: tid,
|
|
12274
|
-
title: `${taxName} (${studies.length} studies \xb7 ${geneCount} genes)`,
|
|
12275
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($1fd2507769d5bd00$var$TaxonPanel, {
|
|
12276
|
-
taxon: tid,
|
|
12277
|
-
studies: studies,
|
|
12278
|
-
expressionSamples: expressionSamples,
|
|
12279
|
-
tabState: exprViz.byTaxon[tid],
|
|
12280
|
-
onOpenFields: ()=>doToggleExprVizFieldsModal(tid, true),
|
|
12281
|
-
onLoad: ()=>doFetchExprVizData(tid),
|
|
12282
|
-
onReorder: (next)=>props.doReorderExprVizFields(tid, next),
|
|
12283
|
-
onAddRangeQuery: props.doAddGrameneRangeQuery
|
|
12284
|
-
})
|
|
12285
|
-
}, tid);
|
|
12286
|
-
})
|
|
12287
|
-
}),
|
|
12288
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4d0c2e01f58b53b1$export$2e2bcd8739ae039), {})
|
|
12289
|
-
]
|
|
12290
|
-
});
|
|
12291
|
-
};
|
|
12292
|
-
// Compact axis labels for the parallel-coords plot. The raw Solr field name
|
|
12293
|
-
// (e.g. "E_CURD_148_g5__expr") is uninformative; we prefer the assay's
|
|
12294
|
-
// factor labels, falling back to "organism part" then to the group id.
|
|
12295
|
-
// `full` is exposed via an SVG <title> so the user can hover to see study,
|
|
12296
|
-
// group, and every factor/characteristic.
|
|
12297
|
-
const $1fd2507769d5bd00$var$AXIS_LABEL_MAX = 22;
|
|
12298
|
-
function $1fd2507769d5bd00$var$truncateLabel(s, n) {
|
|
12299
|
-
if (!s) return '';
|
|
12300
|
-
return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
12301
|
-
}
|
|
12302
|
-
function $1fd2507769d5bd00$var$compactAssayLabel(assay, group) {
|
|
12303
|
-
if (!assay) return group || '';
|
|
12304
|
-
const factorVals = (assay.factor || []).map((f)=>f && f.label).filter(Boolean);
|
|
12305
|
-
if (factorVals.length) return factorVals.join('; ');
|
|
12306
|
-
const chars = assay.characteristic || [];
|
|
12307
|
-
const organ = chars.find((c)=>c && c.type === 'organism part');
|
|
12308
|
-
if (organ && organ.label) return organ.label;
|
|
12309
|
-
const firstChar = chars.find((c)=>c && c.label);
|
|
12310
|
-
if (firstChar) return firstChar.label;
|
|
12311
|
-
return group || '';
|
|
12312
|
-
}
|
|
12313
|
-
function $1fd2507769d5bd00$var$assayPairs(list) {
|
|
12314
|
-
return (list || []).filter((x)=>x && x.label).map((x)=>({
|
|
12315
|
-
name: x.type || '',
|
|
12316
|
-
value: x.label
|
|
12317
|
-
}));
|
|
12318
|
-
}
|
|
12319
|
-
function $1fd2507769d5bd00$var$buildAxisLabels(fields, studies, expressionSamples) {
|
|
12320
|
-
const labels = {};
|
|
12321
|
-
if (!fields) return labels;
|
|
12322
|
-
const studyById = {};
|
|
12323
|
-
(studies || []).forEach((s)=>{
|
|
12324
|
-
if (s && s._id) studyById[s._id] = s;
|
|
13983
|
+
if (t.fg.status === 'error') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13984
|
+
className: "gsea-panel",
|
|
13985
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
|
|
13986
|
+
children: [
|
|
13987
|
+
"Foreground error: ",
|
|
13988
|
+
t.fg.error
|
|
13989
|
+
]
|
|
13990
|
+
})
|
|
12325
13991
|
});
|
|
12326
|
-
|
|
12327
|
-
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
characteristics: []
|
|
12340
|
-
}
|
|
12341
|
-
};
|
|
12342
|
-
continue;
|
|
12343
|
-
}
|
|
12344
|
-
const expId = m[1].replace(/_/g, '-');
|
|
12345
|
-
const group = 'g' + m[2];
|
|
12346
|
-
const assay = findAssay(expId, group);
|
|
12347
|
-
const study = studyById[expId];
|
|
12348
|
-
const studyName = study && study.description || expId;
|
|
12349
|
-
labels[f] = {
|
|
12350
|
-
short: $1fd2507769d5bd00$var$truncateLabel($1fd2507769d5bd00$var$compactAssayLabel(assay, group), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
|
|
12351
|
-
structured: {
|
|
12352
|
-
studyTitle: studyName,
|
|
12353
|
-
group: group,
|
|
12354
|
-
factors: $1fd2507769d5bd00$var$assayPairs(assay && assay.factor),
|
|
12355
|
-
characteristics: $1fd2507769d5bd00$var$assayPairs(assay && assay.characteristic)
|
|
12356
|
-
}
|
|
12357
|
-
};
|
|
12358
|
-
}
|
|
12359
|
-
return labels;
|
|
12360
|
-
}
|
|
12361
|
-
function $1fd2507769d5bd00$var$rowMatchesSelections(row, selections) {
|
|
12362
|
-
for (const f of Object.keys(selections)){
|
|
12363
|
-
const v = row[f];
|
|
12364
|
-
if (v == null || Array.isArray(v)) return false;
|
|
12365
|
-
const n = +v;
|
|
12366
|
-
if (!Number.isFinite(n)) return false;
|
|
12367
|
-
const [lo, hi] = selections[f];
|
|
12368
|
-
if (n < lo || n > hi) return false;
|
|
12369
|
-
}
|
|
12370
|
-
return true;
|
|
12371
|
-
}
|
|
12372
|
-
function $1fd2507769d5bd00$var$fmt(n) {
|
|
12373
|
-
if (!Number.isFinite(n)) return String(n);
|
|
12374
|
-
const a = Math.abs(n);
|
|
12375
|
-
if (a !== 0 && (a < 0.001 || a >= 1e6)) return n.toExponential(3);
|
|
12376
|
-
return Number(n.toFixed(4)).toString();
|
|
12377
|
-
}
|
|
12378
|
-
function $1fd2507769d5bd00$var$tsvCell(v) {
|
|
12379
|
-
if (v == null) return '';
|
|
12380
|
-
if (Array.isArray(v)) return v.join(',');
|
|
12381
|
-
const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
|
|
12382
|
-
return s.replace(/[\t\r\n]+/g, ' ');
|
|
12383
|
-
}
|
|
12384
|
-
// Mirror the on-screen table header in the TSV: one row per metadata level
|
|
12385
|
-
// the table is showing (Study, then one row per distinct factor type, then
|
|
12386
|
-
// one row per distinct characteristic type), followed by the leaf header
|
|
12387
|
-
// (Gene ID / Name / per-sample group). The first two columns are repurposed
|
|
12388
|
-
// to carry the row category and the row's specific name, matching the
|
|
12389
|
-
// pinned-column labels in ExprTable.
|
|
12390
|
-
function $1fd2507769d5bd00$var$downloadTsv(filename, rows, fields, studies, expressionSamples) {
|
|
12391
|
-
const cols = [
|
|
12392
|
-
'id',
|
|
12393
|
-
'name',
|
|
12394
|
-
...fields
|
|
12395
|
-
];
|
|
12396
|
-
const fieldInfo = (0, $4ab64e76c1caef59$export$7b242440eb2c300d)(fields, studies, expressionSamples);
|
|
12397
|
-
const factorTypes = new Set();
|
|
12398
|
-
const charTypes = new Set();
|
|
12399
|
-
for (const f of fields){
|
|
12400
|
-
const info = fieldInfo[f];
|
|
12401
|
-
if (!info) continue;
|
|
12402
|
-
Object.keys(info.factors || {}).forEach((t)=>factorTypes.add(t));
|
|
12403
|
-
Object.keys(info.characteristics || {}).forEach((t)=>charTypes.add(t));
|
|
12404
|
-
}
|
|
12405
|
-
const factorTypeList = Array.from(factorTypes).sort();
|
|
12406
|
-
const charTypeList = Array.from(charTypes).sort();
|
|
12407
|
-
const metaRow = (cat, label, getValue)=>{
|
|
12408
|
-
const cells = [
|
|
12409
|
-
cat,
|
|
12410
|
-
label
|
|
12411
|
-
];
|
|
12412
|
-
for (const f of fields)cells.push($1fd2507769d5bd00$var$tsvCell(getValue(fieldInfo[f] || {})));
|
|
12413
|
-
return cells.join('\t');
|
|
12414
|
-
};
|
|
12415
|
-
const lines = [];
|
|
12416
|
-
lines.push(metaRow('Study', 'Title', (info)=>info.studyDescription || ''));
|
|
12417
|
-
for (const t of factorTypeList)lines.push(metaRow('Factor', t, (info)=>info.factors && info.factors[t] || ''));
|
|
12418
|
-
for (const t of charTypeList)lines.push(metaRow('Characteristic', t, (info)=>info.characteristics && info.characteristics[t] || ''));
|
|
12419
|
-
// Leaf header — column ids for the data rows.
|
|
12420
|
-
lines.push([
|
|
12421
|
-
'Gene ID',
|
|
12422
|
-
'Name',
|
|
12423
|
-
...fields.map((f)=>{
|
|
12424
|
-
const info = fieldInfo[f];
|
|
12425
|
-
return info && info.group || f.replace(/__expr$/, '');
|
|
13992
|
+
if (t.bg.status === 'error') return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
13993
|
+
className: "gsea-panel",
|
|
13994
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
|
|
13995
|
+
children: [
|
|
13996
|
+
"Background error: ",
|
|
13997
|
+
t.bg.error
|
|
13998
|
+
]
|
|
13999
|
+
})
|
|
14000
|
+
});
|
|
14001
|
+
if (!results) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
14002
|
+
className: "gsea-panel",
|
|
14003
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
14004
|
+
children: "No data."
|
|
12426
14005
|
})
|
|
12427
|
-
].join('\t'));
|
|
12428
|
-
for (const r of rows)lines.push(cols.map((c)=>$1fd2507769d5bd00$var$tsvCell(r[c])).join('\t'));
|
|
12429
|
-
const blob = new Blob([
|
|
12430
|
-
lines.join('\n') + '\n'
|
|
12431
|
-
], {
|
|
12432
|
-
type: 'text/tab-separated-values'
|
|
12433
14006
|
});
|
|
12434
|
-
const
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
14007
|
+
const allBlocks = ui.ontology === 'all' ? Object.values(results) : results[ui.ontology] ? [
|
|
14008
|
+
results[ui.ontology]
|
|
14009
|
+
] : [];
|
|
14010
|
+
// Hide ontologies that aren't used in this species at all.
|
|
14011
|
+
const blocks = allBlocks.filter((b)=>b.tested > 0);
|
|
14012
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
14013
|
+
className: "gsea-panel",
|
|
14014
|
+
children: [
|
|
14015
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
14016
|
+
className: "gsea-summary",
|
|
14017
|
+
children: [
|
|
14018
|
+
"Foreground: ",
|
|
14019
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("b", {
|
|
14020
|
+
children: t.fg.numFound.toLocaleString()
|
|
14021
|
+
}),
|
|
14022
|
+
" genes \xb7 Background: ",
|
|
14023
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("b", {
|
|
14024
|
+
children: t.bg.numFound.toLocaleString()
|
|
14025
|
+
}),
|
|
14026
|
+
" genes"
|
|
14027
|
+
]
|
|
14028
|
+
}),
|
|
14029
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$ControlsBar, {
|
|
14030
|
+
ui: ui,
|
|
14031
|
+
onChange: onUiChange
|
|
14032
|
+
}),
|
|
14033
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Accordion), {
|
|
14034
|
+
alwaysOpen: true,
|
|
14035
|
+
defaultActiveKey: blocks.length === 1 ? blocks[0].ontology : undefined,
|
|
14036
|
+
children: blocks.map((b)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$OntologySection, {
|
|
14037
|
+
block: b,
|
|
14038
|
+
search: ui.search,
|
|
14039
|
+
onAddFilter: onAddFilter
|
|
14040
|
+
}, b.ontology))
|
|
14041
|
+
})
|
|
14042
|
+
]
|
|
14043
|
+
});
|
|
14044
|
+
};
|
|
14045
|
+
const $f511198465122338$var$GseaViewCmp = (props)=>{
|
|
14046
|
+
const { grameneSearch: grameneSearch, grameneMaps: grameneMaps, grameneTaxonomy: grameneTaxonomy, gsea: gsea, gseaUI: ui, gseaResults: results, doSetGseaActiveTaxon: doSetGseaActiveTaxon, doSetGseaUI: doSetGseaUI, doFetchGseaForeground: doFetchGseaForeground, doFetchGseaBackground: doFetchGseaBackground, doAcceptGrameneSuggestion: doAcceptGrameneSuggestion } = props;
|
|
14047
|
+
const taxa = (0, $gXNCa$react.useMemo)(()=>{
|
|
14048
|
+
if (!grameneSearch || !grameneSearch.facet_counts) return [];
|
|
14049
|
+
const arr = grameneSearch.facet_counts.facet_fields.taxon_id || [];
|
|
14050
|
+
const ids = [];
|
|
14051
|
+
const counts = {};
|
|
14052
|
+
for(let i = 0; i < arr.length; i += 2){
|
|
14053
|
+
ids.push(arr[i]);
|
|
14054
|
+
counts[arr[i]] = +arr[i + 1];
|
|
14055
|
+
}
|
|
14056
|
+
if (grameneMaps) ids.sort((a, b)=>{
|
|
14057
|
+
const ma = grameneMaps[a] || grameneMaps[$f511198465122338$var$speciesTaxonId(a)];
|
|
14058
|
+
const mb = grameneMaps[b] || grameneMaps[$f511198465122338$var$speciesTaxonId(b)];
|
|
14059
|
+
return (ma && ma.left_index || 0) - (mb && mb.left_index || 0);
|
|
14060
|
+
});
|
|
14061
|
+
return ids.map((tid)=>({
|
|
14062
|
+
tid: tid,
|
|
14063
|
+
count: counts[tid]
|
|
14064
|
+
}));
|
|
14065
|
+
}, [
|
|
14066
|
+
grameneSearch,
|
|
14067
|
+
grameneMaps
|
|
14068
|
+
]);
|
|
14069
|
+
const activeTaxon = gsea.activeTaxon;
|
|
14070
|
+
const [treeWidth, setTreeWidth] = (0, $gXNCa$react.useState)(280);
|
|
14071
|
+
const beginResize = (e)=>{
|
|
12459
14072
|
e.preventDefault();
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
startHeight: plotHeight
|
|
12463
|
-
};
|
|
14073
|
+
const startX = e.clientX;
|
|
14074
|
+
const startWidth = treeWidth;
|
|
12464
14075
|
const onMove = (ev)=>{
|
|
12465
|
-
const
|
|
12466
|
-
|
|
12467
|
-
const next = Math.max(120, Math.min(1200, s.startHeight + (ev.clientY - s.startY)));
|
|
12468
|
-
setPlotHeight(next);
|
|
14076
|
+
const next = Math.max(150, Math.min(800, startWidth + (ev.clientX - startX)));
|
|
14077
|
+
setTreeWidth(next);
|
|
12469
14078
|
};
|
|
12470
14079
|
const onUp = ()=>{
|
|
12471
|
-
resizeStateRef.current = null;
|
|
12472
14080
|
document.removeEventListener('mousemove', onMove);
|
|
12473
14081
|
document.removeEventListener('mouseup', onUp);
|
|
12474
|
-
document.body.style.cursor = '';
|
|
12475
14082
|
document.body.style.userSelect = '';
|
|
14083
|
+
document.body.style.cursor = '';
|
|
12476
14084
|
};
|
|
12477
|
-
document.body.style.cursor = 'row-resize';
|
|
12478
14085
|
document.body.style.userSelect = 'none';
|
|
14086
|
+
document.body.style.cursor = 'col-resize';
|
|
12479
14087
|
document.addEventListener('mousemove', onMove);
|
|
12480
14088
|
document.addEventListener('mouseup', onUp);
|
|
12481
14089
|
};
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
if (!
|
|
12485
|
-
return rows.filter((r)=>$1fd2507769d5bd00$var$rowMatchesSelections(r, selections));
|
|
12486
|
-
}, [
|
|
12487
|
-
rows,
|
|
12488
|
-
selections,
|
|
12489
|
-
hasBrush
|
|
12490
|
-
]);
|
|
12491
|
-
// Drop fields with no numeric data in the loaded rows so empty axes/columns
|
|
12492
|
-
// don't clutter the visualization. Selected-but-empty fields stay in the
|
|
12493
|
-
// underlying selection so a future load can repopulate them.
|
|
12494
|
-
const visibleFields = (0, $gXNCa$react.useMemo)(()=>{
|
|
12495
|
-
if (rows.length === 0 || selected.length === 0) return selected;
|
|
12496
|
-
return selected.filter((f)=>rows.some((r)=>{
|
|
12497
|
-
const v = r[f];
|
|
12498
|
-
return v != null && !Array.isArray(v) && Number.isFinite(+v);
|
|
12499
|
-
}));
|
|
14090
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
14091
|
+
if (taxa.length === 0) return;
|
|
14092
|
+
if (!activeTaxon || !taxa.find((t)=>String(t.tid) === String(activeTaxon))) doSetGseaActiveTaxon(taxa[0].tid);
|
|
12500
14093
|
}, [
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
|
|
12504
|
-
const handleReorder = onReorder ? (newVisibleOrder)=>{
|
|
12505
|
-
const visibleSet = new Set(newVisibleOrder);
|
|
12506
|
-
const hidden = selected.filter((f)=>!visibleSet.has(f));
|
|
12507
|
-
onReorder([
|
|
12508
|
-
...newVisibleOrder,
|
|
12509
|
-
...hidden
|
|
12510
|
-
]);
|
|
12511
|
-
} : undefined;
|
|
12512
|
-
const axisLabels = (0, $gXNCa$react.useMemo)(()=>$1fd2507769d5bd00$var$buildAxisLabels(visibleFields, studies, expressionSamples), [
|
|
12513
|
-
visibleFields,
|
|
12514
|
-
studies,
|
|
12515
|
-
expressionSamples
|
|
14094
|
+
taxa,
|
|
14095
|
+
activeTaxon,
|
|
14096
|
+
doSetGseaActiveTaxon
|
|
12516
14097
|
]);
|
|
12517
14098
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
12518
|
-
if (
|
|
12519
|
-
|
|
12520
|
-
|
|
12521
|
-
|
|
14099
|
+
if (!activeTaxon) return;
|
|
14100
|
+
const t = gsea.byTaxon[activeTaxon];
|
|
14101
|
+
if (!t) return;
|
|
14102
|
+
if (t.fg.status === 'idle') doFetchGseaForeground(activeTaxon);
|
|
14103
|
+
if (t.bg.status === 'idle') doFetchGseaBackground(activeTaxon);
|
|
12522
14104
|
}, [
|
|
12523
|
-
|
|
12524
|
-
|
|
14105
|
+
activeTaxon,
|
|
14106
|
+
gsea,
|
|
14107
|
+
doFetchGseaForeground,
|
|
14108
|
+
doFetchGseaBackground
|
|
12525
14109
|
]);
|
|
14110
|
+
if (taxa.length === 0) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
14111
|
+
className: "gsea-view",
|
|
14112
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
14113
|
+
children: "No species in the current results."
|
|
14114
|
+
})
|
|
14115
|
+
});
|
|
14116
|
+
if (!grameneTaxonomy) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
14117
|
+
className: "gsea-view",
|
|
14118
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
14119
|
+
children: "Loading taxonomy\u2026"
|
|
14120
|
+
})
|
|
14121
|
+
});
|
|
14122
|
+
const handleAddFilter = (row)=>{
|
|
14123
|
+
if (!doAcceptGrameneSuggestion) return;
|
|
14124
|
+
const label = row.term_name ? `${row.term_display_id} ${row.term_name}` : row.term_display_id;
|
|
14125
|
+
doAcceptGrameneSuggestion({
|
|
14126
|
+
fq_field: row.field,
|
|
14127
|
+
fq_value: String(row.term_id),
|
|
14128
|
+
name: label,
|
|
14129
|
+
category: row.ontology_label
|
|
14130
|
+
});
|
|
14131
|
+
};
|
|
12526
14132
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12527
|
-
className: "
|
|
14133
|
+
className: "gsea-view gsea-layout",
|
|
12528
14134
|
children: [
|
|
12529
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
12530
|
-
className: "
|
|
12531
|
-
|
|
12532
|
-
|
|
12533
|
-
|
|
12534
|
-
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
-
|
|
12538
|
-
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
]
|
|
12542
|
-
}),
|
|
12543
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
12544
|
-
size: "sm",
|
|
12545
|
-
variant: "primary",
|
|
12546
|
-
disabled: selected.length === 0 || fetchInfo.status === 'loading',
|
|
12547
|
-
onClick: onLoad,
|
|
12548
|
-
children: fetchInfo.status === 'loading' ? "Loading\u2026" : 'Load data'
|
|
12549
|
-
}),
|
|
12550
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.ToggleButtonGroup), {
|
|
12551
|
-
type: "radio",
|
|
12552
|
-
name: `exprviz-scale-${taxon}`,
|
|
12553
|
-
size: "sm",
|
|
12554
|
-
value: scale,
|
|
12555
|
-
onChange: setScale,
|
|
12556
|
-
children: [
|
|
12557
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
|
|
12558
|
-
id: `exprviz-scale-${taxon}-lin`,
|
|
12559
|
-
value: "linear",
|
|
12560
|
-
variant: "outline-secondary",
|
|
12561
|
-
children: "Linear"
|
|
12562
|
-
}),
|
|
12563
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.ToggleButton), {
|
|
12564
|
-
id: `exprviz-scale-${taxon}-log`,
|
|
12565
|
-
value: "log",
|
|
12566
|
-
variant: "outline-secondary",
|
|
12567
|
-
children: "Log"
|
|
12568
|
-
})
|
|
12569
|
-
]
|
|
12570
|
-
}),
|
|
12571
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
12572
|
-
size: "sm",
|
|
12573
|
-
variant: "outline-secondary",
|
|
12574
|
-
disabled: !hasBrush,
|
|
12575
|
-
onClick: ()=>{
|
|
12576
|
-
setClearVersion((v)=>v + 1);
|
|
12577
|
-
setSelections({});
|
|
12578
|
-
},
|
|
12579
|
-
children: "Clear brushes"
|
|
12580
|
-
}),
|
|
12581
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
12582
|
-
size: "sm",
|
|
12583
|
-
variant: "outline-secondary",
|
|
12584
|
-
disabled: filteredRows.length === 0 || visibleFields.length === 0,
|
|
12585
|
-
onClick: ()=>$1fd2507769d5bd00$var$downloadTsv(`expression_${taxon}.tsv`, filteredRows, visibleFields, studies, expressionSamples),
|
|
12586
|
-
title: "Download the visible rows and columns as tab-delimited text",
|
|
12587
|
-
children: "Download TSV"
|
|
12588
|
-
}),
|
|
12589
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
12590
|
-
size: "sm",
|
|
12591
|
-
variant: "success",
|
|
12592
|
-
disabled: !hasBrush || !onAddRangeQuery,
|
|
12593
|
-
onClick: ()=>{
|
|
12594
|
-
const terms = Object.keys(selections).map((field)=>{
|
|
12595
|
-
const [lo, hi] = selections[field];
|
|
12596
|
-
return {
|
|
12597
|
-
category: 'Expression',
|
|
12598
|
-
name: `${field}: ${$1fd2507769d5bd00$var$fmt(lo)}\u{2013}${$1fd2507769d5bd00$var$fmt(hi)}`,
|
|
12599
|
-
fq_field: field,
|
|
12600
|
-
fq_value: `[${lo} TO ${hi}]`
|
|
12601
|
-
};
|
|
12602
|
-
});
|
|
12603
|
-
onAddRangeQuery(terms);
|
|
12604
|
-
},
|
|
12605
|
-
title: "Add brush ranges as an AND-conjunction filter on the search",
|
|
12606
|
-
children: "Apply as filter"
|
|
12607
|
-
}),
|
|
12608
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
|
|
12609
|
-
className: "exprviz-status",
|
|
12610
|
-
children: [
|
|
12611
|
-
hasBrush ? `${filteredRows.length} of ${rows.length}` : rows.length,
|
|
12612
|
-
fetchInfo.total ? ` / ${fetchInfo.total}` : '',
|
|
12613
|
-
" genes",
|
|
12614
|
-
hasBrush ? ' (brushed)' : ' loaded'
|
|
12615
|
-
]
|
|
12616
|
-
})
|
|
12617
|
-
]
|
|
14135
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
14136
|
+
className: "gsea-layout-tree",
|
|
14137
|
+
style: {
|
|
14138
|
+
flex: `0 0 ${treeWidth}px`
|
|
14139
|
+
},
|
|
14140
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$SpeciesTree, {
|
|
14141
|
+
taxonomy: grameneTaxonomy,
|
|
14142
|
+
grameneMaps: grameneMaps,
|
|
14143
|
+
taxa: taxa,
|
|
14144
|
+
activeTaxon: activeTaxon,
|
|
14145
|
+
onSelect: (tid)=>doSetGseaActiveTaxon(String(tid))
|
|
14146
|
+
})
|
|
12618
14147
|
}),
|
|
12619
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
12620
|
-
className: "
|
|
12621
|
-
|
|
12622
|
-
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12626
|
-
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
hoveredId: hoveredId,
|
|
12635
|
-
axisLabels: axisLabels
|
|
12636
|
-
})
|
|
12637
|
-
}),
|
|
12638
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12639
|
-
className: "exprviz-resizer",
|
|
12640
|
-
role: "separator",
|
|
12641
|
-
"aria-orientation": "horizontal",
|
|
12642
|
-
"aria-label": "Resize plot",
|
|
12643
|
-
onMouseDown: startResize,
|
|
12644
|
-
title: "Drag to resize"
|
|
12645
|
-
}),
|
|
12646
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12647
|
-
className: "exprviz-table",
|
|
12648
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4ab64e76c1caef59$export$2e2bcd8739ae039), {
|
|
12649
|
-
rows: filteredRows,
|
|
12650
|
-
fields: visibleFields,
|
|
12651
|
-
onReorder: handleReorder,
|
|
12652
|
-
studies: studies,
|
|
12653
|
-
expressionSamples: expressionSamples,
|
|
12654
|
-
onHoverRow: setHoveredId
|
|
12655
|
-
})
|
|
12656
|
-
})
|
|
12657
|
-
]
|
|
14148
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
14149
|
+
className: "gsea-splitter",
|
|
14150
|
+
onMouseDown: beginResize,
|
|
14151
|
+
title: "Drag to resize"
|
|
14152
|
+
}),
|
|
14153
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
14154
|
+
className: "gsea-layout-panel",
|
|
14155
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($f511198465122338$var$TaxonPanel, {
|
|
14156
|
+
taxon: activeTaxon ? String(activeTaxon) : null,
|
|
14157
|
+
gsea: gsea,
|
|
14158
|
+
results: results,
|
|
14159
|
+
ui: ui,
|
|
14160
|
+
onUiChange: doSetGseaUI,
|
|
14161
|
+
onAddFilter: handleAddFilter
|
|
14162
|
+
})
|
|
12658
14163
|
})
|
|
12659
14164
|
]
|
|
12660
14165
|
});
|
|
12661
14166
|
};
|
|
12662
|
-
var $
|
|
14167
|
+
var $f511198465122338$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneSearch', 'selectGrameneMaps', 'selectGrameneTaxonomy', 'selectGsea', 'selectGseaUI', 'selectGseaResults', 'doSetGseaActiveTaxon', 'doSetGseaUI', 'doFetchGseaForeground', 'doFetchGseaBackground', 'doAcceptGrameneSuggestion', $f511198465122338$var$GseaViewCmp);
|
|
12663
14168
|
|
|
12664
14169
|
|
|
12665
14170
|
|
|
@@ -12780,6 +14285,7 @@ const $693dd8c7a5607c3a$var$inventory = {
|
|
|
12780
14285
|
attribs: (0, $67bf5a43401bffdc$export$2e2bcd8739ae039),
|
|
12781
14286
|
expression: (0, $261baeb81c4d4d8a$export$2e2bcd8739ae039),
|
|
12782
14287
|
exprViz: (0, $1fd2507769d5bd00$export$2e2bcd8739ae039),
|
|
14288
|
+
gsea: (0, $f511198465122338$export$2e2bcd8739ae039),
|
|
12783
14289
|
userLists: (0, $0f50f369018a42ef$export$2e2bcd8739ae039),
|
|
12784
14290
|
export: (0, $37b3bb0145d266b0$export$2e2bcd8739ae039)
|
|
12785
14291
|
};
|