gramene-search 2.23.0 → 2.24.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
@@ -31,6 +31,7 @@ var $gXNCa$reactsimpletreemenu = require("react-simple-tree-menu");
31
31
  require("@fortawesome/fontawesome-free/css/all.min.css");
32
32
  var $gXNCa$gramenedbxrefs = require("gramene-dbxrefs");
33
33
  var $gXNCa$reacticonsai = require("react-icons/ai");
34
+ var $gXNCa$grameneprimers = require("gramene-primers");
34
35
  var $gXNCa$gramenesearchvis = require("gramene-search-vis");
35
36
  var $gXNCa$numeral = require("numeral");
36
37
  var $gXNCa$firebaseauth = require("firebase/auth");
@@ -6280,6 +6281,15 @@ var $d365d8c287ab0c94$export$2e2bcd8739ae039 = $d365d8c287ab0c94$var$ontologyEnr
6280
6281
  // ('gene'|'paralogs'|'eFP'), atlasExperiment (selected GXA experiment id),
6281
6282
  // barStudy (selected eFP/BAR study). Driven in controlled mode from this so a
6282
6283
  // saved view restores the chosen sub-tab and study.
6284
+ // - primers: the Primers detail's PrimerDesignerState (gramene-primers), a
6285
+ // serializable {v: 1, mode, transcriptId, flankUp, flankDown, region,
6286
+ // target, included, excluded, junctionSpanning, avoidRepeats, preset,
6287
+ // params, designed, selectedRank, checkedRanks, check: {checks, genomes,
6288
+ // params, jobId, submitted}, view}. Replaced wholesale on every change and
6289
+ // driven in controlled mode, so a saved view re-runs the design and
6290
+ // re-attaches to the check job. A pasted `sequence` is kept here so it
6291
+ // survives a detail-tab switch; viewSnapshot drops it from saved views.
6292
+ // Design responses and check results are never stored.
6283
6293
  //
6284
6294
  // What does NOT live here:
6285
6295
  // - derived/computed state like `details` (per-render config from
@@ -6403,6 +6413,12 @@ const $7f865ea0feda21af$var$uiViewState = {
6403
6413
  // payload: { geneId, patch } -- merges into the expression slice
6404
6414
  // (e.g. { activeTab }, { atlasExperiment }, { barStudy })
6405
6415
  return $7f865ea0feda21af$var$setExpression(state, payload.geneId, payload.patch);
6416
+ case 'UI_PRIMERS_SET':
6417
+ // payload: { geneId, state } -- replaces the primers slice with the
6418
+ // full PrimerDesignerState emitted by <PrimerDesigner> (not merged)
6419
+ return $7f865ea0feda21af$var$setGene(state, payload.geneId, {
6420
+ primers: payload.state
6421
+ });
6406
6422
  case 'UI_VIEW_STATE_REPLACED':
6407
6423
  // payload: { byGene } -- used by the snapshot loader in a later phase
6408
6424
  return {
@@ -6476,6 +6492,15 @@ const $7f865ea0feda21af$var$uiViewState = {
6476
6492
  }
6477
6493
  });
6478
6494
  },
