@jbrowse/plugin-variants 3.0.5 → 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 (89) hide show
  1. package/dist/MultiLinearVariantDisplay/components/Crosshair.js +1 -1
  2. package/dist/MultiLinearVariantDisplay/model.d.ts +19 -0
  3. package/dist/MultiLinearVariantMatrixDisplay/components/Crosshair.js +1 -1
  4. package/dist/MultiLinearVariantMatrixDisplay/model.d.ts +20 -0
  5. package/dist/MultiLinearVariantMatrixDisplay/model.js +4 -1
  6. package/dist/MultiLinearVariantMatrixRenderer/makeImageData.d.ts +1 -0
  7. package/dist/MultiLinearVariantMatrixRenderer/makeImageData.js +9 -1
  8. package/dist/MultiLinearVariantRenderer/makeImageData.js +9 -1
  9. package/dist/VariantRPC/MultiVariantClusterGenotypeMatrix.d.ts +28 -0
  10. package/dist/VariantRPC/MultiVariantClusterGenotypeMatrix.js +33 -0
  11. package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +13 -11
  12. package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.js +5 -34
  13. package/dist/VariantRPC/MultiVariantGetSimplifiedFeatures.d.ts +11 -8
  14. package/dist/VariantRPC/MultiVariantGetSimplifiedFeatures.js +1 -2
  15. package/dist/VariantRPC/cluster.d.ts +17 -0
  16. package/dist/VariantRPC/cluster.js +84 -0
  17. package/dist/VariantRPC/getGenotypeMatrix.d.ts +6 -0
  18. package/dist/VariantRPC/getGenotypeMatrix.js +55 -0
  19. package/dist/VariantRPC/types.d.ts +13 -0
  20. package/dist/VariantRPC/types.js +2 -0
  21. package/dist/VcfAdapter/VcfAdapter.js +6 -3
  22. package/dist/VcfAdapter/configSchema.js +14 -1
  23. package/dist/VcfFeature/util.js +2 -2
  24. package/dist/VcfTabixAdapter/VcfTabixAdapter.js +11 -6
  25. package/dist/VcfTabixAdapter/configSchema.js +24 -2
  26. package/dist/index.js +2 -0
  27. package/dist/shared/MultiVariantBaseModel.d.ts +19 -0
  28. package/dist/shared/MultiVariantBaseModel.js +27 -35
  29. package/dist/shared/components/ClusterDialog/ClusterDialog.d.ts +6 -0
  30. package/dist/shared/components/ClusterDialog/ClusterDialog.js +29 -0
  31. package/dist/shared/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
  32. package/dist/shared/components/ClusterDialog/ClusterDialogAuto.js +69 -0
  33. package/dist/shared/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
  34. package/dist/shared/components/ClusterDialog/ClusterDialogManual.js +144 -0
  35. package/dist/shared/components/ClusterDialog/types.d.ts +9 -0
  36. package/dist/shared/components/ClusterDialog/types.js +2 -0
  37. package/dist/shared/components/SourcesDataGrid.js +47 -40
  38. package/dist/shared/components/SourcesGridHeader.js +2 -2
  39. package/dist/shared/getSources.d.ts +15 -0
  40. package/dist/shared/getSources.js +34 -0
  41. package/dist/shared/minorAlleleFrequencyUtils.d.ts +3 -5
  42. package/dist/shared/minorAlleleFrequencyUtils.js +13 -8
  43. package/esm/MultiLinearVariantDisplay/components/Crosshair.js +1 -1
  44. package/esm/MultiLinearVariantDisplay/model.d.ts +19 -0
  45. package/esm/MultiLinearVariantMatrixDisplay/components/Crosshair.js +1 -1
  46. package/esm/MultiLinearVariantMatrixDisplay/model.d.ts +20 -0
  47. package/esm/MultiLinearVariantMatrixDisplay/model.js +4 -1
  48. package/esm/MultiLinearVariantMatrixRenderer/makeImageData.d.ts +1 -0
  49. package/esm/MultiLinearVariantMatrixRenderer/makeImageData.js +9 -1
  50. package/esm/MultiLinearVariantRenderer/makeImageData.js +9 -1
  51. package/esm/VariantRPC/MultiVariantClusterGenotypeMatrix.d.ts +28 -0
  52. package/esm/VariantRPC/MultiVariantClusterGenotypeMatrix.js +26 -0
  53. package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +13 -11
  54. package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.js +5 -34
  55. package/esm/VariantRPC/MultiVariantGetSimplifiedFeatures.d.ts +11 -8
  56. package/esm/VariantRPC/MultiVariantGetSimplifiedFeatures.js +1 -2
  57. package/esm/VariantRPC/cluster.d.ts +17 -0
  58. package/esm/VariantRPC/cluster.js +79 -0
  59. package/esm/VariantRPC/getGenotypeMatrix.d.ts +6 -0
  60. package/esm/VariantRPC/getGenotypeMatrix.js +52 -0
  61. package/esm/VariantRPC/types.d.ts +13 -0
  62. package/esm/VariantRPC/types.js +1 -0
  63. package/esm/VcfAdapter/VcfAdapter.js +7 -4
  64. package/esm/VcfAdapter/configSchema.js +14 -1
  65. package/esm/VcfFeature/util.js +2 -2
  66. package/esm/VcfTabixAdapter/VcfTabixAdapter.js +11 -6
  67. package/esm/VcfTabixAdapter/configSchema.js +24 -2
  68. package/esm/index.js +2 -0
  69. package/esm/shared/MultiVariantBaseModel.d.ts +19 -0
  70. package/esm/shared/MultiVariantBaseModel.js +27 -35
  71. package/esm/shared/components/ClusterDialog/ClusterDialog.d.ts +6 -0
  72. package/esm/shared/components/ClusterDialog/ClusterDialog.js +24 -0
  73. package/esm/shared/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
  74. package/esm/shared/components/ClusterDialog/ClusterDialogAuto.js +67 -0
  75. package/esm/shared/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
  76. package/esm/shared/components/ClusterDialog/ClusterDialogManual.js +139 -0
  77. package/esm/shared/components/ClusterDialog/types.d.ts +9 -0
  78. package/esm/shared/components/ClusterDialog/types.js +1 -0
  79. package/esm/shared/components/SourcesDataGrid.js +47 -40
  80. package/esm/shared/components/SourcesGridHeader.js +2 -2
  81. package/esm/shared/getSources.d.ts +15 -0
  82. package/esm/shared/getSources.js +31 -0
  83. package/esm/shared/minorAlleleFrequencyUtils.d.ts +3 -5
  84. package/esm/shared/minorAlleleFrequencyUtils.js +13 -8
  85. package/package.json +6 -6
  86. package/dist/shared/components/ClusterDialog.d.ts +0 -11
  87. package/dist/shared/components/ClusterDialog.js +0 -109
  88. package/esm/shared/components/ClusterDialog.d.ts +0 -11
  89. package/esm/shared/components/ClusterDialog.js +0 -103
