@jbrowse/plugin-linear-genome-view 2.10.3 → 2.11.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 (52) hide show
  1. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.js +0 -1
  2. package/dist/LaunchLinearGenomeView/index.js +14 -6
  3. package/dist/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -1
  4. package/dist/LinearGenomeView/components/Cytobands.d.ts +1 -125
  5. package/dist/LinearGenomeView/components/ExportSvgDialog.js +9 -5
  6. package/dist/LinearGenomeView/components/Highlight.d.ts +3 -3
  7. package/dist/LinearGenomeView/components/Highlight.js +21 -17
  8. package/dist/LinearGenomeView/components/MiniControls.js +13 -15
  9. package/dist/LinearGenomeView/components/OverviewHighlight.d.ts +1 -1
  10. package/dist/LinearGenomeView/components/OverviewHighlight.js +20 -11
  11. package/dist/LinearGenomeView/components/OverviewRubberband.d.ts +1 -1
  12. package/dist/LinearGenomeView/components/OverviewScalebar.js +4 -1
  13. package/dist/LinearGenomeView/components/OverviewScalebarPolygon.d.ts +1 -1
  14. package/dist/LinearGenomeView/components/RefNameAutocomplete/index.d.ts +9 -9
  15. package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +28 -35
  16. package/dist/LinearGenomeView/components/Rubberband.d.ts +1 -1
  17. package/dist/LinearGenomeView/components/SearchBox.d.ts +1 -1
  18. package/dist/LinearGenomeView/components/SearchResultsTable.js +1 -1
  19. package/dist/LinearGenomeView/components/TrackContainer.js +10 -23
  20. package/dist/LinearGenomeView/components/TrackLabel.js +33 -6
  21. package/dist/LinearGenomeView/components/ZoomControls.js +4 -4
  22. package/dist/LinearGenomeView/model.d.ts +67 -7
  23. package/dist/LinearGenomeView/model.js +102 -16
  24. package/dist/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +1 -1
  25. package/dist/index.d.ts +159 -21
  26. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js +0 -1
  27. package/esm/LaunchLinearGenomeView/index.js +14 -6
  28. package/esm/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -1
  29. package/esm/LinearGenomeView/components/Cytobands.d.ts +1 -125
  30. package/esm/LinearGenomeView/components/ExportSvgDialog.js +9 -5
  31. package/esm/LinearGenomeView/components/Highlight.d.ts +3 -3
  32. package/esm/LinearGenomeView/components/Highlight.js +21 -17
  33. package/esm/LinearGenomeView/components/MiniControls.js +14 -16
  34. package/esm/LinearGenomeView/components/OverviewHighlight.d.ts +1 -1
  35. package/esm/LinearGenomeView/components/OverviewHighlight.js +20 -11
  36. package/esm/LinearGenomeView/components/OverviewRubberband.d.ts +1 -1
  37. package/esm/LinearGenomeView/components/OverviewScalebar.js +5 -2
  38. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.d.ts +1 -1
  39. package/esm/LinearGenomeView/components/RefNameAutocomplete/index.d.ts +9 -9
  40. package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +28 -35
  41. package/esm/LinearGenomeView/components/Rubberband.d.ts +1 -1
  42. package/esm/LinearGenomeView/components/SearchBox.d.ts +1 -1
  43. package/esm/LinearGenomeView/components/SearchResultsTable.js +1 -1
  44. package/esm/LinearGenomeView/components/TrackContainer.js +10 -23
  45. package/esm/LinearGenomeView/components/TrackLabel.js +33 -6
  46. package/esm/LinearGenomeView/components/TracksContainer.js +2 -2
  47. package/esm/LinearGenomeView/components/ZoomControls.js +4 -4
  48. package/esm/LinearGenomeView/model.d.ts +67 -7
  49. package/esm/LinearGenomeView/model.js +103 -17
  50. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +1 -1
  51. package/esm/index.d.ts +159 -21
  52. package/package.json +2 -2
