@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.
- package/dist/MultiLinearVariantDisplay/model.d.ts +19 -0
- package/dist/MultiLinearVariantMatrixDisplay/model.d.ts +20 -0
- package/dist/MultiLinearVariantMatrixDisplay/model.js +4 -1
- package/dist/MultiLinearVariantMatrixRenderer/makeImageData.js +9 -1
- package/dist/MultiLinearVariantRenderer/makeImageData.js +9 -1
- package/dist/VariantRPC/MultiVariantClusterGenotypeMatrix.d.ts +28 -0
- package/dist/VariantRPC/MultiVariantClusterGenotypeMatrix.js +33 -0
- package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +13 -11
- package/dist/VariantRPC/MultiVariantGetGenotypeMatrix.js +5 -57
- package/dist/VariantRPC/MultiVariantGetSimplifiedFeatures.d.ts +11 -8
- package/dist/VariantRPC/MultiVariantGetSimplifiedFeatures.js +1 -2
- package/dist/VariantRPC/cluster.d.ts +17 -0
- package/dist/VariantRPC/cluster.js +84 -0
- package/dist/VariantRPC/getGenotypeMatrix.d.ts +6 -0
- package/dist/VariantRPC/getGenotypeMatrix.js +55 -0
- package/dist/VariantRPC/types.d.ts +13 -0
- package/dist/VariantRPC/types.js +2 -0
- package/dist/VcfAdapter/VcfAdapter.js +6 -3
- package/dist/VcfFeature/util.js +2 -2
- package/dist/VcfTabixAdapter/VcfTabixAdapter.js +11 -6
- package/dist/index.js +2 -0
- package/dist/shared/MultiVariantBaseModel.d.ts +19 -0
- package/dist/shared/MultiVariantBaseModel.js +27 -35
- package/dist/shared/components/ClusterDialog/ClusterDialog.d.ts +6 -0
- package/dist/shared/components/ClusterDialog/ClusterDialog.js +29 -0
- package/dist/shared/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
- package/dist/shared/components/ClusterDialog/ClusterDialogAuto.js +69 -0
- package/dist/shared/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
- package/dist/shared/components/ClusterDialog/ClusterDialogManual.js +144 -0
- package/dist/shared/components/ClusterDialog/types.d.ts +9 -0
- package/dist/shared/components/ClusterDialog/types.js +2 -0
- package/dist/shared/components/SourcesDataGrid.js +47 -40
- package/dist/shared/components/SourcesGridHeader.js +2 -2
- package/dist/shared/getSources.d.ts +15 -0
- package/dist/shared/getSources.js +34 -0
- package/esm/MultiLinearVariantDisplay/model.d.ts +19 -0
- package/esm/MultiLinearVariantMatrixDisplay/model.d.ts +20 -0
- package/esm/MultiLinearVariantMatrixDisplay/model.js +4 -1
- package/esm/MultiLinearVariantMatrixRenderer/makeImageData.js +9 -1
- package/esm/MultiLinearVariantRenderer/makeImageData.js +9 -1
- package/esm/VariantRPC/MultiVariantClusterGenotypeMatrix.d.ts +28 -0
- package/esm/VariantRPC/MultiVariantClusterGenotypeMatrix.js +26 -0
- package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.d.ts +13 -11
- package/esm/VariantRPC/MultiVariantGetGenotypeMatrix.js +5 -57
- package/esm/VariantRPC/MultiVariantGetSimplifiedFeatures.d.ts +11 -8
- package/esm/VariantRPC/MultiVariantGetSimplifiedFeatures.js +1 -2
- package/esm/VariantRPC/cluster.d.ts +17 -0
- package/esm/VariantRPC/cluster.js +79 -0
- package/esm/VariantRPC/getGenotypeMatrix.d.ts +6 -0
- package/esm/VariantRPC/getGenotypeMatrix.js +52 -0
- package/esm/VariantRPC/types.d.ts +13 -0
- package/esm/VariantRPC/types.js +1 -0
- package/esm/VcfAdapter/VcfAdapter.js +7 -4
- package/esm/VcfFeature/util.js +2 -2
- package/esm/VcfTabixAdapter/VcfTabixAdapter.js +11 -6
- package/esm/index.js +2 -0
- package/esm/shared/MultiVariantBaseModel.d.ts +19 -0
- package/esm/shared/MultiVariantBaseModel.js +27 -35
- package/esm/shared/components/ClusterDialog/ClusterDialog.d.ts +6 -0
- package/esm/shared/components/ClusterDialog/ClusterDialog.js +24 -0
- package/esm/shared/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
- package/esm/shared/components/ClusterDialog/ClusterDialogAuto.js +67 -0
- package/esm/shared/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
- package/esm/shared/components/ClusterDialog/ClusterDialogManual.js +139 -0
- package/esm/shared/components/ClusterDialog/types.d.ts +9 -0
- package/esm/shared/components/ClusterDialog/types.js +1 -0
- package/esm/shared/components/SourcesDataGrid.js +47 -40
- package/esm/shared/components/SourcesGridHeader.js +2 -2
- package/esm/shared/getSources.d.ts +15 -0
- package/esm/shared/getSources.js +31 -0
- package/package.json +6 -6
- package/dist/shared/components/ClusterDialog.d.ts +0 -11
- package/dist/shared/components/ClusterDialog.js +0 -113
- package/esm/shared/components/ClusterDialog.d.ts +0 -11
- package/esm/shared/components/ClusterDialog.js +0 -107
|
@@ -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>;
|
|
@@ -42,15 +42,17 @@ const configuration_1 = require("@jbrowse/core/configuration");
|
|
|
42
42
|
const util_1 = require("@jbrowse/core/util");
|
|
43
43
|
const stopToken_1 = require("@jbrowse/core/util/stopToken");
|
|
44
44
|
const plugin_linear_genome_view_1 = require("@jbrowse/plugin-linear-genome-view");
|
|
45
|
+
const Category_1 = __importDefault(require("@mui/icons-material/Category"));
|
|
45
46
|
const FilterList_1 = __importDefault(require("@mui/icons-material/FilterList"));
|
|
46
47
|
const Height_1 = __importDefault(require("@mui/icons-material/Height"));
|
|
47
48
|
const Splitscreen_1 = __importDefault(require("@mui/icons-material/Splitscreen"));
|
|
48
49
|
const Visibility_1 = __importDefault(require("@mui/icons-material/Visibility"));
|
|
49
50
|
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
50
51
|
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
52
|
+
const getSources_1 = require("./getSources");
|
|
51
53
|
const SetColorDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/SetColorDialog'))));
|
|
52
54
|
const MAFFilterDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/MAFFilterDialog'))));
|
|
53
|
-
const ClusterDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/ClusterDialog'))));
|
|
55
|
+
const ClusterDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/ClusterDialog/ClusterDialog'))));
|
|
54
56
|
const SetRowHeightDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/SetRowHeightDialog'))));
|
|
55
57
|
function MultiVariantBaseModelF(configSchema) {
|
|
56
58
|
return mobx_state_tree_1.types
|
|
@@ -125,40 +127,26 @@ function MultiVariantBaseModelF(configSchema) {
|
|
|
125
127
|
get preSources() {
|
|
126
128
|
return self.layout.length ? self.layout : self.sourcesVolatile;
|
|
127
129
|
},
|
|
130
|
+
get sourcesWithoutLayout() {
|
|
131
|
+
return self.sourcesVolatile
|
|
132
|
+
? (0, getSources_1.getSources)({
|
|
133
|
+
sources: self.sourcesVolatile,
|
|
134
|
+
renderingMode: self.renderingMode,
|
|
135
|
+
sampleInfo: self.sampleInfo,
|
|
136
|
+
})
|
|
137
|
+
: undefined;
|
|
138
|
+
},
|
|
128
139
|
get sources() {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const id = `${row.name} HP${i}`;
|
|
140
|
-
rows.push({
|
|
141
|
-
...sources[row.name],
|
|
142
|
-
...row,
|
|
143
|
-
label: id,
|
|
144
|
-
HP: i,
|
|
145
|
-
id: id,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
rows.push({
|
|
152
|
-
...sources[row.name],
|
|
153
|
-
...row,
|
|
154
|
-
label: row.name,
|
|
155
|
-
id: row.name,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return rows;
|
|
160
|
-
}
|
|
161
|
-
return undefined;
|
|
140
|
+
const sourcesWithLayout = self.layout.length
|
|
141
|
+
? self.layout
|
|
142
|
+
: self.sourcesVolatile;
|
|
143
|
+
return sourcesWithLayout
|
|
144
|
+
? (0, getSources_1.getSources)({
|
|
145
|
+
sources: sourcesWithLayout,
|
|
146
|
+
renderingMode: self.renderingMode,
|
|
147
|
+
sampleInfo: self.sampleInfo,
|
|
148
|
+
})
|
|
149
|
+
: undefined;
|
|
162
150
|
},
|
|
163
151
|
}))
|
|
164
152
|
.views(self => {
|
|
@@ -261,6 +249,7 @@ function MultiVariantBaseModelF(configSchema) {
|
|
|
261
249
|
},
|
|
262
250
|
{
|
|
263
251
|
label: 'Cluster by genotype',
|
|
252
|
+
icon: Category_1.default,
|
|
264
253
|
onClick: () => {
|
|
265
254
|
(0, util_1.getSession)(self).queueDialog(handleClose => [
|
|
266
255
|
ClusterDialog,
|
|
@@ -295,13 +284,16 @@ function MultiVariantBaseModelF(configSchema) {
|
|
|
295
284
|
var _a;
|
|
296
285
|
return self.rowHeight * (((_a = self.sources) === null || _a === void 0 ? void 0 : _a.length) || 1);
|
|
297
286
|
},
|
|
287
|
+
get featuresReady() {
|
|
288
|
+
return !!self.featuresVolatile;
|
|
289
|
+
},
|
|
298
290
|
}))
|
|
299
291
|
.views(self => ({
|
|
300
292
|
renderProps() {
|
|
301
293
|
const superProps = self.adapterProps();
|
|
302
294
|
return {
|
|
303
295
|
...superProps,
|
|
304
|
-
notReady: superProps.notReady || !self.sources || !self.
|
|
296
|
+
notReady: superProps.notReady || !self.sources || !self.featuresReady,
|
|
305
297
|
height: self.height,
|
|
306
298
|
totalHeight: self.totalHeight,
|
|
307
299
|
renderingMode: self.renderingMode,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const ui_1 = require("@jbrowse/core/ui");
|
|
9
|
+
const material_1 = require("@mui/material");
|
|
10
|
+
const mobx_react_1 = require("mobx-react");
|
|
11
|
+
const ClusterDialogAuto_1 = __importDefault(require("./ClusterDialogAuto"));
|
|
12
|
+
const ClusterDialogManual_1 = __importDefault(require("./ClusterDialogManual"));
|
|
13
|
+
function Header({ activeMode, setActiveMode, }) {
|
|
14
|
+
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { style: { marginBottom: 30 }, children: "This procedure will cluster the visible genotype data using hierarchical clustering" }), (0, jsx_runtime_1.jsx)(material_1.RadioGroup, { children: Object.entries({
|
|
15
|
+
auto: ((0, jsx_runtime_1.jsx)("div", { children: "Run in-app clustering (slow for large data, built in JS clustering)" })),
|
|
16
|
+
manual: ((0, jsx_runtime_1.jsx)("div", { children: "Download R script to run clustering (faster for large data, uses hclust, may be more accurate)" })),
|
|
17
|
+
}).map(([key, val]) => ((0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Radio, { checked: activeMode === key, onChange: () => {
|
|
18
|
+
setActiveMode(key);
|
|
19
|
+
} }), label: val }, key))) })] }));
|
|
20
|
+
}
|
|
21
|
+
const ClusterDialog = (0, mobx_react_1.observer)(function ({ model, handleClose, }) {
|
|
22
|
+
const [activeMode, setActiveMode] = (0, react_1.useState)('auto');
|
|
23
|
+
return ((0, jsx_runtime_1.jsx)(ui_1.Dialog, { open: true, title: "Cluster by genotype", onClose: (_, reason) => {
|
|
24
|
+
if (reason !== 'backdropClick') {
|
|
25
|
+
handleClose();
|
|
26
|
+
}
|
|
27
|
+
}, children: activeMode === 'auto' ? ((0, jsx_runtime_1.jsx)(ClusterDialogAuto_1.default, { model: model, handleClose: handleClose, children: (0, jsx_runtime_1.jsx)(Header, { activeMode: activeMode, setActiveMode: setActiveMode }) })) : ((0, jsx_runtime_1.jsx)(ClusterDialogManual_1.default, { model: model, handleClose: handleClose, children: (0, jsx_runtime_1.jsx)(Header, { activeMode: activeMode, setActiveMode: setActiveMode }) })) }));
|
|
28
|
+
});
|
|
29
|
+
exports.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,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ui_1 = require("@jbrowse/core/ui");
|
|
6
|
+
const util_1 = require("@jbrowse/core/util");
|
|
7
|
+
const stopToken_1 = require("@jbrowse/core/util/stopToken");
|
|
8
|
+
const tracks_1 = require("@jbrowse/core/util/tracks");
|
|
9
|
+
const material_1 = require("@mui/material");
|
|
10
|
+
const mobx_react_1 = require("mobx-react");
|
|
11
|
+
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
12
|
+
const ClusterDialogAuto = (0, mobx_react_1.observer)(function ({ model, children, handleClose, }) {
|
|
13
|
+
const [progress, setProgress] = (0, react_1.useState)('');
|
|
14
|
+
const [error, setError] = (0, react_1.useState)();
|
|
15
|
+
const [stopToken, setStopToken] = (0, react_1.useState)('');
|
|
16
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { children: [children, (0, jsx_runtime_1.jsxs)("div", { children: [progress ? ((0, jsx_runtime_1.jsxs)("div", { style: { padding: 50 }, children: [(0, jsx_runtime_1.jsxs)("span", { style: { width: 400 }, children: ["Progress: ", progress] }), (0, jsx_runtime_1.jsx)(material_1.Button, { onClick: () => {
|
|
17
|
+
(0, stopToken_1.stopStopToken)(stopToken);
|
|
18
|
+
}, children: "Stop" })] })) : null, error ? (0, jsx_runtime_1.jsx)(ui_1.ErrorMessage, { error: error }) : null] })] }), (0, jsx_runtime_1.jsxs)(material_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: async () => {
|
|
19
|
+
try {
|
|
20
|
+
setError(undefined);
|
|
21
|
+
const view = (0, util_1.getContainingView)(model);
|
|
22
|
+
if (!view.initialized) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const { rpcManager } = (0, util_1.getSession)(model);
|
|
26
|
+
const { sourcesWithoutLayout, minorAlleleFrequencyFilter, adapterConfig, } = model;
|
|
27
|
+
if (sourcesWithoutLayout) {
|
|
28
|
+
const sessionId = (0, tracks_1.getRpcSessionId)(model);
|
|
29
|
+
const stopToken = (0, stopToken_1.createStopToken)();
|
|
30
|
+
setStopToken(stopToken);
|
|
31
|
+
const ret = (await rpcManager.call(sessionId, 'MultiVariantClusterGenotypeMatrix', {
|
|
32
|
+
regions: view.dynamicBlocks.contentBlocks,
|
|
33
|
+
sources: sourcesWithoutLayout,
|
|
34
|
+
minorAlleleFrequencyFilter,
|
|
35
|
+
sessionId,
|
|
36
|
+
adapterConfig,
|
|
37
|
+
stopToken,
|
|
38
|
+
statusCallback: (arg) => {
|
|
39
|
+
setProgress(arg);
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
model.setLayout(ret.order.map(idx => {
|
|
43
|
+
const ret = sourcesWithoutLayout[idx];
|
|
44
|
+
if (!ret) {
|
|
45
|
+
throw new Error(`out of bounds at ${idx}`);
|
|
46
|
+
}
|
|
47
|
+
return ret;
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
handleClose();
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
if (!(0, util_1.isAbortException)(e) && (0, mobx_state_tree_1.isAlive)(model)) {
|
|
54
|
+
console.error(e);
|
|
55
|
+
setError(e);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
setProgress('');
|
|
60
|
+
setStopToken('');
|
|
61
|
+
}
|
|
62
|
+
}, children: "Run clustering" }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
63
|
+
handleClose();
|
|
64
|
+
if (stopToken) {
|
|
65
|
+
(0, stopToken_1.stopStopToken)(stopToken);
|
|
66
|
+
}
|
|
67
|
+
}, children: "Cancel" })] })] }));
|
|
68
|
+
});
|
|
69
|
+
exports.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,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const ui_1 = require("@jbrowse/core/ui");
|
|
9
|
+
const util_1 = require("@jbrowse/core/util");
|
|
10
|
+
const tracks_1 = require("@jbrowse/core/util/tracks");
|
|
11
|
+
const material_1 = require("@mui/material");
|
|
12
|
+
const copy_to_clipboard_1 = __importDefault(require("copy-to-clipboard"));
|
|
13
|
+
const file_saver_1 = require("file-saver");
|
|
14
|
+
const mobx_react_1 = require("mobx-react");
|
|
15
|
+
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
16
|
+
const mui_1 = require("tss-react/mui");
|
|
17
|
+
const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
18
|
+
textAreaFont: {
|
|
19
|
+
fontFamily: 'Courier New',
|
|
20
|
+
},
|
|
21
|
+
mgap: {
|
|
22
|
+
display: 'flex',
|
|
23
|
+
flexDirection: 'column',
|
|
24
|
+
gap: theme.spacing(4),
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
const ClusterDialogManuals = (0, mobx_react_1.observer)(function ({ model, handleClose, children, }) {
|
|
28
|
+
const { classes } = useStyles();
|
|
29
|
+
const [paste, setPaste] = (0, react_1.useState)('');
|
|
30
|
+
const [ret, setRet] = (0, react_1.useState)();
|
|
31
|
+
const [error, setError] = (0, react_1.useState)();
|
|
32
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
33
|
+
const [showAdvanced, setShowAdvanced] = (0, react_1.useState)(false);
|
|
34
|
+
const [clusterMethod, setClusterMethod] = (0, util_1.useLocalStorage)('cluster-clusterMethod', 'single');
|
|
35
|
+
(0, react_1.useEffect)(() => {
|
|
36
|
+
;
|
|
37
|
+
(async () => {
|
|
38
|
+
try {
|
|
39
|
+
setError(undefined);
|
|
40
|
+
setRet(undefined);
|
|
41
|
+
setLoading(true);
|
|
42
|
+
const view = (0, util_1.getContainingView)(model);
|
|
43
|
+
if (!view.initialized) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const { rpcManager } = (0, util_1.getSession)(model);
|
|
47
|
+
const { sourcesWithoutLayout, minorAlleleFrequencyFilter, adapterConfig, } = model;
|
|
48
|
+
const sessionId = (0, tracks_1.getRpcSessionId)(model);
|
|
49
|
+
const ret = (await rpcManager.call(sessionId, 'MultiVariantGetGenotypeMatrix', {
|
|
50
|
+
regions: view.dynamicBlocks.contentBlocks,
|
|
51
|
+
sources: sourcesWithoutLayout,
|
|
52
|
+
minorAlleleFrequencyFilter,
|
|
53
|
+
sessionId,
|
|
54
|
+
adapterConfig,
|
|
55
|
+
}));
|
|
56
|
+
setRet(ret);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
if (!(0, util_1.isAbortException)(e) && (0, mobx_state_tree_1.isAlive)(model)) {
|
|
60
|
+
console.error(e);
|
|
61
|
+
setError(e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setLoading(false);
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
}, [model]);
|
|
69
|
+
const results = ret
|
|
70
|
+
? `inputMatrix<-matrix(c(${Object.values(ret)
|
|
71
|
+
.map(val => val.join(','))
|
|
72
|
+
.join(',\n')}
|
|
73
|
+
),nrow=${Object.values(ret).length},byrow=TRUE)
|
|
74
|
+
rownames(inputMatrix)<-c(${Object.keys(ret)
|
|
75
|
+
.map(key => `'${key}'`)
|
|
76
|
+
.join(',')})
|
|
77
|
+
resultClusters<-hclust(dist(inputMatrix), method='${clusterMethod}')
|
|
78
|
+
cat(resultClusters$order,sep='\\n')`
|
|
79
|
+
: undefined;
|
|
80
|
+
const resultsTsv = ret
|
|
81
|
+
? Object.entries(ret)
|
|
82
|
+
.map(([key, val]) => [key, ...val].join('\t'))
|
|
83
|
+
.join('\n')
|
|
84
|
+
: undefined;
|
|
85
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { children: [children, (0, jsx_runtime_1.jsxs)(material_1.Paper, { style: { padding: 16 }, children: [(0, jsx_runtime_1.jsxs)("div", { style: {
|
|
86
|
+
display: 'flex',
|
|
87
|
+
gap: '8px',
|
|
88
|
+
flexWrap: 'wrap',
|
|
89
|
+
marginBottom: '16px',
|
|
90
|
+
}, children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
91
|
+
(0, file_saver_1.saveAs)(new Blob([results || ''], {
|
|
92
|
+
type: 'text/plain;charset=utf-8',
|
|
93
|
+
}), 'cluster.R');
|
|
94
|
+
}, children: "Download Rscript" }), ' ', "or", ' ', (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
95
|
+
(0, copy_to_clipboard_1.default)(results || '');
|
|
96
|
+
}, children: "Copy Rscript to clipboard" }), ' ', "or", ' ', (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
97
|
+
(0, file_saver_1.saveAs)(new Blob([resultsTsv || ''], {
|
|
98
|
+
type: 'text/plain;charset=utf-8',
|
|
99
|
+
}), 'genotypes.tsv');
|
|
100
|
+
}, children: "Download TSV" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
101
|
+
setShowAdvanced(!showAdvanced);
|
|
102
|
+
}, children: showAdvanced
|
|
103
|
+
? 'Hide advanced options'
|
|
104
|
+
: 'Show advanced options' }), showAdvanced ? ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h6", children: "Advanced options" }), (0, jsx_runtime_1.jsx)(material_1.RadioGroup, { children: Object.entries({
|
|
105
|
+
single: 'Single',
|
|
106
|
+
complete: 'Complete',
|
|
107
|
+
}).map(([key, val]) => ((0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Radio, { checked: clusterMethod === key, onChange: () => {
|
|
108
|
+
setClusterMethod(key);
|
|
109
|
+
} }), label: val }, key))) })] })) : null] }), results ? ((0, jsx_runtime_1.jsx)("div", {})) : loading ? ((0, jsx_runtime_1.jsx)(ui_1.LoadingEllipses, { variant: "h6", title: "Generating genotype matrix" })) : error ? ((0, jsx_runtime_1.jsx)(ui_1.ErrorMessage, { error: error })) : null] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "subtitle2", gutterBottom: true, style: { marginTop: '16px' }, children: "Clustering Results:" }), (0, jsx_runtime_1.jsx)(material_1.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 => {
|
|
110
|
+
setPaste(event.target.value);
|
|
111
|
+
}, slotProps: {
|
|
112
|
+
input: {
|
|
113
|
+
classes: {
|
|
114
|
+
input: classes.textAreaFont,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
} })] })] })] }), (0, jsx_runtime_1.jsxs)(material_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
118
|
+
const { sourcesWithoutLayout } = model;
|
|
119
|
+
if (sourcesWithoutLayout) {
|
|
120
|
+
try {
|
|
121
|
+
model.setLayout(paste
|
|
122
|
+
.split('\n')
|
|
123
|
+
.map(t => t.trim())
|
|
124
|
+
.filter(f => !!f)
|
|
125
|
+
.map(r => +r)
|
|
126
|
+
.map(idx => {
|
|
127
|
+
const ret = sourcesWithoutLayout[idx - 1];
|
|
128
|
+
if (!ret) {
|
|
129
|
+
throw new Error(`out of bounds at ${idx}`);
|
|
130
|
+
}
|
|
131
|
+
return ret;
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
console.error(e);
|
|
136
|
+
(0, util_1.getSession)(model).notifyError(`${e}`, e);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
handleClose();
|
|
140
|
+
}, children: "Apply clustering" }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
141
|
+
handleClose();
|
|
142
|
+
}, children: "Cancel" })] })] }));
|
|
143
|
+
});
|
|
144
|
+
exports.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
|
+
}
|
|
@@ -20,49 +20,56 @@ const useStyles = (0, mui_1.makeStyles)()({
|
|
|
20
20
|
});
|
|
21
21
|
function SourcesDataGrid({ rows, onChange, setSelected, }) {
|
|
22
22
|
const { classes } = useStyles();
|
|
23
|
-
const { id: _id, name: _name, label: _label, color: _color, baseUri: _baseUri, HP: _HP, ...rest } = rows[0];
|
|
24
23
|
const [currSort, setCurrSort] = (0, react_1.useState)({
|
|
25
24
|
idx: 0,
|
|
26
25
|
field: null,
|
|
27
26
|
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
elt
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
if (rows.length) {
|
|
28
|
+
const { id: _id, name: _name, label: _label, color: _color, baseUri: _baseUri, HP: _HP, ...rest } = rows[0];
|
|
29
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: { height: 400, width: '100%' }, children: (0, jsx_runtime_1.jsx)(x_data_grid_1.DataGrid, { checkboxSelection: true, disableRowSelectionOnClick: true, onRowSelectionModelChange: arg => {
|
|
30
|
+
setSelected(arg);
|
|
31
|
+
}, rows: rows, rowHeight: 25, columnHeaderHeight: 33, columns: [
|
|
32
|
+
{
|
|
33
|
+
field: 'color',
|
|
34
|
+
headerName: 'Color',
|
|
35
|
+
renderCell: params => {
|
|
36
|
+
const { value, id } = params;
|
|
37
|
+
return ((0, jsx_runtime_1.jsx)(ColorPicker_1.default, { color: value || 'blue', onChange: c => {
|
|
38
|
+
const elt = rows.find(f => f.name === id);
|
|
39
|
+
if (elt) {
|
|
40
|
+
elt.color = c;
|
|
41
|
+
}
|
|
42
|
+
onChange([...rows]);
|
|
43
|
+
} }));
|
|
44
|
+
},
|
|
43
45
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
46
|
+
{
|
|
47
|
+
field: 'label',
|
|
48
|
+
headerName: 'Name',
|
|
49
|
+
width: (0, util_1.measureGridWidth)(rows.map(r => r.label)),
|
|
50
|
+
},
|
|
51
|
+
...Object.keys(rest).map(val => ({
|
|
52
|
+
field: val,
|
|
53
|
+
renderCell: ({ value }) => ((0, jsx_runtime_1.jsx)("div", { className: classes.cell, children: (0, jsx_runtime_1.jsx)(ui_1.SanitizedHTML, { html: (0, util_1.getStr)(value) }) })),
|
|
54
|
+
width: (0, util_1.measureGridWidth)(rows.map(r => `${r[val]}`)),
|
|
55
|
+
})),
|
|
56
|
+
], sortModel: [], onSortModelChange: args => {
|
|
57
|
+
const sort = args[0];
|
|
58
|
+
const idx = (currSort.idx + 1) % 2;
|
|
59
|
+
const field = sort.field || currSort.field;
|
|
60
|
+
setCurrSort({ idx, field });
|
|
61
|
+
onChange(field
|
|
62
|
+
? [...rows].sort((a, b) => {
|
|
63
|
+
const aa = (0, util_1.getStr)(a[field]);
|
|
64
|
+
const bb = (0, util_1.getStr)(b[field]);
|
|
65
|
+
return idx === 1
|
|
66
|
+
? aa.localeCompare(bb)
|
|
67
|
+
: bb.localeCompare(aa);
|
|
68
|
+
})
|
|
69
|
+
: rows);
|
|
70
|
+
} }) }));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
return (0, jsx_runtime_1.jsx)("div", { children: "No rows, check sample metadata configuration" });
|
|
74
|
+
}
|
|
68
75
|
}
|
|
@@ -28,12 +28,12 @@ function SourcesGridHeader({ selected, onChange, rows, showTips, }) {
|
|
|
28
28
|
onChange((0, sourcesGridUtils_1.moveDown)([...rows], selected, rows.length));
|
|
29
29
|
}, disabled: !selected.length, children: [(0, jsx_runtime_1.jsx)(KeyboardDoubleArrowDown_1.default, {}), showTips ? 'Move selected items to bottom' : null] }), (0, jsx_runtime_1.jsx)(ColorPicker_1.ColorPopover, { anchorEl: anchorEl, color: widgetColor, onChange: c => {
|
|
30
30
|
setWidgetColor(c);
|
|
31
|
-
|
|
31
|
+
for (const id of selected) {
|
|
32
32
|
const elt = rows.find(f => f.name === id);
|
|
33
33
|
if (elt) {
|
|
34
34
|
elt.color = c;
|
|
35
35
|
}
|
|
36
|
-
}
|
|
36
|
+
}
|
|
37
37
|
onChange([...rows]);
|
|
38
38
|
}, onClose: () => {
|
|
39
39
|
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,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSources = getSources;
|
|
4
|
+
function getSources({ sources, layout = sources, renderingMode, sampleInfo, }) {
|
|
5
|
+
const rows = [];
|
|
6
|
+
const sourceMap = Object.fromEntries(sources.map(s => [s.name, s]));
|
|
7
|
+
for (const row of layout) {
|
|
8
|
+
if (renderingMode === 'phased') {
|
|
9
|
+
const info = sampleInfo === null || sampleInfo === void 0 ? void 0 : sampleInfo[row.name];
|
|
10
|
+
if (info === null || info === void 0 ? void 0 : info.isPhased) {
|
|
11
|
+
const ploidy = info.maxPloidy;
|
|
12
|
+
for (let i = 0; i < ploidy; i++) {
|
|
13
|
+
const id = `${row.name} HP${i}`;
|
|
14
|
+
rows.push({
|
|
15
|
+
...sourceMap[row.name],
|
|
16
|
+
...row,
|
|
17
|
+
label: id,
|
|
18
|
+
HP: i,
|
|
19
|
+
id: id,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
rows.push({
|
|
26
|
+
...sourceMap[row.name],
|
|
27
|
+
...row,
|
|
28
|
+
label: row.name,
|
|
29
|
+
id: row.name,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return rows;
|
|
34
|
+
}
|
|
@@ -259,6 +259,15 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
259
259
|
setSampleInfo(arg: Record<string, import("../shared/types").SampleInfo>): void;
|
|
260
260
|
} & {
|
|
261
261
|
readonly preSources: import("../shared/types").Source[] | undefined;
|
|
262
|
+
readonly sourcesWithoutLayout: {
|
|
263
|
+
label: string;
|
|
264
|
+
id: string;
|
|
265
|
+
baseUri?: string;
|
|
266
|
+
name: string;
|
|
267
|
+
color?: string;
|
|
268
|
+
group?: string;
|
|
269
|
+
HP?: number;
|
|
270
|
+
}[] | undefined;
|
|
262
271
|
readonly sources: {
|
|
263
272
|
label: string;
|
|
264
273
|
id: string;
|
|
@@ -334,10 +343,20 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
334
343
|
type?: undefined;
|
|
335
344
|
checked?: undefined;
|
|
336
345
|
onClick?: undefined;
|
|
346
|
+
} | {
|
|
347
|
+
label: string;
|
|
348
|
+
icon: import("@mui/material/OverridableComponent").OverridableComponent<import("@mui/material").SvgIconTypeMap<{}, "svg">> & {
|
|
349
|
+
muiName: string;
|
|
350
|
+
};
|
|
351
|
+
onClick: () => void;
|
|
352
|
+
type?: undefined;
|
|
353
|
+
checked?: undefined;
|
|
354
|
+
subMenu?: undefined;
|
|
337
355
|
})[];
|
|
338
356
|
} & {
|
|
339
357
|
readonly canDisplayLabels: boolean;
|
|
340
358
|
readonly totalHeight: number;
|
|
359
|
+
readonly featuresReady: boolean;
|
|
341
360
|
} & {
|
|
342
361
|
renderProps(): any;
|
|
343
362
|
} & {
|