Skip to content

Commit

Permalink
feat(addon/components/paper-checkbox): converts to a glimmer component.
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewhartstonge committed Nov 14, 2024
1 parent 1e72457 commit ce01f39
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 84 deletions.
57 changes: 38 additions & 19 deletions addon/components/paper-checkbox.hbs
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
{{! template-lint-disable no-curly-component-invocation }}
<div class="md-container">
<div class="md-icon"></div>
<PaperRipple
@center={{true}}
@fitRipple={{true}}/>
</div>
{{#if (has-block)}}
<div class="md-label">
<span id={{this.labelId}}>
{{yield}}
</span>
<md-checkbox
class='md-checkbox md-default-theme
{{if this.isChecked " md-checked"}}
{{if this.indeterminate " md-indeterminate"}}
{{if @warn " md-warn"}}
{{if @accent " md-accent"}}
{{if @primary " md-primary"}}
{{if this.focused " md-focused"}}
{{@class}}'
aria-label={{@ariaLabel}}
aria-checked={{this.ariaChecked}}
aria-labelledby={{this.labelId}}
role='checkbox'
disabled={{this.disabled}}
tabindex={{if this.disabled '-1' '0'}}
{{did-insert this.didInsertNode}}
{{will-destroy this.willDestroyNode}}
{{on 'click' this.onClick}}
{{on 'keypress' this.onKeyPress}}
...attributes
>
<div class='md-container'>
<div class='md-icon'></div>
<PaperRipple @center={{true}} @fitRipple={{true}} />
</div>
{{else}}
<div class="md-label">
<span id={{this.labelId}}>
{{@label}}
</span>
</div>
{{/if}}
{{#if (has-block)}}
<div class='md-label'>
<span id={{this.labelId}}>
{{yield}}
</span>
</div>
{{else}}
<div class='md-label'>
<span id={{this.labelId}}>
{{@label}}
</span>
</div>
{{/if}}
</md-checkbox>
204 changes: 139 additions & 65 deletions addon/components/paper-checkbox.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,162 @@
/* eslint-disable ember/no-classic-components, ember/no-get, ember/no-mixins, ember/require-tagless-components */
/**
* @module ember-paper
*/
import Focusable from './-focusable';
import { inject as service } from '@ember/service';

import { computed } from '@ember/object';
import { not, and } from '@ember/object/computed';
import Component from '@ember/component';
import { action } from '@ember/object';
import { guidFor } from '@ember/object/internals';
import { assert } from '@ember/debug';
import FocusableMixin from 'ember-paper/mixins/focusable-mixin';
import ProxiableMixin from 'ember-paper/mixins/proxiable-mixin';
import { invokeAction } from 'ember-paper/utils/invoke-action';

/**
* @class PaperCheckbox
* @extends Ember.Component
* @uses FocusableMixin
* @uses ProxiableMixin
* @extends Component
*/
export default Component.extend(FocusableMixin, ProxiableMixin, {
tagName: 'md-checkbox',
classNames: ['md-checkbox', 'md-default-theme'],
classNameBindings: [
'isChecked:md-checked',
'indeterminate:md-indeterminate',
'warn:md-warn',
'accent:md-accent',
'primary:md-primary',
],

attributeBindings: [
'role:role',
'ariaLabel:aria-label',
'ariaChecked:aria-checked',
'labelId:aria-labelledby',
],

/* FocusableMixin Overrides */
focusOnlyOnKey: true,

constants: service(),
value: false,
role: 'checkbox',
notIndeterminate: not('indeterminate'),
isChecked: and('notIndeterminate', 'value'),

ariaChecked: computed('isChecked', 'indeterminate', function () {
export default class PaperCheckbox extends Focusable {
/**
* Service containing media query breakpoints and constants
*/
@service constants;

/**
* Reference to the component's DOM element
* @type {HTMLElement}
*/
element;
/**
* The parent this component is bound to.
* @type {Boolean}
*/
parent;
/**
* Marks whether the component should register itself to the supplied parent
* @type {Boolean}
*/
shouldRegister;
/**
* Marks whether the component should skip being proxied.
* @type {Boolean}
*/
skipProxy;
/**
* provides a globally unique component id for tracking bindings between aria
* tags and labels.
* @type {string}
*/
labelId;

/* Focusable Overrides */
focusOnlyOnKey = true;

constructor(owner, args) {
super(owner, args);

this.labelId = `${guidFor(args)}-label`;
this.shouldRegister = this.args.shouldRegister || false;
this.skipProxy = this.args.skipProxy || false;

if (this.shouldRegister) {
assert(
'A parent component should be supplied to <PaperCheckbox> when shouldRegister=true',
this.args.parentComponent
);
this.parent = this.args.parentComponent;
}

assert(
'<PaperCheckbox> requires an `onChange` action or null for no action.',
this.args.onChange !== undefined
);
}

/**
* Performs any required DOM setup.
* @param element
*/
@action didInsertNode(element) {
this.element = element;
this.registerListeners(element);

if (this.shouldRegister) {
this.parent.registerChild(this);
}
}

@action didUpdateNode() {
// noop
}

/**
* Performs any required DOM teardown.
* @param element
*/
@action willDestroyNode(element) {
this.unregisterListeners(element);
}

willDestroy() {
super.willDestroy(...arguments);

if (this.shouldRegister) {
this.parent.unregisterChild(this);
}
}

get indeterminate() {
return this.args.indeterminate || false;
}

get notIndeterminate() {
return !this.indeterminate;
}

get value() {
return this.args.value || false;
}

get isChecked() {
return this.notIndeterminate && this.value;
}

get ariaChecked() {
if (this.indeterminate) {
return 'mixed';
}

return this.isChecked ? 'true' : 'false';
}),
}

labelId: computed('elementId', function () {
return `${this.elementId}-label`;
}),
get bubbles() {
return this.args.bubbles === undefined || this.args.bubbles;
}

init() {
this._super(...arguments);
assert(
'{{paper-checkbox}} requires an `onChange` action or null for no action.',
this.onChange !== undefined
);
},

click() {
@action onClick(e) {
console.log('checkbox:onClick');
if (!this.disabled) {
invokeAction(this, 'onChange', !this.value);
if (this.args.onChange) {
this.args.onChange(!this.value);
}

// Prevent bubbling, if specified. If undefined, the event will bubble.
if (!this.bubbles) {
e.stopPropagation();
}
}
// Prevent bubbling, if specified. If undefined, the event will bubble.
return this.bubbles;
},
}

keyPress(ev) {
@action onKeyPress(e) {
if (
ev.which === this.get('constants.KEYCODE.SPACE') ||
ev.which === this.get('constants.KEYCODE.ENTER')
e.which === this.constants.KEYCODE.SPACE ||
e.which === this.constants.KEYCODE.ENTER
) {
ev.preventDefault();
this.click();
e.preventDefault();
this.onClick();
}
},
}

processProxy() {
invokeAction(this, 'onChange', !this.value);
},
});
console.log('checkbox:processProxy');
if (this.args.onChange) {
this.args.onChange(!this.value);
}
}
}

0 comments on commit ce01f39

Please sign in to comment.