@jbrowse/core 2.3.4 → 2.4.1

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 (116) hide show
  1. package/BaseFeatureWidget/BaseFeatureDetail.d.ts +18 -19
  2. package/BaseFeatureWidget/BaseFeatureDetail.js +76 -69
  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/BaseFeatureWidget/index.js +2 -2
  8. package/CorePlugin.js +2 -7
  9. package/PluginLoader.d.ts +1 -1
  10. package/PluginLoader.js +20 -24
  11. package/PluginManager.d.ts +1 -1
  12. package/PluginManager.js +2 -3
  13. package/ReExports/Attributes.d.ts +1 -2
  14. package/ReExports/Attributes.js +4 -3
  15. package/ReExports/BaseCard.d.ts +1 -2
  16. package/ReExports/BaseCard.js +4 -3
  17. package/ReExports/DataGrid.d.ts +1 -2
  18. package/ReExports/DataGrid.js +2 -2
  19. package/ReExports/FeatureDetails.d.ts +1 -2
  20. package/ReExports/FeatureDetails.js +4 -3
  21. package/ReExports/index.d.ts +1 -2
  22. package/ReExports/index.js +3 -2
  23. package/ReExports/modules.d.ts +1 -1
  24. package/ReExports/modules.js +2 -2
  25. package/assemblyManager/assembly.js +5 -5
  26. package/assemblyManager/assemblyConfigSchema.js +2 -2
  27. package/configuration/configurationSchema.js +1 -1
  28. package/configuration/util.js +1 -1
  29. package/data_adapters/BaseAdapter.js +1 -1
  30. package/data_adapters/CytobandAdapter/CytobandAdapter.d.ts +8 -0
  31. package/data_adapters/CytobandAdapter/CytobandAdapter.js +40 -0
  32. package/data_adapters/CytobandAdapter/configSchema.d.ts +2 -0
  33. package/data_adapters/CytobandAdapter/configSchema.js +17 -0
  34. package/data_adapters/CytobandAdapter/index.d.ts +3 -0
  35. package/data_adapters/CytobandAdapter/index.js +37 -0
  36. package/data_adapters/dataAdapterCache.d.ts +3 -2
  37. package/data_adapters/dataAdapterCache.js +2 -3
  38. package/package.json +5 -4
  39. package/pluggableElementTypes/PluggableElementBase.d.ts +1 -1
  40. package/pluggableElementTypes/PluggableElementBase.js +1 -2
  41. package/pluggableElementTypes/RpcMethodType.d.ts +5 -8
  42. package/pluggableElementTypes/RpcMethodType.js +23 -34
  43. package/pluggableElementTypes/index.d.ts +11 -1
  44. package/pluggableElementTypes/index.js +23 -23
  45. package/pluggableElementTypes/models/BaseConnectionModelFactory.js +2 -2
  46. package/pluggableElementTypes/models/BaseTrackModel.js +8 -13
  47. package/pluggableElementTypes/renderers/CircularChordRendererType.d.ts +13 -2
  48. package/pluggableElementTypes/renderers/CircularChordRendererType.js +10 -2
  49. package/pluggableElementTypes/renderers/ComparativeServerSideRendererType.d.ts +8 -2
  50. package/pluggableElementTypes/renderers/ComparativeServerSideRendererType.js +10 -4
  51. package/pluggableElementTypes/renderers/ServerSideRendererType.d.ts +1 -1
  52. package/pluggableElementTypes/renderers/ServerSideRendererType.js +2 -34
  53. package/pluggableElementTypes/renderers/index.d.ts +7 -9
  54. package/pluggableElementTypes/renderers/index.js +15 -15
  55. package/pluggableElementTypes/renderers/util/serializableFilterChain.js +1 -1
  56. package/rpc/BaseRpcDriver.js +7 -8
  57. package/rpc/WebWorkerRpcDriver.js +18 -12
  58. package/rpc/coreRpcMethods.d.ts +9 -11
  59. package/rpc/coreRpcMethods.js +17 -17
  60. package/rpc/methods/CoreGetFeatureDetails.js +1 -1
  61. package/rpc/methods/util.d.ts +2 -2
  62. package/rpc/methods/util.js +2 -2
  63. package/rpc/remoteAbortSignals.js +0 -1
  64. package/tsconfig.build.tsbuildinfo +1 -1
  65. package/ui/App.js +3 -18
  66. package/ui/AppLogo.js +1 -6
  67. package/ui/ColorPicker.js +1 -1
  68. package/ui/Dialog.js +1 -1
  69. package/ui/DrawerWidget.js +4 -4
  70. package/ui/EditableTypography.js +1 -1
  71. package/ui/FileSelector/FileSelector.d.ts +2 -2
  72. package/ui/FileSelector/FileSelector.js +24 -35
  73. package/ui/FileSelector/index.d.ts +1 -2
  74. package/ui/FileSelector/index.js +3 -2
  75. package/ui/LoadingEllipses.js +2 -2
  76. package/ui/Menu.js +45 -32
  77. package/ui/ResizeBar.js +10 -6
  78. package/ui/ResizeHandle.js +3 -6
  79. package/ui/SanitizedHTML.js +2 -0
  80. package/ui/ViewContainer.js +7 -44
  81. package/ui/ViewMenu.d.ts +9 -0
  82. package/ui/ViewMenu.js +69 -0
  83. package/ui/ViewPanel.d.ts +19 -0
  84. package/ui/ViewPanel.js +49 -0
  85. package/ui/theme.d.ts +10 -166
  86. package/ui/theme.js +260 -48
  87. package/util/Base1DUtils.js +16 -14
  88. package/util/Base1DViewModel.d.ts +1 -1
  89. package/util/Base1DViewModel.js +9 -8
  90. package/util/aborting.js +1 -1
  91. package/util/analytics.js +1 -1
  92. package/util/blockTypes.js +10 -10
  93. package/util/color/index.d.ts +1 -2
  94. package/util/color/index.js +4 -3
  95. package/util/idMaker.js +5 -8
  96. package/util/index.d.ts +9 -9
  97. package/util/index.js +35 -52
  98. package/util/io/RemoteFileWithRangeCache.js +2 -2
  99. package/util/io/index.d.ts +1 -2
  100. package/util/io/index.js +6 -6
  101. package/util/jexl.js +3 -1
  102. package/util/layouts/GranularRectLayout.js +10 -4
  103. package/util/layouts/MultiLayout.js +1 -1
  104. package/util/layouts/SceneGraph.js +3 -7
  105. package/util/map-obj.d.ts +3 -0
  106. package/util/map-obj.js +33 -0
  107. package/util/offscreenCanvasPonyfill.js +4 -3
  108. package/util/offscreenCanvasUtils.d.ts +18 -4
  109. package/util/offscreenCanvasUtils.js +49 -7
  110. package/util/tracks.d.ts +1 -1
  111. package/util/tracks.js +0 -1
  112. package/util/types/index.d.ts +1 -1
  113. package/util/types/index.js +3 -3
  114. package/util/types/mst.js +3 -3
  115. package/data_adapters/CytobandAdapter.d.ts +0 -8
  116. 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;
