Feature/introduce sidebar navigation on desktop (#2340)
* Introduce sidebar navigation on desktop * Update changelog
This commit is contained in:
parent
522025ffa0
commit
d0112968e8
@ -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 {
|
||||
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…
x
Reference in New Issue
Block a user