Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / examples / ux / grid / menu / RangeMenu.js
1 /**
2  * @class Ext.ux.grid.menu.RangeMenu
3  * @extends Ext.menu.Menu
4  * Custom implementation of {@link Ext.menu.Menu} that has preconfigured items for entering numeric
5  * range comparison values: less-than, greater-than, and equal-to. This is used internally
6  * by {@link Ext.ux.grid.filter.NumericFilter} to create its menu.
7  */
8 Ext.define('Ext.ux.grid.menu.RangeMenu', {
9     extend: 'Ext.menu.Menu',
10
11     /**
12      * @cfg {String} fieldCls
13      * The Class to use to construct each field item within this menu
14      * Defaults to:<pre>
15      * fieldCls : Ext.form.field.Number
16      * </pre>
17      */
18     fieldCls : 'Ext.form.field.Number',
19
20     /**
21      * @cfg {Object} fieldCfg
22      * The default configuration options for any field item unless superseded
23      * by the <code>{@link #fields}</code> configuration.
24      * Defaults to:<pre>
25      * fieldCfg : {}
26      * </pre>
27      * Example usage:
28      * <pre><code>
29 fieldCfg : {
30     width: 150,
31 },
32      * </code></pre>
33      */
34
35     /**
36      * @cfg {Object} fields
37      * The field items may be configured individually
38      * Defaults to <tt>undefined</tt>.
39      * Example usage:
40      * <pre><code>
41 fields : {
42     gt: { // override fieldCfg options
43         width: 200,
44         fieldCls: Ext.ux.form.CustomNumberField // to override default {@link #fieldCls}
45     }
46 },
47      * </code></pre>
48      */
49
50     /**
51      * @cfg {Object} iconCls
52      * The iconCls to be applied to each comparator field item.
53      * Defaults to:<pre>
54 iconCls : {
55     gt : 'ux-rangemenu-gt',
56     lt : 'ux-rangemenu-lt',
57     eq : 'ux-rangemenu-eq'
58 }
59      * </pre>
60      */
61     iconCls : {
62         gt : 'ux-rangemenu-gt',
63         lt : 'ux-rangemenu-lt',
64         eq : 'ux-rangemenu-eq'
65     },
66
67     /**
68      * @cfg {Object} fieldLabels
69      * Accessible label text for each comparator field item. Can be overridden by localization
70      * files. Defaults to:<pre>
71 fieldLabels : {
72      gt: 'Greater Than',
73      lt: 'Less Than',
74      eq: 'Equal To'
75 }</pre>
76      */
77     fieldLabels: {
78         gt: 'Greater Than',
79         lt: 'Less Than',
80         eq: 'Equal To'
81     },
82
83     /**
84      * @cfg {Object} menuItemCfgs
85      * Default configuration options for each menu item
86      * Defaults to:<pre>
87 menuItemCfgs : {
88     emptyText: 'Enter Filter Text...',
89     selectOnFocus: true,
90     width: 125
91 }
92      * </pre>
93      */
94     menuItemCfgs : {
95         emptyText: 'Enter Number...',
96         selectOnFocus: false,
97         width: 155
98     },
99
100     /**
101      * @cfg {Array} menuItems
102      * The items to be shown in this menu.  Items are added to the menu
103      * according to their position within this array. Defaults to:<pre>
104      * menuItems : ['lt','gt','-','eq']
105      * </pre>
106      */
107     menuItems : ['lt', 'gt', '-', 'eq'],
108
109
110     constructor : function (config) {
111         var me = this,
112             fields, fieldCfg, i, len, item, cfg, Cls;
113
114         me.callParent(arguments);
115
116         fields = me.fields = me.fields || {};
117         fieldCfg = me.fieldCfg = me.fieldCfg || {};
118         
119         me.addEvents(
120             /**
121              * @event update
122              * Fires when a filter configuration has changed
123              * @param {Ext.ux.grid.filter.Filter} this The filter object.
124              */
125             'update'
126         );
127       
128         me.updateTask = Ext.create('Ext.util.DelayedTask', me.fireUpdate, me);
129     
130         for (i = 0, len = me.menuItems.length; i < len; i++) {
131             item = me.menuItems[i];
132             if (item !== '-') {
133                 // defaults
134                 cfg = {
135                     itemId: 'range-' + item,
136                     enableKeyEvents: true,
137                     hideLabel: false,
138                     fieldLabel: me.iconTpl.apply({
139                         cls: me.iconCls[item] || 'no-icon',
140                         text: me.fieldLabels[item] || '',
141                         src: Ext.BLANK_IMAGE_URL
142                     }),
143                     labelSeparator: '',
144                     labelWidth: 29,
145                     listeners: {
146                         scope: me,
147                         change: me.onInputChange,
148                         keyup: me.onInputKeyUp,
149                         el: {
150                             click: function(e) {
151                                 e.stopPropagation();
152                             }
153                         }
154                     },
155                     activate: Ext.emptyFn,
156                     deactivate: Ext.emptyFn
157                 };
158                 Ext.apply(
159                     cfg,
160                     // custom configs
161                     Ext.applyIf(fields[item] || {}, fieldCfg[item]),
162                     // configurable defaults
163                     me.menuItemCfgs
164                 );
165                 Cls = cfg.fieldCls || me.fieldCls;
166                 item = fields[item] = Ext.create(Cls, cfg);
167             }
168             me.add(item);
169         }
170     },
171
172     /**
173      * @private
174      * called by this.updateTask
175      */
176     fireUpdate : function () {
177         this.fireEvent('update', this);
178     },
179     
180     /**
181      * Get and return the value of the filter.
182      * @return {String} The value of this filter
183      */
184     getValue : function () {
185         var result = {}, key, field;
186         for (key in this.fields) {
187             field = this.fields[key];
188             if (field.isValid() && field.getValue() !== null) {
189                 result[key] = field.getValue();
190             }
191         }
192         return result;
193     },
194   
195     /**
196      * Set the value of this menu and fires the 'update' event.
197      * @param {Object} data The data to assign to this menu
198      */ 
199     setValue : function (data) {
200         var key;
201         for (key in this.fields) {
202             this.fields[key].setValue(key in data ? data[key] : '');
203         }
204         this.fireEvent('update', this);
205     },
206
207     /**  
208      * @private
209      * Handler method called when there is a keyup event on an input
210      * item of this menu.
211      */
212     onInputKeyUp: function(field, e) {
213         if (e.getKey() === e.RETURN && field.isValid()) {
214             e.stopEvent();
215             this.hide();
216         }
217     },
218
219     /**
220      * @private
221      * Handler method called when the user changes the value of one of the input
222      * items in this menu.
223      */
224     onInputChange: function(field) {
225         var me = this,
226             fields = me.fields,
227             eq = fields.eq,
228             gt = fields.gt,
229             lt = fields.lt;
230
231         if (field == eq) {
232             if (gt) {
233                 gt.setValue(null);
234             }
235             if (lt) {
236                 lt.setValue(null);
237             }
238         }
239         else {
240             eq.setValue(null);
241         }
242
243         // restart the timer
244         this.updateTask.delay(this.updateBuffer);
245     }
246 }, function() {
247
248     /**
249      * @cfg {Ext.XTemplate} iconTpl
250      * A template for generating the label for each field in the menu
251      */
252     this.prototype.iconTpl = Ext.create('Ext.XTemplate',
253         '<img src="{src}" alt="{text}" class="' + Ext.baseCSSPrefix + 'menu-item-icon ux-rangemenu-icon {cls}" />'
254     );
255
256 });