react-msaview 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ import type { ColumnTrackSpec, Highlight, Region } from './types.ts';
2
+ /**
3
+ * `row: null` opts one entry out of the spec's `query` row, back to alignment
4
+ * columns.
5
+ */
6
+ type RowChoice = {
7
+ row?: string | null;
8
+ };
9
+ export type HighlightShorthand = number | string | (Omit<Highlight, 'row'> & RowChoice);
10
+ export type RegionShorthand = string | (Omit<Region, 'row'> & RowChoice);
11
+ export type ColumnTrackShorthand = Omit<ColumnTrackSpec, 'id' | 'kind' | 'row'> & RowChoice & {
12
+ id?: string;
13
+ kind?: ColumnTrackSpec['kind'];
14
+ /** the 1-based position `values` or `data` begins at */
15
+ start?: number;
16
+ };
17
+ /**
18
+ * The short forms a hand-written or generated link can use. Every long form
19
+ * passes through unchanged, so a snapshot is also a valid spec.
20
+ */
21
+ export interface MsaSpec {
22
+ /**
23
+ * The row the spec is about: sets `relativeTo`, and is the `row` of every
24
+ * highlight, region and column track that names none
25
+ */
26
+ query?: string;
27
+ /** a url, or the alignment itself when it spans more than one line */
28
+ msa?: string;
29
+ /** a url, or a newick string when it starts with "(" */
30
+ tree?: string;
31
+ highlights?: HighlightShorthand[];
32
+ region?: RegionShorthand;
33
+ columnTracks?: ColumnTrackShorthand[];
34
+ [key: string]: unknown;
35
+ }
36
+ /**
37
+ * The label a single-residue shorthand highlight gets, filled in from the
38
+ * sequence when drawn: `175` on a row reading R there becomes "R175".
39
+ */
40
+ export declare const residueLabel = "{residue}{position}";
41
+ /**
42
+ * Expand the short forms of an MsaView spec into the snapshot the model takes.
43
+ * Idempotent, and a no-op on a spec that uses none of them.
44
+ */
45
+ export declare function expandSpec(spec: MsaSpec): Record<string, unknown>;
46
+ export {};
@@ -0,0 +1,127 @@
1
+ /**
2
+ * The label a single-residue shorthand highlight gets, filled in from the
3
+ * sequence when drawn: `175` on a row reading R there becomes "R175".
4
+ */
5
+ export const residueLabel = '{residue}{position}';
6
+ const span = /^(\d+)(?:-(\d+))?(?:\s+(.+))?$/;
7
+ function parseSpan(text, what) {
8
+ const match = span.exec(text.trim());
9
+ if (!match) {
10
+ throw new Error(`${what} "${text}" is not "start", "start-end" or either followed by a label`);
11
+ }
12
+ const start = Number(match[1]);
13
+ const end = match[2] === undefined ? start : Number(match[2]);
14
+ return { start, end, label: match[3] };
15
+ }
16
+ function withRow(entry, query) {
17
+ const { row, ...rest } = entry;
18
+ const resolved = row === undefined ? query : (row ?? undefined);
19
+ return resolved === undefined ? rest : { ...rest, row: resolved };
20
+ }
21
+ function expandHighlight(entry, query) {
22
+ if (typeof entry === 'object') {
23
+ return (entry.rows
24
+ ? entry
25
+ : withRow(entry, entry.start === undefined ? undefined : query));
26
+ }
27
+ const { start, end, label } = parseSpan(String(entry), 'highlight');
28
+ const single = start === end && query !== undefined;
29
+ return {
30
+ ...(query === undefined ? {} : { row: query }),
31
+ start,
32
+ end,
33
+ ...(label ? { label } : single ? { label: residueLabel } : {}),
34
+ };
35
+ }
36
+ function expandRegion(region, query) {
37
+ if (typeof region === 'string') {
38
+ const { start, end } = parseSpan(region, 'region');
39
+ const parsed = { start, end };
40
+ return withRow(parsed, query);
41
+ }
42
+ return withRow(region, query);
43
+ }
44
+ function slug(name) {
45
+ return name
46
+ .toLowerCase()
47
+ .replaceAll(/[^a-z0-9]+/g, '-')
48
+ .replaceAll(/^-|-$/g, '');
49
+ }
50
+ function kindOf(track) {
51
+ if (track.kind) {
52
+ return track.kind;
53
+ }
54
+ if (track.values) {
55
+ return 'bar';
56
+ }
57
+ if (track.data !== undefined) {
58
+ return 'text';
59
+ }
60
+ if (track.arcs) {
61
+ return 'arc';
62
+ }
63
+ throw new Error(`column track "${track.name}" has no values, data or arcs to take its kind from`);
64
+ }
65
+ function expandTrack(track, query, ids) {
66
+ const { start, ...rest } = track;
67
+ const pad = start && start > 1 ? start - 1 : 0;
68
+ let id = track.id ?? (slug(track.name) || 'track');
69
+ for (let n = 2; !track.id && ids.has(id); n++) {
70
+ id = `${slug(track.name) || 'track'}-${n}`;
71
+ }
72
+ ids.add(id);
73
+ return {
74
+ ...withRow(rest, query),
75
+ id,
76
+ kind: kindOf(track),
77
+ ...(pad && track.values
78
+ ? { values: [...Array(pad).fill(0), ...track.values] }
79
+ : {}),
80
+ ...(pad && track.data !== undefined
81
+ ? { data: ' '.repeat(pad) + track.data }
82
+ : {}),
83
+ };
84
+ }
85
+ function isInlineMsa(msa) {
86
+ return msa.includes('\n');
87
+ }
88
+ function isInlineTree(tree) {
89
+ return tree.trimStart().startsWith('(');
90
+ }
91
+ function uri(location) {
92
+ return { uri: location, locationType: 'UriLocation' };
93
+ }
94
+ /**
95
+ * Expand the short forms of an MsaView spec into the snapshot the model takes.
96
+ * Idempotent, and a no-op on a spec that uses none of them.
97
+ */
98
+ export function expandSpec(spec) {
99
+ const { query, msa, tree, highlights, region, columnTracks, ...rest } = spec;
100
+ const data = {
101
+ ...rest.data,
102
+ ...(msa !== undefined && isInlineMsa(msa) ? { msa } : {}),
103
+ ...(tree !== undefined && isInlineTree(tree) ? { tree } : {}),
104
+ };
105
+ const ids = new Set((columnTracks ?? []).map(t => t.id).filter(id => id !== undefined));
106
+ return {
107
+ ...rest,
108
+ ...(msa !== undefined && !isInlineMsa(msa)
109
+ ? { msaFilehandle: uri(msa) }
110
+ : {}),
111
+ ...(tree !== undefined && !isInlineTree(tree)
112
+ ? { treeFilehandle: uri(tree) }
113
+ : {}),
114
+ ...(Object.keys(data).length > 0 ? { data } : {}),
115
+ ...(query !== undefined && rest.relativeTo === undefined
116
+ ? { relativeTo: query }
117
+ : {}),
118
+ ...(highlights
119
+ ? { highlights: highlights.map(h => expandHighlight(h, query)) }
120
+ : {}),
121
+ ...(region === undefined ? {} : { region: expandRegion(region, query) }),
122
+ ...(columnTracks
123
+ ? { columnTracks: columnTracks.map(t => expandTrack(t, query, ids)) }
124
+ : {}),
125
+ };
126
+ }
127
+ //# sourceMappingURL=expandSpec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expandSpec.js","sourceRoot":"","sources":["../src/expandSpec.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export { renderToSvg } from './renderToSvg.tsx';
2
2
  export type { ExportSvgOptions } from './renderToSvg.tsx';
