@datagrok/helm 2.1.31 → 2.1.32
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 +11 -0
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +3 -3
- package/src/cell-renderer.ts +7 -12
- package/src/helm-monomer-placer.ts +55 -24
- package/src/package-test.ts +1 -0
- package/src/tests/get-all-parts-tests.ts +34 -0
- package/src/tests/properties-widget-tests.ts +19 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/helm",
|
|
3
3
|
"friendlyName": "Helm",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.32",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Oleksandra Serhiienko",
|
|
7
7
|
"email": "oserhiienko@datagrok.ai"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"helm/JSDraw/Pistoia.HELM-uncompressed.js"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@datagrok-libraries/bio": "^5.40.
|
|
18
|
+
"@datagrok-libraries/bio": "^5.40.7",
|
|
19
19
|
"@datagrok-libraries/chem-meta": "^1.2.3",
|
|
20
20
|
"@datagrok-libraries/utils": "^4.1.45",
|
|
21
21
|
"cash-dom": "^8.1.1",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@typescript-eslint/parser": "^5.11.0",
|
|
38
38
|
"eslint": "^8.18.0",
|
|
39
39
|
"eslint-config-google": "^0.14.0",
|
|
40
|
-
"@datagrok/bio": "^2.12.
|
|
40
|
+
"@datagrok/bio": "^2.12.11",
|
|
41
41
|
"@datagrok/chem": "^1.9.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
package/src/cell-renderer.ts
CHANGED
|
@@ -29,7 +29,7 @@ function getSeqMonomerFromHelm(
|
|
|
29
29
|
resSeqMonomer = {symbol: symbol, polymerType: polymerType};
|
|
30
30
|
}
|
|
31
31
|
if (!resSeqMonomer)
|
|
32
|
-
|
|
32
|
+
throw new Error(`Monomer not found for symbol = '${symbol}' and helmPrefix = '${helmPrefix}'.`);
|
|
33
33
|
return resSeqMonomer;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -162,7 +162,7 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
162
162
|
const argsX = e.offsetX - gcb.x;
|
|
163
163
|
const argsY = e.offsetY - gcb.y;
|
|
164
164
|
|
|
165
|
-
const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
|
|
165
|
+
const helmPlacer = HelmMonomerPlacer.getOrCreate(gridCell.gridColumn, tableCol);
|
|
166
166
|
const editor: IEditor | null = helmPlacer.getEditor(gridCell.tableRowIndex!);
|
|
167
167
|
let seqMonomer: ISeqMonomer | null;
|
|
168
168
|
let missedMonomers: Set<string> = new Set<string>(); // of .size = 0
|
|
@@ -189,15 +189,10 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
189
189
|
|
|
190
190
|
if (seqMonomer) {
|
|
191
191
|
if (!missedMonomers.has(seqMonomer.symbol)) {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
ui.tooltip.show(ui.divV([
|
|
197
|
-
ui.divText(seqMonomer.symbol),
|
|
198
|
-
monomerSvg,
|
|
199
|
-
]), e.x + 16, e.y + 16);
|
|
200
|
-
}
|
|
192
|
+
const tooltipElements: HTMLElement[] = [ui.div(seqMonomer.symbol)];
|
|
193
|
+
const monomerDiv = helmPlacer.monomerLib.getTooltip(seqMonomer.polymerType, seqMonomer.symbol);
|
|
194
|
+
tooltipElements.push(monomerDiv);
|
|
195
|
+
ui.tooltip.show(ui.divV(tooltipElements), e.x + 16, e.y + 16);
|
|
201
196
|
} else {
|
|
202
197
|
ui.tooltip.show(ui.divV([
|
|
203
198
|
ui.divText(`Monomer '${seqMonomer.symbol}' not found.`),
|
|
@@ -244,7 +239,7 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
|
|
|
244
239
|
const monomerList = parseHelm(seq);
|
|
245
240
|
const monomers: Set<string> = new Set<string>(monomerList);
|
|
246
241
|
const missedMonomers: Set<string> = findMonomers(monomerList);
|
|
247
|
-
const helmPlacer = HelmMonomerPlacer.getOrCreate(tableCol);
|
|
242
|
+
const helmPlacer = HelmMonomerPlacer.getOrCreate(gridCell.gridColumn, tableCol);
|
|
248
243
|
|
|
249
244
|
if (missedMonomers.size == 0) {
|
|
250
245
|
// Recreate host to avoid hanging in window.dojox.gfx.svg.Text.prototype.getTextWidth
|
|
@@ -3,11 +3,13 @@ import * as ui from 'datagrok-api/ui';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import wu from 'wu';
|
|
6
|
+
import {Unsubscribable} from 'rxjs';
|
|
6
7
|
|
|
7
8
|
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types/index';
|
|
8
9
|
|
|
9
10
|
import {getParts, parseHelm} from './utils';
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
import {_package, getMonomerLib} from './package';
|
|
11
13
|
|
|
12
14
|
export const enum Temps {
|
|
13
15
|
helmMonomerPlacer = 'bio-helmMonomerPlacer',
|
|
@@ -36,31 +38,60 @@ export interface IEditorPoint {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export interface ISeqMonomer {
|
|
41
|
+
polymerType: string
|
|
39
42
|
symbol: string,
|
|
40
|
-
polymerType?: string
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export class HelmMonomerPlacer {
|
|
44
|
-
private readonly
|
|
45
|
-
private readonly _lengthsList: (number[] | null)[];
|
|
46
|
-
private readonly _editorList: (IEditor | null)[];
|
|
47
|
-
|
|
46
|
+
private readonly grid: DG.Grid;
|
|
48
47
|
public readonly monomerLib: IMonomerLib;
|
|
49
48
|
|
|
49
|
+
private _allPartsList: (string[] | null)[];
|
|
50
|
+
private _lengthsList: (number[] | null)[];
|
|
51
|
+
private _editorList: (IEditor | null)[];
|
|
52
|
+
|
|
50
53
|
public monomerCharWidth: number = 7;
|
|
51
54
|
public leftPadding: number = 5;
|
|
52
55
|
public monomerTextSizeMap: { [p: string]: TextMetrics } = {};
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
private subs: Unsubscribable[] = [];
|
|
58
|
+
|
|
59
|
+
protected constructor(
|
|
60
|
+
public readonly gridCol: DG.GridColumn,
|
|
61
|
+
public readonly col: DG.Column<string>
|
|
62
|
+
) {
|
|
63
|
+
this.grid = this.gridCol.grid;
|
|
64
|
+
this.reset();
|
|
65
|
+
if (this.grid) {
|
|
66
|
+
this.subs.push(this.col.dataFrame.onDataChanged.subscribe(() => {
|
|
67
|
+
try {
|
|
68
|
+
this.reset();
|
|
69
|
+
} catch (err: any) {
|
|
70
|
+
console.error(err);
|
|
71
|
+
}
|
|
72
|
+
}));
|
|
73
|
+
this.subs.push(grok.events.onViewRemoved.subscribe((view: DG.View) => {
|
|
74
|
+
try {
|
|
75
|
+
if (this.gridCol.grid.view?.id === view.id) this.destroy();
|
|
76
|
+
} catch (err: any) {
|
|
77
|
+
console.error(err);
|
|
78
|
+
}
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
56
81
|
this.monomerLib = getMonomerLib();
|
|
57
|
-
this.monomerLib.onChanged.subscribe(this.monomerLibOnChanged.bind(this));
|
|
82
|
+
this.subs.push(this.monomerLib.onChanged.subscribe(this.monomerLibOnChanged.bind(this)));
|
|
83
|
+
}
|
|
58
84
|
|
|
85
|
+
private reset(): void {
|
|
59
86
|
this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
|
|
60
87
|
this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
|
|
61
88
|
this._editorList = new Array<IEditor | null>(this.col.length).fill(null);
|
|
62
89
|
}
|
|
63
90
|
|
|
91
|
+
private destroy(): void {
|
|
92
|
+
for (const sub of this.subs) sub.unsubscribe();
|
|
93
|
+
}
|
|
94
|
+
|
|
64
95
|
/** Skips cell for the fallback rendering */
|
|
65
96
|
public skipCell(rowIdx: number): void {
|
|
66
97
|
this._allPartsList[rowIdx] = [];
|
|
@@ -80,8 +111,10 @@ export class HelmMonomerPlacer {
|
|
|
80
111
|
|
|
81
112
|
private getCellMonomerLengthsForSeq(rowIdx: number): [string[], number[]] {
|
|
82
113
|
let allParts: string[] | null = this._allPartsList![rowIdx];
|
|
83
|
-
if (allParts === null)
|
|
84
|
-
|
|
114
|
+
if (allParts === null) {
|
|
115
|
+
const seq = this.col.get(rowIdx);
|
|
116
|
+
allParts = this._allPartsList![rowIdx] = getAllParts(seq);
|
|
117
|
+
}
|
|
85
118
|
|
|
86
119
|
let lengths: number[] | null = this._lengthsList![rowIdx];
|
|
87
120
|
if (lengths === null) {
|
|
@@ -95,12 +128,6 @@ export class HelmMonomerPlacer {
|
|
|
95
128
|
return [allParts, lengths];
|
|
96
129
|
}
|
|
97
130
|
|
|
98
|
-
getAllParts(rowIdx: number): string[] {
|
|
99
|
-
const seq: string | null = this.col.get(rowIdx);
|
|
100
|
-
const monomerList: string[] = seq ? parseHelm(seq) : [];
|
|
101
|
-
return seq ? getParts(monomerList, seq) : [];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
131
|
getMonomer(monomer: ISeqMonomer): Monomer | null {
|
|
105
132
|
let res: Monomer | null = null;
|
|
106
133
|
if (monomer.polymerType)
|
|
@@ -117,15 +144,14 @@ export class HelmMonomerPlacer {
|
|
|
117
144
|
// -- Handle events --
|
|
118
145
|
|
|
119
146
|
private monomerLibOnChanged(_value: any): void {
|
|
120
|
-
this.
|
|
121
|
-
this.
|
|
122
|
-
this._editorList.fill(null);
|
|
123
|
-
// TODO: Invalidate all grids of this.col.dataFrame
|
|
147
|
+
this.reset();
|
|
148
|
+
if (this.grid) this.grid.invalidate();
|
|
124
149
|
}
|
|
125
150
|
|
|
126
|
-
public static getOrCreate(col: DG.Column<string>): HelmMonomerPlacer {
|
|
127
|
-
|
|
128
|
-
|
|
151
|
+
public static getOrCreate(gridCol: DG.GridColumn, col: DG.Column<string>): HelmMonomerPlacer {
|
|
152
|
+
let res: HelmMonomerPlacer = gridCol.temp[Temps.helmMonomerPlacer];
|
|
153
|
+
if (!res) res = gridCol.temp[Temps.helmMonomerPlacer] = new HelmMonomerPlacer(gridCol, col);
|
|
154
|
+
return res;
|
|
129
155
|
}
|
|
130
156
|
|
|
131
157
|
public setEditor(tableRowIndex: number, editor: IEditor) {
|
|
@@ -136,3 +162,8 @@ export class HelmMonomerPlacer {
|
|
|
136
162
|
return this._editorList![tableRowIndex];
|
|
137
163
|
}
|
|
138
164
|
}
|
|
165
|
+
|
|
166
|
+
export function getAllParts(seq: string | null): string[] {
|
|
167
|
+
const monomerList: string[] = seq ? parseHelm(seq) : [];
|
|
168
|
+
return seq ? getParts(monomerList, seq) : [];
|
|
169
|
+
}
|
package/src/package-test.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
5
|
+
import {after, before, category, delay, expect, test, expectArray} from '@datagrok-libraries/utils/src/test';
|
|
6
|
+
|
|
7
|
+
import {getAllParts} from '../helm-monomer-placer';
|
|
8
|
+
|
|
9
|
+
category('getAllParts', () => {
|
|
10
|
+
/** All parts of Helm string are required for fallback rendering. */
|
|
11
|
+
|
|
12
|
+
const tests: { [testName: string]: { tgt: string[]; seq: string } } = {
|
|
13
|
+
'peptideSimpleSingleChar': {
|
|
14
|
+
seq: 'PEPTIDE1{L.V.A}$$$$',
|
|
15
|
+
tgt: ['PEPTIDE1{', 'L', '.', 'V', '.', 'A', '}$$$$'],
|
|
16
|
+
},
|
|
17
|
+
'peptideSimpleMultiChar': {
|
|
18
|
+
seq: 'PEPTIDE1{[Ac].V.A}$$$$',
|
|
19
|
+
/* doubt joining square brackets to separator or notation is correct */
|
|
20
|
+
tgt: ['PEPTIDE1{[', 'Ac', '].', 'V', '.', 'A', '}$$$$'],
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
for (const [testName, testData] of Object.entries(tests)) {
|
|
25
|
+
test(`${testName}`, async () => {
|
|
26
|
+
await _testAllParts(testData.seq, testData.tgt);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function _testAllParts(seq: string | null, tgt: string[]): Promise<void> {
|
|
32
|
+
const res: string[] = getAllParts(seq);
|
|
33
|
+
expectArray(res, tgt);
|
|
34
|
+
}
|
|
@@ -2,9 +2,11 @@ import * as ui from 'datagrok-api/ui';
|
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import * as grok from 'datagrok-api/grok';
|
|
4
4
|
|
|
5
|
-
import {category, expect, expectObject, test} from '@datagrok-libraries/utils/src/test';
|
|
5
|
+
import {after, before, category, expect, expectObject, test} from '@datagrok-libraries/utils/src/test';
|
|
6
6
|
import {ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
7
7
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
8
|
+
import {getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
9
|
+
import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
8
10
|
|
|
9
11
|
import {getPropertiesDict} from '../widgets/properties-widget';
|
|
10
12
|
|
|
@@ -60,6 +62,22 @@ const TestsData: {
|
|
|
60
62
|
};
|
|
61
63
|
|
|
62
64
|
category('properties-widget', () => {
|
|
65
|
+
let monomerLibHelper: IMonomerLibHelper;
|
|
66
|
+
/** Backup actual user's monomer libraries settings */
|
|
67
|
+
let userLibSettings: any = null;
|
|
68
|
+
|
|
69
|
+
before(async () => {
|
|
70
|
+
monomerLibHelper = await getMonomerLibHelper();
|
|
71
|
+
userLibSettings = getUserLibSettings();
|
|
72
|
+
await setUserLibSettingsForTests();
|
|
73
|
+
await monomerLibHelper.loadLibraries(true); // load default libraries
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
after(async () => {
|
|
77
|
+
await setUserLibSettings(userLibSettings);
|
|
78
|
+
await monomerLibHelper.loadLibraries(true); // load user settings libraries
|
|
79
|
+
});
|
|
80
|
+
|
|
63
81
|
for (const [testName, testData] of Object.entries(TestsData)) {
|
|
64
82
|
test(testName, async () => {
|
|
65
83
|
testPropertiesDict(testData.seq, testData.units, testData.separator, testData.alphabet, testData.res);
|