@@ -1,7 +1,7 @@
1
1
  import IntervalTree from '@flatten-js/interval-tree';
2
2
  import VcfParser from '@gmod/vcf';
3
3
  import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
4
- import { fetchAndMaybeUnzip } from '@jbrowse/core/util';
4
+ import { fetchAndMaybeUnzip, getProgressDisplayStr } from '@jbrowse/core/util';
5
5
  import { openLocation } from '@jbrowse/core/util/io';
6
6
  import { ObservableCreate } from '@jbrowse/core/util/rxjs';
7
7
  import VcfFeature from '../VcfFeature';
@@ -45,7 +45,7 @@ class VcfAdapter extends BaseFeatureDataAdapter {
45
45
  }
46
46
  }
47
47
  if (i++ % 10000 === 0) {
48
- statusCallback(`Loading ${Math.floor(blockStart / 1000000).toLocaleString('en-US')}/${Math.floor(buffer.length / 1000000).toLocaleString('en-US')} MB`);
48
+ statusCallback(`Loading ${getProgressDisplayStr(blockStart, buffer.length)}`);
49
49
  }
50
50
  blockStart = n + 1;
51
51
  }
@@ -96,9 +96,12 @@ class VcfAdapter extends BaseFeatureDataAdapter {
96
96
  try {
97
97
  const { start, end, refName } = region;
98
98
  const { intervalTreeMap } = await this.setup();
99
- (_a = intervalTreeMap[refName]) === null || _a === void 0 ? void 0 : _a.call(intervalTreeMap, opts.statusCallback).search([start, end]).forEach(f => {
99
+ for (const f of ((_a = intervalTreeMap[refName]) === null || _a === void 0 ? void 0 : _a.call(intervalTreeMap, opts.statusCallback).search([
100
+ start,
101
+ end,
102
+ ])) || []) {
100
103
  observer.next(f);
101
- });
104
+ }
102
105
  observer.complete();
103
106
  }
104
107
  catch (e) {
@@ -16,5 +16,18 @@ const VcfAdapter = ConfigurationSchema('VcfAdapter', {
16
16
  locationType: 'UriLocation',
17
17
  },
18
18
  },
19
- }, { explicitlyTyped: true });
19
+ }, {
20
+ explicitlyTyped: true,
21
+ preProcessSnapshot: snap => {
22
+ return snap.uri
23
+ ? {
24
+ ...snap,
25
+ vcfLocation: {
26
+ uri: snap.uri,
27
+ baseUri: snap.baseUri,
28
+ },
29
+ }
30
+ : snap;
31
+ },
32
+ });
20
33
  export default VcfAdapter;
