@datagrok/peptides 1.21.5 → 1.21.7

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/peptides",
3
3
  "friendlyName": "Peptides",
4
- "version": "1.21.5",
4
+ "version": "1.21.7",
5
5
  "author": {
6
6
  "name": "Davit Rizhinashvili",
7
7
  "email": "drizhinashvili@datagrok.ai"
package/src/demo/fasta.ts CHANGED
@@ -7,15 +7,33 @@ import * as C from '../utils/constants';
7
7
  import {scaleActivity} from '../utils/misc';
8
8
  import {ALIGNMENT, ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
9
9
  import {PeptidesModel} from '../model';
10
- import {delay} from '@datagrok-libraries/utils/src/test';
10
+ import {awaitCheck, delay} from '@datagrok-libraries/utils/src/test';
11
+ import {MCLSettings} from '../utils/types';
11
12
 
12
13
  export async function macromoleculeSarFastaDemoUI(): Promise<void> {
13
14
  grok.shell.windows.showContextPanel = true;
14
15
  const alignedSequenceCol = 'AlignedSequence';
15
16
  const simpleActivityColName = 'IC50';
16
17
  const simpleTable = DG.DataFrame.fromCsv(await _package.files.readAsText('aligned.csv'));
17
- grok.shell.addTableView(simpleTable);
18
- await delay(0);
18
+ simpleTable.name = 'Simple peptides';
19
+ if (!simpleTable.id)
20
+ simpleTable.id = `Simple-peptides-analysis-table-id-${Math.random()}-${Date.now()}`;
21
+ const view = grok.shell.addTableView(simpleTable);
22
+ await delay(50);
23
+ const grid = view.grid;
24
+ await new Promise((res) => {
25
+ let timer: any = null;
26
+ const sub = grid?.onAfterDrawContent?.subscribe(() => {
27
+ if (timer)
28
+ clearTimeout(timer);
29
+ sub.unsubscribe();
30
+ res(undefined);
31
+ });
32
+ timer = setTimeout(() => {
33
+ sub.unsubscribe();
34
+ res(undefined);
35
+ }, 3000);
36
+ });
19
37
  const simpleActivityCol = simpleTable.getCol(simpleActivityColName);
20
38
  const simpleAlignedSeqCol = simpleTable.getCol(alignedSequenceCol);
21
39
  simpleAlignedSeqCol.semType = DG.SEMTYPE.MACROMOLECULE;
@@ -28,7 +46,9 @@ export async function macromoleculeSarFastaDemoUI(): Promise<void> {
28
46
  sequenceCol: simpleAlignedSeqCol,
29
47
  clustersCol,
30
48
  });
49
+ const mclSettings = new MCLSettings();
50
+ mclSettings.threshold = 94;
31
51
  const model: PeptidesModel = await startAnalysis(simpleActivityCol, alignedCol, null, simpleTable, simpleScaledCol,
32
- C.SCALING_METHODS.MINUS_LG) as PeptidesModel;
33
- model.modifyWebLogoSelection({monomerOrCluster: 'D', positionOrClusterType: '13'});
52
+ C.SCALING_METHODS.MINUS_LG, {addMCL: true, useEmbeddingsClusters: true, mclSettings: mclSettings}) as PeptidesModel;
53
+ // model.modifyWebLogoSelection({monomerOrCluster: 'D', positionOrClusterType: '13'});
34
54
  }
