@jbrowse/plugin-linear-genome-view 5.0.0-beta.2 → 5.0.0-beta.20

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 (177) hide show
  1. package/esm/LinearGenomeView/afterAttach.d.ts +22 -5
  2. package/esm/LinearGenomeView/afterAttach.js +129 -106
  3. package/esm/LinearGenomeView/axisConfigSchema.d.ts +18 -0
  4. package/esm/LinearGenomeView/axisConfigSchema.js +22 -0
  5. package/esm/LinearGenomeView/components/CenterLine.js +10 -5
  6. package/esm/LinearGenomeView/components/ConnectedHoverHighlight.js +2 -3
  7. package/esm/LinearGenomeView/components/CrisprGuidePanel.js +3 -5
  8. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +9 -4
  9. package/esm/LinearGenomeView/components/ExportSvgDialog.js +45 -8
  10. package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
  11. package/esm/LinearGenomeView/components/Header.js +11 -1
  12. package/esm/LinearGenomeView/components/HeaderClearHighlightButton.js +2 -2
  13. package/esm/LinearGenomeView/components/HeaderPanControls.d.ts +3 -3
  14. package/esm/LinearGenomeView/components/HeaderPanControls.js +6 -3
  15. package/esm/LinearGenomeView/components/HeaderSearchBoxes.js +4 -1
  16. package/esm/LinearGenomeView/components/HeaderZoomControls.js +6 -35
  17. package/esm/LinearGenomeView/components/Highlight.js +27 -11
  18. package/esm/LinearGenomeView/components/HighlightBand.d.ts +11 -2
  19. package/esm/LinearGenomeView/components/HighlightBand.js +32 -13
  20. package/esm/LinearGenomeView/components/HighlightChip.d.ts +3 -3
  21. package/esm/LinearGenomeView/components/HighlightChip.js +13 -8
  22. package/esm/LinearGenomeView/components/HighlightLabelDialog.d.ts +6 -0
  23. package/esm/LinearGenomeView/components/HighlightLabelDialog.js +18 -0
  24. package/esm/LinearGenomeView/components/HoverPositionHighlight.js +3 -3
  25. package/esm/LinearGenomeView/components/ImportForm.js +7 -9
  26. package/esm/LinearGenomeView/components/LinearGenomeView.js +5 -12
  27. package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +13 -14
  28. package/esm/LinearGenomeView/components/MiniControls.js +11 -5
  29. package/esm/LinearGenomeView/components/OverviewHighlight.js +3 -3
  30. package/esm/LinearGenomeView/components/OverviewHighlightBand.d.ts +3 -2
  31. package/esm/LinearGenomeView/components/OverviewHighlightBand.js +6 -5
  32. package/esm/LinearGenomeView/components/OverviewRubberband.js +25 -48
  33. package/esm/LinearGenomeView/components/OverviewScalebar.js +4 -5
  34. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +5 -3
  35. package/esm/LinearGenomeView/components/PaddingBlocks.js +3 -8
  36. package/esm/LinearGenomeView/components/RangeSelectOverlay.js +5 -6
  37. package/esm/LinearGenomeView/components/RegionWidthEditorDialog.d.ts +3 -2
  38. package/esm/LinearGenomeView/components/RegionWidthEditorDialog.js +4 -4
  39. package/esm/LinearGenomeView/components/ResizeAllTracksHandle.d.ts +5 -0
  40. package/esm/LinearGenomeView/components/ResizeAllTracksHandle.js +74 -0
  41. package/esm/LinearGenomeView/components/Rubberband.d.ts +1 -2
  42. package/esm/LinearGenomeView/components/Rubberband.js +3 -2
  43. package/esm/LinearGenomeView/components/SVGHighlightBand.d.ts +10 -1
  44. package/esm/LinearGenomeView/components/SVGHighlightBand.js +16 -5
  45. package/esm/LinearGenomeView/components/Scalebar.d.ts +2 -6
  46. package/esm/LinearGenomeView/components/Scalebar.js +8 -5
  47. package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +51 -36
  48. package/esm/LinearGenomeView/components/SearchBox.js +10 -16
  49. package/esm/LinearGenomeView/components/SearchPanelForm.d.ts +3 -2
  50. package/esm/LinearGenomeView/components/SearchPanelForm.js +4 -2
  51. package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +4 -8
  52. package/esm/LinearGenomeView/components/SearchResultsDialog.js +7 -4
  53. package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +4 -7
  54. package/esm/LinearGenomeView/components/SearchResultsTable.js +25 -53
  55. package/esm/LinearGenomeView/components/SequenceFeatureHoverHighlight.js +2 -3
  56. package/esm/LinearGenomeView/components/SequenceSearchDialog.d.ts +2 -4
  57. package/esm/LinearGenomeView/components/StrandCheckboxes.d.ts +3 -2
  58. package/esm/LinearGenomeView/components/StrandCheckboxes.js +4 -2
  59. package/esm/LinearGenomeView/components/TrackContainer.d.ts +2 -3
  60. package/esm/LinearGenomeView/components/TrackContainer.js +63 -10
  61. package/esm/LinearGenomeView/components/TrackLabel.js +14 -4
  62. package/esm/LinearGenomeView/components/TrackLabelButtons.js +2 -2
  63. package/esm/LinearGenomeView/components/TrackLabelDragHandle.js +4 -5
  64. package/esm/LinearGenomeView/components/TrackLabelMenu.js +11 -14
  65. package/esm/LinearGenomeView/components/TrackLabelRefNameWarning.d.ts +3 -4
  66. package/esm/LinearGenomeView/components/TrackLabelRefNameWarning.js +23 -10
  67. package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +6 -4
  68. package/esm/LinearGenomeView/components/TrackRenderingContainer.js +12 -24
  69. package/esm/LinearGenomeView/components/TracksContainer.js +11 -23
  70. package/esm/LinearGenomeView/components/VerticalGuide.d.ts +1 -2
  71. package/esm/LinearGenomeView/components/VerticalGuide.js +3 -16
  72. package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
  73. package/esm/LinearGenomeView/components/fetchSequence.js +15 -7
  74. package/esm/LinearGenomeView/components/recentLocationsMenu.js +10 -0
  75. package/esm/LinearGenomeView/components/useRangeSelect.d.ts +0 -3
  76. package/esm/LinearGenomeView/components/useRangeSelect.js +26 -61
  77. package/esm/LinearGenomeView/components/useSearchBoxPrefs.d.ts +3 -1
  78. package/esm/LinearGenomeView/components/useSearchBoxPrefs.js +4 -2
  79. package/esm/LinearGenomeView/components/useSideScroll.d.ts +17 -2
  80. package/esm/LinearGenomeView/components/useSideScroll.js +156 -102
  81. package/esm/LinearGenomeView/components/util.d.ts +4 -0
  82. package/esm/LinearGenomeView/components/util.js +4 -2
  83. package/esm/LinearGenomeView/consts.d.ts +2 -0
  84. package/esm/LinearGenomeView/consts.js +3 -1
  85. package/esm/LinearGenomeView/containingLgv.d.ts +21 -0
  86. package/esm/LinearGenomeView/containingLgv.js +23 -0
  87. package/esm/LinearGenomeView/headerFit.d.ts +4 -2
  88. package/esm/LinearGenomeView/headerFit.js +20 -7
  89. package/esm/LinearGenomeView/index.d.ts +2 -7
  90. package/esm/LinearGenomeView/index.js +5 -7
  91. package/esm/LinearGenomeView/keyboardHandler.d.ts +8 -7
  92. package/esm/LinearGenomeView/keyboardHandler.js +61 -23
  93. package/esm/LinearGenomeView/launchKeys.d.ts +1 -1
  94. package/esm/LinearGenomeView/launchKeys.js +11 -6
  95. package/esm/LinearGenomeView/lazyDialogs.d.ts +16 -3
  96. package/esm/LinearGenomeView/lazyDialogs.js +1 -0
  97. package/esm/LinearGenomeView/linkedViewSync.d.ts +3 -11
  98. package/esm/LinearGenomeView/linkedViewSync.js +27 -49
  99. package/esm/LinearGenomeView/menuItems.d.ts +16 -18
  100. package/esm/LinearGenomeView/menuItems.js +264 -164
  101. package/esm/LinearGenomeView/model.d.ts +1192 -560
  102. package/esm/LinearGenomeView/model.js +805 -590
  103. package/esm/LinearGenomeView/rExport/exportLinearView.d.ts +17 -0
  104. package/esm/LinearGenomeView/rExport/exportLinearView.js +117 -0
  105. package/esm/LinearGenomeView/rExport/rScript.d.ts +110 -0
  106. package/esm/LinearGenomeView/rExport/rScript.js +524 -0
  107. package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
  108. package/esm/LinearGenomeView/sharedScaleContainer.js +2 -12
  109. package/esm/LinearGenomeView/showFiles.d.ts +2 -0
  110. package/esm/LinearGenomeView/showFiles.js +19 -0
  111. package/esm/LinearGenomeView/showRegionsWithUndo.d.ts +1 -1
  112. package/esm/LinearGenomeView/showRegionsWithUndo.js +13 -5
  113. package/esm/LinearGenomeView/stickyChrome.d.ts +5 -9
  114. package/esm/LinearGenomeView/stickyChrome.js +5 -9
  115. package/esm/LinearGenomeView/svgcomponents/SVGGridlines.js +4 -4
  116. package/esm/LinearGenomeView/svgcomponents/SVGHeader.d.ts +4 -1
  117. package/esm/LinearGenomeView/svgcomponents/SVGHeader.js +8 -21
  118. package/esm/LinearGenomeView/svgcomponents/SVGHighlights.d.ts +3 -1
  119. package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +9 -9
  120. package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.d.ts +3 -1
  121. package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +5 -5
  122. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +13 -18
  123. package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.js +2 -5
  124. package/esm/LinearGenomeView/svgcomponents/SVGRowHeader.js +5 -6
  125. package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +9 -10
  126. package/esm/LinearGenomeView/svgcomponents/SVGScalebar.js +3 -3
  127. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.d.ts +26 -0
  128. package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.js +12 -0
  129. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +21 -13
  130. package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +4 -5
  131. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +7 -6
  132. package/esm/LinearGenomeView/svgcomponents/SVGView.d.ts +2 -3
  133. package/esm/LinearGenomeView/svgcomponents/SVGView.js +17 -3
  134. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.d.ts +24 -20
  135. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +42 -29
  136. package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +74 -77
  137. package/esm/LinearGenomeView/svgcomponents/util.d.ts +28 -12
  138. package/esm/LinearGenomeView/svgcomponents/util.js +41 -29
  139. package/esm/LinearGenomeView/types.d.ts +13 -10
  140. package/esm/LinearGenomeView/types.js +0 -1
  141. package/esm/LinearGenomeView/util.d.ts +103 -46
  142. package/esm/LinearGenomeView/util.js +222 -170
  143. package/esm/MultiLevelRubberband/MultiLevelRubberband.js +9 -6
  144. package/esm/MultiLevelRubberband/index.d.ts +2 -0
  145. package/esm/MultiLevelRubberband/index.js +2 -0
  146. package/esm/MultiLevelRubberband/menuItems.d.ts +34 -0
  147. package/esm/MultiLevelRubberband/menuItems.js +52 -0
  148. package/esm/MultiLevelRubberband/rowLabel.d.ts +22 -0
  149. package/esm/MultiLevelRubberband/rowLabel.js +27 -0
  150. package/esm/MultiLevelRubberband/types.d.ts +1 -0
  151. package/esm/MultiLevelRubberband/useRangeSelect.d.ts +2 -1
  152. package/esm/MultiLevelRubberband/useRangeSelect.js +45 -59
  153. package/esm/TrackModifiers/index.d.ts +83 -0
  154. package/esm/TrackModifiers/index.js +231 -0
  155. package/esm/displayKitTests/perRegionTestEnv.d.ts +244 -68
  156. package/esm/displayKitTests/perRegionTestEnv.js +30 -26
  157. package/esm/index.d.ts +21 -99
  158. package/esm/index.js +17 -38
  159. package/esm/searchUtils.d.ts +26 -10
  160. package/esm/searchUtils.js +137 -137
  161. package/esm/shared/RubberbandSpan.d.ts +3 -2
  162. package/esm/shared/RubberbandSpan.js +6 -3
  163. package/esm/shared/coordLabels.d.ts +11 -4
  164. package/esm/shared/coordLabels.js +20 -5
  165. package/esm/shared/useWindowDrag.d.ts +18 -0
  166. package/esm/shared/useWindowDrag.js +47 -0
  167. package/package.json +12 -11
  168. package/esm/FeatureTrack/configSchema.d.ts +0 -124
  169. package/esm/FeatureTrack/configSchema.js +0 -54
  170. package/esm/FeatureTrack/index.d.ts +0 -2
  171. package/esm/FeatureTrack/index.js +0 -13
  172. package/esm/LinearGenomeView/initKeys.d.ts +0 -12
  173. package/esm/LinearGenomeView/initKeys.js +0 -68
  174. package/esm/MultiLevelRubberband/VerticalGuide.d.ts +0 -6
  175. package/esm/MultiLevelRubberband/VerticalGuide.js +0 -21
  176. package/esm/lazyPluginExports.d.ts +0 -23
  177. package/esm/lazyPluginExports.js +0 -36
