@jbrowse/plugin-variants 2.13.0 → 2.14.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 (41) hide show
  1. package/dist/ChordVariantDisplay/models/stateModelFactory.d.ts +3 -3
  2. package/dist/ChordVariantDisplay/models/stateModelFactory.js +3 -1
  3. package/dist/LinearVariantDisplay/model.d.ts +1 -1
  4. package/dist/StructuralVariantChordRenderer/Chord.js +7 -5
  5. package/dist/StructuralVariantChordRenderer/ReactComponent.d.ts +2 -2
  6. package/dist/StructuralVariantChordRenderer/ReactComponent.js +3 -6
  7. package/dist/VariantFeatureWidget/AnnotGrid.js +3 -1
  8. package/dist/VariantFeatureWidget/BreakendOptionDialog.js +9 -3
  9. package/dist/VariantFeatureWidget/BreakendPanel.js +4 -2
  10. package/dist/VariantFeatureWidget/VariantAnnotationTable.js +1 -1
  11. package/dist/VariantFeatureWidget/VariantFeatureWidget.js +6 -6
  12. package/dist/VariantFeatureWidget/VariantSampleGrid.js +7 -3
  13. package/dist/VcfAdapter/VcfAdapter.d.ts +11 -7
  14. package/dist/VcfAdapter/VcfAdapter.js +69 -50
  15. package/dist/VcfFeature/index.d.ts +1 -1
  16. package/dist/VcfFeature/index.js +3 -6
  17. package/dist/VcfFeature/util.d.ts +1 -1
  18. package/dist/VcfFeature/util.js +15 -13
  19. package/dist/VcfTabixAdapter/VcfTabixAdapter.js +1 -1
  20. package/dist/extensionPoints.js +1 -1
  21. package/esm/ChordVariantDisplay/models/stateModelFactory.d.ts +3 -3
  22. package/esm/ChordVariantDisplay/models/stateModelFactory.js +3 -1
  23. package/esm/LinearVariantDisplay/model.d.ts +1 -1
  24. package/esm/StructuralVariantChordRenderer/Chord.js +7 -5
  25. package/esm/StructuralVariantChordRenderer/ReactComponent.d.ts +2 -2
  26. package/esm/StructuralVariantChordRenderer/ReactComponent.js +3 -6
  27. package/esm/VariantFeatureWidget/AnnotGrid.js +3 -1
  28. package/esm/VariantFeatureWidget/BreakendOptionDialog.js +9 -3
  29. package/esm/VariantFeatureWidget/BreakendPanel.js +4 -2
  30. package/esm/VariantFeatureWidget/VariantAnnotationTable.js +1 -1
  31. package/esm/VariantFeatureWidget/VariantFeatureWidget.js +6 -6
  32. package/esm/VariantFeatureWidget/VariantSampleGrid.js +7 -3
  33. package/esm/VcfAdapter/VcfAdapter.d.ts +11 -7
  34. package/esm/VcfAdapter/VcfAdapter.js +69 -50
  35. package/esm/VcfFeature/index.d.ts +1 -1
  36. package/esm/VcfFeature/index.js +3 -6
  37. package/esm/VcfFeature/util.d.ts +1 -1
  38. package/esm/VcfFeature/util.js +15 -13
  39. package/esm/VcfTabixAdapter/VcfTabixAdapter.js +1 -1
  40. package/esm/extensionPoints.js +1 -1
  41. package/package.json +3 -3
@@ -52,7 +52,7 @@ declare const stateModelFactory: (configSchema: AnyConfigurationSchemaType) => i
52
52
  error: unknown;
53
53
  message: string | undefined;
54
54
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
55
- onHorizontalScroll?: Function;
55
+ onHorizontalScroll?: () => void;
56
56
  blockState?: Record<string, any>;
57
57
  }>;
