@jbrowse/plugin-linear-genome-view 3.2.0 → 3.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 (53) hide show
  1. package/dist/BaseLinearDisplay/components/LinearBlocks.js +1 -1
  2. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +1 -1
  3. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +22 -8
  4. package/dist/LinearBareDisplay/model.d.ts +1 -1
  5. package/dist/LinearBasicDisplay/components/AddFiltersDialog.js +1 -1
  6. package/dist/LinearBasicDisplay/model.d.ts +1 -1
  7. package/dist/LinearBasicDisplay/model.js +8 -2
  8. package/dist/LinearGenomeView/components/Cytobands.js +18 -4
  9. package/dist/LinearGenomeView/components/GetSequenceDialog.js +3 -2
  10. package/dist/LinearGenomeView/components/HeaderZoomControls.js +8 -2
  11. package/dist/LinearGenomeView/components/ImportForm.js +1 -1
  12. package/dist/LinearGenomeView/components/OverviewRubberband.js +0 -6
  13. package/dist/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +1 -9
  14. package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +16 -10
  15. package/dist/LinearGenomeView/components/RubberbandSpan.js +2 -2
  16. package/dist/LinearGenomeView/components/SearchBox.d.ts +1 -1
  17. package/dist/LinearGenomeView/components/SearchBox.js +29 -16
  18. package/dist/LinearGenomeView/components/useWheelScroll.js +13 -1
  19. package/dist/LinearGenomeView/components/util.d.ts +1 -0
  20. package/dist/LinearGenomeView/components/util.js +7 -4
  21. package/dist/LinearGenomeView/model.d.ts +4 -3
  22. package/dist/LinearGenomeView/model.js +12 -10
  23. package/dist/LinearGenomeView/util.d.ts +6 -1
  24. package/dist/LinearGenomeView/util.js +20 -6
  25. package/dist/index.d.ts +1 -1
  26. package/dist/searchUtils.js +1 -1
  27. package/esm/BaseLinearDisplay/components/LinearBlocks.js +1 -1
  28. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +1 -1
  29. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +24 -10
  30. package/esm/LinearBareDisplay/model.d.ts +1 -1
  31. package/esm/LinearBasicDisplay/components/AddFiltersDialog.js +1 -1
  32. package/esm/LinearBasicDisplay/model.d.ts +1 -1
  33. package/esm/LinearBasicDisplay/model.js +8 -2
  34. package/esm/LinearGenomeView/components/Cytobands.js +18 -4
  35. package/esm/LinearGenomeView/components/GetSequenceDialog.js +3 -2
  36. package/esm/LinearGenomeView/components/HeaderZoomControls.js +9 -3
  37. package/esm/LinearGenomeView/components/ImportForm.js +1 -1
  38. package/esm/LinearGenomeView/components/OverviewRubberband.js +0 -6
  39. package/esm/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +1 -9
  40. package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +16 -10
  41. package/esm/LinearGenomeView/components/RubberbandSpan.js +3 -3
  42. package/esm/LinearGenomeView/components/SearchBox.d.ts +1 -1
  43. package/esm/LinearGenomeView/components/SearchBox.js +29 -16
  44. package/esm/LinearGenomeView/components/useWheelScroll.js +13 -1
  45. package/esm/LinearGenomeView/components/util.d.ts +1 -0
  46. package/esm/LinearGenomeView/components/util.js +7 -4
  47. package/esm/LinearGenomeView/model.d.ts +4 -3
  48. package/esm/LinearGenomeView/model.js +12 -10
  49. package/esm/LinearGenomeView/util.d.ts +6 -1
  50. package/esm/LinearGenomeView/util.js +20 -6
  51. package/esm/index.d.ts +1 -1
  52. package/esm/searchUtils.js +1 -1
  53. package/package.json +5 -5
@@ -23,7 +23,7 @@ const useStyles = (0, mui_1.makeStyles)()({
23
23
  width: '100%',
24
24
  fontWeight: 'bold',
25
25
  textAlign: 'center',
26
- zIndex: 2000,
26
+ zIndex: 999,
27
27
  boxSizing: 'border-box',
28
28
  },
29
29
  });
