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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/esm/FeatureTrack/configSchema.d.ts +140 -48
  2. package/esm/LinearGenomeView/afterAttach.d.ts +5 -5
  3. package/esm/LinearGenomeView/afterAttach.js +18 -24
  4. package/esm/LinearGenomeView/closeUps.d.ts +83 -0
  5. package/esm/LinearGenomeView/closeUps.js +211 -0
  6. package/esm/LinearGenomeView/components/AddCloseUpDialog.d.ts +19 -0
  7. package/esm/LinearGenomeView/components/AddCloseUpDialog.js +29 -0
  8. package/esm/LinearGenomeView/components/CloseUps.d.ts +5 -0
  9. package/esm/LinearGenomeView/components/CloseUps.js +75 -0
  10. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +2 -1
  11. package/esm/LinearGenomeView/components/ExportSvgDialog.js +6 -2
  12. package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
  13. package/esm/LinearGenomeView/components/Header.js +6 -1
  14. package/esm/LinearGenomeView/components/HeaderZoomControls.js +4 -34
  15. package/esm/LinearGenomeView/components/Highlight.js +2 -4
  16. package/esm/LinearGenomeView/components/HighlightBand.js +2 -2
  17. package/esm/LinearGenomeView/components/HighlightChip.d.ts +1 -2
  18. package/esm/LinearGenomeView/components/HighlightChip.js +5 -7
  19. package/esm/LinearGenomeView/components/ImportForm.js +2 -2
  20. package/esm/LinearGenomeView/components/LinearGenomeView.js +3 -3
  21. package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +3 -2
  22. package/esm/LinearGenomeView/components/OverviewHighlight.js +1 -1
  23. package/esm/LinearGenomeView/components/OverviewHighlightBand.js +2 -3
  24. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.d.ts +7 -1
  25. package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +22 -7
  26. package/esm/LinearGenomeView/components/SVGHighlightBand.js +2 -3
  27. package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +6 -21
  28. package/esm/LinearGenomeView/components/SearchBox.js +2 -2
  29. package/esm/LinearGenomeView/components/TrackContainer.js +12 -5
  30. package/esm/LinearGenomeView/components/TrackLabel.js +1 -1
  31. package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +3 -1
  32. package/esm/LinearGenomeView/components/TrackRenderingContainer.js +3 -7
  33. package/esm/LinearGenomeView/components/TracksContainer.js +7 -15
  34. package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
  35. package/esm/LinearGenomeView/components/useSideScroll.js +1 -1
  36. package/esm/LinearGenomeView/consts.d.ts +2 -0
  37. package/esm/LinearGenomeView/consts.js +5 -0
  38. package/esm/LinearGenomeView/headerFit.d.ts +2 -1
  39. package/esm/LinearGenomeView/headerFit.js +7 -2
  40. package/esm/LinearGenomeView/index.d.ts +1 -1
  41. package/esm/LinearGenomeView/keyboardHandler.d.ts +2 -0
  42. package/esm/LinearGenomeView/keyboardHandler.js +11 -0
  43. package/esm/LinearGenomeView/launchKeys.d.ts +1 -1
  44. package/esm/LinearGenomeView/launchKeys.js +15 -5
  45. package/esm/LinearGenomeView/lazyDialogs.d.ts +7 -0
  46. package/esm/LinearGenomeView/lazyDialogs.js +2 -0
  47. package/esm/LinearGenomeView/menuItems.d.ts +28 -16
  48. package/esm/LinearGenomeView/menuItems.js +213 -85
  49. package/esm/LinearGenomeView/model.d.ts +367 -199
  50. package/esm/LinearGenomeView/model.js +302 -143
  51. package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
  52. package/esm/LinearGenomeView/showRegionsWithUndo.js +7 -1
  53. package/esm/LinearGenomeView/svgcomponents/SVGCloseUpFrame.d.ts +5 -0
  54. package/esm/LinearGenomeView/svgcomponents/SVGCloseUpFrame.js +8 -0
  55. package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +3 -4
  56. package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +3 -3
  57. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +95 -24
  58. package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +4 -5
  59. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +13 -4
  60. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +4 -4
  61. package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +1 -1
  62. package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +52 -63
  63. package/esm/LinearGenomeView/svgcomponents/util.d.ts +8 -6
  64. package/esm/LinearGenomeView/svgcomponents/util.js +37 -16
  65. package/esm/LinearGenomeView/types.d.ts +5 -4
  66. package/esm/LinearGenomeView/util.d.ts +20 -37
  67. package/esm/LinearGenomeView/util.js +58 -141
  68. package/esm/displayKitTests/perRegionTestEnv.d.ts +64 -45
  69. package/esm/displayKitTests/perRegionTestEnv.js +6 -6
  70. package/esm/index.d.ts +9 -67
  71. package/esm/index.js +6 -22
  72. package/esm/searchUtils.d.ts +2 -3
  73. package/esm/searchUtils.js +2 -2
  74. package/package.json +7 -7
  75. package/esm/lazyPluginExports.d.ts +0 -23
  76. package/esm/lazyPluginExports.js +0 -36
