@datagrok/sequence-translator 1.1.5 → 1.2.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/package-test.js +1 -1
  3. package/dist/package-test.js.map +1 -1
  4. package/dist/package.js +1 -1
  5. package/dist/package.js.map +1 -1
  6. package/img/icons/calculator.png +0 -0
  7. package/img/icons/pattern.png +0 -0
  8. package/img/icons/structure.png +0 -0
  9. package/img/icons/toolkit.png +0 -0
  10. package/img/icons/translator.png +0 -0
  11. package/package.json +1 -1
  12. package/src/demo/demo-st-ui.ts +26 -36
  13. package/src/model/helpers.ts +17 -0
  14. package/src/model/sequence-to-structure-utils/monomer-code-parser.ts +2 -5
  15. package/src/model/sequence-to-structure-utils/sdf-tab.ts +1 -1
  16. package/src/model/sequence-to-structure-utils/sequence-to-molfile.ts +20 -3
  17. package/src/package.ts +57 -37
  18. package/src/plugins/const.ts +4 -0
  19. package/src/plugins/mermade.ts +48 -0
  20. package/src/view/app-ui.ts +193 -0
  21. package/src/view/{tabs/axolabs.ts → apps/oligo-pattern.ts} +106 -26
  22. package/src/view/{tabs/sdf.ts → apps/oligo-structure.ts} +13 -14
  23. package/src/view/{tabs/main.ts → apps/oligo-translator.ts} +15 -11
  24. package/src/view/const/ui.ts +14 -0
  25. package/src/view/style/pattern-app.css +1 -0
  26. package/src/view/style/structure-app.css +39 -0
  27. package/src/view/style/translator-app.css +46 -0
  28. package/src/view/utils/colored-input/colored-text-input.ts +7 -6
  29. package/src/view/utils/molecule-img.ts +21 -10
  30. package/src/view/utils/router.ts +71 -0
  31. package/src/demo/handle-error.ts +0 -12
  32. package/src/view/const/view.ts +0 -10
  33. package/src/view/css/axolabs-tab.css +0 -1
  34. package/src/view/css/main-tab.css +0 -46
  35. package/src/view/css/sdf-tab.css +0 -39
  36. package/src/view/view.ts +0 -127
  37. /package/src/view/const/{main-tab.ts → oligo-translator.ts} +0 -0
  38. /package/src/view/{css → style}/colored-text-input.css +0 -0
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/sequence-translator",
3
3
  "friendlyName": "Sequence Translator",
