@datagrok/helm 2.1.33 → 2.2.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/helm",
3
3
  "friendlyName": "Helm",
4
- "version": "2.1.33",
4
+ "version": "2.2.0",
5
5
  "author": {
6
6
  "name": "Oleksandra Serhiienko",
7
7
  "email": "oserhiienko@datagrok.ai"
@@ -12,12 +12,12 @@
12
12
  "https://ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js",
13
13
  "helm/JSDraw/Scilligence.JSDraw2.Lite.js",
14
14
  "helm/JSDraw/Scilligence.JSDraw2.Resources.js",
15
- "helm/JSDraw/Pistoia.HELM-uncompressed.js"
15
+ "helm/JSDraw/Pistoia.HELM.js"
16
16
  ],
17
17
  "dependencies": {
18
- "@datagrok-libraries/bio": "^5.40.8",
18
+ "@datagrok-libraries/bio": "^5.41.0",
19
19
  "@datagrok-libraries/chem-meta": "^1.2.5",
20
- "@datagrok-libraries/utils": "^4.2.1",
20
+ "@datagrok-libraries/utils": "^4.2.2",
21
21
  "cash-dom": "^8.1.1",
22
22
  "datagrok-api": "^1.17.0",
23
23
  "dayjs": "^1.10.6",
@@ -37,8 +37,8 @@
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.11",
41
- "@datagrok/chem": "^1.9.0"
40
+ "@datagrok/bio": "^2.12.17",
41
+ "@datagrok/chem": "^1.9.2"
42
42
  },
