Select what ETF's you would like to use

This commit is contained in:
Zakary Timson 2018-11-29 21:39:13 -05:00
parent deb5251b5a
commit 37a8889a9d
4 changed files with 101 additions and 26 deletions

View File

@ -1,30 +1,57 @@
<!--The content below is only a placeholder and can be replaced.--> <!-- Toolbar -->
<mat-toolbar> <mat-toolbar>
<img class="mr-3" src="assets/logo.png" height="36px" width="auto"> <img class="mr-3" src="assets/logo.png" height="36px" width="auto">
<span class="mr-3">ETF Demo</span> <span class="mr-3">ETF Demo</span>
<small *ngIf="timer" class="text-muted">{{timer}} microseconds to upload</small> <small *ngIf="timer" class="text-muted">{{timer}} microseconds to upload</small>
<span class="mx-auto"><!-- Spacer --></span> <span class="mx-auto"><!-- Spacer --></span>
<mat-chip-list class="mr-2"> <mat-chip-list class="mr-2">
<mat-chip *ngFor="let file of fileNames; let i = index" class="text-white" [style.backgroundColor]="colorScheme.domain[i]" <mat-chip *ngFor="let file of fileNames; let i = index" class="text-white"
[style.backgroundColor]="colorScheme.domain[i]"
[removable]="true" (removed)="remove($event.chip.value)" [value]="file"> [removable]="true" (removed)="remove($event.chip.value)" [value]="file">
{{file}} {{file}}
<mat-icon matChipRemove>cancel</mat-icon> <mat-icon matChipRemove>cancel</mat-icon>
</mat-chip> </mat-chip>
</mat-chip-list> </mat-chip-list>
<button mat-button (click)="fileUploader.click()"> <button mat-button (click)="fileUploader.click()">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
Upload Upload
</button> </button>
<input #fileUploader type="file" accept="text/csv" multiple hidden (change)="upload(fileUploader.files)">
</mat-toolbar> </mat-toolbar>
<img *ngIf="!chartResults" class="float-right mt-2 mr-5" src="assets/starthere.png">
<div *ngIf="chartResults" class="w-100" [style.height]="chartHeight"> <!-- Start Here image -->
<ngx-charts-bar-horizontal-2d #chart <img *ngIf="!fileNames.length" class="float-right mt-2 mr-5" src="assets/starthere.png">
[scheme]="colorScheme"
[results]="chartResults" <!-- Content -->
[xAxis]="true" <div *ngIf="fileNames.length" class="content-height w-100" style="overflow: hidden auto">
[yAxis]="true" <!-- Input for selecting holdings -->
[xAxisTickFormatting]="format"> <div class="p-3">
</ngx-charts-bar-horizontal-2d> <mat-form-field class="w-50">
<mat-chip-list #chipList>
<mat-chip *ngFor="let holding of graphHoldings; let i = index" [removable]="true" (removed)="graphHoldings.splice(i, 1); updateGraph()">
{{holding}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input #holdingInput class="w-100"
placeholder="Add Holdings"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
(keyup)="search($event.target.value)">
</mat-chip-list>
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="updateGraph($event.option.value); holdingInput.blur()">
<mat-option *ngFor="let holding of autoCompleteList | async" [value]="holding">
{{holding}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</div>
<!-- Chart -->
<ngx-charts-bar-vertical-2d #chart
[scheme]="colorScheme"
[results]="chartResults"
[xAxis]="true"
[yAxis]="true"
[yAxisTickFormatting]="format">
</ngx-charts-bar-vertical-2d>
</div> </div>
<input #fileUploader type="file" accept="text/csv" multiple hidden (change)="upload(fileUploader.files)">

View File

@ -1,4 +1,4 @@
import {Component} from '@angular/core'; import {Component, ElementRef, EventEmitter, ViewChild} from '@angular/core';
import {timer} from './timer'; import {timer} from './timer';
import {colorScheme} from './colorScheme'; import {colorScheme} from './colorScheme';
@ -7,10 +7,16 @@ import {colorScheme} from './colorScheme';
templateUrl: './app.component.html' templateUrl: './app.component.html'
}) })
export class AppComponent { export class AppComponent {
@ViewChild('holdingInput') holdingInput: ElementRef;
autoCompleteList = new EventEmitter<string[]>(); // Async pipe to provide autocomplete list after being filtered by the text input
colorScheme = colorScheme; // colors colorScheme = colorScheme; // colors
chartResults; // This is where the chart reads the data from chartResults = []; // This is where the chart reads the data from
chartHeight = '100%'; // Dynamic height for chart holdings: string[] = []; // All the merged holdings
fileNames: string[] = []; // All the filenames
mergedData = {}; // All the holdings merged together
timer = window['timer']; // Async pipe to display the timed data timer = window['timer']; // Async pipe to display the timed data
graphHoldings: string[] = []; // Holdings we are graphing
// ngx-charts requires a different data structure than the hash map we built so I will use a setter to handle converting it when we go to save the processed data. // ngx-charts requires a different data structure than the hash map we built so I will use a setter to handle converting it when we go to save the processed data.
private _data = {}; private _data = {};
@ -19,7 +25,7 @@ export class AppComponent {
this._data = data; this._data = data;
// merge the files together // merge the files together
let mergedData = Object.values(data).reduce((acc, file) => { this.mergedData = Object.values(data).reduce((acc, file) => {
Object.keys(file).forEach(key => { Object.keys(file).forEach(key => {
if(!acc[key]) acc[key] = []; if(!acc[key]) acc[key] = [];
file[key].forEach(val => acc[key].push(val)); file[key].forEach(val => acc[key].push(val));
@ -27,14 +33,14 @@ export class AppComponent {
return acc; return acc;
}, {}); }, {});
// Take the merged data set and get everything ready for it to be charted // Store the keys for easy referencing
this.chartHeight = `${Object.keys(mergedData).length * 100}px`; this.fileNames = Object.keys(this.data);
this.chartResults = Object.keys(mergedData).map(key => ({name: key, series: mergedData[key].map((val, i) => ({name: i, value: val}))})); this.holdings = Object.keys(this.mergedData).sort();
this.autoCompleteList.next(this.holdings);
} }
get fileNames() { return Object.keys(this.data); }
constructor() { constructor() {
// Hack to connect angular context to the native one
setInterval(() => this.timer = Math.round(window['timer'] * 10) / 10, 250); setInterval(() => this.timer = Math.round(window['timer'] * 10) / 10, 250);
} }
@ -44,6 +50,12 @@ export class AppComponent {
this.data = Object.assign({}, this.data); this.data = Object.assign({}, this.data);
} }
search(text: string) {
// Filter the holdings list by the text and push it through the async pipe
if(!text) this.autoCompleteList.next(this.holdings);
this.autoCompleteList.next(this.holdings.filter(holding => holding.toLowerCase().indexOf(text) != -1));
}
@timer @timer
upload(fileList: FileList) { upload(fileList: FileList) {
// Because we enabled uploading multiple fileNames at once we need to process each one individually // Because we enabled uploading multiple fileNames at once we need to process each one individually
@ -74,5 +86,20 @@ export class AppComponent {
}); });
} }
updateGraph(holding?: string) {
if(holding) {
this.graphHoldings.push(holding);
this.holdingInput.nativeElement.value = '';
}
// Take the merged data set and get everything ready for it to be charted
this.chartResults = Object.keys(this.mergedData)
.filter(key => this.graphHoldings.indexOf(key) != -1)
.map(key => ({
name: key,
series: this.mergedData[key].map((val, i) => ({name: i, value: val}))
}));
}
format(text) { return `${text} %`} format(text) { return `${text} %`}
} }

