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

Unified Diff: ui/webui/resources/js/cr/ui/focus_row.js

Issue 807593005: Make downloads list keyboard shortcuts more consistent. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: nit Created 5 years, 11 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 side-by-side diff with in-line comments
Download patch
Index: ui/webui/resources/js/cr/ui/focus_row.js
diff --git a/ui/webui/resources/js/cr/ui/focus_row.js b/ui/webui/resources/js/cr/ui/focus_row.js
index 26c4c5567e2b673f24b19574c2b35afe6c69263f..08c068a0e7c02d6f2104241ef2ca4b31273f6e34 100644
--- a/ui/webui/resources/js/cr/ui/focus_row.js
+++ b/ui/webui/resources/js/cr/ui/focus_row.js
@@ -11,11 +11,13 @@ cr.define('cr.ui', function() {
*
* One could create a FocusRow by doing:
*
- * new cr.ui.FocusRow([checkboxEl, labelEl, buttonEl])
+ * var focusRow = new cr.ui.FocusRow(rowBoundary, rowEl);
*
- * if there are references to each node or querying them from the DOM like so:
+ * focusRow.addFocusableElement(checkboxEl);
+ * focusRow.addFocusableElement(labelEl);
+ * focusRow.addFocusableElement(buttonEl);
*
- * new cr.ui.FocusRow(dialog.querySelectorAll('list input[type=checkbox]'))
+ * focusRow.setInitialFocusability(true);
*
* Pressing left cycles backward and pressing right cycles forward in item
* order. Pressing Home goes to the beginning of the list and End goes to the
@@ -23,48 +25,18 @@ cr.define('cr.ui', function() {
*
* If an item in this row is focused, it'll stay active (accessible via tab).
* If no items in this row are focused, the row can stay active until focus
- * changes to a node inside |this.boundary_|. If opt_boundary isn't
- * specified, any focus change deactivates the row.
+ * changes to a node inside |this.boundary_|. If |boundary| isn't specified,
+ * any focus change deactivates the row.
*
- * @param {!Array.<!Element>|!NodeList} items Elements to track focus of.
- * @param {Node=} opt_boundary Focus events are ignored outside of this node.
+ * @param {Element} rowElement The element representing this row.
+ * @param {Node} boundary Focus events are ignored outside of this node.
* @param {FocusRow.Delegate=} opt_delegate A delegate to handle key events.
- * @param {FocusRow.Observer=} opt_observer An observer that's notified if
- * this focus row is added to or removed from the focus order.
* @constructor
*/
- function FocusRow(items, opt_boundary, opt_delegate, opt_observer) {
- /** @type {!Array.<!Element>} */
- this.items = Array.prototype.slice.call(items);
- assert(this.items.length > 0);
-
- /** @type {!Node} */
- this.boundary_ = opt_boundary || document;
-
- /** @private {cr.ui.FocusRow.Delegate|undefined} */
- this.delegate_ = opt_delegate;
-
- /** @private {cr.ui.FocusRow.Observer|undefined} */
- this.observer_ = opt_observer;
-
- /** @private {!EventTracker} */
- this.eventTracker_ = new EventTracker;
- this.eventTracker_.add(cr.doc, 'focusin', this.onFocusin_.bind(this));
- this.eventTracker_.add(cr.doc, 'keydown', this.onKeydown_.bind(this));
-
- this.items.forEach(function(item) {
- if (item != document.activeElement)
- item.tabIndex = -1;
-
- this.eventTracker_.add(item, 'mousedown', this.onMousedown_.bind(this));
- }, this);
-
- /**
- * The index that should be actively participating in the page tab order.
- * @type {number}
- * @private
- */
- this.activeIndex_ = this.items.indexOf(document.activeElement);
+ function FocusRow(rowElement, boundary, opt_delegate) {
+ rowElement.__proto__ = FocusRow.prototype;
+ rowElement.decorate(boundary, opt_delegate);
+ return rowElement;
}
/** @interface */
@@ -72,8 +44,8 @@ cr.define('cr.ui', function() {
FocusRow.Delegate.prototype = {
/**
- * Called when a key is pressed while an item in |this.items| is focused. If
- * |e|'s default is prevented, further processing is skipped.
+ * Called when a key is pressed while an item in |this.focusableElements| is
+ * focused. If |e|'s default is prevented, further processing is skipped.
* @param {cr.ui.FocusRow} row The row that detected a keydown.
* @param {Event} e
* @return {boolean} Whether the event was handled.
@@ -88,58 +60,91 @@ cr.define('cr.ui', function() {
onMousedown: assertNotReached,
};
- /** @interface */
- FocusRow.Observer = function() {};
+ FocusRow.prototype = {
+ __proto__: HTMLDivElement.prototype,
- FocusRow.Observer.prototype = {
/**
- * Called when the row is activated (added to the focus order).
- * @param {cr.ui.FocusRow} row The row added to the focus order.
+ * Should be called in the constructor to decorate |this|.
+ * @param {Node} boundary Focus events are ignored outside of this node.
+ * @param {FocusRow.Delegate=} opt_delegate A delegate to handle key events.
*/
- onActivate: assertNotReached,
+ decorate: function(boundary, opt_delegate) {
+ /** @type {!Node} */
+ this.boundary_ = boundary || document;
- /**
- * Called when the row is deactivated (removed from the focus order).
- * @param {cr.ui.FocusRow} row The row removed from the focus order.
- */
- onDeactivate: assertNotReached,
- };
+ /** @type {cr.ui.FocusRow.Delegate|undefined} */
+ this.delegate = opt_delegate;
- FocusRow.prototype = {
- get activeIndex() {
- return this.activeIndex_;
+ /** @type {Array<Element>} */
+ this.focusableElements = [];
+
+ /** @private {!EventTracker} */
+ this.eventTracker_ = new EventTracker;
+ this.eventTracker_.add(cr.doc, 'focusin', this.onFocusin_.bind(this));
+ this.eventTracker_.add(cr.doc, 'keydown', this.onKeydown_.bind(this));
},
- set activeIndex(index) {
- var wasActive = this.items[this.activeIndex_];
- if (wasActive)
- wasActive.tabIndex = -1;
- this.items.forEach(function(item) { assert(item.tabIndex == -1); });
- this.activeIndex_ = index;
- if (this.items[index])
- this.items[index].tabIndex = 0;
+ /**
+ * Called when the row's active state changes and it is added/removed from
+ * the focus order.
+ * @param {boolean} state Whether the row has become active or inactive.
+ */
+ onActiveStateChanged: function(state) {},
- if (!this.observer_)
- return;
+ /**
+ * Find the element that best matches |sampleElement|. This function should
+ * NOT return null or undefined.
Dan Beam 2015/01/24 02:37:08 the "should not return null or undefined" is dupli
hcarmona 2015/01/27 18:30:40 Done.
+ * @param {Element} sampleElement An element from a row of the same type
+ * which previously held focus.
+ * @return {!Element} The element that best matches sampleElement.
+ */
+ getEquivalentElement: assertNotReached,
- var isActive = index >= 0 && index < this.items.length;
- if (isActive == !!wasActive)
+ /**
+ * Add an element to this FocusRow. No-op if |element| is not provided.
+ * @param {Element} element The element that should be added.
+ */
+ addFocusableElement: function(element) {
+ if (!element)
return;
- if (isActive)
- this.observer_.onActivate(this);
- else
- this.observer_.onDeactivate(this);
+ assert(this.focusableElements.indexOf(element) == -1);
+ assert(this.contains(element));
+
+ this.focusableElements.push(element);
+ this.eventTracker_.add(element, 'mousedown',
+ this.onMousedown_.bind(this));
+ },
+
+ /**
+ * Called when focus changes to activate/deactivate the row. Focus is
+ * removed from the row when |element| is not in the FocusRow.
+ * @param {Element} element The element that has focus. null if focus should
+ * be removed.
+ * @private
+ */
+ onFocusChange_: function(element) {
+ var isActive = this.contains(element);
+ var wasActive = this.classList.contains('focus-row-active');
+
+ // Only send events if the active state is different for the row.
+ if (isActive != wasActive)
+ this.makeRowActive(isActive);
},
/**
- * Focuses the item at |index|.
- * @param {number} index An index to focus. Must be between 0 and
- * this.items.length - 1.
+ * Enables/disables the tabIndex of the focusable elements in the FocusRow.
+ * tabIndex can be set properly.
+ * @param {boolean} active True if tab is allowed for this row.
*/
- focusIndex: function(index) {
- this.items[index].focus();
+ makeRowActive: function(active) {
+ this.focusableElements.forEach(function(element) {
+ element.tabIndex = active ? 0 : -1;
+ });
+
+ this.classList.toggle('focus-row-active', active);
+ this.onActiveStateChanged(active);
},
/** Call this to clean up event handling before dereferencing. */
@@ -153,37 +158,38 @@ cr.define('cr.ui', function() {
*/
onFocusin_: function(e) {
if (this.boundary_.contains(assertInstanceof(e.target, Node)))
- this.activeIndex = this.items.indexOf(e.target);
+ this.onFocusChange_(e.target);
},
/**
- * @param {Event} e A focus event.
+ * Handles a keypress for an element in this FocusRow.
+ * @param {Event} e The keydown event.
* @private
*/
onKeydown_: function(e) {
- var item = this.items.indexOf(e.target);
- if (item < 0)
+ if (!this.contains(e.target))
return;
- if (this.delegate_ && this.delegate_.onKeydown(this, e))
+ if (this.delegate && this.delegate.onKeydown(this, e))
return;
+ var elementIndex = this.focusableElements.indexOf(e.target);
var index = -1;
if (e.keyIdentifier == 'Left')
- index = item + (isRTL() ? 1 : -1);
+ index = elementIndex + (isRTL() ? 1 : -1);
else if (e.keyIdentifier == 'Right')
- index = item + (isRTL() ? -1 : 1);
+ index = elementIndex + (isRTL() ? -1 : 1);
else if (e.keyIdentifier == 'Home')
index = 0;
else if (e.keyIdentifier == 'End')
- index = this.items.length - 1;
-
- if (!this.items[index])
- return;
+ index = this.focusableElements.length - 1;
- this.focusIndex(index);
- e.preventDefault();
+ var elementToFocus = this.focusableElements[index];
+ if (elementToFocus) {
+ this.getEquivalentElement(elementToFocus).focus();
+ e.preventDefault();
+ }
},
/**
@@ -191,11 +197,14 @@ cr.define('cr.ui', function() {
* @private
*/
onMousedown_: function(e) {
- if (this.delegate_ && this.delegate_.onMousedown(this, e))
+ if (this.delegate && this.delegate.onMousedown(this, e))
return;
- if (!e.button)
- this.activeIndex = this.items.indexOf(e.currentTarget);
+ // Only accept the left mouse click.
+ if (!e.button) {
+ // Focus this row if the target is one of the elements in this row.
+ this.onFocusChange_(e.target);
+ }
},
};
« chrome/test/data/webui/history_browsertest.js ('K') | « ui/webui/resources/js/cr/ui/focus_grid.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698