Task/extract page tabs to dedicated component (#6797)

* Extract page tabs

* Update changelog
main
Thomas Kaul 13 hours ago committed by GitHub
parent 29e9336baf
commit ceea375227
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Changed
- Extracted the page tabs to a reusable component
### Fixed
- Resolved an issue with the cash balance calculation of an account for `SELL` activities to ensure fees are correctly subtracted

@ -1,20 +1,15 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import {
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import { DataService } from '@ghostfolio/ui/services';
import {
ChangeDetectorRef,
Component,
CUSTOM_ELEMENTS_SCHEMA,
DestroyRef,
OnInit
} from '@angular/core';
import { ChangeDetectorRef, Component, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import {
documentTextOutline,
@ -24,18 +19,15 @@ import {
shieldCheckmarkOutline,
sparklesOutline
} from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [IonIcon, MatTabsModule, RouterModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [GfPageTabsComponent],
selector: 'gf-about-page',
styleUrls: ['./about-page.scss'],
templateUrl: './about-page.html'
})
export class AboutPageComponent implements OnInit {
public deviceType: string;
export class AboutPageComponent {
public hasPermissionForSubscription: boolean;
public tabs: TabConfiguration[] = [];
public user: User;
@ -44,7 +36,6 @@ export class AboutPageComponent implements OnInit {
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private destroyRef: DestroyRef,
private deviceDetectorService: DeviceDetectorService,
private userService: UserService
) {
const { globalPermissions } = this.dataService.fetchInfo();
@ -112,8 +103,4 @@ export class AboutPageComponent implements OnInit {
sparklesOutline
});
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
}
}

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -1,10 +1,10 @@
import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import {
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import { Component, OnInit } from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import {
flashOutline,
@ -13,20 +13,18 @@ import {
serverOutline,
settingsOutline
} from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [IonIcon, MatTabsModule, RouterModule],
imports: [GfPageTabsComponent],
selector: 'gf-admin-page',
styleUrls: ['./admin-page.scss'],
templateUrl: './admin-page.html'
})
export class AdminPageComponent implements OnInit {
public deviceType: string;
public tabs: TabConfiguration[] = [];
public constructor(private deviceDetectorService: DeviceDetectorService) {
public constructor() {
addIcons({
flashOutline,
peopleOutline,
@ -37,8 +35,6 @@ export class AdminPageComponent implements OnInit {
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
this.tabs = [
{
iconName: 'reader-outline',

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2" [innerHTML]="tab.label"></div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -1,33 +1,27 @@
import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import {
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import { DataService } from '@ghostfolio/ui/services';
import { CUSTOM_ELEMENTS_SCHEMA, Component, OnInit } from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { Component } from '@angular/core';
import { addIcons } from 'ionicons';
import { cloudyOutline, readerOutline, serverOutline } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [IonIcon, MatTabsModule, RouterModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [GfPageTabsComponent],
selector: 'gf-faq-page',
styleUrls: ['./faq-page.scss'],
templateUrl: './faq-page.html'
})
export class GfFaqPageComponent implements OnInit {
public deviceType: string;
export class GfFaqPageComponent {
public hasPermissionForSubscription: boolean;
public tabs: TabConfiguration[] = [];
public constructor(
private dataService: DataService,
private deviceDetectorService: DeviceDetectorService
) {
public constructor(private dataService: DataService) {
const { globalPermissions } = this.dataService.fetchInfo();
this.hasPermissionForSubscription = hasPermission(
@ -56,8 +50,4 @@ export class GfFaqPageComponent implements OnInit {
addIcons({ cloudyOutline, readerOutline, serverOutline });
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
}
}

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -1,20 +1,20 @@
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import {
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import {
ChangeDetectorRef,
Component,
CUSTOM_ELEMENTS_SCHEMA,
DestroyRef,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import {
albumsOutline,
@ -23,18 +23,15 @@ import {
newspaperOutline,
readerOutline
} from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [IonIcon, MatTabsModule, RouterModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [GfPageTabsComponent],
selector: 'gf-home-page',
styleUrls: ['./home-page.scss'],
templateUrl: './home-page.html'
})
export class GfHomePageComponent implements OnInit {
public deviceType: string;
public hasImpersonationId: boolean;
public tabs: TabConfiguration[] = [];
public user: User;
@ -42,7 +39,6 @@ export class GfHomePageComponent implements OnInit {
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceDetectorService: DeviceDetectorService,
private impersonationStorageService: ImpersonationStorageService,
private userService: UserService
) {
@ -104,8 +100,6 @@ export class GfHomePageComponent implements OnInit {
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntilDestroyed(this.destroyRef))

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -1,16 +1,13 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { User } from '@ghostfolio/common/interfaces';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import {
ChangeDetectorRef,
Component,
DestroyRef,
OnInit
} from '@angular/core';
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import { ChangeDetectorRef, Component, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { addIcons } from 'ionicons';
import {
analyticsOutline,
@ -19,24 +16,21 @@ import {
scanOutline,
swapVerticalOutline
} from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [MatTabsModule, RouterModule],
imports: [GfPageTabsComponent],
selector: 'gf-portfolio-page',
styleUrls: ['./portfolio-page.scss'],
templateUrl: './portfolio-page.html'
})
export class PortfolioPageComponent implements OnInit {
public deviceType: string;
export class PortfolioPageComponent {
public tabs: TabConfiguration[] = [];
public user: User;
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceDetectorService: DeviceDetectorService,
private userService: UserService
) {
this.userService.stateChanged
@ -86,8 +80,4 @@ export class PortfolioPageComponent implements OnInit {
swapVerticalOutline
});
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
}
}

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -1,10 +1,10 @@
import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { publicRoutes } from '@ghostfolio/common/routes/routes';
import {
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import { Component, OnInit } from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { Component } from '@angular/core';
import { addIcons } from 'ionicons';
import {
bookOutline,
@ -12,17 +12,15 @@ import {
newspaperOutline,
readerOutline
} from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [IonIcon, MatTabsModule, RouterModule],
imports: [GfPageTabsComponent],
selector: 'gf-resources-page',
styleUrls: ['./resources-page.scss'],
templateUrl: './resources-page.html'
})
export class ResourcesPageComponent implements OnInit {
public deviceType: string;
export class ResourcesPageComponent {
public tabs: TabConfiguration[] = [
{
iconName: 'reader-outline',
@ -46,11 +44,7 @@ export class ResourcesPageComponent implements OnInit {
}
];
public constructor(private deviceDetectorService: DeviceDetectorService) {
public constructor() {
addIcons({ bookOutline, libraryOutline, newspaperOutline, readerOutline });
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
}
}

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -1,39 +1,30 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { User } from '@ghostfolio/common/interfaces';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import {
ChangeDetectorRef,
Component,
CUSTOM_ELEMENTS_SCHEMA,
DestroyRef,
OnInit
} from '@angular/core';
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import { ChangeDetectorRef, Component, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { diamondOutline, keyOutline, settingsOutline } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [IonIcon, MatTabsModule, RouterModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [GfPageTabsComponent],
selector: 'gf-user-account-page',
styleUrls: ['./user-account-page.scss'],
templateUrl: './user-account-page.html'
})
export class GfUserAccountPageComponent implements OnInit {
public deviceType: string;
export class GfUserAccountPageComponent {
public tabs: TabConfiguration[] = [];
public user: User;
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceDetectorService: DeviceDetectorService,
private userService: UserService
) {
this.userService.stateChanged
@ -68,8 +59,4 @@ export class GfUserAccountPageComponent implements OnInit {
addIcons({ diamondOutline, keyOutline, settingsOutline });
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
}
}

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -1,37 +1,30 @@
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { User } from '@ghostfolio/common/interfaces';
import { internalRoutes } from '@ghostfolio/common/routes/routes';
import {
ChangeDetectorRef,
Component,
DestroyRef,
OnInit
} from '@angular/core';
GfPageTabsComponent,
TabConfiguration
} from '@ghostfolio/ui/page-tabs';
import { ChangeDetectorRef, Component, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { addIcons } from 'ionicons';
import { albumsOutline, analyticsOutline } from 'ionicons/icons';
import { DeviceDetectorService } from 'ngx-device-detector';
@Component({
host: { class: 'page has-tabs' },
imports: [IonIcon, MatTabsModule, RouterModule],
imports: [GfPageTabsComponent],
selector: 'gf-zen-page',
styleUrls: ['./zen-page.scss'],
templateUrl: './zen-page.html'
})
export class GfZenPageComponent implements OnInit {
public deviceType: string;
export class GfZenPageComponent {
public tabs: TabConfiguration[] = [];
public user: User;
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private destroyRef: DestroyRef,
private deviceDetectorService: DeviceDetectorService,
private userService: UserService
) {
this.userService.stateChanged
@ -58,8 +51,4 @@ export class GfZenPageComponent implements OnInit {
addIcons({ albumsOutline, analyticsOutline });
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
}
}

@ -1,30 +1 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs; track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
}
}
</nav>
<gf-page-tabs [tabs]="tabs" />

@ -265,16 +265,6 @@ body {
}
}
.page {
&.has-tabs {
@media (min-width: 576px) {
.mat-mdc-tab-header {
background-color: rgba(var(--palette-foreground-base-dark), 0.02);
}
}
}
}
.svgMap-tooltip {
background: var(--dark-background);
@ -489,7 +479,6 @@ ngx-skeleton-loader {
.page {
display: flex;
flex-direction: column;
overflow-y: auto;
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
@ -506,44 +495,6 @@ ngx-skeleton-loader {
padding: 2rem 0;
}
}
&.has-tabs {
height: calc(100svh - var(--mat-toolbar-standard-height));
.fab-container {
@media (max-width: 575.98px) {
bottom: 5rem;
}
}
.mat-mdc-tab-nav-panel {
padding: 2rem 0;
}
@include mat.tabs-overrides(
(
divider-height: 0
)
);
@media (min-width: 576px) {
flex-direction: row-reverse;
.mat-mdc-tab-header {
background-color: rgba(var(--palette-foreground-base), 0.02);
padding: 2rem 0;
width: 14rem;
.mat-mdc-tab-links {
flex-direction: column;
.mat-mdc-tab-link {
justify-content: flex-start;
}
}
}
}
}
}
.svgMap-tooltip {

@ -293,24 +293,6 @@ $gf-typography: (
container-shape: 4px
)
);
.page.has-tabs {
@include mat.tabs-overrides(
(
container-height: 3rem
)
);
}
}
@media (min-width: 576px) {
.page.has-tabs {
@include mat.tabs-overrides(
(
container-height: 2rem
)
);
}
}
@include mat.badge-overrides(
@ -436,15 +418,6 @@ $gf-typography: (
)
);
}
.page.has-tabs {
@include mat.tabs-overrides(
(
active-indicator-height: 0,
label-text-tracking: normal
)
);
}
}
:root {

@ -91,7 +91,6 @@ import type { SubscriptionOffer } from './subscription-offer.interface';
import type { SymbolItem } from './symbol-item.interface';
import type { SymbolMetrics } from './symbol-metrics.interface';
import type { SystemMessage } from './system-message.interface';
import type { TabConfiguration } from './tab-configuration.interface';
import type { ToggleOption } from './toggle-option.interface';
import type { UserItem } from './user-item.interface';
import type { UserSettings } from './user-settings.interface';
@ -186,7 +185,6 @@ export {
SymbolItem,
SymbolMetrics,
SystemMessage,
TabConfiguration,
ToggleOption,
User,
UserItem,

@ -0,0 +1,2 @@
export * from './interfaces/interfaces';
export * from './page-tabs.component';

@ -0,0 +1,30 @@
<mat-tab-nav-panel #tabPanel class="flex-grow-1 overflow-auto">
<router-outlet />
</mat-tab-nav-panel>
<nav
mat-align-tabs="center"
mat-tab-nav-bar
[disablePagination]="true"
[tabPanel]="tabPanel"
>
@for (tab of tabs(); track tab) {
@if (tab.showCondition !== false) {
<a
#rla="routerLinkActive"
class="no-min-width px-3"
mat-tab-link
routerLinkActive
[active]="rla.isActive"
[routerLink]="tab.routerLink"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large' : 'small'"
/>
<div class="d-none d-sm-block ml-2" [innerHTML]="tab.label"></div>
</a>
}
}
</nav>

@ -0,0 +1,70 @@
@use '@angular/material' as mat;
:host {
display: flex;
flex-direction: column;
height: calc(100svh - var(--mat-toolbar-standard-height));
width: 100%;
@include mat.tabs-overrides(
(
active-indicator-height: 0,
divider-height: 0,
label-text-tracking: normal
)
);
.fab-container {
@media (max-width: 575.98px) {
bottom: 5rem;
}
}
::ng-deep {
.mat-mdc-tab-nav-panel {
padding: 2rem 0;
}
}
@media (max-width: 575.98px) {
@include mat.tabs-overrides(
(
container-height: 3rem
)
);
}
@media (min-width: 576px) {
flex-direction: row-reverse;
@include mat.tabs-overrides(
(
container-height: 2rem
)
);
::ng-deep {
.mat-mdc-tab-header {
background-color: rgba(var(--palette-foreground-base), 0.02);
padding: 2rem 0;
width: 14rem;
.mat-mdc-tab-links {
flex-direction: column;
.mat-mdc-tab-link {
justify-content: flex-start;
}
}
}
}
}
}
:host-context(.theme-dark) {
@media (min-width: 576px) {
.mat-mdc-tab-header {
background-color: rgba(var(--palette-foreground-base-dark), 0.02);
}
}
}

@ -0,0 +1,30 @@
import {
ChangeDetectionStrategy,
Component,
inject,
input
} from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs';
import { RouterModule } from '@angular/router';
import { IonIcon } from '@ionic/angular/standalone';
import { DeviceDetectorService } from 'ngx-device-detector';
import { TabConfiguration } from './interfaces/interfaces';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [IonIcon, MatTabsModule, RouterModule],
selector: 'gf-page-tabs',
styleUrls: ['./page-tabs.component.scss'],
templateUrl: './page-tabs.component.html'
})
export class GfPageTabsComponent {
public deviceType: string;
public readonly tabs = input.required<TabConfiguration[]>();
private readonly deviceService = inject(DeviceDetectorService);
public constructor() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
}
}
Loading…
Cancel
Save