OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | |
6 * @typedef {{ | |
7 * top: number, | |
8 * left: number, | |
9 * width: (number| undefined), | |
10 * height: (number| undefined), | |
11 * anchorPositionX: (number| undefined), | |
12 * anchorPositionY: (number| undefined), | |
13 * minX: (number| undefined), | |
14 * minY: (number| undefined), | |
15 * maxX: (number| undefined), | |
16 * maxY: (number| undefined), | |
17 * }} | |
18 */ | |
19 var ShowConfig; | |
20 | |
5 Polymer({ | 21 Polymer({ |
6 is: 'cr-action-menu', | 22 is: 'cr-action-menu', |
7 extends: 'dialog', | 23 extends: 'dialog', |
8 | 24 |
9 /** | 25 /** |
10 * List of all options in this action menu. | 26 * List of all options in this action menu. |
11 * @private {?NodeList<!Element>} | 27 * @private {?NodeList<!Element>} |
12 */ | 28 */ |
13 options_: null, | 29 options_: null, |
14 | 30 |
15 /** | 31 /** |
16 * The element which the action menu will be anchored to. Also the element | 32 * The element which the action menu will be anchored to. Also the element |
17 * where focus will be returned after the menu is closed. | 33 * where focus will be returned after the menu is closed. |
dpapad
2017/04/14 22:06:31
This needs a comment now, that says "Only populate
calamity
2017/04/19 05:31:07
Done.
| |
18 * @private {?Element} | 34 * @private {?Element} |
19 */ | 35 */ |
20 anchorElement_: null, | 36 anchorElement_: null, |
21 | 37 |
22 /** | 38 /** |
23 * Bound reference to an event listener function such that it can be removed | 39 * Bound reference to an event listener function such that it can be removed |
24 * on detach. | 40 * on detach. |
25 * @private {?Function} | 41 * @private {?Function} |
26 */ | 42 */ |
27 boundClose_: null, | 43 boundClose_: null, |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
144 } while (!nextOption && counter < numOptions); | 160 } while (!nextOption && counter < numOptions); |
145 | 161 |
146 return nextOption; | 162 return nextOption; |
147 }, | 163 }, |
148 | 164 |
149 /** @override */ | 165 /** @override */ |
150 close: function() { | 166 close: function() { |
151 // Removing 'resize' and 'popstate' listeners when dialog is closed. | 167 // Removing 'resize' and 'popstate' listeners when dialog is closed. |
152 this.removeListeners_(); | 168 this.removeListeners_(); |
153 HTMLDialogElement.prototype.close.call(this); | 169 HTMLDialogElement.prototype.close.call(this); |
154 this.anchorElement_.focus(); | 170 if (this.anchorElement_) |
171 this.anchorElement_.focus(); | |
172 | |
155 this.anchorElement_ = null; | 173 this.anchorElement_ = null; |
dpapad
2017/04/14 22:06:31
This line should be inside the if too,
if (this.a
calamity
2017/04/19 05:31:07
Done.
| |
156 }, | 174 }, |
157 | 175 |
158 /** | 176 /** |
159 * Shows the menu anchored to the given element. | 177 * Shows the menu anchored to the given element. |
160 * @param {!Element} anchorElement | 178 * @param {!Element} anchorElement |
161 */ | 179 */ |
162 showAt: function(anchorElement) { | 180 showAt: function(anchorElement) { |
163 this.anchorElement_ = anchorElement; | 181 this.anchorElement_ = anchorElement; |
182 this.anchorElement_.scrollIntoViewIfNeeded(); | |
183 var rect = this.anchorElement_.getBoundingClientRect(); | |
184 this.showAtPosition({ | |
185 top: rect.top, | |
186 left: rect.left, | |
187 height: rect.height, | |
188 width: rect.width, | |
189 // Default to anchoring towards the left. | |
190 anchorPositionX: -1, | |
191 }); | |
192 }, | |
193 | |
194 /** | |
195 * Returns the point to start along the X or Y axis given a start and end | |
196 * point to anchor to, the length of the target and the direction to anchor | |
197 * in. If honoring the anchor would force the menu outside of min/max, this | |
198 * will ignore the anchor position and try to keep the menu within min/max. | |
199 * @param {number} start | |
200 * @param {number} end | |
201 * @param {number} length | |
202 * @param {number} anchorPosition | |
203 * @param {number} min | |
204 * @param {number} max | |
205 * @return {number} | |
206 */ | |
207 getStartPointWithAnchor: function( | |
208 start, end, length, anchorPosition, min, max) { | |
209 var startPoint = (start + end - length) / 2 + | |
dpapad
2017/04/14 22:06:31
TL;DR equivalent to
var startPoint = anchorPositio
calamity
2017/04/19 05:31:07
It depends if you ever want to center-align a men
dpapad
2017/04/19 21:41:55
Ok, I kind of understand the generalization need b
calamity
2017/04/24 05:20:54
I'm not a fan of calling this percent, given that
dpapad
2017/04/25 17:42:43
If you think that will be cleaner, I am fine with
calamity
2017/04/26 03:15:50
Changed to AnchorAlignment. I think this covers li
| |
210 (start - end + length) * anchorPosition / 2; | |
211 if (startPoint + length > max) | |
212 startPoint = end - length; | |
213 if (startPoint < min) | |
214 startPoint = start; | |
215 return startPoint; | |
216 }, | |
217 | |
218 /** | |
219 * Shows the menu anchored to the given box. | |
220 * @param {ShowConfig} config | |
dpapad
2017/04/14 22:06:31
!ShowConfig
calamity
2017/04/19 05:31:07
Done.
| |
221 */ | |
222 showAtPosition: function(config) { | |
223 /** | |
224 * @param {number|undefined} value | |
225 * @param {number} defaultValue | |
226 * @return {number} | |
227 */ | |
228 var defaultIfUndefined = function(value, defaultValue) { | |
229 return value == undefined ? defaultValue : value; | |
230 }; | |
231 | |
232 var top = config.top; | |
dpapad
2017/04/14 22:06:31
Lines 232-241 are basically creating a ShowConfig
calamity
2017/04/19 05:31:07
Done.
| |
233 var left = config.left; | |
234 var bottom = top + defaultIfUndefined(config.height, 0); | |
235 var right = left + defaultIfUndefined(config.width, 0); | |
236 var anchorPositionX = defaultIfUndefined(config.anchorPositionX, 1); | |
237 var anchorPositionY = defaultIfUndefined(config.anchorPositionY, 1); | |
238 var minX = defaultIfUndefined(config.minX, 0); | |
239 var maxX = defaultIfUndefined(config.maxX, window.innerWidth); | |
240 var minY = defaultIfUndefined(config.minY, 0); | |
241 var maxY = defaultIfUndefined(config.maxY, window.innerHeight); | |
242 | |
164 this.boundClose_ = this.boundClose_ || function() { | 243 this.boundClose_ = this.boundClose_ || function() { |
165 if (this.open) | 244 if (this.open) |
166 this.close(); | 245 this.close(); |
167 }.bind(this); | 246 }.bind(this); |
168 window.addEventListener('resize', this.boundClose_); | 247 window.addEventListener('resize', this.boundClose_); |
169 window.addEventListener('popstate', this.boundClose_); | 248 window.addEventListener('popstate', this.boundClose_); |
170 | 249 |
171 // Reset position to prevent previous values from affecting layout. | 250 // Reset position to prevent previous values from affecting layout. |
172 this.style.left = ''; | 251 this.style.left = ''; |
173 this.style.right = ''; | 252 this.style.right = ''; |
174 this.style.top = ''; | 253 this.style.top = ''; |
175 | 254 |
176 this.anchorElement_.scrollIntoViewIfNeeded(); | |
177 this.showModal(); | 255 this.showModal(); |
178 | 256 |
179 var rect = this.anchorElement_.getBoundingClientRect(); | 257 // Flip the X anchor in RTL. |
180 if (getComputedStyle(this.anchorElement_).direction == 'rtl') { | 258 var rtl = getComputedStyle(this).direction == 'rtl'; |
181 var right = window.innerWidth - rect.left - this.offsetWidth; | 259 if (rtl) |
182 this.style.right = right + 'px'; | 260 anchorPositionX *= -1; |
261 | |
262 var menuLeft = this.getStartPointWithAnchor( | |
263 left, right, this.offsetWidth, anchorPositionX, minX, maxX); | |
264 | |
265 if (rtl) { | |
266 var menuRight = window.innerWidth - menuLeft - this.offsetWidth; | |
267 this.style.right = menuRight + 'px'; | |
183 } else { | 268 } else { |
184 var left = rect.right - this.offsetWidth; | 269 this.style.left = menuLeft + 'px'; |
185 this.style.left = left + 'px'; | |
186 } | 270 } |
187 | 271 |
188 // Attempt to show the menu starting from the top of the rectangle and | 272 var menuTop = this.getStartPointWithAnchor( |
189 // extending downwards. If that does not fit within the window, fallback to | 273 top, bottom, this.offsetHeight, anchorPositionY, minY, maxY); |
190 // starting from the bottom and extending upwards. | 274 this.style.top = menuTop + 'px'; |
191 var top = rect.top + this.offsetHeight <= window.innerHeight ? rect.top : | |
192 rect.bottom - | |
193 this.offsetHeight - Math.max(rect.bottom - window.innerHeight, 0); | |
194 | |
195 this.style.top = top + 'px'; | |
196 }, | 275 }, |
197 }); | 276 }); |
OLD | NEW |