@jbrowse/plugin-linear-genome-view 3.1.0 → 3.3.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 (39) hide show
  1. package/dist/BaseLinearDisplay/components/LinearBlocks.js +1 -1
  2. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +9 -9
  3. package/dist/LaunchLinearGenomeView/index.js +9 -5
  4. package/dist/LinearBasicDisplay/components/AddFiltersDialog.js +1 -1
  5. package/dist/LinearBasicDisplay/model.js +8 -2
  6. package/dist/LinearGenomeView/components/HeaderZoomControls.js +8 -2
  7. package/dist/LinearGenomeView/components/ImportForm.js +1 -1
  8. package/dist/LinearGenomeView/components/OverviewRubberband.js +2 -14
  9. package/dist/LinearGenomeView/components/OverviewRubberbandHoverTooltip.d.ts +10 -0
  10. package/dist/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +36 -0
  11. package/dist/LinearGenomeView/components/Rubberband.js +3 -21
  12. package/dist/LinearGenomeView/components/RubberbandSpan.js +7 -21
  13. package/dist/LinearGenomeView/components/RubberbandTooltip.d.ts +5 -0
  14. package/dist/LinearGenomeView/components/RubberbandTooltip.js +28 -0
  15. package/dist/LinearGenomeView/components/TracksContainer.js +2 -2
  16. package/dist/LinearGenomeView/components/VerticalGuide.js +3 -21
  17. package/dist/LinearGenomeView/model.d.ts +28 -3
  18. package/dist/LinearGenomeView/model.js +62 -26
  19. package/dist/LinearGenomeView/types.d.ts +5 -0
  20. package/esm/BaseLinearDisplay/components/LinearBlocks.js +1 -1
  21. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +9 -9
  22. package/esm/LaunchLinearGenomeView/index.js +9 -5
  23. package/esm/LinearBasicDisplay/components/AddFiltersDialog.js +1 -1
  24. package/esm/LinearBasicDisplay/model.js +8 -2
  25. package/esm/LinearGenomeView/components/HeaderZoomControls.js +9 -3
  26. package/esm/LinearGenomeView/components/ImportForm.js +1 -1
  27. package/esm/LinearGenomeView/components/OverviewRubberband.js +3 -15
  28. package/esm/LinearGenomeView/components/OverviewRubberbandHoverTooltip.d.ts +10 -0
  29. package/esm/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +34 -0
  30. package/esm/LinearGenomeView/components/Rubberband.js +4 -22
  31. package/esm/LinearGenomeView/components/RubberbandSpan.js +6 -23
  32. package/esm/LinearGenomeView/components/RubberbandTooltip.d.ts +5 -0
  33. package/esm/LinearGenomeView/components/RubberbandTooltip.js +25 -0
  34. package/esm/LinearGenomeView/components/TracksContainer.js +2 -2
  35. package/esm/LinearGenomeView/components/VerticalGuide.js +4 -22
  36. package/esm/LinearGenomeView/model.d.ts +28 -3
  37. package/esm/LinearGenomeView/model.js +62 -26
  38. package/esm/LinearGenomeView/types.d.ts +5 -0
  39. package/package.json +5 -5
@@ -93,6 +93,7 @@ function stateModelFactory(pluginManager) {
93
93
  highlight: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.array(mobx_state_tree_1.types.frozen()), []),
94
94
  colorByCDS: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.boolean, () => (0, util_1.localStorageGetBoolean)('lgv-colorByCDS', false)),
95
95
  showTrackOutlines: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.boolean, () => (0, util_1.localStorageGetBoolean)('lgv-showTrackOutlines', true)),
96
+ init: mobx_state_tree_1.types.frozen(),
96
97
  }))
