react-msaview 8.0.0 → 8.1.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/bundle/index.js +99 -99
- package/bundle/index.js.map +3 -3
- package/dist/calculateBlocks.js +5 -3
- package/dist/calculateBlocks.js.map +1 -1
- package/dist/components/Loading.js +2 -8
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/SequenceTextArea.js +0 -1
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/Track.js +2 -2
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/AboutDialog.js +6 -6
- package/dist/components/dialogs/AboutDialog.js.map +1 -1
- package/dist/components/dialogs/AnnotationFileDialog.js +5 -2
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +1 -1
- package/dist/components/dialogs/FeatureDialog.js +8 -3
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/header/Header.js +1 -1
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/LoadWarnings.js +3 -19
- package/dist/components/header/LoadWarnings.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +3 -3
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/import/ImportForm.js +1 -1
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +1 -3
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/drawFeatureSpans.d.ts +7 -1
- package/dist/components/msa/drawFeatureSpans.js +58 -23
- package/dist/components/msa/drawFeatureSpans.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +7 -8
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +8 -9
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/tracks/drawTracks.js +8 -5
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tree/TreeBranchMenu.js +1 -1
- package/dist/components/tree/TreeBranchMenu.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +1 -3
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +2 -2
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeNodeMenu.js +1 -1
- package/dist/components/tree/TreeNodeMenu.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +2 -1
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/model.d.ts +31 -32
- package/dist/model.js +184 -141
- package/dist/model.js.map +1 -1
- package/dist/svgTestUtil.d.ts +18 -22
- package/dist/types.d.ts +2 -0
- package/dist/useWheelScroll.js +0 -4
- package/dist/useWheelScroll.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/calculateBlocks.ts +5 -3
- package/src/components/Loading.tsx +2 -30
- package/src/components/SequenceTextArea.tsx +0 -1
- package/src/components/Track.tsx +2 -1
- package/src/components/dialogs/AboutDialog.tsx +35 -6
- package/src/components/dialogs/AnnotationFileDialog.tsx +10 -1
- package/src/components/dialogs/ExportSVGDialog.tsx +1 -1
- package/src/components/dialogs/FeatureDialog.tsx +11 -3
- package/src/components/header/Header.tsx +1 -1
- package/src/components/header/LoadWarnings.tsx +8 -21
- package/src/components/header/settingsMenuItems.ts +3 -3
- package/src/components/import/ImportForm.tsx +3 -5
- package/src/components/msa/MSACanvas.tsx +0 -3
- package/src/components/msa/drawFeatureSpans.ts +80 -25
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +7 -9
- package/src/components/msa/renderMSABlock.ts +9 -8
- package/src/components/tracks/drawTracks.ts +8 -16
- package/src/components/tree/TreeBranchMenu.tsx +1 -2
- package/src/components/tree/TreeCanvas.tsx +0 -3
- package/src/components/tree/TreeCanvasBlock.tsx +2 -2
- package/src/components/tree/TreeNodeMenu.tsx +1 -2
- package/src/components/tree/renderTreeCanvas.ts +2 -1
- package/src/constants.ts +5 -0
- package/src/model.ts +232 -161
- package/src/types.ts +2 -0
- package/src/useWheelScroll.ts +0 -4
- package/src/version.ts +1 -1
package/dist/model.js
CHANGED
|
@@ -2,7 +2,7 @@ import { clamp, groupBy, notEmpty, sum } from '@jbrowse/core/util';
|
|
|
2
2
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
3
3
|
import { ElementId, FileLocation } from '@jbrowse/core/util/types/mst';
|
|
4
4
|
import { addDisposer, applySnapshot, cast, getSnapshot, isAlive, types, } from '@jbrowse/mobx-state-tree';
|
|
5
|
-
import { autorun, transaction } from 'mobx';
|
|
5
|
+
import { autorun, computed, transaction } from 'mobx';
|
|
6
6
|
import { generateNodeIds, gffToAnnotations, interProScanToAnnotations, parseEmfTree, parseGFF, parseMSA, parseNewick, } from 'msa-parsers';
|
|
7
7
|
import { calculateBlocks } from './calculateBlocks.js';
|
|
8
8
|
import { clustalXColumnColors } from './clustalX.js';
|
|
@@ -34,7 +34,7 @@ import { resolveScale } from './scales.js';
|
|
|
34
34
|
import { buildSeqPosIndex } from './seqPosToGlobalCol.js';
|
|
35
35
|
import { maxBitsFor } from './sequenceLogo.js';
|
|
36
36
|
import { stripDefault } from './stripDefault.js';
|
|
37
|
-
import { computeRowInsertions, dropBlanks, len, outlineColor, skipBlanks,
|
|
37
|
+
import { computeRowInsertions, dropBlanks, len, outlineColor, skipBlanks, withAlpha, } from './util.js';
|
|
38
38
|
import { saveAs } from './vendor/fileSaver.js';
|
|
39
39
|
import { parseWuss } from './wuss.js';
|
|
40
40
|
function parseTreeText(text) {
|
|
@@ -114,6 +114,37 @@ function featureLabelMap(annotations, field) {
|
|
|
114
114
|
}
|
|
115
115
|
return labels;
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Lanes for `position: "strandpile"`, gggenomes' `position_strandpile`: the
|
|
119
|
+
* forward features stack above the line and the reverse below it, each strand
|
|
120
|
+
* packing on its own. The deepest row on each side sets the grid every row lays
|
|
121
|
+
* out on, so the line between the strands sits at one height down the panel and
|
|
122
|
+
* a reader can scan it. A feature with no strand piles with the forward ones.
|
|
123
|
+
*/
|
|
124
|
+
function strandpileLanes(rows) {
|
|
125
|
+
const piled = rows.map(([name, bands]) => {
|
|
126
|
+
const reverse = packDomainLanes(bands.filter(b => b.annotation.strand === -1));
|
|
127
|
+
const forward = packDomainLanes(bands.filter(b => b.annotation.strand !== -1));
|
|
128
|
+
return { name, forward, reverse };
|
|
129
|
+
});
|
|
130
|
+
const up = Math.max(0, ...piled.map(p => p.forward[0]?.laneCount ?? 0));
|
|
131
|
+
const down = Math.max(0, ...piled.map(p => p.reverse[0]?.laneCount ?? 0));
|
|
132
|
+
const laneCount = up + down;
|
|
133
|
+
return new Map(piled.map(({ name, forward, reverse }) => [
|
|
134
|
+
name,
|
|
135
|
+
[
|
|
136
|
+
// forward level 0 is the lane just above the line, stacking upward
|
|
137
|
+
...forward.map(b => ({ ...b, lane: up - 1 - b.lane, laneCount })),
|
|
138
|
+
...reverse.map(b => ({ ...b, lane: up + b.lane, laneCount })),
|
|
139
|
+
],
|
|
140
|
+
]));
|
|
141
|
+
}
|
|
142
|
+
/** one row's spans laid out by the panel's `position` */
|
|
143
|
+
function panelLanes(panel, rows) {
|
|
144
|
+
return panel.position === 'strandpile'
|
|
145
|
+
? strandpileLanes(rows)
|
|
146
|
+
: new Map(rows.map(([name, bands]) => [name, packDomainLanes(bands)]));
|
|
147
|
+
}
|
|
117
148
|
/**
|
|
118
149
|
* The spans a `features` panel draws, keyed by row name and measured in the
|
|
119
150
|
* panel's own pixels. `column` takes the bands the overlay draws, at the
|
|
@@ -122,9 +153,9 @@ function featureLabelMap(annotations, field) {
|
|
|
122
153
|
* cover across every row onto the panel width, so a row with no alignment
|
|
123
154
|
* still has an x.
|
|
124
155
|
*/
|
|
125
|
-
function featurePanelSpans({ panel, width,
|
|
156
|
+
function featurePanelSpans({ panel, width, colWidth, domainBands, annotationsByRow, shifts, }) {
|
|
126
157
|
if (panel.x === 'column') {
|
|
127
|
-
return
|
|
158
|
+
return panelLanes(panel, [...domainBands].map(([name, bands]) => [
|
|
128
159
|
name,
|
|
129
160
|
bands.map(band => ({
|
|
130
161
|
...band,
|
|
@@ -152,23 +183,23 @@ function featurePanelSpans({ panel, width, rowHeight, colWidth, domainBands, ann
|
|
|
152
183
|
max = Math.max(max, end);
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
const drawable = Math.max(1, width -
|
|
186
|
+
// a gene arrow ends where its feature does, so the extent maps onto the whole
|
|
187
|
+
// panel less the pixel the rightmost stroke needs
|
|
188
|
+
const drawable = Math.max(1, width - 1);
|
|
158
189
|
const scale = max > min ? drawable / (max - min) : 0;
|
|
159
190
|
// a lane opens where a span covers more than a tenth of the one before it,
|
|
160
191
|
// which keeps the genes of an operon on one lane: adjacent genes commonly
|
|
161
192
|
// share a few bases, and a stop codon overlapping the next start reads as a
|
|
162
193
|
// second lane over the whole row
|
|
163
|
-
return
|
|
194
|
+
return panelLanes(panel, shifted.map(([name, features]) => [
|
|
164
195
|
name,
|
|
165
|
-
|
|
196
|
+
features.map(({ annotation, start, end }) => ({
|
|
166
197
|
annotation,
|
|
167
198
|
xStart: (start - min) * scale,
|
|
168
199
|
xEnd: (end - min) * scale,
|
|
169
200
|
startCol: start,
|
|
170
201
|
endCol: end - (end - start) * laneOverlap,
|
|
171
|
-
}))
|
|
202
|
+
})),
|
|
172
203
|
]));
|
|
173
204
|
}
|
|
174
205
|
// a scale over the values a field takes across the features drawn
|
|
@@ -545,11 +576,7 @@ function stateModelFactory() {
|
|
|
545
576
|
* data from the loaded tree/msa/treeMetadata, generally loaded by
|
|
546
577
|
* autorun
|
|
547
578
|
*/
|
|
548
|
-
data: types.optional(DataModelF(), {
|
|
549
|
-
tree: '',
|
|
550
|
-
msa: '',
|
|
551
|
-
treeMetadata: '',
|
|
552
|
-
}),
|
|
579
|
+
data: types.optional(DataModelF(), {}),
|
|
553
580
|
/**
|
|
554
581
|
* #property
|
|
555
582
|
* the user's explicit hide choices per annotation accession, keyed by
|
|
@@ -788,10 +815,12 @@ function stateModelFactory() {
|
|
|
788
815
|
/**
|
|
789
816
|
* #action
|
|
790
817
|
* record a non-fatal load problem: a layer that failed to load, a file
|
|
791
|
-
* that failed to parse
|
|
818
|
+
* that failed to parse. A message already on the list is not added again
|
|
792
819
|
*/
|
|
793
820
|
addWarning(warning) {
|
|
794
|
-
self.warnings
|
|
821
|
+
if (!self.warnings.includes(warning)) {
|
|
822
|
+
self.warnings = [...self.warnings, warning];
|
|
823
|
+
}
|
|
795
824
|
},
|
|
796
825
|
/**
|
|
797
826
|
* #action
|
|
@@ -1440,7 +1469,7 @@ function stateModelFactory() {
|
|
|
1440
1469
|
*/
|
|
1441
1470
|
get insertionPositions() {
|
|
1442
1471
|
const { blanks, rows } = this;
|
|
1443
|
-
if (blanks.length === 0
|
|
1472
|
+
if (blanks.length === 0) {
|
|
1444
1473
|
return new Map();
|
|
1445
1474
|
}
|
|
1446
1475
|
const result = new Map();
|
|
@@ -1682,7 +1711,7 @@ function stateModelFactory() {
|
|
|
1682
1711
|
* cladogram mode. The tree's scale bar uses it.
|
|
1683
1712
|
*/
|
|
1684
1713
|
get pxPerBranchLength() {
|
|
1685
|
-
const max =
|
|
1714
|
+
const max = this.rootToTipLength;
|
|
1686
1715
|
return this.showBranchLenEffective && max ? self.treeWidth / max : 0;
|
|
1687
1716
|
},
|
|
1688
1717
|
/**
|
|
@@ -1731,6 +1760,125 @@ function stateModelFactory() {
|
|
|
1731
1760
|
return self.drawLabels && self.rowHeight >= minLetterRowHeight;
|
|
1732
1761
|
},
|
|
1733
1762
|
}))
|
|
1763
|
+
.views(self => {
|
|
1764
|
+
// a spec is a frozen value, so it keys its computed until setColumnTracks
|
|
1765
|
+
// replaces it
|
|
1766
|
+
const columnTrackModels = new WeakMap();
|
|
1767
|
+
return {
|
|
1768
|
+
/**
|
|
1769
|
+
* #getter
|
|
1770
|
+
* a data track's values or string, projected from its row's residues
|
|
1771
|
+
* onto alignment columns when it names a row
|
|
1772
|
+
*/
|
|
1773
|
+
get columnTrackContent() {
|
|
1774
|
+
const { MSA, blanks, hideGapsEffective } = self;
|
|
1775
|
+
const width = MSA?.getWidth() ?? 0;
|
|
1776
|
+
const project = (track, items, fill) => {
|
|
1777
|
+
if (!track.row) {
|
|
1778
|
+
return items;
|
|
1779
|
+
}
|
|
1780
|
+
const out = Array.from({ length: width }, () => fill);
|
|
1781
|
+
const index = self.seqPosIndex(track.row);
|
|
1782
|
+
items.forEach((item, seqPos) => {
|
|
1783
|
+
const col = index?.[seqPos];
|
|
1784
|
+
if (col !== undefined) {
|
|
1785
|
+
out[col] = item;
|
|
1786
|
+
}
|
|
1787
|
+
});
|
|
1788
|
+
return out;
|
|
1789
|
+
};
|
|
1790
|
+
const skip = (items) => hideGapsEffective ? dropBlanks(blanks, items) : items;
|
|
1791
|
+
// an arc endpoint maps row residue -> column -> visible column.
|
|
1792
|
+
// visibleColsBefore, not globalColToVisibleCol, so an endpoint in a
|
|
1793
|
+
// hidden column moves to the neighboring visible one and the arc stays
|
|
1794
|
+
const resolve = (track, pos) => {
|
|
1795
|
+
const col = track.row
|
|
1796
|
+
? self.seqPosIndex(track.row)?.[pos - 1]
|
|
1797
|
+
: pos - 1;
|
|
1798
|
+
if (col === undefined || col < 0 || col >= width) {
|
|
1799
|
+
return undefined;
|
|
1800
|
+
}
|
|
1801
|
+
return hideGapsEffective ? visibleColsBefore(blanks, col) : col;
|
|
1802
|
+
};
|
|
1803
|
+
return new Map(self.columnTracks.map(track => {
|
|
1804
|
+
if (track.kind === 'arc') {
|
|
1805
|
+
const arcs = (track.arcs ?? [])
|
|
1806
|
+
.map(arc => {
|
|
1807
|
+
const start = resolve(track, Math.min(arc.start, arc.end));
|
|
1808
|
+
const end = resolve(track, Math.max(arc.start, arc.end));
|
|
1809
|
+
return start !== undefined &&
|
|
1810
|
+
end !== undefined &&
|
|
1811
|
+
start < end
|
|
1812
|
+
? { start, end, color: arc.color }
|
|
1813
|
+
: undefined;
|
|
1814
|
+
})
|
|
1815
|
+
.filter(notEmpty);
|
|
1816
|
+
return [track.id, { arcs }];
|
|
1817
|
+
}
|
|
1818
|
+
if (track.kind === 'bar') {
|
|
1819
|
+
const max = track.max ?? 1;
|
|
1820
|
+
const values = skip(project(track, track.values ?? [], 0)).map(v => Math.min(1, Math.max(0, v / max)));
|
|
1821
|
+
return [track.id, { values }];
|
|
1822
|
+
}
|
|
1823
|
+
const data = skip(project(track, (track.data ?? '').split(''), ' '));
|
|
1824
|
+
return [track.id, { data: data.join('') }];
|
|
1825
|
+
}));
|
|
1826
|
+
},
|
|
1827
|
+
/**
|
|
1828
|
+
* #method
|
|
1829
|
+
* the height a track draws at: what the user dragged its divider to,
|
|
1830
|
+
* then the height its snapshot asked for, then its kind's default. Only
|
|
1831
|
+
* a text track falls through to rowHeight
|
|
1832
|
+
*/
|
|
1833
|
+
trackHeight(kind, heightKey = kind, given) {
|
|
1834
|
+
return (self.trackHeights.get(heightKey) ??
|
|
1835
|
+
given ??
|
|
1836
|
+
defaultTrackHeights[kind] ??
|
|
1837
|
+
self.rowHeight);
|
|
1838
|
+
},
|
|
1839
|
+
/**
|
|
1840
|
+
* #method
|
|
1841
|
+
* the track a column track spec draws as, computed once per spec. A
|
|
1842
|
+
* text track's height falls through to rowHeight, and its own computed
|
|
1843
|
+
* confines the vertical zoom to it, so a sibling keeps its object and
|
|
1844
|
+
* its canvas skips the redraw
|
|
1845
|
+
*/
|
|
1846
|
+
columnTrackModel(track) {
|
|
1847
|
+
let computedModel = columnTrackModels.get(track);
|
|
1848
|
+
if (!computedModel) {
|
|
1849
|
+
computedModel = computed(() => {
|
|
1850
|
+
const heightKey = resizableKinds.has(track.kind)
|
|
1851
|
+
? ownHeightKey(track.id)
|
|
1852
|
+
: undefined;
|
|
1853
|
+
const content = this.columnTrackContent.get(track.id);
|
|
1854
|
+
return {
|
|
1855
|
+
model: {
|
|
1856
|
+
id: track.id,
|
|
1857
|
+
name: track.name,
|
|
1858
|
+
kind: track.kind,
|
|
1859
|
+
heightKey,
|
|
1860
|
+
height: this.trackHeight(track.kind, heightKey, track.height),
|
|
1861
|
+
barColor: track.color,
|
|
1862
|
+
arcColor: track.color,
|
|
1863
|
+
customColorScheme: track.colors,
|
|
1864
|
+
data: content?.data,
|
|
1865
|
+
arcs: content?.arcs,
|
|
1866
|
+
},
|
|
1867
|
+
ReactComponent: TrackBlocks,
|
|
1868
|
+
};
|
|
1869
|
+
});
|
|
1870
|
+
columnTrackModels.set(track, computedModel);
|
|
1871
|
+
}
|
|
1872
|
+
return computedModel.get();
|
|
1873
|
+
},
|
|
1874
|
+
/**
|
|
1875
|
+
* #getter
|
|
1876
|
+
*/
|
|
1877
|
+
get columnTrackModels() {
|
|
1878
|
+
return self.columnTracks.map(track => this.columnTrackModel(track));
|
|
1879
|
+
},
|
|
1880
|
+
};
|
|
1881
|
+
})
|
|
1734
1882
|
.views(self => ({
|
|
1735
1883
|
/**
|
|
1736
1884
|
* #getter
|
|
@@ -1825,101 +1973,6 @@ function stateModelFactory() {
|
|
|
1825
1973
|
ReactComponent: TrackBlocks,
|
|
1826
1974
|
}));
|
|
1827
1975
|
},
|
|
1828
|
-
/**
|
|
1829
|
-
* #getter
|
|
1830
|
-
* a data track's values or string, projected from its row's residues
|
|
1831
|
-
* onto alignment columns when it names a row
|
|
1832
|
-
*/
|
|
1833
|
-
get columnTrackContent() {
|
|
1834
|
-
const { MSA, blanks, hideGapsEffective } = self;
|
|
1835
|
-
const width = MSA?.getWidth() ?? 0;
|
|
1836
|
-
const project = (track, items, fill) => {
|
|
1837
|
-
if (!track.row) {
|
|
1838
|
-
return items;
|
|
1839
|
-
}
|
|
1840
|
-
const out = Array.from({ length: width }, () => fill);
|
|
1841
|
-
const index = self.seqPosIndex(track.row);
|
|
1842
|
-
items.forEach((item, seqPos) => {
|
|
1843
|
-
const col = index?.[seqPos];
|
|
1844
|
-
if (col !== undefined) {
|
|
1845
|
-
out[col] = item;
|
|
1846
|
-
}
|
|
1847
|
-
});
|
|
1848
|
-
return out;
|
|
1849
|
-
};
|
|
1850
|
-
const skip = (items) => hideGapsEffective ? dropBlanks(blanks, items) : items;
|
|
1851
|
-
// an arc endpoint maps row residue -> column -> visible column.
|
|
1852
|
-
// visibleColsBefore, not globalColToVisibleCol, so an endpoint in a
|
|
1853
|
-
// hidden column moves to the neighboring visible one and the arc stays
|
|
1854
|
-
const resolve = (track, pos) => {
|
|
1855
|
-
const col = track.row
|
|
1856
|
-
? self.seqPosIndex(track.row)?.[pos - 1]
|
|
1857
|
-
: pos - 1;
|
|
1858
|
-
if (col === undefined || col < 0 || col >= width) {
|
|
1859
|
-
return undefined;
|
|
1860
|
-
}
|
|
1861
|
-
return hideGapsEffective ? visibleColsBefore(blanks, col) : col;
|
|
1862
|
-
};
|
|
1863
|
-
return new Map(self.columnTracks.map(track => {
|
|
1864
|
-
if (track.kind === 'arc') {
|
|
1865
|
-
const arcs = (track.arcs ?? [])
|
|
1866
|
-
.map(arc => {
|
|
1867
|
-
const start = resolve(track, Math.min(arc.start, arc.end));
|
|
1868
|
-
const end = resolve(track, Math.max(arc.start, arc.end));
|
|
1869
|
-
return start !== undefined && end !== undefined && start < end
|
|
1870
|
-
? { start, end, color: arc.color }
|
|
1871
|
-
: undefined;
|
|
1872
|
-
})
|
|
1873
|
-
.filter(notEmpty);
|
|
1874
|
-
return [track.id, { arcs }];
|
|
1875
|
-
}
|
|
1876
|
-
if (track.kind === 'bar') {
|
|
1877
|
-
const max = track.max ?? 1;
|
|
1878
|
-
const values = skip(project(track, track.values ?? [], 0)).map(v => Math.min(1, Math.max(0, v / max)));
|
|
1879
|
-
return [track.id, { values }];
|
|
1880
|
-
}
|
|
1881
|
-
const data = skip(project(track, (track.data ?? '').split(''), ' '));
|
|
1882
|
-
return [track.id, { data: data.join('') }];
|
|
1883
|
-
}));
|
|
1884
|
-
},
|
|
1885
|
-
/**
|
|
1886
|
-
* #method
|
|
1887
|
-
* the height a track draws at: what the user dragged its divider to,
|
|
1888
|
-
* then the height its snapshot asked for, then its kind's default. Only
|
|
1889
|
-
* a text track falls through to rowHeight, and `??` short-circuits
|
|
1890
|
-
* before reading it, so vertical zoom does not rebuild the other tracks
|
|
1891
|
-
*/
|
|
1892
|
-
trackHeight(kind, heightKey = kind, given) {
|
|
1893
|
-
return (self.trackHeights.get(heightKey) ??
|
|
1894
|
-
given ??
|
|
1895
|
-
defaultTrackHeights[kind] ??
|
|
1896
|
-
self.rowHeight);
|
|
1897
|
-
},
|
|
1898
|
-
/**
|
|
1899
|
-
* #getter
|
|
1900
|
-
*/
|
|
1901
|
-
get columnTrackModels() {
|
|
1902
|
-
return self.columnTracks.map(track => {
|
|
1903
|
-
const heightKey = resizableKinds.has(track.kind)
|
|
1904
|
-
? ownHeightKey(track.id)
|
|
1905
|
-
: undefined;
|
|
1906
|
-
return {
|
|
1907
|
-
model: {
|
|
1908
|
-
id: track.id,
|
|
1909
|
-
name: track.name,
|
|
1910
|
-
kind: track.kind,
|
|
1911
|
-
heightKey,
|
|
1912
|
-
height: this.trackHeight(track.kind, heightKey, track.height),
|
|
1913
|
-
barColor: track.color,
|
|
1914
|
-
arcColor: track.color,
|
|
1915
|
-
customColorScheme: track.colors,
|
|
1916
|
-
data: this.columnTrackContent.get(track.id)?.data,
|
|
1917
|
-
arcs: this.columnTrackContent.get(track.id)?.arcs,
|
|
1918
|
-
},
|
|
1919
|
-
ReactComponent: TrackBlocks,
|
|
1920
|
-
};
|
|
1921
|
-
});
|
|
1922
|
-
},
|
|
1923
1976
|
/**
|
|
1924
1977
|
* #getter
|
|
1925
1978
|
* the consensus secondary structure as a track, when there is one. A
|
|
@@ -1936,7 +1989,7 @@ function stateModelFactory() {
|
|
|
1936
1989
|
name: 'Base pairs',
|
|
1937
1990
|
kind: 'arc',
|
|
1938
1991
|
heightKey: 'arc',
|
|
1939
|
-
height:
|
|
1992
|
+
height: self.trackHeight('arc'),
|
|
1940
1993
|
arcs,
|
|
1941
1994
|
},
|
|
1942
1995
|
ReactComponent: TrackBlocks,
|
|
@@ -1984,7 +2037,7 @@ function stateModelFactory() {
|
|
|
1984
2037
|
model: {
|
|
1985
2038
|
...model,
|
|
1986
2039
|
heightKey: resizableKinds.has(model.kind) ? model.kind : undefined,
|
|
1987
|
-
height:
|
|
2040
|
+
height: self.trackHeight(model.kind),
|
|
1988
2041
|
},
|
|
1989
2042
|
ReactComponent: TrackBlocks,
|
|
1990
2043
|
}));
|
|
@@ -1993,7 +2046,7 @@ function stateModelFactory() {
|
|
|
1993
2046
|
return [
|
|
1994
2047
|
...this.adapterTrackModels,
|
|
1995
2048
|
...this.basePairTrackModels,
|
|
1996
|
-
...
|
|
2049
|
+
...self.columnTrackModels,
|
|
1997
2050
|
// every computed track reads the alignment's columns, and a tree, a
|
|
1998
2051
|
// GFF and a features panel draw a figure with none
|
|
1999
2052
|
...(self.numColumns > 0 ? this.computedTrackModels : []),
|
|
@@ -2778,12 +2831,6 @@ function stateModelFactory() {
|
|
|
2778
2831
|
const categorical = createPaletteMap(this.categoricalDomainTypes.map(d => d.accession));
|
|
2779
2832
|
return { ...segments, ...categorical };
|
|
2780
2833
|
},
|
|
2781
|
-
get strokePalette() {
|
|
2782
|
-
return transform(this.fillPalette, ([key, val]) => [
|
|
2783
|
-
key,
|
|
2784
|
-
outlineColor(val),
|
|
2785
|
-
]);
|
|
2786
|
-
},
|
|
2787
2834
|
/**
|
|
2788
2835
|
* #getter
|
|
2789
2836
|
* the encoding coloring the overlay's spans, undefined when none does,
|
|
@@ -3062,22 +3109,6 @@ function stateModelFactory() {
|
|
|
3062
3109
|
propertyConservation,
|
|
3063
3110
|
});
|
|
3064
3111
|
},
|
|
3065
|
-
/**
|
|
3066
|
-
* #getter
|
|
3067
|
-
* `columnStatsAt` for the hovered column, undefined when nothing is
|
|
3068
|
-
* hovered
|
|
3069
|
-
*/
|
|
3070
|
-
get mouseOverColumnStats() {
|
|
3071
|
-
const { mouseCol, colStats, conservation, propertyConservation } = self;
|
|
3072
|
-
return mouseCol === undefined
|
|
3073
|
-
? undefined
|
|
3074
|
-
: columnStats({
|
|
3075
|
-
col: mouseCol,
|
|
3076
|
-
colStats,
|
|
3077
|
-
conservation,
|
|
3078
|
-
propertyConservation,
|
|
3079
|
-
});
|
|
3080
|
-
},
|
|
3081
3112
|
/**
|
|
3082
3113
|
* #method
|
|
3083
3114
|
*/
|
|
@@ -3139,7 +3170,6 @@ function stateModelFactory() {
|
|
|
3139
3170
|
spans: featurePanelSpans({
|
|
3140
3171
|
panel,
|
|
3141
3172
|
width,
|
|
3142
|
-
rowHeight: self.rowHeight,
|
|
3143
3173
|
colWidth: self.colWidth,
|
|
3144
3174
|
domainBands: this.domainBands,
|
|
3145
3175
|
annotationsByRow: self.annotationsByRow,
|
|
@@ -3406,11 +3436,14 @@ function stateModelFactory() {
|
|
|
3406
3436
|
// alignment all read. The tree arrives with the model for inline data
|
|
3407
3437
|
// and later for a filehandle, so the seeding waits for it and then runs
|
|
3408
3438
|
// once: expanding a seeded clade sticks, and the record collapses it
|
|
3409
|
-
// again only on reload.
|
|
3439
|
+
// again only on reload. `dataInitialized` is true once the MSA alone
|
|
3440
|
+
// has loaded, when the tree is still the flat stub, so a tree
|
|
3441
|
+
// filehandle holds the seeding until its text lands.
|
|
3410
3442
|
let cladesSeeded = false;
|
|
3411
3443
|
addDisposer(self, autorun(() => {
|
|
3412
3444
|
if (cladesSeeded ||
|
|
3413
3445
|
!self.dataInitialized ||
|
|
3446
|
+
(self.treeFilehandle && !self.data.tree) ||
|
|
3414
3447
|
self.clades.length === 0) {
|
|
3415
3448
|
return;
|
|
3416
3449
|
}
|
|
@@ -3566,6 +3599,16 @@ function stateModelFactory() {
|
|
|
3566
3599
|
self.setAnnotations([]);
|
|
3567
3600
|
}
|
|
3568
3601
|
}));
|
|
3602
|
+
// a GFF whose first column names rows of some other alignment parses
|
|
3603
|
+
// without error and draws nothing
|
|
3604
|
+
addDisposer(self, autorun(() => {
|
|
3605
|
+
const { annotations, rowNamesSet } = self;
|
|
3606
|
+
if (annotations.length > 0 &&
|
|
3607
|
+
rowNamesSet.size > 0 &&
|
|
3608
|
+
!annotations.some(a => rowNamesSet.has(a.id))) {
|
|
3609
|
+
self.addWarning(`0 of ${annotations.length} annotations name a row in this alignment`);
|
|
3610
|
+
}
|
|
3611
|
+
}));
|
|
3569
3612
|
// gffFilehandle loads into data.gff, which the autorun above parses
|
|
3570
3613
|
loadOnFilehandleChange({
|
|
3571
3614
|
what: 'annotations',
|