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; | |
arv (Not doing code reviews)
2011/01/11 23:29:29
It would be good if we could move these to cr.ui i
stuartmorgan
2011/01/12 00:26:51
Sure; added to my TODO list.
| |
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 |