@@ -1,6 +1,21 @@
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
+ onPointerDownCapture: (event: React.PointerEvent) => void;
19
+ onPointerDown: (event: React.PointerEvent) => void;
20
+ onClickCapture: (event: React.MouseEvent) => void;
6
21
  };
@@ -1,125 +1,179 @@
1
- import { useEffect, useRef, useState } from 'react';
1
+ import { useEffect, useRef } from 'react';
2
+ import { isClaimedPress } from '@jbrowse/core/util/pressOwner';
3
+ import { transaction } from 'mobx';
2
4
  // A press that travels further than this is a pan, and the click that ends it
3
5
  // is not a click. A hand that wobbles two pixels can still select something.
4
6
  const PAN_CLICK_THRESHOLD_PX = 4;
5
7
  // The pan publishes its state as attributes on the element the press landed in
6
8
  // (TracksContainer), so a track's own pointer handlers read it with `closest`
7
9
  // and nothing has to hand them a ref or a model field — the same marker shape
8
- // as `data-gesture-owner`. `data-pan-dragging` is present from mousedown to
9
- // mouseup; `data-pan-moved` is set once the press travels past the threshold
10
- // and cleared by the next press, so it still answers for the click that
11
- // follows mouseup.
10
+ // as `data-gesture-owner`. `data-pan-dragging` is present from press to
11
+ // release; `data-pan-moved` is set once the press travels past the threshold
12
+ // or becomes a pinch, and cleared by the next press, so it still answers for
13
+ // the click that follows the release: `onClickCapture` swallows that click
14
+ // before any track sees it.
12
15
  const PAN_DRAGGING_ATTR = 'data-pan-dragging';
