@jbrowse/core 2.11.2 → 2.12.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.
Files changed (51) hide show
  1. package/BaseFeatureWidget/BaseFeatureDetail/DataGridDetails.js +16 -21
  2. package/BaseFeatureWidget/BaseFeatureDetail/index.js +1 -3
  3. package/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +24 -85
  4. package/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +2 -2
  5. package/BaseFeatureWidget/SequenceFeatureDetails/SequencePanel.d.ts +3 -4
  6. package/BaseFeatureWidget/SequenceFeatureDetails/SequencePanel.js +55 -24
  7. package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceDialog.d.ts +9 -0
  8. package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceDialog.js +69 -0
  9. package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceFeatureMenu.d.ts +9 -0
  10. package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceFeatureMenu.js +126 -0
  11. package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceTypeSelector.d.ts +6 -0
  12. package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SequenceTypeSelector.js +68 -0
  13. package/BaseFeatureWidget/SequenceFeatureDetails/dialogs/SettingsDialog.js +19 -10
  14. package/BaseFeatureWidget/SequenceFeatureDetails/model.d.ts +47 -0
  15. package/BaseFeatureWidget/SequenceFeatureDetails/model.js +83 -9
  16. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.d.ts +3 -1
  17. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +76 -19
  18. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDSSequence.d.ts +5 -2
  19. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDSSequence.js +12 -3
  20. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/GenomicSequence.d.ts +7 -2
  21. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/GenomicSequence.js +51 -5
  22. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/ProteinSequence.d.ts +5 -2
  23. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/ProteinSequence.js +12 -3
  24. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/SequenceDisplay.d.ts +11 -0
  25. package/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/SequenceDisplay.js +30 -0
  26. package/BaseFeatureWidget/SequenceFeatureDetails/util.d.ts +11 -0
  27. package/BaseFeatureWidget/SequenceFeatureDetails/util.js +43 -1
  28. package/BaseFeatureWidget/stateModelFactory.d.ts +32 -2
  29. package/PluginManager.d.ts +2 -11
  30. package/ReExports/modules.d.ts +2 -11
  31. package/package.json +4 -2
  32. package/stories/JBrowseCore.stories.d.ts +5 -0
  33. package/stories/JBrowseCore.stories.js +10 -0
  34. package/stories/examples/WithSequencePanel.d.ts +7 -0
  35. package/stories/examples/WithSequencePanel.js +43 -0
  36. package/stories/examples/index.d.ts +1 -0
  37. package/stories/examples/index.js +17 -0
  38. package/stories/examples/util.d.ts +33 -0
  39. package/stories/examples/util.js +235 -0
  40. package/tsconfig.build.tsbuildinfo +1 -1
  41. package/ui/Dialog.js +2 -1
  42. package/util/Base1DUtils.d.ts +1 -1
  43. package/util/Base1DUtils.js +3 -7
  44. package/util/Base1DViewModel.d.ts +5 -16
  45. package/util/Base1DViewModel.js +6 -12
  46. package/util/index.d.ts +1 -1
  47. package/util/tracks.d.ts +3 -2
  48. package/util/tracks.js +10 -6
  49. package/util/types/index.d.ts +4 -1
  50. package/ui/useResizeBar.d.ts +0 -5
  51. package/ui/useResizeBar.js +0 -22
@@ -36,25 +36,34 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
36
36
  dialogContent: {
37
37
  width: '80em',
38
38
  },
39
+ root: {
40
+ padding: 4,
41
+ },
39
42
  }));
