react-msaview 8.2.0 → 8.4.0

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 (255) hide show
  1. package/bundle/index.js +77 -300
  2. package/bundle/index.js.map +4 -4
  3. package/dist/blockFasta.d.ts +6 -0
  4. package/dist/blockFasta.js +12 -0
  5. package/dist/blockFasta.js.map +1 -0
  6. package/dist/colorSchemes.d.ts +7 -0
  7. package/dist/colorSchemes.js +15 -0
  8. package/dist/colorSchemes.js.map +1 -1
  9. package/dist/columnCounts.d.ts +4 -3
  10. package/dist/columnCounts.js +11 -6
  11. package/dist/columnCounts.js.map +1 -1
  12. package/dist/components/CopyButton.d.ts +1 -1
  13. package/dist/components/CopyButton.js +1 -1
  14. package/dist/components/CopyButton.js.map +1 -1
  15. package/dist/components/DragHandle.js +14 -2
  16. package/dist/components/DragHandle.js.map +1 -1
  17. package/dist/components/Loading.js +10 -11
  18. package/dist/components/Loading.js.map +1 -1
  19. package/dist/components/MSAView.js +15 -9
  20. package/dist/components/MSAView.js.map +1 -1
  21. package/dist/components/MSAViewer.d.ts +39 -7
  22. package/dist/components/MSAViewer.js +72 -22
  23. package/dist/components/MSAViewer.js.map +1 -1
  24. package/dist/components/MetadataTable.d.ts +5 -0
  25. package/dist/components/MetadataTable.js +29 -0
  26. package/dist/components/MetadataTable.js.map +1 -0
  27. package/dist/components/SequenceTextArea.d.ts +11 -0
  28. package/dist/components/SequenceTextArea.js +40 -8
  29. package/dist/components/SequenceTextArea.js.map +1 -1
  30. package/dist/components/StatusMessage.d.ts +12 -0
  31. package/dist/components/StatusMessage.js +43 -0
  32. package/dist/components/StatusMessage.js.map +1 -0
  33. package/dist/components/Track.js +13 -4
  34. package/dist/components/Track.js.map +1 -1
  35. package/dist/components/VerticalScrollbar.js +6 -1
  36. package/dist/components/VerticalScrollbar.js.map +1 -1
  37. package/dist/components/dialogs/ExportSVGDialog.d.ts +3 -2
  38. package/dist/components/dialogs/ExportSVGDialog.js +4 -2
  39. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  40. package/dist/components/dialogs/FeatureDialog.js +15 -10
  41. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  42. package/dist/components/dialogs/MetadataDialog.js +5 -6
  43. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  44. package/dist/components/getVisibleLeaves.d.ts +1 -1
  45. package/dist/components/header/ColorSchemeMenu.d.ts +6 -0
  46. package/dist/components/header/ColorSchemeMenu.js +23 -5
  47. package/dist/components/header/ColorSchemeMenu.js.map +1 -1
  48. package/dist/components/header/FileMenu.js +9 -0
  49. package/dist/components/header/FileMenu.js.map +1 -1
  50. package/dist/components/header/GappynessSlider.js +15 -3
  51. package/dist/components/header/GappynessSlider.js.map +1 -1
  52. package/dist/components/header/{HeaderStatusArea.d.ts → GoToBox.d.ts} +2 -2
  53. package/dist/components/header/GoToBox.js +21 -0
  54. package/dist/components/header/GoToBox.js.map +1 -0
  55. package/dist/components/header/Header.js +8 -7
  56. package/dist/components/header/Header.js.map +1 -1
  57. package/dist/components/header/SelectionMenu.d.ts +12 -0
  58. package/dist/components/header/SelectionMenu.js +72 -0
  59. package/dist/components/header/SelectionMenu.js.map +1 -0
  60. package/dist/components/header/goTo.d.ts +20 -0
  61. package/dist/components/header/goTo.js +69 -0
  62. package/dist/components/header/goTo.js.map +1 -0
  63. package/dist/components/header/settingsMenuItems.js +57 -1
  64. package/dist/components/header/settingsMenuItems.js.map +1 -1
  65. package/dist/components/import/ImportForm.js +10 -4
  66. package/dist/components/import/ImportForm.js.map +1 -1
  67. package/dist/components/import/ImportFormExamples.js +19 -27
  68. package/dist/components/import/ImportFormExamples.js.map +1 -1
  69. package/dist/components/minimap/Minimap.js +6 -1
  70. package/dist/components/minimap/Minimap.js.map +1 -1
  71. package/dist/components/msa/AnnotationLegend.js +11 -5
  72. package/dist/components/msa/AnnotationLegend.js.map +1 -1
  73. package/dist/components/msa/MSACanvas.js +17 -1
  74. package/dist/components/msa/MSACanvas.js.map +1 -1
  75. package/dist/components/msa/MSACanvasBlock.js +6 -6
  76. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  77. package/dist/components/msa/msaKeys.d.ts +5 -0
  78. package/dist/components/msa/msaKeys.js +41 -0
  79. package/dist/components/msa/msaKeys.js.map +1 -0
  80. package/dist/components/msa/msaRaster.js +1 -0
  81. package/dist/components/msa/msaRaster.js.map +1 -1
  82. package/dist/components/msa/renderHighlights.d.ts +14 -3
  83. package/dist/components/msa/renderHighlights.js +29 -4
  84. package/dist/components/msa/renderHighlights.js.map +1 -1
  85. package/dist/components/msa/tileColor.js +9 -8
  86. package/dist/components/msa/tileColor.js.map +1 -1
  87. package/dist/components/msa/useMsaBlockMouse.d.ts +2 -4
  88. package/dist/components/msa/useMsaBlockMouse.js +34 -14
  89. package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
  90. package/dist/components/overlayColors.d.ts +2 -0
  91. package/dist/components/overlayColors.js +3 -0
  92. package/dist/components/overlayColors.js.map +1 -1
  93. package/dist/components/rowpanels/RowPanelBlock.js +10 -14
  94. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
  95. package/dist/components/selectionDrag.d.ts +17 -0
  96. package/dist/components/selectionDrag.js +30 -0
  97. package/dist/components/selectionDrag.js.map +1 -0
  98. package/dist/components/tracks/drawTracks.d.ts +1 -0
  99. package/dist/components/tracks/drawTracks.js +14 -2
  100. package/dist/components/tracks/drawTracks.js.map +1 -1
  101. package/dist/components/tracks/useTrackHover.d.ts +4 -6
  102. package/dist/components/tracks/useTrackHover.js +30 -10
  103. package/dist/components/tracks/useTrackHover.js.map +1 -1
  104. package/dist/components/tree/ScaleBarSVG.d.ts +14 -0
  105. package/dist/components/tree/ScaleBarSVG.js +11 -0
  106. package/dist/components/tree/ScaleBarSVG.js.map +1 -0
  107. package/dist/components/tree/TreeCanvas.js +8 -26
  108. package/dist/components/tree/TreeCanvas.js.map +1 -1
  109. package/dist/components/tree/TreeCanvasBlock.js +11 -20
  110. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  111. package/dist/components/tree/{TreeBranchMenu.d.ts → TreeMenu.d.ts} +5 -4
  112. package/dist/components/tree/TreeMenu.js +48 -0
  113. package/dist/components/tree/TreeMenu.js.map +1 -0
  114. package/dist/components/tree/TreeRuler.js +3 -3
  115. package/dist/components/tree/TreeRuler.js.map +1 -1
  116. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +9 -8
  117. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  118. package/dist/components/tree/renderTreeCanvas.js +19 -9
  119. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  120. package/dist/components/tree/renderTreeMouseover.d.ts +5 -0
  121. package/dist/components/tree/renderTreeMouseover.js +20 -0
  122. package/dist/components/tree/renderTreeMouseover.js.map +1 -0
  123. package/dist/components/tree/useTreeHover.d.ts +3 -8
  124. package/dist/components/tree/useTreeHover.js +8 -10
  125. package/dist/components/tree/useTreeHover.js.map +1 -1
  126. package/dist/components/useHoverAnchor.d.ts +19 -0
  127. package/dist/components/useHoverAnchor.js +18 -0
  128. package/dist/components/useHoverAnchor.js.map +1 -0
  129. package/dist/constants.d.ts +3 -0
  130. package/dist/constants.js +12 -3
  131. package/dist/constants.js.map +1 -1
  132. package/dist/element.d.ts +7 -3
  133. package/dist/element.js +32 -4
  134. package/dist/element.js.map +1 -1
  135. package/dist/expandSpec.js +3 -3
  136. package/dist/expandSpec.js.map +1 -1
  137. package/dist/exportFileName.d.ts +3 -3
  138. package/dist/exportFileName.js +4 -4
  139. package/dist/exportFileName.js.map +1 -1
  140. package/dist/featuresLayer.d.ts +8 -0
  141. package/dist/featuresLayer.js +42 -0
  142. package/dist/featuresLayer.js.map +1 -0
  143. package/dist/hierarchy.d.ts +51 -0
  144. package/dist/hierarchy.js +125 -0
  145. package/dist/hierarchy.js.map +1 -1
  146. package/dist/index.d.ts +2 -2
  147. package/dist/model/DataModel.d.ts +1 -1
  148. package/dist/model/DataModel.js +3 -3
  149. package/dist/model/DataModel.js.map +1 -1
  150. package/dist/model/msaModel.d.ts +14 -1
  151. package/dist/model/msaModel.js +17 -1
  152. package/dist/model/msaModel.js.map +1 -1
  153. package/dist/model/treeModel.d.ts +12 -0
  154. package/dist/model/treeModel.js +14 -1
  155. package/dist/model/treeModel.js.map +1 -1
  156. package/dist/model.d.ts +285 -53
  157. package/dist/model.js +693 -231
  158. package/dist/model.js.map +1 -1
  159. package/dist/renderToSvg.js +2 -4
  160. package/dist/renderToSvg.js.map +1 -1
  161. package/dist/rerootTree.d.ts +33 -0
  162. package/dist/rerootTree.js +144 -0
  163. package/dist/rerootTree.js.map +1 -0
  164. package/dist/svgTestUtil.d.ts +172 -26
  165. package/dist/types.d.ts +75 -12
  166. package/dist/useMsaSvgFigure.d.ts +2 -1
  167. package/dist/useMsaSvgFigure.js +11 -4
  168. package/dist/useMsaSvgFigure.js.map +1 -1
  169. package/dist/version.d.ts +1 -1
  170. package/dist/version.js +1 -1
  171. package/dist/writeNewick.d.ts +8 -0
  172. package/dist/writeNewick.js +34 -0
  173. package/dist/writeNewick.js.map +1 -0
  174. package/package.json +3 -3
  175. package/src/blockFasta.ts +18 -0
  176. package/src/colorSchemes.ts +20 -0
  177. package/src/columnCounts.ts +14 -6
  178. package/src/components/CopyButton.tsx +2 -2
  179. package/src/components/DragHandle.tsx +21 -1
  180. package/src/components/Loading.tsx +14 -30
  181. package/src/components/MSAView.tsx +16 -9
  182. package/src/components/MSAViewer.tsx +121 -26
  183. package/src/components/MetadataTable.tsx +55 -0
  184. package/src/components/SequenceTextArea.tsx +54 -8
  185. package/src/components/StatusMessage.tsx +70 -0
  186. package/src/components/Track.tsx +21 -4
  187. package/src/components/VerticalScrollbar.tsx +7 -0
  188. package/src/components/dialogs/ExportSVGDialog.tsx +5 -2
  189. package/src/components/dialogs/FeatureDialog.tsx +59 -38
  190. package/src/components/dialogs/MetadataDialog.tsx +9 -8
  191. package/src/components/header/ColorSchemeMenu.tsx +31 -12
  192. package/src/components/header/FileMenu.tsx +9 -0
  193. package/src/components/header/GappynessSlider.tsx +17 -4
  194. package/src/components/header/GoToBox.tsx +50 -0
  195. package/src/components/header/Header.tsx +11 -8
  196. package/src/components/header/SelectionMenu.tsx +117 -0
  197. package/src/components/header/goTo.ts +75 -0
  198. package/src/components/header/settingsMenuItems.ts +66 -0
  199. package/src/components/import/ImportForm.tsx +14 -7
  200. package/src/components/import/ImportFormExamples.tsx +20 -28
  201. package/src/components/minimap/Minimap.tsx +7 -0
  202. package/src/components/msa/AnnotationLegend.tsx +12 -6
  203. package/src/components/msa/MSACanvas.tsx +19 -1
  204. package/src/components/msa/MSACanvasBlock.tsx +9 -11
  205. package/src/components/msa/msaKeys.ts +45 -0
  206. package/src/components/msa/msaRaster.ts +1 -0
  207. package/src/components/msa/renderHighlights.ts +46 -4
  208. package/src/components/msa/tileColor.ts +9 -8
  209. package/src/components/msa/useMsaBlockMouse.ts +44 -31
  210. package/src/components/overlayColors.ts +4 -0
  211. package/src/components/rowpanels/RowPanelBlock.tsx +16 -23
  212. package/src/components/selectionDrag.ts +42 -0
  213. package/src/components/tracks/drawTracks.ts +15 -2
  214. package/src/components/tracks/useTrackHover.ts +32 -10
  215. package/src/components/tree/ScaleBarSVG.tsx +30 -0
  216. package/src/components/tree/TreeCanvas.tsx +9 -40
  217. package/src/components/tree/TreeCanvasBlock.tsx +19 -39
  218. package/src/components/tree/TreeMenu.tsx +129 -0
  219. package/src/components/tree/TreeRuler.tsx +7 -13
  220. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +14 -16
  221. package/src/components/tree/renderTreeCanvas.ts +20 -8
  222. package/src/components/tree/renderTreeMouseover.ts +41 -0
  223. package/src/components/tree/useTreeHover.ts +8 -17
  224. package/src/components/useHoverAnchor.ts +31 -0
  225. package/src/constants.ts +14 -3
  226. package/src/element.ts +45 -5
  227. package/src/expandSpec.ts +3 -3
  228. package/src/exportFileName.ts +4 -3
  229. package/src/featuresLayer.ts +48 -0
  230. package/src/hierarchy.ts +151 -4
  231. package/src/index.ts +8 -1
  232. package/src/model/DataModel.ts +3 -3
  233. package/src/model/msaModel.ts +19 -1
  234. package/src/model/treeModel.ts +22 -0
  235. package/src/model.ts +841 -272
  236. package/src/renderToSvg.tsx +6 -9
  237. package/src/rerootTree.ts +172 -0
  238. package/src/types.ts +92 -12
  239. package/src/useMsaSvgFigure.ts +11 -4
  240. package/src/version.ts +1 -1
  241. package/src/writeNewick.ts +38 -0
  242. package/dist/components/PortalTooltip.d.ts +0 -11
  243. package/dist/components/PortalTooltip.js +0 -29
  244. package/dist/components/PortalTooltip.js.map +0 -1
  245. package/dist/components/header/HeaderStatusArea.js +0 -21
  246. package/dist/components/header/HeaderStatusArea.js.map +0 -1
  247. package/dist/components/tree/TreeBranchMenu.js +0 -29
  248. package/dist/components/tree/TreeBranchMenu.js.map +0 -1
  249. package/dist/components/tree/TreeNodeMenu.d.ts +0 -13
  250. package/dist/components/tree/TreeNodeMenu.js +0 -41
  251. package/dist/components/tree/TreeNodeMenu.js.map +0 -1
  252. package/src/components/PortalTooltip.tsx +0 -47
  253. package/src/components/header/HeaderStatusArea.tsx +0 -32
  254. package/src/components/tree/TreeBranchMenu.tsx +0 -68
  255. package/src/components/tree/TreeNodeMenu.tsx +0 -95