97
98
  .volatile(() => ({
98
99
  volatileWidth: undefined,
@@ -141,10 +142,10 @@ function stateModelFactory(pluginManager) {
141
142
  ? session.stickyViewHeaders
142
143
  : false;
143
144
  },
144
- get pinnedTracksTop() {
145
+ get rubberbandTop() {
145
146
  let pinnedTracksTop = 0;
146
147
  if (this.stickyViewHeaders) {
147
- pinnedTracksTop = ui_1.VIEW_HEADER_HEIGHT + consts_1.SCALE_BAR_HEIGHT;
148
+ pinnedTracksTop = ui_1.VIEW_HEADER_HEIGHT;
148
149
  if (!self.hideHeader) {
149
150
  pinnedTracksTop += consts_1.HEADER_BAR_HEIGHT;
150
151
  if (!self.hideHeaderOverview) {
@@ -154,6 +155,9 @@ function stateModelFactory(pluginManager) {
154
155
  }
155
156
  return pinnedTracksTop;
156
157
  },
158
+ get pinnedTracksTop() {
159
+ return this.rubberbandTop + consts_1.SCALE_BAR_HEIGHT;
160
+ },
157
161
  }))
158
162
  .views(self => ({
159
163
  scaleBarDisplayPrefix() {
@@ -267,32 +271,32 @@ function stateModelFactory(pluginManager) {
267
271
  return results;
268
272
  },
269
273
  rewriteOnClicks(trackType, viewMenuActions) {
270
- viewMenuActions.forEach(action => {
274
+ for (const action of viewMenuActions) {
271
275
  if ('subMenu' in action) {
272
276
  this.rewriteOnClicks(trackType, action.subMenu);
273
277
  }
274
278
  if ('onClick' in action) {
275
279
  const holdOnClick = action.onClick;
276
280
  action.onClick = (...args) => {
277
- self.tracks.forEach(track => {
281
+ for (const track of self.tracks) {
278
282
  if (track.type === trackType) {
279
283
  holdOnClick.apply(track, [track, ...args]);
280
284
  }
281
- });
285
+ }
282
286
  };
283
287
  }
284
- });
288
+ }
285
289
  },
286
290
  get trackTypeActions() {
287
291
  const allActions = new Map();
288
- self.tracks.forEach(track => {
292
+ for (const track of self.tracks) {
289
293
  const trackInMap = allActions.get(track.type);
290
294
  if (!trackInMap) {
291
295
  const viewMenuActions = structuredClone(track.viewMenuActions);
292
296
  this.rewriteOnClicks(track.type, viewMenuActions);
293
297
  allActions.set(track.type, viewMenuActions);
294
298
  }
295
- });
299
+ }
296
300
  return allActions;
297
301
  },
298
302
  }))
@@ -417,11 +421,13 @@ function stateModelFactory(pluginManager) {
417
421
  hideTrack(trackId) {
418
422
  const schema = pluginManager.pluggableConfigSchemaType('track');
419
423
  const conf = (0, mobx_state_tree_1.resolveIdentifier)(schema, (0, mobx_state_tree_1.getRoot)(self), trackId);
420
- const t = self.tracks.filter(t => t.configuration === conf);
424
+ const tracks = self.tracks.filter(t => t.configuration === conf);
421
425
  (0, mobx_1.transaction)(() => {
422
- t.forEach(t => self.tracks.remove(t));
426
+ for (const track of tracks) {
427
+ self.tracks.remove(track);
428
+ }
423
429
  });
424
- return t.length;
430
+ return tracks.length;
425
431
  },
426
432
  }))
427
433
  .actions(self => ({
@@ -562,6 +568,9 @@ function stateModelFactory(pluginManager) {
562
568
  self.scrollTo(0);
563
569
  self.zoomTo(10);
564
570
  },
571
+ setInit(arg) {
572
+ self.init = arg;
573
+ },
565
574
  async exportSvg(opts = {}) {
566
575
  const { renderToSvg } = await Promise.resolve().then(() => __importStar(require('./svgcomponents/SVGLinearGenomeView')));
567
576
  const html = await renderToSvg(self, opts);
@@ -782,7 +791,9 @@ function stateModelFactory(pluginManager) {
782
791
  for (const [key, value] of self.trackTypeActions.entries()) {
783
792
  if (value.length) {
784
793
  menuItems.push({ type: 'divider' }, { type: 'subHeader', label: key });
785
- value.forEach(action => menuItems.push(action));
794
+ for (const action of value) {
795
+ menuItems.push(action);
796
+ }
786
797
  }
787
798
  }
788
799
  return menuItems;
@@ -824,20 +835,6 @@ function stateModelFactory(pluginManager) {
824
835
  self.coarseDynamicBlocks = blocks.contentBlocks;
825
836
  self.coarseTotalBp = blocks.totalBp;
826
837
  },
827
- afterAttach() {
828
- (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
829
- if (self.initialized) {
830
- this.setCoarseDynamicBlocks(self.dynamicBlocks);
831
- }
832
- }, { delay: 150 }));
833
- (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
834
- const s = (s) => JSON.stringify(s);
835
- const { showCytobandsSetting, showCenterLine, colorByCDS } = self;
836
- (0, util_1.localStorageSetItem)('lgv-showCytobands', s(showCytobandsSetting));
837
- (0, util_1.localStorageSetItem)('lgv-showCenterLine', s(showCenterLine));
838
- (0, util_1.localStorageSetItem)('lgv-colorByCDS', s(colorByCDS));
839
- }));
840
- },
841
838
  }))
842
839
  .actions(self => ({
843
840
  moveTo(start, end) {
@@ -859,6 +856,9 @@ function stateModelFactory(pluginManager) {
859
856
  model: self,
860
857
  });
861
858
  },
859
+ async navToLocation(parsedLocString, assemblyName) {
860
+ return this.navToLocations([parsedLocString], assemblyName);
861
+ },
862
862
  async navToLocations(parsedLocStrings, assemblyName) {
863
863
  const { assemblyManager } = (0, util_1.getSession)(self);
864
864
  await (0, mobx_1.when)(() => self.volatileWidth !== undefined);
@@ -1004,6 +1004,33 @@ function stateModelFactory(pluginManager) {
1004
1004
  document.removeEventListener('keydown', handler);
1005
1005
  });
1006
1006
  },
1007
+ afterAttach() {
1008
+ (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
1009
+ var _a;
1010
+ const { init } = self;
1011
+ if (init) {
1012
+ self
1013
+ .navToLocString(init.loc, init.assembly)
1014
+ .catch((e) => {
1015
+ (0, util_1.getSession)(self).notifyError(`${e}`, e);
1016
+ });
1017
+ (_a = init.tracks) === null || _a === void 0 ? void 0 : _a.map(t => self.showTrack(t));
1018
+ self.setInit(undefined);
1019
+ }
1020
+ }));
1021
+ (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
1022
+ if (self.initialized) {
1023
+ self.setCoarseDynamicBlocks(self.dynamicBlocks);
1024
+ }
1025
+ }, { delay: 150 }));
1026
+ (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
1027
+ const s = (s) => JSON.stringify(s);
1028
+ const { showCytobandsSetting, showCenterLine, colorByCDS } = self;
1029
+ (0, util_1.localStorageSetItem)('lgv-showCytobands', s(showCytobandsSetting));
1030
+ (0, util_1.localStorageSetItem)('lgv-showCenterLine', s(showCenterLine));
1031
+ (0, util_1.localStorageSetItem)('lgv-colorByCDS', s(colorByCDS));
1032
+ }));
1033
+ },
1007
1034
  }))
