@jbrowse/plugin-linear-genome-view 3.3.0 → 3.5.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/BaseLinearDisplay/models/BaseLinearDisplayModel.js +22 -6
  2. package/dist/LinearGenomeView/components/Cytobands.js +18 -4
  3. package/dist/LinearGenomeView/components/GetSequenceDialog.js +3 -2
  4. package/dist/LinearGenomeView/components/HeaderPanControls.js +0 -3
  5. package/dist/LinearGenomeView/components/MiniControls.js +1 -0
  6. package/dist/LinearGenomeView/components/OverviewRubberband.js +0 -6
  7. package/dist/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +1 -9
  8. package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +16 -10
  9. package/dist/LinearGenomeView/components/SearchBox.d.ts +1 -1
  10. package/dist/LinearGenomeView/components/SearchBox.js +29 -16
  11. package/dist/LinearGenomeView/components/TrackLabel.js +1 -0
  12. package/dist/LinearGenomeView/components/TrackLabelMenu.js +2 -9
  13. package/dist/LinearGenomeView/components/useWheelScroll.js +13 -1
  14. package/dist/LinearGenomeView/components/util.d.ts +1 -0
  15. package/dist/LinearGenomeView/components/util.js +7 -4
  16. package/dist/LinearGenomeView/model.d.ts +6 -5
  17. package/dist/LinearGenomeView/model.js +16 -13
  18. package/dist/LinearGenomeView/util.d.ts +6 -1
  19. package/dist/LinearGenomeView/util.js +20 -6
  20. package/dist/searchUtils.js +1 -1
  21. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +24 -8
  22. package/esm/LinearGenomeView/components/Cytobands.js +18 -4
  23. package/esm/LinearGenomeView/components/GetSequenceDialog.js +3 -2
  24. package/esm/LinearGenomeView/components/HeaderPanControls.js +0 -3
  25. package/esm/LinearGenomeView/components/MiniControls.js +1 -0
  26. package/esm/LinearGenomeView/components/OverviewRubberband.js +0 -6
  27. package/esm/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +1 -9
  28. package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +16 -10
  29. package/esm/LinearGenomeView/components/SearchBox.d.ts +1 -1
  30. package/esm/LinearGenomeView/components/SearchBox.js +29 -16
  31. package/esm/LinearGenomeView/components/TrackLabel.js +1 -0
  32. package/esm/LinearGenomeView/components/TrackLabelMenu.js +2 -9
  33. package/esm/LinearGenomeView/components/useWheelScroll.js +13 -1
  34. package/esm/LinearGenomeView/components/util.d.ts +1 -0
  35. package/esm/LinearGenomeView/components/util.js +7 -4
  36. package/esm/LinearGenomeView/model.d.ts +6 -5
  37. package/esm/LinearGenomeView/model.js +16 -13
  38. package/esm/LinearGenomeView/util.d.ts +6 -1
  39. package/esm/LinearGenomeView/util.js +20 -6
  40. package/esm/searchUtils.js +1 -1
  41. package/package.json +3 -3
@@ -141,12 +141,28 @@ function stateModelFactory() {
141
141
  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(),
148
- });
149
- session.showWidget(featureWidget);
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
+ (async () => {
150
+ try {
151
+ const descriptions = await rpcManager.call(sessionId, 'CoreGetMetadata', {
152
+ adapterConfig,
153
+ });
154
+ session.showWidget(session.addWidget('BaseFeatureWidget', 'baseFeature', {
155
+ featureData: feature.toJSON(),
156
+ view,
157
+ track,
158
+ descriptions,
159
+ }));
160
+ }
161
+ catch (e) {
162
+ console.error(e);
163
+ (0, util_1.getSession)(e).notifyError(`${e}`, e);
164
+ }
165
+ })();
150
166
  }
151
167
  if ((0, util_1.isSelectionContainer)(session)) {
152
168
  session.setSelection(feature);
@@ -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 || '';
@@ -16,9 +16,6 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
16
16
  color: theme.palette.text.primary,
17
17
  margin: consts_1.SPACING,
18
18
  },
19
- buttonSpacer: {
20
- marginRight: theme.spacing(2),
21
- },
22
19
  }));
23
20
  function HeaderPanControls({ model }) {
24
21
  const { classes } = useStyles();
@@ -17,6 +17,7 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
17
17
  position: 'absolute',
18
18
  right: 0,
19
19
  background: theme.palette.background.paper,
20
+ zIndex: 2,
20
21
  },
