From c835fb493a415d28a1fa03d75fd57ca3a8bb6997 Mon Sep 17 00:00:00 2001 From: Matthew Hartstonge Date: Wed, 13 Nov 2024 23:15:14 +1300 Subject: [PATCH 1/4] chore(addon/components/paper-item): pushes types down to booleans and simplifies bitwise operation for developers to an or. --- addon/components/paper-item.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/components/paper-item.js b/addon/components/paper-item.js index c40c678bc..1e1ebee8e 100644 --- a/addon/components/paper-item.js +++ b/addon/components/paper-item.js @@ -85,9 +85,9 @@ export default class PaperItem extends Component.extend(ParentMixin) { click() { this.proxiedComponents.forEach((component) => { if ( - component.processProxy && + !!component.processProxy && !component.disabled && - component.bubbles | !this.hasPrimaryAction + !!(component.bubbles || !this.hasPrimaryAction) ) { component.processProxy(); } From 841dd5378a8443777457b3dcd62a27d9395f3a27 Mon Sep 17 00:00:00 2001 From: Matthew Hartstonge Date: Wed, 13 Nov 2024 23:15:48 +1300 Subject: [PATCH 2/4] feat(addon/components/paper-item): converts to a glimmer component. --- addon/components/paper-item.hbs | 83 ++++++++++----- addon/components/paper-item.js | 176 ++++++++++++++++++++------------ 2 files changed, 170 insertions(+), 89 deletions(-) diff --git a/addon/components/paper-item.hbs b/addon/components/paper-item.hbs index 6b9458716..c702bbab4 100644 --- a/addon/components/paper-item.hbs +++ b/addon/components/paper-item.hbs @@ -1,28 +1,63 @@ -{{! template-lint-disable no-action }} -{{#with (hash - checkbox=(component "paper-checkbox" parentComponent=this bubbles=false shouldRegister=true) - button=(component "paper-button" parentComponent=this bubbles=false shouldRegister=true skipProxy=true) - switch=(component "paper-switch" parentComponent=this bubbles=false shouldRegister=true) - radio=(component "paper-radio-proxiable" parentComponent=this bubbles=false shouldRegister=true) -) as |controls|}} + + {{#with + (hash + checkbox=(component + 'paper-checkbox' parentComponent=this bubbles=false shouldRegister=true + ) + button=(component + 'paper-button' + parentComponent=this + bubbles=false + shouldRegister=true + skipProxy=true + ) + switch=(component + 'paper-switch' parentComponent=this bubbles=false shouldRegister=true + ) + radio=(component + 'paper-radio-proxiable' + parentComponent=this + bubbles=false + shouldRegister=true + ) + ) + as |controls| + }} - {{#if this.hasPrimaryAction}} -
- -
+ {{#if this.hasPrimaryAction}} +
+ +
+ {{yield controls}} +
+ +
+ {{else}} +
{{yield controls}} +
- -
- {{else}} -
- {{yield controls}} - -
- {{/if}} + {{/if}} -{{/with}} \ No newline at end of file + {{/with}} + \ No newline at end of file diff --git a/addon/components/paper-item.js b/addon/components/paper-item.js index 1e1ebee8e..56d2ea1bc 100644 --- a/addon/components/paper-item.js +++ b/addon/components/paper-item.js @@ -1,88 +1,130 @@ -/* eslint-disable ember/no-classic-components, ember/no-computed-properties-in-native-classes */ -import { - attributeBindings, - classNameBindings, - tagName, -} from '@ember-decorators/component'; -import { computed } from '@ember/object'; -import { or, bool, filter } from '@ember/object/computed'; - -import Component from '@ember/component'; -import { ParentMixin } from 'ember-composability-tools'; -import { invokeAction } from 'ember-paper/utils/invoke-action'; +/** + * @module ember-paper + */ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { A } from '@ember/array'; +import { action } from '@ember/object'; + /** * @class PaperItem * @extends Ember.Component * @uses ParentMixin */ -@tagName('md-list-item') -@classNameBindings( - 'hasProxiedComponent:md-proxy-focus', - 'shouldBeClickable:md-clickable', - 'focused:md-focused', - 'hasPrimaryAction:_md-button-wrap' -) -@attributeBindings('role', 'tabindex', 'title') -export default class PaperItem extends Component.extend(ParentMixin) { - _mouseEnterHandler = undefined; - _mouseLeaveHandler = undefined; - - // Ripple Overrides - // disable ripple when we have a primary action or when we don't have a proxied component - @computed('hasPrimaryAction', 'hasProxiedComponent') - get noink() { - return this.hasPrimaryAction || !this.hasProxiedComponent; +export default class PaperItem extends Component { + /** + * Reference to the component's DOM element + * @type {HTMLElement} + */ + element; + + /** + * Set of child grid tile components + * @type {A} + */ + @tracked children; + @tracked focused = false; + + constructor(owner, args) { + super(owner, args); + + this.children = A([]); + + if (this.args.role) { + this.role = this.args.role; + } } - role = 'listitem'; - tabindex = '-1'; + @action didInsertNode(element) { + element.addEventListener('mouseenter', this.handleMouseEnter); + element.addEventListener('mouseleave', this.handleMouseLeave); - @filter('childComponents', function (c) { - return !c.skipProxy; - }) - proxiedComponents; + this.element = element; + } - @bool('proxiedComponents.length') - hasProxiedComponent; + @action didUpdateNode() { + // noop + } - @or('hasProxiedComponent', 'onClick') - shouldBeClickable; + @action willDestroyNode(element) { + element.removeEventListener('mouseenter', this.handleMouseEnter); + element.removeEventListener('mouseleave', this.handleMouseLeave); + } - @or('onClick', 'href') - hasPrimaryAction; + /** + * Registers a child component + * @param {Component} child - The component to register + */ + @action registerChild(child) { + this.children.pushObject(child); + } - @computed('hasPrimaryAction', 'hasProxiedComponent') - get noProxy() { - return !this.hasPrimaryAction && !this.hasProxiedComponent; + /** + * Unregisters a child component + * @param {Component} child - The component to unregister + */ + @action unregisterChild(child) { + this.children.removeObject(child); } - @computed('proxiedComponents.[]') - get secondaryItem() { - let proxiedComponents = this.proxiedComponents; - return proxiedComponents.objectAt(0); + // Ripple Overrides + /** + * disable ripple when we have a primary action or when we don't have a proxied component + * @returns {boolean} + */ + get noink() { + return this.hasPrimaryAction || !this.hasProxiedComponent; } - didInsertElement() { - super.didInsertElement(...arguments); + /** + * Returns registered child proxy components. + * @returns {Component[]} + */ + get proxiedComponents() { + return this.children.filter((c) => { + return !c.skipProxy; + }); + } - this._mouseEnterHandler = this.handleMouseEnter.bind(this); - this._mouseLeaveHandler = this.handleMouseLeave.bind(this); + /** + * @returns {boolean} + */ + get hasProxiedComponent() { + return this.proxiedComponents ? this.proxiedComponents.length > 0 : false; + } - this.element.addEventListener('mouseenter', this._mouseEnterHandler); - this.element.addEventListener('mouseleave', this._mouseLeaveHandler); + /** + * @returns {boolean} + */ + get shouldBeClickable() { + return this.hasProxiedComponent || !!this.args.onClick; } - willDestroyElement() { - super.willDestroyElement(...arguments); + /** + * @returns {boolean} + */ + get hasPrimaryAction() { + return !!this.args.onClick || !!this.args.href; + } - this.element.removeEventListener('mouseenter', this._mouseEnterHandler); - this.element.removeEventListener('mouseleave', this._mouseLeaveHandler); + /** + * dead code? + * @returns {boolean} + */ + get noProxy() { + return !this.hasPrimaryAction && !this.hasProxiedComponent; + } - this._mouseEnterHandler = undefined; - this._mouseLeaveHandler = undefined; + /** + * Returns a secondary component. + * @returns {Component} + */ + get secondaryItem() { + let proxiedComponents = this.proxiedComponents; + return proxiedComponents.objectAt(0); } - click() { + @action localOnClick() { this.proxiedComponents.forEach((component) => { if ( !!component.processProxy && @@ -94,11 +136,15 @@ export default class PaperItem extends Component.extend(ParentMixin) { }); } - handleMouseEnter(e) { - invokeAction(this, 'onMouseEnter', e); + @action handleMouseEnter(e) { + if (this.args.onMouseEnter) { + this.args.onMouseEnter(e); + } } - handleMouseLeave(e) { - invokeAction(this, 'onMouseLeave', e); + @action handleMouseLeave(e) { + if (this.args.onMouseLeave) { + this.args.onMouseLeave(e); + } } } From 6865bf77202f56403a0ee2274ee70e932d499d56 Mon Sep 17 00:00:00 2001 From: Matthew Hartstonge Date: Thu, 14 Nov 2024 02:45:13 +1300 Subject: [PATCH 3/4] refactor(addon/mixins/proxiable-mixin): migrates to es5 setter/getter to support classic and glimmer components. --- addon/mixins/proxiable-mixin.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/addon/mixins/proxiable-mixin.js b/addon/mixins/proxiable-mixin.js index bcc269647..46546779f 100644 --- a/addon/mixins/proxiable-mixin.js +++ b/addon/mixins/proxiable-mixin.js @@ -1,4 +1,3 @@ -/* eslint-disable prettier/prettier */ /** * @module ember-paper */ @@ -13,7 +12,6 @@ import { ChildMixin } from 'ember-composability-tools'; * @extends Ember.Mixin */ export default Mixin.create(ChildMixin, { - classNameBindings: ['secondary:md-secondary'], shouldRegister: false, @@ -26,12 +24,12 @@ export default Mixin.create(ChildMixin, { this._super(...arguments); let parentComponent = this.parentComponent; if (parentComponent) { - parentComponent.set('mouseActive', true); + parentComponent.mouseActive = true; later(() => { if (parentComponent.isDestroyed) { return; } - parentComponent.set('mouseActive', false); + parentComponent.mouseActive = false; }, 100); } }, @@ -39,8 +37,8 @@ export default Mixin.create(ChildMixin, { focusIn() { this._super(...arguments); let parentComponent = this.parentComponent; - if (parentComponent && !parentComponent.get('mouseActive')) { - parentComponent.set('focused', true); + if (parentComponent && !parentComponent.mouseActive) { + parentComponent.focused = true; } }, @@ -48,7 +46,7 @@ export default Mixin.create(ChildMixin, { this._super(...arguments); let parentComponent = this.parentComponent; if (parentComponent) { - parentComponent.set('focused', false); + parentComponent.focused = false; } - } + }, }); From 3496e2a98f8f36a10f2606c550726eefab8bc0ac Mon Sep 17 00:00:00 2001 From: Matthew Hartstonge Date: Fri, 15 Nov 2024 15:57:53 +1300 Subject: [PATCH 4/4] docs(addon/components/paper-item): adds documentation to `focused`, `didInsertNode`, `willDestroyNode`. --- addon/components/paper-item.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/addon/components/paper-item.js b/addon/components/paper-item.js index 56d2ea1bc..1b3a90663 100644 --- a/addon/components/paper-item.js +++ b/addon/components/paper-item.js @@ -19,10 +19,14 @@ export default class PaperItem extends Component { element; /** - * Set of child grid tile components + * Array of child components. * @type {A} */ @tracked children; + /** + * marks whether the component is focused. Sets class `md-focused` if true. + * @type {boolean} + */ @tracked focused = false; constructor(owner, args) { @@ -35,6 +39,10 @@ export default class PaperItem extends Component { } } + /** + * Performs any required DOM setup. + * @param {HTMLElement} element + */ @action didInsertNode(element) { element.addEventListener('mouseenter', this.handleMouseEnter); element.addEventListener('mouseleave', this.handleMouseLeave); @@ -46,6 +54,10 @@ export default class PaperItem extends Component { // noop } + /** + * Performs any required DOM teardown. + * @param {HTMLElement} element + */ @action willDestroyNode(element) { element.removeEventListener('mouseenter', this.handleMouseEnter); element.removeEventListener('mouseleave', this.handleMouseLeave);