ui: Outlet Loading (#8779)

Moves all Route/model hook loading to use Outlet components which listen on route change.
This commit is contained in:
John Cowen 2020-10-01 09:33:22 +01:00 committed by GitHub
parent ccd0200bd9
commit c0abe1e406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 467 additions and 296 deletions

View File

@ -1,90 +1,90 @@
{{yield}}
<header>
{{#each flashMessages.queue as |flash|}}
<FlashMessage @flash={{flash}} as |component flash|>
{{#if flash.dom}}
{{{flash.dom}}}
{{else}}
{{#let (lowercase component.flashType) (lowercase flash.action) as |status type|}}
{{! flashes automatically ucfirst the type }}
<p data-notification role="alert" class={{concat status ' notification-' type}}>
<strong>
{{capitalize status}}!
</strong>
{{#yield-slot name="notification" params=(block-params status type flash.item flash.error)}}
{{yield}}
{{#if (eq type 'logout')}}
{{#if (eq status 'success') }}
You are now logged out.
{{else}}
There was an error logging out.
{{/if}}
{{else if (eq type 'authorize')}}
{{#if (eq status 'success') }}
You are now logged in.
{{else}}
There was an error, please check your SecretID/Token
{{/if}}
{{/if}}
{{else}}
{{#if (eq type 'logout')}}
{{#if (eq status 'success') }}
You are now logged out.
{{else}}
There was an error logging out.
{{/if}}
{{else if (eq type 'authorize')}}
{{#if (eq status 'success') }}
You are now logged in.
{{else}}
There was an error, please check your SecretID/Token
{{/if}}
{{/if}}
{{/yield-slot}}
</p>
{{/let}}
{{/if}}
</FlashMessage>
{{/each}}
<div>
<div>
{{#if authorized}}
<nav aria-label="Breadcrumb">
<YieldSlot @name="breadcrumbs">{{yield}}</YieldSlot>
</nav>
{{/if}}
<div class="title">
<YieldSlot @name="header">
{{yield}}
</YieldSlot>
<div class="actions">
{{#if authorized}}
<YieldSlot @name="actions">
<PortalTarget @name="app-view-actions" />
{{yield}}
</YieldSlot>
{{/if}}
</div>
</div>
<YieldSlot @name="nav">
{{yield}}
</YieldSlot>
</div>
</div>
{{#if authorized}}
<YieldSlot @name="toolbar">
<input type="checkbox" id="toolbar-toggle" />
{{yield}}
</YieldSlot>
{{/if}}
</header>
<div>
{{#if (not enabled) }}
<YieldSlot @name="disabled">{{yield}}</YieldSlot>
{{else if (not authorized)}}
<YieldSlot @name="authorization">{{yield}}</YieldSlot>
<div class="app-view">
{{yield}}
<header>
{{#each flashMessages.queue as |flash|}}
<FlashMessage @flash={{flash}} as |component flash|>
{{#if flash.dom}}
{{{flash.dom}}}
{{else}}
<YieldSlot @name="content">{{yield}}</YieldSlot>
{{#let (lowercase component.flashType) (lowercase flash.action) as |status type|}}
{{! flashes automatically ucfirst the type }}
<p data-notification role="alert" class={{concat status ' notification-' type}}>
<strong>
{{capitalize status}}!
</strong>
{{#yield-slot name="notification" params=(block-params status type flash.item flash.error)}}
{{yield}}
{{#if (eq type 'logout')}}
{{#if (eq status 'success') }}
You are now logged out.
{{else}}
There was an error logging out.
{{/if}}
{{else if (eq type 'authorize')}}
{{#if (eq status 'success') }}
You are now logged in.
{{else}}
There was an error, please check your SecretID/Token
{{/if}}
{{/if}}
{{else}}
{{#if (eq type 'logout')}}
{{#if (eq status 'success') }}
You are now logged out.
{{else}}
There was an error logging out.
{{/if}}
{{else if (eq type 'authorize')}}
{{#if (eq status 'success') }}
You are now logged in.
{{else}}
There was an error, please check your SecretID/Token
{{/if}}
{{/if}}
{{/yield-slot}}
</p>
{{/let}}
{{/if}}
</FlashMessage>
{{/each}}
<div>
<div>
{{#if authorized}}
<nav aria-label="Breadcrumb">
<YieldSlot @name="breadcrumbs">{{yield}}</YieldSlot>
</nav>
{{/if}}
<div class="title">
<YieldSlot @name="header">
{{yield}}
</YieldSlot>
<div class="actions">
{{#if authorized}}
<YieldSlot @name="actions">
<PortalTarget @name="app-view-actions" />
{{yield}}
</YieldSlot>
{{/if}}
</div>
</div>
<YieldSlot @name="nav">
{{yield}}
</YieldSlot>
</div>
</div>
{{#if authorized}}
<YieldSlot @name="toolbar">
<input type="checkbox" id="toolbar-toggle" />
{{yield}}
</YieldSlot>
{{/if}}
</header>
<div>
{{#if (not enabled) }}
<YieldSlot @name="disabled">{{yield}}</YieldSlot>
{{else}}
<YieldSlot @name="content">{{yield}}</YieldSlot>
{{/if}}
</div>
</div>

View File

@ -1,46 +1,7 @@
import Component from '@ember/component';
import SlotsMixin from 'block-slots';
import { inject as service } from '@ember/service';
import templatize from 'consul-ui/utils/templatize';
export default Component.extend(SlotsMixin, {
tagName: '',
authorized: true,
enabled: true,
classNames: ['app-view'],
classNameBindings: ['enabled::disabled', 'authorized::unauthorized'],
dom: service('dom'),
didReceiveAttrs: function() {
this._super(...arguments);
// right now only manually added classes are hoisted to <html>
const $root = this.dom.root();
if (this.loading) {
$root.classList.add('loading');
} else {
$root.classList.remove('loading');
}
let cls = this['class'] || '';
if (cls) {
// its possible for 'layout' templates to change after insert
// check for these specific layouts and clear them out
const receivedClasses = new Set(templatize(cls.split(' ')));
const difference = new Set([...$root.classList].filter(item => !receivedClasses.has(item)));
[...difference].forEach(function(item, i) {
if (templatize(['edit', 'show', 'list']).indexOf(item) !== -1) {
$root.classList.remove(item);
}
});
$root.classList.add(...receivedClasses);
}
},
didInsertElement: function() {
this._super(...arguments);
this.didReceiveAttrs();
},
didDestroyElement: function() {
this._super(...arguments);
const cls = this['class'] + ' loading';
if (cls) {
const $root = this.dom.root();
$root.classList.remove(...templatize(cls.split(' ')));
}
},
});

View File

@ -14,7 +14,7 @@
<div>
{{yield}}
{{#if (and (env 'CONSUL_ACLS_ENABLED') allowLogin)}}
<label for="login-toggle">
<label for="login-toggle" data-test-empty-state-login>
<DataSource
@src="settings://consul:token"
@onchange={{action (mut token) value="data"}}

View File

@ -0,0 +1,6 @@
export default present => (scope = '.empty-state') => {
return {
scope: scope,
login: present('[data-test-empty-state-login]'),
};
};

View File

@ -1,4 +1,4 @@
export default (collection, clickable, attribute, is, authForm) => scope => {
export default (collection, clickable, attribute, is, authForm, emptyState) => scope => {
const page = {
navigation: [
'services',
@ -34,6 +34,7 @@ export default (collection, clickable, attribute, is, authForm) => scope => {
authdialog: {
form: authForm(),
},
emptystate: emptyState(),
// TODO: errors aren't strictly part of this component
error: {
status: attribute('data-test-status', '[data-test-status]'),

View File

@ -55,11 +55,11 @@ export default Component.extend(Slotted, {
resize: function(e) {
// TODO: This top part is very similar to resize in tabular-collection
// see if it make sense to DRY out
const $appContent = this.dom.element('main > div');
if ($appContent) {
const dom = get(this, 'dom');
const $footer = dom.element('footer[role="contentinfo"]');
if ($footer) {
const border = 1;
const rect = this.$element.getBoundingClientRect();
const $footer = this.dom.element('footer[role="contentinfo"]');
const space = rect.top + $footer.clientHeight + border;
const height = e.target.innerHeight - space;
this.set('height', Math.max(0, height));

View File

@ -0,0 +1,15 @@
{{did-insert this.connect}}
{{will-destroy this.disconnect}}
<section
class="outlet"
data-outlet={{@name}}
data-route={{this.route}}
data-state={{this.state.name}}
data-transition={{concat this.previousState.name ' ' this.state.name}}
>
{{yield (hash
state=this.state
previousState=this.previousState
route=this.route
)}}
</section>

View File

@ -0,0 +1,140 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
class State {
constructor(name) {
this.name = name;
}
matches(match) {
return this.name === match;
}
}
class Outlets {
constructor() {
this.map = new Map();
}
sort() {
this.sorted = [...this.map.keys()];
this.sorted.sort((a, b) => {
const al = a.split('.').length;
const bl = b.split('.').length;
switch (true) {
case al > bl:
return -1;
case al < bl:
return 1;
default:
return 0;
}
});
}
set(name, value) {
this.map.set(name, value);
this.sort();
}
get(name) {
return this.map.get(name);
}
delete(name) {
this.map.delete(name);
this.sort();
}
keys() {
return this.sorted;
}
}
const outlets = new Outlets();
export default class Outlet extends Component {
@service('router') router;
@service('dom') dom;
@tracked route;
@tracked state;
@tracked previousState;
constructor() {
super(...arguments);
if (this.args.name === 'application') {
this.setAppState('loading');
this.setAppRoute(this.router.currentRouteName);
}
}
setAppRoute(name) {
if (name.startsWith('nspace.')) {
name = name.substr(0, 'nspace.'.length);
}
if (name !== 'loading') {
const doc = this.dom.root();
if (doc.classList.contains('ember-loading')) {
doc.classList.remove('ember-loading');
}
doc.dataset.route = name;
this.setAppState('idle');
}
}
setAppState(state) {
this.dom.root().dataset.state = state;
}
setOutletRoutes(route) {
const keys = [...outlets.keys()];
const pos = keys.indexOf(this.name);
const key = pos + 1;
const parent = outlets.get(keys[key]);
parent.route = this.args.name;
this.route = route;
}
@action
startLoad(transition) {
const keys = [...outlets.keys()];
const outlet =
keys.find(item => {
return transition.to.name.indexOf(item) !== -1;
}) || 'application';
if (this.args.name === outlet) {
this.previousState = this.state;
this.state = new State('loading');
}
if (this.args.name === 'application') {
this.setAppState('loading');
}
}
@action
endLoad(transition) {
if (this.state.matches('loading')) {
this.setOutletRoutes(transition.to.name);
this.previousState = this.state;
this.state = new State('idle');
}
if (this.args.name === 'application') {
this.setAppRoute(this.router.currentRouteName);
}
}
@action
connect() {
outlets.set(this.args.name, this);
this.previousState = this.state = new State('idle');
this.router.on('routeWillChange', this.startLoad);
this.router.on('routeDidChange', this.endLoad);
}
@action
disconnect() {
outlets.delete(this.args.name);
this.router.off('routeWillChange', this.startLoad);
this.router.off('routeDidChange', this.endLoad);
}
}

View File

@ -1,4 +1,5 @@
<ModalDialog
class="role-selector"
data-test-role-form
@onclose={{action (mut state) "role"}}
@name="new-role-toggle"

View File

@ -0,0 +1,9 @@
.role-selector {
[name='role[state]'],
[name='role[state]'] + * {
display: none;
}
[name='role[state]']:checked + * {
display: block;
}
}

View File

@ -69,7 +69,7 @@ export default CollectionComponent.extend(Slotted, {
actions: {
resize: function(e) {
const $tbody = this.element;
const $appContent = this.dom.element('main > div');
const $appContent = this.dom.element('.app-view');
if ($appContent) {
const border = 1;
const rect = $tbody.getBoundingClientRect();

View File

@ -1,15 +1,10 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get, set } from '@ember/object';
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
const removeLoading = function($from) {
return $from.classList.remove('ember-loading', 'loading');
};
export default Route.extend(WithBlockingActions, {
dom: service('dom'),
router: service('router'),
nspacesRepo: service('repository/nspace/disabled'),
repo: service('repository/dc'),
@ -34,21 +29,10 @@ export default Route.extend(WithBlockingActions, {
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
actions: {
loading: function(transition, originRoute) {
const from = get(transition, 'from.name') || 'application';
const controller = this.controllerFor(from);
set(controller, 'loading', true);
const $root = this.dom.root();
$root.classList.add('loading');
transition.promise.finally(() => {
set(controller, 'loading', false);
removeLoading($root);
});
},
error: function(e, transition) {
// TODO: Normalize all this better
let error = {
@ -84,7 +68,6 @@ export default Route.extend(WithBlockingActions, {
const app = this.modelFor('application') || {};
const dcs = app.dcs || [model.dc];
const nspaces = app.nspaces || [model.nspace];
const $root = this.dom.root();
hash({
dc:
error.status.toString().indexOf('5') !== 0
@ -96,14 +79,12 @@ export default Route.extend(WithBlockingActions, {
})
.then(model => Promise.all([model, this.repo.clearActive()]))
.then(([model]) => {
removeLoading($root);
// we can't use setupController as we received an error
// so we do it manually instead
this.controllerFor('application').setProperties(model);
this.controllerFor('error').setProperties({ error: error });
})
.catch(e => {
removeLoading($root);
this.controllerFor('error').setProperties({ error: error });
});
return true;

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash, Promise } from 'rsvp';
import { get } from '@ember/object';
@ -63,6 +63,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
// the model here is actually required for the entire application
// but we need to wait until we are in this route so we know what the dc
// and or nspace is if the below changes please revists the comments

View File

@ -1,3 +1,3 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
export default Route.extend(WithBlockingActions, {});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
@ -17,12 +17,12 @@ export default Route.extend(WithAclActions, {
});
return hash({
create: true,
isLoading: false,
item: this.item,
types: ['management', 'client'],
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
deactivate: function() {

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -9,12 +9,12 @@ export default Route.extend(WithAclActions, {
settings: service('settings'),
model: function(params) {
return hash({
isLoading: false,
item: this.repo.findBySlug(params.id, this.modelFor('dc').dc.Name),
types: ['management', 'client'],
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
@ -28,12 +28,12 @@ export default Route.extend(WithAclActions, {
},
model: function(params) {
return hash({
isLoading: false,
items: this.repo.findAllByDatacenter(this.modelFor('dc').dc.Name),
token: this.settings.findBySlug('token'),
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -16,6 +16,7 @@ export default SingleRoute.extend(WithPolicyActions, {
return hash({
...model,
...{
routeName: this.routeName,
items: tokenRepo.findByPolicy(get(model.item, 'ID'), dc, nspace).catch(function(e) {
switch (get(e, 'errors.firstObject.status')) {
case '403':
@ -30,6 +31,7 @@ export default SingleRoute.extend(WithPolicyActions, {
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -21,10 +21,10 @@ export default Route.extend(WithPolicyActions, {
this.modelFor('nspace').nspace.substr(1)
),
}),
isLoading: false,
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -30,6 +30,7 @@ export default SingleRoute.extend(WithRoleActions, {
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -21,10 +21,10 @@ export default Route.extend(WithRoleActions, {
this.modelFor('nspace').nspace.substr(1)
),
}),
isLoading: false,
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -12,12 +12,14 @@ export default SingleRoute.extend(WithTokenActions, {
return hash({
...model,
...{
routeName: this.routeName,
token: this.settings.findBySlug('token'),
},
});
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
@ -33,11 +33,11 @@ export default Route.extend(WithTokenActions, {
),
}),
nspace: this.modelFor('nspace').nspace.substr(1),
isLoading: false,
token: this.settings.findBySlug('token'),
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
beforeModel: function() {

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -19,6 +19,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
queryParams: {
@ -15,6 +15,7 @@ export default Route.extend({
};
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
@ -48,6 +48,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,3 @@
// import Route from '@ember/routing/route';
import Route from './index';
export default Route.extend({

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
@ -26,7 +26,6 @@ export default Route.extend({
const dc = this.modelFor('dc').dc.Name;
const nspace = this.modelFor('nspace').nspace.substr(1);
return hash({
isLoading: false,
parent: this.repo.findBySlug(key, dc, nspace),
}).then(model => {
return hash({
@ -54,6 +53,7 @@ export default Route.extend({
},
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -21,6 +21,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -16,11 +16,11 @@ export default Route.extend({
return hash({
...model,
tomography: this.data.source(uri => uri`/${nspace}/${dc}/coordinates/for-node/${name}`),
sessions: this.data.source(uri => uri`/${nspace}/${dc}/sessions/for-node/${name}`),
});
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { get } from '@ember/object';
export default Route.extend({

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { get } from '@ember/object';
export default Route.extend({
@ -19,6 +19,7 @@ export default Route.extend({
}
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
queryParams: {
@ -15,6 +15,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,8 +1,10 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
export default Route.extend(WithBlockingActions, {
data: service('data-source/service'),
sessionRepo: service('repository/session'),
feedback: service('feedback'),
model: function() {
@ -10,23 +12,26 @@ export default Route.extend(WithBlockingActions, {
.split('.')
.slice(0, -1)
.join('.');
return this.modelFor(parent);
const dc = this.modelFor('dc').dc.Name;
const nspace = this.modelFor('nspace').nspace.substr(1);
const node = this.paramsFor(parent).name;
return hash({
dc: dc,
nspace: nspace,
node: node,
sessions: this.data.source(uri => uri`/${nspace}/${dc}/sessions/for-node/${node}`),
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
actions: {
invalidateSession: function(item) {
const dc = this.modelFor('dc').dc.Name;
const nspace = this.modelFor('nspace').nspace.substr(1);
const controller = this.controller;
const route = this;
return this.feedback.execute(() => {
return this.sessionRepo.remove(item).then(() => {
return this.sessionRepo.findByNode(item.Node, dc, nspace).then(function(sessions) {
controller.setProperties({
sessions: sessions,
});
});
route.refresh();
});
}, 'delete');
},

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -14,7 +14,6 @@ export default Route.extend(WithNspaceActions, {
const create = this.isCreate(...arguments);
const dc = this.modelFor('dc').dc.Name;
return hash({
isLoading: false,
create: create,
dc: dc,
item: create
@ -30,6 +29,7 @@ export default Route.extend(WithNspaceActions, {
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -16,10 +16,10 @@ export default Route.extend(WithNspaceActions, {
model: function(params) {
return hash({
items: this.data.source(uri => uri`/*/*/namespaces`),
isLoading: false,
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
@ -39,6 +39,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
@ -49,6 +49,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { get } from '@ember/object';
export default Route.extend({
@ -19,6 +19,7 @@ export default Route.extend({
}
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { get } from '@ember/object';
export default Route.extend({
@ -22,6 +22,7 @@ export default Route.extend({
}
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import to from 'consul-ui/utils/routing/redirect-to';
export default Route.extend({

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { get } from '@ember/object';
export default Route.extend({
@ -19,6 +19,7 @@ export default Route.extend({
}
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
redirect: function(model, transition) {

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';
@ -46,6 +46,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import to from 'consul-ui/utils/routing/redirect-to';
export default Route.extend({

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
queryParams: {
@ -15,6 +15,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,3 +1,8 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend();
export default Route.extend({
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function(params, transition) {
@ -10,6 +10,7 @@ export default Route.extend({
};
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
queryParams: {
@ -15,6 +15,7 @@ export default Route.extend({
};
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { get } from '@ember/object';
export default Route.extend({
@ -19,6 +19,7 @@ export default Route.extend({
}
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
export default Route.extend({
model: function() {
@ -9,6 +9,7 @@ export default Route.extend({
return this.modelFor(parent);
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
});

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get } from '@ember/object';

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import Error from '@ember/error';
export default Route.extend({

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { getOwner } from '@ember/application';

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import { get, set } from '@ember/object';
@ -22,6 +22,7 @@ export default Route.extend({
});
},
setupController: function(controller, model) {
this._super(...arguments);
controller.setProperties(model);
},
actions: {

View File

@ -0,0 +1,15 @@
import Route from '@ember/routing/route';
import { setProperties } from '@ember/object';
/**
* Set the routeName for the controller so that it is
* available in the template for the route/controller
*/
export default class BaseRoute extends Route {
setupController(controller, model) {
setProperties(controller, {
routeName: this.routeName,
});
super.setupController(...arguments);
}
}

View File

@ -1,4 +1,4 @@
import Route from '@ember/routing/route';
import Route from 'consul-ui/routing/route';
import { assert } from '@ember/debug';
import { Promise, hash } from 'rsvp';
export default Route.extend({
@ -16,7 +16,6 @@ export default Route.extend({
const nspace = this.modelFor('nspace').nspace.substr(1);
const create = this.isCreate(...arguments);
return hash({
isLoading: false,
dc: dc,
nspace: nspace,
create: create,

View File

@ -1,5 +1,4 @@
import Service, { inject as service } from '@ember/service';
import { set } from '@ember/object';
import callableType from 'consul-ui/utils/callable-type';
const TYPE_SUCCESS = 'success';
@ -17,7 +16,6 @@ export default Service.extend({
notify: service('flashMessages'),
logger: service('logger'),
execute: function(handle, action, status = defaultStatus, controller) {
set(controller, 'isLoading', true);
const getAction = callableType(action);
const getStatus = callableType(status);
const notify = this.notify;
@ -59,9 +57,6 @@ export default Service.extend({
});
}
})
.finally(function() {
set(controller, 'isLoading', false);
})
);
},
});

View File

@ -66,3 +66,4 @@
@import 'consul-ui/components/consul-intention-permission-list';
@import 'consul-ui/components/consul-intention-permission-form';
@import 'consul-ui/components/consul-intention-permission-header-list';
@import 'consul-ui/components/role-selector';

View File

@ -8,8 +8,8 @@ main {
overflow: visible !important;
}
%app-view > div.unauthorized,
%app-view > div.error {
%app-view .outlet > div.unauthorized,
%app-view .outlet > div.error {
@extend %app-view-error;
}
%app-view header form {

View File

@ -1,6 +1,6 @@
@import './skin';
@import './layout';
%app-view > div > header {
%app-view .outlet > div > header {
@extend %app-view-header;
}
%app-view-header .title {
@ -9,6 +9,6 @@
%app-view-header .actions {
@extend %app-view-actions;
}
%app-view > div > div {
%app-view .outlet > div > div {
@extend %app-view-content;
}

View File

@ -5,9 +5,10 @@
%loader circle {
fill: $magenta-100;
}
html:not(.loading) .view-loader {
html.ember-loading .view-loader,
html[data-state='idle'] .view-loader {
display: none;
}
html.loading .app-view {
.outlet[data-state='loading'] {
display: none;
}

View File

@ -28,7 +28,7 @@ main,
#wrapper > footer {
@extend %content-container;
}
html.template-edit main {
html[data-route$='.edit'] main {
@extend %content-container-restricted;
}

View File

@ -1,40 +1,33 @@
html.template-acl.template-list main td strong {
html[data-route^='dc.acls.index'] main td strong {
@extend %pill-500, %frame-gray-900;
margin-right: 3px;
}
html.template-acl.template-list .filter-bar {
html[data-route^='dc.acls.index'] .filter-bar {
@extend %filter-bar-reversed;
}
html.template-acl.template-list .filter-bar [role='radiogroup'] {
html[data-route^='dc.acls.index'] .filter-bar [role='radiogroup'] {
@extend %expanded-single-select;
}
@media #{$--horizontal-tabs} {
.template-policy.template-list main header .actions,
.template-token.template-list main header .actions {
html[data-route^='dc.acls.policies.index'] main header .actions,
html[data-route^='dc.acls.policies.index'] main header .actions {
position: relative;
top: 42px;
}
}
@media #{$--lt-wide-form} {
html.template-acl.template-edit main header .actions {
html[data-route^='dc.acls.edit'] main header .actions {
float: none;
display: flex;
justify-content: space-between;
margin-bottom: 1em;
}
html.template-acl.template-edit main header .actions .with-feedback {
html[data-route^='dc.acls.edit'] main header .actions .with-feedback {
position: absolute;
right: 0;
}
html.template-acl.template-edit main header .actions .with-confirmation {
html[data-route^='dc.acls.edit'] main header .actions .with-confirmation {
margin-top: 0;
}
}
[name='role[state]'],
[name='role[state]'] + * {
display: none;
}
[name='role[state]']:checked + * {
display: block;
}

View File

@ -1,8 +1,4 @@
html.template-kv.template-edit div > .with-confirmation {
float: none;
margin-top: 1.7em;
}
html.template-kv.template-edit .type-toggle {
html[data-route^='dc.kv.edit'] .type-toggle {
float: right;
margin-bottom: 0 !important;
}

View File

@ -1,3 +1,3 @@
html.template-node.template-show #meta-data table tr {
html[data-route^='dc.nodes.show.metadata'] table tr {
cursor: default;
}

View File

@ -1,10 +1,10 @@
/* instance detail dl text*/
html.template-instance.template-show .app-view > header dl {
html[data-route^='dc.services.instance'] .app-view > header dl {
float: left;
margin-top: 19px;
margin-bottom: 23px;
margin-right: 50px;
}
html.template-instance.template-show .app-view > header dt {
html[data-route^='dc.services.instance'] .app-view > header dt {
font-weight: $typo-weight-bold;
}

View File

@ -10,7 +10,11 @@
@nspace={{or nspace nspaces.firstObject}}
@onchange={{action "reauthorize"}}
>
{{outlet}}
<Outlet
@name="application"
as |o|>
{{outlet}}
</Outlet>
<ConsulLoader class="view-loader" />
</HashicorpConsul>
{{/if}}

View File

@ -1 +1,5 @@
{{outlet}}
<Outlet
@name={{routeName}}
as |o|>
{{outlet}}
</Outlet>

View File

@ -1,4 +1,4 @@
<AppView @class="acl edit" @loading={{isLoading}}>
<AppView>
<BlockSlot @name="notification" as |status type|>
{{partial 'dc/acls/notifications'}}
</BlockSlot>

View File

@ -7,7 +7,7 @@
)
as |filter|
}}
<AppView @class="acl list" @loading={{isLoading}}>
<AppView>
<BlockSlot @name="notification" as |status type|>
{{partial 'dc/acls/notifications'}}
</BlockSlot>

View File

@ -8,8 +8,6 @@
{{title 'Access Controls'}}
{{/if}}
<AppView
@class={{concat "policy " (if isAuthorized "edit" "list")}}
@loading={{isLoading}}
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>

View File

@ -9,8 +9,6 @@
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
<AppView
@class="policy list"
@loading={{isLoading}}
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>

View File

@ -8,8 +8,6 @@
{{title 'Access Controls'}}
{{/if}}
<AppView
@class={{concat "role " (if isAuthorized "edit" "list")}}
@loading={{isLoading}}
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>

View File

@ -6,8 +6,6 @@
{{#let (or sortBy "Name:asc") as |sort|}}
<AppView
@class="role list"
@loading={{isLoading}}
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>

View File

@ -8,8 +8,6 @@
{{title 'Access Controls'}}
{{/if}}
<AppView
@class={{concat "token " (if isAuthorized "edit" "list")}}
@loading={{isLoading}}
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>

View File

@ -9,8 +9,6 @@
) as |filters|}}
{{#let (or sortBy "CreateTime:desc") as |sort|}}
<AppView
@class="token list"
@loading={{isLoading}}
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>

View File

@ -3,7 +3,7 @@
{{else}}
{{title 'New Intention'}}
{{/if}}
<AppView @class="intention edit">
<AppView>
<BlockSlot @name="breadcrumbs">
<ol>
<li><a data-test-back href={{href-to 'dc.intentions'}}>All Intentions</a></li>

View File

@ -11,7 +11,7 @@
accesses=(if access (split access ',') undefined)
) as |filters|}}
{{#let (or sortBy "Action:asc") as |sort|}}
<AppView @class="intention list">
<AppView>
<BlockSlot @name="header">
<h1>
Intentions <em>{{format-number items.length}} total</em>

View File

@ -3,7 +3,7 @@
{{else}}
{{title 'New Key/Value'}}
{{/if}}
<AppView @class="kv edit">
<AppView>
<BlockSlot @name="breadcrumbs">
<ol>
<li><a data-test-back href={{href-to 'dc.kv.index'}}>Key / Values</a></li>

View File

@ -1,6 +1,6 @@
{{title 'Key/Value'}}
{{#let (or sortBy "isFolder:asc") as |sort|}}
<AppView @class="kv list">
<AppView>
<BlockSlot @name="breadcrumbs">
<ol>
{{#if (not-eq parent.Key '/') }}

View File

@ -4,7 +4,7 @@
statuses=(if status (split status ',') undefined)
) as |filters|}}
{{#let (or sortBy "Node:asc") as |sort|}}
<AppView @class="node list">
<AppView>
<BlockSlot @name="header">
<h1>
Nodes <em>{{format-number items.length}} total</em>

View File

@ -2,7 +2,6 @@
<DataLoader as |api|>
<BlockSlot @name="data">
<EventSource @src={{sessions}} />
<EventSource @src={{tomography}} />
<EventSource @src={{item}} @onerror={{queue (action api.dispatchError)}} />
</BlockSlot>
@ -30,7 +29,7 @@
</BlockSlot>
<BlockSlot @name="loaded">
<AppView @class="node show">
<AppView>
<BlockSlot @name="notification" as |status type|>
{{!TODO: Move sessions to its own folder within nodes }}
{{partial 'dc/nodes/notifications'}}
@ -62,7 +61,11 @@
<CopyButton @value={{item.Address}} @name="Address">{{item.Address}}</CopyButton>
</BlockSlot>
<BlockSlot @name="content">
{{outlet}}
<Outlet
@name={{routeName}}
as |o|>
{{outlet}}
</Outlet>
</BlockSlot>
</AppView>

View File

@ -1,3 +1,4 @@
<EventSource @src={{sessions}} />
<div id="lock-sessions" class="tab-section">
<div role="tabpanel">
{{#if (gt sessions.length 0)}}

View File

@ -3,7 +3,7 @@
{{else}}
{{title 'Edit Namespace'}}
{{/if}}
<AppView @class="nspace edit" @loading={{isLoading}}>
<AppView>
<BlockSlot @name="notification" as |status type item error|>
{{partial 'dc/nspaces/notifications'}}
</BlockSlot>

View File

@ -1,7 +1,7 @@
{{title 'Namespaces'}}
{{#let (or sortBy "Name:asc") as |sort|}}
<EventSource @src={{items}} />
<AppView @class="nspace list" @loading={{isLoading}}>
<AppView>
<BlockSlot @name="notification" as |status type subject|>
{{partial 'dc/nspaces/notifications'}}
</BlockSlot>

View File

@ -6,7 +6,7 @@
sources=(if source (split source ',') undefined)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
<AppView @class="service list">
<AppView>
<BlockSlot @name="notification" as |status type|>
{{partial 'dc/services/notifications'}}
</BlockSlot>

View File

@ -2,7 +2,7 @@
<EventSource @src={{item}} @onerror={{action "error"}} />
<EventSource @src={{proxy}} />
<EventSource @src={{proxyMeta}} />
<AppView @class="instance show">
<AppView>
<BlockSlot @name="notification" as |status type|>
{{partial 'dc/services/notifications'}}
</BlockSlot>
@ -50,6 +50,10 @@
(hash label="Tags & Meta" href=(href-to "dc.services.instance.metadata") selected=(is-href "dc.services.instance.metadata"))
)
}}/>
<Outlet
@name={{routeName}}
as |o|>
{{outlet}}
</Outlet>
</BlockSlot>
</AppView>

View File

@ -4,7 +4,7 @@
<EventSource @src={{proxies}} />
<EventSource @src={{gatewayServices}} />
{{title item.Service.Service}}
<AppView @class="service show">
<AppView>
<BlockSlot @name="notification" as |status type|>
{{partial 'dc/services/notifications'}}
</BlockSlot>
@ -51,6 +51,10 @@
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
{{outlet}}
<Outlet
@name={{routeName}}
as |o|>
{{outlet}}
</Outlet>
</BlockSlot>
</AppView>

View File

@ -1 +1,5 @@
{{outlet}}
<Outlet
@name={{routeName}}
as |o|>
{{outlet}}
</Outlet>

View File

@ -1 +1,5 @@
{{outlet}}
<Outlet
@name={{routeName}}
as |o|>
{{outlet}}
</Outlet>

View File

@ -1,5 +1,5 @@
{{title "Settings"}}
<AppView @class="settings show">
<AppView>
<BlockSlot @name="header">
<h1>
Settings

Some files were not shown because too many files have changed in this diff Show More