13
16
  const PAN_MOVED_ATTR = 'data-pan-moved';
17
+ function pointOf(event) {
18
+ return { x: event.clientX, y: event.clientY };
19
+ }
20
+ /**
21
+ * Drag to pan and pinch to zoom, on pointer events so a finger works the same
22
+ * as a mouse. A second finger turns a pan into a pinch: the spread between the
23
+ * two zooms about their midpoint, and moving the midpoint pans. Lifting either
24
+ * finger goes back to panning with the other.
25
+ *
26
+ * The host sets `touch-action` to leave the browser only the gestures this
27
+ * does not want (TracksContainer keeps vertical scroll); without it a touch
28
+ * drag is a page scroll and a pinch zooms the page, and no pointer stream
29
+ * arrives.
30
+ *
31
+ * The window listeners go on in the press handler itself rather than in an
32
+ * effect, so a release that lands before React commits still finds them.
33
+ */
14
34
  export function useSideScroll(model) {
15
- const [mouseDragging, setMouseDragging] = useState(false);
16
- const scheduledRef = useRef(false);
17
- const rafRef = useRef(null);
18
- const startXRef = useRef(0);
19
- const prevXRef = useRef(0);
20
- const currXRef = useRef(0);
21
- const hostRef = useRef(null);
22
- useEffect(() => {
23
- // apply the movement accumulated since the previous frame, then advance the
24
- // baseline. shared by the rAF tick and the mouseup flush
25
- function flushScroll() {
26
- const distance = currXRef.current - prevXRef.current;
27
- if (distance) {
28
- model.horizontalScroll(-distance);
29
- prevXRef.current = currXRef.current;
35
+ const gestureRef = useRef(undefined);
36
+ useEffect(() => () => {
37
+ gestureRef.current?.cancel();
38
+ }, []);
39
+ // The marker outlives the gesture that set it, so every press clears it, in
40
+ // the capture phase: a track that keeps its own press and stops it bubbling
41
+ // (multiway's lane slide, maf's drag select) would otherwise leave the last
42
+ // pan's marker standing to swallow its click.
43
+ function onPointerDownCapture(event) {
44
+ if (!gestureRef.current) {
45
+ event.currentTarget.removeAttribute(PAN_MOVED_ATTR);
46
+ }
47
+ }
48
+ function onPointerDown(event) {
49
+ const host = event.currentTarget;
50
+ if (gestureRef.current) {
51
+ gestureRef.current.join(event);
52
+ return;
53
+ }
54
+ if (event.shiftKey || event.button !== 0 || !event.isPrimary) {
55
+ return;
56
+ }
57
+ if (isClaimedPress(event)) {
58
+ return;
59
+ }
60
+ const left = host.getBoundingClientRect().left;
61
+ const startX = event.clientX;
62
+ // where each finger is now, and where it was when the view last moved
63
+ const pointers = new Map([[event.pointerId, pointOf(event)]]);
64
+ let applied = new Map(pointers);
65
+ let frame;
66
+ // only a selection that starts in the tracks, so a press that leaks here
67
+ // cannot block selection in the rest of the app
68
+ function blockSelection(e) {
69
+ if (e.target instanceof Node && host.contains(e.target)) {
70
+ e.preventDefault();
30
71
  }
31
72
  }
32
- function globalMouseMove(event) {
33
- event.preventDefault();
34
- currXRef.current = event.clientX;
35
- if (Math.abs(currXRef.current - startXRef.current) > PAN_CLICK_THRESHOLD_PX) {
36
- hostRef.current?.setAttribute(PAN_MOVED_ATTR, '');
73
+ function flush() {
74
+ frame = undefined;
75
+ const [a, b] = [...pointers.keys()];
76
+ const curr1 = pointers.get(a);
77
+ const prev1 = applied.get(a);
78
+ if (b === undefined) {
79
+ if (curr1.x !== prev1.x) {
80
+ model.horizontalScroll(prev1.x - curr1.x);
81
+ }
37
82
  }
38
- const distance = currXRef.current - prevXRef.current;
39
- // use rAF to make it so multiple event handlers aren't fired per-frame
40
- // see https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
41
- if (distance && !scheduledRef.current) {
42
- scheduledRef.current = true;
43
- rafRef.current = window.requestAnimationFrame(() => {
44
- rafRef.current = null;
45
- scheduledRef.current = false;
46
- flushScroll();
83
+ else {
84
+ const curr2 = pointers.get(b);
85
+ const prev2 = applied.get(b);
86
+ const prevSpread = Math.hypot(prev1.x - prev2.x, prev1.y - prev2.y);
87
+ const spread = Math.hypot(curr1.x - curr2.x, curr1.y - curr2.y);
88
+ const prevMid = (prev1.x + prev2.x) / 2;
89
+ const mid = (curr1.x + curr2.x) / 2;
90
+ transaction(() => {
91
+ if (prevSpread > 0 && spread > 0) {
92
+ model.zoomTo((model.bpPerPx * prevSpread) / spread, prevMid - left);
93
+ }
94
+ if (mid !== prevMid) {
95
+ model.horizontalScroll(prevMid - mid);
96
+ }
47
97
  });
48
98
  }
99
+ applied = new Map(pointers);
49
100
  }
50
- function globalMouseUp() {
51
- // flush any movement still queued for the next frame before ending the
52
- // drag; otherwise a quick flick (mousedown/move/up within one frame) or
53
- // the cleanup below would cancel it and drop the scroll
54
- if (rafRef.current !== null) {
55
- window.cancelAnimationFrame(rafRef.current);
56
- rafRef.current = null;
57
- scheduledRef.current = false;
58
- flushScroll();
59
- }
60
- prevXRef.current = 0;
61
- hostRef.current?.removeAttribute(PAN_DRAGGING_ATTR);
62
- if (mouseDragging) {
63
- setMouseDragging(false);
101
+ // Applies what is queued before the set of fingers changes, so the next
102
+ // frame measures from where every finger is now rather than jumping by the
103
+ // distance between them.
104
+ function settle() {
105
+ if (frame !== undefined) {
106
+ window.cancelAnimationFrame(frame);
107
+ flush();
64
108
  }
65
109
  }
66
- if (mouseDragging) {
67
- window.addEventListener('mousemove', globalMouseMove, true);
68
- window.addEventListener('mouseup', globalMouseUp, true);
69
- return () => {
70
- window.removeEventListener('mousemove', globalMouseMove, true);
71
- window.removeEventListener('mouseup', globalMouseUp, true);
72
- // drop a frame queued mid-drag so it can't fire a stray scroll after
73
- // unmount (matches `useVirtualScrollWheel`'s cleanup)
74
- if (rafRef.current !== null) {
75
- window.cancelAnimationFrame(rafRef.current);
76
- rafRef.current = null;
77
- }
78
- scheduledRef.current = false;
79
- };
80
- }
81
- return undefined;
82
- }, [model, mouseDragging]);
83
- function mouseDown(event) {
84
- // Cleared for EVERY press, ahead of the returns below, because the marker
85
- // outlives the gesture that set it: a pan, then a shift-press or a press on
86
- // a button, used to leave `data-pan-moved` standing on the container. The
87
- // one reader today re-enters through a press that reaches the bottom of
88
- // this function, so nothing was wrong — but these attributes are a DOM
89
- // contract other displays read with `closest`, and a stale true is what the
90
- // next reader would inherit.
91
- event.currentTarget.removeAttribute(PAN_MOVED_ATTR);
92
- if (event.shiftKey) {
93
- return;
110
+ function move(e) {
111
+ if (!pointers.has(e.pointerId)) {
112
+ return;
113
+ }
114
+ pointers.set(e.pointerId, pointOf(e));
115
+ if (Math.abs(e.clientX - startX) > PAN_CLICK_THRESHOLD_PX) {
116
+ host.setAttribute(PAN_MOVED_ATTR, '');
117
+ }
118
+ frame ??= window.requestAnimationFrame(flush);
94
119
  }
95
- // skip the click-drag pan when pressing an interactive control: a
96
- // draggable element, a control that claimed the press (resize handles, the
97
- // scalebar), or a button (e.g. the menu button on a highlight/bookmark
98
- // chip, whose actual target is the icon inside it). All three are matched
99
- // with `closest`, since the press usually lands on a child of the control
100
- // rather than the control itself.
101
- const target = event.target;
102
- if (target.draggable ||
103
- target.closest('[data-gesture-owner]') ||
104
- target.closest('button')) {
105
- return;
120
+ function end(applyQueued) {
121
+ if (applyQueued) {
122
+ settle();
123
+ }
124
+ else if (frame !== undefined) {
125
+ window.cancelAnimationFrame(frame);
126
+ }
127
+ host.removeAttribute(PAN_DRAGGING_ATTR);
128
+ window.removeEventListener('pointermove', move, true);
129
+ window.removeEventListener('pointerup', release, true);
130
+ window.removeEventListener('pointercancel', release, true);
131
+ window.removeEventListener('selectstart', blockSelection, true);
132
+ gestureRef.current = undefined;
106
133
  }
107
- // otherwise do click and drag scroll
108
- if (event.button === 0) {
109
- hostRef.current = event.currentTarget;
110
- event.currentTarget.setAttribute(PAN_DRAGGING_ATTR, '');
111
- startXRef.current = event.clientX;
112
- prevXRef.current = event.clientX;
113
- currXRef.current = event.clientX;
114
- setMouseDragging(true);
134
+ // pointercancel as well as pointerup: the browser cancels a touch it takes
135
+ // over as a vertical scroll, and the gesture would otherwise stay latched
136
+ function release(e) {
137
+ if (!pointers.has(e.pointerId)) {
138
+ return;
139
+ }
140
+ if (pointers.size > 1) {
141
+ settle();
142
+ pointers.delete(e.pointerId);
143
+ applied.delete(e.pointerId);
144
+ }
145
+ else {
146
+ end(true);
147
+ }
115
148
  }
149
+ host.setAttribute(PAN_DRAGGING_ATTR, '');
150
+ window.addEventListener('pointermove', move, true);
151
+ window.addEventListener('pointerup', release, true);
152
+ window.addEventListener('pointercancel', release, true);
153
+ window.addEventListener('selectstart', blockSelection, true);
154
+ gestureRef.current = {
155
+ join(e) {
156
+ if (e.pointerType !== 'touch' || pointers.size > 1) {
157
+ return;
158
+ }
159
+ settle();
160
+ pointers.set(e.pointerId, pointOf(e));
161
+ applied = new Map(pointers);
162
+ host.setAttribute(PAN_MOVED_ATTR, '');
163
+ model.cancelZoomAnimation();
164
+ },
165
+ cancel() {
166
+ end(false);
167
+ },
168
+ };
116
169
  }
117
- // this local mouseup is used in addition to the global because sometimes
118
- // the global add/remove are not called in time, resulting in issue #533
119
- function mouseUp(event) {
120
- event.preventDefault();
121
- hostRef.current?.removeAttribute(PAN_DRAGGING_ATTR);
122
- setMouseDragging(false);
170
+ // The browser fires `click` after a release on whatever the press and the
171
+ // release share, which over a track is its canvas, so a pan that ended on a
172
+ // gene would open that gene's details.
173
+ function onClickCapture(event) {
174
+ if (event.currentTarget.hasAttribute(PAN_MOVED_ATTR)) {
175
+ event.stopPropagation();
176
+ }
123
177
  }
124
- return { mouseDown, mouseUp };
178
+ return { onPointerDownCapture, onPointerDown, onClickCapture };
125
179
  }
@@ -1,6 +1,10 @@
1
1
  import type { Assembly } from '@jbrowse/core/assemblyManager/assembly';
2
2
  import type { ViewLayout } from '@jbrowse/core/util/Base1DUtils';
3
3
  export { getHighlightColor, highlightKey } from '@jbrowse/core/util/highlights';
4
+ export declare const scalebarRefLabelProps: {
5
+ 'data-scalebar-ref-label': string;
6
+ };
7
+ export declare function isOnScalebarRefLabel(target: EventTarget | null): boolean;
4
8
  export declare const elidedBlockStyles: {
5
9
  readonly backgroundColor: '#999';
6
10
  readonly backgroundImage: 'repeating-linear-gradient(90deg, transparent, transparent 1px, rgba(255,255,255,.5) 1px, rgba(255,255,255,.5) 3px)';
@@ -3,8 +3,10 @@ import { pxToBp } from '@jbrowse/core/util/Base1DUtils';
3
3
  // getHighlightColor/highlightKey live in core so the dotplot highlight bands can
4
4
  // share the exact same color and React-key semantics
5
5
  export { getHighlightColor, highlightKey } from '@jbrowse/core/util/highlights';
6
- // Shared style for elided (collapsed) blocks — striped grey pattern used
7
- // consistently across OverviewScalebar, ScalebarCoordinateLabels, and Gridlines
6
+ export const scalebarRefLabelProps = { 'data-scalebar-ref-label': '' };
7
+ export function isOnScalebarRefLabel(target) {
8
+ return (target instanceof Element && !!target.closest('[data-scalebar-ref-label]'));
9
+ }
8
10
  export const elidedBlockStyles = {
9
11
  backgroundColor: '#999',
10
12
  backgroundImage: 'repeating-linear-gradient(90deg, transparent, transparent 1px, rgba(255,255,255,.5) 1px, rgba(255,255,255,.5) 3px)',
@@ -1,10 +1,12 @@
1
1
  export declare const MIN_BP_PER_PX: number;
2
+ export declare const MINIMUM_BLOCK_WIDTH = 3;
2
3
  export declare const SHOW_ALL_REGIONS_FILL = 0.9;
3
4
  export declare const HEADER_BAR_HEIGHT = 48;
4
5
  export declare const HEADER_OVERVIEW_HEIGHT = 20;
5
6
  export declare const SCALE_BAR_HEIGHT = 17;
6
7
  export declare const SVG_SCALEBAR_CAP = 5;
7
8
  export declare const RESIZE_HANDLE_HEIGHT = 4;
9
+ export declare const RESIZE_ALL_HANDLE_HEIGHT = 6;
8
10
  export declare const TRACK_TOP_GAP = 2;
9
11
  export declare const TRACK_OUTLINE_BORDER = 1;
10
12
  export declare const MINIMIZED_TRACK_HEIGHT = 20;
@@ -1,5 +1,6 @@
1
1
  // most zoomed-in level: 50px per bp
2
2
  export const MIN_BP_PER_PX = 1 / 50;
3
+ export const MINIMUM_BLOCK_WIDTH = 3;
3
4
  // fraction of the view width the whole genome fills at the most zoomed-out
4
5
  // level, leaving a 10% margin
5
6
  export const SHOW_ALL_REGIONS_FILL = 0.9;
@@ -14,8 +15,9 @@ export const SVG_SCALEBAR_CAP = 5;
14
15
  // TrackContainer lays its Paper out from these same three and nothing else —
15
16
  // the previous spelling had the model on a 3 while the CSS rendered 2 + 1 + 4,
16
17
  // which put every model-derived track offset 5px per track above the pixels.
17
- // The track *label* is deliberately not among them; see `trackChromeHeight`.
18
+ // The track *label* is not among them: it is measured, see `trackLabelBand`.
18
19
  export const RESIZE_HANDLE_HEIGHT = 4;
20
+ export const RESIZE_ALL_HANDLE_HEIGHT = 6;
19
21
  export const TRACK_TOP_GAP = 2;
20
22
  export const TRACK_OUTLINE_BORDER = 1;
21
23
  export const MINIMIZED_TRACK_HEIGHT = 20;
@@ -0,0 +1,21 @@
1
+ import type { LinearGenomeViewModel } from './model.ts';
2
+ import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
3
+ /**
4
+ * The containing view as the linear genome view itself, for a display that
5
+ * needs `pxToBp`, `showTrack`, `navTo` or the view's own chrome settings.
6
+ *
7
+ * The twin of display-kit's `containingHost`, and deliberately not the same
8
+ * function: `RegionHost` is the region contract display-kit reads, and widening
9
+ * it to carry the LGV's own surface is how the display layer would come to
10
+ * depend on the view plugin. A display that wants the view names the view.
11
+ *
12
+ * A view that lays its displays along a strip of its own — the circular view's
13
+ * ring host — is answered here too, the way `containingHost` answers it: the
14
+ * strip carries the members a display reads to draw (`bpToPx`, `pxToBp`, the
15
+ * blocks), and a display drawn there that reaches for a member the strip has
16
+ * not is the display that stays off the circle.
17
+ *
18
+ * Components and structural helpers taking duck-typed model shapes keep calling
19
+ * `getContainingView` — the same carve-out `containingHost` documents.
20
+ */
21
+ export declare function containingLgv(self: IStateTreeNode): LinearGenomeViewModel;
@@ -0,0 +1,23 @@
1
+ import { getContainingView } from '@jbrowse/core/util';
2
+ /**
3
+ * The containing view as the linear genome view itself, for a display that
4
+ * needs `pxToBp`, `showTrack`, `navTo` or the view's own chrome settings.
5
+ *
6
+ * The twin of display-kit's `containingHost`, and deliberately not the same
7
+ * function: `RegionHost` is the region contract display-kit reads, and widening
8
+ * it to carry the LGV's own surface is how the display layer would come to
9
+ * depend on the view plugin. A display that wants the view names the view.
10
+ *
11
+ * A view that lays its displays along a strip of its own — the circular view's
12
+ * ring host — is answered here too, the way `containingHost` answers it: the
13
+ * strip carries the members a display reads to draw (`bpToPx`, `pxToBp`, the
14
+ * blocks), and a display drawn there that reaches for a member the strip has
15
+ * not is the display that stays off the circle.
16
+ *
17
+ * Components and structural helpers taking duck-typed model shapes keep calling
18
+ * `getContainingView` — the same carve-out `containingHost` documents.
19
+ */
20
+ export function containingLgv(self) {
21
+ const view = getContainingView(self);
22
+ return (view.regionHost ?? view);
23
+ }
@@ -1,4 +1,4 @@
1
- declare const SHEDDABLE: readonly [readonly ['trackSelectorIndent', 16], readonly ['panButtonSpacing', 100], readonly ['scrollZoomLabel', 83], readonly ['zoomSlider', 100], readonly ['regionWidth', 54]];
1
+ declare const SHEDDABLE: readonly [readonly ['trackSelectorIndent', 16], readonly ['panButtonSpacing', 100], readonly ['scrollZoomLabel', 88], readonly ['zoomSlider', 100], readonly ['regionWidth', 54]];
2
2
  export type HeaderFit = Record<(typeof SHEDDABLE)[number][0], boolean>;
3
3
  /**
4
4
  * Which of the header bar's optional pieces a header `width` px wide still has
@@ -19,9 +19,11 @@ export type HeaderFit = Record<(typeof SHEDDABLE)[number][0], boolean>;
19
19
  * not a cliff: it shrinks from what it asked for rather than from the 101px the
20
20
  * unshed row used to leave it.
21
21
  */
22
- export declare function headerFit({ width, searchBoxPx, clearHighlight, }: {
22
+ export declare function headerFit({ width, searchBoxPx, clearHighlight, viewMenu, touch, }: {
23
23
  width: number | undefined;
24
24
  searchBoxPx: number;
25
25
  clearHighlight?: boolean;
26
+ viewMenu?: boolean;
27
+ touch?: boolean;
26
28
  }): HeaderFit;
27
29
  export {};
@@ -1,15 +1,23 @@
1
1
  // Widths measured against a built jbrowse-web at the default theme and root
2
2
  // font size — `products/jbrowse-web/browser-tests/probe-lgv-header-fit.ts`
3
- // prints the row piece by piece. It spends 591px on everything that is not the
4
- // search box (47 track selector, 114 scroll-zoom toggle, 156 pan buttons, 50 bp
3
+ // prints the row piece by piece. It spends 596px on everything that is not the
4
+ // search box (47 track selector, 119 scroll-zoom checkbox, 156 pan buttons, 50 bp
5
5
  // readout, 192 zoom controls, 32 of flex gap). What the box itself asks for is
6
6
  // the caller's, because it follows the locstring rather than being a constant.
7
- const ROW_WITHOUT_SEARCH_PX = 591;
7
+ const ROW_WITHOUT_SEARCH_PX = 596;
8
8
  // The one piece of the row that comes and goes on its own. A text search that
9
9
  // lands on a feature raises it — the same flow that was just using the search
10
10
  // box this whole table exists to protect — so its width is taken off the top
11
11
  // rather than left for the search box to absorb.
12
12
  const CLEAR_HIGHLIGHT_PX = 35;
13
+ // An embedded view has no title bar, so its menu button leads this row: a
14
+ // 40px icon button and its flex gap.
15
+ const VIEW_MENU_PX = 44;
16
+ // A touch device drops the scroll-zoom toggle, which has no wheel to
17
+ // govern, and the pan buttons, which a swipe replaces: 119 + 156, and the flex
18
+ // gaps of the three items.
19
+ const TOUCH_ABSENT_PX = 287;
20
+ const TOUCH_ABSENT = new Set(['panButtonSpacing', 'scrollZoomLabel']);
13
21
  // What the header gives up as its window narrows, cheapest first, with the
14
22
  // pixels each one frees. Whitespace goes before words, words before a redundant
15
23
  // control, and that before a readout. The search box is not on the list: it is
@@ -18,7 +26,7 @@ const CLEAR_HIGHLIGHT_PX = 35;
18
26
  const SHEDDABLE = [
19
27
  ['trackSelectorIndent', 16],
20
28
  ['panButtonSpacing', 100],
21
- ['scrollZoomLabel', 83],
29
+ ['scrollZoomLabel', 88],
22
30
  ['zoomSlider', 100],
23
31
  ['regionWidth', 54],
24
32
  ];
@@ -41,17 +49,22 @@ const SHEDDABLE = [
41
49
  * not a cliff: it shrinks from what it asked for rather than from the 101px the
42
50
  * unshed row used to leave it.
43
51
  */
44
- export function headerFit({ width, searchBoxPx, clearHighlight = false, }) {
52
+ export function headerFit({ width, searchBoxPx, clearHighlight = false, viewMenu = false, touch = false, }) {
45
53
  const fit = Object.fromEntries(SHEDDABLE.map(([k]) => [k, true]));
46
54
  if (width === undefined) {
47
55
  return fit;
48
56
  }
49
- const room = width - (clearHighlight ? CLEAR_HIGHLIGHT_PX : 0);
50
- let need = ROW_WITHOUT_SEARCH_PX + searchBoxPx;
57
+ const room = width -
58
+ (clearHighlight ? CLEAR_HIGHLIGHT_PX : 0) -
59
+ (viewMenu ? VIEW_MENU_PX : 0);
60
+ let need = ROW_WITHOUT_SEARCH_PX + searchBoxPx - (touch ? TOUCH_ABSENT_PX : 0);
51
61
  for (const [key, saves] of SHEDDABLE) {
52
62
  if (need <= room) {
53
63
  break;
54
64
  }
65
+ if (touch && TOUCH_ABSENT.has(key)) {
66
+ continue;
67
+ }
55
68
  fit[key] = false;
56
69
  need -= saves;
57
70
  }
@@ -25,11 +25,6 @@ declare module '@jbrowse/core/PluginManager' {
25
25
  }
26
26
  export default function LinearGenomeViewF(pluginManager: PluginManager): void;
27
27
  export { stateModelFactory } from './model.ts';
28
- export { lgvLaunchKeys } from './launchKeys.ts';
29
- export type { LinearGenomeViewModel, LinearGenomeViewStateModel, } from './model.ts';
30
- export type { BpOffset, ExportSvgOptions, HighlightType, InitState, LinearGenomeViewLaunchProps, NavLocation, TrackLabelMode, VolatileGuide, } from './types.ts';
31
- export { type SyncableViewAction, installLinkedViewSync, } from './linkedViewSync.ts';
32
- export { default as HighlightBand } from './components/HighlightBand.tsx';
28
+ export type { LinearGenomeViewModel, LinearGenomeViewStateModel, LinearGenomeViewTrack, } from './model.ts';
29
+ export { installLinkedViewSync } from './linkedViewSync.ts';
33
30
  export { default as HighlightChip } from './components/HighlightChip.tsx';
34
- export { default as OverviewHighlightBand } from './components/OverviewHighlightBand.tsx';
35
- export { default as SVGHighlightBand } from './components/SVGHighlightBand.tsx';
@@ -1,5 +1,6 @@
1
- import { lazy } from 'react';
2
1
  import { ViewType } from '@jbrowse/core/pluggableElementTypes';
2
+ import { lazyWithPreload } from '@jbrowse/core/util/lazyWithPreload';
3
+ import { lgvKeyboardShortcuts } from './keyboardHandler.js';
3
4
  import { lgvLaunchKeys } from './launchKeys.js';
4
5
  import { stateModelFactory } from './model.js';
5
6
  export default function LinearGenomeViewF(pluginManager) {
@@ -13,14 +14,11 @@ export default function LinearGenomeViewF(pluginManager) {
13
14
  displayName: 'Linear genome view',
14
15
  stateModel,
15
16
  launchKeys: lgvLaunchKeys,
16
- ReactComponent: lazy(() => import('./components/LinearGenomeView.js')),
17
+ keyboardShortcuts: lgvKeyboardShortcuts,
18
+ ReactComponent: lazyWithPreload(() => import('./components/LinearGenomeView.js')),
17
19
  });
18
20
  });
19
21
  }
20
22
  export { stateModelFactory } from './model.js';
21
- export { lgvLaunchKeys } from './launchKeys.js';
22
- export { installLinkedViewSync, } from './linkedViewSync.js';
23
- export { default as HighlightBand } from './components/HighlightBand.js';
23
+ export { installLinkedViewSync } from './linkedViewSync.js';
24
24
  export { default as HighlightChip } from './components/HighlightChip.js';
25
- export { default as OverviewHighlightBand } from './components/OverviewHighlightBand.js';
26
- export { default as SVGHighlightBand } from './components/SVGHighlightBand.js';
@@ -1,9 +1,10 @@
1
1
  import type { LinearGenomeViewModel } from './model.ts';
2
- /**
3
- * Sets up keyboard shortcuts for the LinearGenomeView
4
- * - Ctrl/Cmd + ArrowLeft: slide left
5
- * - Ctrl/Cmd + ArrowRight: slide right
6
- * - Ctrl/Cmd + ArrowUp: zoom in
7
- * - Ctrl/Cmd + ArrowDown: zoom out
8
- */
2
+ import type { KeyboardShortcut } from '@jbrowse/core/pluggableElementTypes';
3
+ interface LgvShortcut extends KeyboardShortcut {
4
+ code: string;
5
+ shift?: boolean;
6
+ run: (self: LinearGenomeViewModel) => void;
7
+ }
8
+ export declare const lgvKeyboardShortcuts: readonly LgvShortcut[];
9
9
  export declare function setupKeyboardHandler(self: LinearGenomeViewModel): void;
10
+ export {};