From 4287291e946b1653255ad792e1c73aca1c820129 Mon Sep 17 00:00:00 2001 From: Matthew Hartstonge Date: Wed, 4 Dec 2024 19:11:12 +1300 Subject: [PATCH] feat(addon/components/paper-tab): converts to a glimmer component. --- addon/components/paper-tab.hbs | 32 +++-- addon/components/paper-tab.js | 212 ++++++++++++++++++++++++++------- 2 files changed, 192 insertions(+), 52 deletions(-) diff --git a/addon/components/paper-tab.hbs b/addon/components/paper-tab.hbs index f5007a8b9..1a49402b7 100644 --- a/addon/components/paper-tab.hbs +++ b/addon/components/paper-tab.hbs @@ -1,7 +1,25 @@ -{{! template-lint-disable no-curly-component-invocation }} -{{#if (has-block)}} - {{yield}} -{{else}} - {{@name}} -{{/if}} - \ No newline at end of file +{{#let (element this.tag) as |Tag|}} + + {{#if (has-block)}} + {{yield}} + {{else}} + {{@name}} + {{/if}} + + +{{/let}} \ No newline at end of file diff --git a/addon/components/paper-tab.js b/addon/components/paper-tab.js index a31c0cb49..25ed0aa81 100644 --- a/addon/components/paper-tab.js +++ b/addon/components/paper-tab.js @@ -1,72 +1,194 @@ -/* eslint-disable ember/classic-decorator-hooks, ember/classic-decorator-no-classic-methods, ember/no-classic-components, ember/no-computed-properties-in-native-classes, ember/no-mixins */ -import { - classNames, - attributeBindings, - classNameBindings, - tagName, -} from '@ember-decorators/component'; - -import { computed } from '@ember/object'; -import Component from '@ember/component'; +/** + * @module ember-paper + */ +import Focusable from './-focusable'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; import { htmlSafe } from '@ember/template'; -import { ChildMixin } from 'ember-composability-tools'; -import FocusableMixin from 'ember-paper/mixins/focusable-mixin'; -import { invokeAction } from 'ember-paper/utils/invoke-action'; - -@tagName('md-tab-item') -@classNames('md-tab') -@classNameBindings('isSelected:md-active') -@attributeBindings('isSelected:aria-selected', 'style', 'maybeHref:href') -export default class PaperTab extends Component.extend( - ChildMixin, - FocusableMixin -) { - // tags have browser styles or are usually styled by the user - // this makes sure that tab item still looks good with an anchor tag - @computed('href') +import { assert } from '@ember/debug'; + +/** + * @class PaperTab + * @extends Focusable + */ +export default class PaperTab extends Focusable { + /** + * Reference to the component's DOM element. + * + * @type {HTMLElement} + */ + element; + /** + * The parent this component is bound to. + * + * @type {PaperTabs} + */ + parent; + /** + * Marks whether the component should register itself to the supplied parent. + * + * @type {Boolean} + */ + shouldRegister; + /** + * The top level tag to render. One of {'a', 'md-tab'}. + * + * @type {string} + * @private + * @default 'md-tab-item' + */ + tag; + /** + * provides a proxy value if one is not supplied by the user. + * + * @type {number|*} + * @private + */ + @tracked _value; + /** + * the number of pixels that the upper left corner of the current element is + * offset to the left within the {@link HTMLElement.offsetParent} node. + * + * @type{number} + */ + @tracked left; + /** + * the layout width of the element as an integer. + * + * @type{number} + */ + @tracked width; + + /** + * @constructor + * @param owner + * @param args + */ + constructor(owner, args) { + super(owner, args); + + this.tag = 'md-tab-item'; + if (this.args.href) { + this.tag = 'a'; + } + + this.shouldRegister = this.args.shouldRegister || true; + if (this.shouldRegister) { + assert( + 'A parent component should be supplied to ', + this.args.parentComponent + ); + this.parent = this.args.parentComponent; + } + } + + /** + * Performs any required DOM setup. + * + * @param {HTMLElement} element - the node that has been added to the DOM. + */ + @action didInsertNode(element) { + this.element = element; + this.left = element.offsetLeft; + this.width = element.offsetWidth; + + this.registerListeners(element); + + if (this.shouldRegister) { + this.parent.registerChild(this); + } + } + + /** + * didUpdateNode is called when tracked component attributes change. + */ + @action didUpdateNode() { + if (this.args.value) { + this.value = this.args.value; + } + } + + /** + * Performs any required DOM teardown. + * + * @param {HTMLElement} element - the node to be removed from the DOM. + */ + @action willDestroyNode(element) { + this.unregisterListeners(element); + } + + /** + * lifecycle hook to perform non-DOM related teardown. + */ + willDestroy() { + super.willDestroy(); + + if (this.shouldRegister) { + this.parent.unregisterChild(this); + } + } + + /** + * tags have browser styles or are usually styled by the user + * this makes sure that tab item still looks good with an anchor tag. + * + * @returns {string|undefined} + */ get style() { - if (this.href) { + if (this.args.href) { return htmlSafe('text-decoration: none; border: none;'); } else { return undefined; } } - @computed('href', 'disabled') + /** + * maybeHref returns the user supplied href link url. + * + * @returns {string|undefined} + */ get maybeHref() { - if (this.href && !this.disabled) { - return this.href; + if (this.args.href && !this.disabled) { + return this.args.href; } else { return undefined; } } - @computed('selected', 'value') + /** + * computes whether this is the currently selected tab. + * + * @returns {boolean} + */ get isSelected() { - return this.selected === this.value; - } - - init() { - super.init(...arguments); - if (this.href) { - this.set('tagName', 'a'); - } + return this.args.selected === this.value; } // this method is called by the parent updateDimensions() { // this is the true current width // it is used to calculate the ink bar position & pagination offset - this.setProperties({ - left: this.element.offsetLeft, - width: this.element.offsetWidth, - }); + this.left = this.element.offsetLeft; + this.width = this.element.offsetWidth; } - click() { + get value() { + // enable support for user supplied value + return this.args.value || this._value; + } + set value(value) { + this._value = value; + } + + @action handleClick(e) { if (!this.disabled) { - invokeAction(this, 'onClick', ...arguments); - invokeAction(this, 'onSelect', this); + if (this.args.onClick) { + this.args.onClick(e); + } + + if (this.args.onSelect) { + this.args.onSelect(this); + } } } }