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

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

Issue 2272553002: MD WebUI: Use arrow keys for navigation in cr-shared-menu, close on tab (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 4 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 | « ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.html ('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 /** Same as paper-menu-button's custom easing cubic-bezier param. */ 5 /** Same as paper-menu-button's custom easing cubic-bezier param. */
6 var SLIDE_CUBIC_BEZIER = 'cubic-bezier(0.3, 0.95, 0.5, 1)'; 6 var SLIDE_CUBIC_BEZIER = 'cubic-bezier(0.3, 0.95, 0.5, 1)';
7 7
8 Polymer({ 8 Polymer({
9 is: 'cr-shared-menu', 9 is: 'cr-shared-menu',
10 10
11 behaviors: [Polymer.IronA11yKeysBehavior],
12
13 properties: { 11 properties: {
14 menuOpen: { 12 menuOpen: {
15 type: Boolean, 13 type: Boolean,
16 observer: 'menuOpenChanged_', 14 observer: 'menuOpenChanged_',
17 value: false, 15 value: false,
18 notify: true, 16 notify: true,
19 }, 17 },
20 18
21 /** 19 /**
22 * The contextual item that this menu was clicked for. 20 * The contextual item that this menu was clicked for.
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
69 return [{ 67 return [{
70 name: 'fade-out-animation', 68 name: 'fade-out-animation',
71 timing: { 69 timing: {
72 duration: 150 70 duration: 150
73 } 71 }
74 }]; 72 }];
75 } 73 }
76 } 74 }
77 }, 75 },
78 76
79 keyBindings: {
80 'tab': 'onTabPressed_',
81 },
82
83 listeners: { 77 listeners: {
84 'dropdown.iron-overlay-canceled': 'onOverlayCanceled_', 78 'dropdown.iron-overlay-canceled': 'onOverlayCanceled_',
85 }, 79 },
86 80
87 /** 81 /**
88 * The last anchor that was used to open a menu. It's necessary for toggling. 82 * The last anchor that was used to open a menu. It's necessary for toggling.
89 * @private {?Element} 83 * @private {?Element}
90 */ 84 */
91 lastAnchor_: null, 85 lastAnchor_: null,
92 86
93 /** 87 /**
94 * The first focusable child in the menu's light DOM. 88 * The first focusable child in the menu's light DOM.
95 * @private {?Element} 89 * @private {?Element}
96 */ 90 */
97 firstFocus_: null, 91 firstFocus_: null,
98 92
99 /** 93 /**
100 * The last focusable child in the menu's light DOM. 94 * The last focusable child in the menu's light DOM.
101 * @private {?Element} 95 * @private {?Element}
102 */ 96 */
103 lastFocus_: null, 97 lastFocus_: null,
104 98
105 /** @override */ 99 /** @override */
106 attached: function() { 100 attached: function() {
107 window.addEventListener('resize', this.closeMenu.bind(this)); 101 window.addEventListener('resize', this.closeMenu.bind(this));
102 this.$.menu.addEventListener(
103 'keydown', this.onCaptureKeyDown_.bind(this), true);
108 }, 104 },
109 105
110 /** Closes the menu. */ 106 /** Closes the menu. */
111 closeMenu: function() { 107 closeMenu: function() {
112 if (this.root.activeElement == null) { 108 if (this.root.activeElement == null) {
113 // Something else has taken focus away from the menu. Do not attempt to 109 // Something else has taken focus away from the menu. Do not attempt to
114 // restore focus to the button which opened the menu. 110 // restore focus to the button which opened the menu.
115 this.$.dropdown.restoreFocusOnClose = false; 111 this.$.dropdown.restoreFocusOnClose = false;
116 } 112 }
117 this.menuOpen = false; 113 this.menuOpen = false;
118 }, 114 },
119 115
120 /** 116 /**
121 * Opens the menu at the anchor location. 117 * Opens the menu at the anchor location.
122 * @param {!Element} anchor The location to display the menu. 118 * @param {!Element} anchor The location to display the menu.
123 * @param {!Object} itemData The contextual item's data. 119 * @param {!Object} itemData The contextual item's data.
124 */ 120 */
125 openMenu: function(anchor, itemData) { 121 openMenu: function(anchor, itemData) {
126 if (this.lastAnchor_ == anchor && this.menuOpen) 122 if (this.lastAnchor_ == anchor && this.menuOpen)
127 return; 123 return;
128 124
129 if (this.menuOpen) 125 if (this.menuOpen)
130 this.closeMenu(); 126 this.closeMenu();
131 127
132 this.itemData = itemData; 128 this.itemData = itemData;
133 this.lastAnchor_ = anchor; 129 this.lastAnchor_ = anchor;
134 this.$.dropdown.restoreFocusOnClose = true; 130 this.$.dropdown.restoreFocusOnClose = true;
135 131 this.$.menu.selected = 0;
136 var focusableChildren = Polymer.dom(this).querySelectorAll(
137 '[tabindex]:not([hidden]),button:not([hidden])');
138 if (focusableChildren.length > 0) {
139 this.$.dropdown.focusTarget = focusableChildren[0];
140 this.firstFocus_ = focusableChildren[0];
141 this.lastFocus_ = focusableChildren[focusableChildren.length - 1];
142 }
143 132
144 // Move the menu to the anchor. 133 // Move the menu to the anchor.
145 this.$.dropdown.positionTarget = anchor; 134 this.$.dropdown.positionTarget = anchor;
146 this.menuOpen = true; 135 this.menuOpen = true;
147 }, 136 },
148 137
149 /** 138 /**
150 * Toggles the menu for the anchor that is passed in. 139 * Toggles the menu for the anchor that is passed in.
151 * @param {!Element} anchor The location to display the menu. 140 * @param {!Element} anchor The location to display the menu.
152 * @param {!Object} itemData The contextual item's data. 141 * @param {!Object} itemData The contextual item's data.
153 */ 142 */
154 toggleMenu: function(anchor, itemData) { 143 toggleMenu: function(anchor, itemData) {
155 if (anchor == this.lastAnchor_ && this.menuOpen) 144 if (anchor == this.lastAnchor_ && this.menuOpen)
156 this.closeMenu(); 145 this.closeMenu();
157 else 146 else
158 this.openMenu(anchor, itemData); 147 this.openMenu(anchor, itemData);
159 }, 148 },
160 149
161 /** 150 /**
162 * Trap focus inside the menu. As a very basic heuristic, will wrap focus from 151 * Close the menu when tab is pressed. Note that we must
163 * the first element with a nonzero tabindex to the last such element. 152 * explicitly add a capture event listener to do this as iron-menu-behavior
164 * TODO(tsergeant): Use iron-focus-wrap-behavior once it is available 153 * eats all key events during bubbling. See
165 * (https://github.com/PolymerElements/iron-overlay-behavior/issues/179). 154 * https://github.com/PolymerElements/iron-menu-behavior/issues/56.
166 * @param {CustomEvent} e 155 * @private
167 */ 156 */
168 onTabPressed_: function(e) { 157 onCaptureKeyDown_: function(e) {
169 if (!this.firstFocus_ || !this.lastFocus_) 158 if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(e, 'tab')) {
170 return; 159 this.closeMenu();
171 160 e.preventDefault();
Dan Beam 2016/08/23 05:50:40 er, wat? so you want to stop tab from changing foc
tsergeant 2016/08/23 07:08:40 It's complicated. I probably should put a comment
Dan Beam 2016/08/23 20:45:37 don't we want focus to go to the next focusable th
172 var toFocus; 161 }
173 var keyEvent = e.detail.keyboardEvent;
174 if (keyEvent.shiftKey && keyEvent.target == this.firstFocus_)
175 toFocus = this.lastFocus_;
176 else if (keyEvent.target == this.lastFocus_)
177 toFocus = this.firstFocus_;
178
179 if (!toFocus)
180 return;
181
182 e.preventDefault();
183 toFocus.focus();
184 }, 162 },
185 163
186 /** 164 /**
187 * Ensure the menu is reset properly when it is closed by the dropdown (eg, 165 * Ensure the menu is reset properly when it is closed by the dropdown (eg,
188 * clicking outside). 166 * clicking outside).
189 * @private 167 * @private
190 */ 168 */
191 menuOpenChanged_: function() { 169 menuOpenChanged_: function() {
192 if (!this.menuOpen) { 170 if (!this.menuOpen) {
193 this.itemData = null; 171 this.itemData = null;
194 this.lastAnchor_ = null; 172 this.lastAnchor_ = null;
195 } 173 }
196 }, 174 },
197 175
198 /** 176 /**
199 * Prevent focus restoring when tapping outside the menu. This stops the 177 * Prevent focus restoring when tapping outside the menu. This stops the
200 * focus moving around unexpectedly when closing the menu with the mouse. 178 * focus moving around unexpectedly when closing the menu with the mouse.
201 * @param {CustomEvent} e 179 * @param {CustomEvent} e
202 * @private 180 * @private
203 */ 181 */
204 onOverlayCanceled_: function(e) { 182 onOverlayCanceled_: function(e) {
205 if (e.detail.type == 'tap') 183 if (e.detail.type == 'tap')
206 this.$.dropdown.restoreFocusOnClose = false; 184 this.$.dropdown.restoreFocusOnClose = false;
207 }, 185 },
208 }); 186 });
OLDNEW
« no previous file with comments | « ui/webui/resources/cr_elements/cr_shared_menu/cr_shared_menu.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698