package/src/model.ts CHANGED
@@ -146,11 +146,15 @@ export class PeptidesModel {
146
146
  get analysisView(): DG.TableView {
147
147
  if (this._analysisView === undefined) {
148
148
  this._analysisView = this.id ? wu(grok.shell.tableViews).find(({dataFrame}) => dataFrame?.getTag(DG.TAGS.ID) === this.id) : undefined;
149
- if (typeof this._analysisView === 'undefined')
149
+
150
+ if (!this._analysisView && (grok.shell.v as any)?.preview instanceof DG.TableView && (grok.shell.v as any)?.preview?.dataFrame === this.df)
151
+ this._analysisView = (grok.shell.v as any)!.preview as any as DG.TableView;
152
+
153
+ if (!this._analysisView)
150
154
  this._analysisView = grok.shell.addTableView(this.df);
151
155
  }
152
156
 
153
- if (this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1' && !this._layoutEventInitialized)
157
+ if (this.df.getTag(C.TAGS.MULTIPLE_VIEWS) !== '1' && !this._layoutEventInitialized && !grok.shell.isInDemo)
154
158
  grok.shell.v = this._analysisView;
155
159
 
156
160
 
@@ -369,8 +373,37 @@ export class PeptidesModel {
369
373
  // this is important bit. settings are written by startAnalysis function or other viewers, but separate viewers will not init the peptides model
370
374
  if (settings)
371
375
  model.init(settings);
376
+
377
+ let wasEmptySelectionBefore = true;
378
+ model.subs.push(DG.debounce(dataFrame.onSelectionChanged, 10).subscribe((_) => {
379
+ //clear all selections if user cleared the selection.
380
+ if (wasEmptySelectionBefore || dataFrame.selection.anyTrue || !model._analysisView || !document.contains(model._analysisView.root) ||
381
+ model._analysisView.dataFrame !== dataFrame || !model.positionColumns) {
382
+ wasEmptySelectionBefore = !dataFrame?.selection?.anyTrue;
383
+ return;
384
+ }
385
+ model.webLogoSelection = initSelection(model.positionColumns!);
386
+ const mpViewer = model.findViewer(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP) as MonomerPosition | null;
387
+ if (mpViewer != null) {
388
+ mpViewer.invariantMapSelection = initSelection(model.positionColumns!);
389
+ mpViewer.mutationCliffsSelection = initSelection(model.positionColumns!);
390
+ }
391
+ const mprViewer = model.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
392
+ if (mprViewer != null)
393
+ mprViewer.mutationCliffsSelection = initSelection(model.positionColumns!);
394
+ const lstViewer = model.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
395
+ if (lstViewer != null) {
396
+ lstViewer.initClusterSelection({notify: true});
397
+ lstViewer.render();
398
+ }
399
+ wasEmptySelectionBefore = true;
400
+ //model.fireBitsetChanged();
401
+ }));
372
402
  }
373
- return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
403
+
404
+ const model = dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
405
+
406
+ return model;
374
407
  }
375
408
 
376
409
  /**
@@ -403,7 +436,7 @@ export class PeptidesModel {
403
436
  * @return {DG.Accordion | null} - Accordion with analysis info based on current selection
404
437
  */
405
438
  createAccordion(): DG.Accordion | null {
406
- const trueModel: PeptidesModel | undefined = grok.shell.t?.temp[PeptidesModel.modelName];
439
+ const trueModel: PeptidesModel | undefined = this.df?.temp[PeptidesModel.modelName];
407
440
  if (!trueModel)
408
441
  return null;
409
442
 
@@ -428,41 +461,43 @@ export class PeptidesModel {
428
461
  const selectedClusters: string = (trueLSTViewer === null ? [] :
429
462
  trueLSTViewer.clusterSelection[CLUSTER_TYPE.ORIGINAL].concat(trueLSTViewer.clusterSelection[CLUSTER_TYPE.CUSTOM]))
430
463
  .join(', ');
431
- if (selectedClusters.length !== 0) {
432
- selectionDescription.push(ui.h1('Logo summary table selection'));
433
- selectionDescription.push(ui.divText(`Selected clusters: ${selectedClusters}`));
464
+ const htmlTextEl = (t: string): HTMLElement => {
465
+ const el = ui.divText(t);
466
+ el.innerHTML = t;
467
+ return el;
434
468
  }
469
+ if (selectedClusters.length !== 0)
470
+ selectionDescription.push(htmlTextEl(`<b>Logo Summary Table</b> clusters: ${selectedClusters}`));
435
471
 
436
472
  // Monomer-Position viewer selection overview
437
473
  const trueMPViewer = trueModel.findViewer(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP) as MonomerPosition | null;
438
474
  const selectedMonomerPositions = getSelectionString(trueMPViewer?.invariantMapSelection ?? {});
439
475
  const selectedMutationCliffs = getSelectionString(trueMPViewer?.mutationCliffsSelection ?? {});
440
- if (selectedMonomerPositions.length !== 0 || selectedMutationCliffs.length !== 0)
441
- selectionDescription.push(ui.h1('Sequence Variabily Map viewer selection'));
476
+ // if (selectedMonomerPositions.length !== 0 || selectedMutationCliffs.length !== 0)
477
+ // selectionDescription.push(ui.h1('Sequence Variabily Map viewer selection'));
442
478
 
443
479
 
444
480
  if (selectedMonomerPositions.length !== 0)
445
- selectionDescription.push(ui.divText(`Selected monomer-positions: ${selectedMonomerPositions}`));
481
+ selectionDescription.push(htmlTextEl(`<b>Invariant map</b>: ${selectedMonomerPositions}`));
446
482
 
447
483
 
448
484
  if (selectedMutationCliffs.length !== 0)
449
- selectionDescription.push(ui.divText(`Selected mutation cliffs pairs: ${selectedMutationCliffs}`));
485
+ selectionDescription.push(htmlTextEl(`<b>Mutation cliffs</b>: ${selectedMutationCliffs}`));
450
486
 
451
487
 
452
488
  // Most Potent Residues viewer selection overview
453
489
  const trueMPRViewer = trueModel.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
454
490
  const selectedMPRMonomerPositions = getSelectionString(trueMPRViewer?.mutationCliffsSelection ?? {});
455
- if (selectedMPRMonomerPositions.length !== 0) {
456
- selectionDescription.push(ui.h1('Most Potent Residues viewer selection'));
457
- selectionDescription.push(ui.divText(`Selected monomer-positions: ${selectedMPRMonomerPositions}`));
458
- }
491
+ if (selectedMPRMonomerPositions.length !== 0)
492
+ selectionDescription.push(htmlTextEl(`<b>Most potent residues</b>: ${selectedMPRMonomerPositions}`));
459
493
 
460
494
  // WebLogo selection overview
461
495
  const selectedMonomers = getSelectionString(trueModel.webLogoSelection);
462
- if (selectedMonomers.length !== 0) {
463
- selectionDescription.push(ui.h1('WebLogo selection'));
464
- selectionDescription.push(ui.divText(`Selected monomers: ${selectedMonomers}`));
465
- }
496
+ if (selectedMonomers.length !== 0)
497
+ selectionDescription.push(htmlTextEl(`<b>WebLogo</b>: ${selectedMonomers}`));
498
+
499
+ if (selectionDescription.length !== 0)
500
+ selectionDescription.unshift(ui.h1('Selection Sources'));
466
501
 
467
502
  const descritionsHost = ui.div(ui.divV(selectionDescription));
468
503
  acc.addTitle(ui.divV([
@@ -836,15 +871,23 @@ export class PeptidesModel {
836
871
 
837
872
  const selection = this.df.selection;
838
873
  const filter = this.df.filter;
839
-
874
+ let prevTimer: any = null;
840
875
  const showAccordion = (): void => {
841
876
  try {
877
+ if (prevTimer != null) {
878
+ clearTimeout(prevTimer);
879
+ prevTimer = null;
880
+ }
842
881
  const acc = this.createAccordion();
843
882
  if (acc === null)
844
883
  return;
845
884
 
846
-
885
+ // these shinanigans are needed to prevent frozen custom accordion from sticking
847
886
  grok.shell.o = acc.root;
887
+ prevTimer = setTimeout(() => {
888
+ if (grok.shell.o != acc.root)
889
+ grok.shell.o = acc.root;
890
+ }, 1500);
848
891
  } catch (e) {
849
892
  console.error(e);
850
893
  }
@@ -1134,6 +1177,8 @@ export class PeptidesModel {
1134
1177
  };
1135
1178
  const logoSummaryTable = await this.df.plot
1136
1179
  .fromType(VIEWER_TYPE.LOGO_SUMMARY_TABLE, viewerProperties) as LogoSummaryTable;
1180
+ if (grok.shell.isInDemo)
1181
+ this.analysisView.addViewer(logoSummaryTable);
1137
1182
  this.analysisView.dockManager.dock(logoSummaryTable, DG.DOCK_TYPE.RIGHT, null, VIEWER_TYPE.LOGO_SUMMARY_TABLE);
1138
1183
 
1139
1184
  logoSummaryTable.viewerGrid.invalidate();
@@ -1173,6 +1218,9 @@ export class PeptidesModel {
1173
1218
  };
1174
1219
  const monomerPosition = await this.df.plot
1175
1220
  .fromType(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP, viewerProperties) as MonomerPosition;
1221
+ // for browse mode, we need to add viewer to the analysis view before docking. remove with release of 1.25
1222
+ if (grok.shell.isInDemo)
1223
+ this.analysisView.addViewer(monomerPosition);
1176
1224
  const mostPotentResidues = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResidues | null;
1177
1225
  const dm = this.analysisView.dockManager;
1178
1226
  const [dockType, refNode, ratio] = mostPotentResidues === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
@@ -1195,6 +1243,8 @@ export class PeptidesModel {
1195
1243
  };
1196
1244
  const mostPotentResidues =
1197
1245
  await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES, viewerProperties) as MostPotentResidues;
1246
+ if (grok.shell.isInDemo)
1247
+ this.analysisView.addViewer(mostPotentResidues);
1198
1248
  const monomerPosition = this.findViewer(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP) as MonomerPosition | null;
1199
1249
  const dm = this.analysisView.dockManager;
1200
1250
  const [dockType, refNode, ratio] = monomerPosition === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
@@ -1327,7 +1377,7 @@ export class PeptidesModel {
1327
1377
  minClusterSize: mclParams.minClusterSize,
1328
1378
  } satisfies MCLSerializableOptions);
1329
1379
 
1330
- const tv = grok.shell.getTableView(this.df.name);
1380
+ const tv = this.analysisView ?? grok.shell.getTableView(this.df.name);
1331
1381
  if (tv) {
1332
1382
  const func = DG.Func.find({package: 'EDA', name: 'markovClusteringViewer'})[0];
1333
1383
  if (!func)
package/src/package.ts CHANGED
@@ -36,8 +36,7 @@ export async function initPeptides(): Promise<void> {
36
36
  try {
37
37
  monomerWorks ??= new MonomerWorks(await grok.functions.call('Bio:getBioLib'));
38
38
  treeHelper ??= await getTreeHelper();
39
- await PeptideUtils.loadSeqHelper();
40
- await PeptideUtils.loadMonomerLib();
39
+ await PeptideUtils.loadComponents();
41
40
  } catch (e) {
42
41
  grok.log.error(e as string);
43
42
  }
@@ -178,9 +177,9 @@ export function manualAlignment(_monomer: string): DG.Widget {
178
177
  }
179
178
 
180
179
  // --- Demo ---
181
- //name: Macromolecule SAR Analysis
182
- //description: Macromolecule SAR Analysis demo on peptide sequences in FASTA format
183
- //meta.demoPath: Bioinformatics | Macromolecule SAR Analysis
180
+ //name: Peptide SAR
181
+ //description: Peptide SAR Analysis demo on peptide sequences in FASTA format
182
+ //meta.demoPath: Bioinformatics | Peptide SAR
184
183
  //meta.demoSkip: GROK-14320
185
184
  export async function macromoleculeSarFastaDemo(): Promise<void> {
186
185
  return await macromoleculeSarFastaDemoUI();
@@ -20,6 +20,11 @@ export class PeptideUtils {
20
20
  this._secHelper ??= await getSeqHelper();
21
21
  }
22
22
 
23
+ public static async loadComponents(): Promise<void> {
24
+ await this.loadSeqHelper();
25
+ await this.loadMonomerLib();
26
+ }
27
+
23
28
  public static async loadMonomerLib(): Promise<void> {
24
29
  this._monomerLibHelper ??= await getMonomerLibHelper();
25
30
  await this._monomerLibHelper.awaitLoaded();
@@ -17,7 +17,7 @@ const benchmarkDatasetSizes = [5, 50, 100, 200];
17
17
 
18
18
  category('Benchmarks: Mutation Cliffs', () => {
19
19
  before(async () => {
20
- await PeptideUtils.loadSeqHelper();
20
+ await PeptideUtils.loadComponents();
21
21
  });
22
22
  for (const size of benchmarkDatasetSizes)
23
23
  test(`${size}k sequences`, async () => await mutationCliffsBenchmark(size), {timeout: 300000});
@@ -25,7 +25,7 @@ category('Benchmarks: Mutation Cliffs', () => {
25
25
 
26
26
  category('Benchmarks: Cluster stats', () => {
27
27
  before(async () => {
28
- await PeptideUtils.loadSeqHelper();
28
+ await PeptideUtils.loadComponents();
29
29
  });
30
30
  for (const size of benchmarkDatasetSizes) {
31
31
  test(`${size}k sequences`, async () => {
@@ -49,7 +49,7 @@ category('Benchmarks: Cluster stats', () => {
49
49
 
50
50
  category('Benchmarks: Monomer-Position stats', () => {
51
51
  before(async () => {
52
- await PeptideUtils.loadSeqHelper();
52
+ await PeptideUtils.loadComponents();
53
53
  });
54
54
  for (const size of benchmarkDatasetSizes) {
55
55
  test(`${size}k sequences`, async () => {
@@ -78,7 +78,7 @@ category('Benchmarks: Monomer-Position stats', () => {
78
78
 
79
79
  category('Benchmarks: Analysis start', () => {
80
80
  before(async () => {
81
- await PeptideUtils.loadSeqHelper();
81
+ await PeptideUtils.loadComponents();
82
82
  });
83
83
  for (const size of benchmarkDatasetSizes) {
84
84
  test(`${size}k sequences`, async () => {
package/src/tests/core.ts CHANGED
@@ -15,7 +15,7 @@ import {PeptideUtils} from '../peptideUtils';
15
15
  category('Core', () => {
16
16
  const alignedSequenceCol = 'AlignedSequence';
17
17
  before(async () => {
18
- await PeptideUtils.loadSeqHelper();
18
+ await PeptideUtils.loadComponents();
19
19
  });
20
20
  let model: PeptidesModel | null = null;
21
21
  test('Start analysis: simple', async () => {
package/src/tests/misc.ts CHANGED
@@ -14,7 +14,7 @@ category('Algorithms', () => {
14
14
  let targetCol: type.RawColumn;
15
15
 
16
16
  before(async () => {
17
- await PeptideUtils.loadSeqHelper();
17
+ await PeptideUtils.loadComponents();
18
18
 
19
19
  activityCol = DG.Column.fromList('int', 'test', [1, 2, 5]).getRawData();
20
20
  monomerColumns = [
@@ -63,8 +63,10 @@ category('Model: Settings', () => {
63
63
  };
64
64
 
65
65
  before(async () => {
66
- await PeptideUtils.loadSeqHelper();
66
+ await PeptideUtils.loadComponents();
67
67
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
68
+ df.name = 'HELM_small';
69
+ //df.id ??= `HELM_small-${Date.now()}`;
68
70
  activityCol = df.getCol(TEST_COLUMN_NAMES.ACTIVITY);
69
71
  sequenceCol = df.getCol(TEST_COLUMN_NAMES.SEQUENCE);
70
72
  sequenceCol.semType = DG.SEMTYPE.MACROMOLECULE;
@@ -27,7 +27,7 @@ category('Table view', () => {
27
27
  const secondCluster = {monomerOrCluster: '1', positionOrClusterType: CLUSTER_TYPE.ORIGINAL, count: 3};
28
28
 
29
29
  before(async () => {
30
- await PeptideUtils.loadSeqHelper();
30
+ await PeptideUtils.loadComponents();
31
31
 
32
32
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
33
33
  activityCol = df.getCol(TEST_COLUMN_NAMES.ACTIVITY);
@@ -18,7 +18,7 @@ category('Viewers: Basic', () => {
18
18
  let df: DG.DataFrame;
19
19
 
20
20
  before(async () => {
21
- await PeptideUtils.loadSeqHelper();
21
+ await PeptideUtils.loadComponents();
22
22
 
23
23
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
24
24
  await delay(500);
@@ -26,7 +26,7 @@ category('Widgets: Settings', () => {
26
26
  let scaledActivityCol: DG.Column<number>;
27
27
 
28
28
  before(async () => {
29
- await PeptideUtils.loadSeqHelper();
29
+ await PeptideUtils.loadComponents();
30
30
 
31
31
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
32
32
  activityCol = df.getCol(TEST_COLUMN_NAMES.ACTIVITY);
@@ -73,7 +73,7 @@ category('Widgets: Distribution panel', () => {
73
73
  let scaledActivityCol: DG.Column<number>;
74
74
 
75
75
  before(async () => {
76
- await PeptideUtils.loadSeqHelper();
76
+ await PeptideUtils.loadComponents();
77
77
 
78
78
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
79
79
  activityCol = df.getCol(TEST_COLUMN_NAMES.ACTIVITY);
@@ -114,7 +114,7 @@ category('Widgets: Mutation cliffs', () => {
114
114
  let scaledActivityCol: DG.Column<number>;
115
115
 
116
116
  before(async () => {
117
- await PeptideUtils.loadSeqHelper();
117
+ await PeptideUtils.loadComponents();
118
118
 
119
119
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
120
120
  activityCol = df.getCol(TEST_COLUMN_NAMES.ACTIVITY);
@@ -159,7 +159,7 @@ category('Widgets: Actions', () => {
159
159
  let scaledActivityCol: DG.Column<number>;
160
160
 
161
161
  before(async () => {
162
- await PeptideUtils.loadSeqHelper();
162
+ await PeptideUtils.loadComponents();
163
163
 
164
164
  df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
165
165
  await df.meta.detectSemanticTypes();
@@ -714,8 +714,12 @@ export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
714
714
  });
715
715
  grid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
716
716
  DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell) => {
717
- if (!gridCell.isTableCell)
717
+ if (!gridCell.isTableCell || gridCell.gridRow === -1) {
718
+ this.initClusterSelection({notify: false});
719
+ this.model.fireBitsetChanged(VIEWER_TYPE.LOGO_SUMMARY_TABLE);
720
+ grid.invalidate();
718
721
  return;
722
+ }
719
723
 
720
724
 
721
725
  try {
@@ -882,6 +882,16 @@ export class MonomerPosition extends SARViewer {
882
882
  try {
883
883
  if (!gridCell || !gridCell.dart || !gridCell?.cell?.column?.name || this._monomerMetaColumns.has(gridCell.cell.column.name))
884
884
  return;
885
+ if (gridCell.gridRow === -1) {
886
+ if (this.mode === SELECTION_MODE.INVARIANT_MAP)
887
+ this._invariantMapSelection = initSelection(this.positionColumns);
888
+ else
889
+ this._mutationCliffsSelection = initSelection(this.positionColumns);
890
+
891
+ this.model.fireBitsetChanged(VIEWER_TYPE.SEQUENCE_VARIABILITY_MAP);
892
+ grid.invalidate();
893
+ }
894
+
885
895
  if (!this.keyPressed)
886
896
  return;
887
897
 
@@ -920,7 +930,10 @@ export class MonomerPosition extends SARViewer {
920
930
  setTimeout(() => grid?.invalidate(), 300);
921
931
  } finally {
922
932
  this.keyPressed = false;
923
- this.currentGridCell = gridCell;
933
+ if (gridCell.tableColumn?.name && gridCell.grid)
934
+ this.currentGridCell = DG.GridCell.fromColumnRow(gridCell.grid, gridCell.tableColumn.name, gridCell.gridRow);
935
+ else
936
+ this.currentGridCell = null;
924
937
  }
925
938
  });
926
939
  grid.root.addEventListener('keydown', (ev) => {
@@ -1265,6 +1278,13 @@ export class MostPotentResidues extends SARViewer {
1265
1278
  });
1266
1279
  DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
1267
1280
  try {
1281
+ if (gridCell.gridRow === -1) {
1282
+ this._mutationCliffsSelection = initSelection(this.positionColumns);
1283
+ this.model.fireBitsetChanged(VIEWER_TYPE.MOST_POTENT_RESIDUES);
1284
+ grid.invalidate();
1285
+ return;
1286
+ }
1287
+
1268
1288
  if ((this.keyPressed && mprDf.currentCol.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) || !this.keyPressed)
1269
1289
  return;
1270
1290