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.
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "dom-scheduler",
|
||||
"version": "1.0.0",
|
||||
"homepage": "https://github.com/etiennesegonzac/dom-scheduler",
|
||||
"authors": [
|
||||
"Etienne Segonzac <etienne@segonzac.info>"
|
||||
],
|
||||
"description": "Making the performant thing the easy thing.",
|
||||
"main": "lib/dom-scheduler.js",
|
||||
"keywords": [
|
||||
"web",
|
||||
"performance",
|
||||
"transition",
|
||||
"animation",
|
||||
"promise"
|
||||
],
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"demo-app/bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
227
assets/webapps/music.gaiamobile.org/components/dom-scheduler/bower_components/base/base.css
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
|
||||
/** Variables
|
||||
---------------------------------------------------------*/
|
||||
|
||||
:root {
|
||||
--dim-micro: 6px;
|
||||
--dim-small: 12px;
|
||||
--dim-medium: 24px;
|
||||
--dim-mega: 48px;
|
||||
|
||||
--base-xs: 6px;
|
||||
--base-s: 18px;
|
||||
--base-m: 24px;
|
||||
--base-l: 30px;
|
||||
--base-xl: 42px;
|
||||
--base-xxl: 60px;
|
||||
|
||||
--base-font-xs: 10px;
|
||||
--base-font-s: 14px;
|
||||
--base-font-m: 17px;
|
||||
--base-font-l: 23px;
|
||||
--base-font-xl: 60px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Apply a natural box layout
|
||||
* model to all elements
|
||||
*/
|
||||
|
||||
*,
|
||||
*:after,
|
||||
*:before {
|
||||
box-sizing: border-box; /* 1 */
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/** Document
|
||||
---------------------------------------------------------*/
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** Body
|
||||
---------------------------------------------------------*/
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
font: 300 17px/21px 'FiraSans';
|
||||
background-color: var(--background);
|
||||
color: var(--text-color);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/** Tags
|
||||
---------------------------------------------------------*/
|
||||
|
||||
a {
|
||||
color: var(--highlight-color, inherit);
|
||||
text-decoration: none;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
margin: var(--dim-small) 0 var(--dim-small) 0;
|
||||
border: 0;
|
||||
background:
|
||||
var(--border-color,
|
||||
var(--background-plus,
|
||||
rgba(0,0,0,0.2)));
|
||||
}
|
||||
|
||||
em {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.alpha {
|
||||
font-size: 23px;
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
line-height: 26px;
|
||||
margin: 6px 0 24px 0;
|
||||
}
|
||||
|
||||
.beta {
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
margin: 32px 0 16px 0;
|
||||
}
|
||||
|
||||
.gamma {
|
||||
font-size: 14px;
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
margin: 32px 0 16px 0;
|
||||
}
|
||||
|
||||
.milli {
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.large {
|
||||
font-size: 23px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.mega {
|
||||
font-size: 60px;
|
||||
line-height: 60px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.giga {}
|
||||
.kilo {}
|
||||
|
||||
/* alignment */
|
||||
.l-align-right { float: right; }
|
||||
.l-align-left { float: left; }
|
||||
.l-align-center { text-align: center; }
|
||||
.l-text-align-left { text-align: left; }
|
||||
.l-text-align-right { text-align: right; }
|
||||
.l-text-valign-top {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.l-width-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/** Flexbox
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.l-flex { display: flex; }
|
||||
.l-flex-col { display: flex; flex-direction: column; }
|
||||
|
||||
.l-flex-col-v-align {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.l-flex-v-align { align-items: center; }
|
||||
.l-flex-h-align { justify-content: center; }
|
||||
.l-flex-grow { flex: 1; }
|
||||
|
||||
/** Margin
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/* Top */
|
||||
|
||||
.l-mt-small { margin-top: var(--dim-small); }
|
||||
.l-mt-med { margin-top: var(--dim-medium); }
|
||||
|
||||
/* Bottom */
|
||||
|
||||
.l-mb-small { margin-bottom: var(--dim-small); }
|
||||
.l-mb-med { margin-bottom: var(--dim-medium); }
|
||||
.l-mb-large {}
|
||||
.l-mb-mega { margin-bottom: var(--dim-mega); }
|
||||
|
||||
/* Left */
|
||||
|
||||
.l-ml-small { margin-left: var(--dim-small); }
|
||||
|
||||
/* Right */
|
||||
|
||||
.l-mr-small { margin-right: var(--dim-small); }
|
||||
|
||||
/* Auto */
|
||||
|
||||
.l-mlr-auto { margin-left: auto; margin-right: auto; }
|
||||
.l-m-auto { margin: auto }
|
||||
|
||||
/* Margin */
|
||||
|
||||
.l-m-small { margin: var(--dim-small) }
|
||||
.l-m-med { margin: var(--dim-medium) }
|
||||
.l-m-0 { margin: 0 }
|
||||
|
||||
/** Padding
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.l-p-small { padding: var(--dim-small); }
|
||||
.l-pl-small { padding-left: var(--dim-small); }
|
||||
.l-pr-small { padding-right: var(--dim-small); }
|
||||
.l-plr-small {
|
||||
padding-right: var(--dim-small);
|
||||
padding-left: var(--dim-small);
|
||||
}
|
||||
.l-pb-small { padding-bottom: var(--dim-small); }
|
||||
.l-pt-small { padding-top: var(--dim-small); }
|
||||
.l-pt-mega { padding-top: var(--dim-mega); }
|
||||
.l-pt-micro { padding-top: var(--dim-micro); }
|
||||
.l-pb-micro { padding-bottom: var(--dim-micro); }
|
||||
.l-p-0 { padding: 0 ;}
|
||||
|
||||
/** Helpers
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.l-clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.l-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.is-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.is-invisible {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
16
assets/webapps/music.gaiamobile.org/components/dom-scheduler/bower_components/base/bower.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "base",
|
||||
"version": "0.0.0",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "base.css",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "fast-list",
|
||||
"version": "0.1.2",
|
||||
"homepage": "https://github.com/gaia-components/fast-list",
|
||||
"authors": [
|
||||
"Etienne Segonzac <etienne@segonzac.info>",
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"description": "No, really.",
|
||||
"main": "fast-list.js",
|
||||
"keywords": [
|
||||
"web",
|
||||
"performance",
|
||||
"virtual",
|
||||
"list"
|
||||
],
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "fast-list/examples/sections",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://github.com/gaia-components/fast-list",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "index.html",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"dom-scheduler": "etiennesegonzac/dom-scheduler"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
font-family: sans-serif;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#h1-after {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
left: 0;
|
||||
top: 46px;
|
||||
|
||||
pointer-events: none;
|
||||
background-color: #ff9500;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
animation-duration: 0.25s;
|
||||
animation-timing-function: ease-in;
|
||||
animation-fill-mode: both;
|
||||
|
||||
-webkit-animation-duration: 0.25s;
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
-webkit-animation-fill-mode: both;
|
||||
}
|
||||
|
||||
[data-anim='reveal'] {
|
||||
animation-name: reveal;
|
||||
-webkit-animation-name: reveal;
|
||||
}
|
||||
|
||||
@keyframes reveal {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes reveal {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-anim='hide'] {
|
||||
animation-name: hide;
|
||||
-webkit-animation-name: hide;
|
||||
}
|
||||
|
||||
@keyframes hide {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes hide {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
outline: 0;
|
||||
background: none;
|
||||
padding: 0 15px;
|
||||
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
-moz-appearance: none;
|
||||
|
||||
opacity: 1;
|
||||
transition: transform 0.1s linear,
|
||||
opacity 0.1s linear;
|
||||
-webkit-transition: -webkit-transform 0.1s linear,
|
||||
opacity 0.1s linear;
|
||||
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
button.transitioning {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.6, 0.6);
|
||||
-webkit-transform: scale(0.6, 0.6);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body > section {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
h1 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #27c8c2;
|
||||
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-size: 24px;
|
||||
color: #ffffff;
|
||||
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
ul {
|
||||
list-style: none;
|
||||
padding: 0 0 0 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul section {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul h2 {
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: 0px;
|
||||
height: 32px;
|
||||
|
||||
margin: 0;
|
||||
padding: 0 6px;
|
||||
box-sizing: border-box;
|
||||
|
||||
font-size: 0.9em;
|
||||
line-height: 32px;
|
||||
|
||||
background: #efefef;
|
||||
border-bottom: 1px #bcbcbc solid;
|
||||
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
ul .background {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
background: linear-gradient(to bottom, transparent 0px, transparent 45px, #EDEDED 45px, #EDEDED 70px, transparent 20px),
|
||||
linear-gradient(to bottom, transparent 0px, transparent 15px, #EDEDED 15px, #EDEDED 25px, transparent 20px),
|
||||
linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white);
|
||||
background-position: 20% 88px, 5% -88px, 0 -88px;
|
||||
background-size: 90% 88px, 60% 88px, 100% 88px;
|
||||
background-repeat: repeat-y;
|
||||
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
ul.reordering .background {
|
||||
background: linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white);
|
||||
background-position: 0 -88px;
|
||||
background-size: 100% 88px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
width: 100%;
|
||||
height: 88px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
-moz-user-select: none;
|
||||
|
||||
background: white;
|
||||
border-bottom: 1px #bcbcbc solid;
|
||||
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
ul li h3 {
|
||||
margin: 0;
|
||||
padding: 9px 6px 3px 6px;
|
||||
background: white;
|
||||
|
||||
color: #424242;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ul li p {
|
||||
margin: 0;
|
||||
padding: 3px 6px 10px 6px;
|
||||
background: white;
|
||||
|
||||
color: #797979;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
li.new {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
li .overlay {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 2px);
|
||||
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
|
||||
opacity: 0;
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: ease-in;
|
||||
animation-fill-mode: both;
|
||||
|
||||
-webkit-animation-duration: 0.2s;
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
-webkit-animation-fill-mode: both;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
li.edit .overlay {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.overlay .cursor {
|
||||
margin: 17px auto;
|
||||
padding: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-size: 25px;
|
||||
color: #0b0b0b;
|
||||
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
li.dragging {
|
||||
transition: transform 0s linear;
|
||||
-webkit-transition: -webkit-transform 0s linear;
|
||||
}
|
||||
|
||||
li.dragging .overlay {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
li[data-populated=false] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="css/h1.css">
|
||||
<link rel="stylesheet" href="css/ul.css">
|
||||
<link rel="stylesheet" href="css/app.css">
|
||||
|
||||
<script src="bower_components/dom-scheduler/lib/dom-scheduler.js" defer></script>
|
||||
<script src="fast-list.js" async></script>
|
||||
|
||||
<script src="js/bacon-source.js" async></script>
|
||||
<script src="js/page.js" async></script>
|
||||
|
||||
<title>Dom Scheduler Demo</title>
|
||||
</head>
|
||||
|
||||
<body class="theme-communications">
|
||||
<h1>
|
||||
Main List
|
||||
</h1>
|
||||
<div id="h1-after"></div>
|
||||
<button>
|
||||
Edit
|
||||
</button>
|
||||
<section>
|
||||
<ul>
|
||||
<section
|
||||
><h2> </h2
|
||||
><div class="background"
|
||||
></div
|
||||
></section>
|
||||
<li
|
||||
><h3> </h3
|
||||
><p> </p
|
||||
><div class="overlay"
|
||||
><div class="cursor">↕︎</div></div
|
||||
></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
(function() {
|
||||
var debug = false;
|
||||
var headerHeight = 50;
|
||||
var maxItemCount = 28;
|
||||
|
||||
var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup';
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var listContainer = document.querySelector('section');
|
||||
|
||||
var source = new BaconSource();
|
||||
var list = new FastList(listContainer, source, scheduler);
|
||||
var dialog = document.querySelector('gaia-dialog-alert');
|
||||
|
||||
function updateHeader() {
|
||||
return scheduler.mutation(function() {
|
||||
var h1 = document.querySelector('h1');
|
||||
h1.textContent = 'Main List (' + source.fullLength() + ')';
|
||||
h1.scrollTop; // flush
|
||||
});
|
||||
}
|
||||
updateHeader();
|
||||
|
||||
function clearNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'hide';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
}
|
||||
listContainer.addEventListener('top-reached', clearNewIndicator);
|
||||
|
||||
function updateNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'reveal';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
listContainer.addEventListener('hidden-new-content', updateNewIndicator);
|
||||
|
||||
function openGaiaDialog(evt) {
|
||||
scheduler.mutation(function() {
|
||||
var detail = evt.detail;
|
||||
var li = source.getRecordAt(detail.index);
|
||||
dialog.textContent = li.title + ' item clicked!';
|
||||
dialog.open(detail.clickEvt);
|
||||
});
|
||||
}
|
||||
list.list.addEventListener('item-selected', openGaiaDialog);
|
||||
|
||||
function newContentHandler() {
|
||||
var newContent = {
|
||||
title: 'NEW Bacon ' + Date.now().toString().slice(7, -1),
|
||||
body: 'Turkey BLT please.'
|
||||
};
|
||||
|
||||
source.insertAtIndex(0, newContent);
|
||||
list.insertedAtIndex(0);
|
||||
|
||||
updateHeader();
|
||||
}
|
||||
|
||||
setInterval(newContentHandler, 15000);
|
||||
window.addEventListener('new-content', newContentHandler);
|
||||
|
||||
window.pushNewContent = function() {
|
||||
window.dispatchEvent(new CustomEvent('new-content'));
|
||||
};
|
||||
|
||||
var button = document.querySelector('button');
|
||||
button.addEventListener(endEvent, function() {
|
||||
Promise.all([toggleTransitioning(), list.toggleEditMode()])
|
||||
.then(updateText)
|
||||
.then(toggleTransitioning);
|
||||
});
|
||||
|
||||
function updateText(text) {
|
||||
return scheduler.mutation(function() {
|
||||
button.textContent = list.editing ? 'Exit' : 'Edit';
|
||||
});
|
||||
}
|
||||
|
||||
function toggleTransitioning() {
|
||||
return scheduler.feedback(function() {
|
||||
button.classList.toggle('transitioning');
|
||||
}, button, 'transitionend');
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
(function(exports) {
|
||||
var listSize = 1042;
|
||||
var sectionCount = 25;
|
||||
|
||||
var headerHeight = 32;
|
||||
var itemHeight = 88;
|
||||
|
||||
function makeContent(prefix, name) {
|
||||
return {
|
||||
title: prefix + ' Bacon ipsum dolor ' +
|
||||
Date.now().toString().slice(7, -1),
|
||||
body: 'Turkey pig boudin ' + name.toLowerCase() +
|
||||
' de mignon drums and all ' + prefix + '.'
|
||||
}
|
||||
}
|
||||
|
||||
exports.BaconSource = function BaconSource() {
|
||||
this.sections = [];
|
||||
this.contentMap = new Map();
|
||||
this._cachedLength = null;
|
||||
|
||||
for (var i = 0; i < sectionCount; i++) {
|
||||
this.sections.push('Section ' + i);
|
||||
}
|
||||
|
||||
this.sections.forEach(function(name) {
|
||||
var content = [];
|
||||
for (var i = 0; i < (Math.random() * listSize); i++) {
|
||||
content.push(makeContent(i, name));
|
||||
}
|
||||
this.contentMap.set(name, content);
|
||||
}, this);
|
||||
};
|
||||
|
||||
exports.BaconSource.prototype = {
|
||||
populateItem: function(item, i) {
|
||||
// Simulates the data source not being ready to populate
|
||||
/*if (parseInt(Date.now() / 10) % 8 === 0) {*/
|
||||
/*return new Promise(function(resolve, reject) {*/
|
||||
/*setTimeout(resolve, Math.random() * 1000);*/
|
||||
/*});*/
|
||||
/*}*/
|
||||
|
||||
var title = item.firstChild;
|
||||
var body = title.nextSibling;
|
||||
var record = this.getRecordAt(i);
|
||||
|
||||
title.firstChild.data = record.title;
|
||||
body.firstChild.data = record.body;
|
||||
},
|
||||
|
||||
getSections: function() {
|
||||
return this.contentMap.keys();
|
||||
},
|
||||
|
||||
sectionHeaderHeight: function() {
|
||||
return headerHeight;
|
||||
},
|
||||
|
||||
fullSectionHeight: function(key) {
|
||||
return this.contentMap.get(key).length * itemHeight;
|
||||
},
|
||||
|
||||
fullSectionLength: function(key) {
|
||||
return this.contentMap(key).length;
|
||||
},
|
||||
|
||||
getRecordAt: function(index) {
|
||||
for (var section of this.contentMap.values()) {
|
||||
if (index < section.length) {
|
||||
return section[index];
|
||||
}
|
||||
index -= section.length;
|
||||
}
|
||||
},
|
||||
|
||||
getSectionFor: function(index) {
|
||||
for (var entry of this.contentMap.entries()) {
|
||||
if (index < entry[1].length) {
|
||||
return entry[0];
|
||||
}
|
||||
index -= entry[1].length;
|
||||
}
|
||||
},
|
||||
|
||||
indexAtPosition: function(pos) {
|
||||
var index = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
pos -= headerHeight;
|
||||
var sectionHeight = section.length * itemHeight;
|
||||
if (pos > sectionHeight) {
|
||||
pos -= sectionHeight;
|
||||
index += section.length;
|
||||
continue;
|
||||
}
|
||||
for (var item of section) {
|
||||
pos -= itemHeight;
|
||||
index++;
|
||||
if (pos <= 0 || index === this.fullLength() - 1) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
positionForIndex: function(index) {
|
||||
var top = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
top += headerHeight;
|
||||
if (index < section.length) {
|
||||
top += index * itemHeight;
|
||||
return top;
|
||||
}
|
||||
index -= section.length;
|
||||
top += section.length * itemHeight;
|
||||
}
|
||||
},
|
||||
|
||||
fullLength: function() {
|
||||
if (this._cachedLength) {
|
||||
return this._cachedLength;
|
||||
}
|
||||
|
||||
var length = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
length += section.length;
|
||||
}
|
||||
this._cachedLength = length;
|
||||
return length;
|
||||
},
|
||||
|
||||
itemHeight: function() {
|
||||
return itemHeight;
|
||||
},
|
||||
|
||||
fullHeight: function() {
|
||||
var height = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
height += headerHeight + section.length * itemHeight;
|
||||
}
|
||||
return height;
|
||||
},
|
||||
|
||||
insertAtIndex: function(index, record, toSection) {
|
||||
this._cachedLength = null;
|
||||
for (var entry of this.contentMap.entries()) {
|
||||
if (index < entry[1].length || entry[0] === toSection) {
|
||||
return entry[1].splice(index, 0, record);
|
||||
}
|
||||
index -= entry[1].length;
|
||||
}
|
||||
},
|
||||
|
||||
replaceAtIndex: function(index, record) {
|
||||
for (var section of this.contentMap.values()) {
|
||||
if (index < section.length) {
|
||||
return section.splice(index, 1, record);
|
||||
}
|
||||
index -= section.length;
|
||||
}
|
||||
},
|
||||
|
||||
removeAtIndex: function(index) {
|
||||
this._cachedLength = null;
|
||||
|
||||
for (var section of this.contentMap.values()) {
|
||||
if (index < section.length) {
|
||||
return section.splice(index, 1)[0];
|
||||
}
|
||||
index -= section.length;
|
||||
}
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
(function() {
|
||||
/*global BaconSource, FastList*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup';
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var listContainer = document.querySelector('section');
|
||||
|
||||
var source = new BaconSource();
|
||||
var list = new FastList(listContainer, source);
|
||||
var scheduler = FastList.scheduler;
|
||||
|
||||
function updateHeader() {
|
||||
return scheduler.mutation(function() {
|
||||
var h1 = document.querySelector('h1');
|
||||
h1.textContent = 'Main List (' + source.fullLength() + ')';
|
||||
h1.scrollTop; // flush
|
||||
});
|
||||
}
|
||||
updateHeader();
|
||||
|
||||
function openAlert(evt) {
|
||||
scheduler.mutation(function() {
|
||||
var detail = evt.detail;
|
||||
var li = source.getRecordAt(detail.index);
|
||||
alert(li.title + ' item clicked!');
|
||||
});
|
||||
}
|
||||
list.list.addEventListener('item-selected', openAlert);
|
||||
|
||||
function clearNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'hide';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
}
|
||||
listContainer.addEventListener('top-reached', clearNewIndicator);
|
||||
|
||||
function updateNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'reveal';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
listContainer.addEventListener('hidden-new-content', updateNewIndicator);
|
||||
|
||||
function newContentHandler() {
|
||||
var newContent = {
|
||||
title: 'NEW Bacon ' + Date.now().toString().slice(7, -1),
|
||||
body: 'Turkey BLT please.'
|
||||
};
|
||||
|
||||
source.insertAtIndex(0, newContent);
|
||||
list.insertedAtIndex(0);
|
||||
|
||||
updateHeader();
|
||||
}
|
||||
|
||||
setInterval(newContentHandler, 15000);
|
||||
window.addEventListener('new-content', newContentHandler);
|
||||
|
||||
window.pushNewContent = function() {
|
||||
window.dispatchEvent(new CustomEvent('new-content'));
|
||||
};
|
||||
|
||||
var button = document.querySelector('button');
|
||||
button.addEventListener(endEvent, function() {
|
||||
Promise.all([toggleTransitioning(), list.toggleEditMode()])
|
||||
.then(updateText)
|
||||
.then(toggleTransitioning);
|
||||
});
|
||||
|
||||
function updateText(text) {
|
||||
return scheduler.mutation(function() {
|
||||
button.textContent = list.editing ? 'Exit' : 'Edit';
|
||||
});
|
||||
}
|
||||
|
||||
function toggleTransitioning() {
|
||||
return scheduler.feedback(function() {
|
||||
button.classList.toggle('transitioning');
|
||||
}, button, 'transitionend');
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "fast-list-demo",
|
||||
"description": "Faaaaasssst",
|
||||
"launch_path": "/index.html",
|
||||
"icons": {
|
||||
"16": "/icons/icon16x16.png",
|
||||
"48": "/icons/icon48x48.png",
|
||||
"60": "/icons/icon60x60.png",
|
||||
"128": "/icons/icon128x128.png"
|
||||
},
|
||||
"type": "certified",
|
||||
"orientation": "default",
|
||||
"permissions": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
/* mini reset */
|
||||
* { margin: 0; padding: 0; }
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
height: 50px;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
#content {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0 0 0 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul section {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul h2 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.background {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
|
||||
width: 100%;
|
||||
|
||||
background: linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white);
|
||||
background-position: 0 -88px;
|
||||
background-size: 100% 88px;
|
||||
background-repeat: repeat-y;
|
||||
}
|
||||
|
||||
|
||||
ul li {
|
||||
width: 100%;
|
||||
height: 88px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
-moz-user-select: none;
|
||||
|
||||
background: white;
|
||||
border-bottom: 1px #bcbcbc solid;
|
||||
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
ul li h3 {
|
||||
margin: 0;
|
||||
padding: 9px 6px 3px 6px;
|
||||
background: white;
|
||||
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
ul li p {
|
||||
margin: 0;
|
||||
padding: 3px 6px 10px 6px;
|
||||
background: white;
|
||||
|
||||
color: #797979;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*global DataSource, FastList*/
|
||||
|
||||
var content = document.getElementById('content');
|
||||
var data = createDummyData(1000);
|
||||
var dataSource = new DataSource(data);
|
||||
var list = new FastList(content, dataSource);
|
||||
|
||||
function createDummyData(count) {
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
result.push({
|
||||
name: 'Song ' + i,
|
||||
metadata: {
|
||||
title: 'Song title ' + i,
|
||||
artist: 'Song artist ' + i
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
(function(exports) {
|
||||
var itemHeight = 88;
|
||||
|
||||
var debug = 0 ? (...args) => console.log('[DataSource]', ...args) : ()=>{};
|
||||
|
||||
exports.DataSource = function DataSource(items) {
|
||||
this.items = items;
|
||||
debug('initialized', items);
|
||||
};
|
||||
|
||||
exports.DataSource.prototype = {
|
||||
populateItem: function(item, i) {
|
||||
debug('populate item', item);
|
||||
var title = item.firstChild;
|
||||
var body = title.nextSibling;
|
||||
var record = this.getRecordAt(i);
|
||||
|
||||
title.firstChild.data = record.name;
|
||||
body.firstChild.data = record.metadata.artist;
|
||||
},
|
||||
|
||||
getSections() {
|
||||
debug('get sections');
|
||||
return [''];
|
||||
},
|
||||
|
||||
sectionHeaderHeight() {
|
||||
return 0;
|
||||
},
|
||||
|
||||
fullSectionHeight() {
|
||||
var result = this.items.length * itemHeight;
|
||||
debug('full section height', result);
|
||||
return result;
|
||||
},
|
||||
|
||||
fullSectionLength() {
|
||||
var result = this.items.length;
|
||||
debug('full section length', result);
|
||||
return result;
|
||||
},
|
||||
|
||||
getSectionFor() {
|
||||
return '';
|
||||
},
|
||||
|
||||
getRecordAt: function(index) {
|
||||
return this.items[index];
|
||||
},
|
||||
|
||||
indexAtPosition: function(pos) {
|
||||
return Math.floor(pos / itemHeight);
|
||||
},
|
||||
|
||||
positionForIndex: function(index) {
|
||||
return index * itemHeight;
|
||||
},
|
||||
|
||||
fullLength: function() {
|
||||
return this.items.length;
|
||||
},
|
||||
|
||||
itemHeight: function() {
|
||||
return itemHeight;
|
||||
},
|
||||
|
||||
fullHeight: function() {
|
||||
this.items.length * itemHeight;
|
||||
},
|
||||
|
||||
insertAtIndex: function(index, record, toSection) {
|
||||
return this.items.splice(index, 0, record);
|
||||
},
|
||||
|
||||
replaceAtIndex: function(index, record) {
|
||||
return this.items.splice(index, 1)[0];
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
<link rel="stylesheet" href="app.css">
|
||||
<title>fast-list: simple</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>Header</header>
|
||||
<section id="content" style="height:100%;">
|
||||
<ul>
|
||||
<section
|
||||
><h2> </h2
|
||||
><div class="background"
|
||||
></div
|
||||
></section>
|
||||
<li
|
||||
><h3> </h3
|
||||
><p> </p
|
||||
></li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer>Footer</footer>
|
||||
<!-- <script src="bower_components/dom-scheduler/lib/dom-scheduler.js"></script> -->
|
||||
<script src="fast-list.js"></script>
|
||||
<script src="data-source.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "simple-list-demo",
|
||||
"description": "Sweet",
|
||||
"launch_path": "/index.html",
|
||||
"icons": {
|
||||
"16": "/icons/icon16x16.png",
|
||||
"48": "/icons/icon48x48.png",
|
||||
"60": "/icons/icon60x60.png",
|
||||
"128": "/icons/icon128x128.png"
|
||||
},
|
||||
"orientation": "default",
|
||||
"permissions": {}
|
||||
}
|
||||
935
assets/webapps/music.gaiamobile.org/components/dom-scheduler/bower_components/fast-list/fast-list.js
vendored
Normal file
|
|
@ -0,0 +1,935 @@
|
|||
!(function(define){'use strict';define(function(require,exports,module){
|
||||
|
||||
var debug = 0 ? console.log.bind(console, '[FastList]') : function() {};
|
||||
|
||||
var startEvent = ('ontouchstart' in window) ? 'touchstart' : 'mousedown';
|
||||
var moveEvent = ('ontouchstart' in window) ? 'touchmove' : 'mousemove';
|
||||
var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup';
|
||||
|
||||
/**
|
||||
* 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 : schedulerShim());
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
exports = module.exports = FastList;
|
||||
exports.scheduler = schedule;
|
||||
|
||||
function FastList(container, source, scheduler) {
|
||||
this.editing = false;
|
||||
|
||||
container.style.overflowX = 'hidden';
|
||||
container.style.overflowY = 'scroll';
|
||||
this.container = container;
|
||||
this.list = container.querySelector('ul');
|
||||
this.source = source;
|
||||
|
||||
this.geometry = {
|
||||
topPosition: 0,
|
||||
forward: true,
|
||||
|
||||
viewportHeight: 0,
|
||||
itemHeight: 0,
|
||||
headerHeight: 0,
|
||||
maxItemCount: 0,
|
||||
switchWindow: 0
|
||||
};
|
||||
|
||||
this._templateSection = this.list.querySelector('section');
|
||||
this.geometry.headerHeight = source.sectionHeaderHeight();
|
||||
this._templateSection.remove();
|
||||
|
||||
var template = this.list.querySelector('li');
|
||||
template.style.position = 'absolute';
|
||||
template.style.left = template.style.top = 0;
|
||||
template.style.overflow = 'hidden';
|
||||
template.style.willChange = 'transform';
|
||||
|
||||
this.geometry.itemHeight = template.offsetHeight;
|
||||
template.remove();
|
||||
|
||||
this._template = template;
|
||||
|
||||
this.updateContainerGeometry();
|
||||
|
||||
if (debug.name) {
|
||||
if (this.geometry.itemHeight !== source.itemHeight()) {
|
||||
debug('Template height and source height are not the same.');
|
||||
}
|
||||
}
|
||||
|
||||
this._rendered = false;
|
||||
this._previousTop = -1;
|
||||
this._previousFast = false;
|
||||
this._items = [];
|
||||
this._itemsInDOM = [];
|
||||
|
||||
this.reorderingContext = {
|
||||
item: null,
|
||||
initialY: null,
|
||||
identifier: null,
|
||||
moveUp: null,
|
||||
moveDown: null,
|
||||
};
|
||||
|
||||
this.updateListHeight();
|
||||
|
||||
schedule.attachDirect(
|
||||
this.container,
|
||||
'scroll',
|
||||
this.handleScroll.bind(this)
|
||||
);
|
||||
|
||||
on(this.container, 'click', this);
|
||||
on(window, 'resize', this);
|
||||
|
||||
schedule.mutation(function() {
|
||||
this.updateSections();
|
||||
this.render();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
FastList.prototype = {
|
||||
|
||||
/* Rendering
|
||||
========= */
|
||||
updateContainerGeometry: function() {
|
||||
var geo = this.geometry;
|
||||
|
||||
geo.viewportHeight = this.container.offsetHeight;
|
||||
var itemPerScreen = geo.viewportHeight / geo.itemHeight;
|
||||
// Taking into account the will-change budget multiplier from
|
||||
// layout/base/nsDisplayList.cpp#1193
|
||||
geo.maxItemCount = Math.floor(itemPerScreen * 2.4);
|
||||
geo.switchWindow = Math.floor(itemPerScreen / 2);
|
||||
|
||||
debug('maxItemCount: ' + geo.maxItemCount);
|
||||
},
|
||||
|
||||
// Returns true if we're fast scrolling
|
||||
updateViewportGeometry: function() {
|
||||
var geo = this.geometry;
|
||||
|
||||
var position = this.container.scrollTop;
|
||||
|
||||
// Don't compute velocity on first load
|
||||
if (this._previousTop === -1) {
|
||||
geo.topPosition = position;
|
||||
}
|
||||
|
||||
this._previousTop = geo.topPosition;
|
||||
geo.topPosition = position;
|
||||
|
||||
var distanceSinceLast = geo.topPosition - this._previousTop;
|
||||
|
||||
if ((distanceSinceLast > 0) && !geo.forward) {
|
||||
geo.forward = true;
|
||||
}
|
||||
|
||||
if ((distanceSinceLast < 0) && geo.forward) {
|
||||
geo.forward = false;
|
||||
}
|
||||
|
||||
if (geo.topPosition === 0 && this._previousTop !== 0) {
|
||||
this.container.dispatchEvent(new CustomEvent('top-reached'));
|
||||
}
|
||||
|
||||
var moved = Math.abs(distanceSinceLast);
|
||||
var fastScrolling = this._previousFast;
|
||||
if (!fastScrolling && moved > geo.viewportHeight * 2) {
|
||||
fastScrolling = true;
|
||||
}
|
||||
if (fastScrolling && moved > 0 && moved < geo.viewportHeight * 0.5) {
|
||||
fastScrolling = false;
|
||||
}
|
||||
this._previousFast = fastScrolling;
|
||||
|
||||
var onTop = position === 0;
|
||||
var atBottom = position === this.source.fullHeight() - geo.viewportHeight;
|
||||
|
||||
return fastScrolling && !onTop && !atBottom;
|
||||
},
|
||||
|
||||
render: function(reload, changedIndex) {
|
||||
var source = this.source;
|
||||
var items = this._items;
|
||||
var itemsInDOM = this._itemsInDOM;
|
||||
var geo = this.geometry;
|
||||
var template = this._template;
|
||||
var list = this.list;
|
||||
|
||||
var indices = computeIndices(this.source, this.geometry);
|
||||
var criticalStart = indices.cStart;
|
||||
var criticalEnd = indices.cEnd;
|
||||
var startIndex = indices.start;
|
||||
var endIndex = indices.end;
|
||||
|
||||
// Initial render generating all dom nodes
|
||||
if (!this._rendered) {
|
||||
this._rendered = true;
|
||||
endIndex = Math.min(source.fullLength() - 1,
|
||||
startIndex + this.geometry.maxItemCount - 1);
|
||||
}
|
||||
|
||||
var recyclableItems = recycle(items, criticalStart, criticalEnd,
|
||||
geo.forward ? endIndex : startIndex);
|
||||
|
||||
var findItemFor = function(index) {
|
||||
var item;
|
||||
|
||||
if (recyclableItems.length > 0) {
|
||||
var recycleIndex = recyclableItems.pop();
|
||||
item = items[recycleIndex];
|
||||
delete items[recycleIndex];
|
||||
} else if (itemsInDOM.length < geo.maxItemCount){
|
||||
item = template.cloneNode(true);
|
||||
list.appendChild(item);
|
||||
itemsInDOM.push(item);
|
||||
} else {
|
||||
console.warn('missing a cell');
|
||||
return;
|
||||
}
|
||||
|
||||
items[index] = item;
|
||||
return item;
|
||||
};
|
||||
|
||||
var renderItem = function(i) {
|
||||
var item = items[i];
|
||||
if (!item) {
|
||||
item = findItemFor(i);
|
||||
tryToPopulate(item, i, source, true);
|
||||
item.classList.toggle('new', i === changedIndex);
|
||||
} else if (reload) {
|
||||
source.populateItem(item, i);
|
||||
}
|
||||
|
||||
var section = source.getSectionFor(i);
|
||||
placeItem(item, i, section, geo, source, reload);
|
||||
};
|
||||
|
||||
if (geo.forward) {
|
||||
for (var i = startIndex; i <= endIndex; ++i) {
|
||||
renderItem(i);
|
||||
}
|
||||
} else {
|
||||
for (var j = endIndex; j >= startIndex; --j) {
|
||||
renderItem(j);
|
||||
}
|
||||
}
|
||||
|
||||
debugViewport(items, geo.forward, criticalStart, criticalEnd,
|
||||
startIndex, endIndex);
|
||||
},
|
||||
|
||||
/* Scrolling
|
||||
========= */
|
||||
handleScroll: function(evt) {
|
||||
var fast = this.updateViewportGeometry();
|
||||
if (fast) {
|
||||
debug('[x] ---------- faaaaassssstttt');
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
|
||||
updateListHeight: function() {
|
||||
return schedule.mutation((function() {
|
||||
this.list.style.height = this.source.fullHeight() + 'px';
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
get scrollTop() {
|
||||
return this.geometry.topPosition;
|
||||
},
|
||||
|
||||
scrollInstantly: function(by) {
|
||||
this.container.scrollTop += by;
|
||||
this.updateViewportGeometry();
|
||||
this.render();
|
||||
},
|
||||
|
||||
reloadData: function() {
|
||||
return schedule.mutation((function() {
|
||||
this.updateSections();
|
||||
this.render(true);
|
||||
this.updateListHeight();
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
updateSections: function() {
|
||||
var list = this.list;
|
||||
var source = this.source;
|
||||
var template = this._templateSection;
|
||||
|
||||
return schedule.mutation(function() {
|
||||
var nodes = list.querySelectorAll('section');
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var toRemove = nodes[i];
|
||||
toRemove.remove();
|
||||
}
|
||||
|
||||
var headerHeight = source.sectionHeaderHeight();
|
||||
for (var section of source.getSections()) {
|
||||
var height = source.fullSectionHeight(section);
|
||||
|
||||
var sectionNode = template.cloneNode(true);
|
||||
sectionNode.style.height = headerHeight + height + 'px';
|
||||
|
||||
var title = sectionNode.firstChild;
|
||||
title.firstChild.data = section;
|
||||
|
||||
var background = title.nextSibling;
|
||||
background.style.height = height + 'px';
|
||||
|
||||
list.appendChild(sectionNode);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/* Edit mode
|
||||
========= */
|
||||
toggleEditMode: function() {
|
||||
this.editing = !this.editing;
|
||||
|
||||
if (this.editing) {
|
||||
this._startTouchListeners();
|
||||
} else {
|
||||
this._stopTouchListeners();
|
||||
}
|
||||
|
||||
return toggleEditClass(this.list,
|
||||
this._itemsInDOM, this.editing);
|
||||
},
|
||||
|
||||
_startTouchListeners: function() {
|
||||
on(this.list, startEvent, this);
|
||||
on(this.list, endEvent, this);
|
||||
},
|
||||
|
||||
_stopTouchListeners: function() {
|
||||
off(this.list, startEvent, this);
|
||||
off(this.list, endEvent, this);
|
||||
},
|
||||
|
||||
/* Reordering
|
||||
---------- */
|
||||
_reorderStart: function(evt) {
|
||||
debug('reorder start');
|
||||
var ctx = this.reorderingContext;
|
||||
|
||||
// Already tracking an item, bailing out
|
||||
if (ctx.item) {
|
||||
return;
|
||||
}
|
||||
|
||||
var li = evt.target.parentNode.parentNode;
|
||||
var touch = evt.touches && evt.touches[0] || evt;
|
||||
|
||||
ctx.item = li;
|
||||
ctx.initialY = touch.pageY;
|
||||
ctx.identifier = touch.identifier;
|
||||
ctx.moveUp = new Set();
|
||||
ctx.moveDown = new Set();
|
||||
ctx.moveHandler = this._reorderMove.bind(this);
|
||||
|
||||
var listenToMove = (function() {
|
||||
schedule.attachDirect(this.list, moveEvent, ctx.moveHandler);
|
||||
}).bind(this);
|
||||
|
||||
setupForDragging(this.scheduler, li, true)
|
||||
.then(listenToMove)
|
||||
.then(toggleDraggingClass.bind(null, this.scheduler, li, true));
|
||||
},
|
||||
|
||||
_reorderMove: function(evt) {
|
||||
debug('reorder move');
|
||||
var ctx = this.reorderingContext;
|
||||
if (!ctx.item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Multi touch
|
||||
if (evt.touches && evt.touches.length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var li = ctx.item;
|
||||
var touch = evt.touches && evt.touches[0] || evt;
|
||||
var position = touch.pageY;
|
||||
|
||||
var delta = position - ctx.initialY;
|
||||
|
||||
this._rearrange(delta);
|
||||
tweakTransform(li, delta);
|
||||
|
||||
// TODO: scroll when close to the beginning/end of the viewport
|
||||
},
|
||||
|
||||
_reorderEnd: function(evt) {
|
||||
debug('reorder end');
|
||||
|
||||
var ctx = this.reorderingContext;
|
||||
if (!ctx.item) {
|
||||
return;
|
||||
}
|
||||
|
||||
var li = ctx.item;
|
||||
schedule.detachDirect(this.list, moveEvent, ctx.moveHandler);
|
||||
|
||||
var touch = evt.touches && evt.touches[0] || evt;
|
||||
if (touch.identifier == ctx.identifier) {
|
||||
var position = touch.pageY;
|
||||
var delta = position - ctx.initialY;
|
||||
computeChanges(ctx, this.geometry, this._itemsInDOM, delta);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
applyChanges(ctx, this.geometry, this._itemsInDOM),
|
||||
moveInPlace(ctx, this.geometry)
|
||||
]).then(this._reorderFinish.bind(this, li))
|
||||
.then(toggleDraggingClass.bind(null, schedule, li, false));
|
||||
},
|
||||
|
||||
_reorderFinish: function(li) {
|
||||
var scheduler = this.scheduler;
|
||||
return Promise.all([this._commitToDocument(),
|
||||
setupForDragging(scheduler, li, false)]);
|
||||
},
|
||||
|
||||
_rearrange: function(delta) {
|
||||
var ctx = this.reorderingContext;
|
||||
var upCount = ctx.moveUp.size;
|
||||
var downCount = ctx.moveDown.size;
|
||||
|
||||
computeChanges(this.reorderingContext, this.geometry,
|
||||
this._itemsInDOM, delta);
|
||||
|
||||
if (ctx.moveUp.size === upCount &&
|
||||
ctx.moveDown.size === downCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyChanges(this.reorderingContext,
|
||||
this.geometry, this._itemsInDOM);
|
||||
},
|
||||
|
||||
_commitToDocument: function() {
|
||||
var ctx = this.reorderingContext;
|
||||
var li = ctx.item;
|
||||
var itemsInDOM = this._itemsInDOM;
|
||||
var items = this._items;
|
||||
var source = this.source;
|
||||
|
||||
return schedule.mutation((function() {
|
||||
// Nothing to do
|
||||
if (!ctx.moveUp && !ctx.moveDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
var index = items.indexOf(li);
|
||||
var section = li.dataset.section;
|
||||
var newIndex = index + ctx.moveUp.size - ctx.moveDown.size;
|
||||
|
||||
items.splice(index, 1);
|
||||
items.splice(newIndex, 0, li);
|
||||
var c = source.removeAtIndex(index);
|
||||
source.insertAtIndex(newIndex, c, section);
|
||||
|
||||
itemsInDOM.forEach(function(item) {
|
||||
resetTransition(item);
|
||||
resetTransform(item);
|
||||
});
|
||||
|
||||
this.updateSections();
|
||||
this.render();
|
||||
|
||||
ctx.item = null;
|
||||
ctx.initialY = null;
|
||||
ctx.identifier = null;
|
||||
ctx.moveUp = null;
|
||||
ctx.moveDown = null;
|
||||
ctx.moveHandler = null;
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
/* External content changes
|
||||
======================== */
|
||||
insertedAtIndex: function(index) {
|
||||
if (index !== 0) {
|
||||
//TODO: support any point of insertion
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.geometry.topPosition > this.geometry.itemHeight ||
|
||||
this.editing) {
|
||||
// No transition needed, just keep the scroll position
|
||||
this._insertOnTop(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var domItems = this._itemsInDOM;
|
||||
var list = this.list;
|
||||
|
||||
list.classList.add('reordering');
|
||||
pushDown(this.scheduler, domItems, this.geometry)
|
||||
.then(this._insertOnTop.bind(this, false))
|
||||
.then(cleanInlineStyles.bind(null, domItems))
|
||||
.then(reveal.bind(null, this.list))
|
||||
.then(function() {
|
||||
list.classList.remove('reordering');
|
||||
});
|
||||
},
|
||||
|
||||
_insertOnTop: function(keepScrollPosition) {
|
||||
return schedule.mutation((function() {
|
||||
this._items.unshift(null);
|
||||
delete this._items[0]; // keeping it sparse
|
||||
|
||||
this.updateSections();
|
||||
|
||||
if (keepScrollPosition) {
|
||||
this.scrollInstantly(this.geometry.itemHeight);
|
||||
this.container.dispatchEvent(new CustomEvent('hidden-new-content'));
|
||||
} else {
|
||||
this.render(false, 0);
|
||||
}
|
||||
|
||||
this.updateListHeight();
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
handleEvent: function(evt) {
|
||||
switch (evt.type) {
|
||||
case 'resize':
|
||||
this.geometry.viewportHeight = this.container.offsetHeight;
|
||||
this.updateContainerGeometry();
|
||||
break;
|
||||
case startEvent:
|
||||
if (!evt.target.classList.contains('cursor')) {
|
||||
return;
|
||||
}
|
||||
evt.preventDefault();
|
||||
this._reorderStart(evt);
|
||||
break;
|
||||
case endEvent:
|
||||
if (!evt.target.classList.contains('cursor') &&
|
||||
endEvent === 'touchend') {
|
||||
return;
|
||||
}
|
||||
evt.preventDefault();
|
||||
this._reorderEnd(evt);
|
||||
break;
|
||||
case 'click':
|
||||
if (this.editing) {
|
||||
break;
|
||||
}
|
||||
|
||||
var li = evt.target;
|
||||
var index = this._items.indexOf(li);
|
||||
|
||||
this.list.dispatchEvent(new CustomEvent('item-selected', {detail: {
|
||||
index: index,
|
||||
clickEvt: evt
|
||||
}}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Internals
|
||||
========= */
|
||||
|
||||
/* ASCII Art viewport debugging
|
||||
---------------------------- */
|
||||
function debugViewport(items, forward, cStart, cEnd, start, end) {
|
||||
if (!debug.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
var str = '[' + (forward ? 'v' : '^') + ']';
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (i == start) {
|
||||
str += '|';
|
||||
}
|
||||
if (i == cStart) {
|
||||
str += '[';
|
||||
}
|
||||
if (items[i]) {
|
||||
str += 'x';
|
||||
} else {
|
||||
str += '-';
|
||||
}
|
||||
if (i == cEnd) {
|
||||
str += ']';
|
||||
}
|
||||
if (i == end) {
|
||||
str += '|';
|
||||
}
|
||||
}
|
||||
|
||||
debug(str);
|
||||
}
|
||||
|
||||
function computeIndices(source, geometry) {
|
||||
var criticalStart = source.indexAtPosition(geometry.topPosition);
|
||||
var criticalEnd = source.indexAtPosition(geometry.topPosition +
|
||||
geometry.viewportHeight);
|
||||
|
||||
var canPrerender = geometry.maxItemCount -
|
||||
(criticalEnd - criticalStart) - 1;
|
||||
var before = geometry.switchWindow;
|
||||
var after = canPrerender - before;
|
||||
var lastIndex = source.fullLength() - 1;
|
||||
|
||||
var startIndex;
|
||||
var endIndex;
|
||||
|
||||
if (geometry.forward) {
|
||||
startIndex = Math.max(0, criticalStart - before);
|
||||
endIndex = Math.min(lastIndex,
|
||||
criticalEnd + after);
|
||||
} else {
|
||||
startIndex = Math.max(0, criticalStart - after);
|
||||
endIndex = Math.min(lastIndex,
|
||||
criticalEnd + before);
|
||||
}
|
||||
|
||||
return {
|
||||
cStart: criticalStart,
|
||||
cEnd: criticalEnd,
|
||||
start: startIndex,
|
||||
end: endIndex
|
||||
};
|
||||
}
|
||||
|
||||
function recycle(items, start, end, action) {
|
||||
var recyclableItems = [];
|
||||
for (var i in items) {
|
||||
if ((i < start) || (i > end)) {
|
||||
recyclableItems.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the items that are furthest away from the displayport edge
|
||||
// at the end of the array.
|
||||
recyclableItems.sort(function(a, b) {
|
||||
return Math.abs(a - action) - Math.abs(b - action);
|
||||
});
|
||||
|
||||
return recyclableItems;
|
||||
}
|
||||
|
||||
function tryToPopulate(item, index, source, first) {
|
||||
if (parseInt(item.dataset.index) !== index && !first) {
|
||||
// The item was probably reused
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: should be in a mutation block when !first
|
||||
var populateResult = source.populateItem(item, index);
|
||||
|
||||
if (populateResult instanceof Promise) {
|
||||
item.dataset.populated = false;
|
||||
populateResult.then(tryToPopulate.bind(null, item, index,
|
||||
source));
|
||||
return;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
item.dataset.populated = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Revealing the populated item
|
||||
item.style.transition = 'opacity 0.2s linear';
|
||||
schedule.transition(function() {
|
||||
item.dataset.populated = true;
|
||||
}, item, 'transitionend').then(function() {
|
||||
item.style.transition = '';
|
||||
});
|
||||
}
|
||||
|
||||
function placeItem(item, index, section, geometry, source, reload) {
|
||||
if (parseInt(item.dataset.index) === index && !reload) {
|
||||
// The item was probably reused
|
||||
return;
|
||||
}
|
||||
|
||||
item.dataset.position = source.positionForIndex(index);
|
||||
item.dataset.index = index;
|
||||
item.dataset.section = section;
|
||||
|
||||
var tweakedBy = parseInt(item.dataset.tweakDelta);
|
||||
if (tweakedBy) {
|
||||
tweakTransform(item, tweakedBy);
|
||||
} else {
|
||||
resetTransform(item);
|
||||
}
|
||||
}
|
||||
|
||||
function resetTransform(item) {
|
||||
var position = parseInt(item.dataset.position);
|
||||
item.style.webkitTransform =
|
||||
item.style.transform = 'translate3d(0, ' + position + 'px, 0)';
|
||||
item.dataset.tweakDelta = '';
|
||||
}
|
||||
|
||||
function tweakTransform(item, delta) {
|
||||
var position = parseInt(item.dataset.position) + delta;
|
||||
item.style.webkitTransform =
|
||||
item.style.transform = 'translate3d(0, ' + position + 'px, 0)';
|
||||
item.dataset.tweakDelta = delta;
|
||||
}
|
||||
|
||||
function resetTransition(item) {
|
||||
item.style.transition = '';
|
||||
item.style.webkitTransition = '';
|
||||
}
|
||||
|
||||
function tweakAndTransition(item, tweak) {
|
||||
return schedule.feedback(function() {
|
||||
item.style.transition = 'transform 0.15s ease';
|
||||
item.style.webkitTransition = '-webkit-transform 0.15s ease';
|
||||
if (tweak === 0) {
|
||||
setTimeout(resetTransform.bind(null, item));
|
||||
return;
|
||||
}
|
||||
setTimeout(tweakTransform.bind(null, item, tweak));
|
||||
}, item, 'transitionend')
|
||||
.then(resetTransition.bind(null, item));
|
||||
}
|
||||
|
||||
function pushDown(domItems, geometry) {
|
||||
if (!domItems.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return schedule.transition(function() {
|
||||
for (var i = 0; i < domItems.length; i++) {
|
||||
var item = domItems[i];
|
||||
item.style.transition = 'transform 0.15s ease-in';
|
||||
item.style.webkitTransition = '-webkit-transform 0.15s ease-in';
|
||||
tweakTransform(item, geometry.itemHeight);
|
||||
}
|
||||
}, domItems[0], 'transitionend');
|
||||
}
|
||||
|
||||
function reveal(list) {
|
||||
var newEl = list.querySelector('li.new');
|
||||
|
||||
return schedule.transition(function() {
|
||||
newEl.style.transition = 'opacity 0.25s ease-out';
|
||||
newEl.style.webkitTransition = 'opacity 0.25s ease-out';
|
||||
setTimeout(function() {
|
||||
newEl.classList.remove('new');
|
||||
});
|
||||
}, newEl, 'transitionend').then(function() {
|
||||
newEl.style.transition = '';
|
||||
newEl.style.webkitTransition = '';
|
||||
});
|
||||
}
|
||||
|
||||
function cleanInlineStyles(domItems) {
|
||||
return schedule.mutation(function() {
|
||||
for (var i = 0; i < domItems.length; i++) {
|
||||
var item = domItems[i];
|
||||
item.style.transition = '';
|
||||
item.style.webkitTransition = '';
|
||||
resetTransform(item);
|
||||
}
|
||||
domItems[0] && domItems[0].scrollTop; // flushing
|
||||
});
|
||||
}
|
||||
|
||||
function toggleEditClass(list, domItems, editing) {
|
||||
if (!domItems.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return schedule.feedback(function() {
|
||||
for (var i = 0; i < domItems.length; i++) {
|
||||
var item = domItems[i];
|
||||
var overlay = item.querySelector('.overlay');
|
||||
overlay.dataset.anim = editing ? 'reveal' : 'hide';
|
||||
item.classList.toggle('edit', editing);
|
||||
}
|
||||
}, list, 'animationend');
|
||||
}
|
||||
|
||||
function setupForDragging(scheduler, item, on) {
|
||||
return schedule.mutation(function() {
|
||||
item.parentNode.classList.toggle('reordering', on);
|
||||
item.style.zIndex = on ? '1000' : '';
|
||||
item.style.boxShadow = on ? '0 0 3px 1px #bcbcbc' : '';
|
||||
});
|
||||
}
|
||||
|
||||
function toggleDraggingClass(scheduler, item, on) {
|
||||
return schedule.feedback(function() {
|
||||
var overlay = item.querySelector('.overlay');
|
||||
overlay.dataset.anim = on ? 'hide' : 'reveal';
|
||||
}, item, 'animationend');
|
||||
}
|
||||
|
||||
function computeChanges(context, geometry, domItems, delta) {
|
||||
if (!context.item) {
|
||||
return;
|
||||
}
|
||||
|
||||
var draggedItem = context.item;
|
||||
var draggedOriginal = parseInt(draggedItem.dataset.position);
|
||||
var draggedTweaked = draggedOriginal + delta;
|
||||
|
||||
for (var i = 0; i < domItems.length; i++) {
|
||||
var item = domItems[i];
|
||||
if (item === draggedItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: support re-ordering accross sections
|
||||
if (item.dataset.section !== draggedItem.dataset.section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var itemOriginal = parseInt(item.dataset.position);
|
||||
var itemTweaked = itemOriginal + parseInt(item.dataset.tweakDelta);
|
||||
|
||||
if ((itemOriginal < draggedOriginal) && (draggedTweaked < itemOriginal)) {
|
||||
context.moveDown.add(item);
|
||||
} else {
|
||||
if (draggedTweaked > itemTweaked) {
|
||||
context.moveDown.delete(item);
|
||||
}
|
||||
}
|
||||
|
||||
if ((itemOriginal > draggedOriginal) && (draggedTweaked > itemOriginal)) {
|
||||
context.moveUp.add(item);
|
||||
} else {
|
||||
if (draggedTweaked < itemTweaked) {
|
||||
context.moveUp.delete(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyChanges(context, geometry, domItems) {
|
||||
if (!context.item) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var itemHeight = geometry.itemHeight;
|
||||
var draggedItem = context.item;
|
||||
var promises = [];
|
||||
|
||||
for (var i = 0; i < domItems.length; i++) {
|
||||
var item = domItems[i];
|
||||
if (item === draggedItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reset
|
||||
if (item.dataset.tweakDelta &&
|
||||
!context.moveUp.has(item) && !context.moveDown.has(item)) {
|
||||
|
||||
promises.push(tweakAndTransition(item, 0));
|
||||
}
|
||||
|
||||
// Moving down
|
||||
if (context.moveDown.has(item)) {
|
||||
if (parseInt(item.dataset.tweakDelta) !== itemHeight) {
|
||||
promises.push(tweakAndTransition(item, itemHeight));
|
||||
}
|
||||
}
|
||||
|
||||
// Moving up
|
||||
if (context.moveUp.has(item)) {
|
||||
if (parseInt(item.dataset.tweakDelta) !== itemHeight * -1) {
|
||||
promises.push(tweakAndTransition(item, itemHeight * -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function moveInPlace(context, geometry) {
|
||||
var li = context.item;
|
||||
|
||||
// We're already in place
|
||||
if (!li.dataset.tweakDelta) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var position = parseInt(li.dataset.position);
|
||||
var taintedPosition = position + parseInt(li.dataset.tweakDelta);
|
||||
var itemHeight = geometry.itemHeight;
|
||||
var newPosition = position + context.moveUp.size * itemHeight -
|
||||
context.moveDown.size * itemHeight;
|
||||
|
||||
var duration = (Math.abs(taintedPosition - newPosition) / itemHeight) * 150;
|
||||
|
||||
return schedule.feedback(function() {
|
||||
li.style.transition = 'transform ' + duration + 'ms linear';
|
||||
li.style.webkitTransition = '-webkit-transform ' + duration + 'ms linear';
|
||||
setTimeout(tweakTransform.bind(null, li, (newPosition - position)));
|
||||
}, li, 'transitionend');
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
|
||||
function schedulerShim() {
|
||||
var raf = window.requestAnimationFrame;
|
||||
|
||||
return {
|
||||
mutation: function(block) { return Promise.resolve(block()); },
|
||||
transition: function(block, el, event, timeout) {
|
||||
block();
|
||||
return after(el, event, timeout || 500);
|
||||
},
|
||||
|
||||
// Not sure if this should be different from .transition()
|
||||
feedback: function(block, el, event, timeout) {
|
||||
block();
|
||||
return after(el, event, timeout || 500);
|
||||
},
|
||||
|
||||
attachDirect: function(el, event, fn) {
|
||||
fn._raffed = function(e) { raf(function() { fn(e); } ); };
|
||||
on(el, event, fn._raffed);
|
||||
},
|
||||
|
||||
detachDirect: function(el, event, fn) {
|
||||
off(el, event, fn._raffed);
|
||||
}
|
||||
};
|
||||
|
||||
function after(target, event, timeout) {
|
||||
return new Promise(function(resolve) {
|
||||
var timer = timeout && setTimeout(cb, timeout);
|
||||
on(target, event, cb);
|
||||
function cb() {
|
||||
off(target, event, cb);
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Shorthand
|
||||
function on(el, name, fn) { el.addEventListener(name, fn); }
|
||||
function off(el, name, fn) { el.removeEventListener(name, fn); }
|
||||
|
||||
});})((typeof define)[0]=='f'&&define.amd?define:(function(n,w){'use strict';
|
||||
return(typeof module)[0]=='o'?function(c){c(require,exports,module);}:
|
||||
function(c){var m={exports:{}};c(function(n){w[n];},m.exports,m);
|
||||
w[n]=m.exports;};})('FastList',this));
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "gaia-component",
|
||||
"version": "0.3.8",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-component.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"README.md",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,420 @@
|
|||
/* 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.rtl) { 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() { this.attached(); },
|
||||
detachedCallback: function() { 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);
|
||||
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; }
|
||||
el.lightStyle = document.createElement('style');
|
||||
el.lightStyle.setAttribute('scoped', '');
|
||||
el.lightStyle.innerHTML = el.lightCss;
|
||||
el.appendChild(el.lightStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hyphen separated
|
||||
* string to camel-case.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* toCamelCase('foo-bar'); //=> 'fooBar'
|
||||
*
|
||||
* @private
|
||||
* @param {Sring} string
|
||||
* @return {String}
|
||||
*/
|
||||
function toCamelCase(string) {
|
||||
return string.replace(/-(.)/g, function replacer(string, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Observer (singleton)
|
||||
*
|
||||
* @type {MutationObserver|undefined}
|
||||
*/
|
||||
var dirObserver;
|
||||
|
||||
/**
|
||||
* Observes the document `dir` (direction)
|
||||
* attribute and dispatches a global event
|
||||
* when it changes.
|
||||
*
|
||||
* Components can listen to this event and
|
||||
* make internal changes if need be.
|
||||
*
|
||||
* @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));
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "gaia-dialog",
|
||||
"version": "0.2.3",
|
||||
"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",
|
||||
"examples",
|
||||
"test",
|
||||
"tests",
|
||||
"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#^0.8.0",
|
||||
"gaia-component": "gaia-components/gaia-component#^0.3.7"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/* 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')
|
||||
};
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.close());
|
||||
},
|
||||
|
||||
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 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host[opened],
|
||||
:host.animating {
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** 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));
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/* 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;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</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));
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/* 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() {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.close());
|
||||
},
|
||||
|
||||
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: none;
|
||||
}
|
||||
|
||||
:host[opened],
|
||||
:host.animating {
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</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));
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/* 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')
|
||||
};
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.close());
|
||||
},
|
||||
|
||||
template: `
|
||||
<gaia-dialog>
|
||||
<div class="items"><content select="button"></content></div>
|
||||
</gaia-dialog>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: none;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
::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));
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/* 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 GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.close());
|
||||
},
|
||||
|
||||
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 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host[opened],
|
||||
:host.animating {
|
||||
display: block;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
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));
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
/* 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));
|
||||
},
|
||||
|
||||
open: function(e) {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.open(e));
|
||||
},
|
||||
|
||||
close: function() {
|
||||
return GaiaDialogProto.show.call(this)
|
||||
.then(() => this.els.dialog.close());
|
||||
},
|
||||
|
||||
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');
|
||||
} else {
|
||||
this._multiple = true;
|
||||
this.setAttribute('multiple', '');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
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%;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
::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));
|
||||
|
|
@ -0,0 +1,475 @@
|
|||
;(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));
|
||||
},
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 200;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.dialog-inner {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.background {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
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: 90%;
|
||||
max-width: 350px;
|
||||
margin: auto;
|
||||
box-shadow: 0 1px 0 0px rgba(0,0,0,0.15);
|
||||
background: var(--color-iota);
|
||||
transition: opacity 300ms;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.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: #858585;
|
||||
}
|
||||
|
||||
::content strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
::content small {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/** Section
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content section {
|
||||
padding: 33px 18px;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
::content section > *:not(:last-child) {
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
|
||||
/** Paragraphs
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content p {
|
||||
text-align: -moz-start;
|
||||
}
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content button {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0rem 16px;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
background: var(--color-beta);
|
||||
color: var(--color-epsilon);
|
||||
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(--color-zeta);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
::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: #e7e7e7;
|
||||
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));
|
||||
124
assets/webapps/music.gaiamobile.org/components/dom-scheduler/bower_components/gaia-dialog/index.html
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title><gaia-dialog></title>
|
||||
<meta name="description" content="gaia-dialog demo" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/base/base.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-theme/gaia-theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-fonts/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-icons/gaia-icons.css">
|
||||
<script src="bower_components/gaia-theme/lib/gaia-theme-selector.js"></script>
|
||||
<script src="bower_components/pressed/pressed.js"></script>
|
||||
<script src="bower_components/gaia-component/gaia-component.js"></script>
|
||||
<script src="bower_components/gaia-button/gaia-button.js"></script>
|
||||
<script src="bower_components/gaia-text-input/gaia-text-input.js"></script>
|
||||
<!-- <script src="bower_components/dom-scheduler/lib/dom-scheduler.js"></script> -->
|
||||
<script src="gaia-dialog.js"></script>
|
||||
<script src="gaia-dialog-alert.js"></script>
|
||||
<script src="gaia-dialog-confirm.js"></script>
|
||||
<script src="gaia-dialog-prompt.js"></script>
|
||||
<script src="gaia-dialog-action.js"></script>
|
||||
<script src="gaia-dialog-select.js"></script>
|
||||
<script src="gaia-dialog-menu.js"></script>
|
||||
<style>
|
||||
body {
|
||||
padding: 48px var(--base-m) 0 var(--base-m);
|
||||
background-color: var(--background);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="l-align-center">
|
||||
<gaia-theme-selector></gaia-theme-selector>
|
||||
|
||||
<div id="buttons">
|
||||
<div class="l-mb-small"><gaia-button data-for="gaia-dialog-alert">Alert</gaia-button></div>
|
||||
<div class="l-mb-small"><gaia-button data-for="gaia-dialog-confirm">Confirm</gaia-button></div>
|
||||
<div class="l-mb-small"><gaia-button data-for="gaia-dialog-prompt">Prompt</gaia-button></div>
|
||||
<div class="l-mb-small"><gaia-button data-for="gaia-dialog-action">Action</gaia-button></div>
|
||||
<div class="l-mb-small"><gaia-button data-for="gaia-dialog-select">Select</gaia-button></div>
|
||||
<div class="l-mb-small"><gaia-button data-for="gaia-dialog-select[multiple]">Select Multiple</gaia-button></div>
|
||||
<div class="l-mb-small"><gaia-button data-for="gaia-dialog-menu">Menu</gaia-button></div>
|
||||
<div class="l-mb-small"><gaia-button data-for="#custom-dialog-1">Custom</gaia-button></div>
|
||||
</div>
|
||||
|
||||
<div id="dialogs">
|
||||
<gaia-dialog-alert>No SIM card is present</gaia-dialog-alert>
|
||||
<gaia-dialog-confirm>Are you sure you want to delete this contact?</gaia-dialog-confirm>
|
||||
<gaia-dialog-prompt>Device name</gaia-dialog-prompt>
|
||||
|
||||
<gaia-dialog-action>
|
||||
<h1>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eu mollis tortor.</h1>
|
||||
<button on-click="close">Action 1</button>
|
||||
<button on-click="close">Action 2</button>
|
||||
</gaia-dialog-action>
|
||||
|
||||
<gaia-dialog-select>
|
||||
<h1>Ring tone</h1>
|
||||
<li>Classic prism</li>
|
||||
<li>Wallphone</li>
|
||||
<li>Disco drive</li>
|
||||
<li>Touchmatic</li>
|
||||
<li>Classic courier</li>
|
||||
<li>Hacker</li>
|
||||
</gaia-dialog-select>
|
||||
|
||||
<gaia-dialog-select multiple>
|
||||
<h1>Ring tone</h1>
|
||||
<li>Classic prism</li>
|
||||
<li>Wallphone</li>
|
||||
<li>Disco drive</li>
|
||||
<li>Touchmatic</li>
|
||||
<li>Classic courier</li>
|
||||
<li>Hacker</li>
|
||||
</gaia-dialog-select>
|
||||
|
||||
<gaia-dialog-menu>
|
||||
<button on-click="close" data-icon="firefox">Open in new window</button>
|
||||
<button on-click="close" data-icon="firefox">Add to Home Screen</button>
|
||||
<button on-click="close" data-icon="firefox">Share link</button>
|
||||
<button on-click="close" data-icon="firefox">Settings</button>
|
||||
</gaia-dialog-menu>
|
||||
|
||||
<gaia-dialog id="custom-dialog-1">
|
||||
<section>
|
||||
<h1>Application Download</h1>
|
||||
<p>Do you want to download and install this application?</p>
|
||||
</section>
|
||||
<fieldset>
|
||||
<button on-click="close">Cancel</button>
|
||||
<button on-click="close">Action</button>
|
||||
</fieldset>
|
||||
</gaia-dialog>
|
||||
|
||||
<gaia-dialog>
|
||||
<h1>Main Heading</h1>
|
||||
<h2>Sub Heading</h2>
|
||||
<hr></hr>
|
||||
<p>Lorem ipsum dolor sit amet, consecutor adipisicing elit, sed do eiusod tempor incidenti ut la et dolore magna...</p>
|
||||
<button on-click="close">Action</button>
|
||||
<button on-click="close">Cancel</button>
|
||||
</gaia-dialog>
|
||||
|
||||
<gaia-dialog>
|
||||
<h1>Confirmation</h1>
|
||||
<p>Lorem ipsum dolor sit amet, consecutor adipisicing elit, sed do eiusod tempor incidenti ut la et dolore magna...</p>
|
||||
<hr>
|
||||
<p>Lorem ipsum dolor sit amet, consecutor adipisicing elit, sed do eiusod tempor incidenti ut la et dolore magna...</p>
|
||||
<button on-click="close">Action</button>
|
||||
<button on-click="close">Action</button>
|
||||
<button on-click="close">Cancel</button>
|
||||
</gaia-dialog>
|
||||
</div>
|
||||
<script>
|
||||
var buttons = document.querySelectorAll('gaia-button');
|
||||
[].forEach.call(buttons, function(button) {
|
||||
button.addEventListener('click', function(e) {
|
||||
var dialog = document.querySelector(button.dataset.for);
|
||||
dialog.open(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "gaia-fonts",
|
||||
"version": "0.2.1",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "style.css",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-Light.woff") format("woff");
|
||||
font-weight: 300; /* lighter */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-LightItalic.woff") format("woff");
|
||||
font-style: italic;
|
||||
font-weight: 300; /* lighter */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: normal; /* 400 */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-RegularItalic.woff") format("woff");
|
||||
font-style: italic;
|
||||
font-weight: normal; /* 400 */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-Medium.woff") format("woff");
|
||||
font-style: italic;
|
||||
font-weight: bold; /* 700 */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-MediumItalic.woff") format("woff");
|
||||
font-style: italic;
|
||||
font-weight: bold; /* 700 */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-Bold.woff") format("woff");
|
||||
font-weight: 800; /* bolder */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "FiraSans";
|
||||
src: url("fonts/FiraSans-BoldItalic.woff") format("woff");
|
||||
font-style: italic;
|
||||
font-weight: 800; /* bolder */
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
'use strict';
|
||||
|
||||
// Create a report about unused icons in a project.
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var optimist = require('optimist');
|
||||
|
||||
var cli = optimist
|
||||
.usage('Create a report about unused icons in a project.' + '\n' +
|
||||
'Usage: $0 path/to/your/project/');
|
||||
var projectDir = cli.argv._[0];
|
||||
|
||||
// No folder passed? Show help message then.
|
||||
if (!projectDir) {
|
||||
cli.showHelp();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check if the directory exist.
|
||||
var stats = fs.lstatSync(projectDir);
|
||||
if (!stats.isDirectory()) {
|
||||
console.log('\x1b[31mPlease provide a valid folder.\x1b[0m\n');
|
||||
cli.showHelp();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all the icons in the font.
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getIcons() {
|
||||
var icons = fs.readdirSync('./images/');
|
||||
|
||||
icons = icons.map(function(icon) {
|
||||
return path.basename(icon, '.svg');
|
||||
});
|
||||
|
||||
return icons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a grep pattern to extract used icons from files.
|
||||
* @param {string} grep
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function getUsedIcons(grep, callback) {
|
||||
exec(grep, function(err, stdout, stderr) {
|
||||
var usedIcons = stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map(function(icon) {
|
||||
// Take only the icon name.
|
||||
return icon.split(/["']/)[1];
|
||||
})
|
||||
.filter(function(icon) {
|
||||
// Remove non string items.
|
||||
return icon && icon.match(/^[a-z0-9-]+$/);
|
||||
});
|
||||
|
||||
callback(usedIcons);
|
||||
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of unused icons in a folder. The patterns of extraction are:
|
||||
* * data-icon="[^"]+"
|
||||
* * .dataset.icon ?= ?.+
|
||||
*/
|
||||
function getUnusedIcons() {
|
||||
var usedIcons = [];
|
||||
var length = 2;
|
||||
|
||||
var cb = function(icons) {
|
||||
usedIcons = usedIcons.concat(icons);
|
||||
length--;
|
||||
|
||||
if (length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var allIcons = getIcons();
|
||||
var unusedIcons = [];
|
||||
|
||||
allIcons.forEach(function(icon) {
|
||||
if (usedIcons.indexOf(icon) === -1) {
|
||||
unusedIcons.push(icon);
|
||||
}
|
||||
});
|
||||
|
||||
outputUnusedIcons(allIcons, unusedIcons);
|
||||
};
|
||||
|
||||
getUsedIcons('grep -rhoP \'data-icon="[^"]+"\' ' + projectDir, cb);
|
||||
getUsedIcons('grep -rhoP \'.dataset.icon ?= ?.+\' ' + projectDir, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the list of unused icons to the screen.
|
||||
* @param {Array.<string>} icons
|
||||
* @param {Array.<string>} unusedIcons
|
||||
*/
|
||||
function outputUnusedIcons(icons, unusedIcons) {
|
||||
console.log('\x1b[36mList of unused icons in %s (%d/%d):\x1b[0m',
|
||||
projectDir, unusedIcons.length, icons.length);
|
||||
|
||||
unusedIcons.forEach(function(icon) {
|
||||
console.log('* ' + icon);
|
||||
});
|
||||
}
|
||||
|
||||
getUnusedIcons();
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "gaia-icons",
|
||||
"version": "0.8.0",
|
||||
"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"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"create-svg-icon-set-file.py",
|
||||
"Gruntfile.js",
|
||||
"README.md",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
(function(define){define(function(require,exports,module){
|
||||
/*jshint laxbreak:true*/
|
||||
|
||||
/**
|
||||
* 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){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));
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "gaia-text-input",
|
||||
"version": "0.1.2",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-text-input.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"gaia-icons": "gaia-components/gaia-icons#~0.8.0",
|
||||
"gaia-component": "gaia-components/gaia-component#~0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gaia-fonts": "gaia-components/gaia-fonts#~0.2.1",
|
||||
"gaia-theme": "gaia-components/gaia-theme#~0.4.3",
|
||||
"base": "gaia-components/base",
|
||||
"gaia-icons": "gaia-components/gaia-icons#master"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
(function(define){define(function(require,exports,module){
|
||||
/*jshint esnext:true*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-text-input-multiline', {
|
||||
created: function() {
|
||||
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: function(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);
|
||||
}
|
||||
|
||||
/** 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>`
|
||||
});
|
||||
|
||||
});})((function(n,w){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-text-input-multiline',this));
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
(function(define){define(function(require,exports,module){
|
||||
/*jshint esnext:true*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
|
||||
/**
|
||||
* Locals
|
||||
*/
|
||||
|
||||
var DEFAULT_LENGTH = 4;
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-text-input-pin', {
|
||||
created: function() {
|
||||
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: function() {
|
||||
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: function(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: function() {
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
attributeChangedCallback: function(attr, from, to) {
|
||||
if (attrs[attr]) { this[attr] = to; }
|
||||
},
|
||||
|
||||
clear: function(e) {
|
||||
this.value = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 />
|
||||
<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;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
visibility: hidden;
|
||||
background: var(--highlight-color, #000);
|
||||
}
|
||||
|
||||
/**
|
||||
* input:focus
|
||||
*/
|
||||
|
||||
input:focus + .fields .cell::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
</style>
|
||||
`
|
||||
});
|
||||
|
||||
});})((function(n,w){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-text-input-pin',this));
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
(function(define){define(function(require,exports,module){
|
||||
/*jshint esnext:true*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var component = require('gaia-component');
|
||||
require('gaia-icons'); // Load gaia-icons
|
||||
|
||||
/**
|
||||
* Exports
|
||||
*/
|
||||
|
||||
module.exports = component.register('gaia-text-input', {
|
||||
extends: HTMLInputElement,
|
||||
|
||||
created: function() {
|
||||
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.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));
|
||||
},
|
||||
|
||||
clear: function(e) {
|
||||
this.value = '';
|
||||
},
|
||||
|
||||
focus: function() {
|
||||
this.els.input.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
},
|
||||
|
||||
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.els.inner.setAttribute('clearable', '');
|
||||
this.setAttribute('clearable', '');
|
||||
} else {
|
||||
this.els.inner.removeAttribute('clearable');
|
||||
this.removeAttribute('clearable');
|
||||
}
|
||||
|
||||
this._clearable = clearable;
|
||||
}
|
||||
},
|
||||
|
||||
value: {
|
||||
get: function() { return this.els.input.value; },
|
||||
set: function(value) { this.els.input.value = value; }
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/** Reset
|
||||
---------------------------------------------------------*/
|
||||
|
||||
input,
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/** Label
|
||||
---------------------------------------------------------*/
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin: 0 0 4px 16px;
|
||||
}
|
||||
|
||||
/**
|
||||
* [disbled]
|
||||
*/
|
||||
|
||||
[disabled] label {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/** Fields
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.fields {
|
||||
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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [type='search']
|
||||
*/
|
||||
|
||||
[type='search'] .fields {
|
||||
border-radius: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/** Input Field
|
||||
---------------------------------------------------------*/
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
padding: 0 16px;
|
||||
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]
|
||||
*/
|
||||
|
||||
input[disabled] {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/** Placeholder Text
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::-moz-placeholder {
|
||||
font-style: italic;
|
||||
|
||||
color:
|
||||
var(--input-placeholder-color, #909ca7);
|
||||
}
|
||||
|
||||
/** Clear Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.clear {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
right: 10px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
|
||||
background:
|
||||
var(--input-clear-background, #999);
|
||||
}
|
||||
|
||||
/**
|
||||
* [clearable]
|
||||
*/
|
||||
|
||||
[clearable] .clear {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* input:focus
|
||||
*/
|
||||
|
||||
input:focus ~ .clear {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/** Clear Icon
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.clear:before {
|
||||
font: normal 500 19px/16.5px 'gaia-icons';
|
||||
content: 'close';
|
||||
display: block;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
</style>
|
||||
`
|
||||
});
|
||||
|
||||
});})((function(n,w){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-text-input',this));
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><gaia-text-input></title>
|
||||
<meta name="description" content="gaia-text-input demo">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/base/base.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-icons/gaia-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-theme/gaia-theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-fonts/style.css">
|
||||
<script src="bower_components/gaia-theme/lib/gaia-theme-selector.js"></script>
|
||||
<script src="bower_components/gaia-component/gaia-component.js"></script>
|
||||
<script src="gaia-text-input.js"></script>
|
||||
<script src="gaia-text-input-multiline.js"></script>
|
||||
<script src="gaia-text-input-pin.js"></script>
|
||||
<style type="text/css">
|
||||
|
||||
body {
|
||||
padding: 48px 24px 24px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<gaia-theme-selector></gaia-theme-selector>
|
||||
|
||||
<button id="btn">Focus</button>
|
||||
|
||||
|
||||
<gaia-text-input id="field" placeholder="Default"></gaia-text-input>
|
||||
<gaia-text-input placeholder="Search" type="search"></gaia-text-input>
|
||||
<gaia-text-input placeholder="Email" type="email" clearable></gaia-text-input>
|
||||
<gaia-text-input-multiline placeholder="Multiline"></gaia-text-input-multiline>
|
||||
<gaia-text-input-pin length="4"></gaia-text-input-pin>
|
||||
|
||||
<gaia-text-input placeholder="Disabled" disabled></gaia-text-input>
|
||||
<gaia-text-input placeholder="Search" type="search" disabled></gaia-text-input>
|
||||
<gaia-text-input placeholder="Email" type="email" clearable disabled></gaia-text-input>
|
||||
<gaia-text-input-multiline placeholder="Multiline" disabled></gaia-text-input-multiline>
|
||||
<gaia-text-input-pin length="4" disabled></gaia-text-input-pin>
|
||||
|
||||
<!-- <gaia-text-input-password></gaia-text-input-password> -->
|
||||
|
||||
|
||||
<script>
|
||||
btn.onclick = e => field.focus();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "gaia-theme",
|
||||
"version": "0.5.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
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);
|
||||
|
||||
/** 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(--color-beta);
|
||||
}
|
||||
|
||||
.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);
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
font-family: sans-serif;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#h1-after {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
left: 0;
|
||||
top: 46px;
|
||||
|
||||
pointer-events: none;
|
||||
background-color: #ff9500;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
animation-duration: 0.25s;
|
||||
animation-timing-function: ease-in;
|
||||
animation-fill-mode: both;
|
||||
|
||||
-webkit-animation-duration: 0.25s;
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
-webkit-animation-fill-mode: both;
|
||||
}
|
||||
|
||||
[data-anim='reveal'] {
|
||||
animation-name: reveal;
|
||||
-webkit-animation-name: reveal;
|
||||
}
|
||||
|
||||
@keyframes reveal {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes reveal {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
[data-anim='hide'] {
|
||||
animation-name: hide;
|
||||
-webkit-animation-name: hide;
|
||||
}
|
||||
|
||||
@keyframes hide {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes hide {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
outline: 0;
|
||||
background: none;
|
||||
padding: 0 15px;
|
||||
|
||||
text-align: center;
|
||||
text-decoration: underline;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
-moz-appearance: none;
|
||||
|
||||
opacity: 1;
|
||||
transition: transform 0.1s linear,
|
||||
opacity 0.1s linear;
|
||||
-webkit-transition: -webkit-transform 0.1s linear,
|
||||
opacity 0.1s linear;
|
||||
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
button.transitioning {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.6, 0.6);
|
||||
-webkit-transform: scale(0.6, 0.6);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body > section {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
h1 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #27c8c2;
|
||||
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-size: 24px;
|
||||
color: #ffffff;
|
||||
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
ul {
|
||||
list-style: none;
|
||||
padding: 0 0 0 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul section {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul h2 {
|
||||
position: sticky;
|
||||
position: -webkit-sticky;
|
||||
top: 0px;
|
||||
height: 32px;
|
||||
|
||||
margin: 0;
|
||||
padding: 0 6px;
|
||||
box-sizing: border-box;
|
||||
|
||||
font-size: 0.9em;
|
||||
line-height: 32px;
|
||||
|
||||
background: #efefef;
|
||||
border-bottom: 1px #bcbcbc solid;
|
||||
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
ul .background {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
background: linear-gradient(to bottom, transparent 0px, transparent 45px, #EDEDED 45px, #EDEDED 70px, transparent 20px),
|
||||
linear-gradient(to bottom, transparent 0px, transparent 15px, #EDEDED 15px, #EDEDED 25px, transparent 20px),
|
||||
linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white);
|
||||
background-position: 20% 88px, 5% -88px, 0 -88px;
|
||||
background-size: 90% 88px, 60% 88px, 100% 88px;
|
||||
background-repeat: repeat-y;
|
||||
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
ul.reordering .background {
|
||||
background: linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white);
|
||||
background-position: 0 -88px;
|
||||
background-size: 100% 88px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
width: 100%;
|
||||
height: 88px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
-moz-user-select: none;
|
||||
|
||||
background: white;
|
||||
border-bottom: 1px #bcbcbc solid;
|
||||
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
ul li h3 {
|
||||
margin: 0;
|
||||
padding: 9px 6px 3px 6px;
|
||||
background: white;
|
||||
|
||||
color: #424242;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ul li p {
|
||||
margin: 0;
|
||||
padding: 3px 6px 10px 6px;
|
||||
background: white;
|
||||
|
||||
color: #797979;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
li.new {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
li .overlay {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 2px);
|
||||
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
|
||||
opacity: 0;
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: ease-in;
|
||||
animation-fill-mode: both;
|
||||
|
||||
-webkit-animation-duration: 0.2s;
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
-webkit-animation-fill-mode: both;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
li.edit .overlay {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.overlay .cursor {
|
||||
margin: 17px auto;
|
||||
padding: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
font-size: 25px;
|
||||
color: #0b0b0b;
|
||||
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
li.dragging {
|
||||
transition: transform 0s linear;
|
||||
-webkit-transition: -webkit-transform 0s linear;
|
||||
}
|
||||
|
||||
li.dragging .overlay {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
li[data-populated=false] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="css/h1.css">
|
||||
<link rel="stylesheet" href="css/ul.css">
|
||||
<link rel="stylesheet" href="css/app.css">
|
||||
|
||||
<!-- Component Style -->
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/base/base.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-theme/gaia-theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-fonts/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/gaia-icons/gaia-icons.css">
|
||||
|
||||
<!-- Dependencies -->
|
||||
<script type="text/javascript" src="lib/dom-scheduler.js" defer></script>
|
||||
<script src="bower_components/gaia-component/gaia-component.js" defer></script>
|
||||
<script src="bower_components/gaia-button/gaia-button.js" defer></script>
|
||||
<script src="bower_components/gaia-text-input/gaia-text-input.js" defer></script>
|
||||
|
||||
|
||||
<!-- Libs -->
|
||||
<script src="bower_components/gaia-dialog/gaia-dialog.js" defer></script>
|
||||
<script src="bower_components/gaia-dialog/gaia-dialog-alert.js" defer></script>
|
||||
<script src="bower_components/fast-list/fast-list.js" defer></script>
|
||||
|
||||
<!-- App -->
|
||||
<script type="text/javascript" src="js/bacon-source.js" async></script>
|
||||
<script type="text/javascript" src="js/app.js" async></script>
|
||||
|
||||
<title>Dom Scheduler Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>
|
||||
Main List
|
||||
</h1>
|
||||
<div id="h1-after"></div>
|
||||
<button>
|
||||
Edit
|
||||
</button>
|
||||
<section>
|
||||
<ul>
|
||||
<section
|
||||
><h2> </h2
|
||||
><div class="background"
|
||||
></div
|
||||
></section>
|
||||
<li
|
||||
><h3> </h3
|
||||
><p> </p
|
||||
><div class="overlay"
|
||||
><div class="cursor">↕︎</div></div
|
||||
></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<gaia-dialog-alert id="mine">List item clicked!</gaia-dialog-alert>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
(function() {
|
||||
var debug = false;
|
||||
var headerHeight = 50;
|
||||
var maxItemCount = 28;
|
||||
|
||||
var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup';
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var listContainer = document.querySelector('section');
|
||||
|
||||
var source = new BaconSource();
|
||||
var list = new FastList(listContainer, source);
|
||||
var dialog = document.querySelector('gaia-dialog-alert');
|
||||
|
||||
function updateHeader() {
|
||||
return scheduler.mutation(function() {
|
||||
var h1 = document.querySelector('h1');
|
||||
h1.textContent = 'Main List (' + source.fullLength() + ')';
|
||||
h1.scrollTop; // flush
|
||||
});
|
||||
}
|
||||
updateHeader();
|
||||
|
||||
function clearNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'hide';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
}
|
||||
listContainer.addEventListener('top-reached', clearNewIndicator);
|
||||
|
||||
function updateNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'reveal';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
listContainer.addEventListener('hidden-new-content', updateNewIndicator);
|
||||
|
||||
function openGaiaDialog(evt) {
|
||||
scheduler.mutation(function() {
|
||||
var detail = evt.detail;
|
||||
var li = source.getRecordAt(detail.index);
|
||||
dialog.textContent = li.title + ' item clicked!';
|
||||
dialog.open(detail.clickEvt);
|
||||
});
|
||||
}
|
||||
list.list.addEventListener('item-selected', openGaiaDialog);
|
||||
|
||||
function newContentHandler() {
|
||||
var newContent = {
|
||||
title: 'NEW Bacon ' + Date.now().toString().slice(7, -1),
|
||||
body: 'Turkey BLT please.'
|
||||
};
|
||||
|
||||
source.insertAtIndex(0, newContent);
|
||||
list.insertedAtIndex(0);
|
||||
|
||||
updateHeader();
|
||||
}
|
||||
|
||||
setInterval(newContentHandler, 15000);
|
||||
window.addEventListener('new-content', newContentHandler);
|
||||
|
||||
window.pushNewContent = function() {
|
||||
window.dispatchEvent(new CustomEvent('new-content'));
|
||||
};
|
||||
|
||||
var button = document.querySelector('button');
|
||||
button.addEventListener(endEvent, function() {
|
||||
Promise.all([toggleTransitioning(), list.toggleEditMode()])
|
||||
.then(updateText)
|
||||
.then(toggleTransitioning);
|
||||
});
|
||||
|
||||
function updateText(text) {
|
||||
return scheduler.mutation(function() {
|
||||
button.textContent = list.editing ? 'Exit' : 'Edit';
|
||||
});
|
||||
}
|
||||
|
||||
function toggleTransitioning() {
|
||||
return scheduler.feedback(function() {
|
||||
button.classList.toggle('transitioning');
|
||||
}, button, 'transitionend');
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
(function(exports) {
|
||||
var listSize = 1042;
|
||||
var sectionCount = 25;
|
||||
|
||||
var headerHeight = 32;
|
||||
var itemHeight = 88;
|
||||
|
||||
function makeContent(prefix, name) {
|
||||
return {
|
||||
title: prefix + ' Bacon ipsum dolor ' +
|
||||
Date.now().toString().slice(7, -1),
|
||||
body: 'Turkey pig boudin ' + name.toLowerCase() +
|
||||
' de mignon drums and all ' + prefix + '.'
|
||||
}
|
||||
}
|
||||
|
||||
exports.BaconSource = function BaconSource() {
|
||||
this.sections = [];
|
||||
this.contentMap = new Map();
|
||||
this._cachedLength = null;
|
||||
|
||||
for (var i = 0; i < sectionCount; i++) {
|
||||
this.sections.push('Section ' + i);
|
||||
}
|
||||
|
||||
this.sections.forEach(function(name) {
|
||||
var content = [];
|
||||
for (var i = 0; i < (Math.random() * listSize); i++) {
|
||||
content.push(makeContent(i, name));
|
||||
}
|
||||
this.contentMap.set(name, content);
|
||||
}, this);
|
||||
};
|
||||
|
||||
exports.BaconSource.prototype = {
|
||||
populateItem: function(item, i) {
|
||||
// Simulates the data source not being ready to populate
|
||||
/*if (parseInt(Date.now() / 10) % 8 === 0) {*/
|
||||
/*return new Promise(function(resolve, reject) {*/
|
||||
/*setTimeout(resolve, Math.random() * 1000);*/
|
||||
/*});*/
|
||||
/*}*/
|
||||
|
||||
var title = item.firstChild;
|
||||
var body = title.nextSibling;
|
||||
var record = this.getRecordAt(i);
|
||||
|
||||
title.firstChild.data = record.title;
|
||||
body.firstChild.data = record.body;
|
||||
},
|
||||
|
||||
getSections: function() {
|
||||
return this.contentMap.keys();
|
||||
},
|
||||
|
||||
sectionHeaderHeight: function() {
|
||||
return headerHeight;
|
||||
},
|
||||
|
||||
fullSectionHeight: function(key) {
|
||||
return this.contentMap.get(key).length * itemHeight;
|
||||
},
|
||||
|
||||
fullSectionLength: function(key) {
|
||||
return this.contentMap(key).length;
|
||||
},
|
||||
|
||||
getRecordAt: function(index) {
|
||||
for (var section of this.contentMap.values()) {
|
||||
if (index < section.length) {
|
||||
return section[index];
|
||||
}
|
||||
index -= section.length;
|
||||
}
|
||||
},
|
||||
|
||||
getSectionFor: function(index) {
|
||||
for (var entry of this.contentMap.entries()) {
|
||||
if (index < entry[1].length) {
|
||||
return entry[0];
|
||||
}
|
||||
index -= entry[1].length;
|
||||
}
|
||||
},
|
||||
|
||||
indexAtPosition: function(pos) {
|
||||
var index = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
pos -= headerHeight;
|
||||
var sectionHeight = section.length * itemHeight;
|
||||
if (pos > sectionHeight) {
|
||||
pos -= sectionHeight;
|
||||
index += section.length;
|
||||
continue;
|
||||
}
|
||||
for (var item of section) {
|
||||
pos -= itemHeight;
|
||||
index++;
|
||||
if (pos <= 0 || index === this.fullLength() - 1) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
positionForIndex: function(index) {
|
||||
var top = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
top += headerHeight;
|
||||
if (index < section.length) {
|
||||
top += index * itemHeight;
|
||||
return top;
|
||||
}
|
||||
index -= section.length;
|
||||
top += section.length * itemHeight;
|
||||
}
|
||||
},
|
||||
|
||||
fullLength: function() {
|
||||
if (this._cachedLength) {
|
||||
return this._cachedLength;
|
||||
}
|
||||
|
||||
var length = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
length += section.length;
|
||||
}
|
||||
this._cachedLength = length;
|
||||
return length;
|
||||
},
|
||||
|
||||
itemHeight: function() {
|
||||
return itemHeight;
|
||||
},
|
||||
|
||||
fullHeight: function() {
|
||||
var height = 0;
|
||||
for (var section of this.contentMap.values()) {
|
||||
height += headerHeight + section.length * itemHeight;
|
||||
}
|
||||
return height;
|
||||
},
|
||||
|
||||
insertAtIndex: function(index, record, toSection) {
|
||||
this._cachedLength = null;
|
||||
for (var entry of this.contentMap.entries()) {
|
||||
if (index < entry[1].length || entry[0] === toSection) {
|
||||
return entry[1].splice(index, 0, record);
|
||||
}
|
||||
index -= entry[1].length;
|
||||
}
|
||||
},
|
||||
|
||||
replaceAtIndex: function(index, record) {
|
||||
for (var section of this.contentMap.values()) {
|
||||
if (index < section.length) {
|
||||
return section.splice(index, 1, record);
|
||||
}
|
||||
index -= section.length;
|
||||
}
|
||||
},
|
||||
|
||||
removeAtIndex: function(index) {
|
||||
this._cachedLength = null;
|
||||
|
||||
for (var section of this.contentMap.values()) {
|
||||
if (index < section.length) {
|
||||
return section.splice(index, 1)[0];
|
||||
}
|
||||
index -= section.length;
|
||||
}
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "dom-scheduler-demo",
|
||||
"description": "A Hello World app",
|
||||
"launch_path": "/index.html",
|
||||
"icons": {
|
||||
"16": "/icons/icon16x16.png",
|
||||
"48": "/icons/icon48x48.png",
|
||||
"60": "/icons/icon60x60.png",
|
||||
"128": "/icons/icon128x128.png"
|
||||
},
|
||||
"type": "certified",
|
||||
"orientation": "default",
|
||||
"permissions": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="demo-app/css/h1.css">
|
||||
<link rel="stylesheet" href="demo-app/css/ul.css">
|
||||
<link rel="stylesheet" href="demo-app/css/app.css">
|
||||
|
||||
<script type="text/javascript" src="lib/dom-scheduler.js" async></script>
|
||||
<script src="bower_components/fast-list/fast-list.js" defer></script>
|
||||
|
||||
<script type="text/javascript" src="demo-app/js/bacon-source.js" async></script>
|
||||
<script type="text/javascript" src="page.js" async></script>
|
||||
|
||||
<title>Dom Scheduler Demo</title>
|
||||
</head>
|
||||
|
||||
<body class="theme-communications">
|
||||
<h1>
|
||||
Main List
|
||||
</h1>
|
||||
<div id="h1-after"></div>
|
||||
<button>
|
||||
Edit
|
||||
</button>
|
||||
<section>
|
||||
<ul>
|
||||
<section
|
||||
><h2> </h2
|
||||
><div class="background"
|
||||
></div
|
||||
></section>
|
||||
<li
|
||||
><h3> </h3
|
||||
><p> </p
|
||||
><div class="overlay"
|
||||
><div class="cursor">↕︎</div></div
|
||||
></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
;;;;;;;(function(exports){'use strict';var debug=false;function log(str){if(!debug){return;}
|
||||
console.log('🎶 ',str);}
|
||||
var naive=false;var naiveExec=function(block){block();return Promise.resolve();};var directProtectionWindow=360;var Scheduler=function(){this._directTracking=new Map();this._ongoingTransitions=0;this._queuedTransitions=[];this._flushing=false;this._pendingMutations=[];};Scheduler.prototype={attachDirect:function(elm,evtType,block){var tracking=this._directTracking;if(!tracking.get(elm)){tracking.set(elm,{});}
|
||||
var tElm=tracking.get(elm);if(!(evtType in tElm)){tElm[evtType]={rafID:null,protectionTimeout:null,blocks:[]};}
|
||||
var tEvt=tElm[evtType];if(!tEvt.blocks.length){elm.addEventListener(evtType,this);}
|
||||
tEvt.blocks.push(block);},detachDirect:function(elm,evtType,block){var tEvt=this._trackingFor(elm,evtType);if(!tEvt){return;}
|
||||
if(tEvt.rafID){window.cancelAnimationFrame(tEvt.rafID);}
|
||||
if(tEvt.protectionTimeout){clearTimeout(tEvt.protectionTimeout);tEvt.protectionTimeout=null;this._stopProtectingDirect();}
|
||||
var toRemove=tEvt.blocks.indexOf(block);if(toRemove===-1){log('wrong detach block');return;}
|
||||
tEvt.blocks.splice(toRemove,1);if(!tEvt.blocks.length){elm.removeEventListener(evtType,this);var tElm=this._directTracking.get(elm);delete tElm[evtType];if(Object.keys(tElm).length===0){this._directTracking.delete(elm);}}},_trackingFor:function(elm,evtType){var tracking=this._directTracking;if(!tracking.get(elm)){log('wrong detach element');return;}
|
||||
var tElm=tracking.get(elm);if(!(evtType in tElm)){log('wrong detach event');return;}
|
||||
return tElm[evtType];},_shouldProtectDirect:function(){var tracking=this._directTracking;for(var elm of tracking.keys()){var tElm=tracking.get(elm);for(var evtType in tElm){var tEvt=tElm[evtType];if(tEvt.protectionTimeout!==null){return true;}}}
|
||||
return false;},_stopProtectingDirect:function(){this._dequeueTransitions();this._flushMutations();if(debug){console.timeEnd('protecting');}},handleEvent:function(evt){var tEvt=this._trackingFor(evt.currentTarget,evt.type);if(!tEvt){return;}
|
||||
if(naive){tEvt.blocks.forEach(function(block){block(evt);});return;}
|
||||
if(tEvt.protectionTimeout){clearTimeout(tEvt.protectionTimeout);}else{if(debug){console.time('protecting');}}
|
||||
tEvt.protectionTimeout=setTimeout((function(){tEvt.protectionTimeout=null;this._stopProtectingDirect();}).bind(this),directProtectionWindow);if(tEvt.rafID){window.cancelAnimationFrame(tEvt.rafID);}
|
||||
tEvt.rafID=window.requestAnimationFrame(function(){tEvt.blocks.forEach(function(block){var startDate;if(debug){startDate=performance.now();}
|
||||
block(evt);if(debug){var blockDuration=performance.now()-startDate;if(blockDuration>16){log('Direct block took more than a frame ('+
|
||||
blockDuration.toString()+'ms)');}}});});},feedback:function(block,elm,evt,timeout){return this._transition(block,true,elm,evt,timeout);},transition:function(block,elm,evt,timeout){return this._transition(block,false,elm,evt,timeout);},_transition:function(block,feedback,elm,evt,timeout){if(naive){return naiveExec(block);}
|
||||
timeout=timeout||500;return new Promise((function(resolve,reject){var content=(function(){this._ongoingTransitions++;block();if(!elm||!evt){this._ongoingTransitions--;resolve();return;}
|
||||
if(debug){console.time('animating');}
|
||||
var finishTimeout;var done=(function(){clearTimeout(finishTimeout);elm.removeEventListener(evt,done);this._ongoingTransitions--;if(debug){console.timeEnd('animating');}
|
||||
if(this._ongoingTransitions===0){setTimeout(this._flushMutations.bind(this));}
|
||||
resolve();}).bind(this);elm.addEventListener(evt,done);finishTimeout=setTimeout(function(){done();log('Transition block saved by a timeout of '+timeout+' ~ '+
|
||||
elm.style.transition);},timeout);}).bind(this);if(this._flushing||(this._shouldProtectDirect()&&!feedback)){this._queuedTransitions.push(content);}else{content();}}).bind(this));},_dequeueTransitions:function(){if(this._queuedTransitions.length===0){return;}
|
||||
if(this._flushing||this._shouldProtectDirect()){return;}
|
||||
var transitions=this._queuedTransitions;transitions.forEach(function(transition){transition();});this._queuedTransitions=[];},mutation:function(block){if(naive){return naiveExec(block);}
|
||||
return new Promise((function(resolve,reject){if(this._shouldProtectDirect()||this._ongoingTransitions>0){this._pendingMutations.push({block:block,resolve:resolve});}else{Promise.resolve().then(block).then(resolve);}}).bind(this));},_flushMutations:function(){if(this._pendingMutations.length===0){return;}
|
||||
if(this._shouldProtectDirect()||this._ongoingTransitions>0){return;}
|
||||
this._flushing=true;var fulfilments=this._pendingMutations.map(function(obj){return obj.resolve;});var mutations=this._pendingMutations;mutations.forEach(function(mutation){mutation.block();});this._pendingMutations=[];this._flushing=false;fulfilments.forEach(function(resolve){resolve();});this._dequeueTransitions();}};exports.DomScheduler=Scheduler;if(!exports.scheduler||typeof exports.scheduler.mutation!=='function')Object.defineProperty(exports,'scheduler',{value:new Scheduler(),configurable:true,writable:true});})(window);
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
(function() {
|
||||
var debug = false;
|
||||
var headerHeight = 50;
|
||||
var maxItemCount = 28;
|
||||
|
||||
var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup';
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var listContainer = document.querySelector('section');
|
||||
|
||||
var source = new BaconSource();
|
||||
var list = new FastList(listContainer, source);
|
||||
|
||||
function updateHeader() {
|
||||
return scheduler.mutation(function() {
|
||||
var h1 = document.querySelector('h1');
|
||||
h1.textContent = 'Main List (' + source.fullLength() + ')';
|
||||
h1.scrollTop; // flush
|
||||
});
|
||||
}
|
||||
updateHeader();
|
||||
|
||||
function openAlert(evt) {
|
||||
scheduler.mutation(function() {
|
||||
var detail = evt.detail;
|
||||
var li = source.getRecordAt(detail.index);
|
||||
alert(li.title + ' item clicked!');
|
||||
});
|
||||
}
|
||||
list.list.addEventListener('item-selected', openAlert);
|
||||
|
||||
function clearNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'hide';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
}
|
||||
listContainer.addEventListener('top-reached', clearNewIndicator);
|
||||
|
||||
function updateNewIndicator() {
|
||||
var h1After = document.querySelector('#h1-after');
|
||||
if (h1After.dataset.anim == 'reveal') {
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.transition(function() {
|
||||
h1After.dataset.anim = 'reveal';
|
||||
}, h1After, 'animationend');
|
||||
}
|
||||
listContainer.addEventListener('hidden-new-content', updateNewIndicator);
|
||||
|
||||
function newContentHandler() {
|
||||
var newContent = {
|
||||
title: 'NEW Bacon ' + Date.now().toString().slice(7, -1),
|
||||
body: 'Turkey BLT please.'
|
||||
};
|
||||
|
||||
source.insertAtIndex(0, newContent);
|
||||
list.insertedAtIndex(0);
|
||||
|
||||
updateHeader();
|
||||
}
|
||||
|
||||
setInterval(newContentHandler, 15000);
|
||||
window.addEventListener('new-content', newContentHandler);
|
||||
|
||||
window.pushNewContent = function() {
|
||||
window.dispatchEvent(new CustomEvent('new-content'));
|
||||
};
|
||||
|
||||
var button = document.querySelector('button');
|
||||
button.addEventListener(endEvent, function() {
|
||||
Promise.all([toggleTransitioning(), list.toggleEditMode()])
|
||||
.then(updateText)
|
||||
.then(toggleTransitioning);
|
||||
});
|
||||
|
||||
function updateText(text) {
|
||||
return scheduler.mutation(function() {
|
||||
button.textContent = list.editing ? 'Exit' : 'Edit';
|
||||
});
|
||||
}
|
||||
|
||||
function toggleTransitioning() {
|
||||
return scheduler.feedback(function() {
|
||||
button.classList.toggle('transitioning');
|
||||
}, button, 'transitionend');
|
||||
}
|
||||
});
|
||||
})();
|
||||