@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
@@ -5,7 +5,8 @@ import { assembleLocString, assembleLocStrings } from '@jbrowse/core/util';
5
5
  import { copyText } from '@jbrowse/core/util/copyText';
6
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
7
7
  import { observer } from 'mobx-react';
8
- import { REF_NAME_LABEL_FONT_SIZE, regionMoveActions, withRegionMoved, withRegionRemoved, withRegionReversed, } from '../util.js';
8
+ import { REF_NAME_LABEL_FONT_SIZE, regionMoveActions, regionRunBounds, withRegionMoved, withRegionRemoved, withRegionReversed, withRegionsKept, } from '../util.js';
9
+ import { scalebarRefLabelProps } from './util.js';
9
10
  const useStyles = makeStyles()(theme => ({
10
11
  refLabel: {
11
12
  // the width the fit test in getScalebarRefNameLabels measures against
@@ -31,65 +32,45 @@ const useStyles = makeStyles()(theme => ({
31
32
  overflow: 'clip',
32
33
  whiteSpace: 'nowrap',
33
34
  },
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
35
+ // refName labels only: the caption chip opens no menu
37
36
  clickable: {
38
37
  cursor: 'pointer',
39
38
  '&: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
39
  background: theme.palette.action.hover,
43
40
  },
44
41
  },
45
- prefixLabel: {
42
+ caption: {
46
43
  zIndex: 100,
47
44
  },
48
45
  }));
49
46
  const ScalebarRefNameLabels = observer(function ScalebarRefNameLabels({ model, }) {
50
47
  const { classes, cx } = useStyles();
51
48
  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
49
  const { labels, caption } = model.scalebarRefNameLabels;
57
50
  return (_jsxs(_Fragment, { children: [labels.map((label, i) => (_jsx(RefLabel
58
51
  // 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: () => {
52
+ , { 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
53
  model.setIsScalebarRefNameMenuOpen(false);
63
54
  setMenuState(undefined);
64
55
  } })) : null] }));
65
56
  });
66
- // One refName label. Not an observer: `label` is already plain data computed by
67
- // the parent, and `model` is only used to fire actions. mouseDown flags the
68
- // click as starting on a label so the scalebar's rubberband hands the click to
69
- // this label's own onClick instead of opening the range menu.
70
- function RefLabel({ model, label, onOpenMenu, }) {
57
+ // The attribute tells the scalebar's rubberband to leave a click here to this
58
+ // label's onClick.
59
+ const RefLabel = observer(function RefLabel({ model, label, onOpenMenu, }) {
71
60
  const { classes, cx } = useStyles();
72
- const { refName, displayedRegionIndex, lastDisplayedRegionIndex, transform, maxWidth, paddingLeft, text, } = label;
61
+ const { refName, transform, maxWidth, paddingLeft, text } = label;
73
62
  return (_jsx("span", { className: cx(classes.refLabel, classes.clickable), style: {
74
63
  transform: `translateX(${transform}px)`,
75
64
  paddingLeft,
76
65
  maxWidth,
77
- }, "data-testid": `refLabel-${refName}`, onMouseDown: () => {
78
- model.setScalebarRefNameClickPending(true);
79
- }, onClick: e => {
80
- model.setScalebarRefNameClickPending(false);
66
+ }, "data-testid": `refLabel-${refName}`, ...scalebarRefLabelProps, onClick: e => {
81
67
  model.setIsScalebarRefNameMenuOpen(true);
82
- onOpenMenu({
83
- anchorEl: e.currentTarget,
84
- refName,
85
- displayedRegionIndex,
86
- lastDisplayedRegionIndex,
87
- });
68
+ onOpenMenu({ anchorEl: e.currentTarget, label });
88
69
  }, children: text }));
89
- }
70
+ });
90
71
  const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }) {
91
72
  const { displayedRegions } = model;
92
- const { refName, displayedRegionIndex: idx, lastDisplayedRegionIndex: lastIdx, } = menuState;
73
+ const { refName, displayedRegionIndex: idx, lastDisplayedRegionIndex: lastIdx, } = menuState.label;
93
74
  const numRegions = displayedRegions.length;
94
75
  const labeled = displayedRegions.slice(idx, lastIdx + 1);
95
76
  // A label naming several regions is the collapsed-intron case: adjacent
@@ -104,6 +85,16 @@ const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }
104
85
  const locString = oneRegion
105
86
  ? assembleLocString(displayedRegions[idx])
106
87
  : assembleLocStrings(labeled);
88
+ // The region this label's blocks were cut out of. Taken over the WHOLE run
89
+ // rather than `labeled`, which the viewport clips: a collapsed-introns view
90
+ // zoomed into two exons would otherwise offer a span that is not the gene's.
91
+ const { first: runFirst, last: runLast } = regionRunBounds(displayedRegions, idx, lastIdx);
92
+ const run = displayedRegions.slice(runFirst, runLast + 1);
93
+ const wholeSpan = {
94
+ ...run[0],
95
+ start: Math.min(...run.map(r => r.start)),
96
+ end: Math.max(...run.map(r => r.end)),
97
+ };
107
98
  return (_jsx(Menu, { anchorEl: menuState.anchorEl, open: true, onClose: onClose, onMenuItemClick: callback => {
108
99
  callback();
109
100
  onClose();
@@ -126,7 +117,31 @@ const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }
126
117
  ? 'Show only this region'
127
118
  : `Show only ${refName}`,
128
119
  onClick: () => {
129
- model.setDisplayedRegions(labeled);
120
+ model.editDisplayedRegions(withRegionsKept(displayedRegions, idx, lastIdx));
121
+ },
122
+ },
123
+ ]
124
+ : []),
125
+ ...(runLast > runFirst
126
+ ? [
127
+ {
128
+ // Names its subject the way the two rows above it do; the span
129
+ // it lands on is not spelled into the label, which would move
130
+ // under the reader as they navigate.
131
+ label: `Show the whole span of ${refName}`,
132
+ // Focused rather than centre-kept: the rejoined region is one
133
+ // the old viewport never saw, so there is no centre to hold,
134
+ // and showing the span is the whole point of the row.
135
+ onClick: () => {
136
+ model.setDisplayedRegions([
137
+ ...displayedRegions.slice(0, runFirst),
138
+ wholeSpan,
139
+ ...displayedRegions.slice(runLast + 1),
140
+ ]);
141
+ model.moveTo({ index: runFirst, offset: 0 }, {
142
+ index: runFirst,
143
+ offset: wholeSpan.end - wholeSpan.start,
144
+ });
130
145
  },
131
146
  },
132
147
  ]
@@ -156,13 +171,13 @@ const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }
156
171
  {
157
172
  label: 'Reverse region',
158
173
  onClick: () => {
159
- model.setDisplayedRegions(withRegionReversed(displayedRegions, idx));
174
+ model.editDisplayedRegions(withRegionReversed(displayedRegions, idx));
160
175
  },
161
176
  },
162
177
  ...regionMoveActions(idx, numRegions).map(({ label, to }) => ({
163
178
  label,
164
179
  onClick: () => {
165
- model.setDisplayedRegions(withRegionMoved(displayedRegions, idx, to));
180
+ model.editDisplayedRegions(withRegionMoved(displayedRegions, idx, to));
166
181
  },
167
182
  })),
168
183
  ...(numRegions > 1
@@ -170,7 +185,7 @@ const RefNameMenu = observer(function RefNameMenu({ model, menuState, onClose, }
170
185
  {
171
186
  label: 'Remove this region from view',
172
187
  onClick: () => {
173
- model.setDisplayedRegions(withRegionRemoved(displayedRegions, idx));
188
+ model.editDisplayedRegions(withRegionRemoved(displayedRegions, idx));
174
189
  },
175
190
  },
176
191
  ]
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { RefNameAutocomplete, RefNameAutocompleteEndAdornment, adornmentReservePx, getInputWidth, useRecentLocations, } from '@jbrowse/core/ui';
3
+ import { usePalette } from '@jbrowse/core/ui/PaletteContext';
4
+ import { alpha } from '@jbrowse/core/ui/palette';
3
5
  import { getSession } from '@jbrowse/core/util';
4
- import { alpha, useTheme } from '@mui/material';
5
6
  import { observer } from 'mobx-react';
6
7
  import { fetchResults, navigateToSelectedOption, notifySearchFailure, } from '../../searchUtils.js';
7
8
  import { SPACING, WIDGET_HEIGHT } from '../consts.js';
@@ -31,17 +32,16 @@ export function searchBoxWidth(value) {
31
32
  2 * SPACING);
32
33
  }
33
34
  const SearchBox = observer(function SearchBox({ model, showHelp = true, minWidth = MIN_WIDTH, maxWidth = MAX_WIDTH, style = defaultStyle, }) {
34
- const theme = useTheme();
35
+ const palette = usePalette();
35
36
  const session = getSession(model);
36
- const { textSearchManager, assemblyManager } = session;
37
37
  const { assemblyNames } = model;
38
38
  const assemblyName = assemblyNames[0];
39
- const assembly = assemblyManager.get(assemblyName);
40
39
  const { recentLocations, addRecentLocation, clearRecentLocations } = useRecentLocations(assemblyName);
41
40
  async function navigate(option) {
42
41
  try {
43
- await navigateToSelectedOption({ model, assemblyName, option });
44
- addRecentLocation(recentLocationOf(option));
42
+ if (await navigateToSelectedOption({ model, assemblyName, option })) {
43
+ addRecentLocation(recentLocationOf(option));
44
+ }
45
45
  }
46
46
  catch (e) {
47
47
  notifySearchFailure(session, e);
@@ -50,25 +50,19 @@ const SearchBox = observer(function SearchBox({ model, showHelp = true, minWidth
50
50
  const recentMenuItems = recentLocationsMenu({
51
51
  recentLocations,
52
52
  onNavigate: option => {
53
- navigate(option).catch(() => { });
53
+ void navigate(option);
54
54
  },
55
55
  onClear: clearRecentLocations,
56
56
  });
57
57
  return (_jsx(RefNameAutocomplete, { onSelect: option => {
58
- navigate(option).catch(() => { });
59
- }, assemblyName: assemblyName, fetchResults: (queryString, stopToken) => fetchResults({
60
- queryString,
61
- assemblyName,
62
- textSearchManager,
63
- assembly,
64
- stopToken,
65
- }), session: session, value: model.coarseVisibleLocStrings, minWidth: minWidth, maxWidth: maxWidth, adornmentWidth: adornmentReservePx({
58
+ void navigate(option);
59
+ }, assemblyName: assemblyName, fetchResults: (queryString, signal) => fetchResults({ session, queryString, assemblyName, signal }), session: session, value: model.coarseVisibleLocStrings, minWidth: minWidth, maxWidth: maxWidth, adornmentWidth: adornmentReservePx({
66
60
  showHelp,
67
61
  menuItemCount: recentMenuItems.length,
68
62
  }), style: style, endAdornment: _jsx(RefNameAutocompleteEndAdornment, { showHelp: showHelp, menuItems: recentMenuItems }), inputStyle: {
69
63
  padding: 0,
70
64
  height: WIDGET_HEIGHT,
71
- background: alpha(theme.palette.background.paper, 0.8),
65
+ background: alpha(palette.background.paper, 0.8),
72
66
  } }));
73
67
  });
74
68
  export default SearchBox;
@@ -1,9 +1,10 @@
1
1
  import type { ReactNode } from 'react';
2
- export default function SearchPanelForm({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }: {
2
+ declare const SearchPanelForm: ({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }: {
3
3
  onSubmit: () => void;
4
4
  handleClose: () => void;
5
5
  submitDisabled: boolean;
6
6
  submitText?: string;
7
7
  actions?: ReactNode;
8
8
  children: ReactNode;
9
- }): import("react").JSX.Element;
9
+ }) => import("react").JSX.Element;
10
+ export default SearchPanelForm;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { SubmitForm } from '@jbrowse/core/ui';
3
3
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { observer } from 'mobx-react';
4
5
  const useStyles = makeStyles()({
5
6
  dialogContent: {
6
7
  width: '34em',
@@ -12,7 +13,8 @@ const useStyles = makeStyles()({
12
13
  // The chrome shared by the sequence-search mode panels: the fixed-width column
13
14
  // their fields lay out in, plus SubmitForm's Cancel/Submit footer. Each panel
14
15
  // still owns its own fields and its own submit, so this is only the frame.
15
- export default function SearchPanelForm({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }) {
16
+ const SearchPanelForm = observer(function SearchPanelForm({ onSubmit, handleClose, submitDisabled, submitText, actions, children, }) {
16
17
  const { classes } = useStyles();
17
18
  return (_jsx(SubmitForm, { contentClassName: classes.dialogContent, onSubmit: onSubmit, onCancel: handleClose, submitDisabled: submitDisabled, submitText: submitText, actions: actions, children: children }));
18
- }
19
+ });
20
+ export default SearchPanelForm;
@@ -1,9 +1,5 @@
1
- import type { LinearGenomeViewModel } from '../../index.ts';
2
- import type BaseResult from '@jbrowse/core/TextSearch/BaseResults';
3
- export default function SearchResultsDialog({ model, assemblyName, searchQuery, searchResults, handleClose, }: {
1
+ import type { LinearGenomeViewModel } from '../index.ts';
2
+ declare const SearchResultsDialog: ({ model, }: {
4
3
  model: LinearGenomeViewModel;
5
- assemblyName: string;
6
- searchQuery: string;
7
- searchResults: BaseResult[];
8
- handleClose: () => void;
9
- }): import("react").JSX.Element;
4
+ }) => import("react").JSX.Element | null;
5
+ export default SearchResultsDialog;
@@ -1,7 +1,10 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { InfoDialog } from '@jbrowse/core/ui';
3
3
  import { Typography } from '@mui/material';
4
+ import { observer } from 'mobx-react';
4
5
  import SearchResultsTable from './SearchResultsTable.js';
5
- export default function SearchResultsDialog({ model, assemblyName, searchQuery, searchResults, handleClose, }) {
6
- return (_jsx(InfoDialog, { open: true, maxWidth: "xl", onClose: handleClose, title: "Search results", children: !searchResults.length ? (_jsxs(Typography, { children: ["No results found for ", _jsx("b", { children: searchQuery })] })) : (_jsxs(_Fragment, { children: [_jsxs(Typography, { children: ["Showing results for ", _jsx("b", { children: searchQuery })] }), _jsx(SearchResultsTable, { model: model, handleClose: handleClose, assemblyName: assemblyName, searchResults: searchResults })] })) }));
7
- }
6
+ const SearchResultsDialog = observer(function SearchResultsDialog({ model, }) {
7
+ const { searchPicker } = model;
8
+ return searchPicker ? (_jsxs(InfoDialog, { open: true, maxWidth: "xl", onClose: model.closeSearchPicker, title: "Search results", children: [_jsxs(Typography, { children: [searchPicker.rows.length, " matches for ", _jsx("b", { children: searchPicker.query })] }), _jsx(SearchResultsTable, { picker: searchPicker })] })) : null;
9
+ });
10
+ export default SearchResultsDialog;
@@ -1,9 +1,6 @@
1
- import type { LinearGenomeViewModel } from '../../index.ts';
2
- import type BaseResult from '@jbrowse/core/TextSearch/BaseResults';
3
- declare const SearchResultsTable: ({ searchResults, assemblyName, model, handleClose, }: {
4
- searchResults: BaseResult[];
5
- assemblyName: string;
6
- model: LinearGenomeViewModel;
7
- handleClose: () => void;
1
+ import type { SearchPickerRow, SearchPickerState } from '../model.ts';
2
+ export declare function filterRows(rows: SearchPickerRow[], text: string): SearchPickerRow[];
3
+ declare const SearchResultsTable: ({ picker, }: {
4
+ picker: SearchPickerState;
8
5
  }) => import("react").JSX.Element;
9
6
  export default SearchResultsTable;
@@ -1,56 +1,28 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { readConfObject } from '@jbrowse/core/configuration';
3
- import { assembleLocString, getSession, parseLocString, } from '@jbrowse/core/util';
4
- import { Button, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, } from '@mui/material';
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { SanitizedHTML } from '@jbrowse/core/ui';
4
+ import { Button, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, } from '@mui/material';
5
5
  import { observer } from 'mobx-react';
6
- import { isOpenInView, navToOption } from '../../searchUtils.js';
7
- const SearchResultsTable = observer(function SearchResultsTable({ searchResults, assemblyName, model, handleClose, }) {
8
- const session = getSession(model);
9
- const { assemblyManager } = session;
10
- const assembly = assemblyManager.get(assemblyName);
11
- function getTrackName(trackId) {
12
- const conf = trackId !== undefined ? session.getTrackById(trackId) : undefined;
13
- return conf ? readConfObject(conf, 'name') : '';
14
- }
15
- // the raw locString, prettified to the assembly's canonical refName when the
16
- // assembly is loaded and the string parses. A result the assembly can't
17
- // resolve is still listed as-is rather than blowing up the dialog: the Go
18
- // button navigates through navToOption, which reports its own failure
19
- function formatLocation(locString) {
20
- if (assembly && locString) {
21
- try {
22
- const loc = parseLocString(locString, refName => assembly.isValidRefName(refName));
23
- return assembleLocString({
24
- ...loc,
25
- refName: assembly.getCanonicalRefName2(loc.refName),
26
- });
27
- }
28
- catch (e) {
29
- console.warn('failed to parse location string', locString, e);
30
- return locString;
31
- }
32
- }
33
- else {
34
- return locString;
35
- }
36
- }
37
- // A hit in a track that is already on screen is usually the one meant, so it
38
- // is listed first rather than given a control that says so. sort is stable,
39
- // so everything else keeps the ranked order it arrived in.
40
- const ordered = [...searchResults].sort((a, b) => Number(isOpenInView(b, model)) - Number(isOpenInView(a, model)));
41
- return (_jsx(TableContainer, { component: Paper, children: _jsxs(Table, { children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Name" }), _jsx(TableCell, { align: "right", children: "Location" }), _jsx(TableCell, { align: "right", children: "Track" }), _jsx(TableCell, { align: "right" })] }) }), _jsx(TableBody, { children: ordered.map(result => (_jsxs(TableRow, { children: [_jsx(TableCell, { component: "th", scope: "row", children: result.getLabel() }), _jsx(TableCell, { align: "right", children: formatLocation(result.getLocation()) }), _jsx(TableCell, { align: "right", children: getTrackName(result.getTrackId()) || 'N/A' }), _jsx(TableCell, { align: "right", children: _jsx(Button, { onClick: async () => {
42
- try {
43
- await navToOption({
44
- option: result,
45
- model,
46
- assemblyName,
47
- });
48
- }
49
- catch (e) {
50
- console.error(e);
51
- session.notifyError(`${e}`, e);
52
- }
53
- handleClose();
54
- }, color: "primary", variant: "contained", children: "Go" }) })] }, result.getId()))) })] }) }));
6
+ // past this many rows a list is easier to narrow than to read; gene names
7
+ // sort too irregularly (scn1ab, HOXA10-AS) for an order to stand in for it
8
+ const FILTER_AT = 8;
9
+ export function filterRows(rows, text) {
10
+ const needle = text.trim().toLowerCase();
11
+ return needle
12
+ ? rows.filter(row => [row.label, row.location, row.trackName].some(field => field.toLowerCase().includes(needle)))
13
+ : rows;
14
+ }
15
+ const SearchResultsTable = observer(function SearchResultsTable({ picker, }) {
16
+ const [filter, setFilter] = useState('');
17
+ const rows = filterRows(picker.rows, filter);
18
+ return (_jsxs(_Fragment, { children: [picker.rows.length > FILTER_AT ? (_jsx(TextField, { autoFocus: true, fullWidth: true, size: "small", margin: "dense", placeholder: "Filter by name, location or track", slotProps: { htmlInput: { 'aria-label': 'Filter search results' } }, value: filter, onChange: event => {
19
+ setFilter(event.target.value);
20
+ }, onKeyDown: event => {
21
+ if (event.key === 'Enter' && rows.length === 1) {
22
+ void picker.pick(rows[0].id);
23
+ }
24
+ }, helperText: filter ? `${rows.length} of ${picker.rows.length}` : undefined })) : null, _jsx(TableContainer, { component: Paper, children: _jsxs(Table, { children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { children: "Name" }), _jsx(TableCell, { align: "right", children: "Location" }), _jsx(TableCell, { align: "right", children: "Track" }), _jsx(TableCell, { align: "right" })] }) }), _jsx(TableBody, { children: rows.map(row => (_jsxs(TableRow, { children: [_jsx(TableCell, { component: "th", scope: "row", children: row.label }), _jsx(TableCell, { align: "right", children: row.location }), _jsx(TableCell, { align: "right", children: _jsx(SanitizedHTML, { html: row.trackName || 'N/A' }) }), _jsx(TableCell, { align: "right", children: _jsx(Button, { onClick: () => {
25
+ void picker.pick(row.id);
26
+ }, color: "primary", variant: "contained", "aria-label": `Go to ${row.label} at ${row.location}`, children: "Go" }) })] }, row.id))) })] }) })] }));
55
27
  });
