@jbrowse/plugin-linear-genome-view 5.0.0-beta.13 → 5.0.0-beta.16

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 (63) hide show
  1. package/esm/FeatureTrack/configSchema.d.ts +2 -1
  2. package/esm/FeatureTrack/configSchema.js +2 -1
  3. package/esm/LinearGenomeView/afterAttach.js +1 -23
  4. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +6 -2
  5. package/esm/LinearGenomeView/components/ExportSvgDialog.js +31 -7
  6. package/esm/LinearGenomeView/components/HeaderPanControls.d.ts +3 -3
  7. package/esm/LinearGenomeView/components/HeaderPanControls.js +4 -2
  8. package/esm/LinearGenomeView/components/HighlightBand.d.ts +3 -2
  9. package/esm/LinearGenomeView/components/HighlightBand.js +4 -2
  10. package/esm/LinearGenomeView/components/HighlightChip.d.ts +3 -2
  11. package/esm/LinearGenomeView/components/HighlightChip.js +4 -2
  12. package/esm/LinearGenomeView/components/LinearGenomeView.js +5 -12
  13. package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +4 -5
  14. package/esm/LinearGenomeView/components/OverviewHighlightBand.d.ts +3 -2
  15. package/esm/LinearGenomeView/components/OverviewHighlightBand.js +4 -2
  16. package/esm/LinearGenomeView/components/RegionWidthEditorDialog.d.ts +3 -2
  17. package/esm/LinearGenomeView/components/RegionWidthEditorDialog.js +4 -4
  18. package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +4 -4
  19. package/esm/LinearGenomeView/components/SearchPanelForm.d.ts +3 -2
  20. package/esm/LinearGenomeView/components/SearchPanelForm.js +4 -2
  21. package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +4 -9
  22. package/esm/LinearGenomeView/components/SearchResultsDialog.js +7 -4
  23. package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +3 -8
  24. package/esm/LinearGenomeView/components/SearchResultsTable.js +4 -32
  25. package/esm/LinearGenomeView/components/StrandCheckboxes.d.ts +3 -2
  26. package/esm/LinearGenomeView/components/StrandCheckboxes.js +4 -2
  27. package/esm/LinearGenomeView/components/TrackContainer.js +1 -1
  28. package/esm/LinearGenomeView/components/TrackLabelMenu.js +8 -12
  29. package/esm/LinearGenomeView/components/useRangeSelect.js +3 -2
  30. package/esm/LinearGenomeView/components/useSearchBoxPrefs.d.ts +3 -1
  31. package/esm/LinearGenomeView/components/useSearchBoxPrefs.js +4 -2
  32. package/esm/LinearGenomeView/components/useSideScroll.js +11 -12
  33. package/esm/LinearGenomeView/lazyDialogs.d.ts +17 -2
  34. package/esm/LinearGenomeView/menuItems.js +32 -24
  35. package/esm/LinearGenomeView/model.d.ts +197 -151
  36. package/esm/LinearGenomeView/model.js +203 -201
  37. package/esm/LinearGenomeView/rExport/exportLinearView.d.ts +13 -0
  38. package/esm/LinearGenomeView/rExport/exportLinearView.js +98 -0
  39. package/esm/LinearGenomeView/rExport/rScript.d.ts +75 -0
  40. package/esm/LinearGenomeView/rExport/rScript.js +465 -0
  41. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +3 -6
  42. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.d.ts +1 -2
  43. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.js +2 -2
  44. package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +1 -2
  45. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +4 -4
  46. package/esm/LinearGenomeView/svgcomponents/SVGView.d.ts +1 -2
  47. package/esm/LinearGenomeView/svgcomponents/SVGView.js +3 -3
  48. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.d.ts +22 -20
  49. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +42 -29
  50. package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +6 -11
  51. package/esm/LinearGenomeView/svgcomponents/util.d.ts +2 -1
  52. package/esm/LinearGenomeView/util.d.ts +42 -0
  53. package/esm/LinearGenomeView/util.js +81 -0
  54. package/esm/MultiLevelRubberband/MultiLevelRubberband.js +2 -2
  55. package/esm/displayKitTests/perRegionTestEnv.d.ts +45 -12
  56. package/esm/index.d.ts +4 -2
  57. package/esm/index.js +3 -1
  58. package/esm/searchUtils.js +1 -0
  59. package/esm/shared/RubberbandSpan.d.ts +3 -2
  60. package/esm/shared/RubberbandSpan.js +4 -2
  61. package/esm/shared/coordLabels.d.ts +6 -6
  62. package/esm/shared/coordLabels.js +7 -6
  63. package/package.json +9 -9