6495
+ doSetPrimersState: ({ geneId: geneId, state: state })=>({ dispatch: dispatch })=>{
6496
+ dispatch({
6497
+ type: 'UI_PRIMERS_SET',
6498
+ payload: {
6499
+ geneId: geneId,
6500
+ state: state
6501
+ }
6502
+ });
6503
+ },
6479
6504
  doReplaceUiViewState: (byGene)=>({ dispatch: dispatch })=>{
6480
6505
  dispatch({
6481
6506
  type: 'UI_VIEW_STATE_REPLACED',
@@ -6567,7 +6592,9 @@ var $6048209b532f201d$export$2e2bcd8739ae039 = $6048209b532f201d$var$taxTreeView
6567
6592
  // fullscreen: boolean,
6568
6593
  // homology: { viewer, height, tbrowse: <ViewState> } | undefined,
6569
6594
  // sequences: { tab, tid, upstream, downstream } | undefined,
6570
- // expression: { activeTab, atlasExperiment, barStudy } | undefined
6595
+ // expression: { activeTab, atlasExperiment, barStudy } | undefined,
6596
+ // primers: <gramene-primers PrimerDesignerState, {v: 1, ...}, without
6597
+ // the pasted `sequence`> | undefined
6571
6598
  // }, ...
6572
6599
  // ]
6573
6600
  // }
@@ -6622,8 +6649,18 @@ const $472fe3745b238881$var$isMeaningfulGene = (entry)=>{
6622
6649
  if (entry.homology && (entry.homology.viewer !== undefined || entry.homology.height !== undefined || entry.homology.tbrowse !== undefined)) return true;
6623
6650
  if (entry.sequences && Object.keys(entry.sequences).length) return true;
6624
6651
  if (entry.expression && Object.keys(entry.expression).length) return true;
6652
+ if (entry.primers && Object.keys(entry.primers).length) return true;
6625
6653
  return false;
6626
6654
  };
6655
+ // The Primers slice keeps a pasted sequence in memory (so it survives a detail
6656
+ // tab switch), but a saved view must not carry it: it can be long and is the
6657
+ // user's own data. A restored sequence-mode design then waits for a new paste
6658
+ // (gramene-primers does not re-run a design whose inputs are incomplete).
6659
+ const $472fe3745b238881$var$snapshotPrimers = (primers)=>{
6660
+ if (!primers || !Object.keys(primers).length) return undefined;
6661
+ const { sequence: sequence, ...rest } = primers;
6662
+ return rest;
6663
+ };
6627
6664
  const $472fe3745b238881$var$viewSnapshot = {
6628
6665
  name: 'viewSnapshot',
6629
6666
  // No reducer — this bundle is pure derivation + dispatch orchestration.
@@ -6687,6 +6724,9 @@ const $472fe3745b238881$var$viewSnapshot = {
6687
6724
  } : {},
6688
6725
  expression: e.expression ? {
6689
6726
  ...e.expression
6727
+ } : {},
6728
+ primers: e.primers ? {
6729
+ ...e.primers
6690
6730
  } : {}
6691
6731
  };
6692
6732
  }
@@ -6759,7 +6799,8 @@ function $472fe3745b238881$var$buildSnapshot(state) {
6759
6799
  } : undefined,
6760
6800
  expression: e.expression && Object.keys(e.expression).length ? {
6761
6801
  ...e.expression
6762
- } : undefined
6802
+ } : undefined,
6803
+ primers: $472fe3745b238881$var$snapshotPrimers(e.primers)
6763
6804
  }));
6764
6805
  else snap.expandedDetails = [];
6765
6806
  // Expression-visualization view config. Capture only the selection/config
@@ -10206,6 +10247,21 @@ var $54c74a4689d5a778$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
10206
10247
 
10207
10248
 
10208
10249
 
