@jbrowse/core 2.3.3 → 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.
Files changed (52) hide show
  1. package/BaseFeatureWidget/BaseFeatureDetail.d.ts +18 -19
  2. package/BaseFeatureWidget/BaseFeatureDetail.js +64 -61
  3. package/BaseFeatureWidget/SequenceBox.js +9 -3
  4. package/BaseFeatureWidget/SequenceFeatureDetails.js +70 -52
  5. package/BaseFeatureWidget/SequencePanel.d.ts +3 -3
  6. package/BaseFeatureWidget/SequencePanel.js +8 -5
  7. package/CorePlugin.js +2 -7
  8. package/PluginLoader.d.ts +8 -9
  9. package/PluginLoader.js +35 -41
  10. package/data_adapters/CytobandAdapter/CytobandAdapter.d.ts +8 -0
  11. package/data_adapters/CytobandAdapter/CytobandAdapter.js +40 -0
  12. package/data_adapters/CytobandAdapter/configSchema.d.ts +2 -0
  13. package/data_adapters/CytobandAdapter/configSchema.js +17 -0
  14. package/data_adapters/CytobandAdapter/index.d.ts +3 -0
  15. package/data_adapters/CytobandAdapter/index.js +37 -0
  16. package/package.json +5 -4
  17. package/pluggableElementTypes/PluggableElementBase.d.ts +1 -1
  18. package/pluggableElementTypes/PluggableElementBase.js +1 -2
  19. package/pluggableElementTypes/RpcMethodType.d.ts +5 -8
  20. package/pluggableElementTypes/RpcMethodType.js +22 -33
  21. package/pluggableElementTypes/renderers/CircularChordRendererType.d.ts +13 -2
  22. package/pluggableElementTypes/renderers/CircularChordRendererType.js +10 -2
  23. package/pluggableElementTypes/renderers/ComparativeServerSideRendererType.d.ts +6 -0
  24. package/pluggableElementTypes/renderers/ComparativeServerSideRendererType.js +9 -1
  25. package/pluggableElementTypes/renderers/ServerSideRendererType.js +2 -34
  26. package/tsconfig.build.tsbuildinfo +1 -1
  27. package/ui/App.js +3 -18
  28. package/ui/Dialog.js +8 -1
  29. package/ui/DropDownMenu.js +1 -4
  30. package/ui/EditableTypography.js +1 -1
  31. package/ui/LoadingEllipses.js +6 -6
  32. package/ui/Menu.js +11 -3
  33. package/ui/ResizeBar.d.ts +11 -0
  34. package/ui/ResizeBar.js +94 -0
  35. package/ui/ResizeHandle.d.ts +1 -1
  36. package/ui/ResizeHandle.js +1 -1
  37. package/ui/SanitizedHTML.js +3 -1
  38. package/ui/ViewContainer.js +3 -38
  39. package/ui/ViewMenu.d.ts +9 -0
  40. package/ui/ViewMenu.js +69 -0
  41. package/ui/ViewPanel.d.ts +19 -0
  42. package/ui/ViewPanel.js +49 -0
  43. package/ui/theme.d.ts +10 -156
  44. package/ui/theme.js +270 -48
  45. package/util/index.d.ts +16 -9
  46. package/util/index.js +30 -13
  47. package/util/map-obj.d.ts +3 -0
  48. package/util/map-obj.js +33 -0
  49. package/util/offscreenCanvasUtils.d.ts +18 -4
  50. package/util/offscreenCanvasUtils.js +48 -7
  51. package/data_adapters/CytobandAdapter.d.ts +0 -8
  52. 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 const SimpleValue: ({ name, value, description, prefix, width, }: {
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[] | undefined;
31
- width?: number | undefined;
32
- }) => JSX.Element | null;
30
+ prefix?: string[];
31
+ width?: number;
32
+ }): JSX.Element | null;
33
33
  export declare const BaseCoreDetails: (props: BaseProps) => JSX.Element;