1008
1035
  .preProcessSnapshot(snap => {
1009
1036
  if (!snap) {
@@ -1016,6 +1043,15 @@ function stateModelFactory(pluginManager) {
1016
1043
  : [highlight],
1017
1044
  ...rest,
1018
1045
  };
1046
+ })
1047
+ .postProcessSnapshot(snap => {
1048
+ if (!snap) {
1049
+ return snap;
1050
+ }
1051
+ else {
1052
+ const { init, ...rest } = snap;
1053
+ return rest;
1054
+ }
1019
1055
  });
1020
1056
  }
1021
1057
  var LinearGenomeView_1 = require("./components/LinearGenomeView");
@@ -36,3 +36,8 @@ export interface NavLocation {
36
36
  end?: number;
37
37
  assemblyName?: string;
38
38
  }
39
+ export interface InitState {
40
+ loc: string;
41
+ assembly: string;
42
+ tracks?: string[];
43
+ }
@@ -20,7 +20,7 @@ const useStyles = makeStyles()({
20
20
  width: '100%',
21
21
  fontWeight: 'bold',
22
22
  textAlign: 'center',
23
- zIndex: 2000,
23
+ zIndex: 999,
24
24
  boxSizing: 'border-box',
25
25
  },
26
26
  });
@@ -78,14 +78,14 @@ function stateModelFactory() {
78
78
  return (_b = (_a = self.blockState.get(blockKey)) === null || _a === void 0 ? void 0 : _a.layout) === null || _b === void 0 ? void 0 : _b.getByID(id);
79
79
  },
80
80
  searchFeatureByID(id) {
81
+ var _a;
81
82
  let ret;
82
- self.blockState.forEach(block => {
83
- var _a;
83
+ for (const block of self.blockState.values()) {
84
84
  const val = (_a = block.layout) === null || _a === void 0 ? void 0 : _a.getByID(id);
85
85
  if (val) {
86
86
  ret = val;
87
87
  }
88
- });
88
+ }
89
89
  return ret;
90
90
  },
91
91
  }))
@@ -138,9 +138,9 @@ function stateModelFactory() {
138
138
  self.setError();
139
139
  self.setCurrStatsBpPerPx(0);
140
140
  self.clearFeatureDensityStats();
141
- [...self.blockState.values()].forEach(val => {
141
+ for (const val of self.blockState.values()) {
142
142
  val.doReload();
143
- });
143
+ }
144
144
  superReload();
145
145
  },
146
146
  };
