From 551ec882d7842e3f83c0ba22f70016b774b8e7d5 Mon Sep 17 00:00:00 2001 From: Julian Matschinske <ge93nar@mytum.de> Date: Thu, 9 Apr 2020 15:04:03 +0200 Subject: [PATCH] New interface, add toggle component and extend task dialog --- src/app/analysis.service.ts | 26 ++++++- src/app/app.module.ts | 2 + .../analysis-window.component.html | 70 ++++++++----------- .../analysis-window.component.ts | 47 +++++++------ .../launch-analysis.component.html | 59 ++++++++++++---- .../launch-analysis.component.scss | 4 ++ .../launch-analysis.component.ts | 69 ++++++++++++------ .../task-list/task-list.component.html | 11 +-- .../task-list/task-list.component.ts | 4 +- .../components/toggle/toggle.component.html | 15 ++++ .../components/toggle/toggle.component.scss | 0 .../toggle/toggle.component.spec.ts | 25 +++++++ src/app/components/toggle/toggle.component.ts | 29 ++++++++ .../explorer-page.component.html | 18 +---- src/styles.scss | 2 +- 15 files changed, 263 insertions(+), 118 deletions(-) create mode 100644 src/app/components/toggle/toggle.component.html create mode 100644 src/app/components/toggle/toggle.component.scss create mode 100644 src/app/components/toggle/toggle.component.spec.ts create mode 100644 src/app/components/toggle/toggle.component.ts diff --git a/src/app/analysis.service.ts b/src/app/analysis.service.ts index 9bd08cd5..1cf9ff85 100644 --- a/src/app/analysis.service.ts +++ b/src/app/analysis.service.ts @@ -1,14 +1,36 @@ -import {Injectable} from '@angular/core'; import {QueryItem, Task} from './interfaces'; import {Subject} from 'rxjs'; import {HttpClient} from '@angular/common/http'; import {environment} from '../environments/environment'; import {toast} from 'bulma-toast'; +import {Injectable} from '@angular/core'; + +export type AlgorithmType = 'trustrank' | 'keypathwayminer' | 'multisteiner' | 'closeness' | 'degree'; +export type QuickAlgorithmType = 'quick'; + +export const algorithmNames = { + trustrank: 'Trust-Rank', + keypathwayminer: 'KeyPathwayMiner', + multisteiner: 'Multi-Steiner', + closeness: 'Closeness Centrality', + degree: 'Degree Centrality', + quick: 'Quick-Find Drugs', +}; + +export interface Algorithm { + slug: AlgorithmType | QuickAlgorithmType; + name: string; +} + +export const TRUSTRANK: Algorithm = {slug: 'trustrank', name: algorithmNames.trustrank}; +export const CLOSENESS_CENTRALITY: Algorithm = {slug: 'closeness', name: algorithmNames.closeness}; +export const DEGREE_CENTRALITY: Algorithm = {slug: 'degree', name: algorithmNames.degree}; +export const KEYPATHWAYMINER: Algorithm = {slug: 'keypathwayminer', name: algorithmNames.keypathwayminer}; +export const MULTISTEINER: Algorithm = {slug: 'multisteiner', name: algorithmNames.multisteiner}; @Injectable({ providedIn: 'root' }) - export class AnalysisService { private selectedItems = new Map<string, QueryItem>(); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6a15087b..5b416e7e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -18,6 +18,7 @@ import {SelectDatasetComponent} from './components/select-dataset/select-dataset import {AnalysisWindowComponent} from './components/analysis-window/analysis-window.component'; import {TaskListComponent} from './components/task-list/task-list.component'; import {AnalysisService} from './analysis.service'; +import { ToggleComponent } from './components/toggle/toggle.component'; @NgModule({ @@ -31,6 +32,7 @@ import {AnalysisService} from './analysis.service'; SelectDatasetComponent, AnalysisWindowComponent, TaskListComponent, + ToggleComponent, ], imports: [ BrowserModule, diff --git a/src/app/components/analysis-window/analysis-window.component.html b/src/app/components/analysis-window/analysis-window.component.html index e16d4a9c..a3119078 100644 --- a/src/app/components/analysis-window/analysis-window.component.html +++ b/src/app/components/analysis-window/analysis-window.component.html @@ -16,7 +16,7 @@ <div class="card-content"> <div class="tabs is-centered"> <ul> - <li [class.is-active]="tab === 'meta'"><a (click)="tab = 'meta'">Meta</a></li> + <li [class.is-active]="tab === 'meta'"><a (click)="tab = 'meta'">Parameters</a></li> <li [class.is-active]="tab === 'network'"><a (click)="tab = 'network'">Network</a></li> <li [class.is-active]="tab === 'table'"><a (click)="tab = 'table'">Table</a></li> </ul> @@ -24,16 +24,38 @@ <div class="content tab-content" *ngIf="task && task.info.done" [class.is-visible]="tab === 'meta'"> <div *ngIf="task"> <p> - Algorithm: {{task.info.algorithm}} + Algorithm: <strong>{{algorithmNames[task.info.algorithm]}}</strong> </p> - <div *ngIf="task.info.algorithm === 'trustrank'"> + <div> <table class="table is-narrow"> <tbody> - <tr> + <tr *ngIf="task.info.parameters.k"> + <td>K</td> + <td>{{task.info.parameters.k}}</td> + </tr> + <tr *ngIf="task.info.parameters.numTrees"> + <td>Number of trees</td> + <td>{{task.info.parameters.numTrees}}</td> + </tr> + <tr *ngIf="task.info.parameters.dampingFactor"> <td>Damping Factor</td> <td>{{task.info.parameters.dampingFactor}}</td> </tr> - <tr> + <tr *ngIf="task.info.parameters.includeIndirectDrugs !== undefined && task.info.target === 'drug'"> + <td>Include indirect drugs</td> + <td> + <i *ngIf="task.info.parameters.includeIndirectDrugs" class="fa fa-check"></i> + <i *ngIf="!task.info.parameters.includeIndirectDrugs" class="fa fa-times"></i> + </td> + </tr> + <tr *ngIf="task.info.parameters.includeNonApprovedDrugs !== undefined && task.info.target === 'drug'"> + <td>Include non-approved drugs</td> + <td> + <i *ngIf="task.info.parameters.includeNonApprovedDrugs" class="fa fa-check"></i> + <i *ngIf="!task.info.parameters.includeNonApprovedDrugs" class="fa fa-times"></i> + </td> + </tr> + <tr *ngIf="task.info.parameters.resultSize"> <td>Result Size</td> <td>{{task.info.parameters.resultSize}}</td> </tr> @@ -82,41 +104,11 @@ </p> </div> - <div *ngIf="task.info.target === 'drug-target' " class="field has-addons footer-toggle-buttons"> - <p class="control"> - <button class="button is-rounded" [class.is-primary]="showDrugs" - (click)="toggleDrugs(true)"> - <span class="icon is-small"> - <i class="fas fa-capsules"></i> - </span> - <span>Drugs On</span> - </button> - </p> - <p class="control"> - <button class="button is-rounded" [class.is-primary]="!showDrugs" - (click)="toggleDrugs(false)"> - <span>Off</span> - </button> - </p> - </div> + <app-toggle *ngIf="task.info.target === 'drug-target'" class="footer-toggle-buttons" textOn="Drugs On" textOff="Off" + [value]="showDrugs" (valueChange)="toggleDrugs($event)" icon="fa-capsules"></app-toggle> - <div class="field has-addons footer-toggle-buttons"> - <p class="control"> - <button class="button is-rounded" [class.is-primary]="physicsEnabled" - (click)="updatePhysicsEnabled(true)"> - <span class="icon is-small"> - <i class="fas fa-wind"></i> - </span> - <span>Animation On</span> - </button> - </p> - <p class="control"> - <button class="button is-rounded" [class.is-primary]="!physicsEnabled" - (click)="updatePhysicsEnabled(false)"> - <span>Off</span> - </button> - </p> - </div> + <app-toggle class="footer-toggle-buttons" textOn="Animation On" textOff="Off" + [value]="physicsEnabled" (valueChange)="updatePhysicsEnabled($event)" icon="fa-wind"></app-toggle> </footer> </div> <div class="content tab-content scrollable" *ngIf="task && task.info.done" [class.is-visible]="tab === 'table'"> diff --git a/src/app/components/analysis-window/analysis-window.component.ts b/src/app/components/analysis-window/analysis-window.component.ts index 910d077c..adf307ed 100644 --- a/src/app/components/analysis-window/analysis-window.component.ts +++ b/src/app/components/analysis-window/analysis-window.component.ts @@ -11,7 +11,7 @@ import { } from '@angular/core'; import {HttpClient, HttpErrorResponse} from '@angular/common/http'; import {environment} from '../../../environments/environment'; -import {AnalysisService} from '../../analysis.service'; +import {AnalysisService, algorithmNames} from '../../analysis.service'; import {Protein, Task, NodeType, ViralProtein, Drug} from '../../interfaces'; import html2canvas from 'html2canvas'; import {toast} from 'bulma-toast'; @@ -52,6 +52,8 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { public showDrugs = false; public tab = 'network'; public physicsEnabled = true; + public drugstatus = true; + private proteins: any; @@ -65,6 +67,8 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { @Output() visibleItems: EventEmitter<any> = new EventEmitter(); + public algorithmNames = algorithmNames; + async ngOnInit() { } @@ -126,7 +130,7 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { })); await Promise.all(promises); - this.tableHasScores = this.task.info.algorithm === 'trustrank' || this.task.info.algorithm === 'quick'; + this.tableHasScores = ['trustrank', 'closeness', 'degree', 'quick'].indexOf(this.task.info.algorithm) !== -1; if (this.tableHasScores) { this.toggleNormalization(true); } @@ -282,30 +286,27 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { const nodes = []; const edges = []; - const nodeAttributes = result.nodeAttributes || []; + const attributes = result.nodeAttributes || {}; this.proteins = []; this.effects = []; - for (let i = 0; i < result.networks.length; i++) { - const network = result.networks[i]; - - const attributes = nodeAttributes[i] || {}; - const nodeTypes = attributes.nodeTypes || {}; - const isSeed = attributes.isSeed || {}; - const scores = attributes.scores || {}; - const details = attributes.details || {}; - for (const node of network.nodes) { - if (nodeTypes[node] === 'host') { - this.proteins.push(details[node]); - } else if (nodeTypes[node] === 'virus') { - this.effects.push(details[node]); - } - nodes.push(this.mapNode(node, nodeTypes[node] || this.inferNodeType(node), isSeed[node], scores[node], details[node])); + const network = result.network; + + const nodeTypes = attributes.nodeTypes || {}; + const isSeed = attributes.isSeed || {}; + const scores = attributes.scores || {}; + const details = attributes.details || {}; + for (const node of network.nodes) { + if (nodeTypes[node] === 'host') { + this.proteins.push(details[node]); + } else if (nodeTypes[node] === 'virus') { + this.effects.push(details[node]); } + nodes.push(this.mapNode(node, nodeTypes[node] || this.inferNodeType(node), isSeed[node], scores[node], details[node])); + } - for (const edge of network.edges) { - edges.push(this.mapEdge(edge)); - } + for (const edge of network.edges) { + edges.push(this.mapEdge(edge)); } return { @@ -449,5 +450,9 @@ export class AnalysisWindowComponent implements OnInit, OnChanges { }); } + public updateshowdrugs(bool) { + this.drugstatus = bool; + + } } diff --git a/src/app/components/launch-analysis/launch-analysis.component.html b/src/app/components/launch-analysis/launch-analysis.component.html index 54a64d53..95560e6e 100644 --- a/src/app/components/launch-analysis/launch-analysis.component.html +++ b/src/app/components/launch-analysis/launch-analysis.component.html @@ -21,16 +21,21 @@ </ul> </div> + <div *ngIf="target === 'drug' && hasBaits"> + <div class="notification is-warning warning"> + You have selected <i class="fa fa-virus"></i> viral proteins. + When finding drugs, make sure you have selected <i class="fa fa-dna"></i> host proteins only. + </div> + </div> + <div *ngIf="algorithm==='trustrank'"> - <div class="field" *ngIf="target === 'drug-target'"> - <label class="label" for="trustrank-strain">Strain</label> - <div class="control"> - <div class="select"> - <select id="trustrank-strain" [(ngModel)]="trustrankStrain"> - <option [ngValue]="'SARS_CoV2'">SARS Coronavirus 2</option> - </select> - </div> - </div> + <div class="field" *ngIf="target === 'drug'"> + <label class="label">Indirect Drugs</label> + <app-toggle textOn="Include" textOff="Ignore" icon="fa-check" [(value)]="trustrankIncludeIndirectDrugs"></app-toggle> + </div> + <div class="field" *ngIf="target === 'drug'"> + <label class="label">Non-approved Drugs</label> + <app-toggle textOn="Include" textOff="Ignore" icon="fa-check" [(value)]="trustrankIncludeNonApprovedDrugs"></app-toggle> </div> <div class="field"> <label class="label" for="trustrank-df">Damping Factor</label> @@ -50,6 +55,38 @@ </div> </div> + <div *ngIf="algorithm==='closeness'"> + <div class="field" *ngIf="target === 'drug'"> + <label class="label">Indirect Drugs</label> + <app-toggle textOn="Include" textOff="Ignore" icon="fa-check" [(value)]="closenessIncludeIndirectDrugs"></app-toggle> + </div> + <div class="field" *ngIf="target === 'drug'"> + <label class="label">Non-approved Drugs</label> + <app-toggle textOn="Include" textOff="Ignore" icon="fa-check" [(value)]="closenessIncludeNonApprovedDrugs"></app-toggle> + </div> + <div class="field"> + <label class="label" for="closeness-rs">Result Size</label> + <div class="control"> + <input [(ngModel)]="closenessResultSize" id="closeness-rs" class="input" type="number" + placeholder="Result size" required> + </div> + </div> + </div> + + <div *ngIf="algorithm==='degree'"> + <div class="field" *ngIf="target === 'drug'"> + <label class="label">Non-approved Drugs</label> + <app-toggle textOn="Include" textOff="Ignore" icon="fa-check" [(value)]="degreeIncludeNonApprovedDrugs"></app-toggle> + </div> + <div class="field"> + <label class="label" for="degree-rs">Result Size</label> + <div class="control"> + <input [(ngModel)]="degreeResultSize" id="degree-rs" class="input" type="number" + placeholder="Result size" required> + </div> + </div> + </div> + <div *ngIf="algorithm==='keypathwayminer'"> <div class="field"> <label class="label" for="keypathwayminer-k">K</label> @@ -99,10 +136,8 @@ </section> <footer class="modal-card-foot"> - <button (click)="startTask(); close()" class="button is-success is-rounded">Launch</button> + <button (click)="startTask(); close()" class="button is-success is-rounded" [disabled]="target === 'drug' && hasBaits">Launch</button> <button (click)="close()" class="button is-rounded">Close</button> </footer> </div> </div> - - diff --git a/src/app/components/launch-analysis/launch-analysis.component.scss b/src/app/components/launch-analysis/launch-analysis.component.scss index 2ad8df25..192552e6 100644 --- a/src/app/components/launch-analysis/launch-analysis.component.scss +++ b/src/app/components/launch-analysis/launch-analysis.component.scss @@ -1,3 +1,7 @@ .modal-card { height: 500px; } + +.warning { + margin-bottom: 15px; +} diff --git a/src/app/components/launch-analysis/launch-analysis.component.ts b/src/app/components/launch-analysis/launch-analysis.component.ts index 79351769..67e7aad7 100644 --- a/src/app/components/launch-analysis/launch-analysis.component.ts +++ b/src/app/components/launch-analysis/launch-analysis.component.ts @@ -1,14 +1,14 @@ import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core'; -import {AnalysisService} from '../../analysis.service'; - -interface Algorithm { - slug: 'trustrank' | 'keypathwayminer' | 'multisteiner'; - name: string; -} - -const TRUSTRANK: Algorithm = {slug: 'trustrank', name: 'Trust-Rank'}; -const KEYPATHWAYMINER: Algorithm = {slug: 'keypathwayminer', name: 'KeyPathwayMiner'}; -const MULTISTEINER: Algorithm = {slug: 'multisteiner', name: 'Multi-Steiner'}; +import { + Algorithm, + AlgorithmType, + AnalysisService, CLOSENESS_CENTRALITY, + DEGREE_CENTRALITY, + KEYPATHWAYMINER, + MULTISTEINER, + QuickAlgorithmType, + TRUSTRANK +} from '../../analysis.service'; @Component({ selector: 'app-launch-analysis', @@ -24,17 +24,27 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { @Output() public showChange = new EventEmitter<boolean>(); - public algorithm: 'trustrank' | 'keypathwayminer' | 'multisteiner'; + public algorithm: AlgorithmType | QuickAlgorithmType; public algorithms: Array<Algorithm> = []; // Trustrank Parameters public trustrankStrain = 'SARS_CoV2'; + public trustrankIncludeIndirectDrugs = false; + public trustrankIncludeNonApprovedDrugs = false; public trustrankDampingFactor = 0.85; public trustrankResultSize = 20; - public trustrankNumThreads = 1; - public trustrankDatasets = []; - public trustrankIgnoredEdgeTypes = []; + + // Closeness Parameters + public closenessStrain = 'SARS_CoV2'; + public closenessIncludeIndirectDrugs = false; + public closenessIncludeNonApprovedDrugs = false; + public closenessResultSize = 20; + + // Degree Parameters + public degreeStrain = 'SARS_CoV2'; + public degreeIncludeNonApprovedDrugs = false; + public degreeResultSize = 20; // Keypathwayminer Parameters public keypathwayminerK = 1; @@ -43,7 +53,13 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { public multisteinerStrain = 'SARS_CoV2'; public multisteinerNumTrees = 5; + public hasBaits; + constructor(public analysis: AnalysisService) { + this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'Viral Protein'); + analysis.subscribe(() => { + this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'Viral Protein'); + }); } ngOnInit(): void { @@ -51,13 +67,17 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { ngOnChanges(changes: SimpleChanges): void { if (this.target === 'drug-target') { - this.algorithms = [MULTISTEINER, TRUSTRANK, KEYPATHWAYMINER]; + this.algorithms = [MULTISTEINER, KEYPATHWAYMINER, TRUSTRANK, CLOSENESS_CENTRALITY, DEGREE_CENTRALITY]; this.algorithm = MULTISTEINER.slug; - this.trustrankStrain = 'SARS_CoV2'; + this.trustrankStrain = 'SARS_CoV2'; // TODO: Change once we have multiple datasets + this.closenessStrain = 'SARS_CoV2'; // TODO: Change once we have multiple datasets + this.degreeStrain = 'SARS_CoV2'; // TODO: Change once we have multiple datasets } else if (this.target === 'drug') { - this.algorithms = [TRUSTRANK]; + this.algorithms = [TRUSTRANK, CLOSENESS_CENTRALITY, DEGREE_CENTRALITY]; this.algorithm = TRUSTRANK.slug; this.trustrankStrain = 'drugs'; + this.closenessStrain = 'drugs'; + this.degreeStrain = 'drugs'; } } @@ -73,11 +93,20 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges { if (this.algorithm === 'trustrank') { parameters.strain_or_drugs = this.trustrankStrain; - parameters.datasets = []; - parameters.ignored_edge_types = []; parameters.damping_factor = this.trustrankDampingFactor; + parameters.include_indirect_drugs = this.trustrankIncludeIndirectDrugs; + parameters.include_non_approved_drugs = this.trustrankIncludeNonApprovedDrugs; parameters.result_size = this.trustrankResultSize; - parameters.num_threads = this.trustrankNumThreads; + } else if (this.algorithm === 'closeness') { + parameters.strain_or_drugs = this.closenessStrain; + parameters.include_indirect_drugs = this.closenessIncludeIndirectDrugs; + parameters.include_non_approved_drugs = this.closenessIncludeNonApprovedDrugs; + parameters.result_size = this.closenessResultSize; + } else if (this.algorithm === 'degree') { + parameters.strain_or_drugs = this.degreeStrain; + parameters.include_indirect_drugs = this.closenessIncludeIndirectDrugs; + parameters.include_non_approved_drugs = this.closenessIncludeNonApprovedDrugs; + parameters.result_size = this.closenessResultSize; } else if (this.algorithm === 'keypathwayminer') { parameters.k = this.keypathwayminerK; } else if (this.algorithm === 'multisteiner') { diff --git a/src/app/components/task-list/task-list.component.html b/src/app/components/task-list/task-list.component.html index 991c7720..1ee860b2 100644 --- a/src/app/components/task-list/task-list.component.html +++ b/src/app/components/task-list/task-list.component.html @@ -4,11 +4,12 @@ <div *ngIf="!task.info.startedAt" (click)="open(task.token)"> <p> <span class="is-capitalized"><i class="fa" [class.fa-capsules]="task.info.target === 'drug'" - [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{task.info.algorithm}}</span> + [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{algorithmNames[task.info.algorithm]}}</span> <span class="icon is-pulled-right"><i class="fas fa-pause" aria-hidden="true"></i></span> </p> <p> - <small>Queue position: {{task.stats.queuePosition}}/{{task.stats.queueLength}}</small> + <small *ngIf="task.stats.queueLength > 0">Queue position: {{task.stats.queuePosition}}/{{task.stats.queueLength}}</small> + <small *ngIf="task.stats.queueLength === 0">Execution imminent...</small> <a (click)="analysis.removeTask(task.token)" class="has-text-danger"> <span class="icon is-pulled-right"> <i class="fa fa-trash"></i> @@ -19,7 +20,7 @@ <div *ngIf="!task.info.done && !task.info.failed && task.info.startedAt" (click)="open(task.token)"> <p> <span class="is-capitalized"><i class="fa" [class.fa-capsules]="task.info.target === 'drug'" - [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{task.info.algorithm}}</span> + [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{algorithmNames[task.info.algorithm]}}</span> <span class="icon is-pulled-right"><i class="fas fa-spinner fa-spin" aria-hidden="true"></i></span> </p> <p> @@ -35,7 +36,7 @@ <div *ngIf="task.info.done" (click)="open(task.token)"> <p> <span class="is-capitalized"><i class="fa" [class.fa-capsules]="task.info.target === 'drug'" - [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{task.info.algorithm}}</span> + [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{algorithmNames[task.info.algorithm]}}</span> <span class="icon is-pulled-right"><i class="fas fa-check" aria-hidden="true"></i></span> </p> <p> @@ -50,7 +51,7 @@ <div *ngIf="task.info.failed && task.info.finishedAt == null"> <p> <span class="is-capitalized"><i class="fa" [class.fa-capsules]="task.info.target === 'drug'" - [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{task.info.algorithm}}</span> + [class.fa-crosshairs]="task.info.target === 'drug-target'"></i> {{algorithmNames[task.info.algorithm]}}</span> <span class="icon is-pulled-right"><i class="fas fa-exclamation-triangle" aria-hidden="true"></i></span> </p> <p class="has-text-danger"> diff --git a/src/app/components/task-list/task-list.component.ts b/src/app/components/task-list/task-list.component.ts index 8e359156..dda9f6f3 100644 --- a/src/app/components/task-list/task-list.component.ts +++ b/src/app/components/task-list/task-list.component.ts @@ -1,5 +1,5 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; -import {AnalysisService} from '../../analysis.service'; +import {AnalysisService, algorithmNames} from '../../analysis.service'; @Component({ @@ -13,6 +13,8 @@ export class TaskListComponent implements OnInit { @Input() token: string; @Output() tokenChange: EventEmitter<string> = new EventEmitter(); + public algorithmNames = algorithmNames; + constructor(public analysis: AnalysisService) { } diff --git a/src/app/components/toggle/toggle.component.html b/src/app/components/toggle/toggle.component.html new file mode 100644 index 00000000..ac8059b8 --- /dev/null +++ b/src/app/components/toggle/toggle.component.html @@ -0,0 +1,15 @@ +<div class="field has-addons"> + <p class="control"> + <button class="button is-rounded" [class.is-primary]="value" (click)="toggle(true)"> + <span class="icon is-small"> + <i class="fa {{icon}}"></i> + </span> + <span>{{textOn}}</span> + </button> + </p> + <p class="control"> + <button class="button is-rounded" [class.is-primary]="!value" (click)="toggle(false)"> + <span>{{textOff}}</span> + </button> + </p> +</div> diff --git a/src/app/components/toggle/toggle.component.scss b/src/app/components/toggle/toggle.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/toggle/toggle.component.spec.ts b/src/app/components/toggle/toggle.component.spec.ts new file mode 100644 index 00000000..b6f9f897 --- /dev/null +++ b/src/app/components/toggle/toggle.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ToggleComponent } from './toggle.component'; + +describe('ToggleComponent', () => { + let component: ToggleComponent; + let fixture: ComponentFixture<ToggleComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ToggleComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ToggleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/toggle/toggle.component.ts b/src/app/components/toggle/toggle.component.ts new file mode 100644 index 00000000..b2a21e2a --- /dev/null +++ b/src/app/components/toggle/toggle.component.ts @@ -0,0 +1,29 @@ +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; + +@Component({ + selector: 'app-toggle', + templateUrl: './toggle.component.html', + styleUrls: ['./toggle.component.scss'] +}) +export class ToggleComponent implements OnInit { + + @Input() icon = 'fa-check'; + + @Input() textOn = 'On'; + @Input() textOff = 'Off'; + + @Input() value: boolean; + @Output() valueChange = new EventEmitter<boolean>(); + + constructor() { + } + + ngOnInit(): void { + } + + public toggle(value: boolean) { + this.value = value; + this.valueChange.emit(this.value); + } + +} diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html index a640fd84..41773658 100644 --- a/src/app/pages/explorer-page/explorer-page.component.html +++ b/src/app/pages/explorer-page/explorer-page.component.html @@ -172,23 +172,7 @@ <i class="fas fa-camera" aria-hidden="true"></i> </span> <span>Screenshot</span> </button> - <div class="field has-addons footer-toggle-buttons"> - <p class="control"> - <button class="button is-rounded" [class.is-primary]="physicsEnabled" - (click)="updatePhysicsEnabled(true)"> - <span class="icon is-small"> - <i class="fas fa-wind"></i> - </span> - <span>Animation On</span> - </button> - </p> - <p class="control"> - <button class="button is-rounded" [class.is-primary]="!physicsEnabled" - (click)="updatePhysicsEnabled(false)"> - <span>Off</span> - </button> - </p> - </div> + <app-toggle class="footer-toggle-buttons" textOn="Animation On" textOff="Off" [value]="physicsEnabled" (valueChange)="updatePhysicsEnabled($event)" icon="fa-wind"></app-toggle> </footer> </div> </div> diff --git a/src/styles.scss b/src/styles.scss index 041cd42d..57f1acbd 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -175,7 +175,7 @@ div.field.has-addons.add-remove-toggle { color: #EF476F; } -div.field.has-addons.footer-toggle-buttons { +.footer-toggle-buttons { margin-left: 20px; margin-right: 10px; } -- GitLab