package/dist/types.d.ts CHANGED
@@ -12,6 +12,12 @@ export type TrackKind = 'text' | 'bar' | 'logo' | 'arc' | 'ruler';
12
12
  * graphics draws between a shape's interior and its own ink.
13
13
  */
14
14
  export type ResidueEncoding = 'fill' | 'color';
15
+ /**
16
+ * The residue colors: a scheme name from the built-in table, or a scale's
17
+ * `{map}` of letter to color, which leaves every letter it does not list
18
+ * uncolored
19
+ */
20
+ export type ColorScheme = string | Required<Pick<ScaleSpec, 'map'>>;
15
21
  export interface BasicTrackModel {
16
22
  id: string;
17
23
  name: string;
@@ -266,13 +272,49 @@ export interface Highlight {
266
272
  label?: string;
267
273
  color?: string;
268
274
  }
275
+ /**
276
+ * A feature on one row as data, the JSON form of a GFF line: `start` and `end`
277
+ * are residues of `row`, 1-based and inclusive. `name` is the key the legend,
278
+ * the palette and the filter dialog group features by, `color` overrides the
279
+ * palette and any scale, and a `strand` draws the span as an arrow. Every other
280
+ * field is a column the `featureFill` and `featureLabel` encodings can read.
281
+ */
282
+ export interface Feature {
283
+ row: string;
284
+ start: number;
285
+ end: number;
286
+ name?: string;
287
+ description?: string;
288
+ /** a GFF feature type; `exon`, `CDS` and the UTRs draw as numbered segments */
289
+ type?: string;
290
+ strand?: '+' | '-';
291
+ color?: string;
292
+ [field: string]: string | number | undefined;
293
+ }
269
294
  /**
270
295
  * What the viewer draws over a clade. `highlight` fills the rows behind it,
271
296
  * across the tree and the alignment. `bracket` draws a bar and the clade's
272
- * `label` in the gutter right of the tip labels. `collapse` and `focus` seed
273
- * the collapsed list and the subtree in focus at load.
297
+ * `label` in the gutter right of the tip labels. `collapse`, `focus` and
298
+ * `rotate` seed the collapsed list, the subtree in focus and the rotated nodes
299
+ * at load.
300
+ */
301
+ export type CladeMark = 'highlight' | 'bracket' | 'collapse' | 'focus' | 'rotate';
302
+ /**
303
+ * The order each node's children draw in, top to bottom. `branchLength` puts
304
+ * the shortest branch first, `input` keeps the file's order, `ladderize` puts
305
+ * the clade with the fewest tips first, so the deep clades step down toward
306
+ * the bottom, and `ladderizeReverse` puts the one with the most tips first.
274
307
  */
275
- export type CladeMark = 'highlight' | 'bracket' | 'collapse' | 'focus';
308
+ export type TreeOrder = 'branchLength' | 'input' | 'ladderize' | 'ladderizeReverse';
309
+ /**
310
+ * Where the tree is rooted: `midpoint` halfway along the longest path between
311
+ * two tips, and `{outgroup}` on the branch above those tips' most recent common
312
+ * ancestor, or above the rest of the tips' where the outgroup straddles the
313
+ * root the file gives.
314
+ */
315
+ export type TreeRoot = 'midpoint' | {
316
+ outgroup: string[];
317
+ };
276
318
  /**
277
319
  * A clade of the tree and the mark drawn over it. `mrca` names tips whose most
278
320
  * recent common ancestor is the clade; `range` names the first and last tip of
@@ -313,11 +355,31 @@ export interface Region {
313
355
  start: number;
314
356
  end: number;
315
357
  }
358
+ /**
359
+ * A block of the alignment the reader selected: columns `start` to `end` of the
360
+ * file, 1-based and inclusive like a Highlight without a row, across the rows
361
+ * `rows` names, or across every row where `rows` is absent.
362
+ */
363
+ export interface MsaSelection {
364
+ start: number;
365
+ end: number;
366
+ rows?: string[];
367
+ }
368
+ /**
369
+ * An MsaSelection resolved to what is on screen: the visible columns it covers
370
+ * and its rows as runs of consecutive row indices, top to bottom
371
+ */
372
+ export interface ResolvedSelection {
373
+ startCol: number;
374
+ endCol: number;
375
+ rowRuns: [number, number][];
376
+ }
316
377
  /**
317
378
  * A cell of the alignment in the host's coordinates, 1-based like Highlight:
318
379
  * `column` counts every column of the file, hidden gappy ones included, and
319
- * `residue` counts the row's own letters, absent on a gap. A pointer over a
320
- * track has a column and no row.
380
+ * `residue` counts the row's own letters, absent on a gap. `letter` is the
381
+ * character in the cell, the gap character (`-` or `.`) on a gap. A pointer
382
+ * over a track has a column and no row.
321
383
  */
322
384
  export interface Cell {
323
385
  column: number;
@@ -330,6 +392,12 @@ export interface Viewport {
330
392
  startColumn: number;
331
393
  endColumn: number;
332
394
  }
395
+ /**
396
+ * A row table as a host passes it: fields per row name. A number or a boolean
397
+ * reads as its string, the way a GFF value does, and the viewer leaves out any
398
+ * other value. See docs/layers.md
399
+ */
400
+ export type RowDataInput = Record<string, Record<string, string | number | boolean | null>>;
333
401
  /**
334
402
  * A channel of a mark the viewer always draws. `tipLabel` colors each tip label
335
403
  * in the tree, `rowTint` washes the row across the tree gutter and the
@@ -369,10 +437,5 @@ export interface NodeWithIds {
369
437
  length?: number;
370
438
  noTree?: boolean;
371
439
  }
372
- export interface NodeWithIdsAndLength {
373
- id: string;
374
- name: string;
375
- children: NodeWithIdsAndLength[];
376
- noTree?: boolean;
377
- length: number;
378
- }
440
+ /** @deprecated the same type as `NodeWithIds` */
441
+ export type NodeWithIdsAndLength = NodeWithIds;
@@ -8,7 +8,8 @@ export interface MsaSvgFigure {
8
8
  /**
9
9
  * The viewer's SVG export of its viewport, as markup for the page to insert.
10
10
  * It redraws after the view settles: `delay` ms after the last scroll, zoom,
11
- * resize or change to the snapshot. A draw a later one overtakes is dropped.
11
+ * resize, highlight or change to the snapshot. A draw a later one overtakes is
12
+ * dropped.
12
13
  *
13
14
  * The figure's row names and letters are SVG text, so the browser's find
14
15
  * locates them, a reader can select them, and print stylesheets apply.
@@ -1,12 +1,13 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { createJBrowseTheme } from '@jbrowse/core/ui/theme';
3
3
  import { getSnapshot } from '@jbrowse/mobx-state-tree';
4
- import { compareStructural, reaction } from 'mobx';
4
+ import { compareShallow, reaction } from 'mobx';
5
5
  import { renderToSvg } from './renderToSvg.js';
6
6
  /**
7
7
  * The viewer's SVG export of its viewport, as markup for the page to insert.
8
8
  * It redraws after the view settles: `delay` ms after the last scroll, zoom,
9
- * resize or change to the snapshot. A draw a later one overtakes is dropped.
9
+ * resize, highlight or change to the snapshot. A draw a later one overtakes is
10
+ * dropped.
10
11
  *
11
12
  * The figure's row names and letters are SVG text, so the browser's find
12
13
  * locates them, a reader can select them, and print stylesheets apply.
@@ -16,7 +17,13 @@ export function useMsaSvgFigure(model, { theme, includeTracks = false, delay = 3
16
17
  useEffect(() => {
17
18
  let latest = 0;
18
19
  const dispose = reaction(() => model.viewInitialized && model.dataInitialized
19
- ? [getSnapshot(model), model.width, model.height]
20
+ ? [
21
+ getSnapshot(model),
22
+ model.width,
23
+ model.height,
24
+ model.resolvedHighlights,
25
+ model.highlightedColumnRuns,
26
+ ]
20
27
  : undefined, key => {
21
28
  if (!key) {
22
29
  return;
@@ -35,7 +42,7 @@ export function useMsaSvgFigure(model, { theme, includeTracks = false, delay = 3
35
42
  setFigure({ error });
36
43
  }
37
44
  });
38
- }, { delay, fireImmediately: true, equals: compareStructural });
45
+ }, { delay, fireImmediately: true, equals: compareShallow });
39
46
  return () => {
40
47
  latest = -1;
41
48
  dispose();
@@ -1 +1 @@
1
- {"version":3,"file":"useMsaSvgFigure.js","sourceRoot":"","sources":["../src/useMsaSvgFigure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAmB,CAAA;AAW/C;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAmB,EACnB,EACE,KAAK,EACL,aAAa,GAAG,KAAK,EACrB,KAAK,GAAG,GAAG,GACZ,GAA+D,EAAE;IAElE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAe,EAAE,CAAC,CAAA;IACtD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,MAAM,OAAO,GAAG,QAAQ,CACtB,GAAG,EAAE,CACH,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,eAAe;YAC5C,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC;YACjD,CAAC,CAAC,SAAS,EACf,GAAG,CAAC,EAAE;YACJ,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAM;YACR,CAAC;YACD,MAAM,IAAI,GAAG,EAAE,MAAM,CAAA;YACrB,WAAW,CAAC,KAAK,EAAE;gBACjB,KAAK,EAAE,KAAK,IAAI,kBAAkB,EAAE;gBACpC,UAAU,EAAE,UAAU;gBACtB,aAAa;aACd,CAAC,CAAC,IAAI,CACL,GAAG,CAAC,EAAE;gBACJ,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACpB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;gBACpB,CAAC;YACH,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;gBACjB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACpB,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;gBACtB,CAAC;YACH,CAAC,CACF,CAAA;QACH,CAAC,EACD,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAC5D,CAAA;QACD,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,CAAC,CAAC,CAAA;YACX,OAAO,EAAE,CAAA;QACX,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;IACxC,OAAO,MAAM,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"useMsaSvgFigure.js","sourceRoot":"","sources":["../src/useMsaSvgFigure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAmB,CAAA;AAW/C;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAmB,EACnB,EACE,KAAK,EACL,aAAa,GAAG,KAAK,EACrB,KAAK,GAAG,GAAG,GACZ,GAA+D,EAAE;IAElE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAe,EAAE,CAAC,CAAA;IACtD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,MAAM,OAAO,GAAG,QAAQ,CACtB,GAAG,EAAE,CACH,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,eAAe;YAC5C,CAAC,CAAC;gBACE,WAAW,CAAC,KAAK,CAAC;gBAClB,KAAK,CAAC,KAAK;gBACX,KAAK,CAAC,MAAM;gBACZ,KAAK,CAAC,kBAAkB;gBACxB,KAAK,CAAC,qBAAqB;aAC5B;YACH,CAAC,CAAC,SAAS,EACf,GAAG,CAAC,EAAE;YACJ,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAM;YACR,CAAC;YACD,MAAM,IAAI,GAAG,EAAE,MAAM,CAAA;YACrB,WAAW,CAAC,KAAK,EAAE;gBACjB,KAAK,EAAE,KAAK,IAAI,kBAAkB,EAAE;gBACpC,UAAU,EAAE,UAAU;gBACtB,aAAa;aACd,CAAC,CAAC,IAAI,CACL,GAAG,CAAC,EAAE;gBACJ,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACpB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;gBACpB,CAAC;YACH,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;gBACjB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBACpB,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;gBACtB,CAAC;YACH,CAAC,CACF,CAAA;QACH,CAAC,EACD,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CACzD,CAAA;QACD,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,CAAC,CAAC,CAAA;YACX,OAAO,EAAE,CAAA;QACX,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;IACxC,OAAO,MAAM,CAAA;AACf,CAAC"}
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "8.2.0";
1
+ export declare const version = "8.4.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '8.2.0';
1
+ export const version = '8.4.0';
2
2
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,8 @@
1
+ import type { HierarchyNode } from './hierarchy.ts';
2
+ /**
3
+ * The tree as Newick, in the order it is drawn. An internal node whose name is
4
+ * only its generated id goes unnamed. Each subtree's string is dropped once its
5
+ * parent takes it in, since a caterpillar tree is as deep as it has tips and
6
+ * keeping every one is quadratic.
7
+ */
8
+ export declare function writeNewick(root: HierarchyNode): string;
@@ -0,0 +1,34 @@
1
+ import { descendants } from './hierarchy.js';
2
+ // a name holding any of these reads back as grammar unless it is quoted
3
+ const needsQuotes = /[\s(),:;[\]']/;
4
+ function quote(name) {
5
+ return needsQuotes.test(name) ? `'${name.replaceAll("'", "''")}'` : name;
6
+ }
7
+ /**
8
+ * The tree as Newick, in the order it is drawn. An internal node whose name is
9
+ * only its generated id goes unnamed. Each subtree's string is dropped once its
10
+ * parent takes it in, since a caterpillar tree is as deep as it has tips and
11
+ * keeping every one is quadratic.
12
+ */
13
+ export function writeNewick(root) {
14
+ const written = new Map();
15
+ for (const node of descendants(root).reverse()) {
16
+ const { id, name, length } = node.data;
17
+ let text = '';
18
+ if (node.children) {
19
+ text = `(${node.children.map(c => written.get(c)).join(',')})`;
20
+ for (const child of node.children) {
21
+ written.delete(child);
22
+ }
23
+ }
24
+ if (!(node.children && name === id)) {
25
+ text += quote(name);
26
+ }
27
+ if (length !== undefined) {
28
+ text += `:${length}`;
29
+ }
30
+ written.set(node, text);
31
+ }
32
+ return `${written.get(root)};`;
33
+ }
34
+ //# sourceMappingURL=writeNewick.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeNewick.js","sourceRoot":"","sources":["../src/writeNewick.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAI5C,wEAAwE;AACxE,MAAM,WAAW,GAAG,eAAe,CAAA;AAEnC,SAAS,KAAK,CAAC,IAAY;IACzB,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAmB;IAC7C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAA;IAChD,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/C,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAA;QACtC,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;YAC/D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC;YACpC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,CAAA;QACrB,CAAC;QACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,IAAI,IAAI,IAAI,MAAM,EAAE,CAAA;QACtB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACzB,CAAC;IACD,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAE,GAAG,CAAA;AACjC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-msaview",
3
3
  "author": "Colin",
4
- "version": "8.2.0",
4
+ "version": "8.4.0",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "repository": {
@@ -50,10 +50,10 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@gmod/newick": "^1.0.3",
53
- "@jbrowse/svgcanvas": "8.2.0",
53
+ "@jbrowse/svgcanvas": "8.4.0",
54
54
  "colord": "^2.10.0",
55
55
  "flatbush": "^4.6.2",
56
- "msa-parsers": "8.2.0"
56
+ "msa-parsers": "8.4.0"
57
57
  },
58
58
  "scripts": {
59
59
  "clean": "rimraf dist bundle",
@@ -0,0 +1,18 @@
1
+ /**
2
+ * FASTA for a block of the alignment: each row's letters from column `start`
3
+ * to `end`, 1-based and inclusive, under its name. Gaps stay in, and a row that
4
+ * ends before `end` is padded with gaps, so every record is the block's width.
5
+ */
6
+ export function blockFasta(
7
+ rows: readonly (readonly [string, string])[],
8
+ start: number,
9
+ end: number,
10
+ ) {
11
+ const width = Math.max(0, end - start + 1)
12
+ return rows
13
+ .map(
14
+ ([name, seq]) =>
15
+ `>${name}\n${seq.slice(start - 1, end).padEnd(width, '-')}\n`,
16
+ )
17
+ .join('')
18
+ }
@@ -359,3 +359,23 @@ export default transform(colorSchemes, ([key, val]) => [
359
359
  key,
360
360
  transform(val, ([letter, color]) => [letter, colord(color).toHex()]),
361
361
  ])
362
+
363
+ const letterTables = new WeakMap<
364
+ Record<string, string>,
365
+ Record<string, string>
366
+ >()
367
+
368
+ /**
369
+ * A letter map as the renderer looks letters up. The model upper-cases every
370
+ * row before coloring it, so a table keys its letters in upper case and a map
371
+ * may key them in either. The table is cached per map, so its identity is
372
+ * stable enough for the raster cache to key on.
373
+ */
374
+ export function letterColorTable(map: Record<string, string>) {
375
+ let table = letterTables.get(map)
376
+ if (!table) {
377
+ table = transform(map, ([letter, color]) => [letter.toUpperCase(), color])
378
+ letterTables.set(map, table)
379
+ }
380
+ return table
381
+ }
@@ -63,15 +63,21 @@ export class ColumnCounts {
63
63
  this.totals = new Uint32Array(numColumns)
64
64
  }
65
65
 
66
+ /** tallies a row, counting the columns past a short row's end as gaps */
66
67
  addRow(row: string) {
67
- const { counts, totals, slotTotals } = this
68
- const n = Math.min(row.length, this.numColumns)
68
+ const { counts, totals, slotTotals, numColumns } = this
69
+ const n = Math.min(row.length, numColumns)
69
70
  for (let col = 0; col < n; col++) {
70
71
  const slot = slotOf(row.charCodeAt(col))
71
72
  counts[col * SLOTS + slot]!++
72
73
  totals[col]!++
73
74
  slotTotals[slot]!++
74
75
  }
76
+ for (let col = n; col < numColumns; col++) {
77
+ counts[col * SLOTS + DASH]!++
78
+ totals[col]!++
79
+ }
80
+ slotTotals[DASH]! += numColumns - n
75
81
  }
76
82
 
77
83
  add(col: number, letter: string) {
@@ -161,11 +167,13 @@ export const numResidueSlots = SLOTS
161
167
 
162
168
  /**
163
169
  * Tally counts from alignment rows (gapped sequence strings, one per row). The
164
- * column count follows the longest row, matching what a per-column tally of
165
- * ragged input produces.
170
+ * column count defaults to the longest row, and a shorter row counts as gapped
171
+ * to the end.
166
172
  */
167
- export function columnCountsFromRows(rows: string[]) {
168
- const numColumns = rows.reduce((max, row) => Math.max(max, row.length), 0)
173
+ export function columnCountsFromRows(
174
+ rows: string[],
175
+ numColumns = rows.reduce((max, row) => Math.max(max, row.length), 0),
176
+ ) {
169
177
  const counts = new ColumnCounts(numColumns)
170
178
  for (const row of rows) {
171
179
  counts.addRow(row)
@@ -12,7 +12,7 @@ export default function CopyButton({
12
12
  text,
13
13
  label = 'Copy to clipboard',
14
14
  }: {
15
- text: string
15
+ text: string | (() => string)
16
16
  label?: string
17
17
  }) {
18
18
  const [copied, setCopied] = useState(false)
@@ -30,7 +30,7 @@ export default function CopyButton({
30
30
  variant="contained"
31
31
  color="primary"
32
32
  onClick={() => {
33
- copy(text)
33
+ copy(typeof text === 'string' ? text : text())
34
34
  setCopied(true)
35
35
  clearTimeout(timer.current)
36
36
  timer.current = setTimeout(() => {
@@ -8,6 +8,10 @@ import type { Theme } from '@mui/material'
8
8
 
9
9
  export const scrollbarThumbFill = 'rgba(66,119,127,0.3)'
10
10
 
11
+ // the model's resizeHandleWidth feeds the layout, so a divider keeps its drawn
12
+ // width and widens its target with this much invisible margin each side
13
+ const hitSlop = 4
14
+
11
15
  // The scroll thumbs (minimap, vertical scrollbar) and the resize dividers are
12
16
  // one widget -- press, drag along an axis, report the pixel delta -- differing
13
17
  // only in the affordance they present, so the variant carries the whole
@@ -71,6 +75,10 @@ export default function DragHandle({
71
75
 
72
76
  return (
73
77
  <div
78
+ {...(variant === 'resizer' && {
79
+ role: 'separator',
80
+ 'aria-orientation': axis === 'x' ? 'vertical' : 'horizontal',
81
+ })}
74
82
  onMouseEnter={() => {
75
83
  setHovered(true)
76
84
  }}
@@ -87,6 +95,18 @@ export default function DragHandle({
87
95
  background: hovered ? fillHovered(theme) : fill(theme),
88
96
  ...style,
89
97
  }}
90
- />
98
+ >
99
+ {variant === 'resizer' ? (
100
+ <div
101
+ style={{
102
+ position: 'absolute',
103
+ zIndex: 101,
104
+ ...(axis === 'x'
105
+ ? { top: 0, bottom: 0, left: -hitSlop, right: -hitSlop }
106
+ : { left: 0, right: 0, top: -hitSlop, bottom: -hitSlop }),
107
+ }}
108
+ />
109
+ ) : null}
110
+ </div>
91
111
  )
92
112
  }
@@ -2,35 +2,15 @@ import React from 'react'
2
2
 
3
3
  import { ErrorMessage } from '@jbrowse/core/ui'
4
4
  import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary'
5
- import { Button, CircularProgress, Typography } from '@mui/material'
5
+ import { Button } from '@mui/material'
6
6
  import { observer } from 'mobx-react'
7
7
 
8
8
  import MSAView from './MSAView.tsx'
9
+ import StatusMessage from './StatusMessage.tsx'
9
10
  import ImportForm from './import/ImportForm.tsx'
10
11
 
11
12
  import type { MsaViewModel } from '../model.ts'
12
13
 
13
- const LoadingSpinner = observer(function ({ model }: { model: MsaViewModel }) {
14
- const { status } = model
15
- return (
16
- <div
17
- style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 20 }}
18
- >
19
- <CircularProgress size={24} />
20
- <Typography variant="h6">{status?.msg ?? 'Loading...'}</Typography>
21
- {status?.onCancel ? (
22
- <Button
23
- variant="outlined"
24
- size="small"
25
- onClick={() => status.onCancel?.()}
26
- >
27
- Cancel
28
- </Button>
29
- ) : null}
30
- </div>
31
- )
32
- })
33
-
34
14
  const Reset = observer(function ({
35
15
  model,
36
16
  error,
@@ -58,12 +38,15 @@ const Loading = observer(function ({ model }: { model: MsaViewModel }) {
58
38
  const {
59
39
  isLoading,
60
40
  dataInitialized,
41
+ data,
61
42
  error,
62
43
  resetCount,
63
44
  msaFilehandle,
64
45
  treeFilehandle,
65
46
  } = model
66
- const hasPendingFilehandle = !!(msaFilehandle || treeFilehandle)
47
+ const hasData = !!(data.msa || data.tree)
48
+ // a failed load keeps its filehandle, so the error outranks the spinner
49
+ const loading = !error && !!(msaFilehandle || treeFilehandle || isLoading)
67
50
 
68
51
  return (
69
52
  <div>
@@ -76,18 +59,19 @@ const Loading = observer(function ({ model }: { model: MsaViewModel }) {
76
59
  >
77
60
  {dataInitialized ? (
78
61
  isLoading ? (
79
- <LoadingSpinner model={model} />
62
+ <StatusMessage model={model} variant="page" />
80
63
  ) : (
81
64
  <MSAView model={model} />
82
65
  )
83
- ) : error ? (
84
- // checked before the spinner: a failed load keeps its filehandle
85
- // (only an abort clears it), so hasPendingFilehandle stays true
66
+ ) : hasData ? (
86
67
  <Reset model={model} error={error} />
87
- ) : hasPendingFilehandle || isLoading ? (
88
- <LoadingSpinner model={model} />
89
68
  ) : (
90
- <ImportForm model={model} />
69
+ <>
70
+ {loading ? <StatusMessage model={model} variant="page" /> : null}
71
+ <div style={{ display: loading ? 'none' : undefined }}>
72
+ <ImportForm model={model} />
73
+ </div>
74
+ </>
91
75
  )}
92
76
  </ErrorBoundary>
93
77
  </div>
@@ -11,7 +11,7 @@ import VerticalScrollbar from './VerticalScrollbar.tsx'
11
11
  import Header from './header/Header.tsx'
12
12
  import Minimap from './minimap/Minimap.tsx'
13
13
  import MSAPanel from './msa/MSAPanel.tsx'
14
- import { clickColor, hoverColor } from './overlayColors.ts'
14
+ import { clickColor, hoverColor, selectionFill } from './overlayColors.ts'
15
15
  import RowPanelHeaders from './rowpanels/RowPanelHeaders.tsx'
16
16
  import RowPanels from './rowpanels/RowPanels.tsx'
17
17
  import TreeOverview from './tree/TreeOverview.tsx'
@@ -53,12 +53,19 @@ const TrackColumnIndicator = observer(function ({
53
53
  resizeHandleWidth,
54
54
  totalTrackAreaHeight,
55
55
  msaCanvasWidth,
56
+ resolvedSelection,
56
57
  } = model
57
58
 
58
- // hovered column, then the pinned one, matching the alignment's own overlay
59
+ // the selected columns, the hovered column, then the pinned one, matching
60
+ // the alignment's own overlay
59
61
  const bands = [
60
- { col: mouseCol, color: hoverColor },
61
- { col: mouseClickCol, color: clickColor },
62
+ {
63
+ start: resolvedSelection?.startCol,
64
+ end: resolvedSelection?.endCol,
65
+ color: selectionFill,
66
+ },
67
+ { start: mouseCol, end: mouseCol, color: hoverColor },
68
+ { start: mouseClickCol, end: mouseClickCol, color: clickColor },
62
69
  ]
63
70
 
64
71
  return (
@@ -73,15 +80,15 @@ const TrackColumnIndicator = observer(function ({
73
80
  pointerEvents: 'none',
74
81
  }}
75
82
  >
76
- {bands.map(({ col, color }) =>
77
- col === undefined ? null : (
83
+ {bands.map(({ start, end, color }) =>
84
+ start === undefined || end === undefined ? null : (
78
85
  <div
79
86
  key={color}
80
87
  style={{
81
88
  position: 'absolute',
82
- left: col * colWidth + scrollX,
89
+ left: start * colWidth + scrollX,
83
90
  top: 0,
84
- width: colWidth,
91
+ width: (end - start + 1) * colWidth,
85
92
  height: totalTrackAreaHeight,
86
93
  backgroundColor: color,
87
94
  zIndex: 100,
@@ -140,7 +147,7 @@ const MSAView = observer(function ({ model }: { model: MsaViewModel }) {
140
147
  <div>
141
148
  {viewInitialized ? (
142
149
  <>
143
- <div style={{ height, overflow: 'hidden' }}>
150
+ <div style={{ height, overflow: 'clip' }}>
144
151
  {hideHeader ? null : <Header model={model} />}
145
152
  <View model={model} />
146
153
  </div>