jellyfin-web/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-manager.html

259 lines
7.2 KiB
HTML
Raw Normal View History

2015-06-19 09:36:51 -07:00
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="../polymer/polymer.html">
<script>
2016-01-29 19:43:11 -07:00
/**
* @struct
* @constructor
2016-03-11 20:29:37 -07:00
* @private
2016-01-29 19:43:11 -07:00
*/
Polymer.IronOverlayManagerClass = function() {
2016-03-11 20:29:37 -07:00
/**
* Used to keep track of the opened overlays.
* @private {Array<Element>}
*/
2016-01-29 19:43:11 -07:00
this._overlays = [];
2016-03-11 20:29:37 -07:00
/**
* Used to keep track of the last focused node before an overlay gets opened.
* @private {Array<Element>}
*/
2016-02-23 22:36:48 -07:00
this._lastFocusedNodes = [];
2016-01-29 19:43:11 -07:00
/**
2016-03-11 20:29:37 -07:00
* iframes have a default z-index of 100,
* so this default should be at least that.
2016-01-29 19:43:11 -07:00
* @private {number}
*/
this._minimumZ = 101;
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* Used to keep track of the opened overlays with backdrop.
* @private {Array<Element>}
*/
2016-01-29 19:43:11 -07:00
this._backdrops = [];
2016-02-10 12:16:48 -07:00
2016-03-11 20:29:37 -07:00
/**
* Memoized backdrop element.
* @private {Element|null}
*/
2016-02-10 12:16:48 -07:00
this._backdropElement = null;
2016-03-11 20:29:37 -07:00
};
Polymer.IronOverlayManagerClass.prototype = {
constructor: Polymer.IronOverlayManagerClass,
/**
* The shared backdrop element.
* @type {Element} backdropElement
*/
get backdropElement() {
if (!this._backdropElement) {
this._backdropElement = document.createElement('iron-overlay-backdrop');
}
return this._backdropElement;
},
2016-02-23 22:36:48 -07:00
/**
* The deepest active element.
2016-03-11 20:29:37 -07:00
* @type {Element|undefined} activeElement the active element
*/
get deepActiveElement() {
var active = document.activeElement;
// document.activeElement can be null
// https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
while (active && active.root && Polymer.dom(active.root).activeElement) {
active = Polymer.dom(active.root).activeElement;
}
return active;
},
2016-02-23 22:36:48 -07:00
2016-03-11 20:29:37 -07:00
/**
* If a node is contained in an overlay.
* @param {Element=} node
* @return {boolean}
* @private
*/
_isChildOfOverlay: function(node) {
while (node && node !== document.body) {
// Use logical parentNode, or native ShadowRoot host.
node = Polymer.dom(node).parentNode || node.host;
// Check if it is an overlay.
if (node && node.behaviors && node.behaviors.indexOf(Polymer.IronOverlayBehaviorImpl) !== -1) {
return true;
}
2016-02-23 22:36:48 -07:00
}
2016-03-11 20:29:37 -07:00
return false;
},
2015-12-14 08:43:03 -07:00
2016-03-11 20:29:37 -07:00
/**
* @param {Element} overlay
* @param {number} aboveZ
* @private
*/
_applyOverlayZ: function(overlay, aboveZ) {
this._setZ(overlay, aboveZ + 2);
},
2015-12-14 08:43:03 -07:00
2016-03-11 20:29:37 -07:00
/**
* @param {Element} element
* @param {number|string} z
* @private
*/
_setZ: function(element, z) {
element.style.zIndex = z;
},
2015-12-14 08:43:03 -07:00
2016-03-11 20:29:37 -07:00
/**
* Tracks overlays for z-index and focus management.
* @param {Element} overlay
*/
addOverlay: function(overlay) {
var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);
this._overlays.push(overlay);
var newZ = this.currentOverlayZ();
if (newZ <= minimumZ) {
this._applyOverlayZ(overlay, minimumZ);
}
var element = this.deepActiveElement;
// If already in other overlay, don't reset focus there.
if (this._isChildOfOverlay(element)) {
element = null;
}
this._lastFocusedNodes.push(element);
},
2015-12-14 08:43:03 -07:00
2016-03-11 20:29:37 -07:00
/**
* @param {Element} overlay
*/
removeOverlay: function(overlay) {
var i = this._overlays.indexOf(overlay);
if (i >= 0) {
this._overlays.splice(i, 1);
this._setZ(overlay, '');
2016-02-23 22:36:48 -07:00
2016-03-11 20:29:37 -07:00
var node = this._lastFocusedNodes[i];
// Focus only if still contained in document.body
if (overlay.restoreFocusOnClose && node && Polymer.dom(document.body).deepContains(node)) {
node.focus();
}
this._lastFocusedNodes.splice(i, 1);
2016-02-23 22:36:48 -07:00
}
2016-03-11 20:29:37 -07:00
},
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* @return {Element|undefined} overlay The current overlay.
*/
currentOverlay: function() {
var i = this._overlays.length - 1;
while (this._overlays[i] && !this._overlays[i].opened) {
--i;
}
return this._overlays[i];
},
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* @return {number} zIndex the current overlay z-index.
*/
currentOverlayZ: function() {
return this._getOverlayZ(this.currentOverlay());
},
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* Ensures that the minimum z-index of new overlays is at least `minimumZ`.
* This does not effect the z-index of any existing overlays.
* @param {number} minimumZ
*/
ensureMinimumZ: function(minimumZ) {
this._minimumZ = Math.max(this._minimumZ, minimumZ);
},
2015-12-14 08:43:03 -07:00
2016-03-11 20:29:37 -07:00
focusOverlay: function() {
var current = /** @type {?} */ (this.currentOverlay());
// We have to be careful to focus the next overlay _after_ any current
// transitions are complete (due to the state being toggled prior to the
// transition). Otherwise, we risk infinite recursion when a transitioning
// (closed) overlay becomes the current overlay.
//
// NOTE: We make the assumption that any overlay that completes a transition
// will call into focusOverlay to kick the process back off. Currently:
// transitionend -> _applyFocus -> focusOverlay.
if (current && !current.transitioning) {
current._applyFocus();
}
},
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* @param {Element} overlay The overlay that updated its withBackdrop.
*/
trackBackdrop: function(overlay) {
var index = this._backdrops.indexOf(overlay);
if (overlay.opened && overlay.withBackdrop) {
// no duplicates
if (index === -1) {
this._backdrops.push(overlay);
}
} else if (index >= 0) {
this._backdrops.splice(index, 1);
2015-06-19 09:36:51 -07:00
}
2016-03-11 20:29:37 -07:00
},
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* @return {Array<Element>} backdrops
*/
getBackdrops: function() {
return this._backdrops;
},
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* @return {number} index The z-index for the backdrop.
*/
backdropZ: function() {
return this._getOverlayZ(this._overlayWithBackdrop()) - 1;
},
2015-06-19 09:36:51 -07:00
2016-03-11 20:29:37 -07:00
/**
* @return {Element|undefined} overlay The first opened overlay that has a backdrop.
* @private
*/
_overlayWithBackdrop: function() {
for (var i = 0; i < this._overlays.length; i++) {
if (this._overlays[i].opened && this._overlays[i].withBackdrop) {
return this._overlays[i];
}
2016-01-29 19:43:11 -07:00
}
2016-03-11 20:29:37 -07:00
},
2016-01-29 19:43:11 -07:00
2016-03-11 20:29:37 -07:00
/**
* Calculates the minimum z-index for the overlay.
* @param {Element=} overlay
* @private
*/
_getOverlayZ: function(overlay) {
var z = this._minimumZ;
if (overlay) {
var z1 = Number(window.getComputedStyle(overlay).zIndex);
// Check if is a number
// Number.isNaN not supported in IE 10+
if (z1 === z1) {
z = z1;
}
2016-01-29 19:43:11 -07:00
}
2016-03-11 20:29:37 -07:00
return z;
2016-01-29 19:43:11 -07:00
}
};
Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass();
2015-06-19 09:36:51 -07:00
</script>