43
43
  "scripts": {
44
44
  "link-all": "npm link @datagrok-libraries/chem-meta datagrok-api @datagrok-libraries/utils @datagrok-libraries/bio",
@@ -2,14 +2,16 @@ 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 * as JSDraw2 from 'JSDraw2';
5
6
  import wu from 'wu';
6
7
 
7
8
  import {printLeftOrCentered} from '@datagrok-libraries/bio/src/utils/cell-renderer';
8
9
  import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
9
10
 
10
11
  import {findMonomers, parseHelm, removeGapsFromHelm} from './utils';
11
- import {IEditor, HelmMonomerPlacer, IEditorMolAtom, ISeqMonomer, Temps} from './helm-monomer-placer';
12
- import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
12
+ import {HelmMonomerPlacer, ISeqMonomer} from './helm-monomer-placer';
13
+ import {getHoveredMonomerFallback, getHoveredMonomerFromEditorMol} from './utils/get-hovered';
14
+ import {getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
13
15
 
14
16
  // import {_package} from './package'; // NullError: method not found: '_package' on null
15
17
 
@@ -20,147 +22,6 @@ const enum tempTAGS {
20
22
  helmPlacer = 'bio-helmPlacer',
21
23
  }
22
24
 
23
- function getSeqMonomerFromHelm(
24
- helmPrefix: string, symbol: string, monomerLib: IMonomerLib
25
- ): ISeqMonomer {
26
- let resSeqMonomer: ISeqMonomer | undefined = undefined;
27
- for (const polymerType of monomerLib.getPolymerTypes()) {
28
- if (helmPrefix.startsWith(polymerType))
29
- resSeqMonomer = {symbol: symbol, polymerType: polymerType};
30
- }
31
- if (!resSeqMonomer)
32
- throw new Error(`Monomer not found for symbol = '${symbol}' and helmPrefix = '${helmPrefix}'.`);
33
- return resSeqMonomer;
34
- }
35
-
36
- function getSeqMonomerFromHelmAtom(atom: IEditorMolAtom): ISeqMonomer {
37
- let polymerType: string | undefined = undefined;
38
- // @ts-ignore
39
- switch (atom.bio.type) {
40
- case 'HELM_BASE':
41
- case 'HELM_SUGAR': // r - ribose, d - deoxyribose
42
- case 'HELM_LINKER': // p - phosphate
43
- polymerType = 'RNA';
44
- break;
45
- case 'HELM_AA':
46
- polymerType = 'PEPTIDE';
47
- break;
48
- default:
49
- polymerType = 'PEPTIDE';
50
- }
51
- return {symbol: atom.elem, polymerType: polymerType};
52
- }
53
-
54
- function getHoveredMonomerFromEditor(
55
- argsX: number, argsY: number, gridCell: DG.GridCell, editor: IEditor
56
- ): ISeqMonomer | null {
57
- let hoveredSeqMonomer: ISeqMonomer | null = null;
58
-
59
- /** @return {[number, number]} [atom, distance] */
60
- function getNearest(excluded: (number | undefined)[]): [number | undefined, number | undefined] {
61
- let atom: number | undefined = undefined;
62
- let distance: number | undefined = undefined;
63
- for (let atomI = 0; atomI < editor.m.atoms.length; ++atomI) {
64
- if (!excluded.includes(atomI)) {
65
- const aX: number = editor.m.atoms[atomI].p.x;
66
- const aY: number = editor.m.atoms[atomI].p.y;
67
- const distanceToAtomI: number = Math.sqrt((argsX - aX) ** 2 + (argsY - aY) ** 2);
68
- if (distance === undefined || distance > distanceToAtomI) {
69
- atom = atomI;
70
- distance = distanceToAtomI;
71
- }
72
- }
73
- }
74
- return [atom, distance];
75
- }
76
-
77
- const [firstAtomI, firstDistance] = getNearest([]);
78
- const [secondAtomI, secondDistance] = getNearest([firstAtomI]);
79
-
80
- // for (let atomI = 0; atomI < editor.m.atoms.length; ++atomI) {
81
- // const aX: number = editor.m.atoms[atomI].p.x;
82
- // const aY: number = editor.m.atoms[atomI].p.y;
83
- // const distanceToAtomI: number = Math.sqrt((argsX - aX) ** 2 + (argsY - aY) ** 2);
84
- // if (firstDistance === undefined || firstDistance > distanceToAtomI) {
85
- // secondAtomI = firstAtomI;
86
- // firstAtomI = atomI;
87
- //
88
- // secondDistance = firstDistance;
89
- // firstDistance = distanceToAtomI;
90
- // }
91
- // }
92
-
93
- if (firstAtomI !== undefined && firstDistance !== undefined) {
94
- const firstAtom = editor.m.atoms[firstAtomI];
95
- const firstSeqMonomer = getSeqMonomerFromHelmAtom(firstAtom);
96
- if (secondAtomI !== undefined && secondDistance !== undefined) {
97
- if (firstDistance < secondDistance * 0.45)
98
- hoveredSeqMonomer = firstSeqMonomer;
99
- } else {
100
- if (firstDistance < 0.35 * gridCell.bounds.height)
101
- hoveredSeqMonomer = firstSeqMonomer;
102
- }
103
- }
104
- return hoveredSeqMonomer;
105
- }
106
-
107
- function getHoveredMonomerFallback(
108
- argsX: number, _argsY: number, gridCell: DG.GridCell, helmPlacer: HelmMonomerPlacer
109
- ): ISeqMonomer | null {
110
- let hoveredSeqMonomer: ISeqMonomer | null = null;
111
- const [allParts, lengths, sumLengths] = helmPlacer.getCellAllPartsLengths(gridCell.tableRowIndex!);
112
- const maxIndex = Object.values(lengths).length - 1;
113
- let left = 0;
114
- let right = maxIndex;
115
- let found = false;
116
- let iterCount: number = 0;
117
-
118
- let mid = 0;
119
- if (argsX > sumLengths[0]) {
120
- while (!found && iterCount < sumLengths.length) {
121
- mid = Math.floor((right + left) / 2);
122
- if (argsX >= sumLengths[mid] && argsX <= sumLengths[mid + 1]) {
123
- left = mid;
124
- found = true;
125
- } else if (argsX < sumLengths[mid])
126
- right = mid - 1;
127
- else if (argsX > sumLengths[mid + 1])
128
- left = mid + 1;
129
-
130
- if (left == right)
131
- found = true;
132
-
133
- iterCount++;
134
- }
135
- }
136
- left = (argsX >= sumLengths[left]) ? left : left - 1; // correct left to between sumLengths
137
- if (left >= 0)
138
- hoveredSeqMonomer = getSeqMonomerFromHelm(allParts[0], allParts[left], helmPlacer.monomerLib);
139
- return hoveredSeqMonomer;
140
- }
141
-
142
- type RendererGridCellTemp = {
143
- [Temps.helmMonomerPlacer]: HelmMonomerPlacer;
144
- }
145
-
146
- function getRendererGridCellTemp(gridCell: DG.GridCell
147
- ): [DG.GridColumn | null, DG.Column | null, RendererGridCellTemp] {
148
- /** Primarily store/get MonomerPlacer at GridColumn, fallback at (Table) Column for scatter plot tooltip */
149
- let temp: RendererGridCellTemp | null = null;
150
-
151
- let gridCol: DG.GridColumn | null = null;
152
- try { gridCol = gridCell.gridColumn; } catch { gridCol = null; }
153
- temp = gridCol ? gridCol.temp as RendererGridCellTemp : null;
154
-
155
- let tableCol: DG.Column | null = null;
156
- if (!temp) {
157
- try { tableCol = gridCell.cell.column; } catch { tableCol = null; }
158
- temp = tableCol ? tableCol.temp as RendererGridCellTemp : null;
159
- }
160
- if (temp === null) throw new Error(`Monomer placer store (GridColumn or Column) not found.`);
161
- return [gridCol, tableCol, temp];
162
- }
163
-
164
25
  /** Helm cell renderer in case of no missed monomer draws with JSDraw2.Editor (webeditor),
165
26
  * in case of missed monomers presented, draws linear sequences aligned in width per monomer.
166
27
  */
@@ -175,8 +36,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
175
36
 
176
37
  onMouseMove(gridCell: DG.GridCell, e: MouseEvent): void {
177
38
  try {
178
- const [_gridCol, tableCol, temp] = getRendererGridCellTemp(gridCell);
179
- const helmPlacer = temp[Temps.helmMonomerPlacer];
39
+ const [_gridCol, tableCol, temp] =
40
+ getGridCellRendererBack<string, HelmMonomerPlacer>(gridCell);
41
+ const helmPlacer = temp['rendererBack'];
180
42
  /* Can not do anything without tableColumn */
181
43
  if (!tableCol) return;
182
44
 
@@ -184,11 +46,11 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
184
46
  const argsX = e.offsetX - gcb.x;
185
47
  const argsY = e.offsetY - gcb.y;
186
48
 
187
- const editor: IEditor | null = helmPlacer.getEditor(gridCell.tableRowIndex!);
49
+ const editorMol: JSDraw2.IEditorMol | null = helmPlacer.getEditorMol(gridCell.tableRowIndex!);
188
50
  let seqMonomer: ISeqMonomer | null;
189
51
  let missedMonomers: Set<string> = new Set<string>(); // of .size = 0
190
- if (editor)
191
- seqMonomer = getHoveredMonomerFromEditor(argsX, argsY, gridCell, editor);
52
+ if (editorMol)
53
+ seqMonomer = getHoveredMonomerFromEditorMol(argsX, argsY, gridCell, editorMol);
192
54
  else {
193
55
  const seq: string = !gridCell.cell.value ? '' : removeGapsFromHelm(gridCell.cell.value as string);
194
56
  const monomerList = parseHelm(seq);
@@ -246,11 +108,12 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
246
108
  ) {
247
109
  g.save();
248
110
  try {
249
- const [gridCol, tableCol, temp] = getRendererGridCellTemp(gridCell);
111
+ const [gridCol, tableCol, temp] =
112
+ getGridCellRendererBack<string, HelmMonomerPlacer>(gridCell);
250
113
  /* Can not do anything without tableColumn containing temp */
251
114
  if (!tableCol) return;
252
- let helmPlacer = temp[Temps.helmMonomerPlacer];
253
- if (!helmPlacer) helmPlacer = temp[Temps.helmMonomerPlacer] = new HelmMonomerPlacer(gridCol, tableCol);
115
+ let helmPlacer = temp['rendererBack'];
116
+ if (!helmPlacer) helmPlacer = temp['rendererBack'] = new HelmMonomerPlacer(gridCol, tableCol);
254
117
 
255
118
  const grid = gridCell.gridRow !== -1 ? gridCell.grid : undefined;
256
119
  const missedColor = 'red';
@@ -275,8 +138,9 @@ export class HelmCellRenderer extends DG.GridCellRenderer {
275
138
  }
276
139
 
277
140
  // Recreate editor to avoid hanging in window.dojox.gfx.svg.Text.prototype.getTextWidth
278
- const editor = new JSDraw2.Editor(host, {width: w, height: h, skin: 'w8', viewonly: true}) as IEditor;
279
- helmPlacer.setEditor(gridCell.tableRowIndex!, editor);
141
+ const editor = new JSDraw2.Editor(host,
142
+ {width: w, height: h, skin: 'w8', viewonly: true});
143
+ helmPlacer.setEditorMol(gridCell.tableRowIndex!, editor.m);
280
144
 
281
145
  helmPlacer.skipCell(gridCell.tableRowIndex!);
282
146
  return;
@@ -2,8 +2,12 @@ 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 * as scil from 'scil';
6
+ import * as org from 'org';
7
+
5
8
  import {IHelmHelper} from '@datagrok-libraries/bio/src/helm/helm-helper';
6
- import {IHelmWebEditor, IWebEditorApp} from '@datagrok-libraries/bio/src/helm/types';
9
+ import {IHelmWebEditor} from '@datagrok-libraries/bio/src/helm/types';
10
+
7
11
  import {HelmWebEditor} from './helm-web-editor';
8
12
 
9
13
  type HelmHelperWindowType = Window & {
@@ -16,11 +20,9 @@ export class HelmHelper implements IHelmHelper {
16
20
  return new HelmWebEditor();
17
21
  }
18
22
 
19
- createWebEditorApp(host: HTMLDivElement, helm: string): IWebEditorApp {
20
- // @ts-ignore
23
+ createWebEditorApp(host: HTMLDivElement, helm: string): org.helm.IWebEditorApp {
21
24
  org.helm.webeditor.MolViewer.molscale = 0.8;
22
- // @ts-ignore
23
- const webEditorApp = new scil.helm.App(host, {
25
+ const webEditorApp: org.helm.IWebEditorApp = new org.helm.webeditor.App(host, {
24
26
  showabout: false,
25
27
  mexfontsize: '90%',
26
28
  mexrnapinontab: true,
@@ -29,16 +31,13 @@ export class HelmHelper implements IHelmHelper {
29
31
  sequenceviewonly: false,
30
32
  mexfavoritefirst: true,
31
33
  mexfilter: true
32
- }) as IWebEditorApp;
34
+ });
33
35
  const sizes = webEditorApp.calculateSizes();
34
36
  webEditorApp.canvas.resize(sizes.rightwidth - 100, sizes.topheight - 210);
35
- let s = {width: sizes.rightwidth - 100 + 'px', height: sizes.bottomheight + 'px'};
36
- //@ts-ignore
37
+ let s: Partial<CSSStyleDeclaration> = {width: sizes.rightwidth - 100 + 'px', height: sizes.bottomheight + 'px'};
37
38
  scil.apply(webEditorApp.sequence.style, s);
38
- //@ts-ignore
39
39
  scil.apply(webEditorApp.notation.style, s);
40
40
  s = {width: sizes.rightwidth + 'px', height: (sizes.bottomheight + webEditorApp.toolbarheight) + 'px'};
41
- //@ts-ignore
42
41
  scil.apply(webEditorApp.properties.parent.style, s);
43
42
  webEditorApp.structureview.resize(sizes.rightwidth, sizes.bottomheight + webEditorApp.toolbarheight);
44
43
  webEditorApp.mex.resize(sizes.topheight - 80);
@@ -2,94 +2,46 @@ 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 * as org from 'org';
6
+ import * as JSDraw2 from 'JSDraw2';
5
7
  import wu from 'wu';
6
- import {Unsubscribable} from 'rxjs';
7
8
 
8
9
  import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types/index';
10
+ import {CellRendererBackBase} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
9
11
 
10
12
  import {getParts, parseHelm} from './utils';
11
13
 
12
14
  import {_package, getMonomerLib} from './package';
13
15
 
14
- export const enum Temps {
15
- helmMonomerPlacer = 'bio-helmMonomerPlacer',
16
- }
17
-
18
- export interface IEditor {
19
- get m(): IEditorMol;
20
-
21
- redraw(): void;
22
- }
23
-
24
- export interface IEditorMol {
25
- get atoms(): IEditorMolAtom[];
26
- }
27
-
28
- export interface IEditorMolAtom {
29
- get p(): IEditorPoint;
30
-
31
- get elem(): string;
32
- }
33
-
34
- export interface IEditorPoint {
35
- get x(): number;
36
-
37
- get y(): number;
38
- }
39
-
40
16
  export interface ISeqMonomer {
41
17
  polymerType: string
42
18
  symbol: string,
43
19
  }
44
20
 
45
- export class HelmMonomerPlacer {
46
- private readonly grid: DG.Grid | null;
21
+ export class HelmMonomerPlacer extends CellRendererBackBase<string> {
47
22
  public readonly monomerLib: IMonomerLib;
48
23
 
49
24
  private _allPartsList: (string[] | null)[];
50
25
  private _lengthsList: (number[] | null)[];
51
- private _editorList: (IEditor | null)[];
26
+ private _editorMolList: (JSDraw2.IEditorMol | null)[];
52
27
 
53
28
  public monomerCharWidth: number = 7;
54
29
  public leftPadding: number = 5;
55
30
  public monomerTextSizeMap: { [p: string]: TextMetrics } = {};
56
31
 
57
- private subs: Unsubscribable[] = [];
58
-
59
32
  public constructor(
60
- public readonly gridCol: DG.GridColumn | null,
61
- public readonly col: DG.Column<string>
33
+ gridCol: DG.GridColumn | null,
34
+ tableCol: DG.Column<string>
62
35
  ) {
63
- this.grid = this.gridCol ? this.gridCol.grid : null;
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.grid && this.grid.view?.id === view.id) this.destroy();
76
- } catch (err: any) {
77
- console.error(err);
78
- }
79
- }));
80
- }
36
+ super(gridCol, tableCol, _package.logger);
81
37
  this.monomerLib = getMonomerLib();
82
38
  this.subs.push(this.monomerLib.onChanged.subscribe(this.monomerLibOnChanged.bind(this)));
83
39
  }
84
40
 
85
- private reset(): void {
86
- this._allPartsList = new Array<string[] | null>(this.col.length).fill(null);
87
- this._lengthsList = new Array<number[] | null>(this.col.length).fill(null);
88
- this._editorList = new Array<IEditor | null>(this.col.length).fill(null);
89
- }
90
-
91
- private destroy(): void {
92
- for (const sub of this.subs) sub.unsubscribe();
41
+ protected override reset(): void {
42
+ this._allPartsList = new Array<string[] | null>(this.tableCol.length).fill(null);
43
+ this._lengthsList = new Array<number[] | null>(this.tableCol.length).fill(null);
44
+ this._editorMolList = new Array<JSDraw2.IEditorMol | null>(this.tableCol.length).fill(null);
93
45
  }
94
46
 
95
47
  /** Skips cell for the fallback rendering */
@@ -112,7 +64,7 @@ export class HelmMonomerPlacer {
112
64
  private getCellMonomerLengthsForSeq(rowIdx: number): [string[], number[]] {
113
65
  let allParts: string[] | null = this._allPartsList![rowIdx];
114
66
  if (allParts === null) {
115
- const seq = this.col.get(rowIdx);
67
+ const seq = this.tableCol.get(rowIdx);
116
68
  allParts = this._allPartsList![rowIdx] = getAllParts(seq);
117
69
  }
118
70
 
@@ -145,15 +97,15 @@ export class HelmMonomerPlacer {
145
97
 
146
98
  private monomerLibOnChanged(_value: any): void {
147
99
  this.reset();
148
- if (this.grid) this.grid.invalidate();
100
+ this.invalidateGrid();
149
101
  }
150
102
 
151
- public setEditor(tableRowIndex: number, editor: IEditor) {
152
- this._editorList![tableRowIndex] = editor;
103
+ public setEditorMol(tableRowIndex: number, editorMol: JSDraw2.IEditorMol) {
104
+ this._editorMolList![tableRowIndex] = editorMol.clone(false);
153
105
  }
154
106
 
155
- public getEditor(tableRowIndex: number): IEditor | null {
156
- return this._editorList![tableRowIndex];
107
+ public getEditorMol(tableRowIndex: number): JSDraw2.IEditorMol | null {
108
+ return this._editorMolList![tableRowIndex];
157
109
  }
158
110
  }
159
111
 
@@ -2,11 +2,14 @@ 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 * as JSDraw2 from 'JSDraw2';
6
+
5
7
  import {IHelmWebEditor} from '@datagrok-libraries/bio/src/helm/types';
6
8
 
7
9
  export class HelmWebEditor implements IHelmWebEditor {
8
- host: any;
9
- editor: any;
10
+ editor: JSDraw2.Editor;
11
+ host: HTMLDivElement;
12
+
10
13
  w = 200;
11
14
  h = 100;
12
15
 
@@ -5,6 +5,7 @@ import {runTests, tests, TestContext} from '@datagrok-libraries/utils/src/test';
5
5
 
6
6
  import './tests/helm-tests.ts';
7
7
  import './tests/findMonomers-tests';
8
+ import './tests/helm-service-tests';
8
9
  import './tests/renderers-tests';
9
10
  import './tests/get-molfiles';
10
11
  import './tests/properties-widget-tests';
@@ -0,0 +1,17 @@
1
+ import * as ui from 'datagrok-api/ui';
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as DG from 'datagrok-api/dg';
4
+
5
+ import {HelmServiceBase} from '@datagrok-libraries/bio/src/viewers/helm-service';
6
+ import {HelmService} from './utils/helm-service';
7
+
8
+ type HelmWindowType = Window & {
9
+ $helmService?: HelmServiceBase,
10
+ }
11
+ declare const window: HelmWindowType;
12
+
13
+ export function _getHelmService(): HelmServiceBase {
14
+ let res = window.$helmService;
15
+ if (!res) res = window.$helmService = new HelmService();
16
+ return res;
17
+ }