@datagrok/bio 2.4.49 → 2.4.51

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
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.4.49",
8
+ "version": "2.4.51",
9
9
  "description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
10
10
  "repository": {
11
11
  "type": "git",
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "@biowasm/aioli": "^3.1.0",
25
- "@datagrok-libraries/bio": "^5.32.7",
25
+ "@datagrok-libraries/bio": "^5.32.8",
26
26
  "@datagrok-libraries/chem-meta": "^1.0.1",
27
27
  "@datagrok-libraries/ml": "^6.3.39",
28
28
  "@datagrok-libraries/tutorials": "^1.3.2",
File without changes
package/src/package.ts CHANGED
@@ -614,6 +614,16 @@ export function importFasta(fileContent: string): DG.DataFrame [] {
614
614
  const ffh = new FastaFileHandler(fileContent);
615
615
  return ffh.importFasta();
616
616
  }
617
+ //name: importBam
618
+ //description: Opens Bam file
619
+ //tags: file-handler
620
+ //meta.ext: bam, bai
621
+ //input: string fileContent
622
+ //output: list tables
623
+ export function importBam(fileContent: string): DG.DataFrame [] {
624
+ console.log(fileContent);
625
+ return [];
626
+ }
617
627
 
618
628
  //top-menu: Bio | Convert...
619
629
  //name: convertDialog
@@ -3,7 +3,7 @@ import * as DG from 'datagrok-api/dg';
3
3
  import * as ui from 'datagrok-api/ui';
4
4
 
5
5
  import {_package} from '../package-test';
6
- import {after, before, category, delay, expect, test} from '@datagrok-libraries/utils/src/test';
6
+ import {category, test} from '@datagrok-libraries/utils/src/test';
7
7
  import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
8
8
  import {monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
9
  import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
@@ -22,13 +22,14 @@ category('renderers: monomerPlacer', () => {
22
22
  'id3,mon1-M-mon3-mon4-mon5-MON8-N9\n', //
23
23
  testList: [
24
24
  {src: {row: 0, x: -1}, tgt: {pos: null}},
25
- {src: {row: 1, x: 0}, tgt: {pos: 0}},
26
- {src: {row: 1, x: 1}, tgt: {pos: 0}},
25
+ {src: {row: 1, x: 0}, tgt: {pos: null}},
26
+ {src: {row: 1, x: 5}, tgt: {pos: 0}},
27
+ {src: {row: 1, x: 6}, tgt: {pos: 0}},
27
28
  {src: {row: 1, x: 26}, tgt: {pos: 1}},
28
29
  {src: {row: 1, x: 170}, tgt: {pos: 6}},
29
- {src: {row: 1, x: 208}, tgt: {pos: null}},
30
- {src: {row: 2, x: 170}, tgt: {pos: 6}},
31
- {src: {row: 2, x: 175}, tgt: {pos: null}},
30
+ {src: {row: 1, x: 208}, tgt: {pos: 7}},
31
+ {src: {row: 2, x: 170}, tgt: {pos: 5}},
32
+ {src: {row: 2, x: 175}, tgt: {pos: 5}},
32
33
  ]
33
34
  },
34
35
  splitterMsa: {
@@ -41,13 +42,13 @@ category('renderers: monomerPlacer', () => {
41
42
  'id3,mon1-M-mon3-mon4-mon5---MON8-N9\n', //
42
43
  testList: [
43
44
  {src: {row: 0, x: -1}, tgt: {pos: null}},
44
- {src: {row: 1, x: 0}, tgt: {pos: 0}},
45
- {src: {row: 1, x: 1}, tgt: {pos: 0}},
46
- {src: {row: 1, x: 26}, tgt: {pos: 1}},
47
- {src: {row: 1, x: 170}, tgt: {pos: 6}},
48
- {src: {row: 1, x: 227}, tgt: {pos: null}},
49
- {src: {row: 2, x: 220}, tgt: {pos: 8}},
50
- {src: {row: 2, x: 227}, tgt: {pos: null}},
45
+ {src: {row: 1, x: 0}, tgt: {pos: null}},
46
+ {src: {row: 1, x: 1}, tgt: {pos: null}},
47
+ {src: {row: 1, x: 26}, tgt: {pos: 0}},
48
+ {src: {row: 1, x: 170}, tgt: {pos: 4}},
49
+ {src: {row: 1, x: 227}, tgt: {pos: 5}},
50
+ {src: {row: 2, x: 220}, tgt: {pos: 5}},
51
+ {src: {row: 2, x: 227}, tgt: {pos: 5}},
51
52
  ]
52
53
  },
53
54
  fastaMsa: {
@@ -61,13 +62,13 @@ id3,QHIRE--LT
61
62
  `,
62
63
  testList: [
63
64
  {src: {row: 1, x: -1}, tgt: {pos: null}},
64
- {src: {row: 1, x: 0}, tgt: {pos: 0}},
65
- {src: {row: 1, x: 1}, tgt: {pos: 0}},
66
- {src: {row: 1, x: 19}, tgt: {pos: 1}},
65
+ {src: {row: 1, x: 0}, tgt: {pos: null}},
66
+ {src: {row: 1, x: 1}, tgt: {pos: null}},
67
+ {src: {row: 1, x: 19}, tgt: {pos: 0}},
67
68
  {src: {row: 1, x: 170}, tgt: {pos: 8}},
68
- {src: {row: 1, x: 171}, tgt: {pos: null}},
69
+ {src: {row: 1, x: 171}, tgt: {pos: 8}},
69
70
  {src: {row: 2, x: 170}, tgt: {pos: 8}},
70
- {src: {row: 2, x: 171}, tgt: {pos: null}},
71
+ {src: {row: 2, x: 181}, tgt: {pos: null}},
71
72
  ]
72
73
  },
73
74
  };
@@ -78,7 +79,7 @@ id3,QHIRE--LT
78
79
  await grok.data.detectSemanticTypes(df);
79
80
  const seqCol: DG.Column = df.getCol('seq');
80
81
 
81
- const monLength: number = 1;
82
+ const monLength: number = 3;
82
83
  const charWidth: number = 7;
83
84
  const sepWidth: number = 12;
84
85
  const colTemp: MonomerPlacer = new MonomerPlacer(null, seqCol, () => {
@@ -8,15 +8,28 @@ export enum MonomerWidthMode {
8
8
  }
9
9
 
10
10
  export const enum Tags {
11
- calculated = '.mm.cellRenderer.calculated',
11
+ RendererSettingsChanged = '.mm.cellRenderer.settingsChanged',
12
12
  }
13
13
 
14
+ export const rendererSettingsChangedState = {
15
+ true: '1',
16
+ false: '0',
17
+ };
18
+
14
19
  export const enum Temps {
15
20
  monomerWidth = '.mm.cellRenderer.monomerWidth',
16
21
  maxMonomerLength = '.mm.cellRenderer.maxMonomerLength',
17
22
  colorCode = '.mm.cellRenderer.colorCode',
18
23
  compareWithCurrent = '.mm.cellRenderer.compareWithCurrent',
19
24
  highlightDifference = '.mm.cellRenderer.highlightDifference',
25
+ gapLength = '.mm.cellRenderer.gapLength',
26
+ }
27
+
28
+ export const enum tempTAGS {
29
+ referenceSequence = 'reference-sequence',
30
+ currentWord = 'current-word',
31
+ monomerWidth = 'monomer-width',
32
+ bioSeqCol = 'bio-seqCol',
20
33
  }
21
34
 
22
35
  // export const MacromoleculeCellRendererDefaults = new class {
@@ -7,10 +7,8 @@ import {printLeftOrCentered, DrawStyle} from '@datagrok-libraries/bio/src/utils/
7
7
  import * as C from './constants';
8
8
  import {MonomerPlacer} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
9
9
  import {
10
- ALIGNMENT, ALPHABET,
11
10
  getPaletteByType,
12
11
  getSplitter,
13
- getSplitterForColumn,
14
12
  monomerToShort,
15
13
  MonomerToShortFunc,
16
14
  NOTATION,
@@ -20,22 +18,8 @@ import {
20
18
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
21
19
  import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
22
20
  import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
23
- import {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
24
- import {Tags as mmcrTags, Temps as mmcrTemps} from '../utils/cell-renderer-consts';
25
- import { HELM_POLYMER_TYPE } from '@datagrok-libraries/bio/src/utils/const';
26
- import { MonomerLib } from './monomer-lib';
27
- import { IMonomerLib } from '@datagrok-libraries/bio/src/types';
28
-
29
- const enum tempTAGS {
30
- referenceSequence = 'reference-sequence',
31
- currentWord = 'current-word',
32
- monomerWidth = 'monomer-width',
33
- bioSeqCol = 'bio-seqCol',
34
- }
35
-
36
- const enum rndrTAGS {
37
- calculatedCellRender = '.calculatedCellRender',
38
- }
21
+ import {Temps as mmcrTemps, Tags as mmcrTags,
22
+ tempTAGS, rendererSettingsChangedState} from '../utils/cell-renderer-consts';
39
23
 
40
24
  type TempType = { [tagName: string]: any };
41
25
 
@@ -85,7 +69,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
85
69
  // return;
86
70
 
87
71
  const tableCol: DG.Column = gridCell.cell.column;
88
- const tableColTemp: TempType = tableCol.temp;
72
+ //const tableColTemp: TempType = tableCol.temp;
89
73
  const seqColTemp: MonomerPlacer = tableCol.temp[tempTAGS.bioSeqCol];
90
74
  if (!seqColTemp) return; // Can do nothing without precalculated data
91
75
 
@@ -105,10 +89,10 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
105
89
  const monomerSymbol: string = seqMonList[left];
106
90
  const tooltipElements: HTMLElement[] = [ui.div(monomerSymbol)];
107
91
  const monomer = seqColTemp.getMonomer(monomerSymbol);
108
- if(monomer) {
109
- const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
110
- const monomerSVG = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
111
- tooltipElements.push(monomerSVG);
92
+ if (monomer) {
93
+ const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
94
+ const monomerSVG = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
95
+ tooltipElements.push(monomerSVG);
112
96
  }
113
97
  ui.tooltip.show(ui.divV(tooltipElements), e.x + 16, e.y + 16);
114
98
  } else {
@@ -130,9 +114,10 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
130
114
  */
131
115
  render(
132
116
  g: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, gridCell: DG.GridCell,
133
- cellStyle: DG.GridCellStyle
117
+ _cellStyle: DG.GridCellStyle
134
118
  ): void {
135
- let gapRenderer = 5;
119
+ let gapLength = 0;
120
+ const msaGapLength = 8;
136
121
  let maxLengthOfMonomer = 8;
137
122
 
138
123
  // TODO: Store temp data to GridColumn
@@ -144,9 +129,9 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
144
129
  // Cell renderer settings
145
130
  const tempMonomerWidth: string | null = tableColTemp[tempTAGS.monomerWidth];
146
131
  const monomerWidth: string = (tempMonomerWidth != null) ? tempMonomerWidth : 'short';
147
- if (monomerWidth === 'short') {
132
+ if (monomerWidth === 'short')
148
133
  maxLengthOfMonomer = tableColTemp[mmcrTemps.maxMonomerLength] ?? _package.properties.maxMonomerLength;
149
- }
134
+
150
135
 
151
136
  let seqColTemp: MonomerPlacer = tableCol.temp[tempTAGS.bioSeqCol];
152
137
  if (!seqColTemp) {
@@ -155,16 +140,24 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
155
140
  const uh = UnitsHandler.getOrCreate(tableCol);
156
141
  return {
157
142
  unitsHandler: uh,
158
- monomerCharWidth: 7, separatorWidth: !uh.isMsa() ? gapRenderer : 8,
143
+ monomerCharWidth: 7, separatorWidth: !uh.isMsa() ? gapLength : msaGapLength,
159
144
  monomerToShort: monomerToShortFunction, monomerLengthLimit: maxLengthOfMonomer,
160
145
  monomerLib: getBioLib()
161
146
  };
162
147
  });
163
148
  }
164
149
 
150
+ if (tableCol.tags[mmcrTags.RendererSettingsChanged] === rendererSettingsChangedState.true) {
151
+ gapLength = tableColTemp[mmcrTemps.gapLength] as number ?? gapLength;
152
+ // this event means that the mm renderer settings have changed, particularly monomer representation and max width.
153
+ seqColTemp.setMonomerLengthLimit(maxLengthOfMonomer);
154
+ seqColTemp.setSeparatorWidth(seqColTemp.isMsa() ? msaGapLength : gapLength);
155
+ tableCol.setTag(mmcrTags.RendererSettingsChanged, rendererSettingsChangedState.false);
156
+ }
157
+
165
158
  const [maxLengthWords, maxLengthWordsSum]: [number[], number[]] =
166
159
  seqColTemp.getCellMonomerLengths(gridCell.tableRowIndex!);
167
- const maxIndex = maxLengthWords.length;
160
+ const _maxIndex = maxLengthWords.length;
168
161
 
169
162
  // Store updated seqColTemp to the col temp
170
163
  if (seqColTemp.updated) tableColTemp[tempTAGS.bioSeqCol] = seqColTemp;
@@ -1,13 +1,18 @@
1
1
  import * as ui from 'datagrok-api/ui';
2
+ import * as grok from 'datagrok-api/grok';
2
3
  import * as DG from 'datagrok-api/dg';
3
4
 
4
- import * as rxjs from 'rxjs';
5
- import {WebLogoViewer, PROPS as wlPROPS} from '../viewers/web-logo-viewer';
6
- import {IVdRegionsViewer,
5
+ import {fromEvent, Unsubscribable} from 'rxjs';
6
+
7
+ import {
8
+ IVdRegionsViewer,
7
9
  VdRegion, VdRegionType,
8
10
  } from '@datagrok-libraries/bio/src/viewers/vd-regions';
9
- import {FilterSources, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
10
- import {Unsubscribable} from 'rxjs';
11
+ import {FilterSources, IWebLogoViewer, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
12
+
13
+ import {WebLogoViewer, PROPS as wlPROPS} from '../viewers/web-logo-viewer';
14
+
15
+ import {_package} from '../package';
11
16
 
12
17
  const vrt = VdRegionType;
13
18
 
@@ -101,22 +106,29 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
101
106
  // this.mlbView.dockManager.dock(this.regionsFg.root, DG.DOCK_TYPE.LEFT, rootNode, 'Filter regions', 0.2);
102
107
 
103
108
  this.subs.push(ui.onSizeChanged(this.root).subscribe(this.rootOnSizeChanged.bind(this)));
104
- this.subs.push(rxjs.fromEvent<MouseEvent>(this.root, 'mousemove').subscribe(this.rootOnMouseMove.bind(this)));
109
+ this.subs.push(fromEvent<MouseEvent>(this.root, 'mousemove').subscribe(this.rootOnMouseMove.bind(this)));
105
110
 
106
111
  // await this.buildView('init'); // init
107
112
  }
108
113
 
109
- public override async onTableAttached() {
110
- const superOnTableAttached = super.onTableAttached.bind(this);
111
- this.viewPromise = this.viewPromise.then(async () => { // onTableAttached
112
- superOnTableAttached();
113
- if (!this.viewed) {
114
- await this.buildView('onTableAttached'); // onTableAttached
115
- this.viewed = true;
114
+ override detach() {
115
+ if (this.setDataInProgress) return;
116
+ const superDetach = super.detach.bind(this);
117
+ this.detachPromise = this.detachPromise.then(async () => { // detach
118
+ await this.viewPromise;
119
+ if (this.viewed) {
120
+ await this.destroyView('detach');
121
+ this.viewed = false;
116
122
  }
123
+ superDetach();
117
124
  });
118
125
  }
119
126
 
127
+ override onTableAttached() {
128
+ super.onTableAttached();
129
+ this.setData(this.dataFrame, this.regions);
130
+ }
131
+
120
132
  public override onPropertyChanged(property: DG.Property | null): void {
121
133
  super.onPropertyChanged(property);
122
134
 
@@ -156,39 +168,41 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
156
168
 
157
169
  // TODO: .onTableAttached is not calling on dataFrame set, onPropertyChanged also not calling
158
170
  public setData(mlbDf: DG.DataFrame, regions: VdRegion[]) {
159
- console.debug('Bio: VdRegionsViewer.setData()');
171
+ if (!this.setDataInProgress) this.setDataInProgress = true; else return;
172
+ _package.logger.debug('Bio: VdRegionsViewer.setData()');
173
+
160
174
  this.viewPromise = this.viewPromise.then(async () => { // setData
161
175
  if (this.viewed) {
162
- await this.destroyView('setData'); // setData
176
+ await this.destroyView('setData');
163
177
  this.viewed = false;
164
178
  }
165
- });
166
-
167
- this.regions = regions;
168
- this.dataFrame = mlbDf; // causes detach and onTableAttached
169
-
170
- this.viewPromise = this.viewPromise.then(async () => { // setData
179
+ }).then(async () => {
180
+ await this.detachPromise;
181
+ // Wait whether this.dataFrame assigning has called detach() before continue set data and build view
182
+
183
+ // -- Data --
184
+ this.regions = regions;
185
+ if (this.dataFrame.dart !== mlbDf.dart) this.dataFrame = mlbDf; // causes detach and onTableAttached
186
+ }).then(async () => {
171
187
  if (!this.viewed) {
172
- await this.buildView('setData'); // setData
188
+ await this.buildView('setData');
173
189
  this.viewed = true;
174
190
  }
175
- });
176
- }
177
-
178
- override detach() {
179
- const superDetach = super.detach.bind(this);
180
- this.viewPromise = this.viewPromise.then(async () => { // detach
181
- if (this.viewed) {
182
- await this.destroyView('detach'); // detach
183
- this.viewed = false;
184
- }
185
- superDetach();
191
+ }).catch((err: any) => {
192
+ const errMsg = err instanceof Error ? err.message : err.toString();
193
+ const stack = err instanceof Error ? err.stack : undefined;
194
+ grok.shell.error(errMsg);
195
+ _package.logger.error(errMsg, undefined, stack);
196
+ }).finally(() => {
197
+ this.setDataInProgress = false;
186
198
  });
187
199
  }
188
200
 
189
201
  // -- View --
190
202
 
191
203
  private viewPromise: Promise<void> = Promise.resolve();
204
+ private detachPromise: Promise<void> = Promise.resolve();
205
+ private setDataInProgress: boolean = false;
192
206
 
193
207
  private host: HTMLElement | null = null;
194
208
  private filterSourceInput: DG.InputBase<boolean | null> | null = null;
@@ -223,25 +237,32 @@ export class VdRegionsViewer extends DG.JsViewer implements IVdRegionsViewer {
223
237
  const regionsFiltered: VdRegion[] = this.regions.filter((r: VdRegion) => this.regionTypes.includes(r.type));
224
238
  const orderList: number[] = Array.from(new Set(regionsFiltered.map((r) => r.order))).sort();
225
239
 
226
- this.logos = [];
240
+ const logoPromiseList: Promise<[number, string, WebLogoViewer]>[] = [];
227
241
  for (let orderI = 0; orderI < orderList.length; orderI++) {
228
- const regionChains: { [chain: string]: WebLogoViewer } = {};
229
242
  for (const chain of this.chains) {
230
243
  const region: VdRegion | undefined = regionsFiltered
231
244
  .find((r) => r.order == orderList[orderI] && r.chain == chain);
232
- regionChains[chain] = (await this.dataFrame.plot.fromType('WebLogo', {
233
- sequenceColumnName: region!.sequenceColumnName,
234
- startPositionName: region!.positionStartName,
235
- endPositionName: region!.positionEndName,
236
- fixWidth: true,
237
- skipEmptyPositions: this.skipEmptyPositions,
238
- positionWidth: this.positionWidth,
239
- positionHeight: this.positionHeight,
240
- })) as unknown as WebLogoViewer;
245
+ logoPromiseList.push((async () => {
246
+ const wl: WebLogoViewer = await this.dataFrame.plot.fromType('WebLogo', {
247
+ sequenceColumnName: region!.sequenceColumnName,
248
+ startPositionName: region!.positionStartName,
249
+ endPositionName: region!.positionEndName,
250
+ fixWidth: true,
251
+ skipEmptyPositions: this.skipEmptyPositions,
252
+ positionWidth: this.positionWidth,
253
+ positionHeight: this.positionHeight,
254
+ }) as WebLogoViewer;
255
+ return [orderI, chain, wl];
256
+ })());
241
257
  }
242
- // WebLogo creation fires onRootSizeChanged event even before control being added to this.logos
243
- this.logos[orderI] = regionChains;
244
258
  }
259
+ const logoList: [number, string, WebLogoViewer][] = await Promise.all(logoPromiseList);
260
+ // Fill in this.logos with created viewers
261
+ this.logos = new Array(orderList.length);
262
+ for (let orderI = 0; orderI < orderList.length; ++orderI)
263
+ this.logos[orderI] = {};
264
+ for (const [orderI, chain, wl] of logoList)
265
+ this.logos[orderI][chain] = wl;
245
266
 
246
267
  // ui.tableFromMap()
247
268
  // DG.HtmlTable.create()
@@ -5,24 +5,27 @@ import * as DG from 'datagrok-api/dg';
5
5
  import wu from 'wu';
6
6
  import {fromEvent, Unsubscribable} from 'rxjs';
7
7
 
8
- import {SliderOptions} from 'datagrok-api/dg';
9
8
  import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
10
9
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
11
10
  import {
12
- getSplitter, monomerToShort, pickUpPalette, pickUpSeqCol, SplitterFunc,
11
+ monomerToShort,
12
+ pickUpPalette,
13
+ pickUpSeqCol,
13
14
  TAGS as bioTAGS,
14
15
  } from '@datagrok-libraries/bio/src/utils/macromolecule';
15
16
  import {
16
- WebLogoPropsDefault, WebLogoProps,
17
+ FilterSources,
18
+ HorizontalAlignments,
17
19
  PositionHeight,
20
+ PositionMarginStates,
18
21
  positionSeparator,
22
+ TAGS as wlTAGS,
19
23
  VerticalAlignments,
20
- HorizontalAlignments,
21
- PositionMarginStates,
22
- FilterSources,
24
+ WebLogoProps,
25
+ WebLogoPropsDefault,
23
26
  } from '@datagrok-libraries/bio/src/viewers/web-logo';
24
27
  import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
25
- import {TAGS as wlTAGS} from '@datagrok-libraries/bio/src/viewers/web-logo';
28
+
26
29
  import {_package} from '../package';
27
30
 
28
31
  declare global {
@@ -450,7 +453,7 @@ export class WebLogoViewer extends DG.JsViewer {
450
453
  this.filterSource = this.string(PROPS.filterSource, defaults.filterSource,
451
454
  {category: PROPS_CATS.BEHAVIOR, choices: Object.values(FilterSources)}) as FilterSources;
452
455
 
453
- const style: SliderOptions = {style: 'barbell'};
456
+ const style: DG.SliderOptions = {style: 'barbell'};
454
457
  this.slider = ui.rangeSlider(0, 100, 0, 20, false, style);
455
458
  this.canvas = ui.canvas();
456
459
  this.canvas.style.width = '100%';
@@ -576,7 +579,7 @@ export class WebLogoViewer extends DG.JsViewer {
576
579
  } catch (err: any) {
577
580
  this.seqCol = null;
578
581
  this.error = err instanceof Error ? err : new Error(err.toString());
579
- _package.logger.error(this.error.message, undefined, this.error.stack);
582
+ throw err;
580
583
  }
581
584
  if (!this.seqCol) {
582
585
  this.unitsHandler = null;
@@ -700,7 +703,18 @@ export class WebLogoViewer extends DG.JsViewer {
700
703
  break;
701
704
  }
702
705
 
703
- this.render(RecalcLevel.Freqs, 'onPropertyChanged');
706
+ switch (property.name) {
707
+ case PROPS.fixWidth:
708
+ case PROPS.fitArea:
709
+ case PROPS.positionWidth:
710
+ case PROPS.positionMargin:
711
+ this.render(RecalcLevel.Layout, 'onPropertyChanged');
712
+ break;
713
+
714
+ default:
715
+ this.render(RecalcLevel.Freqs, 'onPropertyChanged');
716
+ break;
717
+ }
704
718
  }
705
719
 
706
720
  /** Add filter handlers when table is a attached */
@@ -751,7 +765,7 @@ export class WebLogoViewer extends DG.JsViewer {
751
765
 
752
766
  /** Helper function for remove empty positions */
753
767
  // TODO: use this function in from core
754
- protected removeWhere(array: Array<any>, predicate: (T: any) => boolean): Array<any> {
768
+ protected removeWhere<T>(array: Array<T>, predicate: (item: T) => boolean): Array<any> {
755
769
  const length = array.length;
756
770
  let updateIterator = 0;
757
771
  for (let deleteIterator = 0; deleteIterator < length; deleteIterator++) {
@@ -767,7 +781,7 @@ export class WebLogoViewer extends DG.JsViewer {
767
781
  /** Function for removing empty positions */
768
782
  protected _removeEmptyPositions() {
769
783
  if (this.skipEmptyPositions)
770
- this.removeWhere(this.positions, (item) => item?.freq['-']?.count === item.rowCount);
784
+ this.removeWhere(this.positions, (item) => item?.getFreq('-')?.count === item?.rowCount);
771
785
  }
772
786
 
773
787
  private renderRequested: boolean = false;
@@ -780,11 +794,11 @@ export class WebLogoViewer extends DG.JsViewer {
780
794
  `.render( recalcLevel=${recalcLevel}, reason='${reason}' )`);
781
795
 
782
796
  /** Calculate freqs of monomers */
783
- const calculateFreqsInt = (length: number): void => {
797
+ const calculateFreqsInt = (): void => {
784
798
  _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateFreqsInt(), start `);
785
-
786
799
  if (!this.host || !this.seqCol || !this.dataFrame) return;
787
800
 
801
+ const length: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
788
802
  this.unitsHandler = UnitsHandler.getOrCreate(this.seqCol);
789
803
  const posCount: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
790
804
  this.positions = new Array(posCount);
@@ -815,9 +829,10 @@ export class WebLogoViewer extends DG.JsViewer {
815
829
  };
816
830
 
817
831
  /** Calculate layout of monomers on screen (canvas) based on freqs, required to handle mouse events */
818
- const calculateLayoutInt = (length: number, dpr: number): void => {
832
+ const calculateLayoutInt = (dpr: number): void => {
819
833
  _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateLayoutInt(), start `);
820
834
 
835
+ const length = this.positions.length;
821
836
  this.calcSize();
822
837
  const absoluteMaxHeight = this.canvas.height - this.axisHeight * dpr;
823
838
  const alphabetSize = this.getAlphabetSize();
@@ -835,7 +850,8 @@ export class WebLogoViewer extends DG.JsViewer {
835
850
  *@param {boolean} recalcFreqs - indicates that need to recalculate data for rendering
836
851
  */
837
852
  const renderInt = (recalcLevel: RecalcLevel) => {
838
- _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ), start `);
853
+ _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ), ` +
854
+ `start `);
839
855
  if (this.msgHost) {
840
856
  if (this.seqCol && !this.cp) {
841
857
  this.msgHost!.innerText = `Unknown palette (column semType: '${this.seqCol.semType}').`;
@@ -854,11 +870,11 @@ export class WebLogoViewer extends DG.JsViewer {
854
870
 
855
871
  this.slider.root.style.width = `${this.host.clientWidth}px`;
856
872
 
857
- const length: number = this.Length;
858
873
  const dpr: number = window.devicePixelRatio;
859
- if (recalcLevel >= RecalcLevel.Freqs) calculateFreqsInt(length);
860
- if (recalcLevel >= RecalcLevel.Layout) calculateLayoutInt(length, window.devicePixelRatio);
874
+ if (recalcLevel >= RecalcLevel.Freqs) calculateFreqsInt();
875
+ if (recalcLevel >= RecalcLevel.Layout) calculateLayoutInt(window.devicePixelRatio);
861
876
 
877
+ const length: number = this.Length;
862
878
  g.resetTransform();
863
879
  g.fillStyle = DG.Color.toHtml(this.backgroundColor);
864
880
  g.fillRect(0, 0, this.canvas.width, this.canvas.height);
@@ -896,7 +912,7 @@ export class WebLogoViewer extends DG.JsViewer {
896
912
  this.positionWidthWithMargin, firstVisibleIndex, this.cp);
897
913
  }
898
914
 
899
- _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ), end `);
915
+ _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.renderInt( recalcLevel=${recalcLevel} ), end`);
900
916
  };
901
917
 
902
918
  this.recalcLevelRequested = Math.max(this.recalcLevelRequested, recalcLevel);
@@ -1039,7 +1055,8 @@ export class WebLogoViewer extends DG.JsViewer {
1039
1055
  _package.logger.debug('Bio: WebLogoViewer<${this.viewerId}>.dataFrameFilterChanged()');
1040
1056
  try {
1041
1057
  this.updatePositions();
1042
- this.render(RecalcLevel.Freqs, 'dataFrameFilterOnChanged');
1058
+ if (this.filterSource === FilterSources.Filtered)
1059
+ this.render(RecalcLevel.Freqs, 'dataFrameFilterOnChanged');
1043
1060
  } catch (err: any) {
1044
1061
  const errMsg = errorToConsole(err);
1045
1062
  _package.logger.error('Bio: WebLogoViewer<${this.viewerId}>.dataFrameFilterOnChanged() error:\n' + errMsg);
@@ -1050,7 +1067,8 @@ export class WebLogoViewer extends DG.JsViewer {
1050
1067
  private dataFrameSelectionOnChanged(_value: any): void {
1051
1068
  _package.logger.debug('Bio: WebLogoViewer<${this.viewerId}>.dataFrameSelectionOnChanged()');
1052
1069
  try {
1053
- this.render(RecalcLevel.Freqs, 'dataFrameSelectionOnChanged');
1070
+ if (this.filterSource === FilterSources.Selected)
1071
+ this.render(RecalcLevel.Freqs, 'dataFrameSelectionOnChanged');
1054
1072
  } catch (err: any) {
1055
1073
  const errMsg = errorToConsole(err);
1056
1074
  _package.logger.error('Bio: WebLogoViewer<${this.viewerId}>.dataFrameSelectionOnChanged() error:\n' + errMsg);
@@ -1132,10 +1150,6 @@ export class WebLogoViewer extends DG.JsViewer {
1132
1150
  export function checkSeqForMonomerAtPos(
1133
1151
  df: DG.DataFrame, unitsHandler: UnitsHandler, filter: DG.BitSet, rowI: number, monomer: string, at: PositionInfo,
1134
1152
  ): boolean {
1135
- // if (!filter.get(rowI)) return false;
1136
- // TODO: Use BitSet.get(idx)
1137
- if (!filter.getSelectedIndexes().includes(rowI)) return false;
1138
-
1139
1153
  const seqMList: string[] = unitsHandler.splitted[rowI];
1140
1154
  const seqM = at.pos < seqMList.length ? seqMList[at.pos] : null;
1141
1155
  return ((seqM === monomer) || (seqM === '' && monomer === '-'));