@jbrowse/plugin-linear-genome-view 5.0.0-beta.7 → 5.0.0-beta.9

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 (76) hide show
  1. package/esm/FeatureTrack/configSchema.d.ts +140 -48
  2. package/esm/LinearGenomeView/afterAttach.d.ts +5 -5
  3. package/esm/LinearGenomeView/afterAttach.js +18 -24
  4. package/esm/LinearGenomeView/closeUps.d.ts +83 -0
  5. package/esm/LinearGenomeView/closeUps.js +211 -0
  6. package/esm/LinearGenomeView/components/AddCloseUpDialog.d.ts +19 -0
  7. package/esm/LinearGenomeView/components/AddCloseUpDialog.js +29 -0
  8. package/esm/LinearGenomeView/components/CloseUps.d.ts +5 -0
  9. package/esm/LinearGenomeView/components/CloseUps.js +75 -0
  10. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +2 -1
  11. package/esm/LinearGenomeView/components/ExportSvgDialog.js +6 -2
  12. package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
  13. package/esm/LinearGenomeView/components/Header.js +6 -1
  14. package/esm/LinearGenomeView/components/HeaderZoomControls.js +4 -34
  15. package/esm/LinearGenomeView/components/Highlight.js +2 -4
  16. package/esm/LinearGenomeView/components/HighlightBand.js +2 -2
  17. package/esm/LinearGenomeView/components/HighlightChip.d.ts +1 -2
  18. package/esm/LinearGenomeView/components/HighlightChip.js +5 -7
  19. package/esm/LinearGenomeView/components/ImportForm.js +2 -2
  20. package/esm/LinearGenomeView/components/LinearGenomeView.js +3 -3
  21. package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +3 -2
  22. package/esm/LinearGenomeView/components/OverviewHighlight.js +1 -1
  23. package/esm/LinearGenomeView/components/OverviewHighlightBand.js +2 -3
  24. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.d.ts +7 -1
  25. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +22 -7
  26. package/esm/LinearGenomeView/components/SVGHighlightBand.js +2 -3
  27. package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +6 -21
  28. package/esm/LinearGenomeView/components/SearchBox.js +2 -2
  29. package/esm/LinearGenomeView/components/TrackContainer.js +12 -5
  30. package/esm/LinearGenomeView/components/TrackLabel.js +1 -1
  31. package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +3 -1
  32. package/esm/LinearGenomeView/components/TrackRenderingContainer.js +3 -7
  33. package/esm/LinearGenomeView/components/TracksContainer.js +7 -15
  34. package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
  35. package/esm/LinearGenomeView/components/useSideScroll.js +1 -1
  36. package/esm/LinearGenomeView/consts.d.ts +2 -0
  37. package/esm/LinearGenomeView/consts.js +5 -0
  38. package/esm/LinearGenomeView/headerFit.d.ts +2 -1
  39. package/esm/LinearGenomeView/headerFit.js +7 -2
  40. package/esm/LinearGenomeView/index.d.ts +1 -1
  41. package/esm/LinearGenomeView/keyboardHandler.d.ts +2 -0
  42. package/esm/LinearGenomeView/keyboardHandler.js +11 -0
  43. package/esm/LinearGenomeView/launchKeys.d.ts +1 -1
  44. package/esm/LinearGenomeView/launchKeys.js +15 -5
  45. package/esm/LinearGenomeView/lazyDialogs.d.ts +7 -0
  46. package/esm/LinearGenomeView/lazyDialogs.js +2 -0
  47. package/esm/LinearGenomeView/menuItems.d.ts +28 -16
  48. package/esm/LinearGenomeView/menuItems.js +213 -85
  49. package/esm/LinearGenomeView/model.d.ts +367 -199
  50. package/esm/LinearGenomeView/model.js +302 -143
  51. package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
  52. package/esm/LinearGenomeView/showRegionsWithUndo.js +7 -1
  53. package/esm/LinearGenomeView/svgcomponents/SVGCloseUpFrame.d.ts +5 -0
  54. package/esm/LinearGenomeView/svgcomponents/SVGCloseUpFrame.js +8 -0
  55. package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +3 -4
  56. package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +3 -3
  57. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +95 -24
  58. package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +4 -5
  59. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +13 -4
  60. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +4 -4
  61. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +1 -1
  62. package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +52 -63
  63. package/esm/LinearGenomeView/svgcomponents/util.d.ts +8 -6
  64. package/esm/LinearGenomeView/svgcomponents/util.js +37 -16
  65. package/esm/LinearGenomeView/types.d.ts +5 -4
  66. package/esm/LinearGenomeView/util.d.ts +20 -37
  67. package/esm/LinearGenomeView/util.js +58 -141
  68. package/esm/displayKitTests/perRegionTestEnv.d.ts +64 -45
  69. package/esm/displayKitTests/perRegionTestEnv.js +6 -6
  70. package/esm/index.d.ts +9 -67
  71. package/esm/index.js +6 -22
  72. package/esm/searchUtils.d.ts +2 -3
  73. package/esm/searchUtils.js +2 -2
  74. package/package.json +7 -7
  75. package/esm/lazyPluginExports.d.ts +0 -23
  76. package/esm/lazyPluginExports.js +0 -36
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { SubmitDialog } from '@jbrowse/core/ui';
4
+ import { assembleLocStrings } from '@jbrowse/core/util';
5
+ import { Checkbox, FormControlLabel, Typography } from '@mui/material';
6
+ import { observer } from 'mobx-react';
7
+ /**
8
+ * The one thing a close-up still needs deciding once a drag has named its
9
+ * span: whether it opens showing what this view shows, or empty for a track
10
+ * chosen afterwards.
11
+ *
12
+ * The span is the selection's, named here the way the same menu's "Copy range"
13
+ * names it, so the dialog says what it is about to open rather than describing
14
+ * the feature. The model's action takes the same two offsets, for a stack an
15
+ * agent or a spec builds outright.
16
+ */
17
+ const AddCloseUpDialog = observer(function AddCloseUpDialog({ model, leftOffset, rightOffset, handleClose, }) {
18
+ const [copyTracks, setCopyTracks] = useState(true);
19
+ const rangeString = assembleLocStrings(model.getSelectedRegions(leftOffset, rightOffset));
20
+ return (_jsx(SubmitDialog, { open: true, maxWidth: "xs", fullWidth: true, title: "Add close-up view", onCancel: handleClose, submitText: "Add", onSubmit: () => {
21
+ model.addCloseUpForSpan(leftOffset, rightOffset, {
22
+ trackIds: copyTracks ? undefined : [],
23
+ });
24
+ handleClose();
25
+ }, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 12 }, children: [_jsxs(Typography, { children: ["A closer view of ", rangeString, ", below the tracks. It stays centred on this view, which moves to the middle of that span."] }), _jsx(FormControlLabel, { label: "Copy this view's tracks", control: _jsx(Checkbox, { size: "small", checked: copyTracks, "data-testid": "close-up-copy-tracks", onChange: event => {
26
+ setCopyTracks(event.target.checked);
27
+ } }) })] }) }));
28
+ });
29
+ export default AddCloseUpDialog;
@@ -0,0 +1,5 @@
1
+ import type { LinearGenomeViewModel } from '../index.ts';
2
+ declare const CloseUps: ({ model, }: {
3
+ model: LinearGenomeViewModel;
4
+ }) => import("react").JSX.Element | null;
5
+ export default CloseUps;
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from 'react';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { useResizeDrag } from '@jbrowse/core/util/useResizeDrag';
5
+ import { observer } from 'mobx-react';
6
+ import { CLOSE_UP_FRAME_WIDTH, closeUpColor, closeUpStackRows, } from '../closeUps.js';
7
+ import LinearGenomeView from './LinearGenomeView.js';
8
+ import OverviewScalebarPolygon from './OverviewScalebarPolygon.js';
9
+ const useStyles = makeStyles()(theme => ({
10
+ connector: {
11
+ position: 'relative',
12
+ },
13
+ polygon: {
14
+ display: 'block',
15
+ width: '100%',
16
+ height: '100%',
17
+ overflow: 'visible',
18
+ pointerEvents: 'none',
19
+ },
20
+ closeUp: {
21
+ position: 'relative',
22
+ '&::after': {
23
+ content: '""',
24
+ position: 'absolute',
25
+ inset: 0,
26
+ border: `${CLOSE_UP_FRAME_WIDTH}px solid ${closeUpColor(theme)}`,
27
+ pointerEvents: 'none',
28
+ zIndex: 1,
29
+ },
30
+ },
31
+ // The whole band is the handle — a 4px bar under a 16px trapezoid is a
32
+ // smaller target than the shape it sizes — and `useResizeDrag` rather than
33
+ // `ResizeHandle` because that one's hover paints its whole surface, which
34
+ // here is the figure. A rule at the edge it drags says the same thing over
35
+ // the top of nothing.
36
+ grab: {
37
+ position: 'absolute',
38
+ inset: 0,
39
+ cursor: 'row-resize',
40
+ touchAction: 'none',
41
+ '&:hover': {
42
+ borderBottom: `2px solid ${theme.palette.action.disabled}`,
43
+ },
44
+ },
45
+ }));
46
+ /**
47
+ * The trapezoid from the span `close-up` shows, as it sits in `context`, out to
48
+ * `close-up`'s full width: the header overview's "you are here", drawn between
49
+ * two rows of the stack instead. A close-up is scrolled, where the header
50
+ * overview never is, so its left edge is what its origin is shifted by.
51
+ *
52
+ * Its height belongs to the stack rather than to this pair, so a drag on any
53
+ * band moves all of them: the bands are a ladder the eye reads down, and one
54
+ * rung of its own height reads as a difference in the data. The height is worth
55
+ * dragging at all because the band is a picture of a ratio — a row ten times
56
+ * wider than the one under it narrows to a tenth of the width there — and how
57
+ * steep that reads is the figure's to decide, not ours.
58
+ */
59
+ const CloseUpConnector = observer(function CloseUpConnector({ host, context, closeUp, }) {
60
+ const { classes } = useStyles();
61
+ const height = host.closeUpConnectorHeight;
62
+ const handleProps = useResizeDrag({
63
+ onDrag: delta => {
64
+ host.setCloseUpConnectorHeight(host.closeUpConnectorHeight + delta);
65
+ },
66
+ });
67
+ return context.initialized && closeUp.initialized ? (_jsxs("div", { className: classes.connector, style: { height }, children: [_jsx("svg", { className: classes.polygon, children: _jsx(OverviewScalebarPolygon, { model: closeUp, overview: context, overviewOffsetPx: -context.offsetPx, height: height, gradient: true }) }), _jsx("div", { ...handleProps, "data-testid": `close-up-connector-${closeUp.id}`, className: classes.grab })] })) : null;
68
+ });
69
+ const CloseUps = observer(function CloseUps({ model, }) {
70
+ const closeUps = model.closeUpViews;
71
+ const { classes } = useStyles();
72
+ const rows = closeUpStackRows(model, closeUps);
73
+ return rows.length ? (_jsx("div", { "data-testid": `close-ups-${model.id}`, children: rows.map(({ closeUp, context }) => (_jsxs(Fragment, { children: [_jsx(CloseUpConnector, { host: model, context: context, closeUp: closeUp }), _jsx("div", { className: classes.closeUp, children: _jsx(LinearGenomeView, { model: closeUp }) })] }, closeUp.id))) })) : null;
74
+ });
75
+ export default CloseUps;
@@ -1,7 +1,8 @@
1
- import type { TrackLabelMode } from '../types.ts';
1
+ import type { TrackLabelMode, ViewTrackLabelMode } from '../types.ts';
2
2
  import type { BaseExportSvgOptions } from '@jbrowse/core/ui';