@@ -2,7 +2,8 @@ import type PluginManager from '@jbrowse/core/PluginManager';
2
2
  /**
3
3
  * #config FeatureTrack
4
4
  * #category track
5
- * used for basic gene and feature tracks, generally used with LinearBasicDisplay
5
+ * Holds gene and other feature annotations, such as a GFF3 or BED file, which
6
+ * `LinearBasicDisplay` usually draws.
6
7
  *
7
8
  * #example
8
9
  * A minimal hand-authored entry in the top-level `tracks` array — `adapter`
@@ -3,7 +3,8 @@ import { createBaseTrackConfig } from '@jbrowse/core/pluggableElementTypes';
3
3
  /**
4
4
  * #config FeatureTrack
5
5
  * #category track
6
- * used for basic gene and feature tracks, generally used with LinearBasicDisplay
6
+ * Holds gene and other feature annotations, such as a GFF3 or BED file, which
7
+ * `LinearBasicDisplay` usually draws.
7
8
  *
8
9
  * #example
9
10
  * A minimal hand-authored entry in the top-level `tracks` array — `adapter`
@@ -1,4 +1,4 @@
1
- import { getSession, isSessionModelWithWidgets, localStorageSetItem, resolveNamedRegions, } from '@jbrowse/core/util';
1
+ import { getSession, isSessionModelWithWidgets, resolveNamedRegions, } from '@jbrowse/core/util';
2
2
  import { coerceHighlight } from '@jbrowse/core/util/highlights';
3
3
  import { installInitAutorun } from '@jbrowse/core/util/installInitAutorun';
4
4
  import { normalizeTrackInit, warmTrackDisplayGeneric, } from '@jbrowse/core/util/tracks';
@@ -258,27 +258,6 @@ function setupCoarseDynamicBlocksAutorun(self) {
258
258
  name: 'LGVCoarseDynamicBlocks',
259
259
  }));
260
260
  }
261
- /**
262
- * Autorun that syncs view settings to localStorage
263
- */
264
- function setupLocalStorageAutorun(self) {
265
- addDisposer(self, autorun(function localStorageAutorun() {
266
- const s = (s) => JSON.stringify(s);
267
- const { showCytobands, showCenterLine, colorByCDS, showAminoAcids, showTrackOutlines, trackLabels, } = self;
268
- localStorageSetItem('lgv-showCytobands', s(showCytobands));
269
- localStorageSetItem('lgv-showCenterLine', s(showCenterLine));
270
- localStorageSetItem('lgv-colorByCDS', s(colorByCDS));
271
- localStorageSetItem('lgv-showAminoAcids', s(showAminoAcids));
272
- localStorageSetItem('lgv-showTrackOutlines', s(showTrackOutlines));
273
- // skip writing the empty default — otherwise reads later round-trip to
274
- // '' instead of null, hiding the config-fallback path
275
- if (trackLabels) {
276
- localStorageSetItem('lgv-trackLabels', trackLabels);
277
- }
278
- }, {
279
- name: 'LGVLocalStorage',
280
- }));
281
- }
282
261
  /**
283
262
  * Sets up all afterAttach autoruns for the LinearGenomeView
284
263
  */
@@ -286,5 +265,4 @@ export function doAfterAttach(self) {
286
265
  setupInitWarmup(self);
287
266
  setupInitAutorun(self);
288
267
  setupCoarseDynamicBlocksAutorun(self);
289
- setupLocalStorageAutorun(self);
290
268
  }
@@ -1,12 +1,16 @@
1
1
  import type { TrackLabelMode, ViewTrackLabelMode } from '../types.ts';
2
2
  import type { BaseExportSvgOptions } from '@jbrowse/core/ui';
3
- export default function ExportSvgDialog({ model, handleClose, }: {
3
+ declare const ExportSvgDialog: ({ model, handleClose, chooseWidth, }: {
4
4
  model: {
5
+ width?: number;
5
6
  effectiveTrackLabels?: ViewTrackLabelMode;
6
7
  exportSvg(opts: BaseExportSvgOptions & {
7
8
  trackLabels: TrackLabelMode;
8
9
  showGridlines: boolean;
10
+ viewWidth?: number;
9
11
  }): Promise<unknown>;
10
12
  };
11
13
  handleClose: () => void;
12
- }): import("react").JSX.Element;
14
+ chooseWidth?: boolean;
15
+ }) => import("react").JSX.Element;
16
+ export default ExportSvgDialog;
@@ -1,18 +1,42 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
2
3
  import { BaseExportSvgDialog, LabeledCheckbox, useExportSvgPreference, } from '@jbrowse/core/ui';
3
4
  import { MenuItem, TextField } from '@mui/material';
5
+ import { observer } from 'mobx-react';
6
+ const MIN_WIDTH = 100;
7
+ const MAX_WIDTH = 10000;
4
8
  // Shared track-label + gridlines export dialog. Used by LGV, linear-synteny and
