Skip to content

Commit ac1de9c

Browse files
author
josh
committed
Merge branch 'develop' into one_row_viewer
2 parents 1a8eb63 + 7b8f1a3 commit ac1de9c

File tree

11 files changed

+81
-57
lines changed

11 files changed

+81
-57
lines changed

README.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [Installation](#installation)
2929
- [Instantiation](#instantiation)
3030
- [Props](#props)
31+
- [Custom Viewer Positioning](#custom-viewer-positioning)
3132
- [Without React](#without-react)
3233
- [Contact Us](#contact-us)
3334

@@ -37,7 +38,7 @@ You can see a demo at [tools.latticeautomation.com/seqviz](https://tools.lattice
3738

3839
## Features
3940

40-
### Linear and Circular Sequence Viewer
41+
### Linear and Circular Sequence Viewers
4142

4243
- Annotations with names and colors
4344
- Amino acid translations
@@ -292,40 +293,37 @@ Whether to show the complement sequence.
292293

293294
By default, the circular viewer rotates when scrolling over the viewer. That can be disabled with rotateOnScroll: false.
294295

295-
#### `Custom Rendering`
296+
#### `refs: (={ circular: undefined, linear: undefined })`
297+
298+
See: [custom viewer positioning](#custom-viewer-positioning)
299+
300+
### Custom Viewer Positioning
296301

297302
This makes use of the `children` and `refs` props to allow custom rendering of the sequence viewers. For example, to render the linear viewer above the circular viewer:
298303

299304
```jsx
300305
import { useRef } from "react";
301-
import { SeqViz, Linear, Circular } from "seqviz";
306+
import { Circular, Linear, SeqViz } from "seqviz";
302307

303308
export default () => {
309+
const circular = useRef();
304310
const linearRef = useRef();
305-
const circularRef = useRef();
306311

307312
return (
308-
<SeqViz
309-
name="J23100"
310-
seq="TTGACGGCTAGCTCAGTCCTAGGTACAGTGCTAGC"
311-
refs={{circular: circularRef, linear: linearRef}}
312-
>
313+
<SeqViz name="J23100" seq="TTGACGGCTAGCTCAGTCCTAGGTACAGTGCTAGC" refs={{ circular, linear }}>
313314
{({ circularProps, linearProps, ...props }) => (
314-
<div
315-
style={{ display: "flex", flexDirection: "column", width: "100%" }}
316-
>
317-
<div ref={linearRef} style={{ height: "25%", width: "100%" }}>
315+
<div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
316+
<div ref={linear} style={{ height: "25%", width: "100%" }}>
318317
<Linear {...linearProps} {...props} />
319318
</div>
320-
<div ref={circularRef} style={{ height: "75%", width: "100%" }}>
319+
<div ref={circular} style={{ height: "75%", width: "100%" }}>
321320
<Circular {...circularProps} {...props} />
322321
</div>
323322
</div>
324323
)}
325324
</SeqViz>
326325
);
327326
};
328-
329327
```
330328

331329
### Without React

demo/lib/App.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ interface AppState {
4343
showIndex: boolean;
4444
showSelectionMeta: boolean;
4545
showSidebar: boolean;
46-
translations: { direction?: 1 | -1; end: number; start: number; }[];
46+
translations: { direction?: 1 | -1; end: number; start: number }[];
4747
viewer: string;
4848
zoom: number;
4949
}
@@ -104,19 +104,17 @@ export default class App extends React.Component<any, AppState> {
104104
};
105105

106106
render() {
107-
108107
let customChildren = null;
109108
if (this.state.customChildren) {
110109
customChildren = ({ circularProps, linearProps, ...props }) => {
111110
if (this.state.viewer === "linear") {
112-
return (
113-
<div ref={this.linearRef} style={{ height: "100%", width: "100%" }}>
114-
<Linear {...linearProps} {...props} />
115-
</div>
111+
return (
112+
<div ref={this.linearRef} style={{ height: "100%", width: "100%" }}>
113+
<Linear {...linearProps} {...props} />
114+
</div>
116115
);
117-
}
118-
else if (this.state.viewer === "circular") {
119-
return (
116+
} else if (this.state.viewer === "circular") {
117+
return (
120118
<div ref={this.circularRef} style={{ height: "100%", width: "100%" }}>
121119
<Circular {...circularProps} {...props} />
122120
</div>
@@ -155,7 +153,7 @@ export default class App extends React.Component<any, AppState> {
155153
</div>
156154
);
157155
}
158-
}
156+
};
159157
}
160158

161159
return (
@@ -222,7 +220,7 @@ export default class App extends React.Component<any, AppState> {
222220
annotations={this.state.annotations}
223221
enzymes={this.state.enzymes}
224222
name={this.state.name}
225-
refs={{circular: this.circularRef, linear: this.linearRef}}
223+
refs={{ circular: this.circularRef, linear: this.linearRef }}
226224
search={this.state.search}
227225
selection={this.state.selection}
228226
seq={this.state.seq}

demo/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"deploy": "npm update seqviz && npm run build && aws s3 sync ./out s3://lattice-tools-s3/seqviz --delete && aws cloudfront create-invalidation --distribution-id E3NMX6D92LFTAV --paths '/seqviz/*'",
99
"fix": "prettier ./lib/** ./pages/** --write && eslint lib --ext ts,tsx --fix",
1010
"lint": "prettier ./lib/** --check && eslint lib --ext ts,tsx --quiet",
11-
"start": "open http://localhost:3010/ && next dev -p 3010"
11+
"start": "open http://localhost:3010/ && next dev -p 3010",
12+
"dev": "next dev -p 3010"
1213
},
1314
"dependencies": {
1415
"browserslist": "^4.21.3",

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "seqviz",
33
"description": "A viewer for DNA, RNA, and protein sequences that supports many input formats",
4-
"version": "3.8.0",
4+
"version": "3.8.2",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
77
"unpkg": "dist/seqviz.min.js",
@@ -13,6 +13,7 @@
1313
"minor": "./release.sh minor",
1414
"patch": "./release.sh patch",
1515
"start": "cd demo && npm run start",
16+
"dev": "cd demo && npm install && npm run dev",
1617
"test": "CI=true jest"
1718
},
1819
"keywords": [

src/Linear/Linear.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export default class Linear extends React.Component<LinearProps> {
128128

129129
// TODO: this should also use createMultiRows
130130
const translationRows = translations.length
131-
? createSingleRows(createTranslations(translations, seq, seqType), bpsPerBlock, arrSize)
131+
? createMultiRows(stackElements(createTranslations(translations, seq, seqType), seq.length), bpsPerBlock, arrSize)
132132
: new Array(arrSize).fill([]);
133133

134134
for (let i = 0; i < arrSize; i += 1) {
@@ -202,7 +202,7 @@ export default class Linear extends React.Component<LinearProps> {
202202
size={size}
203203
stackedAnnotations={stackedAnnotations}
204204
stackedTranslations={stackedTranslations}
205-
translations={translationRows[i]}
205+
translationRows={translationRows[i]}
206206
x={xDiff}
207207
y={yDiff}
208208
zoom={zoom}

src/Linear/SeqBlock.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const defaultProps = {
3333
size: { height: 600, width: 1200 },
3434
stackedAnnotations: [],
3535
stackedTranslations: [],
36-
translations: [],
36+
translationRows: [],
3737
y: 0,
3838
zoom: { linear: 50 },
3939
zoomed: true,

src/Linear/SeqBlock.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ interface SeqBlockProps {
5353
size: Size;
5454
stackedAnnotations: Annotation[][];
5555
stackedTranslations: NameRange[][];
56-
translations: Translation[];
56+
translationRows: Translation[][];
5757
x: number;
5858
y: number;
5959
zoom: { linear: number };
@@ -244,7 +244,7 @@ export class SeqBlock extends React.PureComponent<SeqBlockProps> {
244244
size,
245245
stackedAnnotations,
246246
stackedTranslations,
247-
translations,
247+
translationRows,
248248
zoom,
249249
zoomed,
250250
} = this.props;
@@ -275,14 +275,14 @@ export class SeqBlock extends React.PureComponent<SeqBlockProps> {
275275

276276
// height and yDiff of translations
277277
const translationYDiff = compYDiff + compHeight;
278-
const translationHeight = elementHeight * (oneRow ? stackedTranslations.length : translations.length);
278+
const translationHeight = elementHeight * (oneRow ? stackedTranslations.length : translationRows.length);
279279

280280
// height and yDiff of annotations
281281
const annYDiff = translationYDiff + translationHeight;
282282
const annHeight = elementHeight * (oneRow ? stackedAnnotations.length : annotationRows.length);
283283

284-
// height and ydiff of the index row.
285-
const elementGap = translationHeight || annHeight ? 3 : 0;
284+
// height and yDiff of the index row.
285+
const elementGap = annotationRows.length + translationRows.length ? 3 : 0;
286286
const indexRowYDiff = annYDiff + annHeight + elementGap;
287287

288288
// calc the height necessary for the sequence selection
@@ -368,7 +368,7 @@ export class SeqBlock extends React.PureComponent<SeqBlockProps> {
368368
listenerOnly={false}
369369
zoomed={zoomed}
370370
/>
371-
{translations.length && (
371+
{translationRows.length && (
372372
<TranslationRows
373373
bpsPerBlock={bpsPerBlock}
374374
charWidth={charWidth}
@@ -381,7 +381,7 @@ export class SeqBlock extends React.PureComponent<SeqBlockProps> {
381381
oneRow={oneRow}
382382
seqType={seqType}
383383
stackedPositions={stackedTranslations}
384-
translations={translations}
384+
translationRows={translationRows}
385385
yDiff={translationYDiff}
386386
onUnmount={onUnmount}
387387
/>

src/Linear/Translations.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface TranslationRowsProps {
1919
oneRow: boolean;
2020
seqType: SeqType;
2121
stackedPositions: NameRange[][];
22-
translations: Translation[];
22+
translationRows: Translation[][];
2323
yDiff: number;
2424
}
2525

@@ -37,43 +37,66 @@ export const TranslationRows = ({
3737
oneRow,
3838
seqType,
3939
stackedPositions,
40-
translations,
40+
translationRows,
4141
yDiff,
4242
}: TranslationRowsProps) => (
4343
<g className="la-vz-linear-translation" data-testid="la-vz-linear-translation">
44-
{translations.map((t, i) => (
44+
{translationRows.map((translations, i) => (
4545
<TranslationRow
46-
key={`${t.id}-${firstBase}`}
46+
key={`i-${firstBase}`}
4747
bpsPerBlock={bpsPerBlock}
4848
charWidth={charWidth}
4949
findXAndWidth={findXAndWidth}
5050
firstBase={firstBase}
5151
fullSeq={fullSeq}
5252
height={elementHeight * 0.9}
53-
id={t.id}
5453
inputRef={inputRef}
5554
lastBase={lastBase}
5655
seqType={seqType}
57-
translation={t}
58-
y={
59-
yDiff +
60-
elementHeight *
61-
(oneRow ? (stackedPositions.findIndex(rows => rows.some(item => item.id === t.id)) as number) : i)
62-
}
56+
translations={translations}
57+
y={yDiff + elementHeight * i}
6358
onUnmount={onUnmount}
6459
/>
6560
))}
6661
</g>
6762
);
6863

69-
interface TranslationRowProps {
64+
/**
65+
* A single row of translations. Multiple of these may be in one seqBlock
66+
* vertically stacked on top of one another in non-overlapping arrays.
67+
*/
68+
const TranslationRow = (props: {
69+
bpsPerBlock: number;
70+
charWidth: number;
71+
findXAndWidth: FindXAndWidthType;
72+
firstBase: number;
73+
fullSeq: string;
74+
height: number;
75+
inputRef: InputRefFunc;
76+
lastBase: number;
77+
onUnmount: (a: unknown) => void;
78+
seqType: SeqType;
79+
translations: Translation[];
80+
y: number;
81+
}) => (
82+
<>
83+
{props.translations.map((t, i) => (
84+
<SingleNamedElement
85+
{...props} // include overflowLeft in the key to avoid two split annotations in the same row from sharing a key
86+
key={`translation-linear-${t.id}-${i}-${props.firstBase}-${props.lastBase}`}
87+
translation={t}
88+
/>
89+
))}
90+
</>
91+
);
92+
93+
interface SingleNamedElementProps {
7094
bpsPerBlock: number;
7195
charWidth: number;
7296
findXAndWidth: FindXAndWidthType;
7397
firstBase: number;
7498
fullSeq: string;
7599
height: number;
76-
id?: string;
77100
inputRef: InputRefFunc;
78101
lastBase: number;
79102
onUnmount: (a: unknown) => void;
@@ -86,7 +109,7 @@ interface TranslationRowProps {
86109
* A single row for translations of DNA into Amino Acid sequences so a user can
87110
* see the resulting protein or peptide sequence in the viewer
88111
*/
89-
class TranslationRow extends React.PureComponent<TranslationRowProps> {
112+
class SingleNamedElement extends React.PureComponent<SingleNamedElementProps> {
90113
AAs: string[] = [];
91114

92115
// on unmount, clear all AA references.

src/SelectionHandler.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ export default class SelectionHandler extends React.PureComponent<SelectionHandl
234234
* Handle a sequence selection event on the circular viewer
235235
*/
236236
handleCircularSeqEvent = (e: SeqVizMouseEvent) => {
237-
const { seq } = this.props;
237+
const { seq, setCentralIndex } = this.props;
238238
const selection = this.context;
239239

240240
const { start } = selection;
@@ -250,6 +250,7 @@ export default class SelectionHandler extends React.PureComponent<SelectionHandl
250250
: this.calcSelectionLength(selStart, currBase, true); // check clockwise selection length
251251
this.selectionStarted = lookahead > 0; // update check for whether there is a prior selection
252252
this.resetCircleDragVars(selStart); // begin drag event
253+
setCentralIndex?.("LINEAR", selStart);
253254

254255
this.setSelection({
255256
...defaultSelection,

0 commit comments

Comments
 (0)