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 Polymer({ | 5 Polymer({ |
6 is: 'settings-action-menu', | 6 is: 'settings-action-menu', |
7 extends: 'dialog', | 7 extends: 'dialog', |
8 | 8 |
9 /** | 9 /** |
10 * List of all options in this action menu. | 10 * List of all options in this action menu. |
11 * @private {?NodeList<!Element>} | 11 * @private {?NodeList<!Element>} |
12 */ | 12 */ |
13 options_: null, | 13 options_: null, |
14 | 14 |
15 /** | 15 /** |
16 * Index of the currently focused item. | |
17 * @private {number} | |
18 */ | |
19 focusedIndex_: -1, | |
20 | |
21 /** | |
22 * Reference to the bound window's resize listener, such that it can be | 16 * Reference to the bound window's resize listener, such that it can be |
23 * removed on detach. | 17 * removed on detach. |
24 * @private {?Function} | 18 * @private {?Function} |
25 */ | 19 */ |
26 onWindowResize_: null, | 20 onWindowResize_: null, |
27 | 21 |
28 hostAttributes: { | 22 hostAttributes: { |
29 tabindex: 0, | 23 tabindex: 0, |
30 }, | 24 }, |
31 | 25 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
87 * disabled/hidden attributes, or null if no focusable option exists. | 81 * disabled/hidden attributes, or null if no focusable option exists. |
88 * @private | 82 * @private |
89 */ | 83 */ |
90 getNextOption_: function(step) { | 84 getNextOption_: function(step) { |
91 // Using a counter to ensure no infinite loop occurs if all elements are | 85 // Using a counter to ensure no infinite loop occurs if all elements are |
92 // hidden/disabled. | 86 // hidden/disabled. |
93 var counter = 0; | 87 var counter = 0; |
94 var nextOption = null; | 88 var nextOption = null; |
95 var numOptions = this.options_.length; | 89 var numOptions = this.options_.length; |
96 | 90 |
91 // Using |call| to call indexOf on a NodeList, without converting it to an | |
92 // Array. | |
Dan Beam
2016/10/25 00:56:10
don't really think this is a useful comment
dpapad
2016/10/25 01:27:51
Removed.
| |
93 var focusedIndex = Array.prototype.indexOf.call( | |
94 this.options_, this.root.activeElement); | |
95 | |
97 do { | 96 do { |
98 this.focusedIndex_ = | 97 focusedIndex = (numOptions + focusedIndex + step) % numOptions; |
99 (numOptions + this.focusedIndex_ + step) % numOptions; | 98 nextOption = this.options_[focusedIndex]; |
100 nextOption = this.options_[this.focusedIndex_]; | |
101 if (nextOption.disabled || nextOption.hidden) | 99 if (nextOption.disabled || nextOption.hidden) |
102 nextOption = null; | 100 nextOption = null; |
103 counter++; | 101 counter++; |
104 } while (!nextOption && counter < numOptions); | 102 } while (!nextOption && counter < numOptions); |
105 | 103 |
106 return nextOption; | 104 return nextOption; |
107 }, | 105 }, |
108 | 106 |
109 /** @override */ | 107 /** @override */ |
110 close: function() { | 108 close: function() { |
111 // Removing 'resize' listener when dialog is closed. | 109 // Removing 'resize' listener when dialog is closed. |
112 this.removeResizeListener_(); | 110 this.removeResizeListener_(); |
113 HTMLDialogElement.prototype.close.call(this); | 111 HTMLDialogElement.prototype.close.call(this); |
114 window.dispatchEvent(new CustomEvent('resize')); | 112 window.dispatchEvent(new CustomEvent('resize')); |
115 }, | 113 }, |
116 | 114 |
117 /** | 115 /** |
118 * Shows the menu anchored to the given element. | 116 * Shows the menu anchored to the given element. |
119 * @param {!Element} anchorElement | 117 * @param {!Element} anchorElement |
120 */ | 118 */ |
121 showAt: function(anchorElement) { | 119 showAt: function(anchorElement) { |
122 var rect = anchorElement.getBoundingClientRect(); | |
123 | |
124 // Ensure that the correct item is focused when the dialog is shown, by | |
125 // setting the 'autofocus' attribute. | |
126 this.focusedIndex_ = -1; | |
127 var nextOption = this.getNextOption_(1); | |
128 | |
129 /** @suppress {checkTypes} */ | |
130 (function(options) { | |
131 options.forEach(function(option) { | |
132 option.removeAttribute('autofocus'); | |
133 }); | |
134 })(this.options_); | |
135 | |
136 if (nextOption) | |
137 nextOption.setAttribute('autofocus', true); | |
138 | |
139 this.onWindowResize_ = this.onWindowResize_ || function() { | 120 this.onWindowResize_ = this.onWindowResize_ || function() { |
140 if (this.open) | 121 if (this.open) |
141 this.close(); | 122 this.close(); |
142 }.bind(this); | 123 }.bind(this); |
143 window.addEventListener('resize', this.onWindowResize_); | 124 window.addEventListener('resize', this.onWindowResize_); |
144 | 125 |
145 this.showModal(); | 126 this.showModal(); |
146 | 127 |
128 var rect = anchorElement.getBoundingClientRect(); | |
147 if (new settings.DirectionDelegateImpl().isRtl()) { | 129 if (new settings.DirectionDelegateImpl().isRtl()) { |
148 var right = window.innerWidth - rect.left - this.offsetWidth; | 130 var right = window.innerWidth - rect.left - this.offsetWidth; |
149 this.style.right = right + 'px'; | 131 this.style.right = right + 'px'; |
150 } else { | 132 } else { |
151 var left = rect.right - this.offsetWidth; | 133 var left = rect.right - this.offsetWidth; |
152 this.style.left = left + 'px'; | 134 this.style.left = left + 'px'; |
153 } | 135 } |
154 | 136 |
155 // Attempt to show the menu starting from the top of the rectangle and | 137 // Attempt to show the menu starting from the top of the rectangle and |
156 // extending downwards. If that does not fit within the window, fallback to | 138 // extending downwards. If that does not fit within the window, fallback to |
157 // starting from the bottom and extending upwards. | 139 // starting from the bottom and extending upwards. |
158 var top = rect.top + this.offsetHeight <= window.innerHeight ? | 140 var top = rect.top + this.offsetHeight <= window.innerHeight ? |
159 rect.top : | 141 rect.top : |
160 rect.bottom - this.offsetHeight - Math.max( | 142 rect.bottom - this.offsetHeight - Math.max( |
161 rect.bottom - window.innerHeight, 0); | 143 rect.bottom - window.innerHeight, 0); |
162 | 144 |
163 this.style.top = top + 'px'; | 145 this.style.top = top + 'px'; |
164 }, | 146 }, |
165 }); | 147 }); |
OLD | NEW |