@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,41 +1,31 @@
1
- import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons';
2
- import { radioItems } from '@jbrowse/core/ui/menuItems';
3
- import { assembleLocStrings, getDialogHost, getSession, isSessionWithAddSessionTrack, toLocale, } from '@jbrowse/core/util';
4
- import { basePaintedAt } from '@jbrowse/core/util/Base1DUtils';
1
+ import { Highlighter, TrackSelector as TrackSelectorIcon, } from '@jbrowse/core/ui/Icons';
2
+ import { highlightListMenuItems, radioItems, subMenuIfAny, } from '@jbrowse/core/ui/menuItems';
3
+ import { makeShowSubMenu } from '@jbrowse/core/ui/showSubMenu';
4
+ import { assembleLocStrings, getDialogHost, getEnv, getSession, isSessionWithAddSessionTrack, toLocale, } from '@jbrowse/core/util';
5
+ import { basePaintedAt, computeMoveToLayout, } from '@jbrowse/core/util/Base1DUtils';
5
6
  import { copyText } from '@jbrowse/core/util/copyText';
7
+ import { renameIds } from '@jbrowse/core/util/types/mst';
8
+ import { getSnapshot } from '@jbrowse/mobx-state-tree';
6
9
  import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong';
10
+ import CodeIcon from '@mui/icons-material/Code';
7
11
  import ContentCopyIcon from '@mui/icons-material/ContentCopy';
8
- import ExpandLessIcon from '@mui/icons-material/ExpandLess';
9
- import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
12
+ import FitScreenIcon from '@mui/icons-material/FitScreen';
10
13
  import FolderOpenIcon from '@mui/icons-material/FolderOpen';
14
+ import LabelIcon from '@mui/icons-material/Label';
11
15
  import LaunchIcon from '@mui/icons-material/Launch';
12
16
  import MenuOpenIcon from '@mui/icons-material/MenuOpen';
13
- import PaletteIcon from '@mui/icons-material/Palette';
17
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
14
18
  import PhotoCameraIcon from '@mui/icons-material/PhotoCamera';
15
19
  import SearchIcon from '@mui/icons-material/Search';
16
20
  import SyncAltIcon from '@mui/icons-material/SyncAlt';
17
- import VisibilityIcon from '@mui/icons-material/Visibility';
18
21
  import ZoomInIcon from '@mui/icons-material/ZoomIn';
19
- import { ExportSvgDialog, GetSequenceDialog, ReturnToImportFormDialog, SequenceSearchDialog, } from './lazyDialogs.js';
22
+ import { ExportSvgDialog, GetSequenceDialog, RegionWidthEditorDialog, ReturnToImportFormDialog, SequenceSearchDialog, } from './lazyDialogs.js';
20
23
  const TRACK_LABEL_OPTIONS = [
21
24
  { value: 'overlapping', label: 'Overlapping' },
22
25
  { value: 'offset', label: 'Offset' },
23
26
  { value: 'hidden', label: 'Hidden' },
24
27
  ];