4
- "version": "1.1.5",
4
+ "version": "1.2.0",
5
5
  "author": {
6
6
  "name": "Alexey Choposky",
7
7
  "email": "achopovsky@datagrok.ai"
@@ -2,24 +2,18 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {handleError} from './handle-error';
6
-
7
5
  import {delay} from '@datagrok-libraries/utils/src/test';
8
6
  import {getJsonData} from '../model/data-loading-utils/json-loader';
9
- import {SequenceTranslatorUI} from '../view/view';
10
- import {_package} from '../package';
7
+ import {_package, oligoTranslatorApp, oligoPatternApp, oligoStructureApp} from '../package';
8
+ import {tryCatch} from '../model/helpers';
11
9
 
12
- export async function demoTranslateSequenceUI() {
13
- try {
14
- openSequenceTranslatorOnPane(0);
15
- } catch (err: any) {
16
- handleError(err);
17
- }
10
+ export async function demoOligoTranslatorUI() {
11
+ await tryCatch(async () => oligoTranslatorApp());
18
12
  }
19
13
 
20
- export async function demoDesignPatternUI() {
21
- try {
22
- async function emulateUserInput(value: string, idx: number, idxUpdate: (idx: number) => number) {
14
+ export async function demoOligoPatternUI() {
15
+ await tryCatch(async () => {
16
+ async function emulateUserInput(value: string, idx: number, idxUpdate: (idx: number) => number): Promise<void> {
23
17
  await delay(3000);
24
18
 
25
19
  // warning: this redefinition is necessary because
@@ -32,7 +26,7 @@ export async function demoDesignPatternUI() {
32
26
  selectElement.dispatchEvent(event);
33
27
  }
34
28
 
35
- openSequenceTranslatorOnPane(1);
29
+ await oligoPatternApp();
36
30
 
37
31
  let len: number;
38
32
 
@@ -44,28 +38,24 @@ export async function demoDesignPatternUI() {
44
38
  const asNewValues = ['2\'-O-Methyl', '2\'-Fluoro', '2\'-O-MOE'];
45
39
  asNewValues.forEach(async (value, idx) => {
46
40
  emulateUserInput(value, idx, (i) => (len - 2 - 2 * i));
47
- })
48
- } catch (err: any) {
49
- handleError(err);
50
- }
51
- }
52
-
53
- export async function demoVisualizeDuplexUI() {
54
- try {
55
- await openSequenceTranslatorOnPane(2);
56
- } catch (err: any) {
57
- handleError(err);
58
- }
41
+ });
42
+ })
59
43
  }
60
44
 
61
- async function openSequenceTranslatorOnPane(paneNumber: number): Promise<void> {
62
- let tabControl: DG.TabControl;
63
- let panes: DG.TabPane[];
64
- await getJsonData();
65
- await _package.initMonomerLib();
66
- const v = new SequenceTranslatorUI();
67
- await v.createLayout();
68
- tabControl = (await v.tabs.getControl());
69
- panes = tabControl.panes;
70
- tabControl.currentPane = panes[paneNumber];
45
+ export async function demoOligoStructureUI() {
46
+ await tryCatch(async () => {
47
+ async function setInputValue(idx: number, sequence: string): Promise<void> {
48
+ await delay(500);
49
+ const textInputs: NodeListOf<HTMLTextAreaElement> = document.querySelectorAll('.colored-text-input > textarea');
50
+ const textarea = textInputs[idx];
51
+ textarea.value = sequence;
52
+ const event = new Event('input');
53
+ textarea.dispatchEvent(event);
54
+ }
55
+ await oligoStructureApp();
56
+ const inputSequences = ['Afcgacsu', 'Afcgacsu', 'Afcgacsu'];
57
+ inputSequences.forEach(async (sequence, idx) => {
58
+ await setInputValue(idx, sequence);
59
+ })
60
+ });
71
61
  }
@@ -1,3 +1,6 @@
1
+ /* Do not change these import lines to match external modules in webpack configuration */
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
1
4
  import * as DG from 'datagrok-api/dg';
2
5
 
3
6
  export function sortByReverseLength(array: string[]): string[] {
@@ -10,3 +13,17 @@ export function download(name: string, href: string): void {
10
13
  element.setAttribute('download', name);
11
14
  element.click();
12
15
  }
16
+
17
+ export async function tryCatch<T>(func: () => Promise<T>, finallyFunc?: () => any, callbackName: string = 'Oligo app'): Promise<T> {
18
+ try {
19
+ return await func();
20
+ } catch (err: any) {
21
+ const errMsg: string = err.hasOwnProperty('message') ? err.message : err.toString();
22
+ grok.shell.error(`${callbackName} error: ` + errMsg);
23
+ throw err;
24
+ } finally {
25
+ if (finallyFunc)
26
+ finallyFunc();
27
+ }
28
+ };
29
+
@@ -12,15 +12,12 @@ import {monomersWithPhosphateLinkers} from '../data-loading-utils/json-loader';
12
12
  * omitted linkers, if needed) */
13
13
  export class MonomerSequenceParser {
14
14
  constructor(
15
- private sequence: string, private invert: boolean = false,
15
+ private sequence: string,
16
16
  // todo: remove from the list of parameters
17
17
  private codeMap: Map<string, string>
18
18
  ) {
19
- this.lib = MonomerLibWrapper.getInstance();
20
19
  }
21
20
 
22
- private lib: MonomerLibWrapper;
23
-
24
21
  /** Get sequence of parsed monomer symbols, which are unique short names for
25
22
  * the monomers within the Monomer Library */
26
23
  parseSequence(): string[] {
@@ -65,7 +62,7 @@ export class MonomerSequenceParser {
65
62
  const code = allCodesOfFormat.find(
66
63
  (s: string) => s === this.sequence.substring(i, i + s.length)
67
64
  )!;
68
- this.invert ? parsedCodes.unshift(code) : parsedCodes.push(code);
65
+ parsedCodes.push(code);
69
66
  i += code.length;
70
67
  }
71
68
  return parsedCodes;
@@ -62,7 +62,7 @@ export function saveSdf(
62
62
  nonEmptyStrands.length === 0 ||
63
63
  nonEmptyStrands.length === 1 && ss.strand === ''
64
64
  ) {
65
- grok.shell.warning('Enter SS and AS/AS2 to save SDF');
65
+ grok.shell.warning('Enter SS and optionally AS/AS2 to save SDF');
66
66
  } else {
67
67
  let result: string;
68
68
  if (oneEntity) {
@@ -8,11 +8,11 @@ import {MonomerLibWrapper} from '../monomer-lib/lib-wrapper';
8
8
 
9
9
  export class SequenceToMolfileConverter {
10
10
  constructor(
11
- sequence: string, invert: boolean = false, format: string
11
+ sequence: string, private invert: boolean = false, format: string
12
12
  ) {
13
13
  this.lib = MonomerLibWrapper.getInstance();
14
14
  const codeToSymbolMap = this.lib.getCodeToSymbolMap(format);
15
- this.parser = new MonomerSequenceParser(sequence, invert, codeToSymbolMap);
15
+ this.parser = new MonomerSequenceParser(sequence, codeToSymbolMap);
16
16
  }
17
17
 
18
18
  private parser: MonomerSequenceParser;
@@ -25,7 +25,24 @@ export class SequenceToMolfileConverter {
25
25
  const monomerMolfile = this.getMonomerMolfile(monomerSymbol, idx);
26
26
  monomerMolfiles.push(monomerMolfile);
27
27
  })
28
- return this.getPolymerMolfile(monomerMolfiles);
28
+ let molfile = this.getPolymerMolfile(monomerMolfiles);
29
+ if (this.invert) {
30
+ molfile = this.reflect(molfile);
31
+ molfile = this.invertBondConfiguration(molfile);
32
+ }
33
+ return molfile;
34
+ }
35
+
36
+ private invertBondConfiguration(molfile: string): string {
37
+ const beginIdx = molfile.indexOf('M V30 BEGIN BOND');
38
+ const endIdx = molfile.indexOf('M V30 END BOND');
39
+ let bondBlock = molfile.substring(beginIdx, endIdx);
40
+ bondBlock = bondBlock.replace(
41
+ /(CFG=)([13])/g,
42
+ (match, group1, group2) => (group2 === '1') ? `${group1}3` : (group2 === '3') ? `${group1}1` : match
43
+ );
44
+ const result = molfile.substring(0, beginIdx) + bondBlock + molfile.substring(endIdx);
45
+ return result;
29
46
  }
30
47
 
31
48
  private getMonomerMolfile(monomerSymbol: string, idx: number): string {
package/src/package.ts CHANGED
@@ -2,7 +2,8 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
- import {SequenceTranslatorUI} from './view/view';
5
+ import {AppUIFactory, CombinedAppUI} from './view/app-ui';
6
+ import {tryCatch} from './model/helpers';
6
7
  import {LIB_PATH, DEFAULT_LIB_FILENAME} from './model/data-loading-utils/const';
7
8
  import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
8
9
  import {getMonomerLibHelper, IMonomerLibHelper} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
@@ -12,15 +13,17 @@ import {linkStrandsV3000} from './model/sequence-to-structure-utils/mol-transfor
12
13
  import {MonomerLibWrapper} from './model/monomer-lib/lib-wrapper';
13
14
  import {FormatDetector} from './model/parsing-validation/format-detector';
14
15
  import {SequenceValidator} from './model/parsing-validation/sequence-validator';
15
- import {demoDesignPatternUI, demoVisualizeDuplexUI, demoTranslateSequenceUI} from './demo/demo-st-ui';
16
+ import {demoOligoTranslatorUI, demoOligoPatternUI, demoOligoStructureUI} from './demo/demo-st-ui';
16
17
  import {FormatConverter} from './model/format-translation/format-converter';
18
+ import {APP} from './view/const/ui';
19
+ import {getExternalAppViewFactories} from './plugins/mermade';
17
20
 
18
21
  class StPackage extends DG.Package {
19
22
  private _monomerLib?: IMonomerLib;
20
23
 
21
24
  get monomerLib(): IMonomerLib {
22
25
  if (!this._monomerLib)
23
- throw new Error ('ST: monomer lib not loaded')
26
+ throw new Error ('Monomer lib not loaded')
24
27
  return this._monomerLib!;
25
28
  }
26
29
 
@@ -29,37 +32,54 @@ class StPackage extends DG.Package {
29
32
  return;
30
33
 
31
34
  const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create(
32
- 'Initializing Sequence Translator monomer library ...');
33
- try {
35
+ `Initializing ${APP.COMBINED} monomer library ...`);
36
+ await tryCatch(async () => {
34
37
  const libHelper: IMonomerLibHelper = await getMonomerLibHelper();
35
38
  this._monomerLib = await libHelper.readLibrary(LIB_PATH, DEFAULT_LIB_FILENAME);
36
- } catch (err: any) {
37
- const errMsg: string = err.hasOwnProperty('message') ? err.message : err.toString();
38
- throw new Error('Loading monomer library: ' + errMsg);
39
- } finally {
40
- pi.close();
41
- }
39
+ }, () => pi.close());
42
40
  }
43
41
  }
44
42
 
45
43
  export const _package: StPackage = new StPackage();
46
44
 
47
- //name: Sequence Translator
45
+ async function buildLayout(appName: string): Promise<void> {
46
+ await initSequenceTranslatorLibData();
47
+ const appUI = AppUIFactory.getUI(appName);
48
+ await appUI.createAppLayout();
49
+ }
50
+
51
+
52
+ //name: Oligo Toolkit
53
+ //meta.icon: img/icons/toolkit.png
48
54
  //tags: app
49
- export async function sequenceTranslatorApp(): Promise<void> {
50
- const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create('Loading Sequence Translator app ...');
51
-
52
- try {
53
- await initSequenceTranslatorLibData();
54
- const v = new SequenceTranslatorUI();
55
- await v.createLayout();
56
- } catch (err: any) {
57
- const errMsg: string = err.hasOwnProperty('message') ? err.message : err.toString();
58
- grok.shell.error(`Loading Sequence Translator application error: ` + errMsg);
59
- throw err;
60
- } finally {
61
- pi.close();
62
- }
55
+ export async function oligoToolkitApp(): Promise<void> {
56
+ await initSequenceTranslatorLibData();
57
+ const externalViewFactories = await getExternalAppViewFactories();
58
+ if (!externalViewFactories)
59
+ throw new Error('External app view factories not loaded');
60
+ const appUI = new CombinedAppUI(externalViewFactories!);
61
+ await appUI.createAppLayout();
62
+ }
63
+
64
+ //name: Oligo Translator
65
+ //meta.icon: img/icons/translator.png
66
+ //tags: app
67
+ export async function oligoTranslatorApp(): Promise<void> {
68
+ await buildLayout(APP.TRANSLATOR);
69
+ }
70
+
71
+ //name: Oligo Pattern
72
+ //meta.icon: img/icons/pattern.png
73
+ //tags: app
74
+ export async function oligoPatternApp(): Promise<void> {
75
+ await buildLayout(APP.PATTERN);
76
+ }
77
+
78
+ //name: Oligo Structure
79
+ //meta.icon: img/icons/structure.png
80
+ //tags: app
81
+ export async function oligoStructureApp(): Promise<void> {
82
+ await buildLayout(APP.STRUCTRE);
63
83
  }
64
84
 
65
85
  //name: initSequenceTranslatorLibData
@@ -99,28 +119,28 @@ export function linkStrands(strands: { senseStrands: string[], antiStrands: stri
99
119
  return linkStrandsV3000(strands, true);
100
120
  }
101
121
 
102
- //name: demoTranslateSequence
103
- //meta.demoPath: Bioinformatics | Oligonucleotide Sequence: Translate
122
+ //name: demoOligoTranslator
123
+ //meta.demoPath: Bioinformatics | Oligo Toolkit | Translator
104
124
  //description: Translate oligonucleotide sequences across various formats accepted by different synthesizers
105
125
  //meta.path: /apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Translate
106
126
  export async function demoTranslateSequence(): Promise<void> {
107
- await demoTranslateSequenceUI();
127
+ await demoOligoTranslatorUI();
108
128
  }
109
129
 
110
- //name: demoDesignPattern
111
- //meta.demoPath: Bioinformatics | Oligonucleotide Sequence: Design
130
+ //name: demoOligoPattern
131
+ //meta.demoPath: Bioinformatics | Oligo Toolkit | Pattern
112
132
  //description: Design a modification pattern for an oligonucleotide sequence
113
133
  //meta.path:%20/apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Visualize%20duplex
114
- export async function demoDesignPattern(): Promise<void> {
115
- await demoDesignPatternUI();
134
+ export async function demoOligoPattern(): Promise<void> {
135
+ await demoOligoPatternUI();
116
136
  }
117
137
 
118
- //name: demoVisualizeDuplex
119
- //meta.demoPath: Bioinformatics | Oligonucleotide Sequence: Visualize duplex
138
+ //name: demoOligoStructure
139
+ //meta.demoPath: Bioinformatics | Oligo Toolkit | Structure
120
140
  //description: Visualize duplex and save SDF
121
141
  //meta.path:%20/apps/Tutorials/Demo/Bioinformatics/Oligonucleotide%20Sequence:%20Visualize%20duplex
122
- export async function demoVisualizeDuplex(): Promise<void> {
123
- await demoVisualizeDuplexUI();
142
+ export async function demoOligoStructure(): Promise<void> {
143
+ await demoOligoStructureUI();
124
144
  }
125
145
 
126
146
  //name: translateOligonucleotideSequence
@@ -0,0 +1,4 @@
1
+ export const enum MERMADE {
2
+ FUNCTION_NAME = 'Mermadesynthesis:merMadeSynthesis',
3
+ TAB_NAME = 'SYNTHESIZE',
4
+ }
@@ -0,0 +1,48 @@
1
+ /* Do not change these import lines to match external modules in webpack configuration */
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
4
+ import * as DG from 'datagrok-api/dg';
5
+
6
+ import {ExternalPluginUI} from '../view/app-ui';
7
+ import {ColoredTextInput} from '../view/utils/colored-input/colored-text-input';
8
+ import {highlightInvalidSubsequence} from '../view/utils/colored-input/input-painters';
9
+ import {codesToHelmDictionary} from '../model/data-loading-utils/json-loader';
10
+ import {MERMADE} from './const';
11
+
12
+ export async function getExternalAppViewFactories(): Promise<{[name: string]: () => DG.View} | undefined> {
13
+ const externalPluginData = {
14
+ [MERMADE.FUNCTION_NAME]: {
15
+ tabName: MERMADE.TAB_NAME,
16
+ parameters: getMerMadeParameters()
17
+ },
18
+ }
19
+
20
+ const result: {[tabName: string]: () => DG.View} = {};
21
+
22
+ for (const [pluginName, data] of Object.entries(externalPluginData)) {
23
+ let div: HTMLDivElement;
24
+ try {
25
+ div = await grok.functions.call(pluginName, data.parameters);
26
+ } catch (err) {
27
+ console.error(`Plugin ${pluginName} not loaded, error:`, err)
28
+ div = ui.divText('error loading');
29
+ }
30
+ const pluginUI = new ExternalPluginUI(data.tabName, div);
31
+
32
+ // intentonally don't await for the promise
33
+ pluginUI.initView();
34
+
35
+ result[data.tabName] = () => pluginUI.getView();
36
+ }
37
+ return result;
38
+ }
39
+
40
+ function getMerMadeParameters(): {[name: string]: any} {
41
+ const base = ui.textInput('', '');
42
+ const input = new ColoredTextInput(base, highlightInvalidSubsequence);
43
+
44
+ return {
45
+ coloredInput: input,
46
+ codes: codesToHelmDictionary
47
+ }
48
+ }
@@ -0,0 +1,193 @@
1
+ /* Do not change these import lines to match external modules in webpack configuration */
2
+ import * as grok from 'datagrok-api/grok';
3
+ import * as ui from 'datagrok-api/ui';
4
+ import * as DG from 'datagrok-api/dg';
5
+
6
+ import {TAB, APP} from './const/ui';
7
+ import {TranslatorLayoutHandler} from './apps/oligo-translator';
8
+ import {StructureLayoutHandler} from './apps/oligo-structure';
9
+ import {PatternLayoutHandler} from './apps/oligo-pattern';
10
+ import {MonomerLibViewer} from './monomer-lib-viewer/viewer';
11
+ import {_package} from '../package';
12
+ import {tryCatch} from '../model/helpers';
13
+
14
+ type ViewFactories = {[name: string]: () => DG.View};
15
+
16
+ export abstract class AppUIBase {
17
+ constructor(protected appName: string, protected parentAppName?: string) { }
18
+ abstract addView(): Promise<void>;
19
+
20
+ async createAppLayout(): Promise<void> {
21
+ const pi: DG.TaskBarProgressIndicator = DG.TaskBarProgressIndicator.create(`Loading ${this.appName}...`);
22
+
23
+ let currentView = grok.shell.v?.root;
24
+ if (currentView)
25
+ ui.setUpdateIndicator(currentView, true);
26
+
27
+ await tryCatch(async () => {
28
+ await this.addView();
29
+ }, () => pi.close());
30
+
31
+ if (currentView)
32
+ ui.setUpdateIndicator(currentView, false);
33
+ }
34
+ }
35
+
36
+ abstract class SimpleAppUIBase extends AppUIBase {
37
+ constructor(appName: string) {
38
+ super(appName);
39
+ this.view = DG.View.create();
40
+ this.setupView();
41
+ }
42
+
43
+ protected view: DG.View;
44
+ async addView(): Promise<void> {
45
+ await this.initView();
46
+ const name = this.parentAppName ? this.parentAppName + '/' + this.appName : this.appName;
47
+ this.view.path = `/apps/${_package.name}/${name.replace(/\s/g, '')}/`;
48
+ grok.shell.addView(this.view);
49
+ }
50
+
51
+ protected abstract getHtml(): Promise<HTMLDivElement>;
52
+ async initView(): Promise<void> {
53
+ const html = await this.getHtml();
54
+ this.view.append(html);
55
+ }
56
+
57
+ protected setupView(): void {
58
+ this.view.box = true;
59
+ this.view.name = this.appName;
60
+
61
+ const windows = grok.shell.windows;
62
+ windows.showProperties = false;
63
+ windows.showToolbox = false;
64
+ windows.showHelp = false;
65
+ }
66
+
67
+ getView(): DG.View {
68
+ return this.view;
69
+ }
70
+ }
71
+
72
+ export class CombinedAppUI extends AppUIBase {
73
+ constructor(externalViewFactories: ViewFactories) {
74
+ super(APP.COMBINED)
75
+ this.externalViewFactories = externalViewFactories;
76
+ const factories = this.getViewFactories();
77
+ this.multiView = new DG.MultiView({viewFactories: factories});
78
+ }
79
+
80
+ private multiView: DG.MultiView;
81
+ private externalViewFactories?: ViewFactories;
82
+
83
+ private getViewFactories(): ViewFactories {
84
+ function viewFactory(uiConstructor: new (view: DG.View) => SimpleAppUIBase): () => DG.View {
85
+ const view = DG.View.create();
86
+ const translateUI = new uiConstructor(view);
87
+ // intentonally don't await for the promise
88
+ translateUI.initView();
89
+ return () => translateUI.getView();
90
+ }
91
+
92
+ let result: {[key: string]: () => DG.View } = {
93
+ [TAB.TRANSLATOR]: viewFactory(OligoTranslatorUI),
94
+ [TAB.PATTERN]: viewFactory(OligoPatternUI),
95
+ [TAB.STRUCTRE]: viewFactory(OligoStructureUI),
96
+ }
97
+
98
+ if (this.externalViewFactories)
99
+ result = Object.assign({}, result, this.externalViewFactories);
100
+
101
+ return result
102
+ }
103
+
104
+ private getPath(): string {
105
+ let name = this.multiView.tabs.currentPane.name;
106
+ name = name.charAt(0).toUpperCase() + name.substring(1).toLowerCase();
107
+ const path = `/apps/${_package.name}/OligoToolkit/${name}`;
108
+ return path;
109
+ }
110
+
111
+ private setUrl(): void {
112
+ this.multiView.path = this.getPath();
113
+ }
114
+
115
+ async addView(): Promise<void> {
116
+ this.multiView.tabs.onTabChanged.subscribe(() => this.setUrl());
117
+ this.setUrl();
118
+ grok.shell.addView(this.multiView);
119
+ }
120
+ }
121
+
122
+ /** For plugins from external packages */
123
+ export class ExternalPluginUI extends SimpleAppUIBase {
124
+ constructor(viewName: string, layout: HTMLDivElement) {
125
+ super(viewName);
126
+ this.layout = layout;
127
+ }
128
+ private layout: HTMLDivElement;
129
+
130
+ protected getHtml(): Promise<HTMLDivElement> {
131
+ return Promise.resolve(this.layout);
132
+ }
133
+ }
134
+
135
+ export class AppUIFactory {
136
+ private constructor() {}
137
+
138
+ static getUI(appName: string): SimpleAppUIBase {
139
+ switch (appName) {
140
+ case APP.TRANSLATOR:
141
+ return new OligoTranslatorUI();
142
+ case APP.PATTERN:
143
+ return new OligoPatternUI();
144
+ case APP.STRUCTRE:
145
+ return new OligoStructureUI();
146
+ default:
147
+ throw new Error(`Unknown app name: ${appName}`);
148
+ }
149
+ }
150
+ }
151
+
152
+ class OligoTranslatorUI extends SimpleAppUIBase {
153
+ constructor() {
154
+ super(APP.TRANSLATOR);
155
+
156
+ const viewMonomerLibIcon = ui.iconFA('book', MonomerLibViewer.view, 'View monomer library');
157
+ this.topPanel = [
158
+ viewMonomerLibIcon,
159
+ ];
160
+ this.view.setRibbonPanels([this.topPanel]);
161
+ this.ui = new TranslatorLayoutHandler();
162
+ }
163
+
164
+ private readonly topPanel: HTMLElement[];
165
+ private readonly ui: TranslatorLayoutHandler;
166
+
167
+ protected getHtml(): Promise<HTMLDivElement> {
168
+ return this.ui.getHtmlElement();
169
+ };
170
+ }
171
+
172
+ class OligoPatternUI extends SimpleAppUIBase {
173
+ constructor() {
174
+ super(APP.PATTERN);
175
+ this.ui = new PatternLayoutHandler();
176
+ }
177
+ private readonly ui: PatternLayoutHandler;
178
+ protected getHtml(): Promise<HTMLDivElement> {
179
+ return Promise.resolve(this.ui.htmlDivElement);
180
+ }
181
+ }
182
+
183
+ class OligoStructureUI extends SimpleAppUIBase {
184
+ constructor() {
185
+ super(APP.STRUCTRE)
186
+ this.ui = new StructureLayoutHandler();
187
+ }
188
+ private readonly ui: StructureLayoutHandler;
189
+
190
+ protected getHtml(): Promise<HTMLDivElement> {
191
+ return this.ui.getHtmlDivElement();
192
+ }
193
+ }