@datagrok/helm 2.3.4 → 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 +20 -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 +12 -20
- package/src/package.ts +13 -23
- 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 +4 -4
- 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
|
@@ -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'. */
|
package/webpack.config.js
CHANGED
|
@@ -6,7 +6,7 @@ if (mode !== 'production')
|
|
|
6
6
|
console.warn(`Building '${packageName}' in '${mode}' mode.`);
|
|
7
7
|
|
|
8
8
|
module.exports = {
|
|
9
|
-
|
|
9
|
+
// ...(mode !== 'production' ? {} : {cache: {type: 'filesystem'}}),
|
|
10
10
|
cache: {type: 'filesystem'},
|
|
11
11
|
mode: mode,
|
|
12
12
|
entry: {
|
|
@@ -31,9 +31,9 @@ module.exports = {
|
|
|
31
31
|
},
|
|
32
32
|
module: {
|
|
33
33
|
rules: [
|
|
34
|
-
{test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], exclude: [/node_modules
|
|
35
|
-
{test: /\.ts(x?)$/, use: 'ts-loader', exclude: [/node_modules
|
|
36
|
-
{test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: [/node_modules
|
|
34
|
+
{test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], exclude: [/node_modules/]},
|
|
35
|
+
{test: /\.ts(x?)$/, use: 'ts-loader', exclude: [/node_modules/]},
|
|
36
|
+
{test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: [/node_modules/]},
|
|
37
37
|
],
|
|
38
38
|
},
|
|
39
39
|
devtool: 'source-map',
|