gramene-search 2.20.2 → 2.22.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.css +63 -0
- package/dist/index.css.map +1 -1
- package/dist/index.js +547 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -849,6 +849,43 @@ function $af4441dd29af05df$var$markSubtree(node, source, invert) {
|
|
|
849
849
|
$af4441dd29af05df$var$markSubtree(child, source, invert);
|
|
850
850
|
});
|
|
851
851
|
}
|
|
852
|
+
const $af4441dd29af05df$export$6b95404109a3e2ec = 'Genomic intervals';
|
|
853
|
+
function $af4441dd29af05df$export$634f7214da1b780a({ map: map, region: region, start: start, end: end }) {
|
|
854
|
+
return `(map:${map} AND region:${region}` + ` AND start:[* TO ${end}] AND end:[${start} TO *])`;
|
|
855
|
+
}
|
|
856
|
+
function $af4441dd29af05df$export$535c09441f37845d({ mapLabel: mapLabel, map: map, region: region, start: start, end: end }) {
|
|
857
|
+
return `${mapLabel || map} ${region}:${Number(start).toLocaleString()}-${Number(end).toLocaleString()}`;
|
|
858
|
+
}
|
|
859
|
+
// Note the field order is inverted relative to the interval: `start` carries the
|
|
860
|
+
// interval's END and `end` carries its START, because the pair is an overlap test.
|
|
861
|
+
const $af4441dd29af05df$var$INTERVAL_RE = /^\(map:(\S+) AND region:(\S+) AND start:\[\* TO (\d+)\] AND end:\[(\d+) TO \*\]\)$/;
|
|
862
|
+
function $af4441dd29af05df$export$67920d3e3cb3f136(fqValue) {
|
|
863
|
+
const m = $af4441dd29af05df$var$INTERVAL_RE.exec(String(fqValue || ''));
|
|
864
|
+
if (!m) return null;
|
|
865
|
+
return {
|
|
866
|
+
map: m[1],
|
|
867
|
+
region: m[2],
|
|
868
|
+
end: Number(m[3]),
|
|
869
|
+
start: Number(m[4])
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
/** `[lo TO hi]`, as produced by the expression brush and the Location tab. */ const $af4441dd29af05df$var$RANGE_RE = /^\[\s*(\S+)\s+TO\s+(\S+)\s*\]$/;
|
|
873
|
+
function $af4441dd29af05df$export$9ee0ceaa589fc567(fqValue) {
|
|
874
|
+
const m = $af4441dd29af05df$var$RANGE_RE.exec(String(fqValue || ''));
|
|
875
|
+
return m ? {
|
|
876
|
+
lo: m[1],
|
|
877
|
+
hi: m[2]
|
|
878
|
+
} : null;
|
|
879
|
+
}
|
|
880
|
+
function $af4441dd29af05df$export$cd9b2ffd001c6d65(lo, hi) {
|
|
881
|
+
return `[${lo} TO ${hi}]`;
|
|
882
|
+
}
|
|
883
|
+
function $af4441dd29af05df$export$a7f4fa59930e5088(node) {
|
|
884
|
+
if (!node || node.hasOwnProperty('children')) return null;
|
|
885
|
+
if ($af4441dd29af05df$export$67920d3e3cb3f136(node.fq_value)) return 'interval';
|
|
886
|
+
if ($af4441dd29af05df$export$9ee0ceaa589fc567(node.fq_value)) return 'range';
|
|
887
|
+
return 'value';
|
|
888
|
+
}
|
|
852
889
|
const $af4441dd29af05df$export$da3188ffd2da2525 = {
|
|
853
890
|
orthologs: {
|
|
854
891
|
label: 'orthologs',
|
|
@@ -1037,6 +1074,81 @@ const $af4441dd29af05df$var$grameneFilters = {
|
|
|
1037
1074
|
return newState;
|
|
1038
1075
|
}
|
|
1039
1076
|
break;
|
|
1077
|
+
case 'GRAMENE_FILTER_INTERVAL_ADDED':
|
|
1078
|
+
{
|
|
1079
|
+
// Intervals accumulate into a single OR group, because ANDing two
|
|
1080
|
+
// disjoint intervals matches nothing — no gene sits in both. The group
|
|
1081
|
+
// is tagged with `category`, one of the keys viewSnapshot keeps, so a
|
|
1082
|
+
// restored view merges into it instead of starting a second group; were
|
|
1083
|
+
// that tag ever lost the worst case is an extra group, never a corrupt
|
|
1084
|
+
// tree.
|
|
1085
|
+
newState = Object.assign({}, state, {
|
|
1086
|
+
status: 'search',
|
|
1087
|
+
showMarked: true,
|
|
1088
|
+
searchOffset: 0
|
|
1089
|
+
});
|
|
1090
|
+
const { map: map, mapLabel: mapLabel, region: region, start: start, end: end } = payload;
|
|
1091
|
+
// One leaf per interval, not four. getQuery has a raw-passthrough branch
|
|
1092
|
+
// for a `location` leaf whose value starts with "(map:" — every other
|
|
1093
|
+
// branch would escape the colons — so the whole clause rides in one node
|
|
1094
|
+
// and the Filters panel shows one readable row per interval.
|
|
1095
|
+
//
|
|
1096
|
+
// Overlap, not containment: start <= interval end AND end >= interval
|
|
1097
|
+
// start, so a gene that merely crosses the boundary still matches.
|
|
1098
|
+
const intervalNode = {
|
|
1099
|
+
fq_field: 'location',
|
|
1100
|
+
fq_value: $af4441dd29af05df$export$634f7214da1b780a({
|
|
1101
|
+
map: map,
|
|
1102
|
+
region: region,
|
|
1103
|
+
start: start,
|
|
1104
|
+
end: end
|
|
1105
|
+
}),
|
|
1106
|
+
name: $af4441dd29af05df$export$535c09441f37845d({
|
|
1107
|
+
mapLabel: mapLabel,
|
|
1108
|
+
map: map,
|
|
1109
|
+
region: region,
|
|
1110
|
+
start: start,
|
|
1111
|
+
end: end
|
|
1112
|
+
}),
|
|
1113
|
+
category: 'Genomic interval',
|
|
1114
|
+
negate: false,
|
|
1115
|
+
showMenu: false
|
|
1116
|
+
};
|
|
1117
|
+
let group = (newState.children || []).find((c)=>c && c.category === $af4441dd29af05df$export$6b95404109a3e2ec && Array.isArray(c.children));
|
|
1118
|
+
if (group) group.children.push(intervalNode);
|
|
1119
|
+
else newState.children.push({
|
|
1120
|
+
operation: 'OR',
|
|
1121
|
+
category: $af4441dd29af05df$export$6b95404109a3e2ec,
|
|
1122
|
+
negate: false,
|
|
1123
|
+
showMenu: false,
|
|
1124
|
+
children: [
|
|
1125
|
+
intervalNode
|
|
1126
|
+
]
|
|
1127
|
+
});
|
|
1128
|
+
// Inserting a nested subtree invalidates the hand-computed indices the
|
|
1129
|
+
// append actions rely on, so renumber the whole tree.
|
|
1130
|
+
$af4441dd29af05df$var$reindexTree(newState, 0);
|
|
1131
|
+
$af4441dd29af05df$var$markSubtree(newState, newState, false);
|
|
1132
|
+
return newState;
|
|
1133
|
+
}
|
|
1134
|
+
case 'GRAMENE_FILTER_EDITED':
|
|
1135
|
+
{
|
|
1136
|
+
// Edits only rewrite a leaf's value and label. The node keeps its place,
|
|
1137
|
+
// its negate flag and any expansion, and the tree shape is untouched — so
|
|
1138
|
+
// unlike an insert there is nothing to reindex.
|
|
1139
|
+
newState = Object.assign({}, state, {
|
|
1140
|
+
status: 'search',
|
|
1141
|
+
showMarked: true,
|
|
1142
|
+
searchOffset: 0
|
|
1143
|
+
});
|
|
1144
|
+
const node = $af4441dd29af05df$var$findNodeWithLeftIdx(newState, payload.leftIdx);
|
|
1145
|
+
if (!node || node.hasOwnProperty('children')) break;
|
|
1146
|
+
node.fq_value = payload.fq_value;
|
|
1147
|
+
if (payload.name !== undefined) node.name = payload.name;
|
|
1148
|
+
node.showMenu = false;
|
|
1149
|
+
$af4441dd29af05df$var$markSubtree(newState, node, false);
|
|
1150
|
+
return newState;
|
|
1151
|
+
}
|
|
1040
1152
|
case 'GRAMENE_FILTER_EXPANSION_SET':
|
|
1041
1153
|
{
|
|
1042
1154
|
// Setting or clearing an expansion is just a field on the node — the tree
|
|
@@ -1353,6 +1465,47 @@ const $af4441dd29af05df$var$grameneFilters = {
|
|
|
1353
1465
|
]
|
|
1354
1466
|
});
|
|
1355
1467
|
},
|
|
1468
|
+
// Add one genomic interval. Accumulates into a single OR group; see the
|
|
1469
|
+
// GRAMENE_FILTER_INTERVAL_ADDED reducer for why they cannot be ANDed.
|
|
1470
|
+
doAddGrameneInterval: (interval)=>({ dispatch: dispatch })=>{
|
|
1471
|
+
dispatch({
|
|
1472
|
+
type: 'BATCH_ACTIONS',
|
|
1473
|
+
actions: [
|
|
1474
|
+
{
|
|
1475
|
+
type: 'GRAMENE_SEARCH_CLEARED'
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
type: 'GRAMENE_TAXONOMY_CLEARED'
|
|
1479
|
+
},
|
|
1480
|
+
{
|
|
1481
|
+
type: 'GRAMENE_FILTER_INTERVAL_ADDED',
|
|
1482
|
+
payload: interval
|
|
1483
|
+
}
|
|
1484
|
+
]
|
|
1485
|
+
});
|
|
1486
|
+
},
|
|
1487
|
+
// Replace one leaf's value in place, keeping its position in the tree.
|
|
1488
|
+
doEditGrameneFilter: (filter, fq_value, name)=>({ dispatch: dispatch })=>{
|
|
1489
|
+
dispatch({
|
|
1490
|
+
type: 'BATCH_ACTIONS',
|
|
1491
|
+
actions: [
|
|
1492
|
+
{
|
|
1493
|
+
type: 'GRAMENE_SEARCH_CLEARED'
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
type: 'GRAMENE_TAXONOMY_CLEARED'
|
|
1497
|
+
},
|
|
1498
|
+
{
|
|
1499
|
+
type: 'GRAMENE_FILTER_EDITED',
|
|
1500
|
+
payload: {
|
|
1501
|
+
leftIdx: filter.leftIdx,
|
|
1502
|
+
fq_value: fq_value,
|
|
1503
|
+
name: name
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
]
|
|
1507
|
+
});
|
|
1508
|
+
},
|
|
1356
1509
|
doToggleGrameneFilterMenu: (node)=>({ dispatch: dispatch })=>{
|
|
1357
1510
|
dispatch({
|
|
1358
1511
|
type: 'GRAMENE_FILTER_MENU_TOGGLED',
|
|
@@ -20789,6 +20942,350 @@ var $7d7b7dad5e71dfd3$export$2e2bcd8739ae039 = $7d7b7dad5e71dfd3$var$Facets;
|
|
|
20789
20942
|
|
|
20790
20943
|
|
|
20791
20944
|
|
|
20945
|
+
// Sequences that aren't a placed chromosome. Excluded from the dropdown because
|
|
20946
|
+
// an interval on them is not something a user can reason about positionally.
|
|
20947
|
+
// Organelles (C, M, Mt, Pt, mitochondrion_genome) are deliberately kept — they
|
|
20948
|
+
// are real, coordinate-addressable sequences.
|
|
20949
|
+
const $909272efb41be16f$var$UNPLACED = /^(unanchored|u)$/i;
|
|
20950
|
+
function $909272efb41be16f$export$ac97d17982e0cb21(grameneMaps, targetTaxonId) {
|
|
20951
|
+
const genomes = Object.values(grameneMaps || {}).filter((m)=>m && m._id && m.regions);
|
|
20952
|
+
const byLabel = (a, b)=>String(a.display_name || '').localeCompare(String(b.display_name || ''));
|
|
20953
|
+
const reference = genomes.filter((m)=>String(m.taxon_id) === String(targetTaxonId));
|
|
20954
|
+
const refIds = new Set(reference.map((m)=>m.taxon_id));
|
|
20955
|
+
const anchors = genomes.filter((m)=>m.is_anchor && !refIds.has(m.taxon_id)).sort(byLabel);
|
|
20956
|
+
const anchorIds = new Set(anchors.map((m)=>m.taxon_id));
|
|
20957
|
+
const rest = genomes.filter((m)=>!refIds.has(m.taxon_id) && !anchorIds.has(m.taxon_id)).sort(byLabel);
|
|
20958
|
+
return {
|
|
20959
|
+
reference: reference,
|
|
20960
|
+
anchors: anchors,
|
|
20961
|
+
rest: rest
|
|
20962
|
+
};
|
|
20963
|
+
}
|
|
20964
|
+
function $909272efb41be16f$export$94955a075dcb14b2(genome) {
|
|
20965
|
+
const regions = genome && genome.regions || {};
|
|
20966
|
+
const names = regions.names || [];
|
|
20967
|
+
const lengths = regions.lengths || [];
|
|
20968
|
+
return names.map((name, i)=>({
|
|
20969
|
+
name: name,
|
|
20970
|
+
length: lengths[i]
|
|
20971
|
+
})).filter((r)=>!$909272efb41be16f$var$UNPLACED.test(String(r.name)));
|
|
20972
|
+
}
|
|
20973
|
+
const $909272efb41be16f$export$4fa3dba3103266ea = ({ grameneMaps: grameneMaps, targetTaxonId: targetTaxonId, initial: initial, submitLabel: submitLabel, onSubmit: onSubmit, onCancel: onCancel })=>{
|
|
20974
|
+
const [taxonId, setTaxonId] = (0, $gXNCa$react.useState)('');
|
|
20975
|
+
const [region, setRegion] = (0, $gXNCa$react.useState)(initial ? String(initial.region) : '');
|
|
20976
|
+
const [start, setStart] = (0, $gXNCa$react.useState)(initial ? String(initial.start) : '');
|
|
20977
|
+
const [end, setEnd] = (0, $gXNCa$react.useState)(initial ? String(initial.end) : '');
|
|
20978
|
+
const { reference: reference, anchors: anchors, rest: rest } = (0, $gXNCa$react.useMemo)(()=>$909272efb41be16f$export$ac97d17982e0cb21(grameneMaps, targetTaxonId), [
|
|
20979
|
+
grameneMaps,
|
|
20980
|
+
targetTaxonId
|
|
20981
|
+
]);
|
|
20982
|
+
// An edit starts on the filter's own genome; a new interval starts on the
|
|
20983
|
+
// site's reference. `initial.map` is an assembly accession, so find its genome.
|
|
20984
|
+
const initialTaxon = (0, $gXNCa$react.useMemo)(()=>{
|
|
20985
|
+
if (!initial) return '';
|
|
20986
|
+
const hit = Object.values(grameneMaps || {}).find((m)=>m && m._id === initial.map);
|
|
20987
|
+
return hit ? String(hit.taxon_id) : '';
|
|
20988
|
+
}, [
|
|
20989
|
+
initial,
|
|
20990
|
+
grameneMaps
|
|
20991
|
+
]);
|
|
20992
|
+
const effectiveTaxon = taxonId || initialTaxon || reference[0] && String(reference[0].taxon_id) || '';
|
|
20993
|
+
const genome = effectiveTaxon ? (grameneMaps || {})[effectiveTaxon] : null;
|
|
20994
|
+
const chromosomes = (0, $gXNCa$react.useMemo)(()=>$909272efb41be16f$export$94955a075dcb14b2(genome), [
|
|
20995
|
+
genome
|
|
20996
|
+
]);
|
|
20997
|
+
const effectiveRegion = region || chromosomes[0] && chromosomes[0].name || '';
|
|
20998
|
+
const chrLength = (chromosomes.find((c)=>String(c.name) === String(effectiveRegion)) || {}).length;
|
|
20999
|
+
const s = start === '' ? null : Number(start);
|
|
21000
|
+
const e = end === '' ? null : Number(end);
|
|
21001
|
+
// Validated against the assembly's own region lengths, so the message can name
|
|
21002
|
+
// the actual limit rather than just refusing.
|
|
21003
|
+
const error = (()=>{
|
|
21004
|
+
if (!genome || s === null || e === null) return null;
|
|
21005
|
+
if (!Number.isFinite(s) || !Number.isFinite(e)) return 'Positions must be numbers.';
|
|
21006
|
+
if (s < 1 || e < 1) return 'Positions start at 1.';
|
|
21007
|
+
if (s > e) return 'Start must not be greater than end.';
|
|
21008
|
+
if (chrLength && e > chrLength) return `${effectiveRegion} is ${chrLength.toLocaleString()} bp; end is beyond it.`;
|
|
21009
|
+
return null;
|
|
21010
|
+
})();
|
|
21011
|
+
const ready = !!genome && !!effectiveRegion && s !== null && e !== null && !error;
|
|
21012
|
+
const submit = ()=>{
|
|
21013
|
+
if (!ready) return;
|
|
21014
|
+
onSubmit({
|
|
21015
|
+
map: genome._id,
|
|
21016
|
+
mapLabel: genome.display_name || genome._id,
|
|
21017
|
+
region: effectiveRegion,
|
|
21018
|
+
start: s,
|
|
21019
|
+
end: e
|
|
21020
|
+
});
|
|
21021
|
+
if (!initial) {
|
|
21022
|
+
setStart('');
|
|
21023
|
+
setEnd('');
|
|
21024
|
+
}
|
|
21025
|
+
};
|
|
21026
|
+
const genomeOption = (m)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("option", {
|
|
21027
|
+
value: m.taxon_id,
|
|
21028
|
+
children: m.display_name || m.system_name
|
|
21029
|
+
}, m.taxon_id);
|
|
21030
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21031
|
+
className: "gramene-interval-form",
|
|
21032
|
+
children: [
|
|
21033
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Select, {
|
|
21034
|
+
size: "sm",
|
|
21035
|
+
className: "mb-1",
|
|
21036
|
+
value: effectiveTaxon,
|
|
21037
|
+
onChange: (ev)=>{
|
|
21038
|
+
setTaxonId(ev.target.value);
|
|
21039
|
+
setRegion('');
|
|
21040
|
+
},
|
|
21041
|
+
children: [
|
|
21042
|
+
reference.map(genomeOption),
|
|
21043
|
+
anchors.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("optgroup", {
|
|
21044
|
+
label: "Reference genomes",
|
|
21045
|
+
children: anchors.map(genomeOption)
|
|
21046
|
+
}),
|
|
21047
|
+
rest.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("optgroup", {
|
|
21048
|
+
label: "Other genomes",
|
|
21049
|
+
children: rest.map(genomeOption)
|
|
21050
|
+
})
|
|
21051
|
+
]
|
|
21052
|
+
}),
|
|
21053
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Select, {
|
|
21054
|
+
size: "sm",
|
|
21055
|
+
className: "mb-1",
|
|
21056
|
+
value: effectiveRegion,
|
|
21057
|
+
onChange: (ev)=>setRegion(ev.target.value),
|
|
21058
|
+
children: chromosomes.map((c)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("option", {
|
|
21059
|
+
value: c.name,
|
|
21060
|
+
children: [
|
|
21061
|
+
c.name,
|
|
21062
|
+
c.length ? ` (${Math.round(c.length / 1e6)} Mb)` : ''
|
|
21063
|
+
]
|
|
21064
|
+
}, c.name))
|
|
21065
|
+
}),
|
|
21066
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21067
|
+
className: "gramene-interval-range",
|
|
21068
|
+
children: [
|
|
21069
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
21070
|
+
size: "sm",
|
|
21071
|
+
type: "number",
|
|
21072
|
+
min: "1",
|
|
21073
|
+
placeholder: "start",
|
|
21074
|
+
value: start,
|
|
21075
|
+
onChange: (ev)=>setStart(ev.target.value)
|
|
21076
|
+
}),
|
|
21077
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
21078
|
+
className: "gramene-interval-dash",
|
|
21079
|
+
children: "\u2013"
|
|
21080
|
+
}),
|
|
21081
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
21082
|
+
size: "sm",
|
|
21083
|
+
type: "number",
|
|
21084
|
+
min: "1",
|
|
21085
|
+
placeholder: "end",
|
|
21086
|
+
value: end,
|
|
21087
|
+
onChange: (ev)=>setEnd(ev.target.value),
|
|
21088
|
+
onKeyDown: (ev)=>{
|
|
21089
|
+
if (ev.key === 'Enter') submit();
|
|
21090
|
+
}
|
|
21091
|
+
})
|
|
21092
|
+
]
|
|
21093
|
+
}),
|
|
21094
|
+
error && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21095
|
+
className: "gramene-interval-error",
|
|
21096
|
+
children: error
|
|
21097
|
+
}),
|
|
21098
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21099
|
+
className: "gramene-interval-actions",
|
|
21100
|
+
children: [
|
|
21101
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
21102
|
+
size: "sm",
|
|
21103
|
+
variant: "primary",
|
|
21104
|
+
className: "gramene-interval-submit",
|
|
21105
|
+
disabled: !ready,
|
|
21106
|
+
onClick: submit,
|
|
21107
|
+
children: submitLabel || 'Add interval'
|
|
21108
|
+
}),
|
|
21109
|
+
onCancel && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
21110
|
+
size: "sm",
|
|
21111
|
+
variant: "link",
|
|
21112
|
+
className: "gramene-interval-cancel",
|
|
21113
|
+
onClick: onCancel,
|
|
21114
|
+
children: "cancel"
|
|
21115
|
+
})
|
|
21116
|
+
]
|
|
21117
|
+
})
|
|
21118
|
+
]
|
|
21119
|
+
});
|
|
21120
|
+
};
|
|
21121
|
+
/** The collapsible "Add genomic interval" entry at the foot of the Filters panel. */ const $909272efb41be16f$var$GenomicIntervalForm = ({ grameneMaps: grameneMaps, targetTaxonId: targetTaxonId, onAdd: onAdd })=>{
|
|
21122
|
+
const [open, setOpen] = (0, $gXNCa$react.useState)(false);
|
|
21123
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21124
|
+
className: "gramene-interval",
|
|
21125
|
+
children: [
|
|
21126
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21127
|
+
className: "gramene-interval-toggle",
|
|
21128
|
+
onClick: ()=>setOpen(!open),
|
|
21129
|
+
children: [
|
|
21130
|
+
open ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsChevronDown), {}) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsChevronRight), {}),
|
|
21131
|
+
" Add genomic interval"
|
|
21132
|
+
]
|
|
21133
|
+
}),
|
|
21134
|
+
open && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($909272efb41be16f$export$4fa3dba3103266ea, {
|
|
21135
|
+
grameneMaps: grameneMaps,
|
|
21136
|
+
targetTaxonId: targetTaxonId,
|
|
21137
|
+
onSubmit: onAdd
|
|
21138
|
+
})
|
|
21139
|
+
]
|
|
21140
|
+
});
|
|
21141
|
+
};
|
|
21142
|
+
var $909272efb41be16f$export$2e2bcd8739ae039 = $909272efb41be16f$var$GenomicIntervalForm;
|
|
21143
|
+
|
|
21144
|
+
|
|
21145
|
+
|
|
21146
|
+
|
|
21147
|
+
|
|
21148
|
+
|
|
21149
|
+
|
|
21150
|
+
/**
|
|
21151
|
+
* Edit one filter leaf in place, with a form shaped to what that leaf actually is:
|
|
21152
|
+
* a genomic interval gets the genome/chromosome/start/end fields, a `[lo TO hi]`
|
|
21153
|
+
* range gets two number boxes, anything else gets its raw value.
|
|
21154
|
+
*
|
|
21155
|
+
* Groups are not editable — their only parameter is the operator, which the menu
|
|
21156
|
+
* already exposes as "convert to".
|
|
21157
|
+
*
|
|
21158
|
+
* The plain-value case is honest but limited: many leaves carry an opaque id (a
|
|
21159
|
+
* pathway number, an ontology term, a saved-search hash) whose replacement a user
|
|
21160
|
+
* has no way to know. It is offered because a typo in a gene name or a term is
|
|
21161
|
+
* worth fixing without deleting and starting over.
|
|
21162
|
+
*/ const $91efc9c46e3775e3$var$FilterEditor = ({ node: node, grameneMaps: grameneMaps, targetTaxonId: targetTaxonId, onSave: onSave, onCancel: onCancel })=>{
|
|
21163
|
+
const kind = (0, $af4441dd29af05df$export$a7f4fa59930e5088)(node);
|
|
21164
|
+
const range = kind === 'range' ? (0, $af4441dd29af05df$export$9ee0ceaa589fc567)(node.fq_value) : null;
|
|
21165
|
+
const [lo, setLo] = (0, $gXNCa$react.useState)(range ? range.lo : '');
|
|
21166
|
+
const [hi, setHi] = (0, $gXNCa$react.useState)(range ? range.hi : '');
|
|
21167
|
+
const [value, setValue] = (0, $gXNCa$react.useState)(kind === 'value' ? String(node.fq_value ?? '') : '');
|
|
21168
|
+
if (!kind) return null;
|
|
21169
|
+
if (kind === 'interval') {
|
|
21170
|
+
const initial = (0, $af4441dd29af05df$export$67920d3e3cb3f136)(node.fq_value);
|
|
21171
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21172
|
+
className: "gramene-filter-editor",
|
|
21173
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $909272efb41be16f$export$4fa3dba3103266ea), {
|
|
21174
|
+
grameneMaps: grameneMaps,
|
|
21175
|
+
targetTaxonId: targetTaxonId,
|
|
21176
|
+
initial: initial,
|
|
21177
|
+
submitLabel: "Save",
|
|
21178
|
+
onCancel: onCancel,
|
|
21179
|
+
onSubmit: (iv)=>onSave((0, $af4441dd29af05df$export$634f7214da1b780a)(iv), (0, $af4441dd29af05df$export$535c09441f37845d)(iv))
|
|
21180
|
+
})
|
|
21181
|
+
});
|
|
21182
|
+
}
|
|
21183
|
+
if (kind === 'range') {
|
|
21184
|
+
const ok = lo !== '' && hi !== '';
|
|
21185
|
+
const save = ()=>{
|
|
21186
|
+
if (ok) onSave((0, $af4441dd29af05df$export$cd9b2ffd001c6d65)(lo, hi), `${lo}-${hi}`);
|
|
21187
|
+
};
|
|
21188
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21189
|
+
className: "gramene-filter-editor",
|
|
21190
|
+
children: [
|
|
21191
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21192
|
+
className: "gramene-interval-range",
|
|
21193
|
+
children: [
|
|
21194
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
21195
|
+
size: "sm",
|
|
21196
|
+
value: lo,
|
|
21197
|
+
onChange: (e)=>setLo(e.target.value),
|
|
21198
|
+
placeholder: "from"
|
|
21199
|
+
}),
|
|
21200
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
21201
|
+
className: "gramene-interval-dash",
|
|
21202
|
+
children: "\u2013"
|
|
21203
|
+
}),
|
|
21204
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
21205
|
+
size: "sm",
|
|
21206
|
+
value: hi,
|
|
21207
|
+
onChange: (e)=>setHi(e.target.value),
|
|
21208
|
+
placeholder: "to",
|
|
21209
|
+
onKeyDown: (e)=>{
|
|
21210
|
+
if (e.key === 'Enter') save();
|
|
21211
|
+
}
|
|
21212
|
+
})
|
|
21213
|
+
]
|
|
21214
|
+
}),
|
|
21215
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21216
|
+
className: "gramene-interval-actions",
|
|
21217
|
+
children: [
|
|
21218
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
21219
|
+
size: "sm",
|
|
21220
|
+
variant: "primary",
|
|
21221
|
+
className: "gramene-interval-submit",
|
|
21222
|
+
disabled: !ok,
|
|
21223
|
+
onClick: save,
|
|
21224
|
+
children: "Save"
|
|
21225
|
+
}),
|
|
21226
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
21227
|
+
size: "sm",
|
|
21228
|
+
variant: "link",
|
|
21229
|
+
className: "gramene-interval-cancel",
|
|
21230
|
+
onClick: onCancel,
|
|
21231
|
+
children: "cancel"
|
|
21232
|
+
})
|
|
21233
|
+
]
|
|
21234
|
+
})
|
|
21235
|
+
]
|
|
21236
|
+
});
|
|
21237
|
+
}
|
|
21238
|
+
const ok = String(value).trim() !== '';
|
|
21239
|
+
// Keep the node's own label in step with its value, unless the label was
|
|
21240
|
+
// something other than the value to begin with (a suggestion's display name),
|
|
21241
|
+
// in which case leave it alone rather than overwriting it with a raw id.
|
|
21242
|
+
const save = ()=>{
|
|
21243
|
+
if (!ok) return;
|
|
21244
|
+
const v = String(value).trim();
|
|
21245
|
+
const nameFollowedValue = String(node.name ?? '') === String(node.fq_value ?? '');
|
|
21246
|
+
onSave(v, nameFollowedValue ? v : undefined);
|
|
21247
|
+
};
|
|
21248
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21249
|
+
className: "gramene-filter-editor",
|
|
21250
|
+
children: [
|
|
21251
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Form).Control, {
|
|
21252
|
+
size: "sm",
|
|
21253
|
+
value: value,
|
|
21254
|
+
onChange: (e)=>setValue(e.target.value),
|
|
21255
|
+
onKeyDown: (e)=>{
|
|
21256
|
+
if (e.key === 'Enter') save();
|
|
21257
|
+
}
|
|
21258
|
+
}),
|
|
21259
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21260
|
+
className: "gramene-interval-actions",
|
|
21261
|
+
children: [
|
|
21262
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
21263
|
+
size: "sm",
|
|
21264
|
+
variant: "primary",
|
|
21265
|
+
className: "gramene-interval-submit",
|
|
21266
|
+
disabled: !ok,
|
|
21267
|
+
onClick: save,
|
|
21268
|
+
children: "Save"
|
|
21269
|
+
}),
|
|
21270
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Button), {
|
|
21271
|
+
size: "sm",
|
|
21272
|
+
variant: "link",
|
|
21273
|
+
className: "gramene-interval-cancel",
|
|
21274
|
+
onClick: onCancel,
|
|
21275
|
+
children: "cancel"
|
|
21276
|
+
})
|
|
21277
|
+
]
|
|
21278
|
+
})
|
|
21279
|
+
]
|
|
21280
|
+
});
|
|
21281
|
+
};
|
|
21282
|
+
var $91efc9c46e3775e3$export$2e2bcd8739ae039 = $91efc9c46e3775e3$var$FilterEditor;
|
|
21283
|
+
|
|
21284
|
+
|
|
21285
|
+
|
|
21286
|
+
|
|
21287
|
+
|
|
21288
|
+
|
|
20792
21289
|
|
|
20793
21290
|
|
|
20794
21291
|
|
|
@@ -21234,6 +21731,7 @@ function $693dd8c7a5607c3a$var$logAction(action) {
|
|
|
21234
21731
|
});
|
|
21235
21732
|
}
|
|
21236
21733
|
const $693dd8c7a5607c3a$var$Filter = ({ node: node, moveCopyMode: moveCopyMode, showMarked: showMarked, actions: actions })=>{
|
|
21734
|
+
const [editing, setEditing] = (0, $gXNCa$react.useState)(false);
|
|
21237
21735
|
let classes = 'gramene-filter gramene-filter';
|
|
21238
21736
|
if (node.operation) classes = `${classes}-${(0, $af4441dd29af05df$export$c8b6102be216752a)(node.operation)}`;
|
|
21239
21737
|
// An expansion decorates whatever node it sits on, leaf or group — same as negate.
|
|
@@ -21293,6 +21791,14 @@ const $693dd8c7a5607c3a$var$Filter = ({ node: node, moveCopyMode: moveCopyMode,
|
|
|
21293
21791
|
]
|
|
21294
21792
|
}, key++));
|
|
21295
21793
|
});
|
|
21794
|
+
if ((0, $af4441dd29af05df$export$a7f4fa59930e5088)(node)) menuItems.push(/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("li", {
|
|
21795
|
+
onClick: ()=>{
|
|
21796
|
+
$693dd8c7a5607c3a$var$logAction(`Edit filter`);
|
|
21797
|
+
actions.toggleMenu(node);
|
|
21798
|
+
setEditing(true);
|
|
21799
|
+
},
|
|
21800
|
+
children: "edit"
|
|
21801
|
+
}, key++));
|
|
21296
21802
|
if (node.leftIdx > 0) {
|
|
21297
21803
|
menuItems.push(/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("li", {
|
|
21298
21804
|
onClick: ()=>{
|
|
@@ -21401,12 +21907,23 @@ const $693dd8c7a5607c3a$var$Filter = ({ node: node, moveCopyMode: moveCopyMode,
|
|
|
21401
21907
|
})
|
|
21402
21908
|
]
|
|
21403
21909
|
});
|
|
21910
|
+
const editor = editing ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $91efc9c46e3775e3$export$2e2bcd8739ae039), {
|
|
21911
|
+
node: node,
|
|
21912
|
+
grameneMaps: actions.grameneMaps,
|
|
21913
|
+
targetTaxonId: actions.targetTaxonId,
|
|
21914
|
+
onCancel: ()=>setEditing(false),
|
|
21915
|
+
onSave: (fq_value, name)=>{
|
|
21916
|
+
setEditing(false);
|
|
21917
|
+
actions.editNode(node, fq_value, name);
|
|
21918
|
+
}
|
|
21919
|
+
}) : null;
|
|
21404
21920
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
21405
21921
|
className: classes,
|
|
21406
21922
|
onClick: (e)=>$693dd8c7a5607c3a$var$handleClick(e, moveCopyMode, showMarked, node, actions),
|
|
21407
21923
|
children: [
|
|
21408
21924
|
content,
|
|
21409
21925
|
menu,
|
|
21926
|
+
editor,
|
|
21410
21927
|
children
|
|
21411
21928
|
]
|
|
21412
21929
|
}, node.leftIdx);
|
|
@@ -21421,12 +21938,22 @@ const $693dd8c7a5607c3a$var$FiltersCmp = (props)=>{
|
|
|
21421
21938
|
selectTarget: props.doMoveOrCopyGrameneFilter,
|
|
21422
21939
|
markTargets: props.doMarkGrameneFilterTargets,
|
|
21423
21940
|
unmarkTargets: props.doUnmarkGrameneFilterTargets,
|
|
21424
|
-
toggleMenu: props.doToggleGrameneFilterMenu
|
|
21941
|
+
toggleMenu: props.doToggleGrameneFilterMenu,
|
|
21942
|
+
editNode: props.doEditGrameneFilter,
|
|
21943
|
+
// Carried alongside the actions so every nested Filter can reach them without
|
|
21944
|
+
// threading two more props through the recursion.
|
|
21945
|
+
grameneMaps: props.grameneMaps,
|
|
21946
|
+
targetTaxonId: props.targetTaxonId
|
|
21425
21947
|
};
|
|
21948
|
+
const intervalForm = /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $909272efb41be16f$export$2e2bcd8739ae039), {
|
|
21949
|
+
grameneMaps: props.grameneMaps,
|
|
21950
|
+
targetTaxonId: props.targetTaxonId,
|
|
21951
|
+
onAdd: props.doAddGrameneInterval
|
|
21952
|
+
});
|
|
21426
21953
|
const containerClass = props.configuration.id === 'sorghum' ? 'sorghumbase-filter-container' : 'gramene-filter-container';
|
|
21427
21954
|
if (props.grameneFilters.rightIdx > 1) return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21428
21955
|
className: containerClass,
|
|
21429
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
21956
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)($693dd8c7a5607c3a$var$CollapsibleSection, {
|
|
21430
21957
|
title: "Filters",
|
|
21431
21958
|
extra: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
21432
21959
|
style: {
|
|
@@ -21439,26 +21966,32 @@ const $693dd8c7a5607c3a$var$FiltersCmp = (props)=>{
|
|
|
21439
21966
|
},
|
|
21440
21967
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reacticonsbs.BsTrash), {})
|
|
21441
21968
|
}),
|
|
21442
|
-
children:
|
|
21443
|
-
|
|
21444
|
-
|
|
21445
|
-
|
|
21446
|
-
|
|
21447
|
-
|
|
21969
|
+
children: [
|
|
21970
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($693dd8c7a5607c3a$var$Filter, {
|
|
21971
|
+
node: props.grameneFilters,
|
|
21972
|
+
moveCopyMode: props.grameneFilters.moveCopyMode,
|
|
21973
|
+
showMarked: props.grameneFilters.showMarked,
|
|
21974
|
+
actions: actions
|
|
21975
|
+
}),
|
|
21976
|
+
intervalForm
|
|
21977
|
+
]
|
|
21448
21978
|
})
|
|
21449
21979
|
});
|
|
21450
21980
|
else return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21451
21981
|
className: containerClass,
|
|
21452
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
21982
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)($693dd8c7a5607c3a$var$CollapsibleSection, {
|
|
21453
21983
|
title: "Filters",
|
|
21454
|
-
children:
|
|
21455
|
-
|
|
21456
|
-
|
|
21457
|
-
|
|
21984
|
+
children: [
|
|
21985
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
21986
|
+
className: "gramene-filter gramene-filter-AND",
|
|
21987
|
+
children: "No filters defined"
|
|
21988
|
+
}),
|
|
21989
|
+
intervalForm
|
|
21990
|
+
]
|
|
21458
21991
|
})
|
|
21459
21992
|
});
|
|
21460
21993
|
};
|
|
21461
|
-
const $693dd8c7a5607c3a$export$92a576f896a9bf72 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneFilters', 'selectConfiguration', 'doNegateGrameneFilter', 'doDeleteGrameneFilter', 'doChangeGrameneFilterOperation', 'doExpandGrameneFilter', 'doRemoveGrameneExpansion', 'doMoveOrCopyGrameneFilter', 'doMarkGrameneFilterTargets', 'doUnmarkGrameneFilterTargets', 'doToggleGrameneFilterMenu', 'doClearGrameneFilters', $693dd8c7a5607c3a$var$FiltersCmp);
|
|
21994
|
+
const $693dd8c7a5607c3a$export$92a576f896a9bf72 = (0, $gXNCa$reduxbundlerreact.connect)('selectGrameneFilters', 'selectConfiguration', 'doNegateGrameneFilter', 'doDeleteGrameneFilter', 'doChangeGrameneFilterOperation', 'doExpandGrameneFilter', 'doRemoveGrameneExpansion', 'doMoveOrCopyGrameneFilter', 'doMarkGrameneFilterTargets', 'doUnmarkGrameneFilterTargets', 'doToggleGrameneFilterMenu', 'selectGrameneMaps', 'selectTargetTaxonId', 'doAddGrameneInterval', 'doEditGrameneFilter', 'doClearGrameneFilters', $693dd8c7a5607c3a$var$FiltersCmp);
|
|
21462
21995
|
const $693dd8c7a5607c3a$var$ResultsCmp = (props)=>{
|
|
21463
21996
|
let activeViews = props.grameneViews.options;
|
|
21464
21997
|
return props.grameneFilters.rightIdx > 0 ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|