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

feat(addon/components/paper-item): converts to a glimmer component. #1304

Open
wants to merge 4 commits into
base: feature/glimmer-paper-form
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 59 additions & 24 deletions addon/components/paper-item.hbs
Original file line number Diff line number Diff line change
@@ -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|}}
<md-list-item
class='{{if this.hasProxiedComponent " md-proxy-focus"}}
{{if this.shouldBeClickable " md-clickable"}}
{{if this.focused " md-focused"}}
{{if this.hasPrimaryAction " _md-button-wrap"}}
{{@class}}'
role='listitem'
tabindex='-1'
title={{@title}}
{{did-insert this.didInsertNode}}
{{will-destroy this.willDestroyNode}}
{{on 'click' this.localOnClick}}
...attributes
>
{{#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}}
<div class="md-button md-no-style">
<PaperButton @class="md-no-style" @onClick={{@onClick}} @href={{@href}} @target={{@target}} @onMouseEnter={{action this.handleMouseEnter}} @onMouseLeave={{action this.handleMouseLeave}} />
<div class="md-list-item-inner">
{{#if this.hasPrimaryAction}}
<div class='md-button md-no-style'>
<PaperButton
@class='md-no-style'
@onClick={{@onClick}}
@href={{@href}}
@target={{@target}}
@onMouseEnter={{this.handleMouseEnter}}
@onMouseLeave={{this.handleMouseLeave}}
/>
<div class='md-list-item-inner'>
{{yield controls}}
</div>
<PaperRipple @noink={{this.noink}} @dimBackground={{true}} />
</div>
{{else}}
<div class='md-no-style md-list-item-inner'>
{{yield controls}}
<PaperRipple @noink={{this.noink}} @dimBackground={{true}} />
</div>
<PaperRipple
@noink={{this.noink}}
@dimBackground={{true}}/>
</div>
{{else}}
<div class="md-no-style md-list-item-inner">
{{yield controls}}
<PaperRipple
@noink={{this.noink}}
@dimBackground={{true}}/>
</div>
{{/if}}
{{/if}}

{{/with}}
{{/with}}
</md-list-item>
192 changes: 125 additions & 67 deletions addon/components/paper-item.js
Original file line number Diff line number Diff line change
@@ -1,104 +1,162 @@
/* 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;

/**
* 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) {
super(owner, args);

this.children = A([]);

if (this.args.role) {
this.role = this.args.role;
}
}

role = 'listitem';
tabindex = '-1';
/**
* Performs any required DOM setup.
* @param {HTMLElement} element
*/
@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;
/**
* Performs any required DOM teardown.
* @param {HTMLElement} element
*/
@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 &&
!!component.processProxy &&
!component.disabled &&
component.bubbles | !this.hasPrimaryAction
!!(component.bubbles || !this.hasPrimaryAction)
) {
component.processProxy();
}
});
}

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);
}
}
}
14 changes: 6 additions & 8 deletions addon/mixins/proxiable-mixin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable prettier/prettier */
/**
* @module ember-paper
*/
Expand All @@ -13,7 +12,6 @@ import { ChildMixin } from 'ember-composability-tools';
* @extends Ember.Mixin
*/
export default Mixin.create(ChildMixin, {

classNameBindings: ['secondary:md-secondary'],

shouldRegister: false,
Expand All @@ -26,29 +24,29 @@ 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);
}
},

focusIn() {
this._super(...arguments);
let parentComponent = this.parentComponent;
if (parentComponent && !parentComponent.get('mouseActive')) {
parentComponent.set('focused', true);
if (parentComponent && !parentComponent.mouseActive) {
parentComponent.focused = true;
}
},

focusOut() {
this._super(...arguments);
let parentComponent = this.parentComponent;
if (parentComponent) {
parentComponent.set('focused', false);
parentComponent.focused = false;
}
}
},
});
Loading