@jbrowse/plugin-linear-genome-view 5.0.0-beta.1 → 5.0.0-beta.10

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 (145) hide show
  1. package/esm/FeatureTrack/configSchema.d.ts +142 -50
  2. package/esm/LaunchLinearGenomeView/index.js +2 -13
  3. package/esm/LinearGenomeView/afterAttach.d.ts +5 -5
  4. package/esm/LinearGenomeView/afterAttach.js +73 -59
  5. package/esm/LinearGenomeView/components/CenterLine.js +5 -3
  6. package/esm/LinearGenomeView/components/CrisprGuidePanel.js +3 -5
  7. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +3 -2
  8. package/esm/LinearGenomeView/components/ExportSvgDialog.js +6 -2
  9. package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
  10. package/esm/LinearGenomeView/components/Header.js +11 -1
  11. package/esm/LinearGenomeView/components/HeaderClearHighlightButton.js +2 -2
  12. package/esm/LinearGenomeView/components/HeaderPanControls.js +2 -1
  13. package/esm/LinearGenomeView/components/HeaderZoomControls.js +4 -34
  14. package/esm/LinearGenomeView/components/Highlight.js +7 -7
  15. package/esm/LinearGenomeView/components/HighlightBand.js +2 -2
  16. package/esm/LinearGenomeView/components/HighlightChip.d.ts +1 -2
  17. package/esm/LinearGenomeView/components/HighlightChip.js +5 -7
  18. package/esm/LinearGenomeView/components/HoverPositionHighlight.js +3 -3
  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 -4
  22. package/esm/LinearGenomeView/components/MiniControls.js +3 -2
  23. package/esm/LinearGenomeView/components/OverviewHighlight.js +3 -3
  24. package/esm/LinearGenomeView/components/OverviewHighlightBand.js +2 -3
  25. package/esm/LinearGenomeView/components/OverviewRubberband.js +20 -45
  26. package/esm/LinearGenomeView/components/OverviewScalebar.js +4 -5
  27. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +5 -3
  28. package/esm/LinearGenomeView/components/PaddingBlocks.d.ts +1 -1
  29. package/esm/LinearGenomeView/components/PaddingBlocks.js +14 -9
  30. package/esm/LinearGenomeView/components/RangeSelectOverlay.js +5 -6
  31. package/esm/LinearGenomeView/components/ResizeAllTracksHandle.d.ts +5 -0
  32. package/esm/LinearGenomeView/components/ResizeAllTracksHandle.js +71 -0
  33. package/esm/LinearGenomeView/components/Rubberband.d.ts +1 -2
  34. package/esm/LinearGenomeView/components/Rubberband.js +3 -2
  35. package/esm/LinearGenomeView/components/SVGHighlightBand.js +2 -3
  36. package/esm/LinearGenomeView/components/Scalebar.d.ts +2 -6
  37. package/esm/LinearGenomeView/components/Scalebar.js +8 -5
  38. package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +49 -34
  39. package/esm/LinearGenomeView/components/SearchBox.js +8 -7
  40. package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +2 -1
  41. package/esm/LinearGenomeView/components/SearchResultsDialog.js +2 -2
  42. package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +2 -1
  43. package/esm/LinearGenomeView/components/SearchResultsTable.js +12 -29
  44. package/esm/LinearGenomeView/components/SequenceSearchDialog.d.ts +2 -4
  45. package/esm/LinearGenomeView/components/TrackContainer.js +63 -9
  46. package/esm/LinearGenomeView/components/TrackLabel.js +10 -3
  47. package/esm/LinearGenomeView/components/TrackLabelButtons.js +2 -2
  48. package/esm/LinearGenomeView/components/TrackLabelDragHandle.js +2 -2
  49. package/esm/LinearGenomeView/components/TrackLabelMenu.js +3 -2
  50. package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +4 -1
  51. package/esm/LinearGenomeView/components/TrackRenderingContainer.js +11 -23
  52. package/esm/LinearGenomeView/components/TracksContainer.js +10 -22
  53. package/esm/LinearGenomeView/components/VerticalGuide.d.ts +1 -2
  54. package/esm/LinearGenomeView/components/VerticalGuide.js +3 -16
  55. package/esm/LinearGenomeView/components/ZoomButton.js +6 -1
  56. package/esm/LinearGenomeView/components/ZoomTransform.js +4 -0
  57. package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
  58. package/esm/LinearGenomeView/components/recentLocationsMenu.js +10 -0
  59. package/esm/LinearGenomeView/components/searchModes.d.ts +1 -1
  60. package/esm/LinearGenomeView/components/searchModes.js +1 -1
  61. package/esm/LinearGenomeView/components/useRangeSelect.d.ts +0 -3
  62. package/esm/LinearGenomeView/components/useRangeSelect.js +20 -59
  63. package/esm/LinearGenomeView/components/useSideScroll.d.ts +16 -2
  64. package/esm/LinearGenomeView/components/useSideScroll.js +153 -103
  65. package/esm/LinearGenomeView/components/util.d.ts +4 -0
  66. package/esm/LinearGenomeView/components/util.js +4 -2
  67. package/esm/LinearGenomeView/consts.d.ts +2 -0
  68. package/esm/LinearGenomeView/consts.js +3 -1
  69. package/esm/LinearGenomeView/containingLgv.d.ts +21 -0
  70. package/esm/LinearGenomeView/containingLgv.js +23 -0
  71. package/esm/LinearGenomeView/headerFit.d.ts +4 -2
  72. package/esm/LinearGenomeView/headerFit.js +20 -7
  73. package/esm/LinearGenomeView/index.d.ts +1 -5
  74. package/esm/LinearGenomeView/index.js +10 -7
  75. package/esm/LinearGenomeView/keyboardHandler.d.ts +2 -0
  76. package/esm/LinearGenomeView/keyboardHandler.js +15 -1
  77. package/esm/LinearGenomeView/launchKeys.d.ts +2 -0
  78. package/esm/LinearGenomeView/launchKeys.js +27 -0
  79. package/esm/LinearGenomeView/lazyDialogs.d.ts +2 -4
  80. package/esm/LinearGenomeView/lazyDialogs.js +1 -0
  81. package/esm/LinearGenomeView/linkedViewSync.d.ts +3 -11
  82. package/esm/LinearGenomeView/linkedViewSync.js +27 -43
  83. package/esm/LinearGenomeView/menuItems.d.ts +16 -18
  84. package/esm/LinearGenomeView/menuItems.js +242 -151
  85. package/esm/LinearGenomeView/model.d.ts +2009 -200
  86. package/esm/LinearGenomeView/model.js +545 -395
  87. package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
  88. package/esm/LinearGenomeView/showRegionsWithUndo.js +7 -1
  89. package/esm/LinearGenomeView/stickyChrome.d.ts +5 -9
  90. package/esm/LinearGenomeView/stickyChrome.js +5 -9
  91. package/esm/LinearGenomeView/svgcomponents/SVGGridlines.js +4 -4
  92. package/esm/LinearGenomeView/svgcomponents/SVGHeader.d.ts +4 -1
  93. package/esm/LinearGenomeView/svgcomponents/SVGHeader.js +8 -21
  94. package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +8 -8
  95. package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +3 -3
  96. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +7 -11
  97. package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.js +2 -5
  98. package/esm/LinearGenomeView/svgcomponents/SVGRowHeader.js +5 -6
  99. package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +9 -10
  100. package/esm/LinearGenomeView/svgcomponents/SVGScalebar.js +3 -3
  101. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.d.ts +27 -0
  102. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.js +12 -0
  103. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +15 -6
  104. package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +4 -4
  105. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +6 -5
  106. package/esm/LinearGenomeView/svgcomponents/SVGView.d.ts +1 -1
  107. package/esm/LinearGenomeView/svgcomponents/SVGView.js +1 -1
  108. package/esm/LinearGenomeView/svgcomponents/figureContract.js +14 -14
  109. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +1 -1
  110. package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +50 -68
  111. package/esm/LinearGenomeView/svgcomponents/util.d.ts +9 -5
  112. package/esm/LinearGenomeView/svgcomponents/util.js +26 -21
  113. package/esm/LinearGenomeView/types.d.ts +8 -11
  114. package/esm/LinearGenomeView/util.d.ts +60 -45
  115. package/esm/LinearGenomeView/util.js +131 -160
  116. package/esm/MultiLevelRubberband/MultiLevelRubberband.js +7 -4
  117. package/esm/MultiLevelRubberband/index.d.ts +2 -0
  118. package/esm/MultiLevelRubberband/index.js +2 -0
  119. package/esm/MultiLevelRubberband/menuItems.d.ts +34 -0
  120. package/esm/MultiLevelRubberband/menuItems.js +52 -0
  121. package/esm/MultiLevelRubberband/rowLabel.d.ts +22 -0
  122. package/esm/MultiLevelRubberband/rowLabel.js +27 -0
  123. package/esm/MultiLevelRubberband/types.d.ts +1 -0
  124. package/esm/MultiLevelRubberband/useRangeSelect.d.ts +2 -1
  125. package/esm/MultiLevelRubberband/useRangeSelect.js +31 -47
  126. package/esm/displayKitTests/perRegionTestEnv.d.ts +214 -86
  127. package/esm/displayKitTests/perRegionTestEnv.js +30 -29
  128. package/esm/index.d.ts +15 -85
  129. package/esm/index.js +13 -36
  130. package/esm/searchUtils.d.ts +18 -9
  131. package/esm/searchUtils.js +85 -69
  132. package/esm/shared/RubberbandSpan.js +2 -1
  133. package/esm/shared/coordLabels.d.ts +7 -0
  134. package/esm/shared/coordLabels.js +15 -1
  135. package/esm/shared/useWindowDrag.d.ts +18 -0
  136. package/esm/shared/useWindowDrag.js +45 -0
  137. package/package.json +10 -10
  138. package/esm/LinearGenomeView/FloatingSvgOverlay.d.ts +0 -5
  139. package/esm/LinearGenomeView/FloatingSvgOverlay.js +0 -25
  140. package/esm/LinearGenomeView/initKeys.d.ts +0 -13
  141. package/esm/LinearGenomeView/initKeys.js +0 -104
  142. package/esm/MultiLevelRubberband/VerticalGuide.d.ts +0 -6
  143. package/esm/MultiLevelRubberband/VerticalGuide.js +0 -21
  144. package/esm/lazyPluginExports.d.ts +0 -23
  145. package/esm/lazyPluginExports.js +0 -36
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getContainingView } from '@jbrowse/core/util';
3
2
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
3
  import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
