source: trunk/web/addons/job_monarch/lib/extjs-30/examples/ux/RowEditor.js @ 625

Last change on this file since 625 was 625, checked in by ramonb, 15 years ago

lib/extjs-30:

  • new ExtJS 3.0
File size: 16.5 KB
Line 
1/*!
2 * Ext JS Library 3.0.0
3 * Copyright(c) 2006-2009 Ext JS, LLC
4 * licensing@extjs.com
5 * http://www.extjs.com/license
6 */
7Ext.ns('Ext.ux.grid');
8
9/**
10 * @class Ext.ux.grid.RowEditor
11 * @extends Ext.Panel
12 * Plugin (ptype = 'roweditor') that adds the ability to rapidly edit full rows in a grid.
13 * A validation mode may be enabled which uses AnchorTips to notify the user of all
14 * validation errors at once.
15 *
16 * @ptype roweditor
17 */
18Ext.ux.grid.RowEditor = Ext.extend(Ext.Panel, {
19    floating: true,
20    shadow: false,
21    layout: 'hbox',
22    cls: 'x-small-editor',
23    buttonAlign: 'center',
24    baseCls: 'x-row-editor',
25    elements: 'header,footer,body',
26    frameWidth: 5,
27    buttonPad: 3,
28    clicksToEdit: 'auto',
29    monitorValid: true,
30    focusDelay: 250,
31    errorSummary: true,
32
33    defaults: {
34        normalWidth: true
35    },
36
37    initComponent: function(){
38        Ext.ux.grid.RowEditor.superclass.initComponent.call(this);
39        this.addEvents(
40            /**
41             * @event beforeedit
42             * Fired before the row editor is activated.
43             * If the listener returns <tt>false</tt> the editor will not be activated.
44             * @param {Ext.ux.grid.RowEditor} roweditor This object
45             * @param {Number} rowIndex The rowIndex of the row just edited
46             */
47            'beforeedit',
48            /**
49             * @event validateedit
50             * Fired after a row is edited and passes validation.
51             * If the listener returns <tt>false</tt> changes to the record will not be set.
52             * @param {Ext.ux.grid.RowEditor} roweditor This object
53             * @param {Object} changes Object with changes made to the record.
54             * @param {Ext.data.Record} r The Record that was edited.
55             * @param {Number} rowIndex The rowIndex of the row just edited
56             */
57            'validateedit',
58            /**
59             * @event afteredit
60             * Fired after a row is edited and passes validation.  This event is fired
61             * after the store's update event is fired with this edit.
62             * @param {Ext.ux.grid.RowEditor} roweditor This object
63             * @param {Object} changes Object with changes made to the record.
64             * @param {Ext.data.Record} r The Record that was edited.
65             * @param {Number} rowIndex The rowIndex of the row just edited
66             */
67            'afteredit'
68        );
69    },
70
71    init: function(grid){
72        this.grid = grid;
73        this.ownerCt = grid;
74        if(this.clicksToEdit === 2){
75            grid.on('rowdblclick', this.onRowDblClick, this);
76        }else{
77            grid.on('rowclick', this.onRowClick, this);
78            if(Ext.isIE){
79                grid.on('rowdblclick', this.onRowDblClick, this);
80            }
81        }
82
83        // stopEditing without saving when a record is removed from Store.
84        grid.getStore().on('remove', function() {
85            this.stopEditing(false);
86        },this);
87
88        grid.on({
89            scope: this,
90            keydown: this.onGridKey,
91            columnresize: this.verifyLayout,
92            columnmove: this.refreshFields,
93            reconfigure: this.refreshFields,
94            destroy : this.destroy,
95            bodyscroll: {
96                buffer: 250,
97                fn: this.positionButtons
98            }
99        });
100        grid.getColumnModel().on('hiddenchange', this.verifyLayout, this, {delay:1});
101        grid.getView().on('refresh', this.stopEditing.createDelegate(this, []));
102    },
103
104    refreshFields: function(){
105        this.initFields();
106        this.verifyLayout();
107    },
108
109    isDirty: function(){
110        var dirty;
111        this.items.each(function(f){
112            if(String(this.values[f.id]) !== String(f.getValue())){
113                dirty = true;
114                return false;
115            }
116        }, this);
117        return dirty;
118    },
119
120    startEditing: function(rowIndex, doFocus){
121        if(this.editing && this.isDirty()){
122            this.showTooltip('You need to commit or cancel your changes');
123            return;
124        }
125        this.editing = true;
126        if(typeof rowIndex == 'object'){
127            rowIndex = this.grid.getStore().indexOf(rowIndex);
128        }
129        if(this.fireEvent('beforeedit', this, rowIndex) !== false){
130            var g = this.grid, view = g.getView();
131            var row = view.getRow(rowIndex);
132            var record = g.store.getAt(rowIndex);
133            this.record = record;
134            this.rowIndex = rowIndex;
135            this.values = {};
136            if(!this.rendered){
137                this.render(view.getEditorParent());
138            }
139            var w = Ext.fly(row).getWidth();
140            this.setSize(w);
141            if(!this.initialized){
142                this.initFields();
143            }
144            var cm = g.getColumnModel(), fields = this.items.items, f, val;
145            for(var i = 0, len = cm.getColumnCount(); i < len; i++){
146                val = this.preEditValue(record, cm.getDataIndex(i));
147                f = fields[i];
148                f.setValue(val);
149                this.values[f.id] = val || '';
150            }
151            this.verifyLayout(true);
152            if(!this.isVisible()){
153                this.setPagePosition(Ext.fly(row).getXY());
154            } else{
155                this.el.setXY(Ext.fly(row).getXY(), {duration:0.15});
156            }
157            if(!this.isVisible()){
158                this.show().doLayout();
159            }
160            if(doFocus !== false){
161                this.doFocus.defer(this.focusDelay, this);
162            }
163        }
164    },
165
166    stopEditing : function(saveChanges){
167        this.editing = false;
168        if(!this.isVisible()){
169            return;
170        }
171        if(saveChanges === false || !this.isValid()){
172            this.hide();
173            return;
174        }
175        var changes = {}, r = this.record, hasChange = false;
176        var cm = this.grid.colModel, fields = this.items.items;
177        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
178            if(!cm.isHidden(i)){
179                var dindex = cm.getDataIndex(i);
180                if(!Ext.isEmpty(dindex)){
181                    var oldValue = r.data[dindex];
182                    var value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex);
183                    if(String(oldValue) !== String(value)){
184                        changes[dindex] = value;
185                        hasChange = true;
186                    }
187                }
188            }
189        }
190        if(hasChange && this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false){
191            r.beginEdit();
192            for(var k in changes){
193                if(changes.hasOwnProperty(k)){
194                    r.set(k, changes[k]);
195                }
196            }
197            r.endEdit();
198            this.fireEvent('afteredit', this, changes, r, this.rowIndex);
199        }
200        this.hide();
201    },
202
203    verifyLayout: function(force){
204        if(this.el && (this.isVisible() || force === true)){
205            var row = this.grid.getView().getRow(this.rowIndex);
206            this.setSize(Ext.fly(row).getWidth(), Ext.isIE ? Ext.fly(row).getHeight() + (Ext.isBorderBox ? 9 : 0) : undefined);
207            var cm = this.grid.colModel, fields = this.items.items;
208            for(var i = 0, len = cm.getColumnCount(); i < len; i++){
209                if(!cm.isHidden(i)){
210                    var adjust = 0;
211                    if(i === 0){
212                        adjust += 0; // outer padding
213                    }
214                    if(i === (len - 1)){
215                        adjust += 3; // outer padding
216                    } else{
217                        adjust += 1;
218                    }
219                    fields[i].show();
220                    fields[i].setWidth(cm.getColumnWidth(i) - adjust);
221                } else{
222                    fields[i].hide();
223                }
224            }
225            this.doLayout();
226            this.positionButtons();
227        }
228    },
229
230    slideHide : function(){
231        this.hide();
232    },
233
234    initFields: function(){
235        var cm = this.grid.getColumnModel(), pm = Ext.layout.ContainerLayout.prototype.parseMargins;
236        this.removeAll(false);
237        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
238            var c = cm.getColumnAt(i);
239            var ed = c.getEditor();
240            if(!ed){
241                ed = c.displayEditor || new Ext.form.DisplayField();
242            }
243            if(i == 0){
244                ed.margins = pm('0 1 2 1');
245            } else if(i == len - 1){
246                ed.margins = pm('0 0 2 1');
247            } else{
248                ed.margins = pm('0 1 2');
249            }
250            ed.setWidth(cm.getColumnWidth(i));
251            ed.column = c;
252            if(ed.ownerCt !== this){
253                ed.on('focus', this.ensureVisible, this);
254                ed.on('specialkey', this.onKey, this);
255            }
256            this.insert(i, ed);
257        }
258        this.initialized = true;
259    },
260
261    onKey: function(f, e){
262        if(e.getKey() === e.ENTER){
263            this.stopEditing(true);
264            e.stopPropagation();
265        }
266    },
267
268    onGridKey: function(e){
269        if(e.getKey() === e.ENTER && !this.isVisible()){
270            var r = this.grid.getSelectionModel().getSelected();
271            if(r){
272                var index = this.grid.store.indexOf(r);
273                this.startEditing(index);
274                e.stopPropagation();
275            }
276        }
277    },
278
279    ensureVisible: function(editor){
280        if(this.isVisible()){
281             this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);
282        }
283    },
284
285    onRowClick: function(g, rowIndex, e){
286        if(this.clicksToEdit == 'auto'){
287            var li = this.lastClickIndex;
288            this.lastClickIndex = rowIndex;
289            if(li != rowIndex && !this.isVisible()){
290                return;
291            }
292        }
293        this.startEditing(rowIndex, false);
294        this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
295    },
296
297    onRowDblClick: function(g, rowIndex, e){
298        this.startEditing(rowIndex, false);
299        this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
300    },
301
302    onRender: function(){
303        Ext.ux.grid.RowEditor.superclass.onRender.apply(this, arguments);
304        this.el.swallowEvent(['keydown', 'keyup', 'keypress']);
305        this.btns = new Ext.Panel({
306            baseCls: 'x-plain',
307            cls: 'x-btns',
308            elements:'body',
309            layout: 'table',
310            width: (this.minButtonWidth * 2) + (this.frameWidth * 2) + (this.buttonPad * 4), // width must be specified for IE
311            items: [{
312                ref: 'saveBtn',
313                itemId: 'saveBtn',
314                xtype: 'button',
315                text: this.saveText || 'Save',
316                width: this.minButtonWidth,
317                handler: this.stopEditing.createDelegate(this, [true])
318            }, {
319                xtype: 'button',
320                text: this.cancelText || 'Cancel',
321                width: this.minButtonWidth,
322                handler: this.stopEditing.createDelegate(this, [false])
323            }]
324        });
325        this.btns.render(this.bwrap);
326    },
327
328    afterRender: function(){
329        Ext.ux.grid.RowEditor.superclass.afterRender.apply(this, arguments);
330        this.positionButtons();
331        if(this.monitorValid){
332            this.startMonitoring();
333        }
334    },
335
336    onShow: function(){
337        if(this.monitorValid){
338            this.startMonitoring();
339        }
340        Ext.ux.grid.RowEditor.superclass.onShow.apply(this, arguments);
341    },
342
343    onHide: function(){
344        Ext.ux.grid.RowEditor.superclass.onHide.apply(this, arguments);
345        this.stopMonitoring();
346        this.grid.getView().focusRow(this.rowIndex);
347    },
348
349    positionButtons: function(){
350        if(this.btns){
351            var h = this.el.dom.clientHeight;
352            var view = this.grid.getView();
353            var scroll = view.scroller.dom.scrollLeft;
354            var width =  view.mainBody.getWidth();
355            var bw = this.btns.getWidth();
356            this.btns.el.shift({left: (width/2)-(bw/2)+scroll, top: h - 2, stopFx: true, duration:0.2});
357        }
358    },
359
360    // private
361    preEditValue : function(r, field){
362        var value = r.data[field];
363        return this.autoEncode && typeof value === 'string' ? Ext.util.Format.htmlDecode(value) : value;
364    },
365
366    // private
367    postEditValue : function(value, originalValue, r, field){
368        return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;
369    },
370
371    doFocus: function(pt){
372        if(this.isVisible()){
373            var index = 0;
374            if(pt){
375                index = this.getTargetColumnIndex(pt);
376            }
377            var cm = this.grid.getColumnModel();
378            for(var i = index||0, len = cm.getColumnCount(); i < len; i++){
379                var c = cm.getColumnAt(i);
380                if(!c.hidden && c.getEditor()){
381                    c.getEditor().focus();
382                    break;
383                }
384            }
385        }
386    },
387
388    getTargetColumnIndex: function(pt){
389        var grid = this.grid, v = grid.view;
390        var x = pt.left;
391        var cms = grid.colModel.config;
392        var i = 0, match = false;
393        for(var len = cms.length, c; c = cms[i]; i++){
394            if(!c.hidden){
395                if(Ext.fly(v.getHeaderCell(i)).getRegion().right >= x){
396                    match = i;
397                    break;
398                }
399            }
400        }
401        return match;
402    },
403
404    startMonitoring : function(){
405        if(!this.bound && this.monitorValid){
406            this.bound = true;
407            Ext.TaskMgr.start({
408                run : this.bindHandler,
409                interval : this.monitorPoll || 200,
410                scope: this
411            });
412        }
413    },
414
415    stopMonitoring : function(){
416        this.bound = false;
417        if(this.tooltip){
418            this.tooltip.hide();
419        }
420    },
421
422    isValid: function(){
423        var valid = true;
424        this.items.each(function(f){
425            if(!f.isValid(true)){
426                valid = false;
427                return false;
428            }
429        });
430        return valid;
431    },
432
433    // private
434    bindHandler : function(){
435        if(!this.bound){
436            return false; // stops binding
437        }
438        var valid = this.isValid();
439        if(!valid && this.errorSummary){
440            this.showTooltip(this.getErrorText().join(''));
441        }
442        this.btns.saveBtn.setDisabled(!valid);
443        this.fireEvent('validation', this, valid);
444    },
445
446    showTooltip: function(msg){
447        var t = this.tooltip;
448        if(!t){
449            t = this.tooltip = new Ext.ToolTip({
450                maxWidth: 600,
451                cls: 'errorTip',
452                width: 300,
453                title: 'Errors',
454                autoHide: false,
455                anchor: 'left',
456                anchorToTarget: true,
457                mouseOffset: [40,0]
458            });
459        }
460        t.initTarget(this.items.last().getEl());
461        if(!t.rendered){
462            t.show();
463            t.hide();
464        }
465        t.body.update(msg);
466        t.doAutoWidth();
467        t.show();
468    },
469
470    getErrorText: function(){
471        var data = ['<ul>'];
472        this.items.each(function(f){
473            if(!f.isValid(true)){
474                data.push('<li>', f.activeError, '</li>');
475            }
476        });
477        data.push('</ul>');
478        return data;
479    }
480});
481Ext.preg('roweditor', Ext.ux.grid.RowEditor);
482
483Ext.override(Ext.form.Field, {
484    markInvalid : function(msg){
485        if(!this.rendered || this.preventMark){ // not rendered
486            return;
487        }
488        msg = msg || this.invalidText;
489
490        var mt = this.getMessageHandler();
491        if(mt){
492            mt.mark(this, msg);
493        }else if(this.msgTarget){
494            this.el.addClass(this.invalidClass);
495            var t = Ext.getDom(this.msgTarget);
496            if(t){
497                t.innerHTML = msg;
498                t.style.display = this.msgDisplay;
499            }
500        }
501        this.activeError = msg;
502        this.fireEvent('invalid', this, msg);
503    }
504});
505
506Ext.override(Ext.ToolTip, {
507    doAutoWidth : function(){
508        var bw = this.body.getTextWidth();
509        if(this.title){
510            bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
511        }
512        bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + 20;
513        this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
514
515        // IE7 repaint bug on initial show
516        if(Ext.isIE7 && !this.repainted){
517            this.el.repaint();
518            this.repainted = true;
519        }
520    }
521});
Note: See TracBrowser for help on using the repository browser.