Skip to content

Commit

Permalink
Refactor Nav/Navbar/Dropdown's LinkTo components to not extend from E…
Browse files Browse the repository at this point in the history
…mber's LinkComponent

According to RFC671 and deprecations introduced in latest Ember Canary.
  • Loading branch information
simonihmig committed Mar 14, 2021
1 parent d62bce9 commit 5d63a86
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 26 deletions.
29 changes: 29 additions & 0 deletions addon/components/bs-dropdown/menu/link-to.hbs
@@ -0,0 +1,29 @@
{{#if this.model}}
<LinkTo
@route={{this.route}}
@model={{this.model}}
@query={{this.query}}
@replace={{bs-default @replace false}}
@disabled={{bs-default @disabled false}}
@current-when={{@current-when}}
@activeClass={{@activeClass}}
class={{if (macroCondition (macroGetOwnConfig "isBS4")) "dropdown-item"}}
...attributes
>
{{yield}}
</LinkTo>
{{else}}
<LinkTo
@route={{this.route}}
@models={{bs-default this.models (array)}}
@query={{this.query}}
@replace={{bs-default @replace false}}
@disabled={{bs-default @disabled false}}
@current-when={{@current-when}}
@activeClass={{@activeClass}}
class={{if (macroCondition (macroGetOwnConfig "isBS4")) "dropdown-item"}}
...attributes
>
{{yield}}
</LinkTo>
{{/if}}
7 changes: 2 additions & 5 deletions addon/components/bs-dropdown/menu/link-to.js
@@ -1,15 +1,12 @@
import LinkComponent from '@ember/routing/link-component';
import { classNames } from '@ember-decorators/component';
import { macroCondition, getOwnConfig } from '@embroider/macros';
import LinkComponent from 'ember-bootstrap/components/bs-link-to';

/**
Extended `{{link-to}}` component for use within Dropdowns.
@class DropdownMenuLinkTo
@namespace Components
@extends Ember.LinkComponent
@extends Ember.Component
@public
*/
@classNames(macroCondition(getOwnConfig().isBS4) ? 'dropdown-item' : '')
export default class DropdownMenuLinkTo extends LinkComponent {}
113 changes: 113 additions & 0 deletions addon/components/bs-link-to.js
@@ -0,0 +1,113 @@
/* eslint-disable ember/classic-decorator-no-classic-methods */
import Component from '@ember/component';
import { tagName } from '@ember-decorators/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import { assert } from '@ember/debug';

/**
This is largely copied from Ember.LinkComponent. It is used as extending from Ember.LinkComponent has been deprecated.
We need this to
* register ourselves to a parent component that needs to know `active` state due to Bootstrap markup requirements, see Nav/LinkTo
* continue supporting positional params until we can remove support
@class LinkComponent
@namespace Components
@extends Component
@private
*/
@tagName('')
class LinkComponent extends Component {
// We still need to use the private service, so this component can react to changes of `currentState`, e.g. changing
// the model while the route in unchanged.
// eslint-disable-next-line ember/no-private-routing-service
@service('-routing')
_routing;

@computed('models', 'query', 'route', '_routing.currentState', 'current-when')
get active() {
let state = this._routing.currentState;

if (state) {
return this._isActive(state);
} else {
return false;
}
}

@computed('model', 'models')
get _models() {
let { model, models } = this;

if (model !== undefined) {
return [model];
} else if (models !== undefined) {
assert('The `@models` argument must be an array.', Array.isArray(models));
return models;
} else {
return [];
}
}

_isActive(routerState) {
let currentWhen = this['current-when'];

if (typeof currentWhen === 'boolean') {
return currentWhen;
}

let { _models: models, _routing: routing } = this;

if (typeof currentWhen === 'string') {
return currentWhen
.split(' ')
.some((route) => routing.isActiveForRoute(models, undefined, this._namespaceRoute(route), routerState));
} else {
return routing.isActiveForRoute(models, this.query || {}, this.route, routerState);
}
}

// eslint-disable-next-line ember/no-component-lifecycle-hooks
didReceiveAttrs() {
super.didReceiveAttrs(...arguments);

let { params } = this;
if (!params || params.length === 0) {
return;
}

params = params.slice();

// Process the positional arguments, in order.
// 1. Inline link title comes first, if present.
// if (!hasBlock) {
// this.set('linkTitle', params.shift());
// }

// 2. The last argument is possibly the `query` object.
let queryParams = params[params.length - 1];

if (queryParams && queryParams.isQueryParams) {
this.set('query', params.pop().values);
} else {
this.set('query', undefined);
}

// 3. If there is a `route`, it is now at index 0.
if (params.length === 0) {
this.set('route', undefined);
} else {
this.set('route', params.shift());
}

// 4. Any remaining indices (if any) are `models`.
this.set('model', undefined);
this.set('models', params);
}
}

LinkComponent.reopenClass({
positionalParams: 'params',
});

export default LinkComponent;
2 changes: 1 addition & 1 deletion addon/components/bs-nav/item.js
Expand Up @@ -4,7 +4,7 @@ import { filter, filterBy, gt } from '@ember/object/computed';
import Component from '@ember/component';
import { action } from '@ember/object';
import { scheduleOnce } from '@ember/runloop';
import LinkComponent from '@ember/routing/link-component';
import LinkComponent from 'ember-bootstrap/components/bs-link-to';
import ComponentParent from 'ember-bootstrap/mixins/component-parent';
import overrideableCP from 'ember-bootstrap/utils/cp/overrideable';
import { assert } from '@ember/debug';
Expand Down
29 changes: 29 additions & 0 deletions addon/components/bs-nav/link-to.hbs
@@ -0,0 +1,29 @@
{{#if this.model}}
<LinkTo
@route={{this.route}}
@model={{this.model}}
@query={{this.query}}
@replace={{bs-default @replace false}}
@disabled={{bs-default @disabled false}}
@current-when={{@current-when}}
@activeClass={{@activeClass}}
class={{if (macroCondition (macroGetOwnConfig "isBS4")) "nav-link"}}
...attributes
>
{{yield}}
</LinkTo>
{{else}}
<LinkTo
@route={{this.route}}
@models={{bs-default this.models (array)}}
@query={{this.query}}
@replace={{bs-default @replace false}}
@disabled={{bs-default @disabled false}}
@current-when={{@current-when}}
@activeClass={{@activeClass}}
class={{if (macroCondition (macroGetOwnConfig "isBS4")) "nav-link"}}
...attributes
>
{{yield}}
</LinkTo>
{{/if}}
7 changes: 2 additions & 5 deletions addon/components/bs-nav/link-to.js
@@ -1,17 +1,14 @@
import LinkComponent from '@ember/routing/link-component';
import LinkComponent from 'ember-bootstrap/components/bs-link-to';
import ComponentChild from 'ember-bootstrap/mixins/component-child';
import { classNames } from '@ember-decorators/component';
import { macroCondition, getOwnConfig } from '@embroider/macros';

/**
Extended `{{link-to}}` component for use within Navs.
@class NavLinkTo
@namespace Components
@extends Ember.LinkComponent
@extends Ember.Component
@uses Mixins.ComponentChild
@public
*/
@classNames(macroCondition(getOwnConfig().isBS4) ? 'nav-link' : '')
export default class NavLinkTo extends LinkComponent.extend(ComponentChild) {}
14 changes: 14 additions & 0 deletions addon/components/bs-navbar/link-to.hbs
@@ -0,0 +1,14 @@
<BsNav::LinkTo
@route={{this.route}}
@model={{this.model}}
@models={{this.models}}
@query={{this.query}}
@replace={{@replace}}
@disabled={{@disabled}}
@current-when={{@current-when}}
@activeClass={{@activeClass}}
{{on "click" this.onClick}}
...attributes
>
{{yield}}
</BsNav::LinkTo>
22 changes: 7 additions & 15 deletions addon/components/bs-navbar/link-to.js
@@ -1,6 +1,5 @@
import BsNavLinkToComponent from 'ember-bootstrap/components/bs-nav/link-to';
import defaultValue from 'ember-bootstrap/utils/default-decorator';

import Component from '@glimmer/component';
import { action } from '@ember/object';
/**
* Extended `{{link-to}}` component for use within Navbars.
*
Expand All @@ -9,25 +8,18 @@ import defaultValue from 'ember-bootstrap/utils/default-decorator';
* @extends Components.NavLinkTo
* @public
*/
export default class NavbarLinkTo extends BsNavLinkToComponent {
export default class NavbarLinkTo extends Component {
/**
* @property collapseNavbar
* @type {Boolean}
* @default true
* @public
*/
@defaultValue
collapseNavbar = true;

/**
* @event onCollapse
* @private
*/
onCollapse() {}

click() {
if (this.collapseNavbar) {
this.onCollapse();
@action
onClick() {
if (this.args.collapseNavbar ?? true) {
this.args.onCollapse();
}
}
}

0 comments on commit 5d63a86

Please sign in to comment.