Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
205 lines
6.1 KiB
JavaScript
Executable file
205 lines
6.1 KiB
JavaScript
Executable file
'use strict';
|
|
/* global ComponentUtils */
|
|
|
|
window.GaiaRadio = (function(win) {
|
|
// Extend from the HTMLElement prototype
|
|
var proto = Object.create(HTMLElement.prototype);
|
|
|
|
// Allow baseurl to be overridden (used for demo page)
|
|
var baseurl = window.GaiaRadioBaseurl ||
|
|
'/shared/elements/gaia_radio/';
|
|
|
|
proto.createdCallback = function() {
|
|
var shadow = this.createShadowRoot();
|
|
this._template = template.content.cloneNode(true);
|
|
|
|
this._wrapper = this._template.getElementById('radio');
|
|
this._wrapper.addEventListener('click', this.handleClick.bind(this));
|
|
|
|
// The default 'radio' accessibility role could be overridden.
|
|
if (this.dataset.role) {
|
|
this._wrapper.setAttribute('role', this.dataset.role);
|
|
}
|
|
|
|
if (!this.hasAttribute('role')) {
|
|
// The root element has no accessibility use, purge it from the tree.
|
|
this.setAttribute('role', 'presentation');
|
|
}
|
|
|
|
this.configureClass();
|
|
|
|
shadow.appendChild(this._template);
|
|
|
|
this.checked = this.hasAttribute('checked');
|
|
this._wrapper.setAttribute('aria-checked', this.checked);
|
|
|
|
// When events are triggered on content nodes, they do not bubble to
|
|
// our custom element. We add an event listener on our children so we can
|
|
// intercept the click and, process the state change, and notify listeners.
|
|
// Platform bug 887541.
|
|
setTimeout(function nextTick() {
|
|
var label = this.querySelector('label, p');
|
|
if (!label) {
|
|
return;
|
|
}
|
|
label.addEventListener('click', this.handleClick.bind(this));
|
|
}.bind(this));
|
|
|
|
ComponentUtils.style.call(this, baseurl);
|
|
|
|
// Proxy RTL changes to the shadow root so we can style for RTL.
|
|
var dirObserver = new MutationObserver(this.updateInternalDir.bind(this));
|
|
dirObserver.observe(document.documentElement, {
|
|
attributeFilter: ['dir'],
|
|
attributes: true
|
|
});
|
|
this.updateInternalDir();
|
|
};
|
|
|
|
proto.updateInternalDir = function() {
|
|
var internal = this.shadowRoot.firstElementChild;
|
|
if (document.documentElement.dir === 'rtl') {
|
|
internal.setAttribute('dir', 'rtl');
|
|
} else {
|
|
internal.removeAttribute('dir');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handles a click event on the shadow dom.
|
|
* We handle checking/unchecking of radio elements and proxy the click
|
|
* event to any click listeners on the gaia-radio. This is a nice transition
|
|
* that preserves backwards behavior and should make it easier to port apps.
|
|
*/
|
|
proto.handleClick = function(e) {
|
|
// We add this event listener twice (see above) on both the content and
|
|
// custom element nodes. We need to stop the event propagation to prevent
|
|
// this event from firing against both nodes.
|
|
e.stopImmediatePropagation();
|
|
|
|
// Dispatch a click event to any listeners to the app.
|
|
// We should be able to remove this when bug 887541 lands.
|
|
var event = new MouseEvent('click', {
|
|
view: window,
|
|
bubbles: true,
|
|
cancelable: true
|
|
});
|
|
this.dispatchEvent(event);
|
|
|
|
// If this radio component is already checked, don't do anything.
|
|
if (this.checked) {
|
|
return;
|
|
}
|
|
|
|
if (!event.defaultPrevented) {
|
|
this.checked = !this.checked;
|
|
}
|
|
|
|
// Dispatch a change event for the gaia-switch.
|
|
this.dispatchEvent(new CustomEvent('change', {
|
|
bubbles: true,
|
|
cancelable: false
|
|
}));
|
|
};
|
|
|
|
/**
|
|
* Configures the class for the element.
|
|
*/
|
|
proto.configureClass = function() {
|
|
this._wrapper.className = this.className;
|
|
this._wrapper.classList.toggle('checked', this._checked);
|
|
};
|
|
|
|
/**
|
|
* Helper method to remove the component details.
|
|
*/
|
|
proto.hideDetails = function() {
|
|
this.shadowRoot.querySelector('.details').style.display = 'none';
|
|
};
|
|
|
|
/**
|
|
* Proxy className property to the wrapper.
|
|
*/
|
|
proto.attributeChangedCallback = function(name, from, to) {
|
|
if (name === 'class') {
|
|
this._wrapper.className = to;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle setting/getting of the checked property.
|
|
*/
|
|
Object.defineProperty(proto, 'checked', {
|
|
get: function() {
|
|
return this._checked;
|
|
},
|
|
set: function(value) {
|
|
// Return early if we have not initialized.
|
|
// In some cases a radio of the same name might try to toggle this one
|
|
// before we've run the createdCallback.
|
|
if (!this._template) {
|
|
return;
|
|
}
|
|
if (value) {
|
|
// Uncheck other radio elements with the same name and check this one.
|
|
var relevant = document.querySelectorAll(
|
|
'gaia-radio[name="' + this.getAttribute('name') + '"]');
|
|
for (var i = 0, iLen = relevant.length; i < iLen; i++) {
|
|
relevant[i].checked = false;
|
|
}
|
|
this.setAttribute('checked', true);
|
|
} else {
|
|
this.removeAttribute('checked');
|
|
}
|
|
this._wrapper.classList.toggle('checked', value);
|
|
this._wrapper.setAttribute('aria-checked', value);
|
|
this._checked = value;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Proxy the name property to the input element.
|
|
*/
|
|
Object.defineProperty( proto, 'name', {
|
|
get: function() {
|
|
return this.getAttribute('name');
|
|
},
|
|
set: function(value) {
|
|
this.setAttribute('name', value);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Proxy the value property to the input element.
|
|
*/
|
|
Object.defineProperty( proto, 'value', {
|
|
get: function() {
|
|
return this.getAttribute('value');
|
|
},
|
|
set: function(value) {
|
|
this.setAttribute('value', value);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Proxy the input type.
|
|
*/
|
|
Object.defineProperty( proto, 'type', {
|
|
get: function() {
|
|
return 'gaia-radio';
|
|
}
|
|
});
|
|
|
|
// Handle both p and label elements for now to support existing
|
|
// building blocks. Mainly radio labels and inputs inside a list
|
|
// as this is a rather common use case.
|
|
var template = document.createElement('template');
|
|
template.innerHTML = '<span role="radio" id="radio">' +
|
|
'<span><content select="p,label"></content></span>' +
|
|
'<div class="details"><content select="details"></content></div>' +
|
|
'<div class="divider"></div>' +
|
|
'</span>';
|
|
|
|
// Register and return the constructor
|
|
return document.registerElement('gaia-radio', { prototype: proto });
|
|
})(window);
|