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

Unified Diff: ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js

Issue 2814743007: [cr-action-menu] Allow configurable anchors. (Closed)
Patch Set: address comment Created 3 years, 8 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
index 42b00655dd9e1ba6b45a7acd27a3892e9ef6d2fa..124c100fd0ecbc47bc9934914bb8f208d84405c4 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js
@@ -2,6 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+/**
+ * @typedef {{
+ * top: number,
+ * left: number,
+ * width: (number| undefined),
+ * height: (number| undefined),
+ * anchorConfigX: (number| undefined),
+ * anchorConfigY: (number| undefined),
+ * minX: (number| undefined),
+ * minY: (number| undefined),
+ * maxX: (number| undefined),
+ * maxY: (number| undefined),
+ * }}
+ */
+var ShowConfig;
+
Polymer({
is: 'cr-action-menu',
extends: 'dialog',
@@ -14,7 +30,8 @@ Polymer({
/**
* The element which the action menu will be anchored to. Also the element
- * where focus will be returned after the menu is closed.
+ * where focus will be returned after the menu is closed. Only populated if
+ * menu is opened with showAt().
* @private {?Element}
*/
anchorElement_: null,
@@ -151,8 +168,10 @@ Polymer({
// Removing 'resize' and 'popstate' listeners when dialog is closed.
this.removeListeners_();
HTMLDialogElement.prototype.close.call(this);
- this.anchorElement_.focus();
- this.anchorElement_ = null;
+ if (this.anchorElement_) {
+ this.anchorElement_.focus();
+ this.anchorElement_ = null;
+ }
},
/**
@@ -161,6 +180,44 @@ Polymer({
*/
showAt: function(anchorElement) {
this.anchorElement_ = anchorElement;
+ this.anchorElement_.scrollIntoViewIfNeeded();
+ var rect = this.anchorElement_.getBoundingClientRect();
+ this.showAtPosition({
+ top: rect.top,
+ left: rect.left,
+ height: rect.height,
+ width: rect.width,
+ // Default to anchoring towards the left.
+ anchorConfigX: -1,
+ });
+ },
+
+ /**
+ * Shows the menu anchored to the given box. The anchor configuration is
+ * specified as an X and Y alignment which represents a point in the anchor
+ * and the menu that will be aligned where (0, 0) and (1, 1) are the center
+ * and bottom-right of each box respectively.
+ *
+ * For example, having the menu centered around the anchor box would use an
+ * anchor config of (0, 0), whereas aligning the menu to the top-right edge of
+ * the anchor would use a config of (1, -1).
dpapad 2017/04/25 17:42:43 This explanation is much better than before. Can w
calamity 2017/04/26 03:15:50 Done.
+ *
+ * To align the menu outside the given box, use either a point or a line
+ * instead of the full box. e.g aligning a dropdown to center beneath a div
+ * at ((0, 0), (100, 100)) would use ((0, 100), (100, 100)) as the box and
+ * (0, 1) as the anchor, effectively using the bottom edge of the div as the
+ * anchor and centering horizontally beneath that.
+ *
+ * @param {!ShowConfig} config
+ */
+ showAtPosition: function(config) {
+ var c = Object.assign(this.getDefaultShowConfig_(), config);
+
+ var top = c.top;
+ var left = c.left;
+ var bottom = top + c.height;
+ var right = left + c.width;
+
this.boundClose_ = this.boundClose_ || function() {
if (this.open)
this.close();
@@ -173,25 +230,70 @@ Polymer({
this.style.right = '';
this.style.top = '';
- this.anchorElement_.scrollIntoViewIfNeeded();
this.showModal();
- var rect = this.anchorElement_.getBoundingClientRect();
- if (getComputedStyle(this.anchorElement_).direction == 'rtl') {
- var right = window.innerWidth - rect.left - this.offsetWidth;
- this.style.right = right + 'px';
+ // Flip the X anchor in RTL.
+ var rtl = getComputedStyle(this).direction == 'rtl';
+ if (rtl)
+ c.anchorConfigX *= -1;
+
+ var menuLeft = this.getStartPointWithAnchor_(
+ left, right, this.offsetWidth, c.anchorConfigX, c.minX, c.maxX);
+
+ if (rtl) {
+ var menuRight = window.innerWidth - menuLeft - this.offsetWidth;
+ this.style.right = menuRight + 'px';
} else {
- var left = rect.right - this.offsetWidth;
- this.style.left = left + 'px';
+ this.style.left = menuLeft + 'px';
}
- // Attempt to show the menu starting from the top of the rectangle and
- // extending downwards. If that does not fit within the window, fallback to
- // starting from the bottom and extending upwards.
- var top = rect.top + this.offsetHeight <= window.innerHeight ? rect.top :
- rect.bottom -
- this.offsetHeight - Math.max(rect.bottom - window.innerHeight, 0);
+ var menuTop = this.getStartPointWithAnchor_(
+ top, bottom, this.offsetHeight, c.anchorConfigY, c.minY, c.maxY);
+ this.style.top = menuTop + 'px';
+ },
+
+ /**
+ * Returns the point to start along the X or Y axis given a start and end
+ * point to anchor to, the length of the target and the direction to anchor
+ * in. If honoring the anchor would force the menu outside of min/max, this
+ * will ignore the anchor position and try to keep the menu within min/max.
+ * @private
+ * @param {number} start
+ * @param {number} end
+ * @param {number} length
+ * @param {number} anchorConfig
+ * @param {number} min
+ * @param {number} max
+ * @return {number}
+ */
+ getStartPointWithAnchor_: function(
+ start, end, length, anchorConfig, min, max) {
+ var startPoint =
+ ((start + end - length) + (start - end + length) * anchorConfig) / 2;
+ if (startPoint + length > max)
+ startPoint = end - length;
+ if (startPoint < min)
+ startPoint = start;
+ return startPoint;
+ },
+
- this.style.top = top + 'px';
+ /**
+ * @private
+ * @return {!ShowConfig}
+ */
+ getDefaultShowConfig_: function() {
+ return {
+ top: 0,
+ left: 0,
+ height: 0,
+ width: 0,
+ anchorConfigX: 1,
+ anchorConfigY: 1,
+ minX: 0,
+ minY: 0,
+ maxX: window.innerWidth,
+ maxY: window.innerHeight,
+ };
},
});
« 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