react-msaview 5.4.0 → 5.5.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 (94) hide show
  1. package/LICENSE +21 -0
  2. package/bundle/index.js +465 -138
  3. package/bundle/index.js.map +7 -1
  4. package/dist/colorSchemes.js +11 -7
  5. package/dist/colorSchemes.js.map +1 -1
  6. package/dist/components/Checkbox2.js +3 -1
  7. package/dist/components/Checkbox2.js.map +1 -1
  8. package/dist/components/MSAViewer.d.ts +7 -1
  9. package/dist/components/MSAViewer.js +11 -10
  10. package/dist/components/MSAViewer.js.map +1 -1
  11. package/dist/components/Track.js +8 -5
  12. package/dist/components/Track.js.map +1 -1
  13. package/dist/components/dialogs/FeatureDialog.js +27 -23
  14. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  15. package/dist/components/dialogs/InterProScanDialog.js +2 -1
  16. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  17. package/dist/components/header/MultiAlignmentSelector.js +0 -0
  18. package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
  19. package/dist/components/msa/DomainLegend.d.ts +6 -0
  20. package/dist/components/msa/DomainLegend.js +41 -0
  21. package/dist/components/msa/DomainLegend.js.map +1 -0
  22. package/dist/components/msa/MSACanvasBlock.js +17 -5
  23. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  24. package/dist/components/msa/MSAPanel.js +3 -1
  25. package/dist/components/msa/MSAPanel.js.map +1 -1
  26. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +58 -14
  27. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  28. package/dist/components/msa/renderMSABlock.js +7 -9
  29. package/dist/components/msa/renderMSABlock.js.map +1 -1
  30. package/dist/components/msa/renderMSAMouseover.js +32 -4
  31. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  32. package/dist/components/renderCtx.d.ts +1 -1
  33. package/dist/components/tracks/renderTracksSvg.js +4 -4
  34. package/dist/components/tracks/renderTracksSvg.js.map +1 -1
  35. package/dist/components/tree/renderTreeCanvas.js +3 -0
  36. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  37. package/dist/constants.d.ts +3 -0
  38. package/dist/constants.js +16 -0
  39. package/dist/constants.js.map +1 -1
  40. package/dist/createPaletteMap.d.ts +5 -2
  41. package/dist/createPaletteMap.js +15 -5
  42. package/dist/createPaletteMap.js.map +1 -1
  43. package/dist/hierarchy.js +4 -1
  44. package/dist/hierarchy.js.map +1 -1
  45. package/dist/launchInterProScan.d.ts +2 -0
  46. package/dist/launchInterProScan.js.map +1 -1
  47. package/dist/model/msaModel.js.map +1 -1
  48. package/dist/model.d.ts +100 -3
  49. package/dist/model.js +121 -19
  50. package/dist/model.js.map +1 -1
  51. package/dist/neighborJoining.js +44 -30
  52. package/dist/neighborJoining.js.map +1 -1
  53. package/dist/renderToSvg.js +104 -74
  54. package/dist/renderToSvg.js.map +1 -1
  55. package/dist/{webpack.js → umd.js} +1 -1
  56. package/dist/umd.js.map +1 -0
  57. package/dist/version.d.ts +1 -1
  58. package/dist/version.js +1 -1
  59. package/package.json +8 -7
  60. package/src/colorSchemes.ts +12 -7
  61. package/src/components/Checkbox2.tsx +7 -1
  62. package/src/components/MSAViewer.tsx +31 -21
  63. package/src/components/Track.tsx +8 -5
  64. package/src/components/dialogs/FeatureDialog.tsx +62 -48
  65. package/src/components/dialogs/InterProScanDialog.tsx +2 -1
  66. package/src/components/header/MultiAlignmentSelector.tsx +0 -0
  67. package/src/components/msa/DomainLegend.tsx +74 -0
  68. package/src/components/msa/MSACanvasBlock.tsx +22 -4
  69. package/src/components/msa/MSAPanel.tsx +2 -0
  70. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +73 -17
  71. package/src/components/msa/renderMSABlock.ts +5 -11
  72. package/src/components/msa/renderMSAMouseover.ts +32 -4
  73. package/src/components/renderCtx.ts +1 -0
  74. package/src/components/tracks/renderTracksSvg.ts +4 -4
  75. package/src/components/tree/renderTreeCanvas.ts +5 -1
  76. package/src/constants.ts +18 -0
  77. package/src/createPaletteMap.ts +18 -5
  78. package/src/hierarchy.ts +4 -1
  79. package/src/launchInterProScan.ts +3 -1
  80. package/src/model/msaModel.ts +3 -1
  81. package/src/model.ts +137 -36
  82. package/src/neighborJoining.ts +44 -31
  83. package/src/renderDomainLegendSvg.test.tsx +134 -0
  84. package/src/renderToSvg.tsx +210 -147
  85. package/src/segmentOverlay.test.ts +70 -0
  86. package/src/version.ts +1 -1
  87. package/bundle/index.js.LICENSE.txt +0 -100
  88. package/dist/components/util.d.ts +0 -1
  89. package/dist/components/util.js +0 -7
  90. package/dist/components/util.js.map +0 -1
  91. package/dist/webpack.js.map +0 -1
  92. package/src/components/util.ts +0 -9
  93. /package/dist/{webpack.d.ts → umd.d.ts} +0 -0
  94. /package/src/{webpack.ts → umd.ts} +0 -0
