Browse Source

Refactoring

pull/4044/head
Thomas Kaul 9 months ago
parent
commit
bf9ebdb84e
  1. 2
      apps/client/src/app/pages/portfolio/allocations/allocations-page.html
  2. 117
      libs/ui/src/lib/top-holdings/top-holdings.component.html
  3. 73
      libs/ui/src/lib/top-holdings/top-holdings.component.scss
  4. 76
      libs/ui/src/lib/top-holdings/top-holdings.component.ts

2
apps/client/src/app/pages/portfolio/allocations/allocations-page.html

@ -347,7 +347,7 @@
[locale]="user?.settings?.locale"
[pageSize]="10"
[topHoldings]="topHoldings"
(proportionChartClicked)="onSymbolChartClicked($event)"
(holdingClicked)="onSymbolChartClicked($event)"
/>
</mat-card-content>
</mat-card>

117
libs/ui/src/lib/top-holdings/top-holdings.component.html

@ -2,14 +2,18 @@
<table
class="gf-table holdings-table w-100"
mat-table
matSort
matSortActive="allocationInPercentage"
matSortDirection="desc"
multiTemplateDataRows
style="table-layout: auto"
[dataSource]="dataSource"
>
<colgroup>
<col class="w-100" />
<col />
<col />
</colgroup>
<ng-container matColumnDef="name">
<th *matHeaderCellDef class="px-2" mat-header-cell mat-sort-header>
<th *matHeaderCellDef class="px-2" mat-header-cell>
<ng-container i18n>Name</ng-container>
</th>
<td *matCellDef="let element" class="px-2" mat-cell>
@ -18,12 +22,7 @@
</ng-container>
<ng-container matColumnDef="valueInBaseCurrency">
<th
*matHeaderCellDef
class="justify-content-end px-2"
mat-header-cell
mat-sort-header
>
<th *matHeaderCellDef class="px-2 text-right" mat-header-cell>
<ng-container i18n>Value</ng-container>
</th>
<td *matCellDef="let element" class="px-2" mat-cell>
@ -38,12 +37,7 @@
</ng-container>
<ng-container matColumnDef="allocationInPercentage" stickyEnd>
<th
*matHeaderCellDef
class="justify-content-end px-2"
mat-header-cell
mat-sort-header
>
<th *matHeaderCellDef class="justify-content-end px-2" mat-header-cell>
<span class="d-none d-sm-block" i18n>Allocation</span>
<span class="d-block d-sm-none" title="Allocation">%</span>
</th>
@ -61,6 +55,7 @@
<ng-container matColumnDef="expandedDetail">
<td
*matCellDef="let element"
class="p-0"
mat-cell
[attr.colspan]="displayedColumns.length"
>
@ -68,71 +63,61 @@
[@detailExpand]="element.expand ? 'expanded' : 'collapsed'"
class="allocationByParent"
>
<div class="holdingParents">
<div class="holdingParentProportionChart d-flex">
@for (
parentHolding of element.parents?.slice(0, 10);
track parentHolding;
let i = $index
) {
<div
[class.cursor-pointer]="parentHolding?.symbol.length > 0"
[matTooltip]="parentHolding.name"
[style.background-color]="getColor(parentHolding?.symbol)"
[style.width.%]="100 * parentHolding?.allocationInPercentage"
(click)="onClickOpenPositionModal(parentHolding.position)"
>
{{ parentHolding?.symbol }}
</div>
}
</div>
<div class="holding-parents-table">
<table mat-table [dataSource]="element.parents">
<colgroup>
<col class="w-100" />
<col />
<col />
</colgroup>
<ng-container matColumnDef="name">
<th *matHeaderCellDef mat-header-cell>Name</th>
<td *matCellDef="let parentHolding" mat-cell>
<span
class="colorBadge"
[style.background-color]="getColor(parentHolding?.symbol)"
></span>
{{ parentHolding?.name }}
</td>
</ng-container>
<ng-container matColumnDef="symbol">
<th *matHeaderCellDef mat-header-cell>Symbol</th>
<td *matCellDef="let parentHolding" mat-cell>
{{ parentHolding?.symbol }}
<td *matFooterCellDef class="px-2" mat-footer-cell>
<ng-container i18n>Name</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="valueInBaseCurrency">
<th *matHeaderCellDef mat-header-cell>Value</th>
<td *matCellDef="let parentHolding" mat-cell>
<gf-value
[isCurrency]="true"
[locale]="locale"
[value]="parentHolding?.valueInBaseCurrency"
/>
<div class="d-flex justify-content-end">
<gf-value
[isCurrency]="true"
[locale]="locale"
[value]="parentHolding?.valueInBaseCurrency"
/>
</div>
</td>
<td *matFooterCellDef class="px-2" mat-footer-cell>
<ng-container i18n>Value</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="allocationInPercentage">
<th *matHeaderCellDef mat-header-cell>Allocation</th>
<td *matCellDef="let parentHolding" mat-cell>
<gf-value
[isPercent]="true"
[locale]="locale"
[value]="parentHolding?.allocationInPercentage"
/>
<div class="d-flex justify-content-end">
<gf-value
[isPercent]="true"
[locale]="locale"
[value]="parentHolding?.allocationInPercentage"
/>
</div>
</td>
<td *matFooterCellDef class="px-2" mat-footer-cell>
<span class="d-none d-sm-block" i18n>Allocation</span>
<span class="d-block d-sm-none">%</span>
</td>
</ng-container>
<tr
*matHeaderRowDef="displayedHoldingParentColumns"
mat-header-row
*matRowDef="let row; columns: displayedColumns"
mat-row
[ngClass]="{ 'cursor-pointer': row.position }"
(click)="onClickHolding(row.position)"
></tr>
<tr
*matRowDef="let row; columns: displayedHoldingParentColumns"
mat-row
[class.cursor-pointer]="row.position"
(click)="onClickOpenPositionModal(row.position)"
*matFooterRowDef="displayedColumns"
mat-footer-row
style="visibility: hidden"
></tr>
</table>
</div>
@ -144,8 +129,10 @@
<tr
*matRowDef="let element; columns: displayedColumns"
mat-row
[class.cursor-pointer]="element.parents?.length > 0"
[class.expanded]="element.expand ?? false"
[ngClass]="{
'cursor-pointer': element.parents?.length > 0,
expanded: element.expand ?? false
}"
(click)="
element.expand ? (element.expand = false) : (element.expand = true)
"

