Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // require: event_tracker.js | |
| 6 | |
| 7 cr.define('cr.ui', function() { | |
| 8 'use strict'; | |
| 9 | |
| 10 /** | |
| 11 * ExpandableBubble is a free-floating compact informational bubble with an | |
| 12 * arrow that points at a place of interest on the page. When clicked, the | |
| 13 * bubble expands to show more of its content. Width of the bubble is the | |
| 14 * width of the node it is overlapping when unexpanded. Expanded, it is of a | |
| 15 * fixed width, but variable height. Currently the arrow is always positioned | |
| 16 * at the bottom right and points down. | |
| 17 * @constructor | |
| 18 * @extends {cr.ui.div} | |
| 19 */ | |
| 20 var ExpandableBubble = cr.ui.define('div'); | |
| 21 | |
| 22 ExpandableBubble.prototype = { | |
| 23 __proto__: HTMLDivElement.prototype, | |
| 24 | |
| 25 /** @inheritDoc */ | |
| 26 decorate: function() { | |
| 27 this.className = 'ex-bubble'; | |
| 28 this.innerHTML = | |
| 29 '<div class=\"ex-bubble-contents\">' + | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
Replace \" with "
| |
| 30 '<div class=\"ex-bubble-title\"></div>' + | |
| 31 '<div class=\"ex-bubble-main\" hidden=\"true\"></div>' + | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
It is hidden, not hidden="..."
'<div class="ex-bu
| |
| 32 '</div>' + | |
| 33 '<div class=\"ex-bubble-close\" hidden=\"true\"></div>' + | |
| 34 '<div class=\"ex-bubble-shadow\"></div>' + | |
| 35 '<div class=\"ex-bubble-arrow\"></div>'; | |
| 36 | |
| 37 this.hidden = true; | |
| 38 this.handleCloseEvent = this.hide; | |
| 39 this.dismissOnBlur_ = true; | |
|
Evan Stade
2011/10/14 18:22:59
this is always false, right? for now I wouldn't ma
arv (Not doing code reviews)
2011/10/14 22:20:55
This can be moved to the prototype.
| |
| 40 }, | |
| 41 | |
| 42 /** | |
| 43 * Sets the title of the bubble. The title is always visible when the | |
| 44 * bubble is visible. | |
| 45 * @param {node} An HTML element. | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
@param {Node} node An HTML element.
or
@type {N
| |
| 46 */ | |
| 47 set contentTitle(node) { | |
| 48 var bubbleTitle = this.querySelector('.ex-bubble-title'); | |
| 49 bubbleTitle.innerHTML = ''; | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
textContent = ''
But it doesn't really matter
| |
| 50 bubbleTitle.appendChild(node); | |
| 51 }, | |
| 52 | |
| 53 /** | |
| 54 * Sets the content node of the bubble. The content node is only visible | |
| 55 * when the bubble is expanded. | |
| 56 * @param {node} An HTML element. | |
| 57 */ | |
| 58 set content(node) { | |
| 59 var bubbleMain = this.querySelector('.ex-bubble-main'); | |
| 60 bubbleMain.innerHTML = ''; | |
| 61 bubbleMain.appendChild(node); | |
| 62 }, | |
| 63 | |
| 64 /** | |
| 65 * Sets the anchor node, i.e. the node that this bubble points at and | |
| 66 * partially overlaps. | |
| 67 * @param {HTMLElement} node The new anchor node. | |
| 68 */ | |
| 69 set anchorNode(node) { | |
| 70 this.anchorNode_ = node; | |
| 71 | |
| 72 if (!this.hidden) | |
| 73 this.resizeAndReposition(); | |
| 74 }, | |
| 75 | |
| 76 /** | |
| 77 * Sets whether the bubble will dismiss on blur. By default, it does. | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
Please fix formatting of the comment
| |
| 78 * @param {boolean} value Whether to dismiss on blur. | |
| 79 **/ | |
| 80 set dismissOnBlur(value) { | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
This does not need a setter. since it has no side
| |
| 81 this.dismissOnBlur_ = value; | |
| 82 }, | |
| 83 | |
| 84 /** | |
| 85 * Handles the close event which is triggered when the close button | |
| 86 * is clicked. By default is set to this.hide. | |
| 87 * @param {function} A function with no parameters | |
| 88 */ | |
| 89 set handleCloseEvent(func) { | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
no need for setter
| |
| 90 this.handleCloseEvent_ = func; | |
| 91 }, | |
| 92 | |
| 93 /** | |
| 94 * Updates the position of the bubble. | |
| 95 * @private | |
| 96 */ | |
| 97 reposition: function() { | |
| 98 var clientRect = this.anchorNode_.getBoundingClientRect(); | |
| 99 this.style.left = this.style.right = clientRect.left + 'px'; | |
| 100 | |
| 101 if (this.classList.contains('.ex-bubble-contents-expanded')) { | |
| 102 this.style.top = (clientRect.top - this.offsetHeight + | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
Maybe a variable for top to reduce the code size
| |
| 103 this.unexpandedHeight - 1) + 'px'; | |
| 104 } else { | |
| 105 this.style.top = (clientRect.top - 1) + 'px'; | |
| 106 } | |
| 107 }, | |
| 108 | |
| 109 /** | |
| 110 * Resizes the bubble and then repositions it. | |
| 111 * @private | |
| 112 */ | |
| 113 resizeAndReposition: function() { | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
This should have a trailing underscore since it is
| |
| 114 var clientRect = this.anchorNode_.getBoundingClientRect(); | |
| 115 var width = clientRect.width; | |
| 116 if (this.classList.contains('.ex-bubble-contents-expanded')) { | |
| 117 var expandedWidth = 250; | |
| 118 this.style.marginLeft = (width - expandedWidth) + 'px'; | |
| 119 width = expandedWidth; | |
| 120 } else { | |
| 121 this.style.marginLeft = '0'; | |
| 122 } | |
| 123 | |
| 124 // Width is dynamic (when not expanded) based on the width of the anchor | |
| 125 // node, and the title and shadow need to follow suit. | |
| 126 this.style.width = width + 'px'; | |
| 127 var bubbleTitle = this.querySelector('.ex-bubble-title'); | |
| 128 bubbleTitle.style.width = (width - 2) + 'px'; | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
I feel like the parentheses on expression like the
Evan Stade
2011/10/17 23:15:14
I believe he got this style from copying my code.
| |
| 129 var bubbleContent = this.querySelector('.ex-bubble-main'); | |
| 130 bubbleContent.style.width = (width - 12) + 'px'; | |
| 131 var bubbleShadow = this.querySelector('.ex-bubble-shadow'); | |
| 132 bubbleShadow.style.width = (width + 2) + 'px'; | |
| 133 | |
| 134 // Also reposition the bubble -- dimensions have potentially changed. | |
| 135 this.reposition(); | |
| 136 }, | |
| 137 | |
| 138 /* | |
| 139 * Expand the bubble (bringing the full content into view). | |
| 140 * @private | |
| 141 */ | |
| 142 expandBubble_: function() { | |
| 143 this.querySelector('.ex-bubble-main').hidden = false; | |
| 144 this.querySelector('.ex-bubble-close').hidden = false; | |
| 145 var title = this.querySelector('.ex-bubble-title'); | |
| 146 title.classList.add('ex-bubble-title-bubble-expanded'); | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
This could probably be done by adding and removing
Finnur
2011/10/18 12:39:10
Indeed. And I like it a lot more, actually. Done.
| |
| 147 this.classList.add('.ex-bubble-contents-expanded'); | |
| 148 this.resizeAndReposition(); | |
| 149 }, | |
| 150 | |
| 151 /** | |
| 152 * Collapse the bubble, hiding the main content and the close button. | |
| 153 * This is automatically called when the window is resized. | |
| 154 * @private | |
| 155 */ | |
| 156 collapseBubble_: function() { | |
| 157 this.querySelector('.ex-bubble-main').hidden = true; | |
| 158 this.querySelector('.ex-bubble-close').hidden = true; | |
| 159 var title = this.querySelector('.ex-bubble-title'); | |
| 160 title.classList.remove('ex-bubble-title-bubble-expanded'); | |
| 161 this.classList.remove('.ex-bubble-contents-expanded'); | |
| 162 this.resizeAndReposition(); | |
| 163 }, | |
| 164 | |
| 165 /** | |
| 166 * The onclick handler for the notification (expands the bubble). | |
| 167 * @param {Event} e The event. | |
| 168 * @private | |
| 169 */ | |
| 170 onNotificationClick_ : function(e) { | |
| 171 if (!this.contains(e.target)) | |
| 172 return; | |
| 173 | |
| 174 if (!this.classList.contains('.ex-bubble-contents-expanded')) { | |
| 175 // Save the height of the unexpanded bubble, so we can make sure to | |
| 176 // position it correctly (arrow points in the same location) after | |
| 177 // we expand it. | |
| 178 this.unexpandedHeight = this.offsetHeight; | |
| 179 } | |
| 180 | |
| 181 this.expandBubble_(); | |
| 182 }, | |
| 183 | |
| 184 /** | |
| 185 * Starts showing the bubble. The bubble will grab input and show until the | |
| 186 * user clicks away. | |
| 187 */ | |
| 188 show: function() { | |
| 189 if (!this.hidden) | |
| 190 return; | |
| 191 | |
| 192 document.body.appendChild(this); | |
| 193 this.hidden = false; | |
| 194 this.resizeAndReposition(); | |
| 195 | |
| 196 this.eventTracker_ = new EventTracker; | |
| 197 this.eventTracker_.add(window, | |
| 198 'load', this.resizeAndReposition.bind(this)); | |
| 199 this.eventTracker_.add(window, | |
| 200 'resize', this.collapseBubble_.bind(this)); | |
| 201 this.eventTracker_.add(window, | |
| 202 'click', this.onNotificationClick_.bind(this)); | |
| 203 | |
| 204 var doc = this.ownerDocument; | |
| 205 this.eventTracker_.add(doc, 'keydown', this, true); | |
| 206 this.eventTracker_.add(doc, 'mousedown', this, true); | |
| 207 }, | |
| 208 | |
| 209 /** | |
| 210 * Hides the bubble from view. | |
| 211 */ | |
| 212 hide: function() { | |
| 213 this.hidden = true; | |
| 214 this.eventTracker_.removeAll(); | |
| 215 this.parentNode.removeChild(this); | |
| 216 }, | |
| 217 | |
| 218 /** | |
| 219 * Handles keydown and mousedown events, dismissing the bubble if | |
| 220 * necessary. | |
| 221 * @param {Event} e The event. | |
| 222 * @private | |
| 223 */ | |
| 224 handleEvent: function(e) { | |
| 225 switch (e.type) { | |
| 226 case 'keydown': | |
| 227 if (e.keyCode == 27) // Esc. | |
| 228 this.hide(); | |
| 229 break; | |
| 230 | |
| 231 case 'mousedown': | |
| 232 if (e.target == this.querySelector('.ex-bubble-close')) { | |
| 233 this.handleCloseEvent_(); | |
| 234 } else if (!this.contains(e.target)) { | |
| 235 // A click outside the bubble dismisses the bubble, unless that has | |
| 236 // been disabled. If dismissOnBlur has been disabled, it should | |
| 237 // collapse if expanded. | |
| 238 if (this.dismissOnBlur_) | |
| 239 this.hide(); | |
| 240 else if (this.classList.contains('.ex-bubble-contents-expanded')) | |
| 241 this.collapseBubble_(); | |
| 242 } | |
| 243 break; | |
| 244 } | |
| 245 | |
| 246 e.stopPropagation(); | |
|
arv (Not doing code reviews)
2011/10/14 22:20:55
Please add comments about both of these to explain
Finnur
2011/10/18 12:39:10
I don't know if they are needed. This is from the
Evan Stade
2011/10/18 16:34:15
yea they are needed, this is a hacked up way of im
| |
| 247 e.preventDefault(); | |
| 248 return; | |
| 249 }, | |
| 250 }; | |
| 251 | |
| 252 return { | |
| 253 ExpandableBubble: ExpandableBubble | |
| 254 }; | |
| 255 }); | |
| OLD | NEW |