@@ -21,86 +21,119 @@ export interface ExportSvgOptions {
21
21
  includeTracks?: boolean
22
22
  exportType: 'entire' | 'viewport'
23
23
  }
24
- export async function renderToSvg(model: MsaViewModel, opts: ExportSvgOptions) {
25
- await when(() => model.dataInitialized)
24
+
25
+ // domain-legend geometry, shared by the width calculation and the renderer so
26
+ // the reserved column on the right exactly fits the drawn legend
27
+ const LEGEND_PAD = 8
28
+ const LEGEND_ROW_H = 16
29
+ const LEGEND_SWATCH = 12
30
+ const LEGEND_FONT = 12
31
+ const LEGEND_CHAR_W = 7
32
+
33
+ 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))
38
+ }
39
+
40
+ // resolved sizes/offsets (in svg user units) for the chosen export, shared by
41
+ // every layer; 'entire' renders the whole alignment unscrolled, 'viewport'
42
+ // mirrors the live scroll position
43
+ interface Layout {
44
+ width: number
45
+ height: number
46
+ contentHeight: number
47
+ trackHeight: number
48
+ offsetX: number
49
+ offsetY: number
50
+ includeMinimap: boolean
51
+ legendWidth: number
52
+ }
53
+
54
+ function getLayout(model: MsaViewModel, opts: ExportSvgOptions): Layout {
26
55
  const {
27
56
  width,
28
57
  height,
29
58
  scrollX,
30
59
  scrollY,
60
+ totalWidth,
61
+ totalHeight,
62
+ treeAreaWidth,
31
63
  totalTrackAreaHeight,
32
64
  minimapHeight,
33
65
  } = model
34
- const { exportType, theme, includeTracks } = opts
35
- const trackHeight = includeTracks ? totalTrackAreaHeight : 0
66
+ const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0
36
67
  // the minimap reflects the live viewport scroll position, so it's only
37
68
  // meaningful for a viewport export, never for the entire alignment
38
- const includeMinimap = exportType === 'viewport' && !!opts.includeMinimap
69
+ const includeMinimap = opts.exportType === 'viewport' && !!opts.includeMinimap
70
+ const legendWidth =
71
+ model.actuallyShowDomains && model.visibleDomainTypes.length > 0
72
+ ? getLegendWidth(model)
73
+ : 0
39
74
 
40
- if (exportType === 'entire') {
41
- return render({
42
- width: model.totalWidth + model.treeAreaWidth,
43
- height: model.totalHeight + trackHeight,
44
- contentHeight: model.totalHeight,
45
- trackHeight,
46
- theme,
47
- model,
48
- offsetY: 0,
49
- offsetX: 0,
50
- includeMinimap,
51
- includeTracks,
52
- })
53
- }
54
- return render({
55
- width,
56
- height: height + (includeMinimap ? minimapHeight : 0) + trackHeight,
57
- contentHeight: height,
58
- trackHeight,
59
- theme,
60
- model,
61
- offsetY: -scrollY,
62
- offsetX: -scrollX,
63
- includeMinimap,
64
- includeTracks,
65
- })
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
77
+ return opts.exportType === 'entire'
78
+ ? {
79
+ width: totalWidth + treeAreaWidth,
80
+ height: totalHeight + trackHeight,
81
+ contentHeight: totalHeight,
82
+ trackHeight,
83
+ offsetX: 0,
84
+ offsetY: 0,
85
+ includeMinimap,
86
+ legendWidth,
87
+ }
88
+ : {
89
+ width,
90
+ height: height + (includeMinimap ? minimapHeight : 0) + trackHeight,
91
+ contentHeight: height,
92
+ trackHeight,
93
+ offsetX: -scrollX,
94
+ offsetY: -scrollY,
95
+ includeMinimap,
96
+ legendWidth,
97
+ }
66
98
  }