5
- // breakpoint-split views (their lazyDialogs re-export this).
6
- export default function ExportSvgDialog({ model, handleClose, }) {
9
+ // breakpoint-split views (their lazyDialogs re-export this). Only a caller
10
+ // passing `chooseWidth` offers the width field, whose model lays itself out at
11
+ // `viewWidth`.
12
+ const ExportSvgDialog = observer(function ExportSvgDialog({ model, handleClose, chooseWidth, }) {
7
13
  // The view's own setting is already an export mode, so a user who has not
8
14
  // picked one here gets the figure their screen shows. The key is `-mode`
9
15
  // because the modes were once spelled `overlay`/`none`, and a stored one of
10
16
  // those would select nothing in the dropdown.
11
17
  const [trackLabels, setTrackLabels] = useExportSvgPreference('tracklabels-mode', model.effectiveTrackLabels ?? 'offset');
12
18
  const [showGridlines, setShowGridlines] = useExportSvgPreference('gridlines', false);
13
- return (_jsx(BaseExportSvgDialog, { model: model, handleClose: handleClose, exportSvg: opts => model.exportSvg({ ...opts, trackLabels, showGridlines }), checkboxes: _jsx(LabeledCheckbox, { checked: showGridlines, onChange: val => {
19
+ // not a stored preference: a remembered width would drift from the screen's
20
+ const [widthText, setWidthText] = useState(() => String(Math.round(model.width ?? 0)));
21
+ const typedWidth = Number(widthText);
22
+ const widthValid = Number.isInteger(typedWidth) &&
23
+ typedWidth >= MIN_WIDTH &&
24
+ typedWidth <= MAX_WIDTH;
25
+ return (_jsxs(BaseExportSvgDialog, { model: model, handleClose: handleClose, exportSvg: opts => model.exportSvg({
26
+ ...opts,
27
+ trackLabels,
28
+ showGridlines,
29
+ viewWidth: chooseWidth && widthValid ? typedWidth : undefined,
30
+ }), checkboxes: _jsx(LabeledCheckbox, { checked: showGridlines, onChange: val => {
14
31
  setShowGridlines(val);
15
- }, label: "Show gridlines" }), children: _jsxs(TextField, { select: true, label: "Track label positioning", variant: "outlined", style: { minWidth: 200 }, value: trackLabels, onChange: event => {
16
- setTrackLabels(event.target.value);
17
- }, children: [_jsx(MenuItem, { value: "offset", children: "Offset" }), _jsx(MenuItem, { value: "overlapping", children: "Overlapping" }), _jsx(MenuItem, { value: "left", children: "Left" }), _jsx(MenuItem, { value: "hidden", children: "Hidden" })] }) }));
18
- }
32
+ }, label: "Show gridlines" }), children: [_jsxs(TextField, { select: true, label: "Track label positioning", variant: "outlined", style: { minWidth: 200 }, value: trackLabels, onChange: event => {
33
+ setTrackLabels(event.target.value);
34
+ }, children: [_jsx(MenuItem, { value: "offset", children: "Offset" }), _jsx(MenuItem, { value: "overlapping", children: "Overlapping" }), _jsx(MenuItem, { value: "left", children: "Left" }), _jsx(MenuItem, { value: "hidden", children: "Hidden" })] }), chooseWidth ? (_jsx(TextField, { label: "Width (px)", variant: "outlined", type: "number", value: widthText, error: !widthValid, helperText: widthValid
35
+ ? 'Lays the tracks out as a window this wide would; text keeps its size'
36
+ : `A whole number from ${MIN_WIDTH} to ${MAX_WIDTH}`, slotProps: {
37
+ htmlInput: { min: MIN_WIDTH, max: MAX_WIDTH, step: 1 },
38
+ }, onChange: event => {
39
+ setWidthText(event.target.value);
40
+ } })) : null] }));
41
+ });
42
+ export default ExportSvgDialog;
@@ -1,7 +1,7 @@
1
1
  import type { LinearGenomeViewModel } from '../index.ts';
2
2
  type LGV = LinearGenomeViewModel;
3
- export default function HeaderPanControls({ model, compact, }: {
3
+ declare const HeaderPanControls: ({ model, compact, }: {
4
4
  model: LGV;
5
5
  compact?: boolean;
6
- }): import("react").JSX.Element;
7
- export {};
6
+ }) => import("react").JSX.Element;
7
+ export default HeaderPanControls;
@@ -4,6 +4,7 @@ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
4
  import ArrowBackIcon from '@mui/icons-material/ArrowBack';
5
5
  import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
6
6
  import { Button } from '@mui/material';
7
+ import { observer } from 'mobx-react';
7
8
  import { SPACING, WIDGET_HEIGHT } from '../consts.js';
8
9
  const useStyles = makeStyles()(theme => ({
9
10
  panButton: {
@@ -20,7 +21,7 @@ const useStyles = makeStyles()(theme => ({
20
21
  padding: theme.spacing(0, 0.5),
21
22
  },
22
23
  }));
23
- export default function HeaderPanControls({ model, compact, }) {
24
+ const HeaderPanControls = observer(function HeaderPanControls({ model, compact, }) {
24
25
  const { classes, cx } = useStyles();
25
26
  const className = cx(classes.panButton, compact && classes.compact);
26
27
  return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outlined", "aria-label": "Pan left", className: className, onClick: () => {
@@ -28,4 +29,5 @@ export default function HeaderPanControls({ model, compact, }) {
28
29
  }, children: _jsx(ArrowBackIcon, {}) }), _jsx(Button, { variant: "outlined", "aria-label": "Pan right", className: className, onClick: () => {
29
30
  model.slide(0.9);
30
31
  }, children: _jsx(ArrowForwardIcon, {}) })] }));
31
- }
32
+ });
33
+ export default HeaderPanControls;
@@ -2,7 +2,7 @@ export declare function gapMask(gaps: {
2
2
  top: number;
3
3
  height: number;
4
4
  }[]): string;
