Merge pull request #10384 from AllForNothing/modify-project-tab1

Improve tabs UI for project detail page
This commit is contained in:
Will Sun 2020-01-02 13:40:25 +08:00 committed by GitHub
commit 061b37764a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 31 deletions

View File

@ -6,7 +6,7 @@
<clr-tabs id="project-tabs" class="tabs" [class.in-overflow]="isTabLinkInOverFlow()">
<ng-container *ngFor="let tab of tabLinkNavList;let i=index">
<ng-container *ngIf="tab.permissions()">
<clr-tab >
<clr-tab>
<button [class.clear-default-active]="isDefaultTab(tab, i)" [clrTabLinkInOverflow]="tab.tabLinkInOverflow" id="{{'project-'+tab.linkName}}" clrTabLink
routerLink="{{tab.linkName}}" routerLinkActive="active" type="button">
<a class="nav-link">{{tab.showTabName | translate}}</a></button>

View File

@ -42,16 +42,3 @@ button {
padding: 0;
}
}
.in-overflow {
::ng-deep {
.tabs-overflow {
> .nav-item {
> button {
box-shadow: 0 -3px 0 #0077b8 inset;
color: 0077b8;
}
}
}
}
}

View File

@ -11,21 +11,24 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, HostListener, AfterViewInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Project } from '../project';
import { SessionService } from '../../shared/session.service';
import { AppConfigService } from "../../app-config.service";
import { forkJoin } from "rxjs";
import { ProjectService, UserPermissionService, USERSTATICPERMISSION } from "../../../lib/services";
import { forkJoin, Subject, Subscription } from "rxjs";
import { UserPermissionService, USERSTATICPERMISSION } from "../../../lib/services";
import { ErrorHandler } from "../../../lib/utils/error-handler";
import { debounceTime } from 'rxjs/operators';
import { DOWN, SHOW_ELLIPSIS_WIDTH, UP } from './project-detail.const';
@Component({
selector: 'project-detail',
templateUrl: 'project-detail.component.html',
styleUrls: ['project-detail.component.scss']
})
export class ProjectDetailComponent implements OnInit {
export class ProjectDetailComponent implements OnInit, AfterViewInit, OnDestroy {
hasSignedIn: boolean;
currentProject: Project;
@ -89,29 +92,32 @@ export class ProjectDetailComponent implements OnInit {
},
{
linkName: "tag-strategy",
tabLinkInOverflow: true,
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.TAG_STRATEGY",
permissions: () => this.hasTagRetentionPermission
},
{
linkName: "robot-account",
tabLinkInOverflow: true,
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.ROBOT_ACCOUNTS",
permissions: () => this.hasRobotListPermission
},
{
linkName: "webhook",
tabLinkInOverflow: true,
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.WEBHOOKS",
permissions: () => this.hasWebhookListPermission
},
{
linkName: "logs",
tabLinkInOverflow: true,
tabLinkInOverflow: false,
showTabName: "PROJECT_DETAIL.LOGS",
permissions: () => this.hasLogListPermission
}
];
previousWindowWidth: number;
private _subject = new Subject<string>();
private _subscription: Subscription;
constructor(
private route: ActivatedRoute,
private router: Router,
@ -119,8 +125,7 @@ export class ProjectDetailComponent implements OnInit {
private appConfigService: AppConfigService,
private userPermissionService: UserPermissionService,
private errorHandler: ErrorHandler,
private projectService: ProjectService) {
private cdf: ChangeDetectorRef) {
this.hasSignedIn = this.sessionService.getCurrentUser() !== null;
this.route.data.subscribe(data => {
this.currentProject = <Project>data['projectResolver'];
@ -131,6 +136,32 @@ export class ProjectDetailComponent implements OnInit {
ngOnInit() {
this.projectId = this.route.snapshot.params['id'];
this.getPermissionsList(this.projectId);
if (!this._subscription) {
this._subscription = this._subject.pipe(
debounceTime(100)
).subscribe(
type => {
if (type === DOWN) {
this.resetTabsForDownSize();
} else {
this.resetTabsForUpSize();
}
}
);
}
}
ngAfterViewInit() {
this.previousWindowWidth = window.innerWidth;
setTimeout(() => {
this.resetTabsForDownSize();
}, 0);
}
ngOnDestroy() {
if (this._subscription) {
this._subscription.unsubscribe();
this._subscription = null;
}
}
getPermissionsList(projectId: number): void {
let permissionsList = [];
@ -153,17 +184,17 @@ export class ProjectDetailComponent implements OnInit {
permissionsList.push(this.userPermissionService.getPermission(projectId,
USERSTATICPERMISSION.LABEL.KEY, USERSTATICPERMISSION.LABEL.VALUE.CREATE));
permissionsList.push(this.userPermissionService.getPermission(projectId,
USERSTATICPERMISSION.TAG_RETENTION.KEY, USERSTATICPERMISSION.TAG_RETENTION.VALUE.READ));
USERSTATICPERMISSION.TAG_RETENTION.KEY, USERSTATICPERMISSION.TAG_RETENTION.VALUE.READ));
permissionsList.push(this.userPermissionService.getPermission(projectId,
USERSTATICPERMISSION.WEBHOOK.KEY, USERSTATICPERMISSION.WEBHOOK.VALUE.LIST));
permissionsList.push(this.userPermissionService.getPermission(projectId,
USERSTATICPERMISSION.SCANNER.KEY, USERSTATICPERMISSION.SCANNER.VALUE.READ));
USERSTATICPERMISSION.SCANNER.KEY, USERSTATICPERMISSION.SCANNER.VALUE.READ));
forkJoin(...permissionsList).subscribe(Rules => {
[this.hasProjectReadPermission, this.hasLogListPermission, this.hasConfigurationListPermission, this.hasMemberListPermission
, this.hasLabelListPermission, this.hasRepositoryListPermission, this.hasHelmChartsListPermission, this.hasRobotListPermission
, this.hasLabelCreatePermission, this.hasTagRetentionPermission, this.hasWebhookListPermission,
this.hasScannerReadPermission] = Rules;
this.hasScannerReadPermission] = Rules;
}, error => this.errorHandler.error(error));
}
@ -188,10 +219,46 @@ export class ProjectDetailComponent implements OnInit {
isDefaultTab(tab, index) {
return this.route.snapshot.children[0].routeConfig.path !== tab.linkName && index === 0;
}
isTabLinkInOverFlow() {
return this.tabLinkNavList.some(tab => {
return tab.tabLinkInOverflow && this.route.snapshot.children[0].routeConfig.path === tab.linkName;
});
}
@HostListener('window:resize', ['$event'])
onResize(event) {
if (this.previousWindowWidth) {
// down size
if (this.previousWindowWidth > event.target.innerWidth) {
this._subject.next(DOWN);
} else { // up size
this._subject.next(UP);
}
}
this.previousWindowWidth = event.target.innerWidth;
}
resetTabsForDownSize(): void {
this.tabLinkNavList.filter(item => !item.tabLinkInOverflow).forEach((item, index) => {
const tabEle: HTMLElement = document.getElementById('project-' + item.linkName);
// strengthen code
if (tabEle && tabEle.getBoundingClientRect) {
const right: number = window.innerWidth - document.getElementById('project-' + item.linkName).getBoundingClientRect().right;
if (right < SHOW_ELLIPSIS_WIDTH) {
this.tabLinkNavList[index].tabLinkInOverflow = true;
}
}
});
}
resetTabsForUpSize() {
// 1.Set tabLinkInOverflow to false for all tabs(show all tabs)
for ( let i = 0; i < this.tabLinkNavList.length; i++) {
this.tabLinkNavList[i].tabLinkInOverflow = false;
}
// 2.Manually detect changes to rerender dom
this.cdf.detectChanges();
// 3. Hide overflowed tabs
this.resetTabsForDownSize();
}
}

View File

@ -0,0 +1,3 @@
export const SHOW_ELLIPSIS_WIDTH = 80;
export const DOWN: string = "down";
export const UP: string = "up";

View File

@ -0,0 +1,11 @@
// styles for dark and light theme should be defined here.
.in-overflow {
.tabs-overflow {
> .nav-item {
> button {
box-shadow: 0 -3px 0 $dark-active-tab-color inset;
color: 0077b8;
}
}
}
}

View File

@ -1,7 +1,9 @@
// Variables for dark theme should be defined here.
@import "../../node_modules/@clr/ui/clr-ui-dark.min.css";
$dark-background-color: rgb(27, 42, 50) !important;
$dark-font-color1: #acbac3 !important;
$dark-font-color-title1: #eaedf0 !important;
$dark-active-tab-color: #4aaed9;
.label-form {
background-color: #212129 !important;
@ -111,3 +113,5 @@ clr-dg-action-overflow {
}
}
}
@import "./common.scss";

View File

@ -1 +1,5 @@
// Variables for dark theme should be defined here.
@import "../../node_modules/@clr/ui/clr-ui.min.css";
$dark-active-tab-color: #0077b8;
@import "./common.scss";