@jbrowse/plugin-variants 3.1.0 → 3.2.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.
Files changed (75) hide show
  1. package/dist/MultiLinearVariantDisplay/model.d.ts +19 -0
  2. package/dist/MultiLinearVariantMatrixDisplay/model.d.ts +20 -0
  3. package/dist/MultiLinearVariantMatrixDisplay/model.js +4 -1
  4. package/dist/MultiLinearVariantMatrixRenderer/makeImageData.js +9 -1
  5. package/dist/MultiLinearVariantRenderer/makeImageData.js +9 -1
  6. package/dist/VariantRPC/MultiVariantClusterGenotypeMatrix.d.ts +28 -0
  7. package/dist/VariantRPC/MultiVariantClusterGenotypeMatrix.js +33 -0
  8. package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +13 -11
  9. package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.js +5 -57
  10. package/dist/VariantRPC/MultiVariantGetSimplifiedFeatures.d.ts +11 -8
  11. package/dist/VariantRPC/MultiVariantGetSimplifiedFeatures.js +1 -2
  12. package/dist/VariantRPC/cluster.d.ts +17 -0
  13. package/dist/VariantRPC/cluster.js +84 -0
  14. package/dist/VariantRPC/getGenotypeMatrix.d.ts +6 -0
  15. package/dist/VariantRPC/getGenotypeMatrix.js +55 -0
  16. package/dist/VariantRPC/types.d.ts +13 -0
  17. package/dist/VariantRPC/types.js +2 -0
  18. package/dist/VcfAdapter/VcfAdapter.js +6 -3
  19. package/dist/VcfFeature/util.js +2 -2
  20. package/dist/VcfTabixAdapter/VcfTabixAdapter.js +11 -6
  21. package/dist/index.js +2 -0
  22. package/dist/shared/MultiVariantBaseModel.d.ts +19 -0
  23. package/dist/shared/MultiVariantBaseModel.js +27 -35
  24. package/dist/shared/components/ClusterDialog/ClusterDialog.d.ts +6 -0
  25. package/dist/shared/components/ClusterDialog/ClusterDialog.js +29 -0
  26. package/dist/shared/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
  27. package/dist/shared/components/ClusterDialog/ClusterDialogAuto.js +69 -0
  28. package/dist/shared/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
  29. package/dist/shared/components/ClusterDialog/ClusterDialogManual.js +144 -0
  30. package/dist/shared/components/ClusterDialog/types.d.ts +9 -0
  31. package/dist/shared/components/ClusterDialog/types.js +2 -0
  32. package/dist/shared/components/SourcesDataGrid.js +47 -40
  33. package/dist/shared/components/SourcesGridHeader.js +2 -2
  34. package/dist/shared/getSources.d.ts +15 -0
  35. package/dist/shared/getSources.js +34 -0
  36. package/esm/MultiLinearVariantDisplay/model.d.ts +19 -0
  37. package/esm/MultiLinearVariantMatrixDisplay/model.d.ts +20 -0
  38. package/esm/MultiLinearVariantMatrixDisplay/model.js +4 -1
  39. package/esm/MultiLinearVariantMatrixRenderer/makeImageData.js +9 -1
  40. package/esm/MultiLinearVariantRenderer/makeImageData.js +9 -1
  41. package/esm/VariantRPC/MultiVariantClusterGenotypeMatrix.d.ts +28 -0
  42. package/esm/VariantRPC/MultiVariantClusterGenotypeMatrix.js +26 -0
  43. package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +13 -11
  44. package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.js +5 -57
  45. package/esm/VariantRPC/MultiVariantGetSimplifiedFeatures.d.ts +11 -8
  46. package/esm/VariantRPC/MultiVariantGetSimplifiedFeatures.js +1 -2
  47. package/esm/VariantRPC/cluster.d.ts +17 -0
  48. package/esm/VariantRPC/cluster.js +79 -0
  49. package/esm/VariantRPC/getGenotypeMatrix.d.ts +6 -0
  50. package/esm/VariantRPC/getGenotypeMatrix.js +52 -0
  51. package/esm/VariantRPC/types.d.ts +13 -0
  52. package/esm/VariantRPC/types.js +1 -0
  53. package/esm/VcfAdapter/VcfAdapter.js +7 -4
  54. package/esm/VcfFeature/util.js +2 -2
  55. package/esm/VcfTabixAdapter/VcfTabixAdapter.js +11 -6
  56. package/esm/index.js +2 -0
  57. package/esm/shared/MultiVariantBaseModel.d.ts +19 -0
  58. package/esm/shared/MultiVariantBaseModel.js +27 -35
  59. package/esm/shared/components/ClusterDialog/ClusterDialog.d.ts +6 -0
  60. package/esm/shared/components/ClusterDialog/ClusterDialog.js +24 -0
  61. package/esm/shared/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
  62. package/esm/shared/components/ClusterDialog/ClusterDialogAuto.js +67 -0
  63. package/esm/shared/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
  64. package/esm/shared/components/ClusterDialog/ClusterDialogManual.js +139 -0
  65. package/esm/shared/components/ClusterDialog/types.d.ts +9 -0
  66. package/esm/shared/components/ClusterDialog/types.js +1 -0
  67. package/esm/shared/components/SourcesDataGrid.js +47 -40
  68. package/esm/shared/components/SourcesGridHeader.js +2 -2
  69. package/esm/shared/getSources.d.ts +15 -0
  70. package/esm/shared/getSources.js +31 -0
  71. package/package.json +6 -6
  72. package/dist/shared/components/ClusterDialog.d.ts +0 -11
  73. package/dist/shared/components/ClusterDialog.js +0 -113
  74. package/esm/shared/components/ClusterDialog.d.ts +0 -11
  75. package/esm/shared/components/ClusterDialog.js +0 -107