@@ -206,7 +206,7 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
206
206
  } & {
207
207
  addBlock(key: string, block: BaseBlock): void;
208
208
  deleteBlock(key: string): void;
209
- selectFeature(feature: Feature): void;
209
+ selectFeature(feature: Feature): Promise<void>;
210
210
  navToFeature(feature: Feature): void;
211
211
  clearFeatureSelection(): void;
212
212
  setFeatureIdUnderMouse(feature?: string): void;
@@ -138,15 +138,23 @@ function stateModelFactory() {
138
138
  deleteBlock(key) {
139
139
  self.blockState.delete(key);
140
140
  },
141
- selectFeature(feature) {
141
+ async selectFeature(feature) {
142
142
  const session = (0, util_1.getSession)(self);
143
143
  if ((0, util_1.isSessionModelWithWidgets)(session)) {
144
- const featureWidget = session.addWidget('BaseFeatureWidget', 'baseFeature', {
145
- view: (0, util_1.getContainingView)(self),
146
- track: (0, util_1.getContainingTrack)(self),
147
- featureData: feature.toJSON(),
144
+ const { rpcManager } = session;
145
+ const sessionId = (0, tracks_1.getRpcSessionId)(self);
146
+ const track = (0, util_1.getContainingTrack)(self);
147
+ const view = (0, util_1.getContainingView)(self);
148
+ const adapterConfig = (0, configuration_1.getConf)(track, 'adapter');
149
+ const descriptions = await rpcManager.call(sessionId, 'CoreGetMetadata', {
150
+ adapterConfig,
148
151
  });
149
- session.showWidget(featureWidget);
152
+ session.showWidget(session.addWidget('BaseFeatureWidget', 'baseFeature', {
153
+ featureData: feature.toJSON(),
154
+ view,
155
+ track,
156
+ descriptions,
157
+ }));
150
158
  }
151
159
  if ((0, util_1.isSelectionContainer)(session)) {
152
160
  session.setSelection(feature);
@@ -197,7 +205,11 @@ function stateModelFactory() {
197
205
  icon: MenuOpen_1.default,
198
206
  onClick: () => {
199
207
  if (self.contextMenuFeature) {
200
- self.selectFeature(self.contextMenuFeature);
208
+ self
209
+ .selectFeature(self.contextMenuFeature)
210
+ .catch((e) => {
211
+ (0, util_1.getSession)(e).notifyError(`${e}`, e);
212
+ });
201
213
  }
202
214
  },
203
215
  },
@@ -228,7 +240,9 @@ function stateModelFactory() {
228
240
  else {
229
241
  const feature = self.features.get(f);
230
242
  if (feature) {
231
- self.selectFeature(feature);
243
+ self.selectFeature(feature).catch((e) => {
244
+ (0, util_1.getSession)(e).notifyError(`${e}`, e);
245
+ });
232
246
  }
233
247
  }
234
248
  },
@@ -190,7 +190,7 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
190
190
  } & {
191
191
  addBlock(key: string, block: import("@jbrowse/core/util/blockTypes").BaseBlock): void;
192
192
  deleteBlock(key: string): void;
193
- selectFeature(feature: import("@jbrowse/core/util").Feature): void;
193
+ selectFeature(feature: import("@jbrowse/core/util").Feature): Promise<void>;
194
194
  navToFeature(feature: import("@jbrowse/core/util").Feature): void;
195
195
  clearFeatureSelection(): void;
196
196
  setFeatureIdUnderMouse(feature?: string): void;
@@ -43,7 +43,7 @@ const AddFiltersDialog = (0, mobx_react_1.observer)(function ({ model, handleClo
43
43
  setError(e);
44
44
  }
45
45
  }, [data]);
46
- return ((0, jsx_runtime_1.jsxs)(ui_1.Dialog, { maxWidth: "xl", open: true, onClose: handleClose, title: "Add track filters", children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { children: [(0, jsx_runtime_1.jsxs)("div", { children: ["Add filters, in jexl format, one per line, starting with the string jexl:. Examples:", ' ', (0, jsx_runtime_1.jsxs)("ul", { children: [(0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("code", { children: "jexl:get(feature,'name')=='BRCA1'" }), " - show only feature where the name attribute is BRCA1"] }), (0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("code", { children: "jexl:get(feature,'type')=='gene'" }), " - show only gene type features in a GFF that has many other feature types"] }), (0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("code", { children: "jexl:get(feature,'score') > 400" }), " - show only features that have a score greater than 400"] })] })] }), error ? (0, jsx_runtime_1.jsx)("p", { className: classes.error, children: `${error}` }) : null, (0, jsx_runtime_1.jsx)(material_1.TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event => {
46
+ return ((0, jsx_runtime_1.jsxs)(ui_1.Dialog, { maxWidth: "xl", open: true, onClose: handleClose, title: "Add track filters", children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { children: [(0, jsx_runtime_1.jsxs)("div", { children: ["Add filters, in jexl format, one per line, starting with the string jexl:. Examples:", ' ', (0, jsx_runtime_1.jsxs)("ul", { children: [(0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("code", { children: "jexl:get(feature,'name')=='BRCA1'" }), " - show only feature where the name attribute is BRCA1"] }), (0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("code", { children: "jexl:get(feature,'type')=='gene'" }), " - show only gene type features in a GFF that has many other feature types"] }), (0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("code", { children: "jexl:get(feature,'score') > 400" }), " - show only features that have a score greater than 400"] }), (0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("code", { children: "jexl:get(feature,'end') - get(feature,'start') < 1000000" }), ' ', "- show only features with length less than 1Mbp"] })] })] }), error ? (0, jsx_runtime_1.jsx)("p", { className: classes.error, children: `${error}` }) : null, (0, jsx_runtime_1.jsx)(material_1.TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event => {
47
47
  setData(event.target.value);
48
48
  }, slotProps: {
49
49
  input: {
@@ -198,7 +198,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
198
198
  } & {
199
199
  addBlock(key: string, block: import("@jbrowse/core/util/blockTypes").BaseBlock): void;
200
200
  deleteBlock(key: string): void;
201
- selectFeature(feature: import("@jbrowse/core/util").Feature): void;
201
+ selectFeature(feature: import("@jbrowse/core/util").Feature): Promise<void>;
202
202
  navToFeature(feature: import("@jbrowse/core/util").Feature): void;
203
203
  clearFeatureSelection(): void;
204
204
  setFeatureIdUnderMouse(feature?: string): void;
@@ -165,7 +165,10 @@ function stateModelFactory(configSchema) {
165
165
  onClick: () => {
166
166
  (0, util_1.getSession)(self).queueDialog(handleClose => [
167
167
  SetMaxHeightDialog,
168
- { model: self, handleClose },
168
+ {
169
+ model: self,
170
+ handleClose,
171
+ },
169
172
  ]);
170
173
  },
171
174
  },
@@ -174,7 +177,10 @@ function stateModelFactory(configSchema) {
174
177
  onClick: () => {
175
178
  (0, util_1.getSession)(self).queueDialog(handleClose => [
176
179
  AddFiltersDialog,
177
- { model: self, handleClose },
180
+ {
181
+ model: self,
182
+ handleClose,
183
+ },
178
184
  ]);
179
185
  },
180
186
  },
@@ -11,14 +11,14 @@ function rightRoundedRect(x, y, width, height, radius) {
11
11
  function leftRoundedRect(x, y, width, height, radius) {
12
12
  return `M${x + radius},${y}h${width - radius}v${height}h${radius - width}a${radius},${radius} 0 0 1 ${-radius},${-radius}v${2 * radius - height}a${radius},${radius} 0 0 1 ${radius},${-radius}z`;
13
13
  }
14
- function leftTriangle(x, y, width, height) {
14
+ function leftTriangle(x, _y, width, height) {
15
15
  return [
16
16
  [x, 0],
17
17
  [x + width, height / 2],
18
18
  [x, height],
19
19
  ].toString();
20
20
  }
21
- function rightTriangle(x, y, width, height) {
21
+ function rightTriangle(x, _y, width, height) {
22
22
  return [
23
23
  [x, height / 2],
24
24
  [x + width, 0],
@@ -42,14 +42,28 @@ const Cytobands = (0, mobx_react_1.observer)(function ({ overview, block, assemb
42
42
  const rcap = reversed ? 0 : cytobands.length - 1;
43
43
  const h = consts_1.HEADER_OVERVIEW_HEIGHT;
44
44
  let centromereSeen = false;
45
+ let curr = '';
46
+ let idx = 0;
45
47
  return ((0, jsx_runtime_1.jsx)("g", { transform: `translate(-${offsetPx})`, children: cytobands.map((args, index) => {
46
48
  const k = JSON.stringify(args);
47
- const { refName, type, start, end } = args;
49
+ const { refName, name, type, start, end } = args;
48
50
  const s = overview.bpToPx({ refName, coord: start }) || 0;
49
51
  const e = overview.bpToPx({ refName, coord: end }) || 0;
50
52
  const l = Math.min(s, e);
51
53
  const w = Math.abs(e - s);
52
- const c = colorMap[type] || 'black';
54
+ if (type === 'n/a') {
55
+ const match = name === null || name === void 0 ? void 0 : name.match(/^(\d+)([A-Za-z])/);
56
+ const ret = match[1] + match[2];
57
+ if (ret && ret !== curr) {
58
+ curr = ret;
59
+ idx++;
60
+ }
61
+ }
62
+ const c = type === 'n/a'
63
+ ? idx % 2
64
+ ? 'black'
65
+ : '#a77'
66
+ : colorMap[type] || 'black';
53
67
  if (type === 'acen' && !centromereSeen) {
54
68
  centromereSeen = true;
55
69
  return ((0, jsx_runtime_1.jsx)("polygon", { points: reversed
@@ -33,8 +33,9 @@ async function fetchSequence(model, regions) {
33
33
  if (!leftOffset || !rightOffset) {
34
34
  throw new Error('no offsets on model to use for range');
35
35
  }
36
- if (leftOffset.assemblyName !== rightOffset.assemblyName) {
37
- throw new Error('not able to fetch sequences from multiple assemblies');
36
+ const assemblyNames = new Set(regions.map(r => r.assemblyName));
37
+ if (assemblyNames.size > 1) {
38
+ throw new Error('not able to fetch sequences from multiple assemblies currently');
38
39
  }
39
40
  const { rpcManager, assemblyManager } = session;
40
41
  const assemblyName = leftOffset.assemblyName || rightOffset.assemblyName || '';
@@ -58,9 +58,13 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
58
58
  color: theme.palette.text.secondary,
59
59
  },
60
60
  }));
61
+ function ValueLabelComponent(props) {
62
+ const { children, open, value } = props;
63
+ return ((0, jsx_runtime_1.jsx)(material_1.Tooltip, { open: open, enterTouchDelay: 0, placement: "top", title: value, arrow: true, children: children }));
64
+ }
61
65
  const HeaderZoomControls = (0, mobx_react_1.observer)(function ({ model, }) {
62
66
  const { classes } = useStyles();
63
- const { maxBpPerPx, minBpPerPx, bpPerPx } = model;
67
+ const { width, maxBpPerPx, minBpPerPx, bpPerPx } = model;
64
68
  const [value, setValue] = (0, react_1.useState)(-Math.log2(bpPerPx) * 100);
65
69
  (0, react_1.useEffect)(() => {
66
70
  setValue(-Math.log2(bpPerPx) * 100);
@@ -69,7 +73,9 @@ const HeaderZoomControls = (0, mobx_react_1.observer)(function ({ model, }) {
69
73
  const zoomOutDisabled = bpPerPx >= maxBpPerPx - 0.0001;
70
74
  return ((0, jsx_runtime_1.jsxs)("div", { className: classes.container, children: [(0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: "Zoom out 2x", children: (0, jsx_runtime_1.jsx)("span", { children: (0, jsx_runtime_1.jsx)(material_1.IconButton, { "data-testid": "zoom_out", disabled: zoomOutDisabled, onClick: () => {
71
75
  model.zoom(bpPerPx * 2);
72
- }, children: (0, jsx_runtime_1.jsx)(ZoomOut_1.default, {}) }) }) }), (0, jsx_runtime_1.jsx)(material_1.Slider, { size: "small", className: classes.slider, value: value, min: -Math.log2(maxBpPerPx) * 100, max: -Math.log2(minBpPerPx) * 100, onChangeCommitted: () => model.zoomTo(2 ** (-value / 100)), onChange: (_, val) => {
76
+ }, children: (0, jsx_runtime_1.jsx)(ZoomOut_1.default, {}) }) }) }), (0, jsx_runtime_1.jsx)(material_1.Slider, { size: "small", className: classes.slider, value: value, min: -Math.log2(maxBpPerPx) * 100, max: -Math.log2(minBpPerPx) * 100, onChangeCommitted: () => model.zoomTo(2 ** (-value / 100)), valueLabelDisplay: "auto", valueLabelFormat: newValue => `Window size: ${(0, util_1.getBpDisplayStr)(2 ** (-newValue / 100) * width)}`, slots: {
77
+ valueLabel: ValueLabelComponent,
78
+ }, onChange: (_, val) => {
73
79
  setValue(val);
74
80
  } }), (0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: "Zoom in 2x", children: (0, jsx_runtime_1.jsx)("span", { children: (0, jsx_runtime_1.jsx)(material_1.IconButton, { "data-testid": "zoom_in", disabled: zoomInDisabled, onClick: () => {
75
81
  model.zoom(model.bpPerPx / 2);
@@ -70,7 +70,7 @@ const LinearGenomeViewImportForm = (0, mobx_react_1.observer)(function ({ model,
70
70
  session.notify(`${e}`, 'warning');
71
71
  }
72
72
  }
73
- }, children: (0, jsx_runtime_1.jsxs)(material_1.Grid2, { container: true, spacing: 1, justifyContent: "center", alignItems: "center", children: [(0, jsx_runtime_1.jsx)(material_1.FormControl, { children: (0, jsx_runtime_1.jsx)(ui_1.AssemblySelector, { onChange: val => {
73
+ }, children: (0, jsx_runtime_1.jsxs)(material_1.Grid, { container: true, spacing: 1, justifyContent: "center", alignItems: "center", children: [(0, jsx_runtime_1.jsx)(material_1.FormControl, { children: (0, jsx_runtime_1.jsx)(ui_1.AssemblySelector, { onChange: val => {
74
74
  setSelectedAsm(val);
75
75
  }, localStorageKey: "lgv", session: session, selected: selectedAsm }) }), selectedAsm ? (assemblyError ? ((0, jsx_runtime_1.jsx)(Close_1.default, { style: { color: 'red' } })) : assemblyLoaded ? ((0, jsx_runtime_1.jsx)(material_1.FormControl, { children: (0, jsx_runtime_1.jsx)(ImportFormRefNameAutocomplete_1.default, { value: value, setValue: setValue, selectedAsm: selectedAsm, setOption: setOption, model: model }) })) : ((0, jsx_runtime_1.jsx)(material_1.CircularProgress, { size: 20, disableShrink: true }))) : null, (0, jsx_runtime_1.jsx)(material_1.FormControl, { children: (0, jsx_runtime_1.jsx)(material_1.Button, { type: "submit", disabled: !value, className: classes.button, variant: "contained", color: "primary", children: "Open" }) }), (0, jsx_runtime_1.jsx)(material_1.FormControl, { children: (0, jsx_runtime_1.jsx)(material_1.Button, { disabled: !value, className: classes.button, onClick: () => {
76
76
  model.setError(undefined);
@@ -17,12 +17,6 @@ const useStyles = (0, mui_1.makeStyles)()({
17
17
  width: '100%',
18
18
  minHeight: 8,
19
19
  },
20
- guide: {
21
- pointerEvents: 'none',
22
- height: '100%',
23
- width: 1,
24
- position: 'absolute',
25
- },
26
20
  rel: {
27
21
  position: 'relative',
28
22
  },
@@ -6,20 +6,12 @@ const material_1 = require("@mui/material");
6
6
  const mobx_react_1 = require("mobx-react");
7
7
  const mui_1 = require("tss-react/mui");
8
8
  const useStyles = (0, mui_1.makeStyles)()({
9
- rubberbandControl: {
10
- cursor: 'crosshair',
11
- width: '100%',
12
- minHeight: 8,
13
- },
14
9
  guide: {
15
10
  pointerEvents: 'none',
16
11
  height: '100%',
17
12
  width: 1,
18
13
  position: 'absolute',
19
14
  },
20
- rel: {
21
- position: 'relative',
22
- },
23
15
  });
24
16
  const OverviewRubberbandHoverTooltip = (0, mobx_react_1.observer)(function ({ model, open, guideX, overview, }) {
25
17
  var _a;
@@ -31,6 +23,6 @@ const OverviewRubberbandHoverTooltip = (0, mobx_react_1.observer)(function ({ mo
31
23
  const cytoband = (_a = assembly === null || assembly === void 0 ? void 0 : assembly.cytobands) === null || _a === void 0 ? void 0 : _a.find(f => px.coord > f.get('start') &&
32
24
  px.coord < f.get('end') &&
33
25
  px.refName === assembly.getCanonicalRefName(f.get('refName')));
34
- return ((0, jsx_runtime_1.jsx)(material_1.Tooltip, { open: open, placement: "top", title: [(0, util_1.stringify)(px), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('name')].join(' '), arrow: true, children: (0, jsx_runtime_1.jsx)("div", { className: classes.guide, style: { left: guideX } }) }));
26
+ return ((0, jsx_runtime_1.jsx)(material_1.Tooltip, { open: open, placement: "top", title: [(0, util_1.stringify)(px), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('name'), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('type')].join(' '), arrow: true, children: (0, jsx_runtime_1.jsx)("div", { className: classes.guide, style: { left: guideX } }) }));
35
27
  });
36
28
  exports.default = OverviewRubberbandHoverTooltip;
@@ -63,27 +63,31 @@ const RefNameAutocomplete = (0, mobx_react_1.observer)(function ({ model, onSele
63
63
  return;
64
64
  }
65
65
  setLoaded(false);
66
- const results = await fetchResults(debouncedSearch);
67
- setLoaded(true);
68
- setSearchOptions((0, util_2.getDeduplicatedResult)(results));
66
+ setSearchOptions((0, util_2.getDeduplicatedResult)(await fetchResults(debouncedSearch)));
69
67
  }
70
68
  catch (e) {
71
69
  console.error(e);
72
70
  session.notifyError(`${e}`, e);
73
71
  }
72
+ finally {
73
+ setLoaded(true);
74
+ }
74
75
  })();
75
76
  }, [assemblyName, fetchResults, debouncedSearch, session]);
76
77
  const inputBoxVal = coarseVisibleLocStrings || value || '';
77
- const width = Math.min(Math.max((0, util_1.measureText)(inputBoxVal, 14) + 100, minWidth), maxWidth);
78
- const refNames = assembly === null || assembly === void 0 ? void 0 : assembly.refNames;
79
- const regionOptions = (refNames === null || refNames === void 0 ? void 0 : refNames.map(refName => ({
78
+ const regions = assembly === null || assembly === void 0 ? void 0 : assembly.regions;
79
+ const regionOptions = (regions === null || regions === void 0 ? void 0 : regions.map(region => ({
80
80
  result: new BaseResults_1.RefSequenceResult({
81
- refName,
82
- label: refName,
81
+ refName: region.refName,
82
+ label: region.refName,
83
+ displayString: region.refName,
83
84
  matchedAttribute: 'refName',
84
85
  }),
85
86
  }))) || [];
86
- return ((0, jsx_runtime_1.jsx)(material_1.Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: { ...style, width }, value: inputBoxVal, loading: !loaded, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
87
+ return ((0, jsx_runtime_1.jsx)(material_1.Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: {
88
+ ...style,
89
+ width: Math.min(Math.max((0, util_1.measureText)(inputBoxVal, 14) + 100, minWidth), maxWidth),
90
+ }, value: inputBoxVal, loading: !loaded, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
87
91
  setInputValue(newInputValue);
88
92
  onChange === null || onChange === void 0 ? void 0 : onChange(newInputValue);
89
93
  }, loadingText: "loading results", open: open, onOpen: () => {
@@ -100,7 +104,9 @@ const RefNameAutocomplete = (0, mobx_react_1.observer)(function ({ model, onSele
100
104
  return;
101
105
  }
102
106
  if (typeof selectedOption === 'string') {
103
- onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResults_1.default({ label: selectedOption }));
107
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResults_1.default({
108
+ label: selectedOption,
109
+ }));
104
110
  }
105
111
  else {
106
112
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
@@ -34,10 +34,10 @@ const useStyles = (0, mui_1.makeStyles)()(theme => {
34
34
  function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top = 0, sticky = false, }) {
35
35
  const { classes } = useStyles();
36
36
  const [anchorEl, setAnchorEl] = (0, react_1.useState)(null);
37
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [anchorEl ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(RubberbandTooltip_1.default, { side: "left", anchorEl: anchorEl, text: (0, util_1.stringify)(leftBpOffset) }), (0, jsx_runtime_1.jsx)(RubberbandTooltip_1.default, { side: "right", anchorEl: anchorEl, text: (0, util_1.stringify)(rightBpOffset) })] })) : null, (0, jsx_runtime_1.jsx)("div", { className: classes.rubberband, style: { left, width }, children: numOfBpSelected ? ((0, jsx_runtime_1.jsxs)(material_1.Typography, { ref: el => {
37
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [anchorEl ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(RubberbandTooltip_1.default, { side: "left", anchorEl: anchorEl, text: (0, util_1.stringify)(leftBpOffset) }), (0, jsx_runtime_1.jsx)(RubberbandTooltip_1.default, { side: "right", anchorEl: anchorEl, text: (0, util_1.stringify)(rightBpOffset) })] })) : null, (0, jsx_runtime_1.jsx)("div", { className: classes.rubberband, style: { left, width }, children: numOfBpSelected ? ((0, jsx_runtime_1.jsx)(material_1.Typography, { ref: el => {
38
38
  setAnchorEl(el);
39
39
  }, variant: "h6", className: classes.rubberbandText, style: {
40
40
  top,
41
41
  position: sticky ? 'sticky' : undefined,
42
- }, children: [(0, util_1.toLocale)(numOfBpSelected), " bp"] })) : null })] }));
42
+ }, children: (0, util_1.getBpDisplayStr)(numOfBpSelected) })) : null })] }));
43
43
  }
@@ -1,4 +1,4 @@
1
- import type { LinearGenomeViewModel } from '..';
1
+ import type { LinearGenomeViewModel } from '../model';
2
2
  declare const SearchBox: ({ model, showHelp, }: {
3
3
  showHelp?: boolean;
4
4
  model: LinearGenomeViewModel;
@@ -13,11 +13,33 @@ const EndAdornment_1 = __importDefault(require("./RefNameAutocomplete/EndAdornme
13
13
  const util_2 = require("./util");
14
14
  const searchUtils_1 = require("../../searchUtils");
15
15
  const consts_1 = require("../consts");
16
- const useStyles = (0, mui_1.makeStyles)()(() => ({
16
+ const useStyles = (0, mui_1.makeStyles)()({
17
17
  headerRefName: {
18
18
  minWidth: 100,
19
19
  },
20
- }));
20
+ });
21
+ async function onSelect({ option, model, assemblyName, }) {
22
+ var _a;
23
+ const { assemblyManager } = (0, util_1.getSession)(model);
24
+ const assembly = assemblyManager.get(assemblyName);
25
+ if (option.hasLocation()) {
26
+ await (0, searchUtils_1.navToOption)({
27
+ option,
28
+ model,
29
+ assemblyName,
30
+ });
31
+ }
32
+ else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
33
+ model.setSearchResults(option.results, option.getLabel());
34
+ }
35
+ else if (assembly) {
36
+ await (0, searchUtils_1.handleSelectedRegion)({
37
+ input: option.getLabel(),
38
+ assembly,
39
+ model,
40
+ });
41
+ }
42
+ }
21
43
  const SearchBox = (0, mobx_react_1.observer)(function ({ model, showHelp = true, }) {
22
44
  const { classes } = useStyles();
23
45
  const theme = (0, material_1.useTheme)();
@@ -28,21 +50,12 @@ const SearchBox = (0, mobx_react_1.observer)(function ({ model, showHelp = true,
28
50
  const assembly = assemblyManager.get(assemblyName);
29
51
  const searchScope = model.searchScope(assemblyName);
30
52
  return ((0, jsx_runtime_1.jsx)(RefNameAutocomplete_1.default, { onSelect: async (option) => {
31
- var _a;
32
53
  try {
33
- if (option.hasLocation()) {
34
- await (0, searchUtils_1.navToOption)({ option, model, assemblyName });
35
- }
36
- else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
37
- model.setSearchResults(option.results, option.getLabel());
38
- }
39
- else if (assembly) {
40
- await (0, searchUtils_1.handleSelectedRegion)({
41
- input: option.getLabel(),
42
- assembly,
43
- model,
44
- });
45
- }
54
+ await onSelect({
55
+ model,
56
+ assemblyName,
57
+ option,
58
+ });
46
59
  }
47
60
  catch (e) {
48
61
  console.error(e);
@@ -2,16 +2,27 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useWheelScroll = useWheelScroll;
4
4
  const react_1 = require("react");
5
+ const util_1 = require("@jbrowse/core/util");
5
6
  function useWheelScroll(ref, model) {
6
7
  const delta = (0, react_1.useRef)(0);
7
8
  const timeout = (0, react_1.useRef)(null);
8
9
  const scheduled = (0, react_1.useRef)(false);
9
10
  (0, react_1.useEffect)(() => {
11
+ let samples = [];
10
12
  const curr = ref.current;
11
13
  function onWheel(event) {
12
14
  if (event.ctrlKey) {
13
15
  event.preventDefault();
14
- delta.current += event.deltaY / 500;
16
+ samples.push(event.deltaY);
17
+ const averageDeltaY = Math.abs((0, util_1.sum)(samples)) / samples.length;
18
+ const normalizer = averageDeltaY < 6
19
+ ? 25
20
+ : averageDeltaY > 30
21
+ ? averageDeltaY > 150
22
+ ? 500
23
+ : 150
24
+ : 75;
25
+ delta.current += event.deltaY / normalizer;
15
26
  model.setScaleFactor(delta.current < 0 ? 1 - delta.current : 1 / (1 + delta.current));
16
27
  if (timeout.current) {
17
28
  clearTimeout(timeout.current);
@@ -22,6 +33,7 @@ function useWheelScroll(ref, model) {
22
33
  ? model.bpPerPx * (1 + delta.current)
23
34
  : model.bpPerPx / (1 - delta.current), event.clientX - ((curr === null || curr === void 0 ? void 0 : curr.getBoundingClientRect().left) || 0));
24
35
  delta.current = 0;
36
+ samples = [];
25
37
  }, 300);
26
38
  }
27
39
  else {
@@ -21,4 +21,5 @@ export declare function getCytobands(assembly: Assembly | undefined, refName: st
21
21
  start: number;
22
22
  end: number;
23
23
  type: string;
24
+ name: any;
24
25
  }[];
@@ -14,12 +14,14 @@ async function fetchResults({ queryString, searchType, searchScope, rankSearchRe
14
14
  if (!textSearchManager) {
15
15
  console.warn('No text search manager');
16
16
  }
17
- const textSearchResults = await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
17
+ const textSearchResults = (await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
18
18
  queryString,
19
19
  searchType,
20
- }, searchScope, rankSearchResults));
21
- const refNameResults = (_a = assembly === null || assembly === void 0 ? void 0 : assembly.allRefNames) === null || _a === void 0 ? void 0 : _a.filter(ref => ref.toLowerCase().startsWith(queryString.toLowerCase())).slice(0, 10).map(r => new BaseResults_1.default({ label: r }));
22
- return (0, util_1.dedupe)([...(refNameResults || []), ...(textSearchResults || [])], elt => elt.getId());
20
+ }, searchScope, rankSearchResults))) || [];
21
+ const refNameResults = ((_a = assembly === null || assembly === void 0 ? void 0 : assembly.allRefNames) === null || _a === void 0 ? void 0 : _a.filter(ref => ref.toLowerCase().startsWith(queryString.toLowerCase())).slice(0, 10).map(r => new BaseResults_1.default({
22
+ label: r,
23
+ }))) || [];
24
+ return (0, util_1.dedupe)([...refNameResults, ...textSearchResults], elt => elt.getId());
23
25
  }
24
26
  function splitLast(str, split) {
25
27
  const lastIndex = str.lastIndexOf(split);
@@ -42,5 +44,6 @@ function getCytobands(assembly, refName) {
42
44
  start: f.get('start'),
43
45
  end: f.get('end'),
44
46
  type: f.get('gieStain'),
47
+ name: f.get('name'),
45
48
  })).filter(f => f.refName === refName)) || []);
46
49
  }
@@ -169,12 +169,13 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
169
169
  setCoarseDynamicBlocks(blocks: BlockSet): void;
170
170
  } & {
171
171
  moveTo(start?: BpOffset, end?: BpOffset): void;
172
- navToLocString(input: string, optAssemblyName?: string): Promise<void>;
172
+ navToLocString(input: string, optAssemblyName?: string, grow?: number): Promise<void>;
173
173
  navToSearchString({ input, assembly, }: {
174
174
  input: string;
175
175
  assembly: Assembly;
176
176
  }): Promise<void>;
177
- navToLocations(parsedLocStrings: ParsedLocString[], assemblyName?: string): Promise<void>;
177
+ navToLocation(parsedLocString: ParsedLocString, assemblyName?: string, grow?: number): Promise<void>;
178
+ navToLocations(regions: ParsedLocString[], assemblyName?: string, grow?: number): Promise<void>;
178
179
  navTo(query: NavLocation): void;
179
180
  navToMultiple(locations: NavLocation[]): void;
180
181
  } & {
@@ -242,9 +243,9 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
242
243
  displayName: string | undefined;
243
244
  tracks: any[];
244
245
  minimized: boolean;
245
- displayedRegions: Region[];
246
246
  offsetPx: number;
247
247
  bpPerPx: number;
248
+ displayedRegions: Region[];
248
249
  hideHeader: boolean;
249
250
  hideHeaderOverview: boolean;
250
251
  hideNoTracksActive: boolean;
@@ -262,12 +262,6 @@ function stateModelFactory(pluginManager) {
262
262
  return self.tracks.find(t => t.configuration.trackId === id);
263
263
  },
264
264
  rankSearchResults(results) {
265
- const openTrackIds = new Set(self.tracks.map(track => track.configuration.trackId));
266
- for (const result of results) {
267
- if (openTrackIds.has(result.trackId)) {
268
- result.updateScore(result.getScore() + 1);
269
- }
270
- }
271
265
  return results;
272
266
  },
273
267
  rewriteOnClicks(trackType, viewMenuActions) {
@@ -840,14 +834,14 @@ function stateModelFactory(pluginManager) {
840
834
  moveTo(start, end) {
841
835
  (0, Base1DUtils_1.moveTo)(self, start, end);
842
836
  },
843
- async navToLocString(input, optAssemblyName) {
837
+ async navToLocString(input, optAssemblyName, grow) {
844
838
  const { assemblyNames } = self;
845
839
  const { assemblyManager } = (0, util_1.getSession)(self);
846
840
  const assemblyName = optAssemblyName || assemblyNames[0];
847
841
  if (assemblyName) {
848
842
  await assemblyManager.waitForAssembly(assemblyName);
849
843
  }
850
- return this.navToLocations((0, util_2.parseLocStrings)(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName);
844
+ return this.navToLocations((0, util_2.parseLocStrings)(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName, grow);
851
845
  },
852
846
  async navToSearchString({ input, assembly, }) {
853
847
  await (0, searchUtils_1.handleSelectedRegion)({
@@ -856,10 +850,18 @@ function stateModelFactory(pluginManager) {
856
850
  model: self,
857
851
  });
858
852
  },
859
- async navToLocations(parsedLocStrings, assemblyName) {
853
+ async navToLocation(parsedLocString, assemblyName, grow) {
854
+ return this.navToLocations([parsedLocString], assemblyName, grow);
855
+ },
856
+ async navToLocations(regions, assemblyName, grow) {
860
857
  const { assemblyManager } = (0, util_1.getSession)(self);
861
858
  await (0, mobx_1.when)(() => self.volatileWidth !== undefined);
862
- const locations = await (0, util_2.generateLocations)(parsedLocStrings, assemblyManager, assemblyName);
859
+ const locations = await (0, util_2.generateLocations)({
860
+ regions,
861
+ assemblyManager,
862
+ assemblyName,
863
+ grow,
864
+ });
863
865
  if (locations.length === 1) {
864
866
  const loc = locations[0];
865
867
  const { reversed, parentRegion, start, end } = loc;
@@ -9,7 +9,12 @@ export declare function makeTicks(start: number, end: number, bpPerPx: number, e
9
9
  base: number;
10
10
  index: number;
11
11
  }[];
12
- export declare function generateLocations(regions: ParsedLocString[], assemblyManager: AssemblyManager, assemblyName?: string): Promise<{
12
+ export declare function generateLocations({ regions, assemblyManager, assemblyName, grow, }: {
13
+ regions: ParsedLocString[];
14
+ assemblyManager: AssemblyManager;
15
+ assemblyName?: string;
16
+ grow?: number;
17
+ }): Promise<{
13
18
  assemblyName: string;
14
19
  parentRegion: import("@jbrowse/core/assemblyManager/assembly").BasicRegion;
15
20
  end?: number | undefined;
@@ -57,7 +57,7 @@ function makeTicks(start, end, bpPerPx, emitMajor = true, emitMinor = true) {
57
57
  }
58
58
  return ticks;
59
59
  }
60
- async function generateLocations(regions, assemblyManager, assemblyName) {
60
+ async function generateLocations({ regions, assemblyManager, assemblyName, grow, }) {
61
61
  return Promise.all(regions.map(async (region) => {
62
62
  const asmName = region.assemblyName || assemblyName;
63
63
  if (!asmName) {
@@ -80,11 +80,25 @@ async function generateLocations(regions, assemblyManager, assemblyName) {
80
80
  if (!parentRegion) {
81
81
  throw new Error(`Could not find refName ${refName} in ${asmName}`);
82
82
  }
83
- return {
84
- ...region,
85
- assemblyName: asmName,
86
- parentRegion,
87
- };
83
+ const { start, end } = region;
84
+ if (grow && start && end) {
85
+ const len = end - start;
86
+ const margin = len * grow;
87
+ return {
88
+ ...region,
89
+ start: Math.max(0, start - margin),
90
+ end: end + margin,
91
+ assemblyName: asmName,
92
+ parentRegion,
93
+ };
94
+ }
95
+ else {
96
+ return {
97
+ ...region,
98
+ assemblyName: asmName,
99
+ parentRegion,
100
+ };
101
+ }
88
102
  }));
89
103
  }
90
104
  function parseLocStrings(input, assemblyName, isValidRefName) {
package/dist/index.d.ts CHANGED
@@ -195,7 +195,7 @@ export default class LinearGenomeViewPlugin extends Plugin {
195
195
  } & {
196
196
  addBlock(key: string, block: import("@jbrowse/core/util/blockTypes").BaseBlock): void;
197
197
  deleteBlock(key: string): void;
198
- selectFeature(feature: import("@jbrowse/core/util").Feature): void;
198
+ selectFeature(feature: import("@jbrowse/core/util").Feature): Promise<void>;
199
199
  navToFeature(feature: import("@jbrowse/core/util").Feature): void;
200
200
  clearFeatureSelection(): void;
201
201
  setFeatureIdUnderMouse(feature?: string): void;