@datagrok/bio 2.15.7 → 2.15.9

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.
@@ -16,6 +16,7 @@ import {MonomerLibManager} from '../lib-manager';
16
16
  import {LIB_PATH} from '../consts';
17
17
 
18
18
  import '../../../../css/monomer-manager.css';
19
+ import { MONOMER_RENDERER_TAGS } from '@datagrok-libraries/bio/src/utils/cell-renderer';
19
20
 
20
21
  // columns of monomers dataframe, note that rgroups is hidden and will be displayed as separate columns
21
22
  export enum MONOMER_DF_COLUMN_NAMES {
@@ -50,6 +51,16 @@ export const MONOMER_DF_COLUMNS = {
50
51
 
51
52
 
52
53
  export class MonomerManager implements IMonomerManager {
54
+
55
+ private adjustColWidths() {
56
+ setTimeout(() => {
57
+ if (this.tv?.grid) {
58
+ this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.NAME)!.width = 100;
59
+ this.tv!.grid.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.width = 70;
60
+ }
61
+ }, 200);
62
+ }
63
+
53
64
  public static readonly VIEW_NAME = 'Manage Monomers';
54
65
  private _newMonomer: Monomer = DUMMY_MONOMER;
55
66
  private _newMonomerForm: MonomerForm;
@@ -63,7 +74,11 @@ export class MonomerManager implements IMonomerManager {
63
74
  this.monomerLib = monomerLibManamger.getBioLib();
64
75
  this._newMonomerForm = new MonomerForm(monomerLibManamger, () => this.activeMonomerLib, async () => {
65
76
  const df = await this.getMonomersDf(this.libInput.value!);
66
- this.tv?.dataFrame && (this.tv.dataFrame = df);
77
+ if (this.tv?.dataFrame) {
78
+ this.tv.dataFrame = df;
79
+ this.adjustColWidths();
80
+ }
81
+
67
82
  }, () => this.tv?.dataFrame);
68
83
  }
69
84
 
@@ -150,7 +165,7 @@ export class MonomerManager implements IMonomerManager {
150
165
  const df = await this.getMonomersDf(fileName);
151
166
  this.tv = DG.TableView.create(df, true);
152
167
  //const f = tv.filters();
153
- this.tv.grid.col(MONOMER_DF_COLUMN_NAMES.NAME)!.width = 100;
168
+ this.adjustColWidths();
154
169
  this.tv.subs.push(
155
170
  grok.events.onContextMenu.subscribe(({args}) => {
156
171
  if (!args || !args.menu || !args.context || args.context.type !== DG.VIEWER.GRID || !args.context.tableView ||
@@ -200,7 +215,7 @@ export class MonomerManager implements IMonomerManager {
200
215
  libName && (this.libInput.value = libName);
201
216
  const df = await this.getMonomersDf(libName);
202
217
  this.tv.dataFrame = df;
203
- this.tv.grid.col(MONOMER_DF_COLUMN_NAMES.NAME)!.width = 100;
218
+ this.adjustColWidths();
204
219
  return this.tv;
205
220
  }
206
221
 
@@ -262,6 +277,7 @@ export class MonomerManager implements IMonomerManager {
262
277
  try {
263
278
  const df = await this.getMonomersDf(this.libInput.value!);
264
279
  this.tv!.dataFrame = df;
280
+ this.adjustColWidths();
265
281
  } catch (e) {
266
282
  console.error(e);
267
283
  }
@@ -311,6 +327,8 @@ export class MonomerManager implements IMonomerManager {
311
327
  df.columns.addNew(rgroupName, DG.COLUMN_TYPE.STRING);
312
328
  }
313
329
  }
330
+ df.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.semType = 'Monomer';
331
+ df.col(MONOMER_DF_COLUMN_NAMES.SYMBOL)!.setTag(MONOMER_RENDERER_TAGS.applyToBackground, 'true');
314
332
 
315
333
 
316
334
  for (let i = 0; i < monomers.length; i++) {
@@ -470,6 +488,12 @@ class MonomerForm implements INewMonomerForm {
470
488
  monomerNaturalAnalogInput: DG.InputBase<string | null>;
471
489
  rgroupsGrid: ItemsGrid;
472
490
  metaGrid: ItemsGrid;
491
+ colors: {
492
+ line: string,
493
+ background: string,
494
+ text: string
495
+ };
496
+ colorsEditor: ColorsEditor;
473
497
  saveButton: HTMLButtonElement;
474
498
  rgroupsGridRoot: HTMLElement;
475
499
  private _molChanged: boolean = false;
@@ -480,6 +504,12 @@ class MonomerForm implements INewMonomerForm {
480
504
  private getMonomerLib: () => IMonomerLib | null, private refreshTable: () => Promise<void>,
481
505
  private getMonomersDataFrame: () => DG.DataFrame | undefined) {
482
506
  const monomerTypes = ['PEPTIDE', 'RNA', 'CHEM', 'BLOB', 'G'];
507
+ this.colors = {
508
+ line: '#000000',
509
+ background: '#000000',
510
+ text: '#000000',
511
+ };
512
+ this.colorsEditor = new ColorsEditor(this.colors);
483
513
  this.molSketcher = new DG.chem.Sketcher();
484
514
  this.molSketcher.root.classList.add('monomer-manager-sketcher');
485
515
  this.polymerTypeInput = ui.input.choice('Polymer Type', {value: 'PEPTIDE', items: monomerTypes,
@@ -603,13 +633,12 @@ class MonomerForm implements INewMonomerForm {
603
633
  this.monomerIdInput.value = monomer.id;
604
634
  this.monomerNaturalAnalogInput.value = monomer.naturalAnalog ?? null;
605
635
  this.rgroupsGrid.items = resolveRGroupInfo(monomer.rgroups);
606
- this.metaGrid.items = Object.entries(monomer.meta ?? {}).map(([k, v]) => {
636
+ this.metaGrid.items = Object.entries(monomer.meta ?? {}).filter(([k, v]) => k?.toLowerCase() !== 'colors').map(([k, v]) => {
607
637
  return {Property: k, Value: v};
608
638
  });
609
639
  this.rgroupsGrid.render();
610
640
  this.metaGrid.render();
611
641
  this.rgroupsGridRoot.style.display = 'flex';
612
-
613
642
  this.onMonomerInputChanged();
614
643
  if (!monomer.naturalAnalog) {
615
644
  mostSimilarNaturalAnalog(capSmiles(monomer.smiles, this.rgroupsGrid.items as RGroup[]), monomer.polymerType).then((mostSimilar) => {
@@ -617,6 +646,19 @@ class MonomerForm implements INewMonomerForm {
617
646
  this.monomerNaturalAnalogInput.value = mostSimilar;
618
647
  });
619
648
  }
649
+ const colorsString = monomer.meta?.colors ?? '';
650
+ let colorsObj: Partial<typeof this.colors> = {};
651
+ try {
652
+ colorsObj = colorsString ? JSON.parse(colorsString)?.default : {};
653
+ } catch (e) {
654
+ console.error(e);
655
+ }
656
+
657
+ this.colorsEditor.colors = {
658
+ line: colorsObj.line ?? '#000000',
659
+ background: colorsObj.background ?? '#000000',
660
+ text: colorsObj.text ?? '#000000',
661
+ };
620
662
  }
621
663
 
622
664
  validateInputs(): string | null | undefined {
@@ -656,6 +698,7 @@ class MonomerForm implements INewMonomerForm {
656
698
  'Monomer': mainInputsDiv,
657
699
  'R-groups': this.rgroupsGridRoot,
658
700
  'Meta': ui.divV([this.metaGrid.root]),
701
+ 'Colors': this.colorsEditor.form,
659
702
  }, false);
660
703
  inputsPanel.header.style.marginBottom = '10px';
661
704
  const saveB = ui.buttonsInput([this.saveButton]);
@@ -802,6 +845,13 @@ class MonomerForm implements INewMonomerForm {
802
845
  this.metaGrid.items.filter((item) => (!!item['Property']) && (!!item['Value'])).forEach((item) => {
803
846
  meta[item['Property']] = item['Value'];
804
847
  });
848
+ const addingItem = this.metaGrid.addingItem;
849
+ if (addingItem && addingItem['Property'] && addingItem['Value']) {
850
+ meta[addingItem['Property']] = addingItem['Value'];
851
+ }
852
+ //console.log(this.metaGrid.addingItem);
853
+ if (this.colorsEditor.colors.line !== '#000000' || this.colorsEditor.colors.background !== '#000000' || this.colorsEditor.colors.text !== '#000000')
854
+ meta.colors = {default: this.colorsEditor.colors};
805
855
  const monomer: Monomer = {
806
856
  symbol: this.monomerSymbolInput.value,
807
857
  name: this.monomerNameInput.value,
@@ -929,3 +979,42 @@ function monomerFromDfRow(dfRow: DG.Row): Monomer {
929
979
  createDate: dfRow.get(MONOMER_DF_COLUMN_NAMES.CREATE_DATE),
930
980
  };
931
981
  }
982
+
983
+ class ColorsEditor {
984
+ private _colors: { line: string, background: string, text: string };
985
+ private _colorInputs: { [key in keyof ColorsEditor['_colors']]: DG.InputBase<string> };
986
+ constructor(colors: { line: string, background: string, text: string }) {
987
+ this._colors = colors;
988
+ this._colorInputs = {
989
+ line: ui.input.color('Line', {value: colors.line, onValueChanged: (v) => this._colors.line = v}),
990
+ background: ui.input.color('Background', {value: colors.background, onValueChanged: (v) => this._colors.background = v}),
991
+ text: ui.input.color('Text', {value: colors.text, onValueChanged: (v) => this._colors.text = v}),
992
+ };
993
+ }
994
+
995
+ get colors() {
996
+ return this._colors;
997
+ }
998
+
999
+ set colors(cols: { line: string, background: string, text: string }) {
1000
+ //need to convert to hex as the input accepts only hex
1001
+ const colsHex = {
1002
+ line: DG.Color.toHtml(DG.Color.fromHtml(cols.line ?? '#000000')),
1003
+ background: DG.Color.toHtml(DG.Color.fromHtml(cols.background ?? '#000000')),
1004
+ text: DG.Color.toHtml(DG.Color.fromHtml(cols.text ?? '#000000')),
1005
+ };
1006
+
1007
+ this._colors = colsHex;
1008
+ for (const key in this._colorInputs) {
1009
+ this._colorInputs[key as keyof ColorsEditor['_colors']].value = colsHex[key as keyof ColorsEditor['_colors']];
1010
+ }
1011
+ }
1012
+
1013
+ get colorsMetaFormat() {
1014
+ return {colors: {default: this._colors}};
1015
+ }
1016
+
1017
+ get form() {
1018
+ return ui.form(Object.values(this._colorInputs));
1019
+ }
1020
+ }
@@ -3,7 +3,7 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {
6
- HelmType, PolymerType, MonomerType,
6
+ PolymerType, MonomerType,
7
7
  IWebEditorMonomer, WebEditorRGroups
8
8
  } from '@datagrok-libraries/bio/src/helm/types';
9
9
 
@@ -85,8 +85,9 @@ export class GapWebEditorMonomer extends WebEditorMonomerDummy {
85
85
  public readonly linecolor: string = '#808080';
86
86
  public readonly textcolor: string = '#808080';
87
87
 
88
- constructor(biotype: string, id: string) {
89
- super(biotype, id, 'gap');
88
+ constructor(biotype: string) {
89
+ // monomer symbol is used to build pseudo molfile for Helm, symbol can not be empty for molfile
90
+ super(biotype, '*', 'gap');
90
91
  }
91
92
  }
92
93
 
@@ -105,8 +106,14 @@ export class MissingWebEditorMonomer extends WebEditorMonomerDummy {
105
106
  public readonly linecolor: string = '#800000';
106
107
  public readonly textcolor: string = '#FFFFFF';
107
108
 
108
- constructor(biotype: string, id: string) {
109
+ constructor(biotype: string, id: string, isLibEmpty: boolean) {
109
110
  super(biotype, id, 'missing');
111
+
112
+ if (isLibEmpty) {
113
+ this.backgroundcolor = '#C0C0C0';
114
+ this.linecolor = '#404040';
115
+ this.textcolor = '#404040';
116
+ }
110
117
  }
111
118
  }
112
119
 
@@ -2,12 +2,11 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {HelmType, IMonomerColors, IWebEditorMonomer, MonomerType, PolymerType, WebEditorRGroups} from '@datagrok-libraries/bio/src/helm/types';
5
+ import {HelmType, IWebEditorMonomer, MonomerType, PolymerType, WebEditorRGroups} from '@datagrok-libraries/bio/src/helm/types';
6
6
  import {IMonomerLibBase, Monomer} from '@datagrok-libraries/bio/src/types/index';
7
7
  import {HELM_OPTIONAL_FIELDS as OPT, HELM_REQUIRED_FIELD as REQ, HELM_RGROUP_FIELDS as RGP} from '@datagrok-libraries/bio/src/utils/const';
8
8
 
9
- import {BrokenWebEditorMonomer, MissingWebEditorMonomer} from './web-editor-monomer-dummy';
10
- import {naturalMonomerColors} from './monomer-colors';
9
+ import {BrokenWebEditorMonomer} from './web-editor-monomer-dummy';
11
10
 
12
11
  export class LibraryWebEditorMonomer implements IWebEditorMonomer {
13
12
  public get rs(): number { return Object.keys(this.at).length; }
@@ -43,7 +42,8 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
43
42
  at = monomerLib.getRS(smiles);
44
43
  } else if (!monomer.lib) {
45
44
  // missing
46
- return new MissingWebEditorMonomer(biotype, symbol);
45
+ throw new Error('Unexpected missing monomer without .lib');
46
+ // return new MissingWebEditorMonomer(biotype, symbol);
47
47
  } else {
48
48
  // broken
49
49
  return new BrokenWebEditorMonomer(biotype, symbol);
@@ -58,7 +58,7 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
58
58
  monomer[REQ.MONOMER_TYPE],
59
59
  at);
60
60
 
61
- const colors = getMonomerColors(biotype, monomer);
61
+ const colors = monomerLib.getMonomerColors(biotype, monomer[REQ.SYMBOL]);
62
62
  if (colors) {
63
63
  res.textcolor = colors?.textcolor;
64
64
  res.linecolor = colors?.linecolor;
@@ -68,35 +68,3 @@ export class LibraryWebEditorMonomer implements IWebEditorMonomer {
68
68
  return res;
69
69
  }
70
70
  }
71
-
72
- function getMonomerColors(biotype: HelmType, monomer: Monomer): IMonomerColors | null {
73
- const currentMonomerSchema = 'default';
74
- let monomerSchema: string = currentMonomerSchema;
75
-
76
- let res: any;
77
- if (monomer.meta && monomer.meta.colors) {
78
- const monomerColors: { [colorSchemaName: string]: any } = monomer.meta.colors;
79
- if (!(currentMonomerSchema in monomerColors)) monomerSchema = 'default';
80
- let res = monomerColors[monomerSchema];
81
- }
82
-
83
- if (!res) {
84
- const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
85
- const nColor = biotypeColors?.[monomer.symbol];
86
- if (nColor)
87
- res = {textColor: "#000000", lineColor: "#000000", backgroundColor: nColor};
88
- }
89
-
90
- const na = monomer[OPT.NATURAL_ANALOG];
91
- if (!res && na) {
92
- const biotypeColors: { [symbol: string]: string } | undefined = naturalMonomerColors[biotype];
93
- const naColor = biotypeColors?.[na];
94
- res = {textColor: "#000000", lineColor: "#000000", backgroundColor: naColor ?? "#FFFFFF",};
95
- }
96
-
97
- return !res ? null : {
98
- textcolor: res.text ?? res.textColor,
99
- linecolor: res.line ?? res.lineColor,
100
- backgroundcolor: res.background ?? res.backgroundColor
101
- } as IMonomerColors;
102
- }
@@ -7,9 +7,8 @@ import wu from 'wu';
7
7
  import {fromEvent, Observable, Subject, Unsubscribable} from 'rxjs';
8
8
 
9
9
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
10
- import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
11
10
  import {
12
- monomerToShort, pickUpPalette, pickUpSeqCol, TAGS as bioTAGS, positionSeparator
11
+ monomerToShort, pickUpSeqCol, TAGS as bioTAGS, positionSeparator, ALPHABET
13
12
  } from '@datagrok-libraries/bio/src/utils/macromolecule';
14
13
  import {
15
14
  FilterSources, HorizontalAlignments, IWebLogoViewer, PositionHeight, PositionMarginStates,
@@ -21,11 +20,14 @@ import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/type
21
20
  import {testEvent} from '@datagrok-libraries/utils/src/test';
22
21
  import {PromiseSyncer} from '@datagrok-libraries/bio/src/utils/syncer';
23
22
  import {GAP_SYMBOL} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
23
+ import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types/index';
24
+ import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
25
+ import {undefinedColor} from '@datagrok-libraries/bio/src/utils/cell-renderer-monomer-placer';
24
26
 
25
27
  import {AggFunc, getAgg} from '../utils/agg';
26
28
  import {buildCompositionTable} from '../widgets/composition-analysis-widget';
27
29
 
28
- import {_package} from '../package';
30
+ import {_package, getMonomerLibHelper} from '../package';
29
31
 
30
32
  declare global {
31
33
  interface HTMLCanvasElement {
@@ -199,7 +201,8 @@ export class PositionInfo {
199
201
  }
200
202
 
201
203
  render(g: CanvasRenderingContext2D,
202
- fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number, cp: SeqPalette
204
+ fontStyle: string, uppercaseLetterAscent: number, uppercaseLetterHeight: number,
205
+ biotype: HelmType, monomerLib: IMonomerLibBase | null
203
206
  ) {
204
207
  for (const [monomer, pmInfo] of Object.entries(this._freqs)) {
205
208
  if (monomer !== GAP_SYMBOL) {
@@ -207,11 +210,17 @@ export class PositionInfo {
207
210
  const b = pmInfo.bounds!;
208
211
  const left = b.left;
209
212
 
213
+ let color: string = undefinedColor;
214
+ if (monomerLib) {
215
+ const wem = monomerLib.getWebEditorMonomer(biotype, monomer)!;
216
+ color = wem.backgroundcolor!;
217
+ }
218
+
210
219
  g.resetTransform();
211
220
  g.strokeStyle = 'lightgray';
212
221
  g.lineWidth = 1;
213
222
  g.rect(left, b.top, b.width, b.height);
214
- g.fillStyle = cp.get(monomer) ?? cp.get('other');
223
+ g.fillStyle = color;
215
224
  g.textAlign = 'left';
216
225
  g.font = fontStyle;
217
226
  //g.fillRect(b.left, b.top, b.width, b.height);
@@ -233,13 +242,13 @@ export class PositionInfo {
233
242
  return !!findRes ? findRes[0] : undefined;
234
243
  }
235
244
 
236
- buildCompositionTable(palette: SeqPalette): HTMLTableElement {
245
+ buildCompositionTable(biotype: HelmType, monomerLib: IMonomerLibBase): HTMLTableElement {
237
246
  if ('-' in this._freqs)
238
247
  throw new Error(`Unexpected monomer symbol '-'.`);
239
- return buildCompositionTable(palette,
248
+ return buildCompositionTable(
240
249
  Object.assign({}, ...Object.entries(this._freqs)
241
- .map(([m, pmi]) => ({[m]: pmi.rowCount})))
242
- );
250
+ .map(([m, pmi]) => ({[m]: pmi.rowCount}))),
251
+ biotype, monomerLib);
243
252
  }
244
253
  }
245
254
 
@@ -304,9 +313,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
304
313
  private seqHandler: SeqHandler | null;
305
314
  private initialized: boolean = false;
306
315
 
307
- // private readonly colorScheme: ColorScheme = ColorSchemes[NucleotidesWebLogo.residuesSet];
308
- protected palette: SeqPalette | null = null;
309
-
316
+ private monomerLib: IMonomerLibBase | null = null;
310
317
  private host?: HTMLDivElement;
311
318
  private msgHost?: HTMLElement;
312
319
  private canvas: HTMLCanvasElement;
@@ -446,6 +453,14 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
446
453
  this.canvas.classList.value = 'bio-wl-canvas';
447
454
  this.canvas.style.width = '100%';
448
455
 
456
+ getMonomerLibHelper().then((libHelper) => {
457
+ this.monomerLib = libHelper.getMonomerLib();
458
+ this.render(WlRenderLevel.Render, 'monomerLib');
459
+ this.subs.push(this.monomerLib.onChanged.subscribe(() => {
460
+ this.render(WlRenderLevel.Render, 'monomerLib changed');
461
+ }));
462
+ });
463
+
449
464
  /* this.root.style.background = '#FFEEDD'; */
450
465
  this.viewSyncer = new PromiseSyncer(_package.logger);
451
466
  }
@@ -583,7 +598,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
583
598
  // Set valueColumnNameProp.choices has no effect
584
599
  }
585
600
 
586
- /** Assigns {@link seqCol} and {@link palette} based on {@link sequenceColumnName} and calls {@link render}(). */
601
+ /** Assigns {@link seqCol} based on {@link sequenceColumnName} and calls {@link render}(). */
587
602
  private updateSeqCol(): void {
588
603
  if (this.dataFrame) {
589
604
  this.seqCol = this.sequenceColumnName ? this.dataFrame.col(this.sequenceColumnName) : null;
@@ -595,7 +610,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
595
610
  try {
596
611
  this.seqHandler = SeqHandler.forColumn(this.seqCol);
597
612
 
598
- this.palette = pickUpPalette(this.seqCol);
599
613
  this.render(WlRenderLevel.Freqs, 'updateSeqCol()');
600
614
  this.error = null;
601
615
  } catch (err: any) {
@@ -609,7 +623,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
609
623
  this.positionLabels = [];
610
624
  this.startPosition = -1;
611
625
  this.endPosition = -1;
612
- this.palette = null;
613
626
  }
614
627
  }
615
628
  }
@@ -1085,15 +1098,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1085
1098
  this._onLayoutCalculated.next();
1086
1099
  };
1087
1100
 
1088
- if (this.msgHost) {
1089
- if (this.seqCol && !this.palette) {
1090
- this.msgHost!.innerText = `Unknown palette (column semType: '${this.seqCol.semType}').`;
1091
- this.msgHost!.style.display = '';
1092
- } else
1093
- this.msgHost!.style.display = 'none';
1094
- }
1101
+ if (this.msgHost)
1102
+ this.msgHost!.style.display = 'none';
1095
1103
 
1096
- if (!this.seqCol || !this.dataFrame || !this.palette || this.host == null || this.slider == null)
1104
+ if (!this.seqCol || !this.dataFrame || this.host == null || this.slider == null)
1097
1105
  return;
1098
1106
 
1099
1107
  const dpr: number = window.devicePixelRatio;
@@ -1133,8 +1141,10 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1133
1141
  // Hacks to scale uppercase characters to target rectangle
1134
1142
  const uppercaseLetterAscent = 0.25;
1135
1143
  const uppercaseLetterHeight = 12.2;
1144
+ const sh = SeqHandler.forColumn(this.seqCol);
1145
+ const biotype = sh.defaultBiotype;
1136
1146
  for (let jPos = firstPos; jPos <= lastPos; jPos++)
1137
- this.positions[jPos].render(g, fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, this.palette);
1147
+ this.positions[jPos].render(g, fontStyle, uppercaseLetterAscent, uppercaseLetterHeight, biotype, this.monomerLib);
1138
1148
  } finally {
1139
1149
  g.restore();
1140
1150
  }
@@ -1216,12 +1226,15 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1216
1226
  const [pi, monomer] = this.getMonomer(cursorP, dpr);
1217
1227
  const positionLabelHeight = this.showPositionLabels ? POSITION_LABELS_HEIGHT * dpr : 0;
1218
1228
 
1219
- if (pi !== null && monomer === null && 0 <= cursorP.y && cursorP.y <= positionLabelHeight) {
1229
+ if (pi !== null && monomer === null && 0 <= cursorP.y && cursorP.y <= positionLabelHeight && this.monomerLib) {
1220
1230
  // Position tooltip
1221
1231
 
1222
1232
  const tooltipRows = [ui.divText(`Position ${pi.label}`)];
1223
- if (this.valueAggrType === DG.AGG.TOTAL_COUNT)
1224
- tooltipRows.push(pi.buildCompositionTable(this.palette!));
1233
+ if (this.valueAggrType === DG.AGG.TOTAL_COUNT) {
1234
+ const sh = SeqHandler.forColumn(this.seqCol!);
1235
+ const biotype = sh.defaultBiotype;
1236
+ tooltipRows.push(pi.buildCompositionTable(biotype, this.monomerLib));
1237
+ }
1225
1238
  const tooltipEl = ui.divV(tooltipRows);
1226
1239
  ui.tooltip.show(tooltipEl, args.x + 16, args.y + 16);
1227
1240
  } else if (pi !== null && monomer && this.dataFrame && this.seqCol && this.seqHandler) {
@@ -7,27 +7,19 @@ import wu from 'wu';
7
7
  import {TAGS as bioTAGS, ALPHABET, getPaletteByType} from '@datagrok-libraries/bio/src/utils/macromolecule';
8
8
  import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
9
9
  import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
10
- import '../../css/composition-analysis.css';
11
10
  import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler';
12
11
  import {GAP_SYMBOL} from '@datagrok-libraries/bio/src/utils/macromolecule/consts';
12
+ import {IMonomerLibBase} from '@datagrok-libraries/bio/src/types';
13
+ import {HelmType} from '@datagrok-libraries/bio/src/helm/types';
14
+ import {HelmTypes} from '@datagrok-libraries/bio/src/helm/consts';
13
15
 
16
+ import '../../css/composition-analysis.css';
14
17
 
15
- export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
18
+ export function getCompositionAnalysisWidget(val: DG.SemanticValue, monomerLib: IMonomerLibBase): DG.Widget {
16
19
  const host = ui.div();
17
20
  host.classList.add('macromolecule-cell-comp-analysis-host');
18
21
  const alphabet = val.cell.column.tags[bioTAGS.alphabet];
19
- let palette: SeqPalette = UnknownSeqPalettes.Color;
20
- switch (alphabet) {
21
- case ALPHABET.DNA:
22
- case ALPHABET.RNA:
23
- palette = getPaletteByType(ALPHABET.DNA);
24
- break;
25
- case ALPHABET.PT:
26
- palette = getPaletteByType(ALPHABET.PT);
27
- break;
28
- default:
29
- break;
30
- }
22
+ const biotype = alphabet === ALPHABET.DNA || alphabet === ALPHABET.RNA ? HelmTypes.NUCLEOTIDE : HelmTypes.AA;
31
23
 
32
24
  const counts: { [m: string]: number } = {};
33
25
  const sh = SeqHandler.forColumn(val.cell.column as DG.Column<string>);
@@ -38,7 +30,7 @@ export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
38
30
  const count = counts[cm] || 0;
39
31
  counts[cm] = count + 1;
40
32
  });
41
- const table = buildCompositionTable(palette, counts);
33
+ const table = buildCompositionTable(counts, biotype, monomerLib);
42
34
  Array.from(table.rows).forEach((row) => {
43
35
  const barCol = (row.getElementsByClassName('macromolecule-cell-comp-analysis-bar')[0] as HTMLDivElement)
44
36
  .style.backgroundColor;
@@ -49,7 +41,9 @@ export function getCompositionAnalysisWidget(val: DG.SemanticValue): DG.Widget {
49
41
  return new DG.Widget(host);
50
42
  }
51
43
 
52
- export function buildCompositionTable(palette: SeqPalette, counts: { [m: string]: number }): HTMLTableElement {
44
+ export function buildCompositionTable(
45
+ counts: { [m: string]: number }, biotype: HelmType, monomerLib: IMonomerLibBase
46
+ ): HTMLTableElement {
53
47
  let sumValue: number = 0;
54
48
  let maxValue: number | null = null;
55
49
  for (const value of Object.values(counts)) {
@@ -61,7 +55,8 @@ export function buildCompositionTable(palette: SeqPalette, counts: { [m: string]
61
55
  .sort((a, b) => b[1] - a[1])
62
56
  .map(([cm, value]) => {
63
57
  const ratio = value / sumValue;
64
- const color = palette.get(cm);
58
+ const wem = monomerLib.getWebEditorMonomer(biotype, cm)!;
59
+ const color = wem.backgroundcolor!;
65
60
  const barDiv = ui.div('', {classes: 'macromolecule-cell-comp-analysis-bar'});
66
61
  barDiv.style.width = `${50 * ratio / maxRatio}px`;
67
62
  barDiv.style.backgroundColor = color;