@datagrok/bio 2.6.0 → 2.7.0
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/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/package.json +8 -2
- package/src/package-types.ts +10 -0
- package/src/package.ts +18 -48
- package/src/utils/cell-renderer.ts +13 -8
- package/src/utils/macromolecule-column-widget.ts +47 -0
- package/src/utils/monomer-lib.ts +41 -2
- package/src/viewers/web-logo-viewer.ts +14 -11
- package/src/widgets/package-settings-editor-widget.ts +11 -1
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "Leonid Stolbov",
|
|
6
6
|
"email": "lstolbov@datagrok.ai"
|
|
7
7
|
},
|
|
8
|
-
"version": "2.
|
|
8
|
+
"version": "2.7.0",
|
|
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",
|
|
@@ -18,11 +18,17 @@
|
|
|
18
18
|
"propertyType": "int",
|
|
19
19
|
"defaultValue": 3,
|
|
20
20
|
"nullable": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "TooltipWebLogo",
|
|
24
|
+
"propertyType": "bool",
|
|
25
|
+
"defaultValue": "true",
|
|
26
|
+
"nullable": false
|
|
21
27
|
}
|
|
22
28
|
],
|
|
23
29
|
"dependencies": {
|
|
24
30
|
"@biowasm/aioli": "^3.1.0",
|
|
25
|
-
"@datagrok-libraries/bio": "^5.33.
|
|
31
|
+
"@datagrok-libraries/bio": "^5.33.2",
|
|
26
32
|
"@datagrok-libraries/chem-meta": "^1.0.1",
|
|
27
33
|
"@datagrok-libraries/ml": "^6.3.39",
|
|
28
34
|
"@datagrok-libraries/tutorials": "^1.3.2",
|
package/src/package-types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {ObjectPropertyBag} from 'datagrok-api/dg';
|
|
|
8
8
|
/** Names of package properties/settings declared in properties section of {@link './package.json'} */
|
|
9
9
|
export const enum BioPackagePropertiesNames {
|
|
10
10
|
MaxMonomerLength = 'MaxMonomerLength',
|
|
11
|
+
TooltipWebLogo = 'TooltipWebLogo',
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
|
|
@@ -26,6 +27,15 @@ export class BioPackageProperties extends Map<string, any> {
|
|
|
26
27
|
this._onPropertyChanged.next(BioPackagePropertiesNames.MaxMonomerLength);
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
public get tooltipWebLogo(): boolean {
|
|
31
|
+
return super.get(BioPackagePropertiesNames.TooltipWebLogo) as unknown as boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public set tooltipWebLogo(value: boolean) {
|
|
35
|
+
super.set(BioPackagePropertiesNames.TooltipWebLogo, value as unknown as boolean);
|
|
36
|
+
this._onPropertyChanged.next(BioPackagePropertiesNames.TooltipWebLogo);
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
constructor(source: any) {
|
|
30
40
|
super(Object.entries(source));
|
|
31
41
|
}
|
package/src/package.ts
CHANGED
|
@@ -33,7 +33,13 @@ import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
|
33
33
|
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
34
34
|
import {WebLogoViewer} from './viewers/web-logo-viewer';
|
|
35
35
|
import {createJsonMonomerLibFromSdf, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
36
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
MonomerLibHelper,
|
|
38
|
+
getUserLibSettings,
|
|
39
|
+
setUserLibSetting,
|
|
40
|
+
getLibFileNameList,
|
|
41
|
+
getLibraryPanelUI
|
|
42
|
+
} from './utils/monomer-lib';
|
|
37
43
|
import {getMacromoleculeColumn} from './utils/ui-utils';
|
|
38
44
|
import {DimReductionMethods, ITSNEOptions, IUMAPOptions} from '@datagrok-libraries/ml/src/reduce-dimensionality';
|
|
39
45
|
import {SequenceSpaceFunctionEditor} from '@datagrok-libraries/ml/src/functionEditors/seq-space-editor';
|
|
@@ -55,9 +61,9 @@ import {splitToMonomersUI} from './utils/split-to-monomers';
|
|
|
55
61
|
import {MonomerCellRenderer} from './utils/monomer-cell-renderer';
|
|
56
62
|
import {BioPackage, BioPackageProperties} from './package-types';
|
|
57
63
|
import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api';
|
|
58
|
-
import {ObjectPropertyBag} from 'datagrok-api/dg';
|
|
59
64
|
import {PackageSettingsEditorWidget} from './widgets/package-settings-editor-widget';
|
|
60
65
|
import {getCompositionAnalysisWidget} from './widgets/composition-analysis-widget';
|
|
66
|
+
import {MacromoleculeColumnWidget} from './utils/macromolecule-column-widget';
|
|
61
67
|
|
|
62
68
|
export const _package = new BioPackage();
|
|
63
69
|
|
|
@@ -126,17 +132,14 @@ export async function initBio() {
|
|
|
126
132
|
//tags: tooltip
|
|
127
133
|
//input: column col {semType: Macromolecule}
|
|
128
134
|
//output: widget result
|
|
129
|
-
export
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
});
|
|
138
|
-
viewer.root.style.height = '50px';
|
|
139
|
-
return viewer;
|
|
135
|
+
export function sequenceTooltip(col: DG.Column): DG.Widget<any> {
|
|
136
|
+
const resWidget = new MacromoleculeColumnWidget(col);
|
|
137
|
+
const _resPromise = resWidget.init().then(() => { })
|
|
138
|
+
.catch((err: any) => {
|
|
139
|
+
const errMsg = err instanceof Error ? err.message : err.toString();
|
|
140
|
+
grok.shell.error(errMsg);
|
|
141
|
+
});
|
|
142
|
+
return resWidget;
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
//name: getBioLib
|
|
@@ -145,46 +148,12 @@ export function getBioLib(): IMonomerLib {
|
|
|
145
148
|
return MonomerLibHelper.instance.getBioLib();
|
|
146
149
|
}
|
|
147
150
|
|
|
148
|
-
//name: manageFiles
|
|
149
|
-
export async function manageFiles() {
|
|
150
|
-
const a = ui.dialog({title: 'Manage files'})
|
|
151
|
-
//@ts-ignore
|
|
152
|
-
.add(ui.fileBrowser({path: 'System:AppData/Bio/libraries'}).root)
|
|
153
|
-
.addButton('OK', () => a.close())
|
|
154
|
-
.show();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
151
|
//name: Manage Libraries
|
|
158
152
|
//input: column seqColumn {semType: Macromolecule}
|
|
159
153
|
//tags: panel, exclude-actions-panel
|
|
160
154
|
//output: widget result
|
|
161
155
|
export async function libraryPanel(_seqColumn: DG.Column): Promise<DG.Widget> {
|
|
162
|
-
|
|
163
|
-
const filesButton: HTMLButtonElement = ui.button('Manage', manageFiles);
|
|
164
|
-
const inputsForm: HTMLDivElement = ui.inputs([]);
|
|
165
|
-
const libFileNameList: string[] = await getLibFileNameList();
|
|
166
|
-
|
|
167
|
-
let userStoragePromise: Promise<void> = Promise.resolve();
|
|
168
|
-
for (const libFileName of libFileNameList) {
|
|
169
|
-
const settings = await getUserLibSettings();
|
|
170
|
-
const libInput: DG.InputBase<boolean | null> = ui.boolInput(libFileName, !settings.exclude.includes(libFileName),
|
|
171
|
-
() => {
|
|
172
|
-
userStoragePromise = userStoragePromise.then(async () => {
|
|
173
|
-
if (libInput.value == true) {
|
|
174
|
-
// Checked library remove from excluded list
|
|
175
|
-
settings.exclude = settings.exclude.filter((l) => l != libFileName);
|
|
176
|
-
} else {
|
|
177
|
-
// Unchecked library add to excluded list
|
|
178
|
-
if (!settings.exclude.includes(libFileName)) settings.exclude.push(libFileName);
|
|
179
|
-
}
|
|
180
|
-
await setUserLibSetting(settings);
|
|
181
|
-
await MonomerLibHelper.instance.loadLibraries(true); // from libraryPanel()
|
|
182
|
-
grok.shell.info('Monomer library user settings saved.');
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
inputsForm.append(libInput.root);
|
|
186
|
-
}
|
|
187
|
-
return new DG.Widget(ui.divV([inputsForm, ui.div(filesButton)]));
|
|
156
|
+
return getLibraryPanelUI();
|
|
188
157
|
}
|
|
189
158
|
|
|
190
159
|
// -- Package settings editor --
|
|
@@ -614,6 +583,7 @@ export function importFasta(fileContent: string): DG.DataFrame [] {
|
|
|
614
583
|
const ffh = new FastaFileHandler(fileContent);
|
|
615
584
|
return ffh.importFasta();
|
|
616
585
|
}
|
|
586
|
+
|
|
617
587
|
//name: importBam
|
|
618
588
|
//description: Opens Bam file
|
|
619
589
|
//tags: file-handler
|
|
@@ -88,11 +88,16 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
88
88
|
if (left !== null && left < seqMonList.length) {
|
|
89
89
|
const monomerSymbol: string = seqMonList[left];
|
|
90
90
|
const tooltipElements: HTMLElement[] = [ui.div(monomerSymbol)];
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
91
|
+
if (seqColTemp._monomerStructureMap[monomerSymbol]) {
|
|
92
|
+
tooltipElements.push(seqColTemp._monomerStructureMap[monomerSymbol]);
|
|
93
|
+
} else {
|
|
94
|
+
const monomer = seqColTemp.getMonomer(monomerSymbol);
|
|
95
|
+
if (monomer) {
|
|
96
|
+
const options = {autoCrop: true, autoCropMargin: 0, suppressChiralText: true};
|
|
97
|
+
const monomerSVG = grok.chem.svgMol(monomer.smiles, undefined, undefined, options);
|
|
98
|
+
tooltipElements.push(monomerSVG);
|
|
99
|
+
seqColTemp._monomerStructureMap[monomerSymbol] = monomerSVG;
|
|
100
|
+
}
|
|
96
101
|
}
|
|
97
102
|
ui.tooltip.show(ui.divV(tooltipElements), e.x + 16, e.y + 16);
|
|
98
103
|
} else {
|
|
@@ -122,7 +127,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
122
127
|
|
|
123
128
|
// TODO: Store temp data to GridColumn
|
|
124
129
|
// Now the renderer requires data frame table Column underlying GridColumn
|
|
125
|
-
const
|
|
130
|
+
const grid = gridCell.grid;
|
|
126
131
|
const tableCol: DG.Column = gridCell.cell.column;
|
|
127
132
|
const tableColTemp: TempType = tableCol.temp;
|
|
128
133
|
|
|
@@ -135,7 +140,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
135
140
|
|
|
136
141
|
let seqColTemp: MonomerPlacer = tableCol.temp[tempTAGS.bioSeqCol];
|
|
137
142
|
if (!seqColTemp) {
|
|
138
|
-
seqColTemp = new MonomerPlacer(
|
|
143
|
+
seqColTemp = new MonomerPlacer(grid, tableCol,
|
|
139
144
|
() => {
|
|
140
145
|
const uh = UnitsHandler.getOrCreate(tableCol);
|
|
141
146
|
return {
|
|
@@ -236,7 +241,7 @@ export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer {
|
|
|
236
241
|
/*x1 = */
|
|
237
242
|
printLeftOrCentered(x + this.padding, y, w, h,
|
|
238
243
|
g, amino, color, 0, true, 1.0, separator, last, drawStyle,
|
|
239
|
-
maxLengthWordsSum, index, gridCell, referenceSequence, maxLengthOfMonomer);
|
|
244
|
+
maxLengthWordsSum, index, gridCell, referenceSequence, maxLengthOfMonomer, seqColTemp._monomerLengthMap);
|
|
240
245
|
if (minDistanceRenderer > w) break;
|
|
241
246
|
}
|
|
242
247
|
} catch (err: any) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
|
+
|
|
5
|
+
import {TAGS as wlTAGS} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
6
|
+
import {WebLogoViewer} from '../viewers/web-logo-viewer';
|
|
7
|
+
|
|
8
|
+
import {_package} from '../package';
|
|
9
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
10
|
+
|
|
11
|
+
/** Used in Macromolecule column tooltip */
|
|
12
|
+
export class MacromoleculeColumnWidget extends DG.Widget {
|
|
13
|
+
private viewed: boolean = false;
|
|
14
|
+
|
|
15
|
+
private seqCol: DG.Column<string>;
|
|
16
|
+
|
|
17
|
+
private wlViewer: WebLogoViewer;
|
|
18
|
+
|
|
19
|
+
constructor(seqCol: DG.Column<string>) {
|
|
20
|
+
super(ui.divV([]));
|
|
21
|
+
|
|
22
|
+
this.seqCol = seqCol;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async init(): Promise<void> {
|
|
26
|
+
const uh = UnitsHandler.getOrCreate(this.seqCol);
|
|
27
|
+
const pkgTooltipWebLogo = _package.properties.tooltipWebLogo;
|
|
28
|
+
const colTooltipWebLogo = this.seqCol.getTag(wlTAGS.tooltipWebLogo);
|
|
29
|
+
|
|
30
|
+
if (pkgTooltipWebLogo !== false && !['false', 'off', 'disable', 'disabled'].includes(colTooltipWebLogo)) {
|
|
31
|
+
this.wlViewer = await this.seqCol.dataFrame.plot.fromType('WebLogo', {
|
|
32
|
+
sequenceColumnName: this.seqCol.name,
|
|
33
|
+
backgroundColor: 0x00000000,
|
|
34
|
+
positionHeight: 'Entropy',
|
|
35
|
+
positionWidth: (uh.getAlphabetIsMultichar() ? 24 : 16),
|
|
36
|
+
fixWidth: true,
|
|
37
|
+
fitArea: false,
|
|
38
|
+
// maxHeight: 100,
|
|
39
|
+
// minHeight: 25,
|
|
40
|
+
}) as unknown as WebLogoViewer;
|
|
41
|
+
this.wlViewer.root.style.height = `50px`;
|
|
42
|
+
|
|
43
|
+
this.root.appendChild(this.wlViewer.root);
|
|
44
|
+
this.root.style.width = '100%';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/utils/monomer-lib.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
// import * as ui from 'datagrok-api/ui';
|
|
2
|
-
import * as DG from 'datagrok-api/dg';
|
|
3
1
|
import * as grok from 'datagrok-api/grok';
|
|
2
|
+
import * as ui from 'datagrok-api/ui';
|
|
3
|
+
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import {Observable, Subject} from 'rxjs';
|
|
6
|
+
|
|
6
7
|
import {IMonomerLib, Monomer} from '@datagrok-libraries/bio/src/types/index';
|
|
7
8
|
import {
|
|
8
9
|
createJsonMonomerLibFromSdf,
|
|
9
10
|
IMonomerLibHelper,
|
|
10
11
|
} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
11
12
|
import {HELM_REQUIRED_FIELDS as REQ, HELM_OPTIONAL_FIELDS as OPT} from '@datagrok-libraries/bio/src/utils/const';
|
|
13
|
+
|
|
12
14
|
import {_package} from '../package';
|
|
13
15
|
|
|
14
16
|
const _HELM_REQUIRED_FIELDS_ARRAY = [
|
|
@@ -51,6 +53,43 @@ export async function setUserLibSetting(value: LibSettings): Promise<void> {
|
|
|
51
53
|
await grok.dapi.userDataStorage.postValue(LIB_STORAGE_NAME, 'Settings', JSON.stringify(value), true);
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
export async function manageFiles() {
|
|
57
|
+
const a = ui.dialog({title: 'Manage files'})
|
|
58
|
+
//@ts-ignore
|
|
59
|
+
.add(ui.fileBrowser({path: 'System:AppData/Bio/libraries'}).root)
|
|
60
|
+
.addButton('OK', () => a.close())
|
|
61
|
+
.show();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function getLibraryPanelUI(): Promise<DG.Widget> {
|
|
65
|
+
//@ts-ignore
|
|
66
|
+
const filesButton: HTMLButtonElement = ui.button('Manage', manageFiles);
|
|
67
|
+
const inputsForm: HTMLDivElement = ui.inputs([]);
|
|
68
|
+
const libFileNameList: string[] = await getLibFileNameList();
|
|
69
|
+
|
|
70
|
+
let userStoragePromise: Promise<void> = Promise.resolve();
|
|
71
|
+
for (const libFileName of libFileNameList) {
|
|
72
|
+
const settings = await getUserLibSettings();
|
|
73
|
+
const libInput: DG.InputBase<boolean | null> = ui.boolInput(libFileName, !settings.exclude.includes(libFileName),
|
|
74
|
+
() => {
|
|
75
|
+
userStoragePromise = userStoragePromise.then(async () => {
|
|
76
|
+
if (libInput.value == true) {
|
|
77
|
+
// Checked library remove from excluded list
|
|
78
|
+
settings.exclude = settings.exclude.filter((l) => l != libFileName);
|
|
79
|
+
} else {
|
|
80
|
+
// Unchecked library add to excluded list
|
|
81
|
+
if (!settings.exclude.includes(libFileName)) settings.exclude.push(libFileName);
|
|
82
|
+
}
|
|
83
|
+
await setUserLibSetting(settings);
|
|
84
|
+
await MonomerLibHelper.instance.loadLibraries(true); // from libraryPanel()
|
|
85
|
+
grok.shell.info('Monomer library user settings saved.');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
inputsForm.append(libInput.root);
|
|
89
|
+
}
|
|
90
|
+
return new DG.Widget(ui.divV([inputsForm, ui.div(filesButton)]));
|
|
91
|
+
}
|
|
92
|
+
|
|
54
93
|
export class MonomerLib implements IMonomerLib {
|
|
55
94
|
public readonly error: string | undefined;
|
|
56
95
|
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
WebLogoPropsDefault,
|
|
27
27
|
} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
28
28
|
import {errorToConsole} from '@datagrok-libraries/utils/src/to-console';
|
|
29
|
+
import {intToHtmlA} from '@datagrok-libraries/utils/src/color';
|
|
29
30
|
|
|
30
31
|
import {_package} from '../package';
|
|
31
32
|
|
|
@@ -155,6 +156,7 @@ export class PositionInfo {
|
|
|
155
156
|
jPos: number, absoluteMaxHeight: number, heightMode: PositionHeight, alphabetSizeLog: number,
|
|
156
157
|
positionWidthWithMargin: number, positionWidth: number, r: number, axisHeight: number
|
|
157
158
|
): void {
|
|
159
|
+
const dpr = window.devicePixelRatio;
|
|
158
160
|
// const rowCount = this.positions[jPos].rowCount;
|
|
159
161
|
// const alphabetSize = this.getAlphabetSize();
|
|
160
162
|
// if ((this.positionHeight == PositionHeight.Entropy) && (alphabetSize == null))
|
|
@@ -207,7 +209,7 @@ export class PositionInfo {
|
|
|
207
209
|
// const m: string = entry[0];
|
|
208
210
|
const h: number = maxHeight * pmInfo.count / this.rowCount;
|
|
209
211
|
|
|
210
|
-
pmInfo.bounds = new DG.Rect(jPos * positionWidthWithMargin, y, positionWidth, h);
|
|
212
|
+
pmInfo.bounds = new DG.Rect(jPos * dpr * positionWidthWithMargin, y, positionWidth * dpr, h);
|
|
211
213
|
y += h;
|
|
212
214
|
}
|
|
213
215
|
}
|
|
@@ -755,9 +757,9 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
755
757
|
|
|
756
758
|
// -- Routines --
|
|
757
759
|
|
|
758
|
-
getMonomer(p: DG.Point): [number, string | null, PositionMonomerInfo | null] {
|
|
759
|
-
const calculatedX = p.x + this.firstVisibleIndex * this.positionWidthWithMargin;
|
|
760
|
-
const jPos = Math.floor(p.x / this.positionWidthWithMargin + this.firstVisibleIndex);
|
|
760
|
+
getMonomer(p: DG.Point, dpr: number): [number, string | null, PositionMonomerInfo | null] {
|
|
761
|
+
const calculatedX = p.x + this.firstVisibleIndex * this.positionWidthWithMargin * dpr;
|
|
762
|
+
const jPos = Math.floor(p.x / (this.positionWidthWithMargin * dpr) + this.firstVisibleIndex);
|
|
761
763
|
const position: PositionInfo = this.positions[jPos];
|
|
762
764
|
|
|
763
765
|
if (position === undefined)
|
|
@@ -887,7 +889,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
887
889
|
|
|
888
890
|
const length: number = this.Length;
|
|
889
891
|
g.resetTransform();
|
|
890
|
-
g.fillStyle =
|
|
892
|
+
g.fillStyle = intToHtmlA(this.backgroundColor);
|
|
891
893
|
g.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
|
892
894
|
g.textBaseline = this.textBaseline;
|
|
893
895
|
|
|
@@ -902,14 +904,15 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
902
904
|
g.textAlign = 'center';
|
|
903
905
|
g.font = `${positionFontSize.toFixed(1)}px Roboto, Roboto Local, sans-serif`;
|
|
904
906
|
const posNameMaxWidth = Math.max(...this.positions.map((pos) => g.measureText(pos.name).width));
|
|
905
|
-
const hScale = posNameMaxWidth < (this._positionWidth - 2) ? 1 :
|
|
907
|
+
const hScale = posNameMaxWidth < (this._positionWidth * dpr - 2) ? 1 :
|
|
908
|
+
(this._positionWidth * dpr - 2) / posNameMaxWidth;
|
|
906
909
|
|
|
907
910
|
for (let jPos = this.firstVisibleIndex; jPos < lastVisibleIndex; jPos++) {
|
|
908
911
|
const pos: PositionInfo = this.positions[jPos];
|
|
909
912
|
g.resetTransform();
|
|
910
913
|
g.setTransform(
|
|
911
914
|
hScale, 0, 0, 1,
|
|
912
|
-
jPos * this.positionWidthWithMargin + this._positionWidth / 2 -
|
|
915
|
+
jPos * this.positionWidthWithMargin * dpr + this._positionWidth * dpr / 2 -
|
|
913
916
|
this.positionWidthWithMargin * firstVisibleIndex, 0);
|
|
914
917
|
g.fillText(pos.label, 0, 0);
|
|
915
918
|
}
|
|
@@ -1097,12 +1100,12 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1097
1100
|
}
|
|
1098
1101
|
|
|
1099
1102
|
private canvasOnMouseMove(e: MouseEvent) {
|
|
1103
|
+
const dpr = window.devicePixelRatio;
|
|
1100
1104
|
try {
|
|
1101
1105
|
const args = e as MouseEvent;
|
|
1102
1106
|
|
|
1103
|
-
const dpr: number = window.devicePixelRatio;
|
|
1104
1107
|
const cursorP: DG.Point = this.canvas.getCursorPosition(args, dpr);
|
|
1105
|
-
const [jPos, monomer] = this.getMonomer(cursorP);
|
|
1108
|
+
const [jPos, monomer] = this.getMonomer(cursorP, dpr);
|
|
1106
1109
|
// if (jPos != undefined && monomer == undefined) {
|
|
1107
1110
|
// const preEl = ui.element('pre');
|
|
1108
1111
|
// preEl.innerHTML = jPos < this.positions.length ?
|
|
@@ -1133,8 +1136,8 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
|
|
|
1133
1136
|
private canvasOnMouseDown(e: MouseEvent): void {
|
|
1134
1137
|
try {
|
|
1135
1138
|
const args = e as MouseEvent;
|
|
1136
|
-
const
|
|
1137
|
-
const [jPos, monomer] = this.getMonomer(this.canvas.getCursorPosition(args,
|
|
1139
|
+
const dpr: number = window.devicePixelRatio;
|
|
1140
|
+
const [jPos, monomer] = this.getMonomer(this.canvas.getCursorPosition(args, dpr), dpr);
|
|
1138
1141
|
|
|
1139
1142
|
// prevents deselect all rows if we miss monomer bounds
|
|
1140
1143
|
if (this.dataFrame && this.seqCol && this.unitsHandler && monomer) {
|
|
@@ -5,6 +5,7 @@ import {_package} from '../package';
|
|
|
5
5
|
|
|
6
6
|
export class PackageSettingsEditorWidget extends DG.Widget {
|
|
7
7
|
maxMonomerLengthProp: DG.Property;
|
|
8
|
+
tooltipWebLogo: DG.Property;
|
|
8
9
|
|
|
9
10
|
constructor(propList: DG.Property[]) {
|
|
10
11
|
super(ui.div([], {}));
|
|
@@ -13,6 +14,7 @@ export class PackageSettingsEditorWidget extends DG.Widget {
|
|
|
13
14
|
Object.assign({}, ...propList.map((p) => ({[p.name]: p})));
|
|
14
15
|
|
|
15
16
|
this.maxMonomerLengthProp = props['MaxMonomerLength'];
|
|
17
|
+
this.tooltipWebLogo = props['TooltipWebLogo'];
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
async init(): Promise<void> {
|
|
@@ -23,6 +25,14 @@ export class PackageSettingsEditorWidget extends DG.Widget {
|
|
|
23
25
|
_package.properties.maxMonomerLength = value;
|
|
24
26
|
});
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
const tooltipWebLogo = ui.boolInput('Tooltip WebLogo',
|
|
29
|
+
_package.properties.tooltipWebLogo,
|
|
30
|
+
(value: boolean) => {
|
|
31
|
+
_package.properties.tooltipWebLogo = value;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
this.root.appendChild(ui.form([
|
|
35
|
+
maxMonomerLengthInput,
|
|
36
|
+
tooltipWebLogo]));
|
|
27
37
|
}
|
|
28
38
|
}
|