@@ -3,15 +3,17 @@ import { ConfigurationReference } from '@jbrowse/core/configuration';
3
3
  import { getSession } from '@jbrowse/core/util';
4
4
  import { stopStopToken } from '@jbrowse/core/util/stopToken';
5
5
  import { linearBareDisplayStateModelFactory } from '@jbrowse/plugin-linear-genome-view';
6
+ import CategoryIcon from '@mui/icons-material/Category';
6
7
  import FilterListIcon from '@mui/icons-material/FilterList';
7
8
  import HeightIcon from '@mui/icons-material/Height';
8
9
  import SplitscreenIcon from '@mui/icons-material/Splitscreen';
9
10
  import VisibilityIcon from '@mui/icons-material/Visibility';
10
11
  import deepEqual from 'fast-deep-equal';
11
12
  import { types } from 'mobx-state-tree';
13
+ import { getSources } from './getSources';
12
14
  const SetColorDialog = lazy(() => import('./components/SetColorDialog'));
13
15
  const MAFFilterDialog = lazy(() => import('./components/MAFFilterDialog'));
14
- const ClusterDialog = lazy(() => import('./components/ClusterDialog'));
16
+ const ClusterDialog = lazy(() => import('./components/ClusterDialog/ClusterDialog'));
15
17
  const SetRowHeightDialog = lazy(() => import('./components/SetRowHeightDialog'));
16
18
  export default function MultiVariantBaseModelF(configSchema) {
17
19
  return types
@@ -86,40 +88,26 @@ export default function MultiVariantBaseModelF(configSchema) {
86
88
  get preSources() {
87
89
  return self.layout.length ? self.layout : self.sourcesVolatile;
88
90
  },
91
+ get sourcesWithoutLayout() {
92
+ return self.sourcesVolatile
93
+ ? getSources({
94
+ sources: self.sourcesVolatile,
95
+ renderingMode: self.renderingMode,
96
+ sampleInfo: self.sampleInfo,
97
+ })
98
+ : undefined;
99
+ },
89
100
  get sources() {
90
- var _a, _b;
91
- if (this.preSources) {
92
- const rows = [];
93
- const sources = Object.fromEntries(((_a = self.sourcesVolatile) === null || _a === void 0 ? void 0 : _a.map(s => [s.name, s])) || []);
94
- for (const row of this.preSources) {
95
- if (self.renderingMode === 'phased') {
96
- const info = (_b = self.sampleInfo) === null || _b === void 0 ? void 0 : _b[row.name];
97
- if (info === null || info === void 0 ? void 0 : info.isPhased) {
98
- const ploidy = info.maxPloidy;
99
- for (let i = 0; i < ploidy; i++) {
100
- const id = `${row.name} HP${i}`;
101
- rows.push({
102
- ...sources[row.name],
103
- ...row,
104
- label: id,
105
- HP: i,
106
- id: id,
107
- });
108
- }
109
- }
110
- }
111
- else {
112
- rows.push({
113
- ...sources[row.name],
114
- ...row,
115
- label: row.name,
116
- id: row.name,
117
- });
118
- }
119
- }
120
- return rows;
121
- }
122
- return undefined;
101
+ const sourcesWithLayout = self.layout.length
102
+ ? self.layout
103
+ : self.sourcesVolatile;
104
+ return sourcesWithLayout
105
+ ? getSources({
106
+ sources: sourcesWithLayout,
107
+ renderingMode: self.renderingMode,
108
+ sampleInfo: self.sampleInfo,
109
+ })
110
+ : undefined;
123
111
  },
124
112
  }))
