|
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 |