-
-
Notifications
You must be signed in to change notification settings - Fork 333
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(addon/components/paper-radio-group): converts to a glimmer compo…
…nent.
- Loading branch information
1 parent
e566dc8
commit d86233e
Showing
2 changed files
with
161 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,31 @@ | ||
{{! template-lint-disable no-action }} | ||
{{yield (hash | ||
label=(component this.labelComponent | ||
setAriaLabelledby=(action (mut this.ariaLabelledby)) | ||
) | ||
radio=(component this.radioComponent | ||
toggle=this.toggle | ||
disabled=this.disabled | ||
groupValue=@groupValue | ||
onChange=(action "onChange") | ||
parentComponent=this | ||
shouldRegister=true) | ||
)}} | ||
<md-radio-group | ||
class="{{if this.focused " md-focused"}} {{@class}}" | ||
aria-label={{@ariaLabel}} | ||
aria-labelledby={{if this.hasLabel this.labelId}} | ||
disabled={{this.disabled}} | ||
role="radiogroup" | ||
tabindex="0" | ||
{{did-insert this.didInsertNode}} | ||
{{will-destroy this.willDestroyNode}} | ||
{{on 'keydown' this.onKeyDown}} {{! template-lint-disable no-down-event-binding }} | ||
...attributes | ||
> | ||
{{yield | ||
(hash | ||
label=(component | ||
this.labelComponent | ||
labelId=this.labelId | ||
setAriaLabelledby=this.didInsertLabel | ||
) | ||
radio=(component | ||
this.radioComponent | ||
toggle=this.toggle | ||
disabled=this.disabled | ||
groupValue=@groupValue | ||
onChange=this.onChange | ||
parentComponent=this | ||
shouldRegister=true | ||
) | ||
) | ||
}} | ||
</md-radio-group> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,171 @@ | ||
/* eslint-disable ember/no-actions-hash, 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 { filterBy, mapBy, notEmpty } from '@ember/object/computed'; | ||
import Component from '@ember/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { A } from '@ember/array'; | ||
import { assert } from '@ember/debug'; | ||
import FocusableMixin from 'ember-paper/mixins/focusable-mixin'; | ||
import { ParentMixin } from 'ember-composability-tools'; | ||
import { action } from '@ember/object'; | ||
import { guidFor } from '@ember/object/internals'; | ||
import { isPresent } from '@ember/utils'; | ||
import { invokeAction } from 'ember-paper/utils/invoke-action'; | ||
|
||
/** | ||
* @class PaperRadioGroup | ||
* @extends Ember.Component | ||
* @uses FocusableMixin | ||
* @uses ParentMixin | ||
*/ | ||
export default Component.extend(FocusableMixin, ParentMixin, { | ||
tagName: 'md-radio-group', | ||
tabindex: 0, | ||
export default class PaperRadioGroup extends Focusable { | ||
@service constants; | ||
|
||
/** | ||
* Reference to the component's DOM element | ||
* @type {HTMLElement} | ||
*/ | ||
element; | ||
/** | ||
* labelComponent specifies the component to be yielded as a radio group label. | ||
* @type {string} | ||
*/ | ||
labelComponent; | ||
/** | ||
* radioComponent specifies the component to be yielded as a radio. | ||
* @type {string} | ||
*/ | ||
radioComponent; | ||
/** | ||
* provides a globally unique component id for tracking bindings between aria | ||
* tags and labels. | ||
* @type {string} | ||
*/ | ||
labelId; | ||
/** | ||
* enables toggling the returned value from a child component | ||
* @type {boolean} | ||
*/ | ||
toggle; | ||
|
||
/* FocusableMixin Overrides */ | ||
focusOnlyOnKey: true, | ||
/* Focusable Overrides */ | ||
focusOnlyOnKey = true; | ||
|
||
radioComponent: 'paper-radio', | ||
labelComponent: 'paper-radio-group-label', | ||
role: 'radiogroup', | ||
constants: service(), | ||
/** | ||
* Array of child components | ||
* @type {A} | ||
*/ | ||
@tracked children; | ||
/** | ||
* tracks whether the label id should be displayed. | ||
* @type {boolean} | ||
*/ | ||
@tracked hasLabel; | ||
|
||
// Lifecycle hooks | ||
init() { | ||
this._super(...arguments); | ||
constructor(owner, args) { | ||
super(owner, args); | ||
|
||
this.children = A([]); | ||
this.hasLabel = false; | ||
this.labelId = `${guidFor(this)}-label`; | ||
this.toggle = this.args.toggle || false; | ||
|
||
this.labelComponent = this.args.labelComponent || 'paper-radio-group-label'; | ||
this.radioComponent = this.args.radioComponent || 'paper-radio'; | ||
|
||
assert( | ||
'{{paper-radio-group}} requires an `onChange` action or null for no action', | ||
this.onChange !== undefined | ||
'<PaperRadioGroup> requires an `onChange` action or null for no action', | ||
this.args.onChange !== undefined | ||
); | ||
}, | ||
} | ||
|
||
/** | ||
* Performs any required DOM setup. | ||
* @param element | ||
*/ | ||
@action didInsertNode(element) { | ||
this.registerListeners(element); | ||
} | ||
|
||
attributeBindings: ['role', 'ariaLabelledby:aria-labelledby'], | ||
@action didUpdateNode() { | ||
// noop | ||
} | ||
|
||
enabledChildRadios: filterBy('childComponents', 'disabled', false), | ||
childValues: mapBy('enabledChildRadios', 'value'), | ||
hasLabel: notEmpty('labelNode'), | ||
/** | ||
* Performs any required DOM teardown. | ||
* @param element | ||
*/ | ||
@action willDestroyNode(element) { | ||
this.unregisterListeners(element); | ||
} | ||
|
||
keyDown(ev) { | ||
/** | ||
* Registers a child form component | ||
* @param {Component} child - The form component to register | ||
*/ | ||
@action registerChild(child) { | ||
this.children.pushObject(child); | ||
} | ||
/** | ||
* Removes a registered child form component | ||
* @param {Component} child - The form component to unregister | ||
*/ | ||
@action unregisterChild(child) { | ||
this.children.removeObject(child); | ||
} | ||
|
||
get enabledChildRadios() { | ||
let filteredChildren = A(this.children.filter((c) => c.disabled === false)); | ||
return filteredChildren; | ||
} | ||
get childValues() { | ||
return this.enabledChildRadios.map((c) => c.value); | ||
} | ||
/** | ||
* provides a callback to notify if a label has been injected into the DOM | ||
* to enable aria-labelledby being rendered on the radio group. | ||
*/ | ||
@action didInsertLabel() { | ||
this.hasLabel = true; | ||
} | ||
|
||
@action onKeyDown(ev) { | ||
switch (ev.which) { | ||
case this.get('constants.KEYCODE.LEFT_ARROW'): | ||
case this.get('constants.KEYCODE.UP_ARROW'): | ||
case this.constants.KEYCODE.LEFT_ARROW: | ||
case this.constants.KEYCODE.UP_ARROW: | ||
ev.preventDefault(); | ||
this.select(-1); | ||
break; | ||
case this.get('constants.KEYCODE.RIGHT_ARROW'): | ||
case this.get('constants.KEYCODE.DOWN_ARROW'): | ||
case this.constants.KEYCODE.RIGHT_ARROW: | ||
case this.constants.KEYCODE.DOWN_ARROW: | ||
ev.preventDefault(); | ||
this.select(1); | ||
break; | ||
} | ||
}, | ||
} | ||
|
||
select(increment) { | ||
let groupValue = this.groupValue; | ||
let groupValue = this.args.groupValue; | ||
let index = 0; | ||
|
||
if (isPresent(groupValue)) { | ||
index = this.childValues.indexOf(groupValue); | ||
index += increment; | ||
let length = this.get('childValues.length'); | ||
let length = this.childValues.length; | ||
index = ((index % length) + length) % length; | ||
} | ||
|
||
let childRadio = this.enabledChildRadios.objectAt(index); | ||
childRadio.set('focused', true); | ||
invokeAction(this, 'onChange', childRadio.get('value')); | ||
}, | ||
|
||
actions: { | ||
onChange(value) { | ||
invokeAction(this, 'onChange', value); | ||
}, | ||
}, | ||
}); | ||
if (childRadio) { | ||
childRadio.focused = true; | ||
if (this.args.onChange) { | ||
this.args.onChange(childRadio.value); | ||
} | ||
} | ||
} | ||
|
||
@action onChange(value) { | ||
if (this.args.onChange) { | ||
this.args.onChange(value); | ||
} | ||
} | ||
} |