gramene-search 2.0.4 → 2.0.5
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 +4 -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 +174 -57
- package/dist/index.css.map +1 -1
- package/dist/index.js +650 -223
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/bundles/api.js +33 -26
- package/src/bundles/exporter.js +4 -1
- package/src/bundles/swaggerFields.js +13 -0
- package/src/bundles/views.js +12 -0
- package/src/components/exprViz/ExprTable.js +245 -100
- package/src/components/exprViz/ExprVizView.js +108 -2
- package/src/components/exprViz/FieldsModal.js +45 -18
- package/src/components/exprViz/ParallelCoordsPlot.js +99 -6
- package/src/components/exprViz/styles.css +211 -56
package/dist/index.js
CHANGED
|
@@ -351,8 +351,16 @@ const $9d9aeaf9299e61a1$var$expressionStudies = (0, $gXNCa$reduxbundler.createAs
|
|
|
351
351
|
return fetch(`${store.selectGrameneAPI()}/experiments?rows=-1`).then((res)=>res.json()).then((res)=>(0, ($parcel$interopDefault($gXNCa$lodash))).groupBy(res, 'taxon_id'));
|
|
352
352
|
}
|
|
353
353
|
});
|
|
354
|
-
|
|
355
|
-
|
|
354
|
+
// Only fetch when a view that consumes expression studies is enabled.
|
|
355
|
+
const $9d9aeaf9299e61a1$var$EXPRESSION_VIEWS = [
|
|
356
|
+
'exprViz',
|
|
357
|
+
'expression',
|
|
358
|
+
'export'
|
|
359
|
+
];
|
|
360
|
+
$9d9aeaf9299e61a1$var$expressionStudies.reactExpressionStudies = (0, $gXNCa$reduxbundler.createSelector)('selectExpressionStudiesShouldUpdate', 'selectGrameneViewsOn', (shouldUpdate, viewsOn)=>{
|
|
361
|
+
if (!shouldUpdate) return;
|
|
362
|
+
if (!viewsOn || !$9d9aeaf9299e61a1$var$EXPRESSION_VIEWS.some((id)=>viewsOn.has(id))) return;
|
|
363
|
+
return {
|
|
356
364
|
actionCreator: 'doFetchExpressionStudies'
|
|
357
365
|
};
|
|
358
366
|
});
|
|
@@ -372,8 +380,10 @@ const $9d9aeaf9299e61a1$var$expressionSamples = (0, $gXNCa$reduxbundler.createAs
|
|
|
372
380
|
});
|
|
373
381
|
}
|
|
374
382
|
});
|
|
375
|
-
$9d9aeaf9299e61a1$var$expressionSamples.reactExpressionSamples = (0, $gXNCa$reduxbundler.createSelector)('selectExpressionSamplesShouldUpdate', (shouldUpdate)=>{
|
|
376
|
-
if (shouldUpdate) return
|
|
383
|
+
$9d9aeaf9299e61a1$var$expressionSamples.reactExpressionSamples = (0, $gXNCa$reduxbundler.createSelector)('selectExpressionSamplesShouldUpdate', 'selectGrameneViewsOn', (shouldUpdate, viewsOn)=>{
|
|
384
|
+
if (!shouldUpdate) return;
|
|
385
|
+
if (!viewsOn || !$9d9aeaf9299e61a1$var$EXPRESSION_VIEWS.some((id)=>viewsOn.has(id))) return;
|
|
386
|
+
return {
|
|
377
387
|
actionCreator: 'doFetchExpressionSamples'
|
|
378
388
|
};
|
|
379
389
|
});
|
|
@@ -385,8 +395,12 @@ const $9d9aeaf9299e61a1$var$curatedGenes = (0, $gXNCa$reduxbundler.createAsyncRe
|
|
|
385
395
|
return fetch(`https://devdata.gramene.org/curation/curations?rows=0&minFlagged=0&since=12-12-2029`).then((res)=>res.json()).then((curation)=>(0, ($parcel$interopDefault($gXNCa$lodash))).keyBy(curation.genes, 'gene_id'));
|
|
386
396
|
}
|
|
387
397
|
});
|
|
388
|
-
|
|
389
|
-
|
|
398
|
+
// Curated annotations are consumed by GeneList rows and Homology details,
|
|
399
|
+
// both inside the gene-list view.
|
|
400
|
+
$9d9aeaf9299e61a1$var$curatedGenes.reactCuratedGenes = (0, $gXNCa$reduxbundler.createSelector)('selectCuratedGenesShouldUpdate', 'selectGrameneViewsOn', (shouldUpdate, viewsOn)=>{
|
|
401
|
+
if (!shouldUpdate) return;
|
|
402
|
+
if (!viewsOn || !viewsOn.has('list')) return;
|
|
403
|
+
return {
|
|
390
404
|
actionCreator: 'doFetchCuratedGenes'
|
|
391
405
|
};
|
|
392
406
|
});
|
|
@@ -403,8 +417,11 @@ const $9d9aeaf9299e61a1$var$grameneGermplasm = (0, $gXNCa$reduxbundler.createAsy
|
|
|
403
417
|
});
|
|
404
418
|
}
|
|
405
419
|
});
|
|
406
|
-
|
|
407
|
-
|
|
420
|
+
// Germplasm metadata is consumed by the VEP detail panel inside the gene list.
|
|
421
|
+
$9d9aeaf9299e61a1$var$grameneGermplasm.reactGrameneGermplasm = (0, $gXNCa$reduxbundler.createSelector)('selectGrameneGermplasmShouldUpdate', 'selectGrameneViewsOn', (shouldUpdate, viewsOn)=>{
|
|
422
|
+
if (!shouldUpdate) return;
|
|
423
|
+
if (!viewsOn || !viewsOn.has('list')) return;
|
|
424
|
+
return {
|
|
408
425
|
actionCreator: 'doFetchGrameneGermplasm'
|
|
409
426
|
};
|
|
410
427
|
});
|
|
@@ -522,13 +539,13 @@ const $9d9aeaf9299e61a1$var$grameneGeneAttribs = (0, $gXNCa$reduxbundler.createA
|
|
|
522
539
|
return fetch(`${store.selectGrameneAPI()}/geneAttributes`).then((res)=>$9d9aeaf9299e61a1$var$geneAttribs);
|
|
523
540
|
}
|
|
524
541
|
});
|
|
525
|
-
$9d9aeaf9299e61a1$var$grameneGeneAttribs.reactGrameneGeneAttribs = (0, $gXNCa$reduxbundler.createSelector)('selectGrameneGeneAttribsShouldUpdate', 'selectGrameneFiltersStatus', '
|
|
526
|
-
if (shouldUpdate
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
542
|
+
$9d9aeaf9299e61a1$var$grameneGeneAttribs.reactGrameneGeneAttribs = (0, $gXNCa$reduxbundler.createSelector)('selectGrameneGeneAttribsShouldUpdate', 'selectGrameneFiltersStatus', 'selectGrameneViewsOn', (shouldUpdate, status, viewsOn)=>{
|
|
543
|
+
if (!shouldUpdate) return;
|
|
544
|
+
if (status !== 'finished' && status !== 'ready') return;
|
|
545
|
+
if (!viewsOn || !viewsOn.has('attribs')) return;
|
|
546
|
+
return {
|
|
547
|
+
actionCreator: 'doFetchGrameneGeneAttribs'
|
|
548
|
+
};
|
|
532
549
|
});
|
|
533
550
|
const $9d9aeaf9299e61a1$var$grameneSearch = (0, $gXNCa$reduxbundler.createAsyncResourceBundle)({
|
|
534
551
|
name: 'grameneSearch',
|
|
@@ -538,8 +555,8 @@ const $9d9aeaf9299e61a1$var$grameneSearch = (0, $gXNCa$reduxbundler.createAsyncR
|
|
|
538
555
|
const offset = store.selectGrameneSearchOffset();
|
|
539
556
|
const rows = store.selectGrameneSearchRows();
|
|
540
557
|
const g = store.selectGrameneGenomes();
|
|
541
|
-
const m = store.selectGrameneMaps();
|
|
542
|
-
const taxa = Object.keys(g.active).filter((tid)
|
|
558
|
+
const m = store.selectGrameneMaps() || {};
|
|
559
|
+
const taxa = Object.keys(g && g.active || {}).filter((tid)=>m[tid] && !m[tid].hidden);
|
|
543
560
|
let fq = '';
|
|
544
561
|
if (taxa.length) {
|
|
545
562
|
console.log('search add a fq for ', taxa);
|
|
@@ -1568,6 +1585,15 @@ const $24971af0a229e0e3$var$grameneViews = {
|
|
|
1568
1585
|
...raw,
|
|
1569
1586
|
options: options
|
|
1570
1587
|
};
|
|
1588
|
+
}),
|
|
1589
|
+
// Set of view ids whose `show` is currently 'on'. Used by data bundles to
|
|
1590
|
+
// gate their auto-fetch reactors so we don't pay the cost of loading data
|
|
1591
|
+
// for views the user has turned off.
|
|
1592
|
+
selectGrameneViewsOn: (0, $gXNCa$reduxbundler.createSelector)('selectGrameneViews', (views)=>{
|
|
1593
|
+
const ids = new Set();
|
|
1594
|
+
const opts = views && views.options || [];
|
|
1595
|
+
for (const v of opts)if (v && v.show === 'on') ids.add(v.id);
|
|
1596
|
+
return ids;
|
|
1571
1597
|
})
|
|
1572
1598
|
};
|
|
1573
1599
|
var $24971af0a229e0e3$export$2e2bcd8739ae039 = $24971af0a229e0e3$var$grameneViews;
|
|
@@ -2287,6 +2313,19 @@ const $0f839422d0d8c772$var$grameneFieldCatalog = (0, $gXNCa$reduxbundler.create
|
|
|
2287
2313
|
return fetch(url).then((r)=>r.text()).then((text)=>$0f839422d0d8c772$var$parseCsvHeader(text)).catch(()=>null);
|
|
2288
2314
|
}
|
|
2289
2315
|
});
|
|
2316
|
+
// Auto-fetch the field catalog only when a view that needs it (exprViz fields
|
|
2317
|
+
// modal, exporter field tree) is enabled. Other views never read it.
|
|
2318
|
+
const $0f839422d0d8c772$var$FIELD_CATALOG_VIEWS = [
|
|
2319
|
+
'exprViz',
|
|
2320
|
+
'export'
|
|
2321
|
+
];
|
|
2322
|
+
$0f839422d0d8c772$var$grameneFieldCatalog.reactGrameneFieldCatalog = (0, $gXNCa$reduxbundler.createSelector)('selectGrameneFieldCatalogShouldUpdate', 'selectGrameneViewsOn', (shouldUpdate, viewsOn)=>{
|
|
2323
|
+
if (!shouldUpdate) return;
|
|
2324
|
+
if (!viewsOn || !$0f839422d0d8c772$var$FIELD_CATALOG_VIEWS.some((id)=>viewsOn.has(id))) return;
|
|
2325
|
+
return {
|
|
2326
|
+
actionCreator: 'doFetchGrameneFieldCatalog'
|
|
2327
|
+
};
|
|
2328
|
+
});
|
|
2290
2329
|
function $0f839422d0d8c772$var$assayFactorLabel(assay) {
|
|
2291
2330
|
if (!assay) return '';
|
|
2292
2331
|
const factors = Array.isArray(assay.factor) ? assay.factor : [];
|
|
@@ -3662,10 +3701,12 @@ const $1508f5a42be6e7b5$var$exporter = {
|
|
|
3662
3701
|
});
|
|
3663
3702
|
}, $1508f5a42be6e7b5$var$PREVIEW_DEBOUNCE_MS);
|
|
3664
3703
|
},
|
|
3665
|
-
reactExporterPreview: (0, $gXNCa$reduxbundler.createSelector)('selectExporter', 'selectGrameneFiltersStatus', 'selectGrameneFiltersQueryString', 'selectGrameneGenomes', 'selectGrameneMaps', (exp, filtersStatus, q, g, m)=>{
|
|
3704
|
+
reactExporterPreview: (0, $gXNCa$reduxbundler.createSelector)('selectExporter', 'selectGrameneFiltersStatus', 'selectGrameneFiltersQueryString', 'selectGrameneGenomes', 'selectGrameneMaps', 'selectGrameneViewsOn', (exp, filtersStatus, q, g, m, viewsOn)=>{
|
|
3666
3705
|
if (!exp || !exp.preview) return;
|
|
3667
3706
|
if (filtersStatus === 'init') return;
|
|
3668
3707
|
if (exp.preview.status === 'loading') return;
|
|
3708
|
+
// Don't run the preview query unless the user has the exporter view open.
|
|
3709
|
+
if (!viewsOn || !viewsOn.has('export')) return;
|
|
3669
3710
|
const maps = m || {};
|
|
3670
3711
|
const taxa = Object.keys(g && g.active || {}).filter((tid)=>maps[tid] && !maps[tid].hidden);
|
|
3671
3712
|
const sig = `${q}|${taxa.sort().join(',')}`;
|
|
@@ -11002,6 +11043,21 @@ const $4d0c2e01f58b53b1$var$FieldsModalCmp = (props)=>{
|
|
|
11002
11043
|
records,
|
|
11003
11044
|
selections
|
|
11004
11045
|
]);
|
|
11046
|
+
// Distinct values per property type across the unfiltered candidate set.
|
|
11047
|
+
// A type with a single value across every record can't discriminate (e.g.
|
|
11048
|
+
// "Organism" when every field belongs to the same species), so we hide it
|
|
11049
|
+
// from the property tree.
|
|
11050
|
+
const totalValueCountByKey = (0, $gXNCa$react.useMemo)(()=>{
|
|
11051
|
+
const m = {};
|
|
11052
|
+
for (const r of records)for (const [k, v] of Object.entries(r.props)){
|
|
11053
|
+
if (v == null) continue;
|
|
11054
|
+
if (!m[k]) m[k] = new Set();
|
|
11055
|
+
m[k].add(v);
|
|
11056
|
+
}
|
|
11057
|
+
return m;
|
|
11058
|
+
}, [
|
|
11059
|
+
records
|
|
11060
|
+
]);
|
|
11005
11061
|
// Reset state when modal (re)opens for a taxon.
|
|
11006
11062
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
11007
11063
|
if (open && taxon) {
|
|
@@ -11169,6 +11225,13 @@ const $4d0c2e01f58b53b1$var$FieldsModalCmp = (props)=>{
|
|
|
11169
11225
|
}),
|
|
11170
11226
|
propTree.map((grp)=>{
|
|
11171
11227
|
const typeRows = grp.types.map((t)=>{
|
|
11228
|
+
// Hide property types that can't discriminate among the
|
|
11229
|
+
// candidates: zero values, or a single value shared by every
|
|
11230
|
+
// record. The selection-aware count (valueSetByKey) is what
|
|
11231
|
+
// we display; the unfiltered count gates visibility so a type
|
|
11232
|
+
// doesn't pop in/out as the user clicks values.
|
|
11233
|
+
const totalDistinct = totalValueCountByKey[t.key] && totalValueCountByKey[t.key].size || 0;
|
|
11234
|
+
if (totalDistinct < 2) return null;
|
|
11172
11235
|
const numValues = valueSetByKey[t.key] && valueSetByKey[t.key].size || 0;
|
|
11173
11236
|
if (numValues === 0) return null;
|
|
11174
11237
|
const typeLabelMatches = isSearching && t.label.toLowerCase().includes(searchLc);
|
|
@@ -11239,37 +11302,32 @@ const $4d0c2e01f58b53b1$var$FieldsModalCmp = (props)=>{
|
|
|
11239
11302
|
}),
|
|
11240
11303
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
11241
11304
|
className: "exprviz-fields-preview",
|
|
11242
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
11305
|
+
children: orderedSelectedKeys.length === 0 ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
11306
|
+
className: "exprviz-fields-placeholder",
|
|
11307
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
11308
|
+
children: "Select a property type to preview the property values for each matching field."
|
|
11309
|
+
})
|
|
11310
|
+
}) : /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("table", {
|
|
11243
11311
|
className: "exprviz-fields-table",
|
|
11244
11312
|
children: [
|
|
11245
11313
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("thead", {
|
|
11246
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
11247
|
-
children:
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
}),
|
|
11251
|
-
orderedSelectedKeys.map((k)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("th", {
|
|
11252
|
-
children: $4d0c2e01f58b53b1$var$labelForKey(k, propTree)
|
|
11253
|
-
}, k))
|
|
11254
|
-
]
|
|
11314
|
+
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("tr", {
|
|
11315
|
+
children: orderedSelectedKeys.map((k)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("th", {
|
|
11316
|
+
children: $4d0c2e01f58b53b1$var$labelForKey(k, propTree)
|
|
11317
|
+
}, k))
|
|
11255
11318
|
})
|
|
11256
11319
|
}),
|
|
11257
11320
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("tbody", {
|
|
11258
11321
|
children: [
|
|
11259
|
-
matchingFields.map((f)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
}),
|
|
11265
|
-
orderedSelectedKeys.map((k)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
11266
|
-
children: f.props[k] || ''
|
|
11267
|
-
}, k))
|
|
11268
|
-
]
|
|
11322
|
+
matchingFields.map((f)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("tr", {
|
|
11323
|
+
title: f.fieldName,
|
|
11324
|
+
children: orderedSelectedKeys.map((k)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
11325
|
+
children: f.props[k] || ''
|
|
11326
|
+
}, k))
|
|
11269
11327
|
}, f.fieldName)),
|
|
11270
11328
|
matchingFields.length === 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("tr", {
|
|
11271
11329
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("td", {
|
|
11272
|
-
colSpan:
|
|
11330
|
+
colSpan: orderedSelectedKeys.length,
|
|
11273
11331
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("em", {
|
|
11274
11332
|
children: "No matching fields"
|
|
11275
11333
|
})
|
|
@@ -11316,7 +11374,6 @@ var $4d0c2e01f58b53b1$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.conn
|
|
|
11316
11374
|
|
|
11317
11375
|
|
|
11318
11376
|
|
|
11319
|
-
|
|
11320
11377
|
const $4ab64e76c1caef59$var$baseColDefs = [
|
|
11321
11378
|
{
|
|
11322
11379
|
field: 'id',
|
|
@@ -11328,6 +11385,7 @@ const $4ab64e76c1caef59$var$baseColDefs = [
|
|
|
11328
11385
|
{
|
|
11329
11386
|
field: 'name',
|
|
11330
11387
|
headerName: 'Name',
|
|
11388
|
+
pinned: 'left',
|
|
11331
11389
|
width: 140,
|
|
11332
11390
|
suppressMovable: true
|
|
11333
11391
|
}
|
|
@@ -11372,189 +11430,287 @@ function $4ab64e76c1caef59$var$buildFieldInfo(fields, studies, expressionSamples
|
|
|
11372
11430
|
}
|
|
11373
11431
|
return info;
|
|
11374
11432
|
}
|
|
11375
|
-
//
|
|
11376
|
-
//
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
const
|
|
11381
|
-
(0, $gXNCa$
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
const
|
|
11390
|
-
if (enableSorting && progressSort) progressSort(event.shiftKey);
|
|
11391
|
-
};
|
|
11392
|
-
const onMenuClick = (event)=>{
|
|
11393
|
-
event.stopPropagation();
|
|
11394
|
-
if (showColumnMenu && menuRef.current) showColumnMenu(menuRef.current);
|
|
11395
|
-
};
|
|
11396
|
-
const factorEntries = info ? Object.entries(info.factors || {}) : [];
|
|
11397
|
-
const charEntries = info ? Object.entries(info.characteristics || {}) : [];
|
|
11398
|
-
const popover = info ? /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Popover), {
|
|
11399
|
-
id: `exprviz-header-${info.studyId}-${info.group}`,
|
|
11400
|
-
className: "exprviz-header-popover",
|
|
11401
|
-
children: [
|
|
11402
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Popover).Header, {
|
|
11403
|
-
as: "h6",
|
|
11404
|
-
children: info.studyDescription
|
|
11405
|
-
}),
|
|
11406
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Popover).Body, {
|
|
11407
|
-
children: [
|
|
11408
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
11409
|
-
children: [
|
|
11410
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("strong", {
|
|
11411
|
-
children: "Study:"
|
|
11412
|
-
}),
|
|
11413
|
-
" ",
|
|
11414
|
-
info.studyId
|
|
11415
|
-
]
|
|
11416
|
-
}),
|
|
11417
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
11418
|
-
children: [
|
|
11419
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("strong", {
|
|
11420
|
-
children: "Group:"
|
|
11421
|
-
}),
|
|
11422
|
-
" ",
|
|
11423
|
-
info.group,
|
|
11424
|
-
" (",
|
|
11425
|
-
info.replicates,
|
|
11426
|
-
" ",
|
|
11427
|
-
info.replicates === 1 ? 'rep' : 'reps',
|
|
11428
|
-
")"
|
|
11429
|
-
]
|
|
11430
|
-
}),
|
|
11431
|
-
factorEntries.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
11432
|
-
className: "exprviz-header-section",
|
|
11433
|
-
children: [
|
|
11434
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("strong", {
|
|
11435
|
-
children: "Factors"
|
|
11436
|
-
}),
|
|
11437
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("ul", {
|
|
11438
|
-
children: factorEntries.map(([t, v])=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
|
|
11439
|
-
children: [
|
|
11440
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
|
|
11441
|
-
children: [
|
|
11442
|
-
t,
|
|
11443
|
-
":"
|
|
11444
|
-
]
|
|
11445
|
-
}),
|
|
11446
|
-
" ",
|
|
11447
|
-
v
|
|
11448
|
-
]
|
|
11449
|
-
}, t))
|
|
11450
|
-
})
|
|
11451
|
-
]
|
|
11452
|
-
}),
|
|
11453
|
-
charEntries.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
11454
|
-
className: "exprviz-header-section",
|
|
11455
|
-
children: [
|
|
11456
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("strong", {
|
|
11457
|
-
children: "Characteristics"
|
|
11458
|
-
}),
|
|
11459
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("ul", {
|
|
11460
|
-
children: charEntries.map(([t, v])=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("li", {
|
|
11461
|
-
children: [
|
|
11462
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("em", {
|
|
11463
|
-
children: [
|
|
11464
|
-
t,
|
|
11465
|
-
":"
|
|
11466
|
-
]
|
|
11467
|
-
}),
|
|
11468
|
-
" ",
|
|
11469
|
-
v
|
|
11470
|
-
]
|
|
11471
|
-
}, t))
|
|
11472
|
-
})
|
|
11473
|
-
]
|
|
11474
|
-
})
|
|
11475
|
-
]
|
|
11476
|
-
})
|
|
11477
|
-
]
|
|
11478
|
-
}) : null;
|
|
11433
|
+
// Plain right-aligned label cell for the metadata header rows above the
|
|
11434
|
+
// Gene ID / Name columns. Rendered as a React component (instead of
|
|
11435
|
+
// relying on ag-grid's default group label) so the alignment is controlled
|
|
11436
|
+
// by our own DOM and isn't fighting the quartz theme's CSS.
|
|
11437
|
+
const $4ab64e76c1caef59$var$LabelHeaderGroup = (props)=>{
|
|
11438
|
+
const { displayName: displayName } = props;
|
|
11439
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
11440
|
+
className: "exprviz-label-header",
|
|
11441
|
+
children: displayName
|
|
11442
|
+
});
|
|
11443
|
+
};
|
|
11444
|
+
// Same idea, but for the cells that toggle a section between expanded and
|
|
11445
|
+
// collapsed. The caret + label sit at the right edge.
|
|
11446
|
+
const $4ab64e76c1caef59$var$ToggleHeaderGroup = (props)=>{
|
|
11447
|
+
const { displayName: displayName, onToggle: onToggle, expanded: expanded, suffix: suffix } = props;
|
|
11479
11448
|
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
11480
|
-
className: "exprviz-header",
|
|
11449
|
+
className: "exprviz-toggle-header",
|
|
11450
|
+
onClick: onToggle,
|
|
11451
|
+
role: "button",
|
|
11452
|
+
tabIndex: 0,
|
|
11453
|
+
onKeyDown: (e)=>{
|
|
11454
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
11455
|
+
e.preventDefault();
|
|
11456
|
+
onToggle && onToggle();
|
|
11457
|
+
}
|
|
11458
|
+
},
|
|
11459
|
+
title: expanded ? 'Click to collapse' : 'Click to expand',
|
|
11481
11460
|
children: [
|
|
11482
|
-
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
11483
|
-
className: "exprviz-
|
|
11484
|
-
|
|
11485
|
-
style: {
|
|
11486
|
-
cursor: enableSorting ? 'pointer' : 'default'
|
|
11487
|
-
},
|
|
11488
|
-
children: [
|
|
11489
|
-
displayName,
|
|
11490
|
-
sort === 'asc' && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
11491
|
-
className: "exprviz-header-sort",
|
|
11492
|
-
children: " \u25B2"
|
|
11493
|
-
}),
|
|
11494
|
-
sort === 'desc' && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
11495
|
-
className: "exprviz-header-sort",
|
|
11496
|
-
children: " \u25BC"
|
|
11497
|
-
})
|
|
11498
|
-
]
|
|
11499
|
-
}),
|
|
11500
|
-
info && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.OverlayTrigger), {
|
|
11501
|
-
trigger: [
|
|
11502
|
-
'hover',
|
|
11503
|
-
'focus'
|
|
11504
|
-
],
|
|
11505
|
-
placement: "bottom",
|
|
11506
|
-
overlay: popover,
|
|
11507
|
-
delay: {
|
|
11508
|
-
show: 200,
|
|
11509
|
-
hide: 100
|
|
11510
|
-
},
|
|
11511
|
-
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
11512
|
-
className: "exprviz-header-info",
|
|
11513
|
-
onClick: (e)=>e.stopPropagation(),
|
|
11514
|
-
onMouseDown: (e)=>e.stopPropagation(),
|
|
11515
|
-
"aria-label": "More info",
|
|
11516
|
-
children: "\u24D8"
|
|
11517
|
-
})
|
|
11461
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
11462
|
+
className: "exprviz-toggle-caret",
|
|
11463
|
+
children: expanded ? "\u25BC" : "\u25B6"
|
|
11518
11464
|
}),
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
onClick: onMenuClick
|
|
11523
|
-
})
|
|
11465
|
+
' ',
|
|
11466
|
+
displayName,
|
|
11467
|
+
suffix ? ` ${suffix}` : ''
|
|
11524
11468
|
]
|
|
11525
11469
|
});
|
|
11526
11470
|
};
|
|
11471
|
+
function $4ab64e76c1caef59$var$exprValueFormatter(p) {
|
|
11472
|
+
const v = p.value;
|
|
11473
|
+
if (v == null) return '';
|
|
11474
|
+
if (Array.isArray(v)) return v.join(', ');
|
|
11475
|
+
if (typeof v === 'object') return JSON.stringify(v);
|
|
11476
|
+
return String(v);
|
|
11477
|
+
}
|
|
11478
|
+
// Discover every distinct factor and characteristic type across the loaded
|
|
11479
|
+
// fields. Each becomes one header row (with adjacent equal-value cells
|
|
11480
|
+
// merged). Order is alphabetical for stability across reloads.
|
|
11481
|
+
function $4ab64e76c1caef59$var$collectMetadataTypes(fields, fieldInfo) {
|
|
11482
|
+
const factorTypes = new Set();
|
|
11483
|
+
const charTypes = new Set();
|
|
11484
|
+
for (const f of fields || []){
|
|
11485
|
+
const info = fieldInfo[f];
|
|
11486
|
+
if (!info) continue;
|
|
11487
|
+
Object.keys(info.factors || {}).forEach((t)=>factorTypes.add(t));
|
|
11488
|
+
Object.keys(info.characteristics || {}).forEach((t)=>charTypes.add(t));
|
|
11489
|
+
}
|
|
11490
|
+
return {
|
|
11491
|
+
factorTypes: Array.from(factorTypes).sort(),
|
|
11492
|
+
charTypes: Array.from(charTypes).sort()
|
|
11493
|
+
};
|
|
11494
|
+
}
|
|
11495
|
+
// Wrap a leaf column in N nested column groups so it participates at every
|
|
11496
|
+
// header row, with a custom label per level. Lets the Gene ID / Name columns
|
|
11497
|
+
// carry the row labels ("Study"/"Factor"/"Characteristic" and the type name).
|
|
11498
|
+
function $4ab64e76c1caef59$var$wrapLeafWithLabels(leaf, labels) {
|
|
11499
|
+
let wrapped = leaf;
|
|
11500
|
+
for(let i = labels.length - 1; i >= 0; i--){
|
|
11501
|
+
const label = labels[i];
|
|
11502
|
+
const cls = label.headerClass ? `${label.headerClass} exprviz-hg-labels` : 'exprviz-hg-labels';
|
|
11503
|
+
wrapped = {
|
|
11504
|
+
headerName: label.headerName,
|
|
11505
|
+
headerClass: cls,
|
|
11506
|
+
// Always render through one of our components so the alignment and
|
|
11507
|
+
// spacing are controlled by our own DOM, not ag-grid's defaults.
|
|
11508
|
+
headerGroupComponent: label.headerGroupComponent || $4ab64e76c1caef59$var$LabelHeaderGroup,
|
|
11509
|
+
headerGroupComponentParams: label.headerGroupComponentParams,
|
|
11510
|
+
children: [
|
|
11511
|
+
wrapped
|
|
11512
|
+
]
|
|
11513
|
+
};
|
|
11514
|
+
}
|
|
11515
|
+
return wrapped;
|
|
11516
|
+
}
|
|
11517
|
+
// Build the column tree:
|
|
11518
|
+
// row 1 — Study | Title | <study description>…
|
|
11519
|
+
// row 2..(2+F-1) — Factor | <factor type> | <value or blank>…
|
|
11520
|
+
// row 2+F..end-1 — Characteristic | <char type> | <value or blank>…
|
|
11521
|
+
// leaf row — Gene ID | Name | g3, g4, …
|
|
11522
|
+
// Adjacent expression cells sharing the same value at a given level merge
|
|
11523
|
+
// (because they're children of one group definition). Blank values for a
|
|
11524
|
+
// type that's defined only in other studies render as empty cells, and
|
|
11525
|
+
// adjacent blanks under the same parent merge automatically.
|
|
11526
|
+
function $4ab64e76c1caef59$var$buildColumnDefs(fields, fieldInfo, expanded, toggles) {
|
|
11527
|
+
if (!fields || fields.length === 0) return $4ab64e76c1caef59$var$baseColDefs;
|
|
11528
|
+
const { factorTypes: factorTypes, charTypes: charTypes } = $4ab64e76c1caef59$var$collectMetadataTypes(fields, fieldInfo);
|
|
11529
|
+
const levels = [
|
|
11530
|
+
{
|
|
11531
|
+
kind: 'study',
|
|
11532
|
+
getValue: (info)=>info && info.studyDescription || ''
|
|
11533
|
+
}
|
|
11534
|
+
];
|
|
11535
|
+
if (expanded.factors) for (const t of factorTypes)levels.push({
|
|
11536
|
+
kind: 'factor',
|
|
11537
|
+
type: t,
|
|
11538
|
+
getValue: (info)=>info && info.factors && info.factors[t] || '',
|
|
11539
|
+
headerClass: 'exprviz-hg-factors'
|
|
11540
|
+
});
|
|
11541
|
+
else if (factorTypes.length > 0) // One placeholder level standing in for the collapsed factor rows.
|
|
11542
|
+
levels.push({
|
|
11543
|
+
kind: 'factors-collapsed',
|
|
11544
|
+
getValue: ()=>'',
|
|
11545
|
+
headerClass: 'exprviz-hg-factors exprviz-hg-collapsed'
|
|
11546
|
+
});
|
|
11547
|
+
if (expanded.chars) for (const t of charTypes)levels.push({
|
|
11548
|
+
kind: 'char',
|
|
11549
|
+
type: t,
|
|
11550
|
+
getValue: (info)=>info && info.characteristics && info.characteristics[t] || '',
|
|
11551
|
+
headerClass: 'exprviz-hg-chars'
|
|
11552
|
+
});
|
|
11553
|
+
else if (charTypes.length > 0) levels.push({
|
|
11554
|
+
kind: 'chars-collapsed',
|
|
11555
|
+
getValue: ()=>'',
|
|
11556
|
+
headerClass: 'exprviz-hg-chars exprviz-hg-collapsed'
|
|
11557
|
+
});
|
|
11558
|
+
// Walk fields, opening a new group at level i (and resetting all levels
|
|
11559
|
+
// below) the first time the value at level i differs from the previous
|
|
11560
|
+
// field's. Same value → same parent group → ag-grid merges the cells.
|
|
11561
|
+
const exprTopGroups = [];
|
|
11562
|
+
const currentGroups = new Array(levels.length).fill(null);
|
|
11563
|
+
const currentKeys = new Array(levels.length).fill(undefined);
|
|
11564
|
+
for (const f of fields){
|
|
11565
|
+
const info = fieldInfo[f] || {};
|
|
11566
|
+
let firstChange = levels.length;
|
|
11567
|
+
for(let i = 0; i < levels.length; i++){
|
|
11568
|
+
const key = levels[i].getValue(info);
|
|
11569
|
+
if (currentGroups[i] === null || currentKeys[i] !== key) {
|
|
11570
|
+
firstChange = i;
|
|
11571
|
+
break;
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
for(let i = firstChange; i < levels.length; i++){
|
|
11575
|
+
const key = levels[i].getValue(info);
|
|
11576
|
+
const group = {
|
|
11577
|
+
headerName: key,
|
|
11578
|
+
headerClass: levels[i].headerClass,
|
|
11579
|
+
children: []
|
|
11580
|
+
};
|
|
11581
|
+
currentGroups[i] = group;
|
|
11582
|
+
currentKeys[i] = key;
|
|
11583
|
+
if (i === 0) exprTopGroups.push(group);
|
|
11584
|
+
else currentGroups[i - 1].children.push(group);
|
|
11585
|
+
}
|
|
11586
|
+
const leaf = {
|
|
11587
|
+
field: f,
|
|
11588
|
+
headerName: info.group || f.replace(/__expr$/, ''),
|
|
11589
|
+
width: 160,
|
|
11590
|
+
suppressMovable: false,
|
|
11591
|
+
valueFormatter: $4ab64e76c1caef59$var$exprValueFormatter
|
|
11592
|
+
};
|
|
11593
|
+
currentGroups[levels.length - 1].children.push(leaf);
|
|
11594
|
+
}
|
|
11595
|
+
// Labels for the two pinned columns. The leftmost column carries the row
|
|
11596
|
+
// category ("Study" / "Factor" / "Characteristic"); the next column shows
|
|
11597
|
+
// the specific row name (the literal "Title" for the study row, then each
|
|
11598
|
+
// factor/characteristic type name). The first row of each
|
|
11599
|
+
// factor/characteristic section gets a clickable caret that toggles the
|
|
11600
|
+
// section between expanded (one row per type) and collapsed (one
|
|
11601
|
+
// placeholder row).
|
|
11602
|
+
const studyLabels = [
|
|
11603
|
+
{
|
|
11604
|
+
headerName: 'Study'
|
|
11605
|
+
}
|
|
11606
|
+
];
|
|
11607
|
+
const titleLabels = [
|
|
11608
|
+
{
|
|
11609
|
+
headerName: 'Title'
|
|
11610
|
+
}
|
|
11611
|
+
];
|
|
11612
|
+
if (expanded.factors) factorTypes.forEach((t, i)=>{
|
|
11613
|
+
studyLabels.push({
|
|
11614
|
+
headerName: 'Factor',
|
|
11615
|
+
headerClass: 'exprviz-hg-factors',
|
|
11616
|
+
...i === 0 ? {
|
|
11617
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
11618
|
+
headerGroupComponentParams: {
|
|
11619
|
+
onToggle: toggles.toggleFactors,
|
|
11620
|
+
expanded: true
|
|
11621
|
+
}
|
|
11622
|
+
} : {}
|
|
11623
|
+
});
|
|
11624
|
+
titleLabels.push({
|
|
11625
|
+
headerName: t,
|
|
11626
|
+
headerClass: 'exprviz-hg-factors'
|
|
11627
|
+
});
|
|
11628
|
+
});
|
|
11629
|
+
else if (factorTypes.length > 0) {
|
|
11630
|
+
studyLabels.push({
|
|
11631
|
+
headerName: 'Factors',
|
|
11632
|
+
headerClass: 'exprviz-hg-factors exprviz-hg-collapsed',
|
|
11633
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
11634
|
+
headerGroupComponentParams: {
|
|
11635
|
+
onToggle: toggles.toggleFactors,
|
|
11636
|
+
expanded: false,
|
|
11637
|
+
suffix: `(${factorTypes.length})`
|
|
11638
|
+
}
|
|
11639
|
+
});
|
|
11640
|
+
titleLabels.push({
|
|
11641
|
+
headerName: '',
|
|
11642
|
+
headerClass: 'exprviz-hg-factors exprviz-hg-collapsed'
|
|
11643
|
+
});
|
|
11644
|
+
}
|
|
11645
|
+
if (expanded.chars) charTypes.forEach((t, i)=>{
|
|
11646
|
+
studyLabels.push({
|
|
11647
|
+
headerName: 'Characteristic',
|
|
11648
|
+
headerClass: 'exprviz-hg-chars',
|
|
11649
|
+
...i === 0 ? {
|
|
11650
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
11651
|
+
headerGroupComponentParams: {
|
|
11652
|
+
onToggle: toggles.toggleChars,
|
|
11653
|
+
expanded: true
|
|
11654
|
+
}
|
|
11655
|
+
} : {}
|
|
11656
|
+
});
|
|
11657
|
+
titleLabels.push({
|
|
11658
|
+
headerName: t,
|
|
11659
|
+
headerClass: 'exprviz-hg-chars'
|
|
11660
|
+
});
|
|
11661
|
+
});
|
|
11662
|
+
else if (charTypes.length > 0) {
|
|
11663
|
+
studyLabels.push({
|
|
11664
|
+
headerName: 'Characteristics',
|
|
11665
|
+
headerClass: 'exprviz-hg-chars exprviz-hg-collapsed',
|
|
11666
|
+
headerGroupComponent: $4ab64e76c1caef59$var$ToggleHeaderGroup,
|
|
11667
|
+
headerGroupComponentParams: {
|
|
11668
|
+
onToggle: toggles.toggleChars,
|
|
11669
|
+
expanded: false,
|
|
11670
|
+
suffix: `(${charTypes.length})`
|
|
11671
|
+
}
|
|
11672
|
+
});
|
|
11673
|
+
titleLabels.push({
|
|
11674
|
+
headerName: '',
|
|
11675
|
+
headerClass: 'exprviz-hg-chars exprviz-hg-collapsed'
|
|
11676
|
+
});
|
|
11677
|
+
}
|
|
11678
|
+
const idCol = $4ab64e76c1caef59$var$wrapLeafWithLabels($4ab64e76c1caef59$var$baseColDefs[0], studyLabels);
|
|
11679
|
+
const nameCol = $4ab64e76c1caef59$var$wrapLeafWithLabels($4ab64e76c1caef59$var$baseColDefs[1], titleLabels);
|
|
11680
|
+
return [
|
|
11681
|
+
idCol,
|
|
11682
|
+
nameCol,
|
|
11683
|
+
...exprTopGroups
|
|
11684
|
+
];
|
|
11685
|
+
}
|
|
11527
11686
|
const $4ab64e76c1caef59$var$ExprTable = ({ rows: rows, fields: fields, onReorder: onReorder, studies: studies, expressionSamples: expressionSamples, onHoverRow: onHoverRow })=>{
|
|
11528
11687
|
const fieldInfo = (0, $gXNCa$react.useMemo)(()=>$4ab64e76c1caef59$var$buildFieldInfo(fields, studies, expressionSamples), [
|
|
11529
11688
|
fields,
|
|
11530
11689
|
studies,
|
|
11531
11690
|
expressionSamples
|
|
11532
11691
|
]);
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
}));
|
|
11551
|
-
return [
|
|
11552
|
-
...$4ab64e76c1caef59$var$baseColDefs,
|
|
11553
|
-
...expressionCols
|
|
11554
|
-
];
|
|
11555
|
-
}, [
|
|
11692
|
+
// Default: factor rows expanded, characteristic rows collapsed (per spec).
|
|
11693
|
+
const [expanded, setExpanded] = (0, $gXNCa$react.useState)({
|
|
11694
|
+
factors: true,
|
|
11695
|
+
chars: false
|
|
11696
|
+
});
|
|
11697
|
+
const toggleFactors = (0, $gXNCa$react.useCallback)(()=>setExpanded((e)=>({
|
|
11698
|
+
...e,
|
|
11699
|
+
factors: !e.factors
|
|
11700
|
+
})), []);
|
|
11701
|
+
const toggleChars = (0, $gXNCa$react.useCallback)(()=>setExpanded((e)=>({
|
|
11702
|
+
...e,
|
|
11703
|
+
chars: !e.chars
|
|
11704
|
+
})), []);
|
|
11705
|
+
const columnDefs = (0, $gXNCa$react.useMemo)(()=>$4ab64e76c1caef59$var$buildColumnDefs(fields, fieldInfo, expanded, {
|
|
11706
|
+
toggleFactors: toggleFactors,
|
|
11707
|
+
toggleChars: toggleChars
|
|
11708
|
+
}), [
|
|
11556
11709
|
fields,
|
|
11557
|
-
fieldInfo
|
|
11710
|
+
fieldInfo,
|
|
11711
|
+
expanded,
|
|
11712
|
+
toggleFactors,
|
|
11713
|
+
toggleChars
|
|
11558
11714
|
]);
|
|
11559
11715
|
const onColumnMoved = (e)=>{
|
|
11560
11716
|
if (!onReorder || !e.finished) return;
|
|
@@ -11577,11 +11733,14 @@ const $4ab64e76c1caef59$var$ExprTable = ({ rows: rows, fields: fields, onReorder
|
|
|
11577
11733
|
defaultColDef: {
|
|
11578
11734
|
resizable: true,
|
|
11579
11735
|
sortable: true,
|
|
11580
|
-
filter:
|
|
11736
|
+
filter: false,
|
|
11737
|
+
suppressMenu: true
|
|
11581
11738
|
},
|
|
11582
11739
|
animateRows: false,
|
|
11583
11740
|
suppressFieldDotNotation: true,
|
|
11584
11741
|
suppressDragLeaveHidesColumns: true,
|
|
11742
|
+
suppressColumnVirtualisation: true,
|
|
11743
|
+
groupHeaderHeight: 24,
|
|
11585
11744
|
onColumnMoved: onColumnMoved,
|
|
11586
11745
|
onCellMouseOver: (e)=>onHoverRow && onHoverRow(e.data && e.data.id),
|
|
11587
11746
|
onCellMouseOut: ()=>onHoverRow && onHoverRow(null)
|
|
@@ -11647,12 +11806,41 @@ function $caf32827df861c4e$var$logTickFormat(v) {
|
|
|
11647
11806
|
if (a >= 0.01 && a < 10000) return $gXNCa$d3.format('~g')(v);
|
|
11648
11807
|
return $gXNCa$d3.format('.0e')(v);
|
|
11649
11808
|
}
|
|
11650
|
-
const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields, scale: scale = 'linear', onBrushChange: onBrushChange, onReorder: onReorder, clearVersion: clearVersion = 0, hoveredId: hoveredId = null })=>{
|
|
11809
|
+
const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields, scale: scale = 'linear', onBrushChange: onBrushChange, onReorder: onReorder, clearVersion: clearVersion = 0, hoveredId: hoveredId = null, axisLabels: axisLabels = null })=>{
|
|
11651
11810
|
const svgRef = (0, $gXNCa$react.useRef)(null);
|
|
11652
11811
|
const containerRef = (0, $gXNCa$react.useRef)(null);
|
|
11653
11812
|
// selections in data domain: { [field]: [lo, hi] }
|
|
11654
11813
|
const selectionsRef = (0, $gXNCa$react.useRef)({});
|
|
11655
11814
|
const lastClearRef = (0, $gXNCa$react.useRef)(0);
|
|
11815
|
+
// Track container size so the d3 render reruns when the user drags the
|
|
11816
|
+
// pane resizer (or when the window is resized). The values themselves
|
|
11817
|
+
// aren't read inside the effect — the effect always reads clientWidth/
|
|
11818
|
+
// clientHeight — but listing them in the deps array is what triggers it.
|
|
11819
|
+
const [size, setSize] = (0, $gXNCa$react.useState)({
|
|
11820
|
+
w: 0,
|
|
11821
|
+
h: 0
|
|
11822
|
+
});
|
|
11823
|
+
// Custom HTML tooltip for axis labels — gives us bold labels and structured
|
|
11824
|
+
// sections, which the native SVG <title> can't do.
|
|
11825
|
+
const [tooltip, setTooltip] = (0, $gXNCa$react.useState)(null);
|
|
11826
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
11827
|
+
const el = containerRef.current;
|
|
11828
|
+
if (!el || typeof ResizeObserver === 'undefined') return;
|
|
11829
|
+
const ro = new ResizeObserver((entries)=>{
|
|
11830
|
+
for (const entry of entries){
|
|
11831
|
+
const { width: width, height: height } = entry.contentRect;
|
|
11832
|
+
setSize((prev)=>{
|
|
11833
|
+
if (Math.abs(prev.w - width) < 1 && Math.abs(prev.h - height) < 1) return prev;
|
|
11834
|
+
return {
|
|
11835
|
+
w: width,
|
|
11836
|
+
h: height
|
|
11837
|
+
};
|
|
11838
|
+
});
|
|
11839
|
+
}
|
|
11840
|
+
});
|
|
11841
|
+
ro.observe(el);
|
|
11842
|
+
return ()=>ro.disconnect();
|
|
11843
|
+
}, []);
|
|
11656
11844
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
11657
11845
|
if (clearVersion !== lastClearRef.current) {
|
|
11658
11846
|
selectionsRef.current = {};
|
|
@@ -11749,9 +11937,32 @@ const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields,
|
|
|
11749
11937
|
if (scale === 'log') axisGen.tickValues($caf32827df861c4e$var$logTickValues(yByField[f].domain())).tickFormat($caf32827df861c4e$var$logTickFormat);
|
|
11750
11938
|
else axisGen.ticks(5);
|
|
11751
11939
|
ax.call(axisGen);
|
|
11752
|
-
|
|
11940
|
+
// Compact axis label. Hovering the label or its drag-handle rect shows
|
|
11941
|
+
// a custom HTML tooltip (rendered outside the SVG by React) that can
|
|
11942
|
+
// include bold labels and section headings.
|
|
11943
|
+
const labelInfo = axisLabels && axisLabels[f] || {
|
|
11944
|
+
short: f.replace(/__expr$/, ''),
|
|
11945
|
+
structured: {
|
|
11946
|
+
studyTitle: f,
|
|
11947
|
+
group: '',
|
|
11948
|
+
factors: [],
|
|
11949
|
+
characteristics: []
|
|
11950
|
+
}
|
|
11951
|
+
};
|
|
11952
|
+
const showTip = (event)=>setTooltip({
|
|
11953
|
+
x: event.clientX,
|
|
11954
|
+
y: event.clientY,
|
|
11955
|
+
info: labelInfo.structured
|
|
11956
|
+
});
|
|
11957
|
+
const moveTip = (event)=>setTooltip((t)=>t ? {
|
|
11958
|
+
...t,
|
|
11959
|
+
x: event.clientX,
|
|
11960
|
+
y: event.clientY
|
|
11961
|
+
} : null);
|
|
11962
|
+
const hideTip = ()=>setTooltip(null);
|
|
11963
|
+
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);
|
|
11753
11964
|
// hit area for grabbing — sits along the rotated label
|
|
11754
|
-
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');
|
|
11965
|
+
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);
|
|
11755
11966
|
const brush = $gXNCa$d3.brushY().extent([
|
|
11756
11967
|
[
|
|
11757
11968
|
-$caf32827df861c4e$var$BRUSH_WIDTH / 2,
|
|
@@ -11844,7 +12055,10 @@ const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields,
|
|
|
11844
12055
|
scale,
|
|
11845
12056
|
onBrushChange,
|
|
11846
12057
|
onReorder,
|
|
11847
|
-
clearVersion
|
|
12058
|
+
clearVersion,
|
|
12059
|
+
axisLabels,
|
|
12060
|
+
size.w,
|
|
12061
|
+
size.h
|
|
11848
12062
|
]);
|
|
11849
12063
|
// Highlight the polyline matching the hovered row id without rebuilding the
|
|
11850
12064
|
// SVG. Raises the highlighted path so it draws above its neighbors.
|
|
@@ -11870,15 +12084,114 @@ const $caf32827df861c4e$var$ParallelCoordsPlot = ({ rows: rows, fields: fields,
|
|
|
11870
12084
|
children: "Select fields to plot."
|
|
11871
12085
|
})
|
|
11872
12086
|
});
|
|
11873
|
-
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.
|
|
12087
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
11874
12088
|
ref: containerRef,
|
|
11875
12089
|
className: "exprviz-pc-container",
|
|
11876
|
-
children:
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
12090
|
+
children: [
|
|
12091
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("svg", {
|
|
12092
|
+
ref: svgRef,
|
|
12093
|
+
width: "100%",
|
|
12094
|
+
height: "100%",
|
|
12095
|
+
preserveAspectRatio: "none"
|
|
12096
|
+
}),
|
|
12097
|
+
tooltip && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)($caf32827df861c4e$var$AxisTooltip, {
|
|
12098
|
+
x: tooltip.x,
|
|
12099
|
+
y: tooltip.y,
|
|
12100
|
+
info: tooltip.info
|
|
12101
|
+
})
|
|
12102
|
+
]
|
|
12103
|
+
});
|
|
12104
|
+
};
|
|
12105
|
+
// Position-fixed so it can escape the plot pane's clipping. Offset slightly
|
|
12106
|
+
// from the cursor and clamped to the viewport so it never spills off-screen.
|
|
12107
|
+
const $caf32827df861c4e$var$AxisTooltip = ({ x: x, y: y, info: info })=>{
|
|
12108
|
+
const ref = (0, $gXNCa$react.useRef)(null);
|
|
12109
|
+
const [pos, setPos] = (0, $gXNCa$react.useState)({
|
|
12110
|
+
left: x + 12,
|
|
12111
|
+
top: y + 12
|
|
12112
|
+
});
|
|
12113
|
+
(0, $gXNCa$react.useEffect)(()=>{
|
|
12114
|
+
const el = ref.current;
|
|
12115
|
+
if (!el) return;
|
|
12116
|
+
const w = el.offsetWidth;
|
|
12117
|
+
const h = el.offsetHeight;
|
|
12118
|
+
const vw = window.innerWidth;
|
|
12119
|
+
const vh = window.innerHeight;
|
|
12120
|
+
let left = x + 12;
|
|
12121
|
+
let top = y + 12;
|
|
12122
|
+
if (left + w > vw - 4) left = Math.max(4, x - 12 - w);
|
|
12123
|
+
if (top + h > vh - 4) top = Math.max(4, y - 12 - h);
|
|
12124
|
+
setPos({
|
|
12125
|
+
left: left,
|
|
12126
|
+
top: top
|
|
12127
|
+
});
|
|
12128
|
+
}, [
|
|
12129
|
+
x,
|
|
12130
|
+
y,
|
|
12131
|
+
info
|
|
12132
|
+
]);
|
|
12133
|
+
const { studyTitle: studyTitle, group: group, factors: factors, characteristics: characteristics } = info;
|
|
12134
|
+
return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12135
|
+
ref: ref,
|
|
12136
|
+
className: "exprviz-pc-tooltip",
|
|
12137
|
+
style: pos,
|
|
12138
|
+
children: [
|
|
12139
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12140
|
+
children: [
|
|
12141
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("span", {
|
|
12142
|
+
className: "exprviz-pc-tip-key",
|
|
12143
|
+
children: "Study:"
|
|
12144
|
+
}),
|
|
12145
|
+
" ",
|
|
12146
|
+
studyTitle,
|
|
12147
|
+
group ? ` (${group})` : ''
|
|
12148
|
+
]
|
|
12149
|
+
}),
|
|
12150
|
+
factors.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
12151
|
+
children: [
|
|
12152
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12153
|
+
className: "exprviz-pc-tip-section",
|
|
12154
|
+
children: "Factors"
|
|
12155
|
+
}),
|
|
12156
|
+
factors.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12157
|
+
className: "exprviz-pc-tip-row",
|
|
12158
|
+
children: [
|
|
12159
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
|
|
12160
|
+
className: "exprviz-pc-tip-key",
|
|
12161
|
+
children: [
|
|
12162
|
+
p.name,
|
|
12163
|
+
":"
|
|
12164
|
+
]
|
|
12165
|
+
}),
|
|
12166
|
+
" ",
|
|
12167
|
+
p.value
|
|
12168
|
+
]
|
|
12169
|
+
}, `f-${i}`))
|
|
12170
|
+
]
|
|
12171
|
+
}),
|
|
12172
|
+
characteristics.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactjsxruntime.Fragment), {
|
|
12173
|
+
children: [
|
|
12174
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12175
|
+
className: "exprviz-pc-tip-section",
|
|
12176
|
+
children: "Characteristics"
|
|
12177
|
+
}),
|
|
12178
|
+
characteristics.map((p, i)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("div", {
|
|
12179
|
+
className: "exprviz-pc-tip-row",
|
|
12180
|
+
children: [
|
|
12181
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)("span", {
|
|
12182
|
+
className: "exprviz-pc-tip-key",
|
|
12183
|
+
children: [
|
|
12184
|
+
p.name,
|
|
12185
|
+
":"
|
|
12186
|
+
]
|
|
12187
|
+
}),
|
|
12188
|
+
" ",
|
|
12189
|
+
p.value
|
|
12190
|
+
]
|
|
12191
|
+
}, `c-${i}`))
|
|
12192
|
+
]
|
|
12193
|
+
})
|
|
12194
|
+
]
|
|
11882
12195
|
});
|
|
11883
12196
|
};
|
|
11884
12197
|
var $caf32827df861c4e$export$2e2bcd8739ae039 = $caf32827df861c4e$var$ParallelCoordsPlot;
|
|
@@ -11975,6 +12288,75 @@ const $1fd2507769d5bd00$var$ExprVizViewCmp = (props)=>{
|
|
|
11975
12288
|
]
|
|
11976
12289
|
});
|
|
11977
12290
|
};
|
|
12291
|
+
// Compact axis labels for the parallel-coords plot. The raw Solr field name
|
|
12292
|
+
// (e.g. "E_CURD_148_g5__expr") is uninformative; we prefer the assay's
|
|
12293
|
+
// factor labels, falling back to "organism part" then to the group id.
|
|
12294
|
+
// `full` is exposed via an SVG <title> so the user can hover to see study,
|
|
12295
|
+
// group, and every factor/characteristic.
|
|
12296
|
+
const $1fd2507769d5bd00$var$AXIS_LABEL_MAX = 22;
|
|
12297
|
+
function $1fd2507769d5bd00$var$truncateLabel(s, n) {
|
|
12298
|
+
if (!s) return '';
|
|
12299
|
+
return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
|
|
12300
|
+
}
|
|
12301
|
+
function $1fd2507769d5bd00$var$compactAssayLabel(assay, group) {
|
|
12302
|
+
if (!assay) return group || '';
|
|
12303
|
+
const factorVals = (assay.factor || []).map((f)=>f && f.label).filter(Boolean);
|
|
12304
|
+
if (factorVals.length) return factorVals.join('; ');
|
|
12305
|
+
const chars = assay.characteristic || [];
|
|
12306
|
+
const organ = chars.find((c)=>c && c.type === 'organism part');
|
|
12307
|
+
if (organ && organ.label) return organ.label;
|
|
12308
|
+
const firstChar = chars.find((c)=>c && c.label);
|
|
12309
|
+
if (firstChar) return firstChar.label;
|
|
12310
|
+
return group || '';
|
|
12311
|
+
}
|
|
12312
|
+
function $1fd2507769d5bd00$var$assayPairs(list) {
|
|
12313
|
+
return (list || []).filter((x)=>x && x.label).map((x)=>({
|
|
12314
|
+
name: x.type || '',
|
|
12315
|
+
value: x.label
|
|
12316
|
+
}));
|
|
12317
|
+
}
|
|
12318
|
+
function $1fd2507769d5bd00$var$buildAxisLabels(fields, studies, expressionSamples) {
|
|
12319
|
+
const labels = {};
|
|
12320
|
+
if (!fields) return labels;
|
|
12321
|
+
const studyById = {};
|
|
12322
|
+
(studies || []).forEach((s)=>{
|
|
12323
|
+
if (s && s._id) studyById[s._id] = s;
|
|
12324
|
+
});
|
|
12325
|
+
const findAssay = (studyId, group)=>{
|
|
12326
|
+
const arr = expressionSamples && expressionSamples[studyId];
|
|
12327
|
+
return arr ? arr.find((a)=>a.group === group) : null;
|
|
12328
|
+
};
|
|
12329
|
+
for (const f of fields){
|
|
12330
|
+
const m = f.match(/^(.+?)_g(\d+)__expr$/);
|
|
12331
|
+
if (!m) {
|
|
12332
|
+
labels[f] = {
|
|
12333
|
+
short: $1fd2507769d5bd00$var$truncateLabel(f.replace(/__expr$/, ''), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
|
|
12334
|
+
structured: {
|
|
12335
|
+
studyTitle: f,
|
|
12336
|
+
group: '',
|
|
12337
|
+
factors: [],
|
|
12338
|
+
characteristics: []
|
|
12339
|
+
}
|
|
12340
|
+
};
|
|
12341
|
+
continue;
|
|
12342
|
+
}
|
|
12343
|
+
const expId = m[1].replace(/_/g, '-');
|
|
12344
|
+
const group = 'g' + m[2];
|
|
12345
|
+
const assay = findAssay(expId, group);
|
|
12346
|
+
const study = studyById[expId];
|
|
12347
|
+
const studyName = study && study.description || expId;
|
|
12348
|
+
labels[f] = {
|
|
12349
|
+
short: $1fd2507769d5bd00$var$truncateLabel($1fd2507769d5bd00$var$compactAssayLabel(assay, group), $1fd2507769d5bd00$var$AXIS_LABEL_MAX),
|
|
12350
|
+
structured: {
|
|
12351
|
+
studyTitle: studyName,
|
|
12352
|
+
group: group,
|
|
12353
|
+
factors: $1fd2507769d5bd00$var$assayPairs(assay && assay.factor),
|
|
12354
|
+
characteristics: $1fd2507769d5bd00$var$assayPairs(assay && assay.characteristic)
|
|
12355
|
+
}
|
|
12356
|
+
};
|
|
12357
|
+
}
|
|
12358
|
+
return labels;
|
|
12359
|
+
}
|
|
11978
12360
|
function $1fd2507769d5bd00$var$rowMatchesSelections(row, selections) {
|
|
11979
12361
|
for (const f of Object.keys(selections)){
|
|
11980
12362
|
const v = row[f];
|
|
@@ -12038,6 +12420,34 @@ const $1fd2507769d5bd00$var$TaxonPanel = ({ taxon: taxon, studies: studies, expr
|
|
|
12038
12420
|
const [selections, setSelections] = (0, $gXNCa$react.useState)({});
|
|
12039
12421
|
const [clearVersion, setClearVersion] = (0, $gXNCa$react.useState)(0);
|
|
12040
12422
|
const [hoveredId, setHoveredId] = (0, $gXNCa$react.useState)(null);
|
|
12423
|
+
const [plotHeight, setPlotHeight] = (0, $gXNCa$react.useState)(320);
|
|
12424
|
+
const resizeStateRef = (0, $gXNCa$react.useRef)(null);
|
|
12425
|
+
// Drag the horizontal separator between the plot and the table to retune
|
|
12426
|
+
// their relative sizes. Bounded so neither pane disappears entirely.
|
|
12427
|
+
const startResize = (e)=>{
|
|
12428
|
+
e.preventDefault();
|
|
12429
|
+
resizeStateRef.current = {
|
|
12430
|
+
startY: e.clientY,
|
|
12431
|
+
startHeight: plotHeight
|
|
12432
|
+
};
|
|
12433
|
+
const onMove = (ev)=>{
|
|
12434
|
+
const s = resizeStateRef.current;
|
|
12435
|
+
if (!s) return;
|
|
12436
|
+
const next = Math.max(120, Math.min(1200, s.startHeight + (ev.clientY - s.startY)));
|
|
12437
|
+
setPlotHeight(next);
|
|
12438
|
+
};
|
|
12439
|
+
const onUp = ()=>{
|
|
12440
|
+
resizeStateRef.current = null;
|
|
12441
|
+
document.removeEventListener('mousemove', onMove);
|
|
12442
|
+
document.removeEventListener('mouseup', onUp);
|
|
12443
|
+
document.body.style.cursor = '';
|
|
12444
|
+
document.body.style.userSelect = '';
|
|
12445
|
+
};
|
|
12446
|
+
document.body.style.cursor = 'row-resize';
|
|
12447
|
+
document.body.style.userSelect = 'none';
|
|
12448
|
+
document.addEventListener('mousemove', onMove);
|
|
12449
|
+
document.addEventListener('mouseup', onUp);
|
|
12450
|
+
};
|
|
12041
12451
|
const hasBrush = Object.keys(selections).length > 0;
|
|
12042
12452
|
const filteredRows = (0, $gXNCa$react.useMemo)(()=>{
|
|
12043
12453
|
if (!hasBrush) return rows;
|
|
@@ -12068,6 +12478,11 @@ const $1fd2507769d5bd00$var$TaxonPanel = ({ taxon: taxon, studies: studies, expr
|
|
|
12068
12478
|
...hidden
|
|
12069
12479
|
]);
|
|
12070
12480
|
} : undefined;
|
|
12481
|
+
const axisLabels = (0, $gXNCa$react.useMemo)(()=>$1fd2507769d5bd00$var$buildAxisLabels(visibleFields, studies, expressionSamples), [
|
|
12482
|
+
visibleFields,
|
|
12483
|
+
studies,
|
|
12484
|
+
expressionSamples
|
|
12485
|
+
]);
|
|
12071
12486
|
(0, $gXNCa$react.useEffect)(()=>{
|
|
12072
12487
|
if (rows.length === 0 && hasBrush) {
|
|
12073
12488
|
setSelections({});
|
|
@@ -12175,6 +12590,9 @@ const $1fd2507769d5bd00$var$TaxonPanel = ({ taxon: taxon, studies: studies, expr
|
|
|
12175
12590
|
children: [
|
|
12176
12591
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12177
12592
|
className: "exprviz-plot",
|
|
12593
|
+
style: {
|
|
12594
|
+
height: plotHeight
|
|
12595
|
+
},
|
|
12178
12596
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $caf32827df861c4e$export$2e2bcd8739ae039), {
|
|
12179
12597
|
rows: rows,
|
|
12180
12598
|
fields: visibleFields,
|
|
@@ -12182,9 +12600,18 @@ const $1fd2507769d5bd00$var$TaxonPanel = ({ taxon: taxon, studies: studies, expr
|
|
|
12182
12600
|
onBrushChange: setSelections,
|
|
12183
12601
|
onReorder: handleReorder,
|
|
12184
12602
|
clearVersion: clearVersion,
|
|
12185
|
-
hoveredId: hoveredId
|
|
12603
|
+
hoveredId: hoveredId,
|
|
12604
|
+
axisLabels: axisLabels
|
|
12186
12605
|
})
|
|
12187
12606
|
}),
|
|
12607
|
+
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12608
|
+
className: "exprviz-resizer",
|
|
12609
|
+
role: "separator",
|
|
12610
|
+
"aria-orientation": "horizontal",
|
|
12611
|
+
"aria-label": "Resize plot",
|
|
12612
|
+
onMouseDown: startResize,
|
|
12613
|
+
title: "Drag to resize"
|
|
12614
|
+
}),
|
|
12188
12615
|
/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
|
|
12189
12616
|
className: "exprviz-table",
|
|
12190
12617
|
children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $4ab64e76c1caef59$export$2e2bcd8739ae039), {
|