Chromium Code Reviews| Index: chrome/browser/resources/bluetooth_internals/snackbar.js |
| diff --git a/chrome/browser/resources/bluetooth_internals/snackbar.js b/chrome/browser/resources/bluetooth_internals/snackbar.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c79045acc7d92be12c7e1a89851fc14326486c62 |
| --- /dev/null |
| +++ b/chrome/browser/resources/bluetooth_internals/snackbar.js |
| @@ -0,0 +1,213 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +/** |
| + * Javascript for Snackbar controls, served from chrome://bluetooth-internals/. |
| + */ |
| + |
| +cr.define('snackbar', function() { |
| + /** @typedef {{ |
| + * message: string, |
| + * type: string, |
| + * actionText: string|undefined, |
|
Dan Beam
2016/12/16 08:42:58
I think you need parens around, i.e. (string|undef
mbrunson
2016/12/16 21:42:30
Done.
|
| + * action: function()|undefined |
| + * }} |
| + */ |
| + var SnackbarOptions; |
| + |
| + /** @const {number} */ var SHOW_DURATION = 5000; |
| + |
| + /** |
| + * Enum of Snackbar types. Used by Snackbar to determine the styling for the |
| + * Snackbar. |
| + * @enum {string} |
| + */ |
| + var SnackbarType = { |
| + INFO: 'info', |
| + SUCCESS: 'success', |
| + WARNING: 'warning', |
| + ERROR: 'error', |
| + }; |
| + |
| + /** |
| + * Notification bar for displaying a simple message with an action link. |
| + * This element should not be instantiated directly. Instead, users should |
| + * use the Snackbar.show and Snackbar.dismiss functions to ensure proper |
| + * queuing of messages. |
| + */ |
| + var Snackbar = cr.ui.define('div'); |
| + |
| + Snackbar.prototype = { |
| + __proto__: HTMLDivElement.prototype, |
| + |
| + /** |
| + * Decorates an element as a UI element class. Creates the message div and |
| + * action link for the new Snackbar. |
| + */ |
| + decorate: function() { |
| + this.classList.add('snackbar'); |
| + this.messageDiv_ = document.createElement('div'); |
| + this.appendChild(this.messageDiv_); |
| + this.actionLink_ = document.createElement('a', 'action-link'); |
| + this.appendChild(this.actionLink_); |
| + |
| + this.timeoutFn_ = null; |
| + this.addEventListener('mouseleave', this.startTimeout_.bind(this)); |
| + this.addEventListener('mouseenter', this.stopTimeout_.bind(this)); |
| + |
| + document.addEventListener('contentfocus', this.startTimeout_.bind(this)); |
| + document.addEventListener('contentblur', this.stopTimeout_.bind(this)); |
| + }, |
| + |
| + /** |
| + * Initializes the content of the Snackbar with the given |options| |
| + * including the message, action link text, and click action of the link. |
| + * @param {!SnackbarOptions} options |
| + */ |
| + initialize: function(options) { |
| + this.messageDiv_.textContent = options.message; |
| + this.classList.add(options.type); |
| + |
| + if (options.actionText) { |
| + this.actionLink_.textContent = options.actionText; |
| + } else { |
| + this.actionLink_.textContent = 'Dismiss'; |
| + } |
|
Dan Beam
2016/12/16 08:42:58
no curlies, also:
this.actionLink_.textContent =
mbrunson
2016/12/16 21:42:30
Done.
|
| + |
| + if (options.action) { |
| + this.actionLink_.addEventListener('click', function() { |
| + options.action(); |
| + this.dismiss(); |
| + }.bind(this)); |
| + } else { |
| + this.actionLink_.addEventListener('click', this.dismiss.bind(this)); |
|
Dan Beam
2016/12/16 08:42:58
nit:
this.actionLink_.addEventListener('click', f
mbrunson
2016/12/16 21:42:30
Done.
|
| + } |
| + }, |
| + |
| + /** |
| + * Shows the Snackbar. |
| + */ |
| + show: function() { |
| + this.classList.add('open'); |
| + this.startTimeout_(); |
| + }, |
| + |
| + /** |
| + * Dismisses the Snackbar. Once the Snackbar is completely hidden, the |
| + * 'dismissed' event is fired. |
| + */ |
| + dismiss: function() { |
| + this.addEventListener('webkitTransitionEnd', function(event) { |
| + if (event.propertyName === 'transform') { |
| + this.dispatchEvent(new CustomEvent('dismissed')); |
| + } |
|
Dan Beam
2016/12/16 08:42:58
nit: no curlies
mbrunson
2016/12/16 21:42:30
Done.
|
| + }.bind(this)); |
| + |
| + ensureTransitionEndEvent(this, SHOW_DURATION); |
| + this.classList.remove('open'); |
| + }, |
| + |
| + /** |
| + * Starts the timeout for dismissing the Snackbar. |
| + * @private |
| + */ |
| + startTimeout_: function() { |
| + this.timeoutFn_ = setTimeout(function() { |
|
Dan Beam
2016/12/16 08:42:58
timeoutFn_ -> startTimeoutInterval_? this type is
mbrunson
2016/12/16 21:42:30
Ah yes. Since setTimeout returns an ID, I should c
|
| + this.dismiss(); |
| + }.bind(this), SHOW_DURATION); |
| + }, |
| + |
| + /** |
| + * Stops the timeout for dismissing the Snackbar. Only clears the timeout |
| + * when the Snackbar is open. |
| + * @private |
| + */ |
| + stopTimeout_: function() { |
| + if (this.classList.contains('open')) { |
| + clearTimeout(this.timeoutFn_); |
| + this.timeoutFn_ = null; |
| + } |
| + }, |
| + }; |
| + |
| + /** @private {Snackbar} */ |
|
Dan Beam
2016/12/16 08:42:58
nit: ?Snackbar
mbrunson
2016/12/16 21:42:30
Done.
|
| + Snackbar.current_ = null; |
| + |
| + /** @private {!Array<!SnackbarOptions>} */ |
| + Snackbar.queue_ = []; |
|
Dan Beam
2016/12/16 08:42:58
do you actually need to queue these right now? wh
mbrunson
2016/12/16 21:42:30
A user may attempt to inspect multiple devices at
|
| + |
| + /** |
| + * Creates a Snackbar and shows it if one is not showing already. If a |
| + * Snackbar is already active, the next Snackbar is queued. |
| + * @param {string} message The message to display in the Snackbar. |
| + * @param {string=} opt_type A string determining the Snackbar type: info, |
| + * success, warning, error. If not provided, info type is used. |
| + * @param {string=} opt_actionText The text to display for the action link. |
| + * @param {function()=} opt_action A function to be called when the user |
| + * presses the action link. |
| + * @return {!Snackbar} |
| + */ |
| + Snackbar.show = function(message, opt_type, opt_actionText, opt_action) { |
| + var options = { |
| + message: message, |
| + type: opt_type || SnackbarType.INFO, |
| + actionText: opt_actionText, |
| + action: opt_action, |
| + }; |
| + |
| + var newSnackbar = new Snackbar(); |
| + newSnackbar.initialize(options); |
| + |
| + if (Snackbar.current_) { |
| + Snackbar.queue_.push(newSnackbar); |
| + return newSnackbar; |
| + } |
| + |
| + Snackbar.show_(newSnackbar); |
| + return newSnackbar; |
| + }; |
| + |
| + /** |
| + * Creates a Snackbar and sets events for queuing the next Snackbar to show. |
| + * @param {!Snackbar} newSnackbar |
| + * @private |
| + */ |
| + Snackbar.show_ = function(newSnackbar) { |
| + $('snackbar-container').appendChild(newSnackbar); |
| + |
| + newSnackbar.addEventListener('dismissed', function() { |
| + $('snackbar-container').removeChild(Snackbar.current_); |
| + |
| + var newSnackbar = Snackbar.queue_.shift(); |
| + if (newSnackbar) { |
| + Snackbar.show_(newSnackbar); |
| + return; |
| + } |
| + |
| + Snackbar.current_ = null; |
| + }); |
| + |
| + Snackbar.current_ = newSnackbar; |
| + |
| + // Show the Snackbar after a slight delay to allow for a layout reflow. |
| + setTimeout(function() { |
| + newSnackbar.show(); |
| + }, 10); |
| + }; |
| + |
| + /** |
| + * Dismisses the Snackbar currently showing. |
| + * @param {boolean} clearQueue If true, clears the Snackbar queue before |
| + * dismissing. |
| + */ |
| + Snackbar.dismiss = function(clearQueue) { |
| + if (clearQueue) Snackbar.queue_ = []; |
| + if (Snackbar.current_) Snackbar.current_.dismiss(); |
| + }; |
| + |
| + return { |
| + Snackbar: Snackbar, |
| + SnackbarType: SnackbarType, |
| + }; |
| +}); |