gramene-search 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1492,6 +1492,12 @@ const $24971af0a229e0e3$var$grameneViews = {
1492
1492
  show: 'off',
1493
1493
  shouldScroll: false
1494
1494
  },
1495
+ {
1496
+ id: 'attrTable',
1497
+ name: 'Attribute table',
1498
+ show: 'off',
1499
+ shouldScroll: false
1500
+ },
1495
1501
  {
1496
1502
  id: 'ontologyEnrichment',
1497
1503
  name: 'Ontology Enrichment',
@@ -1632,6 +1638,7 @@ const $24971af0a229e0e3$var$grameneViews = {
1632
1638
  'list',
1633
1639
  'export',
1634
1640
  'exprViz',
1641
+ 'attrTable',
1635
1642
  'ontologyEnrichment'
1636
1643
  ]);
1637
1644
  const autoDisable = numFound === 0 || !hasFilters;
@@ -4663,6 +4670,366 @@ var $4f15cd8a7d970b18$export$2e2bcd8739ae039 = $4f15cd8a7d970b18$var$exprViz;
4663
4670
 
4664
4671
 
4665
4672
 
4673
+ // Shared vocabulary and helpers for the sorghum_v11 per-gene expression
4674
+ // attributes (`expr_*__attr_*`). Used by both the Homology tbrowse Expression
4675
+ // zone (results/details/exprAttrsZone.js) and the Attribute table view
4676
+ // (attrTable/AttrTableView.js) so organ ordering and the ordinal level colors
4677
+ // stay a single source of truth.
4678
+ // Canonical anatomical ordering (vegetative → reproductive → seed). Organs not
4679
+ // listed here are appended alphabetically so nothing is ever dropped.
4680
+ const $4b8473139e48e77e$export$a59633f9e62f0e07 = [
4681
+ 'root',
4682
+ 'shoot',
4683
+ 'stem',
4684
+ 'leaf',
4685
+ 'meristem',
4686
+ 'vasculature',
4687
+ 'tuber',
4688
+ 'cotyledon',
4689
+ 'inflorescence',
4690
+ 'flower',
4691
+ 'anther_pollen',
4692
+ 'fruit',
4693
+ 'pericarp',
4694
+ 'seed',
4695
+ 'endosperm',
4696
+ 'embryo'
4697
+ ];
4698
+ const $4b8473139e48e77e$export$9a0b81895f7233d0 = {
4699
+ root: 'rt',
4700
+ shoot: 'sht',
4701
+ stem: 'stm',
4702
+ leaf: 'lf',
4703
+ meristem: 'mer',
4704
+ vasculature: 'vas',
4705
+ tuber: 'tbr',
4706
+ cotyledon: 'cot',
4707
+ inflorescence: 'inf',
4708
+ flower: 'flw',
4709
+ anther_pollen: 'ant',
4710
+ fruit: 'frt',
4711
+ pericarp: 'per',
4712
+ seed: 'sd',
4713
+ endosperm: 'end',
4714
+ embryo: 'emb'
4715
+ };
4716
+ const $4b8473139e48e77e$export$f8541c2790c3baeb = (o)=>$4b8473139e48e77e$export$9a0b81895f7233d0[o] || o.slice(0, 3);
4717
+ const $4b8473139e48e77e$export$50c1f966fd3af83e = (o)=>o.replace(/_/g, ' ');
4718
+ const $4b8473139e48e77e$export$4bb936dc8856d37a = [
4719
+ 'not_expressed',
4720
+ 'low',
4721
+ 'medium',
4722
+ 'high',
4723
+ 'very_high'
4724
+ ];
4725
+ const $4b8473139e48e77e$export$b352e11f7a2ac9c5 = {
4726
+ not_expressed: '#eef2f6',
4727
+ low: '#cfe0ee',
4728
+ medium: '#8fbbdc',
4729
+ high: '#3f86c2',
4730
+ very_high: '#0a3d72'
4731
+ };
4732
+ const $4b8473139e48e77e$export$433053e2c141d410 = {
4733
+ not_expressed: 'not expressed',
4734
+ low: 'low',
4735
+ medium: 'medium',
4736
+ high: 'high',
4737
+ very_high: 'very high'
4738
+ };
4739
+ const $4b8473139e48e77e$export$a61929d6d484c6d4 = $4b8473139e48e77e$export$4bb936dc8856d37a.reduce((m, l, i)=>{
4740
+ m[l] = i;
4741
+ return m;
4742
+ }, {});
4743
+ const $4b8473139e48e77e$export$c6dbe9be8c8b382e = {
4744
+ up: {
4745
+ bg: '#fdecea',
4746
+ fg: '#c0392b'
4747
+ },
4748
+ down: {
4749
+ bg: '#eaf2fb',
4750
+ fg: '#2e6fae'
4751
+ }
4752
+ };
4753
+ const $4b8473139e48e77e$export$39a35029fe99c21 = '#d35400'; // specific-to dot / enhanced-in outline
4754
+ function $4b8473139e48e77e$export$8ca7625ba2054603(tokens) {
4755
+ const out = {};
4756
+ (tokens || []).forEach((t)=>{
4757
+ const i = t.lastIndexOf(':');
4758
+ if (i < 0) return;
4759
+ out[t.slice(0, i)] = t.slice(i + 1);
4760
+ });
4761
+ return out;
4762
+ }
4763
+ function $4b8473139e48e77e$export$65382efdcdd96f8c(organs) {
4764
+ const set = organs instanceof Set ? organs : new Set(organs || []);
4765
+ const known = $4b8473139e48e77e$export$a59633f9e62f0e07.filter((o)=>set.has(o));
4766
+ const unknown = [
4767
+ ...set
4768
+ ].filter((o)=>!$4b8473139e48e77e$export$a59633f9e62f0e07.includes(o)).sort();
4769
+ return [
4770
+ ...known,
4771
+ ...unknown
4772
+ ];
4773
+ }
4774
+ function $4b8473139e48e77e$export$69fb5d19b5db1cfa(doc) {
4775
+ const d = doc || {};
4776
+ const cls = d.expr_class__attr_ss || [];
4777
+ const maxTpm = Number.isFinite(+d.expr_max_tpm__attr_f) ? +d.expr_max_tpm__attr_f : null;
4778
+ const tau = Number.isFinite(+d.expr_tau__attr_f) ? +d.expr_tau__attr_f : null;
4779
+ return {
4780
+ organLevels: $4b8473139e48e77e$export$8ca7625ba2054603(d.expr_organ_level__attr_ss),
4781
+ specificTo: new Set(d.expr_specific_to__attr_ss || []),
4782
+ enhancedIn: new Set(d.expr_enhanced_in__attr_ss || []),
4783
+ highIn: new Set(d.expr_high_in__attr_ss || []),
4784
+ cls: cls,
4785
+ maxTpm: maxTpm,
4786
+ tau: tau,
4787
+ nOrgans: Number.isFinite(+d.expr_n_organs_detected__attr_i) ? +d.expr_n_organs_detected__attr_i : null,
4788
+ activatedBy: d.expr_activated_by__attr_ss || [],
4789
+ repressedBy: d.expr_repressed_by__attr_ss || []
4790
+ };
4791
+ }
4792
+ function $4b8473139e48e77e$export$fc40d061e6c16645(v, range) {
4793
+ if (!Number.isFinite(v)) return null;
4794
+ const lo = Math.log10((range && range.min || 0) + 1);
4795
+ const hi = Math.log10((range && range.max || 0) + 1);
4796
+ if (hi <= lo) return 0.5;
4797
+ return Math.max(0, Math.min(1, (Math.log10(v + 1) - lo) / (hi - lo)));
4798
+ }
4799
+ function $4b8473139e48e77e$export$4b443417c5259620(v, range) {
4800
+ const f = $4b8473139e48e77e$export$fc40d061e6c16645(v, range);
4801
+ if (f === null) return 'transparent';
4802
+ return `rgba(33, 102, 172, ${(0.1 + 0.65 * f).toFixed(3)})`;
4803
+ }
4804
+ function $4b8473139e48e77e$export$8eaa32ef86afdd9c(v) {
4805
+ if (!Number.isFinite(v)) return '';
4806
+ return v >= 10 ? String(Math.round(v)) : String(Math.round(v * 10) / 10);
4807
+ }
4808
+ function $4b8473139e48e77e$export$1f04174626f2ac64(cls) {
4809
+ return cls && cls.length ? cls.map((c)=>c.replace(/_/g, ' ')).join(', ') : null;
4810
+ }
4811
+ const $4b8473139e48e77e$export$705aff68da3ddba0 = [
4812
+ 'expr_class__attr_ss',
4813
+ 'expr_organ_level__attr_ss',
4814
+ 'expr_specific_to__attr_ss',
4815
+ 'expr_enhanced_in__attr_ss',
4816
+ 'expr_high_in__attr_ss',
4817
+ 'expr_activated_by__attr_ss',
4818
+ 'expr_repressed_by__attr_ss',
4819
+ 'expr_tau__attr_f',
4820
+ 'expr_max_tpm__attr_f',
4821
+ 'expr_n_organs_detected__attr_i'
4822
+ ];
4823
+
4824
+
4825
+ // Backing store for the "Attribute table" view: the genes in the current search
4826
+ // result set, fetched in pages and rendered as a gene × attribute table (basic
4827
+ // identity columns + the expression-attribute heatmap + any extra attribute
4828
+ // columns the user picks from the field catalog).
4829
+ //
4830
+ // Paging mirrors exprViz's doFetchExprVizData: a recursive fetchPage(offset)
4831
+ // with a request-id guard so a superseded fetch can never write stale rows.
4832
+ const $12feb7ca48271e17$var$PAGE_SIZE = 1000;
4833
+ const $12feb7ca48271e17$var$MAX_GENES = 5000;
4834
+ // Identity/basic columns that are always fetched.
4835
+ const $12feb7ca48271e17$var$BASE_FIELDS = [
4836
+ 'id',
4837
+ 'name',
4838
+ 'system_name',
4839
+ 'taxon_id',
4840
+ 'biotype',
4841
+ 'region',
4842
+ 'start',
4843
+ 'end'
4844
+ ];
4845
+ let $12feb7ca48271e17$var$fetchPendingId = 0;
4846
+ // Only send fq=taxon_id:(...) when the user has actually subset the genomes —
4847
+ // same rule the main search uses (see bundles/api.js), so the table matches the
4848
+ // visible result set without bloating the URL.
4849
+ function $12feb7ca48271e17$var$genomeSubset(g, m) {
4850
+ const maps = m || {};
4851
+ const visibleTaxa = Object.keys(maps).filter((tid)=>!maps[tid].hidden);
4852
+ const activeVisible = Object.keys(g && g.active || {}).filter((tid)=>maps[tid] && !maps[tid].hidden);
4853
+ const subset = activeVisible.length > 0 && activeVisible.length < visibleTaxa.length;
4854
+ const sorted = activeVisible.slice().sort();
4855
+ return {
4856
+ fq: subset ? `&fq=taxon_id:(${sorted.join(' OR ')})` : '',
4857
+ key: subset ? sorted.join(',') : ''
4858
+ };
4859
+ }
4860
+ function $12feb7ca48271e17$var$computeSignature(q, g, m, selectedFields) {
4861
+ return `${q}|${$12feb7ca48271e17$var$genomeSubset(g, m).key}|${(selectedFields || []).slice().sort().join(',')}`;
4862
+ }
4863
+ const $12feb7ca48271e17$var$attrTable = {
4864
+ name: 'attrTable',
4865
+ getReducer: ()=>{
4866
+ const initialState = {
4867
+ status: 'idle',
4868
+ docs: [],
4869
+ total: 0,
4870
+ truncated: false,
4871
+ signature: null,
4872
+ error: null,
4873
+ requestId: 0,
4874
+ selectedFields: [] // extra attribute columns chosen from the field catalog
4875
+ };
4876
+ return (state = initialState, { type: type, payload: payload })=>{
4877
+ switch(type){
4878
+ case 'ATTRTABLE_FETCH_STARTED':
4879
+ return {
4880
+ ...state,
4881
+ status: 'loading',
4882
+ docs: [],
4883
+ total: 0,
4884
+ truncated: false,
4885
+ error: null,
4886
+ signature: payload.signature,
4887
+ requestId: payload.requestId
4888
+ };
4889
+ case 'ATTRTABLE_FETCH_BATCH':
4890
+ {
4891
+ if (payload.requestId !== state.requestId) return state; // superseded
4892
+ const docs = state.docs.concat(payload.docs);
4893
+ const target = Math.min(payload.total, $12feb7ca48271e17$var$MAX_GENES);
4894
+ return {
4895
+ ...state,
4896
+ docs: docs,
4897
+ total: payload.total,
4898
+ truncated: payload.total > $12feb7ca48271e17$var$MAX_GENES,
4899
+ status: docs.length >= target ? 'ready' : 'loading'
4900
+ };
4901
+ }
4902
+ case 'ATTRTABLE_FETCH_FAILED':
4903
+ if (payload.requestId !== state.requestId) return state;
4904
+ return {
4905
+ ...state,
4906
+ status: 'error',
4907
+ error: payload.error
4908
+ };
4909
+ case 'ATTRTABLE_FIELD_TOGGLED':
4910
+ {
4911
+ const set = new Set(state.selectedFields);
4912
+ if (set.has(payload)) set.delete(payload);
4913
+ else set.add(payload);
4914
+ return {
4915
+ ...state,
4916
+ selectedFields: [
4917
+ ...set
4918
+ ]
4919
+ };
4920
+ }
4921
+ case 'ATTRTABLE_FIELDS_BULK_SET':
4922
+ {
4923
+ const set = new Set(state.selectedFields);
4924
+ (payload.names || []).forEach((n)=>{
4925
+ if (payload.selected) set.add(n);
4926
+ else set.delete(n);
4927
+ });
4928
+ return {
4929
+ ...state,
4930
+ selectedFields: [
4931
+ ...set
4932
+ ]
4933
+ };
4934
+ }
4935
+ case 'GRAMENE_SEARCH_CLEARED':
4936
+ return {
4937
+ ...initialState,
4938
+ selectedFields: state.selectedFields
4939
+ };
4940
+ default:
4941
+ return state;
4942
+ }
4943
+ };
4944
+ },
4945
+ doFetchAttrTable: ()=>({ dispatch: dispatch, store: store })=>{
4946
+ const { selectedFields: selectedFields } = store.selectAttrTable();
4947
+ const q = store.selectGrameneFiltersQueryString();
4948
+ const { fq: fq } = $12feb7ca48271e17$var$genomeSubset(store.selectGrameneGenomes(), store.selectGrameneMaps());
4949
+ const signature = $12feb7ca48271e17$var$computeSignature(q, store.selectGrameneGenomes(), store.selectGrameneMaps(), selectedFields);
4950
+ const requestId = ++$12feb7ca48271e17$var$fetchPendingId;
4951
+ dispatch({
4952
+ type: 'ATTRTABLE_FETCH_STARTED',
4953
+ payload: {
4954
+ requestId: requestId,
4955
+ signature: signature
4956
+ }
4957
+ });
4958
+ const api = store.selectGrameneAPI();
4959
+ // Explicit fl — never fl=*, which would drag in every per-sample __expr column.
4960
+ const fl = [
4961
+ ...new Set([
4962
+ ...$12feb7ca48271e17$var$BASE_FIELDS,
4963
+ ...(0, $4b8473139e48e77e$export$705aff68da3ddba0),
4964
+ ...selectedFields
4965
+ ])
4966
+ ].join(',');
4967
+ const fetchPage = (offset)=>{
4968
+ if (requestId !== $12feb7ca48271e17$var$fetchPendingId) return; // superseded
4969
+ const rows = Math.min($12feb7ca48271e17$var$PAGE_SIZE, $12feb7ca48271e17$var$MAX_GENES - offset);
4970
+ if (rows <= 0) return;
4971
+ const url = `${api}/search?q=${q}${fq}&fl=${fl}&rows=${rows}&start=${offset}`;
4972
+ fetch(url).then((r)=>r.json()).then((json)=>{
4973
+ if (requestId !== $12feb7ca48271e17$var$fetchPendingId) return;
4974
+ const docs = json.response && json.response.docs || [];
4975
+ const total = json.response && json.response.numFound || 0;
4976
+ dispatch({
4977
+ type: 'ATTRTABLE_FETCH_BATCH',
4978
+ payload: {
4979
+ requestId: requestId,
4980
+ docs: docs,
4981
+ total: total
4982
+ }
4983
+ });
4984
+ const next = offset + docs.length;
4985
+ if (docs.length > 0 && next < Math.min(total, $12feb7ca48271e17$var$MAX_GENES)) fetchPage(next);
4986
+ }).catch((err)=>{
4987
+ dispatch({
4988
+ type: 'ATTRTABLE_FETCH_FAILED',
4989
+ payload: {
4990
+ requestId: requestId,
4991
+ error: String(err)
4992
+ }
4993
+ });
4994
+ });
4995
+ };
4996
+ fetchPage(0);
4997
+ },
4998
+ doToggleAttrTableField: (name)=>({ dispatch: dispatch })=>dispatch({
4999
+ type: 'ATTRTABLE_FIELD_TOGGLED',
5000
+ payload: name
5001
+ }),
5002
+ doBulkSetAttrTableFields: (names, selected)=>({ dispatch: dispatch })=>dispatch({
5003
+ type: 'ATTRTABLE_FIELDS_BULK_SET',
5004
+ payload: {
5005
+ names: names,
5006
+ selected: selected
5007
+ }
5008
+ }),
5009
+ selectAttrTable: (state)=>state.attrTable,
5010
+ selectAttrTableSelectedFields: (state)=>state.attrTable.selectedFields,
5011
+ // Fetch only while the view is actually on, and only when the query context or
5012
+ // the chosen columns have changed. A toggle made mid-load is picked up when the
5013
+ // in-flight fetch settles (the signature will no longer match).
5014
+ reactAttrTableFetch: (0, $gXNCa$reduxbundler.createSelector)('selectAttrTable', 'selectGrameneFiltersStatus', 'selectGrameneViewsOn', 'selectGrameneFiltersQueryString', 'selectGrameneGenomes', 'selectGrameneMaps', (at, filtersStatus, viewsOn, q, g, m)=>{
5015
+ if (!at || filtersStatus === 'init') return;
5016
+ if (!viewsOn || !viewsOn.has('attrTable')) return;
5017
+ if (at.status === 'loading') return;
5018
+ const sig = $12feb7ca48271e17$var$computeSignature(q, g, m, at.selectedFields);
5019
+ if (at.signature === sig && (at.status === 'ready' || at.status === 'error')) return;
5020
+ return {
5021
+ actionCreator: 'doFetchAttrTable'
5022
+ };
5023
+ })
5024
+ };
5025
+ const $12feb7ca48271e17$export$d00a9fd01497b827 = {
5026
+ PAGE_SIZE: $12feb7ca48271e17$var$PAGE_SIZE,
5027
+ MAX_GENES: $12feb7ca48271e17$var$MAX_GENES
5028
+ };
5029
+ var $12feb7ca48271e17$export$2e2bcd8739ae039 = $12feb7ca48271e17$var$attrTable;
5030
+
5031
+
5032
+
4666
5033
  // Ontology over-representation analysis (clusterProfiler::enrichGO-style).
