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.
1
gaia/shared/elements/config.js
Executable file
|
|
@ -0,0 +1 @@
|
|||
window.COMPONENTS_BASE_URL = '/shared/elements/';
|
||||
14
gaia/shared/elements/confirmation-message.html
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
<element name="confirmation-message" extends="form">
|
||||
<template>
|
||||
|
||||
<section>
|
||||
<h1>Confirmation title</h1>
|
||||
<p>Confirmation message</p>
|
||||
</section>
|
||||
<menu>
|
||||
<button>Action 1</button>
|
||||
<button>Action 2</button>
|
||||
</menu>
|
||||
|
||||
</template>
|
||||
</element>
|
||||
12
gaia/shared/elements/contacts/contact_in_overlay.html
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
<element name="contact-in-overlay" extends="button">
|
||||
<template>
|
||||
|
||||
<div class="js-name ci__name ellipsis" dir="auto"></div>
|
||||
<div class="ci__details">
|
||||
<span class="js-tel-type ci__tel-type ellipsis" dir="auto"></span>
|
||||
<span class="js-separator"></span>
|
||||
<span class="js-tel ellipsis" dir="ltr"></span>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</element>
|
||||
10
gaia/shared/elements/contacts/contact_list_overlay.html
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
<element name="contact-list-overlay" extends="form">
|
||||
<template>
|
||||
|
||||
<header></header>
|
||||
<menu id="contact-list">
|
||||
<button id="contact-list-overlay-cancel" data-l10n-id="cancel">Cancel</button>
|
||||
</menu>
|
||||
|
||||
</template>
|
||||
</element>
|
||||
28
gaia/shared/elements/font-fit/.bower.json
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "font-fit",
|
||||
"main": "font-fit.js",
|
||||
"version": "0.3.1",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"README.md",
|
||||
"index.html",
|
||||
"package.json"
|
||||
],
|
||||
"homepage": "https://github.com/gaia-components/font-fit",
|
||||
"_release": "0.3.1",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.3.1",
|
||||
"commit": "07a497519b335a3d86a66268f990d8844b83d517"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/font-fit.git",
|
||||
"_target": "~0.3.0",
|
||||
"_originalSource": "gaia-components/font-fit"
|
||||
}
|
||||
18
gaia/shared/elements/font-fit/bower.json
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "font-fit",
|
||||
"main": "font-fit.js",
|
||||
"version": "0.3.1",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"README.md",
|
||||
"index.html",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
132
gaia/shared/elements/font-fit/font-fit.js
Executable file
|
|
@ -0,0 +1,132 @@
|
|||
;(function(define){define(function(require,exports,module){
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Simple logger.
|
||||
*
|
||||
* @return {Function}
|
||||
*/
|
||||
var debug = 0 ? console.log.bind(console) : function(){};
|
||||
|
||||
/**
|
||||
* Global canvas cache.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Default min/max font-size.
|
||||
*
|
||||
* @type {Number}
|
||||
*/
|
||||
var MIN = 16;
|
||||
var MAX = 24;
|
||||
|
||||
/**
|
||||
* The number of pixels to subtract from
|
||||
* the given `config.space` to ensure
|
||||
* HTML text doesn't overflow container.
|
||||
*
|
||||
* Ideally we would use 1px, but in some
|
||||
* cases italicised text in canvas is ~2px
|
||||
* longer than the same text in HTML.
|
||||
*
|
||||
* http://bugzil.la/1126391
|
||||
*
|
||||
* @type {Number}
|
||||
*/
|
||||
var BUFFER = 3;
|
||||
|
||||
/**
|
||||
* Get the font-size that closest fits
|
||||
* the given space with the given font.
|
||||
*
|
||||
* Config:
|
||||
*
|
||||
* - {String} `text` The text string
|
||||
* - {String} `font` Font shorthand string
|
||||
* - {Number} `space` Width (px) to fit the text into
|
||||
* - {Number} `min` Min font-size (px) (optional)
|
||||
* - {Number} `max` Max font-size (px) (optional)
|
||||
*
|
||||
* @param {Object} config
|
||||
* @return {Object} {fontSize,overflowing,textWidth}
|
||||
*/
|
||||
module.exports = function(config) {
|
||||
debug('font fit', config);
|
||||
var space = config.space - BUFFER;
|
||||
var min = config.min || MIN;
|
||||
var max = config.max || MAX;
|
||||
var text = trim(config.text);
|
||||
var fontSize = max;
|
||||
var textWidth;
|
||||
var font;
|
||||
|
||||
do {
|
||||
font = config.font.replace(/\d+px/, fontSize + 'px');
|
||||
textWidth = getTextWidth(text, font);
|
||||
} while (textWidth > space && fontSize !== min && fontSize--);
|
||||
|
||||
return {
|
||||
textWidth: textWidth,
|
||||
fontSize: fontSize,
|
||||
overflowing: textWidth > space
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the width of the given text
|
||||
* with the given font style.
|
||||
*
|
||||
* @param {String} text
|
||||
* @param {String} font (CSS shorthand)
|
||||
* @return {Number} (px)
|
||||
*/
|
||||
function getTextWidth(text, font) {
|
||||
var ctx = getCanvasContext(font);
|
||||
var width = ctx.measureText(text).width;
|
||||
debug('got text width', width);
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a canvas context configured
|
||||
* to the given font style.
|
||||
*
|
||||
* @param {String} font
|
||||
* @return {CanvasRenderingContext2D}
|
||||
*/
|
||||
function getCanvasContext(font) {
|
||||
debug('get canvas context', font);
|
||||
|
||||
var cached = cache[font];
|
||||
if (cached) { return cached; }
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.setAttribute('moz-opaque', 'true');
|
||||
canvas.setAttribute('width', '1px');
|
||||
canvas.setAttribute('height', '1px');
|
||||
debug('created canvas', canvas);
|
||||
|
||||
var ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
ctx.font = font;
|
||||
|
||||
return cache[font] = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim leading, trailing
|
||||
* and excess whitespace.
|
||||
*
|
||||
* @param {String} text
|
||||
* @return {String}
|
||||
*/
|
||||
function trim(text) {
|
||||
return text.replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('font-fit',this));
|
||||
41
gaia/shared/elements/gaia-button/.bower.json
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "gaia-button",
|
||||
"version": "0.1.0",
|
||||
"main": "gaia-button.js",
|
||||
"homepage": "https://github.com/gaia-components/gaia-button",
|
||||
"authors": [
|
||||
"cyee@mozilla.com"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"base": "gaia-components/base",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts",
|
||||
"gaia-icons": "gaia-components/gaia-icons",
|
||||
"gaia-theme": "gaia-components/gaia-theme"
|
||||
},
|
||||
"dependencies": {
|
||||
"gaia-component": "gaia-components/gaia-component#~0.4.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"gaia-component": "~0.4.0"
|
||||
},
|
||||
"_release": "0.1.0",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.1.0",
|
||||
"commit": "f6ab06010343da9f69cad963fc817d1e8e663c1e"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-button.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "gaia-components/gaia-button"
|
||||
}
|
||||
32
gaia/shared/elements/gaia-button/bower.json
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "gaia-button",
|
||||
"version": "0.0.5",
|
||||
"main": "gaia-button.js",
|
||||
"homepage": "https://github.com/gaia-components/gaia-button",
|
||||
"authors": [
|
||||
"cyee@mozilla.com"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"base": "gaia-components/base",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts",
|
||||
"gaia-icons": "gaia-components/gaia-icons",
|
||||
"gaia-theme": "gaia-components/gaia-theme"
|
||||
},
|
||||
"dependencies": {
|
||||
"gaia-component": "gaia-components/gaia-component#~0.4.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"gaia-component": "~0.4.0"
|
||||
}
|
||||
}
|
||||
234
gaia/shared/elements/gaia-button/gaia-button.js
Executable file
|
|
@ -0,0 +1,234 @@
|
|||
/* globals define */
|
||||
(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Simple logger
|
||||
* @type {Function}
|
||||
*/
|
||||
var debug = 0 ? console.log.bind(console) : function() {};
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-button', {
|
||||
extends: HTMLButtonElement.prototype,
|
||||
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
// Configure
|
||||
this.disabled = this.getAttribute('disabled');
|
||||
|
||||
// process everything that doesn't affect user interaction
|
||||
// after the component is created
|
||||
setTimeout(() => this.makeAccessible());
|
||||
},
|
||||
|
||||
/**
|
||||
* Accessibility enhancements.
|
||||
* Read gaia-button as button.
|
||||
* make it tabable
|
||||
* read its disabled state
|
||||
*/
|
||||
makeAccessible: function() {
|
||||
this.setAttribute('role', 'button');
|
||||
|
||||
// Make tabable
|
||||
this.tabIndex = 0;
|
||||
|
||||
if (this.disabled) {
|
||||
this.setAttribute('aria-disabled', true);
|
||||
}
|
||||
},
|
||||
|
||||
attrs: {
|
||||
circular: {
|
||||
get: function() { return this.getAttribute('circular'); },
|
||||
set: function(value) {
|
||||
value = !!(value === '' || value);
|
||||
if (value) {
|
||||
this.setAttribute('circular', '');
|
||||
} else {
|
||||
this.removeAttribute('circular');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
disabled: {
|
||||
get: function() { return this._disabled; },
|
||||
set: function(value) {
|
||||
value = !!(value || value === '');
|
||||
if (this._disabled === value) { return; }
|
||||
debug('set disabled', value);
|
||||
this._disabled = value;
|
||||
if (value) {
|
||||
this.setAttr('disabled', '');
|
||||
this.setAttribute('aria-disabled', true);
|
||||
} else {
|
||||
this.removeAttr('disabled');
|
||||
this.removeAttribute('aria-disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="inner">
|
||||
<div class="background"></div>
|
||||
<div class="content"><content></content></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
height: 50px;
|
||||
min-width: 50%;
|
||||
border-radius: 50px;
|
||||
margin: var(--base-m, 18px) 0;
|
||||
outline: 0;
|
||||
font-style: italic;
|
||||
font-size: 17px;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
line-height: 1;
|
||||
|
||||
background:
|
||||
var(--button-background,
|
||||
var(--input-background,
|
||||
var(--background-plus,
|
||||
#fff)));
|
||||
|
||||
color:
|
||||
var(--button-color,
|
||||
var(--text-color,
|
||||
inherit));
|
||||
|
||||
box-shadow:
|
||||
var(--button-box-shadow,
|
||||
var(--box-shadow,
|
||||
none));
|
||||
|
||||
transition: color 0ms 300ms;
|
||||
}
|
||||
|
||||
@media(min-width:500px) {
|
||||
:host { min-width: 140px; }
|
||||
}
|
||||
|
||||
/**
|
||||
* :active
|
||||
*/
|
||||
|
||||
:host(:active) {
|
||||
color: var(--button-color-active, #fff);
|
||||
box-shadow: var(--button-box-shadow-active, none);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* [circular]
|
||||
*/
|
||||
|
||||
:host([circular]) {
|
||||
width: 50px;
|
||||
min-width: 0;
|
||||
max-width: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.background {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
|
||||
transition: opacity 500ms 200ms;
|
||||
|
||||
background:
|
||||
var(--button-background-active,
|
||||
var(--highlight-color,
|
||||
#333));
|
||||
}
|
||||
|
||||
:active .background {
|
||||
transition: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Content
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 1. In some cases events seems to be getting
|
||||
* swallowed by text-nodes. Ignoring pointer-
|
||||
* events means we can listen on parent nodes
|
||||
* instead.
|
||||
*/
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: 100%;
|
||||
padding: 0 18px;
|
||||
pointer-events: none; /* 1 */
|
||||
}
|
||||
|
||||
[circular] .content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::content i:before {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
::content i {
|
||||
margin-left: -2px;
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
::content i + span,
|
||||
::content span + i {
|
||||
-moz-margin-start: 8px;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-button',this));
|
||||
28
gaia/shared/elements/gaia-component/.bower.json
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "gaia-component",
|
||||
"version": "0.4.0",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-component.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"README.md",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"homepage": "https://github.com/gaia-components/gaia-component",
|
||||
"_release": "0.4.0",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.3.5",
|
||||
"commit": "e29dfbd8e07a96cbe521df8067120b78c306b584"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-component.git",
|
||||
"_target": "~0.4.0",
|
||||
"_originalSource": "gaia-components/gaia-component"
|
||||
}
|
||||
18
gaia/shared/elements/gaia-component/bower.json
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "gaia-component",
|
||||
"version": "0.4.0",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-component.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"README.md",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
460
gaia/shared/elements/gaia-component/gaia-component.js
Executable file
|
|
@ -0,0 +1,460 @@
|
|||
/* globals define */
|
||||
;(function(define){'use strict';define(function(require,exports,module){
|
||||
/**
|
||||
* Locals
|
||||
*/
|
||||
var textContent = Object.getOwnPropertyDescriptor(Node.prototype,
|
||||
'textContent');
|
||||
var innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
|
||||
var removeAttribute = Element.prototype.removeAttribute;
|
||||
var setAttribute = Element.prototype.setAttribute;
|
||||
var noop = function() {};
|
||||
|
||||
/**
|
||||
* Register a new component.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} props
|
||||
* @return {constructor}
|
||||
* @public
|
||||
*/
|
||||
exports.register = function(name, props) {
|
||||
var baseProto = getBaseProto(props.extends);
|
||||
var template = props.template || baseProto.templateString;
|
||||
|
||||
// Components are extensible by default but can be declared
|
||||
// as non extensible as an optimization to avoid
|
||||
// storing the template strings
|
||||
var extensible = props.extensible = props.hasOwnProperty('extensible')?
|
||||
props.extensible : true;
|
||||
|
||||
// Clean up
|
||||
delete props.extends;
|
||||
|
||||
// Pull out CSS that needs to be in the light-dom
|
||||
if (template) {
|
||||
// Stores the string to be reprocessed when
|
||||
// a new component extends this one
|
||||
if (extensible && props.template) {
|
||||
props.templateString = props.template;
|
||||
}
|
||||
|
||||
var output = processCss(template, name);
|
||||
|
||||
props.template = document.createElement('template');
|
||||
props.template.innerHTML = output.template;
|
||||
props.lightCss = output.lightCss;
|
||||
|
||||
props.globalCss = props.globalCss || '';
|
||||
props.globalCss += output.globalCss;
|
||||
}
|
||||
|
||||
// Inject global CSS into the document,
|
||||
// and delete as no longer needed
|
||||
injectGlobalCss(props.globalCss);
|
||||
delete props.globalCss;
|
||||
|
||||
// Merge base getter/setter attributes with the user's,
|
||||
// then define the property descriptors on the prototype.
|
||||
var descriptors = mixin(props.attrs || {}, base.descriptors);
|
||||
|
||||
// Store the orginal descriptors somewhere
|
||||
// a little more private and delete the original
|
||||
props._attrs = props.attrs;
|
||||
delete props.attrs;
|
||||
|
||||
// Create the prototype, extended from base and
|
||||
// define the descriptors directly on the prototype
|
||||
var proto = createProto(baseProto, props);
|
||||
Object.defineProperties(proto, descriptors);
|
||||
|
||||
// Register the custom-element and return the constructor
|
||||
try {
|
||||
return document.registerElement(name, { prototype: proto });
|
||||
} catch (e) {
|
||||
if (e.name !== 'NotSupportedError') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var base = {
|
||||
properties: {
|
||||
GaiaComponent: true,
|
||||
attributeChanged: noop,
|
||||
attached: noop,
|
||||
detached: noop,
|
||||
created: noop,
|
||||
|
||||
createdCallback: function() {
|
||||
if (this.dirObserver) { addDirObserver(); }
|
||||
injectLightCss(this);
|
||||
this.created();
|
||||
},
|
||||
|
||||
/**
|
||||
* It is very common to want to keep object
|
||||
* properties in-sync with attributes,
|
||||
* for example:
|
||||
*
|
||||
* el.value = 'foo';
|
||||
* el.setAttribute('value', 'foo');
|
||||
*
|
||||
* So we support an object on the prototype
|
||||
* named 'attrs' to provide a consistent
|
||||
* way for component authors to define
|
||||
* these properties. When an attribute
|
||||
* changes we keep the attr[name]
|
||||
* up-to-date.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String||null} from
|
||||
* @param {String||null} to
|
||||
*/
|
||||
attributeChangedCallback: function(name, from, to) {
|
||||
var prop = toCamelCase(name);
|
||||
if (this._attrs && this._attrs[prop]) { this[prop] = to; }
|
||||
this.attributeChanged(name, from, to);
|
||||
},
|
||||
|
||||
attachedCallback: function() {
|
||||
if (this.dirObserver) {
|
||||
this.setInnerDirAttributes = setInnerDirAttributes.bind(null, this);
|
||||
document.addEventListener('dirchanged', this.setInnerDirAttributes);
|
||||
}
|
||||
this.attached();
|
||||
},
|
||||
|
||||
detachedCallback: function() {
|
||||
if (this.dirObserver) {
|
||||
document.removeEventListener('dirchanged', this.setInnerDirAttributes);
|
||||
}
|
||||
this.detached();
|
||||
},
|
||||
|
||||
/**
|
||||
* A convenient method for setting up
|
||||
* a shadow-root using the defined template.
|
||||
*
|
||||
* @return {ShadowRoot}
|
||||
*/
|
||||
setupShadowRoot: function() {
|
||||
if (!this.template) { return; }
|
||||
var node = document.importNode(this.template.content, true);
|
||||
this.createShadowRoot().appendChild(node);
|
||||
if (this.dirObserver) { setInnerDirAttributes(this); }
|
||||
return this.shadowRoot;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets an attribute internally
|
||||
* and externally. This is so that
|
||||
* we can style internal shadow-dom
|
||||
* content.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} value
|
||||
*/
|
||||
setAttr: function(name, value) {
|
||||
var internal = this.shadowRoot.firstElementChild;
|
||||
setAttribute.call(internal, name, value);
|
||||
setAttribute.call(this, name, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an attribute internally
|
||||
* and externally. This is so that
|
||||
* we can style internal shadow-dom
|
||||
* content.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} value
|
||||
*/
|
||||
removeAttr: function(name) {
|
||||
var internal = this.shadowRoot.firstElementChild;
|
||||
removeAttribute.call(internal, name);
|
||||
removeAttribute.call(this, name);
|
||||
}
|
||||
},
|
||||
|
||||
descriptors: {
|
||||
textContent: {
|
||||
set: function(value) {
|
||||
textContent.set.call(this, value);
|
||||
if (this.lightStyle) { this.appendChild(this.lightStyle); }
|
||||
},
|
||||
|
||||
get: function() {
|
||||
return textContent.get();
|
||||
}
|
||||
},
|
||||
|
||||
innerHTML: {
|
||||
set: function(value) {
|
||||
innerHTML.set.call(this, value);
|
||||
if (this.lightStyle) { this.appendChild(this.lightStyle); }
|
||||
},
|
||||
|
||||
get: innerHTML.get
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The default base prototype to use
|
||||
* when `extends` is undefined.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var defaultPrototype = createProto(HTMLElement.prototype, base.properties);
|
||||
|
||||
/**
|
||||
* Returns a suitable prototype based
|
||||
* on the object passed.
|
||||
*
|
||||
* @private
|
||||
* @param {HTMLElementPrototype|undefined} proto
|
||||
* @return {HTMLElementPrototype}
|
||||
*/
|
||||
function getBaseProto(proto) {
|
||||
if (!proto) { return defaultPrototype; }
|
||||
proto = proto.prototype || proto;
|
||||
return !proto.GaiaComponent ?
|
||||
createProto(proto, base.properties) : proto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the given proto and mixes
|
||||
* in the given properties.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} proto
|
||||
* @param {Object} props
|
||||
* @return {Object}
|
||||
*/
|
||||
function createProto(proto, props) {
|
||||
return mixin(Object.create(proto), props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects presence of shadow-dom
|
||||
* CSS selectors.
|
||||
*
|
||||
* @private
|
||||
* @return {Boolean}
|
||||
*/
|
||||
var hasShadowCSS = (function() {
|
||||
var div = document.createElement('div');
|
||||
try { div.querySelector(':host'); div.querySelector('::content'); return true; }
|
||||
catch (e) { return false; }
|
||||
})();
|
||||
|
||||
/**
|
||||
* Regexs used to extract shadow-css
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var regex = {
|
||||
shadowCss: /(?:\:host|\:\:content)[^{]*\{[^}]*\}/g,
|
||||
':host': /(?:\:host)/g,
|
||||
':host()': /\:host\((.+)\)(?: \:\:content)?/g,
|
||||
':host-context': /\:host-context\((.+)\)([^{,]+)?/g,
|
||||
'::content': /(?:\:\:content)/g
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the :host and ::content rules
|
||||
* from the shadow-dom CSS and rewrites
|
||||
* them to work from the <style scoped>
|
||||
* injected at the root of the component.
|
||||
*
|
||||
* @private
|
||||
* @return {String}
|
||||
*/
|
||||
function processCss(template, name) {
|
||||
var globalCss = '';
|
||||
var lightCss = '';
|
||||
|
||||
if (!hasShadowCSS) {
|
||||
template = template.replace(regex.shadowCss, function(match) {
|
||||
var hostContext = regex[':host-context'].exec(match);
|
||||
|
||||
if (hostContext) {
|
||||
globalCss += match
|
||||
.replace(regex['::content'], '')
|
||||
.replace(regex[':host-context'], '$1 ' + name + '$2')
|
||||
.replace(/ +/g, ' '); // excess whitespace
|
||||
} else {
|
||||
lightCss += match
|
||||
.replace(regex[':host()'], name + '$1')
|
||||
.replace(regex[':host'], name)
|
||||
.replace(regex['::content'], name);
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
template: template,
|
||||
lightCss: lightCss,
|
||||
globalCss: globalCss
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Some CSS rules, such as @keyframes
|
||||
* and @font-face don't work inside
|
||||
* scoped or shadow <style>. So we
|
||||
* have to put them into 'global'
|
||||
* <style> in the head of the
|
||||
* document.
|
||||
*
|
||||
* @private
|
||||
* @param {String} css
|
||||
*/
|
||||
function injectGlobalCss(css) {
|
||||
if (!css) {return;}
|
||||
var style = document.createElement('style');
|
||||
style.innerHTML = css.trim();
|
||||
headReady().then(function() {
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a promise once document.head is ready.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function headReady() {
|
||||
return new Promise(function(resolve) {
|
||||
if (document.head) { return resolve(); }
|
||||
window.addEventListener('load', function fn() {
|
||||
window.removeEventListener('load', fn);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The Gecko platform doesn't yet have
|
||||
* `::content` or `:host`, selectors,
|
||||
* without these we are unable to style
|
||||
* user-content in the light-dom from
|
||||
* within our shadow-dom style-sheet.
|
||||
*
|
||||
* To workaround this, we clone the <style>
|
||||
* node into the root of the component,
|
||||
* so our selectors are able to target
|
||||
* light-dom content.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function injectLightCss(el) {
|
||||
if (hasShadowCSS) { return; }
|
||||
var stylesheet = el.querySelector('style');
|
||||
|
||||
if (!stylesheet) {
|
||||
stylesheet = document.createElement('style');
|
||||
stylesheet.setAttribute('scoped', '');
|
||||
stylesheet.appendChild(document.createTextNode(el.lightCss));
|
||||
el.appendChild(stylesheet);
|
||||
}
|
||||
|
||||
el.lightStyle = stylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hyphen separated
|
||||
* string to camel-case.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* toCamelCase('foo-bar'); //=> 'fooBar'
|
||||
*
|
||||
* @private
|
||||
* @param {String} string
|
||||
* @return {String}
|
||||
*/
|
||||
function toCamelCase(string) {
|
||||
return string.replace(/-(.)/g, function replacer(string, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer (singleton)
|
||||
*
|
||||
* @type {MutationObserver|undefined}
|
||||
*/
|
||||
var dirObserver;
|
||||
|
||||
/**
|
||||
* Workaround for bug 1100912: applies a `dir` attribute to all shadowRoot
|
||||
* children so that :-moz-dir() selectors work on shadow DOM elements.
|
||||
*
|
||||
* In order to keep decent performances, the `dir` is the component dir if
|
||||
* defined, or the document dir otherwise. This won't work if the component's
|
||||
* direction is defined by CSS or inherited from a parent container.
|
||||
*
|
||||
* This method should be removed when bug 1100912 is fixed.
|
||||
*
|
||||
* @private
|
||||
* @param {WebComponent}
|
||||
*/
|
||||
function setInnerDirAttributes(component) {
|
||||
var dir = component.dir || document.dir;
|
||||
Array.from(component.shadowRoot.children).forEach(element => {
|
||||
if (element.nodeName !== 'STYLE') {
|
||||
element.dir = dir;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the document `dir` (direction) attribute and when it changes:
|
||||
* - dispatches a global `dirchanged` event;
|
||||
* - forces the `dir` attribute of all shadowRoot children.
|
||||
*
|
||||
* Components can listen to this event and make internal changes if needed.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function addDirObserver() {
|
||||
if (dirObserver) { return; }
|
||||
|
||||
dirObserver = new MutationObserver(onChanged);
|
||||
dirObserver.observe(document.documentElement, {
|
||||
attributeFilter: ['dir'],
|
||||
attributes: true
|
||||
});
|
||||
|
||||
function onChanged(mutations) {
|
||||
document.dispatchEvent(new Event('dirchanged'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the values of all properties from
|
||||
* source object `target` to a target object `source`.
|
||||
* It will return the target object.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} target
|
||||
* @param {Object} source
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mixin(target, source) {
|
||||
for (var key in source) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-component',this));
|
||||
41
gaia/shared/elements/gaia-dialog/.bower.json
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "gaia-dialog",
|
||||
"homepage": "https://github.com/gaia-components/gaia-dialog",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-dialog.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"base": "gaia-components/base",
|
||||
"pressed": "gaia-components/pressed",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts",
|
||||
"gaia-theme": "gaia-components/gaia-theme",
|
||||
"gaia-button": "gaia-components/gaia-button"
|
||||
},
|
||||
"dependencies": {
|
||||
"gaia-text-input": "gaia-components/gaia-text-input",
|
||||
"gaia-icons": "gaia-components/gaia-icons#^1.0.0",
|
||||
"gaia-component": "gaia-components/gaia-component#^0.3.9"
|
||||
},
|
||||
"version": "0.2.4",
|
||||
"_release": "0.2.4",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.2.4",
|
||||
"commit": "ddaea4f4ca5e5fe0fbb71816fb04d30052cfc10e"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-dialog.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "gaia-components/gaia-dialog"
|
||||
}
|
||||
31
gaia/shared/elements/gaia-dialog/bower.json
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "gaia-dialog",
|
||||
"homepage": "https://github.com/gaia-components/gaia-dialog",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-dialog.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"base": "gaia-components/base",
|
||||
"pressed": "gaia-components/pressed",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts",
|
||||
"gaia-theme": "gaia-components/gaia-theme",
|
||||
"gaia-button": "gaia-components/gaia-button"
|
||||
},
|
||||
"dependencies": {
|
||||
"gaia-text-input": "gaia-components/gaia-text-input",
|
||||
"gaia-icons": "gaia-components/gaia-icons#^1.0.0",
|
||||
"gaia-component": "gaia-components/gaia-component#^0.3.9"
|
||||
}
|
||||
}
|
||||
116
gaia/shared/elements/gaia-dialog/gaia-dialog-action.js
Executable file
|
|
@ -0,0 +1,116 @@
|
|||
/* global define */
|
||||
;(function(define){'use strict';define(function(require,exports,module){
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var GaiaDialogProto = require('gaia-dialog').prototype;
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
module.exports = component.register('gaia-dialog-action', {
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
dialog: this.shadowRoot.querySelector('gaia-dialog'),
|
||||
submit: this.shadowRoot.querySelector('.submit'),
|
||||
cancel: this.shadowRoot.querySelector('.cancel')
|
||||
};
|
||||
|
||||
this.els.dialog.addEventListener('closed',
|
||||
GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
// First close (hide) inner dialog and then the container.
|
||||
return this.els.dialog.close()
|
||||
.then(GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
template: `
|
||||
<gaia-dialog>
|
||||
<section>
|
||||
<content select="h1"></content>
|
||||
</section>
|
||||
<content select="button"></content>
|
||||
<button on-click="close" class="cancel">Cancel</button>
|
||||
</gaia-dialog>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
gaia-dialog section {
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
/** Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content section {
|
||||
padding: 33px 16px;
|
||||
}
|
||||
|
||||
::content button {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0rem 25px;
|
||||
font: inherit;
|
||||
background: var(--color-beta);
|
||||
color: var(--color-epsilon);
|
||||
transition: all 200ms;
|
||||
transition-delay: 300ms;
|
||||
}
|
||||
|
||||
::content button:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
left: 6px;
|
||||
right: 6px;
|
||||
top: 49px;
|
||||
background: #E7E7E7;
|
||||
}
|
||||
|
||||
::content button:last-child:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::content button:active {
|
||||
background-color: var(--highlight-color);
|
||||
color: #fff;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
::content button:active:after {
|
||||
background: var(--highlight-color);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-dialog-action',this));
|
||||
67
gaia/shared/elements/gaia-dialog/gaia-dialog-alert.js
Executable file
|
|
@ -0,0 +1,67 @@
|
|||
/* global define */
|
||||
;(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var GaiaDialogProto = require('gaia-dialog').prototype;
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
module.exports = component.register('gaia-dialog-alert', {
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
this.els = {
|
||||
dialog: this.shadowRoot.querySelector('gaia-dialog')
|
||||
};
|
||||
this.els.dialog.addEventListener('closed',
|
||||
GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
return this.els.dialog.close()
|
||||
.then(GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
template: `
|
||||
<gaia-dialog>
|
||||
<section>
|
||||
<p><content></content></p>
|
||||
</section>
|
||||
<div>
|
||||
<button class="submit primary" on-click="close">Ok</button>
|
||||
</div>
|
||||
</gaia-dialog>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
gaia-dialog section {
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-dialog-alert',this));
|
||||
72
gaia/shared/elements/gaia-dialog/gaia-dialog-confirm.js
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
/* global define */
|
||||
;(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var GaiaDialogProto = require('gaia-dialog').prototype;
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
module.exports = component.register('gaia-dialog-confirm', {
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
dialog: this.shadowRoot.querySelector('gaia-dialog'),
|
||||
submit: this.shadowRoot.querySelector('.submit'),
|
||||
cancel: this.shadowRoot.querySelector('.cancel')
|
||||
};
|
||||
|
||||
this.els.cancel.addEventListener('click', this.close.bind(this));
|
||||
this.els.submit.addEventListener('click', this.close.bind(this));
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
// First close (hide) inner dialog and then the container.
|
||||
return this.els.dialog.close()
|
||||
.then(GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
template: `
|
||||
<gaia-dialog>
|
||||
<section>
|
||||
<p><content></content></p>
|
||||
</section>
|
||||
<fieldset>
|
||||
<button class="cancel">Cancel</button>
|
||||
<button class="submit danger">Confirm</button>
|
||||
</fieldset>
|
||||
</gaia-dialog>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
gaia-dialog section {
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-dialog-confirm',this));
|
||||
105
gaia/shared/elements/gaia-dialog/gaia-dialog-menu.js
Executable file
|
|
@ -0,0 +1,105 @@
|
|||
/* global define */
|
||||
;(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var GaiaDialogProto = require('gaia-dialog').prototype;
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
module.exports = component.register('gaia-dialog-menu', {
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
this.els = {
|
||||
dialog: this.shadowRoot.querySelector('gaia-dialog'),
|
||||
items: this.shadowRoot.querySelector('.items')
|
||||
};
|
||||
|
||||
this.els.dialog.addEventListener('closed',
|
||||
GaiaDialogProto.hide.bind(this));
|
||||
|
||||
setTimeout(() => this.makeAccessible());
|
||||
},
|
||||
|
||||
makeAccessible() {
|
||||
this.els.items.setAttribute('role', 'menu');
|
||||
[].forEach.call(this.querySelectorAll('button'),
|
||||
menuitem => menuitem.setAttribute('role', 'menuitem'));
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
return this.els.dialog.close()
|
||||
.then(GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
template: `
|
||||
<gaia-dialog>
|
||||
<div class="items"><content select="button"></content></div>
|
||||
</gaia-dialog>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: none;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
::content > button {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 51px;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0rem 16px;
|
||||
font: inherit;
|
||||
font-style: italic;
|
||||
text-align: start;
|
||||
background: var(--color-beta);
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
::content > button[data-icon]:before {
|
||||
width: 50px;
|
||||
font-size: 22px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/** Button Divider Line
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content > button:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
left: 6px;
|
||||
right: 6px;
|
||||
top: 49px;
|
||||
background: #E7E7E7;
|
||||
}
|
||||
|
||||
::content > button:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-dialog-menu',this));
|
||||
80
gaia/shared/elements/gaia-dialog/gaia-dialog-prompt.js
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
/* global define */
|
||||
;(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var GaiaDialogProto = require('gaia-dialog').prototype;
|
||||
var component = require('gaia-component');
|
||||
|
||||
require('gaia-text-input');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
module.exports = component.register('gaia-dialog-prompt', {
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
dialog: this.shadowRoot.querySelector('gaia-dialog'),
|
||||
input: this.shadowRoot.querySelector('gaia-text-input'),
|
||||
submit: this.shadowRoot.querySelector('.submit'),
|
||||
cancel: this.shadowRoot.querySelector('.cancel')
|
||||
};
|
||||
|
||||
this.els.dialog.addEventListener('opened', () => {
|
||||
this.setAttribute('opened', '');
|
||||
});
|
||||
|
||||
this.els.dialog.addEventListener('closed', () => {
|
||||
this.removeAttribute('opened');
|
||||
});
|
||||
|
||||
this.els.input.placeholder = this.firstChild.textContent;
|
||||
this.els.cancel.addEventListener('click', this.close.bind(this));
|
||||
this.els.submit.addEventListener('click', this.close.bind(this));
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
return this.els.dialog.close()
|
||||
.then(GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
template: `
|
||||
<gaia-dialog>
|
||||
<div><gaia-text-input></gaia-text-input></div>
|
||||
<fieldset>
|
||||
<button class="cancel">Cancel</button>
|
||||
<button class="submit primary">Ok</button>
|
||||
</fieldset>
|
||||
</gaia-dialog>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
gaia-text-input {
|
||||
margin: 16px !important;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-dialog-prompt',this));
|
||||
248
gaia/shared/elements/gaia-dialog/gaia-dialog-select.js
Executable file
|
|
@ -0,0 +1,248 @@
|
|||
/* global define */
|
||||
;(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var GaiaDialogProto = require('gaia-dialog').prototype;
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
module.exports = component.register('gaia-dialog-select', {
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
dialog: this.shadowRoot.querySelector('gaia-dialog'),
|
||||
submit: this.shadowRoot.querySelector('.submit'),
|
||||
cancel: this.shadowRoot.querySelector('.cancel'),
|
||||
list: this.shadowRoot.querySelector('ul')
|
||||
};
|
||||
|
||||
this.multiple = this.hasAttribute('multiple');
|
||||
this.els.list.addEventListener('click', this.onListClick.bind(this));
|
||||
this.els.submit.addEventListener('click', this.close.bind(this));
|
||||
this.els.cancel.addEventListener('click', this.close.bind(this));
|
||||
|
||||
setTimeout(() => this.makeAccessible());
|
||||
},
|
||||
|
||||
makeAccessible() {
|
||||
this.els.list.setAttribute('role', 'listbox');
|
||||
if (this.multiple) {
|
||||
this.els.list.setAttribute('aria-multiselectable', true);
|
||||
}
|
||||
|
||||
[].forEach.call(this.options, option => {
|
||||
option.setAttribute('role', 'option');
|
||||
option.setAttribute('tabindex', '0');
|
||||
});
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
return this.els.dialog.close()
|
||||
.then(GaiaDialogProto.hide.bind(this));
|
||||
},
|
||||
|
||||
onListClick: function(e) {
|
||||
var el = e.target.closest('li');
|
||||
var selected = el.getAttribute('aria-selected') === 'true';
|
||||
|
||||
if (this.multiple) { this.onChangeMultiple(el, selected); }
|
||||
else { this.onChange(el, selected); }
|
||||
},
|
||||
|
||||
onChange: function(el, selected) {
|
||||
this.clearSelected();
|
||||
if (!selected) { el.setAttribute('aria-selected', !selected); }
|
||||
this.fireChange();
|
||||
this.close();
|
||||
},
|
||||
|
||||
onChangeMultiple: function(el, selected) {
|
||||
el.setAttribute('aria-selected', !selected);
|
||||
this.fireChange();
|
||||
},
|
||||
|
||||
clearSelected: function() {
|
||||
[].forEach.call(this.selectedOptions, function(option) {
|
||||
option.removeAttribute('aria-selected');
|
||||
});
|
||||
},
|
||||
|
||||
fireChange: function() {
|
||||
var e = new CustomEvent('change', { detail: { value: this.valueString }});
|
||||
this.dispatchEvent(e);
|
||||
},
|
||||
|
||||
attrs: {
|
||||
multiple: {
|
||||
get: function() { return !!this._multiple; },
|
||||
set: function(value) {
|
||||
value = value === '' ? true : value;
|
||||
if (value === this._multiple) { return; }
|
||||
if (!value) {
|
||||
this._multiple = false;
|
||||
this.removeAttribute('multiple');
|
||||
this.els.list.removeAttribute('aria-multiselectable');
|
||||
} else {
|
||||
this._multiple = true;
|
||||
this.setAttribute('multiple', '');
|
||||
this.els.list.setAttribute('aria-multiselectable', true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
options: {
|
||||
get: function() { return this.querySelectorAll('li'); }
|
||||
},
|
||||
|
||||
selectedOptions: {
|
||||
get: function() {
|
||||
return this.querySelectorAll('li[aria-selected="true"]');
|
||||
}
|
||||
},
|
||||
|
||||
selected: {
|
||||
get: function() { return this.selectedOptions[0]; }
|
||||
},
|
||||
|
||||
value: {
|
||||
get: function() {
|
||||
var selected = this.selectedOptions[0];
|
||||
return selected && selected.getAttribute('value');
|
||||
}
|
||||
},
|
||||
|
||||
valueString: {
|
||||
get: function() {
|
||||
var selected = this.selected;
|
||||
return selected && selected.textContent;
|
||||
}
|
||||
},
|
||||
|
||||
length: {
|
||||
get: function() { return this.options.length; }
|
||||
}
|
||||
},
|
||||
|
||||
template: `
|
||||
<gaia-dialog>
|
||||
<content select="h1"></content>
|
||||
<ul><content select="li"></content></ul>
|
||||
<fieldset>
|
||||
<button class="cancel">Cancel</button>
|
||||
<button class="submit primary">Select</button>
|
||||
</fieldset>
|
||||
</gaia-dialog>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: none;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/** Title (duplicate from gaia-dialog)
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content h1 {
|
||||
padding: 16px;
|
||||
font-size: 23px;
|
||||
line-height: 26px;
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
/** List
|
||||
---------------------------------------------------------*/
|
||||
|
||||
ul {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/** Options
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content li {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 16px;
|
||||
text-align: start;
|
||||
-moz-user-select: none;
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
::content li[aria-selected='true'] {
|
||||
color: #00CAF2;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
::content li:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
bottom: 1px;
|
||||
background: #E7E7E7;
|
||||
}
|
||||
|
||||
::content li:last-of-type:after {
|
||||
display: none
|
||||
}
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::host:not([multiple]) .submit {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
::host:not([multiple]) .cancel {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
::host:not([multiple]) .cancel:after {
|
||||
display: none% !important;
|
||||
}
|
||||
|
||||
/** Tick Icon
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content li[aria-selected='true']:before {
|
||||
content: 'tick';
|
||||
position: absolute;
|
||||
top: 50%; right: 20px;
|
||||
display: block;
|
||||
margin-top: -12px;
|
||||
font-family: 'gaia-icons';
|
||||
font-style: normal;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-dialog-select',this));
|
||||
495
gaia/shared/elements/gaia-dialog/gaia-dialog.js
Executable file
|
|
@ -0,0 +1,495 @@
|
|||
/* global define */
|
||||
|
||||
;(function(define){'use strict';define((require,exports,module) => {
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Simple logger (toggle 0)
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
var debug = 0 ? console.log.bind(console) : () => {};
|
||||
|
||||
/**
|
||||
* Use the dom-scheduler if it's around,
|
||||
* else fallback to fake shim.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var schedule = (window.scheduler && typeof window.scheduler.mutation === "function" ? window.scheduler : null) || {
|
||||
mutation: block => Promise.resolve(block()),
|
||||
transition: (block, el, event, timeout) => {
|
||||
block();
|
||||
return after(el, event, timeout || 500);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-dialog', {
|
||||
created() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
inner: this.shadowRoot.querySelector('.dialog-inner'),
|
||||
background: this.shadowRoot.querySelector('.background'),
|
||||
window: this.shadowRoot.querySelector('.window')
|
||||
};
|
||||
|
||||
this.shadowRoot.addEventListener('click', e => this.onClick(e));
|
||||
|
||||
setTimeout(() => this.makeAccessible());
|
||||
},
|
||||
|
||||
makeAccessible() {
|
||||
this.setAttribute('role', 'dialog');
|
||||
},
|
||||
|
||||
onClick(e) {
|
||||
var el = e.target.closest('[on-click]');
|
||||
if (!el) { return; }
|
||||
debug('onClick');
|
||||
var method = el.getAttribute('on-click');
|
||||
if (typeof this[method] == 'function') { this[method](); }
|
||||
},
|
||||
|
||||
open(options) {
|
||||
if (this.isOpen) { return; }
|
||||
debug('open dialog');
|
||||
this.isOpen = true;
|
||||
|
||||
return this.show()
|
||||
.then(() => this.animateBackgroundIn(options))
|
||||
.then(() => this.animateWindowIn())
|
||||
.then(() => this.dispatch('opened'));
|
||||
},
|
||||
|
||||
close(options) {
|
||||
if (!this.isOpen) { return; }
|
||||
debug('close dialog');
|
||||
this.isOpen = false;
|
||||
|
||||
return this.animateWindowOut()
|
||||
.then(() => this.animateBackgroundOut())
|
||||
.then(() => this.hide())
|
||||
.then(() => this.dispatch('closed'));
|
||||
},
|
||||
|
||||
animateBackgroundIn(options) {
|
||||
if (options) { return this.animateBackgroundInFrom(options); }
|
||||
|
||||
var el = this.els.background;
|
||||
return schedule.transition(() => {
|
||||
debug('animate background in');
|
||||
el.classList.remove('animate-out');
|
||||
el.classList.add('animate-in');
|
||||
}, el, 'animationend');
|
||||
},
|
||||
|
||||
animateBackgroundOut() {
|
||||
var el = this.els.background;
|
||||
return schedule.transition(() => {
|
||||
debug('animate background out');
|
||||
el.classList.add('animate-out');
|
||||
el.classList.remove('animate-in');
|
||||
}, el, 'animationend')
|
||||
.then(() => el.style = '');
|
||||
},
|
||||
|
||||
animateBackgroundInFrom(pos) {
|
||||
var el = this.els.background;
|
||||
var scale = Math.sqrt(window.innerWidth * window.innerHeight) / 15;
|
||||
var duration = scale * 9;
|
||||
|
||||
return schedule.mutation(() => {
|
||||
el.classList.add('circular');
|
||||
el.classList.remove('animate-out');
|
||||
el.style.transform = `translate(${pos.clientX}px, ${pos.clientY}px)`;
|
||||
el.style.transitionDuration = duration + 'ms';
|
||||
el.offsetTop; // Hack, any ideas?
|
||||
})
|
||||
|
||||
.then(() => {
|
||||
return schedule.transition(() => {
|
||||
debug('animate background in from', pos);
|
||||
el.style.transform += ` scale(${scale})`;
|
||||
el.style.opacity = 1;
|
||||
}, el, 'transitionend', duration * 1.5);
|
||||
});
|
||||
},
|
||||
|
||||
show() {
|
||||
return schedule.mutation(() => {
|
||||
debug('show');
|
||||
this.style.display = 'block';
|
||||
});
|
||||
},
|
||||
|
||||
hide() {
|
||||
return schedule.mutation(() => {
|
||||
debug('hide');
|
||||
this.style.display = 'none';
|
||||
});
|
||||
},
|
||||
|
||||
animateWindowIn() {
|
||||
var el = this.els.window;
|
||||
return schedule.transition(() => {
|
||||
debug('animate window in');
|
||||
el.classList.add('animate-in');
|
||||
el.classList.remove('animate-out');
|
||||
}, el, 'animationend');
|
||||
},
|
||||
|
||||
animateWindowOut() {
|
||||
var el = this.els.window;
|
||||
return schedule.transition(() => {
|
||||
debug('animate window out');
|
||||
el.classList.add('animate-out');
|
||||
el.classList.remove('animate-in');
|
||||
}, el, 'animationend');
|
||||
},
|
||||
|
||||
dispatch(name) {
|
||||
this.dispatchEvent(new CustomEvent(name));
|
||||
},
|
||||
|
||||
attrs: {
|
||||
opened: {
|
||||
get: function() { return !!this.isOpen; },
|
||||
set: function(value) {
|
||||
value = value === '' || value;
|
||||
if (!value) { this.close(); }
|
||||
else { this.open(); }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="dialog-inner">
|
||||
<div class="background" on-click="close"></div>
|
||||
<div class="window"><content></content></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
::content * {
|
||||
box-sizing: border-box;
|
||||
font-weight: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
::content p,
|
||||
::content h1,
|
||||
::content h2,
|
||||
::content h3,
|
||||
::content h4,
|
||||
::content button,
|
||||
::content fieldset {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
:host {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 200;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
font-size: 17px;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.dialog-inner {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.background {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
background: var(--overlay-background, rgba(199,199,199,0.85));
|
||||
}
|
||||
|
||||
/**
|
||||
* .circular
|
||||
*/
|
||||
|
||||
.background.circular {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: -20px;
|
||||
border-radius: 50%;
|
||||
will-change: transform, opacity;
|
||||
transition-property: opacity, transform;
|
||||
transition-timing-function: linear;
|
||||
}
|
||||
|
||||
/**
|
||||
* .animate-in
|
||||
*/
|
||||
|
||||
.background.animate-in {
|
||||
animation-name: gaia-dialog-fade-in;
|
||||
animation-duration: 260ms;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
/**
|
||||
* .animate-out
|
||||
*/
|
||||
|
||||
.background.animate-out {
|
||||
animation-name: gaia-dialog-fade-out;
|
||||
animation-duration: 260ms;
|
||||
animation-fill-mode: forwards;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/** Window
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.window {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
box-shadow: 0 1px 0 0px rgba(0,0,0,0.15);
|
||||
background: var(--background);
|
||||
transition: opacity 300ms;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 360px) {
|
||||
.window {
|
||||
width: auto;
|
||||
min-width: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
.window.animate-in {
|
||||
animation-name: gaia-dialog-entrance;
|
||||
animation-duration: 300ms;
|
||||
animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1.275);
|
||||
animation-fill-mode: forwards;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.window.animate-out {
|
||||
animation-name: gaia-dialog-fade-out;
|
||||
animation-duration: 150ms;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/** Title
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content h1 {
|
||||
padding: 16px;
|
||||
font-size: 23px;
|
||||
line-height: 26px;
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
color: var(--title-color);
|
||||
}
|
||||
|
||||
::content strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::content small {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/** Section
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content section {
|
||||
padding: 33px 18px;
|
||||
}
|
||||
|
||||
::content section > *:not(:last-child) {
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
|
||||
/** Paragraphs
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content p {
|
||||
text-align: start;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content button {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border: 0;
|
||||
padding: 0 16px;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
background: var(--background-plus);
|
||||
color: var(--text-color);
|
||||
transition: all 200ms;
|
||||
transition-delay: 300ms;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* .primary
|
||||
*/
|
||||
|
||||
::content button.primary {
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* .danger
|
||||
*/
|
||||
|
||||
::content button.danger {
|
||||
color: var(--color-destructive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabled buttons
|
||||
*/
|
||||
|
||||
::content button[disabled] {
|
||||
color: var(--text-color-minus);
|
||||
}
|
||||
|
||||
/** Button Divider Line
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content button:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
left: 6px;
|
||||
right: 6px;
|
||||
top: 49px;
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
::content button:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::content button:active {
|
||||
background-color: var(--highlight-color);
|
||||
color: #fff;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
::content button:active:after {
|
||||
background: var(--highlight-color);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
::content button[data-icon]:before {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/** Fieldset (button group)
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content fieldset {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
::content fieldset button {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
::content fieldset button:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
right: 0px;
|
||||
left: auto;
|
||||
width: 1px;
|
||||
height: calc(100% - 12px);
|
||||
background: var(--background);
|
||||
transition: all 200ms;
|
||||
transition-delay: 200ms;
|
||||
}
|
||||
|
||||
</style>`,
|
||||
|
||||
globalCss: `
|
||||
@keyframes gaia-dialog-entrance {
|
||||
0% { transform: translateY(100px); }
|
||||
100% { transform: translateY(0px); }
|
||||
}
|
||||
|
||||
@keyframes gaia-dialog-fade-in {
|
||||
0% { opacity: 0 }
|
||||
100% { opacity: 1 }
|
||||
}
|
||||
|
||||
@keyframes gaia-dialog-fade-out {
|
||||
0% { opacity: 1 }
|
||||
100% { opacity: 0 }
|
||||
}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
function after(target, event, timeout) {
|
||||
return new Promise(resolve => {
|
||||
var timer = timeout && setTimeout(cb, timeout);
|
||||
target.addEventListener(event, cb);
|
||||
function cb() {
|
||||
target.removeEventListener(event, cb);
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-dialog',this));
|
||||
39
gaia/shared/elements/gaia-header/.bower.json
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "gaia-header",
|
||||
"version": "0.9.5",
|
||||
"homepage": "https://github.com/gaia-components/gaia-header",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-header.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"gaia-icons": "gaia-components/gaia-icons#~0.10.3",
|
||||
"gaia-component": "gaia-components/gaia-component#~0.4.0",
|
||||
"font-fit": "gaia-components/font-fit#~0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gaia-theme": "gaia-components/gaia-theme",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts",
|
||||
"base": "gaia-components/base"
|
||||
},
|
||||
"_release": "0.9.5",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.9.5",
|
||||
"commit": "aa4c9c5cb83ce8813c8472860671ea514f07274f"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-header.git",
|
||||
"_target": "~0.9.5",
|
||||
"_originalSource": "gaia-components/gaia-header"
|
||||
}
|
||||
33
gaia/shared/elements/gaia-header/.jshintrc
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"camelcase": false,
|
||||
"curly": true,
|
||||
"forin": false,
|
||||
"latedef": "nofunc",
|
||||
"newcap": false,
|
||||
"noarg": true,
|
||||
"node": true,
|
||||
"nonew": true,
|
||||
"quotmark": "single",
|
||||
"undef": true,
|
||||
"unused": "vars",
|
||||
"strict": true,
|
||||
"trailing": true,
|
||||
"maxlen": 80,
|
||||
|
||||
"eqnull": true,
|
||||
"esnext": true,
|
||||
"expr": true,
|
||||
"globalstrict": true,
|
||||
|
||||
"maxerr": 1000,
|
||||
"regexdash": true,
|
||||
"laxcomma": true,
|
||||
"proto": true,
|
||||
|
||||
"browser": true,
|
||||
"devel": true,
|
||||
"nonstandard": true,
|
||||
"worker": true,
|
||||
|
||||
"-W078": true
|
||||
}
|
||||
30
gaia/shared/elements/gaia-header/bower.json
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "gaia-header",
|
||||
"version": "0.9.5",
|
||||
"homepage": "https://github.com/gaia-components/gaia-header",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-header.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"gaia-icons": "gaia-components/gaia-icons#~0.10.3",
|
||||
"gaia-component": "gaia-components/gaia-component#~0.4.0",
|
||||
"font-fit": "gaia-components/font-fit#~0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gaia-theme": "gaia-components/gaia-theme",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts",
|
||||
"base": "gaia-components/base"
|
||||
}
|
||||
}
|
||||
1686
gaia/shared/elements/gaia-header/dist/gaia-header.js
vendored
Executable file
1046
gaia/shared/elements/gaia-header/gaia-header.js
Executable file
42
gaia/shared/elements/gaia-icons/.bower.json
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "gaia-icons",
|
||||
"homepage": "https://github.com/wilsonpage/gaia-icons",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": [
|
||||
"gaia-icons.js",
|
||||
"gaia-icons.css",
|
||||
"fonts/gaia-icons.ttf",
|
||||
"gaia-icons-embedded.css",
|
||||
"bidi-helper.css"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"create-svg-icon-set-file.py",
|
||||
"Gruntfile.js",
|
||||
"Dockerfile",
|
||||
"bin",
|
||||
"README.md",
|
||||
"package.json"
|
||||
],
|
||||
"version": "1.0.2",
|
||||
"_release": "1.0.2",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.0.2",
|
||||
"commit": "42dfa3ed9a9ffa09733dc8db08a06d3dd9d0a081"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-icons.git",
|
||||
"_target": "~1.0.2",
|
||||
"_originalSource": "gaia-components/gaia-icons"
|
||||
}
|
||||
33
gaia/shared/elements/gaia-icons/.jshintrc
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"camelcase": false,
|
||||
"curly": true,
|
||||
"forin": false,
|
||||
"latedef": "nofunc",
|
||||
"newcap": false,
|
||||
"noarg": true,
|
||||
"node": true,
|
||||
"nonew": true,
|
||||
"quotmark": "single",
|
||||
"undef": true,
|
||||
"unused": "vars",
|
||||
"strict": true,
|
||||
"trailing": true,
|
||||
"maxlen": 80,
|
||||
|
||||
"eqnull": true,
|
||||
"esnext": true,
|
||||
"expr": true,
|
||||
"globalstrict": true,
|
||||
|
||||
"maxerr": 1000,
|
||||
"regexdash": true,
|
||||
"laxcomma": true,
|
||||
"proto": true,
|
||||
|
||||
"browser": true,
|
||||
"devel": true,
|
||||
"nonstandard": true,
|
||||
"worker": true,
|
||||
|
||||
"-W078": true
|
||||
}
|
||||
74
gaia/shared/elements/gaia-icons/bidi-helper.css
Executable file
|
|
@ -0,0 +1,74 @@
|
|||
/* Some BiDi-proof icons
|
||||
* In the future, we might handle this at the font level, see
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1214127
|
||||
*/
|
||||
|
||||
[data-icon="back"]:-moz-dir(ltr):before {
|
||||
content: 'left';
|
||||
}
|
||||
[data-icon="back"]:-moz-dir(rtl):before {
|
||||
content: 'right';
|
||||
}
|
||||
|
||||
[data-icon="forward"]:-moz-dir(ltr):before {
|
||||
content: 'right';
|
||||
}
|
||||
[data-icon="forward"]:-moz-dir(rtl):before {
|
||||
content: 'left';
|
||||
}
|
||||
|
||||
[data-icon="back-light"]:-moz-dir(ltr):before {
|
||||
content: 'left-light';
|
||||
}
|
||||
[data-icon="back-light"]:-moz-dir(rtl):before {
|
||||
content: 'right-light';
|
||||
}
|
||||
|
||||
[data-icon="forward-light"]:-moz-dir(ltr):before {
|
||||
content: 'right-light';
|
||||
}
|
||||
[data-icon="forward-light"]:-moz-dir(rtl):before {
|
||||
content: 'left-light';
|
||||
}
|
||||
|
||||
[data-icon="arrow-back"]:-moz-dir(ltr):before {
|
||||
content: 'arrow-left';
|
||||
}
|
||||
[data-icon="arrow-back"]:-moz-dir(rtl):before {
|
||||
content: 'arrow-right';
|
||||
}
|
||||
|
||||
[data-icon="arrow-forward"]:-moz-dir(ltr):before {
|
||||
content: 'arrow-right';
|
||||
}
|
||||
[data-icon="arrow-forward"]:-moz-dir(rtl):before {
|
||||
content: 'arrow-left';
|
||||
}
|
||||
|
||||
[data-icon="clear-input"]:-moz-dir(ltr):before {
|
||||
content: 'clear-input-left';
|
||||
}
|
||||
[data-icon="clear-input"]:-moz-dir(rtl):before {
|
||||
content: 'clear-input-right';
|
||||
}
|
||||
|
||||
[data-icon="expand"]:-moz-dir(ltr):before {
|
||||
content: 'expand-left';
|
||||
}
|
||||
[data-icon="expand"]:-moz-dir(rtl):before {
|
||||
content: 'expand-right';
|
||||
}
|
||||
|
||||
[data-icon="send"]:-moz-dir(ltr):before {
|
||||
content: 'send-right';
|
||||
}
|
||||
[data-icon="send"]:-moz-dir(rtl):before {
|
||||
content: 'send-left';
|
||||
}
|
||||
|
||||
[data-icon="edit"]:-moz-dir(ltr):before {
|
||||
content: 'edit-left';
|
||||
}
|
||||
[data-icon="edit"]:-moz-dir(rtl):before {
|
||||
content: 'edit-right';
|
||||
}
|
||||
32
gaia/shared/elements/gaia-icons/bower.json
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "gaia-icons",
|
||||
"homepage": "https://github.com/wilsonpage/gaia-icons",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": [
|
||||
"gaia-icons.js",
|
||||
"gaia-icons.css",
|
||||
"fonts/gaia-icons.ttf",
|
||||
"gaia-icons-embedded.css",
|
||||
"bidi-helper.css"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"create-svg-icon-set-file.py",
|
||||
"Gruntfile.js",
|
||||
"Dockerfile",
|
||||
"bin",
|
||||
"README.md",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
BIN
gaia/shared/elements/gaia-icons/fonts/gaia-icons.ttf
Executable file
40
gaia/shared/elements/gaia-icons/gaia-icons-embedded.css
Executable file
40
gaia/shared/elements/gaia-icons/gaia-icons.css
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
/* Generated by grunt-webfont */
|
||||
/* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */
|
||||
|
||||
@font-face {
|
||||
font-family: "gaia-icons";
|
||||
src: url("fonts/gaia-icons.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[data-icon]:before,
|
||||
.ligature-icons {
|
||||
font-family: "gaia-icons";
|
||||
content: attr(data-icon);
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
text-transform: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-size: 30px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):before {
|
||||
/* Highlight cases where icons are not localized and still visible to screen
|
||||
reader */
|
||||
background: rgba(255, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):after {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
width: 20rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
content: "Icon is not localized and is still visible to screen reader. Please add data-l10n-id or aria-label for localization or use aria-hidden to hide the icon from screen reader.";
|
||||
z-index: 9999;
|
||||
}
|
||||
38
gaia/shared/elements/gaia-icons/gaia-icons.js
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
/* global define */
|
||||
(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
var base = window.GAIA_ICONS_BASE_URL ||
|
||||
window.COMPONENTS_BASE_URL ||
|
||||
'bower_components/';
|
||||
|
||||
if (!document.documentElement) {
|
||||
window.addEventListener('load', load);
|
||||
} else {
|
||||
load();
|
||||
}
|
||||
|
||||
function load() {
|
||||
if (isLoaded()) { return; }
|
||||
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = base + 'gaia-icons/gaia-icons.css';
|
||||
document.head.appendChild(link);
|
||||
exports.loaded = true;
|
||||
}
|
||||
|
||||
function isLoaded() {
|
||||
return exports.loaded ||
|
||||
document.querySelector('link[href*=gaia-icons]') ||
|
||||
document.documentElement.classList.contains('gaia-icons-loaded');
|
||||
}
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-icons',this));
|
||||
33
gaia/shared/elements/gaia-site-icon/.bower.json
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "gaia-site-icon",
|
||||
"description": "A web component to show an icon for an application or site.",
|
||||
"authors": [
|
||||
"Chris Lord <chrislord.net@gmail.com>"
|
||||
],
|
||||
"main": "script.js",
|
||||
"keywords": [
|
||||
"app",
|
||||
"icon",
|
||||
"homescreen",
|
||||
"B2G",
|
||||
"Firefox OS"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"homepage": "https://github.com/fxos-components/gaia-site-icon",
|
||||
"_release": "dd873b7f7d",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "dd873b7f7d0d8be737566276f3188e68931a9584"
|
||||
},
|
||||
"_source": "git://github.com/fxos-components/gaia-site-icon.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "fxos-components/gaia-site-icon"
|
||||
}
|
||||
24
gaia/shared/elements/gaia-site-icon/bower.json
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "gaia-site-icon",
|
||||
"version": "0.0.0",
|
||||
"description": "A web component to show an icon for an application or site.",
|
||||
"authors": [
|
||||
"Chris Lord <chrislord.net@gmail.com>"
|
||||
],
|
||||
"main": "script.js",
|
||||
"keywords": [
|
||||
"app",
|
||||
"icon",
|
||||
"homescreen",
|
||||
"B2G",
|
||||
"Firefox OS"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
31
gaia/shared/elements/gaia-site-icon/examples/basic.html
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GaiaAppIcon demo</title>
|
||||
|
||||
<script src="../script.js"></script>
|
||||
|
||||
<style>
|
||||
gaia-app-icon {
|
||||
width: 256px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<gaia-app-icon id="icon"></gaia-app-icon>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
// TODO: This example doesn't work anymore, icon takes an app and uses
|
||||
// app.manifest, app.launch and navigator.mozApps.mgmt.getIcon.
|
||||
var icon = document.getElementById('icon');
|
||||
icon.manifest = {
|
||||
name: 'GaiaAppIcon test',
|
||||
origin: '',
|
||||
icons: {
|
||||
'284': 'icon.png'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</html>
|
||||
BIN
gaia/shared/elements/gaia-site-icon/examples/icon.png
Executable file
|
After Width: | Height: | Size: 5.6 KiB |
7
gaia/shared/elements/gaia-site-icon/external/style.css
vendored
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
/* XXX Bug 1018269
|
||||
* animation keyframes have to be specified outside of the shadow DOM.
|
||||
*/
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(1deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
1
gaia/shared/elements/gaia-site-icon/images/app_install_canceled.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="84" height="84"><path opacity=".85" fill="#5F5F5F" d="M42 0c23.3 0 42 18.7 42 42S65.3 84 42 84 0 65.3 0 42 18.7 0 42 0z"/><path fill="#FFF" d="M60.7 40.1c-.9-9.3-9.3-16.8-18.7-16.8-10.3 0-18.7 8.4-18.7 18.7S31.7 60.7 42 60.7c3.7 0 7.5-.9 10.3-2.8l-2.8-3.7C46.7 56.1 44.8 57 42 57c-8.4 0-14.9-6.5-14.9-14.9s6.5-15 14.9-15c7.5 0 13.1 5.6 14 13.1h-6.5l7.5 7.5c.9.9 2.8.9 3.7 0l7.5-7.5h-7.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 443 B |
1
gaia/shared/elements/gaia-site-icon/images/app_install_failed.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="84" height="84"><path opacity=".85" fill="#E51E1E" d="M42 0c23.3 0 42 18.7 42 42S65.3 84 42 84 0 65.3 0 42 18.7 0 42 0z"/><path fill="#FFF" d="M60.7 40.1c-.9-9.3-9.3-16.8-18.7-16.8-10.3 0-18.7 8.4-18.7 18.7S31.7 60.7 42 60.7c3.7 0 7.5-.9 10.3-2.8l-2.8-3.7C46.7 56.1 44.8 57 42 57c-8.4 0-14.9-6.5-14.9-14.9s6.5-15 14.9-15c7.5 0 13.1 5.6 14 13.1h-6.5l7.5 7.5c.9.9 2.8.9 3.7 0l7.5-7.5h-7.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 443 B |
1
gaia/shared/elements/gaia-site-icon/images/app_install_spinner.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="84" height="84"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="24.134" y1="31.414" x2="81.176" y2="46.699"><stop offset="0" stop-color="#00B4CF"/><stop offset=".979" stop-color="#00B4CF" stop-opacity="0"/></linearGradient><path fill-rule="evenodd" clip-rule="evenodd" fill="url(#a)" d="M83.9 39.5v2.6h-3.3c0 21.3-17.3 38.6-38.7 38.6C20.6 80.7 3.3 63.4 3.3 42 3.3 20.6 20.6 3.3 42 3.3V0C18.8 0 0 18.8 0 42s18.8 42 42 42 42-18.8 42-42c0-.8 0-1.7-.1-2.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 518 B |
1
gaia/shared/elements/gaia-site-icon/images/app_install_unrecoverable.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="84" height="84"><path opacity=".85" fill="#E51E1E" d="M42 0c23.3 0 42 18.8 42 42 0 23.3-18.8 42-42 42-23.1.1-42-18.7-42-42C0 18.8 18.9 0 42 0z"/><path fill="#FFF" d="M42 25.4c-.9 0-2.5 2.5-3.4 5.5 1.1-.2 2.3-.3 3.5-.3s2.3.1 3.3.3c-.9-3-2.5-5.5-3.4-5.5M27.1 39.1l5.7-5.7c1.7-8.3 6.7-16.7 9.2-16.7s7.5 8.4 9.2 16.7l5.7 5.7V54h-4l-5.6-5.6c-.6.6-1.2 1.1-1.9 1.5h-7c-.7-.4-1.3-.9-1.9-1.5L31.1 54h-4V39.1M16.6 59.2c.4.9 1.3 1.5 2.4 1.5h46c1 0 1.9-.6 2.4-1.5H16.6z"/></svg>
|
||||
|
After Width: | Height: | Size: 513 B |
1
gaia/shared/elements/gaia-site-icon/images/app_installing.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="84" height="84"><path opacity=".85" fill="#333" d="M42 0c23.3 0 42 18.8 42 42 0 23.3-18.8 42-42 42-23.1.1-42-18.7-42-42C0 18.8 18.9 0 42 0z"/><path fill="#FFF" d="M29.9 50c.4.9 1.3 1.5 2.4 1.5h19.3c1 0 1.9-.6 2.4-1.5H29.9zm22.3-17.8L51 33.4v.1L42.4 42c-.3.3-.7.3-1 0l-8.6-8.6v-.1l-1.2-1.2c-1.6.3-3.1.6-4.6.9.2.5.4 1 .9 1.5l11.3 11.3c1.5 1.5 3.8 1.5 5.3 0l11.3-11.3c.4-.4.7-.9.9-1.5-1.4-.3-2.9-.6-4.5-.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 459 B |
1
gaia/shared/elements/gaia-site-icon/images/default_icon.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="84" height="84"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="42" x2="42" y2="84"><stop offset="0" stop-color="#E5EBEF"/><stop offset="1" stop-color="#EDF1F4"/></linearGradient><path fill="url(#a)" d="M42 0c23.3 0 42 18.8 42 42 0 23.3-18.8 42-42 42-23.1.1-42-18.7-42-42C0 18.8 18.9 0 42 0z"/><path fill="#9AB0C0" d="M41.5 25.9c-.9 0-2.5 2.6-3.5 5.7 1.1-.2 2.3-.3 3.6-.3 1.2 0 2.4.1 3.4.3-1-3.1-2.6-5.7-3.5-5.7M43 53.3h2.2c.3.9.4 2 .4 3.1 0 4-3.1 7.2-4.1 7.2s-4.1-3.2-4.1-7.2c0-1.1.1-2.2.4-3.1H40c-.2.5-.3 1.2-.3 1.9 0 1.8 1.4 3.3 1.9 3.3s1.9-1.5 1.9-3.3c-.2-.7-.3-1.4-.5-1.9M26.1 40l5.9-5.9c1.7-8.6 6.9-17.2 9.5-17.2s7.8 8.7 9.5 17.2l5.9 5.9v15.4h-4.1l-5.7-5.7c-.6.6-1.2 1.1-1.9 1.5h-7.3c-.7-.4-1.3-.9-1.9-1.5l-5.7 5.7h-4.1L26.1 40"/></svg>
|
||||
|
After Width: | Height: | Size: 799 B |
39
gaia/shared/elements/gaia-site-icon/package.json
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "gaia-site-icon",
|
||||
"version": "0.0.1",
|
||||
"description": "A web component to show an icon for an application or site.",
|
||||
"author": [
|
||||
{
|
||||
"name": "Chris Lord",
|
||||
"email": "chrislord.net@gmail.com"
|
||||
}
|
||||
],
|
||||
"main": "script.js",
|
||||
"directories": {
|
||||
"example": "examples"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./node_modules/karma/bin/karma start test/karma.conf.js --single-run",
|
||||
"test-dev": "./node_modules/karma/bin/karma start test/karma.conf.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:gaia-components/gaia-site-icon.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"karma": "^0.12.37",
|
||||
"karma-firefox-launcher": "^0.1.6",
|
||||
"karma-mocha": "^0.2.0",
|
||||
"karma-sinon-chai": "^1.0.0",
|
||||
"mocha": "^2.2.5",
|
||||
"mozilla-download": "^1.1.1"
|
||||
},
|
||||
"keywords": [
|
||||
"app",
|
||||
"icon",
|
||||
"homescreen",
|
||||
"B2G",
|
||||
"Firefox OS"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
607
gaia/shared/elements/gaia-site-icon/script.js
Executable file
|
|
@ -0,0 +1,607 @@
|
|||
'use strict';
|
||||
|
||||
window.GaiaAppIcon = (function(exports) {
|
||||
const LAUNCH_TIMEOUT = 500;
|
||||
|
||||
const PREDEFINED_ICONS = {
|
||||
default: 'default_icon.svg',
|
||||
unrecoverable: 'app_install_unrecoverable.svg',
|
||||
installing: 'app_installing.svg',
|
||||
paused: 'app_install_canceled.svg',
|
||||
failed: 'app_install_failed.svg'
|
||||
};
|
||||
|
||||
const SHADOW_BLUR = 1;
|
||||
const SHADOW_OFFSET = { x: 1, y: 1 };
|
||||
const SHADOW_COLOR = 'rgba(0, 0, 0, 0.2)';
|
||||
const DEFAULT_BACKGROUND_COLOR = 'rgb(228, 234, 238)';
|
||||
const CANVAS_PADDING = 2 * window.devicePixelRatio;
|
||||
const FAVICON_SCALE = 0.55;
|
||||
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
// Allow the base URL to be overridden
|
||||
var base = window.GAIA_ICONS_BASE_URL ||
|
||||
window.COMPONENTS_BASE_URL ||
|
||||
'bower_components/';
|
||||
|
||||
var baseurl = window.GaiaAppIconBaseurl || base + 'gaia-site-icon/';
|
||||
|
||||
proto.createdCallback = function() {
|
||||
this._template = template.content.cloneNode(true);
|
||||
|
||||
var shadow = this.createShadowRoot();
|
||||
shadow.appendChild(this._template);
|
||||
|
||||
this._container = shadow.getElementById('image-container');
|
||||
this._titleContainer = shadow.getElementById('title-container');
|
||||
this._subtitle = shadow.getElementById('subtitle');
|
||||
this._showName = true;
|
||||
this._image = null;
|
||||
this._app = null;
|
||||
this._bookmark = null;
|
||||
this._entryPoint = '';
|
||||
this._size = -1;
|
||||
this._hasIcon = false;
|
||||
this._hasUserSetIcon = false;
|
||||
this._hasPredefinedIcon = false;
|
||||
this._iconUrl = null;
|
||||
this._pendingIconUrl = null;
|
||||
this._pendingIconRefresh = false;
|
||||
this._lastState = null;
|
||||
|
||||
this._predefinedIcons = {};
|
||||
for (var key in PREDEFINED_ICONS) {
|
||||
this._predefinedIcons[key] = baseurl + 'images/' + PREDEFINED_ICONS[key];
|
||||
}
|
||||
|
||||
// Make the icon accessible/allow activation
|
||||
this.tabIndex = 0;
|
||||
this.setAttribute('role', 'link');
|
||||
|
||||
var activate = () => {
|
||||
if (!this.dispatchEvent(new CustomEvent('activated',
|
||||
{ cancelable: true }))) {
|
||||
return;
|
||||
}
|
||||
this.launch();
|
||||
};
|
||||
|
||||
this.addEventListener('keydown', (e) => {
|
||||
switch (e.keyCode) {
|
||||
case 32: // Space
|
||||
case 13: // Enter
|
||||
activate();
|
||||
}
|
||||
});
|
||||
this.addEventListener('click', activate);
|
||||
|
||||
// Refresh now we've created the DOM
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
Object.defineProperty(proto, 'app', {
|
||||
get: function() {
|
||||
return this._app;
|
||||
},
|
||||
|
||||
set: function(app) {
|
||||
if (this._app) {
|
||||
this._app.removeEventListener('progress', this);
|
||||
this._app.removeEventListener('downloaderror', this);
|
||||
this._app.removeEventListener('downloadsuccess', this);
|
||||
this._app.removeEventListener('downloadapplied', this);
|
||||
}
|
||||
|
||||
delete this.dataset.testIconUrl;
|
||||
this._cancelIconLoad();
|
||||
this._removeOldIcon();
|
||||
this._hasIcon = false;
|
||||
|
||||
this._app = app;
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._bookmark = null;
|
||||
this._app.addEventListener('progress', this);
|
||||
this._app.addEventListener('downloaderror', this);
|
||||
this._app.addEventListener('downloadsuccess', this);
|
||||
this._app.addEventListener('downloadapplied', this);
|
||||
|
||||
this._container.classList.add('initial-load');
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'entryPoint', {
|
||||
get: function() {
|
||||
return this._entryPoint;
|
||||
},
|
||||
|
||||
set: function(entryPoint) {
|
||||
this._entryPoint = entryPoint ? entryPoint : '';
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'state', {
|
||||
get: function() {
|
||||
if (!this.app) {
|
||||
return 'unavailable';
|
||||
}
|
||||
|
||||
if (this.app.installState === 'pending' &&
|
||||
!this.app.downloadAvailable &&
|
||||
!this.app.readyToApplyDownload) {
|
||||
return 'unrecoverable';
|
||||
}
|
||||
|
||||
if (this.app.downloading) {
|
||||
return 'installing';
|
||||
}
|
||||
|
||||
if (this.app.downloadError) {
|
||||
if (this.app.downloadError.name === 'DOWNLOAD_CANCELED') {
|
||||
return 'paused';
|
||||
} else {
|
||||
return 'error';
|
||||
}
|
||||
}
|
||||
|
||||
if (this.app.installState === 'pending') {
|
||||
return 'paused';
|
||||
}
|
||||
|
||||
return 'installed';
|
||||
},
|
||||
|
||||
enumberable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'name', {
|
||||
get: function() {
|
||||
return this._subtitle ? this._subtitle.textContent : '';
|
||||
},
|
||||
|
||||
enumberable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'size', {
|
||||
get: function() {
|
||||
return (this._size === -1) ? this.clientWidth : this._size;
|
||||
},
|
||||
|
||||
set: function(size) {
|
||||
this._size = size >= 0 ? size : -1;
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'bookmark', {
|
||||
get: function() {
|
||||
return this._bookmark;
|
||||
},
|
||||
|
||||
set: function(bookmark) {
|
||||
if (!this._bookmark || this._bookmark.id !== bookmark.id) {
|
||||
this.app = null;
|
||||
}
|
||||
this._bookmark = bookmark;
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'icon', {
|
||||
get: function() {
|
||||
if (window.VulpesCompat && this.app && this._vulpesSourceIcon) return Promise.resolve(this._vulpesSourceIcon);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._hasIcon || this._hasPredefinedIcon) {
|
||||
return reject();
|
||||
}
|
||||
|
||||
var image = this._container.querySelector('img');
|
||||
if (!image) {
|
||||
return reject();
|
||||
}
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
var size = this.size * window.devicePixelRatio;
|
||||
canvas.width = canvas.height = size;
|
||||
var ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
ctx.drawImage(image, 0, 0, size, size);
|
||||
try {
|
||||
canvas.toBlob((blob) => {
|
||||
resolve(blob);
|
||||
});
|
||||
} catch(e) {
|
||||
console.error('Failed to get canvas data for icon');
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
set: function(blob) {
|
||||
if (blob) {
|
||||
this._prepareIconLoader();
|
||||
this._hasUserSetIcon = true;
|
||||
this._pendingIconUrl = 'user-set';
|
||||
this._image.src = URL.createObjectURL(blob);
|
||||
} else {
|
||||
this._pendingIconUrl = null;
|
||||
delete this.dataset.testIconUrl;
|
||||
this._cancelIconLoad();
|
||||
this._removeOldIcon();
|
||||
}
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'showName', {
|
||||
get: function() {
|
||||
return this._showName;
|
||||
},
|
||||
|
||||
set: function(show) {
|
||||
show = show ? true : false;
|
||||
if (this._showName !== show) {
|
||||
this._showName = show;
|
||||
this._titleContainer.classList.toggle('hidden', !show);
|
||||
}
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'isUserSet', {
|
||||
get: function() {
|
||||
return this._iconUrl === 'user-set';
|
||||
},
|
||||
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
proto._cancelIconLoad = function() {
|
||||
if (!this._image) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._pendingIconUrl = null;
|
||||
this._image.onload = null;
|
||||
this._image.src = '';
|
||||
this._image = null;
|
||||
};
|
||||
|
||||
proto._removeOldIcon = function() {
|
||||
while (this._container.children.length > 1) {
|
||||
this._container.removeChild(this._container.lastChild);
|
||||
}
|
||||
};
|
||||
|
||||
proto.launch = function() {
|
||||
if (!this.app && !this.bookmark) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.classList.add('launching');
|
||||
setTimeout(() => { this.classList.remove('launching'); }, LAUNCH_TIMEOUT);
|
||||
|
||||
if (this.app) {
|
||||
window.performance.mark('appLaunch@' + this.app.origin);
|
||||
this.app.launch(this.entryPoint);
|
||||
} else {
|
||||
var features = {
|
||||
name: this.bookmark.name,
|
||||
remote: true
|
||||
};
|
||||
|
||||
if (this.bookmark.scope) {
|
||||
features.scope = this.bookmark.scope;
|
||||
}
|
||||
|
||||
window.open(this.bookmark.url, '_samescope', Object.keys(features)
|
||||
.map(function eachFeature(key) {
|
||||
return encodeURIComponent(key) + '=' +
|
||||
encodeURIComponent(features[key]);
|
||||
}).join(','));
|
||||
}
|
||||
};
|
||||
|
||||
proto._prepareIconLoader = function() {
|
||||
// Create new image element to load icon
|
||||
this._cancelIconLoad();
|
||||
this._hasPredefinedIcon = false;
|
||||
this._hasUserSetIcon = false;
|
||||
this._image = document.createElement('img');
|
||||
this._image.setAttribute('role', 'presentation');
|
||||
|
||||
this._image.onload = () => {
|
||||
this._image.onload = () => {
|
||||
// Add new icon
|
||||
this._removeOldIcon();
|
||||
this._container.appendChild(this._image);
|
||||
this._container.classList.remove('initial-load');
|
||||
this._image = null;
|
||||
this._hasIcon = true;
|
||||
|
||||
// Set icon URL on dataset, for testing.
|
||||
if (this._pendingIconUrl) {
|
||||
this._iconUrl = this.dataset.testIconUrl = this._pendingIconUrl;
|
||||
this._pendingIconUrl = null;
|
||||
}
|
||||
|
||||
if (!this._hasPredefinedIcon) {
|
||||
this.dispatchEvent(new CustomEvent('icon-loaded'));
|
||||
}
|
||||
|
||||
if (this._pendingIconRefresh) {
|
||||
this._image = null;
|
||||
this._pendingIconRefresh = false;
|
||||
this.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
if (!this._hasUserSetIcon) {
|
||||
// Process the image
|
||||
var canvas = document.createElement('canvas');
|
||||
var size = this.size * window.devicePixelRatio;
|
||||
canvas.width = canvas.height = size;
|
||||
|
||||
var ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
ctx.shadowColor = SHADOW_COLOR;
|
||||
ctx.shadowBlur = SHADOW_BLUR;
|
||||
ctx.shadowOffsetX = SHADOW_OFFSET.x;
|
||||
ctx.shadowOffsetY = SHADOW_OFFSET.y;
|
||||
|
||||
if (this.bookmark && !this._hasPredefinedIcon) {
|
||||
if (this._image.width <= size / 2) {
|
||||
// Draw filled background circle
|
||||
ctx.beginPath();
|
||||
ctx.arc(size / 2, size / 2,
|
||||
size / 2 - CANVAS_PADDING, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = DEFAULT_BACKGROUND_COLOR;
|
||||
ctx.fill();
|
||||
|
||||
// Disable shadow drawing and image smoothing
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.shadowOffsetY = 0;
|
||||
ctx.mozImageSmoothingEnabled = false;
|
||||
|
||||
var iconSize = size * FAVICON_SCALE;
|
||||
ctx.drawImage(this._image,
|
||||
(size - iconSize) / 2,
|
||||
(size - iconSize) / 2,
|
||||
iconSize, iconSize);
|
||||
} else {
|
||||
// Clip the favicon in a circle. We do this in a new canvas so
|
||||
// the shadow-rendering on the main canvas works correctly.
|
||||
var clipCanvas = document.createElement('canvas');
|
||||
clipCanvas.width = clipCanvas.height = size;
|
||||
|
||||
var clipCtx =
|
||||
clipCanvas.getContext('2d', { willReadFrequently: true });
|
||||
clipCtx.beginPath();
|
||||
clipCtx.arc(size / 2, size / 2,
|
||||
size / 2 - CANVAS_PADDING, 0, 2 * Math.PI);
|
||||
clipCtx.clip();
|
||||
|
||||
clipCtx.drawImage(this._image,
|
||||
CANVAS_PADDING, CANVAS_PADDING,
|
||||
size - CANVAS_PADDING * 2,
|
||||
size - CANVAS_PADDING * 2);
|
||||
ctx.drawImage(clipCanvas, 0, 0);
|
||||
}
|
||||
} else {
|
||||
// Just draw the icon straight (without padding if it was set
|
||||
// explicitly rather than loaded via manifest/bookmark)
|
||||
ctx.drawImage(this._image,
|
||||
CANVAS_PADDING, CANVAS_PADDING,
|
||||
size - CANVAS_PADDING * 2,
|
||||
size - CANVAS_PADDING * 2);
|
||||
}
|
||||
|
||||
canvas.toBlob(function(image, blob) {
|
||||
if (image.onload) {
|
||||
image.src = URL.createObjectURL(blob);
|
||||
}
|
||||
}.bind(this, this._image));
|
||||
}
|
||||
|
||||
if (this._hasUserSetIcon) {
|
||||
this._image.onload();
|
||||
}
|
||||
};
|
||||
|
||||
this._image.onerror = (e) => {
|
||||
console.error('Failed to load icon', e);
|
||||
this._pendingIconUrl = null;
|
||||
|
||||
if (this._pendingIconRefresh) {
|
||||
this._pendingIconRefresh = false;
|
||||
this._image = null;
|
||||
this.refresh();
|
||||
} else if (!this._hasIcon) {
|
||||
this._image.onerror = null;
|
||||
this._setPredefinedIcon('default');
|
||||
} else {
|
||||
this._image = null;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
proto._setPredefinedIcon = function(name) {
|
||||
this._hasPredefinedIcon = true;
|
||||
this._image.src = this._pendingIconUrl = this._predefinedIcons[name];
|
||||
};
|
||||
|
||||
proto._localizeString = function(str) {
|
||||
var userLang = document.documentElement.lang;
|
||||
|
||||
// We want to make sure that we tranlsate only if we're using
|
||||
// a runtime pseudolocale.
|
||||
// mozL10n.ctx.qps contains only runtime pseudolocales
|
||||
if (navigator.mozL10n &&
|
||||
navigator.mozL10n.ctx.qps.indexOf(userLang) !== -1) {
|
||||
return navigator.mozL10n.qps[userLang].translate(str);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
proto.updateName = function() {
|
||||
if (this.app) {
|
||||
var userLang = document.documentElement.lang;
|
||||
var ep = this.entryPoint || undefined;
|
||||
|
||||
this.app.getLocalizedValue('short_name', userLang, ep).then(
|
||||
(shortName) => {
|
||||
this._subtitle.textContent = this._localizeString(shortName);
|
||||
},
|
||||
() => {
|
||||
this.app.getLocalizedValue('name', userLang, ep).then(
|
||||
(name) => {
|
||||
this._subtitle.textContent = this._localizeString(name);
|
||||
},
|
||||
(e) => {
|
||||
// Try to fall back to manifest app name
|
||||
var manifest = this.app.manifest || this.app.updateManifest;
|
||||
if (manifest) {
|
||||
var nameObject = (manifest.entry_points && this.entryPoint) ?
|
||||
manifest.entry_points[this.entryPoint] : manifest;
|
||||
this._subtitle.textContent =
|
||||
this._localizeString(nameObject.short_name || nameObject.name);
|
||||
} else {
|
||||
console.error('Error retrieving app name', e);
|
||||
this._subtitle.textContent = '';
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (this.bookmark) {
|
||||
this._subtitle.textContent = this.bookmark.name;
|
||||
} else {
|
||||
this._subtitle.textContent = '';
|
||||
}
|
||||
};
|
||||
|
||||
proto.refresh = function() {
|
||||
if (!this._template) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateName();
|
||||
this._container.classList.remove('downloading');
|
||||
|
||||
// Set an identifier on the icon to help with automated testing.
|
||||
if (this.app) {
|
||||
this.dataset.identifier = this.app.manifestURL +
|
||||
(this.entryPoint ? '/' + this.entryPoint : '');
|
||||
} else if (this.bookmark) {
|
||||
this.dataset.identifier = this.bookmark.id;
|
||||
} else {
|
||||
delete this.dataset.identifier;
|
||||
}
|
||||
|
||||
// If we're already loading an icon, let that finish first.
|
||||
if (this._image) {
|
||||
this._pendingIconRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the icon won't be visible, don't bother fetching and processing it.
|
||||
var size = this.size * window.devicePixelRatio;
|
||||
if (size < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._prepareIconLoader();
|
||||
|
||||
// Handle icon loading for bookmarks, app icon loading is more involved
|
||||
// and handled below this block.
|
||||
if (this.bookmark) {
|
||||
// Fallback to the default icon
|
||||
if (!this._hasIcon) {
|
||||
this._setPredefinedIcon('default');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the app is downloading/paused/failed to download, use a predefined
|
||||
// icon.
|
||||
var state = this._lastState = this.state;
|
||||
switch (state) {
|
||||
case 'unrecoverable':
|
||||
this._setPredefinedIcon('unrecoverable');
|
||||
break;
|
||||
|
||||
case 'installing':
|
||||
this._setPredefinedIcon('installing');
|
||||
this._container.classList.add('downloading');
|
||||
break;
|
||||
|
||||
case 'paused':
|
||||
this._setPredefinedIcon('paused');
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
this._setPredefinedIcon('failed');
|
||||
break;
|
||||
|
||||
case 'installed':
|
||||
var handleError = function(image, e) {
|
||||
if (e.message !== 'NO_ICON' && e.name !== 'NO_ICON') {
|
||||
console.error('Failed to retrieve icon', e);
|
||||
}
|
||||
if (image.onload && !this._hasIcon) {
|
||||
this._setPredefinedIcon('default');
|
||||
} else {
|
||||
this._image = null;
|
||||
}
|
||||
};
|
||||
|
||||
var getImage = () => {
|
||||
navigator.mozApps.mgmt.getIcon(this.app, size, this.entryPoint).
|
||||
then(function(image, blob) {
|
||||
this._pendingIconUrl = 'app-icon';
|
||||
if (window.VulpesCompat) { this._vulpesSourceIcon = blob; this._hasUserSetIcon = true; } // Keep original app pixels; skip the legacy canvas pass.
|
||||
if (image.onload) {
|
||||
image.src = URL.createObjectURL(blob);
|
||||
}
|
||||
}.bind(this, this._image),
|
||||
handleError.bind(this, this._image));
|
||||
};
|
||||
|
||||
getImage();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
proto.handleEvent = function(e) {
|
||||
switch(e.type) {
|
||||
case 'progress':
|
||||
case 'downloadsuccess':
|
||||
if (this.state !== this._lastState) {
|
||||
this.refresh();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'downloaderror':
|
||||
case 'downloadapplied':
|
||||
this.refresh();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var template = document.createElement('template');
|
||||
var stylesheet = baseurl + 'style.css';
|
||||
template.innerHTML =
|
||||
`<style>@import url(${stylesheet});</style>
|
||||
<div id="image-container"><div id="spinner"></div></div>
|
||||
<div id="title-container"><div dir="auto" id="subtitle"></div></div>`;
|
||||
|
||||
return document.registerElement('gaia-app-icon', { prototype: proto });
|
||||
})(window);
|
||||
54
gaia/shared/elements/gaia-site-icon/style.css
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
gaia-app-icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
#image-container {
|
||||
position: relative;
|
||||
transition: visibility 0.2s, opacity 0.2s;
|
||||
}
|
||||
|
||||
#image-container.initial-load {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#image-container > #spinner {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#image-container.downloading > #spinner {
|
||||
display: block;
|
||||
background: no-repeat center center url('images/app_install_spinner.svg');
|
||||
background-size: contain;
|
||||
animation: rotate 2s infinite linear;
|
||||
}
|
||||
|
||||
#image-container,
|
||||
#image-container > * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#subtitle {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
#title-container {
|
||||
transition: opacity 0.2s, visibility 0.2s;
|
||||
}
|
||||
|
||||
#title-container.hidden {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
}
|
||||
38
gaia/shared/elements/gaia-text-input/.bower.json
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "gaia-text-input",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-text-input.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"manifest.webapp",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"gaia-icons": "fxos-components/gaia-icons#~1.0.0",
|
||||
"gaia-component": "fxos-components/gaia-component#~0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gaia-fonts": "fxos-components/gaia-fonts",
|
||||
"gaia-theme": "fxos-components/gaia-theme",
|
||||
"base": "fxos-components/base"
|
||||
},
|
||||
"homepage": "https://github.com/gaia-components/gaia-text-input",
|
||||
"version": "0.3.0",
|
||||
"_release": "0.3.0",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.3.0",
|
||||
"commit": "6a520d865fe541853f88ca74c098565da28bff41"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-text-input.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "gaia-components/gaia-text-input"
|
||||
}
|
||||
30
gaia/shared/elements/gaia-text-input/bower.json
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "gaia-text-input",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
|
||||
"main": "gaia-text-input.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"manifest.webapp",
|
||||
"README.md"
|
||||
],
|
||||
|
||||
"dependencies": {
|
||||
"gaia-icons": "fxos-components/gaia-icons#~1.0.0",
|
||||
"gaia-component": "fxos-components/gaia-component#~0.6.0"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"gaia-fonts": "fxos-components/gaia-fonts",
|
||||
"gaia-theme": "fxos-components/gaia-theme",
|
||||
"base": "fxos-components/base"
|
||||
}
|
||||
}
|
||||
214
gaia/shared/elements/gaia-text-input/gaia-text-input-multiline.js
Executable file
|
|
@ -0,0 +1,214 @@
|
|||
(define=>{define((require,exports,module)=>{
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-text-input-multiline', {
|
||||
created() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
inner: this.shadowRoot.querySelector('.inner'),
|
||||
field: this.shadowRoot.querySelector('textarea')
|
||||
};
|
||||
|
||||
this.type = this.getAttribute('type');
|
||||
this.disabled = this.hasAttribute('disabled');
|
||||
this.placeholder = this.getAttribute('placeholder');
|
||||
this.required = this.getAttribute('required');
|
||||
this.value = this.getAttribute('value');
|
||||
},
|
||||
|
||||
clear(e) {
|
||||
this.value = '';
|
||||
},
|
||||
|
||||
attrs: {
|
||||
placeholder: {
|
||||
get: function() { return this.els.field.placeholder; },
|
||||
set: function(value) { this.els.field.placeholder = value || ''; }
|
||||
},
|
||||
|
||||
value: {
|
||||
get: function() { return this.els.field.value; },
|
||||
set: function(value) { this.els.field.value = value; }
|
||||
},
|
||||
|
||||
required: {
|
||||
get: function() { return this.els.field.required; },
|
||||
set: function(value) { this.els.field.required = value; }
|
||||
},
|
||||
|
||||
disabled: {
|
||||
get: function() { return this.els.field.disabled; },
|
||||
set: function(value) {
|
||||
value = !!(value === '' || value);
|
||||
this.els.field.disabled = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: `<div class="inner">
|
||||
<content select="label"></content>
|
||||
<div class="fields">
|
||||
<textarea></textarea>
|
||||
<div class="focus focus-1"></div>
|
||||
<div class="focus focus-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
|
||||
/** Reset
|
||||
---------------------------------------------------------*/
|
||||
|
||||
textarea {
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/** Host
|
||||
---------------------------------------------------------*/
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
margin-top: var(--base-m, 18px);
|
||||
margin-bottom: var(--base-m, 18px);
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** Label
|
||||
---------------------------------------------------------*/
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin: 0 0 4px 16px;
|
||||
}
|
||||
|
||||
/**
|
||||
* [disbled]
|
||||
*/
|
||||
|
||||
[disabled] label {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/** Fields
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.fields {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
--gi-border-color:
|
||||
var(--input-border-color,
|
||||
var(--border-color,
|
||||
var(--background-plus,
|
||||
#e7e7e7)));
|
||||
|
||||
border-color:
|
||||
var(--gi-border-color);
|
||||
|
||||
border:
|
||||
var(--input-border,
|
||||
var(--border,
|
||||
1px solid var(--gi-border-color)));
|
||||
}
|
||||
|
||||
/** Textarea
|
||||
---------------------------------------------------------*/
|
||||
|
||||
textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 86px;
|
||||
padding: 10px 16px;
|
||||
font-size: inherit;
|
||||
border: none;
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
resize: none;
|
||||
|
||||
/* dynamic */
|
||||
|
||||
color:
|
||||
var(--text-color, #000);
|
||||
|
||||
background:
|
||||
var(--text-input-background,
|
||||
var(--input-background,
|
||||
var(--background-minus,
|
||||
#fff)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [disabled]
|
||||
*/
|
||||
|
||||
textarea[disabled] {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/** Placeholder Text
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::-moz-placeholder {
|
||||
font-style: italic;
|
||||
|
||||
color:
|
||||
var(--input-placeholder-color, #909ca7);
|
||||
}
|
||||
|
||||
/** Focus Bar
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.focus {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
transition: all 200ms;
|
||||
transform: scaleX(0);
|
||||
visibility: hidden;
|
||||
background: var(--highlight-color, #000);
|
||||
}
|
||||
|
||||
/**
|
||||
* input:focus
|
||||
*/
|
||||
|
||||
:focus ~ .focus {
|
||||
transform: scaleX(1);
|
||||
transition-delay: 200ms;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.focus-2 {
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
</style>`
|
||||
});
|
||||
|
||||
})})(((n,w)=>{return(typeof define)[0]=='f'&&define.amd?define:(typeof module)[0]=='o'?c =>{c(require,exports,module)}:c=>{var m={exports:{}},r=n=>w[n];w[n]=c(r,m.exports,m)||m.exports;};})('gaia-text-input-multiline',this));/*jshint ignore:line*/
|
||||
278
gaia/shared/elements/gaia-text-input/gaia-text-input-pin.js
Executable file
|
|
@ -0,0 +1,278 @@
|
|||
(define=>{define((require,exports,module)=>{
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Locals
|
||||
*/
|
||||
|
||||
var DEFAULT_LENGTH = 4;
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-text-input-pin', {
|
||||
created() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
inner: this.shadowRoot.querySelector('.inner'),
|
||||
fields: this.shadowRoot.querySelector('.fields'),
|
||||
input: this.shadowRoot.querySelector('input')
|
||||
};
|
||||
|
||||
this.disabled = this.hasAttribute('disabled');
|
||||
this.length = this.getAttribute('length') || DEFAULT_LENGTH;
|
||||
|
||||
this.addEventListener('keyup', () => this.updateCells());
|
||||
},
|
||||
|
||||
updateCells() {
|
||||
var l = this.els.input.value.length;
|
||||
this.els.cells.forEach((cell, i) => {
|
||||
cell.classList.toggle('populated', i < l);
|
||||
cell.classList.toggle('focused', i == l);
|
||||
});
|
||||
},
|
||||
|
||||
onBackspace(e) {
|
||||
var input = e.target;
|
||||
var empty = !input.value;
|
||||
var previous = input.previousElementSibling;
|
||||
|
||||
if (!empty && previous) {
|
||||
previous.clear();
|
||||
previous.focus();
|
||||
} else if (empty && previous) {
|
||||
previous.focus();
|
||||
}
|
||||
},
|
||||
|
||||
setupFields() {
|
||||
this.els.fields.innerHTML = '';
|
||||
this.els.cells = [];
|
||||
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
var el = document.createElement('div');
|
||||
el.className = 'cell';
|
||||
this.els.fields.appendChild(el);
|
||||
this.els.cells.push(el);
|
||||
}
|
||||
},
|
||||
|
||||
clear(e) {
|
||||
this.value = '';
|
||||
this.updateCells();
|
||||
},
|
||||
|
||||
focus(e) {
|
||||
this.els.input.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Attributes
|
||||
*/
|
||||
|
||||
attrs: {
|
||||
length: {
|
||||
get: function() { return this._length; },
|
||||
set: function(value) {
|
||||
value = Number(value);
|
||||
this._length = value;
|
||||
this.els.input.setAttribute('maxlength', this.length);
|
||||
this.setupFields();
|
||||
}
|
||||
},
|
||||
|
||||
value: {
|
||||
get: function() { return this.els.input.value; },
|
||||
set: function(value) { this.els.input.value = value; }
|
||||
},
|
||||
|
||||
disabled: {
|
||||
get: function() { return this.els.input.disabled; },
|
||||
set: function(value) {
|
||||
value = !!(value === '' || value);
|
||||
this.els.input.disabled = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="inner">
|
||||
<content select="label"></content>
|
||||
<div class="container">
|
||||
<input x-inputmode="digit"/>
|
||||
<div class="fields"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/** Host
|
||||
---------------------------------------------------------*/
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
height: 40px;
|
||||
margin-top: var(--base-m, 18px);
|
||||
margin-bottom: var(--base-m, 18px);
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** Label
|
||||
---------------------------------------------------------*/
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin: 0 0 4px 16px;
|
||||
}
|
||||
|
||||
/** Container
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** Input (hidden)
|
||||
---------------------------------------------------------*/
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/** Fields
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.fields {
|
||||
display: flex;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
/**
|
||||
* [disbled]
|
||||
*/
|
||||
|
||||
[disabled] + .fields {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/** Cell
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.cell {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
margin-left: 1rem;
|
||||
flex: 1;
|
||||
|
||||
/* dynamic */
|
||||
|
||||
--gi-border-color:
|
||||
var(--input-border-color,
|
||||
var(--border-color,
|
||||
var(--background-plus,
|
||||
#e7e7e7)));
|
||||
|
||||
border-color:
|
||||
var(--gi-border-color);
|
||||
|
||||
border:
|
||||
var(--input-border,
|
||||
var(--border,
|
||||
1px solid var(--gi-border-color)));
|
||||
|
||||
background:
|
||||
var(--text-input-background,
|
||||
var(--input-background,
|
||||
var(--background-minus,
|
||||
#fff)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [disbled]
|
||||
*/
|
||||
|
||||
[disabled] + .fields .cell {
|
||||
background: none;
|
||||
}
|
||||
|
||||
/** Dot
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.cell::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin: -7px;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
|
||||
/* dynamic */
|
||||
|
||||
background:
|
||||
var(--text-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* .populated
|
||||
*/
|
||||
|
||||
.cell.populated::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.cell::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
visibility: hidden;
|
||||
background: var(--highlight-color, #000);
|
||||
}
|
||||
|
||||
/**
|
||||
* input:focus
|
||||
*/
|
||||
|
||||
input:focus + .fields .cell::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
</style>
|
||||
`
|
||||
});
|
||||
|
||||
})})(((n,w)=>{return(typeof define)[0]=='f'&&define.amd?define:(typeof module)[0]=='o'?c =>{c(require,exports,module)}:c=>{var m={exports:{}},r=n=>w[n];w[n]=c(r,m.exports,m)||m.exports;};})('gaia-text-input-pin',this));/*jshint ignore:line*/
|
||||
378
gaia/shared/elements/gaia-text-input/gaia-text-input.js
Executable file
|
|
@ -0,0 +1,378 @@
|
|||
(define=>{define((require,exports,module)=>{
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
require('gaia-icons'); // Load gaia-icons
|
||||
|
||||
/**
|
||||
* Mini logger
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
var debug = 0 ? (...args) => console.log('[GaiaTextInput]', ...args) : () => {};
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-text-input', {
|
||||
created() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
inner: this.shadowRoot.querySelector('.inner'),
|
||||
input: this.shadowRoot.querySelector('input'),
|
||||
clear: this.shadowRoot.querySelector('.clear')
|
||||
};
|
||||
|
||||
this.type = this.getAttribute('type');
|
||||
this.inputmode = this.getAttribute('x-inputmode');
|
||||
this.disabled = this.hasAttribute('disabled');
|
||||
this.clearable = this.hasAttribute('clearable');
|
||||
this.placeholder = this.getAttribute('placeholder');
|
||||
this.required = this.getAttribute('required');
|
||||
this.value = this.getAttribute('value');
|
||||
|
||||
// Don't take focus from the input field
|
||||
this.els.clear.addEventListener('mousedown', e => e.preventDefault());
|
||||
|
||||
this.els.clear.addEventListener('click', e => this.clear(e));
|
||||
this.els.input.addEventListener('input', e => this.onInput(e));
|
||||
this.els.input.addEventListener('focus', e => this.onFocus(e));
|
||||
this.els.input.addEventListener('blur', e => this.onBlur(e));
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the field.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
clear() {
|
||||
debug('clear');
|
||||
this.value = '';
|
||||
this.emit('clear');
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the field.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
focus() {
|
||||
debug('focus');
|
||||
this.els.input.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Unfocus the field.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
blur() {
|
||||
debug('blur');
|
||||
this.els.input.blur();
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs when the field is focused.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onFocus() {
|
||||
debug('on focus');
|
||||
this.els.inner.classList.add('focused');
|
||||
this.emit('focus');
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs when the field is unfocused.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onBlur() {
|
||||
debug('on blur');
|
||||
this.els.inner.classList.remove('focused');
|
||||
this.emit('blur');
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs when the value of the
|
||||
* input is manually changed.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onInput() {
|
||||
debug('on input');
|
||||
this.onValueChanged();
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs when the values changes
|
||||
* programatically or via keystrokes.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onValueChanged() {
|
||||
debug('value changed');
|
||||
var hasValue = !!this.value.length;
|
||||
this.els.inner.classList.toggle('has-value', hasValue);
|
||||
this.emit('input');
|
||||
},
|
||||
|
||||
/**
|
||||
* Emit a DOM event on the component.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {*} detail
|
||||
* @private
|
||||
*/
|
||||
emit(name, detail) {
|
||||
var e = new CustomEvent(name, { detail: detail });
|
||||
this.dispatchEvent(e);
|
||||
},
|
||||
|
||||
/**
|
||||
* Attributes
|
||||
*/
|
||||
|
||||
attrs: {
|
||||
type: {
|
||||
get: function() { return this.els.input.getAttribute('type'); },
|
||||
set: function(value) {
|
||||
if (!value) { return; }
|
||||
this.els.inner.setAttribute('type', value);
|
||||
this.els.input.setAttribute('type', value);
|
||||
}
|
||||
},
|
||||
|
||||
inputmode: {
|
||||
get: function() { return this.els.input.getAttribute('x-inputmode'); },
|
||||
set: function(value) {
|
||||
if (!value) {
|
||||
this.els.input.removeAttribute('x-inputmode');
|
||||
return;
|
||||
}
|
||||
this.els.input.setAttribute('x-inputmode', value);
|
||||
}
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
get: function() { return this.field.placeholder; },
|
||||
set: function(value) {
|
||||
if (!value && value !== '') { return; }
|
||||
this.els.input.placeholder = value;
|
||||
}
|
||||
},
|
||||
|
||||
clearable: {
|
||||
get: function() { return this._clearable; },
|
||||
set: function(value) {
|
||||
var clearable = !!(value === '' || value);
|
||||
if (clearable === this.clearable) { return; }
|
||||
|
||||
if (clearable) this.setAttr('clearable', '');
|
||||
else this.removeAttr('clearable');
|
||||
|
||||
this._clearable = clearable;
|
||||
}
|
||||
},
|
||||
|
||||
value: {
|
||||
get: function() { return this.els.input.value; },
|
||||
set: function(value) {
|
||||
debug('set value', value);
|
||||
this.els.input.value = value;
|
||||
this.onValueChanged();
|
||||
}
|
||||
},
|
||||
|
||||
required: {
|
||||
get: function() { return this.els.input.required; },
|
||||
set: function(value) { this.els.input.required = value; }
|
||||
},
|
||||
|
||||
maxlength: {
|
||||
get: function() { return this.els.input.getAttribute('maxlength'); },
|
||||
set: function(value) { this.els.input.setAttribute('maxlength', value); }
|
||||
},
|
||||
|
||||
disabled: {
|
||||
get: function() { return this.els.input.disabled; },
|
||||
set: function(value) {
|
||||
value = !!(value === '' || value);
|
||||
this.els.input.disabled = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="inner">
|
||||
<content select="label"></content>
|
||||
<div class="fields">
|
||||
<input type="text"/>
|
||||
<button class="clear" tabindex="-1"></button>
|
||||
<div class="focus"></div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
height: 40px;
|
||||
margin-top: var(--base-m, 18px);
|
||||
margin-bottom: var(--base-m, 18px);
|
||||
font-size: 17px;
|
||||
overflow: hidden;
|
||||
|
||||
--this-background:
|
||||
var(--text-input-background,
|
||||
var(--input-background,
|
||||
var(--background-minus,
|
||||
#fff)));
|
||||
|
||||
--this-border-color:
|
||||
var(--input-border-color,
|
||||
var(--border-color,
|
||||
var(--background-plus,
|
||||
#e7e7e7)));
|
||||
}
|
||||
|
||||
.inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin: 0 0 4px 16px;
|
||||
}
|
||||
|
||||
[disabled] label {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.fields {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
border-color:
|
||||
var(--this-border-color);
|
||||
|
||||
border:
|
||||
var(--input-border,
|
||||
var(--border,
|
||||
1px solid var(--this-border-color)));
|
||||
}
|
||||
|
||||
[type='search'] .fields {
|
||||
border-radius: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
|
||||
font: inherit;
|
||||
resize: none;
|
||||
|
||||
/* Dynamic */
|
||||
|
||||
color: var(--text-color, #000);
|
||||
background: var(--this-background);
|
||||
}
|
||||
|
||||
input[disabled] {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
font-style: italic;
|
||||
|
||||
color:
|
||||
var(--input-placeholder-color, #909ca7);
|
||||
}
|
||||
|
||||
.clear {
|
||||
position: absolute;
|
||||
offset-inline-end: 0;
|
||||
top: 0;
|
||||
|
||||
display: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: solid 10px transparent;
|
||||
box-sizing: content-box;
|
||||
|
||||
border-radius: 50%;
|
||||
color: var(--background);
|
||||
cursor: pointer;
|
||||
background: var(--input-clear-background, #999);
|
||||
background-clip: padding-box;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[clearable] .clear {
|
||||
display: block;
|
||||
}
|
||||
|
||||
[clearable].has-value.focused .clear {
|
||||
pointer-events: all;
|
||||
transition: opacity 200ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.clear:before {
|
||||
content: 'close';
|
||||
display: block;
|
||||
|
||||
font: normal 500 19px/17px 'gaia-icons';
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
.focus {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0px;
|
||||
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
|
||||
transition: transform 200ms;
|
||||
transform: scaleX(0);
|
||||
visibility: hidden;
|
||||
background: var(--highlight-color, #000);
|
||||
}
|
||||
|
||||
[type='search'] .focus {
|
||||
border-radius: 0 0 60px 60px;
|
||||
left: 10px;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
/**
|
||||
* .focused
|
||||
*/
|
||||
|
||||
.focused .focus {
|
||||
transform: scaleX(1);
|
||||
transition-delay: 200ms;
|
||||
visibility: visible;
|
||||
}
|
||||
</style>`
|
||||
});
|
||||
|
||||
})})(((n,w)=>{return(typeof define)[0]=='f'&&define.amd?define:(typeof module)[0]=='o'?c =>{c(require,exports,module)}:c=>{var m={exports:{}},r=n=>w[n];w[n]=c(r,m.exports,m)||m.exports;};})('gaia-text-input',this));/*jshint ignore:line*/
|
||||
34
gaia/shared/elements/gaia-text-input/package.json
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "gaia-text-input",
|
||||
"version": "0.3.0",
|
||||
"homepage": "https://github.com/fxos-components/gaia-text-input",
|
||||
"license": "ISC",
|
||||
"main": "gaia-text-input.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": "^1.6.5",
|
||||
"jshint": "^2.8.0",
|
||||
"karma": "^0.13.15",
|
||||
"karma-firefox-launcher": "^0.1.6",
|
||||
"karma-mocha": "^0.2.0",
|
||||
"karma-sinon-chai": "^0.3.0",
|
||||
"mocha": "^2.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "bower install",
|
||||
"test": "npm run --silent test-unit && npm run --silent test-lint",
|
||||
"test-lint": "./node_modules/jshint/bin/jshint *.js",
|
||||
"test-unit": "./node_modules/karma/bin/karma start test/karma.conf.js --single-run",
|
||||
"test-unit-dev": "./node_modules/karma/bin/karma start test/karma.conf.js",
|
||||
"gh-pages": "git co gh-pages; git merge master; git push upstrea gh-pages; git co -"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/fxos-components/gaia-text-input.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/fxos-components/gaia-text-input/issues"
|
||||
}
|
||||
}
|
||||
35
gaia/shared/elements/gaia-theme/.bower.json
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "gaia-theme",
|
||||
"version": "0.4.7",
|
||||
"homepage": "https://github.com/gaia-components/gaia-theme",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"gaia-icons": "gaia-components/gaia-icons",
|
||||
"gaia-header": "gaia-components/gaia-header",
|
||||
"gaia-button": "gaia-components/gaia-button",
|
||||
"gaia-switch": "gaia-components/gaia-switch",
|
||||
"gaia-checkbox": "gaia-components/gaia-checkbox"
|
||||
},
|
||||
"_release": "0.4.7",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.4.7",
|
||||
"commit": "a1b3220e6ae53503b0809eb04de22f28bc701612"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-theme.git",
|
||||
"_target": "~0.4.6",
|
||||
"_originalSource": "gaia-components/gaia-theme"
|
||||
}
|
||||
4
gaia/shared/elements/gaia-theme/.jshintrc
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"strict": false,
|
||||
"node": true
|
||||
}
|
||||
26
gaia/shared/elements/gaia-theme/bower.json
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "gaia-theme",
|
||||
"version": "0.4.7",
|
||||
"homepage": "https://github.com/gaia-components/gaia-theme",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"gaia-icons": "gaia-components/gaia-icons",
|
||||
"gaia-header": "gaia-components/gaia-header",
|
||||
"gaia-button": "gaia-components/gaia-button",
|
||||
"gaia-switch": "gaia-components/gaia-switch",
|
||||
"gaia-checkbox": "gaia-components/gaia-checkbox"
|
||||
}
|
||||
}
|
||||
246
gaia/shared/elements/gaia-theme/gaia-theme.css
Executable file
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
Gaia theme variables
|
||||
*/
|
||||
|
||||
|
||||
:root {
|
||||
|
||||
/** Grey Colors
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--color-alpha: #333333;
|
||||
--color-beta: #ffffff;
|
||||
--color-gamma: #4d4d4d;
|
||||
--color-delta: #5f5f5f;
|
||||
--color-epsilon: #858585;
|
||||
--color-zeta: #a6a6a6;
|
||||
--color-eta: #c7c7c7;
|
||||
--color-theta: #e7e7e7;
|
||||
--color-iota: #f4f4f4;
|
||||
|
||||
/** Brand Colors
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--color-darkblue: #00539f;
|
||||
--color-blue: #00caf2;
|
||||
--color-turquoise: #27c8c2;
|
||||
--color-darkorange: #e66000;
|
||||
--color-orange: #ff9500;
|
||||
--color-yellow: #ffcb00;
|
||||
--color-violet: #c40c84;
|
||||
|
||||
--color-warning: #fbbd3c;
|
||||
--color-destructive: #e2443a;
|
||||
--color-preffered: #00ba91;
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--background: var(--color-beta);
|
||||
--background-plus: var(--color-theta);
|
||||
--background-minus: var(--color-zeta);
|
||||
|
||||
/** Borders
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--border-color: var(--color-theta);
|
||||
|
||||
/** Text Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-color: var(--color-gamma);
|
||||
--text-color-minus: var(--color-eta);
|
||||
--label-color: #000;
|
||||
|
||||
/** Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-background: var(--color-iota);
|
||||
|
||||
/** Links
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--link-color: var(--highlight-color);
|
||||
|
||||
/** Inputs
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--input-background: var(--color-beta);
|
||||
--input-color: var(--color-alpha);
|
||||
--input-clear-background: #909ca7;
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-box-shadow:
|
||||
inset 0 -1px 0 #d8d8d8,
|
||||
inset 0 1px 0 #eeeeee;
|
||||
--button-box-shadow-active: var(--button-box-shadow);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-disabled-button-color: rgba(255,255,255,0.3);
|
||||
|
||||
/** Transitions
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
|
||||
.theme-productivity {
|
||||
|
||||
/** Brand Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-orange);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-background: var(--highlight-color);
|
||||
--header-color: var(--color-beta);
|
||||
}
|
||||
|
||||
.theme-communications {
|
||||
|
||||
/** Brand Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-turquoise);
|
||||
|
||||
/** Links
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--link-color: #177874;
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-background: var(--highlight-color);
|
||||
--header-color: var(--color-beta);
|
||||
--header-icon-color: var(--color-beta);
|
||||
--header-button-color: var(--link-color);
|
||||
}
|
||||
|
||||
.theme-settings {
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--background: var(--color-iota);
|
||||
--background-plus: var(--color-beta);
|
||||
--background-minus: var(--color-epsilon);
|
||||
--background-minus-minus: var(--color-zeta);
|
||||
|
||||
/** Highlight
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-blue);
|
||||
|
||||
/** Borders
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--border-color: var(--color-theta);
|
||||
|
||||
/** Text Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-color: var(--color-gamma);
|
||||
|
||||
/** Titles
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--title-color: var(--background-minus);
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-background: var(--background-plus);
|
||||
|
||||
/** Switch
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--switch-background: var(--background-minus-minus);
|
||||
|
||||
/** Checkbox
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--checkbox-border-color: var(--background-minus-minus);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-color: var(--title-color);
|
||||
--header-background: var(--background);
|
||||
--header-button-color: var(--highlight-color);
|
||||
--header-disabled-button-color: var(--color-theta);
|
||||
--header-action-button-color: var(--title-color);
|
||||
}
|
||||
|
||||
.theme-media {
|
||||
|
||||
/** Highlight Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-blue);
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--background: var(--color-alpha);
|
||||
--background-plus: var(--color-gamma);
|
||||
--background-minus: #2B2B2B;
|
||||
--background-minus-minus: #1a1a1a;
|
||||
|
||||
/** Text Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-color: var(--color-beta);
|
||||
--label-color: var(--color-beta);
|
||||
|
||||
/** Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-background: var(--background-plus);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-background: var(--background);
|
||||
--header-icon-color: var(--text-color);
|
||||
--header-button-color: var(--highlight-color);
|
||||
|
||||
/** Borders
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--border-color: var(--color-gamma);
|
||||
|
||||
/** Inputs
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--input-background: var(--background-plus);
|
||||
|
||||
/** Text Input
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-input-background: var(--background-minus);
|
||||
|
||||
/** Switch
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--switch-head-border-color: var(--background-minus-minus);
|
||||
--switch-background: var(--background-minus-minus);
|
||||
|
||||
/** Checkbox
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--checkbox-border-color: var(--background-minus-minus);
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-box-shadow: none;
|
||||
--button-box-shadow-active: none;
|
||||
}
|
||||
111
gaia/shared/elements/gaia-theme/lib/gaia-theme-selector.js
Executable file
|
|
@ -0,0 +1,111 @@
|
|||
(function(define){'use strict';define(function(require,exports,module){
|
||||
/*jshint esnext:true*/
|
||||
|
||||
/**
|
||||
* Extend from the `HTMLElement` prototype
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
proto.createdCallback = function() {
|
||||
this.createShadowRoot().innerHTML = template;
|
||||
|
||||
this.els = {
|
||||
themes: this.shadowRoot.querySelector('.themes'),
|
||||
rtl: this.shadowRoot.querySelector('.toggle-rtl')
|
||||
};
|
||||
|
||||
this.els.rtl.addEventListener('click', this.onRtlClick.bind(this));
|
||||
this.els.themes.addEventListener('click', this.onThemeClick.bind(this));
|
||||
this.styleHack();
|
||||
this.set(localStorage.theme || 'settings');
|
||||
};
|
||||
|
||||
proto.styleHack = function() {
|
||||
var style = this.shadowRoot.querySelector('style');
|
||||
style.setAttribute('scoped', '');
|
||||
this.appendChild(style.cloneNode(true));
|
||||
};
|
||||
|
||||
proto.onThemeClick = function(e) {
|
||||
var theme = e.target.value;
|
||||
if (theme) { this.set(theme); }
|
||||
};
|
||||
|
||||
proto.onRtlClick = function() {
|
||||
document.dir = this.els.rtl.checked ? 'rtl' : 'ltr';
|
||||
};
|
||||
|
||||
proto.set = function(theme) {
|
||||
var radio = this.shadowRoot.querySelector('[value="' + theme + '"]');
|
||||
document.body.classList.remove('theme-' + this.theme);
|
||||
document.body.classList.add('theme-' + theme);
|
||||
radio.checked = true;
|
||||
this.theme = theme;
|
||||
localStorage.theme = theme;
|
||||
};
|
||||
|
||||
var template = `
|
||||
<style>
|
||||
|
||||
* { margin: 0; padding: 0; }
|
||||
|
||||
gaia-theme-selector {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
z-index: 101;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background: rgba(255,255,255,0.8);
|
||||
color: #000;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
form label {
|
||||
-moz-margin-start: 9px;
|
||||
}
|
||||
|
||||
form input {
|
||||
-moz-margin-end: 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.rtl {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<form>
|
||||
<label class="rtl"><input type="checkbox" class="toggle-rtl" />RTL</label>
|
||||
<span class="themes">
|
||||
<label><input type="radio" name="theme" value="productivity"/>Prod</label>
|
||||
<label><input type="radio" name="theme" value="communications"/>Comms</label>
|
||||
<label><input type="radio" name="theme" value="media"/>Media</label>
|
||||
<label><input type="radio" name="theme" value="settings"/>Settings</label>
|
||||
</span>
|
||||
</form>`;
|
||||
|
||||
document.registerElement('gaia-theme-selector', { prototype: proto });
|
||||
|
||||
});})((function(n,w){'use strict';return typeof define=='function'&&define.amd?
|
||||
define:typeof module=='object'?function(c){c(require,exports,module);}:
|
||||
function(c){var m={exports:{}},r=function(n){return w[n];};
|
||||
w[n]=c(r,m.exports,m)||m.exports;};})('gaia-theme-selector',this));
|
||||
38
gaia/shared/elements/gaia-toast/.bower.json
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "gaia-toast",
|
||||
"homepage": "https://github.com/gaia-components/gaia-toast",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "script.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"index.html",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"base": "gaia-components/base",
|
||||
"gaia-theme": "gaia-components/gaia-theme#~0.4.1",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts#~0.2.1",
|
||||
"gaia-button": "gaia-components/gaia-button#master"
|
||||
},
|
||||
"dependencies": {
|
||||
"gaia-component": "gaia-components/gaia-component#~0.2.0"
|
||||
},
|
||||
"_release": "fd000de635",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "fd000de635edc3298cf78c387a1bcb7667fd7382"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/gaia-toast.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "gaia-components/gaia-toast",
|
||||
"_direct": true
|
||||
}
|
||||
29
gaia/shared/elements/gaia-toast/bower.json
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "gaia-toast",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/gaia-components/gaia-toast",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "script.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"index.html",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"base": "gaia-components/base",
|
||||
"gaia-theme": "gaia-components/gaia-theme#~0.4.1",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts#~0.2.1",
|
||||
"gaia-button": "gaia-components/gaia-button#master"
|
||||
},
|
||||
"dependencies": {
|
||||
"gaia-component": "gaia-components/gaia-component#~0.2.0"
|
||||
}
|
||||
}
|
||||
170
gaia/shared/elements/gaia-toast/gaia-toast.js
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
;(function(define){define(function(require,exports,module){
|
||||
/*jshint esnext:true*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-toast', {
|
||||
|
||||
created: function() {
|
||||
this.setupShadowRoot();
|
||||
|
||||
this.els = {
|
||||
inner: this.shadowRoot.querySelector('.inner'),
|
||||
bread: this.shadowRoot.querySelector('.bread')
|
||||
};
|
||||
|
||||
this.timeout = this.getAttribute('timeout');
|
||||
},
|
||||
|
||||
|
||||
show: function() {
|
||||
this.els.bread.removeEventListener('animationend', this.onAnimateOutEnd);
|
||||
clearTimeout(this.hideTimeout);
|
||||
|
||||
this.els.inner.classList.add('visible');
|
||||
var reflow = this.els.inner.offsetTop;
|
||||
this.els.bread.classList.remove('animate-out');
|
||||
this.els.bread.classList.add('animate-in');
|
||||
this.hideTimeout = setTimeout(this.hide.bind(this), this.timeout);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
var self = this;
|
||||
|
||||
clearTimeout(this.hideTimeout);
|
||||
this.els.bread.classList.remove('animate-in');
|
||||
this.els.bread.classList.add('animate-out');
|
||||
|
||||
this.els.bread.removeEventListener('animationend', this.onAnimateOutEnd);
|
||||
this.onAnimateOutEnd = function() {
|
||||
self.els.bread.removeEventListener('animationend', self.onAnimateOutEnd);
|
||||
self.els.bread.classList.remove('animate-out');
|
||||
self.els.inner.classList.remove('visible');
|
||||
};
|
||||
|
||||
this.els.bread.addEventListener('animationend', this.onAnimateOutEnd);
|
||||
},
|
||||
|
||||
animateOut: function() {},
|
||||
|
||||
attrs: {
|
||||
timeout: {
|
||||
get: function() { return this.getAttribute('timeout') || 1000; },
|
||||
set: function(value) {
|
||||
var current = this.getAttribute('timeout');
|
||||
if (current == value) { return; }
|
||||
else if (!value) { this.removeAttribute('timeout'); }
|
||||
else { this.setAttribute('timeout', value); }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="inner">
|
||||
<div class="bread">
|
||||
<content></content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
/** Host
|
||||
---------------------------------------------------------*/
|
||||
|
||||
:host {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
|
||||
color:
|
||||
var(--highlight-color);
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
display: none;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.inner.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.bread {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(100%);
|
||||
|
||||
background:
|
||||
var(--background-plus,
|
||||
white);
|
||||
}
|
||||
|
||||
.bread.animate-in {
|
||||
animation-name: gaia-toast-enter;
|
||||
animation-fill-mode: forwards;
|
||||
animation-duration: 300ms;
|
||||
}
|
||||
|
||||
.bread.animate-out {
|
||||
animation-name: gaia-toast-leave;
|
||||
animation-duration: 600ms;
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
</style>
|
||||
`,
|
||||
|
||||
globalCss: `
|
||||
@keyframes gaia-toast-enter {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gaia-toast-leave {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
`
|
||||
});
|
||||
|
||||
});})(typeof define=='function'&&define.amd?define
|
||||
:(function(n,w){'use strict';return typeof module=='object'?function(c){
|
||||
c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){
|
||||
return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-toast',this));
|
||||
45
gaia/shared/elements/gaia_buttons/examples/index.html
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>GaiaButtons Demo</title>
|
||||
<meta name="description" content="Demo app for gaia-buttons" />
|
||||
<style type="text/css">
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
var dirInput = document.getElementById('dir');
|
||||
dirInput.checked = false;
|
||||
dirInput.onchange= function() {
|
||||
document.documentElement.dir = this.checked ? "rtl" : "ltr";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script>window.GaiaButtonsBaseurl = '../';</script>
|
||||
<script src="../../../js/component_utils.js"></script>
|
||||
<script src="../script.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body role="application">
|
||||
<p dir="ltr">
|
||||
<label class="bb-docs">
|
||||
<input id="dir" type="checkbox"> RTL mode
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<gaia-buttons class="inline">
|
||||
<button class="full" dir="auto">With class="inline"</button>
|
||||
</gaia-buttons>
|
||||
<gaia-buttons skin="dark">
|
||||
<button class="recommend icon icon-previous">Button 1</button>
|
||||
<button disabled>Button 2</button>
|
||||
<a href="#" class="danger">Anchor 1</a>
|
||||
</gaia-buttons>
|
||||
</body>
|
||||
</html>
|
||||
52
gaia/shared/elements/gaia_buttons/examples/stress.html
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>GaiaButtons Demo</title>
|
||||
<meta name="description" content="Demo app for gaia-buttons" />
|
||||
<style type="text/css">
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
var dirInput = document.getElementById('dir');
|
||||
dirInput.checked = false;
|
||||
dirInput.onchange = function() {
|
||||
document.documentElement.dir = this.checked ? "rtl" : "ltr";
|
||||
};
|
||||
};
|
||||
|
||||
</script>
|
||||
<script>window.GaiaButtonsBaseurl = '../';</script>
|
||||
<script src="../../../js/component_utils.js"></script>
|
||||
<script src="../script.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body role="application">
|
||||
<p dir="ltr">
|
||||
<label class="bb-docs">
|
||||
<input id="dir" type="checkbox"> RTL mode
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<section id="stress-test">
|
||||
</section>
|
||||
<script>
|
||||
// add a thousand buttons
|
||||
var stressSection = document.getElementById('stress-test');
|
||||
var innerSectionHtml = '';
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
innerSectionHtml +=
|
||||
'<gaia-buttons class="inline">' +
|
||||
' <button class="icon icon-next">next</button>' +
|
||||
'</gaia-buttons>';
|
||||
}
|
||||
stressSection.innerHTML = innerSectionHtml;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1
gaia/shared/elements/gaia_buttons/images/left-light.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30"><path fill="#667174" fill-opacity=".7" d="m 18.2995,19.66 -5.056,-5.057 5.056,-5.056 c -0.31,-0.516 -0.62,-1.032 -0.93,-1.34 l -5.672,5.673 v 1.652 l 5.675,5.676 c 0.31,-0.515 0.722,-0.928 0.93,-1.547 z"/></svg>
|
||||
|
After Width: | Height: | Size: 294 B |
1
gaia/shared/elements/gaia_buttons/images/right-light.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path fill="#667174" fill-opacity=".7" d="M11.8,19.9l4.9-4.9l-4.9-4.9c0.3-0.5,0.6-1,0.9-1.3l5.5,5.5c0,0.3,0,0.5,0,0.8c0,0.3,0,0.5,0,0.8l-5.5,5.5 C12.4,20.9,12,20.5,11.8,19.9z"/></svg>
|
||||
|
After Width: | Height: | Size: 243 B |
18
gaia/shared/elements/gaia_buttons/script.js
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
'use strict';
|
||||
/* global ComponentUtils */
|
||||
|
||||
window.GaiaButtons = (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.GaiaButtonsBaseurl ||
|
||||
'/shared/elements/gaia_buttons/';
|
||||
|
||||
proto.createdCallback = function() {
|
||||
ComponentUtils.style.call(this, baseurl);
|
||||
};
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('gaia-buttons', { prototype: proto });
|
||||
})(window);
|
||||
281
gaia/shared/elements/gaia_buttons/style.css
Executable file
|
|
@ -0,0 +1,281 @@
|
|||
/* ---------------------------------------
|
||||
* gaia-buttons:
|
||||
* Container for buttons e.g:
|
||||
* action menu, confirm, edit mode, FTU
|
||||
* --------------------------------------- */
|
||||
|
||||
gaia-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 7rem;
|
||||
box-sizing: border-box;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
gaia-buttons:not(.inline) {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
border-top: 0.1rem solid #e7e7e7;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
/* this 'end' padding must match the buttons 'start' margin */
|
||||
gaia-buttons:not(.inline):-moz-dir(ltr) { padding-right: 1.5rem; }
|
||||
gaia-buttons:not(.inline):-moz-dir(rtl) { padding-left: 1.5rem; }
|
||||
|
||||
gaia-buttons.inline a,
|
||||
gaia-buttons.inline button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
button {
|
||||
position: relative;
|
||||
font-family: sans-serif;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
margin: 0;
|
||||
padding: 0 0.2rem;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background: #d8d8d8;
|
||||
border: none;
|
||||
border-radius: 2rem;
|
||||
font-weight: normal;
|
||||
font-size: 1.6rem;
|
||||
line-height: 4rem;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
:-moz-dir(ltr) a,
|
||||
:-moz-dir(ltr) button {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
:-moz-dir(rtl) a,
|
||||
:-moz-dir(rtl) button {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
/* Recommend */
|
||||
a.recommend,
|
||||
button.recommend {
|
||||
background-color: #00caf2;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Danger */
|
||||
a.danger,
|
||||
button.danger {
|
||||
background-color: #e51e1e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Pressed */
|
||||
a:active,
|
||||
button:active {
|
||||
background-color: #b2f2ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a::-moz-selection,
|
||||
button::-moz-selection {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
a[aria-disabled="true"],
|
||||
button[disabled] {
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
color: #c7c7c7;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
a[aria-disabled="true"].recommend,
|
||||
button[disabled].recommend {
|
||||
background-color: #97cbd5;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
a[aria-disabled="true"].danger,
|
||||
button[disabled].danger {
|
||||
background-color: #eb7474;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
a[aria-disabled="true"]::-moz-selection,
|
||||
button[disabled]::-moz-selection {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
/* Hidden */
|
||||
[hidden] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
a.icon,
|
||||
button.icon {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
a.icon:before,
|
||||
button.icon:before {
|
||||
content: "";
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -1.5rem;
|
||||
background: transparent no-repeat center center / 100% auto;
|
||||
}
|
||||
:-moz-dir(ltr) a.icon:before,
|
||||
:-moz-dir(ltr) button.icon:before { right: 0.5rem; }
|
||||
:-moz-dir(rtl) a.icon:before,
|
||||
:-moz-dir(rtl) button.icon:before { left: 0.5rem; }
|
||||
|
||||
a.icon-next:before,
|
||||
button.icon-next:before {
|
||||
background-size: 3rem;
|
||||
}
|
||||
:-moz-dir(ltr) a.icon-next:before,
|
||||
:-moz-dir(ltr) button.icon-next:before {
|
||||
background-image: url(images/right-light.svg);
|
||||
}
|
||||
:-moz-dir(rtl) a.icon-next:before,
|
||||
:-moz-dir(rtl) button.icon-next:before {
|
||||
background-image: url(images/left-light.svg);
|
||||
}
|
||||
|
||||
a.icon-previous:before,
|
||||
button.icon-previous:before {
|
||||
background-size: 3rem;
|
||||
}
|
||||
:-moz-dir(ltr) a.icon-previous:before,
|
||||
:-moz-dir(ltr) button.icon-previous:before {
|
||||
left: 0.5rem;
|
||||
background-image: url(images/left-light.svg);
|
||||
}
|
||||
:-moz-dir(rtl) a.icon-previous:before,
|
||||
:-moz-dir(rtl) button.icon-previous:before {
|
||||
right: 0.5rem;
|
||||
background-image: url(images/right-light.svg);
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
SKIN: dark
|
||||
----------------------------------------------------------------- */
|
||||
|
||||
gaia-buttons[skin="dark"] {
|
||||
background: #4d4d4d;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Pressed */
|
||||
[skin="dark"] a:active,
|
||||
[skin="dark"] button:active {
|
||||
background-color: #00aacc;
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
[skin="dark"] a[aria-disabled="true"],
|
||||
[skin="dark"] button[disabled] {
|
||||
background-color: #565656;
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
[skin="dark"] a[aria-disabled="true"].recommend,
|
||||
[skin="dark"] button[disabled].recommend {
|
||||
background-color: #006579;
|
||||
}
|
||||
|
||||
[skin="dark"] a[aria-disabled="true"].danger,
|
||||
[skin="dark"] button[disabled].danger {
|
||||
background-color: #730f0f;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
Transitions
|
||||
----------------------------------------------------------------- */
|
||||
|
||||
[transition] {
|
||||
display: flex;
|
||||
transition: visibility var(--transition-duration);
|
||||
}
|
||||
|
||||
[transition][hidden] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[transition="slide"] {
|
||||
transform: translateY(0);
|
||||
transition: transform var(--transition-duration) ease,
|
||||
visibility var(--transition-duration);
|
||||
}
|
||||
|
||||
[transition="slide"][hidden] {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
[transition="fade"] {
|
||||
opacity: 1;
|
||||
transition: opacity var(--transition-duration) ease,
|
||||
visibility var(--transition-duration);
|
||||
}
|
||||
|
||||
[transition="fade"][hidden] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
RESPONSIVE: tablet
|
||||
----------------------------------------------------------------- */
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
gaia-buttons {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
gaia-buttons:not(.inline) {
|
||||
border-top-width: 0.2rem;
|
||||
}
|
||||
/* this 'end' padding must match the buttons 'start' margin */
|
||||
gaia-buttons:not(.inline):-moz-dir(ltr) { padding-right: 3rem; }
|
||||
gaia-buttons:not(.inline):-moz-dir(rtl) { padding-left: 3rem; }
|
||||
|
||||
a,
|
||||
button {
|
||||
font-size: 2rem;
|
||||
height: 5rem;
|
||||
line-height: 5rem;
|
||||
border-radius: 2.5rem;
|
||||
max-width: 40rem;
|
||||
}
|
||||
:-moz-dir(ltr) a,
|
||||
:-moz-dir(ltr) button {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
:-moz-dir(rtl) a,
|
||||
:-moz-dir(rtl) button {
|
||||
margin-right: 3rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
13
gaia/shared/elements/gaia_checkbox/examples/example.js
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
var result = document.getElementById('result');
|
||||
|
||||
var elements = document.querySelectorAll('gaia-checkbox');
|
||||
|
||||
[].forEach.call(elements, function(element, index) {
|
||||
element.addEventListener('click', function(e) {
|
||||
result.textContent = 'Click event for: ' + (index + 1) + ' value: ' +
|
||||
e.target.checked;
|
||||
});
|
||||
|
||||
});
|
||||
64
gaia/shared/elements/gaia_checkbox/examples/index.html
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>GaiaCheckbox Demo</title>
|
||||
<meta name="description" content="Demo app for gaia-checkbox" />
|
||||
<style type="text/css">
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 10px;
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
}
|
||||
section {
|
||||
margin: 2rem;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
var dirInput = document.getElementById('dir');
|
||||
dirInput.checked = false;
|
||||
dirInput.onchange= function() {
|
||||
document.documentElement.dir = this.checked ? 'rtl' : 'ltr';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src="../../../js/component_utils.js"></script>
|
||||
<script>window.GaiaCheckboxBaseurl = '../';</script>
|
||||
<script src="../script.js"></script>
|
||||
<script defer src="example.js"></script>
|
||||
</head>
|
||||
<body role="application">
|
||||
<p dir="ltr">
|
||||
<label class="bb-docs">
|
||||
<input id="dir" type="checkbox"> RTL mode
|
||||
</label>
|
||||
</p>
|
||||
<section>
|
||||
<gaia-checkbox></gaia-checkbox>
|
||||
</section>
|
||||
<section>
|
||||
<gaia-checkbox checked style="width: 100%;">
|
||||
<label>With a label</label>
|
||||
</gaia-checkbox>
|
||||
</section>
|
||||
<section>
|
||||
<gaia-checkbox class="danger"></gaia-checkbox>
|
||||
</section>
|
||||
<section>
|
||||
<gaia-checkbox class="danger" checked>
|
||||
<label>Class = danger</label>
|
||||
</gaia-checkbox>
|
||||
</section>
|
||||
<section>
|
||||
<gaia-checkbox class="inline" checked>
|
||||
<label>Class = inline</label>
|
||||
</gaia-checkbox>
|
||||
</section>
|
||||
<div id="result"></div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
gaia/shared/elements/gaia_checkbox/images/danger.png
Executable file
|
After Width: | Height: | Size: 583 B |
BIN
gaia/shared/elements/gaia_checkbox/images/danger@1.5x.png
Executable file
|
After Width: | Height: | Size: 946 B |
BIN
gaia/shared/elements/gaia_checkbox/images/danger@2.25x.png
Executable file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
gaia/shared/elements/gaia_checkbox/images/danger@2x.png
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
gaia/shared/elements/gaia_checkbox/images/default.png
Executable file
|
After Width: | Height: | Size: 583 B |
BIN
gaia/shared/elements/gaia_checkbox/images/default@1.5x.png
Executable file
|
After Width: | Height: | Size: 946 B |
BIN
gaia/shared/elements/gaia_checkbox/images/default@2.25x.png
Executable file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
gaia/shared/elements/gaia_checkbox/images/default@2x.png
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
129
gaia/shared/elements/gaia_checkbox/script.js
Executable file
|
|
@ -0,0 +1,129 @@
|
|||
'use strict';
|
||||
/* global ComponentUtils */
|
||||
|
||||
window.GaiaCheckbox = (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.GaiaCheckboxBaseurl ||
|
||||
'/shared/elements/gaia_checkbox/';
|
||||
|
||||
proto.createdCallback = function() {
|
||||
|
||||
// A timestamp of the last click on the checkbox.
|
||||
// We use this to throttle click events to the control.
|
||||
// This is a temporary workaround for bug 1221886.
|
||||
this.lastClick = 0;
|
||||
this.throttleTime = 250;
|
||||
|
||||
var shadow = this.createShadowRoot();
|
||||
this._template = template.content.cloneNode(true);
|
||||
|
||||
this._wrapper = this._template.getElementById('checkbox');
|
||||
this._wrapper.addEventListener('click', this.handleClick.bind(this));
|
||||
|
||||
this.configureClass();
|
||||
|
||||
this.checked = this.hasAttribute('checked');
|
||||
this._wrapper.setAttribute('aria-checked', this.checked);
|
||||
this.setAttribute('role', 'presentation');
|
||||
|
||||
shadow.appendChild(this._template);
|
||||
|
||||
ComponentUtils.style.call(this, baseurl);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handles a click event on the shadow dom.
|
||||
* We handle checking/unchecking of the checkbox 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.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
// Workaround for bug 1221886 - throttle clicks if needed.
|
||||
if (this.lastClick + this.throttleTime > Date.now()) {
|
||||
return;
|
||||
}
|
||||
this.lastClick = Date.now();
|
||||
|
||||
// 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 (!event.defaultPrevented) {
|
||||
this.checked = !this.checked;
|
||||
this._wrapper.setAttribute('aria-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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy className property to the wrapper.
|
||||
*/
|
||||
proto.attributeChangedCallback = function(name, from, to) {
|
||||
if (name === 'class') {
|
||||
this._wrapper.className = to;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy the checked property to the input element.
|
||||
*/
|
||||
Object.defineProperty(proto, 'checked', {
|
||||
get: function() {
|
||||
return this._checked || false;
|
||||
},
|
||||
set: function(value) {
|
||||
this._wrapper.classList.toggle('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);
|
||||
}
|
||||
});
|
||||
|
||||
var template = document.createElement('template');
|
||||
|
||||
template.innerHTML =
|
||||
`<span id="checkbox" role="checkbox">
|
||||
<span role="presentation"><content select="label"></content></span>
|
||||
</span>`;
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('gaia-checkbox', { prototype: proto });
|
||||
})(window);
|
||||
71
gaia/shared/elements/gaia_checkbox/style.css
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
gaia-checkbox {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#checkbox {
|
||||
display: block;
|
||||
width: 100%;
|
||||
/* Normal gaia-checkboxes are the heights of their parents. */
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#checkbox span {
|
||||
font-size: 1.9rem;
|
||||
font-weight: normal;
|
||||
color: var(--label-color);
|
||||
line-height: 3rem;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
min-width: 2.2rem;
|
||||
min-height: 2.2rem;
|
||||
width: 100%;
|
||||
padding-inline-end: 2.2rem;
|
||||
/* Override inheriting nowrap from legacy lists building blocks. */
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* Inline gaia-checkboxes are the heights of their parents. */
|
||||
#checkbox.inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#checkbox:not(.invisible) span:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
offset-inline-end: 0;
|
||||
top: 50%;
|
||||
margin: -1.2rem 0 0;
|
||||
margin-inline-start: -1.2rem;
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
}
|
||||
|
||||
#checkbox.inline span {
|
||||
padding-inline-start: 3rem;
|
||||
padding-inline-end: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#checkbox.inline span:after {
|
||||
offset-inline-start: 1.2rem;
|
||||
offset-inline-end: unset;
|
||||
margin: -1.2rem 0 0;
|
||||
margin-inline-start: -1.2rem;
|
||||
}
|
||||
|
||||
#checkbox:not(.invisible) span:after {
|
||||
background: url(images/default.png) no-repeat center top / 2.2rem auto;
|
||||
}
|
||||
|
||||
#checkbox.checked span:after {
|
||||
background-position: center bottom;
|
||||
}
|
||||
|
||||
/* 'Dangerous' checkboxes */
|
||||
#checkbox.danger span:after {
|
||||
background-image: url(images/danger.png);
|
||||
}
|
||||
54
gaia/shared/elements/gaia_confirm/examples/index.html
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html dir="ltr" >
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>GaiaConfirm Demo</title>
|
||||
<meta name="description" content="Demo app for gaia-confirm" />
|
||||
<style type="text/css">
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
||||
<script>window.GaiaConfirmBaseurl = '../';</script>
|
||||
<script>window.GaiaButtonsBaseurl = '../../gaia_buttons/';</script>
|
||||
<script src="../../../js/component_utils.js"></script>
|
||||
<script src="../../gaia_buttons/script.js"></script>
|
||||
<script src="../script.js"></script>
|
||||
<script>
|
||||
document.documentElement.addEventListener("keypress", function(e) {
|
||||
var dir = document.documentElement.dir;
|
||||
document.documentElement.dir = dir == "ltr" ? "rtl" : "ltr";
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body role="application">
|
||||
|
||||
<gaia-confirm>
|
||||
<h1>You are about to do a thing. Are you sure you want to do this thing?</h1>
|
||||
<p>Some additional description here.</p>
|
||||
<gaia-buttons skin="dark">
|
||||
<button class="cancel">Cancel</button>
|
||||
<button class="confirm recommend">Submit</button>
|
||||
</gaia-buttons>
|
||||
</gaia-confirm>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
/**
|
||||
In addition to showing a nice confirm dialog we have some additional events which
|
||||
are emitted on the dialog itself (but not bubbled!) they require the use of
|
||||
the special .cancel and .confirm class names on the buttons inside the confirm.
|
||||
*/
|
||||
var confirm = document.querySelector('gaia-confirm');
|
||||
confirm.addEventListener('confirm', function() {
|
||||
console.log('confirm was pressed');
|
||||
});
|
||||
|
||||
confirm.addEventListener('cancel', function() {
|
||||
console.log('cancel was pressed');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
72
gaia/shared/elements/gaia_confirm/script.js
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
'use strict';
|
||||
/* global ComponentUtils */
|
||||
|
||||
/**
|
||||
* The gaia-confirm component displays a dialog in which the user has a
|
||||
* choice to confirm or cancel the action. It may be displayed along with a
|
||||
* title, description, and image. Buttons may also be configured.
|
||||
* @requires GaiaButtons
|
||||
*/
|
||||
window.GaiaConfirm = (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.GaiaConfirmBaseurl ||
|
||||
'/shared/elements/gaia_confirm/';
|
||||
|
||||
/**
|
||||
preventDefault and prevent other listeners from hearing abut changes to a
|
||||
particular event object.
|
||||
|
||||
@param {Array[Element]|Null} unless a list of elements to allow events to hit.
|
||||
@param {Event} event triggered by addEventListener, etc...
|
||||
*/
|
||||
function eatEvent(unless, event) {
|
||||
// prevent handlers from everything else unless the target is on the list.
|
||||
if (unless && unless.indexOf(event.target) !== -1) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
proto.createdCallback = function() {
|
||||
var shadow = this.createShadowRoot();
|
||||
this._template = template.content.cloneNode(true);
|
||||
|
||||
shadow.appendChild(this._template);
|
||||
ComponentUtils.style.call(this, baseurl);
|
||||
|
||||
var confirm = this.querySelector('gaia-buttons .confirm');
|
||||
var cancel = this.querySelector('gaia-buttons .cancel');
|
||||
|
||||
if (confirm) {
|
||||
confirm.addEventListener('click', (e) => {
|
||||
eatEvent(null, e);
|
||||
this.dispatchEvent(new CustomEvent('confirm'));
|
||||
});
|
||||
}
|
||||
|
||||
if (cancel) {
|
||||
cancel.addEventListener('click', (e) => {
|
||||
eatEvent(null, e);
|
||||
this.dispatchEvent(new CustomEvent('cancel'));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = `<form role="dialog" class="confirm">
|
||||
<section>
|
||||
<content select="h1"></content>
|
||||
<content select="p"></content>
|
||||
</section>
|
||||
<content select="gaia-buttons">
|
||||
</content>
|
||||
</form>`;
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('gaia-confirm', { prototype: proto });
|
||||
|
||||
})(window);
|
||||
155
gaia/shared/elements/gaia_confirm/style.css
Executable file
|
|
@ -0,0 +1,155 @@
|
|||
/* ----------------------------------
|
||||
* Confirm
|
||||
* ---------------------------------- */
|
||||
|
||||
/* Main dialog setup */
|
||||
gaia-confirm {
|
||||
background: #2d2d2d;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding-bottom: 7rem;
|
||||
font-size: 0;
|
||||
text-align: start;
|
||||
/* Using font-size: 0; we avoid the unwanted visual space (about 3px)
|
||||
created by white-spaces and break lines in the code betewen inline-block elements */
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
transition: transform ease-in-out 0.3s, visibility 0.3s;
|
||||
/**
|
||||
magic number ideally to place confirm dialogs on top.
|
||||
*/
|
||||
z-index: 2147483646;
|
||||
}
|
||||
|
||||
gaia-confirm[hidden] {
|
||||
transform: translateY(100%);
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.confirm section {
|
||||
height: 100%;
|
||||
font-weight: 300;
|
||||
font-size: 2.2rem;
|
||||
color: #FAFAFA;
|
||||
padding: 0 1.5rem;
|
||||
box-sizing: padding-box;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
max-height: 100%;
|
||||
vertical-align: middle;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.5em;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
padding: 1rem 1.5rem 0;
|
||||
overflow: hidden;
|
||||
max-height: 7rem;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Specific component code */
|
||||
p {
|
||||
word-wrap: break-word;
|
||||
margin: 1rem 0 0;
|
||||
padding: 1rem 1.5rem;
|
||||
border-top: 0.1rem solid #686868;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
p img {
|
||||
margin-inline-end: 2rem;
|
||||
}
|
||||
p img:-moz-dir(ltr) { float: left; }
|
||||
p img:-moz-dir(rtl) { float: right; }
|
||||
|
||||
p strong {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
p small {
|
||||
font-size: 1.4rem;
|
||||
font-weight: normal;
|
||||
color: #cbcbcb;
|
||||
display: block;
|
||||
}
|
||||
|
||||
dl {
|
||||
border-top: 0.1rem solid #686868;
|
||||
margin: 1rem 0 0;
|
||||
overflow: hidden;
|
||||
padding-top: 1rem;
|
||||
font-size: 1.6rem;
|
||||
line-height: 2.2rem;
|
||||
}
|
||||
|
||||
dl > dt {
|
||||
clear: both;
|
||||
width: 7rem;
|
||||
padding-inline-start: 1.5rem;
|
||||
font-weight: 500;
|
||||
text-align: start;
|
||||
}
|
||||
dl > dt:-moz-dir(ltr) { float: left; }
|
||||
dl > dt:-moz-dir(rtl) { float: right; }
|
||||
|
||||
dl > dd {
|
||||
padding-inline-end: 1.5rem;
|
||||
font-weight: 300;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
figure img {
|
||||
padding-inline-end: 1.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
figure figcaption {
|
||||
font-size: 2.1rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 2rem 1.5rem;
|
||||
border-top: 0.1rem solid #686868;
|
||||
list-style: none;
|
||||
font-size: 1.6rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul li {
|
||||
word-wrap: break-word;
|
||||
line-height: 2rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
ul li:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
ul li ul {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
118
gaia/shared/elements/gaia_drawer/examples/index.html
Executable file
|
|
@ -0,0 +1,118 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>GaiaDrawer Demo</title>
|
||||
<meta name="description" content="Demo app for gaia-drawer" />
|
||||
<style type="text/css">
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-size: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 16px sans-serif;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
float: left;
|
||||
height: 400px;
|
||||
border: solid 1px #eee;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.half {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.third {
|
||||
width: 33.33%;
|
||||
}
|
||||
|
||||
gaia-drawer {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Demo Dependencies -->
|
||||
<link rel="stylesheet" type="text/css" href="../../../style/lists.css">
|
||||
<script>window.GaiaHeaderBaseurl = '../../gaia_header/';</script>
|
||||
<script src="../../gaia_header/script.js"></script>
|
||||
|
||||
<!-- Component -->
|
||||
<script src="../../../js/component_utils.js"></script>
|
||||
<script>window.GaiaDrawerBaseurl = '../';</script>
|
||||
<script src="../script.js"></script>
|
||||
</head>
|
||||
<body role="application">
|
||||
<div class="slot third">
|
||||
<gaia-header action="menu"><h1>Title</h1></gaia-header>
|
||||
<gaia-drawer open>
|
||||
<section data-type="list">
|
||||
<ul>
|
||||
<li><p>List Item 1</p></li>
|
||||
<li><p>List Item 2</p></li>
|
||||
<li><p>List Item 3</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
</gaia-drawer>
|
||||
</div>
|
||||
<div class="slot third">
|
||||
<gaia-header action="menu"><h1>Title</h1></gaia-header>
|
||||
<gaia-drawer>
|
||||
<section data-type="list">
|
||||
<ul>
|
||||
<li><p>List Item 1</p></li>
|
||||
<li><p>List Item 2</p></li>
|
||||
<li><p>List Item 3</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
</gaia-drawer>
|
||||
</div>
|
||||
<div class="slot third">
|
||||
<gaia-header action="menu"><h1>Title</h1></gaia-header>
|
||||
<gaia-drawer>
|
||||
<section data-type="list">
|
||||
<ul>
|
||||
<li><p>List Item 1</p></li>
|
||||
<li><p>List Item 2</p></li>
|
||||
<li><p>List Item 3</p></li>
|
||||
<li><p>List Item 1</p></li>
|
||||
<li><p>List Item 2</p></li>
|
||||
<li><p>List Item 3</p></li>
|
||||
<li><p>List Item 1</p></li>
|
||||
<li><p>List Item 2</p></li>
|
||||
<li><p>List Item 3</p></li>
|
||||
</ul>
|
||||
</section>
|
||||
</gaia-drawer>
|
||||
</div>
|
||||
<div class="half"></div>
|
||||
<div class="half"></div>
|
||||
<script>
|
||||
|
||||
[].forEach.call(document.querySelectorAll('.slot'), function(slot) {
|
||||
var header = slot.querySelector('gaia-header');
|
||||
var drawer = slot.querySelector('gaia-drawer');
|
||||
header.addEventListener('action', function() {
|
||||
drawer.toggle();
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('WebComponentsReady', function() {
|
||||
console.log('WebComponentsReady');
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
138
gaia/shared/elements/gaia_drawer/script.js
Executable file
|
|
@ -0,0 +1,138 @@
|
|||
window.GaiaDrawer = (function(win) {
|
||||
/*global ComponentUtils*/
|
||||
/*jshint maxlen:false*/
|
||||
'use strict';
|
||||
|
||||
// Extend from the HTMLElement prototype
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
// Allow baseurl to be overridden (used for demo page)
|
||||
var baseurl = window.GaiaDrawerBaseurl ||
|
||||
'/shared/elements/gaia_drawer/';
|
||||
|
||||
/**
|
||||
* Runs when an instance of the
|
||||
* element is first created.
|
||||
*
|
||||
* When use this moment to create the
|
||||
* shadow-dom, inject our template
|
||||
* content, setup event listeners
|
||||
* and set the draw state to match
|
||||
* the initial `open` attribute.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
proto.createdCallback = function() {
|
||||
var shadow = this.createShadowRoot();
|
||||
this._template = template.content.cloneNode(true);
|
||||
|
||||
// Fetch some els
|
||||
this._els = {};
|
||||
this._els.background = this._template.getElementById('background');
|
||||
this._els.content = this._template.getElementById('content');
|
||||
this._els.inner = this._template.getElementById('inner');
|
||||
|
||||
this._attachEvents();
|
||||
this.toggle(this.hasAttribute('open'));
|
||||
|
||||
// Put content in the shadow-dom
|
||||
shadow.appendChild(this._template);
|
||||
ComponentUtils.style.call(this, baseurl);
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs when any attribute changes
|
||||
* on the element.
|
||||
*
|
||||
* We only support the `open` attribute,
|
||||
* so we ignore all others. We then toggle,
|
||||
* opening the drawer for all values other
|
||||
* than `null` (which means the attribute
|
||||
* was removed).
|
||||
*
|
||||
* @param {String} attr
|
||||
* @param {String|null} oldVal
|
||||
* @param {String|null} newVal
|
||||
* @private
|
||||
*/
|
||||
proto.attributeChangedCallback = function(attr, oldVal, newVal) {
|
||||
if (attr !== 'open') { return; }
|
||||
this.toggle(newVal !== null);
|
||||
};
|
||||
|
||||
proto._attachEvents = function() {
|
||||
this._els.background.addEventListener('click', this.close.bind(this));
|
||||
this._els.content.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the drawer open/closed.
|
||||
*
|
||||
* If a value is passed, we ignore the
|
||||
* current `open` value and just derive
|
||||
* the state from the value given (similar
|
||||
* to how `classList.toggle` works).
|
||||
*
|
||||
* @param {Boolean} value
|
||||
* @public
|
||||
*/
|
||||
proto.toggle = function(value) {
|
||||
value = arguments.length ? value : !this.hasAttribute('open');
|
||||
if (value) { this.open(); }
|
||||
else { this.close(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the drawer.
|
||||
*
|
||||
* We have to also duplicate the
|
||||
* attribute on the `.inner` element
|
||||
* inside the shadow-root as we are
|
||||
* currently missing the `:host`
|
||||
* selector from the platform.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
proto.open = function() {
|
||||
this.setAttribute('open', '');
|
||||
this._els.inner.setAttribute('open', '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the drawer.
|
||||
*
|
||||
* We have to also duplicate the
|
||||
* attribute on the `.inner` element
|
||||
* inside the shadow-root as we are
|
||||
* currently missing the `:host`
|
||||
* selector from the platform.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
proto.close = function() {
|
||||
this.removeAttribute('open');
|
||||
this._els.inner.removeAttribute('open');
|
||||
};
|
||||
|
||||
// HACK: Create a <template> in memory at runtime.
|
||||
// When the custom-element is created we clone
|
||||
// this template and inject into the shadow-root.
|
||||
// Prior to this we would have had to copy/paste
|
||||
// the template into the <head> of every app that
|
||||
// wanted to use <gaia-switch>, this would make
|
||||
// markup changes complicated, and could lead to
|
||||
// things getting out of sync. This is a short-term
|
||||
// hack until we can import entire custom-elements
|
||||
// using HTML Imports (bug 877072).
|
||||
// var stylesheet = baseurl + 'style.css';
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = `<div class="inner" id="inner">
|
||||
<div class="background" id="background"></div>
|
||||
<div class="content" id="content"><content></content></div>
|
||||
</div>`;
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('gaia-drawer', { prototype: proto });
|
||||
})(window);
|
||||
69
gaia/shared/elements/gaia_drawer/style.css
Executable file
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
/** Gaia Drawer
|
||||
---------------------------------------------------------*/
|
||||
|
||||
gaia-drawer {
|
||||
display: block;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
/** Content
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
min-height: calc(100% - 200px);
|
||||
max-height: calc(100% - 95px);
|
||||
overflow: auto;
|
||||
color: #5F5F5F;
|
||||
background-color: #F4F4F4;
|
||||
transform: translateY(-100%);
|
||||
transition: transform 300ms ease, visibility 300ms;
|
||||
will-change: transform;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* open
|
||||
*/
|
||||
|
||||
[open] .content {
|
||||
transform: translateY(0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.background {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.6);
|
||||
transition: opacity 240ms ease;
|
||||
will-change: opacity;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* open
|
||||
*/
|
||||
|
||||
[open] .background {
|
||||
opacity: 1;
|
||||
}
|
||||
65
gaia/shared/elements/gaia_grid/examples/example.js
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
'use strict';
|
||||
/* global GaiaGrid */
|
||||
|
||||
var grid = document.getElementById('grid');
|
||||
|
||||
var icon = 'mozilla.png';
|
||||
var whitePixel = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///' +
|
||||
'wAAACwAAAAAAQABAAACAkQBADs=';
|
||||
|
||||
var items = [
|
||||
new GaiaGrid.Bookmark({
|
||||
id: 1,
|
||||
name: 'Mozilla',
|
||||
icon: icon,
|
||||
url: 'http://mozilla.org'
|
||||
}),
|
||||
new GaiaGrid.Bookmark({
|
||||
id: 2,
|
||||
name: 'Two',
|
||||
icon: icon,
|
||||
url: 'http://2'
|
||||
}),
|
||||
new GaiaGrid.Bookmark({
|
||||
id: 3,
|
||||
name: 'Three',
|
||||
icon: icon,
|
||||
url: 'http://3'
|
||||
}),
|
||||
new GaiaGrid.Bookmark({
|
||||
id: 4,
|
||||
name: 'Four',
|
||||
icon: icon,
|
||||
url: 'http://4'
|
||||
}),
|
||||
new GaiaGrid.Mozapp({
|
||||
id: 5,
|
||||
icon: icon,
|
||||
manifest: {
|
||||
name: 'A Webapp',
|
||||
icons: {
|
||||
64: whitePixel
|
||||
}
|
||||
}
|
||||
}),
|
||||
new GaiaGrid.Divider(),
|
||||
new GaiaGrid.Mozapp({
|
||||
id: 6,
|
||||
manifestURL: 'app://xfoobar',
|
||||
manifest: {
|
||||
name: 'Another Webapp',
|
||||
icons: {
|
||||
64: whitePixel
|
||||
}
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
items.forEach(grid.add.bind(grid));
|
||||
|
||||
grid.render();
|
||||
|
||||
document.getElementById('clear').addEventListener('click', function() {
|
||||
grid.clear();
|
||||
});
|
||||
|
||||
51
gaia/shared/elements/gaia_grid/examples/index.html
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>GaiaGrid Demo</title>
|
||||
<meta name="description" content="Demo app for gaia-grid" />
|
||||
<style type="text/css">
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 10px;
|
||||
background-color: #48036f;
|
||||
font-family: Verdana, sans-serif;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
gaia-grid {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<script>window.GaiaConfirmBaseurl = '../';</script>
|
||||
<script>window.GaiaButtonsBaseurl = '../../gaia_buttons/';</script>
|
||||
<script src="../../../js/component_utils.js"></script>
|
||||
<script src="../../gaia_buttons/script.js"></script>
|
||||
<script src="../../gaia_confirm/script.js"></script>
|
||||
<script>window.GaiaGridBaseurl = '../';</script>
|
||||
<script src="../../../js/url_helper.js"></script>
|
||||
<script src="../../../js/l10n.js"></script>
|
||||
<script src="../script.js"></script>
|
||||
<script src="../js/grid_icon_renderer.js"></script>
|
||||
<script src="../js/dialog.js"></script>
|
||||
<script src="../js/grid_view.js"></script>
|
||||
<script src="../js/grid_icon_renderer.js"></script>
|
||||
<script src="../js/items/grid_item.js"></script>
|
||||
<script src="../js/items/placeholder.js"></script>
|
||||
<script src="../js/items/bookmark.js"></script>
|
||||
<script src="../js/items/divider.js"></script>
|
||||
<script src="../js/items/mozapp.js"></script>
|
||||
<script src="../js/grid_layout.js"></script>
|
||||
<script src="../js/grid_dragdrop.js"></script>
|
||||
<script src="../js/grid_zoom.js"></script>
|
||||
</head>
|
||||
<body role="application">
|
||||
<button id="clear">Clear Grid</button>
|
||||
<gaia-grid id="grid" dragdrop></gaia-grid>
|
||||
<script defer src="example.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
gaia/shared/elements/gaia_grid/examples/mozilla.png
Executable file
|
After Width: | Height: | Size: 77 KiB |
BIN
gaia/shared/elements/gaia_grid/images/add.png
Executable file
|
After Width: | Height: | Size: 312 B |
BIN
gaia/shared/elements/gaia_grid/images/add@1.5x.png
Executable file
|
After Width: | Height: | Size: 400 B |
BIN
gaia/shared/elements/gaia_grid/images/add@2.25x.png
Executable file
|
After Width: | Height: | Size: 573 B |
BIN
gaia/shared/elements/gaia_grid/images/add@2x.png
Executable file
|
After Width: | Height: | Size: 558 B |
BIN
gaia/shared/elements/gaia_grid/images/add@3.375x.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
gaia/shared/elements/gaia_grid/images/app_install_canceled.png
Executable file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
gaia/shared/elements/gaia_grid/images/app_install_canceled@1.5x.png
Executable file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
gaia/shared/elements/gaia_grid/images/app_install_canceled@2.25x.png
Executable file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
gaia/shared/elements/gaia_grid/images/app_install_canceled@2x.png
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
gaia/shared/elements/gaia_grid/images/app_install_canceled@3.375x.png
Executable file
|
After Width: | Height: | Size: 5.4 KiB |