diff --git a/src/portal/src/app/project/member/add-group/add-group.component.html b/src/portal/src/app/project/member/add-group/add-group.component.html
index 0432901e5..cb265b8a4 100644
--- a/src/portal/src/app/project/member/add-group/add-group.component.html
+++ b/src/portal/src/app/project/member/add-group/add-group.component.html
@@ -105,4 +105,4 @@
-
\ No newline at end of file
+
diff --git a/src/portal/src/app/project/project-detail/project-detail.component.html b/src/portal/src/app/project/project-detail/project-detail.component.html
index 69b5bd842..248973f18 100644
--- a/src/portal/src/app/project/project-detail/project-detail.component.html
+++ b/src/portal/src/app/project/project-detail/project-detail.component.html
@@ -6,7 +6,7 @@
-
+
diff --git a/src/portal/src/app/project/project-detail/project-detail.component.scss b/src/portal/src/app/project/project-detail/project-detail.component.scss
index 8e771e1d4..0e7e8ab29 100644
--- a/src/portal/src/app/project/project-detail/project-detail.component.scss
+++ b/src/portal/src/app/project/project-detail/project-detail.component.scss
@@ -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;
- }
- }
- }
- }
-}
diff --git a/src/portal/src/app/project/project-detail/project-detail.component.ts b/src/portal/src/app/project/project-detail/project-detail.component.ts
index 2422f8041..e6ea1cd1c 100644
--- a/src/portal/src/app/project/project-detail/project-detail.component.ts
+++ b/src/portal/src/app/project/project-detail/project-detail.component.ts
@@ -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();
+ 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 = 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();
+ }
}
diff --git a/src/portal/src/app/project/project-detail/project-detail.const.ts b/src/portal/src/app/project/project-detail/project-detail.const.ts
new file mode 100644
index 000000000..9d195ecb5
--- /dev/null
+++ b/src/portal/src/app/project/project-detail/project-detail.const.ts
@@ -0,0 +1,3 @@
+export const SHOW_ELLIPSIS_WIDTH = 80;
+export const DOWN: string = "down";
+export const UP: string = "up";
diff --git a/src/portal/src/css/common.scss b/src/portal/src/css/common.scss
new file mode 100644
index 000000000..06c40fdf5
--- /dev/null
+++ b/src/portal/src/css/common.scss
@@ -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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/portal/src/css/dark-theme.scss b/src/portal/src/css/dark-theme.scss
index 53999bf2e..2ecf4eb2c 100644
--- a/src/portal/src/css/dark-theme.scss
+++ b/src/portal/src/css/dark-theme.scss
@@ -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;
@@ -110,4 +112,6 @@ clr-dg-action-overflow {
color: #1b2a32;
}
}
-}
\ No newline at end of file
+}
+
+@import "./common.scss";
\ No newline at end of file
diff --git a/src/portal/src/css/light-theme.scss b/src/portal/src/css/light-theme.scss
index b09e7465a..8d93ad912 100644
--- a/src/portal/src/css/light-theme.scss
+++ b/src/portal/src/css/light-theme.scss
@@ -1 +1,5 @@
-@import "../../node_modules/@clr/ui/clr-ui.min.css";
\ No newline at end of file
+// 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";
\ No newline at end of file