4667
5034
  //
4668
5035
  // For each species tab we run:
@@ -6419,6 +6786,7 @@ var $5df6c55c1bef3469$export$2e2bcd8739ae039 = [
6419
6786
  (0, $1508f5a42be6e7b5$export$2e2bcd8739ae039),
6420
6787
  (0, $c921a0d2b34aadb6$export$2e2bcd8739ae039),
6421
6788
  (0, $4f15cd8a7d970b18$export$2e2bcd8739ae039),
6789
+ (0, $12feb7ca48271e17$export$2e2bcd8739ae039),
6422
6790
  (0, $d365d8c287ab0c94$export$2e2bcd8739ae039),
6423
6791
  (0, $7f865ea0feda21af$export$2e2bcd8739ae039),
6424
6792
  (0, $6048209b532f201d$export$2e2bcd8739ae039),
@@ -7432,82 +7800,7 @@ const $5c2c79352d3d7b81$export$b2e089eb3692b073 = (props)=>/*#__PURE__*/ (0, $gX
7432
7800
 
7433
7801
 
7434
7802
 
7435
- // Canonical anatomical ordering (vegetative → reproductive → seed). Organs not
7436
- // listed here are appended alphabetically so nothing is ever dropped.
7437
- const $cd8bc494277e92a4$var$ORGAN_ORDER = [
7438
- 'root',
7439
- 'shoot',
7440
- 'stem',
7441
- 'leaf',
7442
- 'meristem',
7443
- 'vasculature',
7444
- 'tuber',
7445
- 'cotyledon',
7446
- 'inflorescence',
7447
- 'flower',
7448
- 'anther_pollen',
7449
- 'fruit',
7450
- 'pericarp',
7451
- 'seed',
7452
- 'endosperm',
7453
- 'embryo'
7454
- ];
7455
- // Short column-header codes; unknown organs fall back to their first 3 letters.
7456
- const $cd8bc494277e92a4$var$ORGAN_ABBR = {
7457
- root: 'rt',
7458
- shoot: 'sht',
7459
- stem: 'stm',
7460
- leaf: 'lf',
7461
- meristem: 'mer',
7462
- vasculature: 'vas',
7463
- tuber: 'tbr',
7464
- cotyledon: 'cot',
7465
- inflorescence: 'inf',
7466
- flower: 'flw',
7467
- anther_pollen: 'ant',
7468
- fruit: 'frt',
7469
- pericarp: 'per',
7470
- seed: 'sd',
7471
- endosperm: 'end',
7472
- embryo: 'emb'
7473
- };
7474
- const $cd8bc494277e92a4$var$abbr = (o)=>$cd8bc494277e92a4$var$ORGAN_ABBR[o] || o.slice(0, 3);
7475
- // Ordinal expression level → color. not_expressed gets a distinct pale tint (it
7476
- // IS a measurement); an organ a species doesn't report stays transparent (= not
7477
- // assayed). Ramp matches HeatmapPlot's pale→dark blue.
7478
- const $cd8bc494277e92a4$var$LEVEL_ORDER = [
7479
- 'not_expressed',
7480
- 'low',
7481
- 'medium',
7482
- 'high',
7483
- 'very_high'
7484
- ];
7485
- const $cd8bc494277e92a4$var$LEVEL_COLOR = {
7486
- not_expressed: '#eef2f6',
7487
- low: '#cfe0ee',
7488
- medium: '#8fbbdc',
7489
- high: '#3f86c2',
7490
- very_high: '#0a3d72'
7491
- };
7492
- const $cd8bc494277e92a4$var$LEVEL_LABEL = {
7493
- not_expressed: 'not expressed',
7494
- low: 'low',
7495
- medium: 'medium',
7496
- high: 'high',
7497
- very_high: 'very high'
7498
- };
7499
- // Stress chips: activated (induced) = warm, repressed = cool.
7500
- const $cd8bc494277e92a4$var$STRESS = {
7501
- up: {
7502
- bg: '#fdecea',
7503
- fg: '#c0392b'
7504
- },
7505
- down: {
7506
- bg: '#eaf2fb',
7507
- fg: '#2e6fae'
7508
- }
7509
- };
7510
- const $cd8bc494277e92a4$var$MARKER = '#d35400'; // specific-to dot / enhanced-in outline
7803
+
7511
7804
  const $cd8bc494277e92a4$var$ORGAN_CELL_W = 16;
7512
7805
  const $cd8bc494277e92a4$var$MAXTPM_W = 46;
7513
7806
  const $cd8bc494277e92a4$var$STRESS_MIN = 120;
@@ -7528,38 +7821,16 @@ function $cd8bc494277e92a4$export$965a6b1d7408d496(docs, tree) {
7528
7821
  if (!d || !d.id) return;
7529
7822
  const nodeId = nodeOf[d.id];
7530
7823
  if (!nodeId) return;
7531
- const organLevels = {};
7532
- (d.expr_organ_level__attr_ss || []).forEach((t)=>{
7533
- const i = t.lastIndexOf(':');
7534
- if (i < 0) return;
7535
- const organ = t.slice(0, i);
7536
- organLevels[organ] = t.slice(i + 1);
7537
- organSet.add(organ);
7538
- });
7539
- const maxTpm = Number.isFinite(+d.expr_max_tpm__attr_f) ? +d.expr_max_tpm__attr_f : null;
7540
- if (maxTpm !== null) {
7541
- if (maxTpm < tpmMin) tpmMin = maxTpm;
7542
- if (maxTpm > tpmMax) tpmMax = maxTpm;
7824
+ const attrs = (0, $4b8473139e48e77e$export$69fb5d19b5db1cfa)(d);
7825
+ Object.keys(attrs.organLevels).forEach((o)=>organSet.add(o));
7826
+ if (attrs.maxTpm !== null) {
7827
+ if (attrs.maxTpm < tpmMin) tpmMin = attrs.maxTpm;
7828
+ if (attrs.maxTpm > tpmMax) tpmMax = attrs.maxTpm;
7543
7829
  }
7544
- byNode[nodeId] = {
7545
- cls: d.expr_class__attr_ss || [],
7546
- organLevels: organLevels,
7547
- specificTo: new Set(d.expr_specific_to__attr_ss || []),
7548
- enhancedIn: new Set(d.expr_enhanced_in__attr_ss || []),
7549
- maxTpm: maxTpm,
7550
- activatedBy: d.expr_activated_by__attr_ss || [],
7551
- repressedBy: d.expr_repressed_by__attr_ss || []
7552
- };
7830
+ byNode[nodeId] = attrs;
7553
7831
  });
7554
- const known = $cd8bc494277e92a4$var$ORGAN_ORDER.filter((o)=>organSet.has(o));
7555
- const unknown = [
7556
- ...organSet
7557
- ].filter((o)=>!$cd8bc494277e92a4$var$ORGAN_ORDER.includes(o)).sort();
7558
7832
  return {
7559
- organs: [
7560
- ...known,
7561
- ...unknown
7562
- ],
7833
+ organs: (0, $4b8473139e48e77e$export$65382efdcdd96f8c)(organSet),
7563
7834
  byNode: byNode,
7564
7835
  nodeGene: nodeGene,
7565
7836
  maxTpm: {
@@ -7576,21 +7847,6 @@ function $cd8bc494277e92a4$var$rowHighlight(isSelected, isExactHover, isInHovere
7576
7847
  if (isInHoveredSubtree) return 'var(--tbrowse-row-subtree-bg)';
7577
7848
  return 'transparent';
7578
7849
  }
7579
- // Log-scaled fraction of v within [min,max], for the Max TPM heat background.
7580
- function $cd8bc494277e92a4$var$tpmFraction(v, range) {
7581
- if (!Number.isFinite(v)) return null;
7582
- const lo = Math.log10((range.min || 0) + 1);
7583
- const hi = Math.log10((range.max || 0) + 1);
7584
- if (hi <= lo) return 0.5;
7585
- return Math.max(0, Math.min(1, (Math.log10(v + 1) - lo) / (hi - lo)));
7586
- }
7587
- function $cd8bc494277e92a4$var$fmtTpm(v) {
7588
- if (!Number.isFinite(v)) return '';
7589
- return v >= 10 ? String(Math.round(v)) : String(Math.round(v * 10) / 10);
7590
- }
7591
- function $cd8bc494277e92a4$var$fmtClass(cls) {
7592
- return cls && cls.length ? cls.map((c)=>c.replace(/_/g, ' ')).join(', ') : null;
7593
- }
7594
7850
  const $cd8bc494277e92a4$var$gridBorder = '1px solid var(--tbrowse-grid-line, rgba(0,0,0,0.06))';
7595
7851
  const $cd8bc494277e92a4$var$stressWidth = (width, organCount)=>Math.max($cd8bc494277e92a4$var$STRESS_MIN, width - organCount * $cd8bc494277e92a4$var$ORGAN_CELL_W - $cd8bc494277e92a4$var$MAXTPM_W);
7596
7852
  const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState: setZoneState, width: width, data: data, hoveredNodeId: hoveredNodeId })=>{
@@ -7598,7 +7854,7 @@ const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState:
7598
7854
  const organs = ea.organs || [];
7599
7855
  const gid = hoveredNodeId && ea.nodeGene && ea.nodeGene[hoveredNodeId];
7600
7856
  const gene = hoveredNodeId && ea.byNode && ea.byNode[hoveredNodeId];
7601
- const clsText = gene && $cd8bc494277e92a4$var$fmtClass(gene.cls);
7857
+ const clsText = gene && (0, $4b8473139e48e77e$export$1f04174626f2ac64)(gene.cls);
7602
7858
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
7603
7859
  style: {
7604
7860
  height: '100%',
@@ -7634,12 +7890,12 @@ const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState:
7634
7890
  opacity: 0.85
7635
7891
  },
7636
7892
  title: "expression level",
7637
- children: $cd8bc494277e92a4$var$LEVEL_ORDER.map((lv)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
7638
- title: $cd8bc494277e92a4$var$LEVEL_LABEL[lv],
7893
+ children: (0, $4b8473139e48e77e$export$4bb936dc8856d37a).map((lv)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
7894
+ title: (0, $4b8473139e48e77e$export$433053e2c141d410)[lv],
7639
7895
  style: {
7640
7896
  width: 10,
7641
7897
  height: 10,
7642
- background: $cd8bc494277e92a4$var$LEVEL_COLOR[lv],
7898
+ background: (0, $4b8473139e48e77e$export$b352e11f7a2ac9c5)[lv],
7643
7899
  border: $cd8bc494277e92a4$var$gridBorder,
7644
7900
  display: 'inline-block'
7645
7901
  }
@@ -7682,7 +7938,7 @@ const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState:
7682
7938
  },
7683
7939
  children: [
7684
7940
  organs.map((o)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
7685
- title: o.replace(/_/g, ' '),
7941
+ title: (0, $4b8473139e48e77e$export$50c1f966fd3af83e)(o),
7686
7942
  style: {
7687
7943
  width: $cd8bc494277e92a4$var$ORGAN_CELL_W,
7688
7944
  fontSize: 8,
@@ -7692,7 +7948,7 @@ const $cd8bc494277e92a4$var$ExprHeader = ({ zoneState: zoneState, setZoneState:
7692
7948
  borderRight: $cd8bc494277e92a4$var$gridBorder,
7693
7949
  opacity: 0.8
7694
7950
  },
7695
- children: $cd8bc494277e92a4$var$abbr(o)
7951
+ children: (0, $4b8473139e48e77e$export$f8541c2790c3baeb)(o)
7696
7952
  }, o)),
7697
7953
  /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
7698
7954
  style: {
@@ -7724,17 +7980,17 @@ const $cd8bc494277e92a4$var$OrganCell = ({ organ: organ, gene: gene })=>{
7724
7980
  const level = gene && gene.organLevels[organ];
7725
7981
  const specific = !!(gene && gene.specificTo.has(organ));
7726
7982
  const enhanced = !!(gene && gene.enhancedIn.has(organ));
7727
- const title = level ? `${organ.replace(/_/g, ' ')}: ${$cd8bc494277e92a4$var$LEVEL_LABEL[level] || level}${specific ? " \xb7 specific" : enhanced ? " \xb7 enhanced" : ''}` : `${organ.replace(/_/g, ' ')}: not assayed`;
7983
+ const title = level ? `${(0, $4b8473139e48e77e$export$50c1f966fd3af83e)(organ)}: ${(0, $4b8473139e48e77e$export$433053e2c141d410)[level] || level}${specific ? " \xb7 specific" : enhanced ? " \xb7 enhanced" : ''}` : `${(0, $4b8473139e48e77e$export$50c1f966fd3af83e)(organ)}: not assayed`;
7728
7984
  const style = {
7729
7985
  position: 'relative',
7730
7986
  width: $cd8bc494277e92a4$var$ORGAN_CELL_W,
7731
7987
  height: '100%',
7732
7988
  boxSizing: 'border-box',
7733
- background: level ? $cd8bc494277e92a4$var$LEVEL_COLOR[level] || 'transparent' : 'transparent',
7989
+ background: level ? (0, $4b8473139e48e77e$export$b352e11f7a2ac9c5)[level] || 'transparent' : 'transparent',
7734
7990
  borderRight: $cd8bc494277e92a4$var$gridBorder
7735
7991
  };
7736
7992
  // enhanced-in: thin outline; specific-to (stronger): corner dot.
7737
- if (enhanced && !specific) style.boxShadow = `inset 0 0 0 1px ${$cd8bc494277e92a4$var$MARKER}`;
7993
+ if (enhanced && !specific) style.boxShadow = `inset 0 0 0 1px ${0, $4b8473139e48e77e$export$39a35029fe99c21}`;
7738
7994
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
7739
7995
  title: title,
7740
7996
  style: style,
@@ -7746,7 +8002,7 @@ const $cd8bc494277e92a4$var$OrganCell = ({ organ: organ, gene: gene })=>{
7746
8002
  width: 4,
7747
8003
  height: 4,
7748
8004
  borderRadius: '50%',
7749
- background: $cd8bc494277e92a4$var$MARKER
8005
+ background: (0, $4b8473139e48e77e$export$39a35029fe99c21)
7750
8006
  }
7751
8007
  })
7752
8008
  });
@@ -7765,8 +8021,8 @@ const $cd8bc494277e92a4$var$StressCell = ({ gene: gene, width: width })=>{
7765
8021
  padding: '0 3px',
7766
8022
  borderRadius: 2,
7767
8023
  whiteSpace: 'nowrap',
7768
- background: $cd8bc494277e92a4$var$STRESS[dir].bg,
7769
- color: $cd8bc494277e92a4$var$STRESS[dir].fg
8024
+ background: (0, $4b8473139e48e77e$export$c6dbe9be8c8b382e)[dir].bg,
8025
+ color: (0, $4b8473139e48e77e$export$c6dbe9be8c8b382e)[dir].fg
7770
8026
  },
7771
8027
  children: (dir === 'up' ? "\u2191" : "\u2193") + c
7772
8028
  }, key);
@@ -7809,7 +8065,7 @@ const $cd8bc494277e92a4$var$ExprBody = ({ visibleRows: visibleRows, rowRange: ro
7809
8065
  children: rows.map((r)=>{
7810
8066
  const gene = byNode[r.nodeId];
7811
8067
  const background = $cd8bc494277e92a4$var$rowHighlight(selectedNodeId === r.nodeId, hoveredNodeId === r.nodeId, !!(hoveredSubtreeIds && hoveredSubtreeIds.has(r.nodeId)));
7812
- const tpmFrac = gene ? $cd8bc494277e92a4$var$tpmFraction(gene.maxTpm, tpmRange) : null;
8068
+ const tpmFrac = gene ? (0, $4b8473139e48e77e$export$fc40d061e6c16645)(gene.maxTpm, tpmRange) : null;
7813
8069
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
7814
8070
  onMouseEnter: ()=>onHoverNode(r.nodeId),
7815
8071
  onMouseLeave: ()=>onHoverNode(null),
@@ -7847,7 +8103,7 @@ const $cd8bc494277e92a4$var$ExprBody = ({ visibleRows: visibleRows, rowRange: ro
7847
8103
  color: 'var(--tbrowse-text)',
7848
8104
  background: tpmFrac === null ? 'transparent' : `rgba(33, 102, 172, ${(0.1 + 0.65 * tpmFrac).toFixed(3)})`
7849
8105
  },
7850
- children: gene ? $cd8bc494277e92a4$var$fmtTpm(gene.maxTpm) : ''
8106
+ children: gene ? (0, $4b8473139e48e77e$export$8eaa32ef86afdd9c)(gene.maxTpm) : ''
7851
8107
  }),
7852
8108
  /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($cd8bc494277e92a4$var$StressCell, {
7853
8109
  gene: gene,
@@ -13517,19 +13773,18 @@ const $08e62a9eec4f328f$var$GroupNode = ({ group: group, depth: depth, catalog:
13517
13773
  ]
13518
13774
  });
13519
13775
  };
13520
- const $08e62a9eec4f328f$var$FieldTreeCmp = (props)=>{
13521
- const { fieldCatalog: catalog, exporterSelectedFields: exporterSelectedFields, doToggleExporterField: doToggleExporterField, doBulkSetExporterFields: doBulkSetExporterFields, query: query = '' } = props;
13776
+ const $08e62a9eec4f328f$export$8af8343a8541cf1f = ({ catalog: catalog, selectedFields: selectedFields, onToggle: onToggle, onBulkSet: onBulkSet, query: query = '' })=>{
13522
13777
  const [openMap, setOpenMap] = (0, $gXNCa$react.useState)({});
13523
- const selectedSet = (0, $gXNCa$react.useMemo)(()=>new Set(exporterSelectedFields), [
13524
- exporterSelectedFields
13778
+ const selectedSet = (0, $gXNCa$react.useMemo)(()=>new Set(selectedFields), [
13779
+ selectedFields
13525
13780
  ]);
13526
13781
  const handleToggle = (name)=>{
13527
13782
  const f = catalog && catalog.fields && catalog.fields[name];
13528
13783
  const linked = f && f.linkedFields;
13529
13784
  if (linked) {
13530
13785
  const allSelected = linked.every((n)=>selectedSet.has(n));
13531
- doBulkSetExporterFields(linked, !allSelected);
13532
- } else doToggleExporterField(name);
13786
+ onBulkSet(linked, !allSelected);
13787
+ } else onToggle(name);
13533
13788
  };
13534
13789
  const q = query.trim().toLowerCase();
13535
13790
  const filtered = (0, $gXNCa$react.useMemo)(()=>{
@@ -13559,7 +13814,7 @@ const $08e62a9eec4f328f$var$FieldTreeCmp = (props)=>{
13559
13814
  catalog: catalog,
13560
13815
  selectedSet: selectedSet,
13561
13816
  onToggle: handleToggle,
13562
- onBulkSet: doBulkSetExporterFields,
13817
+ onBulkSet: onBulkSet,
13563
13818
  openMap: openMap,
13564
13819
  setOpen: setOpen,
13565
13820
  forceOpen: forceOpen
@@ -13576,6 +13831,14 @@ const $08e62a9eec4f328f$var$FieldTreeCmp = (props)=>{
13576
13831
  })
13577
13832
  });
13578
13833
  };
13834
+ // Exporter-bound wrapper — preserves the existing default-export usage.
13835
+ const $08e62a9eec4f328f$var$FieldTreeCmp = (props)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($08e62a9eec4f328f$export$8af8343a8541cf1f, {
13836
+ catalog: props.fieldCatalog,
13837
+ selectedFields: props.exporterSelectedFields,
13838
+ onToggle: props.doToggleExporterField,
13839
+ onBulkSet: props.doBulkSetExporterFields,
13840
+ query: props.query
13841
+ });
13579
13842
  var $08e62a9eec4f328f$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectFieldCatalog', 'selectExporterSelectedFields', 'doToggleExporterField', 'doBulkSetExporterFields', $08e62a9eec4f328f$var$FieldTreeCmp);
13580
13843
 
13581
13844
 
@@ -16640,6 +16903,335 @@ var $1fd2507769d5bd00$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
16640
16903
 
16641
16904
 
16642
16905
 
16906
+
16907
+
16908
+
16909
+
16910
+
16911
+
16912
+ const { MAX_GENES: $8f8e530286a37ce9$var$MAX_GENES } = (0, $12feb7ca48271e17$export$d00a9fd01497b827);
16913
+ const $8f8e530286a37ce9$var$DEFAULT_COL_DEF = {
16914
+ resizable: true,
16915
+ sortable: true,
16916
+ filter: false,
16917
+ suppressHeaderMenuButton: true
16918
+ };
16919
+ // ↑activated / ↓repressed condition chips, matching the tbrowse Expression zone.
16920
+ const $8f8e530286a37ce9$var$StressCell = ({ value: value })=>{
16921
+ const { up: up = [], down: down = [] } = value || {};
16922
+ if (!up.length && !down.length) return null;
16923
+ const chip = (c, dir, key)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
16924
+ style: {
16925
+ fontSize: 10,
16926
+ lineHeight: '16px',
16927
+ padding: '0 4px',
16928
+ borderRadius: 2,
16929
+ marginRight: 3,
16930
+ whiteSpace: 'nowrap',
16931
+ background: (0, $4b8473139e48e77e$export$c6dbe9be8c8b382e)[dir].bg,
16932
+ color: (0, $4b8473139e48e77e$export$c6dbe9be8c8b382e)[dir].fg
16933
+ },
16934
+ children: (dir === 'up' ? "\u2191" : "\u2193") + c
16935
+ }, key);
16936
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
16937
+ children: [
16938
+ up.map((c, i)=>chip(c, 'up', `u${i}`)),
16939
+ down.map((c, i)=>chip(c, 'down', `d${i}`))
16940
+ ]
16941
+ });
16942
+ };
16943
+ const $8f8e530286a37ce9$var$AttrTableViewCmp = (props)=>{
16944
+ const { attrTable: attrTable, fieldCatalog: fieldCatalog, fieldCatalogByName: fieldCatalogByName, doToggleAttrTableField: doToggleAttrTableField, doBulkSetAttrTableFields: doBulkSetAttrTableFields } = props;
16945
+ const [showColumns, setShowColumns] = (0, $gXNCa$react.useState)(false);
16946
+ const [fieldQuery, setFieldQuery] = (0, $gXNCa$react.useState)('');
16947
+ const { docs: docs, total: total, truncated: truncated, status: status, error: error, selectedFields: selectedFields } = attrTable;
16948
+ // Row objects + the organ union and TPM range needed to build the heatmap.
16949
+ const { rows: rows, organs: organs, tpmRange: tpmRange } = (0, $gXNCa$react.useMemo)(()=>{
16950
+ const organSet = new Set();
16951
+ let tpmMin = Infinity;
16952
+ let tpmMax = -Infinity;
16953
+ const out = (docs || []).map((d)=>{
16954
+ const a = (0, $4b8473139e48e77e$export$69fb5d19b5db1cfa)(d);
16955
+ Object.keys(a.organLevels).forEach((o)=>organSet.add(o));
16956
+ if (a.maxTpm !== null) {
16957
+ if (a.maxTpm < tpmMin) tpmMin = a.maxTpm;
16958
+ if (a.maxTpm > tpmMax) tpmMax = a.maxTpm;
16959
+ }
16960
+ const row = {
16961
+ id: d.id,
16962
+ name: d.name,
16963
+ system_name: d.system_name,
16964
+ biotype: d.biotype,
16965
+ location: d.region ? `${d.region}:${d.start}-${d.end}` : '',
16966
+ _cls: (0, $4b8473139e48e77e$export$1f04174626f2ac64)(a.cls) || '',
16967
+ _tau: a.tau,
16968
+ _maxTpm: a.maxTpm,
16969
+ _stress: {
16970
+ up: a.activatedBy,
16971
+ down: a.repressedBy
16972
+ },
16973
+ _organ: a.organLevels,
16974
+ _specific: a.specificTo,
16975
+ _enhanced: a.enhancedIn
16976
+ };
16977
+ (selectedFields || []).forEach((f)=>{
16978
+ row[f] = d[f];
16979
+ });
16980
+ return row;
16981
+ });
16982
+ return {
16983
+ rows: out,
16984
+ organs: (0, $4b8473139e48e77e$export$65382efdcdd96f8c)(organSet),
16985
+ tpmRange: {
16986
+ min: tpmMin === Infinity ? 0 : tpmMin,
16987
+ max: tpmMax === -Infinity ? 0 : tpmMax
16988
+ }
16989
+ };
16990
+ }, [
16991
+ docs,
16992
+ selectedFields
16993
+ ]);
16994
+ const columnDefs = (0, $gXNCa$react.useMemo)(()=>{
16995
+ const cols = [
16996
+ {
16997
+ colId: 'id',
16998
+ field: 'id',
16999
+ headerName: 'Gene ID',
17000
+ pinned: 'left',
17001
+ width: 190
17002
+ },
17003
+ {
17004
+ colId: 'name',
17005
+ field: 'name',
17006
+ headerName: 'Name',
17007
+ pinned: 'left',
17008
+ width: 130
17009
+ },
17010
+ {
17011
+ colId: 'system_name',
17012
+ field: 'system_name',
17013
+ headerName: 'Species',
17014
+ width: 150
17015
+ },
17016
+ {
17017
+ colId: 'biotype',
17018
+ field: 'biotype',
17019
+ headerName: 'Biotype',
17020
+ width: 120
17021
+ },
17022
+ {
17023
+ colId: 'location',
17024
+ field: 'location',
17025
+ headerName: 'Location',
17026
+ width: 150
17027
+ },
17028
+ {
17029
+ colId: '_cls',
17030
+ field: '_cls',
17031
+ headerName: 'Expression class',
17032
+ width: 150
17033
+ },
17034
+ {
17035
+ colId: '_tau',
17036
+ field: '_tau',
17037
+ headerName: 'Tau',
17038
+ width: 80,
17039
+ type: 'numericColumn',
17040
+ valueFormatter: (p)=>Number.isFinite(p.value) ? p.value.toFixed(3) : ''
17041
+ },
17042
+ {
17043
+ colId: '_maxTpm',
17044
+ field: '_maxTpm',
17045
+ headerName: 'Max TPM',
17046
+ width: 100,
17047
+ type: 'numericColumn',
17048
+ valueFormatter: (p)=>(0, $4b8473139e48e77e$export$8eaa32ef86afdd9c)(p.value),
17049
+ cellStyle: (p)=>({
17050
+ background: (0, $4b8473139e48e77e$export$4b443417c5259620)(p.value, tpmRange)
17051
+ })
17052
+ },
17053
+ {
17054
+ colId: '_stress',
17055
+ field: '_stress',
17056
+ headerName: 'Stress',
17057
+ width: 220,
17058
+ sortable: false,
17059
+ // The renderer draws the chips; the formatter just keeps ag-grid from
17060
+ // warning about an object-valued cell with no formatter.
17061
+ valueFormatter: ()=>'',
17062
+ cellRenderer: $8f8e530286a37ce9$var$StressCell
17063
+ }
17064
+ ];
17065
+ // Per-organ heatmap: one narrow, colour-only column per organ.
17066
+ organs.forEach((o)=>{
17067
+ cols.push({
17068
+ // No ':' in colId — ag-grid uses colId in internal CSS selectors, where a
17069
+ // colon is a metacharacter and silently breaks rendering of the column.
17070
+ colId: `organ_${o}`,
17071
+ headerName: (0, $4b8473139e48e77e$export$f8541c2790c3baeb)(o),
17072
+ headerTooltip: (0, $4b8473139e48e77e$export$50c1f966fd3af83e)(o),
17073
+ width: 46,
17074
+ valueGetter: (p)=>p.data && p.data._organ[o] || '',
17075
+ valueFormatter: ()=>'',
17076
+ tooltipValueGetter: (p)=>{
17077
+ const lvl = p.data && p.data._organ[o];
17078
+ if (!lvl) return `${(0, $4b8473139e48e77e$export$50c1f966fd3af83e)(o)}: not assayed`;
17079
+ const sp = p.data._specific.has(o) ? " \xb7 specific" : p.data._enhanced.has(o) ? " \xb7 enhanced" : '';
17080
+ return `${(0, $4b8473139e48e77e$export$50c1f966fd3af83e)(o)}: ${(0, $4b8473139e48e77e$export$433053e2c141d410)[lvl] || lvl}${sp}`;
17081
+ },
17082
+ comparator: (a, b)=>((0, $4b8473139e48e77e$export$a61929d6d484c6d4)[a] ?? -1) - ((0, $4b8473139e48e77e$export$a61929d6d484c6d4)[b] ?? -1),
17083
+ cellStyle: (p)=>{
17084
+ const lvl = p.value;
17085
+ const style = {
17086
+ background: lvl ? (0, $4b8473139e48e77e$export$b352e11f7a2ac9c5)[lvl] || 'transparent' : 'transparent'
17087
+ };
17088
+ if (p.data && p.data._specific.has(o)) style.boxShadow = `inset 0 0 0 2px ${0, $4b8473139e48e77e$export$39a35029fe99c21}`;
17089
+ else if (p.data && p.data._enhanced.has(o)) style.boxShadow = `inset 0 0 0 1px ${0, $4b8473139e48e77e$export$39a35029fe99c21}`;
17090
+ return style;
17091
+ }
17092
+ });
17093
+ });
17094
+ // Extra attribute columns chosen from the field catalog.
17095
+ (selectedFields || []).forEach((f)=>{
17096
+ const meta = fieldCatalogByName && fieldCatalogByName[f] || {};
17097
+ cols.push({
17098
+ colId: f,
17099
+ field: f,
17100
+ headerName: meta.label || f,
17101
+ headerTooltip: f,
17102
+ width: 170,
17103
+ valueFormatter: (p)=>Array.isArray(p.value) ? p.value.join(', ') : p.value ?? ''
17104
+ });
17105
+ });
17106
+ return cols;
17107
+ }, [
17108
+ organs,
17109
+ selectedFields,
17110
+ fieldCatalogByName,
17111
+ tpmRange
17112
+ ]);
17113
+ const shown = rows.length;
17114
+ const loading = status === 'loading';
17115
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
17116
+ className: "attrtable-view",
17117
+ children: [
17118
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
17119
+ className: "attrtable-toolbar",
17120
+ children: [
17121
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
17122
+ className: "attrtable-status",
17123
+ children: [
17124
+ loading && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Spinner), {
17125
+ animation: "border",
17126
+ size: "sm",
17127
+ className: "mr-2"
17128
+ }),
17129
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("strong", {
17130
+ children: shown.toLocaleString()
17131
+ }),
17132
+ " gene",
17133
+ shown === 1 ? '' : 's',
17134
+ total > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
17135
+ children: [
17136
+ " of ",
17137
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("strong", {
17138
+ children: total.toLocaleString()
17139
+ })
17140
+ ]
17141
+ }),
17142
+ loading && " \u2014 loading\u2026"
17143
+ ]
17144
+ }),
17145
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
17146
+ className: "attrtable-legend",
17147
+ title: "expression level",
17148
+ children: (0, $4b8473139e48e77e$export$4bb936dc8856d37a).map((lv)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
17149
+ title: (0, $4b8473139e48e77e$export$433053e2c141d410)[lv],
17150
+ style: {
17151
+ background: (0, $4b8473139e48e77e$export$b352e11f7a2ac9c5)[lv]
17152
+ }
17153
+ }, lv))
17154
+ }),
17155
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Button), {
17156
+ size: "sm",
17157
+ variant: showColumns ? 'primary' : 'outline-secondary',
17158
+ onClick: ()=>setShowColumns((v)=>!v),
17159
+ children: [
17160
+ "Columns",
17161
+ selectedFields.length ? ` (${selectedFields.length})` : ''
17162
+ ]
17163
+ })
17164
+ ]
17165
+ }),
17166
+ truncated && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Alert), {
17167
+ variant: "info",
17168
+ className: "attrtable-notice",
17169
+ children: [
17170
+ "Showing the first ",
17171
+ $8f8e530286a37ce9$var$MAX_GENES.toLocaleString(),
17172
+ " of ",
17173
+ total.toLocaleString(),
17174
+ " genes. Narrow your search to see the rest."
17175
+ ]
17176
+ }),
17177
+ error && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Alert), {
17178
+ variant: "danger",
17179
+ className: "attrtable-notice",
17180
+ children: [
17181
+ "Failed to load genes: ",
17182
+ error
17183
+ ]
17184
+ }),
17185
+ showColumns && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
17186
+ className: "attrtable-columns-panel",
17187
+ children: [
17188
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("input", {
17189
+ type: "search",
17190
+ className: "form-control form-control-sm attrtable-field-search",
17191
+ placeholder: "Search fields\u2026",
17192
+ value: fieldQuery,
17193
+ onChange: (e)=>setFieldQuery(e.target.value)
17194
+ }),
17195
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $08e62a9eec4f328f$export$8af8343a8541cf1f), {
17196
+ catalog: fieldCatalog,
17197
+ selectedFields: selectedFields,
17198
+ onToggle: doToggleAttrTableField,
17199
+ onBulkSet: doBulkSetAttrTableFields,
17200
+ query: fieldQuery
17201
+ })
17202
+ ]
17203
+ }),
17204
+ rows.length === 0 && !loading ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
17205
+ className: "attrtable-empty",
17206
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
17207
+ children: "No genes to show."
17208
+ })
17209
+ }) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
17210
+ className: "ag-theme-quartz attrtable-aggrid",
17211
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$aggridreact.AgGridReact), {
17212
+ rowData: rows,
17213
+ columnDefs: columnDefs,
17214
+ defaultColDef: $8f8e530286a37ce9$var$DEFAULT_COL_DEF,
17215
+ animateRows: false,
17216
+ suppressFieldDotNotation: true,
17217
+ suppressDragLeaveHidesColumns: true,
17218
+ tooltipShowDelay: 300,
17219
+ rowHeight: 22,
17220
+ headerHeight: 26
17221
+ })
17222
+ })
17223
+ ]
17224
+ });
17225
+ };
17226
+ var $8f8e530286a37ce9$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectAttrTable', 'selectFieldCatalog', 'selectFieldCatalogByName', 'doToggleAttrTableField', 'doBulkSetAttrTableFields', $8f8e530286a37ce9$var$AttrTableViewCmp);
17227
+
17228
+
17229
+
17230
+
17231
+
17232
+
17233
+
17234
+
16643
17235
  function $597fe213417ee6ca$var$speciesTaxonId(tid) {
16644
17236
  const n = +tid;
16645
17237
  return n > 1000000 ? Math.floor(n / 1000) : n;
@@ -19198,6 +19790,7 @@ const $693dd8c7a5607c3a$var$inventory = {
19198
19790
  attribs: (0, $67bf5a43401bffdc$export$2e2bcd8739ae039),
19199
19791
  expression: (0, $261baeb81c4d4d8a$export$2e2bcd8739ae039),
19200
19792
  exprViz: (0, $1fd2507769d5bd00$export$2e2bcd8739ae039),
19793
+ attrTable: (0, $8f8e530286a37ce9$export$2e2bcd8739ae039),
19201
19794
  ontologyEnrichment: (0, $597fe213417ee6ca$export$2e2bcd8739ae039),
19202
19795
  userLists: (0, $0f50f369018a42ef$export$2e2bcd8739ae039),
19203
19796
  export: (0, $37b3bb0145d266b0$export$2e2bcd8739ae039)