@jbrowse/core 2.11.1 → 2.12.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/BaseFeatureWidget/BaseFeatureDetail/DataGridDetails.js +17 -26
- package/BaseFeatureWidget/BaseFeatureDetail/index.js +1 -3
- package/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +24 -85
- package/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +2 -2
- package/BaseFeatureWidget/SequenceFeatureDetails/SequencePanel.d.ts +3 -4
- package/BaseFeatureWidget/SequenceFeatureDetails/SequencePanel.js +55 -24
- package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceDialog.d.ts +9 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceDialog.js +69 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceFeatureMenu.d.ts +9 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceFeatureMenu.js +126 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceTypeSelector.d.ts +6 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceTypeSelector.js +68 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SettingsDialog.js +19 -10
- package/BaseFeatureWidget/SequenceFeatureDetails/model.d.ts +47 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/model.js +83 -9
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.d.ts +3 -1
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +76 -19
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDSSequence.d.ts +5 -2
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDSSequence.js +12 -3
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/GenomicSequence.d.ts +7 -2
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/GenomicSequence.js +51 -5
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/ProteinSequence.d.ts +5 -2
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/ProteinSequence.js +12 -3
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/SequenceDisplay.d.ts +11 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/SequenceDisplay.js +30 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/util.d.ts +11 -0
- package/BaseFeatureWidget/SequenceFeatureDetails/util.js +43 -1
- package/BaseFeatureWidget/stateModelFactory.d.ts +32 -2
- package/PluginManager.d.ts +2 -11
- package/ReExports/modules.d.ts +2 -11
- package/configuration/util.js +4 -11
- package/package.json +5 -3
- package/pluggableElementTypes/models/BaseDisplayModel.d.ts +3 -3
- package/pluggableElementTypes/models/BaseDisplayModel.js +3 -3
- package/pluggableElementTypes/renderers/util/serializableFilterChain.js +4 -1
- package/rpc/methods/util.d.ts +3 -2
- package/rpc/methods/util.js +0 -7
- package/stories/JBrowseCore.stories.d.ts +5 -0
- package/stories/JBrowseCore.stories.js +10 -0
- package/stories/examples/WithSequencePanel.d.ts +7 -0
- package/stories/examples/WithSequencePanel.js +43 -0
- package/stories/examples/index.d.ts +1 -0
- package/stories/examples/index.js +17 -0
- package/stories/examples/util.d.ts +33 -0
- package/stories/examples/util.js +235 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/ui/Dialog.js +2 -1
- package/ui/ErrorMessageStackTraceDialog.js +13 -8
- package/util/Base1DUtils.d.ts +1 -1
- package/util/Base1DUtils.js +3 -7
- package/util/Base1DViewModel.d.ts +5 -16
- package/util/Base1DViewModel.js +6 -12
- package/util/blockTypes.d.ts +4 -0
- package/util/blockTypes.js +14 -3
- package/util/calculateDynamicBlocks.js +2 -3
- package/util/index.d.ts +1 -1
- package/util/range.d.ts +1 -1
- package/util/tracks.d.ts +3 -2
- package/util/tracks.js +10 -6
- package/util/types/index.d.ts +4 -1
- package/ui/ResizeBar.d.ts +0 -7
- package/ui/ResizeBar.js +0 -80
- package/ui/useResizeBar.d.ts +0 -5
- package/ui/useResizeBar.js +0 -22
|
@@ -32,14 +32,11 @@ const x_data_grid_1 = require("@mui/x-data-grid");
|
|
|
32
32
|
const material_1 = require("@mui/material");
|
|
33
33
|
// locals
|
|
34
34
|
const util_1 = require("../../util");
|
|
35
|
-
const ResizeBar_1 = __importDefault(require("../../ui/ResizeBar"));
|
|
36
35
|
const FieldName_1 = __importDefault(require("./FieldName"));
|
|
37
|
-
const useResizeBar_1 = require("../../ui/useResizeBar");
|
|
38
36
|
const ui_1 = require("../../ui");
|
|
39
37
|
const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
40
38
|
margin: {
|
|
41
|
-
|
|
42
|
-
width: '100%',
|
|
39
|
+
marginBottom: theme.spacing(4),
|
|
43
40
|
},
|
|
44
41
|
cell: {
|
|
45
42
|
whiteSpace: 'nowrap',
|
|
@@ -49,7 +46,6 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
|
49
46
|
}));
|
|
50
47
|
function DataGridDetails({ value, prefix, name, }) {
|
|
51
48
|
const { classes } = useStyles();
|
|
52
|
-
const { ref, scrollLeft } = (0, useResizeBar_1.useResizeBar)();
|
|
53
49
|
const [checked, setChecked] = (0, react_1.useState)(false);
|
|
54
50
|
const keys = Object.keys(value[0]).sort();
|
|
55
51
|
const unionKeys = new Set(keys);
|
|
@@ -77,31 +73,26 @@ function DataGridDetails({ value, prefix, name, }) {
|
|
|
77
73
|
else {
|
|
78
74
|
colNames = [...unionKeys];
|
|
79
75
|
}
|
|
80
|
-
const
|
|
76
|
+
const widths = colNames.map(e => (0, util_1.measureGridWidth)(rows.map(r => r[e])));
|
|
81
77
|
if (unionKeys.size < keys.length + 5) {
|
|
82
|
-
return (react_1.default.createElement(
|
|
78
|
+
return (react_1.default.createElement("div", { className: classes.margin },
|
|
83
79
|
react_1.default.createElement(FieldName_1.default, { prefix: prefix, name: name }),
|
|
84
80
|
react_1.default.createElement(material_1.FormControlLabel, { control: react_1.default.createElement(material_1.Checkbox, { checked: checked, onChange: event => setChecked(event.target.checked) }), label: react_1.default.createElement(material_1.Typography, { variant: "body2" }, "Show options") }),
|
|
85
|
-
react_1.default.createElement(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// columns below
|
|
90
|
-
rows: rows, rowCount: 25, rowHeight: 25, columnHeaderHeight: 35, hideFooter: rows.length < 25, slots: { toolbar: checked ? x_data_grid_1.GridToolbar : null }, slotProps: {
|
|
91
|
-
toolbar: {
|
|
92
|
-
printOptions: {
|
|
93
|
-
disableToolbarButton: true,
|
|
94
|
-
},
|
|
81
|
+
react_1.default.createElement(x_data_grid_1.DataGrid, { disableRowSelectionOnClick: true, rows: rows, rowCount: 25, rowHeight: 25, columnHeaderHeight: 35, hideFooter: rows.length < 25, slots: { toolbar: checked ? x_data_grid_1.GridToolbar : null }, slotProps: {
|
|
82
|
+
toolbar: {
|
|
83
|
+
printOptions: {
|
|
84
|
+
disableToolbarButton: true,
|
|
95
85
|
},
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
86
|
+
},
|
|
87
|
+
}, columns: colNames.map((val, index) => ({
|
|
88
|
+
field: val,
|
|
89
|
+
renderCell: params => {
|
|
90
|
+
const value = params.value;
|
|
91
|
+
return (react_1.default.createElement("div", { className: classes.cell },
|
|
92
|
+
react_1.default.createElement(ui_1.SanitizedHTML, { html: (0, util_1.getStr)(value) })));
|
|
93
|
+
},
|
|
94
|
+
width: widths[index],
|
|
95
|
+
})) })));
|
|
105
96
|
}
|
|
106
97
|
return null;
|
|
107
98
|
}
|
|
@@ -69,9 +69,7 @@ function BaseCard({ children, title, defaultExpanded = true, }) {
|
|
|
69
69
|
const [expanded, setExpanded] = (0, react_1.useState)(defaultExpanded);
|
|
70
70
|
return (react_1.default.createElement(material_1.Accordion, { expanded: expanded, onChange: () => setExpanded(s => !s), TransitionProps: { unmountOnExit: true, timeout: 150 } },
|
|
71
71
|
react_1.default.createElement(material_1.AccordionSummary, { expandIcon: react_1.default.createElement(ExpandMore_1.default, { className: classes.expandIcon }) },
|
|
72
|
-
react_1.default.createElement(material_1.Typography, { variant: "button" },
|
|
73
|
-
" ",
|
|
74
|
-
title)),
|
|
72
|
+
react_1.default.createElement(material_1.Typography, { variant: "button" }, title)),
|
|
75
73
|
react_1.default.createElement(material_1.AccordionDetails, { className: classes.expansionPanelDetails }, children)));
|
|
76
74
|
}
|
|
77
75
|
exports.BaseCard = BaseCard;
|
|
@@ -28,110 +28,49 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
const react_1 = __importStar(require("react"));
|
|
30
30
|
const material_1 = require("@mui/material");
|
|
31
|
-
const mui_1 = require("tss-react/mui");
|
|
32
31
|
const mobx_react_1 = require("mobx-react");
|
|
33
|
-
const copy_to_clipboard_1 = __importDefault(require("copy-to-clipboard"));
|
|
34
32
|
// locals
|
|
35
33
|
const hooks_1 = require("./hooks");
|
|
36
34
|
const ui_1 = require("../../ui");
|
|
37
|
-
const util_1 = require("../../util");
|
|
38
35
|
// icons
|
|
39
|
-
const
|
|
36
|
+
const SequenceFeatureMenu_1 = __importDefault(require("./dialogs/SequenceFeatureMenu"));
|
|
37
|
+
const SequenceTypeSelector_1 = __importDefault(require("./dialogs/SequenceTypeSelector"));
|
|
40
38
|
// lazies
|
|
41
39
|
const SequencePanel = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./SequencePanel'))));
|
|
42
|
-
const
|
|
43
|
-
const useStyles = (0, mui_1.makeStyles)()({
|
|
44
|
-
formControl: {
|
|
45
|
-
margin: 0,
|
|
46
|
-
marginLeft: 4,
|
|
47
|
-
},
|
|
48
|
-
});
|
|
40
|
+
const SequenceDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./dialogs/SequenceDialog'))));
|
|
49
41
|
// set the key on this component to feature.id to clear state after new feature
|
|
50
42
|
// is selected
|
|
51
43
|
const SequenceFeatureDetails = (0, mobx_react_1.observer)(function ({ model, feature, }) {
|
|
52
|
-
var _a, _b;
|
|
53
44
|
const { sequenceFeatureDetails } = model;
|
|
54
|
-
const {
|
|
55
|
-
const { classes } = useStyles();
|
|
45
|
+
const { upDownBp } = sequenceFeatureDetails;
|
|
56
46
|
const seqPanelRef = (0, react_1.useRef)(null);
|
|
47
|
+
const [openInDialog, setOpenInDialog] = (0, react_1.useState)(false);
|
|
57
48
|
const [force, setForce] = (0, react_1.useState)(false);
|
|
58
|
-
const hasCDS = (_a = feature.subfeatures) === null || _a === void 0 ? void 0 : _a.some(sub => sub.type === 'CDS');
|
|
59
|
-
const hasExon = (_b = feature.subfeatures) === null || _b === void 0 ? void 0 : _b.some(sub => sub.type === 'exon');
|
|
60
|
-
const hasExonOrCDS = hasExon || hasCDS;
|
|
61
49
|
const { sequence, error } = (0, hooks_1.useFeatureSequence)(model, feature, upDownBp, force);
|
|
62
|
-
|
|
50
|
+
(0, react_1.useEffect)(() => {
|
|
51
|
+
sequenceFeatureDetails.setFeature(feature);
|
|
52
|
+
}, [sequenceFeatureDetails, feature]);
|
|
63
53
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
64
54
|
react_1.default.createElement("div", null,
|
|
65
|
-
react_1.default.createElement(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
: {}),
|
|
82
|
-
...(hasExonOrCDS
|
|
83
|
-
? {
|
|
84
|
-
gene: `Genomic w/ full introns`,
|
|
85
|
-
}
|
|
86
|
-
: {}),
|
|
87
|
-
...(hasExonOrCDS
|
|
88
|
-
? {
|
|
89
|
-
gene_updownstream: `Genomic w/ full introns +/- ${upDownBp}bp up+down stream`,
|
|
90
|
-
}
|
|
91
|
-
: {}),
|
|
92
|
-
...(hasExonOrCDS
|
|
93
|
-
? {
|
|
94
|
-
gene_collapsed_intron: `Genomic w/ ${intronBp}bp intron`,
|
|
95
|
-
}
|
|
96
|
-
: {}),
|
|
97
|
-
...(hasExonOrCDS
|
|
98
|
-
? {
|
|
99
|
-
gene_updownstream_collapsed_intron: `Genomic w/ ${intronBp}bp intron +/- ${upDownBp}bp up+down stream `,
|
|
100
|
-
}
|
|
101
|
-
: {}),
|
|
102
|
-
...(!hasExonOrCDS
|
|
103
|
-
? {
|
|
104
|
-
genomic: 'Genomic',
|
|
105
|
-
}
|
|
106
|
-
: {}),
|
|
107
|
-
...(!hasExonOrCDS
|
|
108
|
-
? {
|
|
109
|
-
genomic_sequence_updownstream: `Genomic +/- ${upDownBp}bp up+down stream`,
|
|
110
|
-
}
|
|
111
|
-
: {}),
|
|
112
|
-
}).map(([key, val]) => (react_1.default.createElement(material_1.MenuItem, { key: key, value: key }, val))))),
|
|
113
|
-
react_1.default.createElement(material_1.Button, { className: classes.formControl, variant: "contained", onClick: () => {
|
|
114
|
-
const ref = seqPanelRef.current;
|
|
115
|
-
if (ref) {
|
|
116
|
-
(0, copy_to_clipboard_1.default)(ref.textContent || '', { format: 'text/plain' });
|
|
117
|
-
}
|
|
118
|
-
} }, "Copy plaintext"),
|
|
119
|
-
react_1.default.createElement(material_1.Button, { className: classes.formControl, variant: "contained", onClick: () => {
|
|
120
|
-
const ref = seqPanelRef.current;
|
|
121
|
-
if (ref) {
|
|
122
|
-
(0, copy_to_clipboard_1.default)(ref.innerHTML, { format: 'text/html' });
|
|
123
|
-
}
|
|
124
|
-
} }, "Copy HTML"),
|
|
125
|
-
react_1.default.createElement(material_1.IconButton, { className: classes.formControl, onClick: () => (0, util_1.getSession)(model).queueDialog(handleClose => [
|
|
126
|
-
SettingsDialog,
|
|
127
|
-
{ model: sequenceFeatureDetails, handleClose },
|
|
128
|
-
]) },
|
|
129
|
-
react_1.default.createElement(Settings_1.default, null))),
|
|
130
|
-
react_1.default.createElement("div", null,
|
|
55
|
+
react_1.default.createElement(SequenceTypeSelector_1.default, { model: sequenceFeatureDetails }),
|
|
56
|
+
react_1.default.createElement(SequenceFeatureMenu_1.default, { ref: seqPanelRef, model: sequenceFeatureDetails, extraItems: [
|
|
57
|
+
{
|
|
58
|
+
label: 'Open in dialog',
|
|
59
|
+
onClick: () => {
|
|
60
|
+
// this is given a setTimeout because it allows the menu to
|
|
61
|
+
// close before dialog opens
|
|
62
|
+
setTimeout(() => setOpenInDialog(true), 1);
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
] })),
|
|
66
|
+
openInDialog ? (react_1.default.createElement("div", null,
|
|
67
|
+
"Open in dialog...",
|
|
68
|
+
react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement(ui_1.LoadingEllipses, null) },
|
|
69
|
+
react_1.default.createElement(SequenceDialog, { model: model, feature: feature, handleClose: () => setOpenInDialog(false) })))) : (react_1.default.createElement("div", null,
|
|
131
70
|
feature.type === 'gene' ? (react_1.default.createElement(material_1.Typography, null, "Note: inspect subfeature sequences for protein/CDS computations")) : null,
|
|
132
71
|
error ? (react_1.default.createElement(ui_1.ErrorMessage, { error: error })) : !sequence ? (react_1.default.createElement(ui_1.LoadingEllipses, null)) : sequence ? ('error' in sequence ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
133
72
|
react_1.default.createElement(material_1.Typography, { color: "error" }, sequence.error),
|
|
134
73
|
react_1.default.createElement(material_1.Button, { variant: "contained", color: "inherit", onClick: () => setForce(true) }, "Force load"))) : (react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement(ui_1.LoadingEllipses, null) },
|
|
135
|
-
react_1.default.createElement(SequencePanel, { ref: seqPanelRef, feature: feature,
|
|
74
|
+
react_1.default.createElement(SequencePanel, { ref: seqPanelRef, feature: feature, sequence: sequence, model: sequenceFeatureDetails })))) : (react_1.default.createElement(material_1.Typography, null, "No sequence found"))))));
|
|
136
75
|
});
|
|
137
76
|
exports.default = SequenceFeatureDetails;
|
|
@@ -53,7 +53,7 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
|
53
53
|
const SequenceFeaturePanel = (0, mobx_react_1.observer)(function ({ model, feature, }) {
|
|
54
54
|
const { classes } = useStyles();
|
|
55
55
|
const [shown, setShown] = (0, react_1.useState)(false);
|
|
56
|
-
return
|
|
56
|
+
return model ? (react_1.default.createElement("div", { className: classes.container },
|
|
57
57
|
react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
|
|
58
58
|
react_1.default.createElement(material_1.Button, { variant: "contained", onClick: () => setShown(!shown) }, shown ? 'Hide feature sequence' : 'Show feature sequence')),
|
|
59
59
|
react_1.default.createElement(material_1.IconButton, { onClick: () => (0, util_1.getSession)(model).queueDialog(handleClose => [
|
|
@@ -62,6 +62,6 @@ const SequenceFeaturePanel = (0, mobx_react_1.observer)(function ({ model, featu
|
|
|
62
62
|
]) },
|
|
63
63
|
react_1.default.createElement(Help_1.default, null)),
|
|
64
64
|
shown ? (react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement(ui_1.LoadingEllipses, null) },
|
|
65
|
-
react_1.default.createElement(SequenceFeatureDetails, { key: feature.uniqueId, model: model, feature: feature }))) : null));
|
|
65
|
+
react_1.default.createElement(SequenceFeatureDetails, { key: feature.uniqueId, model: model, feature: feature }))) : null)) : null;
|
|
66
66
|
});
|
|
67
67
|
exports.default = SequenceFeaturePanel;
|
|
@@ -2,11 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import { SimpleFeatureSerialized } from '../../util';
|
|
3
3
|
import { SeqState } from '../util';
|
|
4
4
|
import { SequenceFeatureDetailsModel } from './model';
|
|
5
|
-
interface
|
|
5
|
+
interface SequencePanelProps {
|
|
6
6
|
sequence: SeqState;
|
|
7
7
|
feature: SimpleFeatureSerialized;
|
|
8
|
-
mode: string;
|
|
9
8
|
model: SequenceFeatureDetailsModel;
|
|
10
9
|
}
|
|
11
|
-
declare const
|
|
12
|
-
export default
|
|
10
|
+
declare const SequencePanel: React.ForwardRefExoticComponent<SequencePanelProps & React.RefAttributes<HTMLDivElement>>;
|
|
11
|
+
export default SequencePanel;
|
|
@@ -4,14 +4,48 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const mobx_react_1 = require("mobx-react");
|
|
8
|
+
// locals
|
|
7
9
|
const util_1 = require("../../util");
|
|
8
10
|
const util_2 = require("../util");
|
|
11
|
+
// panel types
|
|
9
12
|
const CDNASequence_1 = __importDefault(require("./seqtypes/CDNASequence"));
|
|
10
13
|
const ProteinSequence_1 = __importDefault(require("./seqtypes/ProteinSequence"));
|
|
11
14
|
const GenomicSequence_1 = __importDefault(require("./seqtypes/GenomicSequence"));
|
|
12
15
|
const CDSSequence_1 = __importDefault(require("./seqtypes/CDSSequence"));
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
function getStrand(strand) {
|
|
17
|
+
if (strand === -1) {
|
|
18
|
+
return '(-)';
|
|
19
|
+
}
|
|
20
|
+
else if (strand === 1) {
|
|
21
|
+
return '(+)';
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function WordWrap({ children }) {
|
|
28
|
+
return (react_1.default.createElement("pre", { style: {
|
|
29
|
+
/* raw styles instead of className so that html copy works */
|
|
30
|
+
fontFamily: 'monospace',
|
|
31
|
+
color: 'black',
|
|
32
|
+
fontSize: 11,
|
|
33
|
+
} }, children));
|
|
34
|
+
}
|
|
35
|
+
function NoWordWrap({ children }) {
|
|
36
|
+
return (react_1.default.createElement("div", { style: {
|
|
37
|
+
/* raw styles instead of className so that html copy works */
|
|
38
|
+
fontFamily: 'monospace',
|
|
39
|
+
color: 'black',
|
|
40
|
+
fontSize: 11,
|
|
41
|
+
maxWidth: 600,
|
|
42
|
+
whiteSpace: 'wrap',
|
|
43
|
+
wordBreak: 'break-all',
|
|
44
|
+
} }, children));
|
|
45
|
+
}
|
|
46
|
+
const SequencePanel = (0, mobx_react_1.observer)(react_1.default.forwardRef(function S(props, ref) {
|
|
47
|
+
const { model, feature } = props;
|
|
48
|
+
const { showCoordinates, mode } = model;
|
|
15
49
|
let { sequence: { seq, upstream = '', downstream = '' }, } = props;
|
|
16
50
|
const { subfeatures = [] } = feature;
|
|
17
51
|
const children = subfeatures
|
|
@@ -21,10 +55,10 @@ const SeqPanel = react_1.default.forwardRef(function SeqPanel2(props, ref) {
|
|
|
21
55
|
start: sub.start - feature.start,
|
|
22
56
|
end: sub.end - feature.start,
|
|
23
57
|
}));
|
|
24
|
-
// we filter duplicate entries in cds and exon lists duplicate entries
|
|
25
|
-
// rare but was seen in Gencode v36 track NCList, likely a bug
|
|
26
|
-
// probably worth ignoring here (produces broken protein
|
|
27
|
-
// included)
|
|
58
|
+
// we filter duplicate entries in cds and exon lists duplicate entries
|
|
59
|
+
// may be rare but was seen in Gencode v36 track NCList, likely a bug
|
|
60
|
+
// on GFF3 or probably worth ignoring here (produces broken protein
|
|
61
|
+
// translations if included)
|
|
28
62
|
//
|
|
29
63
|
// position 1:224,800,006..225,203,064 gene ENSG00000185842.15 first
|
|
30
64
|
// transcript ENST00000445597.6
|
|
@@ -57,21 +91,18 @@ const SeqPanel = react_1.default.forwardRef(function SeqPanel2(props, ref) {
|
|
|
57
91
|
utr = (0, util_2.revlist)(utr, seq.length);
|
|
58
92
|
}
|
|
59
93
|
const codonTable = (0, util_1.generateCodonTable)(util_1.defaultCodonTable);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
mode === 'genomic' ? (react_1.default.createElement(GenomicSequence_1.default, { sequence: seq })) : mode === 'genomic_sequence_updownstream' ? (react_1.default.createElement(GenomicSequence_1.default, { sequence: seq, upstream: upstream, downstream: downstream })) : mode === 'cds' ? (react_1.default.createElement(CDSSequence_1.default, { cds: cds, sequence: seq })) : mode === 'cdna' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, cds: cds, utr: utr, sequence: seq })) : mode === 'protein' ? (react_1.default.createElement(ProteinSequence_1.default, { cds: cds, codonTable: codonTable, sequence: seq })) : mode === 'gene' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, cds: cds, utr: utr, sequence: seq, includeIntrons: true })) : mode === 'gene_collapsed_intron' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, cds: cds, sequence: seq, utr: utr, includeIntrons: true, collapseIntron: true })) : mode === 'gene_updownstream' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, cds: cds, sequence: seq, utr: utr, upstream: upstream, downstream: downstream, includeIntrons: true })) : mode === 'gene_updownstream_collapsed_intron' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, cds: cds, sequence: seq, utr: utr, upstream: upstream, downstream: downstream, includeIntrons: true, collapseIntron: true })) : (react_1.default.createElement("div", null, "Unknown type")))));
|
|
76
|
-
});
|
|
77
|
-
exports.default = SeqPanel;
|
|
94
|
+
const Container = showCoordinates ? WordWrap : NoWordWrap;
|
|
95
|
+
return (react_1.default.createElement("div", { "data-testid": "sequence_panel", ref: ref, style: { maxHeight: 300, overflow: 'auto' } },
|
|
96
|
+
react_1.default.createElement(Container, null,
|
|
97
|
+
react_1.default.createElement("div", { style: { background: 'white' } }, `>${[
|
|
98
|
+
(feature.name || feature.id) + '-' + mode,
|
|
99
|
+
`${feature.refName}:${(0, util_1.toLocale)(feature.start + 1)}-${(0, util_1.toLocale)(feature.end)}${getStrand(feature.strand)}`,
|
|
100
|
+
mode.endsWith('updownstream')
|
|
101
|
+
? `+/- ${(0, util_1.toLocale)(model.upDownBp)} up/downstream bp`
|
|
102
|
+
: '',
|
|
103
|
+
]
|
|
104
|
+
.filter(f => !!f)
|
|
105
|
+
.join(' ')}\n`),
|
|
106
|
+
mode === 'genomic' ? (react_1.default.createElement(GenomicSequence_1.default, { feature: feature, model: model, sequence: seq })) : mode === 'genomic_sequence_updownstream' ? (react_1.default.createElement(GenomicSequence_1.default, { model: model, feature: feature, sequence: seq, upstream: upstream, downstream: downstream })) : mode === 'cds' ? (react_1.default.createElement(CDSSequence_1.default, { model: model, cds: cds, sequence: seq })) : mode === 'cdna' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, feature: feature, cds: cds, utr: utr, sequence: seq })) : mode === 'protein' ? (react_1.default.createElement(ProteinSequence_1.default, { model: model, cds: cds, codonTable: codonTable, sequence: seq })) : mode === 'gene' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, feature: feature, cds: cds, utr: utr, sequence: seq, includeIntrons: true })) : mode === 'gene_collapsed_intron' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, feature: feature, cds: cds, sequence: seq, utr: utr, includeIntrons: true, collapseIntron: true })) : mode === 'gene_updownstream' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, feature: feature, cds: cds, sequence: seq, utr: utr, upstream: upstream, downstream: downstream, includeIntrons: true })) : mode === 'gene_updownstream_collapsed_intron' ? (react_1.default.createElement(CDNASequence_1.default, { model: model, exons: exons, feature: feature, cds: cds, sequence: seq, utr: utr, upstream: upstream, downstream: downstream, includeIntrons: true, collapseIntron: true })) : (react_1.default.createElement("div", null, "Unknown type")))));
|
|
107
|
+
}));
|
|
108
|
+
exports.default = SequencePanel;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SimpleFeatureSerialized } from '../../../util';
|
|
3
|
+
import { BaseFeatureWidgetModel } from '../../stateModelFactory';
|
|
4
|
+
declare const SequenceDialog: ({ handleClose, model, feature, }: {
|
|
5
|
+
handleClose: () => void;
|
|
6
|
+
feature: SimpleFeatureSerialized;
|
|
7
|
+
model: BaseFeatureWidgetModel;
|
|
8
|
+
}) => React.JSX.Element;
|
|
9
|
+
export default SequenceDialog;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const react_1 = __importStar(require("react"));
|
|
30
|
+
const material_1 = require("@mui/material");
|
|
31
|
+
const ui_1 = require("@jbrowse/core/ui");
|
|
32
|
+
const mui_1 = require("tss-react/mui");
|
|
33
|
+
const mobx_react_1 = require("mobx-react");
|
|
34
|
+
// locals
|
|
35
|
+
const hooks_1 = require("../hooks");
|
|
36
|
+
const SequencePanel_1 = __importDefault(require("../SequencePanel"));
|
|
37
|
+
const SequenceFeatureMenu_1 = __importDefault(require("./SequenceFeatureMenu"));
|
|
38
|
+
const SequenceTypeSelector_1 = __importDefault(require("./SequenceTypeSelector"));
|
|
39
|
+
const useStyles = (0, mui_1.makeStyles)()({
|
|
40
|
+
dialogContent: {
|
|
41
|
+
width: '80em',
|
|
42
|
+
},
|
|
43
|
+
formControl: {
|
|
44
|
+
margin: 0,
|
|
45
|
+
marginLeft: 4,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
const SequenceDialog = (0, mobx_react_1.observer)(function ({ handleClose, model, feature, }) {
|
|
49
|
+
const { sequenceFeatureDetails } = model;
|
|
50
|
+
const { upDownBp } = sequenceFeatureDetails;
|
|
51
|
+
const { classes } = useStyles();
|
|
52
|
+
const seqPanelRef = (0, react_1.useRef)(null);
|
|
53
|
+
const [force, setForce] = (0, react_1.useState)(false);
|
|
54
|
+
const { sequence, error } = (0, hooks_1.useFeatureSequence)(model, feature, upDownBp, force);
|
|
55
|
+
return (react_1.default.createElement(ui_1.Dialog, { maxWidth: "xl", open: true, onClose: () => handleClose(), title: "Sequence view" },
|
|
56
|
+
react_1.default.createElement(material_1.DialogContent, { className: classes.dialogContent },
|
|
57
|
+
react_1.default.createElement("div", null,
|
|
58
|
+
react_1.default.createElement(SequenceTypeSelector_1.default, { model: sequenceFeatureDetails }),
|
|
59
|
+
react_1.default.createElement(SequenceFeatureMenu_1.default, { ref: seqPanelRef, model: sequenceFeatureDetails })),
|
|
60
|
+
react_1.default.createElement("div", null,
|
|
61
|
+
feature.type === 'gene' ? (react_1.default.createElement(material_1.Typography, null, "Note: inspect subfeature sequences for protein/CDS computations")) : null,
|
|
62
|
+
error ? (react_1.default.createElement(ui_1.ErrorMessage, { error: error })) : !sequence ? (react_1.default.createElement(ui_1.LoadingEllipses, null)) : sequence ? ('error' in sequence ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
63
|
+
react_1.default.createElement(material_1.Typography, { color: "error" }, sequence.error),
|
|
64
|
+
react_1.default.createElement(material_1.Button, { variant: "contained", color: "inherit", onClick: () => setForce(true) }, "Force load"))) : (react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement(ui_1.LoadingEllipses, null) },
|
|
65
|
+
react_1.default.createElement(SequencePanel_1.default, { ref: seqPanelRef, feature: feature, sequence: sequence, model: sequenceFeatureDetails })))) : (react_1.default.createElement(material_1.Typography, null, "No sequence found")))),
|
|
66
|
+
react_1.default.createElement(material_1.DialogActions, null,
|
|
67
|
+
react_1.default.createElement(material_1.Button, { onClick: () => handleClose(), variant: "contained" }, "Close"))));
|
|
68
|
+
});
|
|
69
|
+
exports.default = SequenceDialog;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SequenceFeatureDetailsModel } from '../model';
|
|
3
|
+
import { MenuItem } from '../../../ui';
|
|
4
|
+
interface Props {
|
|
5
|
+
model: SequenceFeatureDetailsModel;
|
|
6
|
+
extraItems?: MenuItem[];
|
|
7
|
+
}
|
|
8
|
+
declare const SequenceFeatureMenu: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
export default SequenceFeatureMenu;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const react_1 = __importStar(require("react"));
|
|
30
|
+
const mobx_react_1 = require("mobx-react");
|
|
31
|
+
const copy_to_clipboard_1 = __importDefault(require("copy-to-clipboard"));
|
|
32
|
+
const file_saver_1 = require("file-saver");
|
|
33
|
+
// locals
|
|
34
|
+
const CascadingMenuButton_1 = __importDefault(require("../../../ui/CascadingMenuButton"));
|
|
35
|
+
// icons
|
|
36
|
+
const MoreVert_1 = __importDefault(require("@mui/icons-material/MoreVert"));
|
|
37
|
+
const Settings_1 = __importDefault(require("@mui/icons-material/Settings"));
|
|
38
|
+
// lazies
|
|
39
|
+
const SequenceFeatureSettingsDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./SettingsDialog'))));
|
|
40
|
+
const SequenceFeatureMenu = (0, mobx_react_1.observer)(react_1.default.forwardRef(function SequenceFeatureMenu2({ model, extraItems = [] }, ref) {
|
|
41
|
+
if (typeof ref === 'function') {
|
|
42
|
+
throw new Error('needs a non function ref');
|
|
43
|
+
}
|
|
44
|
+
const [showSettings, setShowSettings] = (0, react_1.useState)(false);
|
|
45
|
+
const { showCoordinatesSetting, showGenomicCoordsOption } = model;
|
|
46
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
47
|
+
react_1.default.createElement(CascadingMenuButton_1.default, { menuItems: [
|
|
48
|
+
{
|
|
49
|
+
label: 'Copy plaintext',
|
|
50
|
+
onClick: () => {
|
|
51
|
+
const r = ref === null || ref === void 0 ? void 0 : ref.current;
|
|
52
|
+
if (r) {
|
|
53
|
+
(0, copy_to_clipboard_1.default)(r.textContent || '', { format: 'text/plain' });
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
label: 'Copy HTML',
|
|
59
|
+
onClick: () => {
|
|
60
|
+
const r = ref === null || ref === void 0 ? void 0 : ref.current;
|
|
61
|
+
if (r) {
|
|
62
|
+
(0, copy_to_clipboard_1.default)(r.outerHTML, { format: 'text/html' });
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
label: 'Download plaintext',
|
|
68
|
+
onClick: () => {
|
|
69
|
+
const r = ref === null || ref === void 0 ? void 0 : ref.current;
|
|
70
|
+
if (r) {
|
|
71
|
+
(0, file_saver_1.saveAs)(new Blob([r.textContent || ''], {
|
|
72
|
+
type: 'text/plain;charset=utf-8',
|
|
73
|
+
}), 'sequence.txt');
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
label: 'Download HTML',
|
|
79
|
+
onClick: () => {
|
|
80
|
+
const r = ref === null || ref === void 0 ? void 0 : ref.current;
|
|
81
|
+
if (r) {
|
|
82
|
+
(0, file_saver_1.saveAs)(new Blob([r.outerHTML || ''], {
|
|
83
|
+
type: 'text/html;charset=utf-8',
|
|
84
|
+
}), 'sequence.html');
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
...extraItems,
|
|
89
|
+
{
|
|
90
|
+
label: 'Show coordinates?',
|
|
91
|
+
type: 'subMenu',
|
|
92
|
+
subMenu: [
|
|
93
|
+
{
|
|
94
|
+
label: 'No coordinates',
|
|
95
|
+
type: 'radio',
|
|
96
|
+
checked: showCoordinatesSetting === 'none',
|
|
97
|
+
onClick: () => model.setShowCoordinates('none'),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
label: 'Coordinates relative to feature start',
|
|
101
|
+
type: 'radio',
|
|
102
|
+
checked: showCoordinatesSetting === 'relative',
|
|
103
|
+
onClick: () => model.setShowCoordinates('relative'),
|
|
104
|
+
},
|
|
105
|
+
...(showGenomicCoordsOption
|
|
106
|
+
? [
|
|
107
|
+
{
|
|
108
|
+
label: 'Coordinates relative to genome (only available for continuous genome based sequence types)',
|
|
109
|
+
type: 'radio',
|
|
110
|
+
checked: showCoordinatesSetting === 'genomic',
|
|
111
|
+
onClick: () => model.setShowCoordinates('genomic'),
|
|
112
|
+
},
|
|
113
|
+
]
|
|
114
|
+
: []),
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
label: 'Settings',
|
|
119
|
+
icon: Settings_1.default,
|
|
120
|
+
onClick: () => setShowSettings(true),
|
|
121
|
+
},
|
|
122
|
+
] },
|
|
123
|
+
react_1.default.createElement(MoreVert_1.default, null)),
|
|
124
|
+
showSettings ? (react_1.default.createElement(SequenceFeatureSettingsDialog, { model: model, handleClose: () => setShowSettings(false) })) : null));
|
|
125
|
+
}));
|
|
126
|
+
exports.default = SequenceFeatureMenu;
|