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

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

Issue 2814743007: [cr-action-menu] Allow configurable anchors. (Closed)
Patch Set: rebase, address comments 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 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 /**
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
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 });
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