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

Side by Side Diff: ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js

Issue 2951703002: [cr-action-menu] Fix anchoring to offscreen elements. (Closed)
Patch Set: fix closure Created 3 years, 6 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 unified diff | Download patch
« no previous file with comments | « chrome/test/data/webui/cr_elements/cr_action_menu_test.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /** 5 /**
6 * @typedef {{ 6 * @typedef {{
7 * top: number, 7 * top: number,
8 * left: number, 8 * left: number,
9 * width: (number| undefined), 9 * width: (number| undefined),
10 * height: (number| undefined), 10 * height: (number| undefined),
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 break; 66 break;
67 } 67 }
68 68
69 if (startPoint + length > max) 69 if (startPoint + length > max)
70 startPoint = end - length; 70 startPoint = end - length;
71 if (startPoint < min) 71 if (startPoint < min)
72 startPoint = start; 72 startPoint = start;
73 return startPoint; 73 return startPoint;
74 } 74 }
75 75
76
77 /** 76 /**
78 * @private 77 * @private
79 * @return {!ShowConfig} 78 * @return {!ShowConfig}
80 */ 79 */
81 function getDefaultShowConfig() { 80 function getDefaultShowConfig() {
82 return { 81 return {
83 top: 0, 82 top: 0,
84 left: 0, 83 left: 0,
85 height: 0, 84 height: 0,
86 width: 0, 85 width: 0,
87 anchorAlignmentX: AnchorAlignment.AFTER_START, 86 anchorAlignmentX: AnchorAlignment.AFTER_START,
88 anchorAlignmentY: AnchorAlignment.AFTER_START, 87 anchorAlignmentY: AnchorAlignment.AFTER_START,
89 minX: 0, 88 minX: document.body.scrollLeft,
90 minY: 0, 89 minY: document.body.scrollTop,
91 maxX: window.innerWidth, 90 maxX: document.body.scrollLeft + window.innerWidth,
92 maxY: window.innerHeight, 91 maxY: document.body.scrollTop + window.innerHeight,
93 }; 92 };
94 } 93 }
95 94
96 Polymer({ 95 Polymer({
97 is: 'cr-action-menu', 96 is: 'cr-action-menu',
98 extends: 'dialog', 97 extends: 'dialog',
99 98
100 /** 99 /**
101 * List of all options in this action menu. 100 * List of all options in this action menu.
102 * @private {?NodeList<!Element>} 101 * @private {?NodeList<!Element>}
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 HTMLDialogElement.prototype.close.call(this); 244 HTMLDialogElement.prototype.close.call(this);
246 if (this.anchorElement_) { 245 if (this.anchorElement_) {
247 cr.ui.focusWithoutInk(assert(this.anchorElement_)); 246 cr.ui.focusWithoutInk(assert(this.anchorElement_));
248 this.anchorElement_ = null; 247 this.anchorElement_ = null;
249 } 248 }
250 }, 249 },
251 250
252 /** 251 /**
253 * Shows the menu anchored to the given element. 252 * Shows the menu anchored to the given element.
254 * @param {!Element} anchorElement 253 * @param {!Element} anchorElement
254 * @param {ShowConfig=} opt_config
255 */ 255 */
256 showAt: function(anchorElement) { 256 showAt: function(anchorElement, opt_config) {
257 this.anchorElement_ = anchorElement; 257 this.anchorElement_ = anchorElement;
258 // Scroll the anchor element into view so that the bounding rect will be
259 // accurate for where the menu should be shown.
258 this.anchorElement_.scrollIntoViewIfNeeded(); 260 this.anchorElement_.scrollIntoViewIfNeeded();
261 var config = getDefaultShowConfig();
262
263 // Save the scroll position that ensures the anchor element is onscreen.
264 var scrollLeft = document.body.scrollLeft;
265 var scrollTop = document.body.scrollTop;
266
267 // Reset position so that layout isn't affected by the previous position,
268 // and so that the dialog is positioned at the top-start corner of the
269 // document.
270 this.resetStyle_();
271
272 // Show the dialog which will focus the top-start of the body. This makes
273 // the client rect calculation relative to the top-start of the body.
274 this.showModal();
275
259 var rect = this.anchorElement_.getBoundingClientRect(); 276 var rect = this.anchorElement_.getBoundingClientRect();
260 this.showAtPosition({ 277 this.positionDialog_(/** @type {ShowConfig} */ (Object.assign(
261 top: rect.top, 278 config, {
262 left: rect.left, 279 top: rect.top,
263 height: rect.height, 280 left: rect.left,
264 width: rect.width, 281 height: rect.height,
265 // Default to anchoring towards the left. 282 width: rect.width,
266 anchorAlignmentX: AnchorAlignment.BEFORE_END, 283 // Default to anchoring towards the left.
267 }); 284 anchorAlignmentX: AnchorAlignment.BEFORE_END,
285 },
286 opt_config)));
287
288 // Restore the scroll position.
289 document.body.scrollTop = scrollTop;
290 document.body.scrollLeft = scrollLeft;
291
292 this.addCloseListeners_();
268 }, 293 },
269 294
270 /** 295 /**
271 * Shows the menu anchored to the given box. The anchor alignment is 296 * Shows the menu anchored to the given box. The anchor alignment is
272 * specified as an X and Y alignment which represents a point in the anchor 297 * specified as an X and Y alignment which represents a point in the anchor
273 * where the menu will align to, which can have the menu either before or 298 * where the menu will align to, which can have the menu either before or
274 * after the given point in each axis. Center alignment places the center of 299 * after the given point in each axis. Center alignment places the center of
275 * the menu in line with the center of the anchor. 300 * the menu in line with the center of the anchor.
276 * 301 *
277 * y-start 302 * y-start
278 * _____________ 303 * _____________
279 * | | 304 * | |
280 * | | 305 * | |
281 * | CENTER | 306 * | CENTER |
282 * x-start | x | x-end 307 * x-start | x | x-end
283 * | | 308 * | |
284 * |anchor box | 309 * |anchor box |
285 * |___________| 310 * |___________|
286 * 311 *
287 * y-end 312 * y-end
288 * 313 *
289 * For example, aligning the menu to the inside of the top-right edge of 314 * For example, aligning the menu to the inside of the top-right edge of
290 * the anchor, extending towards the bottom-left would use a alignment of 315 * the anchor, extending towards the bottom-left would use a alignment of
291 * (BEFORE_END, AFTER_START), whereas centering the menu below the bottom 316 * (BEFORE_END, AFTER_START), whereas centering the menu below the bottom
292 * edge of the anchor would use (CENTER, AFTER_END). 317 * edge of the anchor would use (CENTER, AFTER_END).
293 * 318 *
294 * @param {!ShowConfig} config 319 * @param {!ShowConfig} config
295 */ 320 */
296 showAtPosition: function(config) { 321 showAtPosition: function(config) {
322 this.resetStyle_();
323 this.showModal();
324 this.positionDialog_(config);
325 this.addCloseListeners_();
326 },
327
328 /** @private */
329 resetStyle_: function() {
330 this.style.left = '';
331 this.style.right = '';
332 this.style.top = '0';
dpapad 2017/06/22 01:36:40 Should this be '0px' ?
calamity 2017/06/22 06:41:41 It's convention for 0 to be used in place of 0px.
333 },
334
335 /**
336 * @param {!ShowConfig} config
337 * @private
338 */
339 positionDialog_: function(config) {
297 var c = Object.assign(getDefaultShowConfig(), config); 340 var c = Object.assign(getDefaultShowConfig(), config);
dpapad 2017/06/22 01:36:40 positionDialog_() is called from both showAtPositi
calamity 2017/06/22 06:41:41 Replaced the other call instead. This getDefaultSh
298 341
299 var top = c.top; 342 var top = c.top;
300 var left = c.left; 343 var left = c.left;
301 var bottom = top + c.height; 344 var bottom = top + c.height;
302 var right = left + c.width; 345 var right = left + c.width;
303 346
304 this.boundClose_ = this.boundClose_ || function() {
305 if (this.open)
306 this.close();
307 }.bind(this);
308 window.addEventListener('resize', this.boundClose_);
309 window.addEventListener('popstate', this.boundClose_);
310
311 // Reset position to prevent previous values from affecting layout.
312 this.style.left = '';
313 this.style.right = '';
314 this.style.top = '';
315
316 this.showModal();
317
318 // Flip the X anchor in RTL. 347 // Flip the X anchor in RTL.
319 var rtl = getComputedStyle(this).direction == 'rtl'; 348 var rtl = getComputedStyle(this).direction == 'rtl';
320 if (rtl) 349 if (rtl)
321 c.anchorAlignmentX *= -1; 350 c.anchorAlignmentX *= -1;
322 351
323 var menuLeft = getStartPointWithAnchor( 352 var menuLeft = getStartPointWithAnchor(
324 left, right, this.offsetWidth, c.anchorAlignmentX, c.minX, c.maxX); 353 left, right, this.offsetWidth, c.anchorAlignmentX, c.minX, c.maxX);
325 354
326 if (rtl) { 355 if (rtl) {
327 var menuRight = window.innerWidth - menuLeft - this.offsetWidth; 356 var menuRight = document.body.scrollWidth - menuLeft - this.offsetWidth;
328 this.style.right = menuRight + 'px'; 357 this.style.right = menuRight + 'px';
329 } else { 358 } else {
330 this.style.left = menuLeft + 'px'; 359 this.style.left = menuLeft + 'px';
331 } 360 }
332 361
333 var menuTop = getStartPointWithAnchor( 362 var menuTop = getStartPointWithAnchor(
334 top, bottom, this.offsetHeight, c.anchorAlignmentY, c.minY, c.maxY); 363 top, bottom, this.offsetHeight, c.anchorAlignmentY, c.minY, c.maxY);
335 this.style.top = menuTop + 'px'; 364 this.style.top = menuTop + 'px';
336 }, 365 },
366
367 /**
368 * @private
369 */
370 addCloseListeners_: function() {
371 this.boundClose_ = this.boundClose_ || function() {
372 if (this.open)
373 this.close();
374 }.bind(this);
375 window.addEventListener('resize', this.boundClose_);
376 window.addEventListener('popstate', this.boundClose_);
377 },
337 }); 378 });
338 })(); 379 })();
OLDNEW
« no previous file with comments | « chrome/test/data/webui/cr_elements/cr_action_menu_test.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698