@@ -165,7 +169,7 @@ function Position(props) {
165
169
  '1': '+',
166
170
  };
167
171
  const str = strandMap[strand] ? `(${strandMap[strand]})` : '';
168
- // @ts-ignore
172
+ // @ts-expect-error
169
173
  const loc = (0, util_1.assembleLocString)(feature);
170
174
  return react_1.default.createElement(react_1.default.Fragment, null, `${loc} ${str}`);
171
175
  }
@@ -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,10 +206,14 @@ 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
- value.forEach((val) => Object.keys(val).forEach(k => unionKeys.add(k)));
212
+ for (const val of value) {
213
+ for (const k of Object.keys(val)) {
214
+ unionKeys.add(k);
215
+ }
216
+ }
209
217
  if (unionKeys.size < keys.length + 5) {
210
218
  // avoids key 'id' from being used in row data
211
219
  const rows = Object.entries(value).map(([k, val]) => {
@@ -249,7 +257,7 @@ const DataGridDetails = ({ value, prefix, name, }) => {
249
257
  react_1.default.createElement(x_data_grid_1.DataGrid, { disableSelectionOnClick: true, rowHeight: rowHeight, rows: rows, hideFooterSelectedRowCount: true, columns: columns, hideFooter: hideFoot }))));
250
258
  }
251
259
  return null;
252
- };
260
+ }
253
261
  // pick using a path from an object, similar to _.get from lodash with special logic
254
262
  // for Descriptions from e.g. VCF headers
255
263
  // @param arr example ['a','b'], obj = {a:{b:'hello}}
@@ -290,12 +298,12 @@ function UriAttribute({ value, prefix, name, }) {
290
298
  }
291
299
  function Attributes(props) {
292
300
  const { attributes, omit = [], omitSingleLevel = [], descriptions, formatter = val => val, hideUris, prefix = [], } = props;
293
- const omits = [...omit, ...globalOmit, ...omitSingleLevel];
301
+ const omits = new Set([...omit, ...globalOmit, ...omitSingleLevel]);
294
302
  const { __jbrowsefmt, ...rest } = attributes;
295
303
  const formattedAttributes = { ...rest, ...__jbrowsefmt };
296
- const maxLabelWidth = generateMaxWidth(Object.entries(formattedAttributes).filter(([k, v]) => v !== undefined && !omits.includes(k)), prefix);
304
+ const maxLabelWidth = generateMaxWidth(Object.entries(formattedAttributes).filter(([k, v]) => v !== undefined && !omits.has(k)), prefix);
297
305
  return (react_1.default.createElement(react_1.default.Fragment, null, Object.entries(formattedAttributes)
298
- .filter(([k, v]) => v !== undefined && !omits.includes(k))
306
+ .filter(([k, v]) => v !== undefined && !omits.has(k))
299
307
  .map(([key, value]) => {
300
308
  const description = accessNested([...prefix, key], descriptions);
301
309
  if (Array.isArray(value)) {
@@ -308,7 +316,7 @@ function Attributes(props) {
308
316
  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
317
  }
310
318
  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) }));
319
+ 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
320
  }
313
321
  })));