@@ -17,7 +17,7 @@ export function getSOTermAndDescription(ref, alt, parser) {
17
17
  }
18
18
  const soTerms = new Set();
19
19
  let descriptions = new Set();
20
- alt.forEach(a => {
20
+ for (const a of alt) {
21
21
  let [soTerm, description] = getSOAndDescFromAltDefs(a, parser);
22
22
  if (!soTerm) {
23
23
  ;
@@ -27,7 +27,7 @@ export function getSOTermAndDescription(ref, alt, parser) {
27
27
  soTerms.add(soTerm);
28
28
  descriptions.add(description);
29
29
  }
30
- });
30
+ }
31
31
  if (descriptions.size > 1) {
32
32
  const descs = [...descriptions];
33
33
  const prefixes = new Set(descs
@@ -86,16 +86,21 @@ export default class VcfTabixAdapter extends BaseFeatureDataAdapter {
86
86
  const header = lines[0].split('\t');
87
87
  const { parser } = await this.configure();
88
88
  const s = new Set(parser.samples);
89
- return lines
89
+ const ret = lines
90
90
  .slice(1)
91
+ .filter(f => !!f)
91
92
  .map(line => {
92
- const cols = line.split('\t');
93
+ const [name, ...rest] = line.split('\t');
93
94
  return {
94
- name: cols[0],
95
- ...Object.fromEntries(cols.slice(1).map((c, idx) => [header[idx + 1], c])),
95
+ ...Object.fromEntries(rest.map((c, idx) => [header[idx + 1], c])),
96
+ name: name,
96
97
  };
97
- })
98
- .filter(f => s.has(f.name));
98
+ });
99
+ const missing = ret.filter(f => !s.has(f.name));
100
+ if (missing.length) {
101
+ console.warn('Samples in metadata file not in VCF:', ret.filter(f => !s.has(f.name)));
102
+ }
103
+ return ret.filter(f => s.has(f.name));
99
104
  }
100
105
  }
101
106
  freeResources() { }