125
113
  .views(self => {
@@ -222,6 +210,7 @@ export default function MultiVariantBaseModelF(configSchema) {
222
210
  },
223
211
  {
224
212
  label: 'Cluster by genotype',
213
+ icon: CategoryIcon,
225
214
  onClick: () => {
226
215
  getSession(self).queueDialog(handleClose => [
227
216
  ClusterDialog,
@@ -256,13 +245,16 @@ export default function MultiVariantBaseModelF(configSchema) {
256
245
  var _a;
257
246
  return self.rowHeight * (((_a = self.sources) === null || _a === void 0 ? void 0 : _a.length) || 1);
258
247
  },
248
+ get featuresReady() {
249
+ return !!self.featuresVolatile;
250
+ },
259
251
  }))
260
252
  .views(self => ({
261
253
  renderProps() {
262
254
  const superProps = self.adapterProps();
263
255
  return {
264
256
  ...superProps,
265
- notReady: superProps.notReady || !self.sources || !self.featuresVolatile,
257
+ notReady: superProps.notReady || !self.sources || !self.featuresReady,
266
258
  height: self.height,
267
259
  totalHeight: self.totalHeight,
268
260
  renderingMode: self.renderingMode,
@@ -0,0 +1,6 @@
1
+ import type { ReducedModel } from './types';
2
+ declare const ClusterDialog: ({ model, handleClose, }: {
3
+ model: ReducedModel;
4
+ handleClose: () => void;
5
+ }) => import("react/jsx-runtime").JSX.Element;
6
+ export default ClusterDialog;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Dialog } from '@jbrowse/core/ui';
4
+ import { FormControlLabel, Radio, RadioGroup, Typography } from '@mui/material';
5
+ import { observer } from 'mobx-react';
6
+ import ClusterDialogAuto from './ClusterDialogAuto';
7
+ import ClusterDialogManual from './ClusterDialogManual';
8
+ function Header({ activeMode, setActiveMode, }) {
9
+ return (_jsxs("div", { children: [_jsx(Typography, { style: { marginBottom: 30 }, children: "This procedure will cluster the visible genotype data using hierarchical clustering" }), _jsx(RadioGroup, { children: Object.entries({
10
+ auto: (_jsx("div", { children: "Run in-app clustering (slow for large data, built in JS clustering)" })),
11
+ manual: (_jsx("div", { children: "Download R script to run clustering (faster for large data, uses hclust, may be more accurate)" })),
12
+ }).map(([key, val]) => (_jsx(FormControlLabel, { control: _jsx(Radio, { checked: activeMode === key, onChange: () => {
13
+ setActiveMode(key);
14
+ } }), label: val }, key))) })] }));
15
+ }
16
+ const ClusterDialog = observer(function ({ model, handleClose, }) {
17
+ const [activeMode, setActiveMode] = useState('auto');
18
+ return (_jsx(Dialog, { open: true, title: "Cluster by genotype", onClose: (_, reason) => {
19
+ if (reason !== 'backdropClick') {
20
+ handleClose();
21
+ }
22
+ }, children: activeMode === 'auto' ? (_jsx(ClusterDialogAuto, { model: model, handleClose: handleClose, children: _jsx(Header, { activeMode: activeMode, setActiveMode: setActiveMode }) })) : (_jsx(ClusterDialogManual, { model: model, handleClose: handleClose, children: _jsx(Header, { activeMode: activeMode, setActiveMode: setActiveMode }) })) }));
23
+ });
24
+ export default ClusterDialog;
@@ -0,0 +1,7 @@
1
+ import type { ReducedModel } from './types';
2
+ declare const ClusterDialogAuto: ({ model, children, handleClose, }: {
3
+ model: ReducedModel;
4
+ children: React.ReactNode;
5
+ handleClose: () => void;
6
+ }) => import("react/jsx-runtime").JSX.Element;
7
+ export default ClusterDialogAuto;
@@ -0,0 +1,67 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { ErrorMessage } from '@jbrowse/core/ui';
4
+ import { getContainingView, getSession, isAbortException, } from '@jbrowse/core/util';
5
+ import { createStopToken, stopStopToken } from '@jbrowse/core/util/stopToken';
6
+ import { getRpcSessionId } from '@jbrowse/core/util/tracks';
7
+ import { Button, DialogActions, DialogContent } from '@mui/material';
8
+ import { observer } from 'mobx-react';
9
+ import { isAlive } from 'mobx-state-tree';
10
+ const ClusterDialogAuto = observer(function ({ model, children, handleClose, }) {
11
+ const [progress, setProgress] = useState('');
12
+ const [error, setError] = useState();
13
+ const [stopToken, setStopToken] = useState('');
14
+ return (_jsxs(_Fragment, { children: [_jsxs(DialogContent, { children: [children, _jsxs("div", { children: [progress ? (_jsxs("div", { style: { padding: 50 }, children: [_jsxs("span", { style: { width: 400 }, children: ["Progress: ", progress] }), _jsx(Button, { onClick: () => {
15
+ stopStopToken(stopToken);
16
+ }, children: "Stop" })] })) : null, error ? _jsx(ErrorMessage, { error: error }) : null] })] }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", onClick: async () => {
17
+ try {
18
+ setError(undefined);
19
+ const view = getContainingView(model);
20
+ if (!view.initialized) {
21
+ return;
22
+ }
23
+ const { rpcManager } = getSession(model);
24
+ const { sourcesWithoutLayout, minorAlleleFrequencyFilter, adapterConfig, } = model;
25
+ if (sourcesWithoutLayout) {
26
+ const sessionId = getRpcSessionId(model);
27
+ const stopToken = createStopToken();
28
+ setStopToken(stopToken);
29
+ const ret = (await rpcManager.call(sessionId, 'MultiVariantClusterGenotypeMatrix', {
30
+ regions: view.dynamicBlocks.contentBlocks,
31
+ sources: sourcesWithoutLayout,
32
+ minorAlleleFrequencyFilter,
33
+ sessionId,
34
+ adapterConfig,
35
+ stopToken,
36
+ statusCallback: (arg) => {
37
+ setProgress(arg);
38
+ },
39
+ }));
40
+ model.setLayout(ret.order.map(idx => {
41
+ const ret = sourcesWithoutLayout[idx];
42
+ if (!ret) {
43
+ throw new Error(`out of bounds at ${idx}`);
44
+ }
45
+ return ret;
46
+ }));
47
+ }
48
+ handleClose();
49
+ }
50
+ catch (e) {
51
+ if (!isAbortException(e) && isAlive(model)) {
52
+ console.error(e);
53
+ setError(e);
54
+ }
55
+ }
56
+ finally {
57
+ setProgress('');
58
+ setStopToken('');
59
+ }
60
+ }, children: "Run clustering" }), _jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
61
+ handleClose();
62
+ if (stopToken) {
63
+ stopStopToken(stopToken);
64
+ }
65
+ }, children: "Cancel" })] })] }));
66
+ });
67
+ export default ClusterDialogAuto;
@@ -0,0 +1,7 @@
1
+ import type { ReducedModel } from './types';
2
+ declare const ClusterDialogManuals: ({ model, handleClose, children, }: {
3
+ model: ReducedModel;
4
+ handleClose: () => void;
5
+ children: React.ReactNode;
6
+ }) => import("react/jsx-runtime").JSX.Element;
7
+ export default ClusterDialogManuals;
@@ -0,0 +1,139 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
4
+ import { getContainingView, getSession, isAbortException, useLocalStorage, } from '@jbrowse/core/util';
5
+ import { getRpcSessionId } from '@jbrowse/core/util/tracks';
6
+ import { Button, DialogActions, DialogContent, FormControlLabel, Paper, Radio, RadioGroup, TextField, Typography, } from '@mui/material';
7
+ import copy from 'copy-to-clipboard';
8
+ import { saveAs } from 'file-saver';
9
+ import { observer } from 'mobx-react';
10
+ import { isAlive } from 'mobx-state-tree';
11
+ import { makeStyles } from 'tss-react/mui';
12
+ const useStyles = makeStyles()(theme => ({
13
+ textAreaFont: {
14
+ fontFamily: 'Courier New',
15
+ },
16
+ mgap: {
17
+ display: 'flex',
18
+ flexDirection: 'column',
19
+ gap: theme.spacing(4),
20
+ },
21
+ }));
22
+ const ClusterDialogManuals = observer(function ({ model, handleClose, children, }) {
23
+ const { classes } = useStyles();
24
+ const [paste, setPaste] = useState('');
25
+ const [ret, setRet] = useState();
26
+ const [error, setError] = useState();
27
+ const [loading, setLoading] = useState(false);
28
+ const [showAdvanced, setShowAdvanced] = useState(false);
29
+ const [clusterMethod, setClusterMethod] = useLocalStorage('cluster-clusterMethod', 'single');
30
+ useEffect(() => {
31
+ ;
32
+ (async () => {
33
+ try {
34
+ setError(undefined);
35
+ setRet(undefined);
36
+ setLoading(true);
37
+ const view = getContainingView(model);
38
+ if (!view.initialized) {
39
+ return;
40
+ }
41
+ const { rpcManager } = getSession(model);
42
+ const { sourcesWithoutLayout, minorAlleleFrequencyFilter, adapterConfig, } = model;
43
+ const sessionId = getRpcSessionId(model);
44
+ const ret = (await rpcManager.call(sessionId, 'MultiVariantGetGenotypeMatrix', {
45
+ regions: view.dynamicBlocks.contentBlocks,
46
+ sources: sourcesWithoutLayout,
47
+ minorAlleleFrequencyFilter,
48
+ sessionId,
49
+ adapterConfig,
50
+ }));
51
+ setRet(ret);
52
+ }
53
+ catch (e) {
54
+ if (!isAbortException(e) && isAlive(model)) {
55
+ console.error(e);
56
+ setError(e);
57
+ }
58
+ }
59
+ finally {
60
+ setLoading(false);
61
+ }
62
+ })();
63
+ }, [model]);
64
+ const results = ret
65
+ ? `inputMatrix<-matrix(c(${Object.values(ret)
66
+ .map(val => val.join(','))
67
+ .join(',\n')}
68
+ ),nrow=${Object.values(ret).length},byrow=TRUE)
69
+ rownames(inputMatrix)<-c(${Object.keys(ret)
70
+ .map(key => `'${key}'`)
71
+ .join(',')})
72
+ resultClusters<-hclust(dist(inputMatrix), method='${clusterMethod}')
73
+ cat(resultClusters$order,sep='\\n')`
74
+ : undefined;
75
+ const resultsTsv = ret
76
+ ? Object.entries(ret)
77
+ .map(([key, val]) => [key, ...val].join('\t'))
78
+ .join('\n')
79
+ : undefined;
80
+ return (_jsxs(_Fragment, { children: [_jsxs(DialogContent, { children: [children, _jsxs(Paper, { style: { padding: 16 }, children: [_jsxs("div", { style: {
81
+ display: 'flex',
82
+ gap: '8px',
83
+ flexWrap: 'wrap',
84
+ marginBottom: '16px',
85
+ }, children: [_jsx(Button, { variant: "contained", onClick: () => {
86
+ saveAs(new Blob([results || ''], {
87
+ type: 'text/plain;charset=utf-8',
88
+ }), 'cluster.R');
89
+ }, children: "Download Rscript" }), ' ', "or", ' ', _jsx(Button, { variant: "contained", onClick: () => {
90
+ copy(results || '');
91
+ }, children: "Copy Rscript to clipboard" }), ' ', "or", ' ', _jsx(Button, { variant: "contained", onClick: () => {
92
+ saveAs(new Blob([resultsTsv || ''], {
93
+ type: 'text/plain;charset=utf-8',
94
+ }), 'genotypes.tsv');
95
+ }, children: "Download TSV" }), _jsxs("div", { children: [_jsx(Button, { variant: "contained", onClick: () => {
96
+ setShowAdvanced(!showAdvanced);
97
+ }, children: showAdvanced
98
+ ? 'Hide advanced options'
99
+ : 'Show advanced options' }), showAdvanced ? (_jsxs("div", { children: [_jsx(Typography, { variant: "h6", children: "Advanced options" }), _jsx(RadioGroup, { children: Object.entries({
100
+ single: 'Single',
101
+ complete: 'Complete',
102
+ }).map(([key, val]) => (_jsx(FormControlLabel, { control: _jsx(Radio, { checked: clusterMethod === key, onChange: () => {
103
+ setClusterMethod(key);
104
+ } }), label: val }, key))) })] })) : null] }), results ? (_jsx("div", {})) : loading ? (_jsx(LoadingEllipses, { variant: "h6", title: "Generating genotype matrix" })) : error ? (_jsx(ErrorMessage, { error: error })) : null] }), _jsxs("div", { children: [_jsx(Typography, { variant: "subtitle2", gutterBottom: true, style: { marginTop: '16px' }, children: "Clustering Results:" }), _jsx(TextField, { multiline: true, fullWidth: true, variant: "outlined", placeholder: "Paste results from Rscript here (sequence of numbers, one per line, specifying the new ordering)", rows: 10, value: paste, onChange: event => {
105
+ setPaste(event.target.value);
106
+ }, slotProps: {
107
+ input: {
108
+ classes: {
109
+ input: classes.textAreaFont,
110
+ },
111
+ },
112
+ } })] })] })] }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", onClick: () => {
113
+ const { sourcesWithoutLayout } = model;
114
+ if (sourcesWithoutLayout) {
115
+ try {
116
+ model.setLayout(paste
117
+ .split('\n')
118
+ .map(t => t.trim())
119
+ .filter(f => !!f)
120
+ .map(r => +r)
121
+ .map(idx => {
122
+ const ret = sourcesWithoutLayout[idx - 1];
123
+ if (!ret) {
124
+ throw new Error(`out of bounds at ${idx}`);
125
+ }
126
+ return ret;
127
+ }));
128
+ }
129
+ catch (e) {
130
+ console.error(e);
131
+ getSession(model).notifyError(`${e}`, e);
132
+ }
133
+ }
134
+ handleClose();
135
+ }, children: "Apply clustering" }), _jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
136
+ handleClose();
137
+ }, children: "Cancel" })] })] }));
138
+ });
139
+ export default ClusterDialogManuals;
@@ -0,0 +1,9 @@
1
+ import type { Source } from '../../types';
2
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
+ export interface ReducedModel {
4
+ sourcesWithoutLayout?: Source[];
5
+ minorAlleleFrequencyFilter?: number;
6
+ adapterConfig: AnyConfigurationModel;
7
+ setLayout: (arg: Source[]) => void;
8
+ clearLayout: () => void;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -14,49 +14,56 @@ const useStyles = makeStyles()({
14
14
  });
15
15
  export default function SourcesDataGrid({ rows, onChange, setSelected, }) {
16
16
  const { classes } = useStyles();
17
- const { id: _id, name: _name, label: _label, color: _color, baseUri: _baseUri, HP: _HP, ...rest } = rows[0];
18
17
  const [currSort, setCurrSort] = useState({
19
18
  idx: 0,
20
19
  field: null,
21
20
  });
22
- return (_jsx("div", { style: { height: 400, width: '100%' }, children: _jsx(DataGrid, { checkboxSelection: true, disableRowSelectionOnClick: true, onRowSelectionModelChange: arg => {
23
- setSelected(arg);
24
- }, rows: rows, rowHeight: 25, columnHeaderHeight: 33, columns: [
25
- {
26
- field: 'color',
27
- headerName: 'Color',
28
- renderCell: params => {
29
- const { value, id } = params;
30
- return (_jsx(ColorPicker, { color: value || 'blue', onChange: c => {
31
- const elt = rows.find(f => f.name === id);
32
- if (elt) {
33
- elt.color = c;
34
- }
35
- onChange([...rows]);
36
- } }));
21
+ if (rows.length) {
22
+ const { id: _id, name: _name, label: _label, color: _color, baseUri: _baseUri, HP: _HP, ...rest } = rows[0];
23
+ return (_jsx("div", { style: { height: 400, width: '100%' }, children: _jsx(DataGrid, { checkboxSelection: true, disableRowSelectionOnClick: true, onRowSelectionModelChange: arg => {
24
+ setSelected(arg);
25
+ }, rows: rows, rowHeight: 25, columnHeaderHeight: 33, columns: [
26
+ {
27
+ field: 'color',
28
+ headerName: 'Color',
29
+ renderCell: params => {
30
+ const { value, id } = params;
31
+ return (_jsx(ColorPicker, { color: value || 'blue', onChange: c => {
32
+ const elt = rows.find(f => f.name === id);
33
+ if (elt) {
34
+ elt.color = c;
35
+ }
36
+ onChange([...rows]);
37
+ } }));
38
+ },
37
39
  },
38
- },
39
- {
40
- field: 'label',
41
- headerName: 'Name',
42
- width: measureGridWidth(rows.map(r => r.label)),
43
- },
44
- ...Object.keys(rest).map(val => ({
45
- field: val,
46
- renderCell: ({ value }) => (_jsx("div", { className: classes.cell, children: _jsx(SanitizedHTML, { html: getStr(value) }) })),
47
- width: measureGridWidth(rows.map(r => `${r[val]}`)),
48
- })),
49
- ], sortModel: [], onSortModelChange: args => {
50
- const sort = args[0];
51
- const idx = (currSort.idx + 1) % 2;
52
- const field = sort.field || currSort.field;
53
- setCurrSort({ idx, field });
54
- onChange(field
55
- ? [...rows].sort((a, b) => {
56
- const aa = getStr(a[field]);
57
- const bb = getStr(b[field]);
58
- return idx === 1 ? aa.localeCompare(bb) : bb.localeCompare(aa);
59
- })
60
- : rows);
61
- } }) }));
40
+ {
41
+ field: 'label',
42
+ headerName: 'Name',
43
+ width: measureGridWidth(rows.map(r => r.label)),
44
+ },
45
+ ...Object.keys(rest).map(val => ({
46
+ field: val,
47
+ renderCell: ({ value }) => (_jsx("div", { className: classes.cell, children: _jsx(SanitizedHTML, { html: getStr(value) }) })),
48
+ width: measureGridWidth(rows.map(r => `${r[val]}`)),
49
+ })),
50
+ ], sortModel: [], onSortModelChange: args => {
51
+ const sort = args[0];
52
+ const idx = (currSort.idx + 1) % 2;
53
+ const field = sort.field || currSort.field;
54
+ setCurrSort({ idx, field });
55
+ onChange(field
56
+ ? [...rows].sort((a, b) => {
57
+ const aa = getStr(a[field]);
58
+ const bb = getStr(b[field]);
59
+ return idx === 1
60
+ ? aa.localeCompare(bb)
61
+ : bb.localeCompare(aa);
62
+ })
63
+ : rows);
64
+ } }) }));
65
+ }
66
+ else {
67
+ return _jsx("div", { children: "No rows, check sample metadata configuration" });
68
+ }
62
69
  }
@@ -22,12 +22,12 @@ export default function SourcesGridHeader({ selected, onChange, rows, showTips,
22
22
  onChange(moveDown([...rows], selected, rows.length));
23
23
  }, disabled: !selected.length, children: [_jsx(KeyboardDoubleArrowDownIcon, {}), showTips ? 'Move selected items to bottom' : null] }), _jsx(ColorPopover, { anchorEl: anchorEl, color: widgetColor, onChange: c => {
24
24
  setWidgetColor(c);
25
- selected.forEach(id => {
25
+ for (const id of selected) {
26
26
  const elt = rows.find(f => f.name === id);
27
27
  if (elt) {
28
28
  elt.color = c;
29
29
  }
30
- });
30
+ }
31
31
  onChange([...rows]);
32
32
  }, onClose: () => {
33
33
  setAnchorEl(null);
@@ -0,0 +1,15 @@
1
+ import type { SampleInfo, Source } from './types';
2
+ export declare function getSources({ sources, layout, renderingMode, sampleInfo, }: {
3
+ sources: Source[];
4
+ layout?: Source[];
5
+ renderingMode: string;
6
+ sampleInfo?: Record<string, SampleInfo>;
7
+ }): {
8
+ label: string;
9
+ id: string;
10
+ baseUri?: string;
11
+ name: string;
12
+ color?: string;
13
+ group?: string;
14
+ HP?: number;
15
+ }[];
@@ -0,0 +1,31 @@
1
+ export function getSources({ sources, layout = sources, renderingMode, sampleInfo, }) {
2
+ const rows = [];
3
+ const sourceMap = Object.fromEntries(sources.map(s => [s.name, s]));
4
+ for (const row of layout) {
5
+ if (renderingMode === 'phased') {
6
+ const info = sampleInfo === null || sampleInfo === void 0 ? void 0 : sampleInfo[row.name];
7
+ if (info === null || info === void 0 ? void 0 : info.isPhased) {
8
+ const ploidy = info.maxPloidy;
9
+ for (let i = 0; i < ploidy; i++) {
10
+ const id = `${row.name} HP${i}`;
11
+ rows.push({
12
+ ...sourceMap[row.name],
13
+ ...row,
14
+ label: id,
15
+ HP: i,
16
+ id: id,
17
+ });
18
+ }
19
+ }
20
+ }
21
+ else {
22
+ rows.push({
23
+ ...sourceMap[row.name],
24
+ ...row,
25
+ label: row.name,
26
+ id: row.name,
27
+ });
28
+ }
29
+ }
30
+ return rows;
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-variants",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "JBrowse 2 variant adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -40,10 +40,10 @@
40
40
  "@gmod/bgzf-filehandle": "^2.0.1",
41
41
  "@gmod/tabix": "^2.0.0",
42
42
  "@gmod/vcf": "^6.0.8",
43
- "@jbrowse/core": "^3.1.0",
44
- "@jbrowse/plugin-circular-view": "^3.1.0",
45
- "@jbrowse/plugin-linear-genome-view": "^3.1.0",
46
- "@jbrowse/sv-core": "^3.1.0",
43
+ "@jbrowse/core": "^3.2.0",
44
+ "@jbrowse/plugin-circular-view": "^3.2.0",
45
+ "@jbrowse/plugin-linear-genome-view": "^3.2.0",
46
+ "@jbrowse/sv-core": "^3.2.0",
47
47
  "@mui/icons-material": "^6.0.0",
48
48
  "@mui/material": "^6.0.0",
49
49
  "@mui/x-data-grid": "^7.0.0",
@@ -62,5 +62,5 @@
62
62
  "distModule": "esm/index.js",
63
63
  "srcModule": "src/index.ts",
64
64
  "module": "esm/index.js",
65
- "gitHead": "91492049ddea0aed90eb24d3c066c2d9f5a6b189"
65
+ "gitHead": "c750e3f56706a490c19ba75abd807fec5e38aebf"
66
66
  }
@@ -1,11 +0,0 @@
1
- import type { Source } from '../types';
2
- import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
- export default function ClusterDialog({ model, handleClose, }: {
4
- model: {
5
- sources?: Source[];
6
- minorAlleleFrequencyFilter?: number;
7
- adapterConfig: AnyConfigurationModel;
8
- setLayout: (arg: Source[]) => void;
9
- };
10
- handleClose: () => void;
11
- }): import("react/jsx-runtime").JSX.Element;