@datagrok/bio 2.15.8 → 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 +12 -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 +6 -6
- package/src/utils/cell-renderer-custom.ts +10 -2
- package/src/utils/cell-renderer.ts +5 -21
- package/src/utils/monomer-cell-renderer.ts +1 -2
- package/src/utils/monomer-lib/monomer-lib-base.ts +91 -5
- package/src/utils/monomer-lib/monomer-manager/monomer-manager.ts +94 -5
- package/src/utils/monomer-lib/web-editor-monomer-of-library.ts +3 -42
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Aleksandr Tanas",
|
|
6
6
|
"email": "atanas@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.15.
|
|
8
|
+
"version": "2.15.9",
|
|
9
9
|
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@biowasm/aioli": "^3.1.0",
|
|
40
|
-
"@datagrok-libraries/bio": "^5.44.
|
|
40
|
+
"@datagrok-libraries/bio": "^5.44.5",
|
|
41
41
|
"@datagrok-libraries/chem-meta": "^1.2.7",
|
|
42
|
-
"@datagrok-libraries/math": "^1.2.
|
|
43
|
-
"@datagrok-libraries/ml": "^6.7.
|
|
44
|
-
"@datagrok-libraries/tutorials": "^1.4.
|
|
45
|
-
"@datagrok-libraries/utils": "^4.3.
|
|
42
|
+
"@datagrok-libraries/math": "^1.2.1",
|
|
43
|
+
"@datagrok-libraries/ml": "^6.7.3",
|
|
44
|
+
"@datagrok-libraries/tutorials": "^1.4.3",
|
|
45
|
+
"@datagrok-libraries/utils": "^4.3.6",
|
|
46
46
|
"@webgpu/types": "^0.1.40",
|
|
47
47
|
"ajv": "^8.12.0",
|
|
48
48
|
"ajv-errors": "^3.0.0",
|
|
@@ -5,6 +5,7 @@ import * as ui from 'datagrok-api/ui';
|
|
|
5
5
|
import {getGridCellColTemp, CellRendererBackBase} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
6
6
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
7
7
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
|
+
import {GridCell} from 'datagrok-api/dg';
|
|
8
9
|
|
|
9
10
|
export class MacromoleculeCustomCellRenderer extends DG.GridCellRenderer {
|
|
10
11
|
get name(): string { return 'sequence'; }
|
|
@@ -24,9 +25,16 @@ export class MacromoleculeCustomCellRenderer extends DG.GridCellRenderer {
|
|
|
24
25
|
return back;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
override
|
|
28
|
+
override onMouseEnter(gridCell: GridCell, e: MouseEvent) {
|
|
28
29
|
const back = this.getRendererBack(gridCell);
|
|
29
|
-
back.
|
|
30
|
+
back.onMouseEnter(gridCell, e);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override onMouseLeave(gridCell: DG.GridCell, e: MouseEvent) {
|
|
34
|
+
// TODO: We get gridCell from another column here, so we can not get back object from the column rendered.
|
|
35
|
+
ui.tooltip.hide();
|
|
36
|
+
// const back = this.getRendererBack(gridCell);
|
|
37
|
+
// back.onMouseLeave(gridCell, e);
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
override onMouseMove(gridCell: DG.GridCell, e: MouseEvent) {
|
|
@@ -102,10 +102,11 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
102
102
|
): void {
|
|
103
103
|
const logPrefix: string = 'MacromoleculeSequenceCellRenderer.render()';
|
|
104
104
|
|
|
105
|
-
const [gridCol, tableCol,
|
|
105
|
+
const [gridCol, tableCol, temp] =
|
|
106
106
|
getGridCellColTemp<string, MonomerPlacer>(gridCell);
|
|
107
107
|
if (!tableCol) return;
|
|
108
108
|
const tableColTemp: TempType = tableCol.temp;
|
|
109
|
+
const sh = SeqHandler.forColumn(tableCol);
|
|
109
110
|
|
|
110
111
|
let gapLength = 0;
|
|
111
112
|
const msaGapLength = 8;
|
|
@@ -121,31 +122,16 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
121
122
|
maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
const [_gc, _tc, temp] =
|
|
125
|
-
getGridCellColTemp<string, MonomerPlacer>(gridCell);
|
|
126
125
|
let seqColTemp: MonomerPlacer = temp.rendererBack;
|
|
127
126
|
if (!seqColTemp) {
|
|
128
127
|
seqColTemp = temp.rendererBack = new MonomerPlacer(gridCol, tableCol, _package.logger, maxLengthOfMonomer,
|
|
129
128
|
() => {
|
|
130
|
-
const sh = SeqHandler.forColumn(tableCol);
|
|
131
129
|
return {
|
|
132
|
-
seqHandler: sh,
|
|
133
130
|
monomerCharWidth: 7, separatorWidth: !sh.isMsa() ? gapLength : msaGapLength,
|
|
134
131
|
monomerToShort: monomerToShortFunction,
|
|
135
132
|
};
|
|
136
133
|
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
tableCol.temp[MmcrTemps.rendererSettingsChanged] === rendererSettingsChangedState.true ||
|
|
141
|
-
seqColTemp.monomerLengthLimit != maxLengthOfMonomer
|
|
142
|
-
) {
|
|
143
|
-
gapLength = tableColTemp[MmcrTemps.gapLength] as number ?? gapLength;
|
|
144
|
-
// this event means that the mm renderer settings have changed,
|
|
145
|
-
// particularly monomer representation and max width.
|
|
146
|
-
seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
|
|
147
|
-
seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
|
|
148
|
-
tableCol.temp[MmcrTemps.rendererSettingsChanged] = rendererSettingsChangedState.false;
|
|
134
|
+
tableCol.temp[MmcrTemps.rendererSettingsChanged] === rendererSettingsChangedState.true;
|
|
149
135
|
}
|
|
150
136
|
|
|
151
137
|
seqColTemp.render(g, x, y, w, h, gridCell, _cellStyle);
|
|
@@ -269,15 +255,13 @@ export function drawMoleculeDifferenceOnCanvas(
|
|
|
269
255
|
|
|
270
256
|
let color1 = undefinedColor;
|
|
271
257
|
if (monomerLib) {
|
|
272
|
-
|
|
273
|
-
color1 = wem1.backgroundcolor!;
|
|
258
|
+
color1 = monomerLib.getMonomerTextColor(biotype, amino1);
|
|
274
259
|
}
|
|
275
260
|
|
|
276
261
|
if (amino1 != amino2) {
|
|
277
262
|
let color2 = undefinedColor;
|
|
278
263
|
if (monomerLib) {
|
|
279
|
-
|
|
280
|
-
color2 = wem2.backgroundcolor!;
|
|
264
|
+
color2 = monomerLib.getMonomerTextColor(biotype, amino2);
|
|
281
265
|
}
|
|
282
266
|
const subX0 = printLeftOrCentered(g, amino1, updatedX, updatedY - vShift, w, h,
|
|
283
267
|
{color: color1, pivot: 0, left: true});
|
|
@@ -41,8 +41,7 @@ export class MonomerCellRendererBack extends CellRendererWithMonomerLibBackBase
|
|
|
41
41
|
if (this.monomerLib) {
|
|
42
42
|
const alphabet = this.tableCol.getTag(bioTAGS.alphabet);
|
|
43
43
|
const biotype = alphabet === ALPHABET.RNA || alphabet === ALPHABET.DNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
|
|
44
|
-
|
|
45
|
-
color = wem.backgroundcolor!;
|
|
44
|
+
color = this.monomerLib.getMonomerTextColor(biotype, symbol);
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
//cell width of monomer should dictate how many characters can be displayed
|
|
@@ -6,15 +6,18 @@ import wu from 'wu';
|
|
|
6
6
|
import {Observable, Subject} from 'rxjs';
|
|
7
7
|
|
|
8
8
|
import {IMonomerLibBase, Monomer, RGroup} from '@datagrok-libraries/bio/src/types/index';
|
|
9
|
-
import {HelmAtom, HelmType, IWebEditorMonomer, MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
|
|
9
|
+
import {HelmAtom, HelmType, IMonomerColors, IWebEditorMonomer, MonomerType, PolymerType} from '@datagrok-libraries/bio/src/helm/types';
|
|
10
10
|
import {getMonomerHandleArgs} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
11
11
|
import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
12
12
|
import {HelmTypes, PolymerTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
13
|
-
import {HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP} from '@datagrok-libraries/bio/src/utils/const';
|
|
13
|
+
import {HELM_OPTIONAL_FIELDS as OPT, HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP} from '@datagrok-libraries/bio/src/utils/const';
|
|
14
14
|
import {GAP_SYMBOL, GapOriginals, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
|
|
15
|
+
import {Vector} from '@datagrok-libraries/utils/src/type-declarations';
|
|
16
|
+
import {vectorAdd, vectorDotProduct, vectorLength} from '@datagrok-libraries/utils/src/vector-operations';
|
|
15
17
|
|
|
16
18
|
import {AmbiguousWebEditorMonomer, GapWebEditorMonomer, MissingWebEditorMonomer} from './web-editor-monomer-dummy';
|
|
17
19
|
import {LibraryWebEditorMonomer} from './web-editor-monomer-of-library';
|
|
20
|
+
import {naturalMonomerColors} from './monomer-colors';
|
|
18
21
|
|
|
19
22
|
import {_package} from '../../package';
|
|
20
23
|
|
|
@@ -24,6 +27,10 @@ const ambMonomerRe = RegExp(String.raw`\(${monomerRe}(,${monomerRe})+\)`);
|
|
|
24
27
|
|
|
25
28
|
export type MonomerLibDataType = { [polymerType: string]: { [monomerSymbol: string]: Monomer } };
|
|
26
29
|
|
|
30
|
+
const whiteColorV = new Vector([255.0, 255.0, 255.0]);
|
|
31
|
+
const blackColorV = new Vector([0.0, 0.0, 0.0]);
|
|
32
|
+
const maxTextColorVLen = vectorLength(whiteColorV) * 0.7;
|
|
33
|
+
|
|
27
34
|
export class MonomerLibBase implements IMonomerLibBase {
|
|
28
35
|
protected _isEmpty: boolean;
|
|
29
36
|
get isEmpty(): boolean { return this._isEmpty; }
|
|
@@ -143,7 +150,7 @@ export class MonomerLibBase implements IMonomerLibBase {
|
|
|
143
150
|
resWem = m.wem = LibraryWebEditorMonomer.fromMonomer(biotype, m, this);
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
return resWem
|
|
153
|
+
return resWem;
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
getTooltip(biotype: HelmType, monomerSymbol: string): HTMLElement {
|
|
@@ -155,10 +162,18 @@ export class MonomerLibBase implements IMonomerLibBase {
|
|
|
155
162
|
const symbol = monomer[REQ.SYMBOL];
|
|
156
163
|
const _name = monomer[REQ.NAME];
|
|
157
164
|
const wem = this.getWebEditorMonomer(biotype, monomerSymbol)!;
|
|
165
|
+
|
|
158
166
|
const htmlColor = wem.backgroundcolor;
|
|
159
167
|
res.append(ui.divH([
|
|
160
|
-
ui.div([symbol], {
|
|
161
|
-
|
|
168
|
+
ui.div([symbol], {
|
|
169
|
+
style: {
|
|
170
|
+
/* fontWeight: 'bolder', */ textWrap: 'nowrap', marginLeft: '4px', marginRight: '4px',
|
|
171
|
+
color: wem.textcolor, backgroundColor: wem.backgroundcolor, borderColor: wem.linecolor,
|
|
172
|
+
borderWidth: '1px', borderStyle: 'solid', borderRadius: '2px', padding: '3px',
|
|
173
|
+
minWidth: '24px', textAlign: 'center',
|
|
174
|
+
}
|
|
175
|
+
}),
|
|
176
|
+
ui.div([monomer.name], {style: {padding: '4px'}}),
|
|
162
177
|
], {style: {display: 'flex', flexDirection: 'row', justifyContent: 'left'}}));
|
|
163
178
|
|
|
164
179
|
// Structure
|
|
@@ -190,6 +205,77 @@ export class MonomerLibBase implements IMonomerLibBase {
|
|
|
190
205
|
return res;
|
|
191
206
|
}
|
|
192
207
|
|
|
208
|
+
getMonomerTextColor(biotype: HelmType, symbol: string): string {
|
|
209
|
+
const colors: IMonomerColors = this.getMonomerColors(biotype, symbol);
|
|
210
|
+
const htmlToA = (html: string): number[] => {
|
|
211
|
+
const rgbM = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/.exec(html);
|
|
212
|
+
if (rgbM)
|
|
213
|
+
return [parseInt(rgbM[1]), parseInt(rgbM[2]), parseInt(rgbM[3])];
|
|
214
|
+
|
|
215
|
+
const n = DG.Color.fromHtml(colors.textcolor!);
|
|
216
|
+
return [DG.Color.r(n), DG.Color.g(n), DG.Color.b(n)];
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const textColorA = htmlToA(colors.textcolor!);
|
|
220
|
+
const textColorAdjV = new Vector([...textColorA.map((v) => v + 1)]);
|
|
221
|
+
const cosTextToWhite = vectorDotProduct(textColorAdjV, whiteColorV) /
|
|
222
|
+
(vectorLength(textColorAdjV) * vectorLength(whiteColorV));
|
|
223
|
+
|
|
224
|
+
const backColorA = htmlToA(colors.backgroundcolor!);
|
|
225
|
+
const backColorAdjV = new Vector([...backColorA.map((v) => v + 0.01)]);
|
|
226
|
+
const cosBackToWhite = vectorDotProduct(backColorAdjV, whiteColorV) /
|
|
227
|
+
(vectorLength(backColorAdjV) * vectorLength(whiteColorV));
|
|
228
|
+
|
|
229
|
+
let resColorA;
|
|
230
|
+
if (cosBackToWhite < cosTextToWhite)
|
|
231
|
+
resColorA = backColorA;
|
|
232
|
+
else
|
|
233
|
+
resColorA = textColorA;
|
|
234
|
+
|
|
235
|
+
let resColorV = new Vector(resColorA);
|
|
236
|
+
const resColorLen = vectorLength(resColorV);
|
|
237
|
+
if (resColorLen > maxTextColorVLen) resColorV = vectorAdd(blackColorV, resColorV, maxTextColorVLen / resColorLen);
|
|
238
|
+
return `rgb(${resColorV[0]}, ${resColorV[1]}, ${resColorV[2]})`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
getMonomerColors(biotype: HelmType, symbol: string): IMonomerColors {
|
|
242
|
+
const currentMonomerSchema = 'default';
|
|
243
|
+
let monomerSchema: string = currentMonomerSchema;
|
|
244
|
+
|
|
245
|
+
const polymerType = helmTypeToPolymerType(biotype);
|
|
246
|
+
const monomer = this.getMonomer(polymerType, symbol)!;
|
|
247
|
+
let res: any;
|
|
248
|
+
if (monomer) {
|
|
249
|
+
if (monomer.meta && monomer.meta.colors) {
|
|
250
|
+
const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
|
|
251
|
+
if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
|
|
252
|
+
res = monomerColors[monomerSchema];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!res) {
|
|
256
|
+
const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
|
|
257
|
+
const nColor: string = biotypeColors?.[monomer.symbol];
|
|
258
|
+
if (nColor) {
|
|
259
|
+
const nTextColor = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(nColor)));
|
|
260
|
+
res = {textColor: nTextColor, lineColor: '#202020', backgroundColor: nColor};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const naSymbol: string | undefined = monomer[OPT.NATURAL_ANALOG];
|
|
265
|
+
if (!res && naSymbol) {
|
|
266
|
+
return this.getMonomerColors(biotype, naSymbol);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!res)
|
|
271
|
+
res = {textColor: "#202020", lineColor: "#202020", backgroundColor: "#A0A0A0"};
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
textcolor: res.text ?? res.textColor,
|
|
275
|
+
linecolor: res.line ?? res.lineColor,
|
|
276
|
+
backgroundcolor: res.background ?? res.backgroundColor
|
|
277
|
+
} as IMonomerColors;
|
|
278
|
+
}
|
|
193
279
|
|
|
194
280
|
getRS(smiles: string): { [r: string]: string } {
|
|
195
281
|
const newS = smiles.match(/(?<=\[)[^\][]*(?=])/gm);
|
|
@@ -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
|
+
}
|
|
@@ -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; }
|
|
@@ -59,7 +58,7 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
|
|
|
59
58
|
monomer[REQ.MONOMER_TYPE],
|
|
60
59
|
at);
|
|
61
60
|
|
|
62
|
-
const colors = getMonomerColors(biotype, monomer
|
|
61
|
+
const colors = monomerLib.getMonomerColors(biotype, monomer[REQ.SYMBOL]);
|
|
63
62
|
if (colors) {
|
|
64
63
|
res.textcolor = colors?.textcolor;
|
|
65
64
|
res.linecolor = colors?.linecolor;
|
|
@@ -69,41 +68,3 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
|
|
|
69
68
|
return res;
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
|
-
|
|
73
|
-
function getMonomerColors(biotype: HelmType, monomer: Monomer, monomerLib?: IMonomerLibBase): IMonomerColors | null {
|
|
74
|
-
const currentMonomerSchema = 'default';
|
|
75
|
-
let monomerSchema: string = currentMonomerSchema;
|
|
76
|
-
|
|
77
|
-
let res: any;
|
|
78
|
-
if (monomer.meta && monomer.meta.colors) {
|
|
79
|
-
const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
|
|
80
|
-
if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
|
|
81
|
-
res = monomerColors[monomerSchema];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!res) {
|
|
85
|
-
const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
|
|
86
|
-
const nColor: string = biotypeColors?.[monomer.symbol];
|
|
87
|
-
if (nColor) {
|
|
88
|
-
const nTextColor = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(nColor)));
|
|
89
|
-
res = {textColor: nTextColor, lineColor: '#202020', backgroundColor: nColor};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const naSymbol: string | undefined = monomer[OPT.NATURAL_ANALOG];
|
|
94
|
-
if (!res && naSymbol) {
|
|
95
|
-
const polymerType = monomer[REQ.POLYMER_TYPE];
|
|
96
|
-
const naMonomer = monomerLib?.getMonomer(polymerType, naSymbol);
|
|
97
|
-
if (naMonomer)
|
|
98
|
-
return getMonomerColors(biotype, naMonomer);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!res)
|
|
102
|
-
res = {textColor: "#202020", lineColor: "#202020", backgroundColor: "#A0A0A0"};
|
|
103
|
-
|
|
104
|
-
return !res ? null : {
|
|
105
|
-
textcolor: res.text ?? res.textColor,
|
|
106
|
-
linecolor: res.line ?? res.lineColor,
|
|
107
|
-
backgroundcolor: res.background ?? res.backgroundColor
|
|
108
|
-
} as IMonomerColors;
|
|
109
|
-
}
|