@@ -4,7 +4,10 @@ function x() { }
4
4
  const VcfTabixAdapter = ConfigurationSchema('VcfTabixAdapter', {
5
5
  vcfGzLocation: {
6
6
  type: 'fileLocation',
7
- defaultValue: { uri: '/path/to/my.vcf.gz', locationType: 'UriLocation' },
7
+ defaultValue: {
8
+ uri: '/path/to/my.vcf.gz',
9
+ locationType: 'UriLocation',
10
+ },
8
11
  },
9
12
  index: ConfigurationSchema('VcfIndex', {
10
13
  indexType: {
@@ -28,5 +31,24 @@ const VcfTabixAdapter = ConfigurationSchema('VcfTabixAdapter', {
28
31
  locationType: 'UriLocation',
29
32
  },
30
33
  },
31
- }, { explicitlyTyped: true });
34
+ }, {
35
+ explicitlyTyped: true,
36
+ preProcessSnapshot: snap => {
37
+ return snap.uri
38
+ ? {
39
+ ...snap,
40
+ vcfGzLocation: {
41
+ uri: snap.uri,
42
+ baseUri: snap.baseUri,
43
+ },
44
+ index: {
45
+ location: {
46
+ uri: `${snap.uri}.tbi`,
47
+ baseUri: snap.baseUri,
48
+ },
49
+ },
50
+ }
51
+ : snap;
52
+ },
53
+ });
32
54
  export default VcfTabixAdapter;
package/esm/index.js CHANGED
@@ -8,6 +8,7 @@ import MultiVariantRendererF from './MultiLinearVariantRenderer';
8
8
  import SplitVcfTabixAdapterF from './SplitVcfTabixAdapter';
9
9
  import StructuralVariantChordRendererF from './StructuralVariantChordRenderer';
10
10
  import VariantFeatureWidgetF from './VariantFeatureWidget';
11
+ import { MultiVariantClusterGenotypeMatrix } from './VariantRPC/MultiVariantClusterGenotypeMatrix';
11
12
  import { MultiVariantGetGenotypeMatrix } from './VariantRPC/MultiVariantGetGenotypeMatrix';
12
13
  import { MultiVariantGetSimplifiedFeatures } from './VariantRPC/MultiVariantGetSimplifiedFeatures';
13
14
  import { MultiVariantGetSources } from './VariantRPC/MultiVariantGetSources';
@@ -36,6 +37,7 @@ export default class VariantsPlugin extends Plugin {
36
37
  ChordVariantDisplayF(pluginManager);
37
38
  pluginManager.addRpcMethod(() => new MultiVariantGetSources(pluginManager));
38
39
  pluginManager.addRpcMethod(() => new MultiVariantGetGenotypeMatrix(pluginManager));
40
+ pluginManager.addRpcMethod(() => new MultiVariantClusterGenotypeMatrix(pluginManager));
39
41
  pluginManager.addRpcMethod(() => new MultiVariantGetSimplifiedFeatures(pluginManager));
40
42
  }
41
43
  }
@@ -255,6 +255,15 @@ export default function MultiVariantBaseModelF(configSchema: AnyConfigurationSch
255
255
  setSampleInfo(arg: Record<string, SampleInfo>): void;
256
256
  } & {
257
257
  readonly preSources: Source[] | undefined;
258
+ readonly sourcesWithoutLayout: {
259
+ label: string;
260
+ id: string;
261
+ baseUri?: string;
262
+ name: string;
263
+ color?: string;
264
+ group?: string;
265
+ HP?: number;
266
+ }[] | undefined;
258
267
  readonly sources: {
259
268
  label: string;
260
269
  id: string;
@@ -330,10 +339,20 @@ export default function MultiVariantBaseModelF(configSchema: AnyConfigurationSch
330
339
  type?: undefined;
331
340
  checked?: undefined;
332
341
  onClick?: undefined;
342
+ } | {
343
+ label: string;
344
+ icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
345
+ muiName: string;
346
+ };
347
+ onClick: () => void;
348
+ type?: undefined;
349
+ checked?: undefined;
350
+ subMenu?: undefined;
333
351
  })[];
334
352
  } & {
335
353
  readonly canDisplayLabels: boolean;
336
354
  readonly totalHeight: number;
355
+ readonly featuresReady: boolean;
337
356
  } & {
338
357
  renderProps(): any;
339
358
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
@@ -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 {};