21
22
  focusedBackground: {
22
23
  background: (0, material_1.alpha)(theme.palette.secondary.light, 0.2),
@@ -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);
@@ -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);
@@ -17,6 +17,7 @@ const TrackLabelDragHandle_1 = __importDefault(require("./TrackLabelDragHandle")
17
17
  const TrackLabelMenu_1 = __importDefault(require("./TrackLabelMenu"));
18
18
  const useStyles = (0, mui_1.makeStyles)()(theme => ({
19
19
  root: {
20
+ zIndex: 200,
20
21
  background: (0, material_1.alpha)(theme.palette.background.paper, 0.8),
21
22
  '&:hover': {
22
23
  background: theme.palette.background.paper,
@@ -22,16 +22,9 @@ const TrackLabelMenu = (0, mobx_react_1.observer)(function ({ track, }) {
22
22
  const trackConf = track.configuration;
23
23
  const minimized = track.minimized;
24
24
  const pinned = track.pinned;
25
- let lgvHasParentView;
26
- try {
27
- (0, util_1.getContainingView)(view);
28
- lgvHasParentView = true;
29
- }
30
- catch (error) {
31
- lgvHasParentView = false;
32
- }
25
+ const { isTopLevelView } = view;
33
26
  const items = [
34
- ...(lgvHasParentView
27
+ ...(isTopLevelView
35
28
  ? []
36
29
  : [
37
30
  {
@@ -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
  }
@@ -58,7 +58,8 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
58
58
  readonly width: number;
59
59
  readonly interRegionPaddingWidth: number;
60
60
  readonly assemblyNames: string[];
61
- readonly stickyViewHeaders: boolean;
61
+ readonly isTopLevelView: import("@jbrowse/core/util").AbstractViewModel | undefined;
62
+ readonly stickyViewHeaders: boolean | undefined;
62
63
  readonly rubberbandTop: number;
63
64
  readonly pinnedTracksTop: number;
64
65
  } & {
@@ -169,13 +170,13 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
169
170
  setCoarseDynamicBlocks(blocks: BlockSet): void;
170
171
  } & {
171
172
  moveTo(start?: BpOffset, end?: BpOffset): void;
172
- navToLocString(input: string, optAssemblyName?: string): Promise<void>;
173
+ navToLocString(input: string, optAssemblyName?: string, grow?: number): Promise<void>;
173
174
  navToSearchString({ input, assembly, }: {
174
175
  input: string;
175
176
  assembly: Assembly;
176
177
  }): Promise<void>;
177
- navToLocation(parsedLocString: ParsedLocString, assemblyName?: string): Promise<void>;
178
- navToLocations(parsedLocStrings: ParsedLocString[], assemblyName?: string): Promise<void>;
178
+ navToLocation(parsedLocString: ParsedLocString, assemblyName?: string, grow?: number): Promise<void>;
179
+ navToLocations(regions: ParsedLocString[], assemblyName?: string, grow?: number): Promise<void>;
179
180
  navTo(query: NavLocation): void;
180
181
  navToMultiple(locations: NavLocation[]): void;
181
182
  } & {
@@ -243,9 +244,9 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
243
244
  displayName: string | undefined;
244
245
  tracks: any[];
245
246
  minimized: boolean;
246
- displayedRegions: Region[];
247
247
  offsetPx: number;
248
248
  bpPerPx: number;
249
+ displayedRegions: Region[];
249
250
  hideHeader: boolean;
250
251
  hideHeaderOverview: boolean;
251
252
  hideNoTracksActive: boolean;
@@ -136,10 +136,14 @@ function stateModelFactory(pluginManager) {
136
136
  ...new Set(self.displayedRegions.map(region => region.assemblyName)),
137
137
  ];
138
138
  },
139
+ get isTopLevelView() {
140
+ const session = (0, util_1.getSession)(self);
141
+ return session.views.find(r => r.id === self.id);
142
+ },
139
143
  get stickyViewHeaders() {
140
144
  const session = (0, util_1.getSession)(self);
141
145
  return (0, product_core_1.isSessionWithMultipleViews)(session)
142
- ? session.stickyViewHeaders
146
+ ? this.isTopLevelView && session.stickyViewHeaders
143
147
  : false;
144
148
  },
145
149
  get rubberbandTop() {
@@ -262,12 +266,6 @@ function stateModelFactory(pluginManager) {
262
266
  return self.tracks.find(t => t.configuration.trackId === id);
263
267
  },
264
268
  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
269
  return results;
272
270
  },
273
271
  rewriteOnClicks(trackType, viewMenuActions) {
@@ -840,14 +838,14 @@ function stateModelFactory(pluginManager) {
840
838
  moveTo(start, end) {
841
839
  (0, Base1DUtils_1.moveTo)(self, start, end);
842
840
  },
843
- async navToLocString(input, optAssemblyName) {
841
+ async navToLocString(input, optAssemblyName, grow) {
844
842
  const { assemblyNames } = self;
845
843
  const { assemblyManager } = (0, util_1.getSession)(self);
846
844
  const assemblyName = optAssemblyName || assemblyNames[0];
847
845
  if (assemblyName) {
848
846
  await assemblyManager.waitForAssembly(assemblyName);
849
847
  }
850
- return this.navToLocations((0, util_2.parseLocStrings)(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName);
848
+ return this.navToLocations((0, util_2.parseLocStrings)(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName, grow);
851
849
  },
852
850
  async navToSearchString({ input, assembly, }) {
853
851
  await (0, searchUtils_1.handleSelectedRegion)({
@@ -856,13 +854,18 @@ function stateModelFactory(pluginManager) {
856
854
  model: self,
857
855
  });
858
856
  },
859
- async navToLocation(parsedLocString, assemblyName) {
860
- return this.navToLocations([parsedLocString], assemblyName);
857
+ async navToLocation(parsedLocString, assemblyName, grow) {
858
+ return this.navToLocations([parsedLocString], assemblyName, grow);
861
859
  },
862
- async navToLocations(parsedLocStrings, assemblyName) {
860
+ async navToLocations(regions, assemblyName, grow) {
863
861
  const { assemblyManager } = (0, util_1.getSession)(self);
864
862
  await (0, mobx_1.when)(() => self.volatileWidth !== undefined);
865
- const locations = await (0, util_2.generateLocations)(parsedLocStrings, assemblyManager, assemblyName);
863
+ const locations = await (0, util_2.generateLocations)({
864
+ regions,
865
+ assemblyManager,
866
+ assemblyName,
867
+ grow,
868
+ });
866
869
  if (locations.length === 1) {
867
870
  const loc = locations[0];
868
871
  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) {
@@ -14,7 +14,7 @@ async function navToOption({ option, model, assemblyName, }) {
14
14
  const location = option.getLocation();
15
15
  const trackId = option.getTrackId();
16
16
  if (location) {
17
- await model.navToLocString(location, assemblyName);
17
+ await model.navToLocString(location, assemblyName, 0.2);
18
18
  if (trackId) {
19
19
  model.showTrack(trackId);
20
20
  }
@@ -1,9 +1,9 @@
1
1
  import { lazy } from 'react';
2
- import { ConfigurationReference } from '@jbrowse/core/configuration';
2
+ import { ConfigurationReference, getConf } from '@jbrowse/core/configuration';
3
3
  import { BaseDisplay } from '@jbrowse/core/pluggableElementTypes/models';
4
4
  import { getContainingTrack, getContainingView, getSession, isFeature, isSelectionContainer, isSessionModelWithWidgets, } from '@jbrowse/core/util';
5
5
  import CompositeMap from '@jbrowse/core/util/compositeMap';
6
- import { getParentRenderProps } from '@jbrowse/core/util/tracks';
6
+ import { getParentRenderProps, getRpcSessionId, } from '@jbrowse/core/util/tracks';
7
7
  import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong';
8
8
  import MenuOpenIcon from '@mui/icons-material/MenuOpen';
9
9
  import { autorun } from 'mobx';
@@ -102,12 +102,28 @@ function stateModelFactory() {
102
102
  selectFeature(feature) {
103
103
  const session = getSession(self);
104
104
  if (isSessionModelWithWidgets(session)) {
105
- const featureWidget = session.addWidget('BaseFeatureWidget', 'baseFeature', {
106
- view: getContainingView(self),
107
- track: getContainingTrack(self),
108
- featureData: feature.toJSON(),
109
- });
110
- session.showWidget(featureWidget);
105
+ const { rpcManager } = session;
106
+ const sessionId = getRpcSessionId(self);
107
+ const track = getContainingTrack(self);
108
+ const view = getContainingView(self);
109
+ const adapterConfig = getConf(track, 'adapter');
110
+ (async () => {
111
+ try {
112
+ const descriptions = await rpcManager.call(sessionId, 'CoreGetMetadata', {
113
+ adapterConfig,
114
+ });
115
+ session.showWidget(session.addWidget('BaseFeatureWidget', 'baseFeature', {
116
+ featureData: feature.toJSON(),
117
+ view,
118
+ track,
119
+ descriptions,
120
+ }));
121
+ }
122
+ catch (e) {
123
+ console.error(e);
124
+ getSession(e).notifyError(`${e}`, e);
125
+ }
126
+ })();
111
127
  }
112
128
  if (isSelectionContainer(session)) {
113
129
  session.setSelection(feature);
@@ -9,14 +9,14 @@ function rightRoundedRect(x, y, width, height, radius) {
9
9
  function leftRoundedRect(x, y, width, height, radius) {
10
10
  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`;
11
11
  }
12
- function leftTriangle(x, y, width, height) {
12
+ function leftTriangle(x, _y, width, height) {
13
13
  return [
14
14
  [x, 0],
15
15
  [x + width, height / 2],
16
16
  [x, height],
17
17
  ].toString();
18
18
  }
19
- function rightTriangle(x, y, width, height) {
19
+ function rightTriangle(x, _y, width, height) {
20
20
  return [
21
21
  [x, height / 2],
22
22
  [x + width, 0],
@@ -40,14 +40,28 @@ const Cytobands = observer(function ({ overview, block, assembly, }) {
40
40
  const rcap = reversed ? 0 : cytobands.length - 1;
41
41
  const h = HEADER_OVERVIEW_HEIGHT;
42
42
  let centromereSeen = false;
43
+ let curr = '';
44
+ let idx = 0;
43
45
  return (_jsx("g", { transform: `translate(-${offsetPx})`, children: cytobands.map((args, index) => {
44
46
  const k = JSON.stringify(args);
45
- const { refName, type, start, end } = args;
47
+ const { refName, name, type, start, end } = args;
46
48
  const s = overview.bpToPx({ refName, coord: start }) || 0;
47
49
  const e = overview.bpToPx({ refName, coord: end }) || 0;
48
50
  const l = Math.min(s, e);
49
51
  const w = Math.abs(e - s);
50
- const c = colorMap[type] || 'black';
52
+ if (type === 'n/a') {
53
+ const match = name === null || name === void 0 ? void 0 : name.match(/^(\d+)([A-Za-z])/);
54
+ const ret = match[1] + match[2];
55
+ if (ret && ret !== curr) {
56
+ curr = ret;
57
+ idx++;
58
+ }
59
+ }
60
+ const c = type === 'n/a'
61
+ ? idx % 2
62
+ ? 'black'
63
+ : '#a77'
64
+ : colorMap[type] || 'black';
51
65
  if (type === 'acen' && !centromereSeen) {
52
66
  centromereSeen = true;
53
67
  return (_jsx("polygon", { points: reversed
@@ -28,8 +28,9 @@ async function fetchSequence(model, regions) {
28
28
  if (!leftOffset || !rightOffset) {
29
29
  throw new Error('no offsets on model to use for range');
30
30
  }
31
- if (leftOffset.assemblyName !== rightOffset.assemblyName) {
32
- throw new Error('not able to fetch sequences from multiple assemblies');
31
+ const assemblyNames = new Set(regions.map(r => r.assemblyName));
32
+ if (assemblyNames.size > 1) {
33
+ throw new Error('not able to fetch sequences from multiple assemblies currently');
33
34
  }
34
35
  const { rpcManager, assemblyManager } = session;
35
36
  const assemblyName = leftOffset.assemblyName || rightOffset.assemblyName || '';
@@ -10,9 +10,6 @@ const useStyles = makeStyles()(theme => ({
10
10
  color: theme.palette.text.primary,
11
11
  margin: SPACING,
12
12
  },
13
- buttonSpacer: {
14
- marginRight: theme.spacing(2),
15
- },
16
13
  }));
17
14
  export default function HeaderPanControls({ model }) {
18
15
  const { classes } = useStyles();
@@ -12,6 +12,7 @@ const useStyles = makeStyles()(theme => ({
12
12
  position: 'absolute',
13
13
  right: 0,
14
14
  background: theme.palette.background.paper,
15
+ zIndex: 2,
15
16
  },
16
17
  focusedBackground: {
17
18
  background: alpha(theme.palette.secondary.light, 0.2),
@@ -12,12 +12,6 @@ const useStyles = makeStyles()({
12
12
  width: '100%',
13
13
  minHeight: 8,
14
14
  },
15
- guide: {
16
- pointerEvents: 'none',
17
- height: '100%',
18
- width: 1,
19
- position: 'absolute',
20
- },
21
15
  rel: {
22
16
  position: 'relative',
23
17
  },
@@ -4,20 +4,12 @@ import { Tooltip } from '@mui/material';
4
4
  import { observer } from 'mobx-react';
5
5
  import { makeStyles } from 'tss-react/mui';
6
6
  const useStyles = makeStyles()({
7
- rubberbandControl: {
8
- cursor: 'crosshair',
9
- width: '100%',
10
- minHeight: 8,
11
- },
12
7
  guide: {
13
8
  pointerEvents: 'none',
14
9
  height: '100%',
15
10
  width: 1,
16
11
  position: 'absolute',
17
12
  },
18
- rel: {
19
- position: 'relative',
20
- },
21
13
  });
22
14
  const OverviewRubberbandHoverTooltip = observer(function ({ model, open, guideX, overview, }) {
23
15
  var _a;
@@ -29,6 +21,6 @@ const OverviewRubberbandHoverTooltip = observer(function ({ model, open, guideX,
29
21
  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') &&
30
22
  px.coord < f.get('end') &&
31
23
  px.refName === assembly.getCanonicalRefName(f.get('refName')));
32
- return (_jsx(Tooltip, { open: open, placement: "top", title: [stringify(px), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('name')].join(' '), arrow: true, children: _jsx("div", { className: classes.guide, style: { left: guideX } }) }));
24
+ return (_jsx(Tooltip, { open: open, placement: "top", title: [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: _jsx("div", { className: classes.guide, style: { left: guideX } }) }));
33
25
  });
34
26
  export default OverviewRubberbandHoverTooltip;
@@ -25,27 +25,31 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
25
25
  return;
26
26
  }
27
27
  setLoaded(false);
28
- const results = await fetchResults(debouncedSearch);
29
- setLoaded(true);
30
- setSearchOptions(getDeduplicatedResult(results));
28
+ setSearchOptions(getDeduplicatedResult(await fetchResults(debouncedSearch)));
31
29
  }
32
30
  catch (e) {
33
31
  console.error(e);
34
32
  session.notifyError(`${e}`, e);
35
33
  }
34
+ finally {
35
+ setLoaded(true);
36
+ }
36
37
  })();
37
38
  }, [assemblyName, fetchResults, debouncedSearch, session]);
38
39
  const inputBoxVal = coarseVisibleLocStrings || value || '';
39
- const width = Math.min(Math.max(measureText(inputBoxVal, 14) + 100, minWidth), maxWidth);
40
- const refNames = assembly === null || assembly === void 0 ? void 0 : assembly.refNames;
41
- const regionOptions = (refNames === null || refNames === void 0 ? void 0 : refNames.map(refName => ({
40
+ const regions = assembly === null || assembly === void 0 ? void 0 : assembly.regions;
41
+ const regionOptions = (regions === null || regions === void 0 ? void 0 : regions.map(region => ({
42
42
  result: new RefSequenceResult({
43
- refName,
44
- label: refName,
43
+ refName: region.refName,
44
+ label: region.refName,
45
+ displayString: region.refName,
45
46
  matchedAttribute: 'refName',
46
47
  }),
47
48
  }))) || [];
48
- return (_jsx(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) => {
49
+ return (_jsx(Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: {
50
+ ...style,
51
+ width: Math.min(Math.max(measureText(inputBoxVal, 14) + 100, minWidth), maxWidth),
52
+ }, value: inputBoxVal, loading: !loaded, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
49
53
  setInputValue(newInputValue);
50
54
  onChange === null || onChange === void 0 ? void 0 : onChange(newInputValue);
51
55
  }, loadingText: "loading results", open: open, onOpen: () => {
@@ -62,7 +66,9 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
62
66
  return;
63
67
  }
64
68
  if (typeof selectedOption === 'string') {
65
- onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResult({ label: selectedOption }));
69
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResult({
70
+ label: selectedOption,
71
+ }));
66
72
  }
67
73
  else {
68
74
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
@@ -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;
@@ -8,11 +8,33 @@ import EndAdornment from './RefNameAutocomplete/EndAdornment';
8
8
  import { fetchResults } from './util';
9
9
  import { handleSelectedRegion, navToOption } from '../../searchUtils';
10
10
  import { SPACING, WIDGET_HEIGHT } from '../consts';
11
- const useStyles = makeStyles()(() => ({
11
+ const useStyles = makeStyles()({
12
12
  headerRefName: {
13
13
  minWidth: 100,
14
14
  },
15
- }));
15
+ });
16
+ async function onSelect({ option, model, assemblyName, }) {
17
+ var _a;
18
+ const { assemblyManager } = getSession(model);
19
+ const assembly = assemblyManager.get(assemblyName);
20
+ if (option.hasLocation()) {
21
+ await navToOption({
22
+ option,
23
+ model,
24
+ assemblyName,
25
+ });
26
+ }
27
+ else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
28
+ model.setSearchResults(option.results, option.getLabel());
29
+ }
30
+ else if (assembly) {
31
+ await handleSelectedRegion({
32
+ input: option.getLabel(),
33
+ assembly,
34
+ model,
35
+ });
36
+ }
37
+ }
16
38
  const SearchBox = observer(function ({ model, showHelp = true, }) {
17
39
  const { classes } = useStyles();
18
40
  const theme = useTheme();
@@ -23,21 +45,12 @@ const SearchBox = observer(function ({ model, showHelp = true, }) {
23
45
  const assembly = assemblyManager.get(assemblyName);
24
46
  const searchScope = model.searchScope(assemblyName);
25
47
  return (_jsx(RefNameAutocomplete, { onSelect: async (option) => {
26
- var _a;
27
48
  try {
28
- if (option.hasLocation()) {
29
- await navToOption({ option, model, assemblyName });
30
- }
31
- else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
32
- model.setSearchResults(option.results, option.getLabel());
33
- }
34
- else if (assembly) {
35
- await handleSelectedRegion({
36
- input: option.getLabel(),
37
- assembly,
38
- model,
39
- });
40
- }
49
+ await onSelect({
50
+ model,
51
+ assemblyName,
52
+ option,
53
+ });
41
54
  }
42
55
  catch (e) {
43
56
  console.error(e);
@@ -12,6 +12,7 @@ import TrackLabelDragHandle from './TrackLabelDragHandle';
12
12
  import TrackLabelMenu from './TrackLabelMenu';
13
13
  const useStyles = makeStyles()(theme => ({
14
14
  root: {
15
+ zIndex: 200,
15
16
  background: alpha(theme.palette.background.paper, 0.8),
16
17
  '&:hover': {
17
18
  background: theme.palette.background.paper,
@@ -17,16 +17,9 @@ const TrackLabelMenu = observer(function ({ track, }) {
17
17
  const trackConf = track.configuration;
18
18
  const minimized = track.minimized;
19
19
  const pinned = track.pinned;
20
- let lgvHasParentView;
21
- try {
22
- getContainingView(view);
23
- lgvHasParentView = true;
24
- }
25
- catch (error) {
26
- lgvHasParentView = false;
27
- }
20
+ const { isTopLevelView } = view;
28
21
  const items = [
29
- ...(lgvHasParentView
22
+ ...(isTopLevelView
30
23
  ? []
31
24
  : [
32
25
  {
@@ -1,14 +1,25 @@
1
1
  import { useEffect, useRef } from 'react';
2
+ import { sum } from '@jbrowse/core/util';
2
3
  export function useWheelScroll(ref, model) {
3
4
  const delta = useRef(0);
4
5
  const timeout = useRef(null);
5
6
  const scheduled = useRef(false);
6
7
  useEffect(() => {
8
+ let samples = [];
7
9
  const curr = ref.current;
8
10
  function onWheel(event) {
9
11
  if (event.ctrlKey) {
10
12
  event.preventDefault();
11
- delta.current += event.deltaY / 500;
13
+ samples.push(event.deltaY);
14
+ const averageDeltaY = Math.abs(sum(samples)) / samples.length;
15
+ const normalizer = averageDeltaY < 6
16
+ ? 25
17
+ : averageDeltaY > 30
18
+ ? averageDeltaY > 150
19
+ ? 500
20
+ : 150
21
+ : 75;
22
+ delta.current += event.deltaY / normalizer;
12
23
  model.setScaleFactor(delta.current < 0 ? 1 - delta.current : 1 / (1 + delta.current));
13
24
  if (timeout.current) {
14
25
  clearTimeout(timeout.current);
@@ -19,6 +30,7 @@ export function useWheelScroll(ref, model) {
19
30
  ? model.bpPerPx * (1 + delta.current)
20
31
  : model.bpPerPx / (1 - delta.current), event.clientX - ((curr === null || curr === void 0 ? void 0 : curr.getBoundingClientRect().left) || 0));
21
32
  delta.current = 0;
33
+ samples = [];
22
34
  }, 300);
23
35
  }
24
36
  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
  }[];
@@ -5,12 +5,14 @@ export async function fetchResults({ queryString, searchType, searchScope, rankS
5
5
  if (!textSearchManager) {
6
6
  console.warn('No text search manager');
7
7
  }
8
- const textSearchResults = await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
8
+ const textSearchResults = (await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
9
9
  queryString,
10
10
  searchType,
11
- }, searchScope, rankSearchResults));
12
- 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 BaseResult({ label: r }));
13
- return dedupe([...(refNameResults || []), ...(textSearchResults || [])], elt => elt.getId());
11
+ }, searchScope, rankSearchResults))) || [];
12
+ 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 BaseResult({
13
+ label: r,
14
+ }))) || [];
15
+ return dedupe([...refNameResults, ...textSearchResults], elt => elt.getId());
14
16
  }
15
17
  export function splitLast(str, split) {
16
18
  const lastIndex = str.lastIndexOf(split);
@@ -33,5 +35,6 @@ export function getCytobands(assembly, refName) {
33
35
  start: f.get('start'),
34
36
  end: f.get('end'),
35
37
  type: f.get('gieStain'),
38
+ name: f.get('name'),
36
39
  })).filter(f => f.refName === refName)) || []);
37
40
  }
@@ -58,7 +58,8 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
58
58
  readonly width: number;
59
59
  readonly interRegionPaddingWidth: number;
60
60
  readonly assemblyNames: string[];
61
- readonly stickyViewHeaders: boolean;
61
+ readonly isTopLevelView: import("@jbrowse/core/util").AbstractViewModel | undefined;
62
+ readonly stickyViewHeaders: boolean | undefined;
62
63
  readonly rubberbandTop: number;
63
64
  readonly pinnedTracksTop: number;
64
65
  } & {
@@ -169,13 +170,13 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
169
170
  setCoarseDynamicBlocks(blocks: BlockSet): void;
170
171
  } & {
171
172
  moveTo(start?: BpOffset, end?: BpOffset): void;
172
- navToLocString(input: string, optAssemblyName?: string): Promise<void>;
173
+ navToLocString(input: string, optAssemblyName?: string, grow?: number): Promise<void>;
173
174
  navToSearchString({ input, assembly, }: {
174
175
  input: string;
175
176
  assembly: Assembly;
176
177
  }): Promise<void>;
177
- navToLocation(parsedLocString: ParsedLocString, assemblyName?: string): Promise<void>;
178
- navToLocations(parsedLocStrings: ParsedLocString[], assemblyName?: string): Promise<void>;
178
+ navToLocation(parsedLocString: ParsedLocString, assemblyName?: string, grow?: number): Promise<void>;
179
+ navToLocations(regions: ParsedLocString[], assemblyName?: string, grow?: number): Promise<void>;
179
180
  navTo(query: NavLocation): void;
180
181
  navToMultiple(locations: NavLocation[]): void;
181
182
  } & {
@@ -243,9 +244,9 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
243
244
  displayName: string | undefined;
244
245
  tracks: any[];
245
246
  minimized: boolean;
246
- displayedRegions: Region[];
247
247
  offsetPx: number;
248
248
  bpPerPx: number;
249
+ displayedRegions: Region[];
249
250
  hideHeader: boolean;
250
251
  hideHeaderOverview: boolean;
251
252
  hideNoTracksActive: boolean;
@@ -96,10 +96,14 @@ export function stateModelFactory(pluginManager) {
96
96
  ...new Set(self.displayedRegions.map(region => region.assemblyName)),
97
97
  ];
98
98
  },
99
+ get isTopLevelView() {
100
+ const session = getSession(self);
101
+ return session.views.find(r => r.id === self.id);
102
+ },
99
103
  get stickyViewHeaders() {
100
104
  const session = getSession(self);
101
105
  return isSessionWithMultipleViews(session)
102
- ? session.stickyViewHeaders
106
+ ? this.isTopLevelView && session.stickyViewHeaders
103
107
  : false;
104
108
  },
105
109
  get rubberbandTop() {
@@ -222,12 +226,6 @@ export function stateModelFactory(pluginManager) {
222
226
  return self.tracks.find(t => t.configuration.trackId === id);
223
227
  },
224
228
  rankSearchResults(results) {
225
- const openTrackIds = new Set(self.tracks.map(track => track.configuration.trackId));
226
- for (const result of results) {
227
- if (openTrackIds.has(result.trackId)) {
228
- result.updateScore(result.getScore() + 1);
229
- }
230
- }
231
229
  return results;
232
230
  },
233
231
  rewriteOnClicks(trackType, viewMenuActions) {
@@ -800,14 +798,14 @@ export function stateModelFactory(pluginManager) {
800
798
  moveTo(start, end) {
801
799
  moveTo(self, start, end);
802
800
  },
803
- async navToLocString(input, optAssemblyName) {
801
+ async navToLocString(input, optAssemblyName, grow) {
804
802
  const { assemblyNames } = self;
805
803
  const { assemblyManager } = getSession(self);
806
804
  const assemblyName = optAssemblyName || assemblyNames[0];
807
805
  if (assemblyName) {
808
806
  await assemblyManager.waitForAssembly(assemblyName);
809
807
  }
810
- return this.navToLocations(parseLocStrings(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName);
808
+ return this.navToLocations(parseLocStrings(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName, grow);
811
809
  },
812
810
  async navToSearchString({ input, assembly, }) {
813
811
  await handleSelectedRegion({
@@ -816,13 +814,18 @@ export function stateModelFactory(pluginManager) {
816
814
  model: self,
817
815
  });
818
816
  },
819
- async navToLocation(parsedLocString, assemblyName) {
820
- return this.navToLocations([parsedLocString], assemblyName);
817
+ async navToLocation(parsedLocString, assemblyName, grow) {
818
+ return this.navToLocations([parsedLocString], assemblyName, grow);
821
819
  },
822
- async navToLocations(parsedLocStrings, assemblyName) {
820
+ async navToLocations(regions, assemblyName, grow) {
823
821
  const { assemblyManager } = getSession(self);
824
822
  await when(() => self.volatileWidth !== undefined);
825
- const locations = await generateLocations(parsedLocStrings, assemblyManager, assemblyName);
823
+ const locations = await generateLocations({
824
+ regions,
825
+ assemblyManager,
826
+ assemblyName,
827
+ grow,
828
+ });
826
829
  if (locations.length === 1) {
827
830
  const loc = locations[0];
828
831
  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;
@@ -50,7 +50,7 @@ export function makeTicks(start, end, bpPerPx, emitMajor = true, emitMinor = tru
50
50
  }
51
51
  return ticks;
52
52
  }
53
- export async function generateLocations(regions, assemblyManager, assemblyName) {
53
+ export async function generateLocations({ regions, assemblyManager, assemblyName, grow, }) {
54
54
  return Promise.all(regions.map(async (region) => {
55
55
  const asmName = region.assemblyName || assemblyName;
56
56
  if (!asmName) {
@@ -73,11 +73,25 @@ export async function generateLocations(regions, assemblyManager, assemblyName)
73
73
  if (!parentRegion) {
74
74
  throw new Error(`Could not find refName ${refName} in ${asmName}`);
75
75
  }
76
- return {
77
- ...region,
78
- assemblyName: asmName,
79
- parentRegion,
80
- };
76
+ const { start, end } = region;
77
+ if (grow && start && end) {
78
+ const len = end - start;
79
+ const margin = len * grow;
80
+ return {
81
+ ...region,
82
+ start: Math.max(0, start - margin),
83
+ end: end + margin,
84
+ assemblyName: asmName,
85
+ parentRegion,
86
+ };
87
+ }
88
+ else {
89
+ return {
90
+ ...region,
91
+ assemblyName: asmName,
92
+ parentRegion,
93
+ };
94
+ }
81
95
  }));
82
96
  }
83
97
  export function parseLocStrings(input, assemblyName, isValidRefName) {
@@ -4,7 +4,7 @@ export async function navToOption({ option, model, assemblyName, }) {
4
4
  const location = option.getLocation();
5
5
  const trackId = option.getTrackId();
6
6
  if (location) {
7
- await model.navToLocString(location, assemblyName);
7
+ await model.navToLocString(location, assemblyName, 0.2);
8
8
  if (trackId) {
9
9
  model.showTrack(trackId);
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-linear-genome-view",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "description": "JBrowse 2 linear genome view",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -38,7 +38,7 @@
38
38
  "useSrc": "node ../../scripts/useSrc.js"
39
39
  },
40
40
  "dependencies": {
41
- "@jbrowse/core": "^3.3.0",
41
+ "@jbrowse/core": "^3.5.0",
42
42
  "@mui/icons-material": "^7.0.0",
43
43
  "@mui/material": "^7.0.0",
44
44
  "@types/file-saver": "^2.0.1",
@@ -58,5 +58,5 @@
58
58
  "access": "public"
59
59
  },
60
60
  "module": "esm/index.js",
61
- "gitHead": "0bb64d8cc7ecdd167515308b31eec3d9acbc59e4"
61
+ "gitHead": "8a8aa0aab2229dece106a5715a767e649e2fe92b"
62
62
  }