Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
247
gaia/tv_apps/smart-system/js/action_menu.js
Executable file
247
gaia/tv_apps/smart-system/js/action_menu.js
Executable file
|
|
@ -0,0 +1,247 @@
|
|||
/* global focusManager */
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
/**
|
||||
* ActionMenu displays a list of user-selectable actions in an overlay.
|
||||
* An example of this would be when the user selects a single activity form
|
||||
* a list of several activities. Each ActionMenu instance maintains its own
|
||||
* dom and event listeners.
|
||||
* @class ActionMenu
|
||||
* @param {Array} listItems An array of objects to display.
|
||||
* @param {String} titleL10nId The l10n id of the header.
|
||||
* @param {Function} successCb Called when the user selects an option.
|
||||
* @param {Function} cancelCb Called when the menu is cancelled.
|
||||
* @param {Boolean} preventFocusChange Set to true to prevent focus changing.
|
||||
*/
|
||||
function ActionMenu(listItems, titleL10nId, successCb, cancelCb,
|
||||
preventFocusChange) {
|
||||
this.onselected = successCb || function() {};
|
||||
this.oncancel = cancelCb || function() {};
|
||||
this.listItems = listItems;
|
||||
this.titleL10nId = titleL10nId;
|
||||
}
|
||||
|
||||
ActionMenu.prototype = {
|
||||
|
||||
/**
|
||||
* Whether or not the ActionMenu is visible.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @return {Boolean} The ActionMenu is visible.
|
||||
*/
|
||||
get visible() {
|
||||
return this.container.classList.contains('visible');
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds dom and adds event listeners
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
start: function() {
|
||||
// Create the structure
|
||||
this.container = document.createElement('form');
|
||||
this.container.dataset.type = 'action';
|
||||
this.container.setAttribute('role', 'dialog');
|
||||
this.container.setAttribute('data-z-index-level', 'action-menu');
|
||||
|
||||
// An action menu has a mandatory header
|
||||
this.header = document.createElement('header');
|
||||
if (this.titleL10nId !== undefined) {
|
||||
this.header.setAttribute('data-l10n-id', this.titleL10nId);
|
||||
}
|
||||
|
||||
this.container.appendChild(this.header);
|
||||
|
||||
// Following our paradigm we need a cancel
|
||||
this.cancel = document.createElement('button');
|
||||
this.cancel.dataset.action = 'cancel';
|
||||
this.cancel.dataset.l10nId = 'cancel';
|
||||
|
||||
// We have a menu with all the options
|
||||
this.menu = document.createElement('menu');
|
||||
|
||||
this.container.appendChild(this.menu);
|
||||
this.container.classList.add('visible');
|
||||
|
||||
// We append to System app (actually to '#screen')
|
||||
var screen = document.getElementById('screen');
|
||||
screen.appendChild(this.container);
|
||||
screen.classList.add('action-menu');
|
||||
|
||||
this.buildMenu(this.listItems);
|
||||
|
||||
this.container.addEventListener('submit', this);
|
||||
this.menu.addEventListener('click', this);
|
||||
|
||||
window.addEventListener('attentionopened', this, true);
|
||||
window.addEventListener('screenchange', this, true);
|
||||
window.addEventListener('home', this);
|
||||
window.addEventListener('holdhome', this);
|
||||
window.addEventListener('sheets-gesture-begin', this);
|
||||
|
||||
focusManager.addUI(this);
|
||||
focusManager.focus();
|
||||
if (this.preventFocusChange) {
|
||||
this.menu.addEventListener('mousedown', this.preventFocusChange);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the dom and stops event listeners
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
stop: function() {
|
||||
var screen = document.getElementById('screen');
|
||||
focusManager.removeUI(this);
|
||||
screen.removeChild(this.container);
|
||||
screen.classList.remove('action-menu');
|
||||
|
||||
window.removeEventListener('attentionopened', this, true);
|
||||
window.removeEventListener('screenchange', this, true);
|
||||
window.removeEventListener('home', this);
|
||||
window.removeEventListener('holdhome', this);
|
||||
window.removeEventListener('sheets-gesture-begin', this);
|
||||
|
||||
if (this.preventFocusChange) {
|
||||
this.menu.removeEventListener('mousedown', this.preventFocusChange);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds the dom for the menu.
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
buildMenu: function(items) {
|
||||
this.menu.innerHTML = '';
|
||||
items.forEach(function traveseItems(item) {
|
||||
var action = document.createElement('button');
|
||||
action.dataset.value = item.value;
|
||||
action.textContent = item.label;
|
||||
|
||||
if (item.icon) {
|
||||
action.classList.add(item.iconClass || 'icon');
|
||||
action.style.backgroundImage = 'url(' + item.icon + ')';
|
||||
}
|
||||
this.menu.appendChild(action);
|
||||
}, this);
|
||||
this.cancel.setAttribute('data-l10n-id', 'cancel');
|
||||
this.menu.appendChild(this.cancel);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides the ActionMenu.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @param {Function} callback The callback to call after hiding.
|
||||
*/
|
||||
hide: function(callback) {
|
||||
this.container.classList.remove('visible');
|
||||
this.stop();
|
||||
// focus back to the top most window/overlay.
|
||||
focusManager.focus();
|
||||
if (callback && typeof callback === 'function') {
|
||||
setTimeout(callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When IME switcher shows, prevent the keyboard focus getting changed.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @param {DOMEvent} evt The event.
|
||||
*/
|
||||
preventFocusChange: function(evt) {
|
||||
evt.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* General event handler interface.
|
||||
* Handles submission and cancellation events.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @param {DOMEvent} evt The event.
|
||||
*/
|
||||
handleEvent: function(evt) {
|
||||
var target = evt.target;
|
||||
var type = evt.type;
|
||||
switch (type) {
|
||||
case 'submit':
|
||||
evt.preventDefault();
|
||||
break;
|
||||
case 'screenchange':
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!evt.detail.screenEnabled) {
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'click':
|
||||
evt.preventDefault();
|
||||
var action = target.dataset.action;
|
||||
if (action && action === 'cancel') {
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
return;
|
||||
}
|
||||
|
||||
var value = target.dataset.value;
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
value = parseInt(value);
|
||||
this.hide(this.onselected.bind(this, value));
|
||||
break;
|
||||
|
||||
case 'home':
|
||||
case 'holdhome':
|
||||
case 'sheets-gesture-begin':
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
break;
|
||||
|
||||
case 'attentionopened':
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether or not the ActionMenu is visible.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @return {Boolean} if the container is focusible, return true
|
||||
*/
|
||||
isFocusable: function() {
|
||||
return this.container && this.visible;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the z-index value of container
|
||||
* @memberof ActionMenu.prototype
|
||||
* @return {HTMLElement} the container
|
||||
*/
|
||||
getElement: function() {
|
||||
return this.container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus cancel button in ActionMenu
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
focus: function() {
|
||||
if (this.cancel) {
|
||||
document.activeElement.blur();
|
||||
this.cancel.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.ActionMenu = ActionMenu;
|
||||
|
||||
}(window));
|
||||
Loading…
Reference in a new issue