10250
+ // Grid row height. Overrides the ag-theme-quartz default (42px) via the
10251
+ // `rowHeight` prop, and drives the container height calc below so the two
10252
+ // can't drift apart.
10253
+ const $283508ffcf8a47c4$var$ROW_HEIGHT = 36;
10254
+ // Header allowance in the container height calc (quartz header ≈ 49px).
10255
+ const $283508ffcf8a47c4$var$HEADER_HEIGHT_ALLOWANCE = 50;
10256
+ // Compact "Search" button so it sits comfortably inside a ROW_HEIGHT row —
10257
+ // react-bootstrap's `size="sm"` alone is ~31px, which crowds a 36px row.
10258
+ // Bump padding/fontSize here if the button should be taller.
10259
+ const $283508ffcf8a47c4$var$SEARCH_BUTTON_STYLE = {
10260
+ whiteSpace: 'nowrap',
10261
+ lineHeight: 1.4,
10262
+ padding: '1px 8px',
10263
+ fontSize: 12
10264
+ };
10209
10265
  const $283508ffcf8a47c4$var$ggURL = {
10210
10266
  xIRRI: 'https://gringlobal.irri.org/gringlobal/accessiondetail?id=',
10211
10267
  IRRI: 'https://www.irri.org/genesys-rice#/a/',
@@ -10500,6 +10556,7 @@ const $283508ffcf8a47c4$var$GridWithGroups = ({ groups: groups, gene_id: gene_id
10500
10556
  currentURL.searchParams.set('name', accession.germplasm.ens_id);
10501
10557
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
10502
10558
  size: "sm",
10559
+ style: $283508ffcf8a47c4$var$SEARCH_BUTTON_STYLE,
10503
10560
  href: currentURL.toString(),
10504
10561
  children: "Search"
10505
10562
  });
@@ -10519,7 +10576,7 @@ const $283508ffcf8a47c4$var$GridWithGroups = ({ groups: groups, gene_id: gene_id
10519
10576
  }
10520
10577
  };
10521
10578
  const nVisible = getVisibleRowData().length;
10522
- const tableHeight = 50 + Math.min(nVisible, 10) * 42;
10579
+ const tableHeight = $283508ffcf8a47c4$var$HEADER_HEIGHT_ALLOWANCE + Math.min(nVisible, 10) * $283508ffcf8a47c4$var$ROW_HEIGHT;
10523
10580
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
10524
10581
  style: {
10525
10582
  overflowX: "auto",
@@ -10537,8 +10594,20 @@ const $283508ffcf8a47c4$var$GridWithGroups = ({ groups: groups, gene_id: gene_id
10537
10594
  rowData: getVisibleRowData(),
10538
10595
  columnDefs: columnDefs,
10539
10596
  getRowNodeId: (data)=>data.id,
10540
- animateRows: true,
10541
- defaultColDef: defaultColDef
10597
+ rowHeight: $283508ffcf8a47c4$var$ROW_HEIGHT,
10598
+ defaultColDef: defaultColDef,
10599
+ // ag-grid sets `user-select: none` on cells by default, which is why
10600
+ // accession / consequence text couldn't be selected and copied.
10601
+ // `enableCellTextSelection` restores native selection; `ensureDomOrder`
10602
+ // keeps DOM order matching visual order so a multi-row copy comes out
10603
+ // as displayed rather than scrambled by row virtualization/sorting.
10604
+ //
10605
+ // NB: ag-grid's isAnimateRows() returns false whenever ensureDomOrder
10606
+ // is set, so `animateRows` was removed rather than left as dead config.
10607
+ // Correct copy order matters more here than row-slide animation; drop
10608
+ // `ensureDomOrder` and restore `animateRows={true}` to trade back.
10609
+ enableCellTextSelection: true,
10610
+ ensureDomOrder: true
10542
10611
  })
10543
10612
  })
10544
10613
  });
@@ -11416,6 +11485,80 @@ var $527ebc19dc92444d$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
11416
11485
 
11417
11486
 
11418
11487
 