56
28
  export default SearchResultsTable;
@@ -15,8 +15,7 @@ function isFeatureWidgetForView(w, viewId) {
15
15
  // is owned by the display rather than by a widget — so the view's own displays
16
16
  // publish hover positions too.
17
17
  function displayHoverPosition(display) {
18
- const candidate = display;
19
- return candidate?.sequenceHoverPosition;
18
+ return display.sequenceHoverPosition;
20
19
  }
21
20
  // Draws a crosshair on the LGV at the base a connected feature-detail sequence
22
21
  // panel is hovering, so mousing over the sequence readout points at the genome.
@@ -24,7 +23,7 @@ const SequenceFeatureHoverHighlight = observer(function SequenceFeatureHoverHigh
24
23
  return (_jsxs(_Fragment, { children: [_jsx(ConnectedHoverHighlight, { model: model, getPosition: (widget, viewId) => isFeatureWidgetForView(widget, viewId)
25
24
  ? widget.sequenceHoverPosition
26
25
  : undefined }), model.tracks
27
- .flatMap(track => track.displays)
26
+ .flatMap((track) => track.displays)
28
27
  .flatMap(display => {
29
28
  const position = displayHoverPosition(display);
30
29
  return position
@@ -1,8 +1,6 @@
1
+ import type { SequenceSearchModel } from './searchModes.ts';
1
2
  declare const SequenceSearchDialog: ({ model, handleClose, }: {
2
- model: {
3
- assemblyNames: string[];
4
- launchTrack: (trackId: string) => Promise<unknown>;
5
- };
3
+ model: SequenceSearchModel;
6
4
  handleClose: () => void;
7
5
  }) => import("react").JSX.Element;
8
6
  export default SequenceSearchDialog;
@@ -1,8 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
- export default function StrandCheckboxes({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }: {
2
+ declare const StrandCheckboxes: ({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }: {
3
3
  searchForward: boolean;
4
4
  searchReverse: boolean;
5
5
  setSearchForward: (arg: boolean) => void;
6
6
  setSearchReverse: (arg: boolean) => void;
7
7
  children?: ReactNode;
8
- }): import("react").JSX.Element;
8
+ }) => import("react").JSX.Element;
9
+ export default StrandCheckboxes;
@@ -1,13 +1,15 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { LabeledCheckbox } from '@jbrowse/core/ui';
3
3
  import { FormGroup, Typography } from '@mui/material';
4
+ import { observer } from 'mobx-react';
4
5
  // Forward/reverse strand checkboxes plus the "select at least one" guard, shared
5
6
  // by the sequence-search mode panels. Extra checkboxes for a specific panel (e.g.
6
7
  // "Case insensitive") slot in alongside via `children`.
7
- export default function StrandCheckboxes({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }) {
8
+ const StrandCheckboxes = observer(function StrandCheckboxes({ searchForward, searchReverse, setSearchForward, setSearchReverse, children, }) {
8
9
  return (_jsxs(_Fragment, { children: [_jsxs(FormGroup, { row: true, children: [_jsx(LabeledCheckbox, { size: "small", checked: searchForward, onChange: val => {
9
10
  setSearchForward(val);
10
11
  }, label: "Forward strand" }), _jsx(LabeledCheckbox, { size: "small", checked: searchReverse, onChange: val => {
11
12
  setSearchReverse(val);
12
13
  }, label: "Reverse strand" }), children] }), !searchForward && !searchReverse ? (_jsx(Typography, { color: "error", variant: "body2", children: "Select at least one strand" })) : null] }));
13
- }
14
+ });
15
+ export default StrandCheckboxes;
@@ -1,8 +1,7 @@
1
- import type { LinearGenomeViewModel } from '../index.ts';
2
- import type { BaseTrackModel } from '@jbrowse/core/pluggableElementTypes/models';
1
+ import type { LinearGenomeViewModel, LinearGenomeViewTrack } from '../index.ts';
3
2
  type LGV = LinearGenomeViewModel;
4
3
  declare const TrackContainer: ({ model, track, }: {
5
4
  model: LGV;
6
- track: BaseTrackModel;
5
+ track: LinearGenomeViewTrack;
7
6
  }) => import("react").JSX.Element;
8
7
  export default TrackContainer;
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
2
3
  import { ErrorBanner, ResizeHandle } from '@jbrowse/core/ui';
3
4
  import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary';
4
5
  import { cx, makeStyles } from '@jbrowse/core/util/tss-react';
5
6
  import { TrackOverlaySlot } from '@jbrowse/display-ui';
7
+ import { isAlive } from '@jbrowse/mobx-state-tree';
6
8
  import { Paper } from '@mui/material';
7
9
  import { observer } from 'mobx-react';
8
10
  import { RESIZE_HANDLE_HEIGHT, TRACK_OUTLINE_BORDER, TRACK_TOP_GAP, } from '../consts.js';
@@ -16,8 +18,8 @@ const useStyles = makeStyles()({
16
18
  // described in TrackRenderingContainer. flow-root keeps the block formatting
17
19
  // context that `overflow: hidden` used to establish as a side effect (neither
18
20
  // `clip` nor no overflow does), so child margins still can't collapse through
19
- // and shift the track — which the breakpoint split view would pick up, since
20
- // its connector overlay measures trackRefs' getBoundingClientRect().top.
21
+ // and shift the track — which would put the pixels off the model's offset
22
+ // arithmetic, which the breakpoint split view draws its connectors from.
21
23
  root: {
22
24
  marginTop: TRACK_TOP_GAP,
23
25
  position: 'relative',
@@ -56,17 +58,61 @@ const useStyles = makeStyles()({
56
58
  // spends the other 4 on clearance.
57
59
  trackLabelOffset: {
58
60
  position: 'relative',
59
- display: 'inline-block',
60
61
  marginBottom: 8,
61
62
  },
62
63
  trackLabelOverlap: {
63
64
  position: 'absolute',
64
65
  },
66
+ // in flow, so the track keeps the banner's height, and raised over the
67
+ // `PaddingBlocks` painted after it
68
+ crashBanner: {
69
+ position: 'relative',
70
+ zIndex: 1,
71
+ },
65
72
  });
73
+ // Measures the in-flow label band, the distance from the Paper's content top to
74
+ // the rendering container, into the model. Observes the Paper: it grows and
75
+ // shrinks with the label, the label setting, and the display, and each of those
76
+ // can move the container. Published after a requestAnimationFrame like
77
+ // useWidthSetter, and cleared on unmount so a removed track leaves no band.
78
+ function useTrackLabelBand(model, trackId) {
79
+ const [paper, setPaper] = useState(null);
80
+ const [container, setContainer] = useState(null);
81
+ useEffect(() => {
82
+ if (paper && container && 'ResizeObserver' in window) {
83
+ let token;
84
+ const publish = () => {
85
+ token = requestAnimationFrame(() => {
86
+ if (isAlive(model)) {
87
+ const paperRect = paper.getBoundingClientRect();
88
+ const band = container.getBoundingClientRect().top -
89
+ paperRect.top -
90
+ paper.clientTop;
91
+ model.setTrackLabelBand(trackId, band);
92
+ }
93
+ });
94
+ };
95
+ publish();
96
+ const observer = new ResizeObserver(publish);
97
+ observer.observe(paper);
98
+ return () => {
99
+ observer.disconnect();
100
+ if (token !== undefined) {
101
+ cancelAnimationFrame(token);
102
+ }
103
+ if (isAlive(model)) {
104
+ model.setTrackLabelBand(trackId, 0);
105
+ }
106
+ };
107
+ }
108
+ return undefined;
109
+ }, [paper, container, model, trackId]);
110
+ return { setPaper, setContainer };
111
+ }
66
112
  const TrackContainer = observer(function TrackContainer({ model, track, }) {
67
113
  const { classes } = useStyles();
68
- // an LGV track always holds at least one linear display (activeDisplay =
69
- // displays[0]); narrow to the linear shape for prefersOffset/resizeHeight
114
+ // read once and handed down, so a track's chrome and its body can't read two
115
+ // displays
70
116
  const display = track.activeDisplay;
71
117
  const { showTrackOutlines } = model;
72
118
  // The Paper's 1px border, present iff outlines are on. Everything laid out in
@@ -76,12 +122,19 @@ const TrackContainer = observer(function TrackContainer({ model, track, }) {
76
122
  // the data. One binding rather than three copies of the ternary, because the
77
123
  // three agreeing is the whole point.
78
124
  const outlineOffset = showTrackOutlines ? TRACK_OUTLINE_BORDER : 0;
79
- const trackLabelStyle = model.effectiveTrackLabels !== 'overlapping' || display.prefersOffset
80
- ? classes.trackLabelOffset
81
- : classes.trackLabelOverlap;
82
- return (_jsxs(Paper, { className: cx(classes.root, track.pinned ? null : classes.unpinnedTrack), variant: showTrackOutlines ? 'outlined' : undefined, elevation: showTrackOutlines ? undefined : 0, onDragOver: event => {
125
+ // A label in flow is a row of track height, which is most of what minimizing
126
+ // buys back — so a minimized track overlaps its label whatever the setting
127
+ // says, and there is nothing under it to print over. `prefersOffset` is the
128
+ // display asking for the offset row in 'overlapping' mode, because its left
129
+ // edge carries an axis or a sidebar the label would cover.
130
+ const trackLabelStyle = track.minimized ||
131
+ (model.effectiveTrackLabels === 'overlapping' && !display.prefersOffset)
132
+ ? classes.trackLabelOverlap
133
+ : classes.trackLabelOffset;
134
+ const { setPaper, setContainer } = useTrackLabelBand(model, track.trackId);
135
+ return (_jsxs(Paper, { ref: setPaper, className: cx(classes.root, track.pinned ? null : classes.unpinnedTrack), variant: showTrackOutlines ? 'outlined' : undefined, elevation: showTrackOutlines ? undefined : 0, onDragOver: event => {
83
136
  model.onTrackDragOver(track.id, event.clientY);
84
- }, 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 }) }) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, onDrag: distance => display.resizeHeight(distance),
137
+ }, 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 }, labelBand: model.trackLabelBand(track), children: _jsx(ErrorBoundary, { FallbackComponent: e => (_jsx("div", { className: classes.crashBanner, "data-gesture-owner": "true", children: _jsx(ErrorBanner, { error: e.error, onReset: e.resetErrorBoundary }) })), children: _jsx(TrackRenderingContainer, { model: model, track: track, display: display, ref: setContainer }) }, display.id) }), _jsx(PaddingBlocks, { model: model, offset: outlineOffset }), _jsx(ResizeHandle, { style: { height: RESIZE_HANDLE_HEIGHT }, value: display.height, onChange: height => display.resizeHeight(height - display.height),
85
138
  // the drag's shortcut: grow the track by whatever it is scrolled over,
86
139
  // so a pileup showing three rows of forty opens to all forty
87
140
  // Optional: only a display that scrolls its own content has anything
@@ -13,17 +13,27 @@ const useStyles = makeStyles()(theme => ({
13
13
  root: {
14
14
  // above breakpoint split view
15
15
  zIndex: 200,
16
- background: theme.palette.background.paper,
16
+ ...(theme.palette.mode === 'dark' && {
17
+ outline: `1px solid ${theme.palette.divider}`,
18
+ outlineOffset: -1,
19
+ }),
20
+ display: 'inline-flex',
21
+ alignItems: 'center',
22
+ maxWidth: 'calc(100% - 16px)',
17
23
  },
24
+ // one line: a long name wrapping over a narrow view covered the data below
18
25
  trackName: {
19
- margin: '0 auto',
20
- width: '90%',
26
+ margin: theme.spacing(0, 0.5),
27
+ minWidth: 0,
28
+ overflow: 'hidden',
29
+ textOverflow: 'ellipsis',
30
+ whiteSpace: 'nowrap',
21
31
  pointerEvents: 'none',
22
32
  },
23
33
  }));
24
34
  const TrackLabel = observer(function TrackLabel({ track, className, }) {
25
35
  const { classes } = useStyles();
26
36
  const trackName = getTrackName(track.configuration, getSession(track));
27
- return (_jsxs(Paper, { className: cx(className, classes.root), children: [_jsx(TrackLabelDragHandle, { track: track }), _jsx(TrackLabelCloseButton, { track: track }), _jsx(TrackLabelMinimizeButton, { track: track }), _jsx(Typography, { variant: "body2", component: "span", className: classes.trackName, children: _jsx(SanitizedHTML, { html: trackName }) }), _jsx(TrackLabelRefNameWarning, { track: track }), _jsx(TrackLabelMenu, { track: track })] }));
37
+ return (_jsxs(Paper, { className: cx(className, classes.root), "data-testid": `trackLabel-${track.trackId}`, children: [_jsx(TrackLabelDragHandle, { track: track }), _jsx(TrackLabelCloseButton, { track: track }), _jsx(TrackLabelMinimizeButton, { track: track }), _jsx(Typography, { variant: "body2", component: "span", className: classes.trackName, children: _jsx(SanitizedHTML, { html: trackName }) }), _jsx(TrackLabelRefNameWarning, { track: track }), _jsx(TrackLabelMenu, { track: track })] }));
28
38
  });
29
39
  export default TrackLabel;