34
- interface AttributeProps {
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 const FeatureDetails: (props: {
56
+ export declare function FeatureDetails(props: {
58
57
  model: IAnyStateTreeNode;
59
58
  feature: SimpleFeatureSerializedNoId;
60
- depth?: number | undefined;
61
- omit?: string[] | undefined;
62
- formatter?: ((val: unknown, key: string) => React.ReactNode) | undefined;
63
- }) => JSX.Element;
64
- declare const BaseFeatureDetails: ({ model }: BaseInputProps) => JSX.Element | null;
65
- export default BaseFeatureDetails;
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
- expansionPanelDetails: {
69
- display: 'block',
70
- padding: theme.spacing(1),
71
- },
72
- expandIcon: {
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
- fieldName: {
85
- wordBreak: 'break-all',
86
- minWidth: 90,
87
- borderBottom: '1px solid #0003',
88
- fontSize: 12,
89
- background: theme.palette.grey[200],
90
- marginRight: theme.spacing(1),
91
- padding: theme.spacing(0.5),
92
- },
93
- fieldValue: {
94
- wordBreak: 'break-word',
95
- maxHeight: 300,
96
- fontSize: 12,
97
- padding: theme.spacing(0.5),
98
- overflow: 'auto',
99
- },
100
- fieldSubvalue: {
101
- wordBreak: 'break-word',
102
- maxHeight: 300,
103
- padding: theme.spacing(0.5),
104
- background: theme.palette.grey[100],
105
- border: `1px solid ${theme.palette.grey[300]}`,
106
- boxSizing: 'border-box',
107
- overflow: 'auto',
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(ui_1.SanitizedHTML, { html: `<a href="${value}">${value}</a>` })) : (react_1.default.createElement(ui_1.SanitizedHTML, { html: (0, is_object_1.default)(value) ? JSON.stringify(value) : String(value) }))));
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
- const SimpleValue = ({ name, value, description, prefix, width, }) => {
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
- const ArrayValue = ({ name, value, description, prefix = [], }) => {
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(exports.SimpleValue, { name: "Position", value: react_1.default.createElement(Position, { ...props, feature: formattedFeat }) }),
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(exports.SimpleValue, { key: name, name: name, value: value })))));
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
- const DataGridDetails = ({ value, prefix, name, }) => {
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(exports.SimpleValue, { key: key, name: key, value: formatter(value, key), description: description, prefix: prefix, width: Math.min(maxLabelWidth, MAX_FIELD_NAME_WIDTH) }));
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
- const FeatureDetails = (props) => {
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(exports.FeatureDetails, { key: JSON.stringify(sub), feature: sub, model: model, depth: depth + 1 }))))) : null));
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
- const BaseFeatureDetails = (0, mobx_react_1.observer)(({ model }) => {
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 feature = JSON.parse(JSON.stringify(featureData, (_, v) => typeof v === 'undefined' ? null : v));
369
- return isEmpty(feature) ? null : (react_1.default.createElement(exports.FeatureDetails, { model: model, feature: feature }));
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: { background: updownstreamColor } }, upstream)) : null,
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: { background: updownstreamColor } }, upstream)) : null,
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: { background: updownstreamColor } }, downstream)) : null));
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({ id: feature.uniqueId, force: false });
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 || !shown) {
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, shown, model, upDownBp, forceLoad]);
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 contents of the select box can completely change depending on whether we click on a gene feature or non-gene feature, so the current value in the select box must change accordingly
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 (isGene && !hasCDS) || !model ? null : (react_1.default.createElement("div", { className: classes.container2 },
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.IconButton, { onClick: () => setHelpShown(true) },
178
- react_1.default.createElement(Help_1.default, null))),
179
- react_1.default.createElement("br", null),
180
- shown ? (react_1.default.createElement("div", { className: classes.container2 },
181
- react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
182
- 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))))),
183
- react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
184
- react_1.default.createElement(material_1.Button, { className: classes.button, variant: "contained", color: "inherit", onClick: () => {
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.textContent || '', { format: 'text/plain' });
188
- setCopied(true);
189
- setTimeout(() => setCopied(false), 1000);
215
+ (0, copy_to_clipboard_1.default)(ref.innerHTML, { format: 'text/html' });
216
+ setCopiedHtml(true);
217
+ setTimeout(() => setCopiedHtml(false), 1000);
190
218
  }
191
- } }, copied ? 'Copied to clipboard!' : 'Copy plaintext')),
192
- react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
193
- 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" },
194
- react_1.default.createElement(material_1.Button, { className: classes.button, variant: "contained", color: "inherit", onClick: () => {
195
- const ref = seqPanelRef.current;
196
- if (ref) {
197
- (0, copy_to_clipboard_1.default)(ref.innerHTML, { format: 'text/html' });
198
- setCopiedHtml(true);
199
- setTimeout(() => setCopiedHtml(false), 1000);
200
- }
201
- } }, copiedHtml ? 'Copied to clipboard!' : 'Copy HTML'))),
202
- react_1.default.createElement(material_1.FormControl, { className: classes.formControl },
203
- react_1.default.createElement(material_1.IconButton, { onClick: () => setSettingsDlgOpen(true) },
204
- react_1.default.createElement(Settings_1.default, null))),
205
- react_1.default.createElement("br", null),
206
- react_1.default.createElement(react_1.default.Fragment, null, 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,
207
- react_1.default.createElement(material_1.Typography, { color: "error" }, sequence.error),
208
- react_1.default.createElement(material_1.Button, { variant: "contained", color: "inherit", onClick: () => setForceLoad({ ...forceLoad, force: true }) }, "Force load"))) : (react_1.default.createElement(SequencePanel_1.default, { ref: seqPanelRef, feature: parentFeature, mode: mode, sequence: sequence, intronBp: intronBp }))) : (react_1.default.createElement(material_1.Typography, null, "No sequence found"))))) : null,
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 SequencePanelProps {
3
+ interface SeqPanelProps {
4
4
  sequence: SeqState;
5
5
  feature: ParentFeat;
6
6
  mode: string;
7
7
  intronBp?: number;
8
8
  }
9
- declare const SequencePanel: React.ForwardRefExoticComponent<SequencePanelProps & React.RefAttributes<HTMLDivElement>>;
10
- export default SequencePanel;
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 SequencePanel = react_1.default.forwardRef((props, ref) => {
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 = SequencePanel;
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
- pluginManager.addAdapterType(() => new AdapterType_1.default({
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
@@ -40,24 +40,23 @@ export interface PluginRecord {
40
40
  export interface LoadedPlugin {
41
41
  default: PluginConstructor;
42
42
  }
43
- export declare function getWindowPath(windowHref: string): string;
44
43
  export default class PluginLoader {
45
44
  definitions: PluginDefinition[];
46
- fetchESM?: (url: string) => Promise<unknown>;
45
+ fetchESM?: (url: string) => Promise<LoadedPlugin>;
47
46
  fetchCJS?: (url: string) => Promise<LoadedPlugin>;
48
47
  constructor(defs?: PluginDefinition[], args?: {
49
- fetchESM?: (url: string) => Promise<unknown>;
48
+ fetchESM?: (url: string) => Promise<LoadedPlugin>;
50
49
  fetchCJS?: (url: string) => Promise<LoadedPlugin>;
51
50
  });
52
- loadScript(scriptUrl: string): Promise<void>;
53
- loadCJSPlugin(def: CJSPluginDefinition, windowHref: string): Promise<LoadedPlugin>;
54
- loadESMPlugin(def: ESMPluginDefinition, windowHref: string): Promise<LoadedPlugin>;
55
- loadUMDPlugin(def: UMDPluginDefinition | LegacyUMDPluginDefinition, windowHref: string): Promise<{
51
+ loadScript(scriptUrl: string): Promise<any>;
52
+ loadCJSPlugin(def: CJSPluginDefinition, baseUri?: string): Promise<LoadedPlugin>;
53
+ loadESMPlugin(def: ESMPluginDefinition, baseUri?: string): Promise<LoadedPlugin>;
54
+ loadUMDPlugin(def: UMDPluginDefinition | LegacyUMDPluginDefinition, baseUri?: string): Promise<{
56
55
  default: PluginConstructor;
57
56
  }>;
58
- loadPlugin(def: PluginDefinition, windowHref: string): Promise<PluginConstructor>;
57
+ loadPlugin(def: PluginDefinition, baseUri?: string): Promise<PluginConstructor>;
59
58
  installGlobalReExports(target: WindowOrWorkerGlobalScope): void;
60
- load(windowHref?: string): Promise<{
59
+ load(baseUri?: string): Promise<{
61
60
  plugin: PluginConstructor;
62
61
  definition: PluginDefinition;
63
62
  }[]>;