Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ui: Add Route component / routlet service #9813

Merged
merged 14 commits into from
Mar 8, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
@extend %main-nav-horizontal-action;
}
%main-nav-horizontal .popover-menu [type='checkbox']:checked + label > *,
%main-nav-horizontal > ul > li > a:active,
%main-nav-horizontal > ul > li.is-active > a,
%main-nav-horizontal > ul > li.is-active > label > * {
@extend %main-nav-horizontal-action-active;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
%main-nav-vertical > ul > li > span {
@extend %main-nav-vertical-action;
}
%main-nav-vertical > ul > li > a:active,
%main-nav-vertical > ul > li.is-active > a {
@extend %main-nav-vertical-action-active;
}
Expand Down
8 changes: 5 additions & 3 deletions ui/packages/consul-ui/app/components/outlet/index.hbs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{{did-insert this.connect}}
{{will-destroy this.disconnect}}
{{did-insert this.connect}}
{{will-destroy this.disconnect}}
<section
{{did-insert (fn this.attributeChanged 'element')}}
class="outlet"
data-outlet={{@name}}
data-route={{this.route}}
data-route={{this.routeName}}
data-state={{this.state.name}}
data-transition={{concat this.previousState.name ' ' this.state.name}}
{{on 'transitionend' this.transitionEnd}}
>
{{yield (hash
state=this.state
Expand Down
100 changes: 36 additions & 64 deletions ui/packages/consul-ui/app/components/outlet/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,18 @@ class State {
}
}

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('routlet') routlet;
@service('router') router;
@service('dom') dom;

@tracked route;
@tracked element;
@tracked routeName;
@tracked state;
@tracked previousState;
@tracked endTransition;

constructor() {
super(...arguments);
if (this.args.name === 'application') {
this.setAppState('loading');
this.setAppRoute(this.router.currentRouteName);
}
get model() {
return this.args.model || {};
}

setAppRoute(name) {
Expand All @@ -70,7 +32,7 @@ export default class Outlet extends Component {
name = name.substr(nspace.length);
}
if (name !== 'loading') {
const doc = this.dom.root();
const doc = this.element.ownerDocument.documentElement;
if (doc.classList.contains('ember-loading')) {
doc.classList.remove('ember-loading');
}
Expand All @@ -80,31 +42,43 @@ export default class Outlet extends Component {
}

setAppState(state) {
this.dom.root().dataset.state = state;
const doc = this.element.ownerDocument.documentElement;
doc.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;
@action
attributeChanged(prop, value) {
switch (prop) {
case 'element':
this.element = value;
if (this.args.name === 'application') {
this.setAppState('loading');
this.setAppRoute(this.router.currentRouteName);
}
break;
}
}

this.route = route;
@action transitionEnd($el) {
if (typeof this.endTransition === 'function') {
this.endTransition();
}
}

@action
startLoad(transition) {
const keys = [...outlets.keys()];

const outlet =
keys.find(item => {
return transition.to.name.indexOf(item) !== -1;
}) || 'application';

const outlet = this.routlet.findOutlet(transition.to.name) || 'application';
if (this.args.name === outlet) {
this.previousState = this.state;
this.state = new State('loading');
this.endTransition = this.routlet.transition();
// if we have no transition-duration set immediately end the transition
const duration = window
.getComputedStyle(this.element)
.getPropertyValue('transition-duration');
if (parseFloat(duration) === 0) {
this.endTransition();
}
}
if (this.args.name === 'application') {
this.setAppState('loading');
Expand All @@ -114,8 +88,6 @@ export default class Outlet extends Component {
@action
endLoad(transition) {
if (this.state.matches('loading')) {
this.setOutletRoutes(transition.to.name);

this.previousState = this.state;
this.state = new State('idle');
}
Expand All @@ -126,15 +98,15 @@ export default class Outlet extends Component {

@action
connect() {
outlets.set(this.args.name, this);
this.routlet.addOutlet(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.routlet.removeOutlet(this.args.name);
this.router.off('routeWillChange', this.startLoad);
this.router.off('routeDidChange', this.endLoad);
}
Expand Down
5 changes: 5 additions & 0 deletions ui/packages/consul-ui/app/components/route/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{did-insert this.connect}}
{{will-destroy this.disconnect}}
{{yield (hash
model=model
)}}
19 changes: 19 additions & 0 deletions ui/packages/consul-ui/app/components/route/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class RouteComponent extends Component {
@service('routlet') routlet;

@tracked model;

@action
connect() {
this.routlet.addRoute(this.args.name, this);
}
@action
disconnect() {
this.routlet.removeRoute(this.args.name, this);
}
}
20 changes: 15 additions & 5 deletions ui/packages/consul-ui/app/helpers/is-href.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
/*eslint ember/no-observers: "warn"*/
// TODO: Remove ^
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { observes } from '@ember-decorators/object';
import { action } from '@ember/object';

export default class IsHrefHelper extends Helper {
@service('router') router;
init() {
super.init(...arguments);
this.router.on('routeWillChange', this.routeWillChange);
}

compute([targetRouteName, ...rest]) {
if (this.router.currentRouteName.startsWith('nspace.') && targetRouteName.startsWith('dc.')) {
targetRouteName = `nspace.${targetRouteName}`;
}
if (typeof this.next !== 'undefined' && this.next !== 'loading') {
return this.next.startsWith(targetRouteName);
}
return this.router.isActive(...[targetRouteName, ...rest]);
}

@observes('router.currentURL')
onURLChange() {
@action
routeWillChange(transition) {
this.next = transition.to.name.replace('.index', '');
this.recompute();
}

willDestroy() {
this.router.off('routeWillChange', this.routeWillChange);
}
}
21 changes: 12 additions & 9 deletions ui/packages/consul-ui/app/routes/dc.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ export default class DcRoute extends Route {
dc: params.dc,
nspace: get(nspace || {}, 'Name'),
});
// 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 revisit the comments
// in routes/application:model
// We do this here instead of in setupController to prevent timing issues
// in lower routes
this.controllerFor('application').setProperties({
dc,
nspace,
token,
permissions,
});
return {
dc,
nspace,
Expand All @@ -55,15 +67,6 @@ export default class DcRoute extends Route {
};
}

setupController(controller, model) {
super.setupController(...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
// in routes/application:model
this.controllerFor('application').setProperties(model);
}

// TODO: This will eventually be deprecated please see
// https://deprecations.emberjs.com/v3.x/#toc_deprecate-router-events
@action
Expand Down
4 changes: 2 additions & 2 deletions ui/packages/consul-ui/app/routes/dc/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export default class IndexRoute extends Route {
async model(params, transition) {
const nspace = this.modelFor('nspace').nspace.substr(1);
const dc = this.modelFor('dc').dc.Name;
const items = await this.data.source(uri => uri`/${nspace}/${dc}/services`);
const items = this.data.source(uri => uri`/${nspace}/${dc}/services`);
return {
dc,
nspace,
items,
items: await items,
searchProperties: this.queryParams.searchproperty.empty[0],
};
}
Expand Down
7 changes: 4 additions & 3 deletions ui/packages/consul-ui/app/routes/dc/services/show/routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ export default class RoutingRoute extends Route {
.slice(0, -1)
.join('.');
const model = this.modelFor(parent);
const chain = this.data.source(
uri => uri`/${model.nspace}/${model.dc.Name}/discovery-chain/${model.slug}`
);
return {
...model,
chain: await this.data.source(
uri => uri`/${model.nspace}/${model.dc.Name}/discovery-chain/${model.slug}`
),
chain: await chain,
};
}

Expand Down
2 changes: 1 addition & 1 deletion ui/packages/consul-ui/app/routes/dc/services/show/tags.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Route from 'consul-ui/routing/route';

export default class TagsRoute extends Route {
model() {
async model() {
const parent = this.routeName
.split('.')
.slice(0, -1)
Expand Down
Loading