@datagrok/sequence-translator 1.2.6 → 1.2.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/sequence-translator",
|
|
3
3
|
"friendlyName": "Sequence Translator",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.7",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexey Choposky",
|
|
7
7
|
"email": "achopovsky@datagrok.ai"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@datagrok-libraries/utils": "^1.17.2",
|
|
20
20
|
"@types/react": "^18.0.15",
|
|
21
21
|
"cash-dom": "^8.1.0",
|
|
22
|
-
"datagrok-api": "^1.
|
|
22
|
+
"datagrok-api": "^1.17.4",
|
|
23
23
|
"openchemlib": "6.0.1",
|
|
24
24
|
"save-svg-as-png": "^1.4.17",
|
|
25
25
|
"ts-loader": "^9.3.1",
|
|
@@ -3,6 +3,9 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
5
|
|
|
6
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
7
|
+
import {ALPHABET, NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
|
+
|
|
6
9
|
import * as rxjs from 'rxjs';
|
|
7
10
|
import '../style/translator-app.css';
|
|
8
11
|
|
|
@@ -12,7 +15,7 @@ import {SequenceToMolfileConverter} from '../../model/structure-app/sequence-to-
|
|
|
12
15
|
import {getTranslatedSequences} from '../../model/translator-app/conversion-utils';
|
|
13
16
|
import {MoleculeImage} from '../utils/molecule-img';
|
|
14
17
|
import {download} from '../../model/helpers';
|
|
15
|
-
import {SEQUENCE_COPIED_MSG, SEQ_TOOLTIP_MSG} from '../const/oligo-translator';
|
|
18
|
+
import {SEQUENCE_COPIED_MSG, SEQ_TOOLTIP_MSG, NUCLEOTIDES} from '../const/oligo-translator';
|
|
16
19
|
import {DEFAULT_AXOLABS_INPUT} from '../const/ui';
|
|
17
20
|
import {FormatDetector} from '../../model/parsing-validation/format-detector';
|
|
18
21
|
import {SequenceValidator} from '../../model/parsing-validation/sequence-validator';
|
|
@@ -20,9 +23,15 @@ import {FormatConverter} from '../../model/translator-app/format-converter';
|
|
|
20
23
|
import {codesToHelmDictionary} from '../../model/data-loading-utils/json-loader';
|
|
21
24
|
import {DEFAULT_FORMATS} from '../../model/const';
|
|
22
25
|
|
|
26
|
+
const enum REQUIRED_COLUMN_LABEL {
|
|
27
|
+
SEQUENCE = 'Sequence',
|
|
28
|
+
}
|
|
29
|
+
const REQUIRED_COLUMN_LABELS = [ REQUIRED_COLUMN_LABEL.SEQUENCE ];
|
|
30
|
+
|
|
23
31
|
export class TranslatorLayoutHandler {
|
|
32
|
+
private eventBus: EventBus;
|
|
33
|
+
private inputFormats = Object.keys(codesToHelmDictionary).concat(DEFAULT_FORMATS.HELM);
|
|
24
34
|
constructor() {
|
|
25
|
-
const INPUT_FORMATS = Object.keys(codesToHelmDictionary).concat(DEFAULT_FORMATS.HELM);
|
|
26
35
|
this.moleculeImgDiv = ui.div([]);
|
|
27
36
|
this.moleculeImgDiv.className = 'mol-host';
|
|
28
37
|
this.moleculeImgDiv.style.border = '1px solid var(--grey-2)';
|
|
@@ -30,7 +39,7 @@ export class TranslatorLayoutHandler {
|
|
|
30
39
|
this.moleculeImgDiv.style.marginTop = '12px';
|
|
31
40
|
|
|
32
41
|
this.outputTableDiv = ui.div([]);
|
|
33
|
-
this.formatChoiceInput = ui.choiceInput('', DEFAULT_FORMATS.HELM,
|
|
42
|
+
this.formatChoiceInput = ui.choiceInput('', DEFAULT_FORMATS.HELM, this.inputFormats, async () => {
|
|
34
43
|
this.format = this.formatChoiceInput.value;
|
|
35
44
|
this.updateTable();
|
|
36
45
|
await this.updateMolImg();
|
|
@@ -46,6 +55,8 @@ export class TranslatorLayoutHandler {
|
|
|
46
55
|
this.updateTable();
|
|
47
56
|
await this.updateMolImg();
|
|
48
57
|
});
|
|
58
|
+
|
|
59
|
+
this.eventBus = EventBus.getInstance();
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
// todo: reduce # of state vars by further refactoring legacy code
|
|
@@ -59,6 +70,97 @@ export class TranslatorLayoutHandler {
|
|
|
59
70
|
private format: string | null;
|
|
60
71
|
|
|
61
72
|
async getHtmlElement(): Promise<HTMLDivElement> {
|
|
73
|
+
const singleSequenceControls = this.constructSingleSequenceControls();
|
|
74
|
+
const bulkControls = this.constructBulkTranslationControls();
|
|
75
|
+
const layout = ui.box(
|
|
76
|
+
ui.panel([
|
|
77
|
+
singleSequenceControls,
|
|
78
|
+
bulkControls,
|
|
79
|
+
ui.block([ui.box(this.moleculeImgDiv)])
|
|
80
|
+
]),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
this.formatChoiceInput.value = this.format;
|
|
84
|
+
this.updateTable();
|
|
85
|
+
await this.updateMolImg();
|
|
86
|
+
return layout;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private constructBulkTranslationControls(): HTMLDivElement {
|
|
90
|
+
const title = ui.h1('Bulk');
|
|
91
|
+
ui.tooltip.bind(title, 'Bulk translation from table input');
|
|
92
|
+
|
|
93
|
+
const tableControlsManager = new TableControlsManager(this.eventBus);
|
|
94
|
+
const tableControls = tableControlsManager.createUIComponents();
|
|
95
|
+
const inputFormats = ui.choiceInput('Input format', DEFAULT_FORMATS.AXOLABS, this.inputFormats, (value: string) => this.eventBus.selectInputFormat(value));
|
|
96
|
+
const outputFormats = ui.choiceInput('Output format', NUCLEOTIDES, [NUCLEOTIDES], () => {});
|
|
97
|
+
const convertBulkButton = this.createConvertBulkButton();
|
|
98
|
+
|
|
99
|
+
const tableControlsContainer = ui.div([
|
|
100
|
+
...tableControls,
|
|
101
|
+
inputFormats,
|
|
102
|
+
outputFormats,
|
|
103
|
+
convertBulkButton
|
|
104
|
+
], 'ui-form');
|
|
105
|
+
|
|
106
|
+
const bulkTranslationControls = ui.block25([
|
|
107
|
+
title,
|
|
108
|
+
tableControlsContainer,
|
|
109
|
+
]);
|
|
110
|
+
return bulkTranslationControls;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private createConvertBulkButton(): HTMLButtonElement {
|
|
114
|
+
const convertBulkButton = ui.bigButton('Convert', () => this.processConvertBulkButtonClick());
|
|
115
|
+
|
|
116
|
+
ui.tooltip.bind(convertBulkButton, 'Convert sequences from table input');
|
|
117
|
+
$(convertBulkButton).css({
|
|
118
|
+
'float': 'right',
|
|
119
|
+
'margin-top': '20px',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return convertBulkButton;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private processConvertBulkButtonClick(): void {
|
|
126
|
+
const selectedTable = this.eventBus.getSelectedTable();
|
|
127
|
+
if (!selectedTable) {
|
|
128
|
+
grok.shell.warning('No table selected');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const inputFormat = this.eventBus.getSelectedInputFormat();
|
|
133
|
+
const outputFormat = NUCLEOTIDES;
|
|
134
|
+
const sequenceColumn = this.eventBus.getSelectedColumn(REQUIRED_COLUMN_LABEL.SEQUENCE);
|
|
135
|
+
if (!sequenceColumn) {
|
|
136
|
+
grok.shell.warning('No sequence column selected');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const newColumnName = `${sequenceColumn.name} (${outputFormat})`;
|
|
141
|
+
const translatedColumn = DG.Column.fromList(
|
|
142
|
+
DG.TYPE.STRING,
|
|
143
|
+
newColumnName,
|
|
144
|
+
sequenceColumn.toList().map((sequence: string) => {
|
|
145
|
+
const translatedSequences = getTranslatedSequences(sequence, -1, inputFormat);
|
|
146
|
+
return translatedSequences[outputFormat];
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
translatedColumn.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
151
|
+
translatedColumn.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
|
|
152
|
+
const unitsHandler = UnitsHandler.getOrCreate(translatedColumn);
|
|
153
|
+
UnitsHandler.setUnitsToFastaColumn(unitsHandler);
|
|
154
|
+
|
|
155
|
+
// add newColumn to the table
|
|
156
|
+
selectedTable.columns.add(translatedColumn);
|
|
157
|
+
// update the view
|
|
158
|
+
|
|
159
|
+
grok.data.detectSemanticTypes(selectedTable);
|
|
160
|
+
grok.shell.v = grok.shell.getTableView(selectedTable.name);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private constructSingleSequenceControls(): HTMLDivElement {
|
|
62
164
|
const sequenceColoredInput = new ColoredTextInput(this.sequenceInputBase, highlightInvalidSubsequence);
|
|
63
165
|
|
|
64
166
|
const downloadMolfileButton = ui.button(
|
|
@@ -84,29 +186,24 @@ export class TranslatorLayoutHandler {
|
|
|
84
186
|
textInput: sequenceColoredInput.root,
|
|
85
187
|
clearBtn: clearButton
|
|
86
188
|
};
|
|
87
|
-
const
|
|
189
|
+
const singleSequenceInputControls = ui.table(
|
|
88
190
|
[inputTableRow], (item) => [item.format, item.textInput, item.clearBtn]
|
|
89
191
|
);
|
|
90
|
-
|
|
192
|
+
singleSequenceInputControls.classList.add('st-translator-input-table');
|
|
91
193
|
|
|
92
|
-
const
|
|
194
|
+
const singleSequenceOutputTable = ui.block([
|
|
93
195
|
this.outputTableDiv,
|
|
94
196
|
downloadMolfileButton,
|
|
95
197
|
copySmilesButton,
|
|
96
198
|
]);
|
|
97
199
|
|
|
98
|
-
const
|
|
99
|
-
ui.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
]),
|
|
104
|
-
);
|
|
200
|
+
const singleSequenceControls = ui.block75([
|
|
201
|
+
ui.h1('Single sequence'),
|
|
202
|
+
singleSequenceInputControls,
|
|
203
|
+
singleSequenceOutputTable,
|
|
204
|
+
])
|
|
105
205
|
|
|
106
|
-
|
|
107
|
-
this.updateTable();
|
|
108
|
-
await this.updateMolImg();
|
|
109
|
-
return mainTabBody;
|
|
206
|
+
return singleSequenceControls;
|
|
110
207
|
}
|
|
111
208
|
|
|
112
209
|
private saveMolfile(): void {
|
|
@@ -182,3 +279,223 @@ export class TranslatorLayoutHandler {
|
|
|
182
279
|
return molfile;
|
|
183
280
|
}
|
|
184
281
|
}
|
|
282
|
+
|
|
283
|
+
// todo: port to dedicated file, together with event bus
|
|
284
|
+
class TableControlsManager {
|
|
285
|
+
private tableInputManager: TableInputManager;
|
|
286
|
+
private columnInputManager: ColumnInputsManager;
|
|
287
|
+
|
|
288
|
+
constructor(eventBus: EventBus) {
|
|
289
|
+
this.tableInputManager = new TableInputManager(eventBus);
|
|
290
|
+
this.columnInputManager = new ColumnInputsManager(eventBus);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
createUIComponents(): HTMLElement[] {
|
|
294
|
+
const tableInput = this.tableInputManager.getTableInputContainer();
|
|
295
|
+
const columnControls = this.columnInputManager.getColumnControlsContainer();
|
|
296
|
+
|
|
297
|
+
return [
|
|
298
|
+
tableInput,
|
|
299
|
+
columnControls,
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
class TableInputManager {
|
|
305
|
+
private availableTables: DG.DataFrame[] = [];
|
|
306
|
+
private tableInputContainer: HTMLDivElement = ui.div([]);
|
|
307
|
+
|
|
308
|
+
constructor(private eventBus: EventBus) {
|
|
309
|
+
this.subscribeToTableEvents();
|
|
310
|
+
this.refreshTableInput();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
getTableInputContainer(): HTMLDivElement {
|
|
314
|
+
return this.tableInputContainer;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private subscribeToTableEvents(): void {
|
|
318
|
+
grok.events.onTableAdded.subscribe((eventData) => this.handleTableAdded(eventData));
|
|
319
|
+
grok.events.onTableRemoved.subscribe((eventData) => this.handleTableRemoved(eventData));
|
|
320
|
+
this.eventBus.tableSelected$.subscribe(() => this.handleTableChoice());
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private getTableFromEventData(eventData: any): DG.DataFrame {
|
|
324
|
+
if (! eventData && eventData.args && eventData.args.dataFrame instanceof DG.DataFrame)
|
|
325
|
+
throw new Error(`EventData does not contain a dataframe`, eventData);
|
|
326
|
+
|
|
327
|
+
return eventData.args.dataFrame as DG.DataFrame;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
private handleTableAdded(eventData: any): void {
|
|
331
|
+
const table = this.getTableFromEventData(eventData);
|
|
332
|
+
|
|
333
|
+
if (this.availableTables.some((t: DG.DataFrame) => t.name === table.name))
|
|
334
|
+
return;
|
|
335
|
+
|
|
336
|
+
this.availableTables.push(table);
|
|
337
|
+
this.eventBus.selectTable(table);
|
|
338
|
+
this.refreshTableInput();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private handleTableRemoved(eventData: any): void {
|
|
342
|
+
const removedTable = this.getTableFromEventData(eventData);
|
|
343
|
+
this.availableTables = this.availableTables.filter((table: DG.DataFrame) => table.name !== removedTable.name);
|
|
344
|
+
|
|
345
|
+
const table = this.availableTables[0];
|
|
346
|
+
this.eventBus.selectTable(table ? table : null);
|
|
347
|
+
this.refreshTableInput();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private refreshTableInput(): void {
|
|
351
|
+
const tableInput = this.createTableInput();
|
|
352
|
+
$(this.tableInputContainer).empty();
|
|
353
|
+
this.tableInputContainer.append(tableInput.root);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private createTableInput(): DG.InputBase<DG.DataFrame | null> {
|
|
357
|
+
const currentlySelectedTable = this.eventBus.getSelectedTable();
|
|
358
|
+
|
|
359
|
+
const tableInput = ui.tableInput(
|
|
360
|
+
'Table',
|
|
361
|
+
currentlySelectedTable,
|
|
362
|
+
this.availableTables,
|
|
363
|
+
(table: DG.DataFrame) => {
|
|
364
|
+
// WARNING: non-null check necessary to prevent resetting columns to
|
|
365
|
+
// null upon handling onTableAdded
|
|
366
|
+
if (table !== null && table instanceof DG.DataFrame)
|
|
367
|
+
this.eventBus.selectTable(table);
|
|
368
|
+
});
|
|
369
|
+
return tableInput;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private handleTableChoice(): void {
|
|
373
|
+
const selectedTable = this.eventBus.getSelectedTable();
|
|
374
|
+
if (!selectedTable) return;
|
|
375
|
+
if (!this.isTableDisplayed(selectedTable))
|
|
376
|
+
this.displayTable(selectedTable);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private isTableDisplayed(table: DG.DataFrame): boolean {
|
|
380
|
+
return grok.shell.tableNames.includes(table.name);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private displayTable(table: DG.DataFrame): void {
|
|
384
|
+
const previousView = grok.shell.v;
|
|
385
|
+
grok.shell.addTableView(table);
|
|
386
|
+
grok.shell.v = previousView;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
class ColumnInputsManager {
|
|
391
|
+
private columnControlsContainer: HTMLDivElement = ui.div([]);
|
|
392
|
+
|
|
393
|
+
constructor(private eventBus: EventBus) {
|
|
394
|
+
this.eventBus.tableSelected$.subscribe(() => this.handleTableChoice());
|
|
395
|
+
this.refreshColumnControls();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
getColumnControlsContainer(): HTMLDivElement {
|
|
399
|
+
return this.columnControlsContainer;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
private handleTableChoice(): void {
|
|
403
|
+
this.refreshColumnControls();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private refreshColumnControls(): void {
|
|
407
|
+
const columnInputs = this.createColumnInputs();
|
|
408
|
+
$(this.columnControlsContainer).empty();
|
|
409
|
+
const inputRoots = columnInputs.map((input) => input.root);
|
|
410
|
+
this.columnControlsContainer.append(
|
|
411
|
+
...inputRoots
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private createColumnInputs(): DG.ChoiceInput<string | null>[] {
|
|
416
|
+
const selectedTable = this.eventBus.getSelectedTable();
|
|
417
|
+
const columnNames = selectedTable !== null ?
|
|
418
|
+
selectedTable.columns.names().sort((a, b) => a.localeCompare(b)) : [];
|
|
419
|
+
|
|
420
|
+
const columnInputs = REQUIRED_COLUMN_LABELS.map((columnLabel: REQUIRED_COLUMN_LABEL) => {
|
|
421
|
+
const input = this.createColumnInput(columnLabel, columnNames, selectedTable);
|
|
422
|
+
return input;
|
|
423
|
+
});
|
|
424
|
+
return columnInputs;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private createColumnInput(
|
|
428
|
+
columnLabel: REQUIRED_COLUMN_LABEL,
|
|
429
|
+
columnNames: string[],
|
|
430
|
+
selectedTable: DG.DataFrame | null
|
|
431
|
+
): DG.ChoiceInput<string | null> {
|
|
432
|
+
const namePattern = columnLabel.toLowerCase();
|
|
433
|
+
const matchingColumnName = columnNames.find((name: string) => name.toLowerCase().includes(namePattern));
|
|
434
|
+
const selectedColumnName = matchingColumnName ? matchingColumnName : columnNames[0];
|
|
435
|
+
this.selectColumnIfTableNotNull(selectedTable, selectedColumnName, columnLabel);
|
|
436
|
+
|
|
437
|
+
const input = ui.choiceInput(
|
|
438
|
+
`${columnLabel}`,
|
|
439
|
+
selectedColumnName, columnNames,
|
|
440
|
+
(colName: string) => this.selectColumnIfTableNotNull(selectedTable, colName, columnLabel)
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
return input;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private selectColumnIfTableNotNull(
|
|
447
|
+
table: DG.DataFrame | null, columnName: string, columnLabel: REQUIRED_COLUMN_LABEL
|
|
448
|
+
): void {
|
|
449
|
+
if (table !== null) {
|
|
450
|
+
const selectedColumn = table.getCol(columnName);
|
|
451
|
+
this.eventBus.selectColumn(columnLabel, selectedColumn);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
export class EventBus {
|
|
457
|
+
private static _instance: EventBus;
|
|
458
|
+
|
|
459
|
+
private _tableSelection$ = new rxjs.BehaviorSubject<DG.DataFrame | null>(null);
|
|
460
|
+
private _columnSelection = Object.fromEntries(REQUIRED_COLUMN_LABELS.map((columnLabel: string) => {
|
|
461
|
+
const columnSelection$ = new rxjs.BehaviorSubject<DG.Column<string> | null>(null);
|
|
462
|
+
return [columnLabel, columnSelection$];
|
|
463
|
+
}));
|
|
464
|
+
private _inputFormatSelection$ = new rxjs.BehaviorSubject<string>(DEFAULT_FORMATS.AXOLABS);
|
|
465
|
+
|
|
466
|
+
private constructor() {}
|
|
467
|
+
|
|
468
|
+
public static getInstance(): EventBus {
|
|
469
|
+
if (EventBus._instance === undefined)
|
|
470
|
+
EventBus._instance = new EventBus();
|
|
471
|
+
return EventBus._instance;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
get tableSelected$(): rxjs.Observable<DG.DataFrame | null> {
|
|
475
|
+
return this._tableSelection$.asObservable();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
getSelectedTable(): DG.DataFrame | null {
|
|
479
|
+
return this._tableSelection$.getValue();
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
selectTable(table: DG.DataFrame | null): void {
|
|
483
|
+
this._tableSelection$.next(table);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
selectColumn(columnLabel: REQUIRED_COLUMN_LABEL, column: DG.Column<string>): void {
|
|
487
|
+
this._columnSelection[columnLabel].next(column);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
getSelectedColumn(columnLabel: REQUIRED_COLUMN_LABEL): DG.Column<string> | null {
|
|
491
|
+
return this._columnSelection[columnLabel].getValue();
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
getSelectedInputFormat(): string {
|
|
495
|
+
return this._inputFormatSelection$.getValue();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
selectInputFormat(format: string): void {
|
|
499
|
+
this._inputFormatSelection$.next(format);
|
|
500
|
+
}
|
|
501
|
+
}
|