| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 cr.define('cr.ui', function() { | 5 cr.define('cr.ui', function() { |
| 6 /** | 6 /** |
| 7 * A class to manage focus between given horizontally arranged elements. | 7 * A class to manage focus between given horizontally arranged elements. |
| 8 * For example, given the page: | 8 * For example, given the page: |
| 9 * | 9 * |
| 10 * <input type="checkbox"> <label>Check me!</label> <button>X</button> | 10 * <input type="checkbox"> <label>Check me!</label> <button>X</button> |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 onKeydown: assertNotReached, | 47 onKeydown: assertNotReached, |
| 48 | 48 |
| 49 /** | 49 /** |
| 50 * @param {cr.ui.FocusRow} row The row that detected the mouse going down. | 50 * @param {cr.ui.FocusRow} row The row that detected the mouse going down. |
| 51 * @param {Event} e | 51 * @param {Event} e |
| 52 * @return {boolean} Whether the event was handled. | 52 * @return {boolean} Whether the event was handled. |
| 53 */ | 53 */ |
| 54 onMousedown: assertNotReached, | 54 onMousedown: assertNotReached, |
| 55 }; | 55 }; |
| 56 | 56 |
| 57 /** @const {string} */ |
| 58 FocusRow.ACTIVE_CLASS = 'focus-row-active'; |
| 59 |
| 57 FocusRow.prototype = { | 60 FocusRow.prototype = { |
| 58 __proto__: HTMLDivElement.prototype, | 61 __proto__: HTMLDivElement.prototype, |
| 59 | 62 |
| 60 /** | 63 /** |
| 61 * Should be called in the constructor to decorate |this|. | 64 * Should be called in the constructor to decorate |this|. |
| 62 * @param {Node} boundary Focus events are ignored outside of this node. | 65 * @param {Node} boundary Focus events are ignored outside of this node. |
| 63 * @param {cr.ui.FocusRow.Delegate=} opt_delegate A delegate to handle key | 66 * @param {cr.ui.FocusRow.Delegate=} opt_delegate A delegate to handle key |
| 64 * events. | 67 * events. |
| 65 */ | 68 */ |
| 66 decorate: function(boundary, opt_delegate) { | 69 decorate: function(boundary, opt_delegate) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 98 * Add an element to this FocusRow. No-op if |element| is not provided. | 101 * Add an element to this FocusRow. No-op if |element| is not provided. |
| 99 * @param {Element} element The element that should be added. | 102 * @param {Element} element The element that should be added. |
| 100 */ | 103 */ |
| 101 addFocusableElement: function(element) { | 104 addFocusableElement: function(element) { |
| 102 if (!element) | 105 if (!element) |
| 103 return; | 106 return; |
| 104 | 107 |
| 105 assert(this.focusableElements.indexOf(element) == -1); | 108 assert(this.focusableElements.indexOf(element) == -1); |
| 106 assert(this.contains(element)); | 109 assert(this.contains(element)); |
| 107 | 110 |
| 111 element.tabIndex = this.isActive() ? 0 : -1; |
| 112 |
| 108 this.focusableElements.push(element); | 113 this.focusableElements.push(element); |
| 109 this.eventTracker_.add(element, 'mousedown', | 114 this.eventTracker_.add(element, 'mousedown', |
| 110 this.onMousedown_.bind(this)); | 115 this.onMousedown_.bind(this)); |
| 111 }, | 116 }, |
| 112 | 117 |
| 113 /** | 118 /** |
| 114 * Called when focus changes to activate/deactivate the row. Focus is | 119 * Called when focus changes to activate/deactivate the row. Focus is |
| 115 * removed from the row when |element| is not in the FocusRow. | 120 * removed from the row when |element| is not in the FocusRow. |
| 116 * @param {Element} element The element that has focus. null if focus should | 121 * @param {Element} element The element that has focus. null if focus should |
| 117 * be removed. | 122 * be removed. |
| 118 * @private | 123 * @private |
| 119 */ | 124 */ |
| 120 onFocusChange_: function(element) { | 125 onFocusChange_: function(element) { |
| 121 var isActive = this.contains(element); | 126 this.makeActive(this.contains(element)); |
| 122 var wasActive = this.classList.contains('focus-row-active'); | 127 }, |
| 123 | 128 |
| 124 // Only send events if the active state is different for the row. | 129 /** @return {boolean} Whether this row is currently active. */ |
| 125 if (isActive != wasActive) | 130 isActive: function() { |
| 126 this.makeRowActive(isActive); | 131 return this.classList.contains(FocusRow.ACTIVE_CLASS); |
| 127 }, | 132 }, |
| 128 | 133 |
| 129 /** | 134 /** |
| 130 * Enables/disables the tabIndex of the focusable elements in the FocusRow. | 135 * Enables/disables the tabIndex of the focusable elements in the FocusRow. |
| 131 * tabIndex can be set properly. | 136 * tabIndex can be set properly. |
| 132 * @param {boolean} active True if tab is allowed for this row. | 137 * @param {boolean} active True if tab is allowed for this row. |
| 133 */ | 138 */ |
| 134 makeRowActive: function(active) { | 139 makeActive: function(active) { |
| 140 if (active == this.isActive()) |
| 141 return; |
| 142 |
| 135 this.focusableElements.forEach(function(element) { | 143 this.focusableElements.forEach(function(element) { |
| 136 element.tabIndex = active ? 0 : -1; | 144 element.tabIndex = active ? 0 : -1; |
| 137 }); | 145 }); |
| 138 | 146 |
| 139 this.classList.toggle('focus-row-active', active); | 147 this.classList.toggle(FocusRow.ACTIVE_CLASS, active); |
| 140 this.onActiveStateChanged(active); | 148 this.onActiveStateChanged(active); |
| 141 }, | 149 }, |
| 142 | 150 |
| 143 /** Call this to clean up event handling before dereferencing. */ | 151 /** Dereferences nodes and removes event handlers. */ |
| 144 destroy: function() { | 152 destroy: function() { |
| 153 this.focusableElements.length = 0; |
| 145 this.eventTracker_.removeAll(); | 154 this.eventTracker_.removeAll(); |
| 146 }, | 155 }, |
| 147 | 156 |
| 148 /** | 157 /** |
| 149 * @param {Event} e The focusin event. | 158 * @param {Event} e The focusin event. |
| 150 * @private | 159 * @private |
| 151 */ | 160 */ |
| 152 onFocusin_: function(e) { | 161 onFocusin_: function(e) { |
| 153 var target = assertInstanceof(e.target, Element); | 162 var target = assertInstanceof(e.target, Element); |
| 154 if (this.boundary_.contains(target)) | 163 if (this.boundary_.contains(target)) |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 200 // Focus this row if the target is one of the elements in this row. | 209 // Focus this row if the target is one of the elements in this row. |
| 201 this.onFocusChange_(assertInstanceof(e.target, Element)); | 210 this.onFocusChange_(assertInstanceof(e.target, Element)); |
| 202 } | 211 } |
| 203 }, | 212 }, |
| 204 }; | 213 }; |
| 205 | 214 |
| 206 return { | 215 return { |
| 207 FocusRow: FocusRow, | 216 FocusRow: FocusRow, |
| 208 }; | 217 }; |
| 209 }); | 218 }); |
| OLD | NEW |