43
+ function TextField2(props) {
44
+ return (react_1.default.createElement("div", null,
45
+ react_1.default.createElement(material_1.TextField, { ...props })));
46
+ }
47
+ function FormControl2({ children }) {
48
+ return (react_1.default.createElement("div", null,
49
+ react_1.default.createElement(material_1.FormControl, null, children)));
50
+ }
40
51
  const SequenceFeatureSettingsDialog = (0, mobx_react_1.observer)(function ({ handleClose, model, }) {
41
52
  const { classes } = useStyles();
53
+ const { upperCaseCDS } = model;
42
54
  const [intronBp, setIntronBp] = (0, react_1.useState)(`${model.intronBp}`);
43
55
  const [upDownBp, setUpDownBp] = (0, react_1.useState)(`${model.upDownBp}`);
44
56
  const intronBpValid = !Number.isNaN(+intronBp);
45
57
  const upDownBpValid = !Number.isNaN(+upDownBp);
46
58
  return (react_1.default.createElement(ui_1.Dialog, { maxWidth: "xl", open: true, onClose: () => handleClose(), title: "Feature sequence settings" },
47
59
  react_1.default.createElement(material_1.DialogContent, { className: classes.dialogContent },
48
- react_1.default.createElement("div", null,
49
- react_1.default.createElement(material_1.TextField, { label: "Number of intronic bases around splice site to display", className: classes.formElt, value: intronBp, helperText: !intronBpValid ? 'Not a number' : '', error: !intronBpValid, onChange: event => setIntronBp(event.target.value) })),
50
- react_1.default.createElement("div", null,
51
- react_1.default.createElement(material_1.TextField, { label: "Number of bases up/down stream of feature to display", className: classes.formElt, value: upDownBp, helperText: !upDownBpValid ? 'Not a number' : '', error: !upDownBpValid, onChange: event => setUpDownBp(event.target.value) })),
52
- react_1.default.createElement("div", null,
53
- react_1.default.createElement(material_1.FormControl, null,
54
- react_1.default.createElement(material_1.FormLabel, null, "Sequence capitalization"),
55
- react_1.default.createElement(material_1.RadioGroup, { value: model.upperCaseCDS ? 'cds' : 'unchanged', onChange: e => model.setUpperCaseCDS(e.target.value === 'cds') },
56
- react_1.default.createElement(material_1.FormControlLabel, { value: "cds", control: react_1.default.createElement(material_1.Radio, null), label: "Capitalize CDS and lower case everything else" }),
57
- react_1.default.createElement(material_1.FormControlLabel, { value: "unchanged", control: react_1.default.createElement(material_1.Radio, null), label: "Capitalization from reference genome sequence" }))))),
60
+ react_1.default.createElement(TextField2, { label: "Number of intronic bases around splice site to display", className: classes.formElt, value: intronBp, helperText: !intronBpValid ? 'Not a number' : '', error: !intronBpValid, onChange: event => setIntronBp(event.target.value) }),
61
+ react_1.default.createElement(TextField2, { label: "Number of bases up/down stream of feature to display", className: classes.formElt, value: upDownBp, helperText: !upDownBpValid ? 'Not a number' : '', error: !upDownBpValid, onChange: event => setUpDownBp(event.target.value) }),
62
+ react_1.default.createElement(FormControl2, null,
63
+ react_1.default.createElement(material_1.FormLabel, null, "Sequence capitalization"),
64
+ react_1.default.createElement(material_1.RadioGroup, { value: upperCaseCDS ? 'cds' : 'unchanged', onChange: e => model.setUpperCaseCDS(e.target.value === 'cds') },
65
+ react_1.default.createElement(material_1.FormControlLabel, { value: "cds", control: react_1.default.createElement(material_1.Radio, { className: classes.root, size: "small" }), label: "Capitalize CDS and lower case everything else" }),
66
+ react_1.default.createElement(material_1.FormControlLabel, { value: "unchanged", control: react_1.default.createElement(material_1.Radio, { className: classes.root, size: "small" }), label: "Capitalization from reference genome sequence" })))),
58
67
  react_1.default.createElement(material_1.DialogActions, null,
59
68
  react_1.default.createElement(material_1.Button, { onClick: () => {
60
69
  model.setIntronBp(+intronBp);
@@ -1,12 +1,59 @@
1
1
  import { Instance } from 'mobx-state-tree';
2
+ import { SimpleFeatureSerialized } from '../../util';
2
3
  export declare function SequenceFeatureDetailsF(): import("mobx-state-tree").IModelType<{}, {
4
+ showCoordinatesSetting: string;
3
5
  intronBp: number;
4
6
  upDownBp: number;
5
7
  upperCaseCDS: boolean;
8
+ charactersPerRow: number;
9
+ feature: SimpleFeatureSerialized | undefined;
10
+ mode: string;
6
11
  } & {
12
+ /**
13
+ * #action
14
+ */
15
+ setFeature(f: SimpleFeatureSerialized): void;
16
+ /**
17
+ * #action
18
+ */
7
19
  setUpDownBp(f: number): void;
20
+ /**
21
+ * #action
22
+ */
8
23
  setIntronBp(f: number): void;
24
+ /**
25
+ * #action
26
+ */
9
27
  setUpperCaseCDS(f: boolean): void;
28
+ /**
29
+ * #action
30
+ */
31
+ setShowCoordinates(f: 'none' | 'relative' | 'genomic'): void;
32
+ /**
33
+ * #action
34
+ */
35
+ setMode(mode: string): void;
36
+ } & {
37
+ /**
38
+ * #getter
39
+ */
40
+ readonly showCoordinates: boolean;
41
+ /**
42
+ * #getter
43
+ */
44
+ readonly showGenomicCoordsOption: boolean;
45
+ /**
46
+ * #getter
47
+ */
48
+ readonly hasCDS: boolean | undefined;
49
+ /**
50
+ * #getter
51
+ */
52
+ readonly hasExon: boolean | undefined;
53
+ /**
54
+ * #getter
55
+ */
56
+ readonly hasExonOrCDS: boolean | undefined;
10
57
  } & {
11
58
  afterAttach(): void;
12
59
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
@@ -5,27 +5,97 @@ const mobx_state_tree_1 = require("mobx-state-tree");
5
5
  const mobx_1 = require("mobx");
6
6
  // locals
7
7
  const util_1 = require("../../util");
8
+ function localStorageGetNumber(key, defaultVal) {
9
+ var _a;
10
+ return +((_a = (0, util_1.localStorageGetItem)(key)) !== null && _a !== void 0 ? _a : defaultVal);
11
+ }
8
12
  function SequenceFeatureDetailsF() {
9
13
  return mobx_state_tree_1.types
10
- .model('SequenceFeatureDetails', {})
11
- .volatile(() => {
12
- var _a, _b;
13
- return ({
14
- intronBp: +((_a = (0, util_1.localStorageGetItem)('sequenceFeatureDetails-intronBp')) !== null && _a !== void 0 ? _a : 10),
15
- upDownBp: +((_b = (0, util_1.localStorageGetItem)('sequenceFeatureDetails-upDownBp')) !== null && _b !== void 0 ? _b : 100),
16
- upperCaseCDS: Boolean(JSON.parse((0, util_1.localStorageGetItem)('sequenceFeatureDetails-upperCaseCDS') || 'true')),
17
- });
18
- })
14
+ .model('SequenceFeatureDetails')
15
+ .volatile(() => ({
16
+ showCoordinatesSetting: (0, util_1.localStorageGetItem)('sequenceFeatureDetails-showCoordinatesSetting') ||
17
+ 'none',
18
+ intronBp: localStorageGetNumber('sequenceFeatureDetails-intronBp', 10),
19
+ upDownBp: localStorageGetNumber('sequenceFeatureDetails-upDownBp', 100),
20
+ upperCaseCDS: Boolean(JSON.parse((0, util_1.localStorageGetItem)('sequenceFeatureDetails-upperCaseCDS') || 'true')),
21
+ charactersPerRow: 100,
22
+ feature: undefined,
23
+ mode: '',
24
+ }))
19
25
  .actions(self => ({
26
+ /**
27
+ * #action
28
+ */
29
+ setFeature(f) {
30
+ self.feature = f;
31
+ },
32
+ /**
33
+ * #action
34
+ */
20
35
  setUpDownBp(f) {
21
36
  self.upDownBp = f;
22
37
  },
38
+ /**
39
+ * #action
40
+ */
23
41
  setIntronBp(f) {
24
42
  self.intronBp = f;
25
43
  },
44
+ /**
45
+ * #action
46
+ */
26
47
  setUpperCaseCDS(f) {
27
48
  self.upperCaseCDS = f;
28
49
  },
50
+ /**
51
+ * #action
52
+ */
53
+ setShowCoordinates(f) {
54
+ self.showCoordinatesSetting = f;
55
+ },
56
+ /**
57
+ * #action
58
+ */
59
+ setMode(mode) {
60
+ self.mode = mode;
61
+ },
62
+ }))
63
+ .views(self => ({
64
+ /**
65
+ * #getter
66
+ */
67
+ get showCoordinates() {
68
+ return self.showCoordinatesSetting !== 'none';
69
+ },
70
+ /**
71
+ * #getter
72
+ */
73
+ get showGenomicCoordsOption() {
74
+ return (self.mode === 'gene' ||
75
+ self.mode === 'gene_updownstream' ||
76
+ self.mode === 'genomic' ||
77
+ self.mode === 'genomic_sequence_updownstream');
78
+ },
79
+ /**
80
+ * #getter
81
+ */
82
+ get hasCDS() {
83
+ var _a, _b;
84
+ return (_b = (_a = self.feature) === null || _a === void 0 ? void 0 : _a.subfeatures) === null || _b === void 0 ? void 0 : _b.some(sub => sub.type === 'CDS');
85
+ },
86
+ /**
87
+ * #getter
88
+ */
89
+ get hasExon() {
90
+ var _a, _b;
91
+ return (_b = (_a = self.feature) === null || _a === void 0 ? void 0 : _a.subfeatures) === null || _b === void 0 ? void 0 : _b.some(sub => sub.type === 'exon');
92
+ },
93
+ /**
94
+ * #getter
95
+ */
96
+ get hasExonOrCDS() {
97
+ return this.hasExon || this.hasCDS;
98
+ },
29
99
  }))
30
100
  .actions(self => ({
31
101
  afterAttach() {
@@ -33,6 +103,10 @@ function SequenceFeatureDetailsF() {
33
103
  (0, util_1.localStorageSetItem)('sequenceFeatureDetails-upDownBp', JSON.stringify(self.upDownBp));
34
104
  (0, util_1.localStorageSetItem)('sequenceFeatureDetails-intronBp', JSON.stringify(self.intronBp));
35
105
  (0, util_1.localStorageSetItem)('sequenceFeatureDetails-upperCaseCDS', JSON.stringify(self.upperCaseCDS));
106
+ (0, util_1.localStorageSetItem)('sequenceFeatureDetails-showCoordinatesSetting', self.showCoordinatesSetting);
107
+ }));
108
+ (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
109
+ self.setMode(self.hasCDS ? 'cds' : self.hasExon ? 'cdna' : 'genomic');
36
110
  }));
37
111
  },
38
112
  }));
@@ -1,13 +1,15 @@
1
1
  import React from 'react';
2
2
  import { Feat } from '../../util';
3
3
  import { SequenceFeatureDetailsModel } from '../model';
4
- declare const CDNASequence: ({ utr, cds, exons, sequence, upstream, downstream, includeIntrons, collapseIntron, model, }: {
4
+ import { SimpleFeatureSerialized } from '../../../util';
5
+ declare const CDNASequence: ({ utr, cds, exons, sequence, upstream, downstream, feature, includeIntrons, collapseIntron, model, }: {
5
6
  utr: Feat[];
6
7
  cds: Feat[];
7
8
  exons: Feat[];
8
9
  sequence: string;
9
10
  upstream?: string;
10
11
  downstream?: string;
12
+ feature: SimpleFeatureSerialized;
11
13
  includeIntrons?: boolean;
12
14
  collapseIntron?: boolean;
13
15
  model: SequenceFeatureDetailsModel;
@@ -6,29 +6,86 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  const mobx_react_1 = require("mobx-react");
8
8
  const util_1 = require("../util");
9
- const CDNASequence = (0, mobx_react_1.observer)(function ({ utr, cds, exons, sequence, upstream, downstream, includeIntrons, collapseIntron, model, }) {
10
- const { upperCaseCDS, intronBp } = model;
9
+ const SequenceDisplay_1 = __importDefault(require("./SequenceDisplay"));
10
+ const CDNASequence = (0, mobx_react_1.observer)(function ({ utr, cds, exons, sequence, upstream, downstream, feature, includeIntrons, collapseIntron, model, }) {
11
+ var _a;
12
+ const { upperCaseCDS, intronBp, charactersPerRow, showCoordinates, showCoordinatesSetting, } = model;
11
13
  const hasCds = cds.length > 0;
12
14
  const chunks = (cds.length ? [...cds, ...utr].sort((a, b) => a.start - b.start) : exons).filter(f => f.start !== f.end);
13
15
  const toLower = (s) => (upperCaseCDS ? s.toLowerCase() : s);
14
16
  const toUpper = (s) => (upperCaseCDS ? s.toUpperCase() : s);
17
+ const strand = feature.strand === -1 ? -1 : 1;
18
+ const fullGenomicCoordinates = showCoordinatesSetting === 'genomic' && includeIntrons && !collapseIntron;
19
+ const mult = fullGenomicCoordinates ? strand : 1;
20
+ let coordStart = fullGenomicCoordinates
21
+ ? strand > 0
22
+ ? feature.start + 1 - ((upstream === null || upstream === void 0 ? void 0 : upstream.length) || 0)
23
+ : feature.end + ((upstream === null || upstream === void 0 ? void 0 : upstream.length) || 0)
24
+ : 0;
25
+ let currStart = 0;
26
+ let currRemainder = 0;
27
+ let upstreamChunk = null;
28
+ if (upstream) {
29
+ const { segments, remainder } = (0, util_1.splitString)({
30
+ str: toLower(upstream),
31
+ charactersPerRow,
32
+ showCoordinates,
33
+ });
34
+ upstreamChunk = (react_1.default.createElement(SequenceDisplay_1.default, { model: model, color: util_1.updownstreamColor, strand: mult, start: currStart, coordStart: coordStart, chunks: segments }));
35
+ currRemainder = remainder;
36
+ currStart = currStart + upstream.length * mult;
37
+ coordStart = coordStart + upstream.length * mult;
38
+ }
39
+ const middleChunks = [];
40
+ for (let idx = 0; idx < chunks.length; idx++) {
41
+ const chunk = chunks[idx];
42
+ const intron = sequence.slice(chunk.end, (_a = chunks[idx + 1]) === null || _a === void 0 ? void 0 : _a.start);
43
+ const s = sequence.slice(chunk.start, chunk.end);
44
+ const { segments, remainder } = (0, util_1.splitString)({
45
+ str: hasCds
46
+ ? chunk.type === 'CDS'
47
+ ? toUpper(s)
48
+ : toLower(s)
49
+ : toUpper(s),
50
+ charactersPerRow,
51
+ currRemainder,
52
+ showCoordinates,
53
+ });
54
+ middleChunks.push(react_1.default.createElement(SequenceDisplay_1.default, { key: JSON.stringify(chunk) + '-mid', model: model, color: chunk.type === 'CDS' ? util_1.cdsColor : util_1.utrColor, strand: mult, start: currStart, coordStart: coordStart, chunks: segments }));
55
+ currRemainder = remainder;
56
+ currStart = currStart + s.length * mult;
57
+ coordStart = coordStart + s.length * mult;
58
+ if (intron && includeIntrons && idx < chunks.length - 1) {
59
+ const str = toLower(collapseIntron && intron.length > intronBp * 2
60
+ ? `${intron.slice(0, intronBp)}...${intron.slice(-intronBp)}`
61
+ : intron);
62
+ const { segments, remainder } = (0, util_1.splitString)({
63
+ str,
64
+ charactersPerRow,
65
+ currRemainder,
66
+ showCoordinates,
67
+ });
68
+ if (segments.length) {
69
+ middleChunks.push(react_1.default.createElement(SequenceDisplay_1.default, { key: JSON.stringify(chunk) + '-intron', model: model, strand: mult, coordStart: coordStart, start: currStart, chunks: segments }));
70
+ currRemainder = remainder;
71
+ currStart = currStart + str.length * mult;
72
+ coordStart = coordStart + str.length * mult;
73
+ }
74
+ }
75
+ }
76
+ let downstreamChunk = null;
77
+ if (downstream) {
78
+ const { segments } = (0, util_1.splitString)({
79
+ str: toLower(downstream),
80
+ charactersPerRow,
81
+ currRemainder,
82
+ showCoordinates,
83
+ });
84
+ downstreamChunk = (react_1.default.createElement(SequenceDisplay_1.default, { start: currStart, model: model, strand: mult, chunks: segments, coordStart: coordStart, color: util_1.updownstreamColor }));
85
+ }
15
86
  return (react_1.default.createElement(react_1.default.Fragment, null,
16
- upstream ? (react_1.default.createElement("span", { style: { background: util_1.updownstreamColor } }, toLower(upstream))) : null,
17
- chunks.map((chunk, idx) => {
18
- var _a;
19
- const intron = sequence.slice(chunk.end, (_a = chunks[idx + 1]) === null || _a === void 0 ? void 0 : _a.start);
20
- return (react_1.default.createElement(react_1.default.Fragment, { key: JSON.stringify(chunk) },
21
- react_1.default.createElement("span", { style: {
22
- background: chunk.type === 'CDS' ? util_1.cdsColor : util_1.utrColor,
23
- } }, hasCds
24
- ? chunk.type === 'CDS'
25
- ? toUpper(sequence.slice(chunk.start, chunk.end))
26
- : toLower(sequence.slice(chunk.start, chunk.end))
27
- : toUpper(sequence.slice(chunk.start, chunk.end))),
28
- includeIntrons && idx < chunks.length - 1 ? (react_1.default.createElement("span", { style: { background: util_1.intronColor } }, toLower(collapseIntron && intron.length > intronBp * 2
29
- ? `${intron.slice(0, intronBp)}...${intron.slice(-intronBp)}`
30
- : intron))) : null));
31
- }),
32
- downstream ? (react_1.default.createElement("span", { style: { background: util_1.updownstreamColor } }, toLower(downstream))) : null));
87
+ upstreamChunk,
88
+ middleChunks,
89
+ downstreamChunk));
33
90
  });
34
91
  exports.default = CDNASequence;
@@ -1,6 +1,9 @@
1
1
  import React from 'react';
2
2
  import { Feat } from '../../util';
3
- export default function CDSSequence({ cds, sequence, }: {
3
+ import { SequenceFeatureDetailsModel } from '../model';
4
+ declare const CDSSequence: ({ cds, sequence, model, }: {
4
5
  cds: Feat[];
5
6
  sequence: string;
6
- }): React.JSX.Element;
7
+ model: SequenceFeatureDetailsModel;
8
+ }) => React.JSX.Element;
9
+ export default CDSSequence;
@@ -4,9 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
+ const mobx_react_1 = require("mobx-react");
8
+ // locals
7
9
  const util_1 = require("../util");
8
10
  const util_2 = require("../../util");
9
- function CDSSequence({ cds, sequence, }) {
10
- return react_1.default.createElement("span", { style: { background: util_1.cdsColor } }, (0, util_2.stitch)(cds, sequence));
11
- }
11
+ const SequenceDisplay_1 = __importDefault(require("./SequenceDisplay"));
12
+ const CDSSequence = (0, mobx_react_1.observer)(function ({ cds, sequence, model, }) {
13
+ const { charactersPerRow, showCoordinates } = model;
14
+ const { segments } = (0, util_1.splitString)({
15
+ str: (0, util_2.stitch)(cds, sequence),
16
+ charactersPerRow,
17
+ showCoordinates,
18
+ });
19
+ return (react_1.default.createElement(SequenceDisplay_1.default, { model: model, color: util_1.cdsColor, chunks: segments, start: 0 }));
20
+ });
12
21
  exports.default = CDSSequence;
@@ -1,6 +1,11 @@
1
1
  import React from 'react';
2
- export default function GenomicSequence({ sequence, upstream, downstream, }: {
2
+ import { SequenceFeatureDetailsModel } from '../model';
3
+ import { SimpleFeatureSerialized } from '../../../util';
4
+ declare const GenomicSequence: ({ sequence, upstream, feature, downstream, model, }: {
3
5
  sequence: string;
6
+ feature: SimpleFeatureSerialized;
4
7
  upstream?: string;
5
8
  downstream?: string;
6
- }): React.JSX.Element;
9
+ model: SequenceFeatureDetailsModel;
10
+ }) => React.JSX.Element;
11
+ export default GenomicSequence;
@@ -4,11 +4,57 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
+ const mobx_react_1 = require("mobx-react");
8
+ // locals
7
9
  const util_1 = require("../util");
8
- function GenomicSequence({ sequence, upstream, downstream, }) {
10
+ const SequenceDisplay_1 = __importDefault(require("./SequenceDisplay"));
11
+ const GenomicSequence = (0, mobx_react_1.observer)(function ({ sequence, upstream, feature, downstream, model, }) {
12
+ const { charactersPerRow, showCoordinatesSetting, showCoordinates } = model;
13
+ let currStart = 0;
14
+ let upstreamChunk = null;
15
+ let currRemainder = 0;
16
+ const strand = feature.strand === -1 ? -1 : 1;
17
+ const fullGenomicCoordinates = showCoordinatesSetting === 'genomic';
18
+ const mult = fullGenomicCoordinates ? strand : 1;
19
+ let coordStart = fullGenomicCoordinates
20
+ ? strand > 0
21
+ ? feature.start + 1 - ((upstream === null || upstream === void 0 ? void 0 : upstream.length) || 0)
22
+ : feature.end + ((upstream === null || upstream === void 0 ? void 0 : upstream.length) || 0)
23
+ : 0;
24
+ if (upstream) {
25
+ const { segments, remainder } = (0, util_1.splitString)({
26
+ str: upstream,
27
+ charactersPerRow,
28
+ showCoordinates,
29
+ });
30
+ upstreamChunk = (react_1.default.createElement(SequenceDisplay_1.default, { model: model, color: util_1.updownstreamColor, start: currStart, coordStart: coordStart, chunks: segments }));
31
+ currRemainder = remainder;
32
+ currStart = currStart + upstream.length * mult;
33
+ coordStart = coordStart + upstream.length * mult;
34
+ }
35
+ const { segments, remainder } = (0, util_1.splitString)({
36
+ str: sequence,
37
+ charactersPerRow,
38
+ showCoordinates,
39
+ currRemainder,
40
+ });
41
+ const middleChunk = (react_1.default.createElement(SequenceDisplay_1.default, { model: model, color: util_1.genomeColor, start: currStart, coordStart: coordStart, chunks: segments }));
42
+ currRemainder = remainder;
43
+ currStart += sequence.length * mult;
44
+ coordStart = coordStart + sequence.length * mult;
45
+ let downstreamChunk = null;
46
+ if (downstream) {
47
+ const { segments } = (0, util_1.splitString)({
48
+ str: downstream,
49
+ charactersPerRow,
50
+ currRemainder,
51
+ showCoordinates,
52
+ });
53
+ downstreamChunk = (react_1.default.createElement(SequenceDisplay_1.default, { start: currStart, model: model, chunks: segments, coordStart: coordStart, color: util_1.updownstreamColor }));
54
+ }
9
55
  return (react_1.default.createElement(react_1.default.Fragment, null,
10
- upstream ? (react_1.default.createElement("span", { style: { background: util_1.updownstreamColor } }, upstream)) : null,
11
- react_1.default.createElement("span", { style: { background: util_1.genomeColor } }, sequence),
12
- downstream ? (react_1.default.createElement("span", { style: { background: util_1.updownstreamColor } }, downstream)) : null));
13
- }
56
+ upstreamChunk,
57
+ middleChunk,
58
+ downstreamChunk));
59
+ });
14
60
  exports.default = GenomicSequence;
@@ -1,7 +1,10 @@
1
1
  import React from 'react';
2
2
  import { Feat } from '../../util';
3
- export default function ProteinSequence({ cds, sequence, codonTable, }: {
3
+ import { SequenceFeatureDetailsModel } from '../model';
4
+ declare const ProteinSequence: ({ cds, sequence, codonTable, model, }: {
4
5
  cds: Feat[];
5
6
  sequence: string;
6
7
  codonTable: Record<string, string>;
7
- }): React.JSX.Element;
8
+ model: SequenceFeatureDetailsModel;
9
+ }) => React.JSX.Element;
10
+ export default ProteinSequence;
@@ -4,15 +4,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
+ const mobx_react_1 = require("mobx-react");
8
+ // locals
7
9
  const util_1 = require("../../util");
8
10
  const util_2 = require("../util");
9
- function ProteinSequence({ cds, sequence, codonTable, }) {
11
+ const SequenceDisplay_1 = __importDefault(require("./SequenceDisplay"));
12
+ const ProteinSequence = (0, mobx_react_1.observer)(function ({ cds, sequence, codonTable, model, }) {
13
+ const { charactersPerRow, showCoordinates } = model;
10
14
  const str = (0, util_1.stitch)(cds, sequence);
11
15
  let protein = '';
12
16
  for (let i = 0; i < str.length; i += 3) {
13
17
  // use & symbol for undefined codon, or partial slice
14
18
  protein += codonTable[str.slice(i, i + 3)] || '&';
15
19
  }
16
- return react_1.default.createElement("span", { style: { background: util_2.proteinColor } }, protein);
17
- }
20
+ const { segments } = (0, util_2.splitString)({
21
+ str: protein,
22
+ charactersPerRow,
23
+ showCoordinates,
24
+ });
25
+ return (react_1.default.createElement(SequenceDisplay_1.default, { model: model, color: util_2.proteinColor, chunks: segments, start: 0 }));
26
+ });
18
27
  exports.default = ProteinSequence;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { SequenceFeatureDetailsModel } from '../model';
3
+ declare const SequenceDisplay: ({ chunks, start, color, strand, coordStart, model, }: {
4
+ chunks: string[];
5
+ start: number;
6
+ coordStart?: number;
7
+ strand?: number;
8
+ color?: string;
9
+ model: SequenceFeatureDetailsModel;
10
+ }) => React.JSX.Element[];
11
+ export default SequenceDisplay;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const mobx_react_1 = require("mobx-react");
8
+ const SequenceDisplay = (0, mobx_react_1.observer)(function ({ chunks, start, color, strand = 1, coordStart = start, model, }) {
9
+ const { charactersPerRow, showCoordinates } = model;
10
+ return chunks.map((chunk, idx) => {
11
+ var _a;
12
+ const f = coordStart - (start % charactersPerRow);
13
+ const prefix = (idx == 0 && start % charactersPerRow == 0) || idx > 0
14
+ ? `${f + idx * strand * charactersPerRow}`.padStart(4) + ' '
15
+ : '';
16
+ const postfix = idx === chunks.length - 1 &&
17
+ (((_a = chunks.at(-1)) === null || _a === void 0 ? void 0 : _a.replaceAll(' ', '').length) || 0) +
18
+ (idx === 0 ? start % charactersPerRow : 0) !==
19
+ charactersPerRow
20
+ ? null
21
+ : showCoordinates
22
+ ? ' \n'
23
+ : '';
24
+ return (react_1.default.createElement(react_1.default.Fragment, { key: `${chunk}-${idx}` },
25
+ showCoordinates ? prefix : null,
26
+ react_1.default.createElement("span", { style: { background: color } }, chunk),
27
+ postfix));
28
+ });
29
+ });
30
+ exports.default = SequenceDisplay;
@@ -4,3 +4,14 @@ export declare const proteinColor = "rgb(220,160,220)";
4
4
  export declare const cdsColor = "rgb(220,220,180)";
5
5
  export declare const updownstreamColor = "rgba(250,200,200)";
6
6
  export declare const genomeColor = "rgb(200,280,200)";
7
+ export declare function splitString({ str, charactersPerRow, showCoordinates, currRemainder, splitSize, }: {
8
+ str: string;
9
+ charactersPerRow: number;
10
+ showCoordinates: boolean;
11
+ currRemainder?: number;
12
+ splitStart?: number;
13
+ splitSize?: number;
14
+ }): {
15
+ segments: any[];
16
+ remainder: number;
17
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.genomeColor = exports.updownstreamColor = exports.cdsColor = exports.proteinColor = exports.utrColor = exports.intronColor = void 0;
3
+ exports.splitString = exports.genomeColor = exports.updownstreamColor = exports.cdsColor = exports.proteinColor = exports.utrColor = exports.intronColor = void 0;
4
4
  // note that these are currently put into the style section instead of being
5
5
  // defined in classes to aid copy and paste to an external document e.g. word
6
6
  exports.intronColor = undefined;
@@ -9,3 +9,45 @@ exports.proteinColor = 'rgb(220,160,220)';
9
9
  exports.cdsColor = 'rgb(220,220,180)';
10
10
  exports.updownstreamColor = 'rgba(250,200,200)';
11
11
  exports.genomeColor = 'rgb(200,280,200)';
12
+ function splitString({ str, charactersPerRow, showCoordinates, currRemainder = 0, splitSize = 10, }) {
13
+ var _a;
14
+ const numChunks = Math.ceil(str.length / charactersPerRow);
15
+ const chunks = new Array(numChunks);
16
+ let splitStart = currRemainder % 10;
17
+ let iter = 0;
18
+ let offset = 0;
19
+ for (; iter < numChunks + 1; ++iter) {
20
+ const inc = iter === 0 ? charactersPerRow - currRemainder : charactersPerRow;
21
+ const r = str.slice(offset, offset + inc);
22
+ if (!r) {
23
+ break;
24
+ }
25
+ if (showCoordinates) {
26
+ let res = '';
27
+ for (let i = 0, j = splitStart; i < r.length; i++, j++) {
28
+ // note: this adds a space at the start but it causes trouble to try to
29
+ // say e.g. j%splitSize==0 && j to try to only add non-zero spaces
30
+ if (j % splitSize === 0) {
31
+ res += ' ';
32
+ j = 0;
33
+ }
34
+ res += r[i];
35
+ }
36
+ if (res) {
37
+ chunks[iter] = res;
38
+ }
39
+ }
40
+ else {
41
+ chunks[iter] = r;
42
+ }
43
+ splitStart = 0; // after newline, reset
44
+ offset += inc;
45
+ }
46
+ return {
47
+ segments: chunks,
48
+ remainder: ((((_a = chunks.at(-1)) === null || _a === void 0 ? void 0 : _a.replaceAll(' ', '').length) || 0) +
49
+ (iter < 2 ? currRemainder : 0)) %
50
+ charactersPerRow,
51
+ };
52
+ }
53
+ exports.splitString = splitString;