3
3
  export { CHAR_WIDTH_RATIO, installHeadlessRenderEnv, } from './headlessRenderEnv.ts';
4
4
  export { maxNeighborJoiningRows } from './constants.ts';
5
+ export { expandSpec, residueLabel } from './expandSpec.ts';
6
+ export type { ColumnTrackShorthand, HighlightShorthand, MsaSpec, RegionShorthand, } from './expandSpec.ts';
5
7
  export { default as MSAView } from './components/Loading.tsx';
6
8
  export { default as MSAViewer } from './components/MSAViewer.tsx';
7
9
  export type { MSAViewerProps } from './components/MSAViewer.tsx';
package/dist/index.js CHANGED
@@ -14,6 +14,8 @@ export { CHAR_WIDTH_RATIO, installHeadlessRenderEnv, } from './headlessRenderEnv
14
14
  // the row count above which calculateNeighborJoiningTreeFromMSA throws, for
15
15
  // hosts gating their own menu item
16
16
  export { maxNeighborJoiningRows } from './constants.js';
17
+ // the short forms a link or a script can write a view in; see docs/layers.md
18
+ export { expandSpec, residueLabel } from './expandSpec.js';
17
19
  export { default as MSAView } from './components/Loading.js';
18
20
  export { default as MSAViewer } from './components/MSAViewer.js';
