react-msaview 6.1.1 → 6.2.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 (47) hide show
  1. package/bundle/index.js +92 -92
  2. package/bundle/index.js.map +4 -4
  3. package/dist/components/dialogs/ExportSVGDialog.js +48 -20
  4. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  5. package/dist/components/getVisibleLeaves.d.ts +8 -0
  6. package/dist/components/getVisibleLeaves.js +13 -6
  7. package/dist/components/getVisibleLeaves.js.map +1 -1
  8. package/dist/components/minimap/Minimap.js +1 -1
  9. package/dist/components/minimap/Minimap.js.map +1 -1
  10. package/dist/components/minimap/MinimapSVG.d.ts +4 -4
  11. package/dist/components/minimap/MinimapSVG.js +16 -10
  12. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  13. package/dist/components/minimap/minimapLayout.d.ts +5 -8
  14. package/dist/components/minimap/minimapLayout.js +10 -11
  15. package/dist/components/minimap/minimapLayout.js.map +1 -1
  16. package/dist/components/msa/msaRaster.d.ts +32 -2
  17. package/dist/components/msa/msaRaster.js +93 -15
  18. package/dist/components/msa/msaRaster.js.map +1 -1
  19. package/dist/{renderTestEnv.d.ts → headlessRenderEnv.d.ts} +15 -13
  20. package/dist/{renderTestEnv.js → headlessRenderEnv.js} +22 -18
  21. package/dist/headlessRenderEnv.js.map +1 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +3 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/renderToSvg.js +115 -36
  26. package/dist/renderToSvg.js.map +1 -1
  27. package/dist/version.d.ts +1 -1
  28. package/dist/version.js +1 -1
  29. package/package.json +3 -3
  30. package/src/components/dialogs/ExportSVGDialog.tsx +55 -20
  31. package/src/components/getVisibleLeaves.ts +25 -9
  32. package/src/components/minimap/Minimap.tsx +1 -4
  33. package/src/components/minimap/MinimapSVG.tsx +50 -35
  34. package/src/components/minimap/minimapLayout.test.ts +16 -5
  35. package/src/components/minimap/minimapLayout.ts +10 -11
  36. package/src/components/msa/msaRaster.ts +124 -17
  37. package/src/{renderTestEnv.ts → headlessRenderEnv.ts} +23 -19
  38. package/src/impgRender.test.tsx +2 -2
  39. package/src/index.ts +6 -0
  40. package/src/renderDomainsSvg.test.tsx +2 -2
  41. package/src/renderMinimapSvg.test.tsx +29 -15
  42. package/src/renderRasterSvg.test.tsx +216 -0
  43. package/src/renderSequenceLogoSvg.test.tsx +6 -4
  44. package/src/renderToSvg.test.tsx +79 -0
  45. package/src/renderToSvg.tsx +201 -65
  46. package/src/version.ts +1 -1
  47. package/dist/renderTestEnv.js.map +0 -1
@@ -4,9 +4,12 @@ import React from 'react'
4
4
  import { renderToStaticMarkup } from '@jbrowse/core/util'
5
5
  import { when } from 'mobx'
6
6
 
7
+ import { visibleRowRange } from './components/getVisibleLeaves.ts'
7
8
  import MinimapSVG from './components/minimap/MinimapSVG.tsx'
9
+ import { rasterImageHref, rasterSupported } from './components/msa/msaRaster.ts'
8
10
  import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock.ts'
9
11
  import { renderMSABlock } from './components/msa/renderMSABlock.ts'
12
+ import { visibleColRange } from './components/msa/visibleColRange.ts'
10
13
  import { renderAllTracks } from './components/tracks/drawTracks.ts'
11
14
  import { renderTreeCanvas } from './components/tree/renderTreeCanvas.ts'
12
15
  import { colorContrast } from './util.ts'
@@ -30,11 +33,16 @@ const LEGEND_SWATCH = 12
30
33
  const LEGEND_FONT = 12
31
34
  const LEGEND_CHAR_W = 7
32
35
 
