@jbrowse/core 2.3.4 → 2.4.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.d.ts +18 -19
- package/BaseFeatureWidget/BaseFeatureDetail.js +64 -61
- package/BaseFeatureWidget/SequenceBox.js +9 -3
- package/BaseFeatureWidget/SequenceFeatureDetails.js +70 -52
- package/BaseFeatureWidget/SequencePanel.d.ts +3 -3
- package/BaseFeatureWidget/SequencePanel.js +8 -5
- package/CorePlugin.js +2 -7
- package/PluginLoader.d.ts +1 -1
- package/PluginLoader.js +17 -21
- package/data_adapters/CytobandAdapter/CytobandAdapter.d.ts +8 -0
- package/data_adapters/CytobandAdapter/CytobandAdapter.js +40 -0
- package/data_adapters/CytobandAdapter/configSchema.d.ts +2 -0
- package/data_adapters/CytobandAdapter/configSchema.js +17 -0
- package/data_adapters/CytobandAdapter/index.d.ts +3 -0
- package/data_adapters/CytobandAdapter/index.js +37 -0
- package/package.json +5 -4
- package/pluggableElementTypes/PluggableElementBase.d.ts +1 -1
- package/pluggableElementTypes/PluggableElementBase.js +1 -2
- package/pluggableElementTypes/RpcMethodType.d.ts +5 -8
- package/pluggableElementTypes/RpcMethodType.js +22 -33
- package/pluggableElementTypes/renderers/CircularChordRendererType.d.ts +13 -2
- package/pluggableElementTypes/renderers/CircularChordRendererType.js +10 -2
- package/pluggableElementTypes/renderers/ComparativeServerSideRendererType.d.ts +6 -0
- package/pluggableElementTypes/renderers/ComparativeServerSideRendererType.js +9 -1
- package/pluggableElementTypes/renderers/ServerSideRendererType.js +2 -34
- package/tsconfig.build.tsbuildinfo +1 -1
- package/ui/App.js +3 -18
- package/ui/LoadingEllipses.js +6 -6
- package/ui/Menu.js +2 -2
- package/ui/ResizeBar.js +4 -5
- package/ui/SanitizedHTML.js +2 -0
- package/ui/ViewContainer.js +3 -38
- package/ui/ViewMenu.d.ts +9 -0
- package/ui/ViewMenu.js +69 -0
- package/ui/ViewPanel.d.ts +19 -0
- package/ui/ViewPanel.js +49 -0
- package/ui/theme.d.ts +10 -166
- package/ui/theme.js +260 -48
- package/util/index.d.ts +3 -2
- package/util/index.js +11 -5
- package/util/map-obj.d.ts +3 -0
- package/util/map-obj.js +33 -0
- package/util/offscreenCanvasUtils.d.ts +18 -4
- package/util/offscreenCanvasUtils.js +48 -7
- package/data_adapters/CytobandAdapter.d.ts +0 -8
- package/data_adapters/CytobandAdapter.js +0 -49
|
@@ -23,15 +23,21 @@ export declare const FieldName: ({ description, name, width, prefix, }: {
|
|
|
23
23
|
export declare const BasicValue: ({ value }: {
|
|
24
24
|
value: string | React.ReactNode;
|
|
25
25
|
}) => JSX.Element;
|
|
26
|
-
export declare
|
|
26
|
+
export declare function SimpleValue({ name, value, description, prefix, width, }: {
|
|
27
27
|
description?: React.ReactNode;
|
|
28
28
|
name: string;
|
|
29
29
|
value: any;
|
|
30
|
-
prefix?: string[]
|
|
31
|
-
width?: number
|
|
32
|
-
})
|
|
30
|
+
prefix?: string[];
|
|
31
|
+
width?: number;
|
|
32
|
+
}): JSX.Element | null;
|
|
33
33
|
export declare const BaseCoreDetails: (props: BaseProps) => JSX.Element;
|
|
34
|
-
|
|
34
|
+
export declare function UriLink({ value, }: {
|
|
35
|
+
value: {
|
|
36
|
+
uri: string;
|
|
37
|
+
baseUri?: string;
|
|
38
|
+
};
|
|
39
|
+
}): JSX.Element;
|
|
40
|
+
export declare function Attributes(props: {
|
|
35
41
|
attributes: Record<string, any>;
|
|
36
42
|
omit?: string[];
|
|
37
43
|
omitSingleLevel?: string[];
|
|
@@ -39,14 +45,7 @@ interface AttributeProps {
|
|
|
39
45
|
descriptions?: Record<string, React.ReactNode>;
|
|
40
46
|
prefix?: string[];
|
|
41
47
|
hideUris?: boolean;
|
|
42
|
-
}
|
|
43
|
-
export declare function UriLink({ value, }: {
|
|
44
|
-
value: {
|
|
45
|
-
uri: string;
|
|
46
|
-
baseUri?: string;
|
|
47
|
-
};
|
|
48
48
|
}): JSX.Element;
|
|
49
|
-
export declare function Attributes(props: AttributeProps): JSX.Element;
|
|
50
49
|
export declare const BaseAttributes: (props: BaseProps) => JSX.Element;
|
|
51
50
|
export interface BaseInputProps extends BaseCardProps {
|
|
52
51
|
omit?: string[];
|
|
@@ -54,12 +53,12 @@ export interface BaseInputProps extends BaseCardProps {
|
|
|
54
53
|
descriptions?: Record<string, React.ReactNode>;
|
|
55
54
|
formatter?: (val: unknown, key: string) => React.ReactNode;
|
|
56
55
|
}
|
|
57
|
-
export declare
|
|
56
|
+
export declare function FeatureDetails(props: {
|
|
58
57
|
model: IAnyStateTreeNode;
|
|
59
58
|
feature: SimpleFeatureSerializedNoId;
|
|
60
|
-
depth?: number
|
|
61
|
-
omit?: string[]
|
|
62
|
-
formatter?: (
|
|
63
|
-
})
|
|
64
|
-
declare const
|
|
65
|
-
export default
|
|
59
|
+
depth?: number;
|
|
60
|
+
omit?: string[];
|
|
61
|
+
formatter?: (val: unknown, key: string) => React.ReactNode;
|
|
62
|
+
}): JSX.Element;
|
|
63
|
+
declare const _default: ({ model }: BaseInputProps) => JSX.Element | null;
|
|
64
|
+
export default _default;
|
|
@@ -32,10 +32,11 @@ const react_1 = __importStar(require("react"));
|
|
|
32
32
|
const react_error_boundary_1 = require("react-error-boundary");
|
|
33
33
|
const material_1 = require("@mui/material");
|
|
34
34
|
const mui_1 = require("tss-react/mui");
|
|
35
|
-
const ExpandMore_1 = __importDefault(require("@mui/icons-material/ExpandMore"));
|
|
36
35
|
const x_data_grid_1 = require("@mui/x-data-grid");
|
|
37
36
|
const mobx_react_1 = require("mobx-react");
|
|
38
37
|
const is_object_1 = __importDefault(require("is-object"));
|
|
38
|
+
// icons
|
|
39
|
+
const ExpandMore_1 = __importDefault(require("@mui/icons-material/ExpandMore"));
|
|
39
40
|
// locals
|
|
40
41
|
const util_1 = require("../util");
|
|
41
42
|
const ui_1 = require("../ui");
|
|
@@ -64,53 +65,56 @@ const coreDetails = [
|
|
|
64
65
|
'description',
|
|
65
66
|
'type',
|
|
66
67
|
];
|
|
67
|
-
exports.useStyles = (0, mui_1.makeStyles)()(theme =>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
color: '#FFFFFF',
|
|
74
|
-
},
|
|
75
|
-
field: {
|
|
76
|
-
display: 'flex',
|
|
77
|
-
flexWrap: 'wrap',
|
|
78
|
-
},
|
|
79
|
-
fieldDescription: {
|
|
80
|
-
'&:hover': {
|
|
81
|
-
background: 'yellow',
|
|
68
|
+
exports.useStyles = (0, mui_1.makeStyles)()(theme => {
|
|
69
|
+
var _a;
|
|
70
|
+
return ({
|
|
71
|
+
expansionPanelDetails: {
|
|
72
|
+
display: 'block',
|
|
73
|
+
padding: theme.spacing(1),
|
|
82
74
|
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
75
|
+
expandIcon: {
|
|
76
|
+
color: ((_a = theme.palette.tertiary) === null || _a === void 0 ? void 0 : _a.contrastText) || '#fff',
|
|
77
|
+
},
|
|
78
|
+
field: {
|
|
79
|
+
display: 'flex',
|
|
80
|
+
flexWrap: 'wrap',
|
|
81
|
+
},
|
|
82
|
+
fieldDescription: {
|
|
83
|
+
'&:hover': {
|
|
84
|
+
background: theme.palette.mode === 'dark' ? '#e65100' : 'yellow',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
fieldName: {
|
|
88
|
+
wordBreak: 'break-all',
|
|
89
|
+
minWidth: 90,
|
|
90
|
+
borderBottom: '1px solid #0003',
|
|
91
|
+
fontSize: 12,
|
|
92
|
+
background: theme.palette.action.disabledBackground,
|
|
93
|
+
marginRight: theme.spacing(1),
|
|
94
|
+
padding: theme.spacing(0.5),
|
|
95
|
+
},
|
|
96
|
+
fieldValue: {
|
|
97
|
+
wordBreak: 'break-word',
|
|
98
|
+
maxHeight: 300,
|
|
99
|
+
fontSize: 12,
|
|
100
|
+
padding: theme.spacing(0.5),
|
|
101
|
+
overflow: 'auto',
|
|
102
|
+
},
|
|
103
|
+
fieldSubvalue: {
|
|
104
|
+
wordBreak: 'break-word',
|
|
105
|
+
maxHeight: 300,
|
|
106
|
+
padding: theme.spacing(0.5),
|
|
107
|
+
background: theme.palette.action.disabledBackground,
|
|
108
|
+
border: `1px solid ${theme.palette.action.disabledBackground}`,
|
|
109
|
+
boxSizing: 'border-box',
|
|
110
|
+
overflow: 'auto',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
110
114
|
function BaseCard({ children, title, defaultExpanded = true, }) {
|
|
111
115
|
const { classes } = (0, exports.useStyles)();
|
|
112
116
|
const [expanded, setExpanded] = (0, react_1.useState)(defaultExpanded);
|
|
113
|
-
return (react_1.default.createElement(material_1.Accordion, { expanded: expanded, onChange: () => setExpanded(s => !s), TransitionProps: { unmountOnExit: true } },
|
|
117
|
+
return (react_1.default.createElement(material_1.Accordion, { expanded: expanded, onChange: () => setExpanded(s => !s), TransitionProps: { unmountOnExit: true, timeout: 150 } },
|
|
114
118
|
react_1.default.createElement(material_1.AccordionSummary, { expandIcon: react_1.default.createElement(ExpandMore_1.default, { className: classes.expandIcon }) },
|
|
115
119
|
react_1.default.createElement(material_1.Typography, { variant: "button" },
|
|
116
120
|
" ",
|
|
@@ -128,17 +132,17 @@ exports.FieldName = FieldName;
|
|
|
128
132
|
const BasicValue = ({ value }) => {
|
|
129
133
|
const { classes } = (0, exports.useStyles)();
|
|
130
134
|
const isLink = `${value}`.match(/^https?:\/\//);
|
|
131
|
-
return (react_1.default.createElement("div", { className: classes.fieldValue }, react_1.default.isValidElement(value) ? (value) : isLink ? (react_1.default.createElement(
|
|
135
|
+
return (react_1.default.createElement("div", { className: classes.fieldValue }, react_1.default.isValidElement(value) ? (value) : isLink ? (react_1.default.createElement(material_1.Link, { href: `${value}` }, `${value}`)) : (react_1.default.createElement(ui_1.SanitizedHTML, { html: (0, is_object_1.default)(value) ? JSON.stringify(value) : String(value) }))));
|
|
132
136
|
};
|
|
133
137
|
exports.BasicValue = BasicValue;
|
|
134
|
-
|
|
138
|
+
function SimpleValue({ name, value, description, prefix, width, }) {
|
|
135
139
|
const { classes } = (0, exports.useStyles)();
|
|
136
140
|
return value !== null && value !== undefined ? (react_1.default.createElement("div", { className: classes.field },
|
|
137
141
|
react_1.default.createElement(exports.FieldName, { prefix: prefix, description: description, name: name, width: width }),
|
|
138
142
|
react_1.default.createElement(exports.BasicValue, { value: value }))) : null;
|
|
139
|
-
}
|
|
143
|
+
}
|
|
140
144
|
exports.SimpleValue = SimpleValue;
|
|
141
|
-
|
|
145
|
+
function ArrayValue({ name, value, description, prefix = [], }) {
|
|
142
146
|
const { classes } = (0, exports.useStyles)();
|
|
143
147
|
if (value.length === 1) {
|
|
144
148
|
return (0, is_object_1.default)(value[0]) ? (react_1.default.createElement(Attributes, { attributes: value[0], prefix: [...prefix, name] })) : (react_1.default.createElement("div", { className: classes.field },
|
|
@@ -154,7 +158,7 @@ const ArrayValue = ({ name, value, description, prefix = [], }) => {
|
|
|
154
158
|
value.map((val, i) => (react_1.default.createElement("div", { key: JSON.stringify(val) + '-' + i, className: classes.fieldSubvalue },
|
|
155
159
|
react_1.default.createElement(exports.BasicValue, { value: val }))))));
|
|
156
160
|
}
|
|
157
|
-
}
|
|
161
|
+
}
|
|
158
162
|
const toLocale = (n) => n.toLocaleString('en-US');
|
|
159
163
|
function Position(props) {
|
|
160
164
|
const { feature } = props;
|
|
@@ -186,11 +190,11 @@ function CoreDetails(props) {
|
|
|
186
190
|
type: 'Type',
|
|
187
191
|
};
|
|
188
192
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
189
|
-
react_1.default.createElement(
|
|
193
|
+
react_1.default.createElement(SimpleValue, { name: "Position", value: react_1.default.createElement(Position, { ...props, feature: formattedFeat }) }),
|
|
190
194
|
Object.entries(coreRenderedDetails)
|
|
191
195
|
.map(([key, name]) => [name, displayedDetails[key]])
|
|
192
196
|
.filter(([, value]) => value != null)
|
|
193
|
-
.map(([name, value]) => (react_1.default.createElement(
|
|
197
|
+
.map(([name, value]) => (react_1.default.createElement(SimpleValue, { key: name, name: name, value: value })))));
|
|
194
198
|
}
|
|
195
199
|
const BaseCoreDetails = (props) => {
|
|
196
200
|
return (react_1.default.createElement(BaseCard, { ...props, title: "Primary data" },
|
|
@@ -202,7 +206,7 @@ function UriLink({ value, }) {
|
|
|
202
206
|
return react_1.default.createElement(ui_1.SanitizedHTML, { html: `<a href="${href}">${href}</a>` });
|
|
203
207
|
}
|
|
204
208
|
exports.UriLink = UriLink;
|
|
205
|
-
|
|
209
|
+
function DataGridDetails({ value, prefix, name, }) {
|
|
206
210
|
const keys = Object.keys(value[0]).sort();
|
|
207
211
|
const unionKeys = new Set(keys);
|
|
208
212
|
value.forEach((val) => Object.keys(val).forEach(k => unionKeys.add(k)));
|
|
@@ -249,7 +253,7 @@ const DataGridDetails = ({ value, prefix, name, }) => {
|
|
|
249
253
|
react_1.default.createElement(x_data_grid_1.DataGrid, { disableSelectionOnClick: true, rowHeight: rowHeight, rows: rows, hideFooterSelectedRowCount: true, columns: columns, hideFooter: hideFoot }))));
|
|
250
254
|
}
|
|
251
255
|
return null;
|
|
252
|
-
}
|
|
256
|
+
}
|
|
253
257
|
// pick using a path from an object, similar to _.get from lodash with special logic
|
|
254
258
|
// for Descriptions from e.g. VCF headers
|
|
255
259
|
// @param arr example ['a','b'], obj = {a:{b:'hello}}
|
|
@@ -308,7 +312,7 @@ function Attributes(props) {
|
|
|
308
312
|
return (0, util_1.isUriLocation)(value) ? (hideUris ? null : (react_1.default.createElement(UriAttribute, { key: key, name: key, prefix: prefix, value: value }))) : (react_1.default.createElement(Attributes, { ...rest, key: key, attributes: value, descriptions: descriptions, prefix: [...prefix, key] }));
|
|
309
313
|
}
|
|
310
314
|
else {
|
|
311
|
-
return (react_1.default.createElement(
|
|
315
|
+
return (react_1.default.createElement(SimpleValue, { key: key, name: key, value: formatter(value, key), description: description, prefix: prefix, width: Math.min(maxLabelWidth, MAX_FIELD_NAME_WIDTH) }));
|
|
312
316
|
}
|
|
313
317
|
})));
|
|
314
318
|
}
|
|
@@ -327,7 +331,7 @@ function generateTitle(name, id, type) {
|
|
|
327
331
|
.filter(f => !!f)
|
|
328
332
|
.join(' - ');
|
|
329
333
|
}
|
|
330
|
-
|
|
334
|
+
function FeatureDetails(props) {
|
|
331
335
|
const { omit = [], model, feature, depth = 0 } = props;
|
|
332
336
|
const { name = '', id = '', type = '', subfeatures } = feature;
|
|
333
337
|
const pm = (0, util_1.getEnv)(model).pluginManager;
|
|
@@ -353,10 +357,10 @@ const FeatureDetails = (props) => {
|
|
|
353
357
|
react_1.default.createElement(material_1.Divider, null),
|
|
354
358
|
react_1.default.createElement(BaseCard, { title: ExtraPanel.name },
|
|
355
359
|
react_1.default.createElement(ExtraPanel.Component, { ...props })))) : null,
|
|
356
|
-
(subfeatures === null || subfeatures === void 0 ? void 0 : subfeatures.length) ? (react_1.default.createElement(BaseCard, { title: "Subfeatures", defaultExpanded: depth < 1 }, subfeatures.map(sub => (react_1.default.createElement(
|
|
357
|
-
}
|
|
360
|
+
(subfeatures === null || subfeatures === void 0 ? void 0 : subfeatures.length) ? (react_1.default.createElement(BaseCard, { title: "Subfeatures", defaultExpanded: depth < 1 }, subfeatures.map(sub => (react_1.default.createElement(FeatureDetails, { key: JSON.stringify(sub), feature: sub, model: model, depth: depth + 1 }))))) : null));
|
|
361
|
+
}
|
|
358
362
|
exports.FeatureDetails = FeatureDetails;
|
|
359
|
-
|
|
363
|
+
exports.default = (0, mobx_react_1.observer)(function ({ model }) {
|
|
360
364
|
const { featureData } = model;
|
|
361
365
|
if (!featureData) {
|
|
362
366
|
return null;
|
|
@@ -365,7 +369,6 @@ const BaseFeatureDetails = (0, mobx_react_1.observer)(({ model }) => {
|
|
|
365
369
|
// setting null is not allowed by jexl so we set it to undefined to hide. see
|
|
366
370
|
// config guide. this replacement happens both here and when snapshotting the
|
|
367
371
|
// featureData
|
|
368
|
-
const
|
|
369
|
-
return isEmpty(
|
|
372
|
+
const g = JSON.parse(JSON.stringify(featureData, (_, v) => typeof v === 'undefined' ? null : v));
|
|
373
|
+
return isEmpty(g) ? null : react_1.default.createElement(FeatureDetails, { model: model, feature: g });
|
|
370
374
|
});
|
|
371
|
-
exports.default = BaseFeatureDetails;
|
|
@@ -31,7 +31,9 @@ exports.GeneProtein = GeneProtein;
|
|
|
31
31
|
function GenecDNA({ utr, cds, exons, sequence, upstream, downstream, includeIntrons, collapseIntron, intronBp, }) {
|
|
32
32
|
const chunks = (cds.length ? [...cds, ...utr].sort((a, b) => a.start - b.start) : exons).filter(f => f.start !== f.end);
|
|
33
33
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
34
|
-
upstream ? (react_1.default.createElement("span", { style: {
|
|
34
|
+
upstream ? (react_1.default.createElement("span", { style: {
|
|
35
|
+
background: updownstreamColor,
|
|
36
|
+
} }, upstream)) : null,
|
|
35
37
|
chunks.map((chunk, idx) => {
|
|
36
38
|
var _a;
|
|
37
39
|
const intron = sequence.slice(chunk.end, (_a = chunks[idx + 1]) === null || _a === void 0 ? void 0 : _a.start);
|
|
@@ -48,10 +50,14 @@ function GenecDNA({ utr, cds, exons, sequence, upstream, downstream, includeIntr
|
|
|
48
50
|
exports.GenecDNA = GenecDNA;
|
|
49
51
|
function Genomic({ sequence, upstream, downstream, }) {
|
|
50
52
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
51
|
-
upstream ? (react_1.default.createElement("span", { style: {
|
|
53
|
+
upstream ? (react_1.default.createElement("span", { style: {
|
|
54
|
+
background: updownstreamColor,
|
|
55
|
+
} }, upstream)) : null,
|
|
52
56
|
react_1.default.createElement("span", { style: {
|
|
53
57
|
background: genomeColor,
|
|
54
58
|
} }, sequence),
|
|
55
|
-
downstream ? (react_1.default.createElement("span", { style: {
|
|
59
|
+
downstream ? (react_1.default.createElement("span", { style: {
|
|
60
|
+
background: updownstreamColor,
|
|
61
|
+
} }, downstream)) : null));
|
|
56
62
|
}
|
|
57
63
|
exports.Genomic = Genomic;
|
|
@@ -31,15 +31,16 @@ const material_1 = require("@mui/material");
|
|
|
31
31
|
const mui_1 = require("tss-react/mui");
|
|
32
32
|
const copy_to_clipboard_1 = __importDefault(require("copy-to-clipboard"));
|
|
33
33
|
// locals
|
|
34
|
-
const SequenceFeatureSettingsDialog_1 = __importDefault(require("./SequenceFeatureSettingsDialog"));
|
|
35
|
-
const SequenceHelpDialog_1 = __importDefault(require("./SequenceHelpDialog"));
|
|
36
|
-
const SequencePanel_1 = __importDefault(require("./SequencePanel"));
|
|
37
34
|
const util_1 = require("../util");
|
|
38
35
|
const configuration_1 = require("../configuration");
|
|
39
36
|
const ui_1 = require("../ui");
|
|
40
37
|
// icons
|
|
41
38
|
const Settings_1 = __importDefault(require("@mui/icons-material/Settings"));
|
|
42
39
|
const Help_1 = __importDefault(require("@mui/icons-material/Help"));
|
|
40
|
+
// lazies
|
|
41
|
+
const SettingsDlg = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./SequenceFeatureSettingsDialog'))));
|
|
42
|
+
const HelpDlg = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./SequenceHelpDialog'))));
|
|
43
|
+
const SequencePanel = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./SequencePanel'))));
|
|
43
44
|
const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
44
45
|
button: {
|
|
45
46
|
margin: theme.spacing(1),
|
|
@@ -64,27 +65,46 @@ function SequenceFeatureDetails({ model, feature }) {
|
|
|
64
65
|
const parentFeature = feature;
|
|
65
66
|
const hasCDS = !!((_a = parentFeature.subfeatures) === null || _a === void 0 ? void 0 : _a.find(sub => sub.type === 'CDS'));
|
|
66
67
|
const isGene = feature.type === 'gene';
|
|
67
|
-
const seqPanelRef = (0, react_1.useRef)(null);
|
|
68
|
-
const [settingsDlgOpen, setSettingsDlgOpen] = (0, react_1.useState)(false);
|
|
69
68
|
const [shown, setShown] = (0, react_1.useState)(false);
|
|
70
69
|
const [helpShown, setHelpShown] = (0, react_1.useState)(false);
|
|
70
|
+
return (isGene && !hasCDS) || !model ? null : (react_1.default.createElement("div", { className: classes.container2 },
|
|
71
|
+
react_1.default.createElement(material_1.Button, { variant: "contained", onClick: () => setShown(!shown) }, shown ? 'Hide feature sequence' : 'Show feature sequence'),
|
|
72
|
+
react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
|
|
73
|
+
react_1.default.createElement(material_1.IconButton, { onClick: () => setHelpShown(true) },
|
|
74
|
+
react_1.default.createElement(Help_1.default, null))),
|
|
75
|
+
react_1.default.createElement("br", null),
|
|
76
|
+
shown ? react_1.default.createElement(FeatureSequence, { model: model, feature: feature }) : null,
|
|
77
|
+
helpShown ? (react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement("div", null) },
|
|
78
|
+
react_1.default.createElement(HelpDlg, { handleClose: () => setHelpShown(false) }))) : null));
|
|
79
|
+
}
|
|
80
|
+
exports.default = SequenceFeatureDetails;
|
|
81
|
+
function FeatureSequence({ model, feature }) {
|
|
82
|
+
var _a;
|
|
83
|
+
const { classes } = useStyles();
|
|
84
|
+
const parentFeature = feature;
|
|
85
|
+
const hasCDS = !!((_a = parentFeature.subfeatures) === null || _a === void 0 ? void 0 : _a.find(sub => sub.type === 'CDS'));
|
|
86
|
+
const seqPanelRef = (0, react_1.useRef)(null);
|
|
87
|
+
const [settingsDlgOpen, setSettingsDlgOpen] = (0, react_1.useState)(false);
|
|
88
|
+
const [intronBp, setIntronBp] = (0, util_1.useLocalStorage)('intronBp', 10);
|
|
89
|
+
const [upDownBp, setUpDownBp] = (0, util_1.useLocalStorage)('upDownBp', 500);
|
|
71
90
|
const [sequence, setSequence] = (0, react_1.useState)();
|
|
72
91
|
const [error, setError] = (0, react_1.useState)();
|
|
73
92
|
const [copied, setCopied] = (0, react_1.useState)(false);
|
|
74
93
|
const [copiedHtml, setCopiedHtml] = (0, react_1.useState)(false);
|
|
75
|
-
const [intronBp, setIntronBp] = (0, util_1.useLocalStorage)('intronBp', 10);
|
|
76
|
-
const [upDownBp, setUpDownBp] = (0, util_1.useLocalStorage)('upDownBp', 500);
|
|
77
94
|
const [forceLoad, setForceLoad] = (0, react_1.useState)({
|
|
78
95
|
id: feature.uniqueId,
|
|
79
96
|
force: false,
|
|
80
97
|
});
|
|
81
98
|
(0, react_1.useEffect)(() => {
|
|
82
|
-
setForceLoad({
|
|
99
|
+
setForceLoad({
|
|
100
|
+
id: feature.uniqueId,
|
|
101
|
+
force: false,
|
|
102
|
+
});
|
|
83
103
|
}, [feature]);
|
|
84
104
|
(0, react_1.useEffect)(() => {
|
|
85
105
|
var _a;
|
|
86
106
|
let finished = false;
|
|
87
|
-
if (!model
|
|
107
|
+
if (!model) {
|
|
88
108
|
return () => { };
|
|
89
109
|
}
|
|
90
110
|
const { assemblyManager, rpcManager } = (0, util_1.getSession)(model);
|
|
@@ -103,6 +123,7 @@ function SequenceFeatureDetails({ model, feature }) {
|
|
|
103
123
|
start,
|
|
104
124
|
end,
|
|
105
125
|
refName: assembly.getCanonicalRefName(refName),
|
|
126
|
+
assemblyName,
|
|
106
127
|
},
|
|
107
128
|
],
|
|
108
129
|
});
|
|
@@ -137,7 +158,7 @@ function SequenceFeatureDetails({ model, feature }) {
|
|
|
137
158
|
return () => {
|
|
138
159
|
finished = true;
|
|
139
160
|
};
|
|
140
|
-
}, [feature,
|
|
161
|
+
}, [feature, model, upDownBp, forceLoad]);
|
|
141
162
|
const loading = !sequence;
|
|
142
163
|
const session = (0, util_1.getSession)(model);
|
|
143
164
|
const defaultSeqTypes = ['mRNA', 'transcript', 'gene'];
|
|
@@ -147,7 +168,10 @@ function SequenceFeatureDetails({ model, feature }) {
|
|
|
147
168
|
? sequenceTypes.includes('CDS') && !feature.parentId
|
|
148
169
|
: sequenceTypes.includes(feature.type);
|
|
149
170
|
const val = attemptGeneType ? (hasCDS ? 'cds' : 'cdna') : 'genomic';
|
|
150
|
-
// this useEffect is needed to reset the mode/setMode useState because the
|
|
171
|
+
// this useEffect is needed to reset the mode/setMode useState because the
|
|
172
|
+
// contents of the select box can completely change depending on whether we
|
|
173
|
+
// click on a gene feature or non-gene feature, so the current value in the
|
|
174
|
+
// select box must change accordingly
|
|
151
175
|
(0, react_1.useEffect)(() => {
|
|
152
176
|
setMode(val);
|
|
153
177
|
}, [attemptGeneType, val]);
|
|
@@ -171,49 +195,43 @@ function SequenceFeatureDetails({ model, feature }) {
|
|
|
171
195
|
genomic: 'Genomic seq',
|
|
172
196
|
genomic_sequence_updown: `Genomic seq w/ ${upDownBp}bp up+down stream`,
|
|
173
197
|
};
|
|
174
|
-
return (
|
|
175
|
-
react_1.default.createElement(material_1.Button, { variant: "contained", onClick: () => setShown(!shown) }, shown ? 'Hide feature sequence' : 'Show feature sequence'),
|
|
198
|
+
return (react_1.default.createElement("div", { className: classes.container2 },
|
|
176
199
|
react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
|
|
177
|
-
react_1.default.createElement(material_1.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
200
|
+
react_1.default.createElement(material_1.Select, { value: mode, onChange: event => setMode(event.target.value) }, Object.entries(arg).map(([key, val]) => (react_1.default.createElement(material_1.MenuItem, { key: key, value: key }, val))))),
|
|
201
|
+
react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
|
|
202
|
+
react_1.default.createElement(material_1.Button, { className: classes.button, variant: "contained", onClick: () => {
|
|
203
|
+
const ref = seqPanelRef.current;
|
|
204
|
+
if (ref) {
|
|
205
|
+
(0, copy_to_clipboard_1.default)(ref.textContent || '', { format: 'text/plain' });
|
|
206
|
+
setCopied(true);
|
|
207
|
+
setTimeout(() => setCopied(false), 1000);
|
|
208
|
+
}
|
|
209
|
+
} }, copied ? 'Copied to clipboard!' : 'Copy plaintext')),
|
|
210
|
+
react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
|
|
211
|
+
react_1.default.createElement(material_1.Tooltip, { title: "The 'Copy HTML' function retains the colors from the sequence panel but cannot be pasted into some programs like notepad that only expect plain text" },
|
|
212
|
+
react_1.default.createElement(material_1.Button, { className: classes.button, variant: "contained", onClick: () => {
|
|
185
213
|
const ref = seqPanelRef.current;
|
|
186
214
|
if (ref) {
|
|
187
|
-
(0, copy_to_clipboard_1.default)(ref.
|
|
188
|
-
|
|
189
|
-
setTimeout(() =>
|
|
215
|
+
(0, copy_to_clipboard_1.default)(ref.innerHTML, { format: 'text/html' });
|
|
216
|
+
setCopiedHtml(true);
|
|
217
|
+
setTimeout(() => setCopiedHtml(false), 1000);
|
|
190
218
|
}
|
|
191
|
-
} },
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
settingsDlgOpen ? (react_1.default.createElement(SequenceFeatureSettingsDialog_1.default, { handleClose: arg => {
|
|
210
|
-
if (arg) {
|
|
211
|
-
const { upDownBp, intronBp } = arg;
|
|
212
|
-
setIntronBp(intronBp);
|
|
213
|
-
setUpDownBp(upDownBp);
|
|
214
|
-
}
|
|
215
|
-
setSettingsDlgOpen(false);
|
|
216
|
-
}, upDownBp: upDownBp, intronBp: intronBp })) : null,
|
|
217
|
-
helpShown ? react_1.default.createElement(SequenceHelpDialog_1.default, { handleClose: () => setHelpShown(false) }) : null));
|
|
219
|
+
} }, copiedHtml ? 'Copied to clipboard!' : 'Copy HTML'))),
|
|
220
|
+
react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
|
|
221
|
+
react_1.default.createElement(material_1.IconButton, { onClick: () => setSettingsDlgOpen(true) },
|
|
222
|
+
react_1.default.createElement(Settings_1.default, null))),
|
|
223
|
+
react_1.default.createElement("br", null),
|
|
224
|
+
error ? (react_1.default.createElement(material_1.Typography, { color: "error" }, `${error}`)) : loading ? (react_1.default.createElement(ui_1.LoadingEllipses, null)) : sequence ? ('error' in sequence ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
225
|
+
react_1.default.createElement(material_1.Typography, { color: "error" }, sequence.error),
|
|
226
|
+
react_1.default.createElement(material_1.Button, { variant: "contained", color: "inherit", onClick: () => setForceLoad({ ...forceLoad, force: true }) }, "Force load"))) : (react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement("div", null, "Loading") },
|
|
227
|
+
react_1.default.createElement(SequencePanel, { ref: seqPanelRef, feature: parentFeature, mode: mode, sequence: sequence, intronBp: intronBp })))) : (react_1.default.createElement(material_1.Typography, null, "No sequence found")),
|
|
228
|
+
settingsDlgOpen ? (react_1.default.createElement(react_1.Suspense, { fallback: react_1.default.createElement("div", null) },
|
|
229
|
+
react_1.default.createElement(SettingsDlg, { handleClose: arg => {
|
|
230
|
+
if (arg) {
|
|
231
|
+
const { upDownBp, intronBp } = arg;
|
|
232
|
+
setIntronBp(intronBp);
|
|
233
|
+
setUpDownBp(upDownBp);
|
|
234
|
+
}
|
|
235
|
+
setSettingsDlgOpen(false);
|
|
236
|
+
}, upDownBp: upDownBp, intronBp: intronBp }))) : null));
|
|
218
237
|
}
|
|
219
|
-
exports.default = SequenceFeatureDetails;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ParentFeat, SeqState } from './util';
|
|
3
|
-
interface
|
|
3
|
+
interface SeqPanelProps {
|
|
4
4
|
sequence: SeqState;
|
|
5
5
|
feature: ParentFeat;
|
|
6
6
|
mode: string;
|
|
7
7
|
intronBp?: number;
|
|
8
8
|
}
|
|
9
|
-
declare const
|
|
10
|
-
export default
|
|
9
|
+
declare const SeqPanel: React.ForwardRefExoticComponent<SeqPanelProps & React.RefAttributes<HTMLDivElement>>;
|
|
10
|
+
export default SeqPanel;
|
|
@@ -7,7 +7,7 @@ const react_1 = __importDefault(require("react"));
|
|
|
7
7
|
const util_1 = require("../util");
|
|
8
8
|
const util_2 = require("./util");
|
|
9
9
|
const SequenceBox_1 = require("./SequenceBox");
|
|
10
|
-
const
|
|
10
|
+
const SeqPanel = react_1.default.forwardRef(function (props, ref) {
|
|
11
11
|
const { feature, mode, intronBp = 10 } = props;
|
|
12
12
|
let { sequence: { seq, upstream = '', downstream = '' }, } = props;
|
|
13
13
|
const { subfeatures = [] } = feature;
|
|
@@ -55,18 +55,21 @@ const SequencePanel = react_1.default.forwardRef((props, ref) => {
|
|
|
55
55
|
}
|
|
56
56
|
const codonTable = (0, util_1.generateCodonTable)(util_1.defaultCodonTable);
|
|
57
57
|
return (react_1.default.createElement("div", { ref: ref, "data-testid": "sequence_panel" },
|
|
58
|
-
react_1.default.createElement("div", { style:
|
|
58
|
+
react_1.default.createElement("div", { style:
|
|
59
|
+
/* raw styles so that html copy works */
|
|
60
|
+
{
|
|
59
61
|
fontFamily: 'monospace',
|
|
60
62
|
wordWrap: 'break-word',
|
|
61
63
|
overflow: 'auto',
|
|
64
|
+
color: 'black',
|
|
62
65
|
fontSize: 12,
|
|
63
66
|
maxWidth: 600,
|
|
64
67
|
maxHeight: 500,
|
|
65
68
|
} },
|
|
66
|
-
`>${feature.name ||
|
|
69
|
+
react_1.default.createElement("span", { style: { background: 'white' } }, `>${feature.name ||
|
|
67
70
|
feature.id ||
|
|
68
|
-
feature.refName + ':' + (feature.start + 1) + '-' + feature.end}-${mode}\n
|
|
71
|
+
feature.refName + ':' + (feature.start + 1) + '-' + feature.end}-${mode}\n`),
|
|
69
72
|
react_1.default.createElement("br", null),
|
|
70
73
|
mode === 'genomic' ? (react_1.default.createElement(SequenceBox_1.Genomic, { sequence: seq })) : mode === 'genomic_sequence_updown' ? (react_1.default.createElement(SequenceBox_1.Genomic, { sequence: seq, upstream: upstream, downstream: downstream })) : mode === 'cds' ? (react_1.default.createElement(SequenceBox_1.GeneCDS, { cds: cds, sequence: seq })) : mode === 'cdna' ? (react_1.default.createElement(SequenceBox_1.GenecDNA, { exons: exons, cds: cds, utr: utr, sequence: seq, intronBp: intronBp })) : mode === 'protein' ? (react_1.default.createElement(SequenceBox_1.GeneProtein, { cds: cds, codonTable: codonTable, sequence: seq })) : mode === 'gene' ? (react_1.default.createElement(SequenceBox_1.GenecDNA, { exons: exons, cds: cds, utr: utr, sequence: seq, includeIntrons: true, intronBp: intronBp })) : mode === 'gene_collapsed_intron' ? (react_1.default.createElement(SequenceBox_1.GenecDNA, { exons: exons, cds: cds, sequence: seq, utr: utr, includeIntrons: true, collapseIntron: true, intronBp: intronBp })) : mode === 'gene_updownstream' ? (react_1.default.createElement(SequenceBox_1.GenecDNA, { exons: exons, cds: cds, sequence: seq, utr: utr, upstream: upstream, downstream: downstream, includeIntrons: true, intronBp: intronBp })) : mode === 'gene_updownstream_collapsed_intron' ? (react_1.default.createElement(SequenceBox_1.GenecDNA, { exons: exons, cds: cds, sequence: seq, utr: utr, upstream: upstream, downstream: downstream, includeIntrons: true, collapseIntron: true, intronBp: intronBp })) : (react_1.default.createElement("div", null, "Unknown type")))));
|
|
71
74
|
});
|
|
72
|
-
exports.default =
|
|
75
|
+
exports.default = SeqPanel;
|
package/CorePlugin.js
CHANGED
|
@@ -30,9 +30,8 @@ const react_1 = require("react");
|
|
|
30
30
|
const BaseFeatureWidget_1 = require("./BaseFeatureWidget");
|
|
31
31
|
const Plugin_1 = __importDefault(require("./Plugin"));
|
|
32
32
|
const coreRpcMethods = __importStar(require("./rpc/coreRpcMethods"));
|
|
33
|
-
const AdapterType_1 = __importDefault(require("./pluggableElementTypes/AdapterType"));
|
|
34
33
|
const WidgetType_1 = __importDefault(require("./pluggableElementTypes/WidgetType"));
|
|
35
|
-
const CytobandAdapter_1 = require("./data_adapters/CytobandAdapter");
|
|
34
|
+
const CytobandAdapter_1 = __importDefault(require("./data_adapters/CytobandAdapter"));
|
|
36
35
|
// the core plugin, which registers types that ALL JBrowse applications are
|
|
37
36
|
// expected to need.
|
|
38
37
|
class CorePlugin extends Plugin_1.default {
|
|
@@ -45,11 +44,7 @@ class CorePlugin extends Plugin_1.default {
|
|
|
45
44
|
Object.values(coreRpcMethods).forEach(RpcMethod => {
|
|
46
45
|
pluginManager.addRpcMethod(() => new RpcMethod(pluginManager));
|
|
47
46
|
});
|
|
48
|
-
|
|
49
|
-
name: 'CytobandAdapter',
|
|
50
|
-
configSchema: CytobandAdapter_1.configSchema,
|
|
51
|
-
AdapterClass: CytobandAdapter_1.DataAdapter,
|
|
52
|
-
}));
|
|
47
|
+
(0, CytobandAdapter_1.default)(pluginManager);
|
|
53
48
|
pluginManager.addWidgetType(() => {
|
|
54
49
|
return new WidgetType_1.default({
|
|
55
50
|
name: 'BaseFeatureWidget',
|
package/PluginLoader.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export default class PluginLoader {
|
|
|
48
48
|
fetchESM?: (url: string) => Promise<LoadedPlugin>;
|
|
49
49
|
fetchCJS?: (url: string) => Promise<LoadedPlugin>;
|
|
50
50
|
});
|
|
51
|
-
loadScript(scriptUrl: string): Promise<
|
|
51
|
+
loadScript(scriptUrl: string): Promise<any>;
|
|
52
52
|
loadCJSPlugin(def: CJSPluginDefinition, baseUri?: string): Promise<LoadedPlugin>;
|
|
53
53
|
loadESMPlugin(def: ESMPluginDefinition, baseUri?: string): Promise<LoadedPlugin>;
|
|
54
54
|
loadUMDPlugin(def: UMDPluginDefinition | LegacyUMDPluginDefinition, baseUri?: string): Promise<{
|