mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 01:27:49 +01:00
Merge pull request #10344 from jwangyangls/fix-css-load-1
Add theme of dark
This commit is contained in:
commit
d01c852698
@ -15,6 +15,7 @@
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"extractCss": true,
|
||||
"assets": [
|
||||
"src/images",
|
||||
"src/favicon.ico",
|
||||
@ -27,7 +28,17 @@
|
||||
"node_modules/swagger-ui/dist/swagger-ui.css",
|
||||
"node_modules/prismjs/themes/prism-solarizedlight.css",
|
||||
"src/global.scss",
|
||||
"src/styles.css"
|
||||
"src/styles.css",
|
||||
{
|
||||
"input": "src/css/dark-theme.scss",
|
||||
"bundleName": "dark-theme",
|
||||
"lazy": true
|
||||
},
|
||||
{
|
||||
"input": "src/css/light-theme.scss",
|
||||
"bundleName": "light-theme",
|
||||
"lazy": true
|
||||
}
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/core-js/client/shim.min.js",
|
||||
|
8
src/portal/package-lock.json
generated
8
src/portal/package-lock.json
generated
@ -8333,7 +8333,7 @@
|
||||
"dependencies": {
|
||||
"form-data": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "http://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz",
|
||||
"integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=",
|
||||
"requires": {
|
||||
"async": "^2.0.1",
|
||||
@ -10251,7 +10251,7 @@
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||
},
|
||||
"ng-packagr": {
|
||||
@ -12874,7 +12874,7 @@
|
||||
},
|
||||
"reselect": {
|
||||
"version": "2.5.4",
|
||||
"resolved": "http://registry.npmjs.org/reselect/-/reselect-2.5.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-2.5.4.tgz",
|
||||
"integrity": "sha1-t9I/3wC4P6etAnlUb427vXZccEc="
|
||||
},
|
||||
"resolve": {
|
||||
@ -13496,7 +13496,7 @@
|
||||
},
|
||||
"serialize-error": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "http://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
|
||||
"integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go="
|
||||
},
|
||||
"serialize-javascript": {
|
||||
|
@ -19,18 +19,29 @@ import { CookieService } from 'ngx-cookie';
|
||||
|
||||
import { SessionService } from './shared/session.service';
|
||||
import { AppConfigService } from './app-config.service';
|
||||
import { ThemeService } from './theme.service';
|
||||
import { themeArray, ThemeInterface } from './theme';
|
||||
import { clone } from '../lib/utils/utils';
|
||||
|
||||
const HAS_STYLE_MODE: string = 'styleModeLocal';
|
||||
|
||||
@Component({
|
||||
selector: 'harbor-app',
|
||||
templateUrl: 'app.component.html'
|
||||
})
|
||||
export class AppComponent {
|
||||
themeArray: ThemeInterface[] = clone(themeArray);
|
||||
|
||||
styleMode: string = this.themeArray[0].showStyle;
|
||||
constructor(
|
||||
private translate: TranslateService,
|
||||
private cookie: CookieService,
|
||||
private session: SessionService,
|
||||
private appConfigService: AppConfigService,
|
||||
private titleService: Title) {
|
||||
private titleService: Title,
|
||||
public theme: ThemeService
|
||||
|
||||
) {
|
||||
// Override page title
|
||||
let key: string = "APP_TITLE.HARBOR";
|
||||
if (this.appConfigService.isIntegrationMode()) {
|
||||
@ -40,5 +51,20 @@ export class AppComponent {
|
||||
translate.get(key).subscribe((res: string) => {
|
||||
this.titleService.setTitle(res);
|
||||
});
|
||||
this.setTheme();
|
||||
}
|
||||
setTheme () {
|
||||
let styleMode = this.themeArray[0].showStyle;
|
||||
const localHasStyle = localStorage && localStorage.getItem(HAS_STYLE_MODE);
|
||||
if (localHasStyle) {
|
||||
styleMode = localStorage.getItem(HAS_STYLE_MODE);
|
||||
} else {
|
||||
localStorage.setItem(HAS_STYLE_MODE, styleMode);
|
||||
}
|
||||
this.themeArray.forEach((themeItem) => {
|
||||
if (themeItem.showStyle === styleMode) {
|
||||
this.theme.loadStyle(themeItem.currentFileName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ import { ProjectQuotasComponent } from './project-quotas/project-quotas.componen
|
||||
import { HarborLibraryModule } from "../lib/harbor-library.module";
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
||||
|
||||
registerLocaleData(zh, 'zh-cn');
|
||||
registerLocaleData(es, 'es-es');
|
||||
registerLocaleData(localeFr, 'fr-fr');
|
||||
@ -90,16 +89,16 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
||||
exports: [
|
||||
],
|
||||
providers: [
|
||||
AppConfigService,
|
||||
SkinableConfig,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initConfig,
|
||||
deps: [ AppConfigService, SkinableConfig],
|
||||
multi: true
|
||||
},
|
||||
{provide: LOCALE_ID, useValue: "en-US"},
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: InterceptHttpService, multi: true }
|
||||
AppConfigService,
|
||||
SkinableConfig,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initConfig,
|
||||
deps: [AppConfigService, SkinableConfig],
|
||||
multi: true
|
||||
},
|
||||
{ provide: LOCALE_ID, useValue: "en-US" },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: InterceptHttpService, multi: true }
|
||||
|
||||
],
|
||||
schemas: [
|
||||
@ -107,4 +106,4 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule { }
|
||||
|
@ -19,12 +19,6 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-title {
|
||||
font-size: 28px;
|
||||
letter-spacing: normal;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.search-close {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
|
@ -8,16 +8,17 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="clr-col-2 right">
|
||||
<a class="all-scanners" href="#" routerLink="/harbor/interrogation-services/scanners">{{'SCANNER.ALL_SCANNERS' | translate }}</a>
|
||||
<a class="all-scanners" href="#"
|
||||
routerLink="/harbor/interrogation-services/scanners">{{'SCANNER.ALL_SCANNERS' | translate }}</a>
|
||||
<clr-icon (click)="closeInfo()" class="close-icon" shape="times" size="24"></clr-icon>
|
||||
</div>
|
||||
</div>
|
||||
<global-message [isAppLevel]="true"></global-message>
|
||||
<global-message [isAppLevel]="true" ></global-message>
|
||||
<navigator (showAccountSettingsModal)="openModal($event)" (showDialogModalAction)="openModal($event)"></navigator>
|
||||
<div class="content-container">
|
||||
<div class="content-area" [class.container-override]="showSearch"
|
||||
[class.content-area-override]="!shouldOverrideContent"
|
||||
[class.start-content-padding]="shouldOverrideContent">
|
||||
[class.content-area-override]="!shouldOverrideContent"
|
||||
[class.start-content-padding]="shouldOverrideContent">
|
||||
<global-message [isAppLevel]="false"></global-message>
|
||||
<!-- Only appear when searching -->
|
||||
<search-result></search-result>
|
||||
@ -42,7 +43,8 @@
|
||||
<clr-icon shape="users" clrVerticalNavIcon></clr-icon>
|
||||
{{'SIDE_NAV.SYSTEM_MGMT.USER' | translate}}
|
||||
</a>
|
||||
<a *ngIf='isLdapMode || isHttpAuthMode || isOidcMode' clrVerticalNavLink routerLink="/harbor/groups" routerLinkActive="active">
|
||||
<a *ngIf='isLdapMode || isHttpAuthMode || isOidcMode' clrVerticalNavLink
|
||||
routerLink="/harbor/groups" routerLinkActive="active">
|
||||
<clr-icon shape="users" clrVerticalNavIcon></clr-icon>
|
||||
{{'SIDE_NAV.SYSTEM_MGMT.GROUP' | translate}}
|
||||
</a>
|
||||
@ -54,7 +56,8 @@
|
||||
<clr-icon shape="cloud-traffic" clrVerticalNavIcon></clr-icon>
|
||||
{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}
|
||||
</a>
|
||||
<a *ngIf="!withAdmiral" clrVerticalNavLink routerLink="/harbor/labels" routerLinkActive="active">
|
||||
<a *ngIf="!withAdmiral" clrVerticalNavLink routerLink="/harbor/labels"
|
||||
routerLinkActive="active">
|
||||
<clr-icon shape="tag" clrVerticalNavIcon></clr-icon>
|
||||
{{'CONFIG.LABEL' | translate }}
|
||||
</a>
|
||||
@ -84,6 +87,7 @@
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</clr-vertical-nav>
|
||||
<hbr-operation-model *ngIf="isUserExisting"></hbr-operation-model>
|
||||
</div>
|
||||
|
@ -39,16 +39,16 @@ const YES: string = 'yes';
|
||||
|
||||
export class HarborShellComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild(AccountSettingsModalComponent, {static: false})
|
||||
@ViewChild(AccountSettingsModalComponent, { static: false })
|
||||
accountSettingsModal: AccountSettingsModalComponent;
|
||||
|
||||
@ViewChild(PasswordSettingComponent, {static: false})
|
||||
@ViewChild(PasswordSettingComponent, { static: false })
|
||||
pwdSetting: PasswordSettingComponent;
|
||||
|
||||
@ViewChild(NavigatorComponent, {static: false})
|
||||
@ViewChild(NavigatorComponent, { static: false })
|
||||
navigator: NavigatorComponent;
|
||||
|
||||
@ViewChild(AboutDialogComponent, {static: false})
|
||||
@ViewChild(AboutDialogComponent, { static: false })
|
||||
aboutDialog: AboutDialogComponent;
|
||||
|
||||
// To indicator whwther or not the search results page is displayed
|
||||
@ -62,14 +62,14 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
||||
isHttpAuthMode: boolean;
|
||||
showScannerInfo: boolean = false;
|
||||
scannerDocUrl: string = SCANNERS_DOC;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private session: SessionService,
|
||||
private searchTrigger: SearchTriggerService,
|
||||
private appConfigService: AppConfigService,
|
||||
private scannerService: ConfigScannerService) { }
|
||||
private scannerService: ConfigScannerService
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.appConfigService.isLdapMode()) {
|
||||
@ -89,7 +89,7 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
||||
this.isSearchResultsOpened = false;
|
||||
});
|
||||
if (!(localStorage && localStorage.getItem(HAS_SHOWED_SCANNER_INFO) === YES)) {
|
||||
this.getDefaultScanner();
|
||||
this.getDefaultScanner();
|
||||
}
|
||||
}
|
||||
closeInfo() {
|
||||
|
@ -44,5 +44,16 @@
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
<a href="javascript:void(0)" id="aboutMenu" class="nav-link nav-text nav-about-link" (click)="openAboutDialog()" *ngIf="!isSessionValid">{{'ACCOUNT_SETTINGS.ABOUT' | translate}}</a>
|
||||
<div class="nav-divider"></div>
|
||||
<ng-container *ngFor="let theme of themeArray;let i=index">
|
||||
<ng-container *ngIf="theme.showStyle === styleMode">
|
||||
<a class="nav-link nav-icon theme-select"
|
||||
(click)="themeChanged(theme)">
|
||||
<clr-icon size="20" *ngIf="styleMode ==='DARK'" shape="sun" class="is-solid"></clr-icon>
|
||||
<clr-icon size="20" *ngIf="styleMode ==='LIGHT'" shape="moon" class="is-solid"></clr-icon>
|
||||
{{ theme.text | translate }}
|
||||
</a>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</clr-header>
|
||||
|
@ -68,3 +68,27 @@
|
||||
.dropdown-lang {
|
||||
padding-right: 1.3rem;
|
||||
}
|
||||
.header {
|
||||
.header-actions {
|
||||
.theme-select {
|
||||
width: 5.4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
clr-icon {
|
||||
position: static;
|
||||
transform: none;
|
||||
margin-right: .2rem;
|
||||
min-width: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,10 @@ import { SearchTriggerService } from '../global-search/search-trigger.service';
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
import { SkinableConfig } from "../../skinable-config.service";
|
||||
import { CommonRoutes } from "../../../lib/entities/shared.const";
|
||||
import { ThemeInterface, themeArray } from '../../theme';
|
||||
import { clone } from '../../../lib/utils/utils';
|
||||
import { ThemeService } from '../../theme.service';
|
||||
const HAS_STYLE_MODE: string = 'styleModeLocal';
|
||||
|
||||
@Component({
|
||||
selector: 'navigator',
|
||||
@ -42,7 +46,9 @@ export class NavigatorComponent implements OnInit {
|
||||
appTitle: string = 'APP_TITLE.HARBOR';
|
||||
customStyle: { [key: string]: any };
|
||||
customProjectName: { [key: string]: any };
|
||||
themeArray: ThemeInterface[] = clone(themeArray);
|
||||
|
||||
styleMode = this.themeArray[0].showStyle;
|
||||
constructor(
|
||||
private session: SessionService,
|
||||
private router: Router,
|
||||
@ -52,6 +58,7 @@ export class NavigatorComponent implements OnInit {
|
||||
private appConfigService: AppConfigService,
|
||||
private msgHandler: MessageHandlerService,
|
||||
private searchTrigger: SearchTriggerService,
|
||||
public theme: ThemeService,
|
||||
private skinableConfig: SkinableConfig) {
|
||||
}
|
||||
|
||||
@ -79,6 +86,8 @@ export class NavigatorComponent implements OnInit {
|
||||
if (this.appConfigService.getConfig().read_only) {
|
||||
this.msgHandler.handleReadOnly();
|
||||
}
|
||||
// set local in app
|
||||
this.styleMode = localStorage.getItem(HAS_STYLE_MODE);
|
||||
}
|
||||
|
||||
public get isSessionValid(): boolean {
|
||||
@ -187,4 +196,10 @@ export class NavigatorComponent implements OnInit {
|
||||
registryAction(): void {
|
||||
this.searchTrigger.closeSearch(true);
|
||||
}
|
||||
|
||||
themeChanged(theme) {
|
||||
this.styleMode = theme.mode;
|
||||
this.theme.loadStyle(theme.toggleFileName);
|
||||
localStorage.setItem(HAS_STYLE_MODE, this.styleMode);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div [class.alert-app-level]="!isAppLevel">
|
||||
<clr-alert [clrAlertType]="globalMessage.type" [clrAlertAppLevel]="isAppLevel" [(clrAlertClosed)]="!globalMessageOpened" (clrAlertClosedChange)="onClose()">
|
||||
<div [class.alert-app-level]="!isAppLevel" [hidden]="!globalMessageOpened">
|
||||
<clr-alert [clrAlertType]="globalMessage.type" [clrAlertAppLevel]="isAppLevel" [(clrAlertClosed)]="!globalMessageOpened" (clrAlertClosedChange)="onClose()">
|
||||
<div class="alert-item">
|
||||
<span class="alert-text">{{message}}</span>
|
||||
<div class="alert-actions alert-style" *ngIf="needAuth">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<h2 class="custom-h2" sub-header-title>{{'SIDE_NAV.SYSTEM_MGMT.INTERROGATION_SERVICES' | translate}}</h2>
|
||||
<nav class="subnav sub-nav-bg-color mt-1">
|
||||
<nav class="mt-1">
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" routerLink="scanners" routerLinkActive="active">{{'SCANNER.SCANNERS' | translate }}</a>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div>
|
||||
<div class="version-position">
|
||||
<div class="row flex-items-xs-between">
|
||||
<div class="col-xs-4">
|
||||
<div class="title-container">
|
||||
@ -150,7 +150,7 @@
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div *ngIf="loading">
|
||||
<div *ngIf="loading" class="center-x">
|
||||
<span class="vertical-helper"></span>
|
||||
<span class="spinner"></span>
|
||||
</div>
|
||||
|
@ -103,3 +103,16 @@ hbr-resource-label-signpost {
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.center-x {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 6.5rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.version-position {
|
||||
position: relative;
|
||||
}
|
@ -63,7 +63,7 @@
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="isCardView" class="row card-container">
|
||||
<div *ngIf="isCardView" class="row card-container version-position">
|
||||
<div *ngFor="let item of charts;" class="chart-card">
|
||||
<a let i=index; class="card clickable" (click)="onChartClick(item)">
|
||||
<div class="card-header">
|
||||
@ -88,7 +88,7 @@
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div *ngIf="loading">
|
||||
<div *ngIf="loading" class="center-x">
|
||||
<span class="vertical-helper"></span>
|
||||
<span class="spinner"></span>
|
||||
</div>
|
||||
|
@ -103,7 +103,7 @@ clr-modal {
|
||||
|
||||
.file-browser-btn {
|
||||
margin-left: 15px;
|
||||
max-width: 32%;
|
||||
max-width: 34%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,3 +121,16 @@ button {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.center-x {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 2.5rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.version-position {
|
||||
position: relative;
|
||||
}
|
@ -21,7 +21,6 @@
|
||||
color: #007cbb;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
background: #fafafa;
|
||||
padding: 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<form #robotForm="ngForm">
|
||||
<section class="form-block">
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-3 permission">
|
||||
<div class="clr-col-3 permission permission-dark">
|
||||
<label class="col-md-3 required">
|
||||
{{'ROBOT_ACCOUNT.NAME' | translate}}
|
||||
</label>
|
||||
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-row mt-1">
|
||||
<div class="clr-col-3 permission">
|
||||
<div class="clr-col-3 permission permission-dark">
|
||||
<label class="col-md-3">{{'REPLICATION.DESCRIPTION' |translate}}</label>
|
||||
</div>
|
||||
<div class="clr-col padding-left-0">
|
||||
@ -48,7 +48,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-row mt-1">
|
||||
<div class="clr-col-3 permission">
|
||||
<div class="clr-col-3 permission permission-dark">
|
||||
<label class="col-md-3">
|
||||
{{'ROBOT_ACCOUNT.PERMISSIONS' | translate}}
|
||||
</label>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="summary display-flex" *ngIf="summaryInformation">
|
||||
<div class="summary summary-dark display-flex" *ngIf="summaryInformation">
|
||||
<div class="summary-left">
|
||||
<div class="display-flex project-detail pt-1">
|
||||
<h5 class="mt-0 width-7-5">{{'SUMMARY.PROJECT_REPOSITORY' | translate}}</h5>
|
||||
|
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<div class="clr-col">
|
||||
<span>
|
||||
<clr-icon *ngIf="!rule?.disabled" class="color-green" shape="success-standard"></clr-icon>
|
||||
<clr-icon *ngIf="!rule?.disabled" class="color-green color-white-dark" shape="success-standard"></clr-icon>
|
||||
<clr-icon id="{{'disable-icon'+i}}" *ngIf="rule?.disabled" class="color-red" shape="error-standard"></clr-icon>
|
||||
</span>
|
||||
<span class="rule-name ml-5">
|
||||
|
@ -15,9 +15,6 @@
|
||||
opacity: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
.label-left {
|
||||
color: #000;
|
||||
}
|
||||
.v-center {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
|
@ -30,7 +30,7 @@
|
||||
</div>
|
||||
<div class="clr-col">
|
||||
<span>
|
||||
<clr-icon *ngIf="!rule?.disabled" class="color-green" shape="success-standard"></clr-icon>
|
||||
<clr-icon *ngIf="!rule?.disabled" class="color-green color-white-dark" shape="success-standard"></clr-icon>
|
||||
<clr-icon *ngIf="rule?.disabled" class="color-red" shape="error-standard"></clr-icon>
|
||||
</span>
|
||||
<span class="rule-name ml-5">
|
||||
|
@ -48,9 +48,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.label-left {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.v-center {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
|
@ -2,7 +2,6 @@
|
||||
cursor: pointer;
|
||||
color: #007cbb;
|
||||
font-size: 16px;
|
||||
background: #fafafa;
|
||||
padding: 5px;
|
||||
border-radius: 2px;
|
||||
margin-right: 5px;
|
||||
|
@ -3,12 +3,10 @@
|
||||
cursor: pointer;
|
||||
color: #007cbb;
|
||||
font-size: 16px;
|
||||
background: #fafafa;
|
||||
padding: 5px;
|
||||
border-radius: 2px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
color: gray;
|
||||
}
|
||||
|
@ -4,13 +4,9 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.about-build {
|
||||
font-size: 14px;
|
||||
color: #565656;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
display:flex;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@ -4,14 +4,12 @@
|
||||
|
||||
.confirmation-title {
|
||||
line-height: 24px;
|
||||
color: #000000;
|
||||
font-size: 22px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.confirmation-content {
|
||||
font-size: 14px;
|
||||
color: #565656;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="esxc-gauge-circle-bg"></div>
|
||||
<div #barOne [style.transform]="'rotate(' + posOne + 'deg)'" [style.background-color]="_colorOne" [style.transition]="transition" class="esxc-gauge-circle-fill esxc-gauge-bar-one"></div>
|
||||
<div #barTwo [style.transform]="'rotate(' + posTwo + 'deg)'" [style.background-color]="_colorTwo" [style.transition]="transition" class="esxc-gauge-circle-fill esxc-gauge-bar-two"></div>
|
||||
<div class="esxc-gauge-circle-inner" [ngStyle]="{'background-color': backgroundColor}">
|
||||
<div class="esxc-gauge-circle-inner esxc-gauge-circle-bgc-dark" [ngStyle]="{'background-color': backgroundColor}">
|
||||
<div class="esxc-gauge-circle-caption">
|
||||
<span class="esxc-value">{{used}}</span>
|
||||
</div>
|
||||
|
12
src/portal/src/app/theme.service.spec.ts
Normal file
12
src/portal/src/app/theme.service.spec.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ThemeService } from './theme.service';
|
||||
|
||||
describe('ThemeService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: ThemeService = TestBed.get(ThemeService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
31
src/portal/src/app/theme.service.ts
Normal file
31
src/portal/src/app/theme.service.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ThemeService {
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: Document
|
||||
|
||||
) { }
|
||||
|
||||
loadStyle(styleName: string) {
|
||||
const head = this.document.getElementsByTagName('head')[0];
|
||||
|
||||
let themeLink = this.document.getElementById(
|
||||
'client-theme'
|
||||
) as HTMLLinkElement;
|
||||
if (themeLink) {
|
||||
themeLink.href = styleName;
|
||||
} else {
|
||||
const style = this.document.createElement('link');
|
||||
style.id = 'client-theme';
|
||||
style.rel = 'stylesheet';
|
||||
style.href = `${styleName}`;
|
||||
|
||||
head.appendChild(style);
|
||||
}
|
||||
}
|
||||
}
|
24
src/portal/src/app/theme.ts
Normal file
24
src/portal/src/app/theme.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export interface ThemeInterface {
|
||||
showStyle: string;
|
||||
mode: string;
|
||||
text: string;
|
||||
currentFileName: string;
|
||||
toggleFileName: string;
|
||||
}
|
||||
|
||||
export const themeArray: ThemeInterface[] = [
|
||||
{
|
||||
showStyle: "DARK",
|
||||
mode: "LIGHT",
|
||||
text: "APP_TITLE.THEME_LIGHT_TEXT",
|
||||
currentFileName: "dark-theme.css",
|
||||
toggleFileName: "light-theme.css",
|
||||
},
|
||||
{
|
||||
showStyle: "LIGHT",
|
||||
mode: "DARK", // show button icon
|
||||
text: "APP_TITLE.THEME_DARK_TEXT", // show button text
|
||||
currentFileName: "light-theme.css", // loaded current theme file name
|
||||
toggleFileName: "dark-theme.css", // to toggle theme file name
|
||||
}
|
||||
];
|
113
src/portal/src/css/dark-theme.scss
Normal file
113
src/portal/src/css/dark-theme.scss
Normal file
@ -0,0 +1,113 @@
|
||||
@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;
|
||||
|
||||
.label-form {
|
||||
background-color: #212129 !important;
|
||||
}
|
||||
.esxc-gauge-circle-bgc-dark {
|
||||
background-color: $dark-background-color;
|
||||
}
|
||||
|
||||
.harbor-icon {
|
||||
filter: drop-shadow(#ccc 58px 2px) !important;
|
||||
}
|
||||
|
||||
.color-white-dark {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.label-filter-panel {
|
||||
background-color: $dark-background-color;
|
||||
}
|
||||
|
||||
.filter-dark {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.side-form {
|
||||
background-color: $dark-background-color;
|
||||
}
|
||||
.toolBar {
|
||||
background-color: $dark-background-color;
|
||||
}
|
||||
|
||||
.search-overlay {
|
||||
background-color: $dark-background-color;
|
||||
}
|
||||
|
||||
.add-modal-dark {
|
||||
background: $dark-background-color;
|
||||
}
|
||||
.license {
|
||||
background-color: $dark-background-color;
|
||||
}
|
||||
|
||||
.pagination-list {
|
||||
.pagination-current {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
}
|
||||
.no-info-div,
|
||||
.info-div {
|
||||
background-color: $dark-background-color;
|
||||
}
|
||||
.bottom-line-project-config {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.config-subtext {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.permission-dark {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.scanner-name {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.permission-dark {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.summary-dark {
|
||||
color: $dark-font-color1;
|
||||
h5 {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
}
|
||||
.about-version {
|
||||
color: $dark-font-color-title1;
|
||||
}
|
||||
.update-time {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.font-style {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.go-link {
|
||||
color: #4aaed9 !important;
|
||||
border-bottom-color: #4aaed9 !important;
|
||||
}
|
||||
.circle-block {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.executions-detail {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.black-point {
|
||||
background-color: $dark-font-color1;
|
||||
}
|
||||
clr-dg-action-overflow {
|
||||
clr-icon {
|
||||
fill: $dark-font-color1;
|
||||
}
|
||||
}
|
||||
.oidc-tip {
|
||||
color: $dark-font-color1;
|
||||
}
|
||||
.clr-select-wrapper{
|
||||
select {
|
||||
option {
|
||||
background: $dark-font-color1;
|
||||
color: #1b2a32;
|
||||
}
|
||||
}
|
||||
}
|
1
src/portal/src/css/light-theme.scss
Normal file
1
src/portal/src/css/light-theme.scss
Normal file
@ -0,0 +1 @@
|
||||
@import "../../node_modules/@clr/ui/clr-ui.min.css";
|
@ -325,3 +325,6 @@ clr-datagrid {
|
||||
}
|
||||
}
|
||||
}
|
||||
button:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
"VIC": "vSphere Integrated Containers",
|
||||
"MGMT": "Management",
|
||||
"REG": "Registry",
|
||||
"HARBOR_SWAGGER": "Harbor Swagger"
|
||||
"HARBOR_SWAGGER": "Harbor Swagger",
|
||||
"THEME_DARK_TEXT": "DARK",
|
||||
"THEME_LIGHT_TEXT": "LIGHT"
|
||||
},
|
||||
"SIGN_IN": {
|
||||
"REMEMBER": "Remember me",
|
||||
|
@ -5,7 +5,9 @@
|
||||
"VIC": "Contenedores Integrados vSphere",
|
||||
"MGMT": "Administración",
|
||||
"REG": "Registro",
|
||||
"HARBOR_SWAGGER": "Harbor Swagger"
|
||||
"HARBOR_SWAGGER": "Harbor Swagger",
|
||||
"THEME_DARK_TEXT": "DARK",
|
||||
"THEME_LIGHT_TEXT": "LIGHT"
|
||||
},
|
||||
"SIGN_IN": {
|
||||
"REMEMBER": "Recordarme",
|
||||
|
@ -5,7 +5,9 @@
|
||||
"VIC": "vSphere Integrated Containers",
|
||||
"MGMT": "Management",
|
||||
"REG": "Registre",
|
||||
"HARBOR_SWAGGER": "Harbor Swagger"
|
||||
"HARBOR_SWAGGER": "Harbor Swagger",
|
||||
"THEME_DARK_TEXT": "DARK",
|
||||
"THEME_LIGHT_TEXT": "LIGHT"
|
||||
},
|
||||
"SIGN_IN": {
|
||||
"REMEMBER": "Se souvenir de moi",
|
||||
|
@ -5,7 +5,9 @@
|
||||
"VIC": "vSphere Integrated Containers",
|
||||
"MGMT": "Gerência",
|
||||
"REG": "Registro",
|
||||
"HARBOR_SWAGGER": "Harbor Swagger"
|
||||
"HARBOR_SWAGGER": "Harbor Swagger",
|
||||
"THEME_DARK_TEXT": "DARK",
|
||||
"THEME_LIGHT_TEXT": "LIGHT"
|
||||
},
|
||||
"SIGN_IN": {
|
||||
"REMEMBER": "Lembrar-se de mim",
|
||||
|
@ -5,7 +5,9 @@
|
||||
"VIC": "vSphere Entegre Konteynerler",
|
||||
"MGMT": "Yönetim",
|
||||
"REG": "Kayıt",
|
||||
"HARBOR_SWAGGER": "Harbor Swagger"
|
||||
"HARBOR_SWAGGER": "Harbor Swagger",
|
||||
"THEME_DARK_TEXT": "DARK",
|
||||
"THEME_LIGHT_TEXT": "LIGHT"
|
||||
},
|
||||
"SIGN_IN": {
|
||||
"REMEMBER": "Beni Hatırla",
|
||||
|
@ -5,7 +5,9 @@
|
||||
"VIC": "vSphere Integrated Containers",
|
||||
"MGMT": "Management",
|
||||
"REG": "Registry",
|
||||
"HARBOR_SWAGGER": "Harbor Swagger"
|
||||
"HARBOR_SWAGGER": "Harbor Swagger",
|
||||
"THEME_DARK_TEXT": "深色主题",
|
||||
"THEME_LIGHT_TEXT": "浅色主题"
|
||||
},
|
||||
"SIGN_IN": {
|
||||
"REMEMBER": "记住我",
|
||||
|
@ -7,11 +7,12 @@
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico?v=2">
|
||||
<link rel="preload" as="style" href="./light-theme.css">
|
||||
<link rel="preload" as="style" href="./dark-theme.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<harbor-app>
|
||||
<div class="spinner spinner-lg app-loading">
|
||||
<div class="spinner spinner-lg app-loading app-loading-fixed">
|
||||
Loading...
|
||||
</div>
|
||||
</harbor-app>
|
||||
|
@ -1,5 +1,4 @@
|
||||
.history-header {
|
||||
color: #000;
|
||||
margin:20px 0 6px 0;
|
||||
display: inline-block;
|
||||
width: 97%;
|
||||
|
@ -8,7 +8,7 @@
|
||||
[class.clr-form-compact-common]="!defaultTextsObj.isSystemDefaultQuota">
|
||||
|
||||
<clr-input-container>
|
||||
<label class="left-label required" for="storage">{{ defaultTextsObj?.countQuota | translate}}
|
||||
<label class="left-label left-label-light required" for="storage">{{ defaultTextsObj?.countQuota | translate}}
|
||||
<clr-tooltip>
|
||||
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
||||
<clr-tooltip-content clrPosition="top-right" clrSize="lg" *clrIfOpen>
|
||||
|
@ -8,7 +8,6 @@
|
||||
overflow-y: visible;
|
||||
overflow-x: visible;
|
||||
.body-label {
|
||||
color: #565656;
|
||||
font-size: 14px
|
||||
}
|
||||
.clr-form {
|
||||
@ -16,7 +15,6 @@
|
||||
.left-label {
|
||||
width: 9.5rem;
|
||||
font-weight: 100;
|
||||
color: #000;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.color-0 {
|
||||
color: #333;
|
||||
}
|
||||
.pt-19px {
|
||||
padding-top: 19px;
|
||||
}
|
||||
|
@ -49,6 +49,9 @@
|
||||
width: 200px;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.font-style {
|
||||
color: #000;
|
||||
}
|
||||
.margin-left-5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
@ -4,14 +4,12 @@
|
||||
|
||||
.confirmation-title {
|
||||
line-height: 24px;
|
||||
color: #000000;
|
||||
font-size: 22px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.confirmation-content {
|
||||
font-size: 14px;
|
||||
color: #565656;
|
||||
line-height: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<form #labelForm="ngForm" [hidden]="!formShow">
|
||||
<form #labelForm="ngForm" [hidden]="!formShow" class="label-form">
|
||||
<section>
|
||||
<label>
|
||||
<label for="name">{{'LABEL.LABEL_NAME' | translate}}</label>
|
||||
|
@ -1,4 +1,4 @@
|
||||
form {
|
||||
.label-form {
|
||||
margin-bottom: -10px;
|
||||
padding-top: 0;
|
||||
margin-top: 20px;
|
||||
|
@ -100,7 +100,7 @@
|
||||
(click)="addSystem()"
|
||||
class="btn btn-link ml-1">{{'CVE_WHITELIST.ADD_SYSTEM'|translate}}</button>
|
||||
</div>
|
||||
<div class="add-modal" *ngIf="showAddModal && !isUseSystemWhitelist()">
|
||||
<div class="add-modal add-modal-dark" *ngIf="showAddModal && !isUseSystemWhitelist()">
|
||||
<clr-icon (click)="showAddModal=false" class="float-lg-right margin-top-4"
|
||||
shape="window-close"></clr-icon>
|
||||
<div>
|
||||
@ -137,7 +137,7 @@
|
||||
<div class="clr-col padding-top-16 pl-2">
|
||||
<div class="clr-row expire-data">
|
||||
<label for="expires"
|
||||
class="bottom-line clr-col-3">{{'CVE_WHITELIST.EXPIRES_AT'|translate}}</label>
|
||||
class="bottom-line bottom-line-project-config clr-col-3">{{'CVE_WHITELIST.EXPIRES_AT'|translate}}</label>
|
||||
<div class="underline">
|
||||
<input #dateSystemInput readonly type="date" [(clrDate)]="systemExpiresDate">
|
||||
<input [disabled]="!hasChangeConfigRole" *ngIf="!isUseSystemWhitelist()" #dateInput
|
||||
|
@ -73,7 +73,6 @@
|
||||
.config-subtext {
|
||||
font-size: 0.55rem;
|
||||
line-height: 1.2rem;
|
||||
color: rgb(86, 86, 86);
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
||||
<div class="vulnerability" [hidden]="hasCve || showStatBar">
|
||||
<hbr-vulnerability-bar [repoName]="repositoryId" [tagId]="tagDetails.name" [summary]="vulnerabilitySummary"></hbr-vulnerability-bar>
|
||||
</div>
|
||||
<histogram-chart *ngIf="hasCve" [metadata]="passMetadataToChart()" [isWhiteBackground]="true"></histogram-chart>
|
||||
<histogram-chart *ngIf="hasCve" [metadata]="passMetadataToChart()" [isWhiteBackground]="isThemeLight()"></histogram-chart>
|
||||
</div>
|
||||
<div *ngIf="!withAdmiral && tagDetails?.labels?.length">
|
||||
<div class="third-column detail-title">{{'TAG.LABELS' | translate }}</div>
|
||||
|
@ -256,4 +256,7 @@ export class TagDetailComponent implements OnInit {
|
||||
},
|
||||
];
|
||||
}
|
||||
isThemeLight() {
|
||||
return localStorage.getItem('styleModeLocal') === 'LIGHT';
|
||||
}
|
||||
}
|
||||
|
@ -31,9 +31,9 @@
|
||||
</div>
|
||||
<div class="flex-xs-middle">
|
||||
<hbr-filter [readonly]="'readonly'" [withDivider]="true" filterPlaceholder="{{'TAG.FILTER_FOR_TAGS' | translate}}" (filterEvt)="doSearchTagNames($event)" (openFlag)="openFlagEvent($event)" [currentValue]="lastFilteredTagName"></hbr-filter>
|
||||
<div class="labelFilterPanel" *ngIf="!withAdmiral" [hidden]="!openLabelFilterPanel">
|
||||
<div class="label-filter-panel" *ngIf="!withAdmiral" [hidden]="!openLabelFilterPanel">
|
||||
<a class="filterClose" (click)="closeFilter()">×</a>
|
||||
<label class="filterLabelHeader">{{'REPOSITORY.FILTER_BY_LABEL' | translate}}</label>
|
||||
<label class="filterLabelHeader filter-dark">{{'REPOSITORY.FILTER_BY_LABEL' | translate}}</label>
|
||||
<div class="form-group"><input clrInput type="text" placeholder="Filter labels" [(ngModel)]="filterName" (keyup)="handleInputFilter()"></div>
|
||||
<div [hidden]='imageFilterLabels.length' class="no-labels">{{'LABEL.NO_LABELS' | translate }}</div>
|
||||
<div [hidden]='!imageFilterLabels.length' class="has-label">
|
||||
|
@ -134,7 +134,7 @@
|
||||
@include dropdown-as-action-button;
|
||||
}
|
||||
|
||||
.labelFilterPanel {
|
||||
.label-filter-panel {
|
||||
display: flex;
|
||||
position: relative;
|
||||
-ms-flex-direction: column;
|
||||
@ -198,7 +198,6 @@
|
||||
padding: 0 .5rem;
|
||||
line-height: .75rem;
|
||||
margin: 0;
|
||||
color: #313131;
|
||||
}
|
||||
|
||||
.filterClose {
|
||||
|
@ -61,7 +61,7 @@
|
||||
</div>
|
||||
<hr>
|
||||
<div class="bar-summary bar-tooltip-fon" *ngIf="!isNone">
|
||||
<histogram-chart [isWhiteBackground]="false" [metadata]="passMetadataToChart()"></histogram-chart>
|
||||
<histogram-chart [isWhiteBackground]="!isThemeLight()" [metadata]="passMetadataToChart()"></histogram-chart>
|
||||
</div>
|
||||
<div *ngIf="scanner">
|
||||
<span class="bar-scanning-time">{{'SCANNER.SCANNED_BY' | translate }}</span>
|
||||
|
@ -232,4 +232,7 @@ export class ResultTipHistogramComponent implements OnInit {
|
||||
},
|
||||
];
|
||||
}
|
||||
isThemeLight() {
|
||||
return localStorage.getItem('styleModeLocal') === 'LIGHT';
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user