5
- export default function HighlightBand({ coords, background, label, gaps, children, }: {
5
+ declare const HighlightBand: ({ coords, background, label, gaps, children, }: {
6
6
  coords: {
7
7
  left: number;
8
8
  width: number;
@@ -14,4 +14,5 @@ export default function HighlightBand({ coords, background, label, gaps, childre
14
14
  height: number;
15
15
  }[];
16
16
  children?: React.ReactNode;
17
- }): import("react").JSX.Element;
17
+ }) => import("react").JSX.Element;
18
+ export default HighlightBand;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { makeStyles } from '@jbrowse/core/util/tss-react';
3
3
  import { Typography } from '@mui/material';
4
+ import { observer } from 'mobx-react';
4
5
  const useStyles = makeStyles()(theme => ({
5
6
  highlight: {
6
7
  height: '100%',
@@ -37,7 +38,7 @@ export function gapMask(gaps) {
37
38
  // colored band with an
38
39
  // optional top label; when `children` (the highlight chip) is passed it renders
39
40
  // that instead of the plain label. Visibility is toggled from the view menu
40
- export default function HighlightBand({ coords, background, label, gaps, children, }) {
41
+ const HighlightBand = observer(function HighlightBand({ coords, background, label, gaps, children, }) {
41
42
  const { classes } = useStyles();
42
43
  const content = children ??
43
44
  (label ? (_jsx(Typography, { variant: "caption", className: classes.label, noWrap: true, children: label })) : null);
@@ -49,4 +50,5 @@ export default function HighlightBand({ coords, background, label, gaps, childre
49
50
  ? { maskImage: gapMask(gaps), WebkitMaskImage: gapMask(gaps) }
50
51
  : undefined),
51
52
  }, children: coords.width > 3 && content ? (_jsx("div", { className: classes.overlay, children: content })) : null }));
52
- }
53
+ });
54
+ export default HighlightBand;
@@ -1,8 +1,9 @@
1
1
  import type { MenuItem } from '@jbrowse/core/ui';
2
2
  import type { Colord } from '@jbrowse/core/util/colord';
3
- export default function HighlightChip({ color, label, tooltip, menuItems, }: {
3
+ declare const HighlightChip: ({ color, label, tooltip, menuItems, }: {
4
4
  color: Colord;
5
5
  label?: string;
6
6
  tooltip?: string;
7
7
  menuItems: MenuItem[];
8
- }): import("react").JSX.Element;
8
+ }) => import("react").JSX.Element;
9
+ export default HighlightChip;
@@ -2,12 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
3
3
  import LinkIcon from '@mui/icons-material/Link';
4
4
  import { Box, Tooltip, Typography } from '@mui/material';
5
+ import { observer } from 'mobx-react';
5
6
  // Interactive chip drawn inside a highlight band: a link icon tinted to the
6
7
  // band color plus an optional inline label, wrapped in a context menu. Sets its
7
8
  // own pointer-events since the band is click-through.
8
- export default function HighlightChip({ color, label, tooltip, menuItems, }) {
9
+ const HighlightChip = observer(function HighlightChip({ color, label, tooltip, menuItems, }) {
9
10
  // hide the icon color when the band is fully transparent ("label-only"
10
11
  // highlight); otherwise bump to 0.8 alpha so the chip stays legible
11
12
  const chipAlpha = color.alpha() === 0 ? 0 : 0.8;
12
13
  return (_jsx(Box, { sx: { pointerEvents: 'auto' }, children: _jsx(CascadingMenuButton, { "data-testid": "highlight-chip", menuItems: menuItems, children: _jsx(Tooltip, { title: tooltip, arrow: true, children: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.5 }, children: [_jsx(LinkIcon, { fontSize: "small", sx: { color: color.alpha(chipAlpha).toRgbString() } }), label ? (_jsx(Typography, { variant: "caption", noWrap: true, children: label })) : null] }) }) }) }));
13
- }
14
+ });
15
+ export default HighlightChip;
@@ -1,20 +1,13 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { lazy } from 'react';
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Suspense, lazy } from 'react';
3
3
  import { ViewLoadingScreen } from '@jbrowse/core/ui';
4
4
  import { observer } from 'mobx-react';
5
5
  import LinearGenomeViewContainer from './LinearGenomeViewContainer.js';
6
6
  // lazies
7
7
  const ImportForm = lazy(() => import('./ImportForm.js'));
8
+ const SearchResultsDialog = lazy(() => import('./SearchResultsDialog.js'));
8
9
  const LinearGenomeView = observer(function LinearGenomeView({ model, }) {
9
- const { loading, showImportForm } = model;
10
- if (loading) {
11
- return _jsx(ViewLoadingScreen, { ...loading });
12
- }
13
- else if (showImportForm) {
14
- return _jsx(ImportForm, { model: model });
15
- }
16
- else {
17
- return _jsx(LinearGenomeViewContainer, { model: model });
18
- }
10
+ const { loading, showImportForm, searchPicker } = model;
11
+ return (_jsxs(_Fragment, { children: [loading ? (_jsx(ViewLoadingScreen, { ...loading })) : showImportForm ? (_jsx(ImportForm, { model: model })) : (_jsx(LinearGenomeViewContainer, { model: model })), searchPicker ? (_jsx(Suspense, { fallback: null, children: _jsx(SearchResultsDialog, { model: model }) })) : null] }));
19
12
  });
20
13
  export default LinearGenomeView;
@@ -105,11 +105,10 @@ const LinearGenomeViewContainer = observer(function LinearGenomeViewContainer({
105
105
  session.setHovered({ hoverPosition, hoverFeature });
106
106
  }, children: [_jsx(NavigationAnnouncer, { model: model }), _jsxs("div", { ref: headerRef, "data-testid": `lgv-header-${model.id}`, className: classes.header, style: { position: stickyViewHeaders ? 'sticky' : undefined }, children: [_jsx(Header, { model: model }), hideHeader ? _jsx(MiniControls, { model: model }) : null] }), _jsx("div", { ref: tracksRef, children: model.scalebarOnly ? (_jsx(Rubberband, { model: model })) : (_jsx(TracksContainer, { model: model, children: !tracks.length ? (_jsx(NoTracksActiveButton, { model: model })) : (_jsxs(_Fragment, { children: [pinnedTracks.length ? (_jsx(Paper, { elevation: 2, "data-testid": "pinned-tracks", className: classes.pinnedTracks, style: {
107
107
  top: stickyTops.pinnedTracks,
108
- // the scroll port this sticks in, not the window: it is
109
- // under the app bar in the classic stack, a dockview
110
- // cell in a workspace, and the host's box when embedded.
111
- // The fallback is what an unbounded embed gets, where
112
- // the page itself is the scroller
108
+ // the scroll port this sticks in, not the window: a
109
+ // workspace cell, or the host's box when embedded. The
110
+ // fallback is what an unbounded embed gets, where the
111
+ // page itself is the scroller
113
112
  maxHeight: `calc(var(${SCROLL_PORT_HEIGHT_VAR}, 100vh) - ${stickyTops.pinnedTracks})`,
114
113
  }, children: pinnedTracks.map(track => (_jsx(TrackContainer, { model: model, track: track }, track.id))) })) : null, unpinnedTracks.map(track => (_jsx(TrackContainer, { model: model, track: track }, track.id))), _jsx(ResizeAllTracksHandle, { model: model })] })) })) })] }) }));
115
114
  });
@@ -1,4 +1,4 @@
1
- export default function OverviewHighlightBand({ coords, background, borderColor, tooltip, }: {
1
+ declare const OverviewHighlightBand: ({ coords, background, borderColor, tooltip, }: {
2
2
  coords: {
3
3
  left: number;
4
4
  width: number;
@@ -6,4 +6,5 @@ export default function OverviewHighlightBand({ coords, background, borderColor,
6
6
  background: string;
7
7
  borderColor?: string;
8
8
  tooltip?: string;
9
- }): import("react").JSX.Element;
9
+ }) => import("react").JSX.Element;
10
+ export default OverviewHighlightBand;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { makeStyles } from '@jbrowse/core/util/tss-react';
3
3
  import { Tooltip } from '@mui/material';
4
+ import { observer } from 'mobx-react';
4
5
  // A single absolute-positioned band over the overview scalebar. The 1px border
5
6
  // keeps very narrow bands visible.
6
7
  const useStyles = makeStyles()({
@@ -10,7 +11,7 @@ const useStyles = makeStyles()({
10
11
  left: 0,
11
12
  },
12
13
  });
13
- export default function OverviewHighlightBand({ coords, background, borderColor, tooltip, }) {
14
+ const OverviewHighlightBand = observer(function OverviewHighlightBand({ coords, background, borderColor, tooltip, }) {
14
15
  const { classes } = useStyles();
15
16
  const band = (_jsx("div", { className: classes.highlight, style: {
16
17
  transform: `translateX(${coords.left}px)`,
@@ -22,4 +23,5 @@ export default function OverviewHighlightBand({ coords, background, borderColor,
22
23
  }),
23
24
  } }));
24
25
  return tooltip ? (_jsx(Tooltip, { title: tooltip, arrow: true, children: band })) : (band);
25
- }
26
+ });
27
+ export default OverviewHighlightBand;
@@ -1,5 +1,6 @@
1
1
  import type { LinearGenomeViewModel } from '../model.ts';
2
- export default function RegionWidthEditorDialog({ model, handleClose, }: {
2
+ declare const RegionWidthEditorDialog: ({ model, handleClose, }: {
3
3
  model: LinearGenomeViewModel;
4
4
  handleClose: () => void;
5
- }): import("react").JSX.Element;
5
+ }) => import("react").JSX.Element;
6
+ export default RegionWidthEditorDialog;
@@ -3,12 +3,11 @@ import { useState } from 'react';
3
3
  import { SubmitDialog } from '@jbrowse/core/ui';
4
4
  import { parseBpString, toLocale } from '@jbrowse/core/util';
5
5
  import { TextField, Typography } from '@mui/material';
6
+ import { observer } from 'mobx-react';
6
7
  function format(n) {
7
8
  return toLocale(Math.floor(n));
8
9
  }
9
- // not an observer: the initial value is seeded once and the field is
10
- // user-edited thereafter, so it shouldn't reactively reset if bpPerPx changes
11
- export default function RegionWidthEditorDialog({ model, handleClose, }) {
10
+ const RegionWidthEditorDialog = observer(function RegionWidthEditorDialog({ model, handleClose, }) {
12
11
  const [val, setVal] = useState(() => format(model.bpPerPx * model.width));
13
12
  const bp = parseBpString(val);
14
13
  const valid = bp !== undefined && bp > 0;
@@ -20,4 +19,5 @@ export default function RegionWidthEditorDialog({ model, handleClose, }) {
20
19
  }, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 30 }, children: [_jsx(Typography, { children: "Enter a specific number of base pairs to change the viewport to show, either in full or abbreviated as e.g. 500kb or 1.5Mbp. This is approximate and does not account for padding between regions or off-screen scrolling" }), _jsx(TextField, { label: "Zoom level (bp)", autoFocus: true, fullWidth: true, variant: "outlined", value: val, onChange: event => {
21
20
  setVal(event.target.value);
22
21
  } })] }) }));
23
- }
22
+ });
23
+ export default RegionWidthEditorDialog;
@@ -54,9 +54,9 @@ const ScalebarRefNameLabels = observer(function ScalebarRefNameLabels({ model, }
54
54
  setMenuState(undefined);
55
55
  } })) : null] }));
