react-msaview 5.4.1 → 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.
- package/LICENSE +21 -0
- package/bundle/index.js +465 -138
- package/bundle/index.js.map +7 -1
- package/dist/colorSchemes.js +11 -7
- package/dist/colorSchemes.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +7 -1
- package/dist/components/MSAViewer.js +10 -7
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/Track.js +8 -5
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +27 -23
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/header/MultiAlignmentSelector.js +0 -0
- package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
- package/dist/components/msa/DomainLegend.d.ts +6 -0
- package/dist/components/msa/DomainLegend.js +41 -0
- package/dist/components/msa/DomainLegend.js.map +1 -0
- package/dist/components/msa/MSACanvasBlock.js +17 -5
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAPanel.js +3 -1
- package/dist/components/msa/MSAPanel.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +58 -14
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +3 -3
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +32 -4
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/renderTracksSvg.js +3 -3
- package/dist/components/tracks/renderTracksSvg.js.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +16 -0
- package/dist/constants.js.map +1 -1
- package/dist/createPaletteMap.d.ts +5 -2
- package/dist/createPaletteMap.js +15 -5
- package/dist/createPaletteMap.js.map +1 -1
- package/dist/launchInterProScan.d.ts +2 -0
- package/dist/launchInterProScan.js.map +1 -1
- package/dist/model.d.ts +96 -1
- package/dist/model.js +91 -3
- package/dist/model.js.map +1 -1
- package/dist/renderToSvg.js +104 -74
- package/dist/renderToSvg.js.map +1 -1
- package/dist/{webpack.js → umd.js} +1 -1
- package/dist/umd.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -6
- package/src/colorSchemes.ts +12 -7
- package/src/components/MSAViewer.tsx +30 -18
- package/src/components/Track.tsx +8 -5
- package/src/components/dialogs/FeatureDialog.tsx +62 -48
- package/src/components/header/MultiAlignmentSelector.tsx +0 -0
- package/src/components/msa/DomainLegend.tsx +74 -0
- package/src/components/msa/MSACanvasBlock.tsx +22 -4
- package/src/components/msa/MSAPanel.tsx +2 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +73 -17
- package/src/components/msa/renderMSABlock.ts +3 -3
- package/src/components/msa/renderMSAMouseover.ts +32 -4
- package/src/components/renderCtx.ts +1 -0
- package/src/components/tracks/renderTracksSvg.ts +3 -3
- package/src/constants.ts +18 -0
- package/src/createPaletteMap.ts +18 -5
- package/src/launchInterProScan.ts +3 -1
- package/src/model.ts +105 -2
- package/src/renderDomainLegendSvg.test.tsx +134 -0
- package/src/renderToSvg.tsx +210 -147
- package/src/segmentOverlay.test.ts +70 -0
- package/src/version.ts +1 -1
- package/bundle/index.js.LICENSE.txt +0 -100
- package/dist/components/util.d.ts +0 -1
- package/dist/components/util.js +0 -7
- package/dist/components/util.js.map +0 -1
- package/dist/webpack.js.map +0 -1
- package/src/components/util.ts +0 -9
- /package/dist/{webpack.d.ts → umd.d.ts} +0 -0
- /package/src/{webpack.ts → umd.ts} +0 -0
package/src/renderToSvg.tsx
CHANGED
|
@@ -21,86 +21,119 @@ export interface ExportSvgOptions {
|
|
|
21
21
|
includeTracks?: boolean
|
|
22
22
|
exportType: 'entire' | 'viewport'
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
-
|
|
89
|
-
|
|
120
|
+
theme: Theme
|
|
121
|
+
Context: typeof ContextType
|
|
122
|
+
layout: Layout
|
|
90
123
|
}) {
|
|
91
|
-
const {
|
|
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
|
|
129
|
+
const body = (
|
|
95
130
|
<>
|
|
96
|
-
{
|
|
131
|
+
{trackHeight > 0 ? (
|
|
97
132
|
<TrackRendering
|
|
98
133
|
Context={Context}
|
|
99
134
|
model={model}
|
|
100
135
|
theme={theme}
|
|
101
|
-
|
|
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
|
-
|
|
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
|
|
123
|
-
<
|
|
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})`}>{
|
|
166
|
+
<g transform={`translate(0 ${minimapHeight})`}>{body}</g>
|
|
130
167
|
</>
|
|
131
168
|
) : (
|
|
132
|
-
|
|
169
|
+
body
|
|
133
170
|
)}
|
|
134
|
-
|
|
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
|
-
|
|
142
|
-
contentHeight,
|
|
143
|
-
offsetX,
|
|
144
|
-
offsetY,
|
|
225
|
+
layout,
|
|
145
226
|
Context,
|
|
146
227
|
}: {
|
|
147
228
|
model: MsaViewModel
|
|
148
229
|
theme: Theme
|
|
149
|
-
|
|
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
|
|
157
|
-
const clipId2 = `msa-${id}`
|
|
235
|
+
const msaAreaWidth = width - treeAreaWidth
|
|
158
236
|
const contrastScheme = colorContrast(colorScheme, theme)
|
|
159
237
|
|
|
160
|
-
const
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
-
offsetX,
|
|
260
|
+
ctx: msaCtx,
|
|
183
261
|
contrastScheme,
|
|
184
|
-
|
|
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={
|
|
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
|
-
|
|
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
|
-
|
|
234
|
-
trackHeight,
|
|
235
|
-
offsetX,
|
|
291
|
+
layout,
|
|
236
292
|
Context,
|
|
237
293
|
}: {
|
|
238
294
|
model: MsaViewModel
|
|
239
295
|
theme: Theme
|
|
240
|
-
|
|
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
|
|
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
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
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
|
-
|
|
332
|
+
transform,
|
|
333
|
+
ctx,
|
|
276
334
|
}: {
|
|
335
|
+
clipId: string
|
|
277
336
|
width: number
|
|
278
337
|
height: number
|
|
279
|
-
|
|
338
|
+
transform?: string
|
|
339
|
+
ctx: ContextType
|
|
280
340
|
}) {
|
|
281
341
|
return (
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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.
|
|
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.11 | (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.11/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;
|
package/dist/components/util.js
DELETED
|
@@ -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"}
|
package/dist/webpack.js.map
DELETED
|
@@ -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"}
|
package/src/components/util.ts
DELETED
|
File without changes
|
|
File without changes
|