@@ -10,6 +10,10 @@ function LoadingMessage() {
10
10
  function useSvgLocal(key, val) {
11
11
  return useLocalStorage('svg-' + key, val);
12
12
  }
13
+ function TextField2({ children, ...rest }) {
14
+ return (React.createElement("div", null,
15
+ React.createElement(TextField, { ...rest }, children)));
16
+ }
13
17
  export default function ExportSvgDialog({ model, handleClose, }) {
14
18
  const session = getSession(model);
15
19
  const offscreenCanvas = typeof OffscreenCanvas !== 'undefined';
@@ -22,15 +26,13 @@ export default function ExportSvgDialog({ model, handleClose, }) {
22
26
  return (React.createElement(Dialog, { open: true, onClose: handleClose, title: "Export SVG" },
23
27
  React.createElement(DialogContent, null,
24
28
  error ? (React.createElement(ErrorMessage, { error: error })) : loading ? (React.createElement(LoadingMessage, null)) : null,
25
- React.createElement(TextField, { helperText: "filename", value: filename, onChange: event => setFilename(event.target.value) }),
26
- React.createElement("br", null),
27
- React.createElement(TextField, { select: true, label: "Track label positioning", variant: "outlined", style: { width: 150 }, value: trackLabels, onChange: event => setTrackLabels(event.target.value) },
29
+ React.createElement(TextField2, { helperText: "filename", value: filename, onChange: event => setFilename(event.target.value) }),
30
+ React.createElement(TextField2, { select: true, label: "Track label positioning", variant: "outlined", style: { width: 150 }, value: trackLabels, onChange: event => setTrackLabels(event.target.value) },
28
31
  React.createElement(MenuItem, { value: "offset" }, "Offset"),
29
32
  React.createElement(MenuItem, { value: "overlay" }, "Overlay"),
30
33
  React.createElement(MenuItem, { value: "left" }, "Left"),
31
34
  React.createElement(MenuItem, { value: "none" }, "None")),
32
- React.createElement("br", null),
33
- session.allThemes ? (React.createElement(TextField, { select: true, label: "Theme", variant: "outlined", value: themeName, onChange: event => setThemeName(event.target.value) }, Object.entries(session.allThemes()).map(([key, val]) => (React.createElement(MenuItem, { key: key, value: key },
35
+ session.allThemes ? (React.createElement(TextField2, { select: true, label: "Theme", variant: "outlined", value: themeName, onChange: event => setThemeName(event.target.value) }, Object.entries(session.allThemes()).map(([key, val]) => (React.createElement(MenuItem, { key: key, value: key },
34
36
  // @ts-expect-error
35
37
  val.name || '(Unknown name)'))))) : null,
36
38
  offscreenCanvas ? (React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { checked: rasterizeLayers, onChange: () => setRasterizeLayers(val => !val) }), label: "Rasterize canvas based tracks? File may be much larger if this is turned off" })) : (React.createElement(Typography, null, "Note: rasterizing layers not yet supported in this browser, so SVG size may be large"))),
@@ -51,6 +53,8 @@ export default function ExportSvgDialog({ model, handleClose, }) {
51
53
  catch (e) {
52
54
  console.error(e);
53
55
  setError(e);
56
+ }
57
+ finally {
54
58
  setLoading(false);
55
59
  }
56
60
  } }, "Submit"))));
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { LinearGenomeViewModel } from '../model';
3
3
  type LGV = LinearGenomeViewModel;
4
- declare const Highlight: ({ model }: {
4
+ declare const HighlightGroup: ({ model, }: {
5
5
  model: LGV;
6
- }) => React.JSX.Element | null;
7
- export default Highlight;
6
+ }) => React.JSX.Element[];
7
+ export default HighlightGroup;
@@ -10,28 +10,28 @@ import LinkIcon from '@mui/icons-material/Link';
10
10
  import CloseIcon from '@mui/icons-material/Close';
11
11
  import BookmarkIcon from '@mui/icons-material/Bookmark';
12
12
  const useStyles = makeStyles()(theme => {
13
- var _a, _b, _c, _d, _e, _f;
13
+ var _a, _b;
14
14
  return ({
15
15
  highlight: {
16
16
  height: '100%',
17
17
  position: 'absolute',
18
- background: `${colord((_b = (_a = theme.palette.quaternary) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
18
+ overflow: 'hidden',
19
+ background: `${colord((_b = (_a = theme.palette.highlight) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
19
20
  .alpha(0.35)
20
21
  .toRgbString()}`,
21
- borderLeft: `1px solid ${(_d = (_c = theme.palette.quaternary) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
22
- borderRight: `1px solid ${(_f = (_e = theme.palette.quaternary) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
23
22
  },
24
23
  });
25
24
  });
26
- const Highlight = observer(function Highlight({ model }) {
27
- var _a, _b;
25
+ const Highlight = observer(function Highlight({ model, highlight, }) {
26
+ var _a, _b, _c;
28
27
  const { classes } = useStyles();
29
28
  const [open, setOpen] = useState(false);
30
29
  const anchorEl = useRef(null);
31
- const color = (_b = (_a = useTheme().palette.quaternary) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod';
30
+ const color = (_b = (_a = useTheme().palette.highlight) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod';
32
31
  const session = getSession(model);
32
+ const { assemblyManager } = session;
33
33
  const dismissHighlight = () => {
34
- model.setHighlight(undefined);
34
+ model.removeHighlight(highlight);
35
35
  };
36
36
  const menuItems = [
37
37
  {
@@ -48,7 +48,7 @@ const Highlight = observer(function Highlight({ model }) {
48
48
  bookmarkWidget = session.addWidget('GridBookmarkWidget', 'GridBookmark');
49
49
  }
50
50
  // @ts-ignore
51
- bookmarkWidget.addBookmark(model.highlight);
51
+ bookmarkWidget.addBookmark(highlight);
52
52
  dismissHighlight();
53
53
  },
54
54
  },
@@ -56,9 +56,6 @@ const Highlight = observer(function Highlight({ model }) {
56
56
  function handleClose() {
57
57
  setOpen(false);
58
58
  }
59
- if (!model.highlight) {
60
- return null;
61
- }
62
59
  // coords
63
60
  const mapCoords = (r) => {
64
61
  const s = model.bpToPx({
@@ -76,19 +73,26 @@ const Highlight = observer(function Highlight({ model }) {
76
73
  }
77
74
  : undefined;
78
75
  };
79
- const h = mapCoords(model.highlight);
80
- return (React.createElement(React.Fragment, null, h ? (React.createElement("div", { className: classes.highlight, style: {
76
+ const asm = assemblyManager.get(highlight === null || highlight === void 0 ? void 0 : highlight.assemblyName);
77
+ const h = mapCoords({
78
+ ...highlight,
79
+ refName: (_c = asm === null || asm === void 0 ? void 0 : asm.getCanonicalRefName(highlight.refName)) !== null && _c !== void 0 ? _c : highlight.refName,
80
+ });
81
+ return h ? (React.createElement("div", { className: classes.highlight, style: {
81
82
  left: h.left,
82
83
  width: h.width,
83
84
  } },
84
85
  React.createElement(Tooltip, { title: 'Highlighted from URL parameter', arrow: true },
85
- React.createElement(IconButton, { ref: anchorEl, onClick: () => setOpen(true) },
86
+ React.createElement(IconButton, { ref: anchorEl, onClick: () => setOpen(true), style: { zIndex: 3 } },
86
87
  React.createElement(LinkIcon, { fontSize: "small", sx: {
87
88
  color: `${colord(color).darken(0.2).toRgbString()}`,
88
89
  } }))),
89
90
  React.createElement(Menu, { anchorEl: anchorEl.current, onMenuItemClick: (_event, callback) => {
90
91
  callback(session);
91
92
  handleClose();
92
- }, open: open, onClose: handleClose, menuItems: menuItems }))) : null));
93
+ }, open: open, onClose: handleClose, menuItems: menuItems }))) : null;
94
+ });
95
+ const HighlightGroup = observer(function HighlightGroup({ model, }) {
96
+ return model.highlight.map((highlight, idx) => (React.createElement(Highlight, { key: JSON.stringify(highlight) + '-' + idx, model: model, highlight: highlight })));
93
97
  });
94
- export default Highlight;
98
+ export default HighlightGroup;
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { observer } from 'mobx-react';
3
- import { IconButton, Paper } from '@mui/material';
3
+ import { IconButton, Paper, alpha } from '@mui/material';
4
+ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
5
+ import { makeStyles } from 'tss-react/mui';
6
+ import { getSession } from '@jbrowse/core/util';
4
7
  // icons
5
8
  import ZoomIn from '@mui/icons-material/ZoomIn';
6
9
  import ZoomOut from '@mui/icons-material/ZoomOut';
7
10
  import ArrowDown from '@mui/icons-material/KeyboardArrowDown';
8
- import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
9
- import { makeStyles } from 'tss-react/mui';
10
- import { getSession } from '@jbrowse/core/util';
11
11
  const useStyles = makeStyles()(theme => ({
12
12
  background: {
13
13
  position: 'absolute',
@@ -16,22 +16,20 @@ const useStyles = makeStyles()(theme => ({
16
16
  background: theme.palette.background.paper,
17
17
  },
18
18
  focusedBackground: {
19
- background: theme.palette.secondary.light,
20
- svg: {
21
- fill: theme.palette.secondary.contrastText,
22
- },
19
+ background: alpha(theme.palette.secondary.light, 0.2),
23
20
  },
24
21
  }));
25
22
  const MiniControls = observer(function ({ model, }) {
26
- const { classes, cx } = useStyles();
23
+ const { classes } = useStyles();
27
24
  const { id, bpPerPx, maxBpPerPx, minBpPerPx, scaleFactor, hideHeader } = model;
28
25
  const { focusedViewId } = getSession(model);
29
- return hideHeader ? (React.createElement(Paper, { className: cx(classes.background, focusedViewId === id ? classes.focusedBackground : undefined) },
30
- React.createElement(CascadingMenuButton, { menuItems: model.menuItems() },
31
- React.createElement(ArrowDown, { fontSize: "small" })),
32
- React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => model.zoom(bpPerPx * 2), disabled: bpPerPx >= maxBpPerPx - 0.0001 || scaleFactor !== 1 },
33
- React.createElement(ZoomOut, { fontSize: "small" })),
34
- React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => model.zoom(bpPerPx / 2), disabled: bpPerPx <= minBpPerPx + 0.0001 || scaleFactor !== 1 },
35
- React.createElement(ZoomIn, { fontSize: "small" })))) : null;
26
+ return hideHeader ? (React.createElement(Paper, { className: classes.background },
27
+ React.createElement(Paper, { className: focusedViewId === id ? classes.focusedBackground : undefined },
28
+ React.createElement(CascadingMenuButton, { menuItems: model.menuItems() },
29
+ React.createElement(ArrowDown, { fontSize: "small" })),
30
+ React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => model.zoom(bpPerPx * 2), disabled: bpPerPx >= maxBpPerPx - 0.0001 || scaleFactor !== 1 },
31
+ React.createElement(ZoomOut, { fontSize: "small" })),
32
+ React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => model.zoom(bpPerPx / 2), disabled: bpPerPx <= minBpPerPx + 0.0001 || scaleFactor !== 1 },
33
+ React.createElement(ZoomIn, { fontSize: "small" }))))) : null;
36
34
  });
37
35
  export default MiniControls;
@@ -5,5 +5,5 @@ type LGV = LinearGenomeViewModel;
5
5
  declare const OverviewHighlight: ({ model, overview, }: {
6
6
  model: LGV;
7
7
  overview: Base1DViewModel;
8
- }) => React.JSX.Element | null;
8
+ }) => React.JSX.Element[];
9
9
  export default OverviewHighlight;
@@ -2,23 +2,26 @@ import React from 'react';
2
2
  import { observer } from 'mobx-react';
3
3
  import { makeStyles } from 'tss-react/mui';
4
4
  import { colord } from '@jbrowse/core/util/colord';
5
+ import { getSession, notEmpty, } from '@jbrowse/core/util';
5
6
  const useStyles = makeStyles()(theme => {
6
7
  var _a, _b, _c, _d, _e, _f;
7
8
  return ({
8
9
  highlight: {
9
10
  height: '100%',
10
11
  position: 'absolute',
11
- background: `${colord((_b = (_a = theme.palette.quaternary) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
12
+ background: `${colord((_b = (_a = theme.palette.highlight) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
12
13
  .alpha(0.35)
13
14
  .toRgbString()}`,
14
- borderLeft: `1px solid ${(_d = (_c = theme.palette.quaternary) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
15
- borderRight: `1px solid ${(_f = (_e = theme.palette.quaternary) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
15
+ borderLeft: `1px solid ${(_d = (_c = theme.palette.highlight) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
16
+ borderRight: `1px solid ${(_f = (_e = theme.palette.highlight) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
16
17
  },
17
18
  });
18
19
  });
19
20
  const OverviewHighlight = observer(function OverviewHighlight({ model, overview, }) {
20
21
  const { classes } = useStyles();
21
22
  const { cytobandOffset } = model;
23
+ const session = getSession(model);
24
+ const { assemblyManager } = session;
22
25
  // coords
23
26
  const mapCoords = (r) => {
24
27
  const s = overview.bpToPx({
@@ -36,13 +39,19 @@ const OverviewHighlight = observer(function OverviewHighlight({ model, overview,
36
39
  }
37
40
  : undefined;
38
41
  };
39
- if (!model.highlight) {
40
- return null;
41
- }
42
- const h = mapCoords(model.highlight);
43
- return (React.createElement(React.Fragment, null, h ? (React.createElement("div", { className: classes.highlight, style: {
44
- width: h.width,
45
- left: h.left,
46
- } })) : null));
42
+ return model.highlight
43
+ .map(h => {
44
+ var _a;
45
+ const asm = assemblyManager.get(h === null || h === void 0 ? void 0 : h.assemblyName);
46
+ return mapCoords({
47
+ ...h,
48
+ refName: (_a = asm === null || asm === void 0 ? void 0 : asm.getCanonicalRefName(h.refName)) !== null && _a !== void 0 ? _a : h.refName,
49
+ });
50
+ })
51
+ .filter(notEmpty)
52
+ .map(({ left, width }, idx) => (React.createElement("div", { key: `${left}_${width}_${idx}`, className: classes.highlight, style: {
53
+ width: width,
54
+ left: left,
55
+ } })));
47
56
  });
48
57
  export default OverviewHighlight;
@@ -5,6 +5,6 @@ type LGV = LinearGenomeViewModel;
5
5
  declare const OverviewRubberband: ({ model, overview, ControlComponent, }: {
6
6
  model: LGV;
7
7
  overview: Base1DViewModel;
8
- ControlComponent?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined;
8
+ ControlComponent?: React.ReactElement;
9
9
  }) => React.JSX.Element;
10
10
  export default OverviewRubberband;
@@ -4,7 +4,7 @@ import { makeStyles } from 'tss-react/mui';
4
4
  import { observer } from 'mobx-react';
5
5
  // core
6
6
  import Base1DView from '@jbrowse/core/util/Base1DViewModel';
7
- import { getSession, getTickDisplayStr } from '@jbrowse/core/util';
7
+ import { getEnv, getSession, getTickDisplayStr } from '@jbrowse/core/util';
8
8
  import { ContentBlock } from '@jbrowse/core/util/blockTypes';
9
9
  // locals
10
10
  import { HEADER_BAR_HEIGHT, HEADER_OVERVIEW_HEIGHT, } from '..';
@@ -118,6 +118,7 @@ const Scalebar = observer(function ({ model, scale, overview, }) {
118
118
  const { classes } = useStyles();
119
119
  const theme = useTheme();
120
120
  const { dynamicBlocks, showCytobands, cytobandOffset } = model;
121
+ const { pluginManager } = getEnv(model);
121
122
  const visibleRegions = dynamicBlocks.contentBlocks;
122
123
  const overviewVisibleRegions = overview.dynamicBlocks;
123
124
  const { tertiary, primary } = theme.palette;
@@ -138,6 +139,7 @@ const Scalebar = observer(function ({ model, scale, overview, }) {
138
139
  }) || 0;
139
140
  const color = showCytobands ? '#f00' : scalebarColor;
140
141
  const transparency = showCytobands ? 0.1 : 0.3;
142
+ const additional = pluginManager.evaluateExtensionPoint('LinearGenomeView-OverviewScalebarComponent', undefined, { model, overview });
141
143
  return (React.createElement("div", { className: classes.scalebar },
142
144
  React.createElement("div", { className: classes.scalebarVisibleRegion, style: {
143
145
  width: lastOverviewPx - firstOverviewPx,
@@ -153,7 +155,8 @@ const Scalebar = observer(function ({ model, scale, overview, }) {
153
155
  backgroundImage: 'repeating-linear-gradient(90deg, transparent, transparent 1px, rgba(255,255,255,.5) 1px, rgba(255,255,255,.5) 3px)',
154
156
  } })) : (React.createElement(OverviewBox, { scale: scale, block: block, model: model, overview: overview, key: `${JSON.stringify(block)}-${idx}` }));
155
157
  }),
156
- React.createElement(OverviewHighlight, { model: model, overview: overview })));
158
+ React.createElement(OverviewHighlight, { model: model, overview: overview }),
159
+ additional));
157
160
  });
158
161
  const OverviewScalebar = observer(function ({ model, children, }) {
159
162
  const { classes } = useStyles();
@@ -4,6 +4,6 @@ import { LinearGenomeViewModel } from '..';
4
4
  declare const OverviewScalebarPolygon: ({ model, overview, useOffset, }: {
5
5
  model: LinearGenomeViewModel;
6
6
  overview: Base1DViewModel;
7
- useOffset?: boolean | undefined;
7
+ useOffset?: boolean;
8
8
  }) => React.JSX.Element | null;
9
9
  export default OverviewScalebarPolygon;
@@ -4,15 +4,15 @@ import { TextFieldProps as TFP } from '@mui/material';
4
4
  import { LinearGenomeViewModel } from '../../model';
5
5
  declare const RefNameAutocomplete: ({ model, onSelect, assemblyName, style, fetchResults, onChange, value, showHelp, minWidth, maxWidth, TextFieldProps, }: {
6
6
  model: LinearGenomeViewModel;
7
- onSelect?: ((region: BaseResult) => void) | undefined;
8
- onChange?: ((val: string) => void) | undefined;
9
- assemblyName?: string | undefined;
10
- value?: string | undefined;
7
+ onSelect?: (region: BaseResult) => void;
8
+ onChange?: (val: string) => void;
9
+ assemblyName?: string;
10
+ value?: string;
11
11
  fetchResults: (query: string) => Promise<BaseResult[]>;
12
- style?: React.CSSProperties | undefined;
13
- minWidth?: number | undefined;
14
- maxWidth?: number | undefined;
15
- showHelp?: boolean | undefined;
16
- TextFieldProps?: TFP | undefined;
12
+ style?: React.CSSProperties;
13
+ minWidth?: number;
14
+ maxWidth?: number;
15
+ showHelp?: boolean;
16
+ TextFieldProps?: TFP;
17
17
  }) => React.JSX.Element;
18
18
  export default RefNameAutocomplete;
@@ -17,7 +17,8 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
17
17
  const assembly = assemblyName ? assemblyManager.get(assemblyName) : undefined;
18
18
  const { coarseVisibleLocStrings, hasDisplayedRegions } = model;
19
19
  useEffect(() => {
20
- let active = true;
20
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
21
+ ;
21
22
  (async () => {
22
23
  try {
23
24
  if (debouncedSearch === '' || !assemblyName) {
@@ -25,21 +26,14 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
25
26
  }
26
27
  setLoaded(false);
27
28
  const results = await fetchResults(debouncedSearch);
28
- if (active) {
29
- setLoaded(true);
30
- setSearchOptions(getDeduplicatedResult(results));
31
- }
29
+ setLoaded(true);
30
+ setSearchOptions(getDeduplicatedResult(results));
32
31
  }
33
32
  catch (e) {
34
33
  console.error(e);
35
- if (active) {
36
- session.notify(`${e}`, 'error');
37
- }
34
+ session.notifyError(`${e}`, e);
38
35
  }
39
36
  })();
40
- return () => {
41
- active = false;
42
- };
43
37
  }, [assemblyName, fetchResults, debouncedSearch, session, model]);
44
38
  const inputBoxVal = coarseVisibleLocStrings || value || '';
45
39
  // heuristic, text width + 60 accommodates help icon and search icon
@@ -54,29 +48,28 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
54
48
  }))) || [], [regions]);
55
49
  // notes on implementation:
56
50
  // The selectOnFocus setting helps highlight the field when clicked
57
- return (React.createElement(React.Fragment, null,
58
- React.createElement(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) => {
59
- setInputValue(newInputValue);
60
- onChange === null || onChange === void 0 ? void 0 : onChange(newInputValue);
61
- }, loadingText: "loading results", open: open, onOpen: () => setOpen(true), onClose: () => {
62
- setOpen(false);
63
- setLoaded(true);
64
- if (hasDisplayedRegions) {
65
- setCurrentSearch('');
66
- setSearchOptions(undefined);
67
- }
68
- }, onChange: (_event, selectedOption) => {
69
- if (!selectedOption || !assemblyName) {
70
- return;
71
- }
72
- if (typeof selectedOption === 'string') {
73
- // handles string inputs on keyPress enter
74
- onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResult({ label: selectedOption }));
75
- }
76
- else {
77
- onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
78
- }
79
- setInputValue(inputBoxVal);
80
- }, options: (searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.length) ? searchOptions : regionOptions, getOptionDisabled: option => option.group === 'limitOption', filterOptions: (opts, { inputValue }) => getFiltered(opts, inputValue), renderInput: params => (React.createElement(AutocompleteTextField, { showHelp: showHelp, params: params, inputBoxVal: inputBoxVal, TextFieldProps: TextFieldProps, setCurrentSearch: setCurrentSearch, setInputValue: setInputValue })), getOptionLabel: opt => typeof opt === 'string' ? opt : opt.result.getDisplayString() })));
51
+ return (React.createElement(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) => {
52
+ setInputValue(newInputValue);
53
+ onChange === null || onChange === void 0 ? void 0 : onChange(newInputValue);
54
+ }, loadingText: "loading results", open: open, onOpen: () => setOpen(true), onClose: () => {
55
+ setOpen(false);
56
+ setLoaded(true);
57
+ if (hasDisplayedRegions) {
58
+ setCurrentSearch('');
59
+ setSearchOptions(undefined);
60
+ }
61
+ }, onChange: (_event, selectedOption) => {
62
+ if (!selectedOption || !assemblyName) {
63
+ return;
64
+ }
65
+ if (typeof selectedOption === 'string') {
66
+ // handles string inputs on keyPress enter
67
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResult({ label: selectedOption }));
68
+ }
69
+ else {
70
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
71
+ }
72
+ setInputValue(inputBoxVal);
73
+ }, options: (searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.length) ? searchOptions : regionOptions, getOptionDisabled: option => option.group === 'limitOption', filterOptions: (opts, { inputValue }) => getFiltered(opts, inputValue), renderInput: params => (React.createElement(AutocompleteTextField, { showHelp: showHelp, params: params, inputBoxVal: inputBoxVal, TextFieldProps: TextFieldProps, setCurrentSearch: setCurrentSearch, setInputValue: setInputValue })), getOptionLabel: opt => typeof opt === 'string' ? opt : opt.result.getDisplayString() }));
81
74
  });
82
75
  export default RefNameAutocomplete;
@@ -3,6 +3,6 @@ import { LinearGenomeViewModel } from '..';
3
3
  type LGV = LinearGenomeViewModel;
4
4
  declare const Rubberband: ({ model, ControlComponent, }: {
5
5
  model: LGV;
6
- ControlComponent?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined;
6
+ ControlComponent?: React.ReactElement;
7
7
  }) => React.JSX.Element;
8
8
  export default Rubberband;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { LinearGenomeViewModel } from '..';
3
3
  declare const SearchBox: ({ model, showHelp, }: {
4
- showHelp?: boolean | undefined;
4
+ showHelp?: boolean;
5
5
  model: LinearGenomeViewModel;
6
6
  }) => React.JSX.Element;
7
7
  export default SearchBox;
@@ -69,7 +69,7 @@ export default function SearchResultsTable({ searchResults, assemblyName: optAss
69
69
  }
70
70
  catch (e) {
71
71
  console.error(e);
72
- session.notify(`${e}`, 'error');
72
+ session.notifyError(`${e}`, e);
73
73
  }
74
74
  handleClose();
75
75
  }, color: "primary", variant: "contained" }, "Go")))))))));