View File

@ -1,20 +1,33 @@
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {AppComponent} from './app.component'; import {AppComponent} from './app.component';
import {MatButtonModule, MatChipsModule, MatIconModule, MatToolbarModule} from '@angular/material'; import {
MatAutocompleteModule,
MatButtonModule,
MatChipsModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatSelectModule,
MatToolbarModule
} from '@angular/material';
import {NgxChartsModule} from '@swimlane/ngx-charts'; import {NgxChartsModule} from '@swimlane/ngx-charts';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {FormsModule} from '@angular/forms';
@NgModule({ @NgModule({
declarations: [ declarations: [AppComponent],
AppComponent
],
imports: [ imports: [
BrowserAnimationsModule, BrowserAnimationsModule,
BrowserModule, BrowserModule,
FormsModule,
MatAutocompleteModule,
MatButtonModule, MatButtonModule,
MatChipsModule, MatChipsModule,
MatFormFieldModule,
MatIconModule, MatIconModule,
MatInputModule,
MatSelectModule,
MatToolbarModule, MatToolbarModule,
NgxChartsModule, NgxChartsModule,
], ],

View File

@ -10,6 +10,14 @@ html, body {
font-family: 'Archivo', sans-serif; font-family: 'Archivo', sans-serif;
} }
.content-height {
height: calc(100vh - 64px);
}
.w-50 {
width: 50%;
}
// Bootstrap overrides // Bootstrap overrides
.text-muted { .text-muted {
color: #bbbbbb !important; color: #bbbbbb !important;