56
56
  });
57
- // Not an observer: `label` is plain data from the parent. The attribute tells
58
- // the scalebar's rubberband to leave a click here to this label's onClick.
59
- function RefLabel({ model, label, onOpenMenu, }) {
57
+ // The attribute tells the scalebar's rubberband to leave a click here to this
58
+ // label's onClick.
59
+ const RefLabel = observer(function RefLabel({ model, label, onOpenMenu, }) {
60
60
  const { classes, cx } = useStyles();
61
61
  const { refName, transform, maxWidth, paddingLeft, text } = label;
62
62
  return (_jsx("span", { className: cx(classes.refLabel, classes.clickable), style: {
@@ -67,7 +67,7 @@ function RefLabel({ model, label, onOpenMenu, }) {
67
67
  model.setIsScalebarRefNameMenuOpen(true);
68
68
  onOpenMenu({ anchorEl: e.currentTarget, label });
69
69
  }, children: text }));
70
- }
70
+ });
71
71
  const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }) {
72
72
  const { displayedRegions } = model;
73
73
  const { refName, displayedRegionIndex: idx, lastDisplayedRegionIndex: lastIdx, } = menuState.label;
@@ -1,9 +1,10 @@
1
1
  import type { ReactNode } from 'react';
2
- export default function SearchPanelForm({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }: {
2
+ declare const SearchPanelForm: ({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }: {
3
3
  onSubmit: () => void;
4
4
  handleClose: () => void;
5
5
  submitDisabled: boolean;
6
6
  submitText?: string;
7
7
  actions?: ReactNode;
8
8
  children: ReactNode;
9
- }): import("react").JSX.Element;
9
+ }) => import("react").JSX.Element;
10
+ export default SearchPanelForm;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { SubmitForm } from '@jbrowse/core/ui';
3
3
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { observer } from 'mobx-react';
4
5
  const useStyles = makeStyles()({
5
6
  dialogContent: {
6
7
  width: '34em',
@@ -12,7 +13,8 @@ const useStyles = makeStyles()({
12
13
  // The chrome shared by the sequence-search mode panels: the fixed-width column
13
14
  // their fields lay out in, plus SubmitForm's Cancel/Submit footer. Each panel
14
15
  // still owns its own fields and its own submit, so this is only the frame.
15
- export default function SearchPanelForm({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }) {
16
+ const SearchPanelForm = observer(function SearchPanelForm({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }) {
16
17
  const { classes } = useStyles();
17
18
  return (_jsx(SubmitForm, { contentClassName: classes.dialogContent, onSubmit: onSubmit, onCancel: handleClose, submitDisabled: submitDisabled, submitText: submitText, actions: actions, children: children }));
18
- }
19
+ });
20
+ export default SearchPanelForm;
@@ -1,10 +1,5 @@
1
- import type { LinearGenomeViewModel } from '../../index.ts';
2
- import type BaseResult from '@jbrowse/core/TextSearch/BaseResults';
3
- export default function SearchResultsDialog({ model, assemblyName, searchQuery, searchResults, handleClose, onPick, }: {
1
+ import type { LinearGenomeViewModel } from '../index.ts';
2
+ declare const SearchResultsDialog: ({ model, }: {
4
3
  model: LinearGenomeViewModel;
5
- assemblyName: string;
6
- searchQuery: string;
7
- searchResults: BaseResult[];
8
- handleClose: () => void;
9
- onPick: (result: BaseResult) => Promise<unknown>;
10
- }): import("react").JSX.Element;
4
+ }) => import("react").JSX.Element | null;
5
+ export default SearchResultsDialog;
@@ -1,7 +1,10 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { InfoDialog } from '@jbrowse/core/ui';
3
3
  import { Typography } from '@mui/material';
4
+ import { observer } from 'mobx-react';
4
5
  import SearchResultsTable from './SearchResultsTable.js';
5
- export default function SearchResultsDialog({ model, assemblyName, searchQuery, searchResults, handleClose, onPick, }) {
6
- return (_jsx(InfoDialog, { open: true, maxWidth: "xl", onClose: handleClose, title: "Search results", children: !searchResults.length ? (_jsxs(Typography, { children: ["No results found for ", _jsx("b", { children: searchQuery })] })) : (_jsxs(_Fragment, { children: [_jsxs(Typography, { children: ["Showing results for ", _jsx("b", { children: searchQuery })] }), _jsx(SearchResultsTable, { model: model, handleClose: handleClose, assemblyName: assemblyName, searchResults: searchResults, onPick: onPick })] })) }));
7
- }
6
+ const SearchResultsDialog = observer(function SearchResultsDialog({ model, }) {
7
+ const { searchPicker } = model;
8
+ return searchPicker ? (_jsxs(InfoDialog, { open: true, maxWidth: "xl", onClose: model.closeSearchPicker, title: "Search results", children: [_jsxs(Typography, { children: ["Showing results for ", _jsx("b", { children: searchPicker.query })] }), _jsx(SearchResultsTable, { picker: searchPicker })] })) : null;
9
+ });
10
+ export default SearchResultsDialog;
@@ -1,10 +1,5 @@
1
- import type { LinearGenomeViewModel } from '../../index.ts';
2
- import type BaseResult from '@jbrowse/core/TextSearch/BaseResults';
3
- declare const SearchResultsTable: ({ searchResults, assemblyName, model, handleClose, onPick, }: {
4
- searchResults: BaseResult[];
5
- assemblyName: string;
6
- model: LinearGenomeViewModel;
7
- handleClose: () => void;
8
- onPick: (result: BaseResult) => Promise<unknown>;
1
+ import type { SearchPickerState } from '../model.ts';
2
+ declare const SearchResultsTable: ({ picker, }: {
3
+ picker: SearchPickerState;
9
4
  }) => import("react").JSX.Element;
10
5
  export default SearchResultsTable;
@@ -1,38 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { canonicalLocString } from '@jbrowse/core/TextSearch/places';
3
2
  import { SanitizedHTML } from '@jbrowse/core/ui';
4
- import { getSession } from '@jbrowse/core/util';
5
3
  import { Button, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, } from '@mui/material';
6
4
  import { observer } from 'mobx-react';
7
- import { isOpenInView } from '../../searchUtils.js';
8
- const SearchResultsTable = observer(function SearchResultsTable({ searchResults, assemblyName, model, handleClose, onPick, }) {
9
- const session = getSession(model);
10
- const { assemblyManager } = session;
11
- const assembly = assemblyManager.get(assemblyName);
12
- function getTrackName(trackId) {
13
- const conf = trackId !== undefined ? session.getTrackById(trackId) : undefined;
14
- return conf?.name ?? '';
15
- }
16
- // A location the assembly can't resolve is listed as-is: Go navigates
17
- // through navToOption, which reports its own failure
18
- function formatLocation(locString) {
19
- return assembly && locString
20
- ? canonicalLocString(locString, assembly)
21
- : locString;
22
- }
23
- // A hit in a track that is already on screen is usually the one meant, so it
24
- // is listed first rather than given a control that says so. sort is stable,
25
- // so everything else keeps the ranked order it arrived in.
26
- const ordered = [...searchResults].sort((a, b) => Number(isOpenInView(b, model)) - Number(isOpenInView(a, model)));
27
- return (_jsx(TableContainer, { component: Paper, children: _jsxs(Table, { children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Name" }), _jsx(TableCell, { align: "right", children: "Location" }), _jsx(TableCell, { align: "right", children: "Track" }), _jsx(TableCell, { align: "right" })] }) }), _jsx(TableBody, { children: ordered.map(result => (_jsxs(TableRow, { children: [_jsx(TableCell, { component: "th", scope: "row", children: result.getLabel() }), _jsx(TableCell, { align: "right", children: formatLocation(result.getLocation()) }), _jsx(TableCell, { align: "right", children: _jsx(SanitizedHTML, { html: getTrackName(result.getTrackId()) || 'N/A' }) }), _jsx(TableCell, { align: "right", children: _jsx(Button, { onClick: async () => {
28
- try {
29
- await onPick(result);
30
- }
31
- catch (e) {
32
- console.error(e);
33
- session.notifyError(`${e}`, e);
34
- }
35
- handleClose();
36
- }, color: "primary", variant: "contained", children: "Go" }) })] }, result.getId()))) })] }) }));
5
+ const SearchResultsTable = observer(function SearchResultsTable({ picker, }) {
6
+ return (_jsx(TableContainer, { component: Paper, children: _jsxs(Table, { children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Name" }), _jsx(TableCell, { align: "right", children: "Location" }), _jsx(TableCell, { align: "right", children: "Track" }), _jsx(TableCell, { align: "right" })] }) }), _jsx(TableBody, { children: picker.rows.map(row => (_jsxs(TableRow, { children: [_jsx(TableCell, { component: "th", scope: "row", children: row.label }), _jsx(TableCell, { align: "right", children: row.location }), _jsx(TableCell, { align: "right", children: _jsx(SanitizedHTML, { html: row.trackName || 'N/A' }) }), _jsx(TableCell, { align: "right", children: _jsx(Button, { onClick: () => {
7
+ void picker.pick(row.id);
8
+ }, color: "primary", variant: "contained", children: "Go" }) })] }, row.id))) })] }) }));
37
9
  });