@@ -5,7 +5,6 @@ import { observer } from 'mobx-react';
5
5
  import { isAlive } from 'mobx-state-tree';
6
6
  import { ErrorBoundary } from 'react-error-boundary';
7
7
  import { ResizeHandle, ErrorMessage } from '@jbrowse/core/ui';
8
- import { useDebouncedCallback } from '@jbrowse/core/util';
9
8
  import TrackLabelContainer from './TrackLabelContainer';
10
9
  import TrackRenderingContainer from './TrackRenderingContainer';
11
10
  const useStyles = makeStyles()({
@@ -18,40 +17,28 @@ const useStyles = makeStyles()({
18
17
  position: 'relative',
19
18
  zIndex: 2,
20
19
  },
21
- overlay: {
22
- pointerEvents: 'none',
23
- position: 'absolute',
24
- top: 0,
25
- left: 0,
26
- width: '100%',
27
- zIndex: 3,
28
- },
29
20
  });
30
21
  const TrackContainer = observer(function ({ model, track, }) {
31
22
  const { classes } = useStyles();
32
23
  const display = track.displays[0];
33
24
  const { draggingTrackId } = model;
34
- const ref2 = useRef(null);
35
- const dimmed = draggingTrackId !== undefined && draggingTrackId !== display.id;
36
- const debouncedOnDragEnter = useDebouncedCallback(() => {
37
- if (isAlive(display) && dimmed) {
38
- model.moveTrack(draggingTrackId, track.id);
39
- }
40
- }, 100);
41
- return (React.createElement(Paper, { ref: ref2, className: classes.root, variant: "outlined", onClick: event => {
25
+ const ref = useRef(null);
26
+ return (React.createElement(Paper, { ref: ref, className: classes.root, variant: "outlined", onClick: event => {
42
27
  var _a;
43
28
  if (event.detail === 2 && !track.displays[0].featureIdUnderMouse) {
44
- const left = ((_a = ref2.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().left) || 0;
29
+ const left = ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().left) || 0;
45
30
  model.zoomTo(model.bpPerPx / 2, event.clientX - left, true);
46
31
  }
47
32
  } },
48
33
  React.createElement(TrackLabelContainer, { track: track, view: model }),
49
34
  React.createElement(ErrorBoundary, { FallbackComponent: e => React.createElement(ErrorMessage, { error: e.error }) },
50
- React.createElement(TrackRenderingContainer, { model: model, track: track, onDragEnter: debouncedOnDragEnter })),
51
- React.createElement("div", { className: classes.overlay, style: {
52
- height: display.height,
53
- background: dimmed ? 'rgba(0, 0, 0, 0.4)' : undefined,
54
- }, onDragEnter: debouncedOnDragEnter }),
35
+ React.createElement(TrackRenderingContainer, { model: model, track: track, onDragEnter: () => {
36
+ if (isAlive(display) &&
37
+ draggingTrackId !== undefined &&
38
+ draggingTrackId !== display.id) {
39
+ model.moveTrack(draggingTrackId, track.id);
40
+ }
41
+ } })),
55
42
  React.createElement(ResizeHandle, { onDrag: display.resizeHeight, className: classes.resizeHandle })));
56
43
  });
57
44
  export default TrackContainer;
@@ -12,6 +12,10 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';
12
12
  import CloseIcon from '@mui/icons-material/Close';
13
13
  import MinimizeIcon from '@mui/icons-material/Minimize';
14
14
  import AddIcon from '@mui/icons-material/Add';
15
+ import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
16
+ import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
17
+ import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
18
+ import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
15
19
  import TrackLabelDragHandle from './TrackLabelDragHandle';
16
20
  const useStyles = makeStyles()(theme => ({
17
21
  root: {
@@ -19,9 +23,6 @@ const useStyles = makeStyles()(theme => ({
19
23
  '&:hover': {
20
24
  background: theme.palette.background.paper,
21
25
  },
22
- transition: theme.transitions.create(['background'], {
23
- duration: theme.transitions.duration.shortest,
24
- }),
25
26
  },
26
27
  trackName: {
27
28
  margin: '0 auto',
@@ -44,9 +45,35 @@ const TrackLabel = observer(React.forwardRef(function TrackLabel2({ track, class
44
45
  const trackName = getTrackName(trackConf, session);
45
46
  const items = [
46
47
  {
47
- label: minimized ? 'Restore track' : 'Minimize track',
48
- icon: minimized ? AddIcon : MinimizeIcon,
49
- onClick: () => track.setMinimized(!minimized),
48
+ label: 'Track order',
49
+ type: 'subMenu',
50
+ subMenu: [
51
+ {
52
+ label: minimized ? 'Restore track' : 'Minimize track',
53
+ icon: minimized ? AddIcon : MinimizeIcon,
54
+ onClick: () => track.setMinimized(!minimized),
55
+ },
56
+ {
57
+ label: 'Move track to top',
58
+ icon: KeyboardDoubleArrowUpIcon,
59
+ onClick: () => view.moveTrackToTop(track.id),
60
+ },
61
+ {
62
+ label: 'Move track up',
63
+ icon: KeyboardArrowUpIcon,
64
+ onClick: () => view.moveTrackUp(track.id),
65
+ },
66
+ {
67
+ label: 'Move track down',
68
+ icon: KeyboardArrowDownIcon,
69
+ onClick: () => view.moveTrackDown(track.id),
70
+ },
71
+ {
72
+ label: 'Move track to bottom',
73
+ icon: KeyboardDoubleArrowDownIcon,
74
+ onClick: () => view.moveTrackToBottom(track.id),
75
+ },
76
+ ],
50
77
  },
51
78
  ...(((_a = session.getTrackActionMenuItems) === null || _a === void 0 ? void 0 : _a.call(session, trackConf)) || []),
52
79
  ...track.trackMenuItems(),
@@ -13,7 +13,7 @@ import Gridlines from './Gridlines';
13
13
  import CenterLine from './CenterLine';
14
14
  import VerticalGuide from './VerticalGuide';
15
15
  import RubberbandSpan from './RubberbandSpan';
16
- import Highlight from './Highlight';
16
+ import HighlightGroup from './Highlight';
17
17
  const useStyles = makeStyles()({
18
18
  tracksContainer: {
19
19
  position: 'relative',
@@ -40,7 +40,7 @@ const TracksContainer = observer(function TracksContainer({ children, model, })
40
40
  top: anchorPosition.clientY,
41
41
  }, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null,
42
42
  React.createElement(Rubberband, { model: model, ControlComponent: React.createElement(Scalebar, { model: model, style: { height: SCALE_BAR_HEIGHT, boxSizing: 'border-box' } }) }),
43
- React.createElement(Highlight, { model: model }),
43
+ React.createElement(HighlightGroup, { model: model }),
44
44
  additional,
45
45
  children));
46
46
  });
@@ -17,16 +17,16 @@ const useStyles = makeStyles()(theme => ({
17
17
  }));
18
18
  const ZoomControls = observer(function ({ model, }) {
19
19
  const { classes } = useStyles();
20
- const { maxBpPerPx, minBpPerPx, bpPerPx, scaleFactor } = model;
20
+ const { maxBpPerPx, minBpPerPx, bpPerPx } = model;
21
21
  const [value, setValue] = useState(-Math.log2(bpPerPx) * 100);
22
22
  useEffect(() => {
23
23
  setValue(-Math.log2(bpPerPx) * 100);
24
24
  }, [setValue, bpPerPx]);
25
25
  return (React.createElement("div", { className: classes.container },
26
- React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => model.zoom(bpPerPx * 2), disabled: bpPerPx >= maxBpPerPx - 0.0001 || scaleFactor !== 1, size: "large" },
26
+ React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => model.zoom(bpPerPx * 2), disabled: bpPerPx >= maxBpPerPx - 0.0001, size: "large" },
27
27
  React.createElement(ZoomOut, null)),
28
- React.createElement(Slider, { size: "small", className: classes.slider, value: value, min: -Math.log2(maxBpPerPx) * 100, max: -Math.log2(minBpPerPx) * 100, onChange: (_, val) => setValue(val), onChangeCommitted: () => model.zoomTo(2 ** (-value / 100)), disabled: scaleFactor !== 1 }),
29
- React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => model.zoom(model.bpPerPx / 2), disabled: bpPerPx <= minBpPerPx + 0.0001 || scaleFactor !== 1, size: "large" },
28
+ React.createElement(Slider, { size: "small", className: classes.slider, value: value, min: -Math.log2(maxBpPerPx) * 100, max: -Math.log2(minBpPerPx) * 100, onChange: (_, val) => setValue(val), onChangeCommitted: () => model.zoomTo(2 ** (-value / 100)) }),
29
+ React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => model.zoom(model.bpPerPx / 2), disabled: bpPerPx <= minBpPerPx + 0.0001, size: "large" },
30
30
  React.createElement(ZoomIn, null))));
31
31
  });
32
32
  export default ZoomControls;