@@ -230,17 +230,17 @@ function stateModelFactory() {
230
230
  if (!view.initialized) {
231
231
  return;
232
232
  }
233
- self.blockDefinitions.contentBlocks.forEach(block => {
233
+ for (const block of self.blockDefinitions.contentBlocks) {
234
234
  blocksPresent[block.key] = true;
235
235
  if (!self.blockState.has(block.key)) {
236
236
  self.addBlock(block.key, block);
237
237
  }
238
- });
239
- self.blockState.forEach((_, key) => {
238
+ }
239
+ for (const key of self.blockState.keys()) {
240
240
  if (!blocksPresent[key]) {
241
241
  self.deleteBlock(key);
242
242
  }
243
- });
243
+ }
244
244
  }));
245
245
  },
246
246
  }))
@@ -20,7 +20,7 @@ export default function LaunchLinearGenomeViewF(pluginManager) {
20
20
  view.setHideHeader(!nav);
21
21
  }
22
22
  if (highlight !== undefined) {
23
- highlight.forEach(async (h) => {
23
+ for (const h of highlight) {
24
24
  const p = parseLocString(h, refName => assemblyManager.isValidRefName(refName, assembly));
25
25
  const { start, end } = p;
26
26
  if (start !== undefined && end !== undefined) {
@@ -31,13 +31,17 @@ export default function LaunchLinearGenomeViewF(pluginManager) {
31
31
  assemblyName: assembly,
32
32
  });
33
33
  }
34
- });
34
+ }
35
35
  }
36
- await handleSelectedRegion({ input: loc, model: view, assembly: asm });
36
+ await handleSelectedRegion({
37
+ input: loc,
38
+ model: view,
39
+ assembly: asm,
40
+ });
37
41
  const idsNotFound = [];
38
- tracks.forEach(track => {
42
+ for (const track of tracks) {
39
43
  tryTrack(view, track, idsNotFound);
40
- });
44
+ }
41
45
  if (idsNotFound.length) {
42
46
  throw new Error(`Could not resolve identifiers: ${idsNotFound.join(',')}`);
43
47
  }
@@ -41,7 +41,7 @@ const AddFiltersDialog = observer(function ({ model, handleClose, }) {
41
41
  setError(e);
42
42
  }
43
43
  }, [data]);
