@datagrok/bio 2.15.7 → 2.15.9
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/CHANGELOG.md +25 -0
- package/dist/284.js +1 -1
- package/dist/284.js.map +1 -1
- package/dist/package-test.js +3 -3
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +3 -3
- package/dist/package.js.map +1 -1
- package/files/monomer-libraries/sample-lib-Aca-colored.json +2 -2
- package/package.json +8 -8
- package/src/analysis/sequence-activity-cliffs.ts +10 -5
- package/src/analysis/sequence-similarity-viewer.ts +1 -1
- package/src/package.ts +1 -1
- package/src/tests/WebLogo-positions-test.ts +1 -1
- package/src/tests/splitters-test.ts +23 -6
- package/src/utils/cell-renderer-custom.ts +10 -2
- package/src/utils/cell-renderer.ts +102 -128
- package/src/utils/monomer-cell-renderer-base.ts +30 -0
- package/src/utils/monomer-cell-renderer.ts +73 -54
- package/src/utils/monomer-lib/lib-manager.ts +1 -1
- package/src/utils/monomer-lib/monomer-colors.ts +33 -30
- package/src/utils/monomer-lib/monomer-lib-base.ts +151 -13
- package/src/utils/monomer-lib/monomer-lib.ts +7 -50
- package/src/utils/monomer-lib/monomer-manager/monomer-manager.ts +94 -5
- package/src/utils/monomer-lib/web-editor-monomer-dummy.ts +11 -4
- package/src/utils/monomer-lib/web-editor-monomer-of-library.ts +5 -37
- package/src/viewers/web-logo-viewer.ts +40 -27
- package/src/widgets/composition-analysis-widget.ts +12 -17
|
@@ -16,6 +16,7 @@ import {MonomerLibManager} from '../lib-manager';
|
|
|
16
16
|
import {LIB_PATH} from '../consts';
|
|
17
17
|
|
|
18
18
|
import '../../../../css/monomer-manager.css';
|
|
19
|
+
import { MONOMER_RENDERER_TAGS } from '@datagrok-libraries/bio/src/utils/cell-renderer';
|
|
19
20
|
|
|
20
21
|
// columns of monomers dataframe, note that rgroups is hidden and will be displayed as separate columns
|
|
21
22
|
export enum MONOMER_DF_COLUMN_NAMES {
|
|
@@ -50,6 +51,16 @@ export const MONOMER_DF_COLUMNS = {
|
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
export class MonomerManager implements IMonomerManager {
|
|
54
|
+
|
|
55
|
+
private adjustColWidths() {
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
if (this.tv?.grid) {
|
|
58
|
+
this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.NAME)!.width = 100;
|
|
59
|
+
this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.width = 70;
|
|
60
|
+
}
|
|
61
|
+
}, 200);
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
public static readonly VIEW_NAME = 'Manage Monomers';
|
|
54
65
|
private _newMonomer: Monomer = DUMMY_MONOMER;
|
|
55
66
|
private _newMonomerForm: MonomerForm;
|
|
@@ -63,7 +74,11 @@ export class MonomerManager implements IMonomerManager {
|
|
|
63
74
|
this.monomerLib = monomerLibManamger.getBioLib();
|
|
64
75
|
this._newMonomerForm = new MonomerForm(monomerLibManamger, () => this.activeMonomerLib, async () => {
|
|
65
76
|
const df = await this.getMonomersDf(this.libInput.value!);
|
|
66
|
-
|
|
77
|
+
if (this.tv?.dataFrame) {
|
|
78
|
+
this.tv.dataFrame = df;
|
|
79
|
+
this.adjustColWidths();
|
|
80
|
+
}
|
|
81
|
+
|
|
67
82
|
}, () => this.tv?.dataFrame);
|
|
68
83
|
}
|
|
69
84
|
|
|
@@ -150,7 +165,7 @@ export class MonomerManager implements IMonomerManager {
|
|
|
150
165
|
const df = await this.getMonomersDf(fileName);
|
|
151
166
|
this.tv = DG.TableView.create(df, true);
|
|
152
167
|
//const f = tv.filters();
|
|
153
|
-
this.
|
|
168
|
+
this.adjustColWidths();
|
|
154
169
|
this.tv.subs.push(
|
|
155
170
|
grok.events.onContextMenu.subscribe(({args}) => {
|
|
156
171
|
if (!args || !args.menu || !args.context || args.context.type !== DG.VIEWER.GRID || !args.context.tableView ||
|
|
@@ -200,7 +215,7 @@ export class MonomerManager implements IMonomerManager {
|
|
|
200
215
|
libName && (this.libInput.value = libName);
|
|
201
216
|
const df = await this.getMonomersDf(libName);
|
|
202
217
|
this.tv.dataFrame = df;
|
|
203
|
-
this.
|
|
218
|
+
this.adjustColWidths();
|
|
204
219
|
return this.tv;
|
|
205
220
|
}
|
|
206
221
|
|
|
@@ -262,6 +277,7 @@ export class MonomerManager implements IMonomerManager {
|
|
|
262
277
|
try {
|
|
263
278
|
const df = await this.getMonomersDf(this.libInput.value!);
|
|
264
279
|
this.tv!.dataFrame = df;
|
|
280
|
+
this.adjustColWidths();
|
|
265
281
|
} catch (e) {
|
|
266
282
|
console.error(e);
|
|
267
283
|
}
|
|
@@ -311,6 +327,8 @@ export class MonomerManager implements IMonomerManager {
|
|
|
311
327
|
df.columns.addNew(rgroupName, DG.COLUMN_TYPE.STRING);
|
|
312
328
|
}
|
|
313
329
|
}
|
|
330
|
+
df.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.semType = 'Monomer';
|
|
331
|
+
df.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.setTag(MONOMER_RENDERER_TAGS.applyToBackground, 'true');
|
|
314
332
|
|
|
315
333
|
|
|
316
334
|
for (let i = 0; i < monomers.length; i++) {
|
|
@@ -470,6 +488,12 @@ class MonomerForm implements INewMonomerForm {
|
|
|
470
488
|
monomerNaturalAnalogInput: DG.InputBase<string | null>;
|
|
471
489
|
rgroupsGrid: ItemsGrid;
|
|
472
490
|
metaGrid: ItemsGrid;
|
|
491
|
+
colors: {
|
|
492
|
+
line: string,
|
|
493
|
+
background: string,
|
|
494
|
+
text: string
|
|
495
|
+
};
|
|
496
|
+
colorsEditor: ColorsEditor;
|
|
473
497
|
saveButton: HTMLButtonElement;
|
|
474
498
|
rgroupsGridRoot: HTMLElement;
|
|
475
499
|
private _molChanged: boolean = false;
|
|
@@ -480,6 +504,12 @@ class MonomerForm implements INewMonomerForm {
|
|
|
480
504
|
private getMonomerLib: () => IMonomerLib | null, private refreshTable: () => Promise<void>,
|
|
481
505
|
private getMonomersDataFrame: () => DG.DataFrame | undefined) {
|
|
482
506
|
const monomerTypes = ['PEPTIDE', 'RNA', 'CHEM', 'BLOB', 'G'];
|
|
507
|
+
this.colors = {
|
|
508
|
+
line: '#000000',
|
|
509
|
+
background: '#000000',
|
|
510
|
+
text: '#000000',
|
|
511
|
+
};
|
|
512
|
+
this.colorsEditor = new ColorsEditor(this.colors);
|
|
483
513
|
this.molSketcher = new DG.chem.Sketcher();
|
|
484
514
|
this.molSketcher.root.classList.add('monomer-manager-sketcher');
|
|
485
515
|
this.polymerTypeInput = ui.input.choice('Polymer Type', {value: 'PEPTIDE', items: monomerTypes,
|
|
@@ -603,13 +633,12 @@ class MonomerForm implements INewMonomerForm {
|
|
|
603
633
|
this.monomerIdInput.value = monomer.id;
|
|
604
634
|
this.monomerNaturalAnalogInput.value = monomer.naturalAnalog ?? null;
|
|
605
635
|
this.rgroupsGrid.items = resolveRGroupInfo(monomer.rgroups);
|
|
606
|
-
this.metaGrid.items = Object.entries(monomer.meta ?? {}).map(([k, v]) => {
|
|
636
|
+
this.metaGrid.items = Object.entries(monomer.meta ?? {}).filter(([k, v]) => k?.toLowerCase() !== 'colors').map(([k, v]) => {
|
|
607
637
|
return {Property: k, Value: v};
|
|
608
638
|
});
|
|
609
639
|
this.rgroupsGrid.render();
|
|
610
640
|
this.metaGrid.render();
|
|
611
641
|
this.rgroupsGridRoot.style.display = 'flex';
|
|
612
|
-
|
|
613
642
|
this.onMonomerInputChanged();
|
|
614
643
|
if (!monomer.naturalAnalog) {
|
|
615
644
|
mostSimilarNaturalAnalog(capSmiles(monomer.smiles, this.rgroupsGrid.items as RGroup[]), monomer.polymerType).then((mostSimilar) => {
|
|
@@ -617,6 +646,19 @@ class MonomerForm implements INewMonomerForm {
|
|
|
617
646
|
this.monomerNaturalAnalogInput.value = mostSimilar;
|
|
618
647
|
});
|
|
619
648
|
}
|
|
649
|
+
const colorsString = monomer.meta?.colors ?? '';
|
|
650
|
+
let colorsObj: Partial<typeof this.colors> = {};
|
|
651
|
+
try {
|
|
652
|
+
colorsObj = colorsString ? JSON.parse(colorsString)?.default : {};
|
|
653
|
+
} catch (e) {
|
|
654
|
+
console.error(e);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
this.colorsEditor.colors = {
|
|
658
|
+
line: colorsObj.line ?? '#000000',
|
|
659
|
+
background: colorsObj.background ?? '#000000',
|
|
660
|
+
text: colorsObj.text ?? '#000000',
|
|
661
|
+
};
|
|
620
662
|
}
|
|
621
663
|
|
|
622
664
|
validateInputs(): string | null | undefined {
|
|
@@ -656,6 +698,7 @@ class MonomerForm implements INewMonomerForm {
|
|
|
656
698
|
'Monomer': mainInputsDiv,
|
|
657
699
|
'R-groups': this.rgroupsGridRoot,
|
|
658
700
|
'Meta': ui.divV([this.metaGrid.root]),
|
|
701
|
+
'Colors': this.colorsEditor.form,
|
|
659
702
|
}, false);
|
|
660
703
|
inputsPanel.header.style.marginBottom = '10px';
|
|
661
704
|
const saveB = ui.buttonsInput([this.saveButton]);
|
|
@@ -802,6 +845,13 @@ class MonomerForm implements INewMonomerForm {
|
|
|
802
845
|
this.metaGrid.items.filter((item) => (!!item['Property']) && (!!item['Value'])).forEach((item) => {
|
|
803
846
|
meta[item['Property']] = item['Value'];
|
|
804
847
|
});
|
|
848
|
+
const addingItem = this.metaGrid.addingItem;
|
|
849
|
+
if (addingItem && addingItem['Property'] && addingItem['Value']) {
|
|
850
|
+
meta[addingItem['Property']] = addingItem['Value'];
|
|
851
|
+
}
|
|
852
|
+
//console.log(this.metaGrid.addingItem);
|
|
853
|
+
if (this.colorsEditor.colors.line !== '#000000' || this.colorsEditor.colors.background !== '#000000' || this.colorsEditor.colors.text !== '#000000')
|
|
854
|
+
meta.colors = {default: this.colorsEditor.colors};
|
|
805
855
|
const monomer: Monomer = {
|
|
806
856
|
symbol: this.monomerSymbolInput.value,
|
|
807
857
|
name: this.monomerNameInput.value,
|
|
@@ -929,3 +979,42 @@ function monomerFromDfRow(dfRow: DG.Row): Monomer {
|
|
|
929
979
|
createDate: dfRow.get(MONOMER_DF_COLUMN_NAMES.CREATE_DATE),
|
|
930
980
|
};
|
|
931
981
|
}
|
|
982
|
+
|
|
983
|
+
class ColorsEditor {
|
|
984
|
+
private _colors: { line: string, background: string, text: string };
|
|
985
|
+
private _colorInputs: { [key in keyof ColorsEditor['_colors']]: DG.InputBase<string> };
|
|
986
|
+
constructor(colors: { line: string, background: string, text: string }) {
|
|
987
|
+
this._colors = colors;
|
|
988
|
+
this._colorInputs = {
|
|
989
|
+
line: ui.input.color('Line', {value: colors.line, onValueChanged: (v) => this._colors.line = v}),
|
|
990
|
+
background: ui.input.color('Background', {value: colors.background, onValueChanged: (v) => this._colors.background = v}),
|
|
991
|
+
text: ui.input.color('Text', {value: colors.text, onValueChanged: (v) => this._colors.text = v}),
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
get colors() {
|
|
996
|
+
return this._colors;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
set colors(cols: { line: string, background: string, text: string }) {
|
|
1000
|
+
//need to convert to hex as the input accepts only hex
|
|
1001
|
+
const colsHex = {
|
|
1002
|
+
line: DG.Color.toHtml(DG.Color.fromHtml(cols.line ?? '#000000')),
|
|
1003
|
+
background: DG.Color.toHtml(DG.Color.fromHtml(cols.background ?? '#000000')),
|
|
1004
|
+
text: DG.Color.toHtml(DG.Color.fromHtml(cols.text ?? '#000000')),
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
this._colors = colsHex;
|
|
1008
|
+
for (const key in this._colorInputs) {
|
|
1009
|
+
this._colorInputs[key as keyof ColorsEditor['_colors']].value = colsHex[key as keyof ColorsEditor['_colors']];
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
get colorsMetaFormat() {
|
|
1014
|
+
return {colors: {default: this._colors}};
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
get form() {
|
|
1018
|
+
return ui.form(Object.values(this._colorInputs));
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
@@ -3,7 +3,7 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
PolymerType, MonomerType,
|
|
7
7
|
IWebEditorMonomer, WebEditorRGroups
|
|
8
8
|
} from '@datagrok-libraries/bio/src/helm/types';
|
|
9
9
|
|
|
@@ -85,8 +85,9 @@ export class GapWebEditorMonomer extends WebEditorMonomerDummy {
|
|
|
85
85
|
public readonly linecolor: string = '#808080';
|
|
86
86
|
public readonly textcolor: string = '#808080';
|
|
87
87
|
|
|
88
|
-
constructor(biotype: string
|
|
89
|
-
|
|
88
|
+
constructor(biotype: string) {
|
|
89
|
+
// monomer symbol is used to build pseudo molfile for Helm, symbol can not be empty for molfile
|
|
90
|
+
super(biotype, '*', 'gap');
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
|
|
@@ -105,8 +106,14 @@ export class MissingWebEditorMonomer extends WebEditorMonomerDummy {
|
|
|
105
106
|
public readonly linecolor: string = '#800000';
|
|
106
107
|
public readonly textcolor: string = '#FFFFFF';
|
|
107
108
|
|
|
108
|
-
constructor(biotype: string, id: string) {
|
|
109
|
+
constructor(biotype: string, id: string, isLibEmpty: boolean) {
|
|
109
110
|
super(biotype, id, 'missing');
|
|
111
|
+
|
|
112
|
+
if (isLibEmpty) {
|
|
113
|
+
this.backgroundcolor = '#C0C0C0';
|
|
114
|
+
this.linecolor = '#404040';
|
|
115
|
+
this.textcolor = '#404040';
|
|
116
|
+
}
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
|
|
@@ -2,12 +2,11 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as ui from 'datagrok-api/ui';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
|
-
import {HelmType,
|
|
5
|
+
import {HelmType, IWebEditorMonomer, MonomerType, PolymerType, WebEditorRGroups} from '@datagrok-libraries/bio/src/helm/types';
|
|
6
6
|
import {IMonomerLibBase, Monomer} from '@datagrok-libraries/bio/src/types/index';
|
|
7
7
|
import {HELM_OPTIONAL_FIELDS as OPT, HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP} from '@datagrok-libraries/bio/src/utils/const';
|
|
8
8
|
|
|
9
|
-
import {BrokenWebEditorMonomer
|
|
10
|
-
import {naturalMonomerColors} from './monomer-colors';
|
|
9
|
+
import {BrokenWebEditorMonomer} from './web-editor-monomer-dummy';
|
|
11
10
|
|
|
12
11
|
export class LibraryWebEditorMonomer implements IWebEditorMonomer {
|
|
13
12
|
public get rs(): number { return Object.keys(this.at).length; }
|
|
@@ -43,7 +42,8 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
|
|
|
43
42
|
at = monomerLib.getRS(smiles);
|
|
44
43
|
} else if (!monomer.lib) {
|
|
45
44
|
// missing
|
|
46
|
-
|
|
45
|
+
throw new Error('Unexpected missing monomer without .lib');
|
|
46
|
+
// return new MissingWebEditorMonomer(biotype, symbol);
|
|
47
47
|
} else {
|
|
48
48
|
// broken
|
|
49
49
|
return new BrokenWebEditorMonomer(biotype, symbol);
|
|
@@ -58,7 +58,7 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
|
|
|
58
58
|
monomer[REQ.MONOMER_TYPE],
|
|
59
59
|
at);
|
|
60
60
|
|
|
61
|
-
const colors = getMonomerColors(biotype, monomer);
|
|
61
|
+
const colors = monomerLib.getMonomerColors(biotype, monomer[REQ.SYMBOL]);
|
|
62
62
|
if (colors) {
|
|
63
63
|
res.textcolor = colors?.textcolor;
|
|
64
64
|
res.linecolor = colors?.linecolor;
|
|
@@ -68,35 +68,3 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
|
|
|
68
68
|
return res;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
function getMonomerColors(biotype: HelmType, monomer: Monomer): IMonomerColors | null {
|
|
73
|
-
const currentMonomerSchema = 'default';
|
|
74
|
-
let monomerSchema: string = currentMonomerSchema;
|
|
75
|
-
|
|
76
|
-
let res: any;
|
|
77
|
-
if (monomer.meta && monomer.meta.colors) {
|
|
78
|
-
const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
|
|
79
|
-
if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
|
|
80
|
-
let res = monomerColors[monomerSchema];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!res) {
|
|
84
|
-
const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
|
|
85
|
-
const nColor = biotypeColors?.[monomer.symbol];
|
|
86
|
-
if (nColor)
|
|
87
|
-
res = {textColor: "#000000", lineColor: "#000000", backgroundColor: nColor};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const na = monomer[OPT.NATURAL_ANALOG];
|
|
91
|
-
if (!res && na) {
|
|
92
|
-
const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
|
|
93
|
-
const naColor = biotypeColors?.[na];
|
|
94
|
-
res = {textColor: "#000000", lineColor: "#000000", backgroundColor: naColor ?? "#FFFFFF",};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return !res ? null : {
|
|
98
|
-
textcolor: res.text ?? res.textColor,
|
|
99
|
-
linecolor: res.line ?? res.lineColor,
|
|
100
|
-
backgroundcolor: res.background ?? res.backgroundColor
|
|
101
|
-
} as IMonomerColors;
|
|
102
|
-
}
|
|
@@ -7,9 +7,8 @@ import wu from 'wu';
|
|
|
7
7
|
import {fromEvent, Observable, Subject, Unsubscribable} from 'rxjs';
|
|
8
8
|
|
|
9
9
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
10
|
-
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
11
10
|
import {
|
|
12
|
-
monomerToShort,
|
|
11
|
+
monomerToShort, pickUpSeqCol, TAGS as bioTAGS, positionSeparator, ALPHABET
|
|
13
12
|
} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
14
13
|
import {
|
|
15
14
|
FilterSources, HorizontalAlignments, IWebLogoViewer, PositionHeight, PositionMarginStates,
|
|
@@ -21,11 +20,14 @@ import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/type
|
|
|
21
20
|
import {testEvent} from '@datagrok-libraries/utils/src/test';
|
|
22
21
|
import {PromiseSyncer} from '@datagrok-libraries/bio/src/utils/syncer';
|
|
23
22
|
import {GAP_SYMBOL} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
23
|
+
import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
|
|
24
|
+
import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
|
|
25
|
+
import {undefinedColor} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
|
|
24
26
|
|
|
25
27
|
import {AggFunc, getAgg} from '../utils/agg';
|
|
26
28
|
import {buildCompositionTable} from '../widgets/composition-analysis-widget';
|
|
27
29
|
|
|
28
|
-
import {_package} from '../package';
|
|
30
|
+
import {_package, getMonomerLibHelper} from '../package';
|
|
29
31
|
|
|
30
32
|
declare global {
|
|
31
33
|
interface HTMLCanvasElement {
|
|
@@ -199,7 +201,8 @@ export class PositionInfo {
|
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
render(g: CanvasRenderingContext2D,
|
|
202
|
-
fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number,
|
|
204
|
+
fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number,
|
|
205
|
+
biotype: HelmType, monomerLib: IMonomerLibBase | null
|
|
203
206
|
) {
|
|
204
207
|
for (const [monomer, pmInfo] of Object.entries(this._freqs)) {
|
|
205
208
|
if (monomer !== GAP_SYMBOL) {
|
|
@@ -207,11 +210,17 @@ export class PositionInfo {
|
|
|
207
210
|
const b = pmInfo.bounds!;
|
|
208
211
|
const left = b.left;
|
|
209
212
|
|
|
213
|
+
let color: string = undefinedColor;
|
|
214
|
+
if (monomerLib) {
|
|
215
|
+
const wem = monomerLib.getWebEditorMonomer(biotype, monomer)!;
|
|
216
|
+
color = wem.backgroundcolor!;
|
|
217
|
+
}
|
|
218
|
+
|
|
210
219
|
g.resetTransform();
|
|
211
220
|
g.strokeStyle = 'lightgray';
|
|
212
221
|
g.lineWidth = 1;
|
|
213
222
|
g.rect(left, b.top, b.width, b.height);
|
|
214
|
-
g.fillStyle =
|
|
223
|
+
g.fillStyle = color;
|
|
215
224
|
g.textAlign = 'left';
|
|
216
225
|
g.font = fontStyle;
|
|
217
226
|
//g.fillRect(b.left, b.top, b.width, b.height);
|
|
@@ -233,13 +242,13 @@ export class PositionInfo {
|
|
|
233
242
|
return !!findRes ? findRes[0] : undefined;
|
|
234
243
|
}
|
|
235
244
|
|
|
236
|
-
buildCompositionTable(
|
|
245
|
+
buildCompositionTable(biotype: HelmType, monomerLib: IMonomerLibBase): HTMLTableElement {
|
|
237
246
|
if ('-' in this._freqs)
|
|
238
247
|
throw new Error(`Unexpected monomer symbol '-'.`);
|
|
239
|
-
return buildCompositionTable(
|
|
248
|
+
return buildCompositionTable(
|
|
240
249
|
Object.assign({}, ...Object.entries(this._freqs)
|
|
241
|
-
.map(([m, pmi]) => ({[m]: pmi.rowCount})))
|
|
242
|
-
|
|
250
|
+
.map(([m, pmi]) => ({[m]: pmi.rowCount}))),
|
|
251
|
+
biotype, monomerLib);
|
|
243
252
|
}
|
|
244
253
|
}
|
|
245
254
|
|
|
@@ -304,9 +313,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
304
313
|
private seqHandler: SeqHandler | null;
|
|
305
314
|
private initialized: boolean = false;
|
|
306
315
|
|
|
307
|
-
|
|
308
|
-
protected palette: SeqPalette | null = null;
|
|
309
|
-
|
|
316
|
+
private monomerLib: IMonomerLibBase | null = null;
|
|
310
317
|
private host?: HTMLDivElement;
|
|
311
318
|
private msgHost?: HTMLElement;
|
|
312
319
|
private canvas: HTMLCanvasElement;
|
|
@@ -446,6 +453,14 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
446
453
|
this.canvas.classList.value = 'bio-wl-canvas';
|
|
447
454
|
this.canvas.style.width = '100%';
|
|
448
455
|
|
|
456
|
+
getMonomerLibHelper().then((libHelper) => {
|
|
457
|
+
this.monomerLib = libHelper.getMonomerLib();
|
|
458
|
+
this.render(WlRenderLevel.Render, 'monomerLib');
|
|
459
|
+
this.subs.push(this.monomerLib.onChanged.subscribe(() => {
|
|
460
|
+
this.render(WlRenderLevel.Render, 'monomerLib changed');
|
|
461
|
+
}));
|
|
462
|
+
});
|
|
463
|
+
|
|
449
464
|
/* this.root.style.background = '#FFEEDD'; */
|
|
450
465
|
this.viewSyncer = new PromiseSyncer(_package.logger);
|
|
451
466
|
}
|
|
@@ -583,7 +598,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
583
598
|
// Set valueColumnNameProp.choices has no effect
|
|
584
599
|
}
|
|
585
600
|
|
|
586
|
-
/** Assigns {@link seqCol}
|
|
601
|
+
/** Assigns {@link seqCol} based on {@link sequenceColumnName} and calls {@link render}(). */
|
|
587
602
|
private updateSeqCol(): void {
|
|
588
603
|
if (this.dataFrame) {
|
|
589
604
|
this.seqCol = this.sequenceColumnName ? this.dataFrame.col(this.sequenceColumnName) : null;
|
|
@@ -595,7 +610,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
595
610
|
try {
|
|
596
611
|
this.seqHandler = SeqHandler.forColumn(this.seqCol);
|
|
597
612
|
|
|
598
|
-
this.palette = pickUpPalette(this.seqCol);
|
|
599
613
|
this.render(WlRenderLevel.Freqs, 'updateSeqCol()');
|
|
600
614
|
this.error = null;
|
|
601
615
|
} catch (err: any) {
|
|
@@ -609,7 +623,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
609
623
|
this.positionLabels = [];
|
|
610
624
|
this.startPosition = -1;
|
|
611
625
|
this.endPosition = -1;
|
|
612
|
-
this.palette = null;
|
|
613
626
|
}
|
|
614
627
|
}
|
|
615
628
|
}
|
|
@@ -1085,15 +1098,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1085
1098
|
this._onLayoutCalculated.next();
|
|
1086
1099
|
};
|
|
1087
1100
|
|
|
1088
|
-
if (this.msgHost)
|
|
1089
|
-
|
|
1090
|
-
this.msgHost!.innerText = `Unknown palette (column semType: '${this.seqCol.semType}').`;
|
|
1091
|
-
this.msgHost!.style.display = '';
|
|
1092
|
-
} else
|
|
1093
|
-
this.msgHost!.style.display = 'none';
|
|
1094
|
-
}
|
|
1101
|
+
if (this.msgHost)
|
|
1102
|
+
this.msgHost!.style.display = 'none';
|
|
1095
1103
|
|
|
1096
|
-
if (!this.seqCol || !this.dataFrame ||
|
|
1104
|
+
if (!this.seqCol || !this.dataFrame || this.host == null || this.slider == null)
|
|
1097
1105
|
return;
|
|
1098
1106
|
|
|
1099
1107
|
const dpr: number = window.devicePixelRatio;
|
|
@@ -1133,8 +1141,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1133
1141
|
// Hacks to scale uppercase characters to target rectangle
|
|
1134
1142
|
const uppercaseLetterAscent = 0.25;
|
|
1135
1143
|
const uppercaseLetterHeight = 12.2;
|
|
1144
|
+
const sh = SeqHandler.forColumn(this.seqCol);
|
|
1145
|
+
const biotype = sh.defaultBiotype;
|
|
1136
1146
|
for (let jPos = firstPos; jPos <= lastPos; jPos++)
|
|
1137
|
-
this.positions[jPos].render(g, fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, this.
|
|
1147
|
+
this.positions[jPos].render(g, fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, biotype, this.monomerLib);
|
|
1138
1148
|
} finally {
|
|
1139
1149
|
g.restore();
|
|
1140
1150
|
}
|
|
@@ -1216,12 +1226,15 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1216
1226
|
const [pi, monomer] = this.getMonomer(cursorP, dpr);
|
|
1217
1227
|
const positionLabelHeight = this.showPositionLabels ? POSITION_LABELS_HEIGHT * dpr : 0;
|
|
1218
1228
|
|
|
1219
|
-
if (pi !== null && monomer === null && 0 <= cursorP.y && cursorP.y <= positionLabelHeight) {
|
|
1229
|
+
if (pi !== null && monomer === null && 0 <= cursorP.y && cursorP.y <= positionLabelHeight && this.monomerLib) {
|
|
1220
1230
|
// Position tooltip
|
|
1221
1231
|
|
|
1222
1232
|
const tooltipRows = [ui.divText(`Position ${pi.label}`)];
|
|
1223
|
-
if (this.valueAggrType === DG.AGG.TOTAL_COUNT)
|
|
1224
|
-
|
|
1233
|
+
if (this.valueAggrType === DG.AGG.TOTAL_COUNT) {
|
|
1234
|
+
const sh = SeqHandler.forColumn(this.seqCol!);
|
|
1235
|
+
const biotype = sh.defaultBiotype;
|
|
1236
|
+
tooltipRows.push(pi.buildCompositionTable(biotype, this.monomerLib));
|
|
1237
|
+
}
|
|
1225
1238
|
const tooltipEl = ui.divV(tooltipRows);
|
|
1226
1239
|
ui.tooltip.show(tooltipEl, args.x + 16, args.y + 16);
|
|
1227
1240
|
} else if (pi !== null && monomer && this.dataFrame && this.seqCol && this.seqHandler) {
|
|
@@ -7,27 +7,19 @@ import wu from 'wu';
|
|
|
7
7
|
import {TAGS as bioTAGS, ALPHABET, getPaletteByType} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
8
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
9
9
|
import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
|
|
10
|
-
import '../../css/composition-analysis.css';
|
|
11
10
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
12
11
|
import {GAP_SYMBOL} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
12
|
+
import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types';
|
|
13
|
+
import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
|
|
14
|
+
import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
13
15
|
|
|
16
|
+
import '../../css/composition-analysis.css';
|
|
14
17
|
|
|
15
|
-
export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
|
|
18
|
+
export function getCompositionAnalysisWidget(val: DG.SemanticValue, monomerLib: IMonomerLibBase): DG.Widget {
|
|
16
19
|
const host = ui.div();
|
|
17
20
|
host.classList.add('macromolecule-cell-comp-analysis-host');
|
|
18
21
|
const alphabet = val.cell.column.tags[bioTAGS.alphabet];
|
|
19
|
-
|
|
20
|
-
switch (alphabet) {
|
|
21
|
-
case ALPHABET.DNA:
|
|
22
|
-
case ALPHABET.RNA:
|
|
23
|
-
palette = getPaletteByType(ALPHABET.DNA);
|
|
24
|
-
break;
|
|
25
|
-
case ALPHABET.PT:
|
|
26
|
-
palette = getPaletteByType(ALPHABET.PT);
|
|
27
|
-
break;
|
|
28
|
-
default:
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
22
|
+
const biotype = alphabet === ALPHABET.DNA || alphabet === ALPHABET.RNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
|
|
31
23
|
|
|
32
24
|
const counts: { [m: string]: number } = {};
|
|
33
25
|
const sh = SeqHandler.forColumn(val.cell.column as DG.Column<string>);
|
|
@@ -38,7 +30,7 @@ export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
|
|
|
38
30
|
const count = counts[cm] || 0;
|
|
39
31
|
counts[cm] = count + 1;
|
|
40
32
|
});
|
|
41
|
-
const table = buildCompositionTable(
|
|
33
|
+
const table = buildCompositionTable(counts, biotype, monomerLib);
|
|
42
34
|
Array.from(table.rows).forEach((row) => {
|
|
43
35
|
const barCol = (row.getElementsByClassName('macromolecule-cell-comp-analysis-bar')[0] as HTMLDivElement)
|
|
44
36
|
.style.backgroundColor;
|
|
@@ -49,7 +41,9 @@ export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
|
|
|
49
41
|
return new DG.Widget(host);
|
|
50
42
|
}
|
|
51
43
|
|
|
52
|
-
export function buildCompositionTable(
|
|
44
|
+
export function buildCompositionTable(
|
|
45
|
+
counts: { [m: string]: number }, biotype: HelmType, monomerLib: IMonomerLibBase
|
|
46
|
+
): HTMLTableElement {
|
|
53
47
|
let sumValue: number = 0;
|
|
54
48
|
let maxValue: number | null = null;
|
|
55
49
|
for (const value of Object.values(counts)) {
|
|
@@ -61,7 +55,8 @@ export function buildCompositionTable(palette: SeqPalette, counts: { [m: string]
|
|
|
61
55
|
.sort((a, b) => b[1] - a[1])
|
|
62
56
|
.map(([cm, value]) => {
|
|
63
57
|
const ratio = value / sumValue;
|
|
64
|
-
const
|
|
58
|
+
const wem = monomerLib.getWebEditorMonomer(biotype, cm)!;
|
|
59
|
+
const color = wem.backgroundcolor!;
|
|
65
60
|
const barDiv = ui.div('', {classes: 'macromolecule-cell-comp-analysis-bar'});
|
|
66
61
|
barDiv.style.width = `${50 * ratio / maxRatio}px`;
|
|
67
62
|
barDiv.style.backgroundColor = color;
|