|
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 = 'expandable-bubble'; | |
28 this.innerHTML = | |
29 '<div class="expandable-bubble-contents">' + | |
30 '<div class="expandable-bubble-title"></div>' + | |
31 '<div class="expandable-bubble-main" hidden></div>' + | |
32 '</div>' + | |
33 '<div class="expandable-bubble-close" hidden></div>' + | |
34 '<div class="expandable-bubble-shadow"></div>' + | |
35 '<div class="expandable-bubble-arrow"></div>'; | |
36 | |
37 this.hidden = true; | |
38 }, | |
39 | |
40 /** | |
41 * Sets the title of the bubble. The title is always visible when the | |
42 * bubble is visible. | |
43 * @type {node} An HTML element to set as the title. | |
arv (Not doing code reviews)
2011/10/18 16:50:30
{Node}
| |
44 */ | |
45 set contentTitle(node) { | |
46 var bubbleTitle = this.querySelector('.expandable-bubble-title'); | |
47 bubbleTitle.textContent = ''; | |
48 bubbleTitle.appendChild(node); | |
49 }, | |
50 | |
51 /** | |
52 * Sets the content node of the bubble. The content node is only visible | |
53 * when the bubble is expanded. | |
54 * @param {node} An HTML element. | |
55 */ | |
56 set content(node) { | |
57 var bubbleMain = this.querySelector('.expandable-bubble-main'); | |
58 bubbleMain.textContent = ''; | |
59 bubbleMain.appendChild(node); | |
60 }, | |
61 | |
62 /** | |
63 * Sets the anchor node, i.e. the node that this bubble points at and | |
64 * partially overlaps. | |
65 * @param {HTMLElement} node The new anchor node. | |
66 */ | |
67 set anchorNode(node) { | |
68 this.anchorNode_ = node; | |
69 | |
70 if (!this.hidden) | |
71 this.resizeAndReposition_(); | |
72 }, | |
73 | |
74 /** | |
75 * Updates the position of the bubble. | |
76 * @private | |
77 */ | |
78 reposition_: function() { | |
79 var clientRect = this.anchorNode_.getBoundingClientRect(); | |
80 this.style.left = this.style.right = clientRect.left + 'px'; | |
81 | |
82 var top = clientRect.top - 1; | |
83 if (this.getAttribute('expanded') != null) { | |
arv (Not doing code reviews)
2011/10/18 16:50:30
or hasAttribute
There is also a "macro" in cr.js
Finnur
2011/10/19 15:12:30
Ooh, nice.
| |
84 this.style.top = | |
85 (top - this.offsetHeight + this.unexpandedHeight) + 'px'; | |
86 } else { | |
87 this.style.top = top + 'px'; | |
88 } | |
89 }, | |
90 | |
91 /** | |
92 * Resizes the bubble and then repositions it. | |
93 * @private | |
94 */ | |
95 resizeAndReposition_: function() { | |
96 var clientRect = this.anchorNode_.getBoundingClientRect(); | |
97 var width = clientRect.width; | |
98 if (this.getAttribute('expanded') != null) { | |
99 var expandedWidth = 250; | |
100 this.style.marginLeft = (width - expandedWidth) + 'px'; | |
101 width = expandedWidth; | |
102 } else { | |
103 this.style.marginLeft = '0'; | |
104 } | |
105 | |
106 // Width is dynamic (when not expanded) based on the width of the anchor | |
107 // node, and the title and shadow need to follow suit. | |
108 this.style.width = width + 'px'; | |
109 var bubbleTitle = this.querySelector('.expandable-bubble-title'); | |
110 bubbleTitle.style.width = width - 2 + 'px'; | |
111 var bubbleContent = this.querySelector('.expandable-bubble-main'); | |
112 bubbleContent.style.width = width - 12 + 'px'; | |
113 var bubbleShadow = this.querySelector('.expandable-bubble-shadow'); | |
114 bubbleShadow.style.width = width + 2 + 'px'; | |
115 | |
116 // Also reposition the bubble -- dimensions have potentially changed. | |
117 this.reposition_(); | |
118 }, | |
119 | |
120 /* | |
121 * Expand the bubble (bringing the full content into view). | |
122 * @private | |
123 */ | |
124 expandBubble_: function() { | |
125 this.querySelector('.expandable-bubble-main').hidden = false; | |
126 this.querySelector('.expandable-bubble-close').hidden = false; | |
127 this.setAttribute('expanded', ''); | |
128 this.resizeAndReposition_(); | |
129 }, | |
130 | |
131 /** | |
132 * Collapse the bubble, hiding the main content and the close button. | |
133 * This is automatically called when the window is resized. | |
134 * @private | |
135 */ | |
136 collapseBubble_: function() { | |
137 this.querySelector('.expandable-bubble-main').hidden = true; | |
138 this.querySelector('.expandable-bubble-close').hidden = true; | |
139 this.removeAttribute('expanded'); | |
140 this.resizeAndReposition_(); | |
141 }, | |
142 | |
143 /** | |
144 * The onclick handler for the notification (expands the bubble). | |
145 * @param {Event} e The event. | |
146 * @private | |
147 */ | |
148 onNotificationClick_ : function(e) { | |
149 if (!this.contains(e.target)) | |
150 return; | |
151 | |
152 if (this.getAttribute('expanded') == null) { | |
153 // Save the height of the unexpanded bubble, so we can make sure to | |
154 // position it correctly (arrow points in the same location) after | |
155 // we expand it. | |
156 this.unexpandedHeight = this.offsetHeight; | |
157 } | |
158 | |
159 this.expandBubble_(); | |
160 }, | |
161 | |
162 /** | |
163 * Starts showing the bubble. The bubble will grab input and show until the | |
164 * user clicks away. | |
165 */ | |
166 show: function() { | |
167 if (!this.hidden) | |
168 return; | |
169 | |
170 document.body.appendChild(this); | |
171 this.hidden = false; | |
172 this.resizeAndReposition_(); | |
173 | |
174 this.eventTracker_ = new EventTracker; | |
175 this.eventTracker_.add(window, | |
176 'load', this.resizeAndReposition_.bind(this)); | |
177 this.eventTracker_.add(window, | |
178 'resize', this.collapseBubble_.bind(this)); | |
179 this.eventTracker_.add(window, | |
180 'click', this.onNotificationClick_.bind(this)); | |
181 | |
182 var doc = this.ownerDocument; | |
183 this.eventTracker_.add(doc, 'keydown', this, true); | |
Finnur
2011/10/20 11:36:08
Silly question: arv & estade: How do I convert thi
Evan Stade
2011/10/20 17:08:50
change doc to the node you want to listen on
arv (Not doing code reviews)
2011/10/20 18:54:13
Remember that key events are dispatched from the f
| |
184 this.eventTracker_.add(doc, 'mousedown', this, true); | |
185 }, | |
186 | |
187 /** | |
188 * Hides the bubble from view. | |
189 */ | |
190 hide: function() { | |
191 this.hidden = true; | |
192 this.eventTracker_.removeAll(); | |
193 this.parentNode.removeChild(this); | |
194 }, | |
195 | |
196 /** | |
197 * Handles keydown and mousedown events, dismissing the bubble if | |
198 * necessary. | |
199 * @param {Event} e The event. | |
200 * @private | |
201 */ | |
202 handleEvent: function(e) { | |
203 switch (e.type) { | |
204 case 'keydown': | |
205 if (e.keyCode == 27) // Esc. | |
Evan Stade
2011/10/19 01:57:05
if escape hides, then don't you want blur() to hid
Finnur
2011/10/19 15:12:30
Good question. I think, since the key event is not
Evan Stade
2011/10/19 23:26:14
I agree. Your keyboard handler should only be conn
Finnur
2011/10/20 11:36:08
See question above...
On 2011/10/19 23:26:14, Eva
| |
206 this.hide(); | |
207 break; | |
208 | |
209 case 'mousedown': | |
210 if (e.target == this.querySelector('.expandable-bubble-close')) { | |
211 this.hide(); | |
212 } else if (!this.contains(e.target)) { | |
213 if (this.getAttribute('expanded') != null) | |
214 this.collapseBubble_(); | |
215 } | |
216 break; | |
217 } | |
218 | |
219 e.stopPropagation(); | |
Evan Stade
2011/10/19 01:57:05
I am not sure whether these are desired in the mou
Finnur
2011/10/19 15:12:30
Do you mean I don't need it for the keyboard event
Evan Stade
2011/10/19 23:26:14
no, I mean that you don't need it for mousedown
Finnur
2011/10/20 11:36:08
Ah, I see.
On 2011/10/19 23:26:14, Evan Stade wr
| |
220 e.preventDefault(); | |
221 return; | |
222 }, | |
223 }; | |
224 | |
225 return { | |
226 ExpandableBubble: ExpandableBubble | |
227 }; | |
228 }); | |
OLD | NEW |