36
+ const LEGEND_MAX_W = 360
37
+ const LEGEND_TEXT_X = LEGEND_PAD + LEGEND_SWATCH + 6
38
+
33
39
  function getLegendWidth(model: MsaViewModel) {
34
- const names = model.visibleDomainTypes.map(d => d.name)
35
- const maxLen = Math.max(0, ...names.map(n => n.length))
36
- const w = LEGEND_PAD * 2 + LEGEND_SWATCH + 6 + maxLen * LEGEND_CHAR_W
37
- return Math.min(360, Math.max(120, w))
40
+ const maxLen = model.visibleDomainTypes.reduce(
41
+ (a, d) => Math.max(a, d.name.length),
42
+ 0,
43
+ )
44
+ const w = LEGEND_TEXT_X + LEGEND_PAD + maxLen * LEGEND_CHAR_W
45
+ return Math.min(LEGEND_MAX_W, Math.max(120, w))
38
46
  }
39
47
 
40
48
  // resolved sizes/offsets (in svg user units) for the chosen export, shared by
@@ -42,6 +50,7 @@ function getLegendWidth(model: MsaViewModel) {
42
50
  // mirrors the live scroll position
43
51
  interface Layout {
44
52
  width: number
53
+ msaAreaWidth: number
45
54
  height: number
46
55
  contentHeight: number
47
56
  trackHeight: number
@@ -53,8 +62,6 @@ interface Layout {
53
62
 
54
63
  function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
55
64
  const {
56
- width,
57
- height,
58
65
  scrollX,
59
66
  scrollY,
60
67
  totalWidth,
@@ -62,21 +69,33 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
62
69
  treeAreaWidth,
63
70
  totalTrackAreaHeight,
64
71
  minimapHeight,
72
+ msaAreaHeight,
73
+ msaCanvasWidth,
74
+ showHorizontalScrollbar,
65
75
  } = model
66
76
  const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0
67
77
  // the minimap reflects the live viewport scroll position, so it's only
68
- // meaningful for a viewport export, never for the entire alignment
69
- const includeMinimap = opts.exportType === 'viewport' && !!opts.includeMinimap
78
+ // meaningful for a viewport export, never for the entire alignment -- and
79
+ // only when the live view shows one at all, since a minimap over an alignment
80
+ // that already fits marks a viewport wider than the bar
81
+ const includeMinimap =
82
+ opts.exportType === 'viewport' &&
83
+ !!opts.includeMinimap &&
84
+ showHorizontalScrollbar
70
85
  const legendWidth =
71
86
  model.actuallyShowDomains && model.visibleDomainTypes.length > 0
72
87
  ? getLegendWidth(model)
73
88
  : 0
74
89
 
75
- // width stays content-only (the renderers derive msaAreaWidth from it); the
76
- // legend occupies an extra column added at the svg root in MsaSvg
90
+ // width stays content-only; the legend occupies an extra column added at the
91
+ // svg root in MsaSvg. The viewport export takes the alignment canvas's own
92
+ // size rather than the widget's -- the widget box also covers the header, the
93
+ // resize handle and the scrollbars, and rendering that box exports rows and
94
+ // columns the scrollbars hide on screen
77
95
  return opts.exportType === 'entire'
78
96
  ? {
79
97
  width: totalWidth + treeAreaWidth,
98
+ msaAreaWidth: totalWidth,
80
99
  height: totalHeight + trackHeight,
81
100
  contentHeight: totalHeight,
82
101
  trackHeight,
@@ -86,9 +105,11 @@ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
86
105
  legendWidth,
87
106
  }
88
107
  : {
89
- width,
90
- height: height + (includeMinimap ? minimapHeight : 0) + trackHeight,
91
- contentHeight: height,
108
+ width: msaCanvasWidth + treeAreaWidth,
109
+ msaAreaWidth: msaCanvasWidth,
110
+ height:
111
+ msaAreaHeight + (includeMinimap ? minimapHeight : 0) + trackHeight,
112
+ contentHeight: msaAreaHeight,
92
113
  trackHeight,
93
114
  offsetX: -scrollX,
94
115
  offsetY: -scrollY,
@@ -125,26 +146,16 @@ function MsaSvg({
125
146
  const { treeAreaWidth, minimapHeight } = model
126
147
  const totalWidth = width + legendWidth
127
148
  const legendTop = includeMinimap ? minimapHeight : 0
149
+ const contrastScheme = colorContrast(model.colorScheme, theme)
150
+ const props = { Context, model, theme, layout, contrastScheme }
128
151
 
129
152
  const body = (
130
153
  <>
131
- {trackHeight > 0 ? (
132
- <TrackRendering
133
- Context={Context}
134
- model={model}
135
- theme={theme}
136
- layout={layout}
137
- />
138
- ) : null}
154
+ {trackHeight > 0 ? <TrackRendering {...props} /> : null}
139
155
  <g
140
156
  transform={trackHeight > 0 ? `translate(0 ${trackHeight})` : undefined}
141
157
  >
142
- <CoreRendering
143
- Context={Context}
144
- model={model}
145
- theme={theme}
146
- layout={layout}
147
- />
158
+ <CoreRendering {...props} />
148
159
  </g>
149
160
  </>
150
161
  )
@@ -157,11 +168,18 @@ function MsaSvg({
157
168
  xmlnsXlink="http://www.w3.org/1999/xlink"
158
169
  viewBox={`0 0 ${totalWidth} ${height}`}
159
170
  >
160
- <rect width="100%" height="100%" fill="white" />
171
+ {/* every layer below draws in the theme's colors, so the page has to be
172
+ the theme's background too -- a hardcoded white one turns a dark-theme
173
+ export into white text on white */}
174
+ <rect
175
+ width="100%"
176
+ height="100%"
177
+ fill={theme.palette.background.default}
178
+ />
161
179
  {includeMinimap ? (
162
180
  <>
163
181
  <g transform={`translate(${treeAreaWidth} 0)`}>
164
- <MinimapSVG model={model} width={width - treeAreaWidth} />
182
+ <MinimapSVG model={model} theme={theme} />
165
183
  </g>
166
184
  <g transform={`translate(0 ${minimapHeight})`}>{body}</g>
167
185
  </>
@@ -170,7 +188,7 @@ function MsaSvg({
170
188
  )}
171
189
  {legendWidth > 0 ? (
172
190
  <g transform={`translate(${width} ${legendTop})`}>
173
- <LegendSVG model={model} width={legendWidth} />
191
+ <LegendSVG model={model} theme={theme} width={legendWidth} />
174
192
  </g>
175
193
  ) : null}
176
194
  </svg>
@@ -179,9 +197,22 @@ function MsaSvg({
179
197
 
180
198
  // the domain color key drawn as a reserved column to the right of the
181
199
  // alignment, mirroring the on-screen AnnotationLegend overlay
182
- function LegendSVG({ model, width }: { model: MsaViewModel; width: number }) {
200
+ function LegendSVG({
201
+ model,
202
+ theme,
203
+ width,
204
+ }: {
205
+ model: MsaViewModel
206
+ theme: Theme
207
+ width: number
208
+ }) {
183
209
  const { visibleDomainTypes, fillPalette, strokePalette } = model
184
210
  const boxHeight = LEGEND_PAD * 2 + visibleDomainTypes.length * LEGEND_ROW_H
211
+ // the column is capped at LEGEND_MAX_W, so a name too long for it has to be
212
+ // clipped here -- text that overruns the reserved width runs off the figure
213
+ const maxChars = Math.floor(
214
+ (width - LEGEND_TEXT_X - LEGEND_PAD) / LEGEND_CHAR_W,
215
+ )
185
216
  return (
186
217
  <g>
187
218
  <rect
@@ -189,7 +220,7 @@ function LegendSVG({ model, width }: { model: MsaViewModel; width: number }) {
189
220
  y={0}
190
221
  width={width - 4}
191
222
  height={boxHeight}
192
- fill="white"
223
+ fill={theme.palette.background.paper}
193
224
  stroke="#ccc"
194
225
  rx={2}
195
226
  />
@@ -206,11 +237,14 @@ function LegendSVG({ model, width }: { model: MsaViewModel; width: number }) {
206
237
  stroke={strokePalette[d.accession]}
207
238
  />
208
239
  <text
209
- x={LEGEND_PAD + LEGEND_SWATCH + 6}
240
+ x={LEGEND_TEXT_X}
210
241
  y={y + LEGEND_SWATCH - 1}
211
242
  fontSize={LEGEND_FONT}
243
+ fill={theme.palette.text.primary}
212
244
  >
213
- {d.name}
245
+ {d.name.length > maxChars
246
+ ? `${d.name.slice(0, Math.max(1, maxChars - 1))}…`
247
+ : d.name}
214
248
  </text>
215
249
  </g>
216
250
  )
@@ -219,21 +253,23 @@ function LegendSVG({ model, width }: { model: MsaViewModel; width: number }) {
219
253
  )
220
254
  }
221
255
 
256
+ interface LayerProps {
257
+ model: MsaViewModel
258
+ theme: Theme
259
+ layout: Layout
260
+ Context: typeof ContextType
261
+ contrastScheme: Record<string, string>
262
+ }
263
+
222
264
  function CoreRendering({
223
265
  model,
224
266
  theme,
225
267
  layout,
226
268
  Context,
227
- }: {
228
- model: MsaViewModel
229
- theme: Theme
230
- layout: Layout
231
- Context: typeof ContextType
232
- }) {
233
- const { contentHeight, offsetX, offsetY, width } = layout
234
- const { treeAreaWidth, colorScheme, id } = model
235
- const msaAreaWidth = width - treeAreaWidth
236
- const contrastScheme = colorContrast(colorScheme, theme)
269
+ contrastScheme,
270
+ }: LayerProps) {
271
+ const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout
272
+ const { treeAreaWidth, id } = model
237
273
 
238
274
  const treeCtx = new Context(treeAreaWidth, contentHeight)
239
275
  renderTreeCanvas({
@@ -245,6 +281,8 @@ function CoreRendering({
245
281
  highResScaleFactorOverride: 1,
246
282
  })
247
283
 
284
+ const raster = rasterBackground({ model, theme, layout })
285
+
248
286
  const msaCtx = new Context(msaAreaWidth, contentHeight)
249
287
  renderBoxFeatureCanvasBlock({
250
288
  model,
@@ -267,6 +305,7 @@ function CoreRendering({
267
305
  blockSizeXOverride: msaAreaWidth,
268
306
  blockSizeYOverride: contentHeight,
269
307
  highResScaleFactorOverride: 1,
308
+ rasterTiles: !!raster,
270
309
  })
271
310
 
272
311
  return (
@@ -283,26 +322,79 @@ function CoreRendering({
283
322
  height={contentHeight}
284
323
  transform={`translate(${treeAreaWidth} 0)`}
285
324
  ctx={msaCtx}
325
+ underlay={raster}
286
326
  />
287
327
  </>
288
328
  )
289
329
  }
290
330
 
291
- function TrackRendering({
331
+ /**
332
+ * The alignment background as a single <image>, on the same terms the live
333
+ * canvas uses it: no domain overlay painting its own boxes, background coloring
334
+ * on, and a canvas we can actually read back.
335
+ *
336
+ * One node replaces the <rect>-per-cell the vector path emits, which is what
337
+ * lets an export of a real alignment finish at all. The image is one pixel per
338
+ * cell drawn across the cells' own rectangle, so it upscales by whole cells --
339
+ * image-rendering keeps those edges hard rather than smearing them, making it
340
+ * pixel-for-pixel what the rects drew.
341
+ */
342
+ function rasterBackground({
292
343
  model,
293
344
  theme,
294
345
  layout,
295
- Context,
296
346
  }: {
297
347
  model: MsaViewModel
298
348
  theme: Theme
299
349
  layout: Layout
300
- Context: typeof ContextType
301
350
  }) {
302
- const { trackHeight, offsetX, width } = layout
303
- const { treeAreaWidth, colorScheme, id } = model
304
- const msaAreaWidth = width - treeAreaWidth
305
- const contrastScheme = colorContrast(colorScheme, theme)
351
+ const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout
352
+ const { colWidth, rowHeight, actuallyShowDomains, bgColor } = model
353
+ if (actuallyShowDomains || !bgColor || !rasterSupported()) {
354
+ return undefined
355
+ }
356
+ const { xStart, xEnd } = visibleColRange({
357
+ offsetX,
358
+ blockWidth: msaAreaWidth,
359
+ colWidth,
360
+ })
361
+ const { yStart, yEnd } = visibleRowRange({
362
+ model,
363
+ offsetY,
364
+ blockSizeY: contentHeight,
365
+ })
366
+ const numCols = Math.min(xEnd, model.numColumns) - xStart
367
+ const numRows = Math.min(yEnd, model.leaves.length) - yStart
368
+ const href = rasterImageHref({
369
+ model,
370
+ theme,
371
+ col0: xStart,
372
+ row0: yStart,
373
+ numCols,
374
+ numRows,
375
+ })
376
+ return href ? (
377
+ <image
378
+ href={href}
379
+ x={xStart * colWidth - offsetX}
380
+ y={yStart * rowHeight - offsetY}
381
+ width={numCols * colWidth}
382
+ height={numRows * rowHeight}
383
+ imageRendering="pixelated"
384
+ preserveAspectRatio="none"
385
+ />
386
+ ) : undefined
387
+ }
388
+
389
+ function TrackRendering({
390
+ model,
391
+ theme,
392
+ layout,
393
+ Context,
394
+ contrastScheme,
395
+ }: LayerProps) {
396
+ const { trackHeight, offsetX, msaAreaWidth } = layout
397
+ const { treeAreaWidth, id } = model
306
398
 
307
399
  const ctx = new Context(msaAreaWidth, trackHeight)
308
400
  renderAllTracks({
@@ -316,31 +408,76 @@ function TrackRendering({
316
408
  })
317
409
 
318
410
  return (
319
- <g transform={`translate(${treeAreaWidth} 0)`}>
320
- <ClipGroup
321
- clipId={`tracks-${id}`}
322
- width={msaAreaWidth}
323
- height={trackHeight}
324
- ctx={ctx}
325
- />
326
- </g>
411
+ <>
412
+ <TrackLabelsSVG model={model} theme={theme} />
413
+ <g transform={`translate(${treeAreaWidth} 0)`}>
414
+ <ClipGroup
415
+ clipId={`tracks-${id}`}
416
+ width={msaAreaWidth}
417
+ height={trackHeight}
418
+ ctx={ctx}
419
+ />
420
+ </g>
421
+ </>
422
+ )
423
+ }
424
+
425
+ // Each track's name in the tree-width column beside it, where the live view puts
426
+ // it (see TrackLabel). Without these the figure has an unlabeled bar chart and
427
+ // an unlabeled logo, and nothing says which is which.
428
+ function TrackLabelsSVG({
429
+ model,
430
+ theme,
431
+ }: {
432
+ model: MsaViewModel
433
+ theme: Theme
434
+ }) {
435
+ const { turnedOnTracks, treeAreaWidth, fontSize, drawLabels } = model
436
+ if (!drawLabels) {
437
+ return null
438
+ }
439
+ let y = 0
440
+ return (
441
+ <>
442
+ {turnedOnTracks.map(track => {
443
+ const { id, name, height } = track.model
444
+ const size = Math.min(height, fontSize)
445
+ const baseline = y + height / 2 + size / 3
446
+ y += height
447
+ return size < 5 ? null : (
448
+ <text
449
+ key={id}
450
+ x={treeAreaWidth - 4}
451
+ y={baseline}
452
+ fontSize={size}
453
+ textAnchor="end"
454
+ fill={theme.palette.text.primary}
455
+ >
456
+ {name}
457
+ </text>
458
+ )
459
+ })}
460
+ </>
327
461
  )
328
462
  }
329
463
 
330
464
  // Clips a svgcanvas Context to its box and injects its markup verbatim (it is
331
- // already serialized SVG, not React).
465
+ // already serialized SVG, not React). `underlay` draws beneath that markup,
466
+ // inside the same clip, for the parts of a layer React emits directly.
332
467
  function ClipGroup({
333
468
  clipId,
334
469
  width,
335
470
  height,
336
471
  transform,
337
472
  ctx,
473
+ underlay,
338
474
  }: {
339
475
  clipId: string
340
476
  width: number
341
477
  height: number
342
478
  transform?: string
343
479
  ctx: ContextType
480
+ underlay?: React.ReactNode
344
481
  }) {
345
482
  return (
346
483
  <>
@@ -349,11 +486,10 @@ function ClipGroup({
349
486
  <rect x={0} y={0} width={width} height={height} />
350
487
  </clipPath>
351
488
  </defs>
352
- <g
353
- clipPath={`url(#${clipId})`}
354
- transform={transform}
355
- dangerouslySetInnerHTML={{ __html: ctx.getSvg().innerHTML }}
356
- />
489
+ <g clipPath={`url(#${clipId})`} transform={transform}>
490
+ {underlay}
491
+ <g dangerouslySetInnerHTML={{ __html: ctx.getSvg().innerHTML }} />
492
+ </g>
357
493
  </>
358
494
  )
359
495
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '6.1.1'
1
+ export const version = '6.2.0'
@@ -1 +0,0 @@
1
- {"version":3,"file":"renderTestEnv.js","sourceRoot":"","sources":["../src/renderTestEnv.ts"],"names":[],"mappings":"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"}