Merge pull request #2591 from coreos-inc/cor-tabs

Change cor-tabs to be a TypeScript and Angular "neu" component
This commit is contained in:
josephschorr 2017-05-05 15:13:44 -04:00 committed by GitHub
commit f228ebdc22
24 changed files with 832 additions and 588 deletions

View file

@ -162,161 +162,6 @@ a:focus {
} }
} }
.co-tab-container {
padding: 0px;
}
.co-tabs {
margin: 0px;
padding: 0px;
width: 82px;
display: table-cell;
vertical-align: top;
}
.co-tab-element {
display: table-cell;
float: none;
vertical-align: top;
background-color: #e8f1f6;
border-right: 1px solid #DDE7ED;
}
.co-tab-content {
width: 100%;
display: table-cell;
float: none;
padding: 30px;
}
@media (max-width: 767px) {
.co-tab-content {
padding: 20px;
width: 100%;
display: block;
}
}
.co-tabs li {
list-style: none;
display: block;
border-bottom: 1px solid #DDE7ED;
}
.co-tabs li.active {
background-color: white;
border-right: 1px solid white;
margin-right: -1px;
}
.co-tabs li a {
display: block;
width: 82px;
height: 82px;
line-height: 82px;
text-align: center;
font-size: 36px;
}
.co-tabs li a i {
font-size: 36px;
color: gray;
}
.co-tabs li.active a {
color: black;
}
.co-tabs .xs-toggle {
display: none;
}
@media (max-width: 767px) {
.co-tabs {
display: block;
width: auto;
border-right: none;
border-bottom: 1px solid #DDE7ED;
}
.co-tab-element {
position: relative;
display: block;
}
.co-tab-element .xs-toggle {
display: inline-block;
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
width: 40px;
height: 40px;
cursor: pointer;
font-size: 18px;
text-align: center;
line-height: 40px;
border: 2px solid #DDE7ED;
background: white;
}
.co-tab-element.closed .xs-toggle:before {
content: "\f0d7";
font-family: FontAwesome;
}
.co-tab-element.open .xs-toggle:before {
content: "\f0d8";
font-family: FontAwesome;
}
.co-tab-element .xs-label {
line-height: 60px;
font-size: 16px;
margin-left: 16px;
display: inline-block !important;
color: gray;
}
.co-tabs li a {
display: inline-block;
height: 60px;
line-height: 60px;
white-space: nowrap;
width: 100%;
text-align: left;
padding-left: 20px;
text-decoration: none !important;
font-size: 28px;
}
.co-tabs li a i {
vertical-align: middle;
font-size: 28px;
}
.co-tabs li.active a .xs-label {
color: black;
}
.co-tabs li.active a i {
color: black;
}
.co-tab-element.open li {
height: 60px;
}
.co-tab-element.closed li {
height: 0px;
overflow: hidden;
}
.co-tab-element.closed li.active {
height: 60px;
}
}
.co-main-content-panel { .co-main-content-panel {
margin-bottom: 20px; margin-bottom: 20px;
background-color: #fff; background-color: #fff;
@ -330,11 +175,6 @@ a:focus {
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4); box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4);
} }
.co-tab-panel {
padding: 0px;
}
.cor-log-box { .cor-log-box {
width: 100%; width: 100%;
height: 550px; height: 550px;
@ -1647,98 +1487,6 @@ a:focus {
color: #aaa; color: #aaa;
} }
.co-dialog .co-tabs {
width: auto;
min-height: 400px;
padding-top: 58px;
}
.co-dialog .co-tabs li a {
width: auto;
height: 42px;
line-height: 42px;
text-align: left;
font-size: 120%;
white-space: nowrap;
padding: 0px;
padding-left: 16px;
padding-right: 30px;
color: #5A5A5A;
}
.co-dialog .co-tabs li.active a, .co-dialog .co-tabs li.active a .fa {
color: black;
filter: none !important;
-webkit-filter: none !important;
}
.co-dialog .co-tabs li a .fa {
vertical-align: middle;
margin-right: 10px;
font-size: 20px;
line-height: 20px;
width: 20px;
text-align: center;
margin-top: -2px;
}
.co-dialog .co-tabs li a .fa.icon {
width: 20px;
height: 20px;
background-size: 20px;
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
}
.co-dialog .co-tab-content {
padding: 16px;
padding-bottom: 30px;
}
.co-dialog .co-tab-content h3 {
margin-top: 0px;
margin-bottom: 0px;
}
.co-dialog .co-tab-content label {
margin-top: 24px;
font-weight: bold;
font-size: 16px;
}
.co-dialog .co-tab-content .help-text {
margin-top: 7px;
margin-left: 6px;
color: #aaa;
}
.co-dialog .co-tab-content .co-list-table {
width: 100%;
}
.co-dialog .co-tab-content .co-list-table td:first-child {
white-space: nowrap;
vertical-align: middle;
font-weight: normal;
}
.co-dialog .co-tabs li:first-child {
border-top: 1px solid #DDE7ED;
}
@media screen and (max-width: 767px) {
.co-dialog .co-tabs {
min-height: 0px;
padding-top: 0px;
}
.co-dialog .co-tabs li a {
line-height: 60px;
height: auto;
}
}
.co-modal-body-scrollable { .co-modal-body-scrollable {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;

View file

@ -0,0 +1,254 @@
cor-tabs {
display: table-cell;
float: none;
vertical-align: top;
background-color: #e8f1f6;
border-right: 1px solid #DDE7ED;
}
.co-tab-container {
padding: 0px;
}
.co-tabs {
margin: 0px;
padding: 0px;
width: 82px;
display: table-cell;
vertical-align: top;
}
.co-tab-content {
width: 100%;
display: table-cell;
float: none;
padding: 30px;
}
@media (max-width: 767px) {
.co-tab-content {
padding: 20px;
width: 100%;
display: block;
}
}
.co-tabs li {
list-style: none;
display: block;
border-bottom: 1px solid #DDE7ED;
}
.co-tabs li.active {
background-color: white;
border-right: 1px solid white;
margin-right: -1px;
}
.co-tabs li a {
display: block;
width: 82px;
height: 82px;
line-height: 82px;
text-align: center;
font-size: 36px;
}
.co-tabs li a i {
font-size: 36px;
color: gray;
}
.co-tabs li.active a {
color: black;
}
.co-tabs .xs-toggle {
display: none;
}
@media (max-width: 767px) {
.co-tabs {
display: block;
width: auto;
border-right: none;
border-bottom: 1px solid #DDE7ED;
}
cor-tabs {
position: relative;
display: block;
}
.co-tab-element .xs-toggle {
display: inline-block;
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
width: 40px;
height: 40px;
cursor: pointer;
font-size: 18px;
text-align: center;
line-height: 40px;
border: 2px solid #DDE7ED;
background: white;
}
.co-tab-element.closed .xs-toggle:before {
content: "\f0d7";
font-family: FontAwesome;
}
.co-tab-element.open .xs-toggle:before {
content: "\f0d8";
font-family: FontAwesome;
}
.co-tab-element .xs-label {
line-height: 60px;
font-size: 16px;
margin-left: 16px;
display: inline-block !important;
color: gray;
}
.co-tabs li a {
display: inline-block;
height: 60px;
line-height: 60px;
white-space: nowrap;
width: 100%;
text-align: left;
padding-left: 20px;
text-decoration: none !important;
font-size: 28px;
}
.co-tabs li a i {
vertical-align: middle;
font-size: 28px;
}
.co-tabs li.active a .xs-label {
color: black;
}
.co-tabs li.active a i {
color: black;
}
.co-tab-element.open li {
height: 60px;
}
.co-tab-element.closed li {
height: 0px;
overflow: hidden;
}
.co-tab-element.closed li.active {
height: 60px;
}
}
.co-tab-panel {
padding: 0px;
}
.co-dialog .co-tabs {
width: auto;
min-height: 400px;
padding-top: 58px;
}
.co-dialog .co-main-content-panel {
margin-bottom: 0px;
}
.co-dialog .co-tabs li a {
width: auto;
height: 42px;
line-height: 42px;
text-align: left;
font-size: 120%;
white-space: nowrap;
padding: 0px;
padding-left: 16px;
padding-right: 30px;
color: #5A5A5A;
}
.co-dialog .co-tabs li.active a, .co-dialog .co-tabs li.active a .fa {
color: black;
filter: none !important;
-webkit-filter: none !important;
}
.co-dialog .co-tabs li a .fa {
vertical-align: middle;
margin-right: 10px;
font-size: 20px;
line-height: 20px;
width: 20px;
text-align: center;
margin-top: -2px;
}
.co-dialog .co-tabs li a .fa.icon {
width: 20px;
height: 20px;
background-size: 20px;
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
}
.co-dialog .co-tab-content {
padding: 16px;
padding-bottom: 30px;
}
.co-dialog .co-tab-content h3 {
margin-top: 0px;
margin-bottom: 0px;
}
.co-dialog .co-tab-content label {
margin-top: 24px;
font-weight: bold;
font-size: 16px;
}
.co-dialog .co-tab-content .help-text {
margin-top: 7px;
margin-left: 6px;
color: #aaa;
}
.co-dialog .co-tab-content .co-list-table {
width: 100%;
}
.co-dialog .co-tab-content .co-list-table td:first-child {
white-space: nowrap;
vertical-align: middle;
font-weight: normal;
}
.co-dialog .co-tabs li:first-child {
border-top: 1px solid #DDE7ED;
}
@media screen and (max-width: 767px) {
.co-dialog .co-tabs {
min-height: 0px;
padding-top: 0px;
}
.co-dialog .co-tabs li a {
line-height: 60px;
height: auto;
}
}

View file

@ -1,12 +0,0 @@
<li ng-class="tabActive == 'true' ? 'active' : ''">
<a data-toggle="tab"
data-target="{{ tabTarget }}"
ng-click="tabInit()">
<span data-title="{{ tabTitle }}"
data-placement="right"
data-container="body"
style="display:inline-block"
bs-tooltip><span ng-transclude/></span><span class="visible-xs-inline xs-label">{{ tabTitle }}</span>
</span>
</a>
</li>

View file

@ -1,4 +0,0 @@
<span class="co-tab-element" ng-class="isClosed ? 'closed' : 'open'">
<span class="xs-toggle" ng-click="toggleClosed($event)"></span>
<ul class="co-tabs col-md-1" ng-transclude></ul>
</span>

View file

@ -6,42 +6,42 @@
<div class="cor-loader"></div> <div class="cor-loader"></div>
</div> </div>
<div class="co-tab-modal-body" ng-show="!credentials.loading"> <div class="co-tab-modal-body" ng-show="!credentials.loading">
<div class="co-tab-panel"> <cor-tab-panel remember-cookie="quay.credentialsTab">
<!-- Tabs --> <!-- Tabs -->
<div class="cor-tabs" remember-cookie="quay.credentialsTab"> <cor-tabs>
<span class="cor-tab" tab-active="true" tab-target="#cred-secret-{{ dialogID }}"> <cor-tab tab-active="true" tab-id="cred-secret-{{ :dialogID }}">
<i class="fa" ng-class="entityIcon"></i> {{ secretTitle }} <i class="fa" ng-class="entityIcon"></i> {{ secretTitle }}
</span> </cor-tab>
<span class="cor-tab" tab-target="#cred-kubernetes-{{ dialogID }}"> <cor-tab tab-id="cred-kubernetes-{{ :dialogID }}">
<i class="fa kubernetes-icon icon"></i> Kubernetes Secret <i class="fa kubernetes-icon icon"></i> Kubernetes Secret
</span> </cor-tab>
<span class="cor-tab" tab-target="#cred-rkt-{{ dialogID }}"> <cor-tab tab-id="cred-rkt-{{ :dialogID }}">
<i class="fa rocket-icon icon"></i> rkt Configuration <i class="fa rocket-icon icon"></i> rkt Configuration
</span> </cor-tab>
<span class="cor-tab" tab-target="#cred-docker-login-{{ dialogID }}"> <cor-tab tab-id="cred-docker-login-{{ :dialogID }}">
<i class="fa docker-icon icon"></i> Docker Login <i class="fa docker-icon icon"></i> Docker Login
</span> </cor-tab>
<span class="cor-tab" tab-target="#cred-docker-{{ dialogID }}"> <cor-tab tab-id="cred-docker-{{ :dialogID }}">
<i class="fa docker-icon icon"></i> Docker Configuration <i class="fa docker-icon icon"></i> Docker Configuration
</span> </cor-tab>
<span class="cor-tab" tab-target="#cred-mesos-{{ dialogID }}" quay-show="isDownloadSupported()"> <cor-tab tab-id="cred-mesos-{{ :dialogID }}" quay-show="isDownloadSupported()">
<i class="fa mesos-icon icon"></i> Mesos Credentials <i class="fa mesos-icon icon"></i> Mesos Credentials
</span> </cor-tab>
</div> </cor-tabs>
<!-- Tab contents --> <!-- Tab contents -->
<div class="cor-tab-content"> <cor-tab-content>
<h3> <h3>
Credentials for {{ credentials.username }} Credentials for {{ credentials.username }}
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
</h3> </h3>
<div id="cred-secret-{{ dialogID }}" class="tab-pane active"> <cor-tab-pane id="cred-secret-{{ :dialogID }}">
<label>{{ secretTitle }}:</label> <label>{{ secretTitle }}:</label>
<div class="copy-box" value="credentials.password"></div> <div class="copy-box" value="credentials.password"></div>
<div class="help-text"> <div class="help-text">
@ -49,9 +49,9 @@
</div> </div>
<div ng-transclude/> <div ng-transclude/>
</div> </cor-tab-pane>
<div id="cred-kubernetes-{{ dialogID }}" class="tab-pane"> <cor-tab-pane id="cred-kubernetes-{{ :dialogID }}">
<label>Step 1: Download secret</label> <label>Step 1: Download secret</label>
<div class="action-text">First, download the Kubernetes pull secret for the {{ entityTitle }}:</div> <div class="action-text">First, download the Kubernetes pull secret for the {{ entityTitle }}:</div>
<ul class="action-bar"> <ul class="action-bar">
@ -83,9 +83,9 @@ spec:
imagePullSecrets: imagePullSecrets:
- name: {{ getKubernetesSecretName(credentials) }}</code></pre> - name: {{ getKubernetesSecretName(credentials) }}</code></pre>
</div> </div>
</div> </cor-tab-pane>
<div id="cred-mesos-{{ dialogID }}" class="tab-pane"> <cor-tab-pane id="cred-mesos-{{ :dialogID }}">
<label>Step 1: Download credentials bundle</label> <label>Step 1: Download credentials bundle</label>
<div class="action-text">First, download the Docker credentials file as a bundle:</div> <div class="action-text">First, download the Docker credentials file as a bundle:</div>
<ul class="action-bar"> <ul class="action-bar">
@ -122,9 +122,9 @@ spec:
]</code> ]</code>
}</pre> }</pre>
</div> </div>
</div> </cor-tab-pane>
<div id="cred-rkt-{{ dialogID }}" class="tab-pane"> <cor-tab-pane id="cred-rkt-{{ :dialogID }}">
<label>Step 1: Download credentials config</label> <label>Step 1: Download credentials config</label>
<div class="action-text">First, download the rkt credentials file for the {{ entityTitle }}:</div> <div class="action-text">First, download the rkt credentials file for the {{ entityTitle }}:</div>
<ul class="action-bar"> <ul class="action-bar">
@ -137,15 +137,15 @@ spec:
<label>Step 2: Write to disk</label> <label>Step 2: Write to disk</label>
<div class="action-text">Second, place the file in the rkt configuration directory:</div> <div class="action-text">Second, place the file in the rkt configuration directory:</div>
<div class="copy-box" value="'mv ' + getRktFilename(credentials) + ' /etc/rkt/auth.d/'"></div> <div class="copy-box" value="'mv ' + getRktFilename(credentials) + ' /etc/rkt/auth.d/'"></div>
</div> </cor-tab-pane>
<div id="cred-docker-login-{{ dialogID }}" class="tab-pane"> <cor-tab-pane id="cred-docker-login-{{ :dialogID }}">
<label>Run docker login</label> <label>Run docker login</label>
<div class="action-text">Enter the following command on the command line:</div> <div class="action-text">Enter the following command on the command line:</div>
<div class="copy-box" value="getDockerLogin(credentials)"></div> <div class="copy-box" value="getDockerLogin(credentials)"></div>
</div> </cor-tab-pane>
<div id="cred-docker-{{ dialogID }}" class="tab-pane"> <cor-tab-pane id="cred-docker-{{ :dialogID }}">
<label>Step 1: Download credentials config</label> <label>Step 1: Download credentials config</label>
<div class="action-text">First, download the Docker credentials file for the {{ entityTitle }}:</div> <div class="action-text">First, download the Docker credentials file for the {{ entityTitle }}:</div>
<ul class="action-bar"> <ul class="action-bar">
@ -158,9 +158,9 @@ spec:
<label>Step 2: Write to disk</label> <label>Step 2: Write to disk</label>
<div class="action-text">Second, place the file in the Docker configuration directory. <strong>Note:</strong> This will <strong>overwrite</strong> existing credentials:</div> <div class="action-text">Second, place the file in the Docker configuration directory. <strong>Note:</strong> This will <strong>overwrite</strong> existing credentials:</div>
<div class="copy-box" value="'mv ' + getDockerFilename(credentials) + ' ~/.docker/config.json'"></div> <div class="copy-box" value="'mv ' + getDockerFilename(credentials) + ' ~/.docker/config.json'"></div>
</div> </cor-tab-pane>
</div> </cor-tab-content>
</div> </cor-tab-panel>
</div><!-- /.co-tab-modal-body --> </div><!-- /.co-tab-modal-body -->
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->

