Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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('options', function() { | 5 cr.define('options', function() { |
| 6 /** @const */ var DeletableItem = options.DeletableItem; | 6 /** @const */ var DeletableItem = options.DeletableItem; |
| 7 /** @const */ var DeletableItemList = options.DeletableItemList; | 7 /** @const */ var DeletableItemList = options.DeletableItemList; |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * Creates a new list item with support for inline editing. | 10 * Creates a new list item with support for inline editing. |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 81 selectionChanged: function() { | 81 selectionChanged: function() { |
| 82 this.updateEditState(); | 82 this.updateEditState(); |
| 83 }, | 83 }, |
| 84 | 84 |
| 85 /** | 85 /** |
| 86 * Called when this element gains or loses 'lead' status. Updates editing | 86 * Called when this element gains or loses 'lead' status. Updates editing |
| 87 * mode accordingly. | 87 * mode accordingly. |
| 88 * @private | 88 * @private |
| 89 */ | 89 */ |
| 90 handleLeadChange_: function() { | 90 handleLeadChange_: function() { |
| 91 // Add focusability before call to updateEditState. | |
| 92 if (this.lead) { | |
| 93 this.setEditableValuesFocusable(true); | |
| 94 this.setCloseButtonFocusable(true); | |
| 95 } | |
| 96 | |
| 91 this.updateEditState(); | 97 this.updateEditState(); |
| 98 | |
| 99 // Remove focusability after call to updateEditState. | |
| 100 this.setStaticValuesFocusable(false); | |
| 101 if (!this.lead) { | |
| 102 this.setEditableValuesFocusable(false); | |
| 103 this.setCloseButtonFocusable(false); | |
| 104 } | |
| 92 }, | 105 }, |
| 93 | 106 |
| 94 /** | 107 /** |
| 95 * Updates the edit state based on the current selected and lead states. | 108 * Updates the edit state based on the current selected and lead states. |
| 96 */ | 109 */ |
| 97 updateEditState: function() { | 110 updateEditState: function() { |
| 98 if (this.editable) | 111 if (this.editable) |
| 99 this.editing = this.selected && this.lead; | 112 this.editing = this.selected && this.lead; |
| 100 }, | 113 }, |
| 101 | 114 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 115 else | 128 else |
| 116 this.removeAttribute('editing'); | 129 this.removeAttribute('editing'); |
| 117 | 130 |
| 118 if (editing) { | 131 if (editing) { |
| 119 this.editCancelled_ = false; | 132 this.editCancelled_ = false; |
| 120 | 133 |
| 121 cr.dispatchSimpleEvent(this, 'edit', true); | 134 cr.dispatchSimpleEvent(this, 'edit', true); |
| 122 | 135 |
| 123 var focusElement = this.editClickTarget_ || this.initialFocusElement; | 136 var focusElement = this.editClickTarget_ || this.initialFocusElement; |
| 124 this.editClickTarget_ = null; | 137 this.editClickTarget_ = null; |
| 125 | 138 if (focusElement) |
| 126 if (focusElement) { | 139 this.focusAndMaybeSelect_(focusElement); |
| 127 var self = this; | |
| 128 // We should delay to give focus on |focusElement| if this is called | |
| 129 // in mousedown event handler. If we did give focus immediately, Blink | |
| 130 // would try to focus on an ancestor of the mousedown target element, | |
| 131 // and remove focus from |focusElement|. | |
| 132 if (focusElement.staticVersion && | |
| 133 focusElement.staticVersion.hasAttribute('tabindex')) { | |
| 134 setTimeout(function() { | |
| 135 if (self.editing) { | |
| 136 if (focusElement.disabled) | |
| 137 self.parentNode.focus(); | |
| 138 self.focusAndMaybeSelect_(focusElement); | |
| 139 } | |
| 140 focusElement.staticVersion.removeAttribute('tabindex'); | |
| 141 }, 0); | |
| 142 } else { | |
| 143 this.focusAndMaybeSelect_(focusElement); | |
| 144 } | |
| 145 } | |
| 146 } else { | 140 } else { |
| 147 if (!this.editCancelled_ && this.hasBeenEdited && | 141 if (!this.editCancelled_ && this.hasBeenEdited && |
| 148 this.currentInputIsValid) { | 142 this.currentInputIsValid) { |
| 149 if (this.isPlaceholder) | 143 if (this.isPlaceholder) |
| 150 this.parentNode.focusPlaceholder = true; | 144 this.parentNode.focusPlaceholder = true; |
| 151 | 145 |
| 152 this.updateStaticValues_(); | 146 this.updateStaticValues_(); |
| 153 cr.dispatchSimpleEvent(this, 'commitedit', true); | 147 cr.dispatchSimpleEvent(this, 'commitedit', true); |
| 154 } else { | 148 } else { |
| 155 this.resetEditableValues_(); | 149 this.resetEditableValues_(); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 222 * Returns true if the item has been changed by an edit. | 216 * Returns true if the item has been changed by an edit. |
| 223 * Can be overridden by subclasses to return false when nothing has changed | 217 * Can be overridden by subclasses to return false when nothing has changed |
| 224 * to avoid unnecessary commits. | 218 * to avoid unnecessary commits. |
| 225 * @type {boolean} | 219 * @type {boolean} |
| 226 */ | 220 */ |
| 227 get hasBeenEdited() { | 221 get hasBeenEdited() { |
| 228 return true; | 222 return true; |
| 229 }, | 223 }, |
| 230 | 224 |
| 231 /** | 225 /** |
| 226 * Sets whether the editable values can be given focus using the keyboard. | |
| 227 * @param {boolean} focusable The desired focusable state. | |
| 228 */ | |
| 229 setEditableValuesFocusable: function(focusable) { | |
| 230 var editFields = this.editFields_; | |
| 231 for (var i = 0; i < editFields.length; i++) { | |
| 232 if (this.editable) { | |
| 233 editFields[i].tabIndex = focusable && !editFields[i].disabled ? | |
| 234 0 : -1; | |
| 235 } else { | |
| 236 editFields[i].removeAttribute('tabindex'); | |
|
bondd
2014/10/29 23:08:54
Re-added
removeAttribute('tabindex');
because I fo
Dan Beam
2014/10/29 23:30:13
maybe this method should just ignore if editFields
bondd
2014/10/30 01:25:32
I put this back to the way it was in patch set #2.
| |
| 237 } | |
| 238 } | |
| 239 }, | |
| 240 | |
| 241 /** | |
| 242 * Sets whether the static values can be given focus using the keyboard. | |
| 243 * @param {boolean} focusable The desired focusable state. | |
| 244 */ | |
| 245 setStaticValuesFocusable: function(focusable) { | |
| 246 var editFields = this.editFields_; | |
| 247 for (var i = 0; i < editFields.length; i++) { | |
| 248 var staticVersion = editFields[i].staticVersion; | |
| 249 if (staticVersion) { | |
| 250 if (this.editable) { | |
| 251 staticVersion.tabIndex = focusable && !staticVersion.disabled ? | |
| 252 0 : -1; | |
| 253 } else { | |
| 254 staticVersion.removeAttribute('tabindex'); | |
| 255 } | |
| 256 } | |
| 257 } | |
| 258 }, | |
| 259 | |
| 260 /** | |
| 261 * Sets whether the close button can be focused using the keyboard. | |
| 262 * | |
| 263 * @param {boolean} focusable The desired focusable state. | |
| 264 */ | |
| 265 setCloseButtonFocusable: function(focusable) { | |
| 266 this.closeButtonElement.tabIndex = | |
| 267 focusable && this.closeButtonFocusAllowed ? 0 : -1; | |
| 268 }, | |
| 269 | |
| 270 /** | |
| 232 * Returns a div containing an <input>, as well as static text if | 271 * Returns a div containing an <input>, as well as static text if |
| 233 * isPlaceholder is not true. | 272 * isPlaceholder is not true. |
| 234 * @param {string} text The text of the cell. | 273 * @param {string} text The text of the cell. |
| 235 * @return {HTMLElement} The HTML element for the cell. | 274 * @return {HTMLElement} The HTML element for the cell. |
| 236 * @private | 275 * @private |
| 237 */ | 276 */ |
| 238 createEditableTextCell: function(text) { | 277 createEditableTextCell: function(text) { |
| 239 var container = /** @type {HTMLElement} */( | 278 var container = /** @type {HTMLElement} */( |
| 240 this.ownerDocument.createElement('div')); | 279 this.ownerDocument.createElement('div')); |
| 241 var textEl = null; | 280 var textEl = null; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 261 if (list && list.focusPlaceholder) { | 300 if (list && list.focusPlaceholder) { |
| 262 list.focusPlaceholder = false; | 301 list.focusPlaceholder = false; |
| 263 if (list.shouldFocusPlaceholder()) | 302 if (list.shouldFocusPlaceholder()) |
| 264 inputEl.focus(); | 303 inputEl.focus(); |
| 265 } | 304 } |
| 266 }, 50); | 305 }, 50); |
| 267 } | 306 } |
| 268 | 307 |
| 269 // In some cases 'focus' event may arrive before 'input'. | 308 // In some cases 'focus' event may arrive before 'input'. |
| 270 // To make sure revalidation is triggered we postpone 'focus' handling. | 309 // To make sure revalidation is triggered we postpone 'focus' handling. |
| 271 var handler = this.handleFocus_.bind(this); | 310 var handler = this.handleFocus.bind(this); |
| 272 inputEl.addEventListener('focus', function() { | 311 inputEl.addEventListener('focus', function() { |
| 273 window.setTimeout(function() { | 312 window.setTimeout(function() { |
| 274 if (inputEl.ownerDocument.activeElement == inputEl) | 313 if (inputEl.ownerDocument.activeElement == inputEl) |
| 275 handler(); | 314 handler(); |
| 276 }, 0); | 315 }, 0); |
| 277 }); | 316 }); |
| 278 container.appendChild(inputEl); | 317 container.appendChild(inputEl); |
| 279 this.addEditField(inputEl, textEl); | 318 this.addEditField(inputEl, textEl); |
| 280 | 319 |
| 281 return container; | 320 return container; |
| 282 }, | 321 }, |
| 283 | 322 |
| 284 /** | 323 /** |
| 285 * Register an edit field. | 324 * Register an edit field. |
| 286 * @param {!Element} control An editable element. It's a form control | 325 * @param {!Element} control An editable element. It's a form control |
| 287 * element typically. | 326 * element typically. |
| 288 * @param {Element} staticElement An element representing non-editable | 327 * @param {Element} staticElement An element representing non-editable |
| 289 * state. | 328 * state. |
| 290 */ | 329 */ |
| 291 addEditField: function(control, staticElement) { | 330 addEditField: function(control, staticElement) { |
| 292 control.staticVersion = staticElement; | 331 control.staticVersion = staticElement; |
| 332 if (this.editable) | |
| 333 control.tabIndex = -1; | |
| 334 | |
| 335 if (control.staticVersion) { | |
| 336 if (this.editable) | |
| 337 control.staticVersion.tabIndex = -1; | |
| 338 control.staticVersion.editableVersion = control; | |
| 339 control.staticVersion.addEventListener('focus', | |
| 340 this.handleFocus.bind(this)); | |
| 341 } | |
| 293 this.editFields_.push(control); | 342 this.editFields_.push(control); |
| 294 }, | 343 }, |
| 295 | 344 |
| 296 /** | 345 /** |
| 297 * Resets the editable version of any controls created by createEditable* | 346 * Resets the editable version of any controls created by createEditable* |
| 298 * to match the static text. | 347 * to match the static text. |
| 299 * @private | 348 * @private |
| 300 */ | 349 */ |
| 301 resetEditableValues_: function() { | 350 resetEditableValues_: function() { |
| 302 var editFields = this.editFields_; | 351 var editFields = this.editFields_; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 368 } | 417 } |
| 369 }, | 418 }, |
| 370 | 419 |
| 371 /** | 420 /** |
| 372 * Called when the list item is clicked. If the click target corresponds to | 421 * Called when the list item is clicked. If the click target corresponds to |
| 373 * an editable item, stores that item to focus when edit mode is started. | 422 * an editable item, stores that item to focus when edit mode is started. |
| 374 * @param {Event} e The mouse down event. | 423 * @param {Event} e The mouse down event. |
| 375 * @private | 424 * @private |
| 376 */ | 425 */ |
| 377 handleMouseDown_: function(e) { | 426 handleMouseDown_: function(e) { |
| 378 if (!this.editable || this.editing) | 427 if (!this.editable) |
| 379 return; | 428 return; |
| 380 | 429 |
| 381 var clickTarget = e.target; | 430 var clickTarget = e.target; |
| 382 var editFields = this.editFields_; | 431 var editFields = this.editFields_; |
| 432 var editClickTarget; | |
| 383 for (var i = 0; i < editFields.length; i++) { | 433 for (var i = 0; i < editFields.length; i++) { |
| 384 if (editFields[i].staticVersion == clickTarget) | |
| 385 clickTarget.tabIndex = 0; | |
| 386 if (editFields[i] == clickTarget || | 434 if (editFields[i] == clickTarget || |
| 387 editFields[i].staticVersion == clickTarget) { | 435 editFields[i].staticVersion == clickTarget) { |
| 388 this.editClickTarget_ = editFields[i]; | 436 editClickTarget = editFields[i]; |
| 389 return; | 437 break; |
| 390 } | 438 } |
| 391 } | 439 } |
| 440 | |
| 441 if (this.editing) { | |
| 442 if (!editClickTarget) { | |
| 443 // Clicked on the list item outside of an edit field. Don't lose focus | |
| 444 // from currently selected edit field. | |
| 445 e.stopPropagation(); | |
| 446 e.preventDefault(); | |
| 447 } | |
| 448 return; | |
| 449 } | |
| 450 | |
| 451 if (editClickTarget && !editClickTarget.disabled) | |
| 452 this.editClickTarget_ = editClickTarget; | |
| 392 }, | 453 }, |
| 393 }; | 454 }; |
| 394 | 455 |
| 395 /** | 456 /** |
| 396 * Takes care of committing changes to inline editable list items when the | 457 * Takes care of committing changes to inline editable list items when the |
| 397 * window loses focus. | 458 * window loses focus. |
| 398 */ | 459 */ |
| 399 function handleWindowBlurs() { | 460 function handleWindowBlurs() { |
| 400 window.addEventListener('blur', function(e) { | 461 window.addEventListener('blur', function(e) { |
| 401 var itemAncestor = findAncestor(document.activeElement, function(node) { | 462 var itemAncestor = findAncestor(document.activeElement, function(node) { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 | 496 |
| 436 /** | 497 /** |
| 437 * Called when the list hierarchy as a whole loses or gains focus; starts | 498 * Called when the list hierarchy as a whole loses or gains focus; starts |
| 438 * or ends editing for the lead item if necessary. | 499 * or ends editing for the lead item if necessary. |
| 439 * @param {Event} e The change event. | 500 * @param {Event} e The change event. |
| 440 * @private | 501 * @private |
| 441 */ | 502 */ |
| 442 handleListFocusChange_: function(e) { | 503 handleListFocusChange_: function(e) { |
| 443 var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex); | 504 var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex); |
| 444 if (leadItem) { | 505 if (leadItem) { |
| 445 if (e.newValue) | 506 if (e.newValue) { |
| 507 // Add focusability before making other changes. | |
| 508 leadItem.setEditableValuesFocusable(true); | |
| 509 leadItem.setCloseButtonFocusable(true); | |
| 446 leadItem.updateEditState(); | 510 leadItem.updateEditState(); |
| 447 else | 511 // Remove focusability after making other changes. |
| 512 leadItem.setStaticValuesFocusable(false); | |
| 513 } else { | |
| 514 // Add focusability before making other changes. | |
| 515 leadItem.setStaticValuesFocusable(true); | |
| 516 leadItem.setCloseButtonFocusable(true); | |
| 448 leadItem.editing = false; | 517 leadItem.editing = false; |
| 518 // Remove focusability after making other changes. | |
| 519 if (!leadItem.isPlaceholder) | |
| 520 leadItem.setEditableValuesFocusable(false); | |
| 521 } | |
| 449 } | 522 } |
| 450 }, | 523 }, |
| 451 | 524 |
| 525 /** | |
| 526 * Called after the DataModel for the list has been set. | |
| 527 * @override | |
| 528 */ | |
| 529 onSetDataModelComplete: function() { | |
| 530 DeletableItemList.prototype.onSetDataModelComplete.call(this); | |
| 531 | |
| 532 var firstItem = this.getListItemByIndex(0); | |
| 533 if (firstItem) { | |
| 534 firstItem.setStaticValuesFocusable(true); | |
| 535 firstItem.setCloseButtonFocusable(true); | |
| 536 if (firstItem.isPlaceholder) | |
| 537 firstItem.setEditableValuesFocusable(true); | |
| 538 } | |
| 539 }, | |
| 540 | |
| 452 /** | 541 /** |
| 453 * May be overridden by subclasses to disable focusing the placeholder. | 542 * May be overridden by subclasses to disable focusing the placeholder. |
| 454 * @return {boolean} True if the placeholder element should be focused on | 543 * @return {boolean} True if the placeholder element should be focused on |
| 455 * edit commit. | 544 * edit commit. |
| 456 */ | 545 */ |
| 457 shouldFocusPlaceholder: function() { | 546 shouldFocusPlaceholder: function() { |
| 458 return true; | 547 return true; |
| 459 }, | 548 }, |
| 460 }; | 549 }; |
| 461 | 550 |
| 462 // Export | 551 // Export |
| 463 return { | 552 return { |
| 464 InlineEditableItem: InlineEditableItem, | 553 InlineEditableItem: InlineEditableItem, |
| 465 InlineEditableItemList: InlineEditableItemList, | 554 InlineEditableItemList: InlineEditableItemList, |
| 466 }; | 555 }; |
| 467 }); | 556 }); |
| OLD | NEW |