OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 cr.define('options', function() { |
| 6 const DeletableItem = options.DeletableItem; |
| 7 const DeletableItemList = options.DeletableItemList; |
| 8 |
| 9 /** |
| 10 * Creates a new list item with support for inline editing. |
| 11 * @constructor |
| 12 * @extends {options.DeletableListItem} |
| 13 */ |
| 14 function InlineEditableItem() { |
| 15 var el = cr.doc.createElement('div'); |
| 16 InlineEditableItem.decorate(el); |
| 17 return el; |
| 18 } |
| 19 |
| 20 /** |
| 21 * Decorates an element as a inline-editable list item. Note that this is |
| 22 * a subclass of DeletableItem. |
| 23 * @param {!HTMLElement} el The element to decorate. |
| 24 */ |
| 25 InlineEditableItem.decorate = function(el) { |
| 26 el.__proto__ = InlineEditableItem.prototype; |
| 27 el.decorate(); |
| 28 }; |
| 29 |
| 30 InlineEditableItem.prototype = { |
| 31 __proto__: DeletableItem.prototype, |
| 32 |
| 33 /** |
| 34 * Whether or not this item can be edited. |
| 35 * @type {boolean} |
| 36 * @private |
| 37 */ |
| 38 editable_: true, |
| 39 |
| 40 /** |
| 41 * Whether or not the current edit should be considered cancelled, rather |
| 42 * than committed, when editing ends. |
| 43 * @type {boolean} |
| 44 * @private |
| 45 */ |
| 46 editCancelled_: true, |
| 47 |
| 48 /** @inheritDoc */ |
| 49 decorate: function() { |
| 50 DeletableItem.prototype.decorate.call(this); |
| 51 |
| 52 this.addEventListener('keydown', this.handleKeyDown_.bind(this)); |
| 53 }, |
| 54 |
| 55 /** @inheritDoc */ |
| 56 selectionChanged: function() { |
| 57 if (this.editable) |
| 58 this.editing = this.selected; |
| 59 }, |
| 60 |
| 61 /** |
| 62 * Whether the user is currently editing the list item. |
| 63 * @type {boolean} |
| 64 */ |
| 65 get editing() { |
| 66 return this.hasAttribute('editing'); |
| 67 }, |
| 68 set editing(editing) { |
| 69 if (this.editing == editing) |
| 70 return; |
| 71 |
| 72 if (editing) |
| 73 this.setAttribute('editing', ''); |
| 74 else |
| 75 this.removeAttribute('editing'); |
| 76 |
| 77 if (editing) { |
| 78 this.editCancelled_ = false; |
| 79 |
| 80 cr.dispatchSimpleEvent(this, 'edit', true); |
| 81 |
| 82 var focusElement = this.initialFocusElement; |
| 83 // When this is called in response to the selectedChange event, |
| 84 // the list grabs focus immediately afterwards. Thus we must delay |
| 85 // our focus grab. |
| 86 if (focusElement) { |
| 87 window.setTimeout(function() { |
| 88 focusElement.focus(); |
| 89 focusElement.select(); |
| 90 }, 50); |
| 91 } |
| 92 } else { |
| 93 if (!this.editCancelled_ && this.hasBeenEdited() && |
| 94 this.currentInputIsValid) { |
| 95 cr.dispatchSimpleEvent(this, 'commitedit', true); |
| 96 } else { |
| 97 cr.dispatchSimpleEvent(this, 'canceledit', true); |
| 98 } |
| 99 } |
| 100 }, |
| 101 |
| 102 /** |
| 103 * Whether the item is editable. |
| 104 * @type {boolean} |
| 105 */ |
| 106 get editable() { |
| 107 return this.editable_; |
| 108 }, |
| 109 set editable(editable) { |
| 110 this.editable_ = editable; |
| 111 if (!editable) |
| 112 this.editing = false; |
| 113 }, |
| 114 |
| 115 /** |
| 116 * The HTML element that should have focus initially when editing starts. |
| 117 * Should be overriden by subclasses. |
| 118 * @type {HTMLElement} |
| 119 */ |
| 120 get initialFocusElement() { |
| 121 return null; |
| 122 }, |
| 123 |
| 124 /** |
| 125 * Whether the input in currently valid to submit. If this returns false |
| 126 * when editing would be submitted, either editing will not be ended, |
| 127 * or it will be cancelled, depending on the context. |
| 128 * Can be overrided by subclasses to perform input validation. |
| 129 */ |
| 130 get currentInputIsValid() { |
| 131 return true; |
| 132 }, |
| 133 |
| 134 /** |
| 135 * Returns true if the item has been changed by an edit. |
| 136 * Can be overrided by subclasses to return false when nothing has changed |
| 137 * to avoid unnecessary commits. |
| 138 */ |
| 139 hasBeenEdited: function() { |
| 140 return true; |
| 141 }, |
| 142 |
| 143 /** |
| 144 * Called a key is pressed. Handles committing and cancelling edits. |
| 145 * @param {Event} e The key down event. |
| 146 * @private |
| 147 */ |
| 148 handleKeyDown_: function(e) { |
| 149 if (!this.editing) |
| 150 return; |
| 151 |
| 152 var endEdit = false; |
| 153 switch (e.keyIdentifier) { |
| 154 case 'U+001B': // Esc |
| 155 this.editCancelled_ = true; |
| 156 endEdit = true; |
| 157 break; |
| 158 case 'Enter': |
| 159 if (this.currentInputIsValid) |
| 160 endEdit = true; |
| 161 break; |
| 162 } |
| 163 |
| 164 if (endEdit) { |
| 165 // Blurring will trigger the edit to end; see InlineEditableItemList. |
| 166 this.ownerDocument.activeElement.blur(); |
| 167 // Make sure that handled keys aren't passed on and double-handled. |
| 168 // (e.g., esc shouldn't both cancel an edit and close a subpage) |
| 169 e.stopPropagation(); |
| 170 } |
| 171 }, |
| 172 }; |
| 173 |
| 174 var InlineEditableItemList = cr.ui.define('list'); |
| 175 |
| 176 InlineEditableItemList.prototype = { |
| 177 __proto__: DeletableItemList.prototype, |
| 178 |
| 179 /** @inheritDoc */ |
| 180 decorate: function() { |
| 181 DeletableItemList.prototype.decorate.call(this); |
| 182 this.addEventListener('blur', this.handleBlur_.bind(this), true); |
| 183 }, |
| 184 |
| 185 /** |
| 186 * Called when an element in the list is blurred. Removes selection (thus |
| 187 * ending edit) if focus moves outside the list. |
| 188 * @param {Event} e The blur event. |
| 189 * @private |
| 190 */ |
| 191 handleBlur_: function(e) { |
| 192 // When the blur event happens we do not know who is getting focus so we |
| 193 // delay this a bit until we know if the new focus node is outside the |
| 194 // list. |
| 195 var list = this; |
| 196 var doc = e.target.ownerDocument; |
| 197 window.setTimeout(function() { |
| 198 var activeElement = doc.activeElement; |
| 199 if (!list.contains(activeElement)) |
| 200 list.selectionModel.unselectAll(); |
| 201 }, 50); |
| 202 }, |
| 203 }; |
| 204 |
| 205 // Export |
| 206 return { |
| 207 InlineEditableItem: InlineEditableItem, |
| 208 InlineEditableItemList: InlineEditableItemList, |
| 209 }; |
| 210 }); |
OLD | NEW |