Chromium Code Reviews| 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 /** | 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 Loading... | |
| 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 Loading... | |
| 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 })(); |
| OLD | NEW |