mirror of https://github.com/rancher/ui.git
commit
049abae695
|
|
@ -21,6 +21,11 @@ export default Ember.Component.extend({
|
||||||
click(e) {
|
click(e) {
|
||||||
var tgt = Ember.$(e.target);
|
var tgt = Ember.$(e.target);
|
||||||
var more = tgt.closest('.more-actions');
|
var more = tgt.closest('.more-actions');
|
||||||
|
var offsets = {
|
||||||
|
y: -1,
|
||||||
|
x: 2,
|
||||||
|
mirror: true
|
||||||
|
};
|
||||||
if ( more && more.length ) {
|
if ( more && more.length ) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -31,7 +36,7 @@ export default Ember.Component.extend({
|
||||||
this.get('resourceActions').set('tooltipActions', false);
|
this.get('resourceActions').set('tooltipActions', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get('resourceActions').show(this.get('model'), more, this.$());
|
this.get('resourceActions').show(this.get('model'), more, this.$(), offsets);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import NewOrEdit from 'ui/mixins/new-or-edit';
|
import NewOrEdit from 'ui/mixins/new-or-edit';
|
||||||
import C from 'ui/utils/constants';
|
import C from 'ui/utils/constants';
|
||||||
|
import StackState from 'ui/mixins/stack-memory';
|
||||||
|
|
||||||
export default Ember.Component.extend(NewOrEdit, {
|
export default Ember.Component.extend(NewOrEdit, StackState, {
|
||||||
intl : Ember.inject.service(),
|
intl : Ember.inject.service(),
|
||||||
settings : Ember.inject.service(),
|
settings : Ember.inject.service(),
|
||||||
|
prefs: Ember.inject.service(),
|
||||||
|
|
||||||
service : null,
|
service : null,
|
||||||
editing : null,
|
editing : null,
|
||||||
|
|
@ -252,6 +254,7 @@ export default Ember.Component.extend(NewOrEdit, {
|
||||||
},
|
},
|
||||||
|
|
||||||
doneSaving() {
|
doneSaving() {
|
||||||
|
this._super(...arguments);
|
||||||
this.send('done');
|
this.send('done');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import NewOrEdit from 'ui/mixins/new-or-edit';
|
import NewOrEdit from 'ui/mixins/new-or-edit';
|
||||||
import Errors from 'ui/utils/errors';
|
import Errors from 'ui/utils/errors';
|
||||||
|
import StackState from 'ui/mixins/stack-memory';
|
||||||
|
|
||||||
const HOSTNAME = 'externalhostname';
|
const HOSTNAME = 'externalhostname';
|
||||||
const IP = 'externalip';
|
const IP = 'externalip';
|
||||||
|
|
@ -15,7 +16,7 @@ function modeToType(mode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Ember.Component.extend(NewOrEdit, {
|
export default Ember.Component.extend(NewOrEdit, StackState, {
|
||||||
intl: Ember.inject.service(),
|
intl: Ember.inject.service(),
|
||||||
|
|
||||||
record: null,
|
record: null,
|
||||||
|
|
@ -156,6 +157,7 @@ export default Ember.Component.extend(NewOrEdit, {
|
||||||
},
|
},
|
||||||
|
|
||||||
doneSaving() {
|
doneSaving() {
|
||||||
this.send('cancel');
|
this._super(...arguments);
|
||||||
|
this.send('done');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,31 @@
|
||||||
{{~#if project~}}
|
{{~#if project~}}
|
||||||
<li class="dropdown pull-right ">
|
<li class="dropdown pull-right ">
|
||||||
{{#if (gt byCluster.length 1)}}
|
{{#if (gt byCluster.length 1)}}
|
||||||
<ul class="list-unstyled pr-40" style="padding: 4px;">
|
<div class="cluster-dropdown dropdown-toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{t 'nav.cluster.label'}}" data-toggle="environment">
|
||||||
<li>
|
<span class="text-white clip" >
|
||||||
<a href="#" role="button" class="text-white dropdown-toggle clip" aria-haspopup="true" aria-expanded="false" aria-label="{{t 'nav.cluster.label'}}">
|
|
||||||
<i class="icon icon-cluster icon-fw"></i>
|
<i class="icon icon-cluster icon-fw"></i>
|
||||||
{{project.cluster.displayName}}
|
{{project.cluster.displayName}}
|
||||||
<span class="sr-only">{{t 'nav.srToggleDropdown'}}</span>
|
<span class="sr-only">{{t 'nav.srToggleDropdown'}}</span>
|
||||||
</a>
|
</span>
|
||||||
</li>
|
<span class="block pl-15 text-small text-muted">
|
||||||
|
|
||||||
<li class="pl-15 text-small text-muted">
|
|
||||||
<a href="#" role="button" class="text-white dropdown-toggle clip" aria-haspopup="true" aria-expanded="false" aria-label="{{t 'nav.environment.label'}}">
|
|
||||||
<i class="{{project.icon}} project-icon icon-fw"></i>
|
<i class="{{project.icon}} project-icon icon-fw"></i>
|
||||||
{{project.displayName}}
|
{{project.displayName}}
|
||||||
<span class="sr-only">{{t 'nav.srToggleDropdown'}}</span>
|
<span class="sr-only">{{t 'nav.srToggleDropdown'}}</span>
|
||||||
</a>
|
</span>
|
||||||
</li>
|
|
||||||
|
|
||||||
<i class="icon icon-chevron-down project-chevron ml-10 text-white" style="position: absolute; right: 20px; top: 20px;"></i>
|
<i class="icon icon-chevron-down project-chevron ml-10 text-white" style="position: absolute; right: 20px; top: 20px;"></i>
|
||||||
</ul>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="environment-dropdown">
|
<span class="environment-dropdown dropdown-toggle" aria-role="button" aria-haspopup="true" aria-expanded="false" aria-label="{{t 'nav.environment.label'}}" data-toggle="environment">
|
||||||
<i class="{{project.icon}} project-icon icon-fw"></i>
|
<i class="{{project.icon}} project-icon icon-fw"></i>
|
||||||
<a href="#" role="button" class="dropdown-toggle clip" aria-haspopup="true" aria-expanded="false" aria-label="{{t 'nav.environment.label'}}">
|
<span class="text-white clip" >
|
||||||
{{project.displayName}}
|
{{project.displayName}}
|
||||||
<span class="sr-only">{{t 'nav.srToggleDropdown'}}</span>
|
<span class="sr-only">{{t 'nav.srToggleDropdown'}}</span>
|
||||||
</a>
|
</span>
|
||||||
<i class="icon icon-chevron-down project-chevron"></i>
|
<i class="icon icon-chevron-down project-chevron"></i>
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<ul class="dropdown-menu project-menu" role="menu" data-dropdown-id="enviroment">
|
<ul class="dropdown-menu dropdown-menu-right project-menu" role="menu" data-dropdown-id="environment">
|
||||||
{{#if (gt byCluster.length 1)}}
|
{{#if (gt byCluster.length 1)}}
|
||||||
{{#each byCluster as |entry|}}
|
{{#each byCluster as |entry|}}
|
||||||
<li class="{{if entry.system.active 'active selected'}}">
|
<li class="{{if entry.system.active 'active selected'}}">
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,14 @@ export default Ember.Component.extend(HoverDropdown, {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
willRender() {
|
||||||
|
if (Ember.$('BODY').hasClass('touch') && Ember.$('header > nav').hasClass('nav-open')) {
|
||||||
|
Ember.run.later(() => {
|
||||||
|
Ember.$('header > nav').removeClass('nav-open');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
this.get('intl.locale');
|
this.get('intl.locale');
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
|
|
||||||
<ul class="nav-user dropdown">
|
<ul class="nav-user dropdown">
|
||||||
<li>
|
<li>
|
||||||
<a class="btn dropdown-toggle" style="height: 51px; padding-top: 8px;" role="button" aria-haspopup="true" aria-expanded="false" aria-label={{t 'nav.user.label' username=session.user}}>
|
<a class="btn dropdown-toggle" style="height: 51px; padding-top: 8px;" role="button" aria-haspopup="true" aria-expanded="false" aria-label="{{t 'nav.user.label' username=session.user}}" data-toggle="header-user-menu" >
|
||||||
{{#if accessEnabled}}
|
{{#if accessEnabled}}
|
||||||
{{identity-avatar link=false identity=access.identity}}
|
{{identity-avatar link=false identity=access.identity}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
||||||
|
|
@ -452,7 +452,6 @@ export default Ember.Component.extend(Sortable, StickyHeader, {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set('prevNode', node);
|
this.set('prevNode', node);
|
||||||
e.stopPropagation();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
nodesBetween(a,b) {
|
nodesBetween(a,b) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,13 @@ let timerObj = null;
|
||||||
let dropdown = null;
|
let dropdown = null;
|
||||||
|
|
||||||
export default Ember.Mixin.create({
|
export default Ember.Mixin.create({
|
||||||
|
willRender(){
|
||||||
|
if (Ember.$('BODY').hasClass('touch')) {
|
||||||
|
Ember.run.later(() => {
|
||||||
|
this.clearHeaderMenus();
|
||||||
|
}, DROPDOWNCLOSETIMER);
|
||||||
|
}
|
||||||
|
},
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
let $body = Ember.$('BODY');
|
let $body = Ember.$('BODY');
|
||||||
|
|
||||||
|
|
@ -24,7 +31,7 @@ export default Ember.Mixin.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$('#environment-dropdown').on('touchstart', (e) => {
|
this.$('[data-toggle="header-user-menu"]').on('touchstart', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.touchHandler(e);
|
this.touchHandler(e);
|
||||||
|
|
@ -111,7 +118,20 @@ export default Ember.Mixin.create({
|
||||||
},
|
},
|
||||||
|
|
||||||
enterHandler(e) {
|
enterHandler(e) {
|
||||||
let anchor = Ember.$(e.currentTarget).find('a:first');
|
let anchor = Ember.$(e.currentTarget).find('.dropdown-toggle');
|
||||||
|
let dataTarget = anchor.data('toggle') ? anchor.data('toggle') : null;
|
||||||
|
let findTarget = dataTarget ? `ul[data-dropdown-id ="${dataTarget}"]` : 'ul';
|
||||||
|
let offset = null;
|
||||||
|
|
||||||
|
if (anchor.data('offset-x') || anchor.data('offset-y')) {
|
||||||
|
offset = {};
|
||||||
|
if (anchor.data('offset-x')) {
|
||||||
|
offset.x = anchor.data('offset-x');
|
||||||
|
}
|
||||||
|
if (anchor.data('offset-y')) {
|
||||||
|
offset.y = anchor.data('offset-y');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ember.run.cancel(timerObj);
|
Ember.run.cancel(timerObj);
|
||||||
|
|
||||||
|
|
@ -126,15 +146,15 @@ export default Ember.Mixin.create({
|
||||||
dropdown = Ember.$(e.currentTarget).find('ul');
|
dropdown = Ember.$(e.currentTarget).find('ul');
|
||||||
|
|
||||||
if (dropdown) {
|
if (dropdown) {
|
||||||
this.showMenu(anchor, dropdown);
|
this.showMenu(anchor, dropdown, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // no dropdown open
|
} else { // no dropdown open
|
||||||
|
|
||||||
dropdown = Ember.$(e.currentTarget).find('ul');
|
dropdown = Ember.$(e.currentTarget).find(findTarget);
|
||||||
|
|
||||||
if (dropdown) {
|
if (dropdown) {
|
||||||
this.showMenu(anchor, dropdown);
|
this.showMenu(anchor, dropdown, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,15 +244,10 @@ export default Ember.Mixin.create({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
showMenu: function(el, drpd) {
|
showMenu: function(el, drpd, offset) {
|
||||||
let body = Ember.$('BODY');
|
|
||||||
if (body.hasClass('touch')) {
|
|
||||||
Ember.$('BODY').addClass('nav-dropdown-open');
|
|
||||||
}
|
|
||||||
|
|
||||||
drpd.addClass('invisible');
|
drpd.addClass('invisible');
|
||||||
drpd.addClass('block');
|
drpd.addClass('block');
|
||||||
positionDropdown(drpd, el, drpd.hasClass('dropdown-menu-right'));
|
positionDropdown(drpd, el, drpd.hasClass('dropdown-menu-right'), offset);
|
||||||
drpd.removeClass('invisible');
|
drpd.removeClass('invisible');
|
||||||
|
|
||||||
if (el.attr('aria-expanded')) {
|
if (el.attr('aria-expanded')) {
|
||||||
|
|
@ -241,10 +256,6 @@ export default Ember.Mixin.create({
|
||||||
},
|
},
|
||||||
|
|
||||||
clearHeaderMenus: function() {
|
clearHeaderMenus: function() {
|
||||||
let body = Ember.$('BODY');
|
|
||||||
if (body.hasClass('touch')) {
|
|
||||||
Ember.$('BODY').removeClass('nav-dropdown-open');
|
|
||||||
}
|
|
||||||
const navbar = Ember.$(PARENT);
|
const navbar = Ember.$(PARENT);
|
||||||
|
|
||||||
dropdown = null;
|
dropdown = null;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
import C from 'ui/utils/constants';
|
||||||
|
|
||||||
|
export default Ember.Mixin.create({
|
||||||
|
prefs: Ember.inject.service(),
|
||||||
|
stack: null,
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.setStack();
|
||||||
|
},
|
||||||
|
setStack() {
|
||||||
|
let stackId = this.get(`prefs.${C.PREFS.LAST_STACK}`) || null;
|
||||||
|
if (stackId) {
|
||||||
|
this.set('stack', this.get('store').getById('stack', stackId));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
doneSaving() {
|
||||||
|
if (this.get('stack')) {
|
||||||
|
this.set(`prefs.${C.PREFS.LAST_STACK}`, this.get('stack.id'));
|
||||||
|
}
|
||||||
|
this.send('done');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -2,6 +2,7 @@ import Ember from 'ember';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
application: Ember.inject.controller(),
|
application: Ember.inject.controller(),
|
||||||
|
projects: Ember.inject.service(),
|
||||||
|
|
||||||
service: Ember.computed.alias('model.service'),
|
service: Ember.computed.alias('model.service'),
|
||||||
rules: Ember.computed.alias('service.lbConfig.portRules'),
|
rules: Ember.computed.alias('service.lbConfig.portRules'),
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ export default Ember.Route.extend({
|
||||||
if (model.get('service.initPorts')) {
|
if (model.get('service.initPorts')) {
|
||||||
model.get('service').initPorts();
|
model.get('service').initPorts();
|
||||||
}
|
}
|
||||||
|
if (model.get('service.stackId')) {
|
||||||
|
model.set('stack', this.get('store').getById('stack', model.get('service.stackId')));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getServiceLogs(serviceId) {
|
getServiceLogs(serviceId) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,28 @@
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="row banner bg-info basics">
|
<div class="row banner bg-info basics">
|
||||||
|
{{#if service.externalIpAddresses}}
|
||||||
|
<div class="inline-block">
|
||||||
|
<label class="text-muted ml-15">{{t 'servicePage.external.externalIp' count=service.externalIpAddresses.length}}</label>
|
||||||
|
{{join-array service.externalIpAddresses}}
|
||||||
|
</div>
|
||||||
|
{{else if service.hostname}}
|
||||||
|
<div class="inline-block">
|
||||||
|
<label class="text-muted">{{t 'servicePage.external.externalHostname'}} </label> {{service.hostname}}
|
||||||
|
</div>
|
||||||
|
{{else if service.selector}}
|
||||||
|
<div class="inline-block">
|
||||||
|
<label class="text-muted">{{t 'servicePage.selector.label'}} </label> {{service.selector}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if service.hasImage}}
|
||||||
|
<div class="inline-block">
|
||||||
|
<label class="acc-label p-0">{{t 'servicePage.multistat.image'}}</label>
|
||||||
|
{{fixedLaunchConfig.image}} {{copy-to-clipboard clipboardText=fixedLaunchConfig.image size="small"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if fixedLaunchConfig.memoryReservation}}
|
{{#if fixedLaunchConfig.memoryReservation}}
|
||||||
<div class="inline-block">
|
<div class="inline-block">
|
||||||
<label class="acc-label p-0">{{t 'containersPage.containerPage.infoMultiStats.memoryReservation.labelText'}}</label>
|
<label class="acc-label p-0">{{t 'containersPage.containerPage.infoMultiStats.memoryReservation.labelText'}}</label>
|
||||||
|
|
@ -57,27 +79,14 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if service.hasImage}}
|
|
||||||
|
{{#if model.stack}}
|
||||||
<div class="inline-block">
|
<div class="inline-block">
|
||||||
<label class="acc-label p-0">{{t 'servicePage.multistat.image'}}</label>
|
<label class="acc-label p-0">{{t 'generic.stack'}}:</label>
|
||||||
{{fixedLaunchConfig.image}} {{copy-to-clipboard clipboardText=fixedLaunchConfig.image size="small"}}
|
{{#link-to "stack" projects.current.id model.stack.id}}{{model.stack.displayName}}{{/link-to}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if service.externalIpAddresses}}
|
|
||||||
<div class="inline-block">
|
|
||||||
<label class="text-muted ml-15">{{t 'servicePage.external.externalIp' count=service.externalIpAddresses.length}}</label>
|
|
||||||
{{join-array service.externalIpAddresses}}
|
|
||||||
</div>
|
|
||||||
{{else if service.hostname}}
|
|
||||||
<div class="inline-block">
|
|
||||||
<label class="text-muted">{{t 'servicePage.external.externalHostname'}} </label> {{service.hostname}}
|
|
||||||
</div>
|
|
||||||
{{else if service.selector}}
|
|
||||||
<div class="inline-block">
|
|
||||||
<label class="text-muted">{{t 'servicePage.selector.label'}} </label> {{service.selector}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,16 @@ export default Ember.Service.extend({
|
||||||
actionToggle : null,
|
actionToggle : null,
|
||||||
actionMenu : null,
|
actionMenu : null,
|
||||||
|
|
||||||
show: function(model,trigger,toggle) {
|
// offset is a parameter that we need to open in our api, it allows us to pass a
|
||||||
|
// positial x/y offset to the position-calculator library
|
||||||
|
// http://tlindig.github.io/position-calculator/
|
||||||
|
// itemOffset: {
|
||||||
|
// y: -1,
|
||||||
|
// x: 2,
|
||||||
|
// mirror: true
|
||||||
|
// },
|
||||||
|
|
||||||
|
show: function(model,trigger,toggle, offset) {
|
||||||
if (this.get('open') && this.get('actionMenu')) {
|
if (this.get('open') && this.get('actionMenu')) {
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +61,7 @@ export default Ember.Service.extend({
|
||||||
this.set('open',true);
|
this.set('open',true);
|
||||||
// Delay ensure it works in firefox
|
// Delay ensure it works in firefox
|
||||||
Ember.run.next(() => {
|
Ember.run.next(() => {
|
||||||
positionDropdown($menu, trigger, true);
|
positionDropdown($menu, trigger, true, offset);
|
||||||
$('#resource-actions-first')[0].focus();
|
$('#resource-actions-first')[0].focus();
|
||||||
$menu.css('visibility','visible');
|
$menu.css('visibility','visible');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import { headersWithHost as containerHeaders } from 'ui/components/container-table/component';
|
import { headersWithHost as containerHeaders } from 'ui/components/container-table/component';
|
||||||
|
import { searchFields as containerSearchFields } from 'ui/components/container-dots/component';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
prefs: Ember.inject.service(),
|
prefs: Ember.inject.service(),
|
||||||
|
|
@ -99,11 +100,68 @@ export default Ember.Controller.extend({
|
||||||
name: 'scale',
|
name: 'scale',
|
||||||
sort: ['scale:desc','isGlobalScale:desc','displayName'],
|
sort: ['scale:desc','isGlobalScale:desc','displayName'],
|
||||||
searchField: null,
|
searchField: null,
|
||||||
width: 100,
|
|
||||||
translationKey: 'stacksPage.table.scale',
|
translationKey: 'stacksPage.table.scale',
|
||||||
classNames: 'text-center',
|
classNames: 'text-center',
|
||||||
|
width: 100
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
storageSortBy: 'state',
|
||||||
|
storageHeaders: [
|
||||||
|
{
|
||||||
|
name: 'expand',
|
||||||
|
sort: false,
|
||||||
|
searchField: null,
|
||||||
|
width: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'state',
|
||||||
|
sort: ['stateSort','displayName'],
|
||||||
|
searchField: 'displayState',
|
||||||
|
translationKey: 'generic.state',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
sort: ['displayName','id'],
|
||||||
|
searchField: 'displayName',
|
||||||
|
translationKey: 'generic.name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mounts',
|
||||||
|
sort: ['mounts.length','displayName','id'],
|
||||||
|
translationKey: 'volumesPage.mounts.label',
|
||||||
|
searchField: null,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'scope',
|
||||||
|
sort: ['scope'],
|
||||||
|
translationKey: 'volumesPage.scope.label',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'driver',
|
||||||
|
sort: ['driver','displayName','id'],
|
||||||
|
searchField: 'displayType',
|
||||||
|
translationKey: 'volumesPage.driver.label',
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
extraSearchFields: ['id:prefix','displayIp:ip'],
|
||||||
|
extraSearchSubFields: containerSearchFields,
|
||||||
|
rows: Ember.computed('instances.[]', 'scalingGroups.[]', function() {
|
||||||
|
let out = [];
|
||||||
|
let containers = this.get('instances');
|
||||||
|
let scalinggroups = this.get('scalingGroups');
|
||||||
|
return out.concat(containers, scalinggroups);
|
||||||
|
}),
|
||||||
|
|
||||||
|
containerStats: Ember.computed('instances.[]', 'scalingGroups.[]', function() {
|
||||||
|
let containerLength = this.get('instances.length') || 0;
|
||||||
|
let scalingGroupsLength = this.get('scalingGroups.length') || 0;
|
||||||
|
return containerLength += scalingGroupsLength;
|
||||||
|
}),
|
||||||
|
|
||||||
getType(ownType, real=true) {
|
getType(ownType, real=true) {
|
||||||
return this.get('model.services').filter((service) => {
|
return this.get('model.services').filter((service) => {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,27 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
|
parentRoute: 'stack',
|
||||||
|
|
||||||
resetController: function (controller, isExisting/*, transition*/) {
|
resetController: function (controller, isExisting/*, transition*/) {
|
||||||
if (isExisting)
|
if (isExisting)
|
||||||
{
|
{
|
||||||
controller.set('showAddtlInfo', false);
|
controller.set('showAddtlInfo', false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
model(/* params, transition */) {
|
||||||
|
let model = this.modelFor(this.get('parentRoute'));
|
||||||
|
|
||||||
|
return this.get('store').findAll('volume').then((volumes) => {
|
||||||
|
return this.get('store').findAll('volumetemplate').then((volumeTemplates) => {
|
||||||
|
|
||||||
|
let volOut = [];
|
||||||
|
|
||||||
|
model.volumes = volOut.concat(volumes.filterBy('stackId', model.get('stack.id')), volumeTemplates.filterBy('stackId', model.get('stack.id')));
|
||||||
|
|
||||||
|
return model;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,63 @@
|
||||||
{{stack-header model=model.stack all=model.all.stacks}}
|
{{stack-header model=model.stack all=model.all.stacks}}
|
||||||
|
|
||||||
|
{{#if model.stack.description}}
|
||||||
|
{{banner-message color='bg-secondary mb-0 mt-10' message=model.stack.description}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
{{#accordion-list as |al expandFn| }}
|
{{#accordion-list as |al expandFn| }}
|
||||||
{{#accordion-list-item
|
{{#accordion-list-item
|
||||||
title=(t 'stackPage.containers.header')
|
title=(t 'stackPage.containers.header')
|
||||||
detail=(t 'stackPage.containers.detail')
|
detail=(t 'stackPage.containers.detail')
|
||||||
status=(t 'stackPage.containers.status' count=instances.length)
|
status=(t 'stackPage.containers.status' count=instances.length)
|
||||||
statusClass=(if instances.length 'bg-success' 'text-muted')
|
statusClass=(if containerStats 'bg-success' 'text-muted')
|
||||||
|
expandOnInit=true
|
||||||
expandAll=al.expandAll
|
expandAll=al.expandAll
|
||||||
expand=(action expandFn)
|
expand=(action expandFn)
|
||||||
componentName='container-table'
|
componentName='container-table'
|
||||||
as | parent |
|
as | parent |
|
||||||
}}
|
}}
|
||||||
{{component parent.intent
|
{{#sortable-table
|
||||||
body=instances
|
tableClassNames="double-rows"
|
||||||
search=true
|
|
||||||
sortBy=sortBy
|
|
||||||
stickyHeader=false
|
|
||||||
bulkActions=true
|
|
||||||
showHost=false
|
|
||||||
}}
|
|
||||||
{{/accordion-list-item}}
|
|
||||||
|
|
||||||
{{#accordion-list-item
|
|
||||||
title=(t 'stackPage.scalingGroups.header')
|
|
||||||
detail=(t 'stackPage.scalingGroups.detail')
|
|
||||||
status=(t 'stackPage.scalingGroups.status' count=scalingGroups.length)
|
|
||||||
statusClass=(if scalingGroups.length 'bg-success' 'text-muted')
|
|
||||||
expandAll=al.expandAll
|
|
||||||
expand=(action expandFn)
|
|
||||||
componentName='sortable-table'
|
|
||||||
as | parent |
|
|
||||||
}}
|
|
||||||
{{#component parent.intent
|
|
||||||
body=scalingGroups
|
|
||||||
bulkActions=true
|
|
||||||
classNames="grid sortable-table"
|
classNames="grid sortable-table"
|
||||||
fullRows=true
|
body=rows
|
||||||
headers=sgHeaders
|
|
||||||
pagingLabel="pagination.service"
|
|
||||||
searchText=searchText
|
searchText=searchText
|
||||||
sortBy=sortBy
|
sortBy=sortBy
|
||||||
stickyHeader=false
|
bulkActions=true
|
||||||
subHeaders=containerHeaders
|
|
||||||
subRows=true
|
subRows=true
|
||||||
|
fullRows=true
|
||||||
|
pagingLabel="pagination.containerService"
|
||||||
subSearchField="instances"
|
subSearchField="instances"
|
||||||
as |sortable kind serv dt|}}
|
extraSearchFields=extraSearchFields
|
||||||
|
extraSearchSubFields=extraSearchSubFields
|
||||||
|
headers=sgHeaders as |sortable kind inst dt|}}
|
||||||
{{#if (eq kind "row")}}
|
{{#if (eq kind "row")}}
|
||||||
{{service-row
|
{{#if (eq inst.baseType "instance")}}
|
||||||
canExpand=true
|
{{container-row
|
||||||
expanded=(array-includes expandedInstances serv.id)
|
model=inst
|
||||||
|
dt=dt
|
||||||
|
showHost=true
|
||||||
|
expandPlaceholder=true
|
||||||
|
scalePlaceholder=true
|
||||||
fullColspan=sortable.fullColspan
|
fullColspan=sortable.fullColspan
|
||||||
model=serv
|
}}
|
||||||
|
{{else}}
|
||||||
|
{{service-row
|
||||||
|
model=inst
|
||||||
|
toggle=(action "toggleExpand" inst.id)
|
||||||
|
expanded=(array-includes expandedInstances inst.id)
|
||||||
searchText=searchText
|
searchText=searchText
|
||||||
showInstanceCount=false
|
|
||||||
subMatches=sortable.subMatches
|
subMatches=sortable.subMatches
|
||||||
toggle=(action "toggleExpand" serv.id)
|
fullColspan=sortable.fullColspan
|
||||||
dt=dt
|
dt=dt
|
||||||
}}
|
}}
|
||||||
{{else if (eq kind "nomatch")}}
|
|
||||||
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted pt-20 pb-20">{{t 'containersPage.table.noMatch'}}</td></tr>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/component}}
|
{{else if (eq kind "nomatch")}}
|
||||||
|
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'containersPage.table.noMatch'}}</td></tr>
|
||||||
|
{{else if (eq kind "norows")}}
|
||||||
|
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted lacsso pt-20 pb-20">{{t 'containersPage.table.noData'}}</td></tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/sortable-table}}
|
||||||
{{/accordion-list-item}}
|
{{/accordion-list-item}}
|
||||||
|
|
||||||
{{#accordion-list-item
|
{{#accordion-list-item
|
||||||
|
|
@ -144,5 +140,48 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/component}}
|
{{/component}}
|
||||||
{{/accordion-list-item}}
|
{{/accordion-list-item}}
|
||||||
|
|
||||||
|
{{#accordion-list-item
|
||||||
|
title=(t 'stackPage.volumesTab.header')
|
||||||
|
detail=(t 'stackPage.volumesTab.detail')
|
||||||
|
status=(t 'pagination.volume' pages=1 count=(or model.volumes.length 0))
|
||||||
|
statusClass=(if model.volumes.length 'bg-success' 'text-muted')
|
||||||
|
expandAll=al.expandAll
|
||||||
|
expand=(action expandFn)
|
||||||
|
componentName='sortable-table'
|
||||||
|
as | parent |
|
||||||
|
}}
|
||||||
|
{{#component parent.intent
|
||||||
|
body=model.volumes
|
||||||
|
bulkActions=true
|
||||||
|
classNames="grid sortable-table"
|
||||||
|
fullRows=true
|
||||||
|
isVisible=parent.expanded
|
||||||
|
pagingLabel="pagination.volume"
|
||||||
|
searchText=searchText
|
||||||
|
sortBy=sortBy
|
||||||
|
stickyHeader=false
|
||||||
|
subHeaders=containerHeaders
|
||||||
|
subRows=true
|
||||||
|
subSearchField="instances"
|
||||||
|
headers=storageHeaders as |sortable kind mount dt|
|
||||||
|
}}
|
||||||
|
{{#if (eq kind "row")}}
|
||||||
|
{{volume-row
|
||||||
|
model=mount
|
||||||
|
toggle=(action "toggleExpand" mount.id)
|
||||||
|
expanded=(array-includes expandedInstances mount.id)
|
||||||
|
searchText=searchText
|
||||||
|
subMatches=sortable.subMatches
|
||||||
|
fullColspan=sortable.fullColspan
|
||||||
|
dt=dt
|
||||||
|
}}
|
||||||
|
{{else if (eq kind "nomatch")}}
|
||||||
|
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted pt-20 pb-20">{{t 'stackPage.volumesTab.table.noMatch'}}</td></tr>
|
||||||
|
{{else if (eq kind "norows")}}
|
||||||
|
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted pt-20 pb-20">{{t 'stackPage.volumesTab.table.noData'}}</td></tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/component}}
|
||||||
|
{{/accordion-list-item}}
|
||||||
{{/accordion-list}}
|
{{/accordion-list}}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,15 @@ $user-btn : darken($header, 10%) !default;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cluster-dropdown {
|
||||||
|
padding: 4px 40px 4px 4px;
|
||||||
|
@extend .clip;
|
||||||
|
@extend .hand;
|
||||||
|
}
|
||||||
.environment-dropdown {
|
.environment-dropdown {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
|
@extend .clip;
|
||||||
|
@extend .hand;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,6 @@
|
||||||
//nav
|
//nav
|
||||||
.responsive-nav {
|
.responsive-nav {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
|
||||||
background-color: $header;
|
background-color: $header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,7 +201,9 @@
|
||||||
}
|
}
|
||||||
/* Class added via JS when toggled open */
|
/* Class added via JS when toggled open */
|
||||||
.nav-open {
|
.nav-open {
|
||||||
overflow: visible;
|
.nav-list {
|
||||||
|
max-height: 1000px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NAV LIST*/
|
/* NAV LIST*/
|
||||||
|
|
@ -213,6 +214,13 @@
|
||||||
border-bottom-right-radius: 3px;
|
border-bottom-right-radius: 3px;
|
||||||
border-bottom-left-radius: 3px;
|
border-bottom-left-radius: 3px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-transition: max-height .5s;
|
||||||
|
-moz-transition: max-height .5s;
|
||||||
|
-ms-transition: max-height .5s;
|
||||||
|
-o-transition: max-height .5s;
|
||||||
|
transition: max-height .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -284,7 +292,7 @@
|
||||||
background-size: 75%;
|
background-size: 75%;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
.page-header NAV .dropdown-menu {
|
.page-header NAV .nav-list .dropdown-menu {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
.dot {
|
.dot {
|
||||||
float: left;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ export function resizeDropdown(event) {
|
||||||
return positionDropdown($item, target, right);
|
return positionDropdown($item, target, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function positionDropdown(menu, trigger, right) {
|
export function positionDropdown(menu, trigger, right, offset) {
|
||||||
// https://github.com/twbs/bootstrap/issues/10756#issuecomment-41041800
|
// https://github.com/twbs/bootstrap/issues/10756#issuecomment-41041800
|
||||||
var direction = (right === true ? 'right' : 'left');
|
var direction = (right === true ? 'right' : 'left');
|
||||||
var $menu = $(menu);
|
var $menu = $(menu);
|
||||||
|
|
@ -17,19 +17,20 @@ export function positionDropdown(menu, trigger, right) {
|
||||||
left: 0
|
left: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// calculate new position
|
let pco = {
|
||||||
var calculator = new $.PositionCalculator({
|
|
||||||
item: $menu,
|
item: $menu,
|
||||||
target: trigger,
|
target: trigger,
|
||||||
itemAt: 'top ' + direction,
|
itemAt: 'top ' + direction,
|
||||||
itemOffset: {
|
|
||||||
y: 3,
|
|
||||||
x: 0,
|
|
||||||
mirror: true
|
|
||||||
},
|
|
||||||
targetAt: 'bottom ' + direction,
|
targetAt: 'bottom ' + direction,
|
||||||
flip: 'both'
|
flip: 'both'
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
pco.itemOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate new position
|
||||||
|
var calculator = new $.PositionCalculator(pco);
|
||||||
var posResult = calculator.calculate();
|
var posResult = calculator.calculate();
|
||||||
|
|
||||||
// set new position
|
// set new position
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,19 @@ export default Ember.Route.extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
model: function(params) {
|
model: function(params) {
|
||||||
|
|
||||||
let out = Ember.Object.create({
|
let out = Ember.Object.create({
|
||||||
volume: this.get('store').getById(params.type, params.volume_id)
|
volume: this.get('store').getById(params.type, params.volume_id)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (out.volume.stackId) {
|
if (out.volume.stackId) {
|
||||||
out.stack = this.controllerFor('volume').set('stack', this.get('store').getById('stack', out.volume.stackId));
|
out.stack = this.get('store').getById('stack', out.volume.stackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out.volume.hostId) {
|
if (out.volume.hostId) {
|
||||||
out.host = this.controllerFor('volume').set('host', this.get('store').getById('host', out.volume.hostId));
|
out.host = this.get('store').getById('host', out.volume.hostId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -922,7 +922,7 @@ stackPage:
|
||||||
backLink: Back to all stacks
|
backLink: Back to all stacks
|
||||||
containers:
|
containers:
|
||||||
header: Containers
|
header: Containers
|
||||||
detail: Standalone Containers that are not part of a Service or Load Balancer
|
detail: Standalone Containers and Services contained in this stack
|
||||||
status: |
|
status: |
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {No containers}
|
=0 {No containers}
|
||||||
|
|
@ -956,6 +956,15 @@ stackPage:
|
||||||
=1 {# container}
|
=1 {# container}
|
||||||
other {# containers}
|
other {# containers}
|
||||||
}
|
}
|
||||||
|
volumesTab:
|
||||||
|
header: Volumes
|
||||||
|
detail: 'These properties show the volumes attached to your container.'
|
||||||
|
table:
|
||||||
|
path: Mount Point
|
||||||
|
shared: Shared With
|
||||||
|
writable: Writable
|
||||||
|
noData: This stack has no volumes mounted
|
||||||
|
noMatch: No volumes match the current search
|
||||||
|
|
||||||
newStack:
|
newStack:
|
||||||
header: Import Compose.yml
|
header: Import Compose.yml
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue