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

Side by Side Diff: chrome/browser/resources/settings/settings_action_menu.js

Issue 2402553002: MD Settings: Implementing modal popup/action menus. (Closed)
Patch Set: Address comments. Created 4 years, 2 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 Polymer({
6 is: 'settings-action-menu',
7 extends: 'dialog',
8
9 /**
10 * List of all options in this action menu.
11 * @private {?NodeList<!Element>}
12 */
13 options_: null,
14
15 /**
16 * Index of the currently focused item.
17 * @private {number}
18 */
19 focusedIndex_: 0,
20
21 /**
22 * Reference to the bound window's resize listener, such that it can be
23 * removed on detach.
24 * @private {?Function}
25 */
26 onWindowResize_: null,
27
28 /** override */
29 attached: function() {
30 this.setAttribute('tabindex', 0);
Dan Beam 2016/10/13 05:03:01 nit: hostAttributes: { tabindex: 0, },
dpapad 2016/10/13 18:18:01 Done.
31 this.options_ = this.querySelectorAll('.dropdown-item');
32
33 this.onWindowResize_ = function() {
34 if (this.open)
35 this.close();
36 }.bind(this);
37
38 window.addEventListener('resize', this.onWindowResize_);
39 },
40
41 /** override */
42 detached: function() {
43 window.removeEventListener('resize', this.onWindowResize_);
44 },
45
46 /** @override */
47 ready: function() {
48 this.addEventListener('click', function(e) {
Dan Beam 2016/10/13 05:03:01 do you need to remove these events on detached as
dpapad 2016/10/13 18:18:01 I don't think so. Firstly because ready() is only
49 if (e.target == this) {
50 this.close();
51 e.stopPropagation();
52 }
53 }.bind(this));
54
55 this.addEventListener('keydown', function(e) {
56 if (e.key == 'Tab') {
57 this.close();
58 return;
59 }
60
61 if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp')
62 return;
63
64 var nextOption = this.getNextOption_(e.key == 'ArrowDown' ? 1 : - 1);
65 if (nextOption)
66 nextOption.focus();
67
68 e.preventDefault();
69 }.bind(this));
70 },
71
72 /**
73 * @param {number} step -1 for getting previous option (up), 1 for getting
74 * next option (down).
75 * @return {?HTMLElement} The next focusable option, taking into account
76 * disabled/hidden attributes, or null if no focusable option exists.
77 * @private
78 */
79 getNextOption_: function(step) {
80 // Storing the focused index before searching, to avoid infinite loop if all
81 // options are disabled.
82 var previousFocusedIndex = this.focusedIndex_;
83
84 var getNextOptionRec_ = function() {
85 // Advance index.
86 this.focusedIndex_ = (this.focusedIndex_ + step) % this.options_.length;
87 if (this.focusedIndex_ == -1)
88 this.focusedIndex_ = this.options_.length - 1;
Dan Beam 2016/10/13 05:03:01 var numOptions = this.options_.length; this.focuse
dpapad 2016/10/13 18:18:00 Done.
89
90 // Adjust index in case item is disabled/hidden.
91 var nextOption = this.options_[this.focusedIndex_];
92 if (nextOption.disabled || nextOption.hidden) {
93 return this.focusedIndex_ != previousFocusedIndex ?
94 getNextOptionRec_() : null;
95 } else {
Dan Beam 2016/10/13 05:03:01 don't use else after return
dpapad 2016/10/13 18:18:01 Done.
96 return this.options_[this.focusedIndex_];
97 }
98 }.bind(this);
99
100 return getNextOptionRec_();
Dan Beam 2016/10/13 05:03:01 var enabled = Array.from(this.options_).filter(fun
dpapad 2016/10/13 18:18:01 Getting a filtered array of the enabled elements f
dpapad 2016/10/13 18:58:25 Implemented a non-recursive version in latest patc
101 },
102
103 /**
104 * Shows the menu anchored to the given rectangle.
105 * @param {!ClientRect} rect
Dan Beam 2016/10/13 05:03:01 can this just take an element instead of a clientr
Dan Beam 2016/10/13 19:01:24 ping
106 */
107 showAtLocation: function(rect) {
108 // Ensure that the correct item is focused when the dialog is shown, by
109 // setting the 'autofocus' attribute.
110 this.focusedIndex_ = 0;
Dan Beam 2016/10/13 05:03:01 can we just start this at -1 and call getNextOptio
dpapad 2016/10/13 18:18:01 Done.
111 var nextOption = this.options_[this.focusedIndex_];
112 if (nextOption.disabled || nextOption.hidden)
113 nextOption = this.getNextOption_(1);
114 for (var option of this.options_)
115 option.removeAttribute('autofocus');
116 if (nextOption !== null)
117 nextOption.setAttribute('autofocus', true);
118
119 this.showModal();
120
121 var directionDelegate = new settings.DirectionDelegateImpl();
122 if (directionDelegate.isRtl()) {
Dan Beam 2016/10/13 05:03:01 maybe this? if (new settings.DirectionDelegateImp
dpapad 2016/10/13 18:18:01 Done.
123 var right = window.innerWidth - rect.left - this.offsetWidth;
124 this.style.right = right + 'px';
125 } else {
126 var left = rect.right - this.offsetWidth;
127 this.style.left = left + 'px';
128 }
129
130 // Attempt to show the menu starting from the top of the rectangle and
131 // extending downwards. If that does not fit within the window, fallback to
132 // starting from the bottom and extending upwards.
133 var top = rect.top + this.offsetHeight <= window.innerHeight ?
134 rect.top :
135 rect.bottom - this.offsetHeight - Math.max(
136 rect.bottom - window.innerHeight, 0);
137
138 this.style.top = top + 'px';
139 },
140 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698