19
21
  export { mount } from './mount.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,yDAAyD;AACzD,6EAA6E;AAC7E,+DAA+D;AAC/D,gFAAgF;AAChF,0EAA0E;AAC1E,yEAAyE;AACzE,8EAA8E;AAC9E,mEAAmE;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAmB,CAAA;AAE/C,8EAA8E;AAC9E,yDAAyD;AACzD,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,wBAAwB,CAAA;AAC/B,4EAA4E;AAC5E,mCAAmC;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,yBAA0B,CAAA;AAC7D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,2BAA4B,CAAA;AAEjE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAa,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAGtD,OAAO,EAAqB,OAAO,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,yDAAyD;AACzD,6EAA6E;AAC7E,+DAA+D;AAC/D,gFAAgF;AAChF,0EAA0E;AAC1E,yEAAyE;AACzE,8EAA8E;AAC9E,mEAAmE;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAmB,CAAA;AAE/C,8EAA8E;AAC9E,yDAAyD;AACzD,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,wBAAwB,CAAA;AAC/B,4EAA4E;AAC5E,mCAAmC;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,6EAA6E;AAC7E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAO1D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,yBAA0B,CAAA;AAC7D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,2BAA4B,CAAA;AAEjE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAa,CAAA;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAGtD,OAAO,EAAqB,OAAO,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA"}
package/dist/model.d.ts CHANGED
@@ -57,7 +57,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
57
57
  colorSchemeName: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
58
58
  showColumnStats: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
59
59
  msaFormat: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<import("msa-parsers").MSAFormat>>;
60
- }, "allowedGappyness" | "clades" | "colWidth" | "collapsed" | "columnTracks" | "currentAlignment" | "data" | "drawMsaLetters" | "encodings" | "gffFilehandle" | "height" | "hideGaps" | "highlightColumns" | "highlights" | "id" | "msaFilehandle" | "relativeTo" | "residueMappings" | "rowHeight" | "rowPanels" | "scrollX" | "scrollY" | "scrollZoom" | "scrollZoomAxis" | "showDomainLegend" | "showDomains" | "showOnly" | "subFeatureRows" | "trackHeights" | "treeFilehandle" | "treeMetadataFilehandle" | "turnedOffFeatures" | "turnedOffTracks" | "type"> & {
60
+ }, "allowedGappyness" | "clades" | "colWidth" | "collapsed" | "columnTracks" | "currentAlignment" | "data" | "drawMsaLetters" | "encodings" | "gffFilehandle" | "height" | "hideGaps" | "highlightColumns" | "highlights" | "id" | "msaFilehandle" | "region" | "relativeTo" | "residueMappings" | "rowHeight" | "rowPanels" | "scrollX" | "scrollY" | "scrollZoom" | "scrollZoomAxis" | "showDomainLegend" | "showDomains" | "showOnly" | "subFeatureRows" | "trackHeights" | "treeFilehandle" | "treeMetadataFilehandle" | "turnedOffFeatures" | "turnedOffTracks" | "type"> & {
61
61
  /**
62
62
  * #property
63
63
  * id of view, randomly generated if not provided
@@ -616,6 +616,14 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
616
616
  * Persists in the snapshot and the URL.
617
617
  */
618
618
  highlights: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IType<Highlight, Highlight, Highlight>>, [undefined]>;
619
+ /**
620
+ * #property
621
+ * where the view opens, in `highlights` coordinates: `{row, start,
622
+ * end}` zooms onto residues of that row, `{start, end}` onto alignment
623
+ * columns. Applied once the alignment and any tree file have loaded,
624
+ * then cleared, so a reloaded session keeps the reader's own scroll.
625
+ */
626
+ region: import("@jbrowse/mobx-state-tree").IType<Region | undefined, Region | undefined, Region | undefined>;
619
627
  /**
620
628
  * #property
621
629
  * clades of the tree with a mark drawn over them. `mrca` names tips
@@ -866,6 +874,10 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
866
874
  * through it.
867
875
  */
868
876
  setHighlightedColumns(columns?: number[]): void;
877
+ /**
878
+ * #action
879
+ */
880
+ setRegion(region?: Region): void;
869
881
  /**
870
882
  * #action
871
883
  */
@@ -1946,6 +1958,12 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
1946
1958
  start: number;
1947
1959
  end: number;
1948
1960
  }[];
