Feature/introduce sidebar navigation on desktop (#2340)

* Introduce sidebar navigation on desktop

* Update changelog
pull/1596/head^2
Thomas Kaul 9 months ago committed by GitHub
parent 522025ffa0
commit d0112968e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Introduced a sidebar navigation on desktop
## 2.1.0 - 2023-09-15
### Added

@ -9,10 +9,12 @@ import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
host: { class: 'page with-tabs' },
selector: 'gf-about-page',
styleUrls: ['./about-page.scss'],
templateUrl: './about-page.html'
@ -22,6 +24,7 @@ export class AboutPageComponent implements OnDestroy, OnInit {
return this.hasMessage;
}
public deviceType: string;
public hasMessage: boolean;
public hasPermissionForSubscription: boolean;
public tabs: TabConfiguration[] = [];
@ -32,6 +35,7 @@ export class AboutPageComponent implements OnDestroy, OnInit {
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private deviceService: DeviceDetectorService,
private userService: UserService
) {
const { globalPermissions, systemMessage } = this.dataService.fetchInfo();
@ -88,7 +92,9 @@ export class AboutPageComponent implements OnDestroy, OnInit {
});
}
public ngOnInit() {}
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
}
public ngOnDestroy() {
this.unsubscribeSubject.next();

@ -14,7 +14,10 @@
[routerLink]="tab.path"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon size="large" [name]="tab.iconName"></ion-icon>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large': 'small'"
></ion-icon>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
</ng-container>

@ -2,27 +2,6 @@
:host {
color: rgb(var(--dark-primary-text));
display: flex;
flex-direction: column;
height: calc(100vh - 5rem);
overflow-y: auto;
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
::ng-deep {
.mat-mdc-tab-link-container {
--mat-tab-header-active-focus-indicator-color: transparent;
--mat-tab-header-active-hover-indicator-color: transparent;
--mdc-tab-indicator-active-indicator-color: transparent;
.mat-mdc-tab-link {
&:hover {
opacity: 0.75;
}
}
}
}
}
:host-context(.is-dark-theme) {

@ -1,9 +1,11 @@
import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core';
import { DataService } from '@ghostfolio/client/services/data.service';
import { TabConfiguration } from '@ghostfolio/common/interfaces';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
@Component({
host: { class: 'page with-tabs' },
selector: 'gf-admin-page',
styleUrls: ['./admin-page.scss'],
templateUrl: './admin-page.html'
@ -13,18 +15,24 @@ export class AdminPageComponent implements OnDestroy, OnInit {
return this.hasMessage;
}
public deviceType: string;
public hasMessage: boolean;
public tabs: TabConfiguration[] = [];
private unsubscribeSubject = new Subject<void>();
public constructor(private dataService: DataService) {
public constructor(
private dataService: DataService,
private deviceService: DeviceDetectorService
) {
const { systemMessage } = this.dataService.fetchInfo();
this.hasMessage = !!systemMessage;
}
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.tabs = [
{
iconName: 'reader-outline',

@ -14,7 +14,10 @@
[routerLink]="tab.path"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon size="large" [name]="tab.iconName"></ion-icon>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large': 'small'"
></ion-icon>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
</ng-container>

@ -2,27 +2,6 @@
:host {
color: rgb(var(--dark-primary-text));
display: flex;
flex-direction: column;
height: calc(100vh - 5rem);
overflow-y: auto;
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
::ng-deep {
.mat-mdc-tab-link-container {
--mat-tab-header-active-focus-indicator-color: transparent;
--mat-tab-header-active-hover-indicator-color: transparent;
--mdc-tab-indicator-active-indicator-color: transparent;
.mat-mdc-tab-link {
&:hover {
opacity: 0.75;
}
}
}
}
}
:host-context(.is-dark-theme) {

@ -9,10 +9,12 @@ import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
host: { class: 'page with-tabs' },
selector: 'gf-home-page',
styleUrls: ['./home-page.scss'],
templateUrl: './home-page.html'
@ -22,6 +24,7 @@ export class HomePageComponent implements OnDestroy, OnInit {
return this.hasMessage;
}
public deviceType: string;
public hasMessage: boolean;
public hasPermissionToAccessFearAndGreedIndex: boolean;
public tabs: TabConfiguration[] = [];
@ -32,6 +35,7 @@ export class HomePageComponent implements OnDestroy, OnInit {
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private deviceService: DeviceDetectorService,
private userService: UserService
) {
const { globalPermissions, systemMessage } = this.dataService.fetchInfo();
@ -81,7 +85,9 @@ export class HomePageComponent implements OnDestroy, OnInit {
});
}
public ngOnInit() {}
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
}
public ngOnDestroy() {
this.unsubscribeSubject.next();

@ -14,7 +14,10 @@
[routerLink]="tab.path"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon size="large" [name]="tab.iconName"></ion-icon>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large': 'small'"
></ion-icon>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
</ng-container>

@ -2,27 +2,6 @@
:host {
color: rgb(var(--dark-primary-text));
display: flex;
flex-direction: column;
height: calc(100vh - 5rem);
overflow-y: auto;
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
::ng-deep {
.mat-mdc-tab-link-container {
--mat-tab-header-active-focus-indicator-color: transparent;
--mat-tab-header-active-hover-indicator-color: transparent;
--mdc-tab-indicator-active-indicator-color: transparent;
.mat-mdc-tab-link {
&:hover {
opacity: 0.75;
}
}
}
}
}
:host-context(.is-dark-theme) {

@ -13,10 +13,12 @@ import {
User
} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
host: { class: 'page with-tabs' },
selector: 'gf-portfolio-page',
styleUrls: ['./portfolio-page.scss'],
templateUrl: './portfolio-page.html'
@ -26,6 +28,7 @@ export class PortfolioPageComponent implements OnDestroy, OnInit {
return this.hasMessage;
}
public deviceType: string;
public hasMessage: boolean;
public info: InfoItem;
public tabs: TabConfiguration[] = [];
@ -36,6 +39,7 @@ export class PortfolioPageComponent implements OnDestroy, OnInit {
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private deviceService: DeviceDetectorService,
private userService: UserService
) {
this.info = this.dataService.fetchInfo();
@ -84,7 +88,9 @@ export class PortfolioPageComponent implements OnDestroy, OnInit {
});
}
public ngOnInit() {}
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
}
public ngOnDestroy() {
this.unsubscribeSubject.next();

@ -14,7 +14,10 @@
[routerLink]="tab.path"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon size="large" [name]="tab.iconName"></ion-icon>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large': 'small'"
></ion-icon>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
</ng-container>

@ -2,27 +2,6 @@
:host {
color: rgb(var(--dark-primary-text));
display: flex;
flex-direction: column;
height: calc(100vh - 5rem);
overflow-y: auto;
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
::ng-deep {
.mat-mdc-tab-link-container {
--mat-tab-header-active-focus-indicator-color: transparent;
--mat-tab-header-active-hover-indicator-color: transparent;
--mdc-tab-indicator-active-indicator-color: transparent;
.mat-mdc-tab-link {
&:hover {
opacity: 0.75;
}
}
}
}
}
:host-context(.is-dark-theme) {

@ -1,33 +1,27 @@
import { ViewportScroller } from '@angular/common';
import {
AfterViewInit,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { TabConfiguration, User } from '@ghostfolio/common/interfaces';
import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs';
import { first, takeUntil } from 'rxjs/operators';
import { takeUntil } from 'rxjs/operators';
@Component({
host: { class: 'page with-tabs' },
selector: 'gf-zen-page',
templateUrl: './zen-page.html',
styleUrls: ['./zen-page.scss']
styleUrls: ['./zen-page.scss'],
templateUrl: './zen-page.html'
})
export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
export class ZenPageComponent implements OnDestroy, OnInit {
public deviceType: string;
public tabs: TabConfiguration[] = [];
public user: User;
private unsubscribeSubject = new Subject<void>();
public constructor(
private route: ActivatedRoute,
private changeDetectorRef: ChangeDetectorRef,
private userService: UserService,
private viewportScroller: ViewportScroller
private deviceService: DeviceDetectorService,
private userService: UserService
) {
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
@ -52,12 +46,8 @@ export class ZenPageComponent implements AfterViewInit, OnDestroy, OnInit {
});
}
public ngOnInit() {}
public ngAfterViewInit(): void {
this.route.fragment
.pipe(first())
.subscribe((fragment) => this.viewportScroller.scrollToAnchor(fragment));
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
}
public ngOnDestroy() {

@ -14,7 +14,10 @@
[routerLink]="tab.path"
[routerLinkActiveOptions]="{ exact: true }"
>
<ion-icon size="large" [name]="tab.iconName"></ion-icon>
<ion-icon
[name]="tab.iconName"
[size]="deviceType === 'mobile' ? 'large': 'small'"
></ion-icon>
<div class="d-none d-sm-block ml-2">{{ tab.label }}</div>
</a>
</ng-container>

@ -2,27 +2,6 @@
:host {
color: rgb(var(--dark-primary-text));
display: flex;
flex-direction: column;
height: calc(100vh - 5rem);
overflow-y: auto;
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
::ng-deep {
.mat-mdc-tab-link-container {
--mat-tab-header-active-focus-indicator-color: transparent;
--mat-tab-header-active-hover-indicator-color: transparent;
--mdc-tab-indicator-active-indicator-color: transparent;
.mat-mdc-tab-link {
&:hover {
opacity: 0.75;
}
}
}
}
}
:host-context(.is-dark-theme) {

@ -274,6 +274,16 @@ body {
}
}
.page {
&.with-tabs {
.mat-mdc-tab-nav-bar {
--mat-tab-header-inactive-label-text-color: rgba(
var(--light-primary-text)
);
}
}
}
.svgMap-tooltip {
background: var(--dark-background);
@ -455,7 +465,45 @@ ngx-skeleton-loader {
}
.page {
padding-bottom: 5rem;
display: flex;
flex-direction: column;
overflow-y: auto;
&:not(.with-tabs) {
padding-bottom: 5rem;
}
&.with-tabs {
height: calc(100vh - 5rem);
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
.mat-mdc-tab-nav-bar {
--mat-tab-header-active-focus-indicator-color: transparent;
--mat-tab-header-active-hover-indicator-color: transparent;
--mat-tab-header-inactive-label-text-color: rgba(
var(--dark-primary-text)
);
--mdc-tab-indicator-active-indicator-color: transparent;
}
@media (min-width: 576px) {
flex-direction: row-reverse;
.mat-mdc-tab-header {
width: 12rem;
--mdc-secondary-navigation-tab-container-height: 2rem;
.mat-mdc-tab-links {
flex-direction: column;
.mat-mdc-tab-link {
justify-content: flex-start;
}
}
}
}
}
}
.svgMap-tooltip {

Loading…
Cancel
Save