44
- return (_jsxs(Dialog, { maxWidth: "xl", open: true, onClose: handleClose, title: "Add track filters", children: [_jsxs(DialogContent, { children: [_jsxs("div", { children: ["Add filters, in jexl format, one per line, starting with the string jexl:. Examples:", ' ', _jsxs("ul", { children: [_jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'name')=='BRCA1'" }), " - show only feature where the name attribute is BRCA1"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'type')=='gene'" }), " - show only gene type features in a GFF that has many other feature types"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'score') > 400" }), " - show only features that have a score greater than 400"] })] })] }), error ? _jsx("p", { className: classes.error, children: `${error}` }) : null, _jsx(TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event => {
44
+ return (_jsxs(Dialog, { maxWidth: "xl", open: true, onClose: handleClose, title: "Add track filters", children: [_jsxs(DialogContent, { children: [_jsxs("div", { children: ["Add filters, in jexl format, one per line, starting with the string jexl:. Examples:", ' ', _jsxs("ul", { children: [_jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'name')=='BRCA1'" }), " - show only feature where the name attribute is BRCA1"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'type')=='gene'" }), " - show only gene type features in a GFF that has many other feature types"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'score') > 400" }), " - show only features that have a score greater than 400"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'end') - get(feature,'start') < 1000000" }), ' ', "- show only features with length less than 1Mbp"] })] })] }), error ? _jsx("p", { className: classes.error, children: `${error}` }) : null, _jsx(TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event => {
45
45
  setData(event.target.value);
46
46
  }, slotProps: {
47
47
  input: {
@@ -127,7 +127,10 @@ function stateModelFactory(configSchema) {
127
127
  onClick: () => {
128
128
  getSession(self).queueDialog(handleClose => [
129
129
  SetMaxHeightDialog,
130
- { model: self, handleClose },
130
+ {
131
+ model: self,
132
+ handleClose,
133
+ },
131
134
  ]);
132
135
  },
133
136
  },
@@ -136,7 +139,10 @@ function stateModelFactory(configSchema) {
136
139
  onClick: () => {
137
140
  getSession(self).queueDialog(handleClose => [
138
141
  AddFiltersDialog,
139
- { model: self, handleClose },
142
+ {
143
+ model: self,
144
+ handleClose,
145
+ },
140
146
  ]);
141
147
  },
142
148
  },
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { lazy, useEffect, useState } from 'react';
3
3
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
4
- import { getSession } from '@jbrowse/core/util';
4
+ import { getBpDisplayStr, getSession } from '@jbrowse/core/util';
5
5
  import MoreVert from '@mui/icons-material/MoreVert';
6
6
  import ZoomIn from '@mui/icons-material/ZoomIn';
7
7
  import ZoomOut from '@mui/icons-material/ZoomOut';
@@ -20,9 +20,13 @@ const useStyles = makeStyles()(theme => ({
20
20
  color: theme.palette.text.secondary,
21
21
  },
22
22
  }));
23
+ function ValueLabelComponent(props) {
24
+ const { children, open, value } = props;
25
+ return (_jsx(Tooltip, { open: open, enterTouchDelay: 0, placement: "top", title: value, arrow: true, children: children }));
26
+ }
23
27
  const HeaderZoomControls = observer(function ({ model, }) {
24
28
  const { classes } = useStyles();
25
- const { maxBpPerPx, minBpPerPx, bpPerPx } = model;
29
+ const { width, maxBpPerPx, minBpPerPx, bpPerPx } = model;
26
30
  const [value, setValue] = useState(-Math.log2(bpPerPx) * 100);
27
31
  useEffect(() => {
28
32
  setValue(-Math.log2(bpPerPx) * 100);
@@ -31,7 +35,9 @@ const HeaderZoomControls = observer(function ({ model, }) {
31
35
  const zoomOutDisabled = bpPerPx >= maxBpPerPx - 0.0001;
32
36
  return (_jsxs("div", { className: classes.container, children: [_jsx(Tooltip, { title: "Zoom out 2x", children: _jsx("span", { children: _jsx(IconButton, { "data-testid": "zoom_out", disabled: zoomOutDisabled, onClick: () => {
33
37
  model.zoom(bpPerPx * 2);
34
- }, children: _jsx(ZoomOut, {}) }) }) }), _jsx(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) => {
38
+ }, children: _jsx(ZoomOut, {}) }) }) }), _jsx(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: ${getBpDisplayStr(2 ** (-newValue / 100) * width)}`, slots: {
39
+ valueLabel: ValueLabelComponent,
40
+ }, onChange: (_, val) => {
35
41
  setValue(val);
36
42
  } }), _jsx(Tooltip, { title: "Zoom in 2x", children: _jsx("span", { children: _jsx(IconButton, { "data-testid": "zoom_in", disabled: zoomInDisabled, onClick: () => {
37
43
  model.zoom(model.bpPerPx / 2);
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
3
3
  import { AssemblySelector, ErrorMessage } from '@jbrowse/core/ui';
4
4
  import { getSession } from '@jbrowse/core/util';
5
5
  import CloseIcon from '@mui/icons-material/Close';
6
- import { Button, CircularProgress, Container, FormControl, Grid2 as Grid, } from '@mui/material';
6
+ import { Button, CircularProgress, Container, FormControl, Grid as Grid, } from '@mui/material';
7
7
  import { observer } from 'mobx-react';
8
8
  import { makeStyles } from 'tss-react/mui';
9
9
  import ImportFormRefNameAutocomplete from './ImportFormRefNameAutocomplete';
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
- import { getSession, stringify } from '@jbrowse/core/util';
4
- import { Tooltip } from '@mui/material';
3
+ import { getSession } from '@jbrowse/core/util';
5
4
  import { observer } from 'mobx-react';
6
5
  import { makeStyles } from 'tss-react/mui';
6
+ import OverviewRubberbandHoverTooltip from './OverviewRubberbandHoverTooltip';
7
7
  import RubberbandSpan from './RubberbandSpan';
8
8
  import { getRelativeX } from './util';
9
9
  const useStyles = makeStyles()({
@@ -22,18 +22,6 @@ const useStyles = makeStyles()({
22
22
  position: 'relative',
23
23
  },
24
24
  });
25
- const HoverTooltip = observer(function ({ model, open, guideX, overview, }) {
26
- var _a;
27
- const { classes } = useStyles();
28
- const { cytobandOffset } = model;
29
- const { assemblyManager } = getSession(model);
30
- const px = overview.pxToBp(guideX - cytobandOffset);
31
- const assembly = assemblyManager.get(px.assemblyName);
32
- 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') &&
33
- px.coord < f.get('end') &&
34
- px.refName === assembly.getCanonicalRefName(f.get('refName')));
35
- 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 } }) }));
36
- });
37
25
  const OverviewRubberband = observer(function OverviewRubberband({ model, overview, ControlComponent = _jsx("div", {}), }) {
38
26
  const { cytobandOffset } = model;
39
27
  const [startX, setStartX] = useState();
@@ -103,7 +91,7 @@ const OverviewRubberband = observer(function OverviewRubberband({ model, overvie
103
91
  setGuideX(undefined);
104
92
  }
105
93
  if (startX === undefined) {
106
- return (_jsxs("div", { className: classes.rel, children: [guideX !== undefined ? (_jsx(HoverTooltip, { model: model, open: !mouseDragging, overview: overview, guideX: guideX })) : null, _jsx("div", { className: classes.rubberbandControl, ref: controlsRef, onMouseDown: mouseDown, onMouseOut: mouseOut, onMouseMove: mouseMove, children: ControlComponent })] }));
94
+ return (_jsxs("div", { className: classes.rel, children: [guideX !== undefined ? (_jsx(OverviewRubberbandHoverTooltip, { model: model, open: !mouseDragging, overview: overview, guideX: guideX })) : null, _jsx("div", { className: classes.rubberbandControl, ref: controlsRef, onMouseDown: mouseDown, onMouseOut: mouseOut, onMouseMove: mouseMove, children: ControlComponent })] }));
107
95
  }
108
96
  let left = startX || 0;
109
97
  let width = 0;
@@ -0,0 +1,10 @@
1
+ import type { LinearGenomeViewModel } from '..';
2
+ import type { Base1DViewModel } from '@jbrowse/core/util/Base1DViewModel';
3
+ type LGV = LinearGenomeViewModel;
4
+ declare const OverviewRubberbandHoverTooltip: ({ model, open, guideX, overview, }: {
5
+ model: LGV;
6
+ open: boolean;
7
+ guideX: number;
8
+ overview: Base1DViewModel;
9
+ }) => import("react/jsx-runtime").JSX.Element;
10
+ export default OverviewRubberbandHoverTooltip;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { getSession, stringify } from '@jbrowse/core/util';
3
+ import { Tooltip } from '@mui/material';
4
+ import { observer } from 'mobx-react';
5
+ import { makeStyles } from 'tss-react/mui';
6
+ const useStyles = makeStyles()({
7
+ rubberbandControl: {
8
+ cursor: 'crosshair',
9
+ width: '100%',
10
+ minHeight: 8,
11
+ },
12
+ guide: {
13
+ pointerEvents: 'none',
14
+ height: '100%',
15
+ width: 1,
16
+ position: 'absolute',
17
+ },
18
+ rel: {
19
+ position: 'relative',
20
+ },
21
+ });
22
+ const OverviewRubberbandHoverTooltip = observer(function ({ model, open, guideX, overview, }) {
23
+ var _a;
24
+ const { classes } = useStyles();
25
+ const { cytobandOffset } = model;
26
+ const { assemblyManager } = getSession(model);
27
+ const px = overview.pxToBp(guideX - cytobandOffset);
28
+ const assembly = assemblyManager.get(px.assemblyName);
29
+ 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
+ px.coord < f.get('end') &&
31
+ 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 } }) }));
33
+ });
34
+ export default OverviewRubberbandHoverTooltip;
@@ -1,14 +1,11 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRef } from 'react';
3
- import { Menu, VIEW_HEADER_HEIGHT } from '@jbrowse/core/ui';
4
- import { getSession } from '@jbrowse/core/util';
5
- import { isSessionWithMultipleViews } from '@jbrowse/product-core';
3
+ import { Menu } from '@jbrowse/core/ui';
6
4
  import { observer } from 'mobx-react';
7
5
  import { makeStyles } from 'tss-react/mui';
8
6
  import RubberbandSpan from './RubberbandSpan';
9
7
  import VerticalGuide from './VerticalGuide';
10
8
  import { useRangeSelect } from './useRangeSelect';
11
- import { HEADER_BAR_HEIGHT, HEADER_OVERVIEW_HEIGHT } from '../consts';
12
9
  const useStyles = makeStyles()({
13
10
  rubberbandControl: {
14
11
  cursor: 'crosshair',
@@ -20,28 +17,13 @@ const useStyles = makeStyles()({
20
17
  const Rubberband = observer(function ({ model, ControlComponent = _jsx("div", {}), }) {
21
18
  const ref = useRef(null);
22
19
  const { classes } = useStyles();
23
- const session = getSession(model);
20
+ const { stickyViewHeaders, rubberbandTop } = model;
24
21
  const { guideX, rubberbandOn, leftBpOffset, rightBpOffset, numOfBpSelected, width, left, anchorPosition, open, handleMenuItemClick, handleClose, mouseMove, mouseDown, mouseOut, } = useRangeSelect(ref, model);
25
- let stickyViewHeaders = false;
26
- if (isSessionWithMultipleViews(session)) {
27
- ;
28
- ({ stickyViewHeaders } = session);
29
- }
30
- let rubberbandControlTop = 0;
31
- if (stickyViewHeaders) {
32
- rubberbandControlTop = VIEW_HEADER_HEIGHT;
33
- if (!model.hideHeader) {
34
- rubberbandControlTop += HEADER_BAR_HEIGHT;
35
- if (!model.hideHeaderOverview) {
36
- rubberbandControlTop += HEADER_OVERVIEW_HEIGHT;
37
- }
38
- }
39
- }
40
- return (_jsxs(_Fragment, { children: [guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : rubberbandOn ? (_jsx(RubberbandSpan, { leftBpOffset: leftBpOffset, rightBpOffset: rightBpOffset, numOfBpSelected: numOfBpSelected, width: width, left: left, top: rubberbandControlTop, sticky: stickyViewHeaders })) : null, anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
22
+ return (_jsxs(_Fragment, { children: [guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : rubberbandOn ? (_jsx(RubberbandSpan, { leftBpOffset: leftBpOffset, rightBpOffset: rightBpOffset, numOfBpSelected: numOfBpSelected, width: width, left: left, top: rubberbandTop, sticky: stickyViewHeaders })) : null, anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
41
23
  left: anchorPosition.clientX,
42
24
  top: anchorPosition.clientY,
43
25
  }, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null, _jsx("div", { "data-testid": "rubberband_controls", className: classes.rubberbandControl, style: {
44
- top: rubberbandControlTop,
26
+ top: rubberbandTop,
45
27
  position: stickyViewHeaders ? 'sticky' : undefined,
46
28
  }, ref: ref, onMouseDown: mouseDown, onMouseMove: mouseMove, onMouseOut: mouseOut, children: ControlComponent })] }));
47
29
  });
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
- import { stringify, toLocale } from '@jbrowse/core/util';
4
- import { Popover, Typography, alpha } from '@mui/material';
3
+ import { getBpDisplayStr, stringify } from '@jbrowse/core/util';
4
+ import { Typography, alpha } from '@mui/material';
5
5
  import { makeStyles } from 'tss-react/mui';
6
+ import RubberbandTooltip from './RubberbandTooltip';
6
7
  const useStyles = makeStyles()(theme => {
7
8
  const { tertiary } = theme.palette;
8
9
  const background = alpha(tertiary.light, 0.7);
@@ -20,35 +21,17 @@ const useStyles = makeStyles()(theme => {
20
21
  minHeight: 8,
21
22
  },
22
23
  rubberbandText: {
23
- color: tertiary.contrastText,
24
- },
25
- popover: {
26
- mouseEvents: 'none',
27
- cursor: 'crosshair',
28
- },
29
- paper: {
30
- paddingLeft: theme.spacing(1),
31
- paddingRight: theme.spacing(1),
24
+ color: theme.palette.tertiary.contrastText,
32
25
  },
33
26
  };
34
27
  });
35
- function Tooltip({ anchorEl, side, text, }) {
36
- const { classes } = useStyles();
37
- return (_jsx(Popover, { className: classes.popover, classes: { paper: classes.paper }, open: true, anchorEl: anchorEl, anchorOrigin: {
38
- vertical: 'top',
39
- horizontal: side === 'left' ? 'right' : 'left',
40
- }, transformOrigin: {
41
- vertical: 'bottom',
42
- horizontal: side === 'left' ? 'left' : 'right',
43
- }, keepMounted: true, disableRestoreFocus: true, children: _jsx(Typography, { children: text }) }));
44
- }
45
28
  export default function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top = 0, sticky = false, }) {
46
29
  const { classes } = useStyles();
47
30
  const [anchorEl, setAnchorEl] = useState(null);
48
- return (_jsxs(_Fragment, { children: [anchorEl ? (_jsxs(_Fragment, { children: [_jsx(Tooltip, { side: "left", anchorEl: anchorEl, text: stringify(leftBpOffset) }), _jsx(Tooltip, { side: "right", anchorEl: anchorEl, text: stringify(rightBpOffset) })] })) : null, _jsx("div", { className: classes.rubberband, style: { left, width }, children: numOfBpSelected ? (_jsxs(Typography, { ref: el => {
31
+ return (_jsxs(_Fragment, { children: [anchorEl ? (_jsxs(_Fragment, { children: [_jsx(RubberbandTooltip, { side: "left", anchorEl: anchorEl, text: stringify(leftBpOffset) }), _jsx(RubberbandTooltip, { side: "right", anchorEl: anchorEl, text: stringify(rightBpOffset) })] })) : null, _jsx("div", { className: classes.rubberband, style: { left, width }, children: numOfBpSelected ? (_jsx(Typography, { ref: el => {
49
32
  setAnchorEl(el);
50
33
  }, variant: "h6", className: classes.rubberbandText, style: {
51
34
  top,
52
35
  position: sticky ? 'sticky' : undefined,
53
- }, children: [toLocale(numOfBpSelected), " bp"] })) : null })] }));
36
+ }, children: getBpDisplayStr(numOfBpSelected) })) : null })] }));
54
37
  }
@@ -0,0 +1,5 @@
1
+ export default function RubberbandTooltip({ anchorEl, side, text, }: {
2
+ anchorEl: HTMLSpanElement;
3
+ side: string;
4
+ text: string;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Popover, Typography } from '@mui/material';
3
+ import { makeStyles } from 'tss-react/mui';
4
+ const useStyles = makeStyles()(theme => {
5
+ return {
6
+ popover: {
7
+ mouseEvents: 'none',
8
+ cursor: 'crosshair',
9
+ },
10
+ paper: {
11
+ paddingLeft: theme.spacing(1),
12
+ paddingRight: theme.spacing(1),
13
+ },
14
+ };
15
+ });
16
+ export default function RubberbandTooltip({ anchorEl, side, text, }) {
17
+ const { classes } = useStyles();
18
+ return (_jsx(Popover, { className: classes.popover, classes: { paper: classes.paper }, open: true, anchorEl: anchorEl, anchorOrigin: {
19
+ vertical: 'top',
20
+ horizontal: side === 'left' ? 'left' : 'right',
21
+ }, transformOrigin: {
22
+ vertical: 'bottom',
23
+ horizontal: side === 'left' ? 'right' : 'left',
24
+ }, keepMounted: true, disableRestoreFocus: true, children: _jsx(Typography, { children: text }) }));
25
+ }
@@ -24,7 +24,7 @@ const TracksContainer = observer(function TracksContainer({ children, model, })
24
24
  const { classes } = useStyles();
25
25
  const { pluginManager } = getEnv(model);
26
26
  const { mouseDown: mouseDown1, mouseUp } = useSideScroll(model);
27
- const { stickyViewHeaders, pinnedTracksTop, showGridlines, showCenterLine } = model;
27
+ const { stickyViewHeaders, rubberbandTop, showGridlines, showCenterLine } = model;
28
28
  const ref = useRef(null);
29
29
  const { guideX, rubberbandOn, leftBpOffset, rightBpOffset, numOfBpSelected, width, left, anchorPosition, open, handleMenuItemClick, handleClose, mouseMove, mouseDown: mouseDown2, } = useRangeSelect(ref, model, true);
30
30
  useWheelScroll(ref, model);
@@ -32,7 +32,7 @@ const TracksContainer = observer(function TracksContainer({ children, model, })
32
32
  return (_jsxs("div", { ref: ref, "data-testid": "tracksContainer", className: classes.tracksContainer, onMouseDown: event => {
33
33
  mouseDown1(event);
34
34
  mouseDown2(event);
35
- }, onMouseMove: mouseMove, onMouseUp: mouseUp, children: [showGridlines ? _jsx(Gridlines, { model: model }) : null, _jsx(Suspense, { fallback: null, children: showCenterLine ? _jsx(CenterLine, { model: model }) : null }), guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : rubberbandOn ? (_jsx(Suspense, { fallback: null, children: _jsx(RubberbandSpan, { leftBpOffset: leftBpOffset, rightBpOffset: rightBpOffset, numOfBpSelected: numOfBpSelected, width: width, left: left, top: pinnedTracksTop, sticky: stickyViewHeaders }) })) : null, anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
35
+ }, onMouseMove: mouseMove, onMouseUp: mouseUp, children: [showGridlines ? _jsx(Gridlines, { model: model }) : null, _jsx(Suspense, { fallback: null, children: showCenterLine ? _jsx(CenterLine, { model: model }) : null }), guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : rubberbandOn ? (_jsx(Suspense, { fallback: null, children: _jsx(RubberbandSpan, { leftBpOffset: leftBpOffset, rightBpOffset: rightBpOffset, numOfBpSelected: numOfBpSelected, width: width, left: left, top: rubberbandTop, sticky: stickyViewHeaders }) })) : null, anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
36
36
  left: anchorPosition.clientX,
37
37
  top: anchorPosition.clientY,
38
38
  }, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null, _jsx(Rubberband, { model: model, ControlComponent: _jsx(Scalebar, { model: model, style: {