@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.
@@ -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.loadLibraries(true);
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.loadLibraries(true);
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 {JSDraw2HelmModule} from '../types';
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: JSDraw2HelmModule;
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.loadLibraries(true);
51
+ await libHelper.loadMonomerLib(true);
51
52
  });
52
53
 
53
54
  after(async () => {
54
55
  await setUserLibSettings(userLibSettings);
55
- await libHelper.loadLibraries(true);
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, null);
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, null);
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.loadLibraries(true); // load default libraries
88
+ await monomerLibHelper.loadMonomerLib(true); // load default libraries
89
89
  });
90
90
 
91
91
  after(async () => {
92
92
  await setUserLibSettings(userLibSettings);
93
- await monomerLibHelper.loadLibraries(true); // load user settings libraries
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.loadLibraries(true); // load default libraries
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.loadLibraries(true); // load user settings libraries
42
+ await monomerLibHelper.loadMonomerLib(true); // load user settings libraries
43
43
  });
44
44
 
45
45
 
@@ -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 JSDraw2HelmModule = JSDraw2ModuleType<HelmType>;
4
+ export type JSDraw2Module = JSDraw2ModuleType;
5
5
  export type OrgHelmModule = OrgType;
@@ -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: Mol<HelmType>, cellHeight? : number
13
- ): ISeqMonomer | null {
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
- hoveredSeqMonomer = firstSeqMonomer;
40
+ resAtom = firstAtom;
43
41
  } else {
44
42
  if (cellHeight && firstDistance < 0.35 * cellHeight)
45
- hoveredSeqMonomer = firstSeqMonomer;
43
+ resAtom = firstAtom;
46
44
  }
47
45
  }
48
- return hoveredSeqMonomer;
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: Atom<HelmType>): ISeqMonomer {
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
  }
@@ -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, new WrapLogger(_package.logger) /* _package.logger */, true);
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 seqMonomer: ISeqMonomer | null =
145
- getHoveredMonomerFromEditorMol(argsX, argsY, editorMol, gridCell.bounds.height);
130
+ const hoveredAtom = getHoveredMonomerFromEditorMol(argsX, argsY, editorMol, gridCell.bounds.height);
146
131
 
147
- if (seqMonomer) {
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.reset();
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 {errInfo} from '@datagrok-libraries/bio/src/utils/err-info';
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 {JSDraw2HelmModule} from '../types';
13
+ import {JSDraw2Module} from '../types';
16
14
 
17
15
  import {_package} from '../package';
18
16
 
19
- declare const JSDraw2: JSDraw2HelmModule;
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!: Editor<HelmType>;
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, IInputInitOptions} from '@datagrok-libraries/bio/src/helm/helm-helper';
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 {_package} from '../package';
15
+ import {MonomerNumberingTypes} from '@datagrok-libraries/bio/src/helm/consts';
16
16
 
17
+ import {_package} from '../package';
17
18
 
18
- export class HelmInput extends DG.JsInputBase<HelmMol> {
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(): HelmMol {
34
- return this.viewer.editor.m;
34
+ getValue(): HelmString {
35
+ return this.viewer.editor.getHelm();
35
36
  }
36
37
 
37
- setValue(value: HelmMol): void {
38
- this.viewer.editor.setMol(value);
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
- viewerHost: HTMLDivElement;
50
- viewer: IHelmWebEditor;
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
- this.subs.push(fromEvent<MouseEvent>(this.viewer.host, 'mousemove')
76
- .subscribe(this.viewerOnMouseMove.bind(this)));
77
- this.subs.push(fromEvent<MouseEvent>(this.viewer.host, 'click')
78
- .subscribe(this.viewerOnClick.bind(this)));
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
- helmHelper: IHelmHelper, monomerLib: IMonomerLib, name?: string, options?: IInputInitOptions<HelmMol>
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
- input.value = value;
150
+ throw new Error(`Unsupported value of type '${typeof value}'.`);
100
151
  }
101
152
 
102
153
  return input;
103
154
  }
104
155
 
105
- // static async init(host?: HTMLElement, cell?: DG.Cell): Promise<HelmInput> {
106
- // const helmHelper = await getHelmHelper();
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
- getDiv(): HTMLDivElement {
131
- const title = ui.divText('Macromolecule', {style: {paddingTop: '43px', color: 'var(--grey-4)'}});
132
-
133
- return ui.divH([title, this.viewer.host], {style: {paddingLeft: '48px'}});
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 dlg = ui.dialog({showHeader: false, showFooter: true})
232
+ const _editorDialog = ui.dialog({showHeader: false, showFooter: true})
150
233
  .add(webEditorHost!)
151
234
  .onOK(() => {
152
235
  try {
153
- const webEditorValue = webEditorApp.canvas!.getHelm(true)
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.canvas!.helm!.jsd.m.atoms;
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 {JSDraw2HelmModule} from '../types';
10
+ import {JSDraw2Module} from '../types';
11
11
  import {removeGapsFromHelm} from '../utils';
12
12
 
13
- declare const JSDraw2: JSDraw2HelmModule;
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": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8
- "module": "es2020", /* 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. */
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'. */