3
3
  export default function ExportSvgDialog({ model, handleClose, }: {
4
4
  model: {
5
+ effectiveTrackLabels?: ViewTrackLabelMode;
5
6
  exportSvg(opts: BaseExportSvgOptions & {
6
7
  trackLabels: TrackLabelMode;
7
8
  showGridlines: boolean;
@@ -4,11 +4,15 @@ import { MenuItem, TextField } from '@mui/material';
4
4
  // Shared track-label + gridlines export dialog. Used by LGV, linear-synteny and
5
5
  // breakpoint-split views (their lazyDialogs re-export this).
6
6
  export default function ExportSvgDialog({ model, handleClose, }) {
7
- const [trackLabels, setTrackLabels] = useExportSvgPreference('tracklabels', 'offset');
7
+ // The view's own setting is already an export mode, so a user who has not
8
+ // picked one here gets the figure their screen shows. The key is `-mode`
9
+ // because the modes were once spelled `overlay`/`none`, and a stored one of
10
+ // those would select nothing in the dropdown.
11
+ const [trackLabels, setTrackLabels] = useExportSvgPreference('tracklabels-mode', model.effectiveTrackLabels ?? 'offset');
8
12
  const [showGridlines, setShowGridlines] = useExportSvgPreference('gridlines', false);
9
13
  return (_jsx(BaseExportSvgDialog, { model: model, handleClose: handleClose, exportSvg: opts => model.exportSvg({ ...opts, trackLabels, showGridlines }), checkboxes: _jsx(LabeledCheckbox, { checked: showGridlines, onChange: val => {
10
14
  setShowGridlines(val);
11
15
  }, label: "Show gridlines" }), children: _jsxs(TextField, { select: true, label: "Track label positioning", variant: "outlined", style: { minWidth: 200 }, value: trackLabels, onChange: event => {
12
16
  setTrackLabels(event.target.value);
13
- }, children: [_jsx(MenuItem, { value: "offset", children: "Offset" }), _jsx(MenuItem, { value: "overlay", children: "Overlay" }), _jsx(MenuItem, { value: "left", children: "Left" }), _jsx(MenuItem, { value: "none", children: "None" })] }) }));
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" })] }) }));
14
18
  }
@@ -32,12 +32,12 @@ const GetSequenceDialog = observer(function GetSequenceDialog({ model, regions,
32
32
  // a selection this dialog will happily take is a whole chromosome of
33
33
  // sequence, so both handles are forwarded: closing the dialog stops the
34
34
  // read rather than leaving the worker on it, and the wait is named
35
- async (_name, _locs, stopToken, statusCallback) => {
35
+ async (_name, _locs, signal, statusCallback) => {
36
36
  if (regions.length === 0) {
37
37
  throw new Error('Selected region is out of bounds');
38
38
  }
39
39
  const chunks = await fetchSequence(model, regions, {
40
- stopToken,
40
+ signal,
41
41
  statusCallback,
42
42
  });
43
43
  // validate here (in the async path) so a length mismatch surfaces via the
@@ -1,7 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
2
3
  import ScrollZoomToggle from '@jbrowse/core/ui/ScrollZoomToggle';
4
+ import { getSession } from '@jbrowse/core/util';
3
5
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
6
  import useMeasure from '@jbrowse/core/util/useMeasure';
7
+ import MenuIcon from '@mui/icons-material/Menu';
5
8
  import { observer } from 'mobx-react';
6
9
  import { HEADER_BAR_HEIGHT } from '../consts.js';
7
10
  import { headerFit } from '../headerFit.js';
@@ -35,12 +38,14 @@ const Controls = observer(function Controls({ model, }) {
35
38
  // row
36
39
  const [ref, { width }] = useMeasure('width');
37
40
  const highlighted = highlightedDisplays(model);
41
+ const viewMenu = getSession(model).viewTitleBars === false;
38
42
  const fit = headerFit({
39
43
  width,
40
44
  searchBoxPx: searchBoxWidth(model.coarseVisibleLocStrings),
41
45
  clearHighlight: highlighted.length > 0,
46
+ viewMenu,
42
47
  });
43
- return (_jsxs("div", { className: classes.headerBar, ref: ref, children: [_jsx(HeaderTrackSelectorButton, { model: model, indent: fit.trackSelectorIndent }), _jsx(ScrollZoomToggle, { model: model, iconOnly: !fit.scrollZoomLabel }), _jsx("div", { className: classes.spacer }), _jsx(HeaderPanControls, { model: model, compact: !fit.panButtonSpacing }), _jsx(SearchBox, { model: model }), _jsx(HeaderClearHighlightButton, { highlighted: highlighted }), fit.regionWidth ? _jsx(HeaderRegionWidth, { model: model }) : null, _jsx(HeaderZoomControls, { model: model, showSlider: fit.zoomSlider }), _jsx("div", { className: classes.spacer })] }));
48
+ return (_jsxs("div", { className: classes.headerBar, ref: ref, children: [viewMenu ? (_jsx(CascadingMenuButton, { menuItems: () => model.menuItems(), "data-testid": "view_menu_icon", children: _jsx(MenuIcon, {}) })) : null, _jsx(HeaderTrackSelectorButton, { model: model, indent: fit.trackSelectorIndent }), _jsx(ScrollZoomToggle, { model: model, iconOnly: !fit.scrollZoomLabel }), _jsx("div", { className: classes.spacer }), _jsx(HeaderPanControls, { model: model, compact: !fit.panButtonSpacing }), _jsx(SearchBox, { model: model }), _jsx(HeaderClearHighlightButton, { highlighted: highlighted }), fit.regionWidth ? _jsx(HeaderRegionWidth, { model: model }) : null, _jsx(HeaderZoomControls, { model: model, showSlider: fit.zoomSlider }), _jsx("div", { className: classes.spacer })] }));
44
49
  });
45
50
  const LinearGenomeViewHeader = observer(function LinearGenomeViewHeader({ model, }) {
46
51
  const { hideHeader, hideHeaderOverview } = model;
@@ -1,14 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { lazy, useState } from 'react';
2
+ import { useState } from 'react';
3
3
  import { SingleSlider } from '@jbrowse/core/ui';
4
4
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
5
- import { getBpDisplayStr, getDialogHost } from '@jbrowse/core/util';
5
+ import { getBpDisplayStr } from '@jbrowse/core/util';
6
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
7
7
  import MoreVert from '@mui/icons-material/MoreVert';
8
8
  import { observer } from 'mobx-react';
9
- import { showAllRegionsMenuItem } from '../menuItems.js';
9
+ import { zoomMenuItems } from '../menuItems.js';
10
10
  import ZoomButton from './ZoomButton.js';
11
- const RegionWidthEditorDialog = lazy(() => import('./RegionWidthEditorDialog.js'));
12
11
  const useStyles = makeStyles()(theme => ({
13
12
  container: {
14
13
  display: 'flex',
@@ -29,35 +28,6 @@ const useStyles = makeStyles()(theme => ({
29
28
  },
30
29
  },
31
30
  }));
32
- function getZoomMenuItems(model) {
33
- return [
34
- ...[10, 50, 100].map(r => ({
35
- label: `Zoom in ${r}x`,
36
- onClick: () => {
37
- model.zoom(model.bpPerPx / r);
38
- },
39
- })),
40
- ...[10, 50, 100].map(r => ({
41
- label: `Zoom out ${r}x`,
42
- onClick: () => {
43
- model.zoom(model.bpPerPx * r);
44
- },
45
- })),
46
- showAllRegionsMenuItem(model),
47
- {
48
- label: 'Custom zoom',
49
- onClick: () => {
50
- getDialogHost(model).queueDialog(handleClose => [
51
- RegionWidthEditorDialog,
52
- {
53
- model,
54
- handleClose,
55
- },
56
- ]);
57
- },
58
- },
59
- ];
60
- }
61
31
  // The slider tracks live `bpPerPx`, which changes on every animation frame of a
62
32
  // zoom. Isolated into its own observer so the surrounding zoom buttons/menu —
63
33
  // whose MUI Tooltip/IconButton machinery is comparatively expensive and only
@@ -82,6 +52,6 @@ const ZoomSlider = observer(function ZoomSlider({ model, }) {
82
52
  });
83
53
  const HeaderZoomControls = observer(function HeaderZoomControls({ model, showSlider = true, }) {
84
54
  const { classes } = useStyles();
85
- return (_jsxs("div", { className: classes.container, children: [_jsx(ZoomButton, { model: model, direction: "out" }), showSlider ? _jsx(ZoomSlider, { model: model }) : null, _jsx(ZoomButton, { model: model, direction: "in" }), _jsx(CascadingMenuButton, { menuItems: () => getZoomMenuItems(model), children: _jsx(MoreVert, {}) })] }));
55
+ return (_jsxs("div", { className: classes.container, children: [_jsx(ZoomButton, { model: model, direction: "out" }), showSlider ? _jsx(ZoomSlider, { model: model }) : null, _jsx(ZoomButton, { model: model, direction: "in" }), _jsx(CascadingMenuButton, { menuItems: () => zoomMenuItems(model), children: _jsx(MoreVert, {}) })] }));
86
56
  });
87
57
  export default HeaderZoomControls;
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useHighlightChip } from '@jbrowse/core/ui/highlightChipReveal';
3
2
  import { getSession } from '@jbrowse/core/util';
4
3
  import CloseIcon from '@mui/icons-material/Close';
5
4
  import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
@@ -16,13 +15,12 @@ const Highlight = observer(function Highlight({ model, highlight, }) {
16
15
  const coords = model.getHighlightCoords(highlight);
17
16
  const bandColor = getHighlightColor(highlight, theme);
18
17
  const label = model.labelsVisible ? highlight.label : undefined;
19
- const { chipVisible, setMenuOpen } = useHighlightChip(coords, model.showHighlightChips);
20
- return coords ? (_jsx(HighlightBand, { coords: coords, background: bandColor.toRgbString(), label: label, children: chipVisible && coords.width >= CHIP_MIN_WIDTH ? (_jsx(HighlightChip, { color: bandColor, label: label, tooltip: highlight.label ?? 'Highlighted region', setOpen: setMenuOpen, menuItems: [
18
+ return coords ? (_jsx(HighlightBand, { coords: coords, background: bandColor.toRgbString(), label: label, children: coords.width >= CHIP_MIN_WIDTH ? (_jsx(HighlightChip, { color: bandColor, label: label, tooltip: highlight.label ?? 'Highlighted region', menuItems: [
21
19
  {
22
20
  label: 'Dismiss highlight',
23
21
  icon: CloseIcon,
24
22
  onClick: () => {
25
- model.removeHighlight(highlight);
23
+ getSession(model).removeHighlight(highlight);
26
24
  },
27
25
  },
28
26
  {
@@ -24,8 +24,8 @@ const useStyles = makeStyles()(theme => ({
24
24
  color: theme.palette.text.primary,
25
25
  },
26
26
  }));
27
- // Shared band renderer for LGV and grid-bookmark highlights; OverviewHighlight
28
- // stays separate (different bpToPx API). A non-interactive colored band with an
27
+ // OverviewHighlight stays separate (different bpToPx API). A non-interactive
28
+ // colored band with an
29
29
  // optional top label; when `children` (the highlight chip) is passed it renders
30
30
  // that instead of the plain label. Visibility is toggled from the view menu
31
31
  export default function HighlightBand({ coords, background, label, children, }) {
@@ -1,9 +1,8 @@
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, setOpen, }: {
3
+ export default function HighlightChip({ color, label, tooltip, menuItems, }: {
4
4
  color: Colord;
5
5
  label?: string;
6
6
  tooltip?: string;
7
7
  menuItems: MenuItem[];
8
- setOpen?: (arg: boolean) => void;
9
8
  }): import("react").JSX.Element;
@@ -2,14 +2,12 @@ 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
- // Interactive chip drawn inside a highlight band while the band is revealing it
6
- // (see `useHighlightChip`): a link icon tinted to the band color plus an
7
- // optional inline label, wrapped in a context menu. Sets its own pointer-events
8
- // since the band is click-through. `setOpen` is how the band learns to keep the
9
- // chip drawn while the menu is up.
10
- export default function HighlightChip({ color, label, tooltip, menuItems, setOpen, }) {
5
+ // Interactive chip drawn inside a highlight band: a link icon tinted to the
6
+ // band color plus an optional inline label, wrapped in a context menu. Sets its
7
+ // own pointer-events since the band is click-through.
8
+ export default function HighlightChip({ color, label, tooltip, menuItems, }) {
11
9
  // hide the icon color when the band is fully transparent ("label-only"
12
10
  // highlight); otherwise bump to 0.8 alpha so the chip stays legible
13
11
  const chipAlpha = color.alpha() === 0 ? 0 : 0.8;
14
- return (_jsx(Box, { sx: { pointerEvents: 'auto' }, children: _jsx(CascadingMenuButton, { "data-testid": "highlight-chip", menuItems: menuItems, setOpen: setOpen, 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] }) }) }) }));
12
+ 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] }) }) }) }));
15
13
  }
@@ -84,12 +84,12 @@ const LinearGenomeViewImportForm = observer(function LinearGenomeViewImportForm(
84
84
  // reason.
85
85
  model.setError(undefined);
86
86
  setSelectedAssemblyName(val);
87
- }, session: session, selected: selectedAsm }), selectedAsm ? (assemblyError ? (_jsx(CloseIcon, { color: "error" })) : regions ? (_jsx(RefNameAutocomplete, { fetchResults: (queryString, stopToken) => fetchResults({
87
+ }, session: session, selected: selectedAsm }), selectedAsm ? (assemblyError ? (_jsx(CloseIcon, { color: "error" })) : regions ? (_jsx(RefNameAutocomplete, { fetchResults: (queryString, signal) => fetchResults({
88
88
  queryString,
89
89
  assembly,
90
90
  textSearchManager,
91
91
  assemblyName: selectedAsm,
92
- stopToken,
92
+ signal,
93
93
  }), session: session, assemblyName: selectedAsm, value: value, minWidth: 270, adornmentWidth: adornmentReservePx({
94
94
  menuItemCount: recentMenuItems.length,
95
95
  }), onChange: v => {
@@ -6,9 +6,9 @@ import LinearGenomeViewContainer from './LinearGenomeViewContainer.js';
6
6
  // lazies
7
7
  const ImportForm = lazy(() => import('./ImportForm.js'));
8
8
  const LinearGenomeView = observer(function LinearGenomeView({ model, }) {
9
- const { showLoading, showImportForm, loadingMessage, loadingProgress } = model;
10
- if (showLoading) {
11
- return (_jsx(ViewLoadingScreen, { message: loadingMessage, fraction: loadingProgress, source: model.loadingSource }));
9
+ const { loading, showImportForm } = model;
10
+ if (loading) {
11
+ return _jsx(ViewLoadingScreen, { ...loading });
12
12
  }
13
13
  else if (showImportForm) {
14
14
  return _jsx(ImportForm, { model: model });
@@ -9,6 +9,7 @@ import Paper from '@mui/material/Paper';
9
9
  import { observer } from 'mobx-react';
10
10
  import { SCALE_BAR_HEIGHT } from '../consts.js';
11
11
  import { stickyChromeTops } from '../stickyChrome.js';
12
+ import CloseUps from './CloseUps.js';
12
13
  import Header from './Header.js';
13
14
  import MiniControls from './MiniControls.js';
14
15
  import NavigationAnnouncer from './NavigationAnnouncer.js';
@@ -105,7 +106,7 @@ const LinearGenomeViewContainer = observer(function LinearGenomeViewContainer({
105
106
  .map(d => d.hoveredFeature)
106
107
  .find(Boolean);
107
108
  session.setHovered({ hoverPosition, hoverFeature });
108
- }, children: [_jsx(NavigationAnnouncer, { model: model }), _jsxs("div", { ref: headerRef, 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, ControlComponent: _jsx(Scalebar, { model: model, style: { height: SCALE_BAR_HEIGHT, boxSizing: 'border-box' } }) })) : (_jsx(TracksContainer, { model: model, children: !tracks.length ? (_jsx(Suspense, { fallback: null, children: _jsx(NoTracksActiveButton, { model: model }) })) : (_jsxs(_Fragment, { children: [pinnedTracks.length ? (_jsx(Paper, { elevation: 6, className: classes.pinnedTracks, style: {
109
+ }, 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, ControlComponent: _jsx(Scalebar, { model: model, style: { height: SCALE_BAR_HEIGHT, boxSizing: 'border-box' } }) })) : (_jsx(TracksContainer, { model: model, children: !tracks.length ? (_jsx(Suspense, { fallback: null, children: _jsx(NoTracksActiveButton, { model: model }) })) : (_jsxs(_Fragment, { children: [pinnedTracks.length ? (_jsx(Paper, { elevation: 6, className: classes.pinnedTracks, style: {
109
110
  top: stickyTops.pinnedTracks,
110
111
  // the scroll port this sticks in, not the window: it is
111
112
  // under the app bar in the classic stack, a dockview
@@ -113,6 +114,6 @@ const LinearGenomeViewContainer = observer(function LinearGenomeViewContainer({
113
114
  // The fallback is what an unbounded embed gets, where
114
115
  // the page itself is the scroller
115
116
  maxHeight: `calc(var(${SCROLL_PORT_HEIGHT_VAR}, 100vh) - ${stickyTops.pinnedTracks})`,
116
- }, children: pinnedTracks.map(track => (_jsx(TrackContainer, { model: model, track: track }, track.id))) })) : null, unpinnedTracks.map(track => (_jsx(TrackContainer, { model: model, track: track }, track.id)))] })) })) })] }) }));
117
+ }, 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(CloseUps, { model: model })] }) }));
117
118
  });
118
119
  export default LinearGenomeViewContainer;
@@ -11,7 +11,7 @@ const OverviewHighlight = observer(function OverviewHighlight({ model, }) {
11
11
  // gate on highlightsVisible to match the main-view band, scalebar band, and
12
12
  // SVG export — otherwise "Turn off highlights" leaves the overview bands up
13
13
  return getSession(model).highlightsVisible
14
- ? model.highlight
14
+ ? model.highlights
15
15
  .map(highlight => {
16
16
  const coords = model.getOverviewHighlightCoords(highlight);
17
17
  return coords ? { coords, highlight } : undefined;
@@ -1,9 +1,8 @@
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
- // Renders a single absolute-positioned band over the overview scalebar. Used
5
- // for both LGV `view.highlight` entries and grid-bookmark bookmark highlights.
6
- // The 1px border keeps very narrow bands visible.
4
+ // A single absolute-positioned band over the overview scalebar. The 1px border
5
+ // keeps very narrow bands visible.
7
6
  const useStyles = makeStyles()({
8
7
  highlight: {
9
8
  height: '100%',
@@ -12,11 +12,17 @@ import type { ViewLayout } from '@jbrowse/core/util/Base1DUtils';
12
12
  * @param exportSvg - serialize explicit fill/stroke attributes (the split the
13
13
  * `getFill/StrokeProps` helpers exist for) instead of the on-screen CSS class,
14
14
  * which wouldn't survive into a standalone exported SVG.
15
+ * @param gradient - a close-up's connector: a fill fading from the narrow edge
16
+ * to the wide one, outlined in the colour of the frame it opens into, so the
17
+ * close-up below reads as an inset of the row above rather than a second view
18
+ * at the same zoom.
15
19
  */
16
- declare const OverviewScalebarPolygon: ({ model, overview, overviewOffsetPx, exportSvg, }: {
20
+ declare const OverviewScalebarPolygon: ({ model, overview, overviewOffsetPx, height, exportSvg, gradient, }: {
17
21
  model: LinearGenomeViewModel;
18
22
  overview: ViewLayout;
19
23
  overviewOffsetPx?: number;
24
+ height?: number;
20
25
  exportSvg?: boolean;
26
+ gradient?: boolean;
21
27
  }) => import("react").JSX.Element | null;
22
28
  export default OverviewScalebarPolygon;
@@ -1,9 +1,11 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { svgNodeId } from '@jbrowse/core/svg/svgId';
2
3
  import { alpha } from '@jbrowse/core/ui/palette';
3
- import { getFillProps, getStrokeProps } from '@jbrowse/core/util';
4
+ import { getFillProps, getStrokeProps, stripAlpha } from '@jbrowse/core/util';
4
5
  import { regionBlocksPxExtent, transformPxSpan, } from '@jbrowse/core/util/Base1DUtils';
5
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
6
7
  import { observer } from 'mobx-react';
8
+ import { CLOSE_UP_FRAME_WIDTH, closeUpColor } from '../closeUps.js';
7
9
  import { HEADER_BAR_HEIGHT } from '../consts.js';
8
10
  /**
9
11
  * SVG points for a trapezoid with horizontal top and bottom edges, wound as a
@@ -24,10 +26,14 @@ function trapezoidPoints(top, bottom, height) {
24
26
  // class computed once per theme (makeStyles memoizes) — an on-screen zoom frame
25
27
  // then does zero color parsing. SVG export can't carry a CSS class, so that path
26
28
  // serializes explicit fill/stroke attributes instead (see `exportSvg` below).
29
+ const FILL_OPACITY = 0.3;
30
+ const STROKE_OPACITY = 0.8;
31
+ const CLOSE_UP_FILL_TOP = 0.6;
32
+ const CLOSE_UP_FILL_BOTTOM = 0.2;
27
33
  const useStyles = makeStyles()(theme => ({
28
34
  polygon: {
29
- fill: alpha(theme.palette.tertiary.light, 0.3),
30
- stroke: alpha(theme.palette.tertiary.light, 0.8),
35
+ fill: alpha(theme.palette.tertiary.light, FILL_OPACITY),
36
+ stroke: alpha(theme.palette.tertiary.light, STROKE_OPACITY),
31
37
  },
32
38
  }));
33
39
  /**
@@ -42,8 +48,12 @@ const useStyles = makeStyles()(theme => ({
42
48
  * @param exportSvg - serialize explicit fill/stroke attributes (the split the
43
49
  * `getFill/StrokeProps` helpers exist for) instead of the on-screen CSS class,
44
50
  * which wouldn't survive into a standalone exported SVG.
51
+ * @param gradient - a close-up's connector: a fill fading from the narrow edge
52
+ * to the wide one, outlined in the colour of the frame it opens into, so the
53
+ * close-up below reads as an inset of the row above rather than a second view
54
+ * at the same zoom.
45
55
  */
46
- const OverviewScalebarPolygon = observer(function OverviewScalebarPolygon({ model, overview, overviewOffsetPx = 0, exportSvg = false, }) {
56
+ const OverviewScalebarPolygon = observer(function OverviewScalebarPolygon({ model, overview, overviewOffsetPx = 0, height = HEADER_BAR_HEIGHT, exportSvg = false, gradient = false, }) {
47
57
  const { classes, theme } = useStyles();
48
58
  const { offsetPx, bpPerPx, dynamicBlocks } = model;
49
59
  const extent = regionBlocksPxExtent(dynamicBlocks.blocks);
@@ -56,7 +66,12 @@ const OverviewScalebarPolygon = observer(function OverviewScalebarPolygon({ mode
56
66
  // the scroll offset
57
67
  const top = transformPxSpan(extent, bpPerPx / overview.bpPerPx, overviewOffsetPx);
58
68
  const bottom = transformPxSpan(extent, 1, -offsetPx);
59
- const points = trapezoidPoints(top, bottom, HEADER_BAR_HEIGHT);
60
- return exportSvg ? (_jsx("polygon", { points: points, ...getFillProps(alpha(theme.palette.tertiary.light, 0.3)), ...getStrokeProps(alpha(theme.palette.tertiary.light, 0.8)) })) : (_jsx("polygon", { points: points, className: classes.polygon }));
69
+ const points = trapezoidPoints(top, bottom, height);
70
+ if (gradient) {
71
+ const id = `close-up-connector-${svgNodeId(model)}`;
72
+ const color = stripAlpha(theme.palette.tertiary.light);
73
+ return (_jsxs(_Fragment, { children: [_jsx("defs", { children: _jsxs("linearGradient", { id: id, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: color, stopOpacity: CLOSE_UP_FILL_TOP }), _jsx("stop", { offset: "100%", stopColor: color, stopOpacity: CLOSE_UP_FILL_BOTTOM })] }) }), _jsx("polygon", { points: points, fill: `url(#${id})`, stroke: closeUpColor(theme), strokeWidth: CLOSE_UP_FRAME_WIDTH, strokeLinejoin: "round" })] }));
74
+ }
75
+ return exportSvg ? (_jsx("polygon", { points: points, ...getFillProps(alpha(theme.palette.tertiary.light, FILL_OPACITY)), ...getStrokeProps(alpha(theme.palette.tertiary.light, STROKE_OPACITY)) })) : (_jsx("polygon", { points: points, className: classes.polygon }));
61
76
  });
62
77
  export default OverviewScalebarPolygon;
@@ -1,9 +1,8 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getFillProps } from '@jbrowse/core/util';
3
3
  // One full-height SVG highlight band with an optional label at the top,
4
- // mirroring the on-screen HighlightBand div. Shared by the LGV native-highlight
5
- // export (SVGHighlights) and the grid-bookmark export (LGVHighlightSVG). The
6
- // label is hidden on bands too narrow to fit it.
4
+ // mirroring the on-screen HighlightBand div. The label is hidden on bands too
5
+ // narrow to fit it.
7
6
  export default function SVGHighlightBand({ coords, height, color, label, labelColor, }) {
8
7
  return (_jsxs(_Fragment, { children: [_jsx("rect", { x: coords.left, y: 0, width: coords.width, height: height, ...getFillProps(color) }), label && coords.width > 3 ? (_jsx("text", { x: coords.left + 3, y: 2, fontSize: 11, dominantBaseline: "hanging", ...getFillProps(labelColor), children: label })) : null] }));
9
8
  }
@@ -31,34 +31,24 @@ const useStyles = makeStyles()(theme => ({
31
31
  overflow: 'clip',
32
32
  whiteSpace: 'nowrap',
33
33
  },
34
- // only a refName label opens a menu. The assembly-name chip beside it is a
35
- // caption, and wearing the pointer and the hover tint made it look like the
36
- // one thing on the row that does nothing when clicked
34
+ // refName labels only: the caption chip opens no menu
37
35
  clickable: {
38
36
  cursor: 'pointer',
39
37
  '&:hover': {
40
- // action.hover is a mode-aware translucent overlay; the old hardcoded
41
- // grey[300] stayed light in dark mode, washing out the light label text
42
38
  background: theme.palette.action.hover,
43
39
  },
44
40
  },
45
- prefixLabel: {
41
+ caption: {
46
42
  zIndex: 100,
47
43
  },
48
44
  }));
49
45
  const ScalebarRefNameLabels = observer(function ScalebarRefNameLabels({ model, }) {
50
46
  const { classes, cx } = useStyles();
51
47
  const [menuState, setMenuState] = useState();
52
- // `model.scalebarRefNameLabels`, not a getScalebarRefNameLabels call of its
53
- // own: a host drawing its own region names reads the same getter, so the
54
- // sticky/dedup/fit rules can't be one thing here and another there. The SVG
55
- // export still calls the helper directly, deliberately and with no prefix.
56
48
  const { labels, caption } = model.scalebarRefNameLabels;
57
49
  return (_jsxs(_Fragment, { children: [labels.map((label, i) => (_jsx(RefLabel
58
50
  // eslint-disable-next-line @eslint-react/no-array-index-key -- position IS the identity here; keying by the run makes the list churn on zoom
59
- , { model: model, label: label, onOpenMenu: state => {
60
- setMenuState(state);
61
- } }, i))), caption === undefined ? null : (_jsx("span", { className: cx(classes.prefixLabel, classes.refLabel), "data-testid": "refLabel-prefix", children: caption })), menuState ? (_jsx(RefNameMenu, { model: model, menuState: menuState, onClose: () => {
51
+ , { model: model, label: label, onOpenMenu: setMenuState }, i))), caption === undefined ? null : (_jsx("span", { className: cx(classes.caption, classes.refLabel), "data-testid": "refLabel-prefix", children: caption })), menuState ? (_jsx(RefNameMenu, { model: model, menuState: menuState, onClose: () => {
62
52
  model.setIsScalebarRefNameMenuOpen(false);
63
53
  setMenuState(undefined);
64
54
  } })) : null] }));
@@ -69,7 +59,7 @@ const ScalebarRefNameLabels = observer(function ScalebarRefNameLabels({ model, }
69
59
  // this label's own onClick instead of opening the range menu.
70
60
  function RefLabel({ model, label, onOpenMenu, }) {
71
61
  const { classes, cx } = useStyles();
72
- const { refName, displayedRegionIndex, lastDisplayedRegionIndex, transform, maxWidth, paddingLeft, text, } = label;
62
+ const { refName, transform, maxWidth, paddingLeft, text } = label;
73
63
  return (_jsx("span", { className: cx(classes.refLabel, classes.clickable), style: {
74
64
  transform: `translateX(${transform}px)`,
75
65
  paddingLeft,
@@ -79,17 +69,12 @@ function RefLabel({ model, label, onOpenMenu, }) {
79
69
  }, onClick: e => {
80
70
  model.setScalebarRefNameClickPending(false);
81
71
  model.setIsScalebarRefNameMenuOpen(true);
82
- onOpenMenu({
83
- anchorEl: e.currentTarget,
84
- refName,
85
- displayedRegionIndex,
86
- lastDisplayedRegionIndex,
87
- });
72
+ onOpenMenu({ anchorEl: e.currentTarget, label });
88
73
  }, children: text }));
89
74
  }
90
75
  const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }) {
91
76
  const { displayedRegions } = model;
92
- const { refName, displayedRegionIndex: idx, lastDisplayedRegionIndex: lastIdx, } = menuState;
77
+ const { refName, displayedRegionIndex: idx, lastDisplayedRegionIndex: lastIdx, } = menuState.label;
93
78
  const numRegions = displayedRegions.length;
94
79
  const labeled = displayedRegions.slice(idx, lastIdx + 1);
95
80
  // A label naming several regions is the collapsed-intron case: adjacent
@@ -56,12 +56,12 @@ const SearchBox = observer(function SearchBox({ model, showHelp = true, minWidth
56
56
  });
57
57
  return (_jsx(RefNameAutocomplete, { onSelect: option => {
58
58
  navigate(option).catch(() => { });
59
- }, assemblyName: assemblyName, fetchResults: (queryString, stopToken) => fetchResults({
59
+ }, assemblyName: assemblyName, fetchResults: (queryString, signal) => fetchResults({
60
60
  queryString,
61
61
  assemblyName,
62
62
  textSearchManager,
63
63
  assembly,
64
- stopToken,
64
+ signal,
65
65
  }), session: session, value: model.coarseVisibleLocStrings, minWidth: minWidth, maxWidth: maxWidth, adornmentWidth: adornmentReservePx({
66
66
  showHelp,
67
67
  menuItemCount: recentMenuItems.length,
@@ -107,7 +107,8 @@ function useTrackLabelBand(model, trackId) {
107
107
  const TrackContainer = observer(function TrackContainer({ model, track, }) {
108
108
  const { classes } = useStyles();
109
109
  // an LGV track always holds at least one linear display (activeDisplay =
110
- // displays[0]); narrow to the linear shape for prefersOffset/resizeHeight
110
+ // displays[0]). The one narrowing to the linear shape, handed down rather
111
+ // than repeated, so a track's chrome and its body can't read two displays.
111
112
  const display = track.activeDisplay;
112
113
  const { showTrackOutlines } = model;
113
114
  // The Paper's 1px border, present iff outlines are on. Everything laid out in
@@ -117,13 +118,19 @@ const TrackContainer = observer(function TrackContainer({ model, track, }) {
117
118
  // the data. One binding rather than three copies of the ternary, because the
118
119
  // three agreeing is the whole point.
119
120
  const outlineOffset = showTrackOutlines ? TRACK_OUTLINE_BORDER : 0;
120
- const trackLabelStyle = model.effectiveTrackLabels !== 'overlapping' || display.prefersOffset
121
- ? classes.trackLabelOffset
122
- : classes.trackLabelOverlap;
121
+ // A label in flow is a row of track height, which is most of what minimizing
122
+ // buys back — so a minimized track overlaps its label whatever the setting
123
+ // says, and there is nothing under it to print over. `prefersOffset` is the
124
+ // display asking for the offset row in 'overlapping' mode, because its left
125
+ // edge carries an axis or a sidebar the label would cover.
126
+ const trackLabelStyle = track.minimized ||
127
+ (model.effectiveTrackLabels === 'overlapping' && !display.prefersOffset)
128
+ ? classes.trackLabelOverlap
129
+ : classes.trackLabelOffset;
123
130
  const { setPaper, setContainer } = useTrackLabelBand(model, track.trackId);
124
131
  return (_jsxs(Paper, { ref: setPaper, className: cx(classes.root, track.pinned ? null : classes.unpinnedTrack), variant: showTrackOutlines ? 'outlined' : undefined, elevation: showTrackOutlines ? undefined : 0, onDragOver: event => {
125
132
  model.onTrackDragOver(track.id, event.clientY);
126
- }, 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, { resetKeys: [display.id], FallbackComponent: e => (_jsx(ErrorBanner, { error: e.error, onReset: e.resetErrorBoundary })), children: _jsx(TrackRenderingContainer, { model: model, track: track, ref: setContainer }) }) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, onDrag: distance => display.resizeHeight(distance),
133
+ }, 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, { resetKeys: [display.id], FallbackComponent: e => (_jsx(ErrorBanner, { error: e.error, onReset: e.resetErrorBoundary })), children: _jsx(TrackRenderingContainer, { model: model, track: track, display: display, ref: setContainer }) }) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, onDrag: distance => display.resizeHeight(distance),
127
134
  // the drag's shortcut: grow the track by whatever it is scrolled over,
128
135
  // so a pileup showing three rows of forty opens to all forty
129
136
  // Optional: only a display that scrolls its own content has anything