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

Unified Diff: chrome/browser/resources/options/bubble.js

Issue 10907148: Implement popup bubbles for the controlled setting indicator (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comment addressed. Created 8 years, 3 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: chrome/browser/resources/options/bubble.js
diff --git a/chrome/browser/resources/options/bubble.js b/chrome/browser/resources/options/bubble.js
new file mode 100644
index 0000000000000000000000000000000000000000..bc09a33fb256a7c7e14385ce4b659c75addc49dd
--- /dev/null
+++ b/chrome/browser/resources/options/bubble.js
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 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.
+
+cr.define('options', function() {
+ var BubbleBase = cr.ui.BubbleBase;
+
+ var Bubble = cr.ui.define('div');
+
+ Bubble.prototype = {
Evan Stade 2012/09/21 12:12:11 even though they are in different namespaces, I th
bartfab (slow) 2012/09/24 17:15:55 Renamed to OptionsBubble.
+ // Set up the prototype chain.
+ __proto__: BubbleBase.prototype,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ BubbleBase.prototype.decorate.call(this);
+ this.classList.add('options-bubble');
+ },
+
+ /**
+ * Show the bubble.
+ */
+ show: function() {
+ if (!this.hidden)
+ return;
+
+ BubbleBase.prototype.show.call(this);
+ this.position_();
+
+ var doc = this.ownerDocument;
+ this.eventTracker_.add(doc, 'mousewheel', this, true);
+ this.eventTracker_.add(doc, 'scroll', this, true);
+ this.eventTracker_.add(doc, 'elementFocused', this, true);
+ this.eventTracker_.add(window, 'resize', this);
+ },
+
+ /**
+ * Position the bubble so that it is entirely within view and does not
+ * obstruct the anchor element, if possible.
Evan Stade 2012/09/21 12:12:11 this seems pretty useful for bubbles in general. W
Evan Stade 2012/09/21 12:12:11 nit: @private
bartfab (slow) 2012/09/24 17:15:55 I moved this to BubbleBase as a third alignment al
+ */
+ position_: function() {
+ var documentWidth = document.documentElement.clientWidth;
+ var documentHeight = document.documentElement.clientHeight;
+ var anchor = this.anchorNode_.getBoundingClientRect();
+ var anchorMid = (anchor.left + anchor.right) / 2.;
Evan Stade 2012/09/21 12:12:11 don't need decimal point
bartfab (slow) 2012/09/24 17:15:55 Done.
+ var bubble = this.getBoundingClientRect();
+ var arrow = this.querySelector('.bubble-arrow').getBoundingClientRect();
+
+ // Work out horizontal placement. The bubble is initially positioned so
+ // that the arrow tip points toward the midpoint of the anchor and is
+ // BubbleBase.ARROW_OFFSET pixels from the reference edge and (as
+ // specified by the arrow location). If the bubble is not entirely within
+ // view, it is then shifted, preserving the arrow tip position.
+ if (this.arrowAtRight_) {
+ var right = Math.min(Math.max(anchorMid + BubbleBase.ARROW_OFFSET,
+ bubble.width), documentWidth);
+ var left = right - bubble.width;
+ var arrowTip = right - anchorMid;
+ } else {
+ var left = Math.max(Math.min(anchorMid - BubbleBase.ARROW_OFFSET,
+ documentWidth - bubble.width), 0);
+ var arrowTip = anchorMid - left;
+ }
+ arrowTip = Math.min(Math.max(arrowTip, arrow.width / 2),
+ bubble.width - arrow.width / 2);
+
+ // Work out the vertical placement, attempting to fit the bubble entirely
+ // into view. The following placements are considered in decreasing order
+ // of preference:
+ // * Outside the anchor, arrow tip touching the anchor (arrow at
+ // top/bottom as specified by the arrow location).
+ // * Outside the anchor, arrow tip touching the anchor (arrow at
+ // bottom/top, opposite the specified arrow location).
+ // * Outside the anchor, arrow tip overlapping the anchor (arrow at
+ // top/bottom as specified by the arrow location).
+ // * Outside the anchor, arrow tip overlapping the anchor (arrow at
+ // bottom/top, opposite the specified arrow location).
+ // * Overlapping the anchor.
+ var offsetTop = Math.min(documentHeight - anchor.bottom - bubble.height,
+ arrow.height / 2);
+ var offsetBottom = Math.min(anchor.top - bubble.height, arrow.height / 2);
+ if (offsetTop < 0 && offsetBottom < 0) {
+ var top = 0;
+ this.updateArrowPosition_(false, false, arrowTip);
+ } else if (offsetTop > offsetBottom ||
+ offsetTop == offsetBottom && this.arrowAtTop_) {
+ var top = anchor.bottom + offsetTop;
+ this.updateArrowPosition_(true, true, arrowTip);
+ } else {
+ var top = anchor.top - bubble.height - offsetBottom;
+ this.updateArrowPosition_(true, false, arrowTip);
+ }
+
+ this.style.left = left + 'px';
+ this.style.top = top + 'px';
+ },
+
+ /**
+ * Handle events, closing the bubble when the user clicks or moves the focus
+ * outside the bubble and its anchor elements, scrolls the underlying
+ * document or resizes the window.
+ * @param {Event} event The event.
+ */
+ handleEvent: function(event) {
+ BubbleBase.prototype.handleEvent.call(this, event);
+
+ switch (event.type) {
+ // Close the bubble when the user clicks outside it, except if it is a
+ // left-click on the anchor element (allowing the anchor to handle the
+ // event and close the bubble itself).
+ case 'mousedown':
+ if (event.button == 0 && this.anchorNode_.contains(event.target))
+ break;
+ // Close the bubble when the underlying document is scrolled.
+ case 'mousewheel':
+ case 'scroll':
+ if (this.contains(event.target))
+ break;
+ // Close the bubble when the window is resized.
+ case 'resize':
+ this.hide();
+ break;
+ // Close the bubble when the focus moves to an element that is not the
+ // anchor and is not inside the bubble.
+ case 'elementFocused':
+ if (!this.anchorNode_.contains(event.target) &&
+ !this.contains(event.target)) {
+ this.hide();
+ }
+ break;
+ }
+ },
+
+ /**
+ * Attach the bubble to the document's DOM, making it a sibling of the
+ * anchor element so that focusable elements inside the bubble follow the
+ * anchor in the document's tab order.
+ * @private
+ */
+ attachToDOM_: function() {
+ var parent = this.anchorNode_.parentNode;
+ parent.insertBefore(this, this.anchorNode_.nextSibling);
+ },
+ };
+
+ return {
+ Bubble: Bubble
+ };
+});

Powered by Google App Engine
This is Rietveld 408576698