25
- /**
26
- * Zoom all the way out, shared by the view menu and the header's zoom menu —
27
- * one definition so the two cannot drift in label, and the view menu is the
28
- * only one of the two that survives `hideHeader`.
29
- *
30
- * The zoom menu earns it on the merits: this is the bottom of the same "Zoom
31
- * out 100x" ladder, and it is where someone already zooming looks.
32
- *
33
- * The label is the import form's button text verbatim (`ImportForm.tsx`), which
34
- * is where most people meet the phrase. No icon: the four-arrows glyph it
35
- * carried reads as "fullscreen", and nothing else names the same gesture, so a
36
- * substitute would be decoration rather than a distinction.
37
- */
38
- export function showAllRegionsMenuItem(self) {
28
+ function showAllRegionsMenuItem(self) {
39
29
  return {
40
30
  label: 'Show all regions in assembly',
41
31
  onClick: () => {
@@ -43,8 +33,57 @@ export function showAllRegionsMenuItem(self) {
43
33
  },
44
34
  };
45
35
  }
36
+ export function zoomMenuItems(self) {
37
+ return [
38
+ ...[10, 50, 100].map(r => ({
39
+ label: `Zoom in ${r}x`,
40
+ onClick: () => {
41
+ self.zoom(self.bpPerPx / r);
42
+ },
43
+ })),
44
+ ...[10, 50, 100].map(r => ({
45
+ label: `Zoom out ${r}x`,
46
+ onClick: () => {
47
+ self.zoom(self.bpPerPx * r);
48
+ },
49
+ })),
50
+ showAllRegionsMenuItem(self),
51
+ {
52
+ label: 'Custom zoom',
53
+ onClick: () => {
54
+ getDialogHost(self).queueDialog(handleClose => [
55
+ RegionWidthEditorDialog,
56
+ { model: self, handleClose },
57
+ ]);
58
+ },
59
+ },
60
+ ];
61
+ }
62
+ export function buildHighlightsSubMenuItems(self) {
63
+ const session = getSession(self);
64
+ return [
65
+ {
66
+ label: 'Show highlights',
67
+ type: 'checkbox',
68
+ checked: session.highlightsVisible,
69
+ onClick: () => {
70
+ session.setHighlightsVisible(!session.highlightsVisible);
71
+ },
72
+ },
73
+ {
74
+ label: 'Show highlight labels',
75
+ type: 'checkbox',
76
+ checked: session.highlightLabelsVisible,
77
+ onClick: () => {
78
+ session.setHighlightLabelsVisible(!session.highlightLabelsVisible);
79
+ },
80
+ },
81
+ ...highlightListMenuItems(session, getEnv(self).pluginManager),
82
+ ];
83
+ }
46
84
  /**
47
- * Build the main view menu items
85
+ * Build the main view menu items. A row stacked in another view has no import
86
+ * form of its own, so only a top-level view offers the way back to one.
48
87
  */
49
88
  export function buildMenuItems(self) {
50
89
  if (!self.hasDisplayedRegions) {
@@ -53,24 +92,36 @@ export function buildMenuItems(self) {
53
92
  const session = getSession(self);
54
93
  const menuItems = [
55
94
  {
56
- label: self.scalebarOnly ? 'Expand tracks' : 'Collapse to ruler',
57
- icon: self.scalebarOnly ? ExpandMoreIcon : ExpandLessIcon,
95
+ label: 'Open track selector',
58
96
  onClick: () => {
59
- self.setScalebarOnly(!self.scalebarOnly);
97
+ self.activateTrackSelector();
60
98
  },
99
+ icon: TrackSelectorIcon,
61
100
  },
62
101
  {
63
- label: 'Return to import form',
102
+ label: 'Export SVG...',
103
+ icon: PhotoCameraIcon,
64
104
  onClick: () => {
65
105
  session.queueDialog(handleClose => [
66
- ReturnToImportFormDialog,
106
+ ExportSvgDialog,
67
107
  {
68
108
  model: self,
69
109
  handleClose,
110
+ chooseWidth: true,
70
111
  },
71
112
  ]);
72
113
  },
73
- icon: FolderOpenIcon,
114
+ },
115
+ {
116
+ label: 'Export R script',
117
+ icon: CodeIcon,
118
+ onClick: () => {
119
+ import('./rExport/exportLinearView.js')
120
+ .then(({ downloadRScript }) => downloadRScript(self))
121
+ .catch((e) => {
122
+ session.notifyError(`${e}`, e);
123
+ });
124
+ },
74
125
  },
75
126
  ...(isSessionWithAddSessionTrack(session)
76
127
  ? [
@@ -89,30 +140,35 @@ export function buildMenuItems(self) {
89
140
  },
90
141
  ]
91
142
  : []),
92
- {
93
- label: 'Export SVG',
94
- icon: PhotoCameraIcon,
95
- onClick: () => {
96
- session.queueDialog(handleClose => [
97
- ExportSvgDialog,
98
- {
99
- model: self,
100
- handleClose,
143
+ ...(self.isTopLevelView
144
+ ? [
145
+ {
146
+ label: 'Return to import form',
147
+ onClick: () => {
148
+ session.queueDialog(handleClose => [
149
+ ReturnToImportFormDialog,
150
+ {
151
+ model: self,
152
+ handleClose,
153
+ },
154
+ ]);
101
155
  },
102
- ]);
103
- },
104
- },
105
- {
106
- label: 'Open track selector',
107
- onClick: () => {
108
- self.activateTrackSelector();
109
- },
110
- icon: TrackSelectorIcon,
111
- },
112
- // Top-level rather than under a "Navigation" group: with scroll-to-zoom
113
- // gone from here the group held two rows, and a popup for two is a click
114
- // charged for nothing. Not under "Show...", which is visibility toggles.
115
- showAllRegionsMenuItem(self),
156
+ icon: FolderOpenIcon,
157
+ },
158
+ ]
159
+ : []),
160
+ ...(self.isTopLevelView && self.tracks.length
161
+ ? [
162
+ {
163
+ label: 'Fit tracks to window',
164
+ icon: FitScreenIcon,
165
+ disabled: !self.scrollPortExcess,
166
+ onClick: () => {
167
+ self.fitTracksToWindow();
168
+ },
169
+ },
170
+ ]
171
+ : []),
116
172
  {
117
173
  label: 'Horizontally flip',
118
174
  icon: SyncAltIcon,
@@ -120,115 +176,138 @@ export function buildMenuItems(self) {
120
176
  self.horizontallyFlip();
121
177
  },
122
178
  },
123
- {
124
- label: 'Color CDS by reading frame',
125
- type: 'checkbox',
126
- checked: self.colorByCDS,
127
- icon: PaletteIcon,
128
- onClick: () => {
129
- self.setColorByCDS(!self.colorByCDS);
179
+ ...makeShowSubMenu([
180
+ showAllRegionsMenuItem(self),
181
+ {
182
+ label: 'Ruler only',
183
+ type: 'checkbox',
184
+ checked: self.scalebarOnly,
185
+ onClick: () => {
186
+ self.setScalebarOnly(!self.scalebarOnly);
187
+ },
130
188
  },
131
- },
132
- {
133
- label: 'Show...',
134
- icon: VisibilityIcon,
135
- subMenu: [
136
- {
137
- label: 'Show center line',
138
- type: 'checkbox',
139
- checked: self.showCenterLine,
140
- onClick: () => {
141
- self.setShowCenterLine(!self.showCenterLine);
142
- },
189
+ {
190
+ label: 'Header',
191
+ type: 'checkbox',
192
+ checked: !self.hideHeader,
193
+ // opts out of the checkbox "stay open" default: with the header
194
+ // hidden these same items are reachable from MiniControls, which this
195
+ // row unmounts — leaving the menu anchored to a removed node
196
+ keepMenuOpen: false,
197
+ onClick: () => {
198
+ self.setHideHeader(!self.hideHeader);
143
199
  },
144
- {
145
- label: 'Show header',
146
- type: 'checkbox',
147
- checked: !self.hideHeader,
148
- // opts out of the checkbox "stay open" default: with the header
149
- // hidden these same items are reachable from MiniControls, which this
150
- // row unmounts — leaving the menu anchored to a removed node
151
- keepMenuOpen: false,
152
- onClick: () => {
153
- self.setHideHeader(!self.hideHeader);
154
- },
200
+ },
201
+ {
202
+ label: 'Header overview',
203
+ type: 'checkbox',
204
+ checked: !self.hideHeaderOverview,
205
+ onClick: () => {
206
+ self.setHideHeaderOverview(!self.hideHeaderOverview);
155
207
  },
156
- {
157
- label: 'Show track outlines',
158
- type: 'checkbox',
159
- checked: self.showTrackOutlines,
160
- onClick: () => {
161
- self.setShowTrackOutlines(!self.showTrackOutlines);
208
+ disabled: self.hideHeader,
209
+ },
210
+ ...(self.canShowCytobands
211
+ ? [
212
+ {
213
+ label: 'Ideogram',
214
+ type: 'checkbox',
215
+ checked: self.showCytobands,
216
+ onClick: () => {
217
+ self.setShowCytobands(!self.showCytobands);
218
+ },
162
219
  },
220
+ ]
221
+ : []),
222
+ {
223
+ label: 'Center line',
224
+ type: 'checkbox',
225
+ checked: self.showCenterLine,
226
+ onClick: () => {
227
+ self.setShowCenterLine(!self.showCenterLine);
163
228
  },
164
- {
165
- label: 'Show header overview',
166
- type: 'checkbox',
167
- checked: !self.hideHeaderOverview,
168
- onClick: () => {
169
- self.setHideHeaderOverview(!self.hideHeaderOverview);
170
- },
171
- disabled: self.hideHeader,
229
+ },
230
+ {
231
+ label: 'Guidelines',
232
+ type: 'checkbox',
233
+ checked: self.axisGrid,
234
+ onClick: () => {
235
+ self.setAxisGrid(!self.axisGrid);
172
236
  },
173
- {
174
- label: 'Show no tracks active button',
175
- type: 'checkbox',
176
- checked: !self.hideNoTracksActive,
177
- onClick: () => {
178
- self.setHideNoTracksActive(!self.hideNoTracksActive);
179
- },
237
+ },
238
+ {
239
+ label: 'Track outlines',
240
+ type: 'checkbox',
241
+ checked: self.showTrackOutlines,
242
+ onClick: () => {
243
+ self.setShowTrackOutlines(!self.showTrackOutlines);
180
244
  },
181
- {
182
- // no icon: the palette it used to carry sat directly against the
183
- // color-by-CDS one above it, which is the reason it moved here
184
- label: 'Show amino acids when zoomed in',
185
- type: 'checkbox',
186
- checked: self.showAminoAcids,
187
- onClick: () => {
188
- self.setShowAminoAcids(!self.showAminoAcids);
189
- },
245
+ },
246
+ {
247
+ label: 'No tracks active button',
248
+ type: 'checkbox',
249
+ checked: !self.hideNoTracksActive,
250
+ onClick: () => {
251
+ self.setHideNoTracksActive(!self.hideNoTracksActive);
190
252
  },
191
- {
192
- label: 'Show guidelines',
193
- type: 'checkbox',
194
- checked: self.showGridlines,
195
- onClick: () => {
196
- self.setShowGridlines(!self.showGridlines);
197
- },
253
+ },
254
+ { type: 'subHeader', label: 'Sequence' },
255
+ {
256
+ label: 'CDS reading frame colors',
257
+ type: 'checkbox',
258
+ checked: self.colorByFrame,
259
+ onClick: () => {
260
+ self.setColorByFrame(!self.colorByFrame);
198
261
  },
199
- ...(self.canShowCytobands
200
- ? [
201
- {
202
- label: 'Show ideogram',
203
- type: 'checkbox',
204
- checked: self.showCytobands,
205
- onClick: () => {
206
- self.setShowCytobands(!self.showCytobands);
207
- },
208
- },
209
- ]
210
- : []),
211
- // Where a track's name is drawn, and "Hidden" is one of the three
212
- // answers, so this is a visibility setting like everything above it.
213
- // Inline under a subheader rather than in a submenu of its own: it was a
214
- // top-level row for three radios, and nesting it here instead would have
215
- // put those radios a popup further from the hamburger than they were.
216
- // The icons went with it — all three rows carried the same one, which
217
- // told a reader nothing about which to pick.
218
- { type: 'subHeader', label: 'Track labels' },
219
- ...radioItems(TRACK_LABEL_OPTIONS, self.effectiveTrackLabels, setting => {
220
- self.setTrackLabels(setting);
221
- }),
222
- ],
262
+ },
263
+ {
264
+ label: 'Amino acids when zoomed in',
265
+ type: 'checkbox',
266
+ checked: self.showAminoAcids,
267
+ onClick: () => {
268
+ self.setShowAminoAcids(!self.showAminoAcids);
269
+ },
270
+ },
271
+ ]),
272
+ {
273
+ label: 'Highlights',
274
+ icon: Highlighter,
275
+ subMenu: self.highlightsSubMenuItems(),
276
+ },
277
+ {
278
+ label: 'Track labels',
279
+ icon: LabelIcon,
280
+ subMenu: radioItems(TRACK_LABEL_OPTIONS, self.effectiveTrackLabels, setting => {
281
+ self.setTrackLabels(setting);
282
+ }),
223
283
  },
224
284
  ];
225
285
  return menuItems;
226
286
  }
227
287
  /**
228
- * Build rubberband selection menu items. `launchItems` are the plugin-supplied
229
- * things a selection can start (`rubberBandLaunchMenuItems()`); they collect
230
- * under one "Launch" submenu so the menu stays three actions plus a group
231
- * however many plugins are loaded, and vanish entirely when none apply.
288
+ * Open a copy of the view, tracks and their settings included, framed on the
289
+ * span between two offsets. The same copy as the view menu's "Copy view", and
290
+ * nothing links it back to the original.
291
+ */
292
+ export function openSpanInNewView(view, start, end) {
293
+ if (!start || !end) {
294
+ return undefined;
295
+ }
296
+ const { bpPerPx, offsetPx } = computeMoveToLayout(view, start, end);
297
+ return getSession(view).addView(view.type, {
298
+ ...renameIds(getSnapshot(view)),
299
+ windowWidthBp: bpPerPx * view.width,
300
+ windowStartBp: offsetPx * bpPerPx,
301
+ });
302
+ }
303
+ /**
304
+ * Build rubberband selection menu items. What a selection can open collects
305
+ * under one "Launch" submenu, which sorts last, below any row a plugin appends
306
+ * to the menu itself: a new linear genome view of the span first, then
307
+ * `launchItems`, the plugin-supplied ones (`rubberBandLaunchMenuItems()`).
308
+ *
309
+ * The new view is a copy of this one, so only a top-level view offers it: a row
310
+ * of a comparative view carries that view's framing, such as a hidden header.
232
311
  */
233
312
  export function buildRubberBandMenuItems(self, launchItems) {
234
313
  // captured once here and used by the items below rather than re-read inside
@@ -241,7 +320,23 @@ export function buildRubberBandMenuItems(self, launchItems) {
241
320
  // reversed region it names neither the base painted at the pixel nor the ends
242
321
  // in ascending order, and a `leftRef === rightRef` test calls a selection
243
322
  // crossing a collapsed intron one range when it is two.
244
- const rangeString = assembleLocStrings(self.getSelectedRegions(leftOffset, rightOffset));
323
+ const regions = self.getSelectedRegions(leftOffset, rightOffset);
324
+ const rangeString = assembleLocStrings(regions);
325
+ const launch = [
326
+ ...(self.isTopLevelView
327
+ ? [
328
+ {
329
+ label: 'Linear genome view',
330
+ icon: OpenInNewIcon,
331
+ helpText: 'A new view of this span, with the same tracks. It scrolls and zooms on its own.',
332
+ onClick: () => {
333
+ openSpanInNewView(self, leftOffset, rightOffset);
334
+ },
335
+ },
336
+ ]
337
+ : []),
338
+ ...launchItems,
339
+ ];
245
340
  return [
246
341
  {
247
342
  label: 'Zoom to region',
@@ -251,10 +346,9 @@ export function buildRubberBandMenuItems(self, launchItems) {
251
346
  },
252
347
  },
253
348
  {
254
- label: 'Get sequence',
349
+ label: 'Get sequence...',
255
350
  icon: MenuOpenIcon,
256
351
  onClick: () => {
257
- const regions = self.getSelectedRegions(leftOffset, rightOffset);
258
352
  getDialogHost(self).queueDialog(handleClose => [
259
353
  GetSequenceDialog,
260
354
  {
@@ -275,23 +369,29 @@ export function buildRubberBandMenuItems(self, launchItems) {
275
369
  void copyText(self, rangeString, 'range');
276
370
  },
277
371
  },
278
- ...(launchItems.length
279
- ? [
280
- {
281
- label: 'Launch',
282
- icon: LaunchIcon,
283
- type: 'subMenu',
284
- subMenu: launchItems,
285
- },
286
- ]
287
- : []),
372
+ {
373
+ label: 'Highlight region',
374
+ icon: Highlighter,
375
+ onClick: () => {
376
+ const session = getSession(self);
377
+ for (const region of regions) {
378
+ session.addHighlight(region);
379
+ }
380
+ },
381
+ },
382
+ ...subMenuIfAny({
383
+ label: 'Launch',
384
+ icon: LaunchIcon,
385
+ priority: -1000,
386
+ subMenu: launch,
387
+ }),
288
388
  ];
289
389
  }
290
390
  /**
291
391
  * Build rubberband click menu items (single click on rubberband area)
292
392
  */
293
393
  export function buildRubberbandClickMenuItems(self, clickOffset) {
294
- const { refName, start, end, reversed, offset } = clickOffset;
394
+ const { refName, start, end, reversed, offset, index } = clickOffset;
295
395
  if (refName === undefined || start === undefined || end === undefined) {
296
396
  return [];
297
397
  }
@@ -309,15 +409,15 @@ export function buildRubberbandClickMenuItems(self, clickOffset) {
309
409
  label: 'Center view here',
310
410
  icon: CenterFocusStrongIcon,
311
411
  onClick: () => {
312
- self.centerAt(coord0, refName);
412
+ self.centerAt(coord0, refName, index);
313
413
  },
314
414
  },
315
415
  {
316
416
  label: 'Zoom to base level',
317
417
  icon: ZoomInIcon,
318
418
  onClick: () => {
319
- self.centerAt(coord0, refName);
320
419
  self.zoomTo(self.minBpPerPx);
420
+ self.centerAt(coord0, refName, index);
321
421
  },
322
422
  },
323
423
  {