1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-05 15:32:55 +02:00
epub.js/examples/polymer/toolkit-ui/elements/g-overlay.html
2013-07-18 18:46:01 -07:00

288 lines
9.3 KiB
HTML

<!--
Copyright 2013 The Polymer Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<!--
/**
* @module Polymer Elements
*/
/**
* g-overlay displays overlayed on top of other content. It starts out
* hidden and is displayed by setting it's opened property
* to true. A g-overlay's opened state can be toggled by calling the toggle
* method.
*
* It's common to want a g-overlay to animate to its opened position. A number
* of helper css classes provide some basic open/close animations. For example,
* assigning the class g-overlay-fade to a g-overlay will make it fade into
* and out of view as it opens and closes. Note, if multiple g-overlay's are
* opened, they should stack on top of each other.
*
* Styling: The size and position of a g-overlay should be setup via css.
* g-overlay is natually sized around its content. When a g-overlay is opened
* it is shown and the 'opened' class is added to it. This is typically where
* css transitions and animations are applied. When the g-overlay is closed,
* the 'opened' class is removed and a 'closing' class is added. Use 'closing'
* to customize the closing animation.
*
* Classes for animating g-overlay:
*
* * g-overlay-fade: fade in/out when opened/closed
* * g-overlay-scale-slideup: open: fade in and shrink; close: slide up
* * g-overlay-shake: open: fly in and shake; close: shake and fly out.
*
* It's common to use g-overlay to gather user input, for example a login
* dialog. To facilitate this, g-overlay supports automatic focusing of a
* specific element when it's opened. The element to be focused should be given
* an autofocus attribute.
*
* An element that should close the g-overlay will automatically do so if it
* is given the overlay-toggle attribute. Please note that g-overlay will
* close whenever the user taps outside it or presses the escape key. The
* behavior can be turned off via the autoCloseDisabled property.
*
* <g-overlay class="g-overlay-scale-slideup">
* <h2>Dialog</h2>
* <input placeholder="say something..." autofocus"></input>
* <div>I agree with this wholeheartedly.</div>
* <button overlay-toggle>OK</button>
* </g-overlay>
*
*
* @class g-overlay
*/
/**
* Fired when the g-overlay opened property is set.
*
* @event open
* @param {Object} inDetail
* @param {Object} inDetail.opened the opened state
*/
-->
<polymer-element name="g-overlay"
attributes="opened autoCloseDisabled"
on-webkitAnimationStart="openedAnimationStart"
on-animationStart="openedAnimationStart"
on-webkitAnimationEnd="openedAnimationEnd"
on-animationEnd="openedAnimationEnd"
on-webkitTransitionEnd="openedTransitionEnd"
on-transitionEnd="openedTransitionEnd"
on-tap="tapHandler"
on-keydown="keydownHandler"
>
<template>
<link rel="stylesheet" href="css/g-overlay.css">
<link rel="stylesheet" polymer-scope="global" href="css/g-overlay-global.css">
<content></content>
</template>
<script>
(function() {
// TODO(sorvell): need keyhelper component.
var ESCAPE_KEY = 27;
// track overlays for z-index and focus managemant
var overlays = [];
var trackOverlays = function(inOverlay) {
if (inOverlay.opened) {
var z0 = currentOverlayZ();
overlays.push(inOverlay);
var z1 = currentOverlayZ();
if (z1 <= z0) {
applyOverlayZ(inOverlay, z0);
}
} else {
var i = overlays.indexOf(inOverlay);
if (i >= 0) {
overlays.splice(i, 1);
setZ(inOverlay, null);
}
}
}
var applyOverlayZ = function(inOverlay, inAboveZ) {
setZ(inOverlay, inAboveZ + 2);
}
var setZ = function(inNode, inZ) {
inNode.style.zIndex = inZ;
}
var currentOverlay = function() {
return overlays[overlays.length-1];
}
var DEFAULT_Z = 10;
var currentOverlayZ = function() {
var z;
var current = currentOverlay();
if (current) {
var z1 = window.getComputedStyle(current).zIndex;
if (!isNaN(z1)) {
z = Number(z1);
}
}
return z || DEFAULT_Z;
}
var focusOverlay = function() {
var current = currentOverlay();
if (current) {
current.applyFocus();
}
}
Polymer('g-overlay', {
/**
* Set opened to true to show an overlay and to false to hide it.
* A g-overlay may be made intially opened by setting its opened
* attribute.
* @attribute opened
* @type boolean
* @default false
*/
opened: false,
/**
* By default an overlay will close automatically if the user taps outside
* it or presses the escape key. Disable this behavior by setting the
* autoCloseDisabled property to true.
* @attribute autoCloseDisabled
* @type boolean
* @default false
*/
autoCloseDisabled: false,
timeout: 1000,
captureEventType: 'tap',
ready: function() {
if (this.tabIndex === undefined) {
this.tabIndex = -1;
}
this.setAttribute('touch-action', 'none');
},
/**
* Toggle the opened state of the overlay.
* @method toggle
*/
toggle: function() {
this.opened = !this.opened;
},
openedChanged: function() {
this.renderOpened();
trackOverlays(this);
if (!this.autoCloseDisabled) {
this.enableCaptureHandler(this.opened);
}
this.enableResizeHandler(this.opened);
this.fire('opened', this.opened);
},
enableHandler: function(inEnable, inMethodName, inNode, inEventName, inCapture) {
var m = 'bound' + inMethodName;
this[m] = this[m] || this[inMethodName].bind(this);
inNode[inEnable ? 'addEventListener' : 'removeEventListener'](
inEventName, this[m], inCapture);
},
enableResizeHandler: function(inEnable) {
this.enableHandler(inEnable, 'resizeHandler', window,
'resize');
},
enableCaptureHandler: function(inEnable) {
this.enableHandler(inEnable, 'captureHandler', document,
this.captureEventType, true);
},
getFocusNode: function() {
return this.querySelector('[autofocus]') || this;
},
// TODO(sorvell): nodes stay focused when they become un-focusable due to
// an ancestory becoming display: none; file bug.
applyFocus: function() {
var focusNode = this.getFocusNode();
if (this.opened) {
focusNode.focus();
} else {
focusNode.blur();
focusOverlay();
}
},
renderOpened: function() {
this.classList.remove('closing');
this.classList.add('revealed');
// continue styling after delay so display state can change without
// aborting transitions
this.asyncMethod('continueRenderOpened');
},
continueRenderOpened: function() {
this.classList.toggle('opened', this.opened);
this.classList.toggle('closing', !this.opened);
//this.animating = this.asyncMethod('completeOpening', null, this.timeout);
},
completeOpening: function() {
//clearTimeout(this.animating);
this.animating = null;
this.classList.remove('closing');
this.classList.toggle('revealed', this.opened);
this.applyFocus();
},
openedAnimationEnd: function(e) {
if (!this.opened) {
this.classList.remove('animation');
}
// same steps as when a transition ends
this.openedTransitionEnd(e);
},
openedTransitionEnd: function(e) {
// TODO(sorvell): Necessary due to
// https://bugs.webkit.org/show_bug.cgi?id=107892
// Remove when that bug is addressed.
if (e.target == this) {
this.completeOpening();
e.stopPropagation();
e.cancelBubble = true;
}
},
openedAnimationStart: function(e) {
this.classList.add('animation');
e.stopPropagation();
e.cancelBubble = true;
},
tapHandler: function(e) {
if (e.target && e.target.hasAttribute('overlay-toggle')) {
this.toggle();
} else {
if (this.autoCloseJob) {
this.autoCloseJob.stop();
this.autoCloseJob = null;
}
}
},
// TODO(sorvell): This approach will not work with modal. For this we need a
// scrim.
captureHandler: function(e) {
if (!this.autoCloseDisabled && (currentOverlay() == this) && (this
!= e.target) && !(this.contains(e.target))) {
this.autoCloseJob = this.job(this.autoCloseJob, function() {
this.opened = false;
});
}
},
keydownHandler: function(e) {
if (!this.autoCloseDisabled && (e.keyCode == ESCAPE_KEY)) {
this.opened = false;
e.stopPropagation();
e.cancelBubble = true;
}
},
/**
* Extensions of g-overlay should implement the resizeHandler
* method to adjust the size and position of the overlay when the
* browser window resizes.
* @method resizeHandler
*/
resizeHandler: function() {
}
});
})();
</script>
</polymer-element>