73
libs/ui/src/lib/top-holdings/top-holdings.component.scss

@ -2,73 +2,32 @@
display: block;
.holdings-table {
th {
::ng-deep {
.mat-sort-header-container {
justify-content: inherit;
}
tr {
&:not(.expanded) + tr.holding-detail td {
border-bottom: 0;
}
}
tr.holding-detail {
height: 0;
&:hover {
// Disable hover effect.
background-color: var(--mat-table-background-color-even) !important;
}
}
tr.expanded {
background-color: var(--mat-table-background-color-even);
}
tr:not(.expanded) + tr.holding-detail td {
border-bottom: 0;
}
.holdingParents {
padding: 1em 0;
--table-padding: 0.5em;
.holdingParentProportionChart {
height: 2em;
div {
box-sizing: border-box;
line-height: 2em;
padding: 0 var(--table-padding);
overflow: hidden;
&:hover {
filter: brightness(0.95);
}
&.expanded {
> td {
font-weight: bold;
}
}
.colorBadge {
width: 0.75em;
height: 0.75em;
border-radius: 100%;
display: inline-block;
&.holding-detail {
height: 0;
}
table,
tr,
th,
td {
background-color: transparent;
}
.holding-parents-table {
--table-padding: 0.5em;
tr {
height: auto;
tr {
height: auto;
&:hover {
background-color: var(--mat-table-background-color-hover);
td {
padding: var(--table-padding);
}
}
}
th,
td {
padding: var(--table-padding);
}
}
}
}

76
libs/ui/src/lib/top-holdings/top-holdings.component.ts

@ -27,29 +27,11 @@ import {
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { DataSource } from '@prisma/client';
import { get } from 'lodash';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs';
const {
blue,
cyan,
grape,
green,
indigo,
lime,
orange,
pink,
red,
teal,
violet,
yellow
} = require('open-color');
@Component({
animations: [
trigger('detailExpand', [
@ -67,9 +49,7 @@ const {
GfValueComponent,
MatButtonModule,
MatPaginatorModule,
MatSortModule,
MatTableModule,
MatTooltipModule,
NgxSkeletonLoaderModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
@ -91,14 +71,9 @@ export class GfTopHoldingsComponent implements OnChanges, OnDestroy {
};
} = {};
@Output() proportionChartClicked = new EventEmitter<AssetProfileIdentifier>();
@Output() holdingClicked = new EventEmitter<AssetProfileIdentifier>();
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
private colorMap: {
[symbol: string]: string;
} = {};
public dataSource = new MatTableDataSource<HoldingWithParents>();
public displayedColumns: string[] = [
@ -106,66 +81,25 @@ export class GfTopHoldingsComponent implements OnChanges, OnDestroy {
'valueInBaseCurrency',
'allocationInPercentage'
];
public displayedHoldingParentColumns: string[] = [
'name',
'symbol',
'valueInBaseCurrency',
'allocationInPercentage'
];
public isLoading = true;
private unsubscribeSubject = new Subject<void>();
private colorPaletteIndex = 0;
private colorPalette = [
blue[5],
teal[5],
lime[5],
orange[5],
pink[5],
violet[5],
indigo[5],
cyan[5],
green[5],
yellow[5],
red[5],
grape[5]
];
public getColor(symbol) {
if (this.colorMap[symbol]) {
// Reuse color
return this.colorMap[symbol];
} else {
const color = this.colorPalette[this.colorPaletteIndex];
this.colorPaletteIndex =
this.colorPaletteIndex < this.colorPalette.length
? this.colorPaletteIndex + 1
: 0;
this.colorMap[symbol] = color;
return color;
}
}
public onClickOpenPositionModal(holding: AssetProfileIdentifier) {
try {
this.proportionChartClicked.emit(holding);
} catch {}
}
public ngOnChanges() {
this.isLoading = true;
this.dataSource = new MatTableDataSource(this.topHoldings);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.dataSource.sortingDataAccessor = get;
if (this.topHoldings) {
this.isLoading = false;
}
}
public onClickHolding(assetProfileIdentifier: AssetProfileIdentifier) {
this.holdingClicked.emit(assetProfileIdentifier);
}
public onShowAllHoldings() {
this.pageSize = Number.MAX_SAFE_INTEGER;

Loading…
Cancel
Save