Select what ETF's you would like to use
This commit is contained in:
parent
deb5251b5a
commit
37a8889a9d
@ -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">
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div *ngIf="fileNames.length" class="content-height w-100" style="overflow: hidden auto">
|
||||||
|
<!-- Input for selecting holdings -->
|
||||||
|
<div class="p-3">
|
||||||
|
<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"
|
[scheme]="colorScheme"
|
||||||
[results]="chartResults"
|
[results]="chartResults"
|
||||||
[xAxis]="true"
|
[xAxis]="true"
|
||||||
[yAxis]="true"
|
[yAxis]="true"
|
||||||
[xAxisTickFormatting]="format">
|
[yAxisTickFormatting]="format">
|
||||||
</ngx-charts-bar-horizontal-2d>
|
</ngx-charts-bar-vertical-2d>
|
||||||
</div>
|
</div>
|
||||||
<input #fileUploader type="file" accept="text/csv" multiple hidden (change)="upload(fileUploader.files)">
|
|
||||||
|
@ -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} %`}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
],
|
],
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user