jbrowse-plugin-protein3d 1.2.0 → 1.3.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/README.md +5 -3
- package/dist/LaunchProteinView/utils/util.js +23 -3
- package/dist/ProteinView/components/FeatureBar.js +5 -4
- package/dist/ProteinView/components/FeatureTypeLabel.js +2 -2
- package/dist/ProteinView/components/ProteinAlignment.js +15 -22
- package/dist/ProteinView/components/ProteinFeatureTrack.js +7 -7
- package/dist/ProteinView/components/ResidueValueTrack.js +4 -4
- package/dist/ProteinView/constants.js +10 -0
- package/dist/ProteinView/hooks/useProteinFeatureTrackData.js +41 -15
- package/dist/ProteinView/hooks/useUniProtFeatures.js +6 -15
- package/dist/ProteinView/model.js +18 -0
- package/dist/ProteinView/structureModel.js +16 -7
- package/dist/chunks/ProteinView-JMWV7XDK.js +9 -0
- package/dist/chunks/ProteinView-JMWV7XDK.js.map +7 -0
- package/dist/chunks/chunk-UAUJEOAS.js +16 -0
- package/dist/chunks/chunk-UAUJEOAS.js.map +7 -0
- package/dist/jbrowse-plugin-protein3d.esm.js +7 -7
- package/dist/jbrowse-plugin-protein3d.esm.js.map +4 -4
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/src/LaunchProteinView/utils/util.ts +28 -3
- package/src/ProteinView/components/FeatureBar.tsx +6 -3
- package/src/ProteinView/components/FeatureTypeLabel.tsx +2 -2
- package/src/ProteinView/components/ProteinAlignment.tsx +79 -69
- package/src/ProteinView/components/ProteinFeatureTrack.tsx +8 -11
- package/src/ProteinView/components/ResidueValueTrack.tsx +4 -4
- package/src/ProteinView/constants.ts +20 -0
- package/src/ProteinView/hooks/layoutFeature.test.ts +35 -37
- package/src/ProteinView/hooks/packLanes.test.ts +7 -1
- package/src/ProteinView/hooks/useProteinFeatureTrackData.ts +49 -15
- package/src/ProteinView/hooks/useUniProtFeatures.ts +6 -15
- package/src/ProteinView/model.test.ts +21 -0
- package/src/ProteinView/model.ts +21 -0
- package/src/ProteinView/proteinViewSpec.ts +1 -0
- package/src/ProteinView/structureModel.ts +19 -10
- package/src/version.ts +1 -1
- package/dist/chunks/ProteinView-B57QOH3K.js +0 -9
- package/dist/chunks/ProteinView-B57QOH3K.js.map +0 -7
- package/dist/chunks/chunk-UTUOAPWU.js +0 -16
- package/dist/chunks/chunk-UTUOAPWU.js.map +0 -7
package/README.md
CHANGED
|
@@ -37,9 +37,11 @@ Using the plugin:
|
|
|
37
37
|
gene, and reads the R248 hotspot back to its codon;
|
|
38
38
|
[Proteins on genomes.jbrowse.org](https://jbrowse.org/jb2/docs/tutorials/genomes_proteins/)
|
|
39
39
|
launches a structure and an MSA from any gene's right-click menu.
|
|
40
|
-
- [Demos](docs/demos.md):
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
- [Demos](docs/demos.md): one link each. Genes opened by name alone in human,
|
|
41
|
+
mouse, yeast, bacteria, fungi and viruses (E. coli recA, M. tuberculosis katG,
|
|
42
|
+
the SARS-CoV-2 spike, the HIV-1 capsid), then the structures that are easy to
|
|
43
|
+
map wrong — a peptide bound to a larger partner, a protein bound to DNA, a
|
|
44
|
+
receptor with another protein fused into it, a phosphorylated residue, and a
|
|
43
45
|
mitochondrial protein.
|
|
44
46
|
- [Your own structures](docs/your-own-structures.md): opening a model you folded
|
|
45
47
|
yourself (ColabFold, AlphaFold 3, Boltz…) instead of the AlphaFold DB one,
|
|
@@ -122,6 +122,15 @@ export function findRecognizedDbIds(f) {
|
|
|
122
122
|
}
|
|
123
123
|
return [...new Set(recognizedIds)];
|
|
124
124
|
}
|
|
125
|
+
// `UniProtKB/Swiss-Prot:P0A7G6`, the reviewed entry before a TrEMBL one
|
|
126
|
+
function dbxrefUniProtId(f) {
|
|
127
|
+
const entries = parseDbxref(f?.get('Dbxref') ?? f?.get('dbxref') ?? f?.get('db_xref'));
|
|
128
|
+
return ['UniProtKB/Swiss-Prot:', 'UniProtKB/TrEMBL:']
|
|
129
|
+
.flatMap(prefix => entries
|
|
130
|
+
.filter(e => e.startsWith(prefix))
|
|
131
|
+
.map(e => e.slice(prefix.length)))
|
|
132
|
+
.find(id => id.length > 0);
|
|
133
|
+
}
|
|
125
134
|
/**
|
|
126
135
|
* Extract all useful identifiers from a feature for UniProt lookup.
|
|
127
136
|
* If the feature is a gene, prioritizes identifiers from the preferred
|
|
@@ -140,13 +149,24 @@ export function extractFeatureIdentifiers(f, preferredTranscriptId) {
|
|
|
140
149
|
transcripts[0] ??
|
|
141
150
|
f;
|
|
142
151
|
}
|
|
143
|
-
//
|
|
144
|
-
|
|
152
|
+
// NCBI's GFF3 puts the protein on the CDS record, not the transcript: its
|
|
153
|
+
// RefSeq accession always, and for a curated genome (E. coli K-12, yeast)
|
|
154
|
+
// the UniProt accession itself. A prokaryotic gene has no transcript record
|
|
155
|
+
// between, so the CDS is the only place either appears.
|
|
156
|
+
const cds = featureToProcess
|
|
157
|
+
.get('subfeatures')
|
|
158
|
+
?.find(sub => sub.get('type')?.toLowerCase() === 'cds');
|
|
159
|
+
const recognizedIds = [
|
|
160
|
+
...findRecognizedDbIds(featureToProcess),
|
|
161
|
+
...findRecognizedDbIds(cds),
|
|
162
|
+
];
|
|
145
163
|
// Handle UniProt ID from feature attributes (trust that it's valid if present)
|
|
146
164
|
const uniprotIdAttr = featureToProcess.get('uniprot') ??
|
|
147
165
|
featureToProcess.get('uniprotId') ??
|
|
148
166
|
featureToProcess.get('uniprotid') ??
|
|
149
|
-
featureToProcess.get('UniProt')
|
|
167
|
+
featureToProcess.get('UniProt') ??
|
|
168
|
+
dbxrefUniProtId(featureToProcess) ??
|
|
169
|
+
dbxrefUniProtId(cds);
|
|
150
170
|
const uniprotId = typeof uniprotIdAttr === 'string' && uniprotIdAttr.length > 0
|
|
151
171
|
? uniprotIdAttr
|
|
152
172
|
: undefined;
|
|
@@ -23,16 +23,17 @@ function FeatureTooltipContent({ feature, layout, model, }) {
|
|
|
23
23
|
"Structure residue: ",
|
|
24
24
|
first,
|
|
25
25
|
"-",
|
|
26
|
-
last
|
|
26
|
+
last,
|
|
27
|
+
layout.clipped ? ' (the structure lacks the rest)' : '')) : null,
|
|
27
28
|
feature.description ? React.createElement("div", null, feature.description) : null));
|
|
28
29
|
}
|
|
29
30
|
// `selected` comes from the track rather than each bar reading the model's
|
|
30
31
|
// selection itself, so a click re-renders the two bars it changes instead of
|
|
31
32
|
// every bar of every track (p53 has 1,363 natural variants).
|
|
32
|
-
const FeatureBar = observer(function FeatureBar({ layout, top, selected, model, }) {
|
|
33
|
+
const FeatureBar = observer(function FeatureBar({ layout, top, height, selected, model, }) {
|
|
33
34
|
const [isHovered, setIsHovered] = useState(false);
|
|
34
35
|
const { feature, alignmentStart, alignmentEnd } = layout;
|
|
35
|
-
const { columnWidth
|
|
36
|
+
const { columnWidth } = model;
|
|
36
37
|
return (React.createElement(Tooltip, { title: React.createElement(FeatureTooltipContent, { feature: feature, layout: layout, model: model }), followCursor: true },
|
|
37
38
|
React.createElement("div", { "data-testid": `protein-feature-${feature.type}`, "data-feature-id": feature.uniqueId, "data-feature-start": feature.start, "data-feature-end": feature.end, onClick: () => {
|
|
38
39
|
if (selected) {
|
|
@@ -64,7 +65,7 @@ const FeatureBar = observer(function FeatureBar({ layout, top, selected, model,
|
|
|
64
65
|
left: alignmentStart * columnWidth,
|
|
65
66
|
top,
|
|
66
67
|
width: (alignmentEnd - alignmentStart + 1) * columnWidth,
|
|
67
|
-
height
|
|
68
|
+
height,
|
|
68
69
|
backgroundColor: getFeatureColor(feature.type),
|
|
69
70
|
opacity: isHovered || selected ? 0.9 : 0.6,
|
|
70
71
|
cursor: 'pointer',
|
|
@@ -4,10 +4,10 @@ import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
|
|
|
4
4
|
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
|
|
5
5
|
import { IconButton, Tooltip } from '@mui/material';
|
|
6
6
|
import { observer } from 'mobx-react';
|
|
7
|
-
import { HIDE_BUTTON_COLOR } from '../constants';
|
|
7
|
+
import { HIDE_BUTTON_COLOR, NORMAL_TRACK_HEIGHT } from '../constants';
|
|
8
8
|
const FeatureTypeLabel = observer(function FeatureTypeLabel({ type, laneCount, model, }) {
|
|
9
9
|
const expanded = model.expandedFeatureTypes.has(type);
|
|
10
|
-
const iconSize = model.trackHeight;
|
|
10
|
+
const iconSize = Math.min(model.trackHeight, NORMAL_TRACK_HEIGHT);
|
|
11
11
|
return (React.createElement(Tooltip, { title: type, placement: "left" },
|
|
12
12
|
React.createElement("div", { style: {
|
|
13
13
|
height: '100%',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { ResizeHandle } from '@jbrowse/core/ui';
|
|
2
3
|
import { Tooltip } from '@mui/material';
|
|
3
4
|
import { autorun } from 'mobx';
|
|
4
5
|
import { observer } from 'mobx-react';
|
|
@@ -9,7 +10,7 @@ import { ColorKey, GradientKey } from './ColorKey';
|
|
|
9
10
|
import ColumnOverlays, { SelectionBackdrop } from './ColumnOverlays';
|
|
10
11
|
import FeatureTypeLabel from './FeatureTypeLabel';
|
|
11
12
|
import MismatchShading from './MismatchShading';
|
|
12
|
-
import ProteinFeatureTrack
|
|
13
|
+
import ProteinFeatureTrack from './ProteinFeatureTrack';
|
|
13
14
|
import ResidueValueTrack from './ResidueValueTrack';
|
|
14
15
|
import SplitString from './SplitString';
|
|
15
16
|
import { followHover, offScreenCenterTarget } from '../autoScroll';
|
|
@@ -48,7 +49,7 @@ function GutterLabel({ label, title, }) {
|
|
|
48
49
|
} }, label)));
|
|
49
50
|
}
|
|
50
51
|
const ProteinAlignment = observer(function ProteinAlignment({ model, }) {
|
|
51
|
-
const { alignment, showProteinTracks, showAllFeatureTracks, label, confidenceCells, columnWidth,
|
|
52
|
+
const { alignment, showProteinTracks, showAllFeatureTracks, label, confidenceCells, columnWidth, laneHeight, } = model;
|
|
52
53
|
const hydrophobicityCells = showAllFeatureTracks
|
|
53
54
|
? model.hydrophobicityCells
|
|
54
55
|
: [];
|
|
@@ -102,7 +103,6 @@ const ProteinAlignment = observer(function ProteinAlignment({ model, }) {
|
|
|
102
103
|
return null;
|
|
103
104
|
}
|
|
104
105
|
const columns = alignmentLength(alignment);
|
|
105
|
-
const valueRowHeight = trackHeight + trackGap;
|
|
106
106
|
const sequenceRow = (key, rowLabel, title, str, shading) => ({
|
|
107
107
|
key,
|
|
108
108
|
height: ROW_HEIGHT,
|
|
@@ -131,6 +131,11 @@ const ProteinAlignment = observer(function ProteinAlignment({ model, }) {
|
|
|
131
131
|
},
|
|
132
132
|
];
|
|
133
133
|
const sequenceHeight = sequenceRows.length * ROW_HEIGHT;
|
|
134
|
+
let trackLanes = 0;
|
|
135
|
+
const trackRow = (key, lanes, rowLabel, content) => {
|
|
136
|
+
trackLanes += lanes;
|
|
137
|
+
rows.push({ key, height: lanes * laneHeight, label: rowLabel, content });
|
|
138
|
+
};
|
|
134
139
|
if (showProteinTracks) {
|
|
135
140
|
if (featureStatus) {
|
|
136
141
|
rows.push({
|
|
@@ -141,28 +146,13 @@ const ProteinAlignment = observer(function ProteinAlignment({ model, }) {
|
|
|
141
146
|
});
|
|
142
147
|
}
|
|
143
148
|
for (const group of groups ?? []) {
|
|
144
|
-
|
|
145
|
-
key: `feature-${group.type}`,
|
|
146
|
-
height: featureTrackHeight(model, group),
|
|
147
|
-
label: (React.createElement(FeatureTypeLabel, { type: group.type, laneCount: group.laneCount, model: model })),
|
|
148
|
-
content: React.createElement(ProteinFeatureTrack, { group: group, model: model }),
|
|
149
|
-
});
|
|
149
|
+
trackRow(`feature-${group.type}`, model.expandedFeatureTypes.has(group.type) ? group.laneCount : 1, React.createElement(FeatureTypeLabel, { type: group.type, laneCount: group.laneCount, model: model }), React.createElement(ProteinFeatureTrack, { group: group, model: model }));
|
|
150
150
|
}
|
|
151
151
|
if (confidenceCells.length > 0) {
|
|
152
|
-
|
|
153
|
-
key: 'plddt',
|
|
154
|
-
height: valueRowHeight,
|
|
155
|
-
label: (React.createElement(GutterLabel, { label: "pLDDT", title: React.createElement(ColorKey, { title: "AlphaFold per-residue confidence (pLDDT)", entries: PLDDT_BANDS, color: "inherit" }) })),
|
|
156
|
-
content: (React.createElement(ResidueValueTrack, { cells: confidenceCells, colorFor: plddtColor, formatValue: v => `pLDDT ${v.toFixed(0)}`, model: model })),
|
|
157
|
-
});
|
|
152
|
+
trackRow('plddt', 1, React.createElement(GutterLabel, { label: "pLDDT", title: React.createElement(ColorKey, { title: "AlphaFold per-residue confidence (pLDDT)", entries: PLDDT_BANDS, color: "inherit" }) }), React.createElement(ResidueValueTrack, { cells: confidenceCells, colorFor: plddtColor, formatValue: v => `pLDDT ${v.toFixed(0)}`, model: model }));
|
|
158
153
|
}
|
|
159
154
|
if (hydrophobicityCells.length > 0) {
|
|
160
|
-
|
|
161
|
-
key: 'hydrophobicity',
|
|
162
|
-
height: valueRowHeight,
|
|
163
|
-
label: (React.createElement(GutterLabel, { label: "hydro", title: React.createElement(GradientKey, { title: "Kyte-Doolittle hydrophobicity", testId: "hydrophobicity-legend", minLabel: "hydrophilic", maxLabel: "hydrophobic", colors: HYDROPHOBICITY_KEY_SCORES.map(score => hydrophobicityColor(score)), color: "inherit" }) })),
|
|
164
|
-
content: (React.createElement(ResidueValueTrack, { cells: hydrophobicityCells, colorFor: hydrophobicityColor, formatValue: v => `Kyte-Doolittle ${v.toFixed(1)}`, model: model })),
|
|
165
|
-
});
|
|
155
|
+
trackRow('hydrophobicity', 1, React.createElement(GutterLabel, { label: "hydro", title: React.createElement(GradientKey, { title: "Kyte-Doolittle hydrophobicity", testId: "hydrophobicity-legend", minLabel: "hydrophilic", maxLabel: "hydrophobic", colors: HYDROPHOBICITY_KEY_SCORES.map(score => hydrophobicityColor(score)), color: "inherit" }) }), React.createElement(ResidueValueTrack, { cells: hydrophobicityCells, colorFor: hydrophobicityColor, formatValue: v => `Kyte-Doolittle ${v.toFixed(1)}`, model: model }));
|
|
166
156
|
}
|
|
167
157
|
}
|
|
168
158
|
const columnAt = (event) => {
|
|
@@ -213,6 +203,9 @@ const ProteinAlignment = observer(function ProteinAlignment({ model, }) {
|
|
|
213
203
|
}
|
|
214
204
|
}
|
|
215
205
|
: undefined }, row.content))),
|
|
216
|
-
React.createElement(ColumnOverlays, { model: model }))))
|
|
206
|
+
React.createElement(ColumnOverlays, { model: model })))),
|
|
207
|
+
trackLanes > 0 ? (React.createElement(ResizeHandle, { bar: true, title: "Drag to resize the feature tracks", "data-testid": "feature-tracks-resize-handle", gain: trackLanes, onDrag: distance => {
|
|
208
|
+
model.resizeTracks(distance);
|
|
209
|
+
} })) : null));
|
|
217
210
|
});
|
|
218
211
|
export default ProteinAlignment;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import FeatureBar from './FeatureBar';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
4
|
+
// Bars are placed as fractions of the row, which alone carries the px height:
|
|
5
|
+
// dragging the resize handle then restyles one row instead of re-rendering
|
|
6
|
+
// every bar (p53 has 1,432, and a drag cost 180ms a frame).
|
|
8
7
|
const ProteinFeatureTrack = observer(function ProteinFeatureTrack({ group, model, }) {
|
|
9
|
-
const { selectedFeatureId,
|
|
10
|
-
const
|
|
11
|
-
|
|
8
|
+
const { selectedFeatureId, trackGap } = model;
|
|
9
|
+
const lanes = model.expandedFeatureTypes.has(group.type) ? group.laneCount : 1;
|
|
10
|
+
const height = `calc(${100 / lanes}% - ${trackGap}px)`;
|
|
11
|
+
return group.layouts.map(layout => (React.createElement(FeatureBar, { key: layout.feature.uniqueId, layout: layout, top: `${(Math.min(layout.lane, lanes - 1) / lanes) * 100}%`, height: height, selected: selectedFeatureId === layout.feature.uniqueId, model: model })));
|
|
12
12
|
});
|
|
13
13
|
export default ProteinFeatureTrack;
|
|
@@ -2,12 +2,12 @@ import React, { useMemo } from 'react';
|
|
|
2
2
|
import { Tooltip } from '@mui/material';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
const Cells = observer(function Cells({ cells, colorFor, model, }) {
|
|
5
|
-
const { columnWidth,
|
|
6
|
-
return (React.createElement("div", { style: { position: 'relative', height:
|
|
5
|
+
const { columnWidth, trackGap } = model;
|
|
6
|
+
return (React.createElement("div", { style: { position: 'relative', height: `calc(100% - ${trackGap}px)` } }, cells.map(cell => (React.createElement("div", { key: cell.col, style: {
|
|
7
7
|
position: 'absolute',
|
|
8
8
|
left: cell.col * columnWidth,
|
|
9
9
|
width: columnWidth,
|
|
10
|
-
height:
|
|
10
|
+
height: '100%',
|
|
11
11
|
backgroundColor: colorFor(cell.value),
|
|
12
12
|
} })))));
|
|
13
13
|
});
|
|
@@ -24,7 +24,7 @@ const ResidueValueTrack = observer(function ResidueValueTrack({ cells, colorFor,
|
|
|
24
24
|
? undefined
|
|
25
25
|
: valueByCol.get(alignmentHoverPos);
|
|
26
26
|
return (React.createElement(Tooltip, { title: hoveredValue === undefined ? '' : formatValue(hoveredValue), followCursor: true },
|
|
27
|
-
React.createElement("div",
|
|
27
|
+
React.createElement("div", { style: { height: '100%' } },
|
|
28
28
|
React.createElement(Cells, { cells: cells, colorFor: colorFor, model: model }))));
|
|
29
29
|
});
|
|
30
30
|
export default ResidueValueTrack;
|
|
@@ -4,7 +4,17 @@ export const COMPACT_TRACK_HEIGHT = 8;
|
|
|
4
4
|
export const COMPACT_TRACK_GAP = 1;
|
|
5
5
|
export const NORMAL_TRACK_HEIGHT = 12;
|
|
6
6
|
export const NORMAL_TRACK_GAP = 2;
|
|
7
|
+
export const MIN_TRACK_HEIGHT = 2;
|
|
8
|
+
export const MAX_TRACK_HEIGHT = 40;
|
|
7
9
|
export const LABEL_WIDTH = 124;
|
|
10
|
+
export function trackHeightOf({ trackHeight, compactTracks, }) {
|
|
11
|
+
return (trackHeight ?? (compactTracks ? COMPACT_TRACK_HEIGHT : NORMAL_TRACK_HEIGHT));
|
|
12
|
+
}
|
|
13
|
+
export function trackGapOf(trackHeight) {
|
|
14
|
+
return trackHeight < NORMAL_TRACK_HEIGHT
|
|
15
|
+
? COMPACT_TRACK_GAP
|
|
16
|
+
: NORMAL_TRACK_GAP;
|
|
17
|
+
}
|
|
8
18
|
export const MINOR_FEATURE_TYPES = new Set([
|
|
9
19
|
'Chain',
|
|
10
20
|
'Helix',
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import useUniProtFeatures from './useUniProtFeatures';
|
|
3
|
+
// A bond's two positions are its whole meaning, so one residue of a pair says
|
|
4
|
+
// nothing true about the structure.
|
|
5
|
+
const ENDPOINT_PAIR_TYPES = new Set(['Disulfide bond', 'Cross-link']);
|
|
3
6
|
/**
|
|
4
7
|
* Places a UniProt feature: its 1-based inclusive UniProt range becomes a
|
|
5
8
|
* 0-based half-open structure range (identity for AlphaFold, SIFTS-offset for
|
|
@@ -7,26 +10,41 @@ import useUniProtFeatures from './useUniProtFeatures';
|
|
|
7
10
|
* UniProt->structure coordinate conversion in the tracks; every consumer reads
|
|
8
11
|
* `structureStart`/`structureEnd` off the layout.
|
|
9
12
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* A feature reaching past the residues the structure has is clipped to the
|
|
14
|
+
* ones it does have, and says so (`clipped`): a crystallized fragment used to
|
|
15
|
+
* lose every region straddling the construct's ends. Returns undefined when no
|
|
16
|
+
* residue of the feature has an alignment column.
|
|
13
17
|
*/
|
|
14
18
|
export function layoutFeature(feature, structurePositionToAlignmentMap, mapUniProtPosition) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
const placed = (uniprotPos) => {
|
|
20
|
+
const structurePos = mapUniProtPosition(uniprotPos);
|
|
21
|
+
const column = structurePos === undefined
|
|
22
|
+
? undefined
|
|
23
|
+
: structurePositionToAlignmentMap[structurePos];
|
|
24
|
+
return structurePos === undefined || column === undefined
|
|
25
|
+
? undefined
|
|
26
|
+
: { structurePos, column };
|
|
27
|
+
};
|
|
28
|
+
let first = feature.start;
|
|
29
|
+
let start = placed(first);
|
|
30
|
+
while (!start && first < feature.end) {
|
|
31
|
+
start = placed(++first);
|
|
19
32
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
let last = feature.end;
|
|
34
|
+
let end = placed(last);
|
|
35
|
+
while (!end && last > first) {
|
|
36
|
+
end = placed(--last);
|
|
37
|
+
}
|
|
38
|
+
const clipped = first !== feature.start || last !== feature.end;
|
|
39
|
+
return !start || !end || (clipped && ENDPOINT_PAIR_TYPES.has(feature.type))
|
|
23
40
|
? undefined
|
|
24
41
|
: {
|
|
25
42
|
feature,
|
|
26
|
-
structureStart,
|
|
27
|
-
structureEnd:
|
|
28
|
-
alignmentStart,
|
|
29
|
-
alignmentEnd,
|
|
43
|
+
structureStart: start.structurePos,
|
|
44
|
+
structureEnd: end.structurePos + 1,
|
|
45
|
+
alignmentStart: start.column,
|
|
46
|
+
alignmentEnd: end.column,
|
|
47
|
+
clipped,
|
|
30
48
|
lane: 0,
|
|
31
49
|
};
|
|
32
50
|
}
|
|
@@ -52,6 +70,14 @@ export function packLanes(layouts) {
|
|
|
52
70
|
}
|
|
53
71
|
return Math.max(laneEnds.length, 1);
|
|
54
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Paint order for a collapsed track, where every bar shares one row: a wide
|
|
75
|
+
* bar painted last covers the short ones inside it, which can then be neither
|
|
76
|
+
* hovered nor clicked.
|
|
77
|
+
*/
|
|
78
|
+
export function widestFirst(layouts) {
|
|
79
|
+
return [...layouts].sort((a, b) => b.alignmentEnd - b.alignmentStart - (a.alignmentEnd - a.alignmentStart));
|
|
80
|
+
}
|
|
55
81
|
export default function useProteinFeatureTrackData(model, uniprotId, mapUniProtPosition) {
|
|
56
82
|
const { features, isLoading, error } = useUniProtFeatures(uniprotId);
|
|
57
83
|
const { omittedFeatureTypes, structurePositionToAlignmentMap } = model;
|
|
@@ -76,8 +102,8 @@ export default function useProteinFeatureTrackData(model, uniprotId, mapUniProtP
|
|
|
76
102
|
}
|
|
77
103
|
return [...byType].map(([type, layouts]) => ({
|
|
78
104
|
type,
|
|
79
|
-
layouts,
|
|
80
105
|
laneCount: packLanes(layouts),
|
|
106
|
+
layouts: widestFirst(layouts),
|
|
81
107
|
}));
|
|
82
108
|
}, [
|
|
83
109
|
features,
|
|
@@ -56,26 +56,17 @@ async function fetchUniProtFeatures(url) {
|
|
|
56
56
|
const start = Number.parseInt(parts[3] ?? '', 10);
|
|
57
57
|
const end = Number.parseInt(parts[4] ?? '', 10);
|
|
58
58
|
const attributes = parts[8] ?? '';
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (key === 'Note') {
|
|
64
|
-
description = decodeURIComponent(value ?? '');
|
|
65
|
-
}
|
|
66
|
-
else if (key === 'ID') {
|
|
67
|
-
id = value;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
59
|
+
const note = attributes
|
|
60
|
+
.split(';')
|
|
61
|
+
.find(attr => attr.startsWith('Note='))
|
|
62
|
+
?.slice('Note='.length);
|
|
70
63
|
if (type && start >= 1 && end >= start) {
|
|
71
|
-
const uniqueId = `${type}-${start}-${end}-${features.length}`;
|
|
72
64
|
features.push({
|
|
73
65
|
type,
|
|
74
66
|
start,
|
|
75
67
|
end,
|
|
76
|
-
description,
|
|
77
|
-
|
|
78
|
-
uniqueId,
|
|
68
|
+
description: decodeURIComponent(note ?? ''),
|
|
69
|
+
uniqueId: `${type}-${start}-${end}-${features.length}`,
|
|
79
70
|
});
|
|
80
71
|
}
|
|
81
72
|
}
|
|
@@ -4,6 +4,7 @@ import { addDisposer, getPath, types } from '@jbrowse/mobx-state-tree';
|
|
|
4
4
|
import { autorun } from 'mobx';
|
|
5
5
|
import { ALIGNMENT_ALGORITHM_VALUES, DEFAULT_ALIGNMENT_ALGORITHM, } from 'p2s_mapper';
|
|
6
6
|
import { COLOR_SCHEME_VALUES, applyColorTheme, colorSchemeLegend, } from './applyColorTheme';
|
|
7
|
+
import { MAX_TRACK_HEIGHT, MIN_TRACK_HEIGHT, trackHeightOf } from './constants';
|
|
7
8
|
import { makeSelectionFramer, structuresSettled } from './frameSelection';
|
|
8
9
|
import { makeLociChannel } from './lociChannel';
|
|
9
10
|
import { defaultDisplayName } from './proteinViewSpec';
|
|
@@ -80,6 +81,12 @@ function stateModelFactory() {
|
|
|
80
81
|
* render the feature/residue tracks at reduced height
|
|
81
82
|
*/
|
|
82
83
|
compactTracks: true,
|
|
84
|
+
/**
|
|
85
|
+
* #property
|
|
86
|
+
* px height of one feature-track lane once the tracks' resize handle
|
|
87
|
+
* has been dragged; unset, `compactTracks` decides
|
|
88
|
+
*/
|
|
89
|
+
trackHeight: types.maybe(types.number),
|
|
83
90
|
/**
|
|
84
91
|
* #property
|
|
85
92
|
* also draw the feature types in MINOR_FEATURE_TYPES and the
|
|
@@ -134,6 +141,14 @@ function stateModelFactory() {
|
|
|
134
141
|
self.height = n;
|
|
135
142
|
return n;
|
|
136
143
|
},
|
|
144
|
+
/**
|
|
145
|
+
* #action
|
|
146
|
+
* Every lane of every feature track grows by the same px, so the track
|
|
147
|
+
* area scales in proportion.
|
|
148
|
+
*/
|
|
149
|
+
resizeTracks(distance) {
|
|
150
|
+
self.trackHeight = Math.min(MAX_TRACK_HEIGHT, Math.max(MIN_TRACK_HEIGHT, trackHeightOf(self) + distance));
|
|
151
|
+
},
|
|
137
152
|
/**
|
|
138
153
|
* #action
|
|
139
154
|
*/
|
|
@@ -241,6 +256,9 @@ function stateModelFactory() {
|
|
|
241
256
|
toggleSetting(key) {
|
|
242
257
|
const value = !self[key];
|
|
243
258
|
self[key] = value;
|
|
259
|
+
if (key === 'compactTracks') {
|
|
260
|
+
self.trackHeight = undefined;
|
|
261
|
+
}
|
|
244
262
|
storeSetting(key, value);
|
|
245
263
|
},
|
|
246
264
|
/**
|
|
@@ -4,7 +4,7 @@ import { autorun, when } from 'mobx';
|
|
|
4
4
|
import { alignTranscriptToEntity, alignmentCol, alignmentQuality, chooseMappedEntity, entityLabel, fetchUniProtStructureMappings, fusionPartnerPositions, getPdbIdFromUrl, getUniprotIdFromAlphaFoldTarget, looksLikePlddt, makeCoordinateMapper, makeLabelSeqIdIndex, mappedStructureIdentity, rangeToLabelSeqIds, residueNumber, resolveStructureUrl, stripStopCodon, structureDisplayLabel, structurePos, toLabelSeqIds, unmapStructurePositions, } from 'p2s_mapper';
|
|
5
5
|
import { alignOffThread, isIdentical } from './alignOffThread';
|
|
6
6
|
import { connectedHoverTranscriptPos } from './connectedHover';
|
|
7
|
-
import { CHAR_WIDTH,
|
|
7
|
+
import { CHAR_WIDTH, MINOR_FEATURE_TYPES, trackGapOf, trackHeightOf, } from './constants';
|
|
8
8
|
import { entityAlignedTo } from './entityAlignedTo';
|
|
9
9
|
import { frameResidues } from './frameSelection';
|
|
10
10
|
import { clickProteinToGenome, navigateToProteinPosition, structureRangesToGenomeRegions, } from './proteinToGenomeMapping';
|
|
@@ -984,14 +984,17 @@ const Structure = types
|
|
|
984
984
|
return CHAR_WIDTH;
|
|
985
985
|
},
|
|
986
986
|
get trackHeight() {
|
|
987
|
-
return this.parentView
|
|
988
|
-
? COMPACT_TRACK_HEIGHT
|
|
989
|
-
: NORMAL_TRACK_HEIGHT;
|
|
987
|
+
return trackHeightOf(this.parentView);
|
|
990
988
|
},
|
|
991
989
|
get trackGap() {
|
|
992
|
-
return this.
|
|
993
|
-
|
|
994
|
-
|
|
990
|
+
return trackGapOf(this.trackHeight);
|
|
991
|
+
},
|
|
992
|
+
/**
|
|
993
|
+
* #getter
|
|
994
|
+
* What one lane of the feature tracks takes, bar and gap
|
|
995
|
+
*/
|
|
996
|
+
get laneHeight() {
|
|
997
|
+
return this.trackHeight + this.trackGap;
|
|
995
998
|
},
|
|
996
999
|
get alignmentAlgorithm() {
|
|
997
1000
|
return this.parentView.alignmentAlgorithm;
|
|
@@ -1160,6 +1163,12 @@ const Structure = types
|
|
|
1160
1163
|
setViewError(e) {
|
|
1161
1164
|
self.parentView.setError(e);
|
|
1162
1165
|
},
|
|
1166
|
+
/**
|
|
1167
|
+
* #action
|
|
1168
|
+
*/
|
|
1169
|
+
resizeTracks(distance) {
|
|
1170
|
+
self.parentView.resizeTracks(distance);
|
|
1171
|
+
},
|
|
1163
1172
|
/**
|
|
1164
1173
|
* #action
|
|
1165
1174
|
*/
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{Aa as xt,Ba as wt,Ca as we,Da as Pt,E as tt,Ea as Ct,Ha as Tt,Ja as Mt,M as rt,R as ye,U as nt,W as ot,a as M,b as xn,da as at,ea as ut,fa as dt,g as _e,ga as ct,h as wn,ha as pt,ia as Se,k as qe,ka as xe,l as Ke,m as Ge,p as je,pa as mt,q as Ye,r as Qe,t as Xe,ta as ft,u as Ze,ua as gt,v as Re,va as ht,w as et,wa as bt,xa as yt,ya as vt,za as St}from"./chunk-UAUJEOAS.js";import{a as C}from"./chunk-X5RLRQSB.js";import{c as it,d as st,f as ve,g as lt}from"./chunk-GQU3SL56.js";import{a as $}from"./chunk-533QNNGN.js";import{a as ue,b as U}from"./chunk-J236G3A2.js";import{a as x}from"./chunk-GVEKA5YG.js";import{a as Sn}from"./chunk-B3HKXTGK.js";import{a as A,c as l}from"./chunk-KVMUXFPB.js";var Ot=A((ko,Jt)=>{Jt.exports=JBrowseExports["@mui/material/Checkbox"]});var Wt=A((Io,Vt)=>{Vt.exports=JBrowseExports["@mui/material/Divider"]});var Pe=A((Eo,Nt)=>{Nt.exports=JBrowseExports["@mui/material/IconButton"]});var qt=A((Lo,_t)=>{_t.exports=JBrowseExports["@mui/material/ListItemIcon"]});var Gt=A((Bo,Kt)=>{Kt.exports=JBrowseExports["@mui/material/ListItemText"]});var Yt=A((Ao,jt)=>{jt.exports=JBrowseExports["@mui/material/Menu"]});var Xt=A((Do,Qt)=>{Qt.exports=JBrowseExports["@mui/material/MenuItem"]});var Ce=A((Fo,Zt)=>{Zt.exports=JBrowseExports["@mui/material/Tooltip"]});var Te=A((zo,rr)=>{rr.exports=JBrowseExports["@mui/material/Typography"]});var hr=A((jo,gr)=>{gr.exports=JBrowseExports["@mui/material/Chip"]});var yr=A((Yo,br)=>{br.exports=JBrowseExports["@mui/material/Link"]});var w=l(x()),N=l(ue());var kt=l($(),1),X=(0,C.createSvgIcon)((0,kt.jsx)("path",{d:"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"}),"Close");var yn=l(U()),le=l(M());var E=l(x()),T=l(U());function It(n){let e=n.next();for(;!e.done&&e.value.trim()==="";)e=n.next();return e.value}function Lt(n){let e=n.trim();return e?e.split(/\s+/).every(o=>/^\d+$/.test(o)):!1}function Pn(n){let e=n.trim();return/^\w+\s+/.test(e)&&!Lt(n)}function Cn(n){return n.length>=3&&/^\d+$/.test(n[1])}function Tn(n,e){let o=n.trim().split(/\s+/),r=o[e],i=n.indexOf(r,n.indexOf(o[0])+o[0].length);return[i,i+r.length]}function Et(n){let e=It(n),t=[],o="";if(!e)return;for(;e;)Lt(e)||(Pn(e)?t.push(e):o=e),e=n.next().value;if(t.length===0)return;let r=t.map(P=>P.trim().split(/\s+/)),i=r[0];if(!i)return;let s=Cn(i)?2:1,[a,u]=Tn(t[0],s),h=r.map(P=>P[0]),d=r.map(P=>P[s]),b=o.slice(a,u),v=d[0];if(v){let P=v.length-b.length;P>0&&(b+=" ".repeat(P))}return{ids:h,seqs:d,consensus:b}}function Bt(n){let e,t=Et(n);if(t!==void 0)for(;e=Et(n);){for(let o=0;o<e.seqs.length;o++)t.seqs[o]+=e.seqs[o];t.consensus+=e.consensus}return t}function At(n){let e=n.split(`
|
|
2
|
+
`).filter(s=>!s.startsWith("#")).join(`
|
|
3
|
+
`),t=Bt(e.split(`
|
|
4
|
+
`)[Symbol.iterator]());if(t===void 0)throw new Error("No blocks parsed");let o=t.seqs.map((s,a)=>({id:t.ids[a],seq:s})),{consensus:r}=t,i=o[0];if(!i)throw new Error("No alignments found");if(r.length!==i.seq.length)throw new Error(`Consensus length !== sequence length. Con ${r.length} seq ${i.seq.length}`);if(o.length!==2)throw new Error(`Expected exactly 2 sequences in pairwise alignment, got ${o.length}`);return{consensus:r,alns:o}}var Dt=l(M());var Mn=(0,Dt.observer)(function({model:e}){let[t,o]=(0,E.useState)(""),[r,i]=(0,E.useState)(),{showManualAlignmentDialog:s,alignmentStructure:a}=e,u=()=>{o(""),i(void 0),e.setShowManualAlignmentDialog(!1)},h=()=>{if(t.trim())try{let d=At(t.trim()),b=a?.entities,v=a&&b?Ct(d,a.userProvidedTranscriptSequence,b,a.mappedEntityId):void 0;!a||!v?i("No structure loaded to apply alignment to"):"problem"in v?i(v.problem):(a.importAlignment(Pt(d)),u())}catch(d){i(`Failed to parse alignment: ${d}`)}};return s?E.default.createElement(T.Dialog,{open:!0,onClose:u,maxWidth:"md",fullWidth:!0},E.default.createElement(T.DialogTitle,null,"Import manual alignment"),E.default.createElement(T.DialogContent,null,E.default.createElement(T.Typography,{variant:"body2",color:"text.secondary",sx:{mb:2}},"Paste a pre-computed alignment in Clustal format. The first sequence should be the transcript and the second should be the structure."),E.default.createElement(T.TextField,{multiline:!0,rows:12,fullWidth:!0,placeholder:`Example:
|
|
5
|
+
transcript MKAAYLSMFGKEDHKPFGDDEVELFRAVPGLKLKIAG
|
|
6
|
+
|||||||||||||||||||||||||||||||||||||
|
|
7
|
+
structure MKAAYLSMFGKEDHKPFGDDEVELFRAVPGLKLKIAG`,value:t,onChange:d=>{o(d.target.value),i(void 0)},slotProps:{htmlInput:{style:{fontFamily:"monospace",fontSize:12}}}}),r?E.default.createElement(T.Typography,{color:"error",variant:"body2",sx:{mt:1}},r):null),E.default.createElement(T.DialogActions,null,E.default.createElement(T.Button,{onClick:()=>{u()}},"Cancel"),E.default.createElement(T.Button,{onClick:()=>{h()},variant:"contained",color:"primary",disabled:!t.trim()},"Apply alignment"))):null}),Ft=Mn;var g=l(x()),dn=l(ue());var Ht=l($(),1),$t=(0,C.createSvgIcon)((0,Ht.jsx)("path",{d:"M12 2C6.49 2 2 6.49 2 12s4.49 10 10 10c1.38 0 2.5-1.12 2.5-2.5 0-.61-.23-1.2-.64-1.67-.08-.1-.13-.21-.13-.33 0-.28.22-.5.5-.5H16c3.31 0 6-2.69 6-6 0-4.96-4.49-9-10-9m5.5 11c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5m-3-4c-.83 0-1.5-.67-1.5-1.5S13.67 6 14.5 6s1.5.67 1.5 1.5S15.33 9 14.5 9M5 11.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5S7.33 13 6.5 13 5 12.33 5 11.5m6-4c0 .83-.67 1.5-1.5 1.5S8 8.33 8 7.5 8.67 6 9.5 6s1.5.67 1.5 1.5"}),"Palette");var Ut=l($(),1),zt=(0,C.createSvgIcon)((0,Ut.jsx)("path",{d:"M3 17v2h6v-2zM3 5v2h10V5zm10 16v-2h8v-2h-8v-2h-2v6zM7 9v2H3v2h4v2h2V9zm14 4v-2H11v2zm-6-4h2V7h4V5h-4V3h-2z"}),"Tune");var cn=l(Ot()),pn=l(Wt()),$e=l(Pe()),mn=l(qt()),fn=l(Gt()),Ue=l(Yt()),ze=l(Xt()),Je=l(Ce()),fe=l(M());var p=l(x()),Rt=l(ue()),m=l(U()),er=l(M());var kn=(0,er.observer)(function({model:e}){let[t,o]=(0,p.useState)(),[r,i]=(0,p.useState)(""),[s,a]=(0,p.useState)(""),[u,h]=(0,p.useState)("pdb"),[d,b]=(0,p.useState)(""),[v,P]=(0,p.useState)(),[_,ee]=(0,p.useState)(!0),{showAddStructureDialog:te}=e,Y=!!e.primaryStructure?.userProvidedTranscriptSequence,q=()=>{o(void 0),i(""),a(""),b(""),P(void 0),e.setShowAddStructureDialog(!1)},O=async()=>{try{let S=u==="pdb"&&r?{pdbId:r}:u==="uniprot"&&s?{uniprotId:s}:u==="url"&&d?{url:d}:u==="file"&&t?{data:await ct(t)}:void 0;if(S){let F=e.primaryStructure,re=_&&F?.userProvidedTranscriptSequence?{userProvidedTranscriptSequence:F.userProvidedTranscriptSequence,feature:F.feature,connectedViewId:F.connectedViewId}:{};e.addStructure({...S,...re}),q()}}catch(S){console.error(S),P(S)}};if(!te)return null;let K=r!==""&&!ye(r),B=u==="url"&&d!==""||u==="file"&&t!==void 0||u==="pdb"&&ye(r)||u==="uniprot"&&s!=="";return p.default.createElement(m.Dialog,{open:!0,onClose:q,maxWidth:"sm",fullWidth:!0},p.default.createElement(m.DialogTitle,null,"Add structure"),p.default.createElement(m.DialogContent,null,v?p.default.createElement(Rt.ErrorMessage,{error:v}):null,p.default.createElement(m.Typography,{variant:"body2",color:"text.secondary",sx:{mb:2}},"Add another structure to superpose on the existing structure(s)."),p.default.createElement(m.FormControl,{component:"fieldset",sx:{mb:2}},p.default.createElement(m.RadioGroup,{value:u,onChange:S=>{h(S.target.value)}},p.default.createElement(m.FormControlLabel,{value:"pdb",control:p.default.createElement(m.Radio,null),label:"PDB ID"}),p.default.createElement(m.FormControlLabel,{value:"uniprot",control:p.default.createElement(m.Radio,null),label:"UniProt ID (AlphaFold)"}),p.default.createElement(m.FormControlLabel,{value:"url",control:p.default.createElement(m.Radio,null),label:"URL"}),p.default.createElement(m.FormControlLabel,{value:"file",control:p.default.createElement(m.Radio,null),label:"File"}))),u==="pdb"?p.default.createElement(m.TextField,{fullWidth:!0,value:r,onChange:S=>{i(S.target.value.trim().toUpperCase())},label:"PDB ID (e.g. 1CRN)",placeholder:"Enter PDB ID",error:K,helperText:K?"A PDB ID is four characters beginning with a digit":void 0,sx:{mb:2}}):null,u==="uniprot"?p.default.createElement(m.TextField,{fullWidth:!0,value:s,onChange:S=>{a(S.target.value.trim().toUpperCase())},label:"UniProt ID (e.g. P04637)",placeholder:"Enter UniProt ID",helperText:"Fetches the AlphaFold predicted structure",sx:{mb:2}}):null,u==="url"?p.default.createElement(m.TextField,{fullWidth:!0,label:"Structure URL",value:d,onChange:S=>{b(S.target.value.trim())},placeholder:"https://files.rcsb.org/download/1CRN.cif",sx:{mb:2}}):null,u==="file"?p.default.createElement("div",{style:{marginBottom:16}},p.default.createElement(m.Button,{variant:"outlined",component:"label"},t?t.name:"Choose file",p.default.createElement("input",{type:"file",hidden:!0,accept:pt,onChange:({target:S})=>{let F=S.files?.[0];F&&o(F)}})),t?p.default.createElement(m.Typography,{variant:"body2",sx:{mt:1}},"Selected: ",t.name):null):null,Y?p.default.createElement(m.FormControlLabel,{control:p.default.createElement(m.Checkbox,{checked:_,onChange:S=>{ee(S.target.checked)}}),label:"Map to the same transcript as the first structure (alignment, hover and click linked to the genome)"}):null,p.default.createElement(m.Typography,{variant:"body2",color:"text.secondary",sx:{mt:1}},"Tip: Structures will be automatically superposed using TM-align. For manual control, use the Mol* controls (\u{1F527} wrench icon).")),p.default.createElement(m.DialogActions,null,p.default.createElement(m.Button,{onClick:()=>{q()}},"Cancel"),p.default.createElement(m.Button,{onClick:()=>{O()},variant:"contained",color:"primary",disabled:!B},"Add structure")))}),tr=kn;var H=l(x()),nr=l(Te()),or={width:8,height:8,border:"1px solid rgba(0,0,0,0.3)"};function ir({title:n,testId:e,color:t,children:o}){return H.default.createElement(nr.default,{variant:"caption",color:t,component:"div","data-testid":e,style:{display:"flex",flexWrap:"wrap",alignItems:"center",gap:6,fontSize:9,paddingLeft:8}},H.default.createElement("span",null,n,":"),o)}function ke({title:n,entries:e,testId:t="track-legend",color:o="textSecondary"}){return H.default.createElement(ir,{title:n,testId:t,color:o},e.map(({label:r,color:i},s)=>H.default.createElement("span",{key:`${s}-${r}`,style:{display:"inline-flex",alignItems:"center",gap:3}},H.default.createElement("span",{style:{...or,background:i}}),r)))}function Ie({title:n,minLabel:e,maxLabel:t,colors:o,testId:r="track-legend",color:i="textSecondary"}){return H.default.createElement(ir,{title:n,testId:r,color:i},e,H.default.createElement("span",{style:{...or,width:60,background:`linear-gradient(to right, ${o.join(", ")})`}}),t)}function Me(n){return`#${n.toString(16).padStart(6,"0")}`}function In(n){return n.replaceAll(/([a-z])([A-Z])(?=[a-z])/g,(e,t,o)=>`${t} ${o.toLowerCase()}`)}function sr({title:n,legend:e}){return e.kind==="table-legend"?H.default.createElement(ke,{title:n,testId:"structure-legend",entries:e.table.map(([t,o])=>({label:In(t),color:Me(o)}))}):H.default.createElement(Ie,{title:n,testId:"structure-legend",minLabel:e.minLabel,maxLabel:e.maxLabel,colors:e.colors.map(t=>Array.isArray(t)?`${Me(t[0])} ${100*t[1]}%`:Me(t))})}var y=l(x());var lr=l($(),1),ar=(0,C.createSvgIcon)((0,lr.jsx)("path",{d:"M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"}),"ChevronRight");var ur=l($(),1),dr=(0,C.createSvgIcon)((0,ur.jsx)("path",{d:"M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z"}),"ExpandMore");var cr=l($(),1),pr=(0,C.createSvgIcon)((0,cr.jsx)("path",{d:"M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5M12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5m0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3"}),"Visibility");var mr=l($(),1),fr=(0,C.createSvgIcon)((0,mr.jsx)("path",{d:"M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7M2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2m4.31-.78 3.15 3.15.02-.16c0-1.66-1.34-3-3-3z"}),"VisibilityOff");var wr=l(hr()),ce=l(Pe()),Pr=l(yr()),j=l(Ce()),ne=l(Te()),pe=l(M());var de=l(x()),Z=l(U()),vr=l(M());var En=(0,vr.observer)(function({model:e}){let{entities:t,mappedEntity:o,userProvidedTranscriptSequence:r}=e;return!t||t.length<2?null:de.default.createElement(Z.Tooltip,{title:r?"Mapped chain: the one the transcript maps to":"Chain: the one hovers read",placement:"left"},de.default.createElement(Z.TextField,{select:!0,size:"small",variant:"standard","data-testid":"protein-mapped-chain",value:e.pendingEntityId??o?.entityId??"",onChange:i=>{e.chooseEntity(i.target.value)},slotProps:{input:{disableUnderline:!0,sx:{fontSize:12}},htmlInput:{"aria-label":"Mapped chain"}},sx:{minWidth:60}},t.map(i=>de.default.createElement(Z.MenuItem,{key:i.entityId,value:i.entityId,dense:!0},tt(i)))))}),Sr=En;function xr(n){return n.aligned===0?"no residues aligned":[`${Math.round(n.identity*100)}% identity`,n.aligned===n.transcriptLength?"whole transcript":`transcript ${n.transcriptStart}\u2013${n.transcriptEnd} (${n.aligned} of ${n.transcriptLength})`,n.aligned<n.structureLength?`${n.aligned} of ${n.structureLength} structure residues`:void 0].filter(Boolean).join(" \xB7 ")}var Ln=`Under ${Math.round(je*100)}% of the shorter sequence is identical (${Math.round(Qe*100)}% for an alignment of fewer than ${Ye} residues): an alignment this weak is what two unrelated proteins produce, so the positions it maps may be unrelated. Check the mapped chain, the transcript isoform, or import a curated alignment.`,Bn=(0,pe.observer)(function({structure:e}){let{uniprotId:t,uniprotName:o}=e.uniProtEntry;return t?y.default.createElement(j.default,{title:`The mapped chain is UniProt ${t}${o?` (${o})`:""}, which its feature tracks annotate`},y.default.createElement(Pr.default,{variant:"caption",href:ot(t),target:"_blank",rel:"noreferrer",noWrap:!0,sx:{minWidth:40}},"UniProt ",t)):null}),An=(0,pe.observer)(function({model:e,structure:t,readout:o}){let{label:r,alignmentQuality:i,statusMessage:s,hidden:a}=t,u=e.structures.length>1,h=e.showAlignment&&u,d=e.alignmentStructure===t,b=i?et(i):void 0;return y.default.createElement("div",{"data-testid":"structure-row","data-label":r,"data-open":h?d:void 0,"data-hidden":a||void 0,style:{opacity:a?.6:void 0,display:"flex",alignItems:"center",gap:8,minHeight:24,minWidth:0}},h?y.default.createElement(j.default,{title:d?"Alignment shown below":"Show alignment"},y.default.createElement("span",null,y.default.createElement(ce.default,{size:"small","aria-label":`Show ${r} alignment`,"aria-expanded":d,disabled:d,onClick:()=>{e.openAlignmentOf(t)},sx:{p:0}},d?y.default.createElement(dr,{fontSize:"small"}):y.default.createElement(ar,{fontSize:"small"})))):null,y.default.createElement(ne.default,{variant:"caption",sx:{fontWeight:"bold",flexShrink:0}},r),s?y.default.createElement(ne.default,{variant:"caption",color:"error","data-testid":"structure-status"},s):null,o?y.default.createElement(ne.default,{variant:"caption",noWrap:!0,title:o,"data-testid":"structure-hover"},o):i?y.default.createElement(j.default,{title:i.aligned===0?"":`${Re(i)}${b?`, ${b}`:""}; ${Ze(i)}`},y.default.createElement(ne.default,{variant:"caption",color:"textSecondary",noWrap:!0,"data-testid":"header-alignment-quality"},xr(i))):null,y.default.createElement(Bn,{structure:t}),y.default.createElement("div",{style:{flex:1}}),d&&e.showAlignment||!t.userProvidedTranscriptSequence?y.default.createElement(Sr,{model:t}):null,i&&Xe(i)?y.default.createElement(j.default,{title:Ln},y.default.createElement(wr.default,{size:"small",color:"warning",variant:"outlined",label:"low similarity","data-testid":"header-low-similarity"})):null,u||a?y.default.createElement(j.default,{title:a?`Show ${r}`:`Hide ${r}`},y.default.createElement(ce.default,{size:"small","aria-label":a?`Show ${r}`:`Hide ${r}`,"aria-pressed":a,onClick:()=>{t.setHidden(!a)},sx:{p:.25}},a?y.default.createElement(fr,{fontSize:"small"}):y.default.createElement(pr,{fontSize:"small"}))):null,y.default.createElement(j.default,{title:`Remove ${r}`},y.default.createElement(ce.default,{size:"small","aria-label":`Remove ${r}`,onClick:()=>{e.removeStructure(t)},sx:{p:.25}},y.default.createElement(X,{fontSize:"small"}))))}),Dn=(0,pe.observer)(function({model:e}){let{structures:t}=e,o=t.some(r=>r.hoverPosition&&r.hoverPosition.source!=="structure");return y.default.createElement("div",{style:{flex:1,minWidth:0}},t.map((r,i)=>y.default.createElement(An,{key:i,model:e,structure:r,readout:r.hoverString||(o&&r.genomeToTranscriptSeqMapping?"not in structure":"")})))}),Cr=Dn;var f=l(x()),tn=l(ue()),rn=l(U()),nn=l(mt()),on=l(M());var sn=l(xn());var Ee=l(x()),Tr=l(M());function Fn(n,e,t){let o=[];if(!n)return o;for(let r=0;r<e;r++){let i=n[r];if(i===void 0)continue;let s=t(i);s%10===0?o.push({col:r,label:`${s}`}):s%5===0&&o.push({col:r})}return o}var Hn=(0,Tr.observer)(function({model:e,columns:t}){let{columnWidth:o}=e,r=Fn(e.pairwiseAlignmentToStructurePosition,t,i=>e.residueNumber(i));return Ee.default.createElement("div",{style:{position:"relative",height:"100%",color:"#888",fontSize:8}},r.map(({col:i,label:s})=>Ee.default.createElement("span",{key:i,style:{position:"absolute",left:i*o,top:0,bottom:0,borderLeft:"1px solid #aaa",paddingLeft:2,lineHeight:`${12}px`,whiteSpace:"nowrap"}},s)))}),Mr=Hn;var D=l(x()),Le=l(M());function oe({start:n,end:e,columnWidth:t,background:o,border:r}){return D.default.createElement("div",{style:{position:"absolute",top:0,bottom:0,left:n*t,width:(e-n+1)*t,background:o,border:r,boxSizing:"border-box",pointerEvents:"none"}})}function kr({zIndex:n,children:e}){return D.default.createElement("div",{style:{position:"absolute",inset:0,pointerEvents:"none",zIndex:n}},e)}var Ir=(0,Le.observer)(function({model:e,matchHeight:t}){let{clickAlignmentRanges:o,columnWidth:r,showHighlight:i,alignmentMatchSet:s}=e;return D.default.createElement(kr,{zIndex:0},i&&s?D.default.createElement("div",{style:{position:"relative",height:t}},Mt(s).map(a=>D.default.createElement(oe,{key:a.start,start:a.start,end:a.end-1,columnWidth:r,background:yt}))):null,o.map(a=>D.default.createElement(oe,{key:a.start,start:a.start,end:a.end,columnWidth:r,background:ht})))}),$n=(0,Le.observer)(function({model:e}){let{clickAlignmentRanges:t,alignmentHoverRange:o,alignmentHoverPos:r,columnWidth:i}=e;return D.default.createElement(kr,{zIndex:1},t.map(s=>D.default.createElement(oe,{key:s.start,start:s.start,end:s.end,columnWidth:i,background:"transparent",border:bt})),o?D.default.createElement(oe,{start:o.start,end:o.end,columnWidth:i,background:gt}):null,r===void 0?null:D.default.createElement(oe,{start:r,end:r,columnWidth:i,background:ft}))}),Er=$n;var z=l(x());var Lr=l($(),1),Br=(0,C.createSvgIcon)((0,Lr.jsx)("path",{d:"M7.41 18.59 8.83 20 12 16.83 15.17 20l1.41-1.41L12 14zm9.18-13.18L15.17 4 12 7.17 8.83 4 7.41 5.41 12 10z"}),"UnfoldLess");var Ar=l($(),1),Dr=(0,C.createSvgIcon)((0,Ar.jsx)("path",{d:"M12 5.83 15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15z"}),"UnfoldMore");var ie=l(U()),Fr=l(M());var zn=(0,Fr.observer)(function({type:e,laneCount:t,model:o}){let r=o.expandedFeatureTypes.has(e),i=Math.min(o.trackHeight,12);return z.default.createElement(ie.Tooltip,{title:e,placement:"left"},z.default.createElement("div",{style:{height:"100%",whiteSpace:"nowrap",display:"flex",alignItems:r?"flex-start":"center",justifyContent:"flex-end",gap:2}},z.default.createElement(ie.IconButton,{onClick:()=>{o.hideFeatureType(e)},title:`Hide ${e} track`,sx:{p:0,color:we}},z.default.createElement(X,{sx:{fontSize:i}})),t>1?z.default.createElement(ie.IconButton,{onClick:()=>{o.toggleFeatureTypeExpanded(e)},title:r?`Collapse ${e} track`:`Expand ${e} track (${t} overlapping rows)`,sx:{p:0,color:we}},r?z.default.createElement(Br,{sx:{fontSize:i}}):z.default.createElement(Dr,{sx:{fontSize:i}})):null,z.default.createElement("span",null,e)))}),Hr=zn;var Ur=l(x()),zr=l(M());function Jn(n,e,t){if(!(!n||!e||n==="-"||e==="-"||n.toUpperCase()===e.toUpperCase()))return t===":"||t==="."?"similar":"different"}function $r(n){let e=n.alns[0]?.seq??"",t=n.alns[1]?.seq??"",o=[];for(let r=0;r<e.length;r++){let i=Jn(e[r],t[r],n.consensus[r]),s=o.at(-1);i&&s?.kind===i&&s.end===r?s.end=r+1:i&&o.push({start:r,end:r+1,kind:i})}return o}var On=(0,zr.observer)(function({model:e}){let{alignment:t,columnWidth:o}=e;return t?$r(t).map(r=>Ur.default.createElement("div",{key:r.start,style:{position:"absolute",top:0,bottom:0,left:r.start*o,width:(r.end-r.start)*o,background:r.kind==="similar"?vt:St}})):null}),Jr=On;var _r=l(x()),qr=l(M());var L=l(x()),Vr=l(U()),Wr=l(M());var Vn={Domain:"#1f77b4","DNA binding":"#ff7f0e",Region:"#2ca02c","Zinc finger":"#d62728","Coiled coil":"#9467bd",Motif:"#8c564b","Compositional bias":"#e377c2",Repeat:"#7f7f7f",Transmembrane:"#bcbd22",Intramembrane:"#17becf","Topological domain":"#aec7e8",Signal:"#ffbb78","Signal peptide":"#ffbb78",Propeptide:"#98df8a","Transit peptide":"#ff9896",Chain:"#c5b0d5","Disulfide bond":"#c49c94","Active site":"#f7b6d2","Binding site":"#c7c7c7",Site:"#dbdb8d","Modified residue":"#9edae5",Glycosylation:"#393b79",Lipidation:"#637939","Cross-link":"#8c6d31","Alternative sequence":"#e7969c","Natural variant":"#de9ed6",Mutagenesis:"#ad494a","Sequence conflict":"#b5cf6b",Helix:"#e7ba52","Beta strand":"#6b6ecf",Turn:"#d6616b","Initiator methionine":"#ce6dbd",Peptide:"#6baed6","Calcium binding":"#fd8d3c","Nucleotide binding":"#74c476"};function Or(n){return Vn[n]??"#999999"}async function Wn(n){let e=await(await rt(n)).text(),t=[];for(let o of e.split(`
|
|
8
|
+
`)){if(o.startsWith("#")||!o.trim())continue;let r=o.split(" ");if(r.length<9)continue;let i=r[2],s=Number.parseInt(r[3]??"",10),a=Number.parseInt(r[4]??"",10),h=(r[8]??"").split(";").find(d=>d.startsWith("Note="))?.slice(5);i&&s>=1&&a>=s&&t.push({type:i,start:s,end:a,description:decodeURIComponent(h??""),uniqueId:`${i}-${s}-${a}-${t.length}`})}return t}function Be(n){let{data:e,error:t,isLoading:o}=at(n?nt(n):null,Wn,ut);return{features:e,error:t,isLoading:o}}function Nn({feature:n,layout:e,model:t}){let o=t.residueNumber(e.structureStart),r=t.residueNumber(e.structureEnd-1),i=o!==n.start||r!==n.end;return L.default.createElement("div",null,L.default.createElement("div",null,L.default.createElement("strong",null,n.type)),L.default.createElement("div",null,"UniProt position: ",n.start,"-",n.end),i?L.default.createElement("div",null,"Structure residue: ",o,"-",r,e.clipped?" (the structure lacks the rest)":""):null,n.description?L.default.createElement("div",null,n.description):null)}var _n=(0,Wr.observer)(function({layout:e,top:t,height:o,selected:r,model:i}){let[s,a]=(0,L.useState)(!1),{feature:u,alignmentStart:h,alignmentEnd:d}=e,{columnWidth:b}=i;return L.default.createElement(Vr.Tooltip,{title:L.default.createElement(Nn,{feature:u,layout:e,model:i}),followCursor:!0},L.default.createElement("div",{"data-testid":`protein-feature-${u.type}`,"data-feature-id":u.uniqueId,"data-feature-start":u.start,"data-feature-end":u.end,onClick:()=>{r?(i.setSelectedFeatureId(void 0),i.setClickedStructureRanges([])):(i.setSelectedFeatureId(u.uniqueId),Tt({model:i,structureSeqPos:e.structureStart,structureSeqEndPos:e.structureEnd}).catch(v=>{console.error(v),i.setViewError(v)}))},onMouseEnter:()=>{a(!0),i.setAlignmentHoverRange({start:h,end:d})},onMouseLeave:()=>{a(!1),i.setAlignmentHoverRange(void 0)},style:{position:"absolute",left:h*b,top:t,width:(d-h+1)*b,height:o,backgroundColor:Or(u.type),opacity:s||r?.9:.6,cursor:"pointer",borderRadius:2,border:r?xt:s?wt:"none",boxSizing:"border-box"}}))}),Nr=_n;var qn=(0,qr.observer)(function({group:e,model:t}){let{selectedFeatureId:o,trackGap:r}=t,i=t.expandedFeatureTypes.has(e.type)?e.laneCount:1,s=`calc(${100/i}% - ${r}px)`;return e.layouts.map(a=>_r.default.createElement(Nr,{key:a.feature.uniqueId,layout:a,top:`${Math.min(a.lane,i-1)/i*100}%`,height:s,selected:o===a.feature.uniqueId,model:t}))}),Kr=qn;var W=l(x()),Gr=l(U()),Ae=l(M()),Kn=(0,Ae.observer)(function({cells:e,colorFor:t,model:o}){let{columnWidth:r,trackGap:i}=o;return W.default.createElement("div",{style:{position:"relative",height:`calc(100% - ${i}px)`}},e.map(s=>W.default.createElement("div",{key:s.col,style:{position:"absolute",left:s.col*r,width:r,height:"100%",backgroundColor:t(s.value)}})))}),Gn=(0,Ae.observer)(function({cells:e,colorFor:t,formatValue:o,model:r}){let i=(0,W.useMemo)(()=>new Map(e.map(u=>[u.col,u.value])),[e]),{alignmentHoverPos:s}=r,a=s===void 0?void 0:i.get(s);return W.default.createElement(Gr.Tooltip,{title:a===void 0?"":o(a),followCursor:!0},W.default.createElement("div",{style:{height:"100%"}},W.default.createElement(Kn,{cells:e,colorFor:t,model:r})))}),De=Gn;var jr=l(x()),Yr=l(M()),jn=(0,Yr.observer)(function({model:e,str:t}){let{columnWidth:o}=e;return t.split("").map((r,i)=>jr.default.createElement("span",{key:i,style:{position:"absolute",left:i*o,width:o}},r===" "?"\xA0":r))}),Qr=jn;var Xr=l(mt());function Yn({x:n,width:e,scrollLeft:t,clientWidth:o}){return n>=t&&n+e<=t+o?void 0:n+e/2-o/2}function Zr({start:n,end:e,scrollLeft:t,clientWidth:o}){let r=t+o;return e>=t&&n<=r?void 0:(n+e)/2-o/2}function Rr(n,e){return(0,Xr.reaction)(()=>n.alignmentHoverPos,t=>{let o=e();if(t!==void 0&&o&&n.autoScrollAlignment&&!n.isMouseInAlignment){let r=Yn({x:t*n.columnWidth,width:n.columnWidth,scrollLeft:o.scrollLeft,clientWidth:o.clientWidth});r!==void 0&&(o.scrollLeft=r)}})}var en=l(x());var Qn=new Set(["Disulfide bond","Cross-link"]);function Xn(n,e,t){let o=h=>{let d=t(h),b=d===void 0?void 0:e[d];return d===void 0||b===void 0?void 0:{structurePos:d,column:b}},r=n.start,i=o(r);for(;!i&&r<n.end;)i=o(++r);let s=n.end,a=o(s);for(;!a&&s>r;)a=o(--s);let u=r!==n.start||s!==n.end;return!i||!a||u&&Qn.has(n.type)?void 0:{feature:n,structureStart:i.structurePos,structureEnd:a.structurePos+1,alignmentStart:i.column,alignmentEnd:a.column,clipped:u,lane:0}}function Zn(n){let e=[],t=[...n].sort((o,r)=>o.alignmentStart-r.alignmentStart);for(let o of t){let r=e.findIndex(i=>i<o.alignmentStart);r===-1?(o.lane=e.length,e.push(o.alignmentEnd)):(o.lane=r,e[r]=o.alignmentEnd)}return Math.max(e.length,1)}function Rn(n){return[...n].sort((e,t)=>t.alignmentEnd-t.alignmentStart-(e.alignmentEnd-e.alignmentStart))}function Fe(n,e,t){let{features:o,isLoading:r,error:i}=Be(e),{omittedFeatureTypes:s,structurePositionToAlignmentMap:a}=n;return{groups:(0,en.useMemo)(()=>{if(!o||!a)return;let h=new Map;for(let d of o)if(!s.has(d.type)){let b=Xn(d,a,t);if(b){let v=h.get(d.type);v?v.push(b):h.set(d.type,[b])}}return[...h].map(([d,b])=>({type:d,laneCount:Zn(b),layouts:Rn(b)}))},[o,s,a,t]),isLoading:r,error:i}}var to=(0,sn.makeStyles)()(n=>({scroll:{overflow:"auto",whiteSpace:"nowrap",flex:1,paddingBottom:2,backgroundColor:n.palette.background.paper},trackMessage:{position:"sticky",left:0,lineHeight:`${12}px`,color:n.palette.text.secondary},trackError:{color:n.palette.error.main}}));function se({label:n,title:e}){return f.default.createElement(rn.Tooltip,{title:e,placement:"left"},f.default.createElement("div",{style:{height:"100%",display:"flex",alignItems:"center",justifyContent:"flex-end"}},n))}var ro=(0,on.observer)(function({model:e}){let{alignment:t,showProteinTracks:o,showAllFeatureTracks:r,label:i,confidenceCells:s,columnWidth:a,laneHeight:u}=e,h=r?e.hydrophobicityCells:[],{classes:d,cx:b}=to(),v=(0,f.useRef)(null),P=(0,f.useRef)(void 0),{uniprotId:_,mapUniProtPosition:ee,isLoading:te,error:Y}=e.uniProtEntry,{groups:q,isLoading:O,error:K}=Fe(e,_,ee),B=te||O,S=Y??K,F=S?`${Y?`Could not map ${i} to a UniProt entry through SIFTS`:`Could not load UniProt features for ${_??i}`}: ${_e(S)}`:void 0;if((0,f.useEffect)(()=>Rr(e,()=>v.current),[e]),(0,f.useEffect)(()=>(0,nn.autorun)(()=>{let c=v.current,k=e.clickAlignmentRanges,I=k[0];if(c)if(I){let Q=k.map(G=>`${G.start}-${G.end}`).join(",");if(Q!==P.current){P.current=Q;let G=Zr({start:I.start*e.columnWidth,end:(I.end+1)*e.columnWidth,scrollLeft:c.scrollLeft,clientWidth:c.clientWidth});G!==void 0&&(c.scrollLeft=G)}}else P.current=void 0}),[e]),!t)return null;let re=Ge(t),Ve=(c,k,I,Q,G)=>({key:c,height:12,label:f.default.createElement(se,{label:k,title:I}),content:f.default.createElement("div",{style:{lineHeight:`${12}px`}},G,f.default.createElement(Qr,{model:e,str:Q})),selectsResidue:!0}),ge=F??(B?"Loading UniProt features...":""),We=[Ve("transcript","GENOME","The protein as the reference genome's transcript translates",qe(t)),Ve("structure","STRUCT","The protein as the structure file spells it. Shaded where it differs from the transcript's: amber for a similar amino acid, red for a different one.",Ke(t),f.default.createElement(Jr,{model:e}))],ae=[...We,{key:"ruler",height:12,label:f.default.createElement(se,{label:"residue",title:"Residue numbers as the structure's authors assigned them, the numbering papers and the 3D view's hover label use"}),content:f.default.createElement(Mr,{model:e,columns:re}),selectsResidue:!0}],vn=We.length*12,he=0,be=(c,k,I,Q)=>{he+=k,ae.push({key:c,height:k*u,label:I,content:Q})};if(o){ge&&ae.push({key:"uniprot-status",height:12,label:f.default.createElement(se,{label:"UniProt",title:ge}),content:f.default.createElement("span",{className:b(d.trackMessage,F&&d.trackError)},ge)});for(let c of q??[])be(`feature-${c.type}`,e.expandedFeatureTypes.has(c.type)?c.laneCount:1,f.default.createElement(Hr,{type:c.type,laneCount:c.laneCount,model:e}),f.default.createElement(Kr,{group:c,model:e}));s.length>0&&be("plddt",1,f.default.createElement(se,{label:"pLDDT",title:f.default.createElement(ke,{title:"AlphaFold per-residue confidence (pLDDT)",entries:it,color:"inherit"})}),f.default.createElement(De,{cells:s,colorFor:st,formatValue:c=>`pLDDT ${c.toFixed(0)}`,model:e})),h.length>0&&be("hydrophobicity",1,f.default.createElement(se,{label:"hydro",title:f.default.createElement(Ie,{title:"Kyte-Doolittle hydrophobicity",testId:"hydrophobicity-legend",minLabel:"hydrophilic",maxLabel:"hydrophobic",colors:lt.map(c=>ve(c)),color:"inherit"})}),f.default.createElement(De,{cells:h,colorFor:ve,formatValue:c=>`Kyte-Doolittle ${c.toFixed(1)}`,model:e}))}let Ne=c=>{let{left:k}=c.currentTarget.getBoundingClientRect(),I=Math.floor((c.clientX-k)/a);return I>=0&&I<re?I:void 0};return f.default.createElement("div",{"data-testid":"protein-alignment-panel","data-structure":i},f.default.createElement("div",{style:{display:"flex",fontSize:9,fontFamily:"monospace",margin:"2px 8px 4px"},onMouseEnter:()=>{e.setIsMouseInAlignment(!0)},onMouseLeave:()=>{e.leaveAlignment()}},f.default.createElement("div",{style:{flexShrink:0,minWidth:124,paddingRight:4,whiteSpace:"nowrap",lineHeight:1}},ae.map(c=>f.default.createElement("div",{key:c.key,"data-row-label":c.key,style:{height:c.height,overflow:"hidden"}},c.label))),f.default.createElement("div",{ref:v,className:d.scroll},f.default.createElement("div",{"data-testid":"alignment-rows",style:{position:"relative",width:re*a},onMouseMove:c=>{let k=Ne(c);k===void 0?e.setHoveredPosition(void 0):e.hoverAlignmentPosition(k)},onMouseLeave:()=>{e.setHoveredPosition(void 0)}},f.default.createElement(Ir,{model:e,matchHeight:vn}),ae.map(c=>f.default.createElement("div",{key:c.key,"data-row":c.key,style:{position:"relative",height:c.height,cursor:c.selectsResidue?"pointer":void 0},onClick:c.selectsResidue?k=>{let I=Ne(k);I!==void 0&&e.clickAlignmentPosition(I)}:void 0},c.content)),f.default.createElement(Er,{model:e})))),he>0?f.default.createElement(tn.ResizeHandle,{bar:!0,title:"Drag to resize the feature tracks","data-testid":"feature-tracks-resize-handle",gain:he,onDrag:c=>{e.resizeTracks(c)}}):null)}),ln=ro;var R=l(x()),an=l(Sn());var me=l(U()),no=(0,R.lazy)(()=>import("./ProteinAlignmentHelpDialog-6VFZFN6X.js"));function He({model:n}){return R.default.createElement(me.Tooltip,{title:"What the alignment panel shows"},R.default.createElement(me.IconButton,{size:"small",onClick:()=>{(0,an.getSession)(n).queueDialog(e=>[no,{handleClose:e}])}},R.default.createElement(dt,{fontSize:"small"})))}var oo=(0,fe.observer)(function({model:e}){let[t,o]=(0,g.useState)(null),i=`Color scheme: ${xe.find(s=>s.value===e.colorScheme)?.label??e.colorScheme}`;return g.default.createElement(g.default.Fragment,null,g.default.createElement(Je.default,{title:i},g.default.createElement($e.default,{size:"small","aria-label":i,onClick:s=>{o(s.currentTarget)}},g.default.createElement($t,{fontSize:"small"}))),g.default.createElement(Ue.default,{anchorEl:t,open:!!t,onClose:()=>{o(null)}},xe.map(s=>g.default.createElement(ze.default,{key:s.value,dense:!0,selected:s.value===e.colorScheme,onClick:()=>{e.setColorScheme(s.value),o(null)}},s.label))))});function un({checked:n,label:e,onToggle:t}){return g.default.createElement(ze.default,{onClick:()=>{t()},dense:!0},g.default.createElement(mn.default,null,g.default.createElement(cn.default,{checked:n,size:"small",edge:"start",disableRipple:!0})),g.default.createElement(fn.default,null,e))}var io=(0,fe.observer)(function({model:e}){let[t,o]=(0,g.useState)(null);return g.default.createElement(g.default.Fragment,null,g.default.createElement(Je.default,{title:"Display settings"},g.default.createElement($e.default,{size:"small",onClick:r=>{o(r.currentTarget)}},g.default.createElement(zt,{fontSize:"small"}))),g.default.createElement(Ue.default,{anchorEl:t,open:!!t,onClose:()=>{o(null)}},e.displayToggles.map(r=>g.default.createElement(un,{key:r.label,checked:r.checked,label:r.label,onToggle:r.toggle})),g.default.createElement(pn.default,null),e.behaviorToggles.map(r=>g.default.createElement(un,{key:r.label,checked:r.checked,label:r.label,onToggle:r.toggle}))))}),so=(0,fe.observer)(function({model:e}){let{alignmentStructure:t,showAlignment:o,colorLegend:r}=e;return g.default.createElement("div",null,g.default.createElement("div",{style:{display:"flex",alignItems:"flex-start",gap:8}},g.default.createElement(Cr,{model:e}),g.default.createElement("div",{style:{display:"flex",gap:4,alignItems:"center",flexShrink:0,minHeight:24}},g.default.createElement(oo,{model:e}),g.default.createElement(io,{model:e}),g.default.createElement(He,{model:e}))),r?g.default.createElement(sr,{title:"Structure colors",legend:r}):null,o&&t?.pairwiseAlignment?g.default.createElement(ln,{model:t}):o&&t?.alignmentPending?g.default.createElement(dn.LoadingEllipses,{message:"Loading pairwise alignment"}):null,g.default.createElement(tr,{model:e}))}),gn=so;var J=l(x()),bn=l(wn());var hn=!1;function lo(n){if(!hn){hn=!0;let e=document.createElement("style");e.append(n),document.head.append(e)}}function Oe({showControls:n,model:e}){let t=(0,J.useRef)(null),o=(0,J.useRef)(null),[r,i]=(0,J.useState)(),[s,a]=(0,J.useState)(!0);return(0,J.useEffect)(()=>{let u={cancelled:!1};return(async()=>{try{if(!t.current)return;let{Color:h,GeometryExport:d,MAQualityAssessment:b,PluginConfig:v,PluginSpec:P,DefaultPluginUISpec:_,createPluginUI:ee,registerColorThemes:te,renderReact18:Y,css:q}=await Se();lo(q);let O=document.createElement("div");t.current.append(O),u.host=O;let K=_(),B=await ee({target:O,render:Y,spec:{...K,behaviors:[...K.behaviors,P.Behavior(d),P.Behavior(b)],layout:{initial:{controlsDisplay:"reactive",showControls:n}},config:[[v.Viewport.ShowExpand,!1]]}});await B.initialized,te(B),B.canvas3d?.setProps({renderer:{selectColor:h(16711935),selectStrength:1},marking:{selectEdgeColor:h(16711935)}}),u.cancelled?(B.dispose(),O.remove()):(u.plugin=B,o.current=B,e?.setMolstarPluginContext(B))}catch(h){console.error(h),i(h)}finally{a(!1)}})(),()=>{u.cancelled=!0,o.current=null,e&&(0,bn.isAlive)(e)&&e.setMolstarPluginContext(void 0),u.plugin?.dispose(),u.host?.remove()}},[]),(0,J.useEffect)(()=>{let u={cancelled:!1};return(async()=>{let h=o.current;if(h){let{PluginCommands:d}=await Se();u.cancelled||await d.Layout.Update(h,{state:{showControls:n}})}})(),()=>{u.cancelled=!0}},[n]),{parentRef:t,error:r,loading:s}}var ao=(0,le.observer)(function({model:e}){let{showControls:t}=e,{parentRef:o,error:r,loading:i}=Oe({showControls:t,model:e});return r?w.default.createElement(N.ErrorMessage,{error:r}):w.default.createElement(po,{model:e,parentRef:o,loading:i})}),uo=(0,le.observer)(function({model:e}){let{error:t}=e;return t?w.default.createElement("div",{style:{display:"flex",alignItems:"flex-start"}},w.default.createElement("div",{style:{flex:1}},w.default.createElement(N.ErrorMessage,{error:t})),w.default.createElement(yn.IconButton,{size:"small","aria-label":"Dismiss error",onClick:()=>{e.setError(void 0)}},w.default.createElement(X,{fontSize:"small"}))):null}),co=(0,le.observer)(function({model:e}){let{loadingMessages:t}=e;return t.length>0?w.default.createElement("div",{"data-testid":"protein-view-loading-overlay",style:{position:"absolute",top:8,left:8,maxWidth:"calc(100% - 16px)",padding:"2px 8px",borderRadius:4,background:"rgba(255,255,255,0.85)",color:"#000",pointerEvents:"none"}},t.map(({id:o,message:r})=>w.default.createElement(N.LoadingEllipses,{key:o,message:r}))):null}),po=(0,le.observer)(function({model:e,parentRef:t,loading:o}){let{width:r,height:i}=e,s=!o&&!e.showLoading;return w.default.createElement("div",{style:{background:"#ccc"},"data-testid":s?"protein-view-ready":"protein-view-loading"},w.default.createElement(uo,{model:e}),o?w.default.createElement(N.LoadingEllipses,{message:"Loading protein viewer"}):w.default.createElement(gn,{model:e}),w.default.createElement("div",{style:{position:"relative",width:r,height:i}},w.default.createElement("div",{ref:t,"data-testid":"protein-view-molstar",style:{position:"relative",width:r,height:i}}),w.default.createElement(co,{model:e})),w.default.createElement(N.ResizeHandle,{style:{height:4,background:"grey"},onDrag:a=>e.setHeight(e.height+a)}),w.default.createElement(Ft,{model:e}))}),vs=ao;export{vs as default};
|
|
9
|
+
//# sourceMappingURL=ProteinView-JMWV7XDK.js.map
|