@@ -1,41 +1,37 @@
1
- import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons';
1
+ import { Highlighter, TrackSelector as TrackSelectorIcon, } from '@jbrowse/core/ui/Icons';
2
2
  import { radioItems } from '@jbrowse/core/ui/menuItems';
3
3
  import { assembleLocStrings, getDialogHost, getSession, isSessionWithAddSessionTrack, toLocale, } from '@jbrowse/core/util';
4
4
  import { basePaintedAt } from '@jbrowse/core/util/Base1DUtils';
5
5
  import { copyText } from '@jbrowse/core/util/copyText';
6
6
  import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong';
7
7
  import ContentCopyIcon from '@mui/icons-material/ContentCopy';
8
- import ExpandLessIcon from '@mui/icons-material/ExpandLess';
9
- import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
10
8
  import FolderOpenIcon from '@mui/icons-material/FolderOpen';
11
9
  import LaunchIcon from '@mui/icons-material/Launch';
10
+ import LayersIcon from '@mui/icons-material/Layers';
11
+ import LayersClearIcon from '@mui/icons-material/LayersClear';
12
12
  import MenuOpenIcon from '@mui/icons-material/MenuOpen';
13
- import PaletteIcon from '@mui/icons-material/Palette';
14
13
  import PhotoCameraIcon from '@mui/icons-material/PhotoCamera';
15
14
  import SearchIcon from '@mui/icons-material/Search';
16
15
  import SyncAltIcon from '@mui/icons-material/SyncAlt';
17
16
  import VisibilityIcon from '@mui/icons-material/Visibility';
18
17
  import ZoomInIcon from '@mui/icons-material/ZoomIn';
19
- import { ExportSvgDialog, GetSequenceDialog, ReturnToImportFormDialog, SequenceSearchDialog, } from './lazyDialogs.js';
18
+ import { closeUpHost } from './closeUps.js';
19
+ import { AddCloseUpDialog, ExportSvgDialog, GetSequenceDialog, RegionWidthEditorDialog, ReturnToImportFormDialog, SequenceSearchDialog, } from './lazyDialogs.js';
20
20
  const TRACK_LABEL_OPTIONS = [
21
21
  { value: 'overlapping', label: 'Overlapping' },
22
22
  { value: 'offset', label: 'Offset' },
23
23
  { value: 'hidden', label: 'Hidden' },
24
24
  ];
25
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.
26
+ * Zoom all the way out — the bottom of the same "Zoom out 100x" ladder it sits
27
+ * under.
32
28
  *
33
29
  * The label is the import form's button text verbatim (`ImportForm.tsx`), which
34
30
  * is where most people meet the phrase. No icon: the four-arrows glyph it
35
31
  * carried reads as "fullscreen", and nothing else names the same gesture, so a
36
32
  * substitute would be decoration rather than a distinction.
37
33
  */