View file

@ -214,61 +214,6 @@ angular.module("core-ui", [])
return directiveDefinitionObject; return directiveDefinitionObject;
}) })
.directive('corTabPanel', function() {
var directiveDefinitionObject = {
priority: 1,
templateUrl: '/static/directives/cor-tab-panel.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
})
.directive('corTabContent', function() {
var directiveDefinitionObject = {
priority: 2,
replace: true,
transclude: false,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
$element.addClass('co-tab-content tab-content col-md-11');
}
};
return directiveDefinitionObject;
})
.directive('corTabs', function() {
var directiveDefinitionObject = {
priority: 3,
templateUrl: '/static/directives/cor-tabs.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {
'rememberCookie': '@rememberCookie'
},
controller: function($rootScope, $scope, $element, $timeout, $location, UIService) {
$scope.isClosed = true;
$scope.toggleClosed = function(e) {
$scope.isClosed = !$scope.isClosed;
e.stopPropagation();
e.preventDefault();
};
UIService.initializeTabs($scope, $element, function() {
$scope.isClosed = true;
}, $scope.rememberCookie);
}
};
return directiveDefinitionObject;
})
.directive('corFloatingBottomBar', function() { .directive('corFloatingBottomBar', function() {
var directiveDefinitionObject = { var directiveDefinitionObject = {
priority: 3, priority: 3,
@ -338,34 +283,6 @@ angular.module("core-ui", [])
return directiveDefinitionObject; return directiveDefinitionObject;
}) })
.directive('corTab', function() {
var directiveDefinitionObject = {
priority: 4,
templateUrl: '/static/directives/cor-tab.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {
'tabActive': '@tabActive',
'tabTitle': '@tabTitle',
'tabTarget': '@tabTarget',
'tabInit': '&tabInit',
'tabShown': '&tabShown',
'tabHidden': '&tabHidden'
},
controller: function($rootScope, $scope, $element) {
$element.find('a[data-toggle="tab"]').on('hidden.bs.tab', function (e) {
$scope.tabHidden({});
});
$element.find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
$scope.tabShown({});
});
}
};
return directiveDefinitionObject;
})
.directive('corStep', function() { .directive('corStep', function() {
var directiveDefinitionObject = { var directiveDefinitionObject = {
priority: 4, priority: 4,

View file

@ -0,0 +1 @@
<div class="co-tab-content tab-content col-md-11" ng-transclude></div>

View file

@ -0,0 +1,15 @@
import { Component } from 'ng-metadata/core';
/**
* A component that is placed under a cor-tabs to wrap tab content with additional styling.
*/
@Component({
selector: 'cor-tab-content',
templateUrl: '/static/js/directives/ui/cor-tabs/cor-tab-content.component.html',
legacy: {
transclude: true,
replace: true,
}
})
export class CorTabContentComponent {}

View file

@ -0,0 +1,83 @@
import { CorTabComponent } from './cor-tab.component';
import { CorTabPanelComponent } from './cor-tab-panel.component';
/**
* Defines an interface for reading and writing the current tab state.
*/
export interface CorTabCurrentHandler {
getInitialTabId(): string
notifyTabChanged(tab: CorTabComponent, isDefaultTab: boolean)
dispose(): void
}
export function CorTabCurrentHandlerFactory(options?: any): CorTabCurrentHandler {
switch (options.type) {
case "cookie":
return new CookieCurrentTabHandler(options.cookieService, options.cookieName);
default:
return new LocationCurrentTabHandler(options.panel, options.$location, options.$rootScope);
}
}
/**
* Reads and writes the tab from the `tab` query parameter in the location.
*/
export class LocationCurrentTabHandler implements CorTabCurrentHandler {
private cancelWatchHandle: Function;
constructor (private panel: CorTabPanelComponent,
private $location: ng.ILocationService,
private $rootScope: ng.IRootScopeService) {
}
private checkLocation(): void {
var specifiedTabId = this.$location.search()['tab'];
var specifiedTab = this.panel.findTab(specifiedTabId);
this.panel.setActiveTab(specifiedTab);
}
public getInitialTabId(): string {
if (!this.cancelWatchHandle) {
this.cancelWatchHandle = this.$rootScope.$on('$routeUpdate', () => this.checkLocation());
}
return this.$location.search()['tab'];
}
public notifyTabChanged(tab: CorTabComponent, isDefaultTab: boolean) {
var newSearch = $.extend(this.$location.search(), {});
if (isDefaultTab) {
delete newSearch['tab'];
} else {
newSearch['tab'] = tab.tabId;
}
this.$location.search(newSearch);
}
public dispose(): void {
this.cancelWatchHandle();
}
}
/**
* Reads and writes the tab from a cookie,.
*/
export class CookieCurrentTabHandler implements CorTabCurrentHandler {
constructor (private CookieService: any, private cookieName: string) {}
public getInitialTabId(): string {
return this.CookieService.get(this.cookieName);
}
public notifyTabChanged(tab: CorTabComponent, isDefaultTab: boolean) {
if (isDefaultTab) {
this.CookieService.clear(this.cookieName);
} else {
this.CookieService.putPermanent(this.cookieName, tab.tabId);
}
}
public dispose(): void {}
}

View file

@ -0,0 +1,3 @@
<div class="co-tab-pane" ng-show="$ctrl.isActiveTab">
<div ng-transclude />
</div>

View file

@ -0,0 +1,31 @@
import { Component, Input, Inject, Host, OnInit } from 'ng-metadata/core';
import { CorTabPanelComponent } from './cor-tab-panel.component';
/**
* A component that creates a single tab pane under a cor-tabs component.
*/
@Component({
selector: 'cor-tab-pane',
templateUrl: '/static/js/directives/ui/cor-tabs/cor-tab-pane.component.html',
legacy: {
transclude: true,
}
})
export class CorTabPaneComponent implements OnInit {
@Input('@') public id: string;
// Whether this is the active tab.
private isActiveTab: boolean = false;
constructor(@Host() @Inject(CorTabPanelComponent) private parent: CorTabPanelComponent) {
}
public ngOnInit(): void {
this.parent.addTabPane(this);
}
public changeState(isActive: boolean): void {
this.isActiveTab = isActive;
}
}

View file

@ -0,0 +1,115 @@
import { Component, Input, Inject, OnDestroy } from 'ng-metadata/core';
import { CorTabComponent } from './cor-tab.component';
import { CorTabPaneComponent } from './cor-tab-pane.component';
import { CorTabCurrentHandler, LocationCurrentTabHandler, CookieCurrentTabHandler, CorTabCurrentHandlerFactory } from './cor-tab-handlers'
/**
* A component that contains a cor-tabs and handles all of its logic.
*/
@Component({
selector: 'cor-tab-panel',
templateUrl: '/static/js/directives/ui/cor-tabs/cor-tab-panel.component.html',
legacy: {
transclude: true,
}
})
export class CorTabPanelComponent implements OnDestroy {
// If 'true', the currently selected tab will be remembered via a cookie and not the page URL.
@Input('@') public rememberCookie: string;
// The tabs under this tabs component.
private tabs: CorTabComponent[] = [];
// The tab panes under the tabs component, indexed by the tab id.
private tabPanes: {[id: string]: CorTabPaneComponent} = {};
// The currently active tab, if any.
private activeTab: CorTabComponent = null;
// Whether the initial tab was set.
private initialTabSet: boolean = false;
// The handler to use to read/write the current tab.
private currentTabHandler: CorTabCurrentHandler = null;
constructor(@Inject('$location') private $location: ng.ILocationService,
@Inject('$rootScope') private $rootScope: ng.IRootScopeService,
@Inject('CookieService') private CookieService: any,
@Inject('CorTabCurrentHandlerFactory') private CorTabCurrentHandlerFactory: (Object) => CorTabCurrentHandler) {
}
public ngOnDestroy(): void {
this.currentTabHandler.dispose();
}
public tabClicked(tab: CorTabComponent): void {
this.setActiveTab(tab);
}
public findTab(tabId: string): CorTabComponent {
if (!this.tabs.length) {
return null;
}
var tab = this.tabs.find(function(current) {
return current.tabId == tabId;
}) || this.tabs[0];
if (!this.tabPanes[tab.tabId]) {
return null;
}
return tab;
}
public setActiveTab(tab: CorTabComponent): void {
if (this.activeTab == tab) {
return;
}
if (this.activeTab != null) {
this.activeTab.changeState(false);
this.tabPanes[this.activeTab.tabId].changeState(false);
}
this.activeTab = tab;
this.activeTab.changeState(true);
this.tabPanes[this.activeTab.tabId].changeState(true);
this.currentTabHandler.notifyTabChanged(tab, this.tabs[0] == tab);
}
public addTab(tab: CorTabComponent): void {
this.tabs.push(tab);
this.checkInitialTab();
}
public addTabPane(tabPane: CorTabPaneComponent): void {
this.tabPanes[tabPane.id] = tabPane;
this.checkInitialTab();
}
private checkInitialTab(): void {
if (this.tabs.length < 1 || this.initialTabSet) {
return;
}
this.currentTabHandler = this.CorTabCurrentHandlerFactory({
type: this.rememberCookie ? 'cookie' : 'location',
cookieService: this.CookieService,
cookeName: this.rememberCookie,
panel: this,
$location: this.$location,
$rootScope: this.$rootScope,
});
var tabId = this.currentTabHandler.getInitialTabId();
var tab = this.findTab(tabId);
if (!tab) {
return;
}
this.initialTabSet = true;
this.setActiveTab(tab);
}
}

View file

@ -0,0 +1,10 @@
<li ng-class="{'active': $ctrl.isActive}">
<a ng-click="$ctrl.tabClicked()">
<span data-title="{{ ::$ctrl.tabTitle }}"
data-placement="right"
data-container="body"
style="display: inline-block"
bs-tooltip><span ng-transclude/></span><span class="visible-xs-inline xs-label">{{ ::$ctrl.tabTitle }}</span>
</span>
</a>
</li>

View file

@ -0,0 +1,46 @@
import { Component, Input, Output, Inject, EventEmitter, Host, OnInit } from 'ng-metadata/core';
import { CorTabPanelComponent } from './cor-tab-panel.component';
/**
* A component that creates a single tab under a cor-tabs component.
*/
@Component({
selector: 'cor-tab',
templateUrl: '/static/js/directives/ui/cor-tabs/cor-tab.component.html',
legacy: {
transclude: true,
}
})
export class CorTabComponent implements OnInit {
@Input('@') public tabId: string;
@Input('@') public tabTitle: string;
@Output() public tabInit: EventEmitter<any> = new EventEmitter();
@Output() public tabShown: EventEmitter<any> = new EventEmitter();
@Output() public tabHidden: EventEmitter<any> = new EventEmitter();
// Whether this is the active tab.
private isActive: boolean = false;
constructor(@Host() @Inject(CorTabPanelComponent) private parent: CorTabPanelComponent) {
}
public ngOnInit(): void {
this.parent.addTab(this);
}
public changeState(isActive: boolean): void {
this.isActive = isActive;
if (isActive) {
this.tabInit.emit({});
this.tabShown.emit({});
} else {
this.tabHidden.emit({});
}
}
private tabClicked(): void {
this.parent.tabClicked(this);
}
}

View file

@ -0,0 +1,4 @@
<span class="co-tab-element" ng-class="$ctrl.isClosed ? 'closed' : 'open'">
<span class="xs-toggle" ng-click="$ctrl.toggleClosed($event)"></span>
<ul class="co-tabs col-md-1" ng-transclude></ul>
</span>

View file

@ -0,0 +1,22 @@
import { Component, Host, Inject } from 'ng-metadata/core';
import { CorTabComponent } from './cor-tab.component';
/**
* A component that holds the actual tabs.
*/
@Component({
selector: 'cor-tabs',
templateUrl: '/static/js/directives/ui/cor-tabs/cor-tabs.component.html',
legacy: {
transclude: true,
}
})
export class CorTabsComponent {
// If true, the tabs are in a closed state. Only applies in the mobile view.
private isClosed: boolean = true;
private toggleClosed(e): void {
this.isClosed = !this.isClosed;
}
}

View file

@ -13,6 +13,12 @@ import { AppPublicViewComponent } from './directives/ui/app-public-view/app-publ
import { VisibilityIndicatorComponent } from './directives/ui/visibility-indicator/visibility-indicator.component'; import { VisibilityIndicatorComponent } from './directives/ui/visibility-indicator/visibility-indicator.component';
import { CorTableComponent } from './directives/ui/cor-table/cor-table.component'; import { CorTableComponent } from './directives/ui/cor-table/cor-table.component';
import { CorTableColumn } from './directives/ui/cor-table/cor-table-col.component'; import { CorTableColumn } from './directives/ui/cor-table/cor-table-col.component';
import { CorTabPanelComponent } from './directives/ui/cor-tabs/cor-tab-panel.component';
import { CorTabContentComponent } from './directives/ui/cor-tabs/cor-tab-content.component';
import { CorTabsComponent } from './directives/ui/cor-tabs/cor-tabs.component';
import { CorTabComponent } from './directives/ui/cor-tabs/cor-tab.component';
import { CorTabPaneComponent } from './directives/ui/cor-tabs/cor-tab-pane.component';
import { CorTabCurrentHandlerFactory } from './directives/ui/cor-tabs/cor-tab-handlers';
import { ChannelIconComponent } from './directives/ui/channel-icon/channel-icon.component'; import { ChannelIconComponent } from './directives/ui/channel-icon/channel-icon.component';
import { TagSigningDisplayComponent } from './directives/ui/tag-signing-display/tag-signing-display.component'; import { TagSigningDisplayComponent } from './directives/ui/tag-signing-display/tag-signing-display.component';
import { RepositorySigningConfigComponent } from './directives/ui/repository-signing-config/repository-signing-config.component'; import { RepositorySigningConfigComponent } from './directives/ui/repository-signing-config/repository-signing-config.component';
@ -56,6 +62,11 @@ import { QuayRequireDirective } from './directives/structural/quay-require/quay-
DurationInputComponent, DurationInputComponent,
SearchBoxComponent, SearchBoxComponent,
TypeaheadDirective, TypeaheadDirective,
CorTabPanelComponent,
CorTabContentComponent,
CorTabsComponent,
CorTabComponent,
CorTabPaneComponent,
], ],
providers: [ providers: [
ViewArrayImpl, ViewArrayImpl,
@ -65,6 +76,7 @@ import { QuayRequireDirective } from './directives/structural/quay-require/quay-
DataFileServiceImpl, DataFileServiceImpl,
UtilServiceImpl, UtilServiceImpl,
{provide: 'fileReaderFactory', useValue: () => new FileReader()}, {provide: 'fileReaderFactory', useValue: () => new FileReader()},
{provide: 'CorTabCurrentHandlerFactory', useValue: CorTabCurrentHandlerFactory},
], ],
}) })
export class QuayModule { export class QuayModule {

View file

@ -15,42 +15,46 @@
</span> </span>
</div> </div>
<div class="cor-tab-panel"> <cor-tab-panel>
<div class="cor-tabs"> <cor-tabs>
<span class="cor-tab" tab-active="true" tab-title="Layers" tab-target="#layers"> <cor-tab tab-title="Layers" tab-id="layers">
<i class="fa ci-layers"></i> <i class="fa ci-layers"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Security Scan" tab-target="#vulnerabilities" <cor-tab tab-title="Security Scan" tab-id="vulnerabilities"
tab-init="loadImageSecurity()" tab-init="loadImageSecurity()"
quay-show="Features.SECURITY_SCANNER"> quay-show="Features.SECURITY_SCANNER">
<i class="fa fa-bug"></i> <i class="fa fa-bug"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Packages" tab-target="#packages" <cor-tab tab-title="Packages" tab-id="packages"
tab-init="loadImagePackages()" tab-init="loadImagePackages()"
quay-show="Features.SECURITY_SCANNER"> quay-show="Features.SECURITY_SCANNER">
<i class="fa ci-package"></i> <i class="fa ci-package"></i>
</span> </cor-tab>
</div> <!-- /cor-tabs --> </cor-tabs>
<div class="cor-tab-content"> <cor-tab-content>
<!-- Layers --> <!-- Layers -->
<div id="layers" class="tab-pane active"> <cor-tab-pane id="layers">
<h3>Image Layers</h3> <h3>Image Layers</h3>
<div class="image-view-layer" repository="repository" image="image" images="image.history"></div> <div class="image-view-layer" repository="repository" image="image" images="image.history"></div>
<div class="image-view-layer" repository="repository" image="parent" images="image.history" <div class="image-view-layer" repository="repository" image="parent" images="image.history"
ng-repeat="parent in reversedHistory"></div> ng-repeat="parent in reversedHistory"></div>
</div> </cor-tab-pane>
<!-- Vulnerabilities --> <!-- Vulnerabilities -->
<div id="vulnerabilities" class="tab-pane" quay-require="['SECURITY_SCANNER']"> <cor-tab-pane id="vulnerabilities" quay-show="Features.SECURITY_SCANNER">
<div class="image-vulnerability-view" repository="repository" image="image" is-enabled="imageSecurityCounter"></div> <div quay-require="['SECURITY_SCANNER']">
</div> <div class="image-vulnerability-view" repository="repository" image="image" is-enabled="imageSecurityCounter"></div>
</div>
</cor-tab-pane>
<!-- Features --> <!-- Features -->
<div id="packages" class="tab-pane" quay-require="['SECURITY_SCANNER']"> <cor-tab-pane id="packages" quay-show="Features.SECURITY_SCANNER">
<div class="image-feature-view" repository="repository" image="image" is-enabled="imagePackageCounter"></div> <div quay-require="['SECURITY_SCANNER']">
</div> <div class="image-feature-view" repository="repository" image="image" is-enabled="imagePackageCounter"></div>
</div> </div>
</div> </cor-tab-pane>
</cor-tab-content>
</cor-tab-panel>
</div> </div>
</div> </div>

View file

@ -20,29 +20,28 @@
</div> </div>
</div> </div>
<div class="cor-tab-panel"> <cor-tab-panel>
<div class="cor-tabs"> <cor-tabs>
<span class="cor-tab" tab-active="true" tab-title="Settings" tab-target="#settings"> <cor-tab tab-title="Settings" tab-id="settings">
<i class="fa fa-gear"></i> <i class="fa fa-gear"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="OAuth Information" tab-target="#oauth"> <cor-tab tab-title="OAuth Information" tab-id="oauth">
<i class="fa fa-key"></i> <i class="fa fa-key"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Delete Application" tab-target="#delete"> <cor-tab tab-title="Delete Application" tab-id="delete">
<i class="fa fa-times"></i> <i class="fa fa-times"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Generate Token" tab-target="#gen-token"> <cor-tab tab-title="Generate Token" tab-id="gen-token">
<i class="fa fa-ticket"></i> <i class="fa fa-ticket"></i>
</span> </cor-tab>
</div> <!-- /cor-tabs --> </cor-tabs>
<div class="cor-tab-content">
<cor-tab-content>
<!-- Settings tab --> <!-- Settings tab -->
<div id="settings" class="tab-pane active"> <cor-tab-pane id="settings">
<form method="put" name="applicationForm" id="applicationForm" ng-submit="updateApplication()"> <form method="put" name="applicationForm" id="applicationForm" ng-submit="updateApplication()">
<div class="form-group nested"> <div class="form-group nested">
<label for="fieldAppName">Application Name</label> <label for="fieldAppName">Application Name</label>
@ -81,10 +80,10 @@
<span class="quay-spinner" ng-show="updating"></span> <span class="quay-spinner" ng-show="updating"></span>
</div> </div>
</form> </form>
</div> </cor-tab-pane>
<!-- Delete tab --> <!-- Delete tab -->
<div id="delete" class="tab-pane"> <cor-tab-pane id="delete">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<div style="text-align: center"> <div style="text-align: center">
@ -93,10 +92,10 @@
</div> </div>
</div> </div>
</div> </div>
</div> </cor-tab-pane>
<!-- Generate Token tab --> <!-- Generate Token tab -->
<div id="gen-token" class="tab-pane"> <cor-tab-pane id="gen-token">
<div class="co-alert co-alert-info"> <div class="co-alert co-alert-info">
<div style="margin-bottom: 10px"> <div style="margin-bottom: 10px">
Click the button below to generate a new <a href="http://tools.ietf.org/html/rfc6749#section-1.4" target="_new">OAuth 2 Access Token</a>. Click the button below to generate a new <a href="http://tools.ietf.org/html/rfc6749#section-1.4" target="_new">OAuth 2 Access Token</a>.
@ -125,10 +124,10 @@
ng-disabled="!getScopes(genScopes).length" ng-safenewtab> ng-disabled="!getScopes(genScopes).length" ng-safenewtab>
Generate Access Token Generate Access Token
</a> </a>
</div> </cor-tab-pane>
<!-- OAuth tab --> <!-- OAuth tab -->
<div id="oauth" class="tab-pane"> <cor-tab-pane id="oauth">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>Client ID:</dt> <dt>Client ID:</dt>
<dd><div class="copy-box" hovering-message="true" value="application.client_id"></div></dd> <dd><div class="copy-box" hovering-message="true" value="application.client_id"></div></dd>
@ -139,10 +138,9 @@
</dl> </dl>
<button class="btn btn-primary" ng-click="askResetClientSecret()">Reset Client Secret</button> <button class="btn btn-primary" ng-click="askResetClientSecret()">Reset Client Secret</button>
</div> </cor-tab-pane>
</cor-tab-content>
</div> <!-- /cor-tab-content --> </cor-tab-panel>
</div>
</div> </div>
</div> </div>

View file

@ -20,75 +20,75 @@
<div class="repo-list-view padded" namespaces="[organization]">&nbsp;</div> <div class="repo-list-view padded" namespaces="[organization]">&nbsp;</div>
</div> </div>
<div class="cor-tab-panel" ng-if="!user.anonymous && isMember"> <cor-tab-panel ng-if="!user.anonymous && isMember">
<div class="cor-tabs" quay-show="isMember"> <cor-tabs quay-show="isMember">
<span class="cor-tab" tab-active="true" tab-title="Repositories" tab-target="#repos"> <cor-tab tab-active="true" tab-title="Repositories" tab-id="repos">
<i class="fa fa-hdd-o"></i> <i class="fa fa-hdd-o"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Teams and Membership" tab-target="#teams" tab-init="showTeams()"> <cor-tab tab-title="Teams and Membership" tab-id="teams" tab-init="showTeams()">
<i class="fa fa-users"></i> <i class="fa fa-users"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Robot Accounts" tab-target="#robots" tab-init="showRobots()" <cor-tab tab-title="Robot Accounts" tab-id="robots" tab-init="showRobots()"
ng-show="isAdmin"> ng-show="isAdmin">
<i class="fa ci-robot"></i> <i class="fa ci-robot"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Default Permissions" tab-target="#default" ng-show="isAdmin"> <cor-tab tab-title="Default Permissions" tab-id="default" ng-show="isAdmin">
<i class="fa ci-stamp"></i> <i class="fa ci-stamp"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Usage Logs" tab-target="#logs" <cor-tab tab-title="Usage Logs" tab-id="logs"
tab-init="showLogs()" ng-show="isAdmin"> tab-init="showLogs()" ng-show="isAdmin">
<i class="fa fa-bar-chart"></i> <i class="fa fa-bar-chart"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Applications" tab-target="#applications" <cor-tab tab-title="Applications" tab-id="applications"
tab-init="showApplications()" ng-show="isAdmin"> tab-init="showApplications()" ng-show="isAdmin">
<i class="fa ci-application"></i> <i class="fa ci-application"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Organization Settings" tab-target="#settings" <cor-tab tab-title="Organization Settings" tab-id="settings"
ng-show="isAdmin" tab-init="showBilling()"> ng-show="isAdmin" tab-init="showBilling()">
<i class="fa fa-gears"></i> <i class="fa fa-gears"></i>
</span> </cor-tab>
</div> <!-- /cor-tabs --> </cor-tabs> <!-- /cor-tabs -->
<div class="cor-tab-content"> <cor-tab-content>
<!-- Repositories --> <!-- Repositories -->
<div id="repos" class="tab-pane active"> <cor-tab-pane id="repos">
<div class="repo-list-view" namespaces="[organization]"><h3>Repositories</h3></div> <div class="repo-list-view" namespaces="[organization]"><h3>Repositories</h3></div>
</div> </cor-tab-pane>
<!-- Teams --> <!-- Teams -->
<div id="teams" class="tab-pane"> <cor-tab-pane id="teams">
<div ng-if="!user.anonymous"> <div ng-if="!user.anonymous">
<div class="teams-manager" organization="organization" is-enabled="showTeamsCounter"></div> <div class="teams-manager" organization="organization" is-enabled="showTeamsCounter"></div>
</div> </div>
</div> </cor-tab-pane>
<!-- Robot Accounts --> <!-- Robot Accounts -->
<div id="robots" class="tab-pane"> <cor-tab-pane id="robots">
<div ng-if="isAdmin"> <div ng-if="isAdmin">
<div class="robots-manager" organization="organization" is-enabled="showRobotsCounter"></div> <div class="robots-manager" organization="organization" is-enabled="showRobotsCounter"></div>
</div> </div>
</div> </cor-tab-pane>
<!-- Default Permissions --> <!-- Default Permissions -->
<div id="default" class="tab-pane"> <cor-tab-pane id="default">
<div ng-if="isAdmin"> <div ng-if="isAdmin">
<div class="prototype-manager" organization="organization"></div> <div class="prototype-manager" organization="organization"></div>
</div> </div>
</div> </cor-tab-pane>
<!-- Usage Logs --> <!-- Usage Logs -->
<div id="logs" class="tab-pane"> <cor-tab-pane id="logs">
<div class="logs-view" organization="organization" makevisible="showLogsCounter"></div> <div class="logs-view" organization="organization" makevisible="showLogsCounter"></div>
</div> </cor-tab-pane>
<!-- Applications --> <!-- Applications -->
<div id="applications" class="tab-pane"> <cor-tab-pane id="applications">
<div class="application-manager" organization="organization" <div class="application-manager" organization="organization"
makevisible="showApplicationsCounter"></div> makevisible="showApplicationsCounter"></div>
</div> </cor-tab-pane>
<!-- Settings --> <!-- Settings -->
<div id="settings" class="tab-pane"> <cor-tab-pane id="settings">
<div ng-if="isAdmin"> <div ng-if="isAdmin">
<!-- Org Settings --> <!-- Org Settings -->
<div class="settings-section"> <div class="settings-section">
@ -129,9 +129,9 @@
<div class="billing-management-panel" organization="organization" is-enabled="showBillingCounter" subscription-status="subscriptionStatus"></div> <div class="billing-management-panel" organization="organization" is-enabled="showBillingCounter" subscription-status="subscriptionStatus"></div>
</div> </div>
</div> </div>
</div> </cor-tab-pane>
</div> <!-- /cor-tab-content --> </cor-tab-content>
</div> </cor-tab-panel>
</div> </div>
<!-- Change email dialog --> <!-- Change email dialog -->

View file

@ -35,91 +35,91 @@
</span> </span>
</div> </div>
<div class="cor-tab-panel"> <cor-tab-panel>
<div class="cor-tabs"> <cor-tabs>
<span class="cor-tab" tab-active="true" tab-title="Information" tab-target="#info" <cor-tab tab-title="Information" tab-id="info"
tab-init="showInfo()"> tab-init="showInfo()">
<i class="fa fa-info-circle"></i> <i class="fa fa-info-circle"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Tags" tab-target="#tags" id="tagsTab" <cor-tab tab-title="Tags" tab-id="tags" id="tagsTab"
tab-init="showTags()"> tab-init="showTags()">
<i class="fa fa-tags"></i> <i class="fa fa-tags"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Tag History" tab-target="#history" id="tagHistoryTab" <cor-tab tab-title="Tag History" tab-id="history" id="tagHistoryTab"
tab-init="showHistory()"> tab-init="showHistory()">
<i class="fa fa-history"></i> <i class="fa fa-history"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Builds" tab-target="#builds" id="buildsTab" <cor-tab tab-title="Builds" tab-id="builds" id="buildsTab"
tab-init="showBuilds()" tab-init="showBuilds()"
quay-show="viewScope.repository.can_write && Features.BUILD_SUPPORT"> quay-show="viewScope.repository.can_write && Features.BUILD_SUPPORT">
<i class="fa fa-tasks"></i> <i class="fa fa-tasks"></i>
</span> </cor-tab>
<!-- Admin Only Tabs --> <!-- Admin Only Tabs -->
<span class="cor-tab" tab-title="Usage Logs" tab-target="#logs" tab-init="showLogs()" <cor-tab tab-title="Usage Logs" tab-id="logs" tab-init="showLogs()"
ng-show="viewScope.repository.can_admin"> ng-show="viewScope.repository.can_admin">
<i class="fa fa-bar-chart"></i> <i class="fa fa-bar-chart"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Settings" tab-target="#settings" id="settingsTab" <cor-tab tab-title="Settings" tab-id="settings" id="settingsTab"
tab-init="showSettings()" tab-init="showSettings()"
ng-show="viewScope.repository.can_admin"> ng-show="viewScope.repository.can_admin">
<i class="fa fa-gear"></i> <i class="fa fa-gear"></i>
</span> </cor-tab>
</div> <!-- /cor-tabs --> </cor-tabs>
<div class="cor-tab-content"> <cor-tab-content>
<!-- Information --> <!-- Information -->
<div id="info" class="tab-pane active"> <cor-tab-pane id="info">
<div class="repo-panel-info" <div class="repo-panel-info"
repository="viewScope.repository" repository="viewScope.repository"
builds="viewScope.builds" builds="viewScope.builds"
is-enabled="infoShown"></div> is-enabled="infoShown"></div>
</div> </cor-tab-pane>
<!-- Tags --> <!-- Tags -->
<div id="tags" class="tab-pane"> <cor-tab-pane id="tags">
<div class="repo-panel-tags" <div class="repo-panel-tags"
repository="viewScope.repository" repository="viewScope.repository"
image-loader="viewScope.imageLoader" image-loader="viewScope.imageLoader"
selected-tags="viewScope.selectedTags" selected-tags="viewScope.selectedTags"
history-filter="viewScope.historyFilter" history-filter="viewScope.historyFilter"
is-enabled="tagsShown"></div> is-enabled="tagsShown"></div>
</div> </cor-tab-pane>
<!-- Tag History --> <!-- Tag History -->
<div id="history" class="tab-pane"> <cor-tab-pane id="history">
<h3 class="tab-header">Tag History</h3> <h3 class="tab-header">Tag History</h3>
<div class="repo-tag-history" <div class="repo-tag-history"
repository="viewScope.repository" repository="viewScope.repository"
filter="viewScope.historyFilter" filter="viewScope.historyFilter"
image-loader="viewScope.imageLoader" image-loader="viewScope.imageLoader"
is-enabled="historyShown"></div> is-enabled="historyShown"></div>
</div> </cor-tab-pane>
<!-- Builds --> <!-- Builds -->
<div id="builds" class="tab-pane"> <cor-tab-pane id="builds">
<div class="repo-panel-builds" <div class="repo-panel-builds"
repository="viewScope.repository" repository="viewScope.repository"
builds="viewScope.builds" builds="viewScope.builds"
is-enabled="buildsShown"></div> is-enabled="buildsShown"></div>
</div> </cor-tab-pane>
<!-- Usage Logs --> <!-- Usage Logs -->
<div id="logs" class="tab-pane" ng-if="viewScope.repository.can_admin"> <cor-tab-pane id="logs" ng-if="viewScope.repository.can_admin">
<div class="logs-view" repository="viewScope.repository" makevisible="logsShown"></div> <div class="logs-view" repository="viewScope.repository" makevisible="logsShown"></div>
</div> </cor-tab-pane>
<!-- Settings --> <!-- Settings -->
<div id="settings" class="tab-pane" ng-if="viewScope.repository.can_admin"> <cor-tab-pane id="settings" ng-if="viewScope.repository.can_admin">
<div class="repo-panel-settings" repository="viewScope.repository" <div class="repo-panel-settings" repository="viewScope.repository"
is-enabled="settingsShown"></div> is-enabled="settingsShown"></div>
</div> </cor-tab-pane>
</div> <!-- /cor-tab-content --> </cor-tab-content>
</div> </cor-tabs>
</div> </cor-tab-panel>
</div> </div>
</div> </div>

View file

@ -22,72 +22,71 @@
<span class="cor-title-content">Quay Enterprise Management</span> <span class="cor-title-content">Quay Enterprise Management</span>
</div> </div>
<div class="cor-tab-panel"> <cor-tab-panel>
<div class="cor-tabs"> <cor-tabs>
<span class="cor-tab" tab-active="true" tab-title="Manage Users" <cor-tab tab-title="Manage Users"
tab-target="#users" tab-init="loadUsers()"> tab-id="users" tab-init="loadUsers()">
<i class="fa fa-group"></i> <i class="fa fa-group"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Manage Organizations" <cor-tab tab-title="Manage Organizations"
tab-target="#organizations" tab-init="loadOrganizations()"> tab-id="organizations" tab-init="loadOrganizations()">
<i class="fa fa-sitemap"></i> <i class="fa fa-sitemap"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Manage Service Keys" <cor-tab tab-title="Manage Service Keys"
tab-target="#servicekeys" tab-init="loadServiceKeys()"> tab-id="servicekeys" tab-init="loadServiceKeys()">
<i class="fa fa-key"></i> <i class="fa fa-key"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Change Log" tab-target="#change-log" tab-init="getChangeLog()"> <cor-tab tab-title="Change Log" tab-id="change-log" tab-init="getChangeLog()">
<i class="fa fa-rss"></i> <i class="fa fa-rss"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Usage Logs" tab-target="#logs" tab-init="loadUsageLogs()"> <cor-tab tab-title="Usage Logs" tab-id="logs" tab-init="loadUsageLogs()">
<i class="fa fa-bar-chart"></i> <i class="fa fa-bar-chart"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Internal Logs and Debugging" tab-target="#debug" <cor-tab tab-title="Internal Logs and Debugging" tab-id="debug"
tab-init="loadDebugServices()"> tab-init="loadDebugServices()">
<i class="fa fa-bug"></i> <i class="fa fa-bug"></i>
</span> </cor-tab>
<span class="cor-tab hidden-xs" tab-title="Registry Settings" tab-target="#setup" <cor-tab tab-title="Registry Settings" tab-id="setup"
tab-init="loadConfig()"> tab-init="loadConfig()">
<i class="fa fa-cog"></i> <i class="fa fa-cog"></i>
</span> </cor-tab>
<span class="cor-tab hidden-xs" tab-title="Globally visible user messages" tab-target="#message-of-the-day" <cor-tab tab-title="Globally visible user messages" tab-id="message-of-the-day"
tab-init="loadMessageOfTheDay()"> tab-init="loadMessageOfTheDay()">
<i class="fa fa-newspaper-o"></i> <i class="fa fa-newspaper-o"></i>
</span> </cor-tab>
<span class="cor-tab hidden-xs" tab-title="Build Logs" tab-target="#super-user-build-logs" <cor-tab tab-title="Build Logs" tab-id="super-user-build-logs"
tab-init="loadSuperUserBuildLogs()"> tab-init="loadSuperUserBuildLogs()">
<i class="fa fa-history"></i> <i class="fa fa-history"></i>
</span> </cor-tab>
</div> <!-- /cor-tabs --> </cor-tabs>
<div class="cor-tab-content"> <cor-tab-content>
<!-- Setup tab --> <!-- Setup tab -->
<div id="setup" class="tab-pane"> <cor-tab-pane id="setup">
<div class="co-alert co-alert-warning" ng-if="configProviderId != 'k8s'"> <div class="co-alert co-alert-warning" ng-if="configProviderId != 'k8s'">
<strong>Warning:</strong> If you are running Quay Enterprise under multiple containers, additional actions are necessary in order to apply any configuration changes made here to the entire cluster. It is recommended to make (and validate) configuration changes here, then copy your configuration to all instances and restart them. <strong>Warning:</strong> If you are running Quay Enterprise under multiple containers, additional actions are necessary in order to apply any configuration changes made here to the entire cluster. It is recommended to make (and validate) configuration changes here, then copy your configuration to all instances and restart them.
</div> </div>
<div class="config-setup-tool" is-active="configStatus == 'ready'" <div class="config-setup-tool" is-active="configStatus == 'ready'"
configuration-saved="configurationSaved(config)"></div> configuration-saved="configurationSaved(config)"></div>
</div> </cor-tab-pane>
<!-- Super user build logs tab--> <!-- Super user build logs tab-->
<div id="super-user-build-logs" class="tab-pane"> <cor-tab-pane id="super-user-build-logs">
<div class="super-user-build-logs" is-enabled="superUserBuildLogsActive"></div> <div class="super-user-build-logs" is-enabled="superUserBuildLogsActive"></div>
</div> <!-- Super user build logs tab --> </cor-tab-pane> <!-- Super user build logs tab -->
<!-- Messages tab --> <!-- Messages tab -->
<div id="message-of-the-day" class="tab-pane"> <cor-tab-pane id="message-of-the-day">
<div class="global-message-tab" is-enabled="globalMessagesActive"></div> <div class="global-message-tab" is-enabled="globalMessagesActive"></div>
</div> <!-- Messages tab --> </cor-tab-pane> <!-- Messages tab -->
<!-- Service keys tab --> <!-- Service keys tab -->
<div id="servicekeys" class="tab-pane"> <cor-tab-pane id="servicekeys">
<div class="service-keys-manager" is-enabled="serviceKeysActive"></div> <div class="service-keys-manager" is-enabled="serviceKeysActive"></div>
</div> </cor-tab-pane>
<!-- Debugging tab --> <!-- Debugging tab -->
<div id="debug" class="tab-pane"> <cor-tab-pane id="debug">
<div class="cor-loader" ng-show="!debugServices"></div> <div class="cor-loader" ng-show="!debugServices"></div>
<div role="tabpanel" ng-show="debugServices"> <div role="tabpanel" ng-show="debugServices">
@ -110,22 +109,22 @@
</div> </div>
<div class="cor-log-box" logs="debugLogs" ng-show="debugService"></div> <div class="cor-log-box" logs="debugLogs" ng-show="debugService"></div>
</div> </div>
</div> </cor-tab-pane>
<!-- Logs tab --> <!-- Logs tab -->
<div id="logs" class="tab-pane"> <cor-tab-pane id="logs">
<div class="logsView" makevisible="logsCounter" all-logs="true"></div> <div class="logsView" makevisible="logsCounter" all-logs="true"></div>
</div> <!-- /logs tab--> </cor-tab-pane> <!-- /logs tab-->
<!-- Change Log tab --> <!-- Change Log tab -->
<div id="change-log" class="tab-pane"> <cor-tab-pane id="change-log">
<h3 style="margin-top: 0px;">Change Log</h3> <h3 style="margin-top: 0px;">Change Log</h3>
<div class="cor-loader" ng-if="!changeLog"></div> <div class="cor-loader" ng-if="!changeLog"></div>
<div class="markdown-view" content="changeLog.log" ng-if="changeLog"></div> <div class="markdown-view" content="changeLog.log" ng-if="changeLog"></div>
</div> <!-- /change-log tab--> </cor-tab-pane> <!-- /change-log tab-->
<!-- Organizations tab --> <!-- Organizations tab -->
<div id="organizations" class="tab-pane"> <cor-tab-pane id="organizations">
<div class="resource-view" resource="organizationsResource" <div class="resource-view" resource="organizationsResource"
error-message="'Could not load organizations'"> error-message="'Could not load organizations'">
<div class="manager-header" header-title="Organizations"> <div class="manager-header" header-title="Organizations">
@ -180,15 +179,14 @@
</tr> </tr>
</table> </table>
</div> <!-- /resource --> </div> <!-- /resource -->
</div> <!-- organizations tab --> </cor-tab-pane> <!-- organizations tab -->
<!-- Users tab --> <!-- Users tab -->
<div id="users" class="tab-pane active"> <cor-tab-pane id="users" class="tab-pane active">
<div class="manage-user-tab" is-enabled="manageUsersActive"></div> <div class="manage-user-tab" is-enabled="manageUsersActive"></div>
</div> <!-- users-tab --> </cor-tab-pane> <!-- users-tab -->
</cor-tab-content>
</div> <!-- /cor-tab-content --> </cor-tab-panel>
</div> <!-- /cor-tab-panel -->
<!-- Modal message dialog --> <!-- Modal message dialog -->
<div class="co-dialog modal fade" id="restartingContainerModal"> <div class="co-dialog modal fade" id="restartingContainerModal">

View file

@ -20,54 +20,54 @@
<div class="repo-list-view padded" namespaces="[context.viewuser]">&nbsp;</div> <div class="repo-list-view padded" namespaces="[context.viewuser]">&nbsp;</div>
</div> </div>
<div class="cor-tab-panel" ng-if="context.viewuser.is_me"> <cor-tab-panel ng-if="context.viewuser.is_me">
<div class="cor-tabs" quay-show="context.viewuser.is_me"> <cor-tabs quay-show="context.viewuser.is_me">
<span class="cor-tab" tab-active="true" tab-title="Repositories" tab-target="#repos"> <cor-tab tab-active="true" tab-title="Repositories" tab-id="repos">
<i class="fa fa-hdd-o"></i> <i class="fa fa-hdd-o"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Robot Accounts" tab-init="showRobots()" tab-target="#robots"> <cor-tab tab-title="Robot Accounts" tab-init="showRobots()" tab-id="robots">
<i class="fa ci-robot"></i> <i class="fa ci-robot"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="External Logins And Applications" tab-target="#external" <cor-tab tab-title="External Logins And Applications" tab-id="external"
tab-init="showApplications()"> tab-init="showApplications()">
<i class="fa fa-external-link-square"></i> <i class="fa fa-external-link-square"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="Usage Logs" tab-target="#logs" tab-init="showLogs()" <cor-tab tab-title="Usage Logs" tab-id="logs" tab-init="showLogs()"
quay-show="Features.USER_LOG_ACCESS"> quay-show="Features.USER_LOG_ACCESS">
<i class="fa fa-bar-chart"></i> <i class="fa fa-bar-chart"></i>
</span> </cor-tab>
<span class="cor-tab" tab-title="User Settings" tab-target="#settings" tab-init="showBilling()"> <cor-tab tab-title="User Settings" tab-id="settings" tab-init="showBilling()">
<i class="fa fa-gears"></i> <i class="fa fa-gears"></i>
</span> </cor-tab>
</div> <!-- /cor-tabs --> </cor-tabs>
<div class="cor-tab-content"> <cor-tab-content>
<!-- Repositories --> <!-- Repositories -->
<div id="repos" class="tab-pane active"> <cor-tab-pane id="repos">
<div class="repo-list-view" namespaces="[context.viewuser]"><h3>Repositories</h3></div> <div class="repo-list-view" namespaces="[context.viewuser]"><h3>Repositories</h3></div>
</div> </cor-tab-pane>
<!-- Robot Accounts --> <!-- Robot Accounts -->
<div id="robots" class="tab-pane"> <cor-tab-pane id="robots">
<div class="robots-manager" user="viewuser" is-enabled="showRobotsCounter"></div> <div class="robots-manager" user="viewuser" is-enabled="showRobotsCounter"></div>
</div> </cor-tab-pane>
<!-- Usage Logs --> <!-- Usage Logs -->
<div id="logs" class="tab-pane"> <cor-tab-pane id="logs">
<div class="logs-view" user="viewuser" makevisible="showLogsCounter"></div> <div class="logs-view" user="viewuser" makevisible="showLogsCounter"></div>
</div> </cor-tab-pane>
<!-- External Logins And Applications --> <!-- External Logins And Applications -->
<div id="external" class="tab-pane" quay-show="!hasSingleSignin"> <cor-tab-pane id="external" quay-show="!hasSingleSignin">
<div class="external-logins-manager" user="viewuser" <div class="external-logins-manager" user="viewuser"
quay-show="!hasSingleSignin"></div> quay-show="!hasSingleSignin"></div>
<div style="margin: 50px" quay-show="!hasSingleSignin"> <div style="margin: 50px" quay-show="!hasSingleSignin">
</div> </div>
<div class="authorized-apps-manager" user="viewuser" is-enabled="showAppsCounter"></div> <div class="authorized-apps-manager" user="viewuser" is-enabled="showAppsCounter"></div>
</div> </cor-tab-pane>
<!-- Settings --> <!-- Settings -->
<div id="settings" class="tab-pane"> <cor-tab-pane id="settings">
<!-- Encrypted Password --> <!-- Encrypted Password -->
<div class="settings-section"> <div class="settings-section">
<h3>Docker CLI Password</h3> <h3>Docker CLI Password</h3>
@ -133,21 +133,20 @@
</td> </td>
</tr> </tr>
</table> </table>
<div class="delete-namespace-view" subscription-status="subscriptionStatus" user="context.viewuser" namespace-title="account" quay-show="Config.AUTHENTICATION_TYPE == 'Database'"></div>
<time-machine-settings user="context.viewuser"></time-machine-settings>
</div> </div>
<div class="delete-namespace-view" subscription-status="subscriptionStatus" user="context.viewuser" namespace-title="account" quay-show="Config.AUTHENTICATION_TYPE == 'Database'"></div>
<time-machine-settings user="context.viewuser"></time-machine-settings>
<!-- Billing Information --> <!-- Billing Information -->
<div class="settings-section" quay-show="Features.BILLING"> <div class="settings-section" quay-show="Features.BILLING">
<h3>Billing Information</h3> <h3>Billing Information</h3>
<div class="billing-management-panel" user="context.viewuser" is-enabled="showBillingCounter" subscription-status="subscriptionStatus"></div> <div class="billing-management-panel" user="context.viewuser" is-enabled="showBillingCounter" subscription-status="subscriptionStatus"></div>
</div> </div>
</cor-tab-pane>
</div> <!-- /cor-tab-content --> </cor-tab-content>
</div> </cor-tab-panel>
</div>
<!-- Change email dialog --> <!-- Change email dialog -->
<div class="cor-confirm-dialog" <div class="cor-confirm-dialog"