@datagrok/helm 2.3.3 → 2.4.1
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 +28 -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 +8 -7
- package/src/cell-renderer.ts +9 -8
- package/src/helm-helper.ts +232 -40
- package/src/helm-monomer-placer.ts +1 -1
- package/src/helm-web-editor.ts +24 -34
- package/src/package-utils.ts +92 -117
- package/src/package.ts +13 -22
- package/src/tests/findMonomers-tests.ts +3 -3
- package/src/tests/get-molfiles-tests.ts +2 -2
- package/src/tests/get-monomer-tests.ts +19 -16
- package/src/tests/helm-input-tests.ts +34 -23
- package/src/tests/helm-service-tests.ts +3 -3
- package/src/tests/helm-tests.ts +1 -2
- package/src/tests/helm-web-editor-tests.ts +2 -2
- package/src/tests/parse-helm-tests.ts +9 -8
- package/src/tests/properties-widget-tests.ts +2 -2
- package/src/tests/renderers-tests.ts +2 -2
- package/src/types/index.ts +1 -1
- package/src/utils/get-hovered.ts +8 -10
- package/src/utils/get-monomer.ts +3 -4
- package/src/utils/helm-grid-cell-renderer.ts +16 -29
- package/src/utils/helm-service.ts +4 -6
- package/src/widgets/helm-input.ts +135 -72
- package/src/widgets/properties-widget.ts +4 -4
- package/tsconfig.json +3 -3
- package/webpack.config.js +4 -4
|
@@ -24,14 +24,14 @@ category('helm-web-editor', async () => {
|
|
|
24
24
|
userLibSettings = await getUserLibSettings();
|
|
25
25
|
await setUserLibSettingsForTests();
|
|
26
26
|
await monomerLibHelper.awaitLoaded();
|
|
27
|
-
await monomerLibHelper.
|
|
27
|
+
await monomerLibHelper.loadMonomerLib(true);
|
|
28
28
|
|
|
29
29
|
hh = await getHelmHelper();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
after(async () => {
|
|
33
33
|
await setUserLibSettings(userLibSettings);
|
|
34
|
-
await monomerLibHelper.
|
|
34
|
+
await monomerLibHelper.loadMonomerLib(true);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
test('helm-web-editor', async () => {
|
|
@@ -11,11 +11,12 @@ import {
|
|
|
11
11
|
getUserLibSettings, setUserLibSettings, setUserLibSettingsForTests
|
|
12
12
|
} from '@datagrok-libraries/bio/src/monomer-works/lib-settings';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {JSDraw2Module} from '../types';
|
|
15
15
|
import {initHelmMainPackage} from './utils';
|
|
16
|
+
import {IHelmDrawOptions} from '@datagrok-libraries/helm-web-editor/src/types/org-helm';
|
|
16
17
|
|
|
17
18
|
declare const org: OrgType;
|
|
18
|
-
declare const JSDraw2:
|
|
19
|
+
declare const JSDraw2: JSDraw2Module;
|
|
19
20
|
|
|
20
21
|
type TestTgtType = { atomCount: number, bondCount: number };
|
|
21
22
|
|
|
@@ -47,12 +48,12 @@ category('parseHelm', () => {
|
|
|
47
48
|
await setUserLibSettingsForTests();
|
|
48
49
|
await timeout(async () => { await libHelper.awaitLoaded(); }, 5000,
|
|
49
50
|
'await monomerLib to be loaded');
|
|
50
|
-
await libHelper.
|
|
51
|
+
await libHelper.loadMonomerLib(true);
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
after(async () => {
|
|
54
55
|
await setUserLibSettings(userLibSettings);
|
|
55
|
-
await libHelper.
|
|
56
|
+
await libHelper.loadMonomerLib(true);
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
for (const [testName, {src, tgt}] of Object.entries(testData)) {
|
|
@@ -72,11 +73,11 @@ function _testParseHelmWithEditor(src: string, tgt: TestTgtType): void {
|
|
|
72
73
|
const io = org.helm.webeditor.IO;
|
|
73
74
|
|
|
74
75
|
const editorHost = ui.div();
|
|
75
|
-
const editor = new JSDraw2.Editor(editorHost, {viewonly: true});
|
|
76
|
+
const editor = new JSDraw2.Editor<HelmType, IHelmDrawOptions>(editorHost, {viewonly: true});
|
|
76
77
|
|
|
77
78
|
const plugin = new org.helm.webeditor.Plugin(editor);
|
|
78
79
|
const origin = new JSDraw2.Point(0, 0);
|
|
79
|
-
io.parseHelm(plugin, src, origin,
|
|
80
|
+
io.parseHelm(plugin, src, origin, undefined);
|
|
80
81
|
|
|
81
82
|
const m: Mol<HelmType> = plugin.jsd.m;
|
|
82
83
|
expect(m.atoms.length, tgt.atomCount);
|
|
@@ -86,11 +87,11 @@ function _testParseHelmWithEditor(src: string, tgt: TestTgtType): void {
|
|
|
86
87
|
function _testParseHelmWithoutDOM(src: string, tgt: TestTgtType): void {
|
|
87
88
|
const io = org.helm.webeditor.IO;
|
|
88
89
|
|
|
89
|
-
const molHandler = new JSDraw2.MolHandler();
|
|
90
|
+
const molHandler = new JSDraw2.MolHandler<HelmType>();
|
|
90
91
|
|
|
91
92
|
const plugin = new org.helm.webeditor.Plugin(molHandler);
|
|
92
93
|
const origin = new JSDraw2.Point(0, 0);
|
|
93
|
-
io.parseHelm(plugin, src, origin,
|
|
94
|
+
io.parseHelm(plugin, src, origin, undefined);
|
|
94
95
|
|
|
95
96
|
const m: Mol<HelmType> = plugin.jsd.m;
|
|
96
97
|
expect(m.atoms.length, tgt.atomCount);
|
|
@@ -85,12 +85,12 @@ category('properties-widget', () => {
|
|
|
85
85
|
await setUserLibSettingsForTests();
|
|
86
86
|
await timeout(async () => { await monomerLibHelper.awaitLoaded(); },
|
|
87
87
|
5000, 'MonomerLibHelper.awaitLoaded() timeout');
|
|
88
|
-
await monomerLibHelper.
|
|
88
|
+
await monomerLibHelper.loadMonomerLib(true); // load default libraries
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
after(async () => {
|
|
92
92
|
await setUserLibSettings(userLibSettings);
|
|
93
|
-
await monomerLibHelper.
|
|
93
|
+
await monomerLibHelper.loadMonomerLib(true); // load user settings libraries
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
for (const [testName, testData] of Object.entries(TestsData)) {
|
|
@@ -33,13 +33,13 @@ category('renderers', () => {
|
|
|
33
33
|
|
|
34
34
|
// Test 'helm' requires default monomer library loaded
|
|
35
35
|
await setUserLibSettingsForTests();
|
|
36
|
-
await monomerLibHelper.
|
|
36
|
+
await monomerLibHelper.loadMonomerLib(true); // load default libraries
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
after(async () => {
|
|
40
40
|
// UserDataStorage.put() replaces existing data
|
|
41
41
|
await setUserLibSettings(userLibSettings);
|
|
42
|
-
await monomerLibHelper.
|
|
42
|
+
await monomerLibHelper.loadMonomerLib(true); // load user settings libraries
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
|
package/src/types/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {HelmType, JSDraw2ModuleType, OrgType, ScilModuleType} from '@datagrok-libraries/bio/src/helm/types';
|
|
2
2
|
|
|
3
3
|
export type ScilModule = ScilModuleType;
|
|
4
|
-
export type
|
|
4
|
+
export type JSDraw2Module = JSDraw2ModuleType;
|
|
5
5
|
export type OrgHelmModule = OrgType;
|
package/src/utils/get-hovered.ts
CHANGED
|
@@ -2,17 +2,15 @@ import * as grok from 'datagrok-api/grok';
|
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
|
|
5
|
-
import {HelmType, PolymerType, Atom, Mol, ISeqMonomer} from '@datagrok-libraries/bio/src/helm/types';
|
|
5
|
+
import {HelmType, PolymerType, Atom, Mol, ISeqMonomer, HelmMol, HelmAtom} from '@datagrok-libraries/bio/src/helm/types';
|
|
6
6
|
|
|
7
7
|
import {helmTypeToPolymerType} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
8
8
|
|
|
9
9
|
import {HelmMonomerPlacer} from '../helm-monomer-placer';
|
|
10
10
|
|
|
11
11
|
export function getHoveredMonomerFromEditorMol(
|
|
12
|
-
argsX: number, argsY: number, mol:
|
|
13
|
-
):
|
|
14
|
-
let hoveredSeqMonomer: ISeqMonomer | null = null;
|
|
15
|
-
|
|
12
|
+
argsX: number, argsY: number, mol: HelmMol, cellHeight?: number
|
|
13
|
+
): HelmAtom | null {
|
|
16
14
|
/** @return {[number, number]} [atom, distance] */
|
|
17
15
|
function getNearest(excluded: (number | undefined)[]): [number | undefined, number | undefined] {
|
|
18
16
|
let atom: number | undefined = undefined;
|
|
@@ -34,18 +32,18 @@ export function getHoveredMonomerFromEditorMol(
|
|
|
34
32
|
const [firstAtomI, firstDistance] = getNearest([]);
|
|
35
33
|
const [secondAtomI, secondDistance] = getNearest([firstAtomI]);
|
|
36
34
|
|
|
35
|
+
let resAtom: HelmAtom | null = null;
|
|
37
36
|
if (firstAtomI !== undefined && firstDistance !== undefined) {
|
|
38
37
|
const firstAtom = mol.atoms[firstAtomI];
|
|
39
|
-
const firstSeqMonomer = getSeqMonomerFromHelmAtom(firstAtom);
|
|
40
38
|
if (secondAtomI !== undefined && secondDistance !== undefined) {
|
|
41
39
|
if (firstDistance < secondDistance * 0.45)
|
|
42
|
-
|
|
40
|
+
resAtom = firstAtom;
|
|
43
41
|
} else {
|
|
44
42
|
if (cellHeight && firstDistance < 0.35 * cellHeight)
|
|
45
|
-
|
|
43
|
+
resAtom = firstAtom;
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
|
-
return
|
|
46
|
+
return resAtom;
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
export function getHoveredMonomerFallback(
|
|
@@ -83,7 +81,7 @@ export function getHoveredMonomerFallback(
|
|
|
83
81
|
return hoveredSeqMonomer;
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
function getSeqMonomerFromHelmAtom(atom:
|
|
84
|
+
export function getSeqMonomerFromHelmAtom(atom: HelmAtom): ISeqMonomer {
|
|
87
85
|
const polymerType = helmTypeToPolymerType(atom.bio!.type);
|
|
88
86
|
return {symbol: atom.elem, polymerType: polymerType};
|
|
89
87
|
}
|
package/src/utils/get-monomer.ts
CHANGED
|
@@ -11,10 +11,7 @@ import {
|
|
|
11
11
|
} from '@datagrok-libraries/bio/src/utils/const';
|
|
12
12
|
import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
AmbiguousWebEditorMonomer, GapWebEditorMonomer, getRS, MissingWebEditorMonomer
|
|
17
|
-
} from './get-monomer-dummy';
|
|
14
|
+
import {AmbiguousWebEditorMonomer, GapWebEditorMonomer, getRS, MissingWebEditorMonomer} from './get-monomer-dummy';
|
|
18
15
|
import {LibraryWebEditorMonomer} from './get-monomer-of-library';
|
|
19
16
|
import {OrgHelmModule, ScilModule} from '../types';
|
|
20
17
|
import {RGROUP_CAP_GROUP_NAME, RGROUP_LABEL, SMILES} from '../constants';
|
|
@@ -59,6 +56,8 @@ export function getMonomerOverrideAndLogAlert(
|
|
|
59
56
|
export function getMonomerHandleArgs(
|
|
60
57
|
a: Atom<HelmType> | HelmType, name?: string
|
|
61
58
|
): [/** biotype */ HelmType, /** elem */ string] {
|
|
59
|
+
if (!a)
|
|
60
|
+
throw new Error(`Argument 'a' of type Atom or HelmType is mandatory.`);
|
|
62
61
|
let biotype: HelmType;
|
|
63
62
|
let elem: string;
|
|
64
63
|
if ((a as Atom<HelmType>).T === 'ATOM') {
|
|
@@ -9,54 +9,38 @@ import {
|
|
|
9
9
|
CellRendererBackAsyncBase, RenderServiceBase
|
|
10
10
|
} from '@datagrok-libraries/bio/src/utils/cell-renderer-async-base';
|
|
11
11
|
import {HelmAux, HelmProps} from '@datagrok-libraries/bio/src/viewers/helm-service';
|
|
12
|
+
import {_getHelmService} from '../package-utils';
|
|
12
13
|
import {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
|
|
13
14
|
import {ILogger} from '@datagrok-libraries/bio/src/utils/logger';
|
|
14
15
|
import {getGridCellRendererBack} from '@datagrok-libraries/bio/src/utils/cell-renderer-back-base';
|
|
15
16
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
16
17
|
|
|
17
|
-
import {getHoveredMonomerFromEditorMol} from './get-hovered';
|
|
18
|
-
import {_getHelmService} from '../package-utils';
|
|
18
|
+
import {getHoveredMonomerFromEditorMol, getSeqMonomerFromHelmAtom} from './get-hovered';
|
|
19
19
|
|
|
20
20
|
import {_package} from '../package';
|
|
21
21
|
|
|
22
|
-
class WrapLogger implements ILogger {
|
|
23
|
-
constructor(
|
|
24
|
-
private readonly base: ILogger
|
|
25
|
-
) {}
|
|
26
|
-
|
|
27
|
-
error(message: any, params?: object | undefined, stackTrace?: string | undefined): void {
|
|
28
|
-
this.base.error(message, params, stackTrace);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
warning(message: string, params?: object | undefined): void {
|
|
32
|
-
this.base.warning(message, params);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
info(message: string, params?: object | undefined): void {
|
|
36
|
-
// this.base.info(message, params);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
debug(message: string, params?: object | undefined): void {
|
|
40
|
-
// this.base.debug(message, params);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
22
|
export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProps, HelmAux> {
|
|
45
23
|
private _auxList: (HelmAux | null)[];
|
|
46
24
|
|
|
47
25
|
private monomerLib: IMonomerLib | null = null;
|
|
48
26
|
|
|
27
|
+
// eslint-disable-next-line max-len
|
|
28
|
+
private readonly uuid: string = wu.repeat(1).map(() => Math.floor((Math.random() * 36)).toString(36)).take(4).toArray().join('');
|
|
29
|
+
|
|
49
30
|
constructor(
|
|
50
31
|
gridCol: DG.GridColumn | null,
|
|
51
32
|
tableCol: DG.Column<string>,
|
|
52
33
|
) {
|
|
53
|
-
super(gridCol, tableCol,
|
|
34
|
+
super(gridCol, tableCol, _package.logger, true);
|
|
54
35
|
}
|
|
55
36
|
|
|
56
37
|
protected override reset(): void {
|
|
38
|
+
const logPrefix = `${this.toLog()}.reset()`;
|
|
39
|
+
this.logger.debug(`${logPrefix}, start`);
|
|
57
40
|
super.reset();
|
|
58
41
|
this._auxList = new Array<HelmAux | null>(this.tableCol.length).fill(null);
|
|
59
42
|
if (this.gridCol && this.gridCol.dart) this.gridCol.grid?.invalidate();
|
|
43
|
+
this.logger.debug(`${logPrefix}, end`);
|
|
60
44
|
}
|
|
61
45
|
|
|
62
46
|
protected override getRenderService(): RenderServiceBase<HelmProps, HelmAux> {
|
|
@@ -70,6 +54,8 @@ export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProp
|
|
|
70
54
|
}
|
|
71
55
|
|
|
72
56
|
protected override storeAux(gridCell: DG.GridCell, aux: HelmAux): void {
|
|
57
|
+
const logPrefix = `${this.toLog()}.storeAux()`;
|
|
58
|
+
this.logger.debug(`${logPrefix}, start`);
|
|
73
59
|
if (gridCell.tableRowIndex !== null)
|
|
74
60
|
this._auxList[gridCell.tableRowIndex] = aux;
|
|
75
61
|
}
|
|
@@ -141,10 +127,10 @@ export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProp
|
|
|
141
127
|
this.logger.warning(`${logPrefix}, editorMol of the cell not found.`);
|
|
142
128
|
return; // The gridCell is not rendered yet
|
|
143
129
|
}
|
|
144
|
-
const
|
|
145
|
-
getHoveredMonomerFromEditorMol(argsX, argsY, editorMol, gridCell.bounds.height);
|
|
130
|
+
const hoveredAtom = getHoveredMonomerFromEditorMol(argsX, argsY, editorMol, gridCell.bounds.height);
|
|
146
131
|
|
|
147
|
-
if (
|
|
132
|
+
if (hoveredAtom) {
|
|
133
|
+
const seqMonomer = getSeqMonomerFromHelmAtom(hoveredAtom);
|
|
148
134
|
const monomerLib = _package.monomerLib;
|
|
149
135
|
const tooltipEl = monomerLib ? monomerLib.getTooltip(seqMonomer.polymerType, seqMonomer.symbol) :
|
|
150
136
|
ui.divText('Monomer library is not available');
|
|
@@ -170,7 +156,8 @@ export class HelmGridCellRendererBack extends CellRendererBackAsyncBase<HelmProp
|
|
|
170
156
|
// -- Handle events --
|
|
171
157
|
|
|
172
158
|
private monomerLibOnChanged(_value: any): void {
|
|
173
|
-
this.
|
|
159
|
+
this.dirty = true;
|
|
160
|
+
this.invalidateGrid();
|
|
174
161
|
}
|
|
175
162
|
|
|
176
163
|
static getOrCreate(gridCell: DG.GridCell): HelmGridCellRendererBack {
|
|
@@ -5,23 +5,21 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
6
|
import {Subject, Unsubscribable} from 'rxjs';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
10
|
-
import {Editor, HelmType} from '@datagrok-libraries/bio/src/helm/types';
|
|
8
|
+
import {HelmEditor} from '@datagrok-libraries/bio/src/helm/types';
|
|
11
9
|
import {RenderTask} from '@datagrok-libraries/bio/src/utils/cell-renderer-async-base';
|
|
12
10
|
import {HelmAux, HelmProps, HelmServiceBase} from '@datagrok-libraries/bio/src/viewers/helm-service';
|
|
13
11
|
import {svgToImage} from '@datagrok-libraries/utils/src/svg';
|
|
14
12
|
|
|
15
|
-
import {
|
|
13
|
+
import {JSDraw2Module} from '../types';
|
|
16
14
|
|
|
17
15
|
import {_package} from '../package';
|
|
18
16
|
|
|
19
|
-
declare const JSDraw2:
|
|
17
|
+
declare const JSDraw2: JSDraw2Module;
|
|
20
18
|
|
|
21
19
|
export class HelmService extends HelmServiceBase {
|
|
22
20
|
private readonly hostDiv: HTMLDivElement;
|
|
23
21
|
|
|
24
|
-
private editor!:
|
|
22
|
+
private editor!: HelmEditor;
|
|
25
23
|
private image: HTMLImageElement | null = null;
|
|
26
24
|
|
|
27
25
|
constructor() {
|
|
@@ -3,19 +3,20 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
|
-
import {fromEvent, Unsubscribable} from 'rxjs';
|
|
6
|
+
import {fromEvent, Observable, Unsubscribable} from 'rxjs';
|
|
7
7
|
|
|
8
8
|
import {IMonomerLib} from '@datagrok-libraries/bio/src/types/index';
|
|
9
|
-
import {IHelmHelper,
|
|
10
|
-
import {HelmMol, IHelmWebEditor} from '@datagrok-libraries/bio/src/helm/types';
|
|
9
|
+
import {HelmInputBase, IHelmHelper, IHelmInputInitOptions} from '@datagrok-libraries/bio/src/helm/helm-helper';
|
|
10
|
+
import {HelmAtom, HelmMol, HelmString, IHelmWebEditor} from '@datagrok-libraries/bio/src/helm/types';
|
|
11
11
|
|
|
12
12
|
import {defaultErrorHandler} from '../utils/err-info';
|
|
13
|
-
import {getHoveredMonomerFromEditorMol} from '../utils/get-hovered';
|
|
13
|
+
import {getHoveredMonomerFromEditorMol, getSeqMonomerFromHelmAtom} from '../utils/get-hovered';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {MonomerNumberingTypes} from '@datagrok-libraries/bio/src/helm/consts';
|
|
16
16
|
|
|
17
|
+
import {_package} from '../package';
|
|
17
18
|
|
|
18
|
-
export class HelmInput extends
|
|
19
|
+
export class HelmInput extends HelmInputBase {
|
|
19
20
|
/** Input type identifier (such as "Slider" for the slider input). See {@link InputType}. */
|
|
20
21
|
get inputType(): string {
|
|
21
22
|
return 'Macromolecule';
|
|
@@ -30,12 +31,12 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
30
31
|
return this.viewerHost;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
getValue():
|
|
34
|
-
return this.viewer.editor.
|
|
34
|
+
getValue(): HelmString {
|
|
35
|
+
return this.viewer.editor.getHelm();
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
setValue(value:
|
|
38
|
-
this.viewer.editor.
|
|
38
|
+
setValue(value: HelmString): void {
|
|
39
|
+
this.viewer.editor.setHelm(value);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
getStringValue(): string {
|
|
@@ -46,8 +47,24 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
46
47
|
this.viewer.editor.setHelm(value);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
// -- IHelmInput --
|
|
51
|
+
|
|
52
|
+
get molValue(): HelmMol {
|
|
53
|
+
return this.viewer.editor.m;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set molValue(value: HelmMol) {
|
|
57
|
+
this.viewer.editor.setMol(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get onMouseMove(): Observable<MouseEvent> { return fromEvent<MouseEvent>(this.viewer.host, 'mousemove'); }
|
|
61
|
+
|
|
62
|
+
get onClick(): Observable<MouseEvent> { return fromEvent<MouseEvent>(this.viewer.host, 'click'); }
|
|
63
|
+
|
|
64
|
+
readonly viewerHost: HTMLDivElement;
|
|
65
|
+
readonly viewer: IHelmWebEditor;
|
|
66
|
+
readonly editHintDiv: HTMLDivElement;
|
|
67
|
+
|
|
51
68
|
helmString: string = '';
|
|
52
69
|
helmSelection: number[];
|
|
53
70
|
|
|
@@ -57,6 +74,7 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
57
74
|
private readonly helmHelper: IHelmHelper,
|
|
58
75
|
private readonly monomerLib: IMonomerLib,
|
|
59
76
|
name?: string,
|
|
77
|
+
public readonly options?: IHelmInputInitOptions,
|
|
60
78
|
private readonly logger = _package.logger,
|
|
61
79
|
) {
|
|
62
80
|
super();
|
|
@@ -67,18 +85,49 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
67
85
|
classes: 'ui-input-editor',
|
|
68
86
|
style: {width: '100%', height: '100%', overflow: 'hidden'},
|
|
69
87
|
});
|
|
70
|
-
this.viewer = this.helmHelper.createHelmWebEditor(this.viewerHost
|
|
88
|
+
this.viewer = this.helmHelper.createHelmWebEditor(this.viewerHost, {
|
|
89
|
+
monomerNumbering: MonomerNumberingTypes.continuous
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.editHintDiv = ui.divH([
|
|
93
|
+
ui.link('Click to edit', () => { this.viewer.host.click(); }, undefined, {}),
|
|
94
|
+
], {style: {display: 'none', position: 'absolute', top: '0', right: '0'}});
|
|
71
95
|
|
|
72
96
|
this.subs = [];
|
|
73
97
|
this.subs.push(this.monomerLib.onChanged
|
|
74
98
|
.subscribe(() => this.viewer.editor.redraw()));
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
99
|
+
/* eslint-disable rxjs/no-ignored-subscription */
|
|
100
|
+
fromEvent<MouseEvent>(this.viewer.host, 'mousemove')
|
|
101
|
+
.subscribe(this.viewerOnMouseMove.bind(this));
|
|
102
|
+
fromEvent<MouseEvent>(this.viewer.host, 'mouseenter')
|
|
103
|
+
.subscribe(this.viewerOnMouseEnter.bind(this));
|
|
104
|
+
fromEvent<MouseEvent>(this.viewer.host, 'mouseleave')
|
|
105
|
+
.subscribe(this.viewerOnMouseLeave.bind(this));
|
|
106
|
+
fromEvent<MouseEvent>(this.viewer.host, 'click')
|
|
107
|
+
.subscribe(this.viewerOnClick.bind(this));
|
|
108
|
+
/* eslint-enable rxjs/no-ignored-subscription */
|
|
109
|
+
|
|
110
|
+
this.subs.push(ui.onSizeChanged(this.root).subscribe(() => {
|
|
111
|
+
const rootStyle = window.getComputedStyle(this.root);
|
|
112
|
+
const h = this.root.clientHeight - parseFloat(rootStyle.paddingTop) - parseFloat(rootStyle.paddingBottom);
|
|
113
|
+
this.input.style.height = `${h}px`;
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
this.subs.push(ui.onSizeChanged(this.input).subscribe(() => {
|
|
117
|
+
const rootStyle = window.getComputedStyle(this.root);
|
|
118
|
+
const h = this.root.clientHeight - parseFloat(rootStyle.paddingTop) - parseFloat(rootStyle.paddingBottom);
|
|
119
|
+
this.input.style.height = `{h}px`;
|
|
120
|
+
const w = this.input.clientWidth;
|
|
121
|
+
this.viewer.editor.setSize(w, h);
|
|
122
|
+
}));
|
|
79
123
|
|
|
80
124
|
this.root.classList.add('ui-input-helm');
|
|
81
|
-
this.root.append(this.viewerHost);
|
|
125
|
+
this.root.append(this.viewerHost, this.editHintDiv);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
detach(): void {
|
|
129
|
+
for (const sub of this.subs)
|
|
130
|
+
sub.unsubscribe();
|
|
82
131
|
}
|
|
83
132
|
|
|
84
133
|
protected toLog(): string {
|
|
@@ -86,51 +135,33 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
86
135
|
}
|
|
87
136
|
|
|
88
137
|
/** Inspired by {@link DG.MarkdownInput}, {@link ui._create} */
|
|
89
|
-
static create(
|
|
90
|
-
|
|
138
|
+
static create(helmHelper: IHelmHelper, monomerLib: IMonomerLib,
|
|
139
|
+
name?: string, options?: IHelmInputInitOptions
|
|
91
140
|
): HelmInput {
|
|
92
|
-
const input = new HelmInput(helmHelper, monomerLib, name);
|
|
141
|
+
const input = new HelmInput(helmHelper, monomerLib, name, options);
|
|
93
142
|
// TODO: Apply options
|
|
94
143
|
const value: HelmMol | string | undefined = options?.value;
|
|
95
144
|
if (value !== undefined) {
|
|
96
145
|
if (value instanceof String || typeof value === 'string')
|
|
97
146
|
input.stringValue = value as any as string;
|
|
147
|
+
else if (typeof value === 'object' /* && value.T === 'MOL'*/)
|
|
148
|
+
input.molValue = value;
|
|
98
149
|
else
|
|
99
|
-
|
|
150
|
+
throw new Error(`Unsupported value of type '${typeof value}'.`);
|
|
100
151
|
}
|
|
101
152
|
|
|
102
153
|
return input;
|
|
103
154
|
}
|
|
104
155
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
// const libHelper = await getMonomerLibHelper();
|
|
108
|
-
//
|
|
109
|
-
// const editor = helmHelper.createHelmWebEditor();
|
|
110
|
-
// editor.host.style.width = '270px';
|
|
111
|
-
// editor.host.style.height = '150px';
|
|
112
|
-
// editor.host.style.paddingLeft = '40px';
|
|
113
|
-
//
|
|
114
|
-
// cell = cell ?? grok.shell.tv.dataFrame.currentCell;
|
|
115
|
-
//
|
|
116
|
-
// if (cell.column.semType === DG.SEMTYPE.MACROMOLECULE && cell.column.tags[DG.TAGS.UNITS] === NOTATION.HELM)
|
|
117
|
-
// editor.editor.setHelm(cell.value);
|
|
118
|
-
// else
|
|
119
|
-
// editor.editor.setHelm(PT_HELM_EXAMPLE);
|
|
120
|
-
//
|
|
121
|
-
// return new HelmInput(helmHelper, editor, libHelper);
|
|
122
|
-
// }
|
|
123
|
-
|
|
124
|
-
setHelmString(helm: string): void {
|
|
125
|
-
this.helmString = helm;
|
|
126
|
-
this.viewer.editor.setHelm(helm);
|
|
127
|
-
this.helmSelection = [];
|
|
156
|
+
redraw(): void {
|
|
157
|
+
this.viewer.editor.redraw();
|
|
128
158
|
}
|
|
129
159
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
160
|
+
showTooltip(content: HTMLElement | string, a: HelmAtom): void {
|
|
161
|
+
/** Monomer sign offset for tooltip inspired by {@link org.helm.webeditor.drawMonomer()} */
|
|
162
|
+
const so = this.viewer.editor.getDrawOptions().fontsize * 1.2;
|
|
163
|
+
const bcr = this.input.getBoundingClientRect();
|
|
164
|
+
ui.tooltip.show(content, bcr.left + a.p.x + so, bcr.top + a.p.y + so);
|
|
134
165
|
}
|
|
135
166
|
|
|
136
167
|
// -- Handle events --
|
|
@@ -138,6 +169,58 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
138
169
|
private viewerOnClick(_event: MouseEvent): void {
|
|
139
170
|
ui.tooltip.hide();
|
|
140
171
|
|
|
172
|
+
if (this.options?.editable != false)
|
|
173
|
+
this.showEditorDialog();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private viewerOnMouseMove(event: MouseEvent): void {
|
|
177
|
+
const logPrefix = `${this.toLog()}.viewerOnMouseMove()`;
|
|
178
|
+
|
|
179
|
+
const argsX = event.offsetX;
|
|
180
|
+
const argsY = event.offsetY;
|
|
181
|
+
const mol = this.viewer.editor.m;
|
|
182
|
+
const hoveredAtom = getHoveredMonomerFromEditorMol(argsX, argsY, mol, this.viewer.editor.div.clientHeight);
|
|
183
|
+
if (hoveredAtom) {
|
|
184
|
+
const seqMonomer = getSeqMonomerFromHelmAtom(hoveredAtom);
|
|
185
|
+
const monomerLib = _package.monomerLib;
|
|
186
|
+
const tooltipEl = monomerLib ? monomerLib.getTooltip(seqMonomer.polymerType, seqMonomer.symbol) :
|
|
187
|
+
ui.divText('Monomer library is not available');
|
|
188
|
+
this.showTooltip(tooltipEl, hoveredAtom);
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
event.stopPropagation();
|
|
191
|
+
} else
|
|
192
|
+
ui.tooltip.hide();
|
|
193
|
+
|
|
194
|
+
//this.logger.debug(`${logPrefix}, x = ${event.x}, y = ${event.y}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private viewerOnMouseEnter(_event: MouseEvent): void {
|
|
198
|
+
//this.logger.debug(`${this.toLog()}.viewerOnMouseEnter()`);
|
|
199
|
+
if (this.options?.editable != false)
|
|
200
|
+
this.showEditHint();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private viewerOnMouseLeave(event: MouseEvent): void {
|
|
204
|
+
if (this.editHintDiv.contains(event.relatedTarget as Node)) return; // prevents flickering
|
|
205
|
+
|
|
206
|
+
//this.logger.debug(`${this.toLog()}.viewerOnMouseLeave()`);
|
|
207
|
+
if (this.options?.editable != false)
|
|
208
|
+
this.hideEditHint();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// editHint
|
|
212
|
+
|
|
213
|
+
showEditHint(): void {
|
|
214
|
+
this.editHintDiv.style.removeProperty('display');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
hideEditHint(): void {
|
|
218
|
+
this.editHintDiv.style.display = 'none';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// editor dialog
|
|
222
|
+
|
|
223
|
+
showEditorDialog(): void {
|
|
141
224
|
const webEditorHost = ui.div();
|
|
142
225
|
const webEditorApp = this.helmHelper.createWebEditorApp(webEditorHost, this.viewer.editor.getHelm());
|
|
143
226
|
|
|
@@ -146,16 +229,16 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
146
229
|
};
|
|
147
230
|
|
|
148
231
|
// Edit the molecule and select monomers to enumerate
|
|
149
|
-
const
|
|
232
|
+
const _editorDialog = ui.dialog({showHeader: false, showFooter: true})
|
|
150
233
|
.add(webEditorHost!)
|
|
151
234
|
.onOK(() => {
|
|
152
235
|
try {
|
|
153
|
-
const webEditorValue = webEditorApp
|
|
236
|
+
const webEditorValue = webEditorApp!.canvas!.getHelm(true)
|
|
154
237
|
.replace(/<\/span>/g, '').replace(/<span style='background:#bbf;'>/g, '');
|
|
155
238
|
this.viewer.editor.setHelm(webEditorValue);
|
|
156
239
|
this.helmString = webEditorValue;
|
|
157
240
|
|
|
158
|
-
const selection = webEditorApp
|
|
241
|
+
const selection = webEditorApp!.canvas!.helm!.jsd.m.atoms;
|
|
159
242
|
this.helmSelection = [];
|
|
160
243
|
for (let i = 0; i < selection.length; i++) {
|
|
161
244
|
if (selection[i].selected)
|
|
@@ -175,24 +258,4 @@ export class HelmInput extends DG.JsInputBase<HelmMol> {
|
|
|
175
258
|
})
|
|
176
259
|
.show({modal: true, fullScreen: true});
|
|
177
260
|
}
|
|
178
|
-
|
|
179
|
-
private viewerOnMouseMove(event: MouseEvent): void {
|
|
180
|
-
const logPrefix = `${this.toLog()}.viewerOnMouseMove()`;
|
|
181
|
-
const dpr = window.devicePixelRatio;
|
|
182
|
-
|
|
183
|
-
const argsX = event.offsetX;
|
|
184
|
-
const argsY = event.offsetY;
|
|
185
|
-
const mol = this.viewer.editor.m;
|
|
186
|
-
const seqMonomer = getHoveredMonomerFromEditorMol(argsX, argsY, mol, this.viewer.editor.div.clientHeight);
|
|
187
|
-
if (seqMonomer) {
|
|
188
|
-
const monomerLib = _package.monomerLib;
|
|
189
|
-
const tooltipEl = monomerLib ? monomerLib.getTooltip(seqMonomer.polymerType, seqMonomer.symbol) :
|
|
190
|
-
ui.divText('Monomer library is not available');
|
|
191
|
-
ui.tooltip.show(tooltipEl, event.x + 16, event.y + 16);
|
|
192
|
-
} else {
|
|
193
|
-
// Tooltip for missing monomers
|
|
194
|
-
ui.tooltip.hide();
|
|
195
|
-
}
|
|
196
|
-
this.logger.debug(`${logPrefix}, x = ${event.x}, y = ${event.y}`);
|
|
197
|
-
}
|
|
198
261
|
}
|
|
@@ -7,10 +7,10 @@ import $ from 'cash-dom';
|
|
|
7
7
|
import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
|
|
8
8
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {JSDraw2Module} from '../types';
|
|
11
11
|
import {removeGapsFromHelm} from '../utils';
|
|
12
12
|
|
|
13
|
-
declare const JSDraw2:
|
|
13
|
+
declare const JSDraw2: JSDraw2Module;
|
|
14
14
|
|
|
15
15
|
export class SeqPropertiesError extends Error {
|
|
16
16
|
constructor(message?: string, options?: ErrorOptions) {
|
|
@@ -47,8 +47,8 @@ export function getPropertiesDict(seq: string, sh: SeqHandler): {} {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
export function getPropertiesWidget(value: DG.SemanticValue): DG.Widget {
|
|
51
|
-
const sh = SeqHandler.forColumn(value.cell.column);
|
|
50
|
+
export function getPropertiesWidget(value: DG.SemanticValue<string>): DG.Widget {
|
|
51
|
+
const sh = SeqHandler.forColumn(value.cell.column as DG.Column<string>);
|
|
52
52
|
try {
|
|
53
53
|
const propDict = getPropertiesDict(value.value, sh);
|
|
54
54
|
return new DG.Widget(
|
package/tsconfig.json
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
/* Basic Options */
|
|
6
6
|
// "incremental": true, /* Enable incremental compilation */
|
|
7
|
-
"target": "
|
|
8
|
-
"module": "
|
|
9
|
-
"lib": ["ES2022", "ES2022.String", "
|
|
7
|
+
"target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
|
8
|
+
"module": "ES2022", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
|
9
|
+
"lib": ["ES2022", "ES2022.String", "DOM"], /* Specify library files to be included in the compilation. */
|
|
10
10
|
"allowJs": true, /* Allow javascript files to be compiled. */
|
|
11
11
|
// "checkJs": true, /* Report errors in .js files. */
|
|
12
12
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|