314
322
  }
@@ -327,9 +335,9 @@ function generateTitle(name, id, type) {
327
335
  .filter(f => !!f)
328
336
  .join(' - ');
329
337
  }
330
- const FeatureDetails = (props) => {
338
+ function FeatureDetails(props) {
331
339
  const { omit = [], model, feature, depth = 0 } = props;
332
- const { name = '', id = '', type = '', subfeatures } = feature;
340
+ const { mate, name = '', id = '', type = '', subfeatures } = feature;
333
341
  const pm = (0, util_1.getEnv)(model).pluginManager;
334
342
  const session = (0, util_1.getSession)(model);
335
343
  const ExtraPanel = pm.evaluateExtensionPoint('Core-extraFeaturePanel', null, {
@@ -340,10 +348,10 @@ const FeatureDetails = (props) => {
340
348
  return (react_1.default.createElement(BaseCard, { title: generateTitle(name, id, type) },
341
349
  react_1.default.createElement(material_1.Typography, null, "Core details"),
342
350
  react_1.default.createElement(CoreDetails, { ...props }),
343
- feature.mate ? (react_1.default.createElement(react_1.default.Fragment, null,
351
+ mate ? (react_1.default.createElement(react_1.default.Fragment, null,
344
352
  react_1.default.createElement(material_1.Divider, null),
345
353
  react_1.default.createElement(material_1.Typography, null, "Mate details"),
346
- react_1.default.createElement(CoreDetails, { ...props, feature: feature.mate }))) : null,
354
+ react_1.default.createElement(CoreDetails, { ...props, feature: mate }))) : null,
347
355
  react_1.default.createElement(material_1.Divider, null),
348
356
  react_1.default.createElement(material_1.Typography, null, "Attributes"),
349
357
  react_1.default.createElement(Attributes, { attributes: feature, ...props, omit: omit, omitSingleLevel: coreDetails }),
@@ -353,10 +361,10 @@ const FeatureDetails = (props) => {
353
361
  react_1.default.createElement(material_1.Divider, null),
354
362
  react_1.default.createElement(BaseCard, { title: ExtraPanel.name },
355
363
  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
- };
364
+ (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));
365
+ }
358
366
  exports.FeatureDetails = FeatureDetails;
359
- const BaseFeatureDetails = (0, mobx_react_1.observer)(({ model }) => {
367
+ exports.default = (0, mobx_react_1.observer)(function ({ model }) {
360
368
  const { featureData } = model;
361
369
  if (!featureData) {
362
370
  return null;
@@ -365,7 +373,6 @@ const BaseFeatureDetails = (0, mobx_react_1.observer)(({ model }) => {
365
373
  // setting null is not allowed by jexl so we set it to undefined to hide. see
366
374
  // config guide. this replacement happens both here and when snapshotting the
367
375
  // 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 }));
376
+ const g = JSON.parse(JSON.stringify(featureData, (_, v) => (v === undefined ? null : v)));
377
+ return isEmpty(g) ? null : react_1.default.createElement(FeatureDetails, { model: model, feature: g });
370
378
  });
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;
@@ -82,7 +82,7 @@ function stateModelFactory(pluginManager) {
82
82
  },
83
83
  }))
84
84
  .preProcessSnapshot(snap => {
85
- // @ts-ignore
85
+ // @ts-expect-error
86
86
  const { featureData, finalizedFeatureData, ...rest } = snap;
87
87
  return {
88
88
  unformattedFeatureData: featureData,
@@ -100,7 +100,7 @@ function stateModelFactory(pluginManager) {
100
100
  // hidden, setting null is not allowed by jexl so we set it to
101
101
  // undefined to hide. see config guide. this replacement happens both
102
102
  // here and when displaying the featureData in base feature widget
103
- finalizedFeatureData: JSON.parse(JSON.stringify(featureData, (_, v) => typeof v === 'undefined' ? null : v)),
103
+ finalizedFeatureData: JSON.parse(JSON.stringify(featureData, (_, v) => (v === undefined ? null : v))),
104
104
  ...rest,
105
105
  };
106
106
  });