]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/grid/CellEditor.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / grid / CellEditor.js
CommitLineData
6527f429
DM
1/**\r
2 * Internal utility class that provides default configuration for cell editing.\r
3 * @private\r
4 */\r
5Ext.define('Ext.grid.CellEditor', {\r
6 extend: 'Ext.Editor',\r
7\r
8 /**\r
9 * @property {Boolean} isCellEditor\r
10 * @readonly\r
11 * `true` in this class to identify an object as an instantiated CellEditor, or subclass thereof.\r
12 */\r
13 isCellEditor: true,\r
14 \r
15 alignment: 'l-l!',\r
16\r
17 hideEl : false,\r
18\r
19 cls: Ext.baseCSSPrefix + 'small-editor ' +\r
20 Ext.baseCSSPrefix + 'grid-editor ' +\r
21 Ext.baseCSSPrefix + 'grid-cell-editor',\r
22\r
23 treeNodeSelector: '.' + Ext.baseCSSPrefix + 'tree-node-text',\r
24\r
25 shim: false,\r
26\r
27 shadow: false,\r
28\r
29 // Set the grid that owns this editor.\r
30 // Called by CellEditing#getEditor\r
31 setGrid: function(grid) {\r
32 var me = this,\r
33 oldGrid = me.grid,\r
34 viewListeners;\r
35\r
36 if (grid !== oldGrid) {\r
37 viewListeners = {\r
38 beforeitemupdate: me.beforeItemUpdate,\r
39 itemupdate: me.onItemUpdate,\r
40 scope: me\r
41 };\r
42 // Remove previous refresh listener\r
43 if (oldGrid) {\r
44 oldGrid.getView().un(viewListeners);\r
45 }\r
46\r
47 me.grid = grid;\r
48\r
49 // On view refresh, we need to copy our DOM into the detached body to prevent it from being garbage collected.\r
50 grid.getView().on(viewListeners);\r
51 }\r
52 },\r
53\r
54 beforeViewRefresh: function(view) {\r
55 var me = this,\r
56 dom = me.el && me.el.dom;\r
57\r
58 if (dom) {\r
59 me.wasAllowBlur = me.allowBlur;\r
60 if (me.editing) {\r
61\r
62 // Clear the Panel's cellFocused flag prior to removing it from the DOM\r
63 // This will prevent the Panels onFocusLeave from processing the resulting blurring.\r
64 view.cellFocused = false;\r
65\r
66 // Set the Editor.allowBlur setting so that it does not process the upcoming field blur event and terminate the edit\r
67 me.allowBlur = false;\r
68 }\r
69\r
70 // Remove the editor from the view to protect it from annihilation: https://sencha.jira.com/browse/EXTJSIV-11713\r
71 if (dom.parentNode) {\r
72 // Set refreshing flag so that onFocusLeave caused by removing a focused element\r
73 // does not exit actionableMode\r
74 view.refreshing = true;\r
75 dom.parentNode.removeChild(dom);\r
76 }\r
77 }\r
78 },\r
79\r
80 onViewRefresh: function(view) {\r
81 var me = this,\r
82 dom = me.el && me.el.dom,\r
83 cell,\r
84 context = me.context;\r
85\r
86 if (dom) {\r
87 // Update the context with the possibly new contextual data\r
88 // (refresh might have been caused by a sort or column move etc)\r
89 cell = view.getCellByPosition(context, true);\r
90\r
91 // If the refresh was caused by eg column removal, the cell will not exist.\r
92 // In this case, terminate the edit.\r
93 if (!cell) {\r
94 me.allowBlur = me.wasAllowBlur;\r
95 me.completeEdit();\r
96 Ext.getDetachedBody().dom.appendChild(dom);\r
97 return;\r
98 }\r
99\r
100 context.node = view.getNode(context.record);\r
101 context.row = view.getRow(context.record);\r
102 context.cell = cell;\r
103 context.rowIdx = view.indexOf(context.row);\r
104 cell.insertBefore(dom, cell.firstChild);\r
105 me.boundEl = me.container = Ext.get(cell);\r
106 me.realign(true);\r
107\r
108 // If the view was refreshed while we were editing, replace it.\r
109 // On IE, the blur event will fire asynchronously, so we must leave\r
110 // allowBlur as false for a very short while longer.\r
111 // After which we reset it, and refocus the field.\r
112 if (me.editing) {\r
113 if (Ext.isIE) {\r
114 Ext.defer(function() {\r
115 // May have been destroyed immediately after refreshing!?\r
116 if (!me.destroyed) {\r
117 me.allowBlur = me.wasAllowBlur;\r
118 me.field.focus();\r
119 }\r
120 }, 10);\r
121 } else {\r
122 me.allowBlur = me.wasAllowBlur;\r
123 me.field.focus();\r
124 }\r
125 }\r
126 }\r
127 },\r
128\r
129 beforeItemUpdate: function(record, recordIndex, oldItemDom, columnsToUpdate) {\r
130 var me = this,\r
131 context = me.context,\r
132 l = columnsToUpdate.length,\r
133 i;\r
134\r
135 // If this CellEditor's row is to be updated, we *may* have to restore this editor\r
136 // due to cell content possibly being changed.\r
137 if (record === context.record) {\r
138 for (i = 0; i < l; i++) {\r
139\r
140 // If the cell is scheduled for update, we definitely will need restoration.\r
141 if (columnsToUpdate[i] === context.column) {\r
142 me.needsFixOnItemUpdate = true;\r
143 me.beforeViewRefresh(context.view);\r
144 return;\r
145 }\r
146 }\r
147 }\r
148 },\r
149\r
150 onItemUpdate: function(record, recordIndex, oldItemDom) {\r
151 var view = this.context.view;\r
152\r
153 if (this.needsFixOnItemUpdate) {\r
154\r
155 // The refreshing flag was set to indicate to the onFocusLeave listener that it\r
156 // should ignore focusleave caused by this Editor blurring.\r
157 this.needsFixOnItemUpdate = view.refreshing = false;\r
158 this.onViewRefresh(view);\r
159 }\r
160 },\r
161\r
162 startEdit: function(boundEl, value, doFocus) {\r
163 this.context = this.editingPlugin.context;\r
164 this.callParent([boundEl, value, doFocus]);\r
165 },\r
166\r
167 /**\r
168 * @private\r
169 * Shows the editor, end ensures that it is rendered into the correct view\r
170 * Hides the grid cell inner element when a cell editor is shown.\r
171 */\r
172 onShow: function() {\r
173 var me = this,\r
174 innerCell = me.boundEl.down(me.context.view.innerSelector);\r
175\r
176 if (innerCell) {\r
177 if (me.isForTree) {\r
178 innerCell = innerCell.child(me.treeNodeSelector);\r
179 }\r
180 innerCell.hide();\r
181 }\r
182\r
183 me.callParent(arguments);\r
184 },\r
185\r
186 onFocusEnter: function() {\r
187 var context = this.context,\r
188 view = context.view;\r
189 \r
190 // Focus restoration after a refresh may require realignment and correction\r
191 // of the context because it could have been due to a or filter operation and\r
192 // the context may have changed position.\r
193 context.node = view.getNode(context.record);\r
194 context.row = view.getRow(context.record);\r
195 context.cell = context.getCell(true);\r
196 context.rowIdx = view.indexOf(context.row);\r
197 this.realign(true);\r
198\r
199 this.callParent(arguments);\r
200\r
201 // Ensure that hide processing does not throw focus back to the previously focused element.\r
202 this.focusEnterEvent = null;\r
203 },\r
204\r
205 onEditComplete: function(remainVisible) {\r
206 // When being asked to process edit completion, if we are not hiding, restore the cell now\r
207 if (remainVisible) {\r
208 this.restoreCell();\r
209 }\r
210 this.callParent(arguments);\r
211 },\r
212\r
213 /**\r
214 * @private\r
215 * Shows the grid cell inner element when a cell editor is hidden\r
216 */\r
217 onHide: function() {\r
218 this.restoreCell();\r
219 this.callParent(arguments);\r
220 },\r
221\r
222 onSpecialKey: function(field, event) {\r
223 var me = this,\r
224 key = event.getKey(),\r
225 complete = me.completeOnEnter && key === event.ENTER,\r
226 cancel = me.cancelOnEsc && key === event.ESC,\r
227 view = me.editingPlugin.view;\r
228\r
229 if (complete || cancel) {\r
230 // Do not let the key event bubble into the NavigationModel after we're don processing it.\r
231 // We control the navigation action here; we focus the cell.\r
232 event.stopEvent();\r
233\r
234 // Maintain visibility so that focus doesn't leak.\r
235 // We need to direct focusback to the owning cell.\r
236 if (complete) {\r
237 me.completeEdit(true);\r
238 } else if (cancel) {\r
239 me.cancelEdit(true);\r
240 }\r
241\r
242 view.getNavigationModel().setPosition(me.context, null, event);\r
243 view.ownerGrid.setActionableMode(false);\r
244 }\r
245 },\r
246\r
247 getRefOwner: function() {\r
248 return this.column && this.column.getView();\r
249 },\r
250\r
251 restoreCell: function() {\r
252 var me = this,\r
253 innerCell = me.boundEl.down(me.context.view.innerSelector);\r
254\r
255 if (innerCell) {\r
256 if (me.isForTree) {\r
257 innerCell = innerCell.child(me.treeNodeSelector);\r
258 }\r
259 innerCell.show();\r
260 } \r
261 },\r
262\r
263 /**\r
264 * @private\r
265 * Fix checkbox blur when it is clicked.\r
266 */\r
267 afterRender: function() {\r
268 var me = this,\r
269 field = me.field;\r
270\r
271 me.callParent(arguments);\r
272\r
273 if (field.isCheckbox) {\r
274 field.mon(field.inputEl, {\r
275 mousedown: me.onCheckBoxMouseDown,\r
276 click: me.onCheckBoxClick,\r
277 scope: me\r
278 });\r
279 }\r
280 },\r
281 \r
282 /**\r
283 * @private\r
284 * Because when checkbox is clicked it loses focus completeEdit is bypassed.\r
285 */\r
286 onCheckBoxMouseDown: function() {\r
287 this.completeEdit = Ext.emptyFn;\r
288 },\r
289 \r
290 /**\r
291 * @private\r
292 * Restore checkbox focus and completeEdit method.\r
293 */\r
294 onCheckBoxClick: function() {\r
295 delete this.completeEdit;\r
296 this.field.focus(false, 10);\r
297 },\r
298 \r
299 /**\r
300 * @private\r
301 * Realigns the Editor to the grid cell, or to the text node in the grid inner cell\r
302 * if the inner cell contains multiple child nodes.\r
303 */\r
304 realign: function(autoSize) {\r
305 var me = this,\r
306 boundEl = me.boundEl,\r
307 innerCell = boundEl.down(me.context.view.innerSelector),\r
308 innerCellTextNode = innerCell.dom.firstChild,\r
309 width = boundEl.getWidth(),\r
310 offsets = Ext.Array.clone(me.offsets),\r
311 grid = me.grid,\r
312 xOffset,\r
313 v = '',\r
314\r
315 // innerCell is empty if there are no children, or there is one text node, and it contains whitespace\r
316 isEmpty = !innerCellTextNode || (innerCellTextNode.nodeType === 3 && !(Ext.String.trim(v = innerCellTextNode.data).length));\r
317\r
318 if (me.isForTree) {\r
319 // When editing a tree, adjust the width and offsets of the editor to line\r
320 // up with the tree cell's text element\r
321 xOffset = me.getTreeNodeOffset(innerCell);\r
322 width -= Math.abs(xOffset);\r
323 offsets[0] += xOffset;\r
324 }\r
325\r
326 if (grid.columnLines) {\r
327 // Subtract the column border width so that the editor displays inside the\r
328 // borders. The column border could be either on the left or the right depending\r
329 // on whether the grid is RTL - using the sum of both borders works in both modes.\r
330 width -= boundEl.getBorderWidth('rl');\r
331 }\r
332\r
333 if (autoSize === true) {\r
334 me.field.setWidth(width);\r
335 }\r
336\r
337 // https://sencha.jira.com/browse/EXTJSIV-10871 Ensure the data bearing element has a height from text.\r
338 if (isEmpty) {\r
339 innerCell.dom.innerHTML = 'X';\r
340 }\r
341\r
342 me.alignTo(boundEl, me.alignment, offsets);\r
343\r
344 if (isEmpty) {\r
345 innerCell.dom.firstChild.data = v;\r
346 }\r
347 },\r
348\r
349 getTreeNodeOffset: function(innerCell) {\r
350 return innerCell.child(this.treeNodeSelector).getOffsetsTo(innerCell)[0];\r
351 }\r
352});\r