Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1598)

Unified Diff: third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js

Issue 1766433002: Roll Polymer to 1.3.1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
diff --git a/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
index ee78f3484a259cb71cb4185ff719b1d9a3b4ccef..d3fddcba28261837e79065bf42c0fd86daee09e6 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior-extracted.js
@@ -100,6 +100,23 @@ context. You should place this element as a child of `<body>` whenever possible.
type: Object
},
+ /**
+ * The HTMLElement that will be firing relevant KeyboardEvents.
+ * Used for capturing esc and tab. Overridden from `IronA11yKeysBehavior`.
+ */
+ keyEventTarget: {
+ type: Object,
+ value: document
+ },
+
+ /**
+ * Set to true to enable restoring of focus when overlay is closed.
+ */
+ restoreFocusOnClose: {
+ type: Boolean,
+ value: false
+ },
+
_manager: {
type: Object,
value: Polymer.IronOverlayManager
@@ -112,13 +129,6 @@ context. You should place this element as a child of `<body>` whenever possible.
}
},
- _boundOnCaptureKeydown: {
- type: Function,
- value: function() {
- return this._onCaptureKeydown.bind(this);
- }
- },
-
_boundOnCaptureFocus: {
type: Function,
value: function() {
@@ -126,44 +136,113 @@ context. You should place this element as a child of `<body>` whenever possible.
}
},
- /** @type {?Node} */
+ /**
+ * The node being focused.
+ * @type {?Node}
+ */
_focusedChild: {
type: Object
}
},
+ keyBindings: {
+ 'esc': '__onEsc',
+ 'tab': '__onTab'
+ },
+
listeners: {
'iron-resize': '_onIronResize'
},
/**
* The backdrop element.
- * @type Node
+ * @type {Node}
*/
get backdropElement() {
return this._manager.backdropElement;
},
+ /**
+ * Returns the node to give focus to.
+ * @type {Node}
+ */
get _focusNode() {
return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this;
},
+ /**
+ * Array of nodes that can receive focus (overlay included), ordered by `tabindex`.
+ * This is used to retrieve which is the first and last focusable nodes in order
+ * to wrap the focus for overlays `with-backdrop`.
+ *
+ * If you know what is your content (specifically the first and last focusable children),
+ * you can override this method to return only `[firstFocusable, lastFocusable];`
+ * @type {[Node]}
+ * @protected
+ */
+ get _focusableNodes() {
+ // Elements that can be focused even if they have [disabled] attribute.
+ var FOCUSABLE_WITH_DISABLED = [
+ 'a[href]',
+ 'area[href]',
+ 'iframe',
+ '[tabindex]',
+ '[contentEditable=true]'
+ ];
+
+ // Elements that cannot be focused if they have [disabled] attribute.
+ var FOCUSABLE_WITHOUT_DISABLED = [
+ 'input',
+ 'select',
+ 'textarea',
+ 'button'
+ ];
+
+ // Discard elements with tabindex=-1 (makes them not focusable).
+ var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') +
+ ':not([tabindex="-1"]),' +
+ FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') +
+ ':not([disabled]):not([tabindex="-1"])';
+
+ var focusables = Polymer.dom(this).querySelectorAll(selector);
+ if (this.tabIndex >= 0) {
+ // Insert at the beginning because we might have all elements with tabIndex = 0,
+ // and the overlay should be the first of the list.
+ focusables.splice(0, 0, this);
+ }
+ // Sort by tabindex.
+ return focusables.sort(function (a, b) {
+ if (a.tabIndex === b.tabIndex) {
+ return 0;
+ }
+ if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) {
+ return 1;
+ }
+ return -1;
+ });
+ },
+
ready: function() {
- // with-backdrop need tabindex to be set in order to trap the focus.
+ // with-backdrop needs tabindex to be set in order to trap the focus.
// If it is not set, IronOverlayBehavior will set it, and remove it if with-backdrop = false.
this.__shouldRemoveTabIndex = false;
+ // Used for wrapping the focus on TAB / Shift+TAB.
+ this.__firstFocusableNode = this.__lastFocusableNode = null;
this._ensureSetup();
},
attached: function() {
// Call _openedChanged here so that position can be computed correctly.
- if (this._callOpenedWhenReady) {
+ if (this.opened) {
this._openedChanged();
}
+ this._observer = Polymer.dom(this).observeNodes(this._onNodesChange);
},
detached: function() {
+ Polymer.dom(this).unobserveNodes(this._observer);
+ this._observer = null;
this.opened = false;
this._manager.trackBackdrop(this);
this._manager.removeOverlay(this);
@@ -195,9 +274,10 @@ context. You should place this element as a child of `<body>` whenever possible.
/**
* Cancels the overlay.
+ * @param {?Event} event The original event
*/
- cancel: function() {
- var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true});
+ cancel: function(event) {
+ var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: true});
if (cancelEvent.defaultPrevented) {
return;
}
@@ -220,12 +300,10 @@ context. You should place this element as a child of `<body>` whenever possible.
this.removeAttribute('aria-hidden');
} else {
this.setAttribute('aria-hidden', 'true');
- Polymer.dom(this).unobserveNodes(this._observer);
}
// wait to call after ready only if we're initially open
if (!this._overlaySetup) {
- this._callOpenedWhenReady = this.opened;
return;
}
@@ -300,16 +378,18 @@ context. You should place this element as a child of `<body>` whenever possible.
}
},
- _toggleListeners: function () {
+ _toggleListeners: function() {
this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureClick, true);
- this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptureKeydown, true);
this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureFocus, true);
},
// tasks which must occur before opening; e.g. making the element visible
_prepareRenderOpened: function() {
+
this._manager.addOverlay(this);
+ // Needed to calculate the size of the overlay so that transitions on its size
+ // will have the correct starting points.
this._preparePositioning();
this.fit();
this._finishPositioning();
@@ -317,6 +397,12 @@ context. You should place this element as a child of `<body>` whenever possible.
if (this.withBackdrop) {
this.backdropElement.prepare();
}
+
+ // Safari will apply the focus to the autofocus element when displayed for the first time,
+ // so we blur it. Later, _applyFocus will set the focus if necessary.
+ if (this.noAutoFocus && document.activeElement === this._focusNode) {
+ this._focusNode.blur();
+ }
},
// tasks which cause the overlay to actually open; typically play an
@@ -336,23 +422,24 @@ context. You should place this element as a child of `<body>` whenever possible.
},
_finishRenderOpened: function() {
- // focus the child node with [autofocus]
+ // This ensures the overlay is visible before we set the focus
+ // (by calling _onIronResize -> refit).
+ this.notifyResize();
+ // Focus the child node with [autofocus]
this._applyFocus();
- this._observer = Polymer.dom(this).observeNodes(this.notifyResize);
this.fire('iron-overlay-opened');
},
_finishRenderClosed: function() {
- // hide the overlay and remove the backdrop
+ // Hide the overlay and remove the backdrop.
this.resetFit();
this.style.display = 'none';
this._manager.removeOverlay(this);
- this._focusedChild = null;
this._applyFocus();
-
this.notifyResize();
+
this.fire('iron-overlay-closed', this.closingReason);
},
@@ -365,8 +452,9 @@ context. You should place this element as a child of `<body>` whenever possible.
_finishPositioning: function() {
this.style.display = 'none';
this.style.transform = this.style.webkitTransform = '';
- // force layout to avoid application of transform
- /** @suppress {suspiciousCode} */ this.offsetWidth;
+ // Force layout layout to avoid application of transform.
+ // Set offsetWidth to itself so that compilers won't remove it.
+ this.offsetWidth = this.offsetWidth;
this.style.transition = this.style.webkitTransition = '';
},
@@ -377,6 +465,7 @@ context. You should place this element as a child of `<body>` whenever possible.
}
} else {
this._focusNode.blur();
+ this._focusedChild = null;
this._manager.focusOverlay();
}
},
@@ -387,23 +476,13 @@ context. You should place this element as a child of `<body>` whenever possible.
if (this.noCancelOnOutsideClick) {
this._applyFocus();
} else {
- this.cancel();
+ this.cancel(event);
}
}
},
- _onCaptureKeydown: function(event) {
- var ESC = 27;
- if (this._manager.currentOverlay() === this &&
- !this.noCancelOnEscKey &&
- event.keyCode === ESC) {
- this.cancel();
- }
- },
-
_onCaptureFocus: function (event) {
- if (this._manager.currentOverlay() === this &&
- this.withBackdrop) {
+ if (this._manager.currentOverlay() === this && this.withBackdrop) {
var path = Polymer.dom(event).path;
if (path.indexOf(this) === -1) {
event.stopPropagation();
@@ -418,25 +497,70 @@ context. You should place this element as a child of `<body>` whenever possible.
if (this.opened) {
this.refit();
}
- }
+ },
-/**
- * Fired after the `iron-overlay` opens.
- * @event iron-overlay-opened
- */
+ /**
+ * @protected
+ * Will call notifyResize if overlay is opened.
+ * Can be overridden in order to avoid multiple observers on the same node.
+ */
+ _onNodesChange: function() {
+ if (this.opened) {
+ this.notifyResize();
+ }
+ // Store it so we don't query too much.
+ var focusableNodes = this._focusableNodes;
+ this.__firstFocusableNode = focusableNodes[0];
+ this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];
+ },
-/**
- * Fired when the `iron-overlay` is canceled, but before it is closed.
- * Cancel the event to prevent the `iron-overlay` from closing.
- * @event iron-overlay-canceled
- */
+ __onEsc: function(event) {
+ // Not opened or not on top, so return.
+ if (this._manager.currentOverlay() !== this) {
+ return;
+ }
+ if (!this.noCancelOnEscKey) {
+ this.cancel(event);
+ }
+ },
-/**
- * Fired after the `iron-overlay` closes.
- * @event iron-overlay-closed
- * @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute
- */
+ __onTab: function(event) {
+ // Not opened or not on top, so return.
+ if (this._manager.currentOverlay() !== this) {
+ return;
+ }
+ // TAB wraps from last to first focusable.
+ // Shift + TAB wraps from first to last focusable.
+ var shift = event.detail.keyboardEvent.shiftKey;
+ var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode;
+ var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode;
+ if (this.withBackdrop && this._focusedChild === nodeToCheck) {
+ // We set here the _focusedChild so that _onCaptureFocus will handle the
+ // wrapping of the focus (the next event after tab is focus).
+ this._focusedChild = nodeToSet;
+ }
+ }
};
/** @polymerBehavior */
- Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
+ Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
+
+ /**
+ * Fired after the `iron-overlay` opens.
+ * @event iron-overlay-opened
+ */
+
+ /**
+ * Fired when the `iron-overlay` is canceled, but before it is closed.
+ * Cancel the event to prevent the `iron-overlay` from closing.
+ * @event iron-overlay-canceled
+ * @param {Event} event The closing of the `iron-overlay` can be prevented
+ * by calling `event.preventDefault()`. The `event.detail` is the original event that originated
+ * the canceling (e.g. ESC keyboard event or click event outside the `iron-overlay`).
+ */
+
+ /**
+ * Fired after the `iron-overlay` closes.
+ * @event iron-overlay-closed
+ * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (whether the overlay was canceled).
+ */

Powered by Google App Engine
This is Rietveld 408576698