11488
+ // Primers detail: PCR/qPCR primer design, genome specificity and pan-genome
11489
+ // coverage checks (gramene-primers <PrimerDesigner>, backed by the
11490
+ // gramene-swagger /primers endpoints).
11491
+ //
11492
+ // Enabled per site by config.details.primers:
11493
+ // true use the site's grameneData API
11494
+ // { apiBase, pangenome } apiBase: a swagger that serves /primers;
11495
+ // pangenome: false hides the pan-genome check
11496
+ //
11497
+ // The designer's serializable state ({v: 1, ...}) lives in the uiViewState
11498
+ // bundle keyed by geneId (controlled mode), so a saved view restores the
11499
+ // inputs, re-runs the design and re-attaches to a submitted check (an expired
11500
+ // job shows "Re-run check"). The store keeps a pasted sequence, so switching
11501
+ // detail tabs (which unmounts this component) does not lose it; viewSnapshot
11502
+ // leaves it out of saved views. Styles are injected at runtime by the
11503
+ // component; no CSS import is needed.
11504
+ const $88d24ae9beb83d5d$var$MODES = [
11505
+ 'gene',
11506
+ 'transcript',
11507
+ 'region',
11508
+ 'sequence'
11509
+ ];
11510
+ const $88d24ae9beb83d5d$var$geneHref = (id)=>`?idList=${encodeURIComponent(id)}`;
11511
+ // Plain left clicks on gene links in check results open a new tab, so the
11512
+ // design and a running check's job id survive; modified clicks use geneHref.
11513
+ const $88d24ae9beb83d5d$var$openGene = (id)=>window.open($88d24ae9beb83d5d$var$geneHref(id), '_blank', 'noopener');
11514
+ const $88d24ae9beb83d5d$var$Primers = (props)=>{
11515
+ const geneId = props.searchResult.id;
11516
+ const gene = props.geneDocs[geneId] // Gene.ensureGene() guarantees it is loaded
11517
+ ;
11518
+ const conf = props.config.details.primers // true | { apiBase?, pangenome? }
11519
+ ;
11520
+ const apiBase = conf && typeof conf === 'object' && conf.apiBase || props.grameneAPI;
11521
+ const byGene = props.uiViewState && props.uiViewState.byGene && props.uiViewState.byGene[geneId];
11522
+ const saved = byGene && byGene.primers;
11523
+ const { doSetPrimersState: doSetPrimersState } = props;
11524
+ const onStateChange = (0, $gXNCa$react.useCallback)((state)=>doSetPrimersState({
11525
+ geneId: geneId,
11526
+ state: state
11527
+ }), [
11528
+ geneId,
11529
+ doSetPrimersState
11530
+ ]);
11531
+ if (!conf) // e.g. a saved view opened on a site that does not enable the tab
11532
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("p", {
11533
+ children: "Primer design is not enabled on this site."
11534
+ });
11535
+ if (!gene) return null;
11536
+ return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$grameneprimers.PrimerDesigner), {
11537
+ apiBase: apiBase,
11538
+ gene: gene,
11539
+ systemName: gene.system_name,
11540
+ modes: $88d24ae9beb83d5d$var$MODES,
11541
+ defaultMode: "gene",
11542
+ // an empty slice (never visited, or a snapshot without primers) leaves
11543
+ // the designer uncontrolled until its first change is stored
11544
+ state: saved && Object.keys(saved).length ? saved : undefined,
11545
+ onStateChange: onStateChange,
11546
+ features: {
11547
+ pangenome: !(conf && conf.pangenome === false)
11548
+ },
11549
+ geneHref: $88d24ae9beb83d5d$var$geneHref,
11550
+ onGeneClick: $88d24ae9beb83d5d$var$openGene,
11551
+ geneLabel: gene.name || gene._id,
11552
+ theme: "light"
11553
+ }, geneId);
11554
+ };
11555
+ var $88d24ae9beb83d5d$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneAPI', 'selectUiViewState', 'doSetPrimersState', $88d24ae9beb83d5d$var$Primers);
11556
+
11557
+
11558
+
11559
+
11560
+
11561
+
11419
11562
 
11420
11563
  let $6c5c4f90059875bf$var$external = /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("small", {
11421
11564
  title: "This link opens a page from an external site",
@@ -11428,6 +11571,7 @@ let $6c5c4f90059875bf$var$external = /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.js
11428
11571
  });
11429
11572
  let $6c5c4f90059875bf$var$inventory = {
11430
11573
  sequences: (0, $527ebc19dc92444d$export$2e2bcd8739ae039),
11574
+ primers: (0, $88d24ae9beb83d5d$export$2e2bcd8739ae039),
11431
11575
  location: (0, $264d7db426cab787$export$2e2bcd8739ae039),
11432
11576
  expression: (0, $9e29a4f60318db7a$export$2e2bcd8739ae039),
11433
11577
  homology: (0, $64fad37f770d2bfe$export$2e2bcd8739ae039),
@@ -11559,6 +11703,12 @@ const $6c5c4f90059875bf$var$allDetails = [
11559
11703
  popup: 'Gene/cDNA/protein fasta',
11560
11704
  available: true
11561
11705
  },
11706
+ {
11707
+ id: 'primers',
11708
+ label: 'Primers',
11709
+ popup: 'Design PCR/qPCR primers; check specificity and pan-genome coverage',
11710
+ available: true
11711
+ },
11562
11712
  {
11563
11713
  id: 'location',
11564
11714
  label: 'Location',
@@ -11746,13 +11896,7 @@ class $6c5c4f90059875bf$var$Gene extends (0, ($parcel$interopDefault($gXNCa$reac
11746
11896
  children: [
11747
11897
  d.label,
11748
11898
  isExpanded && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsArrowsFullscreen), {
11749
- style: {
11750
- position: 'absolute',
11751
- right: 8,
11752
- top: '50%',
11753
- transform: 'translateY(-50%)',
11754
- cursor: 'pointer'
11755
- },
11899
+ className: "gene-detail-tab-fullscreen",
11756
11900
  title: "View full screen",
11757
11901
  onClick: (e)=>{
11758
11902
  e.stopPropagation();