58
58
  readonly DisplayBlurb: import("react").FC<{
@@ -100,7 +100,7 @@ declare const stateModelFactory: (configSchema: AnyConfigurationSchemaType) => i
100
100
  } & {
101
101
  readonly blockDefinitions: import("@jbrowse/plugin-circular-view/src/CircularView/models/slices").Slice[];
102
102
  renderProps(): any;
103
- readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType;
103
+ readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType | undefined;
104
104
  isCompatibleWithRenderer(renderer: import("@jbrowse/core/pluggableElementTypes").RendererType): renderer is import("@jbrowse/core/pluggableElementTypes").CircularChordRendererType;
105
105
  readonly selectedFeatureId: string | undefined;
106
106
  } & {
@@ -118,7 +118,7 @@ declare const stateModelFactory: (configSchema: AnyConfigurationSchemaType) => i
118
118
  afterAttach(): void;
119
119
  } & {
120
120
  renderSvg(opts: import("@jbrowse/plugin-circular-view/src/CircularView/models/model").ExportSvgOptions & {
121
- theme: import("@mui/material").ThemeOptions;
121
+ theme?: import("@mui/material").ThemeOptions;
122
122
  }): Promise<import("react").JSX.Element>;
123
123
  } & {
124
124
  /**
@@ -42,7 +42,9 @@ const stateModelFactory = (configSchema) => {
42
42
  radius: view.radiusPx,
43
43
  blockDefinitions: self.blockDefinitions,
44
44
  config: self.configuration.renderer,
45
- onChordClick: (arg) => self.onChordClick(arg),
45
+ onChordClick: (arg) => {
46
+ self.onChordClick(arg);
47
+ },
46
48
  };
47
49
  },
48
50
  }));
@@ -139,7 +139,7 @@ export default function stateModelFactory(configSchema: AnyConfigurationSchemaTy
139
139
  error: unknown;
140
140
  message: string | undefined;
141
141
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
142
- onHorizontalScroll?: Function;
142
+ onHorizontalScroll?: () => void;
143
143
  blockState?: Record<string, any>;
144
144
  }>;
145
145
  readonly DisplayBlurb: import("react").FC<{
@@ -54,6 +54,7 @@ const Chord = (0, mobx_react_1.observer)(function Chord({ feature, blocksForRefs
54
54
  let endBlock;
55
55
  const alt = (_a = feature.get('ALT')) === null || _a === void 0 ? void 0 : _a[0];
56
56
  const bnd = alt && (0, vcf_1.parseBreakend)(alt);
57
+ const startPos = feature.get('start');
57
58
  if (bnd) {
58
59
  // VCF BND
59
60
  const matePosition = bnd.MatePosition.split(':');
@@ -64,7 +65,7 @@ const Chord = (0, mobx_react_1.observer)(function Chord({ feature, blocksForRefs
64
65
  // VCF TRA
65
66
  const chr2 = (_c = (_b = feature.get('INFO')) === null || _b === void 0 ? void 0 : _b.CHR2) === null || _c === void 0 ? void 0 : _c[0];
66
67
  const end = (_e = (_d = feature.get('INFO')) === null || _d === void 0 ? void 0 : _d.END) === null || _e === void 0 ? void 0 : _e[0];
67
- endPosition = parseInt(end, 10);
68
+ endPosition = Number.parseInt(end, 10);
68
69
  endBlock = blocksForRefs[chr2];
69
70
  }
70
71
  else if (svType === 'mate') {
@@ -74,8 +75,11 @@ const Chord = (0, mobx_react_1.observer)(function Chord({ feature, blocksForRefs
74
75
  endPosition = mate.start;
75
76
  endBlock = blocksForRefs[chr2];
76
77
  }
78
+ else {
79
+ console.warn('unknown sv type', svType);
80
+ endPosition = startPos + 1;
81
+ }
77
82
  if (endBlock) {
78
- const startPos = feature.get('start');
79
83
  const startRadians = bpToRadians(startBlock, startPos);
80
84
  const endRadians = bpToRadians(endBlock, endPosition);
81
85
  const startXY = (0, util_1.polarToCartesian)(radius, startRadians);
@@ -88,9 +92,7 @@ const Chord = (0, mobx_react_1.observer)(function Chord({ feature, blocksForRefs
88
92
  feature,
89
93
  });
90
94
  return (react_1.default.createElement("path", { "data-testid": `chord-${feature.id()}`, cursor: "crosshair", fill: "none", d: ['M', ...startXY, 'Q', ...controlXY, ...endXY].join(' '), ...(0, util_1.getStrokeProps)(hovered ? hoverStrokeColor : strokeColor), strokeWidth: hovered ? 3 : 1, onClick: evt => {
91
- if (endBlock && startBlock) {
92
- onClick(feature, startBlock.region, endBlock.region, evt);
93
- }
95
+ onClick(feature, startBlock.region, endBlock.region, evt);
94
96
  }, onMouseOver: () => {
95
97
  if (!selected) {
96
98
  setHovered(true);
@@ -2,11 +2,11 @@ import React from 'react';
2
2
  import { Feature } from '@jbrowse/core/util';
3
3
  import { AnyConfigurationModel } from '@jbrowse/core/configuration';
4
4
  import { Block, AnyRegion } from './Chord';
5
- declare const StructuralVariantChordsReactComponent: ({ features, config, displayModel, blockDefinitions, radius, bezierRadius, displayModel: { selectedFeatureId }, onChordClick, }: {
5
+ declare const StructuralVariantChordsReactComponent: ({ features, config, displayModel, blockDefinitions, radius, bezierRadius, onChordClick, }: {
6
6
  features: Map<string, Feature>;
7
7
  radius: number;
8
8
  config: AnyConfigurationModel;
9
- displayModel: {
9
+ displayModel?: {
10
10
  id: string;
11
11
  selectedFeatureId: string;
12
12
  };
@@ -30,7 +30,8 @@ const react_1 = __importStar(require("react"));
30
30
  const mobx_react_1 = require("mobx-react");
31
31
  // locals
32
32
  const Chord_1 = __importDefault(require("./Chord"));
33
- const StructuralVariantChordsReactComponent = (0, mobx_react_1.observer)(function ({ features, config, displayModel, blockDefinitions, radius, bezierRadius, displayModel: { selectedFeatureId }, onChordClick, }) {
33
+ const StructuralVariantChordsReactComponent = (0, mobx_react_1.observer)(function ({ features, config, displayModel, blockDefinitions, radius, bezierRadius, onChordClick, }) {
34
+ const { id, selectedFeatureId } = displayModel || {};
34
35
  // make a map of refName -> blockDefinition
35
36
  const blocksForRefsMemo = (0, react_1.useMemo)(() => {
36
37
  const blocksForRefs = {};
@@ -44,10 +45,6 @@ const StructuralVariantChordsReactComponent = (0, mobx_react_1.observer)(functio
44
45
  }
45
46
  return blocksForRefs;
46
47
  }, [blockDefinitions]);
47
- return (react_1.default.createElement("g", { id: `chords-${typeof jest !== 'undefined' ? 'test' : displayModel.id}`, "data-testid": "structuralVariantChordRenderer" }, [...features.values()].map(feature => {
48
- const id = feature.id();
49
- const selected = String(selectedFeatureId) === String(id);
50
- return (react_1.default.createElement(Chord_1.default, { key: id, feature: feature, config: config, radius: radius, bezierRadius: bezierRadius, blocksForRefs: blocksForRefsMemo, selected: selected, onClick: onChordClick }));
51
- })));
48
+ return (react_1.default.createElement("g", { "data-testid": "structuralVariantChordRenderer" }, [...features.values()].map(feature => (react_1.default.createElement(Chord_1.default, { key: feature.id(), feature: feature, config: config, radius: radius, bezierRadius: bezierRadius, blocksForRefs: blocksForRefsMemo, selected: String(selectedFeatureId) === String(id), onClick: onChordClick })))));
52
49
  });
53
50
  exports.default = StructuralVariantChordsReactComponent;
@@ -32,6 +32,8 @@ function VariantAnnotPanel({ rows, columns, }) {
32
32
  const [checked, setChecked] = (0, react_1.useState)(false);
33
33
  const widths = columns.map(e => (0, util_1.measureGridWidth)(rows.map(r => r[e.field])));
34
34
  return rows.length ? (react_1.default.createElement("div", null,
35
- react_1.default.createElement(material_1.FormControlLabel, { control: react_1.default.createElement(material_1.Checkbox, { checked: checked, onChange: event => setChecked(event.target.checked) }), label: react_1.default.createElement(material_1.Typography, { variant: "body2" }, "Show options") }),
35
+ react_1.default.createElement(material_1.FormControlLabel, { control: react_1.default.createElement(material_1.Checkbox, { checked: checked, onChange: event => {
36
+ setChecked(event.target.checked);
37
+ } }), label: react_1.default.createElement(material_1.Typography, { variant: "body2" }, "Show options") }),
36
38
  react_1.default.createElement(x_data_grid_1.DataGrid, { rowHeight: 25, rows: rows, columns: columns.map((c, i) => ({ ...c, width: widths[i] })), slots: { toolbar: checked ? x_data_grid_1.GridToolbar : null } }))) : null;
37
39
  }
@@ -50,8 +50,12 @@ const BreakendOptionDialog = (0, mobx_react_1.observer)(function ({ model, handl
50
50
  const [mirror, setMirror] = (0, react_1.useState)(true);
51
51
  return (react_1.default.createElement(ui_1.Dialog, { open: true, onClose: handleClose, title: "Breakpoint split view options" },
52
52
  react_1.default.createElement(material_1.DialogContent, null,
53
- react_1.default.createElement(Checkbox2, { checked: copyTracks, onChange: event => setCopyTracks(event.target.checked), label: "Copy tracks into the new view" }),
54
- react_1.default.createElement(Checkbox2, { checked: mirror, onChange: event => setMirror(event.target.checked), label: "Mirror tracks vertically in vertically stacked view" })),
53
+ react_1.default.createElement(Checkbox2, { checked: copyTracks, onChange: event => {
54
+ setCopyTracks(event.target.checked);
55
+ }, label: "Copy tracks into the new view" }),
56
+ react_1.default.createElement(Checkbox2, { checked: mirror, onChange: event => {
57
+ setMirror(event.target.checked);
58
+ }, label: "Mirror tracks vertically in vertically stacked view" })),
55
59
  react_1.default.createElement(material_1.DialogActions, null,
56
60
  react_1.default.createElement(material_1.Button, { onClick: () => {
57
61
  const { view } = model;
@@ -84,6 +88,8 @@ const BreakendOptionDialog = (0, mobx_react_1.observer)(function ({ model, handl
84
88
  }
85
89
  handleClose();
86
90
  }, variant: "contained", color: "primary", autoFocus: true }, "OK"),
87
- react_1.default.createElement(material_1.Button, { onClick: () => handleClose(), color: "secondary", variant: "contained" }, "Cancel"))));
91
+ react_1.default.createElement(material_1.Button, { onClick: () => {
92
+ handleClose();
93
+ }, color: "secondary", variant: "contained" }, "Cancel"))));
88
94
  });
89
95
  exports.default = BreakendOptionDialog;
@@ -34,7 +34,9 @@ function LocStringList({ locStrings, model, }) {
34
34
  const session = (0, util_1.getSession)(model);
35
35
  return (react_1.default.createElement("div", null,
36
36
  react_1.default.createElement(material_1.Typography, null, "Link to linear view of breakend endpoints"),
37
- react_1.default.createElement("ul", null, locStrings.map((locString, index) => (react_1.default.createElement("li", { key: `${locString}-${index}` },
37
+ react_1.default.createElement("ul", null, locStrings.map((locString, index) => (
38
+ /* biome-ignore lint/suspicious/noArrayIndexKey: */
39
+ react_1.default.createElement("li", { key: `${locString}-${index}` },
38
40
  react_1.default.createElement(material_1.Link, { href: "#", onClick: event => {
39
41
  var _a;
40
42
  event.preventDefault();
@@ -58,7 +60,7 @@ function LaunchBreakpointSplitViewPanel({ locStrings, model, feature, viewType,
58
60
  const simpleFeature = new util_1.SimpleFeature(feature);
59
61
  return (react_1.default.createElement("div", null,
60
62
  react_1.default.createElement(material_1.Typography, null, "Launch split views with breakend source and target"),
61
- react_1.default.createElement("ul", null, locStrings.map(locString => (react_1.default.createElement("li", { key: `${JSON.stringify(locString)}` },
63
+ react_1.default.createElement("ul", null, locStrings.map(locString => (react_1.default.createElement("li", { key: JSON.stringify(locString) },
62
64
  react_1.default.createElement(material_1.Link, { href: "#", onClick: event => {
63
65
  event.preventDefault();
64
66
  session.queueDialog(handleClose => [
@@ -12,5 +12,5 @@ function VariantAnnotationTable({ data, fields, title, }) {
12
12
  react_1.default.createElement(AnnotGrid_1.default, { rows: data.map((elt, id) => ({
13
13
  id,
14
14
  ...Object.fromEntries(elt.split('|').map((e, i) => [fields[i], e])),
15
- })) || [], columns: fields.map(c => ({ field: c })) }))) : null;
15
+ })), columns: fields.map(c => ({ field: c })) }))) : null;
16
16
  }
@@ -14,17 +14,17 @@ const BreakendPanel_1 = __importDefault(require("./BreakendPanel"));
14
14
  const VariantAnnotationTable_1 = __importDefault(require("./VariantAnnotationTable"));
15
15
  const variantFieldDescriptions_1 = require("./variantFieldDescriptions");
16
16
  function AnnPanel({ descriptions, feature, }) {
17
- var _a, _b, _c, _d;
17
+ var _a, _b, _c, _d, _e;
18
18
  const annDesc = (_b = (_a = descriptions === null || descriptions === void 0 ? void 0 : descriptions.INFO) === null || _a === void 0 ? void 0 : _a.ANN) === null || _b === void 0 ? void 0 : _b.Description;
19
- const annFields = ((_c = annDesc === null || annDesc === void 0 ? void 0 : annDesc.match(/.*Functional annotations:'(.*)'$/)) === null || _c === void 0 ? void 0 : _c[1].split('|')) || [];
20
- const ann = ((_d = feature.INFO) === null || _d === void 0 ? void 0 : _d.ANN) || [];
19
+ const annFields = ((_d = (_c = annDesc === null || annDesc === void 0 ? void 0 : annDesc.match(/.*Functional annotations:'(.*)'$/)) === null || _c === void 0 ? void 0 : _c[1]) === null || _d === void 0 ? void 0 : _d.split('|')) || [];
20
+ const ann = ((_e = feature.INFO) === null || _e === void 0 ? void 0 : _e.ANN) || [];
21
21
  return (react_1.default.createElement(VariantAnnotationTable_1.default, { fields: annFields, data: ann, title: "Variant ANN field" }));
22
22
  }
23
23
  function CsqPanel({ descriptions, feature, }) {
24
- var _a, _b, _c, _d;
24
+ var _a, _b, _c, _d, _e;
25
25
  const csqDescription = (_b = (_a = descriptions === null || descriptions === void 0 ? void 0 : descriptions.INFO) === null || _a === void 0 ? void 0 : _a.CSQ) === null || _b === void 0 ? void 0 : _b.Description;
26
- const csqFields = ((_c = csqDescription === null || csqDescription === void 0 ? void 0 : csqDescription.match(/.*Format: (.*)/)) === null || _c === void 0 ? void 0 : _c[1].split('|')) || [];
27
- const csq = ((_d = feature.INFO) === null || _d === void 0 ? void 0 : _d.CSQ) || [];
26
+ const csqFields = ((_d = (_c = csqDescription === null || csqDescription === void 0 ? void 0 : csqDescription.match(/.*Format: (.*)/)) === null || _c === void 0 ? void 0 : _c[1]) === null || _d === void 0 ? void 0 : _d.split('|')) || [];
27
+ const csq = ((_e = feature.INFO) === null || _e === void 0 ? void 0 : _e.CSQ) || [];
28
28
  return (react_1.default.createElement(VariantAnnotationTable_1.default, { fields: csqFields, data: csq, title: "Variant CSQ field" }));
29
29
  }
30
30
  const VariantFeatureWidget = (0, mobx_react_1.observer)(function (props) {
@@ -32,7 +32,9 @@ const util_1 = require("@jbrowse/core/util");
32
32
  function SampleFilters({ columns, filter, setFilter, }) {
33
33
  return (react_1.default.createElement(react_1.default.Fragment, null,
34
34
  react_1.default.createElement(material_1.Typography, null, "These filters can use a plain text search or regex style query, e.g. in the genotype field, entering 1 will query for all genotypes that include the first alternate allele e.g. 0|1 or 1|1, entering [1-9]\\d* will find any non-zero allele e.g. 0|2 or 2/33"),
35
- columns.map(({ field }) => (react_1.default.createElement(material_1.TextField, { key: `filter-${field}`, placeholder: `Filter ${field}`, value: filter[field] || '', onChange: event => setFilter({ ...filter, [field]: event.target.value }) })))));
35
+ columns.map(({ field }) => (react_1.default.createElement(material_1.TextField, { key: `filter-${field}`, placeholder: `Filter ${field}`, value: filter[field] || '', onChange: event => {
36
+ setFilter({ ...filter, [field]: event.target.value });
37
+ } })))));
36
38
  }
37
39
  function VariantSamples(props) {
38
40
  var _a;
@@ -59,7 +61,7 @@ function VariantSamples(props) {
59
61
  ? filters.every(key => {
60
62
  const currFilter = filter[key];
61
63
  return currFilter
62
- ? row[key].match(new RegExp(currFilter, 'i'))
64
+ ? new RegExp(currFilter, 'i').exec(row[key])
63
65
  : true;
64
66
  })
65
67
  : true);
@@ -82,7 +84,9 @@ function VariantSamples(props) {
82
84
  // https://github.com/mui-org/material-ui-x/issues/1197
83
85
  return !preFilteredRows.length ? null : (react_1.default.createElement(BaseFeatureDetail_1.BaseCard, { ...props, title: "Samples" },
84
86
  error ? react_1.default.createElement(material_1.Typography, { color: "error" }, `${error}`) : null,
85
- react_1.default.createElement(material_1.FormControlLabel, { control: react_1.default.createElement(material_1.Checkbox, { checked: checked, onChange: event => setChecked(event.target.checked) }), label: react_1.default.createElement(material_1.Typography, { variant: "body2" }, "Show options") }),
87
+ react_1.default.createElement(material_1.FormControlLabel, { control: react_1.default.createElement(material_1.Checkbox, { checked: checked, onChange: event => {
88
+ setChecked(event.target.checked);
89
+ } }), label: react_1.default.createElement(material_1.Typography, { variant: "body2" }, "Show options") }),
86
90
  checked ? (react_1.default.createElement(SampleFilters, { setFilter: setFilter, columns: columns, filter: filter })) : null,
87
91
  react_1.default.createElement(x_data_grid_1.DataGrid, { autoHeight: true, rows: rows, hideFooter: rows.length < 100, columns: columns, disableRowSelectionOnClick: true, rowHeight: 25, columnHeaderHeight: 35, disableColumnMenu: true, slots: { toolbar: checked ? x_data_grid_1.GridToolbar : null }, slotProps: {
88
92
  toolbar: {
@@ -1,24 +1,28 @@
1
1
  import { BaseFeatureDataAdapter, BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
2
2
  import { Region, Feature } from '@jbrowse/core/util';
3
3
  import IntervalTree from '@flatten-js/interval-tree';
4
- import VcfFeature from '../VcfFeature';
4
+ type StatusCallback = (arg: string) => void;
5
5
  export default class VcfAdapter extends BaseFeatureDataAdapter {
6
- static capabilities: string[];
7
- protected vcfFeatures?: Promise<{
6
+ calculatedIntervalTreeMap: Record<string, IntervalTree>;
7
+ vcfFeatures?: Promise<{
8
8
  header: string;
9
- intervalTree: Record<string, IntervalTree<VcfFeature>>;
9
+ intervalTreeMap: Record<string, (sc?: StatusCallback) => IntervalTree>;
10
10
  }>;
11
+ static capabilities: string[];
11
12
  getHeader(): Promise<string>;
12
13
  getMetadata(): Promise<any>;
13
- setupP(): Promise<{
14
+ setupP(opts?: BaseOptions): Promise<{
14
15
  header: string;
15
- intervalTree: Record<string, IntervalTree<VcfFeature>>;
16
+ intervalTreeMap: {
17
+ [k: string]: (sc?: (arg: string) => void) => IntervalTree<any>;
18
+ };
16
19
  }>;
17
20
  setup(): Promise<{
18
21
  header: string;
19
- intervalTree: Record<string, IntervalTree<VcfFeature>>;
22
+ intervalTreeMap: Record<string, (sc?: StatusCallback) => IntervalTree>;
20
23
  }>;
21
24
  getRefNames(_?: BaseOptions): Promise<string[]>;
22
25
  getFeatures(region: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
23
26
  freeResources(): void;
24
27
  }
28
+ export {};
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const BaseAdapter_1 = require("@jbrowse/core/data_adapters/BaseAdapter");
7
+ const util_1 = require("@jbrowse/core/util");
7
8
  const io_1 = require("@jbrowse/core/util/io");
8
9
  const rxjs_1 = require("@jbrowse/core/util/rxjs");
9
10
  const interval_tree_1 = __importDefault(require("@flatten-js/interval-tree"));
@@ -11,66 +12,84 @@ const bgzf_filehandle_1 = require("@gmod/bgzf-filehandle");
11
12
  const vcf_1 = __importDefault(require("@gmod/vcf"));
12
13
  // local
13
14
  const VcfFeature_1 = __importDefault(require("../VcfFeature"));
14
- const readVcf = (f) => {
15
- const header = [];
16
- const rest = [];
17
- f.split(/\n|\r\n|\r/)
18
- .map(f => f.trim())
19
- .filter(f => !!f)
20
- .forEach(line => {
21
- if (line.startsWith('#')) {
22
- header.push(line);
23
- }
24
- else if (line) {
25
- rest.push(line);
26
- }
27
- });
28
- return { header: header.join('\n'), lines: rest };
29
- };
30
- function isGzip(buf) {
31
- return buf[0] === 31 && buf[1] === 139 && buf[2] === 8;
32
- }
33
15
  class VcfAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.calculatedIntervalTreeMap = {};
19
+ }
34
20
  async getHeader() {
35
21
  const { header } = await this.setup();
36
22
  return header;
37
23
  }
38
24
  async getMetadata() {
39
25
  const { header } = await this.setup();
40
- const parser = new vcf_1.default({ header: header });
26
+ const parser = new vcf_1.default({ header });
41
27
  return parser.getMetadata();
42
28
  }
43
- // converts lines into an interval tree
44
- async setupP() {
45
- const pm = this.pluginManager;
46
- const buf = await (0, io_1.openLocation)(this.getConf('vcfLocation'), pm).readFile();
47
- const buffer = isGzip(buf) ? await (0, bgzf_filehandle_1.unzip)(buf) : buf;
48
- // 512MB max chrome string length is 512MB
49
- if (buffer.length > 536870888) {
50
- throw new Error('Data exceeds maximum string length (512MB)');
51
- }
52
- const str = new TextDecoder().decode(buffer);
53
- const { header, lines } = readVcf(str);
54
- const intervalTree = {};
55
- const parser = new vcf_1.default({ header });
56
- let idx = 0;
57
- for (const line of lines) {
58
- const f = new VcfFeature_1.default({
59
- variant: parser.parseLine(line),
60
- parser,
61
- id: `${this.id}-${idx++}`,
62
- });
63
- const key = f.get('refName');
64
- if (!intervalTree[key]) {
65
- intervalTree[key] = new interval_tree_1.default();
29
+ async setupP(opts) {
30
+ const { statusCallback = () => { } } = opts || {};
31
+ const buf = (await (0, io_1.openLocation)(this.getConf('vcfLocation'), this.pluginManager).readFile(opts));
32
+ const buffer = (0, util_1.isGzip)(buf)
33
+ ? await (0, util_1.updateStatus)('Unzipping', statusCallback, () => (0, bgzf_filehandle_1.unzip)(buf))
34
+ : buf;
35
+ const headerLines = [];
36
+ const featureMap = {};
37
+ let blockStart = 0;
38
+ const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined;
39
+ let i = 0;
40
+ while (blockStart < buffer.length) {
41
+ const n = buffer.indexOf('\n', blockStart);
42
+ // could be a non-newline ended file, so slice to end of file if n===-1
43
+ const b = n === -1 ? buffer.slice(blockStart) : buffer.slice(blockStart, n);
44
+ const line = ((decoder === null || decoder === void 0 ? void 0 : decoder.decode(b)) || b.toString()).trim();
45
+ if (line) {
46
+ if (line.startsWith('#')) {
47
+ headerLines.push(line);
48
+ }
49
+ else {
50
+ const ret = line.indexOf('\t');
51
+ const refName = line.slice(0, ret);
52
+ if (!featureMap[refName]) {
53
+ featureMap[refName] = [];
54
+ }
55
+ featureMap[refName].push(line);
56
+ }
66
57
  }
67
- intervalTree[key].insert([f.get('start'), f.get('end')], f);
58
+ if (i++ % 10000 === 0) {
59
+ statusCallback(`Loading ${Math.floor(blockStart / 1000000).toLocaleString('en-US')}/${Math.floor(buffer.length / 1000000).toLocaleString('en-US')} MB`);
60
+ }
61
+ blockStart = n + 1;
68
62
  }
69
- return { header, intervalTree };
63
+ const header = headerLines.join('\n');
64
+ const parser = new vcf_1.default({ header });
65
+ const intervalTreeMap = Object.fromEntries(Object.entries(featureMap).map(([refName, lines]) => [
66
+ refName,
67
+ (sc) => {
68
+ if (!this.calculatedIntervalTreeMap[refName]) {
69
+ sc === null || sc === void 0 ? void 0 : sc('Parsing VCF data');
70
+ let idx = 0;
71
+ const intervalTree = new interval_tree_1.default();
72
+ for (const line of lines) {
73
+ const f = new VcfFeature_1.default({
74
+ variant: parser.parseLine(line),
75
+ parser,
76
+ id: `${this.id}-${refName}-${idx++}`,
77
+ });
78
+ intervalTree.insert([f.get('start'), f.get('end')], f);
79
+ }
80
+ this.calculatedIntervalTreeMap[refName] = intervalTree;
81
+ }
82
+ return this.calculatedIntervalTreeMap[refName];
83
+ },
84
+ ]));
85
+ return {
86
+ header,
87
+ intervalTreeMap,
88
+ };
70
89
  }
71
90
  async setup() {
72
91
  if (!this.vcfFeatures) {
73
- this.vcfFeatures = this.setupP().catch(e => {
92
+ this.vcfFeatures = this.setupP().catch((e) => {
74
93
  this.vcfFeatures = undefined;
75
94
  throw e;
76
95
  });
@@ -78,16 +97,16 @@ class VcfAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
78
97
  return this.vcfFeatures;
79
98
  }
80
99
  async getRefNames(_ = {}) {
81
- const { intervalTree } = await this.setup();
82
- return Object.keys(intervalTree);
100
+ const { intervalTreeMap } = await this.setup();
101
+ return Object.keys(intervalTreeMap);
83
102
  }
84
103
  getFeatures(region, opts = {}) {
85
104
  return (0, rxjs_1.ObservableCreate)(async (observer) => {
86
105
  var _a;
87
106
  try {
88
107
  const { start, end, refName } = region;
89
- const { intervalTree } = await this.setup();
90
- (_a = intervalTree[refName]) === null || _a === void 0 ? void 0 : _a.search([start, end]).forEach((f) => {
108
+ const { intervalTreeMap } = await this.setup();
109
+ (_a = intervalTreeMap[refName]) === null || _a === void 0 ? void 0 : _a.call(intervalTreeMap, opts.statusCallback).search([start, end]).forEach(f => {
91
110
  observer.next(f);
92
111
  });
93
112
  observer.complete();
@@ -36,7 +36,7 @@ export default class VCFFeature implements Feature {
36
36
  ALT: string[];
37
37
  CHROM: string;
38
38
  INFO: any;
39
- ID: string[];
39
+ ID?: string[];
40
40
  }): FeatureData;
41
41
  toJSON(): any;
42
42
  }
@@ -3,14 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  // locals
4
4
  const util_1 = require("./util");
5
5
  class VCFFeature {
6
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
6
  constructor(args) {
8
7
  this.variant = args.variant;
9
8
  this.parser = args.parser;
10
9
  this.data = this.dataFromVariant(this.variant);
11
10
  this._id = args.id;
12
11
  }
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
12
  get(field) {
15
13
  var _a;
16
14
  return field === 'samples'
@@ -34,8 +32,8 @@ class VCFFeature {
34
32
  const { REF, ALT, POS, CHROM, INFO, ID } = variant;
35
33
  const start = POS - 1;
36
34
  const [type, description] = (0, util_1.getSOTermAndDescription)(REF, ALT, this.parser);
37
- const isTRA = ALT === null || ALT === void 0 ? void 0 : ALT.some(f => f === '<TRA>');
38
- const isSymbolic = ALT === null || ALT === void 0 ? void 0 : ALT.some(f => f.includes('<'));
35
+ const isTRA = ALT.includes('<TRA>');
36
+ const isSymbolic = ALT.some(f => f.includes('<'));
39
37
  return {
40
38
  refName: CHROM,
41
39
  start,
@@ -43,10 +41,9 @@ class VCFFeature {
43
41
  description,
44
42
  type,
45
43
  name: ID === null || ID === void 0 ? void 0 : ID.join(','),
46
- aliases: ID && ID.length > 1 ? variant.ID.slice(1) : undefined,
44
+ aliases: ID && ID.length > 1 ? ID.slice(1) : undefined,
47
45
  };
48
46
  }
49
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
47
  toJSON() {
51
48
  return {
52
49
  uniqueId: this._id,
@@ -2,6 +2,6 @@ import VCF from '@gmod/vcf';
2
2
  /**
3
3
  * Get a sequence ontology (SO) term that describes the variant type
4
4
  */
5
- export declare function getSOTermAndDescription(ref: string, alt: string[], parser: VCF): string[];
5
+ export declare function getSOTermAndDescription(ref: string, alt: string[] | undefined, parser: VCF): string[];
6
6
  export declare function getSOAndDescFromAltDefs(alt: string, parser: VCF): string[];
7
7
  export declare function getSOAndDescByExamination(ref: string, alt: string): string[];
@@ -40,10 +40,12 @@ function getSOTermAndDescription(ref, alt, parser) {
40
40
  // Combine descriptions like ["SNV G -> A", "SNV G -> T"] to ["SNV G -> A,T"]
41
41
  if (descriptions.size > 1) {
42
42
  const descs = [...descriptions];
43
- const prefixes = new Set(descs.map(desc => {
43
+ const prefixes = new Set(descs
44
+ .map(desc => {
44
45
  const prefix = desc.split('->');
45
46
  return prefix[1] ? prefix[0] : desc;
46
- }));
47
+ })
48
+ .filter((f) => !!f));
47
49
  descriptions = new Set([...prefixes]
48
50
  .map(r => r.trim())
49
51
  .map(prefix => {
@@ -88,43 +90,43 @@ function getSOAndDescByExamination(ref, alt) {
88
90
  if (bnd) {
89
91
  return ['breakend', alt];
90
92
  }
91
- else if (ref.length === 1 && alt.length === 1) {
93
+ if (ref.length === 1 && alt.length === 1) {
92
94
  return ['SNV', makeDescriptionString('SNV', ref, alt)];
93
95
  }
94
- else if (alt === '<INS>') {
96
+ if (alt === '<INS>') {
95
97
  return ['insertion', alt];
96
98
  }
97
- else if (alt === '<DEL>') {
99
+ if (alt === '<DEL>') {
98
100
  return ['deletion', alt];
99
101
  }
100
- else if (alt === '<INV>') {
102
+ if (alt === '<INV>') {
101
103
  return ['inversion', alt];
102
104
  }
103
- else if (alt === '<TRA>') {
105
+ if (alt === '<TRA>') {
104
106
  return ['translocation', alt];
105
107
  }
106
- else if (alt.includes('<')) {
108
+ if (alt.includes('<')) {
107
109
  return ['sv', alt];
108
110
  }
109
- else if (ref.length === alt.length) {
111
+ if (ref.length === alt.length) {
110
112
  return ref.split('').reverse().join('') === alt
111
113
  ? ['inversion', makeDescriptionString('inversion', ref, alt)]
112
114
  : ['substitution', makeDescriptionString('substitution', ref, alt)];
113
115
  }
114
- else if (ref.length <= alt.length) {
116
+ if (ref.length <= alt.length) {
115
117
  const len = alt.length - ref.length;
116
118
  const lena = len.toLocaleString('en-US');
117
119
  return [
118
120
  'insertion',
119
- len > 5 ? lena + 'bp INS' : makeDescriptionString('insertion', ref, alt),
121
+ len > 5 ? `${lena}bp INS` : makeDescriptionString('insertion', ref, alt),
120
122
  ];
121
123
  }
122
- else if (ref.length > alt.length) {
124
+ if (ref.length > alt.length) {
123
125
  const len = ref.length - alt.length;
124
126
  const lena = len.toLocaleString('en-US');
125
127
  return [
126
128
  'deletion',
127
- len > 5 ? lena + 'bp DEL' : makeDescriptionString('deletion', ref, alt),
129
+ len > 5 ? `${lena}bp DEL` : makeDescriptionString('deletion', ref, alt),
128
130
  ];
129
131
  }
130
132
  return ['indel', makeDescriptionString('indel', ref, alt)];
@@ -33,7 +33,7 @@ class VcfTabixAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
33
33
  }
34
34
  async configure() {
35
35
  if (!this.configured) {
36
- this.configured = this.configurePre().catch(e => {
36
+ this.configured = this.configurePre().catch((e) => {
37
37
  this.configured = undefined;
38
38
  throw e;
39
39
  });
@@ -20,7 +20,7 @@ function ExtensionPointsF(pluginManager) {
20
20
  if (regexGuess.test(fileName) && !adapterHint) {
21
21
  return obj;
22
22
  }
23
- else if (adapterHint === adapterName) {
23
+ if (adapterHint === adapterName) {
24
24
  return obj;
25
25
  }
26
26
  return adapterGuesser(file, index, adapterHint);
@@ -52,7 +52,7 @@ declare const stateModelFactory: (configSchema: AnyConfigurationSchemaType) => i
52
52
  error: unknown;
53
53
  message: string | undefined;
54
54
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
55
- onHorizontalScroll?: Function;
55
+ onHorizontalScroll?: () => void;
56
56
  blockState?: Record<string, any>;
57
57
  }>;
58
58
  readonly DisplayBlurb: import("react").FC<{
@@ -100,7 +100,7 @@ declare const stateModelFactory: (configSchema: AnyConfigurationSchemaType) => i
100
100
  } & {
101
101
  readonly blockDefinitions: import("@jbrowse/plugin-circular-view/src/CircularView/models/slices").Slice[];
102
102
  renderProps(): any;
103
- readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType;
103
+ readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType | undefined;
104
104
  isCompatibleWithRenderer(renderer: import("@jbrowse/core/pluggableElementTypes").RendererType): renderer is import("@jbrowse/core/pluggableElementTypes").CircularChordRendererType;
105
105
  readonly selectedFeatureId: string | undefined;
106
106
  } & {
@@ -118,7 +118,7 @@ declare const stateModelFactory: (configSchema: AnyConfigurationSchemaType) => i
118
118
  afterAttach(): void;
119
119
  } & {
120
120
  renderSvg(opts: import("@jbrowse/plugin-circular-view/src/CircularView/models/model").ExportSvgOptions & {
121
- theme: import("@mui/material").ThemeOptions;
121
+ theme?: import("@mui/material").ThemeOptions;
122
122
  }): Promise<import("react").JSX.Element>;
123
123
  } & {
124
124
  /**