67
99
 
68
- async function render({
69
- width,
70
- height,
71
- contentHeight,
72
- trackHeight,
73
- offsetX,
74
- offsetY,
75
- theme,
100
+ export async function renderToSvg(model: MsaViewModel, opts: ExportSvgOptions) {
101
+ await when(() => model.dataInitialized)
102
+ const { Context } = await import('@jbrowse/svgcanvas')
103
+ return renderToStaticMarkup(
104
+ <MsaSvg
105
+ model={model}
106
+ theme={opts.theme}
107
+ Context={Context}
108
+ layout={getLayout(model, opts)}
109
+ />,
110
+ )
111
+ }
112
+
113
+ function MsaSvg({
76
114
  model,
77
- includeMinimap,
78
- includeTracks,
115
+ theme,
116
+ Context,
117
+ layout,
79
118
  }: {
80
- width: number
81
- height: number
82
- contentHeight: number
83
- trackHeight: number
84
- offsetX: number
85
- offsetY: number
86
- theme: Theme
87
119
  model: MsaViewModel
88
- includeMinimap?: boolean
89
- includeTracks?: boolean
120
+ theme: Theme
121
+ Context: typeof ContextType
122
+ layout: Layout
90
123
  }) {
91
- const { Context } = await import('@jbrowse/svgcanvas')
124
+ const { width, height, trackHeight, includeMinimap, legendWidth } = layout
92
125
  const { treeAreaWidth, minimapHeight } = model
126
+ const totalWidth = width + legendWidth
127
+ const legendTop = includeMinimap ? minimapHeight : 0
93
128
 
94
- const content = (
129
+ const body = (
95
130
  <>
96
- {includeTracks && trackHeight > 0 ? (
131
+ {trackHeight > 0 ? (
97
132
  <TrackRendering
98
133
  Context={Context}
99
134
  model={model}
100
135
  theme={theme}
101
- offsetX={offsetX}
102
- msaAreaWidth={width - treeAreaWidth}
103
- trackHeight={trackHeight}
136
+ layout={layout}
104
137
  />
105
138
  ) : null}
106
139
  <g
@@ -110,184 +143,214 @@ async function render({
110
143
  Context={Context}
111
144
  model={model}
112
145
  theme={theme}
113
- offsetX={offsetX}
114
- offsetY={offsetY}
115
- msaAreaWidth={width - treeAreaWidth}
116
- contentHeight={contentHeight}
146
+ layout={layout}
117
147
  />
118
148
  </g>
119
149
  </>
120
150
  )
121
151
 
122
- return renderToStaticMarkup(
123
- <SvgWrapper width={width} height={height}>
152
+ return (
153
+ <svg
154
+ width={totalWidth}
155
+ height={height}
156
+ xmlns="http://www.w3.org/2000/svg"
157
+ xmlnsXlink="http://www.w3.org/1999/xlink"
158
+ viewBox={`0 0 ${totalWidth} ${height}`}
159
+ >
160
+ <rect width="100%" height="100%" fill="white" />
124
161
  {includeMinimap ? (
125
162
  <>
126
163
  <g transform={`translate(${treeAreaWidth} 0)`}>
127
164
  <MinimapSVG model={model} />
128
165
  </g>
129
- <g transform={`translate(0 ${minimapHeight})`}>{content}</g>
166
+ <g transform={`translate(0 ${minimapHeight})`}>{body}</g>
130
167
  </>
131
168
  ) : (
132
- content
169
+ body
133
170
  )}
134
- </SvgWrapper>,
171
+ {legendWidth > 0 ? (
172
+ <g transform={`translate(${width} ${legendTop})`}>
173
+ <LegendSVG model={model} width={legendWidth} />
174
+ </g>
175
+ ) : null}
176
+ </svg>
177
+ )
178
+ }
179
+
180
+ // the domain color key drawn as a reserved column to the right of the
181
+ // alignment, mirroring the on-screen DomainLegend overlay
182
+ function LegendSVG({ model, width }: { model: MsaViewModel; width: number }) {
183
+ const { visibleDomainTypes, fillPalette, strokePalette } = model
184
+ const boxHeight = LEGEND_PAD * 2 + visibleDomainTypes.length * LEGEND_ROW_H
185
+ return (
186
+ <g>
187
+ <rect
188
+ x={0}
189
+ y={0}
190
+ width={width - 4}
191
+ height={boxHeight}
192
+ fill="white"
193
+ stroke="#ccc"
194
+ rx={2}
195
+ />
196
+ {visibleDomainTypes.map((d, i) => {
197
+ const y = LEGEND_PAD + i * LEGEND_ROW_H
198
+ return (
199
+ <g key={d.accession}>
200
+ <rect
201
+ x={LEGEND_PAD}
202
+ y={y}
203
+ width={LEGEND_SWATCH}
204
+ height={LEGEND_SWATCH}
205
+ fill={fillPalette[d.accession]}
206
+ stroke={strokePalette[d.accession]}
207
+ />
208
+ <text
209
+ x={LEGEND_PAD + LEGEND_SWATCH + 6}
210
+ y={y + LEGEND_SWATCH - 1}
211
+ fontSize={LEGEND_FONT}
212
+ >
213
+ {d.name}
214
+ </text>
215
+ </g>
216
+ )
217
+ })}
218
+ </g>
135
219
  )
136
220
  }
137
221
 
138
222
  function CoreRendering({
139
223
  model,
140
224
  theme,
141
- msaAreaWidth,
142
- contentHeight,
143
- offsetX,
144
- offsetY,
225
+ layout,
145
226
  Context,
146
227
  }: {
147
228
  model: MsaViewModel
148
229
  theme: Theme
149
- msaAreaWidth: number
150
- contentHeight: number
151
- offsetX: number
152
- offsetY: number
230
+ layout: Layout
153
231
  Context: typeof ContextType
154
232
  }) {
233
+ const { contentHeight, offsetX, offsetY, width } = layout
155
234
  const { treeAreaWidth, colorScheme, id } = model
156
- const clipId1 = `tree-${id}`
157
- const clipId2 = `msa-${id}`
235
+ const msaAreaWidth = width - treeAreaWidth
158
236
  const contrastScheme = colorContrast(colorScheme, theme)
159
237
 
160
- const ctx1 = new Context(treeAreaWidth, contentHeight)
161
- const ctx2 = new Context(msaAreaWidth, contentHeight)
162
- renderBoxFeatureCanvasBlock({
163
- ctx: ctx2,
164
- offsetX,
165
- offsetY,
238
+ const treeCtx = new Context(treeAreaWidth, contentHeight)
239
+ renderTreeCanvas({
166
240
  model,
241
+ theme,
242
+ ctx: treeCtx,
243
+ offsetY,
167
244
  blockSizeYOverride: contentHeight,
168
245
  highResScaleFactorOverride: 1,
169
246
  })
170
- renderTreeCanvas({
247
+
248
+ const msaCtx = new Context(msaAreaWidth, contentHeight)
249
+ renderBoxFeatureCanvasBlock({
171
250
  model,
251
+ ctx: msaCtx,
252
+ offsetX,
172
253
  offsetY,
173
- ctx: ctx1,
174
- theme,
175
254
  blockSizeYOverride: contentHeight,
176
255
  highResScaleFactorOverride: 1,
177
256
  })
178
257
  renderMSABlock({
179
258
  model,
180
259
  theme,
181
- offsetY,
182
- offsetX,
260
+ ctx: msaCtx,
183
261
  contrastScheme,
184
- ctx: ctx2,
262
+ offsetX,
263
+ offsetY,
185
264
  blockSizeXOverride: msaAreaWidth,
186
265
  blockSizeYOverride: contentHeight,
187
266
  highResScaleFactorOverride: 1,
188
267
  })
268
+
189
269
  return (
190
270
  <>
191
- <defs>
192
- <clipPath id={clipId1}>
193
- <rect x={0} y={0} width={treeAreaWidth} height={contentHeight} />
194
- </clipPath>
195
- <clipPath id={clipId2}>
196
- <rect x={0} y={0} width={msaAreaWidth} height={contentHeight} />
197
- </clipPath>
198
- </defs>
199
-
200
- <ClipGroup clipId={clipId1} html={ctx1.getSvg().innerHTML} />
201
271
  <ClipGroup
202
- clipId={clipId2}
272
+ clipId={`tree-${id}`}
273
+ width={treeAreaWidth}
274
+ height={contentHeight}
275
+ ctx={treeCtx}
276
+ />
277
+ <ClipGroup
278
+ clipId={`msa-${id}`}
279
+ width={msaAreaWidth}
280
+ height={contentHeight}
203
281
  transform={`translate(${treeAreaWidth} 0)`}
204
- html={ctx2.getSvg().innerHTML}
282
+ ctx={msaCtx}
205
283
  />
206
284
  </>
207
285
  )
208
286
  }
209
287
 
210
- // Wraps SVG markup produced by a svgcanvas Context in a clipped group. The
211
- // markup is injected verbatim since it's already serialized SVG, not React.
212
- function ClipGroup({
213
- clipId,
214
- html,
215
- transform,
216
- }: {
217
- clipId: string
218
- html: string
219
- transform?: string
220
- }) {
221
- return (
222
- <g
223
- clipPath={`url(#${clipId})`}
224
- transform={transform}
225
- dangerouslySetInnerHTML={{ __html: html }}
226
- />
227
- )
228
- }
229
-
230
288
  function TrackRendering({
231
289
  model,
232
290
  theme,
233
- msaAreaWidth,
234
- trackHeight,
235
- offsetX,
291
+ layout,
236
292
  Context,
237
293
  }: {
238
294
  model: MsaViewModel
239
295
  theme: Theme
240
- msaAreaWidth: number
241
- trackHeight: number
242
- offsetX: number
296
+ layout: Layout
243
297
  Context: typeof ContextType
244
298
  }) {
299
+ const { trackHeight, offsetX, width } = layout
245
300
  const { treeAreaWidth, colorScheme, id } = model
246
- const clipId = `tracks-${id}`
301
+ const msaAreaWidth = width - treeAreaWidth
247
302
  const contrastScheme = colorContrast(colorScheme, theme)
248
303
 
249
304
  const ctx = new Context(msaAreaWidth, trackHeight)
250
-
251
305
  renderAllTracks({
252
306
  model,
253
307
  ctx,
254
- offsetX,
255
308
  contrastScheme,
309
+ offsetX,
256
310
  blockSizeXOverride: msaAreaWidth,
257
311
  highResScaleFactorOverride: 1,
258
312
  })
259
313
 
260
314
  return (
261
315
  <g transform={`translate(${treeAreaWidth} 0)`}>
262
- <defs>
263
- <clipPath id={clipId}>
264
- <rect x={0} y={0} width={msaAreaWidth} height={trackHeight} />
265
- </clipPath>
266
- </defs>
267
- <ClipGroup clipId={clipId} html={ctx.getSvg().innerHTML} />
316
+ <ClipGroup
317
+ clipId={`tracks-${id}`}
318
+ width={msaAreaWidth}
319
+ height={trackHeight}
320
+ ctx={ctx}
321
+ />
268
322
  </g>
269
323
  )
270
324
  }
271
325
 
272
- function SvgWrapper({
326
+ // Clips a svgcanvas Context to its box and injects its markup verbatim (it is
327
+ // already serialized SVG, not React).
328
+ function ClipGroup({
329
+ clipId,
273
330
  width,
274
331
  height,
275
- children,
332
+ transform,
333
+ ctx,
276
334
  }: {
335
+ clipId: string
277
336
  width: number
278
337
  height: number
279
- children: React.ReactNode
338
+ transform?: string
339
+ ctx: ContextType
280
340
  }) {
281
341
  return (
282
- <svg
283
- width={width}
284
- height={height}
285
- xmlns="http://www.w3.org/2000/svg"
286
- xmlnsXlink="http://www.w3.org/1999/xlink"
287
- viewBox={`0 0 ${width} ${height}`}
288
- >
289
- <rect width="100%" height="100%" fill="white" />
290
- {children}
291
- </svg>
342
+ <>
343
+ <defs>
344
+ <clipPath id={clipId}>
345
+ <rect x={0} y={0} width={width} height={height} />
346
+ </clipPath>
347
+ </defs>
348
+ <g
349
+ clipPath={`url(#${clipId})`}
350
+ transform={transform}
351
+ // eslint-disable-next-line @eslint-react/dom-no-dangerously-set-innerhtml
352
+ dangerouslySetInnerHTML={{ __html: ctx.getSvg().innerHTML }}
353
+ />
354
+ </>
292
355
  )
293
356
  }
@@ -0,0 +1,70 @@
1
+ // Verifies that ordinal segment features (exons) from a GFF overlay are treated
2
+ // as a numbered gene model — alternating two shades, labeled by number, and
3
+ // kept out of the categorical color-key legend — while real domains keep a
4
+ // distinct color + legend entry.
5
+ import { expect, test } from 'vitest'
6
+
7
+ import { segmentShades } from './constants.ts'
8
+ import MSAModelF from './model.ts'
9
+
10
+ const msa = `>human
11
+ ACDEFGHIKLMNPQRSTVWYACDEFGHIKLMNPQRSTVWYACDEFGHIKLMNPQRSTVWY
12
+ >mouse
13
+ ACDEFGHIKLMNPQRSTVWYACDEFGHIKLMNPQRSTVWYACDEFGHIKLMNPQRSTVWY`
14
+
15
+ // three exons in CDS coordinates plus one real domain, deliberately out of
16
+ // start order in the file to prove ordering is by position not file/length
17
+ const gff = `##gff-version 3
18
+ human src exon 41 60 . . . ID=human.exon3;Name=exon-3
19
+ human src exon 1 20 . . . ID=human.exon1;Name=exon-1
20
+ human src exon 21 40 . . . ID=human.exon2;Name=exon-2
21
+ human Pfam protein_match 5 35 . . . Name=PF00069;signature_desc=Kinase`
22
+
23
+ function makeModel() {
24
+ const model = MSAModelF().create({
25
+ id: 'segment-overlay-test',
26
+ type: 'MsaView',
27
+ height: 400,
28
+ msaFormat: 'fasta',
29
+ data: { msa },
30
+ })
31
+ model.setWidth(800)
32
+ model.applyGFFText(gff)
33
+ return model
34
+ }
35
+
36
+ test('exons are ordered by sequence position, domains kept separate', () => {
37
+ const model = makeModel()
38
+ expect(model.segmentDomainTypes.map(d => d.accession)).toEqual([
39
+ 'exon-1',
40
+ 'exon-2',
41
+ 'exon-3',
42
+ ])
43
+ expect(model.categoricalDomainTypes.map(d => d.accession)).toEqual([
44
+ 'PF00069',
45
+ ])
46
+ })
47
+
48
+ test('exons alternate two shades; domain gets its own color', () => {
49
+ const { fillPalette } = makeModel()
50
+ expect(fillPalette['exon-1']).toBe(segmentShades[0])
51
+ expect(fillPalette['exon-2']).toBe(segmentShades[1])
52
+ expect(fillPalette['exon-3']).toBe(segmentShades[0])
53
+ expect(fillPalette.PF00069).not.toBe(segmentShades[0])
54
+ expect(fillPalette.PF00069).not.toBe(segmentShades[1])
55
+ })
56
+
57
+ test('exons are labeled by their number', () => {
58
+ const { segmentLabels } = makeModel()
59
+ expect(segmentLabels.get('exon-1')).toBe('1')
60
+ expect(segmentLabels.get('exon-2')).toBe('2')
61
+ expect(segmentLabels.get('exon-3')).toBe('3')
62
+ })
63
+
64
+ test('legend lists only categorical domains, not exons', () => {
65
+ const model = makeModel()
66
+ for (const acc of ['exon-1', 'exon-2', 'exon-3', 'PF00069']) {
67
+ model.setFilter(acc, true)
68
+ }
69
+ expect(model.visibleDomainTypes.map(d => d.accession)).toEqual(['PF00069'])
70
+ })
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '5.4.0'
1
+ export const version = '5.5.0'
@@ -1,100 +0,0 @@
1
- /*!
2
- * escape-html
3
- * Copyright(c) 2012-2013 TJ Holowaychuk
4
- * Copyright(c) 2015 Andreas Lubbe
5
- * Copyright(c) 2015 Tiancheng "Timothy" Gu
6
- * MIT Licensed
7
- */
8
-
9
- /*! @license DOMPurify 3.4.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.8/LICENSE */
10
-
11
- /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
12
-
13
- /**
14
- * @license React
15
- * react-dom-client.production.js
16
- *
17
- * Copyright (c) Meta Platforms, Inc. and affiliates.
18
- *
19
- * This source code is licensed under the MIT license found in the
20
- * LICENSE file in the root directory of this source tree.
21
- */
22
-
23
- /**
24
- * @license React
25
- * react-dom.production.js
26
- *
27
- * Copyright (c) Meta Platforms, Inc. and affiliates.
28
- *
29
- * This source code is licensed under the MIT license found in the
30
- * LICENSE file in the root directory of this source tree.
31
- */
32
-
33
- /**
34
- * @license React
35
- * react-is.production.js
36
- *
37
- * Copyright (c) Meta Platforms, Inc. and affiliates.
38
- *
39
- * This source code is licensed under the MIT license found in the
40
- * LICENSE file in the root directory of this source tree.
41
- */
42
-
43
- /**
44
- * @license React
45
- * react-jsx-runtime.production.js
46
- *
47
- * Copyright (c) Meta Platforms, Inc. and affiliates.
48
- *
49
- * This source code is licensed under the MIT license found in the
50
- * LICENSE file in the root directory of this source tree.
51
- */
52
-
53
- /**
54
- * @license React
55
- * react.production.js
56
- *
57
- * Copyright (c) Meta Platforms, Inc. and affiliates.
58
- *
59
- * This source code is licensed under the MIT license found in the
60
- * LICENSE file in the root directory of this source tree.
61
- */
62
-
63
- /**
64
- * @license React
65
- * scheduler.production.js
66
- *
67
- * Copyright (c) Meta Platforms, Inc. and affiliates.
68
- *
69
- * This source code is licensed under the MIT license found in the
70
- * LICENSE file in the root directory of this source tree.
71
- */
72
-
73
- /**
74
- * @license React
75
- * use-sync-external-store-shim.production.js
76
- *
77
- * Copyright (c) Meta Platforms, Inc. and affiliates.
78
- *
79
- * This source code is licensed under the MIT license found in the
80
- * LICENSE file in the root directory of this source tree.
81
- */
82
-
83
- /**
84
- * @license React
85
- * use-sync-external-store-shim/with-selector.production.js
86
- *
87
- * Copyright (c) Meta Platforms, Inc. and affiliates.
88
- *
89
- * This source code is licensed under the MIT license found in the
90
- * LICENSE file in the root directory of this source tree.
91
- */
92
-
93
- /** @license React v16.13.1
94
- * react-is.production.min.js
95
- *
96
- * Copyright (c) Facebook, Inc. and its affiliates.
97
- *
98
- * This source code is licensed under the MIT license found in the
99
- * LICENSE file in the root directory of this source tree.
100
- */
@@ -1 +0,0 @@
1
- export declare function mathPower(num: number): string;
@@ -1,7 +0,0 @@
1
- export function mathPower(num) {
2
- if (num < 999) {
3
- return String(num);
4
- }
5
- return `${mathPower(~~(num / 1000))},${`00${~~(num % 1000)}`.slice(-3, -3 + 3)}`;
6
- }
7
- //# sourceMappingURL=util.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/components/util.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;QACd,OAAO,MAAM,CAAC,GAAG,CAAC,CAAA;IACpB,CAAC;IACD,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,CAChE,CAAC,CAAC,EACF,CAAC,CAAC,GAAG,CAAC,CACP,EAAE,CAAA;AACL,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"webpack.js","sourceRoot":"","sources":["../src/webpack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,yBAA0B,CAAA;AAC7D,OAAO,EAAqB,OAAO,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA;AAEpE,cAAc,kBAAkB,CAAA;AAEhC,OAAO,EAAE,KAAK,EAAE,CAAA"}
@@ -1,9 +0,0 @@
1
- export function mathPower(num: number): string {
2
- if (num < 999) {
3
- return String(num)
4
- }
5
- return `${mathPower(~~(num / 1000))},${`00${~~(num % 1000)}`.slice(
6
- -3,
7
- -3 + 3,
8
- )}`
9
- }
File without changes
File without changes