38
10
  export default SearchResultsTable;
@@ -1,8 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
- export default function StrandCheckboxes({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }: {
2
+ declare const StrandCheckboxes: ({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }: {
3
3
  searchForward: boolean;
4
4
  searchReverse: boolean;
5
5
  setSearchForward: (arg: boolean) => void;
6
6
  setSearchReverse: (arg: boolean) => void;
7
7
  children?: ReactNode;
8
- }): import("react").JSX.Element;
8
+ }) => import("react").JSX.Element;
9
+ export default StrandCheckboxes;
@@ -1,13 +1,15 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { LabeledCheckbox } from '@jbrowse/core/ui';
3
3
  import { FormGroup, Typography } from '@mui/material';
4
+ import { observer } from 'mobx-react';
4
5
  // Forward/reverse strand checkboxes plus the "select at least one" guard, shared
5
6
  // by the sequence-search mode panels. Extra checkboxes for a specific panel (e.g.
6
7
  // "Case insensitive") slot in alongside via `children`.
7
- export default function StrandCheckboxes({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }) {
8
+ const StrandCheckboxes = observer(function StrandCheckboxes({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }) {
8
9
  return (_jsxs(_Fragment, { children: [_jsxs(FormGroup, { row: true, children: [_jsx(LabeledCheckbox, { size: "small", checked: searchForward, onChange: val => {
9
10
  setSearchForward(val);
10
11
  }, label: "Forward strand" }), _jsx(LabeledCheckbox, { size: "small", checked: searchReverse, onChange: val => {
11
12
  setSearchReverse(val);
12
13
  }, label: "Reverse strand" }), children] }), !searchForward && !searchReverse ? (_jsx(Typography, { color: "error", variant: "body2", children: "Select at least one strand" })) : null] }));
13
- }
14
+ });
15
+ export default StrandCheckboxes;
@@ -135,7 +135,7 @@ const TrackContainer = observer(function TrackContainer({ model, track, }) {
135
135
  const { setPaper, setContainer } = useTrackLabelBand(model, track.trackId);
136
136
  return (_jsxs(Paper, { ref: setPaper, className: cx(classes.root, track.pinned ? null : classes.unpinnedTrack), variant: showTrackOutlines ? 'outlined' : undefined, elevation: showTrackOutlines ? undefined : 0, onDragOver: event => {
137
137
  model.onTrackDragOver(track.id, event.clientY);
138
- }, children: [track.pinned ? _jsx(Gridlines, { model: model, offset: outlineOffset }) : null, model.effectiveTrackLabels !== 'hidden' ? (_jsx(TrackLabel, { track: track, className: trackLabelStyle })) : null, _jsx(TrackOverlaySlot, { zIndex: 100, overlayStyle: { left: -outlineOffset }, children: _jsx(ErrorBoundary, { FallbackComponent: e => (_jsx("div", { className: classes.crashBanner, children: _jsx(ErrorBanner, { error: e.error, onReset: e.resetErrorBoundary }) })), children: _jsx(TrackRenderingContainer, { model: model, track: track, display: display, ref: setContainer }) }, display.id) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, onDrag: distance => display.resizeHeight(distance),
138
+ }, children: [track.pinned ? _jsx(Gridlines, { model: model, offset: outlineOffset }) : null, model.effectiveTrackLabels !== 'hidden' ? (_jsx(TrackLabel, { track: track, className: trackLabelStyle })) : null, _jsx(TrackOverlaySlot, { zIndex: 100, overlayStyle: { left: -outlineOffset }, children: _jsx(ErrorBoundary, { FallbackComponent: e => (_jsx("div", { className: classes.crashBanner, "data-gesture-owner": "true", children: _jsx(ErrorBanner, { error: e.error, onReset: e.resetErrorBoundary }) })), children: _jsx(TrackRenderingContainer, { model: model, track: track, display: display, ref: setContainer }) }, display.id) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, onDrag: distance => display.resizeHeight(distance),
139
139
  // the drag's shortcut: grow the track by whatever it is scrolled over,
140
140
  // so a pileup showing three rows of forty opens to all forty
141
141
  // Optional: only a display that scrolls its own content has anything
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
3
+ import { TRACK_MENU_PRIORITY, subMenuIfAny } from '@jbrowse/core/ui/menuItems';
3
4
  import { getSession } from '@jbrowse/core/util';
4
5
  import { makeStyles } from '@jbrowse/core/util/tss-react';
5
6
  import LowPriorityIcon from '@mui/icons-material/LowPriority';
@@ -27,21 +28,16 @@ const TrackLabelMenu = observer(function TrackLabelMenu({ track, }) {
27
28
  view,
28
29
  }) ?? [];
29
30
  return [
30
- ...(orderSubMenu.length
31
- ? [
32
- {
33
- label: 'Track order',
34
- icon: LowPriorityIcon,
35
- type: 'subMenu',
36
- priority: 1000,
37
- subMenu: orderSubMenu,
38
- },
39
- ]
40
- : []),
31
+ ...subMenuIfAny({
32
+ label: 'Track order',
33
+ icon: LowPriorityIcon,
34
+ priority: TRACK_MENU_PRIORITY.trackOrder,
35
+ subMenu: orderSubMenu,
36
+ }),
41
37
  ...sessionItems,
42
38
  track.saveTrackDataMenuItem,
43
39
  ...track.trackMenuItems(),
44
- ].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
40
+ ];
45
41
  }
46
42
  return (_jsx(CascadingMenuButton, { menuItems: getMenuItems, className: classes.menuButton, "data-testid": "track_menu_icon", "data-trackid": track.trackId, tooltip: "Track settings", children: _jsx(MoreVertIcon, { fontSize: "small" }) }));
47
43
  });