1961
+ /**
1962
+ * #method
1963
+ * a highlight label with `{residue}` and `{position}` filled in from the
1964
+ * row's letter at `start`, which is how the `175` shorthand draws "R175"
1965
+ */
1966
+ fillHighlightLabel(label: string, row?: string, start?: number): string;
1949
1967
  /**
1950
1968
  * #getter
1951
1969
  * `highlights` projected onto what is on screen: residue spans go
@@ -2203,6 +2221,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
2203
2221
  relativeTo: string | undefined;
2204
2222
  highlightColumns: number[] | undefined;
2205
2223
  highlights: Highlight[];
2224
+ region: Region | undefined;
2206
2225
  clades: Clade[];
2207
2226
  encodings: Encoding[];
2208
2227
  rowPanels: RowPanelSpec[];
@@ -2334,6 +2353,7 @@ declare function stateModelFactory(): import("@jbrowse/mobx-state-tree").IModelT
2334
2353
  relativeTo: string | undefined;
2335
2354
  highlightColumns: number[] | undefined;
2336
2355
  highlights: Highlight[];
2356
+ region: Region | undefined;
2337
2357
  clades: Clade[];
2338
2358
  encodings: Encoding[];
2339
2359
  rowPanels: RowPanelSpec[];
package/dist/model.js CHANGED
@@ -606,6 +606,14 @@ function stateModelFactory() {
606
606
  * Persists in the snapshot and the URL.
607
607
  */
608
608
  highlights: stripDefault(types.array(types.frozen()), []),
609
+ /**
610
+ * #property
611
+ * where the view opens, in `highlights` coordinates: `{row, start,
612
+ * end}` zooms onto residues of that row, `{start, end}` onto alignment
613
+ * columns. Applied once the alignment and any tree file have loaded,
614
+ * then cleared, so a reloaded session keeps the reader's own scroll.
615
+ */
616
+ region: types.frozen(),
609
617
  /**
610
618
  * #property
611
619
  * clades of the tree with a mark drawn over them. `mrca` names tips
@@ -859,6 +867,12 @@ function stateModelFactory() {
859
867
  setHighlightedColumns(columns) {
860
868
  self.highlightedColumns = columns;
861
869
  },
870
+ /**
871
+ * #action
872
+ */
873
+ setRegion(region) {
874
+ self.region = region;
875
+ },
862
876
  /**
863
877
  * #action
864
878
  */
@@ -3064,6 +3078,23 @@ function stateModelFactory() {
3064
3078
  }
3065
3079
  return runs;
3066
3080
  },
3081
+ /**
3082
+ * #method
3083
+ * a highlight label with `{residue}` and `{position}` filled in from the
3084
+ * row's letter at `start`, which is how the `175` shorthand draws "R175"
3085
+ */
3086
+ fillHighlightLabel(label, row, start) {
3087
+ if (!label.includes('{')) {
3088
+ return label;
3089
+ }
3090
+ const col = row && start !== undefined
3091
+ ? self.seqPosIndex(row)?.[start - 1]
3092
+ : undefined;
3093
+ const residue = row && col !== undefined ? (self.MSA?.getRow(row)[col] ?? '') : '';
3094
+ return label
3095
+ .replaceAll('{residue}', residue)
3096
+ .replaceAll('{position}', start === undefined ? '' : String(start));
3097
+ },
3067
3098
  /**
3068
3099
  * #getter
3069
3100
  * `highlights` projected onto what is on screen: residue spans go
@@ -3079,7 +3110,10 @@ function stateModelFactory() {
3079
3110
  ...Object.values(transientHighlights).flat(),
3080
3111
  ];
3081
3112
  return all.flatMap(({ row, rows, start, end, label, color }) => {
3082
- const base = { label, color };
3113
+ const base = {
3114
+ label: label && this.fillHighlightLabel(label, row, start),
3115
+ color,
3116
+ };
3083
3117
  if (rows) {
3084
3118
  const rowIndices = rows
3085
3119
  .map(name => rowNamesSet.get(name))
@@ -3460,6 +3494,19 @@ function stateModelFactory() {
3460
3494
  }
3461
3495
  }
3462
3496
  }));
3497
+ // zoomToRegion needs a width and resolves residues through the visible
3498
+ // columns, so this waits for both, and for a tree file whose collapsed
3499
+ // clades would hide columns and move the target after the zoom
3500
+ addDisposer(self, autorun(() => {
3501
+ const { region } = self;
3502
+ if (region &&
3503
+ self.viewInitialized &&
3504
+ self.numColumns > 0 &&
3505
+ !(self.treeFilehandle && !self.data.tree)) {
3506
+ this.zoomToRegion(region);
3507
+ self.setRegion(undefined);
3508
+ }
3509
+ }));
3463
3510
  // the matchMedia query is pinned to the current device pixel ratio, so
3464
3511
  // each change re-registers against the new one
3465
3512
  if (typeof window !== 'undefined' &&