@datagrok-libraries/bio 5.53.3 → 5.54.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/package.json +5 -5
- package/src/utils/cell-renderer-consts.d.ts +5 -0
- package/src/utils/cell-renderer-consts.d.ts.map +1 -1
- package/src/utils/cell-renderer-consts.js +5 -0
- package/src/utils/cell-renderer-consts.js.map +1 -1
- package/src/utils/cell-renderer-monomer-placer.d.ts +8 -1
- package/src/utils/cell-renderer-monomer-placer.d.ts.map +1 -1
- package/src/utils/cell-renderer-monomer-placer.js +218 -108
- package/src/utils/cell-renderer-monomer-placer.js.map +1 -1
- package/src/utils/cell-renderer.d.ts +2 -0
- package/src/utils/cell-renderer.d.ts.map +1 -1
- package/src/utils/cell-renderer.js +43 -6
- package/src/utils/cell-renderer.js.map +1 -1
- package/src/utils/composition-table.d.ts +6 -0
- package/src/utils/composition-table.d.ts.map +1 -0
- package/src/utils/composition-table.js +46 -0
- package/src/utils/composition-table.js.map +1 -0
- package/src/utils/sequence-position-scroller.d.ts +12 -8
- package/src/utils/sequence-position-scroller.d.ts.map +1 -1
- package/src/utils/sequence-position-scroller.js +45 -57
- package/src/utils/sequence-position-scroller.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok-libraries/bio",
|
|
3
3
|
"author": {
|
|
4
|
-
"name": "
|
|
5
|
-
"email": "
|
|
4
|
+
"name": "Davit Rizhinashvili",
|
|
5
|
+
"email": "drizhinashvili@datagrok.ai"
|
|
6
6
|
},
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
10
|
"friendlyName": "Datagrok bio library",
|
|
11
|
-
"version": "5.
|
|
11
|
+
"version": "5.54.0",
|
|
12
12
|
"description": "Bio utilities, types supporting Macromolecule, Molecule3D data",
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@datagrok-libraries/chem-meta": "^1.2.7",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@types/wu": "^2.1.44",
|
|
37
37
|
"@typescript-eslint/eslint-plugin": "^8.8.1",
|
|
38
38
|
"@typescript-eslint/parser": "^8.8.1",
|
|
39
|
-
"datagrok-tools": "
|
|
39
|
+
"datagrok-tools": "4.14.9",
|
|
40
40
|
"eslint": "8.57.1",
|
|
41
41
|
"eslint-config-google": "^0.14.0",
|
|
42
42
|
"eslint-plugin-deprecation": "^3.0.0",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"link-utils": "npm link @datagrok-libraries/utils",
|
|
50
50
|
"link-all": "npm link @datagrok-libraries/chem-meta datagrok-api @datagrok-libraries/gridext @datagrok-libraries/utils @datagrok-libraries/ml",
|
|
51
51
|
"build-bio": "git clean -f -X -d ./src && tsc",
|
|
52
|
-
"build": "tsc",
|
|
52
|
+
"build": "grok check --soft && tsc",
|
|
53
53
|
"build-all": "npm --prefix ./../../libraries/chem-meta run build && npm --prefix ./../../js-api run build && npm --prefix ./../../libraries/gridext run build && npm --prefix ./../../libraries/utils run build && npm --prefix ./../../libraries/ml run build && npm run build",
|
|
54
54
|
"lint": "eslint \"./src/**/*.ts\"",
|
|
55
55
|
"lint-fix": "eslint \"./src/**/*.ts\" --fix"
|
|
@@ -18,4 +18,9 @@ export declare const enum tempTAGS {
|
|
|
18
18
|
referenceSequence = "reference-sequence",
|
|
19
19
|
currentWord = "current-word"
|
|
20
20
|
}
|
|
21
|
+
export declare const MULTILINE_TAGS: {
|
|
22
|
+
renderMultiline: string;
|
|
23
|
+
monomersPerLine: string;
|
|
24
|
+
maxVisibleLines: string;
|
|
25
|
+
};
|
|
21
26
|
//# sourceMappingURL=cell-renderer-consts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell-renderer-consts.d.ts","sourceRoot":"","sources":["cell-renderer-consts.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,4BAA4B;;;CAGxC,CAAC;AAEF,kDAAkD;AAClD,0BAAkB,SAAS;IACzB,gBAAgB,sCAAsC;IACtD,SAAS,+BAA+B;IACxC,kBAAkB,wCAAwC;IAC1D,mBAAmB,yCAAyC;IAC5D,SAAS,+BAA+B;IACxC,aAAa,mCAAmC;IAChD,QAAQ,8BAA8B;IAEtC,uBAAuB,qCAAqC;IAC5D,iBAAiB,uCAAuC;CACzD;AAED,0BAAkB,QAAQ;IACxB,iBAAiB,uBAAuB;IACxC,WAAW,iBAAiB;CAC7B"}
|
|
1
|
+
{"version":3,"file":"cell-renderer-consts.d.ts","sourceRoot":"","sources":["cell-renderer-consts.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,4BAA4B;;;CAGxC,CAAC;AAEF,kDAAkD;AAClD,0BAAkB,SAAS;IACzB,gBAAgB,sCAAsC;IACtD,SAAS,+BAA+B;IACxC,kBAAkB,wCAAwC;IAC1D,mBAAmB,yCAAyC;IAC5D,SAAS,+BAA+B;IACxC,aAAa,mCAAmC;IAChD,QAAQ,8BAA8B;IAEtC,uBAAuB,qCAAqC;IAC5D,iBAAiB,uCAAuC;CACzD;AAED,0BAAkB,QAAQ;IACxB,iBAAiB,uBAAuB;IACxC,WAAW,iBAAiB;CAC7B;AAED,eAAO,MAAM,cAAc;;;;CAI1B,CAAC"}
|
|
@@ -2,4 +2,9 @@ export const rendererSettingsChangedState = {
|
|
|
2
2
|
true: '1',
|
|
3
3
|
false: '0',
|
|
4
4
|
};
|
|
5
|
+
export const MULTILINE_TAGS = {
|
|
6
|
+
renderMultiline: '.mm.cellRenderer.renderMultiline',
|
|
7
|
+
monomersPerLine: '.mm.cellRenderer.monomersPerLine',
|
|
8
|
+
maxVisibleLines: '.mm.cellRenderer.maxVisibleLines'
|
|
9
|
+
};
|
|
5
10
|
//# sourceMappingURL=cell-renderer-consts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell-renderer-consts.js","sourceRoot":"","sources":["cell-renderer-consts.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,GAAG;CACX,CAAC"}
|
|
1
|
+
{"version":3,"file":"cell-renderer-consts.js","sourceRoot":"","sources":["cell-renderer-consts.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,GAAG;CACX,CAAC;AAqBF,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,eAAe,EAAE,kCAAkC;IACnD,eAAe,EAAE,kCAAkC;IACnD,eAAe,EAAE,kCAAkC;CACpD,CAAC"}
|
|
@@ -29,10 +29,17 @@ export declare class MonomerPlacer extends CellRendererBackBase<string> {
|
|
|
29
29
|
_monomerStructureMap: {
|
|
30
30
|
[key: string]: HTMLElement;
|
|
31
31
|
};
|
|
32
|
+
private _ellipsisBounds;
|
|
33
|
+
private _totalLinesNeeded;
|
|
34
|
+
private _lineHeight;
|
|
35
|
+
private _cellBounds;
|
|
32
36
|
private seqHelper;
|
|
33
37
|
private sysMonomerLib;
|
|
34
38
|
/** View is required to subscribe and handle for data frame changes */
|
|
35
39
|
constructor(gridCol: DG.GridColumn | null, tableCol: DG.Column<string>, logger: ILogger, monomerLengthLimit: number, propsProvider: () => MonomerPlacerProps);
|
|
40
|
+
private calculateFontBasedSpacing;
|
|
41
|
+
private shouldUseMultilineRendering;
|
|
42
|
+
private calculateMultiLineLayoutDynamic;
|
|
36
43
|
init(): Promise<void>;
|
|
37
44
|
static getFontSettings(tableCol?: DG.Column): {
|
|
38
45
|
font: string;
|
|
@@ -45,8 +52,8 @@ export declare class MonomerPlacer extends CellRendererBackBase<string> {
|
|
|
45
52
|
/** Returns monomers lengths of the {@link rowIdx} and cumulative sums for borders, monomer places */
|
|
46
53
|
getCellMonomerLengths(rowIdx: number, newWidth: number): [number[], number[]];
|
|
47
54
|
private getSummedMonomerLengths;
|
|
48
|
-
private getCellMonomerLengthsForSeq;
|
|
49
55
|
private getCellMonomerLengthsForSeqValue;
|
|
56
|
+
private getCellMonomerLengthsForSeq;
|
|
50
57
|
private getCellMonomerLengthsForSeqMsa;
|
|
51
58
|
/** Returns seq position for pointer x */
|
|
52
59
|
getPosition(rowIdx: number, x: number, width: number, positionShiftPadding?: number): number | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell-renderer-monomer-placer.d.ts","sourceRoot":"","sources":["cell-renderer-monomer-placer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cell-renderer-monomer-placer.d.ts","sourceRoot":"","sources":["cell-renderer-monomer-placer.ts"],"names":[],"mappings":"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"}
|
|
@@ -51,6 +51,10 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
51
51
|
this._processedMaxVisibleSeqLength = 0;
|
|
52
52
|
this._monomerLengthMap = {}; // caches the lengths to save time on g.measureText
|
|
53
53
|
this._monomerStructureMap = {}; // caches the atomic structures of monomers
|
|
54
|
+
this._ellipsisBounds = undefined;
|
|
55
|
+
this._totalLinesNeeded = 0;
|
|
56
|
+
this._lineHeight = 20;
|
|
57
|
+
this._cellBounds = new Map();
|
|
54
58
|
this.sysMonomerLib = null;
|
|
55
59
|
this.padding = 5;
|
|
56
60
|
this._leftThreeDotsPadding = 0;
|
|
@@ -64,18 +68,88 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
64
68
|
if (this.tableCol && this.gridCol) {
|
|
65
69
|
this.subs.push(this.tableCol.dataFrame.onCurrentRowChanged.subscribe(() => {
|
|
66
70
|
const df = this.tableCol.dataFrame;
|
|
67
|
-
const grid = this.gridCol.grid;
|
|
68
71
|
if (df.currentRowIdx === -1) {
|
|
69
72
|
this.tableCol.temp["reference-sequence" /* tempTAGS.referenceSequence */] = null;
|
|
70
73
|
this.tableCol.temp["current-word" /* tempTAGS.currentWord */] = null;
|
|
71
74
|
this.invalidateGrid();
|
|
72
75
|
}
|
|
73
76
|
}));
|
|
77
|
+
const resetTriggerTags = [
|
|
78
|
+
bioTAGS.positionShift,
|
|
79
|
+
'renderMultiline'
|
|
80
|
+
];
|
|
81
|
+
this.subs.push(DG.debounce(this.tableCol.dataFrame.onMetadataChanged.pipe(operators.filter((a) => a.args.source === this.tableCol && resetTriggerTags.includes(a.args.key))), 200).subscribe((_) => {
|
|
82
|
+
this.reset();
|
|
83
|
+
}));
|
|
74
84
|
this.subs.push(DG.debounce(this.tableCol.dataFrame.onMetadataChanged.pipe(operators.filter((a) => a.args.source === this.tableCol && a.args.key === bioTAGS.positionShift)), 200).subscribe((_) => {
|
|
75
85
|
this.reset();
|
|
76
86
|
}));
|
|
77
87
|
}
|
|
78
88
|
}
|
|
89
|
+
calculateFontBasedSpacing(g) {
|
|
90
|
+
const metrics = g.measureText('M');
|
|
91
|
+
// Get font size directly from the column's temp properties for safety.
|
|
92
|
+
// This avoids parsing the font string and breaking the props interface.
|
|
93
|
+
let fontSize = 12; // Default font size
|
|
94
|
+
if (this.tableCol?.temp[".mm.cellRenderer.fontSize" /* MmcrTemps.fontSize */]) {
|
|
95
|
+
const sizeFromCol = this.tableCol.temp[".mm.cellRenderer.fontSize" /* MmcrTemps.fontSize */];
|
|
96
|
+
if (typeof sizeFromCol === 'number' && !isNaN(sizeFromCol))
|
|
97
|
+
fontSize = Math.max(sizeFromCol, 1);
|
|
98
|
+
}
|
|
99
|
+
// Line height is calculated based on the safe font size.
|
|
100
|
+
const lineHeight = Math.max(fontSize * 1.4, metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent + 4);
|
|
101
|
+
// Monomer spacing is proportional to character width.
|
|
102
|
+
const monomerSpacing = Math.max(2, this.props.fontCharWidth * 0.2);
|
|
103
|
+
return { lineHeight, monomerSpacing };
|
|
104
|
+
}
|
|
105
|
+
shouldUseMultilineRendering(tableCol) {
|
|
106
|
+
const renderMultiline = tableCol.getTag('renderMultiline');
|
|
107
|
+
return renderMultiline === 'true';
|
|
108
|
+
}
|
|
109
|
+
calculateMultiLineLayoutDynamic(g, w, h, subParts, positionShift, maxLengthOfMonomer) {
|
|
110
|
+
// --- 1. Setup ---
|
|
111
|
+
const { lineHeight, monomerSpacing } = this.calculateFontBasedSpacing(g);
|
|
112
|
+
const availableWidth = w - (this.padding * 2);
|
|
113
|
+
// --- 2. Find the widest monomer in the sequence to set a uniform column width ---
|
|
114
|
+
let maxMonomerWidth = 0;
|
|
115
|
+
const monomers = [];
|
|
116
|
+
if (subParts.length > 0) {
|
|
117
|
+
for (let i = positionShift; i < subParts.length; i++) {
|
|
118
|
+
const om = subParts.getOriginal(i);
|
|
119
|
+
const shortMon = this.props.monomerToShort(om, maxLengthOfMonomer);
|
|
120
|
+
monomers.push({ text: shortMon, posIdx: i });
|
|
121
|
+
maxMonomerWidth = Math.max(maxMonomerWidth, g.measureText(shortMon).width);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (monomers.length === 0)
|
|
125
|
+
return { lineLayouts: [], lineHeight: lineHeight };
|
|
126
|
+
// --- 3. Calculate how many uniform columns can fit ---
|
|
127
|
+
const uniformColumnWidth = maxMonomerWidth;
|
|
128
|
+
let colsPerLine = Math.floor((availableWidth + monomerSpacing) / (uniformColumnWidth + monomerSpacing));
|
|
129
|
+
colsPerLine = Math.max(1, colsPerLine);
|
|
130
|
+
// --- 4. Generate the final grid layout ---
|
|
131
|
+
const availableHeightForLines = h - (this.padding * 2);
|
|
132
|
+
const linesToRenderCount = Math.max(0, Math.floor(availableHeightForLines / lineHeight));
|
|
133
|
+
const lineLayouts = [];
|
|
134
|
+
let monomerIdx = 0;
|
|
135
|
+
for (let lineIdx = 0; lineIdx < linesToRenderCount && monomerIdx < monomers.length; lineIdx++) {
|
|
136
|
+
const elementsForLine = [];
|
|
137
|
+
for (let colIdx = 0; colIdx < colsPerLine && monomerIdx < monomers.length; colIdx++) {
|
|
138
|
+
const monomer = monomers[monomerIdx];
|
|
139
|
+
const xPos = this.padding + colIdx * (uniformColumnWidth + monomerSpacing);
|
|
140
|
+
elementsForLine.push({
|
|
141
|
+
posIdx: monomer.posIdx,
|
|
142
|
+
x: xPos,
|
|
143
|
+
width: uniformColumnWidth, // Use the uniform width for all slots
|
|
144
|
+
om: monomer.text,
|
|
145
|
+
isSeparator: false,
|
|
146
|
+
});
|
|
147
|
+
monomerIdx++;
|
|
148
|
+
}
|
|
149
|
+
lineLayouts.push({ lineIdx: lineIdx, elements: elementsForLine });
|
|
150
|
+
}
|
|
151
|
+
return { lineLayouts, lineHeight };
|
|
152
|
+
}
|
|
79
153
|
async init() {
|
|
80
154
|
await Promise.all([
|
|
81
155
|
(async () => {
|
|
@@ -115,6 +189,7 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
115
189
|
this._monomerLengthList = null;
|
|
116
190
|
this._monomerLengthMap = {};
|
|
117
191
|
this._monomerStructureMap = {};
|
|
192
|
+
this._cellBounds.clear();
|
|
118
193
|
super.reset();
|
|
119
194
|
this.invalidateGrid();
|
|
120
195
|
}
|
|
@@ -157,6 +232,27 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
157
232
|
}
|
|
158
233
|
return resSum;
|
|
159
234
|
}
|
|
235
|
+
getCellMonomerLengthsForSeqValue(value, width) {
|
|
236
|
+
const sh = this.seqHelper.getSeqHandler(this.tableCol);
|
|
237
|
+
const minMonWidth = this.props.separatorWidth + 1 * this.props.fontCharWidth;
|
|
238
|
+
const visibleSeqStart = this.positionShift;
|
|
239
|
+
const maxVisibleSeqLength = Math.ceil(width / minMonWidth) + visibleSeqStart;
|
|
240
|
+
const seqSS = sh.splitter(value);
|
|
241
|
+
const visibleSeqEnd = Math.min(maxVisibleSeqLength, seqSS.length);
|
|
242
|
+
const res = new Array(visibleSeqEnd - visibleSeqStart);
|
|
243
|
+
let seqWidth = 0;
|
|
244
|
+
for (let seqMonI = visibleSeqStart; seqMonI < visibleSeqEnd; ++seqMonI) {
|
|
245
|
+
const seqMonLabel = seqSS.getOriginal(seqMonI);
|
|
246
|
+
const shortMon = this.props.monomerToShort(seqMonLabel, this.monomerLengthLimit);
|
|
247
|
+
const separatorWidth = sh.isSeparator() ? this.separatorWidth : this.props.separatorWidth;
|
|
248
|
+
const seqMonWidth = separatorWidth + shortMon.length * this.props.fontCharWidth;
|
|
249
|
+
res[seqMonI - visibleSeqStart] = seqMonWidth;
|
|
250
|
+
seqWidth += seqMonWidth;
|
|
251
|
+
if (seqWidth > width)
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
return res;
|
|
255
|
+
}
|
|
160
256
|
getCellMonomerLengthsForSeq(rowIdx) {
|
|
161
257
|
const logPrefix = `${this.toLog()}.getCellMonomerLengthsForSeq()`;
|
|
162
258
|
// this.logger.debug(`${logPrefix}, start`);
|
|
@@ -185,27 +281,6 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
185
281
|
}
|
|
186
282
|
return res;
|
|
187
283
|
}
|
|
188
|
-
getCellMonomerLengthsForSeqValue(value, width) {
|
|
189
|
-
const sh = this.seqHelper.getSeqHandler(this.tableCol);
|
|
190
|
-
const minMonWidth = this.props.separatorWidth + 1 * this.props.fontCharWidth;
|
|
191
|
-
const visibleSeqStart = this.positionShift;
|
|
192
|
-
const maxVisibleSeqLength = Math.ceil(width / minMonWidth) + visibleSeqStart;
|
|
193
|
-
const seqSS = sh.splitter(value);
|
|
194
|
-
const visibleSeqEnd = Math.min(maxVisibleSeqLength, seqSS.length);
|
|
195
|
-
const res = new Array(visibleSeqEnd - visibleSeqStart);
|
|
196
|
-
let seqWidth = 0;
|
|
197
|
-
for (let seqMonI = visibleSeqStart; seqMonI < visibleSeqEnd; ++seqMonI) {
|
|
198
|
-
const seqMonLabel = seqSS.getOriginal(seqMonI);
|
|
199
|
-
const shortMon = this.props.monomerToShort(seqMonLabel, this.monomerLengthLimit);
|
|
200
|
-
const separatorWidth = sh.isSeparator() ? this.separatorWidth : this.props.separatorWidth;
|
|
201
|
-
const seqMonWidth = separatorWidth + shortMon.length * this.props.fontCharWidth;
|
|
202
|
-
res[seqMonI - visibleSeqStart] = seqMonWidth;
|
|
203
|
-
seqWidth += seqMonWidth;
|
|
204
|
-
if (seqWidth > width)
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
return res;
|
|
208
|
-
}
|
|
209
284
|
getCellMonomerLengthsForSeqMsa() {
|
|
210
285
|
var _a;
|
|
211
286
|
const logPrefix = `${this.toLog()}.getCellMonomerLengthsForSeqMsa()`;
|
|
@@ -289,116 +364,143 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
289
364
|
const isRenderedOnGrid = gridCell.grid?.canvas === g.canvas;
|
|
290
365
|
if (!this.seqHelper)
|
|
291
366
|
return;
|
|
292
|
-
const gridCol = this.gridCol;
|
|
293
367
|
const tableCol = this.tableCol;
|
|
294
|
-
const dpr = window.devicePixelRatio;
|
|
295
368
|
const positionShift = this.positionShift;
|
|
296
|
-
const logPrefix = `${this.toLog()}.render()`;
|
|
297
|
-
this.logger.debug(`${logPrefix}, start`);
|
|
298
|
-
// Cell renderer settings
|
|
299
|
-
let maxLengthOfMonomer = this.monomerLengthLimit;
|
|
300
|
-
if (mmcrTAGS.maxMonomerLength in tableCol.tags) {
|
|
301
|
-
const v = parseInt(tableCol.getTag(mmcrTAGS.maxMonomerLength));
|
|
302
|
-
maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
|
|
303
|
-
}
|
|
304
|
-
if (".mm.cellRenderer.maxMonomerLength" /* MmcrTemps.maxMonomerLength */ in tableCol.temp) {
|
|
305
|
-
const v = tableCol.temp[".mm.cellRenderer.maxMonomerLength" /* MmcrTemps.maxMonomerLength */];
|
|
306
|
-
maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
|
|
307
|
-
}
|
|
308
369
|
g.save();
|
|
309
370
|
try {
|
|
310
371
|
const sh = this.seqHelper.getSeqHandler(tableCol);
|
|
372
|
+
let maxLengthOfMonomer = this.monomerLengthLimit;
|
|
373
|
+
if (mmcrTAGS.maxMonomerLength in tableCol.tags) {
|
|
374
|
+
const v = parseInt(tableCol.getTag(mmcrTAGS.maxMonomerLength));
|
|
375
|
+
maxLengthOfMonomer = !isNaN(v) && v ? v : 50;
|
|
376
|
+
}
|
|
311
377
|
if (tableCol.temp[".mm.cellRenderer.settingsChanged" /* MmcrTemps.rendererSettingsChanged */] === rendererSettingsChangedState.true ||
|
|
312
378
|
this.monomerLengthLimit != maxLengthOfMonomer) {
|
|
379
|
+
// this if means that the mm renderer settings have changed,
|
|
380
|
+
// particularly monomer representation and max width.
|
|
313
381
|
let gapLength = 0;
|
|
314
382
|
const msaGapLength = 8;
|
|
315
383
|
gapLength = tableCol.temp[".mm.cellRenderer.gapLength" /* MmcrTemps.gapLength */] ?? gapLength;
|
|
316
|
-
// this event means that the mm renderer settings have changed,
|
|
317
|
-
// particularly monomer representation and max width.
|
|
318
384
|
this.setMonomerLengthLimit(maxLengthOfMonomer);
|
|
319
385
|
this.setSeparatorWidth(sh.isMsa() ? msaGapLength : gapLength);
|
|
320
386
|
tableCol.temp[".mm.cellRenderer.settingsChanged" /* MmcrTemps.rendererSettingsChanged */] = rendererSettingsChangedState.false;
|
|
321
387
|
}
|
|
322
|
-
let [maxLengthWords, maxLengthWordsSum] = this.getCellMonomerLengths(gridCell.tableRowIndex, w);
|
|
323
|
-
const _maxIndex = maxLengthWords.length;
|
|
324
|
-
const value = gridCell.cell.value;
|
|
325
388
|
const rowIdx = gridCell.cell.rowIndex;
|
|
326
|
-
const
|
|
327
|
-
const minDistanceRenderer = 50;
|
|
389
|
+
const value = gridCell.cell.value;
|
|
328
390
|
if (isRenderedOnGrid)
|
|
329
|
-
w = getUpdatedWidth(
|
|
391
|
+
w = getUpdatedWidth(gridCell.grid, g, x, w, window.devicePixelRatio);
|
|
330
392
|
g.beginPath();
|
|
331
|
-
g.rect(x
|
|
393
|
+
g.rect(x, y, w, h);
|
|
332
394
|
g.clip();
|
|
333
395
|
g.font = this.props?.font ?? '12px monospace';
|
|
334
396
|
g.textBaseline = 'top';
|
|
335
|
-
//TODO: can this be replaced/merged with splitSequence?
|
|
336
397
|
const units = tableCol.meta.units;
|
|
337
398
|
const aligned = tableCol.getTag(bioTAGS.aligned);
|
|
338
399
|
const separator = tableCol.getTag(bioTAGS.separator) ?? '';
|
|
339
|
-
const
|
|
340
|
-
|
|
400
|
+
const subParts = isRenderedOnGrid ? sh.getSplitted(rowIdx) : sh.splitter(value);
|
|
401
|
+
let drawStyle = DrawStyle.classic;
|
|
402
|
+
if (aligned?.includes('MSA') && units === NOTATION.SEPARATOR)
|
|
403
|
+
drawStyle = DrawStyle.MSA;
|
|
341
404
|
const tempReferenceSequence = tableCol.temp["reference-sequence" /* tempTAGS.referenceSequence */];
|
|
342
405
|
const tempCurrentWord = this.tableCol.temp["current-word" /* tempTAGS.currentWord */];
|
|
343
|
-
if (tempCurrentWord && tableCol?.dataFrame?.currentRowIdx === -1)
|
|
344
|
-
this.tableCol.temp["current-word" /* tempTAGS.currentWord */] = null;
|
|
345
406
|
const referenceSequence = (() => {
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
const seqSS = splitterFunc(((tempReferenceSequence != null) && (tempReferenceSequence != '')) ?
|
|
407
|
+
const splitterFunc = sh.splitter;
|
|
408
|
+
const seqSS = splitterFunc(((tempReferenceSequence != null) && (tempReferenceSequence !== '')) ?
|
|
349
409
|
tempReferenceSequence : tempCurrentWord ?? '');
|
|
350
|
-
return wu.count(0).take(seqSS.length).slice(positionShift).map((posIdx) => seqSS.
|
|
410
|
+
return wu.count(0).take(seqSS.length).slice(positionShift).map((posIdx) => seqSS.getCanonical(posIdx)).toArray();
|
|
351
411
|
})();
|
|
352
|
-
const subParts = isRenderedOnGrid ? sh.getSplitted(rowIdx) : sh.splitter(value);
|
|
353
|
-
if (!isRenderedOnGrid)
|
|
354
|
-
maxLengthWordsSum = this.getSummedMonomerLengths(this.getCellMonomerLengthsForSeqValue(value, w));
|
|
355
|
-
let drawStyle = DrawStyle.classic;
|
|
356
|
-
if (aligned && aligned.includes('MSA') && units == NOTATION.SEPARATOR)
|
|
357
|
-
drawStyle = DrawStyle.MSA;
|
|
358
|
-
// if the sequence is rendered in shifted mode, we will also render three dots at start, indicating the shift
|
|
359
|
-
this._leftThreeDotsPadding = this.shouldRenderShiftedThreeDots(positionShift) ? g.measureText(shiftedLeftPaddingText).width : 0;
|
|
360
|
-
// currently selected position to highlight
|
|
361
412
|
const selectedPosition = Number.parseInt(tableCol.getTag(bioTAGS.selectedPosition) ?? '-200');
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
413
|
+
const shouldUseMultiLine = this.shouldUseMultilineRendering(tableCol) && drawStyle !== DrawStyle.MSA;
|
|
414
|
+
if (shouldUseMultiLine) {
|
|
415
|
+
const currentCellBounds = [];
|
|
416
|
+
const layout = this.calculateMultiLineLayoutDynamic(g, w, h, subParts, positionShift, maxLengthOfMonomer);
|
|
417
|
+
// --- NEW: Vertical Centering Logic for Single Lines ---
|
|
418
|
+
// Default to top-aligned layout
|
|
419
|
+
let yBase = y + this.padding;
|
|
420
|
+
// If there's exactly one line of content, calculate a new base Y to center it vertically.
|
|
421
|
+
if (layout.lineLayouts.length === 1)
|
|
422
|
+
yBase = y + (h - layout.lineHeight) / 2;
|
|
423
|
+
// --- End of New Logic ---
|
|
424
|
+
for (const lineLayout of layout.lineLayouts) {
|
|
425
|
+
// The Y position for each line is now based on our calculated `yBase`.
|
|
426
|
+
const lineY = yBase + (lineLayout.lineIdx * layout.lineHeight);
|
|
427
|
+
for (const element of lineLayout.elements) {
|
|
428
|
+
const elementX = x + element.x;
|
|
429
|
+
const monomer = element;
|
|
430
|
+
const monomerIndex = monomer.posIdx;
|
|
431
|
+
const cm = subParts.getCanonical(monomerIndex);
|
|
432
|
+
let color = undefinedColor;
|
|
433
|
+
const monomerLib = this.getMonomerLib();
|
|
434
|
+
if (monomerLib)
|
|
435
|
+
color = monomerLib.getMonomerTextColor(sh.defaultBiotype, cm);
|
|
436
|
+
let transparencyRate = 0.0;
|
|
437
|
+
if (gridCell.tableRowIndex !== tableCol.dataFrame.currentRowIdx && referenceSequence.length > 0) {
|
|
438
|
+
const refIndex = monomerIndex - positionShift;
|
|
439
|
+
if (refIndex >= 0 && refIndex < referenceSequence.length) {
|
|
440
|
+
const currentMonomerCanonical = cm;
|
|
441
|
+
const refMonomerCanonical = referenceSequence[refIndex];
|
|
442
|
+
if (currentMonomerCanonical === refMonomerCanonical)
|
|
443
|
+
transparencyRate = 0.7;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
currentCellBounds.push({
|
|
447
|
+
lineIdx: lineLayout.lineIdx,
|
|
448
|
+
monomerIdx: monomerIndex - positionShift,
|
|
449
|
+
bounds: new DG.Rect(element.x, (lineY - y), element.width, layout.lineHeight),
|
|
450
|
+
sequencePosition: monomerIndex,
|
|
451
|
+
});
|
|
452
|
+
printLeftOrCentered(g, monomer.om, elementX, lineY, element.width, layout.lineHeight, {
|
|
453
|
+
color: color,
|
|
454
|
+
isMultiLineContext: true,
|
|
455
|
+
transparencyRate: transparencyRate,
|
|
456
|
+
selectedPosition: isNaN(selectedPosition) || selectedPosition < 1 ? undefined : selectedPosition,
|
|
457
|
+
wordIdx: monomerIndex
|
|
458
|
+
});
|
|
459
|
+
}
|
|
372
460
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
/*x1 = */
|
|
376
|
-
const opts = {
|
|
377
|
-
color: color, pivot: 0, left: true, transparencyRate: 1.0, separator: separator, last: last,
|
|
378
|
-
drawStyle: drawStyle, maxWord: maxLengthWordsSum, wordIdx: posIdx - positionShift, gridCell: gridCell,
|
|
379
|
-
referenceSequence: referenceSequence, maxLengthOfMonomer: maxLengthOfMonomer,
|
|
380
|
-
monomerTextSizeMap: this._monomerLengthMap, logger: this.logger,
|
|
381
|
-
selectedPosition: isNaN(selectedPosition) || selectedPosition < 1 ? undefined : selectedPosition - positionShift,
|
|
382
|
-
};
|
|
383
|
-
printLeftOrCentered(g, om, x + this.padding + this._leftThreeDotsPadding, y, w, h, opts);
|
|
384
|
-
if (minDistanceRenderer > w)
|
|
385
|
-
break;
|
|
461
|
+
if (gridCell.tableRowIndex !== null)
|
|
462
|
+
this._cellBounds.set(gridCell.tableRowIndex, currentCellBounds);
|
|
386
463
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
464
|
+
else {
|
|
465
|
+
// --- Single-line rendering path (is unchanged) ---
|
|
466
|
+
this._leftThreeDotsPadding = this.shouldRenderShiftedThreeDots(positionShift) ? g.measureText(shiftedLeftPaddingText).width : 0;
|
|
467
|
+
let [, maxLengthWordsSum] = this.getCellMonomerLengths(gridCell.tableRowIndex, w);
|
|
468
|
+
if (!isRenderedOnGrid)
|
|
469
|
+
maxLengthWordsSum = this.getSummedMonomerLengths(this.getCellMonomerLengthsForSeqValue(value, w));
|
|
470
|
+
const minMonomerWidth = this.props.separatorWidth + 1 * this.props.fontCharWidth;
|
|
471
|
+
const visibleSeqLength = Math.min(subParts.length, Math.ceil(w / (minMonomerWidth)) + positionShift);
|
|
472
|
+
for (let posIdx = positionShift; posIdx < visibleSeqLength; ++posIdx) {
|
|
473
|
+
const om = posIdx < subParts.length ? subParts.getOriginal(posIdx) : sh.defaultGapOriginal;
|
|
474
|
+
const cm = posIdx < subParts.length ? subParts.getCanonical(posIdx) : sh.defaultGapOriginal;
|
|
475
|
+
let color = undefinedColor;
|
|
476
|
+
if (this.getMonomerLib())
|
|
477
|
+
color = this.getMonomerLib().getMonomerTextColor(sh.defaultBiotype, cm);
|
|
478
|
+
const last = posIdx === subParts.length - 1;
|
|
479
|
+
const opts = {
|
|
480
|
+
color: color, pivot: 0, left: true, transparencyRate: 0.0,
|
|
481
|
+
separator: separator, last: last,
|
|
482
|
+
drawStyle: drawStyle, maxWord: maxLengthWordsSum, wordIdx: posIdx - positionShift, gridCell: gridCell,
|
|
483
|
+
referenceSequence: referenceSequence, maxLengthOfMonomer: maxLengthOfMonomer,
|
|
484
|
+
monomerTextSizeMap: this._monomerLengthMap, logger: this.logger,
|
|
485
|
+
selectedPosition: isNaN(selectedPosition) || selectedPosition < 1 ? undefined : selectedPosition - positionShift,
|
|
486
|
+
};
|
|
487
|
+
printLeftOrCentered(g, om, x + this.padding + this._leftThreeDotsPadding, y, w, h, opts);
|
|
488
|
+
}
|
|
489
|
+
if (this.shouldRenderShiftedThreeDots(positionShift)) {
|
|
490
|
+
const opts = {
|
|
491
|
+
color: undefinedColor, pivot: 0, left: true, transparencyRate: 0, separator: separator, last: false,
|
|
492
|
+
drawStyle: drawStyle, maxWord: maxLengthWordsSum, wordIdx: 0, gridCell: gridCell,
|
|
493
|
+
maxLengthOfMonomer: maxLengthOfMonomer,
|
|
494
|
+
monomerTextSizeMap: this._monomerLengthMap, logger: this.logger,
|
|
495
|
+
};
|
|
496
|
+
printLeftOrCentered(g, shiftedLeftPaddingText, x + this.padding, y, w, h, opts);
|
|
497
|
+
}
|
|
395
498
|
}
|
|
396
499
|
}
|
|
397
500
|
catch (err) {
|
|
398
501
|
const [errMsg, errStack] = errInfo(err);
|
|
399
502
|
this.logger.error(errMsg, undefined, errStack);
|
|
400
503
|
this.errors.push(err);
|
|
401
|
-
//throw err; // Do not throw to prevent disabling renderer
|
|
402
504
|
}
|
|
403
505
|
finally {
|
|
404
506
|
g.restore();
|
|
@@ -411,21 +513,29 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
411
513
|
const logPrefix = `${this.toLog()}.onMouseMove()`;
|
|
412
514
|
if (!this.seqHelper || gridCell.tableRowIndex == null)
|
|
413
515
|
return;
|
|
414
|
-
// if (gridCell.cell.column.getTag(bioTAGS.aligned) !== ALIGNMENT.SEQ_MSA)
|
|
415
|
-
// return;
|
|
416
516
|
const positionShift = this.positionShift;
|
|
417
517
|
const gridCellBounds = gridCell.bounds;
|
|
418
|
-
// const value: any = gridCell.cell.value;
|
|
419
|
-
//
|
|
420
|
-
// const maxLengthWords: number[] = seqColTemp.getCellMonomerLengths(gridCell.tableRowIndex!);
|
|
421
|
-
// const maxLengthWordsSum: number[] = new Array<number>(maxLengthWords.length).fill(0);
|
|
422
|
-
// for (let posI: number = 1; posI < maxLengthWords.length; posI++)
|
|
423
|
-
// maxLengthWordsSum[posI] = maxLengthWordsSum[posI - 1] + maxLengthWords[posI];
|
|
424
|
-
// const maxIndex = maxLengthWords.length;
|
|
425
518
|
const argsX = e.offsetX - gridCell.gridColumn.left + (gridCell.gridColumn.left - gridCellBounds.x);
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
this.
|
|
519
|
+
const argsY = e.offsetY - gridCellBounds.y;
|
|
520
|
+
// Reset cursor to default, as ellipsis is gone
|
|
521
|
+
if (this.gridCol?.grid?.canvas)
|
|
522
|
+
this.gridCol.grid.canvas.style.cursor = 'default';
|
|
523
|
+
let left = null;
|
|
524
|
+
const boundsForCell = this._cellBounds.get(gridCell.tableRowIndex);
|
|
525
|
+
if (boundsForCell) {
|
|
526
|
+
for (const bound of boundsForCell) {
|
|
527
|
+
if (bound.bounds.contains(argsX, argsY)) {
|
|
528
|
+
left = bound.monomerIdx;
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
// Single-line hit detection
|
|
535
|
+
const leftPadding = this.shouldRenderShiftedThreeDots(positionShift) && (this._leftThreeDotsPadding ?? 0) > 0 ? this._leftThreeDotsPadding : 0;
|
|
536
|
+
left = this.getPosition(gridCell.tableRowIndex, argsX, gridCellBounds.width, leftPadding);
|
|
537
|
+
}
|
|
538
|
+
this.logger.debug(`${logPrefix}, argsX: ${argsX}, argsY: ${argsY}, left: ${left}`);
|
|
429
539
|
const sh = this.seqHelper.getSeqHandler(this.tableCol);
|
|
430
540
|
const seqSS = sh.getSplitted(gridCell.tableRowIndex);
|
|
431
541
|
if (left !== null && left >= 0 && left + positionShift < seqSS.length) {
|
|
@@ -437,7 +547,7 @@ export class MonomerPlacer extends CellRendererBackBase {
|
|
|
437
547
|
};
|
|
438
548
|
const tooltipElements = [];
|
|
439
549
|
let monomerDiv = this._monomerStructureMap[seqMonomer.symbol];
|
|
440
|
-
if (!monomerDiv
|
|
550
|
+
if (!monomerDiv) {
|
|
441
551
|
const monomerLib = this.getMonomerLib();
|
|
442
552
|
monomerDiv = this._monomerStructureMap[seqMonomer.symbol] = (() => {
|
|
443
553
|
return monomerLib ? monomerLib.getTooltip(seqMonomer.biotype, seqMonomer.symbol) :
|