38
- export function showAllRegionsMenuItem(self) {
34
+ function showAllRegionsMenuItem(self) {
39
35
  return {
40
36
  label: 'Show all regions in assembly',
41
37
  onClick: () => {
@@ -44,33 +40,71 @@ export function showAllRegionsMenuItem(self) {
44
40
  };
45
41
  }
46
42
  /**
47
- * Build the main view menu items
43
+ * The zoom ladder, shared by the header's zoom button and the view menu's
44
+ * "Zoom" — one definition so the two cannot drift, and the view menu is the
45
+ * only one of the two that survives `hideHeader`.
46
+ */
47
+ export function zoomMenuItems(self) {
48
+ return [
49
+ ...[10, 50, 100].map(r => ({
50
+ label: `Zoom in ${r}x`,
51
+ onClick: () => {
52
+ self.zoom(self.bpPerPx / r);
53
+ },
54
+ })),
55
+ ...[10, 50, 100].map(r => ({
56
+ label: `Zoom out ${r}x`,
57
+ onClick: () => {
58
+ self.zoom(self.bpPerPx * r);
59
+ },
60
+ })),
61
+ showAllRegionsMenuItem(self),
62
+ {
63
+ label: 'Custom zoom',
64
+ onClick: () => {
65
+ getDialogHost(self).queueDialog(handleClose => [
66
+ RegionWidthEditorDialog,
67
+ { model: self, handleClose },
68
+ ]);
69
+ },
70
+ },
71
+ ];
72
+ }
73
+ /**
74
+ * Build the main view menu items. A row stacked in another view has no import
75
+ * form of its own, so only a top-level view offers the way back to one.
76
+ *
77
+ * Four actions, a Zoom group and a Show group. The four are what people open
78
+ * this menu to do — pick tracks, take a picture, search the sequence, start
79
+ * over — and the once-a-session settings that used to sit beside them are a
80
+ * popup away instead of in front of every reader every time.
48
81
  */
49
82
  export function buildMenuItems(self) {
50
83
  if (!self.hasDisplayedRegions) {
51
84
  return [];
52
85
  }
53
86
  const session = getSession(self);
87
+ const host = closeUpHost(self);
54
88
  const menuItems = [
55
89
  {
56
- label: self.scalebarOnly ? 'Expand tracks' : 'Collapse to ruler',
57
- icon: self.scalebarOnly ? ExpandMoreIcon : ExpandLessIcon,
90
+ label: 'Open track selector',
58
91
  onClick: () => {
59
- self.setScalebarOnly(!self.scalebarOnly);
92
+ self.activateTrackSelector();
60
93
  },
94
+ icon: TrackSelectorIcon,
61
95
  },
62
96
  {
63
- label: 'Return to import form',
97
+ label: 'Export SVG',
98
+ icon: PhotoCameraIcon,
64
99
  onClick: () => {
65
100
  session.queueDialog(handleClose => [
66
- ReturnToImportFormDialog,
101
+ ExportSvgDialog,
67
102
  {
68
103
  model: self,
69
104
  handleClose,
70
105
  },
71
106
  ]);
72
107
  },
73
- icon: FolderOpenIcon,
74
108
  },
75
109
  ...(isSessionWithAddSessionTrack(session)
76
110
  ? [
@@ -89,30 +123,37 @@ export function buildMenuItems(self) {
89
123
  },
90
124
  ]
91
125
  : []),
92
- {
93
- label: 'Export SVG',
94
- icon: PhotoCameraIcon,
95
- onClick: () => {
96
- session.queueDialog(handleClose => [
97
- ExportSvgDialog,
98
- {
99
- model: self,
100
- handleClose,
126
+ ...(self.isTopLevelView
127
+ ? [
128
+ {
129
+ label: 'Return to import form',
130
+ onClick: () => {
131
+ session.queueDialog(handleClose => [
132
+ ReturnToImportFormDialog,
133
+ {
134
+ model: self,
135
+ handleClose,
136
+ },
137
+ ]);
101
138
  },
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),
139
+ icon: FolderOpenIcon,
140
+ },
141
+ ]
142
+ : []),
143
+ // The one row a close-up adds, and the only one of its own it needs.
144
+ // Top-level rather than under Zoom: a close-up is a second panel on the
145
+ // page, and taking a panel away is not a zoom.
146
+ ...(host
147
+ ? [
148
+ {
149
+ label: 'Remove close-up view',
150
+ icon: LayersClearIcon,
151
+ onClick: () => {
152
+ host.removeCloseUp(self);
153
+ },
154
+ },
155
+ ]
156
+ : []),
116
157
  {
117
158
  label: 'Horizontally flip',
118
159
  icon: SyncAltIcon,
@@ -121,24 +162,25 @@ export function buildMenuItems(self) {
121
162
  },
122
163
  },
123
164
  {
124
- label: 'Color CDS by reading frame',
125
- type: 'checkbox',
126
- checked: self.colorByCDS,
127
- icon: PaletteIcon,
128
- onClick: () => {
129
- self.setColorByCDS(!self.colorByCDS);
130
- },
165
+ label: 'Zoom',
166
+ icon: ZoomInIcon,
167
+ subMenu: zoomMenuItems(self),
131
168
  },
132
169
  {
133
170
  label: 'Show...',
134
171
  icon: VisibilityIcon,
135
172
  subMenu: [
173
+ // Collapsing a row is about height and what it hides is the tracks, so
174
+ // it is the widest of the visibility answers and heads them. A checkbox
175
+ // rather than the pair of labels it was: "Collapse to ruler" turning
176
+ // into "Expand tracks" names the current state nowhere, and among
177
+ // checkboxes there is no room for the trick anyway.
136
178
  {
137
- label: 'Show center line',
179
+ label: 'Collapse to ruler',
138
180
  type: 'checkbox',
139
- checked: self.showCenterLine,
181
+ checked: self.scalebarOnly,
140
182
  onClick: () => {
141
- self.setShowCenterLine(!self.showCenterLine);
183
+ self.setScalebarOnly(!self.scalebarOnly);
142
184
  },
143
185
  },
144
186
  {
@@ -154,21 +196,49 @@ export function buildMenuItems(self) {
154
196
  },
155
197
  },
156
198
  {
157
- label: 'Show track outlines',
199
+ label: 'Show header overview',
158
200
  type: 'checkbox',
159
- checked: self.showTrackOutlines,
201
+ checked: !self.hideHeaderOverview,
160
202
  onClick: () => {
161
- self.setShowTrackOutlines(!self.showTrackOutlines);
203
+ self.setHideHeaderOverview(!self.hideHeaderOverview);
204
+ },
205
+ disabled: self.hideHeader,
206
+ },
207
+ ...(self.canShowCytobands
208
+ ? [
209
+ {
210
+ label: 'Show ideogram',
211
+ type: 'checkbox',
212
+ checked: self.showCytobands,
213
+ onClick: () => {
214
+ self.setShowCytobands(!self.showCytobands);
215
+ },
216
+ },
217
+ ]
218
+ : []),
219
+ {
220
+ label: 'Show center line',
221
+ type: 'checkbox',
222
+ checked: self.showCenterLine,
223
+ onClick: () => {
224
+ self.setShowCenterLine(!self.showCenterLine);
162
225
  },
163
226
  },
164
227
  {
165
- label: 'Show header overview',
228
+ label: 'Show guidelines',
166
229
  type: 'checkbox',
167
- checked: !self.hideHeaderOverview,
230
+ checked: self.showGridlines,
168
231
  onClick: () => {
169
- self.setHideHeaderOverview(!self.hideHeaderOverview);
232
+ self.setShowGridlines(!self.showGridlines);
233
+ },
234
+ },
235
+ {
236
+ label: 'Show track outlines',
237
+ type: 'checkbox',
238
+ checked: self.showTrackOutlines,
239
+ onClick: () => {
240
+ self.setShowTrackOutlines(!self.showTrackOutlines);
170
241
  },
171
- disabled: self.hideHeader,
172
242
  },
173
243
  {
174
244
  label: 'Show no tracks active button',
@@ -178,36 +248,43 @@ export function buildMenuItems(self) {
178
248
  self.setHideNoTracksActive(!self.hideNoTracksActive);
179
249
  },
180
250
  },
251
+ { type: 'subHeader', label: 'Highlights' },
181
252
  {
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',
253
+ label: 'Show highlights',
185
254
  type: 'checkbox',
186
- checked: self.showAminoAcids,
255
+ checked: session.highlightsVisible,
187
256
  onClick: () => {
188
- self.setShowAminoAcids(!self.showAminoAcids);
257
+ session.setHighlightsVisible(!session.highlightsVisible);
189
258
  },
190
259
  },
191
260
  {
192
- label: 'Show guidelines',
261
+ label: 'Show highlight labels',
193
262
  type: 'checkbox',
194
- checked: self.showGridlines,
263
+ checked: self.labelsVisible,
195
264
  onClick: () => {
196
- self.setShowGridlines(!self.showGridlines);
265
+ self.setLabelsVisible(!self.labelsVisible);
266
+ },
267
+ },
268
+ // The two answers to what the sequence draws as: one colours the
269
+ // codons, the other spells them out. Both lost the palette icon they
270
+ // used to carry — nothing else in this list has one.
271
+ { type: 'subHeader', label: 'Sequence' },
272
+ {
273
+ label: 'Color CDS by reading frame',
274
+ type: 'checkbox',
275
+ checked: self.colorByCDS,
276
+ onClick: () => {
277
+ self.setColorByCDS(!self.colorByCDS);
278
+ },
279
+ },
280
+ {
281
+ label: 'Show amino acids when zoomed in',
282
+ type: 'checkbox',
283
+ checked: self.showAminoAcids,
284
+ onClick: () => {
285
+ self.setShowAminoAcids(!self.showAminoAcids);
197
286
  },
198
287
  },
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
288
  // Where a track's name is drawn, and "Hidden" is one of the three
212
289
  // answers, so this is a visibility setting like everything above it.
213
290
  // Inline under a subheader rather than in a submenu of its own: it was a
@@ -225,10 +302,24 @@ export function buildMenuItems(self) {
225
302
  return menuItems;
226
303
  }
227
304
  /**
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.
305
+ * Build rubberband selection menu items. What a selection can open collects
306
+ * under one "Launch" submenu, which sorts last, below any row a plugin appends
307
+ * to the menu itself: a close-up view first, then `launchItems`, the
308
+ * plugin-supplied ones (`rubberBandLaunchMenuItems()`).
309
+ *
310
+ * A close-up is offered from HERE and nowhere else, because a drag is what the
311
+ * feature was always asking for and a menu item could not: the close-up shows
312
+ * one span of one place, and this is the gesture that names both. From the view
313
+ * menu it could only guess — a tenth of the middle, which is a span nobody
314
+ * picked.
315
+ *
316
+ * Only a view of its own offers it, which `isTopLevelView` is the whole of. A
317
+ * row of a comparative stack is already part of somebody's figure: the synteny
318
+ * ribbons and the breakpoint split panels are placed against each row's own
319
+ * height, and those views' exports draw one row per view, so a close-up grown
320
+ * there walks the ribbons off their rows and is dropped from the picture
321
+ * without a word. A close-up, living under a view rather than under the
322
+ * session, is not a top-level view either.
232
323
  */
233
324
  export function buildRubberBandMenuItems(self, launchItems) {
234
325
  // captured once here and used by the items below rather than re-read inside
@@ -242,6 +333,32 @@ export function buildRubberBandMenuItems(self, launchItems) {
242
333
  // in ascending order, and a `leftRef === rightRef` test calls a selection
243
334
  // crossing a collapsed intron one range when it is two.
244
335
  const rangeString = assembleLocStrings(self.getSelectedRegions(leftOffset, rightOffset));
336
+ const launch = [
337
+ ...(self.isTopLevelView
338
+ ? [
339
+ {
340
+ label: 'Close-up view',
341
+ icon: LayersIcon,
342
+ helpText: 'A zoomed-in copy of this view, opened below its tracks. It stays centred on this view and pans with it.',
343
+ onClick: () => {
344
+ getDialogHost(self).queueDialog(handleClose => [
345
+ AddCloseUpDialog,
346
+ {
347
+ model: self,
348
+ leftOffset,
349
+ rightOffset,
350
+ handleClose: () => {
351
+ handleClose();
352
+ self.setOffsets();
353
+ },
354
+ },
355
+ ]);
356
+ },
357
+ },
358
+ ]
359
+ : []),
360
+ ...launchItems,
361
+ ];
245
362
  return [
246
363
  {
247
364
  label: 'Zoom to region',
@@ -275,13 +392,24 @@ export function buildRubberBandMenuItems(self, launchItems) {
275
392
  void copyText(self, rangeString, 'range');
276
393
  },
277
394
  },
278
- ...(launchItems.length
395
+ {
396
+ label: 'Highlight region',
397
+ icon: Highlighter,
398
+ onClick: () => {
399
+ const [region] = self.getSelectedRegions(leftOffset, rightOffset);
400
+ if (region) {
401
+ getSession(self).addHighlight(region);
402
+ }
403
+ },
404
+ },
405
+ ...(launch.length
279
406
  ? [
280
407
  {
281
408
  label: 'Launch',
282
409
  icon: LaunchIcon,
283
410
  type: 'subMenu',
284
- subMenu: launchItems,
411
+ priority: -1000,
412
+ subMenu: launch,
285
413
  },
286
414
  ]
287
415
  : []),