5
4
  import ArrowRightIcon from '@mui/icons-material/ArrowRight';
6
5
  import CloseIcon from '@mui/icons-material/Close';
7
6
  import { IconButton } from '@mui/material';
8
7
  import { observer } from 'mobx-react';
8
+ import { containingLgv } from '../../LinearGenomeView/containingLgv.js';
9
9
  const useStyles = makeStyles()({
10
10
  iconButton: {
11
11
  padding: 0,
@@ -21,7 +21,7 @@ const useStyles = makeStyles()({
21
21
  // write it.
22
22
  export const TrackLabelCloseButton = observer(function TrackLabelCloseButton({ track, }) {
23
23
  const { classes } = useStyles();
24
- const view = getContainingView(track);
24
+ const view = containingLgv(track);
25
25
  return (_jsx(IconButton, { onClick: () => view.hideTrack(track.trackId), className: classes.iconButton, title: "close this track", "data-testid": `track-close-${track.trackId}`, children: _jsx(CloseIcon, { fontSize: "small" }) }));
26
26
  });
27
27
  export const TrackLabelMinimizeButton = observer(function TrackLabelMinimizeButton({ track }) {
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getContainingView } from '@jbrowse/core/util';
3
2
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
3
  import DragIcon from '@mui/icons-material/DragIndicator';
5
4
  import { observer } from 'mobx-react';
5
+ import { containingLgv } from '../../LinearGenomeView/containingLgv.js';
6
6
  const useStyles = makeStyles()({
7
7
  dragHandle: {
8
8
  cursor: 'grab',
@@ -15,7 +15,7 @@ const useStyles = makeStyles()({
15
15
  });
16
16
  const TrackLabelDragHandle = observer(function TrackLabelDragHandle({ track, }) {
17
17
  const { classes } = useStyles();
18
- const view = getContainingView(track);
18
+ const view = containingLgv(track);
19
19
  const trackId = track.trackId;
20
20
  return (_jsx("span", { draggable: true, className: classes.dragHandle, onDragStart: event => {
21
21
  const target = event.currentTarget;
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
3
- import { getContainingView, getSession } from '@jbrowse/core/util';
3
+ import { getSession } from '@jbrowse/core/util';
4
4
  import { makeStyles } from '@jbrowse/core/util/tss-react';
5
5
  import LowPriorityIcon from '@mui/icons-material/LowPriority';
6
6
  import MoreVertIcon from '@mui/icons-material/MoreVert';
7
7
  import { observer } from 'mobx-react';
8
+ import { containingLgv } from '../../LinearGenomeView/containingLgv.js';
8
9
  import { getTrackOrderSubMenu } from './trackLabelMenuItems.js';
9
10
  // IconButton's default 5px padding made this the tallest thing in the label
10
11
  // (27px), so it, not the text or the other controls, set the label height and
@@ -17,7 +18,7 @@ const useStyles = makeStyles()({
17
18
  });
18
19
  const TrackLabelMenu = observer(function TrackLabelMenu({ track, }) {
19
20
  const { classes } = useStyles();
20
- const view = getContainingView(track);
21
+ const view = containingLgv(track);
21
22
  const session = getSession(track);
22
23
  function getMenuItems() {
23
24
  const orderSubMenu = getTrackOrderSubMenu({ view, track });
@@ -1,8 +1,11 @@
1
1
  import type { LinearGenomeViewModel } from '../index.ts';
2
2
  import type { BaseTrackModel } from '@jbrowse/core/pluggableElementTypes/models';
3
+ import type { LinearDisplayModel } from '@jbrowse/display-kit/types';
3
4
  type LGV = LinearGenomeViewModel;
4
- declare const TrackRenderingContainer: ({ model, track, }: {
5
+ declare const TrackRenderingContainer: ({ model, track, display, ref, }: {
5
6
  model: LGV;
6
7
  track: BaseTrackModel;
8
+ display: LinearDisplayModel;
9
+ ref?: React.Ref<HTMLDivElement>;
7
10
  }) => import("react").JSX.Element;
8
11
  export default TrackRenderingContainer;
@@ -1,11 +1,12 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Suspense, useCallback } from 'react';
2
+ import { Suspense } from 'react';
3
3
  import { LoadingOverlay } from '@jbrowse/core/ui';
4
- import { getSession } from '@jbrowse/core/util';
4
+ import { coarseStripHTML, getSession } from '@jbrowse/core/util';
5
5
  import { getTrackName } from '@jbrowse/core/util/tracks';
6
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
7
+ import { TrackOverlayPortal } from '@jbrowse/display-ui';
7
8
  import { observer } from 'mobx-react';
8
- import { MINIMIZED_TRACK_HEIGHT } from '../consts.js';
9
+ import { TRACK_OUTLINE_BORDER } from '../consts.js';
9
10
  const useStyles = makeStyles()({
10
11
  // aligns with block boundaries. check for example the breakpoint split view
11
12
  // demo to see if features align if wanting to change things. the -1 left
@@ -68,32 +69,19 @@ const useStyles = makeStyles()({
68
69
  // nothing. `figure` names the box and keeps what is inside it reachable. Revisit
69
70
  // if inline interactive chrome ever goes away.
70
71
  const TrackDisplayRegion = observer(function TrackDisplayRegion({ model, track, children, ...divProps }) {
71
- const trackName = getTrackName(track.configuration, getSession(track));
72
+ const trackName = coarseStripHTML(getTrackName(track.configuration, getSession(track)));
72
73
  const loc = model.coarseVisibleLocStrings;
73
74
  return (_jsx("div", { ...divProps, role: "figure", "aria-label": loc ? `${trackName} track, showing ${loc}` : `${trackName} track`, children: children }));
74
75
  });
75
- const TrackRenderingContainer = observer(function TrackRenderingContainer({ model, track, }) {
76
+ const TrackRenderingContainer = observer(function TrackRenderingContainer({ model, track, display, ref, }) {
76
77
  const { classes } = useStyles();
77
- // an LGV track always holds at least one linear display (activeDisplay =
78
- // displays[0]); narrow to the linear shape for height/RenderingComponent
79
- const display = track.activeDisplay;
80
- const { height, RenderingComponent, DisplayBlurb } = display;
81
- const { trackRefs, showTrackOutlines } = model;
78
+ const { RenderingComponent, DisplayBlurb } = display;
79
+ const { showTrackOutlines } = model;
82
80
  const trackId = track.trackId;
83
81
  const minimized = track.minimized;
84
- // callback ref keeps trackRefs in sync as the rendering div mounts/unmounts
85
- // (e.g. on minimize/restore), unlike a useEffect that misses the toggle
86
- const setRef = useCallback((el) => {
87
- if (el) {
88
- trackRefs[trackId] = el;
89
- }
90
- else {
91
- delete trackRefs[trackId];
92
- }
93
- }, [trackRefs, trackId]);
94
- return (_jsx("div", { className: classes.trackRenderingContainer, style: {
95
- height: minimized ? MINIMIZED_TRACK_HEIGHT : height,
96
- }, "data-testid": `trackRenderingContainer-${model.id}-${trackId}`, children: !minimized ? (_jsxs(_Fragment, { children: [_jsx(TrackDisplayRegion, { model: model, track: track, ref: setRef, className: classes.renderingComponentContainer, style: { left: showTrackOutlines ? -1 : 0 }, children: _jsx(Suspense, { fallback: _jsx(LoadingOverlay, { isVisible: true, immediate: true }), children: _jsx(RenderingComponent, { model: display, onHorizontalScroll: model.horizontalScroll }) }) }), DisplayBlurb ? (_jsx("div", { style: {
82
+ return (_jsx("div", { ref: ref, className: classes.trackRenderingContainer, style: {
83
+ height: model.trackHeight(track),
84
+ }, "data-testid": `trackRenderingContainer-${model.id}-${trackId}`, children: !minimized ? (_jsxs(_Fragment, { children: [_jsx(TrackDisplayRegion, { model: model, track: track, className: classes.renderingComponentContainer, style: { left: showTrackOutlines ? -TRACK_OUTLINE_BORDER : 0 }, children: _jsx(Suspense, { fallback: _jsx(TrackOverlayPortal, { children: _jsx(LoadingOverlay, { isVisible: true, immediate: true }) }), children: _jsx(RenderingComponent, { model: display, onHorizontalScroll: model.horizontalScroll }) }) }), DisplayBlurb ? (_jsx("div", { style: {
97
85
  position: 'absolute',
98
86
  left: 0,
99
87
  top: display.height - 20,
@@ -1,11 +1,9 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Suspense, lazy, useRef } from 'react';
3
3
  import { PluggableElements } from '@jbrowse/core/ui';
4
- import { OverlayPointerProvider } from '@jbrowse/core/ui/highlightChipReveal';
5
- import { useMouseTracking } from '@jbrowse/core/ui/useMouseTracking';
4
+ import { useStyleTheme } from '@jbrowse/core/ui/PaletteContext';
6
5
  import { getEnv, getSession } from '@jbrowse/core/util';
7
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
8
- import { useTheme } from '@mui/material';
9
7
  import { observer } from 'mobx-react';
10
8
  import { SCALE_BAR_HEIGHT } from '../consts.js';
11
9
  import Gridlines from './Gridlines.js';
@@ -13,7 +11,6 @@ import OverviewHighlightBand from './OverviewHighlightBand.js';
13
11
  import PaddingBlocks from './PaddingBlocks.js';
14
12
  import RangeSelectOverlay from './RangeSelectOverlay.js';
15
13
  import Rubberband from './Rubberband.js';
16
- import Scalebar from './Scalebar.js';
17
14
  import VerticalGuide from './VerticalGuide.js';
18
15
  import { useRangeSelect } from './useRangeSelect.js';
19
16
  import { useSideScroll } from './useSideScroll.js';
@@ -24,6 +21,9 @@ const useStyles = makeStyles()({
24
21
  tracksContainer: {
25
22
  position: 'relative',
26
23
  contain: 'layout style',
24
+ // horizontal drags and pinches are the view's; vertical scroll stays the
25
+ // page's
26
+ touchAction: 'pan-y',
27
27
  },
28
28
  scalebarHighlights: {
29
29
  position: 'absolute',
@@ -39,40 +39,28 @@ const useStyles = makeStyles()({
39
39
  const TracksContainer = observer(function TracksContainer({ children, model, }) {
40
40
  const { classes } = useStyles();
41
41
  const { pluginManager } = getEnv(model);
42
- const { mouseDown: sideScrollMouseDown, mouseUp } = useSideScroll(model);
42
+ const sideScroll = useSideScroll(model);
43
43
  const { showGridlines, showCenterLine } = model;
44
44
  const ref = useRef(null);
45
45
  // shift-drag range select over the whole tracks area. This is intentionally
46
46
  // a separate range-select instance from the one inside <Rubberband> (the
47
47
  // scalebar control); don't dedupe them — they cover different regions.
48
48
  const range = useRangeSelect(ref, model, true);
49
- // published rather than held (see `useMouseTracking`): the highlight bands
50
- // read it, and a position in state here would re-render the whole tracks area
51
- // every frame the pointer moves over it
52
- const { mouseTracker, handleMouseMove, handleMouseLeave } = useMouseTracking();
53
- return (_jsx("div", { ref: ref, "data-testid": "tracksContainer", className: classes.tracksContainer, onMouseDown: event => {
54
- sideScrollMouseDown(event);
55
- range.mouseDown(event);
56
- }, onMouseMove: event => {
57
- handleMouseMove(event);
49
+ return (_jsxs("div", { ref: ref, "data-testid": "tracksContainer", className: classes.tracksContainer, onPointerDown: sideScroll.pointerDown, onClickCapture: sideScroll.clickCapture, onMouseDown: range.mouseDown, onMouseMove: event => {
58
50
  range.mouseMove(event);
59
51
  }, onMouseLeave: () => {
60
- handleMouseLeave();
61
52
  range.mouseOut();
62
- }, onMouseUp: mouseUp, children: _jsxs(OverlayPointerProvider, { value: mouseTracker, children: [showGridlines ? (_jsxs(_Fragment, { children: [_jsx(Gridlines, { model: model }), _jsx(PaddingBlocks, { model: model })] })) : null, _jsx(Suspense, { fallback: null, children: showCenterLine ? _jsx(CenterLine, { model: model }) : null }), _jsx(RangeSelectOverlay, { model: model, range: range }), model.volatileGuides.map((guide, idx) => (_jsx(VerticalGuide, { model: model, coordX: guide.xPos }, idx))), _jsx(Rubberband, { model: model, ControlComponent: _jsx(Scalebar, { model: model, style: {
63
- height: SCALE_BAR_HEIGHT,
64
- boxSizing: 'border-box',
65
- } }) }), _jsx(ScalebarHighlightGroup, { model: model }), _jsx(HighlightGroup, { model: model }), _jsx(PluggableElements, { pluginManager: pluginManager, name: "LinearGenomeView-TracksContainerComponent", props: { model } }), children] }) }));
53
+ }, children: [showGridlines ? (_jsxs(_Fragment, { children: [_jsx(Gridlines, { model: model }), _jsx(PaddingBlocks, { model: model })] })) : null, _jsx(Suspense, { fallback: null, children: showCenterLine ? _jsx(CenterLine, { model: model }) : null }), _jsx(RangeSelectOverlay, { model: model, range: range }), model.volatileGuides.map((guide, idx) => (_jsx(VerticalGuide, { model: model, coordX: guide.xPos }, idx))), _jsx(Rubberband, { model: model }), _jsx(ScalebarHighlightGroup, { model: model }), _jsx(HighlightGroup, { model: model }), _jsx(PluggableElements, { pluginManager: pluginManager, name: "LinearGenomeView-TracksContainerComponent", props: { model } }), children] }));
66
54
  });
67
55
  const HighlightGroup = observer(function HighlightGroup({ model, }) {
68
- return getSession(model).highlightsVisible && model.highlight.length ? (_jsx(Suspense, { fallback: null, children: model.highlight.map((highlight, idx) => (_jsx(Highlight, { model: model, highlight: highlight }, highlightKey(highlight, idx)))) })) : null;
56
+ return getSession(model).highlightsVisible && model.highlights.length ? (_jsx(Suspense, { fallback: null, children: model.highlights.map((highlight, idx) => (_jsx(Highlight, { model: model, highlight: highlight }, highlightKey(highlight, idx)))) })) : null;
69
57
  });
70
58
  const ScalebarHighlightGroup = observer(function ScalebarHighlightGroup({ model, }) {
71
- const theme = useTheme();
59
+ const theme = useStyleTheme();
72
60
  const { pluginManager } = getEnv(model);
73
61
  const { classes } = useStyles();
74
62
  const viewBands = getSession(model).highlightsVisible
75
- ? model.highlight.map((h, idx) => {
63
+ ? model.highlights.map((h, idx) => {
76
64
  const coords = model.getHighlightCoords(h);
77
65
  return coords ? (_jsx(OverviewHighlightBand, { coords: coords, background: getHighlightColor(h, theme).toRgbString() }, highlightKey(h, idx))) : null;
78
66
  })
@@ -1,7 +1,6 @@
1
1
  import type { LinearGenomeViewModel } from '../index.ts';
2
- type LGV = LinearGenomeViewModel;
3
2
  declare const VerticalGuide: ({ model, coordX, }: {
4
- model: LGV;
3
+ model: LinearGenomeViewModel;
5
4
  coordX: number;
6
5
  }) => import("react").JSX.Element;
7
6
  export default VerticalGuide;
@@ -1,24 +1,11 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { stringify } from '@jbrowse/core/util';
3
- import { makeStyles } from '@jbrowse/core/util/tss-react';
4
3
  import { observer } from 'mobx-react';
5
- import { GuideLabel } from '../../shared/coordLabels.js';
4
+ import { VerticalGuideLine } from '../../shared/coordLabels.js';
6
5
  import { stickyChromeTops } from '../stickyChrome.js';
7
- const useStyles = makeStyles()({
8
- guide: {
9
- pointerEvents: 'none',
10
- height: '100%',
11
- width: 1,
12
- position: 'absolute',
13
- left: 0,
14
- background: 'red',
15
- zIndex: 1001,
16
- },
17
- });
18
6
  const VerticalGuide = observer(function VerticalGuide({ model, coordX, }) {
19
- const { classes } = useStyles();
20
7
  const { width, stickyViewHeaders, headerHeight } = model;
21
8
  const { scalebar } = stickyChromeTops({ stickyViewHeaders, headerHeight });
22
- return (_jsxs(_Fragment, { children: [_jsx(GuideLabel, { coordX: coordX, viewWidth: width, stickyTop: scalebar, children: stringify(model.pxToBp(coordX)) }), _jsx("div", { className: classes.guide, style: { transform: `translateX(${coordX}px)` } })] }));
9
+ return (_jsx(VerticalGuideLine, { coordX: coordX, viewWidth: width, stickyTop: scalebar, children: stringify(model.pxToBp(coordX)) }));
23
10
  });
24
11
  export default VerticalGuide;
@@ -15,7 +15,12 @@ const ZoomButton = observer(function ZoomButton({ model, direction, small, }) {
15
15
  ? coarseBpPerPx >= maxBpPerPx - EPSILON
16
16
  : coarseBpPerPx <= minBpPerPx + EPSILON;
17
17
  const fontSize = small ? 'small' : undefined;
18
- return (_jsx(Tooltip, { title: out ? 'Zoom out 2x' : 'Zoom in 2x', children: _jsx("span", { children: _jsx(IconButton, { "data-testid": out ? 'zoom_out' : 'zoom_in', size: small ? 'small' : undefined, disabled: disabled, onClick: () => {
18
+ return (_jsx(Tooltip, { title: out ? 'Zoom out 2x' : 'Zoom in 2x', children: _jsx("span", { children: _jsx(IconButton
19
+ // on the button, not left to the Tooltip: its title lands on the
20
+ // <span> the disabled case needs, so the button itself reached the
21
+ // accessibility tree unnamed and nothing driving by name could aim at
22
+ // it
23
+ , { "aria-label": out ? 'Zoom out' : 'Zoom in', "data-testid": out ? 'zoom_out' : 'zoom_in', size: small ? 'small' : undefined, disabled: disabled, onClick: () => {
19
24
  model.zoom(out ? model.bpPerPx * 2 : model.bpPerPx / 2);
20
25
  }, children: out ? (_jsx(ZoomOut, { fontSize: fontSize })) : (_jsx(ZoomIn, { fontSize: fontSize })) }) }) }));
21
26
  });
@@ -13,6 +13,10 @@ const useStyles = makeStyles()({
13
13
  position: 'absolute',
14
14
  height: '100%',
15
15
  pointerEvents: 'none',
16
+ // its own compositor layer: a pan then moves the layer instead of
17
+ // re-rasterizing the root layer's tiles, which paid for the gridline paths
18
+ // on every frame
19
+ willChange: 'transform',
16
20
  },
17
21
  });
18
22
  // Shared zoom/scroll-transform wrapper for the gridline ticks and padding-block
@@ -1,8 +1,7 @@
1
1
  import type { StatusCallback } from '@jbrowse/core/util';
2
- import type { StopToken } from '@jbrowse/core/util/stopToken';
3
2
  import type { Region } from '@jbrowse/core/util/types';
4
3
  import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
5
4
  export declare function fetchSequence(model: IAnyStateTreeNode, regions: Region[], opts?: {
6
- stopToken?: StopToken;
5
+ signal?: AbortSignal;
7
6
  statusCallback?: StatusCallback;
8
7
  }): Promise<import("@jbrowse/core/util").Feature[]>;
@@ -19,6 +19,16 @@ export function recentLocationsMenu({ recentLocations, onNavigate, onClear, }) {
19
19
  label: 'Recent',
20
20
  subMenu: [
21
21
  ...recentLocations.map(({ label, loc }) => ({
22
+ // the list dedupes on the location, so two rows can share a
23
+ // display string — two features of the same name at different
24
+ // loci, or two snippets ellipsized to the same text. `id` is what
25
+ // keeps them separate rows to React, which otherwise keys both on
26
+ // that one label.
27
+ //
28
+ // Prefixed, because a freehand row has no location and falls back
29
+ // to its label, which may itself be a locstring — unprefixed, that
30
+ // row and a recorded one at the same locus share an id.
31
+ id: loc ? `loc:${loc}` : `query:${label}`,
22
32
  label,
23
33
  onClick: () => {
24
34
  // a row that recorded a location navigates straight to it; one
@@ -1,6 +1,6 @@
1
1
  export interface SequenceSearchModel {
2
2
  assemblyNames: string[];
3
- showTrack: (trackId: string) => void;
3
+ launchTrack: (trackId: string) => Promise<unknown>;
4
4
  }
5
5
  export interface SequenceSearchModeProps {
6
6
  model: SequenceSearchModel;
@@ -14,7 +14,7 @@ export function addReferenceScanTrack(model, args) {
14
14
  type: 'FeatureTrack',
15
15
  adapter: args.adapter,
16
16
  });
17
- model.showTrack(args.trackId);
17
+ void model.launchTrack(args.trackId);
18
18
  }
19
19
  else {
20
20
  session.notify('This session does not support adding tracks', 'warning');
@@ -7,11 +7,8 @@ interface AnchorPosition {
7
7
  isClick: boolean;
8
8
  }
9
9
  export declare function useRangeSelect(ref: React.RefObject<HTMLDivElement | null>, model: LinearGenomeViewModel, shiftOnly?: boolean): {
10
- open: boolean;
11
10
  isClick: boolean | undefined;
12
- clickBpOffset: import("@jbrowse/core/util/Base1DUtils").PxToBpResult | undefined;
13
11
  guideX: number | undefined;
14
- rubberbandOn: boolean;
15
12
  mouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;
16
13
  mouseMove: (event: React.MouseEvent<HTMLDivElement>) => void;
17
14
  mouseOut: () => void;
@@ -1,8 +1,11 @@
1
- import { useCallback, useEffect, useState } from 'react';
1
+ import { useCallback, useState } from 'react';
2
2
  import { getRelativeX } from '@jbrowse/core/util/getRelativeX';
3
+ import { useWindowDrag } from '../../shared/useWindowDrag.js';
4
+ import { isOnScalebarRefLabel } from './util.js';
3
5
  export function useRangeSelect(ref, model, shiftOnly) {
4
6
  const [startX, setStartX] = useState();
5
7
  const [currentX, setCurrentX] = useState();
8
+ const [startedOnRefLabel, setStartedOnRefLabel] = useState(false);
6
9
  // clientX and clientY used for anchorPosition for menu
7
10
  // offsetX used for calculations about width of selection
8
11
  const [anchorPosition, setAnchorPosition] = useState();
@@ -14,67 +17,32 @@ export function useRangeSelect(ref, model, shiftOnly) {
14
17
  setCurrentX(undefined);
15
18
  setGuideX(undefined);
16
19
  }, []);
17
- useEffect(() => {
18
- const el = ref.current;
19
- if (!mouseDragging || !el) {
20
- return;
21
- }
22
- // the container's left edge is fixed for the duration of a drag, so measure
23
- // it once here rather than calling getBoundingClientRect on every mousemove
24
- const { left } = el.getBoundingClientRect();
25
- function globalMouseMove(event) {
26
- setCurrentX(event.clientX - left);
27
- }
28
- function globalMouseUp(event) {
29
- if (startX === undefined) {
30
- return;
31
- }
32
- const { clientX, clientY } = event;
33
- const offsetX = clientX - left;
34
- const isClick = Math.abs(offsetX - startX) <= 3;
35
- // Clear the flag unconditionally once consumed: the label's own onClick
36
- // also clears it, but that only fires if mouseup lands on the label, so
37
- // relying on it alone can leave the flag stuck (swallowing the next
38
- // scalebar click) when mouseup drifts a few px off the label edge.
39
- const startedOnRefLabel = model.scalebarRefNameClickPending;
40
- if (startedOnRefLabel) {
41
- model.setScalebarRefNameClickPending(false);
42
- }
43
- // A click that began on a scalebar refname label is handled by that
44
- // label's onClick (opens its menu), not by the rubberband menu here.
45
- // Drop the hover guide too: the label's menu covers it while it is open,
46
- // and nothing else clears it, so picking an item like "Focus on X" left a
47
- // red line stranded on the scalebar with the pointer nowhere near it
20
+ useWindowDrag(ref, mouseDragging ? startX : undefined, {
21
+ onMove: setCurrentX,
22
+ onEnd: ({ startX, endX, isClick, clientX, clientY }) => {
23
+ // A click on a scalebar refName label belongs to the label's own menu.
24
+ // Drop the hover guide too: that menu covers it, and nothing else clears
25
+ // it once the menu closes
48
26
  if (isClick && startedOnRefLabel) {
49
27
  setStartX(undefined);
50
28
  setCurrentX(undefined);
51
29
  setGuideX(undefined);
52
30
  return;
53
31
  }
54
- setAnchorPosition({ offsetX, clientX, clientY, isClick });
32
+ setAnchorPosition({ offsetX: endX, clientX, clientY, isClick });
55
33
  if (isClick) {
56
- setGuideX(offsetX);
34
+ setGuideX(endX);
57
35
  }
58
36
  else {
59
- model.setOffsets(model.pxToBp(Math.min(startX, offsetX)), model.pxToBp(Math.max(startX, offsetX)));
37
+ model.setOffsets(model.pxToBp(Math.min(startX, endX)), model.pxToBp(Math.max(startX, endX)));
60
38
  setGuideX(undefined);
61
39
  }
62
- }
63
- function globalKeyDown(event) {
64
- if (event.key === 'Escape') {
65
- setStartX(undefined);
66
- setCurrentX(undefined);
67
- }
68
- }
69
- window.addEventListener('mousemove', globalMouseMove);
70
- window.addEventListener('mouseup', globalMouseUp);
71
- window.addEventListener('keydown', globalKeyDown);
72
- return () => {
73
- window.removeEventListener('mousemove', globalMouseMove);
74
- window.removeEventListener('mouseup', globalMouseUp);
75
- window.removeEventListener('keydown', globalKeyDown);
76
- };
77
- }, [startX, mouseDragging, model, ref]);
40
+ },
41
+ onCancel: () => {
42
+ setStartX(undefined);
43
+ setCurrentX(undefined);
44
+ },
45
+ });
78
46
  function mouseDown(event) {
79
47
  if (shiftOnly && !event.shiftKey) {
80
48
  return;
@@ -90,6 +58,7 @@ export function useRangeSelect(ref, model, shiftOnly) {
90
58
  // clear any leftover menu/selection so a fresh drag isn't blocked by a
91
59
  // stale anchorPosition keeping mouseDragging false
92
60
  setAnchorPosition(undefined);
61
+ setStartedOnRefLabel(isOnScalebarRefLabel(event.target));
93
62
  const relativeX = getRelativeX(event, ref.current);
94
63
  setStartX(relativeX);
95
64
  setCurrentX(relativeX);
@@ -111,11 +80,7 @@ export function useRangeSelect(ref, model, shiftOnly) {
111
80
  callback();
112
81
  handleClose();
113
82
  }
114
- const open = Boolean(anchorPosition);
115
83
  const isClick = anchorPosition?.isClick;
116
- const clickBpOffset = isClick
117
- ? model.pxToBp(anchorPosition.offsetX)
118
- : undefined;
119
84
  // rubberband geometry only exists while a drag is in progress (startX set);
120
85
  // grouped so a single truthiness check narrows all fields for RubberbandSpan
121
86
  let rubberband;
@@ -132,12 +97,8 @@ export function useRangeSelect(ref, model, shiftOnly) {
132
97
  };
133
98
  }
134
99
  return {
135
- open,
136
100
  isClick,
137
- clickBpOffset,
138
101
  guideX,
139
- // true only mid-drag and not a click — drives RubberbandSpan vs guide
140
- rubberbandOn: rubberband !== undefined && !isClick,
141
102
  mouseDown,
142
103
  mouseMove,
143
104
  mouseOut,
@@ -1,6 +1,20 @@
1
1
  import type { LinearGenomeViewModel } from '../index.ts';
2
2
  import type React from 'react';
3
+ /**
4
+ * Drag to pan and pinch to zoom, on pointer events so a finger works the same
5
+ * as a mouse. A second finger turns a pan into a pinch: the spread between the
6
+ * two zooms about their midpoint, and moving the midpoint pans. Lifting either
7
+ * finger goes back to panning with the other.
8
+ *
9
+ * The host sets `touch-action` to leave the browser only the gestures this
10
+ * does not want (TracksContainer keeps vertical scroll); without it a touch
11
+ * drag is a page scroll and a pinch zooms the page, and no pointer stream
12
+ * arrives.
13
+ *
14
+ * The window listeners go on in the press handler itself rather than in an
15
+ * effect, so a release that lands before React commits still finds them.
16
+ */
3
17
  export declare function useSideScroll(model: LinearGenomeViewModel): {
4
- mouseDown: (event: React.MouseEvent) => void;
5
- mouseUp: (event: React.MouseEvent) => void;
18
+ pointerDown: (event: React.PointerEvent) => void;
19
+ clickCapture: (event: React.MouseEvent) => void;
6
20
  };